stride3d/stride · error · NotImplementedException

Can't add type

Error message

Can't add type {type}

What it means

RegisterType in SpirvContext maps SDSL scalar/vector/matrix/array types to SPIR-V type instructions. When a type falls through every known case in the first switch (an unsupported scalar kind or unexpected type shape), it throws NotImplementedException('Can't add type {type}').

Solutions

  1. Check the type's Scalar kind is one supported by the SPIR-V emitter; use a supported scalar type
  2. If you added a new Scalar kind, extend RegisterType's switch to emit the corresponding SPIR-V type instruction
  3. For composite types, verify the correct RegisterType overload/branch is being taken (the throw here is the scalar-path fallthrough)

Example fix

// before (SpirvContext.Types.cs)
_ => throw new NotImplementedException($"Can't add type {type}")
// after
Scalar.Int8 => Buffer.AddData(new OpTypeInt(id, 8, 1)).IdResult,
_ => throw new NotImplementedException($"Can't add type {type}")
Defensive patterns

Strategy: validation

Validate before calling

static readonly HashSet<Scalar> SupportedScalars = new() { Scalar.Bool, Scalar.Int8, Scalar.Int16, Scalar.Int32, Scalar.Int64, Scalar.UInt8, Scalar.UInt16, Scalar.UInt32, Scalar.UInt64, Scalar.Half, Scalar.Float, Scalar.Double };
if (type is ScalarType s && !SupportedScalars.Contains(s.Kind)) throw new NotSupportedException($"Unsupported scalar {s.Kind}");

Type guard

bool IsSupportedType(SdslType t) => t is not ScalarType s || SupportedScalars.Contains(s.Kind);

Try / catch

try { ctx.RegisterType(type); } catch (NotImplementedException ex) { logger.LogError(ex, "Type not supported by SPIR-V backend: {Type}", type); throw; }

Prevention

When it happens

Trigger: Calling RegisterType (directly or via GetOrRegister/ReplaceType) with a type whose scalar kind is not one of the handled Scalar.* values (Bool, Int8..UInt64, Half, Float, Double, etc.) or a type shape not matched in this portion of the switch.

Common situations: Adding a new scalar width/kind to SDSL without updating the SPIR-V backend; passing a struct/image/composite type into the scalar-only switch path.

Related errors


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

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/SpirvContext.Types.cs:155

    }

    public int RegisterType(SymbolType type, int id)
    {
        var instruction = type switch
        {
            ScalarType s =>
                s.Type switch
                {
                    Scalar.Void => Buffer.AddData(new OpTypeVoid(id)).IdResult,
                    Scalar.Boolean => Buffer.AddData(new OpTypeBool(id)).IdResult,
                    Scalar.Int => Buffer.AddData(new OpTypeInt(id, 32, 1)).IdResult,
                    Scalar.UInt => Buffer.AddData(new OpTypeInt(id, 32, 0)).IdResult,
                    Scalar.Int64 => Buffer.AddData(new OpTypeInt(id, 64, 1)).IdResult,
                    Scalar.UInt64 => Buffer.AddData(new OpTypeInt(id, 64, 0)).IdResult,
                    Scalar.Half => Buffer.AddData(new OpTypeFloat(id, 16, null)).IdResult,
                    Scalar.Float => Buffer.AddData(new OpTypeFloat(id, 32, null)).IdResult,
                    Scalar.Double => Buffer.AddData(new OpTypeFloat(id, 64, null)).IdResult,
                    _ => throw new NotImplementedException($"Can't add type {type}")
                },
            VectorType v => Buffer.AddData(new OpTypeVector(id, GetOrRegister(v.BaseType), v.Size)).IdResult,
            MatrixType m => Buffer
                .AddData(new OpTypeMatrix(id, GetOrRegister(new VectorType(m.BaseType, m.Rows)), m.Columns))
                .IdResult,
            ArrayType a when a.Size != -1 || a.SizeExpression != null => RegisterArrayType(a),
            ArrayType a when a.Size == -1 && a.SizeExpression == null => Buffer
                .AddData(new OpTypeRuntimeArray(id, GetOrRegister(a.BaseType))).IdResult,
            StructType st => RegisterStructuredType(st.ToId(), st),
            FunctionType f => RegisterFunctionType(f, id),
            PointerType p => RegisterPointerType(p, id),
            // SampledType stores the full return type (e.g. float4, not just float) so that
            // Texture<float> and Texture<float4> produce structurally distinct OpTypeImage during merge.
            // ShaderMixer normalizes SampledType back to scalar before final SPIR-V emission.
            // Do not infer a storage image format from the element type. The view decides it:
            // RWTexture2D<float4> binds equally to R8G8B8A8_UNorm and R32G32B32A32_Float. An
            // Unknown format makes ShaderMixer request StorageImage{Read,Write}WithoutFormat.
            Texture1DType t => Buffer.AddData(new OpTypeImage(id, GetOrRegister(t.ReturnType), t.Dimension,

View on GitHub (pinned to 96fad776d2)