stride3d/stride · error · NotSupportedException
Unsupported conversion op
Error message
Unsupported conversion op: {op} What it means
Within TryGetConstantValue's OpConvert* handling, only four numeric conversion opcodes (FToS, FToU, SToF, UToF) are implemented; any other conversion opcode reaching this switch throws NotSupportedException 'Unsupported conversion op'. Constant folding is limited to these scalar numeric conversions, so ops like OpConvertSToU, OpConvertUToS, or OpFConvert are unimplemented at fold time.
Solutions
- Avoid the conversion on constant operands in your shader-generation pipeline (emit the already-converted literal).
- Extend the switch with the missing OpConvert* arms (e.g. OpConvertSToU => (object)(uint)(int)convertOperand).
- Work around by emitting the instruction unconverted so it is folded downstream, or bypass constant folding for this expression.
Example fix
// before Specification.Op.OpConvertUToF => (float)(uint)convertOperand, _ => throw new NotSupportedException(...) // after Specification.Op.OpConvertUToF => (float)(uint)convertOperand, Specification.Op.OpConvertSToU => (object)(uint)(int)convertOperand, Specification.Op.OpConvertUToS => (object)(int)(uint)convertOperand, Specification.Op.OpFConvert => (object)(double)(float)convertOperand, _ => throw new NotSupportedException(...)
Defensive patterns
Strategy: validation
Validate before calling
static readonly Specification.Op[] FoldableConversions = { Specification.Op.OpConvertFToS, Specification.Op.OpConvertFToU, Specification.Op.OpConvertSToF, Specification.Op.OpConvertUToF };
if (!FoldableConversions.Contains(op)) EmitRuntimeInstruction(op); // skip folding Type guard
bool IsFoldableConversion(Specification.Op op) => op is Specification.Op.OpConvertFToS or Specification.Op.OpConvertFToU or Specification.Op.OpConvertSToF or Specification.Op.OpConvertUToF;
Try / catch
try { folded = context.GetConstantValue(convertId); } catch (NotSupportedException ex) when (ex.Message.StartsWith("Unsupported conversion op")) { EmitRuntimeInstruction(op); } Prevention
- Avoid integer-to-integer and width conversions inside constant expressions
- Apply conversions to literals before codegen instead of after
- Add tests for every conversion opcode your front end can emit
When it happens
Trigger: Constant-folding a SPIR-V OpConvertSToU, OpConvertUToS, or OpFConvert (or any future conversion opcode) whose operand is itself a constant, hitting the default arm of the conversion switch.
Common situations: Shaders produced by compilers that emit integer-to-integer conversions (S<->U) or float width conversions (FConvert, float<->double) on constant expressions; widening/narrowing literals in generated shader code.
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
- OpBitcast target scalar
- Unsupported binary op
- Unsupported unary op
- Can't convert operand to type
- OpBitcast operand type
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/c3485aae8d711b31.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Context.Constants.cs:105
var resultId = i.Data.Memory.Span[2];
var op = (Specification.Op)i.Data.Memory.Span[3];
switch (op)
{
// Conversions
case Specification.Op.OpConvertFToS:
case Specification.Op.OpConvertFToU:
case Specification.Op.OpConvertSToF:
case Specification.Op.OpConvertUToF:
if (!TryGetConstantValue(i.Data.Memory.Span[4], out var convertOperand, out var convertOperandTypeId))
return false;
value = op switch
{
// Note: first cast to object is important, otherwise int/float will be cast as float
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,View on GitHub (pinned to 96fad776d2)