{"record":{"id":"aa1da89268e94a3d","repo":"stride3d/stride","slug":"can-t-have-member-variables-with-generic-shader-types","errorCode":null,"errorMessage":"Can't have member variables with generic shader types","messagePattern":"Can't have member variables with generic shader types","errorType":"exception","errorClass":"NotImplementedException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/ShaderElements.MethodOrMember.cs","lineNumber":192,"sourceCode":"    public StreamKind StreamKind { get; set; } = streamKind;\n    public bool IsCompose { get; set; }\n    public bool IsArray => TypeName?.IsArray ?? false;\n    public Expression? Value { get; set; } = initialValue;\n    public TypeModifier TypeModifier { get; set; } = typeModifier;\n    public StorageClass StorageClass { get; set; } = storageClass;\n    public InterpolationModifier Interpolation { get; set; } = interpolation;\n\n    public Symbol? Symbol { get; private set; }\n\n    public override void ProcessSymbol(SymbolTable table, SpirvContext context)\n    {\n        base.ProcessSymbol(table, context);\n        foreach (var generic in TypeName.Generics)\n            generic.ProcessSymbol(table);\n        if (!TypeName.TryResolveType(table, context, out var memberType))\n        {\n            if (TypeName.Name.Contains(\"<\"))\n                throw new NotImplementedException(\"Can't have member variables with generic shader types\");\n            var classSource = new ShaderClassInstantiation(TypeName.Name, []);\n            var shader = SpirvBuilder.GetOrLoadShader(table.ShaderLoader, classSource, table.CurrentMacros.AsSpan(), ResolveStep.Compile, context);\n            classSource.Buffer = shader;\n            var shaderType = ShaderClass.LoadAndCacheExternalShaderDefinition(table, context, classSource);\n\n            // Resolve again (we don't use shaderType directly, because it might lack info such as ArrayType)\n            TypeName.ProcessSymbol(table);\n            memberType = TypeName.Type!;\n        }\n\n        if (memberType is AppendStructuredBufferType or ConsumeStructuredBufferType)\n        {\n            var bufTypeName = memberType is AppendStructuredBufferType ? \"AppendStructuredBuffer\" : \"ConsumeStructuredBuffer\";\n            table.AddError(new(TypeName.Info, $\"{bufTypeName} is not supported. Use RWStructuredBuffer with a separate counter buffer instead (variable '{Name}').\"));\n            return;\n        }\n\n        var storageClass = (memberType, StorageClass, StreamKind) switch","sourceCodeStart":174,"sourceCodeEnd":210,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/ShaderElements.MethodOrMember.cs#L174-L210","documentation":"During member-variable symbol processing, if a generic type name (containing '<') cannot be resolved to a known shader type, the compiler cannot instantiate it and throws. Member variables with generic shader types are not supported by the SPIR-V backend, so it fails fast with NotImplementedException.","triggerScenarios":"Declaring a member variable whose TypeName contains '<' and does not resolve via TryResolveType, e.g. an uninstantiated generic shader class as a field.","commonSituations":"Using generic composition types like ShaderBase<T> as member fields instead of concrete instantiated types in a shader class.","solutions":["Replace the generic member type with a fully resolved concrete shader type.","Express the generic instantiation via shader composition/inherit syntax rather than a member variable.","Check spelling and that the referenced shader module exists so it resolves before the generic check."],"exampleFix":"// before\nShaderBase<MyEffect> effectMember;\n// after\nMyEffect effectMember; // concrete instantiated type","handlingStrategy":"type-guard","validationCode":"if (typeName.Contains('<') && !table.TryResolveType(typeName, out _))\n    throw new InvalidOperationException(\"Resolve generic shader types before declaring them as members\");","typeGuard":"static bool IsResolvedMemberType(TypeName tn, SymbolTable table) =>\n    !tn.Name.Contains('<') || tn.TryResolveType(table, null, out _);","tryCatchPattern":"try { shaderClass.ProcessSymbol(table, context); }\ncatch (NotImplementedException ex) when (ex.Message.Contains(\"generic shader types\"))\n{\n    // replace the generic member with a concrete instantiated type\n}","preventionTips":["Never declare member fields with uninstantiated generic shader types.","Use composition/inherit syntax for generic shader combinations instead of fields."],"tags":["shader","sdsl","generics","type-resolution"],"backgroundTag":"type-mismatch","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"}