{"record":{"id":"8905383a6581be31","repo":"stride3d/stride","slug":"shader-compositions-can-only-be-indexed-with-a-constant","errorCode":null,"errorMessage":"Shader compositions can only be indexed with a constant: '{indexer2.Index}' is resolved at mix time, not at runtime.","messagePattern":"Shader compositions can only be indexed with a constant: '(.+?)' is resolved at mix time, not at runtime\\.","errorType":"exception","errorClass":"NotImplementedException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/Expression.cs","lineNumber":1363,"sourceCode":"                    break;\n                // Array indexer for shader compositions: emit an OpAccessChain with a constant index,\n                // which ShaderMixer.ProcessMemberAccessAndForeach resolves to compositions[index] at\n                // mix time. A dynamic index has no composition to resolve to, hence the IntegerLiteral guard.\n                case (PointerType { BaseType: ArrayType { BaseType: ShaderSymbol compositionType } } p, IndexerExpression { Index: IntegerLiteral } indexer):\n                    {\n                        if (compiler == null)\n                        {\n                            indexer.Index.ProcessSymbol(table);\n                            accessor.Type = new PointerType(compositionType, p.StorageClass);\n                            break;\n                        }\n\n                        var indexerValue = indexer.Index.CompileAsValue(table, compiler);\n                        PushAccessChainId(accessChainIds, indexerValue.Id);\n                        break;\n                    }\n                case (PointerType { BaseType: ArrayType { BaseType: ShaderSymbol } }, IndexerExpression indexer2):\n                    throw new NotImplementedException(\n                        $\"Shader compositions can only be indexed with a constant: '{indexer2.Index}' is resolved at mix time, not at runtime.\");\n                // Array indexer for arrays\n                case (PointerType { BaseType: ArrayType { BaseType: var t } } p, IndexerExpression indexer):\n                    {\n                        if (compiler == null)\n                        {\n                            indexer.Index.ProcessSymbol(table);\n                            accessor.Type = new PointerType(t, p.StorageClass);\n                            break;\n                        }\n                        var indexerValue = indexer.Index.CompileAsValue(table, compiler);\n                        PushAccessChainId(accessChainIds, indexerValue.Id);\n                        break;\n                    }\n                // Array indexer for vector/matrix\n                case (PointerType { BaseType: VectorType or MatrixType } p, IndexerExpression indexer):\n                    {\n                        if (compiler == null)","sourceCodeStart":1345,"sourceCodeEnd":1381,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/Expression.cs#L1345-L1381","documentation":"Arrays of ShaderSymbol (shader compositions/mixin arrays) in SDSL are resolved at mix time, not at runtime, so they cannot be dynamically indexed by a runtime value. When the compiler encounters an IndexerExpression over a PointerType to an ArrayType of ShaderSymbol with an actual compiled index value, it throws this NotImplementedException. Only constant (compile-time) indexing of composition arrays is supported.","triggerScenarios":"Writing compositionsArray[i] where i is a runtime/mix-time variable rather than a compile-time constant, inside shader code operating on an array of shader compositions.","commonSituations":"Trying to loop over composition arrays with a runtime index, selecting a mixin variant based on a runtime uniform, or porting code from regular arrays (runtime-indexable) to composition arrays (constant-index only).","solutions":["Use a compile-time constant index (literal or const value) when indexing shader composition arrays.","Select the composition outside the shader via a branching composition/conditional mixin at effect-authoring time.","Restructure so the runtime-selectable data lives in a regular (non-composition) array or a uniform buffer instead.","Unroll the loop manually or use a static for-loop with constant bounds if a loop index is needed."],"exampleFix":"// before (runtime index — not allowed)\ncomps[i].Shade(result);\n\n// after (constant index / static selection)\nconst int i = 1;\ncomps[i].Shade(result);","handlingStrategy":"validation","validationCode":"// composition arrays must be indexed by compile-time constants\nif (arrayElemType is ShaderSymbol && !(indexExpr is LiteralExpression or { ValueType: ScalarType and ConstantValue != null }))\n    throw new InvalidOperationException(\"Shader composition arrays require constant indices\");","typeGuard":"static bool IsConstantIndex(Expression e) => e is LiteralExpression || (e.ValueType is ScalarType s && s.HasConstantValue);","tryCatchPattern":"try { CompileShader(source); }\ncatch (NotImplementedException e) when (e.Message.Contains(\"resolved at mix time\"))\n{\n    // switch to a constant index or move selection to effect authoring\n}","preventionTips":["Index composition arrays only with literals or const values.","Move runtime variant selection to the effect/composition layer, not shader code.","Keep runtime-selectable data in uniform buffers or regular arrays instead of composition arrays."],"tags":["shader","sdsl","composition","constant-indexing"],"backgroundTag":"unsupported-operation","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}