{"record":{"id":"55e3044a22a06deb","repo":"stride3d/stride","slug":"generic-argument-i-expr-for-classsource-classname-could-not","errorCode":null,"errorMessage":"Generic argument {i} ({expr}) for {classSource.ClassName} could not be resolved during mix phase","messagePattern":"Generic argument (.+?) \\((.+?)\\) for (.+?) could not be resolved during mix phase","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Class.cs","lineNumber":814,"sourceCode":"    /// <param name=\"classSource\">The generics parameters should be in <see cref=\"parentBuffer\"/>.</param>\n    /// <param name=\"macros\"></param>\n    /// <param name=\"resolveStep\"></param>\n    /// <returns></returns>\n    /// <param name=\"parentBuffer\"></param>\n    public static ShaderBuffers GetOrLoadShader(IExternalShaderLoader shaderLoader, ShaderClassInstantiation classSource, ReadOnlySpan<ShaderMacro> macros, ResolveStep resolveStep, SpirvContext context)\n    {\n        if (resolveStep == ResolveStep.Mix && classSource.GenericArguments.Length > 0)\n        {\n            // At mix time, generics are fully resolved — resolve to string values\n            // and use the value-based path which caches to shaderLoader.Cache (persistent)\n            var genericValues = new string[classSource.GenericArguments.Length];\n            for (int i = 0; i < genericValues.Length; i++)\n            {\n                var expr = classSource.GenericArguments[i];\n                if (expr.TryEvaluate(out var constantValue) && constantValue is not null)\n                    genericValues[i] = ShaderClassSource.ConvertGenericArgToString(constantValue);\n                else\n                    throw new InvalidOperationException($\"Generic argument {i} ({expr}) for {classSource.ClassName} could not be resolved during mix phase\");\n            }\n\n            var result = GetOrLoadShader(shaderLoader, classSource.ClassName, genericValues, macros);\n\n            // PostProcess: update classSource (same as GenericResolverFromInstantiatingBuffer.PostProcess)\n            var classNameWithGenerics = $\"{classSource.ClassName}<{string.Join(\",\", genericValues)}>\";\n            classSource.ClassName = classNameWithGenerics;\n            classSource.GenericArguments = [];\n\n            return result;\n        }\n\n        return GetOrLoadShader(shaderLoader, classSource.ClassName, new GenericResolverFromInstantiatingBuffer(classSource, resolveStep, context), macros);\n    }\n\n    public static ShaderBuffers GetOrLoadShader(IExternalShaderLoader shaderLoader, string className, string[]? genericValues, ReadOnlySpan<ShaderMacro> macros)\n    {\n        if (genericValues is null)","sourceCodeStart":796,"sourceCodeEnd":832,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Class.cs#L796-L832","documentation":"When instantiating a generic shader during the mix phase, each ConstantExpression in classSource.GenericArguments must TryEvaluate to a non-null constant so it can be converted to a string via ShaderClassSource.ConvertGenericArgToString. If any argument cannot be evaluated to a constant, the builder throws InvalidOperationException naming the argument index, expression, and class.","triggerScenarios":"Calling GetOrLoadShader / InstantiateGenericShader with a ShaderClassSource whose GenericArguments still contain unresolved GenericParamExpr or symbolic expressions at mix phase.","commonSituations":"Instantiating `MyShader<Parent.N>` where Parent's generic N was never substituted because Parent wasn't imported; passing an expression referencing a runtime value instead of a compile-time constant; nested generic instantiations where an inner argument stayed symbolic.","solutions":["Provide concrete constant expressions (literals or previously resolved constants) for every generic argument.","Import the shader whose generic parameter the expression references so it can be substituted before the mix phase.","Evaluate the argument earlier in the pipeline (ResolveStep.Compile) and store the resolved value in classSource.GenericArguments."],"exampleFix":"// before\nvar source = new ShaderClassSource(\"MyShader\", new GenericParamExpr(\"Parent\", 0));\n// after\nvar source = new ShaderClassSource(\"MyShader\", new LiteralExpr(4.0f));","handlingStrategy":"validation","validationCode":"for (int i = 0; i < classSource.GenericArguments.Length; i++)\n    if (!classSource.GenericArguments[i].TryEvaluate(out var v) || v is null)\n        throw new ArgumentException($\"Generic argument {i} of {classSource.ClassName} must be a compile-time constant\");","typeGuard":"bool IsConcrete(ConstantExpression e) => e.TryEvaluate(out var v) && v is not null;","tryCatchPattern":"try { var result = builder.InstantiateGenericShader(loader, classSource, macros); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"could not be resolved during mix phase\")) { /* substitute generics earlier or supply literals */ }","preventionTips":["Only pass literal/constant generic arguments at instantiation sites","Resolve dependent generics (e.g. Parent.N) before the mix phase","Validate all generic arguments with TryEvaluate before calling the builder"],"tags":["spirv","generics","mix-phase","unresolved-expression"],"backgroundTag":"invalid-argument-value","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"}