stride3d/stride · error · NotImplementedException
Cannot parse constant expression from {inst.Op}
Error message
Cannot parse constant expression from {inst.Op} What it means
ConstantExpression.ParseInstruction (reached from ParseFromBuffer) converts SPIR-V instructions into constant expression AST nodes. It only supports a fixed set of opcodes (OpConstant, OpConstantComposite, OpSpecConstantOp with a known inner op, etc.). When the SPIR-V module contains a constant-producing opcode outside that set, the default arm throws this NotImplementedException instead of silently producing a wrong AST.
Solutions
- Read the opcode from the exception message and add a case for it in ParseInstruction's switch in ConstantExpression.cs (handle it like the existing unary/binary/select arms).
- If the opcode is legitimate but unsupported, implement parsing for that inner op of OpSpecConstantOp in the OpSpecConstantOp switch (the sibling 'Unsupported OpSpecConstantOp inner op' error points there).
- As a workaround, rewrite the shader/module to avoid the unsupported constant construct (e.g. bake spec-constant values or use a supported constant form).
- Upgrade to a Stride.Shaders.Parsers version whose parser covers the opcode, if available.
Example fix
// before
default:
throw new NotImplementedException($"Cannot parse constant expression from {inst.Op}");
// after
case Op.OpConstantNull:
return new NullConstExpr(typeId);
default:
throw new NotImplementedException($"Cannot parse constant expression from {inst.Op}"); Defensive patterns
Strategy: try-catch
Validate before calling
// Only pass modules whose constant instructions use supported opcodes
var op = (Op)inst.Op;
var supported = new HashSet<Op> { Op.OpConstant, Op.OpConstantComposite, Op.OpSpecConstantOp /*, others handled in the switch */ };
if (!supported.Contains(op))
throw new NotSupportedException($"Module uses unparsed constant opcode {op}"); Type guard
bool IsSupportedConstantOp(Op op) => op is Op.OpConstant or Op.OpConstantComposite or
(Op.OpSpecConstantOp and var s && IsSupportedSpecOp(s)); Try / catch
try
{
var expr = ConstantExpression.ParseFromBuffer(resultId, buffer, context);
}
catch (NotImplementedException ex) when (ex.Message.StartsWith("Cannot parse constant expression from"))
{
logger.LogWarning(ex, "Unsupported SPIR-V constant opcode; skipping/failing this module");
// fail the shader load or fall back to a non-constant path
} Prevention
- Before parsing, scan the SPIR-V for OpSpecConstantOp inner opcodes and constant opcodes outside the supported set.
- Pin the shader compiler (DXC/glslang) version so emitted constant opcodes match what the parser handles.
- Write a unit test parsing each constant opcode family your shaders actually use.
- Keep the opcode message in the exception — it names exactly which case to add.
When it happens
Trigger: Calling ParseFromBuffer/ParseFromBuffer on a SPIR-V instruction whose Op opcode is not one of the handled constant opcodes — e.g. OpSpecConstant (if unhandled), OpConstantNull, OpConstantTrue/False, or any new/extension constant opcode. The message echoes inst.Op so you can see which opcode is missing.
Common situations: Compiling shaders that use specialization constants or constant constructs the parser never saw before; feeding SPIR-V produced by a newer compiler (e.g. newer DXC/glslang) that emits constant opcodes this parser version doesn't handle; loading SPIR-V from extensions (float controls, ray tracing specialization) outside the supported set.
Related errors
- Unsupported float width {type.Width}
- Unsupported constant type {typeInst.Op}
- Unsupported OpSpecConstantOp inner op: {op}
- Cannot find type instruction for id {typeId}
- Cannot find type for composite constant type id {typeId}
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/a972b8eee47708fe.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Core/ConstantExpression.cs:224
{
var left = ParseFromBuffer(inst.Data.Memory.Span[4], buffer, context);
var right = ParseFromBuffer(inst.Data.Memory.Span[5], buffer, context);
return new BinaryOpExpr(op, left, right);
}
case Op.OpSelect:
{
var cond = ParseFromBuffer(inst.Data.Memory.Span[4], buffer, context);
var trueVal = ParseFromBuffer(inst.Data.Memory.Span[5], buffer, context);
var falseVal = ParseFromBuffer(inst.Data.Memory.Span[6], buffer, context);
return new SelectExpr(cond, trueVal, falseVal);
}
default:
throw new NotImplementedException($"Unsupported OpSpecConstantOp inner op: {op}");
}
}
default:
throw new NotImplementedException($"Cannot parse constant expression from {inst.Op}");
}
}
}
/// <summary>
/// Integer constant. Covers int, uint, long, ulong — signedness determined at emission by SPIR-V type context.
/// </summary>
public sealed record IntConstExpr(long Value) : ConstantExpression
{
public override int Emit(SpirvContext context)
{
// For values that fit in int, use int (signed) — matches the common case for array sizes
if (Value is >= int.MinValue and <= int.MaxValue)
return context.CompileConstant((int)Value).Id;
return context.CompileConstant(Value).Id;
}
public override bool TryEvaluate(out object? value)View on GitHub (pinned to 96fad776d2)