{"record":{"id":"45f29e0996ff4e8e","repo":"stride3d/stride","slug":"unsupported-unary-op-op","errorCode":null,"errorMessage":"Unsupported unary op: {op}","messagePattern":"Unsupported unary op: (.+?)","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Context.Constants.cs","lineNumber":141,"sourceCode":"                        Scalar.Int => (object)(int)bitcastBits,\n                        Scalar.UInt => bitcastBits,\n                        Scalar.Float => BitConverter.UInt32BitsToSingle(bitcastBits),\n                        _ => throw new NotSupportedException($\"OpBitcast target scalar {bitcastTargetScalar} is not supported\"),\n                    };\n                    break;\n                // Unary operations\n                case Specification.Op.OpSNegate:\n                case Specification.Op.OpFNegate:\n                    if (!TryGetConstantValue(i.Data.Memory.Span[4], out var unaryOperand, out var unaryOperandTypeId))\n                        return false;\n                    if (unaryOperandTypeId != resultType)\n                        return false;\n                    value = op switch\n                    {\n                        // Note: first cast to object is important, otherwise int/float will be cast as float\n                        Specification.Op.OpSNegate => (object)(-(int)unaryOperand),\n                        Specification.Op.OpFNegate => -(float)unaryOperand,\n                        _ => throw new NotSupportedException($\"Unsupported unary op: {op}\"),\n                    };\n                    break;\n                // Binary operations\n                case Specification.Op.OpIAdd:\n                case Specification.Op.OpISub:\n                case Specification.Op.OpIMul:\n                case Specification.Op.OpFAdd:\n                case Specification.Op.OpFSub:\n                case Specification.Op.OpFMul:\n                case Specification.Op.OpFDiv:\n                    if (!TryGetConstantValue(i.Data.Memory.Span[4], out var left, out var leftTypeId))\n                        return false;\n                    if (!TryGetConstantValue(i.Data.Memory.Span[5], out var right, out var rightTypeId))\n                        return false;\n                    if (leftTypeId != resultType || rightTypeId != resultType)\n                        return false;\n                    value = op switch\n                    {","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Context.Constants.cs#L123-L159","documentation":"Unary constant folding implements only OpSNegate (integer negate) and OpFNegate (float negate); any other opcode routed into the unary case throws NotSupportedException 'Unsupported unary op'. Missing ops include OpNot (bitwise not) and OpLogicalNot.","triggerScenarios":"Constant-folding OpNot (~x), OpLogicalNot (!x), or any other unary opcode whose operand is a constant, reaching the default arm of the unary switch.","commonSituations":"Bitmask inversion or boolean negation of constant expressions in generated shader code (e.g. ~0u flags, !true).","solutions":["Pre-compute the negation and emit the already-negated constant.","Extend the unary switch: OpNot => (object)~(int)unaryOperand, OpLogicalNot => (object)!(((int)unaryOperand) != 0).","Emit the unary op as a runtime instruction instead of folding."],"exampleFix":"// before\nSpecification.Op.OpFNegate => -(float)unaryOperand,\n_ => throw new NotSupportedException(...)\n// after\nSpecification.Op.OpFNegate => -(float)unaryOperand,\nSpecification.Op.OpNot => (object)~(int)unaryOperand,\nSpecification.Op.OpLogicalNot => (object)!(((int)unaryOperand) != 0),\n_ => throw new NotSupportedException(...)","handlingStrategy":"validation","validationCode":"if (op is not (Specification.Op.OpSNegate or Specification.Op.OpFNegate)) EmitRuntimeInstruction(op); // only negate ops fold","typeGuard":"bool IsFoldableUnary(Specification.Op op) => op is Specification.Op.OpSNegate or Specification.Op.OpFNegate;","tryCatchPattern":"try { folded = context.GetConstantValue(unaryId); } catch (NotSupportedException ex) when (ex.Message.StartsWith(\"Unsupported unary op\")) { EmitRuntimeInstruction(op); }","preventionTips":["Pre-compute bitwise/boolean negation of constants before codegen","Restrict constant unary expressions to numeric negation","Add folding support (or tests) for OpNot/OpLogicalNot if your front end emits them"],"tags":["spirv","constant-folding","unary","not-supported"],"backgroundTag":"unsupported-operation","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}