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
- Check the type's Scalar kind is one supported by the SPIR-V emitter; use a supported scalar type
- If you added a new Scalar kind, extend RegisterType's switch to emit the corresponding SPIR-V type instruction
- 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
- Keep the SDSL Scalar enum and SPIR-V type emitter in sync when adding kinds
- Write a unit test registering every scalar kind
- Route composite types through the correct registration path
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
- 64bit integers
- Binary operator not supported for element type
- Can't write value on input or output struct
- Could not load shader
- Couldn't figure out element type for binary operation…
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)