{"record":{"id":"9c72702676b53e60","repo":"stride3d/stride","slug":"unsupported-element-type-for-saturate-functiontype","errorCode":null,"errorMessage":"Unsupported element type for saturate: {functionType.ReturnType.GetElementType()}","messagePattern":"Unsupported element type for saturate: (.+?)","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/IntrinsicImplementations.cs","lineNumber":241,"sourceCode":"        return new(instruction.ResultId, instruction.ResultType);\n    }\n\n    public override SpirvValue CompileRound(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLRound, x);\n    public override SpirvValue CompileRsqrt(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLInverseSqrt, x);\n    public override SpirvValue CompileSqrt(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLSqrt, x);\n    public override SpirvValue CompileStep(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue a, SpirvValue x, TextLocation location = default) => CompileGLSLFloatBinaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLStep, a, x);\n    public override SpirvValue CompileSaturate(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default)\n    {\n        // Ensure 0.0 amd 1.0 constants have same type as x\n        var constant0 = builder.Convert(context, context.CompileConstant(0.0f), functionType.ReturnType);\n        var constant1 = builder.Convert(context, context.CompileConstant(1.0f), functionType.ReturnType);\n\n        var instruction = functionType.ReturnType.GetElementType() switch\n        {\n            ScalarType { Type: Scalar.Float } => builder.InsertData(new GLSLFClamp(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id, constant0.Id, constant1.Id)),\n            ScalarType { Type: Scalar.UInt } => builder.InsertData(new GLSLUClamp(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id, constant0.Id, constant1.Id)),\n            ScalarType { Type: Scalar.Int } => builder.InsertData(new GLSLSClamp(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id, constant0.Id, constant1.Id)),\n            _ => throw new NotSupportedException($\"Unsupported element type for saturate: {functionType.ReturnType.GetElementType()}\"),\n        };\n        return new(instruction);\n    }\n    public override SpirvValue CompileSign(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default)\n    {\n        var sourceType = context.ReverseTypes[x.TypeId];\n        var instruction = sourceType.GetElementType() switch\n        {\n            ScalarType { Type: Scalar.Float or Scalar.Double } => builder.InsertData(new GLSLFSign(x.TypeId, context.Bound++, context.GetGLSL(), x.Id)),\n            ScalarType { Type: Scalar.UInt or Scalar.Int or Scalar.UInt64 or Scalar.Int64 } => builder.InsertData(new GLSLSSign(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id)),\n            _ => throw new NotSupportedException($\"Unsupported element type for sign: {sourceType.GetElementType()}\"),\n        };\n        // FSign return float whereas HLSL sign() expects int\n        return builder.Convert(context, new(instruction), functionType.ReturnType);\n    }\n\n    public override SpirvValue CompileSmoothstep(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue a, SpirvValue b, SpirvValue x, TextLocation location = default)\n    {","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/IntrinsicImplementations.cs#L223-L259","documentation":"CompileSaturate implements saturate() as a clamp between 0 and 1, selecting the GLSL.std.450 FClamp/UClamp/SClamp opcode from the function's RETURN type element type. If the return type element is not Float, UInt or Int (e.g. double, bool), the opcode cannot be chosen and NotSupportedException is thrown.","triggerScenarios":"Calling saturate(x) where the expression's resolved return type has an unsupported element type — typically double (saturate of a double), or a bool/struct type from overload resolution gone wrong.","commonSituations":"Using saturate on double-precision math in a shader targeting an API without double clamp; a generic T function where T instantiates to an unexpected scalar; implicit conversions producing an unexpected return type.","solutions":["Cast the value to float before saturate, e.g. saturate((float)x).","Avoid saturate on double; write min(max(x, 0.0), 1.0) manually if doubles are truly needed.","Check overload resolution so saturate receives a float/int/uint vector or scalar.","If a scalar kind should be supported, add a case to the switch in CompileSaturate."],"exampleFix":"// before (SDSL)\ndouble d = ...;\nfloat s = saturate(d); // throws\n// after\nfloat s = saturate((float)d);","handlingStrategy":"validation","validationCode":"bool IsSaturatable(TypeBase returnType) =>\n    returnType.GetElementType() is ScalarType { Type: Scalar.Float or Scalar.UInt or Scalar.Int };","typeGuard":"bool IsFloatLike(TypeBase t) => t.GetElementType() is ScalarType { Type: Scalar.Float };","tryCatchPattern":"try { result = intrinsics.CompileSaturate(table, ctx, builder, fnType, x); }\ncatch (NotSupportedException ex) { /* suggest a cast to float */ }","preventionTips":["Avoid saturate on double; cast to float first","Verify the expression's resolved return type, not the literal you wrote","Prefer explicit min/max for non-standard element types"],"tags":["shader","spirv","sdsl","type-mismatch"],"backgroundTag":"unsupported-dtype","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"}