stride3d/stride · error · NotSupportedException

OpBitcast target scalar

Error message

OpBitcast target scalar {bitcastTargetScalar} is not supported

What it means

After computing bitcastBits (a 32-bit uint), TryGetConstantValue reinterprets them as the target scalar: only Scalar.Int, Scalar.UInt, and Scalar.Float are supported; any other target scalar (e.g. Bool, Half, Int64, or Double) throws NotSupportedException 'OpBitcast target scalar ... is not supported'.

Solutions

  1. Restrict constant bitcasts to int/uint/float targets.
  2. Perform half/double bitcasts at runtime (emit the OpBitcast instruction rather than folding).
  3. Extend the target switch (e.g. Scalar.Half via bit manipulation, Scalar.Double via two 32-bit words).

Example fix

// before
value = bitcastTargetScalar switch { Scalar.Int => ..., Scalar.UInt => ..., Scalar.Float => ..., _ => throw ... };
// after
value = bitcastTargetScalar switch { Scalar.Int => ..., Scalar.UInt => ..., Scalar.Float => ..., Scalar.Double => BitConverter.UInt32BitsToDoublePair(bitcastBits), _ => throw ... };
Defensive patterns

Strategy: validation

Validate before calling

if (context.ReverseTypes[resultType] is ScalarType { Type: not (Scalar.Int or Scalar.UInt or Scalar.Float) }) EmitRuntimeInstruction(Specification.Op.OpBitcast);

Type guard

bool IsFoldableBitcastTarget(ScalarType t) => t.Type is Scalar.Int or Scalar.UInt or Scalar.Float;

Try / catch

try { folded = context.GetConstantValue(bitcastId); } catch (NotSupportedException ex) when (ex.Message.Contains("target scalar")) { EmitRuntimeInstruction(Specification.Op.OpBitcast); }

Prevention

When it happens

Trigger: Constant-folding an OpBitcast whose result type is a bool, half, 64-bit int/uint, or double scalar — the target-scalar switch's `_ =>` arm throws.

Common situations: Bitcasting to half/double for precision tricks, or to bool from comparison-style codegen, in constant subexpressions of generated shaders.

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/c3720cdb805813fd. Report an issue: GitHub.

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Context.Constants.cs:126

                // Bitcast: reinterpret the operand's bits as the target scalar type.
                case Specification.Op.OpBitcast:
                    if (!TryGetConstantValue(i.Data.Memory.Span[4], out var bitcastOperand, out _))
                        return false;
                    if (ReverseTypes[resultType] is not ScalarType { Type: var bitcastTargetScalar })
                        throw new NotSupportedException($"OpBitcast result type {ReverseTypes[resultType]} is not a scalar");
                    var bitcastBits = bitcastOperand switch
                    {
                        int v => (uint)v,
                        uint v => v,
                        float v => BitConverter.SingleToUInt32Bits(v),
                        _ => throw new NotSupportedException($"OpBitcast operand type {bitcastOperand.GetType()} is not supported"),
                    };
                    value = bitcastTargetScalar switch
                    {
                        Scalar.Int => (object)(int)bitcastBits,
                        Scalar.UInt => bitcastBits,
                        Scalar.Float => BitConverter.UInt32BitsToSingle(bitcastBits),
                        _ => throw new NotSupportedException($"OpBitcast target scalar {bitcastTargetScalar} is not supported"),
                    };
                    break;
                // Unary operations
                case Specification.Op.OpSNegate:
                case Specification.Op.OpFNegate:
                    if (!TryGetConstantValue(i.Data.Memory.Span[4], out var unaryOperand, out var unaryOperandTypeId))
                        return false;
                    if (unaryOperandTypeId != resultType)
                        return false;
                    value = op switch
                    {
                        // Note: first cast to object is important, otherwise int/float will be cast as float
                        Specification.Op.OpSNegate => (object)(-(int)unaryOperand),
                        Specification.Op.OpFNegate => -(float)unaryOperand,
                        _ => throw new NotSupportedException($"Unsupported unary op: {op}"),
                    };
                    break;
                // Binary operations

View on GitHub (pinned to 96fad776d2)