stride3d/stride · error · NotSupportedException

OpBitcast operand type

Error message

OpBitcast operand type {bitcastOperand.GetType()} is not supported

What it means

For OpBitcast folding, the constant operand must be an int, uint, or float (32-bit values whose bits are reinterpreted); any other operand type — e.g. a double, long, bool, or composite constant — throws NotSupportedException naming the operand's runtime type. Only single-precision bit reinterpretation is implemented.

Solutions

  1. Use 32-bit operand constants (int/uint/float) for bitcasts.
  2. Handle 64-bit bitcasts manually by splitting into two 32-bit constants before the bitcast.
  3. Extend the operand switch with double/long arms (using BitConverter.DoubleToInt64Bits / partitioning into words).

Example fix

// before
var bits = context.Bitcast(doubleConst, uintType); // NotSupported: operand is double
// after
var lo = context.Bitcast((uint)(long)doubleBitsLo, uintType); // split 64-bit into 32-bit words
Defensive patterns

Strategy: type-guard

Validate before calling

if (bitcastOperand is not (int or uint or float)) EmitRuntimeInstruction(Specification.Op.OpBitcast); // only 32-bit operands fold

Type guard

bool Is32BitOperand(object o) => o is int or uint or float;

Try / catch

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

Prevention

When it happens

Trigger: Constant-folding an OpBitcast whose operand constant is a double/long/Half value (i.e. not a 32-bit scalar), so the operand switch's `_ =>` arm throws.

Common situations: Bitcasting double-precision constants or 64-bit integers (common in hashing/random-number shader code) on constant expressions; Half constants flowing into bitcast expressions.

Related errors


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

Appendix: source

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

                        Specification.Op.OpConvertFToS => (object)(int)(float)convertOperand,
                        Specification.Op.OpConvertFToU => (uint)(float)convertOperand,
                        Specification.Op.OpConvertSToF => (float)(int)convertOperand,
                        Specification.Op.OpConvertUToF => (float)(uint)convertOperand,
                        _ => throw new NotSupportedException($"Unsupported conversion op: {op}"),
                    };
                    break;
                // 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
                    {

View on GitHub (pinned to 96fad776d2)