{"record":{"id":"c3485aae8d711b31","repo":"stride3d/stride","slug":"unsupported-conversion-op-op","errorCode":null,"errorMessage":"Unsupported conversion op: {op}","messagePattern":"Unsupported conversion op: (.+?)","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Context.Constants.cs","lineNumber":105,"sourceCode":"            var resultId = i.Data.Memory.Span[2];\n            var op = (Specification.Op)i.Data.Memory.Span[3];\n            switch (op)\n            {\n                // Conversions\n                case Specification.Op.OpConvertFToS:\n                case Specification.Op.OpConvertFToU:\n                case Specification.Op.OpConvertSToF:\n                case Specification.Op.OpConvertUToF:\n                    if (!TryGetConstantValue(i.Data.Memory.Span[4], out var convertOperand, out var convertOperandTypeId))\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.OpConvertFToS => (object)(int)(float)convertOperand,\n                        Specification.Op.OpConvertFToU => (uint)(float)convertOperand,\n                        Specification.Op.OpConvertSToF => (float)(int)convertOperand,\n                        Specification.Op.OpConvertUToF => (float)(uint)convertOperand,\n                        _ => throw new NotSupportedException($\"Unsupported conversion op: {op}\"),\n                    };\n                    break;\n                // Bitcast: reinterpret the operand's bits as the target scalar type.\n                case Specification.Op.OpBitcast:\n                    if (!TryGetConstantValue(i.Data.Memory.Span[4], out var bitcastOperand, out _))\n                        return false;\n                    if (ReverseTypes[resultType] is not ScalarType { Type: var bitcastTargetScalar })\n                        throw new NotSupportedException($\"OpBitcast result type {ReverseTypes[resultType]} is not a scalar\");\n                    var bitcastBits = bitcastOperand switch\n                    {\n                        int v => (uint)v,\n                        uint v => v,\n                        float v => BitConverter.SingleToUInt32Bits(v),\n                        _ => throw new NotSupportedException($\"OpBitcast operand type {bitcastOperand.GetType()} is not supported\"),\n                    };\n                    value = bitcastTargetScalar switch\n                    {\n                        Scalar.Int => (object)(int)bitcastBits,","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Context.Constants.cs#L87-L123","documentation":"Within TryGetConstantValue's OpConvert* handling, only four numeric conversion opcodes (FToS, FToU, SToF, UToF) are implemented; any other conversion opcode reaching this switch throws NotSupportedException 'Unsupported conversion op'. Constant folding is limited to these scalar numeric conversions, so ops like OpConvertSToU, OpConvertUToS, or OpFConvert are unimplemented at fold time.","triggerScenarios":"Constant-folding a SPIR-V OpConvertSToU, OpConvertUToS, or OpFConvert (or any future conversion opcode) whose operand is itself a constant, hitting the default arm of the conversion switch.","commonSituations":"Shaders produced by compilers that emit integer-to-integer conversions (S<->U) or float width conversions (FConvert, float<->double) on constant expressions; widening/narrowing literals in generated shader code.","solutions":["Avoid the conversion on constant operands in your shader-generation pipeline (emit the already-converted literal).","Extend the switch with the missing OpConvert* arms (e.g. OpConvertSToU => (object)(uint)(int)convertOperand).","Work around by emitting the instruction unconverted so it is folded downstream, or bypass constant folding for this expression."],"exampleFix":"// before\nSpecification.Op.OpConvertUToF => (float)(uint)convertOperand,\n_ => throw new NotSupportedException(...)\n// after\nSpecification.Op.OpConvertUToF => (float)(uint)convertOperand,\nSpecification.Op.OpConvertSToU => (object)(uint)(int)convertOperand,\nSpecification.Op.OpConvertUToS => (object)(int)(uint)convertOperand,\nSpecification.Op.OpFConvert => (object)(double)(float)convertOperand,\n_ => throw new NotSupportedException(...)","handlingStrategy":"validation","validationCode":"static readonly Specification.Op[] FoldableConversions = { Specification.Op.OpConvertFToS, Specification.Op.OpConvertFToU, Specification.Op.OpConvertSToF, Specification.Op.OpConvertUToF };\nif (!FoldableConversions.Contains(op)) EmitRuntimeInstruction(op); // skip folding","typeGuard":"bool IsFoldableConversion(Specification.Op op) => op is Specification.Op.OpConvertFToS or Specification.Op.OpConvertFToU or Specification.Op.OpConvertSToF or Specification.Op.OpConvertUToF;","tryCatchPattern":"try { folded = context.GetConstantValue(convertId); } catch (NotSupportedException ex) when (ex.Message.StartsWith(\"Unsupported conversion op\")) { EmitRuntimeInstruction(op); }","preventionTips":["Avoid integer-to-integer and width conversions inside constant expressions","Apply conversions to literals before codegen instead of after","Add tests for every conversion opcode your front end can emit"],"tags":["spirv","constant-folding","conversion","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"}