stride3d/stride · error · NotSupportedException

Unsupported type cast

Error message

Unsupported type cast: {from} to {to}

What it means

Thrown by Builder.Expressions.Convert in the per-element cast switch when the element type pair is a scalar combination not covered by the implemented SPIR-V conversions (OpConvertFToS/U, OpConvertSToF/U, OpUToF, OpSConvert, OpUConvert, OpBitcast int<->uint). Any other scalar pair, e.g. bool to float, double to bool, raises NotSupportedException.

Solutions

  1. Rewrite the cast in the shader using explicit constructs (e.g. `cond ? 1.0 : 0.0` instead of `(float)cond`).
  2. Extend the switch in Builder.Expressions.cs to emit the missing SPIR-V conversion (e.g. OpSelect for bool->numeric).
  3. Cast through an intermediate type that is supported (e.g. bool->int->float via logic ops).

Example fix

// before (shader)
float f = (float)b;
// after
float f = b ? 1.0 : 0.0;
Defensive patterns

Strategy: type-guard

Validate before calling

static readonly HashSet<(Scalar,Scalar)> supported = new() { (Float,Int),(Float,UInt),(Int,Float),(UInt,Float),(Int,Int),(UInt,UInt),(Int,UInt),(UInt,Int) };
bool scalarCastSupported(Scalar from, Scalar to) => supported.Contains((from,to)) || from == to;

Type guard

bool IsUnsupportedScalarCast(ScalarType a, ScalarType b) => a != b && !(a.Type is Scalar.Float or Scalar.Int or Scalar.UInt) || /* check pipeline table */ false;

Try / catch

try { result = builder.Convert(table, context, value, castType); }
catch (NotSupportedException e) when (e.Message.StartsWith("Unsupported type cast"))
{ /* rewrite the expression in shader source, e.g. bool ? 1 : 0 */ }

Prevention

When it happens

Trigger: Calling Convert where both element types are scalars but the (from, to) Scalar.Type pair has no emitted instruction — e.g. Float->Bool, Bool->Float, Double->Int on paths where only bitcast int<->uint was special-cased.

Common situations: Shader code casting to/from bool (common in conditionals like `(float)someBool`) or using half/double casts; the converter's supported numeric conversion matrix is narrower than HLSL.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Expressions.cs:653

                    // Half conversions (OpFConvert for float<->half<->double, standard int conversions)
                    (ScalarType { Type: Scalar.Half }, ScalarType { Type: Scalar.Float }) => InsertData(new OpFConvert(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
                    (ScalarType { Type: Scalar.Half }, ScalarType { Type: Scalar.Double }) => InsertData(new OpFConvert(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
                    (ScalarType { Type: Scalar.Float }, ScalarType { Type: Scalar.Half }) => InsertData(new OpFConvert(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
                    (ScalarType { Type: Scalar.Double }, ScalarType { Type: Scalar.Half }) => InsertData(new OpFConvert(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
                    (ScalarType { Type: Scalar.Half }, ScalarType { Type: Scalar.Int }) => InsertData(new OpConvertFToS(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
                    (ScalarType { Type: Scalar.Half }, ScalarType { Type: Scalar.UInt }) => InsertData(new OpConvertFToU(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
                    (ScalarType { Type: Scalar.Int }, ScalarType { Type: Scalar.Half }) => InsertData(new OpConvertSToF(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
                    (ScalarType { Type: Scalar.UInt }, ScalarType { Type: Scalar.Half }) => InsertData(new OpConvertUToF(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
                    (ScalarType { Type: Scalar.Half }, ScalarType { Type: Scalar.Boolean }) => InsertData(new OpFOrdNotEqual(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue, context.CreateConstantCompositeVectorRepeat(new FloatLiteral(new(16, true, true), 0.0, new()), elementSize).Id)),
                    (ScalarType { Type: Scalar.Boolean }, ScalarType { Type: Scalar.Half }) => InsertData(new OpSelect(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue,
                                                                                                    context.CreateConstantCompositeVectorRepeat(new FloatLiteral(new(16, true, true), 1.0, new()), elementSize).Id,
                                                                                                    context.CreateConstantCompositeVectorRepeat(new FloatLiteral(new(16, true, true), 0.0, new()), elementSize).Id)),

                    // Bitcast (int=>uint or uint=>int)
                    (ScalarType { Type: Scalar.Int }, ScalarType { Type: Scalar.UInt }) => InsertData(new OpBitcast(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
                    (ScalarType { Type: Scalar.UInt }, ScalarType { Type: Scalar.Int }) => InsertData(new OpBitcast(context.GetOrRegister(castTypeSameSize), context.Bound++, rowValue)),
                    (ScalarType { Type: var from }, ScalarType { Type: var to }) => throw new NotSupportedException($"Unsupported type cast: {from} to {to}"),
                };
                values[i] = typeCasting.IdResult!.Value;

                // Update type
                if (i == valueCount - 1)
                    valueType = castTypeSameSize;
            }
        }

        // Expanding
        int result = values[0];
        switch (valueType, castType)
        {
            case (ScalarType, VectorType v2):
                result = Insert(new OpCompositeConstruct(context.GetOrRegister(v2), context.Bound++, new LiteralArray<int>(Enumerable.Repeat(result, v2.Size).ToArray())));
                valueType = v2;
                break;
            case (ScalarType, MatrixType m2):

View on GitHub (pinned to 96fad776d2)