stride3d/stride · error · NotImplementedException

Can't have member variables with generic shader types

Error message

Can't have member variables with generic shader types

What it means

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.

Solutions

  1. Replace the generic member type with a fully resolved concrete shader type.
  2. Express the generic instantiation via shader composition/inherit syntax rather than a member variable.
  3. Check spelling and that the referenced shader module exists so it resolves before the generic check.

Example fix

// before
ShaderBase<MyEffect> effectMember;
// after
MyEffect effectMember; // concrete instantiated type
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeName.Contains('<') && !table.TryResolveType(typeName, out _))
    throw new InvalidOperationException("Resolve generic shader types before declaring them as members");

Type guard

static bool IsResolvedMemberType(TypeName tn, SymbolTable table) =>
    !tn.Name.Contains('<') || tn.TryResolveType(table, null, out _);

Try / catch

try { shaderClass.ProcessSymbol(table, context); }
catch (NotImplementedException ex) when (ex.Message.Contains("generic shader types"))
{
    // replace the generic member with a concrete instantiated type
}

Prevention

When it happens

Trigger: Declaring a member variable whose TypeName contains '<' and does not resolve via TryResolveType, e.g. an uninstantiated generic shader class as a field.

Common situations: Using generic composition types like ShaderBase<T> as member fields instead of concrete instantiated types in a shader class.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/aa1da89268e94a3d. Report an issue: GitHub.

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/ShaderElements.MethodOrMember.cs:192

    public StreamKind StreamKind { get; set; } = streamKind;
    public bool IsCompose { get; set; }
    public bool IsArray => TypeName?.IsArray ?? false;
    public Expression? Value { get; set; } = initialValue;
    public TypeModifier TypeModifier { get; set; } = typeModifier;
    public StorageClass StorageClass { get; set; } = storageClass;
    public InterpolationModifier Interpolation { get; set; } = interpolation;

    public Symbol? Symbol { get; private set; }

    public override void ProcessSymbol(SymbolTable table, SpirvContext context)
    {
        base.ProcessSymbol(table, context);
        foreach (var generic in TypeName.Generics)
            generic.ProcessSymbol(table);
        if (!TypeName.TryResolveType(table, context, out var memberType))
        {
            if (TypeName.Name.Contains("<"))
                throw new NotImplementedException("Can't have member variables with generic shader types");
            var classSource = new ShaderClassInstantiation(TypeName.Name, []);
            var shader = SpirvBuilder.GetOrLoadShader(table.ShaderLoader, classSource, table.CurrentMacros.AsSpan(), ResolveStep.Compile, context);
            classSource.Buffer = shader;
            var shaderType = ShaderClass.LoadAndCacheExternalShaderDefinition(table, context, classSource);

            // Resolve again (we don't use shaderType directly, because it might lack info such as ArrayType)
            TypeName.ProcessSymbol(table);
            memberType = TypeName.Type!;
        }

        if (memberType is AppendStructuredBufferType or ConsumeStructuredBufferType)
        {
            var bufTypeName = memberType is AppendStructuredBufferType ? "AppendStructuredBuffer" : "ConsumeStructuredBuffer";
            table.AddError(new(TypeName.Info, $"{bufTypeName} is not supported. Use RWStructuredBuffer with a separate counter buffer instead (variable '{Name}')."));
            return;
        }

        var storageClass = (memberType, StorageClass, StreamKind) switch

View on GitHub (pinned to 96fad776d2)