{"record":{"id":"d80821d7c702710f","repo":"stride3d/stride","slug":"unsupported-opspecconstantop-inner-op-op","errorCode":null,"errorMessage":"Unsupported OpSpecConstantOp inner op: {op}","messagePattern":"Unsupported OpSpecConstantOp inner op: (.+?)","errorType":"exception","errorClass":"NotImplementedException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Core/ConstantExpression.cs","lineNumber":219,"sourceCode":"                    case Op.OpSGreaterThan:\n                    case Op.OpULessThanEqual:\n                    case Op.OpSLessThanEqual:\n                    case Op.OpUGreaterThanEqual:\n                    case Op.OpSGreaterThanEqual:\n                    {\n                        var left = ParseFromBuffer(inst.Data.Memory.Span[4], buffer, context);\n                        var right = ParseFromBuffer(inst.Data.Memory.Span[5], buffer, context);\n                        return new BinaryOpExpr(op, left, right);\n                    }\n                    case Op.OpSelect:\n                    {\n                        var cond = ParseFromBuffer(inst.Data.Memory.Span[4], buffer, context);\n                        var trueVal = ParseFromBuffer(inst.Data.Memory.Span[5], buffer, context);\n                        var falseVal = ParseFromBuffer(inst.Data.Memory.Span[6], buffer, context);\n                        return new SelectExpr(cond, trueVal, falseVal);\n                    }\n                    default:\n                        throw new NotImplementedException($\"Unsupported OpSpecConstantOp inner op: {op}\");\n                }\n            }\n\n            default:\n                throw new NotImplementedException($\"Cannot parse constant expression from {inst.Op}\");\n        }\n    }\n}\n\n/// <summary>\n/// Integer constant. Covers int, uint, long, ulong — signedness determined at emission by SPIR-V type context.\n/// </summary>\npublic sealed record IntConstExpr(long Value) : ConstantExpression\n{\n    public override int Emit(SpirvContext context)\n    {\n        // For values that fit in int, use int (signed) — matches the common case for array sizes\n        if (Value is >= int.MinValue and <= int.MaxValue)","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Core/ConstantExpression.cs#L201-L237","documentation":"ConstantExpression.ParseInstruction throws this NotImplementedException when an OpSpecConstantOp instruction uses an inner opcode that the expression builder does not implement. Only a fixed set of unary, binary, and OpSelect operations is supported; anything else (e.g. OpUMulExtended, OpSMulExtended, matrix ops) cannot be represented as a ConstantExpression tree.","triggerScenarios":"Parsing a SPIR-V buffer containing a specialization-constant operation outside the supported list in ConstantExpression.cs:161-219 — e.g. a spec constant computed with OpBitFieldInsert, OpUMulExtended, or any op not enumerated in the switch.","commonSituations":"Shaders compiled with aggressive constant propagation producing exotic spec-constant ops; SPIR-V from other compilers (glslang, DXC) using spec-constant operations Stride's SDSL pipeline never emits; using OpSpecConstantOp to encode casts like OpUConvert/OpSConvert that are missing from the conversion list.","solutions":["Identify the inner op from the message and restructure the shader so the spec constant uses only supported arithmetic/bitwise/logical/select ops.","Pre-fold the expression to a plain OpConstant before handing the buffer to ParseFromBuffer (e.g. constant-fold in the producing tool).","If the op is legitimately needed, add a case to the switch in ConstantExpression.cs and a matching representation in the ConstantExpression hierarchy.","Check whether a simple conversion (OpSConvert/OpUConvert) was intended and use one of the supported convert ops instead."],"exampleFix":"// before: spec constant uses an unimplemented inner op (e.g. OpBitFieldUExtract)\n// after: fold it or use a supported op\nvar supported = ConstantExpression.ParseFromBuffer(idOfIAddSpecConstant, buffer, context); // OpIAdd is in the supported set","handlingStrategy":"try-catch","validationCode":"static readonly HashSet<Op> SupportedSpecOps = new()\n{\n    Op.OpConvertFToS, Op.OpConvertFToU, Op.OpConvertSToF, Op.OpConvertUToF,\n    Op.OpSNegate, Op.OpFNegate, Op.OpNot, Op.OpLogicalNot,\n    Op.OpIAdd, Op.OpISub, Op.OpIMul, Op.OpUDiv, Op.OpSDiv,\n    Op.OpFAdd, Op.OpFSub, Op.OpFMul, Op.OpFDiv,\n    Op.OpShiftRightLogical, Op.OpShiftRightArithmetic, Op.OpShiftLeftLogical,\n    Op.OpBitwiseOr, Op.OpBitwiseXor, Op.OpBitwiseAnd,\n    Op.OpLogicalOr, Op.OpLogicalAnd, Op.OpLogicalEqual, Op.OpLogicalNotEqual,\n    Op.OpIEqual, Op.OpINotEqual,\n    Op.OpULessThan, Op.OpSLessThan, Op.OpUGreaterThan, Op.OpSGreaterThan,\n    Op.OpULessThanEqual, Op.OpSLessThanEqual, Op.OpUGreaterThanEqual, Op.OpSGreaterThanEqual,\n    Op.OpSelect\n};\nstatic bool IsSupportedSpecConstantOp(SpirvBuffer buffer, int id)\n    => buffer.TryGetInstructionById(id, out var inst)\n       && inst.Op == Op.OpSpecConstantOp\n       && SupportedSpecOps.Contains((Op)inst.Data.Memory.Span[3]);","typeGuard":null,"tryCatchPattern":"try\n{\n    var expr = ConstantExpression.ParseFromBuffer(id, buffer, context);\n}\ncatch (NotImplementedException ex) when (ex.Message.StartsWith(\"Unsupported OpSpecConstantOp\"))\n{\n    // fall back to leaving the raw spec constant unresolved, or pre-fold upstream\n}","preventionTips":["Restrict shader spec constants to simple arithmetic/bitwise/logical expressions and ternaries.","Constant-fold complex expressions in the producing tool before emitting SPIR-V.","Pre-scan OpSpecConstantOp inner opcodes with the supported set before parsing."],"tags":["spirv","shader-parsing","unsupported-operation","not-implemented"],"backgroundTag":"unsupported-operation","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}