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

  1. 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).
  2. 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).
  3. 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).
  4. 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

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


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)