stride3d/stride · error · NotSupportedException
Unsupported binary op
Error message
Unsupported binary op: {op} What it means
Binary constant folding implements only OpIAdd, OpISub, OpIMul, OpFAdd, OpFSub, OpFMul, and OpFDiv (all as int or float); any other binary opcode reaching the default arm throws NotSupportedException 'Unsupported binary op'. Notably absent are OpUDiv, OpSRem/OpSMod, OpShift*/OpBitwise*, and OpFRem/OpFMod.
Solutions
- Pre-compute the binary result and emit the folded constant directly.
- Extend the switch with the needed arms (e.g. OpUDiv => (int)left / (int)right guarded for zero, OpBitwiseAnd => (int)left & (int)right, OpShiftLeftLogical => (int)left << (int)right).
- Emit the operation as a runtime instruction instead of relying on fold-time evaluation.
Example fix
// before Specification.Op.OpFDiv => (float)left / (float)right, _ => throw new NotSupportedException(...) // after Specification.Op.OpFDiv => (float)left / (float)right, Specification.Op.OpSRem => (int)left % (int)right, Specification.Op.OpBitwiseAnd => (int)left & (int)right, _ => throw new NotSupportedException(...)
Defensive patterns
Strategy: validation
Validate before calling
static readonly Specification.Op[] FoldableBinary = { Specification.Op.OpIAdd, Specification.Op.OpISub, Specification.Op.OpIMul, Specification.Op.OpFAdd, Specification.Op.OpFSub, Specification.Op.OpFMul, Specification.Op.OpFDiv };
if (!FoldableBinary.Contains(op)) EmitRuntimeInstruction(op); Type guard
bool IsFoldableBinary(Specification.Op op) => op is Specification.Op.OpIAdd or Specification.Op.OpISub or Specification.Op.OpIMul or Specification.Op.OpFAdd or Specification.Op.OpFSub or Specification.Op.OpFMul or Specification.Op.OpFDiv;
Try / catch
try { folded = context.GetConstantValue(binaryId); } catch (NotSupportedException ex) when (ex.Message.StartsWith("Unsupported binary op")) { EmitRuntimeInstruction(op); } Prevention
- Fold only add/sub/mul/div in constant expressions; emit %, shifts, bitwise ops at runtime
- Guard OpFDiv/OpUDiv constants against zero divisors
- Keep a registry of foldable opcodes synchronized with TryGetConstantValue's switch
When it happens
Trigger: Constant-folding OpUDiv, OpSRem, OpSMod, OpShiftLeftLogical, OpBitwiseAnd/Or/Xor, OpFRem/OpFMod, or any other binary opcode with constant operands.
Common situations: Constant expressions with integer division/remainder (e.g. array index math like i % 4), bitmask composition of constant flags, or shift-based packing in 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
- OpBitcast target scalar
- Unsupported conversion op
- Unsupported unary op
- OpBitcast operand type
- OpBitcast result type
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/d14d3254fc97e100.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Context.Constants.cs:168
case Specification.Op.OpFMul:
case Specification.Op.OpFDiv:
if (!TryGetConstantValue(i.Data.Memory.Span[4], out var left, out var leftTypeId))
return false;
if (!TryGetConstantValue(i.Data.Memory.Span[5], out var right, out var rightTypeId))
return false;
if (leftTypeId != resultType || rightTypeId != resultType)
return false;
value = op switch
{
// Note: first cast to object is important, otherwise int/float will be cast as float
Specification.Op.OpIAdd => (object)((int)left + (int)right),
Specification.Op.OpISub => (int)left - (int)right,
Specification.Op.OpIMul => (int)left * (int)right,
Specification.Op.OpFAdd => (float)left + (float)right,
Specification.Op.OpFSub => (float)left - (float)right,
Specification.Op.OpFMul => (float)left * (float)right,
Specification.Op.OpFDiv => (float)left / (float)right,
_ => throw new NotSupportedException($"Unsupported binary op: {op}"),
};
break;
default:
throw new NotImplementedException();
}
return true;
}
if ((i.Op == Specification.Op.OpConstantComposite || i.Op == Specification.Op.OpSpecConstantComposite) &&
(OpConstantComposite)i is { } constantComposite)
{
var values = constantComposite.Constituents;
var constants = new object[values.WordCount];
for (int j = 0; j < values.WordCount; ++j)
{
if (!TryGetConstantValue(values.Elements.Span[j], out constants[j]!, out _))
return false;View on GitHub (pinned to 96fad776d2)