stride3d/stride · error · NotSupportedException

Unsupported element type for sign

Error message

Unsupported element type for sign: {sourceType.GetElementType()}

What it means

CompileSign implements sign() using GLSL.std.450 FSign for float/double and SSign for integer types (including 64-bit), then converts the result to the expected int return type. If the source operand's element type is none of these (e.g. bool or half), no opcode applies and NotSupportedException is thrown.

Solutions

  1. Cast the operand to float or int before calling sign (e.g. sign((int)x)).
  2. Replace sign of a bool with explicit comparison logic (x ? 1 : -1).
  3. Ensure the generic type parameter is constrained to numeric scalar types.
  4. If the scalar kind should be supported, add a case to CompileSign mapping it to FSign/SSign.

Example fix

// before (SDSL)
bool b = a > 0;
int s = sign(b); // throws
// after
int s = sign((int)(a > 0 ? 1 : -1));
Defensive patterns

Strategy: validation

Validate before calling

bool IsSignable(TypeBase t) =>
    t.GetElementType() is ScalarType { Type: Scalar.Float or Scalar.Double or Scalar.Int or Scalar.UInt or Scalar.Int64 or Scalar.UInt64 };

Type guard

bool IsNumericScalar(TypeBase t) => t.GetElementType() is ScalarType s && s.Type != Scalar.Bool;

Try / catch

try { result = intrinsics.CompileSign(table, ctx, builder, fnType, x); }
catch (NotSupportedException ex) { /* report element type; suggest cast */ }

Prevention

When it happens

Trigger: Calling sign(x) where x's element type is not float, double, int, uint, int64 or uint64 — e.g. sign of a bool, or of a struct/vector with unexpected element kind resolved from generics.

Common situations: sign() on a bool from a comparison; sign() on a half-precision type where the frontend kept half as a distinct scalar; generic T instantiation to an unsupported scalar.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/cb29de1a2bb79099. Report an issue: GitHub.

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/IntrinsicImplementations.cs:252

        var constant1 = builder.Convert(context, context.CompileConstant(1.0f), functionType.ReturnType);

        var instruction = functionType.ReturnType.GetElementType() switch
        {
            ScalarType { Type: Scalar.Float } => builder.InsertData(new GLSLFClamp(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id, constant0.Id, constant1.Id)),
            ScalarType { Type: Scalar.UInt } => builder.InsertData(new GLSLUClamp(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id, constant0.Id, constant1.Id)),
            ScalarType { Type: Scalar.Int } => builder.InsertData(new GLSLSClamp(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id, constant0.Id, constant1.Id)),
            _ => throw new NotSupportedException($"Unsupported element type for saturate: {functionType.ReturnType.GetElementType()}"),
        };
        return new(instruction);
    }
    public override SpirvValue CompileSign(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default)
    {
        var sourceType = context.ReverseTypes[x.TypeId];
        var instruction = sourceType.GetElementType() switch
        {
            ScalarType { Type: Scalar.Float or Scalar.Double } => builder.InsertData(new GLSLFSign(x.TypeId, context.Bound++, context.GetGLSL(), x.Id)),
            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)),
            _ => throw new NotSupportedException($"Unsupported element type for sign: {sourceType.GetElementType()}"),
        };
        // FSign return float whereas HLSL sign() expects int
        return builder.Convert(context, new(instruction), functionType.ReturnType);
    }

    public override SpirvValue CompileSmoothstep(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue a, SpirvValue b, SpirvValue x, TextLocation location = default)
    {
        var instruction = builder.Insert(new GLSLSmoothStep(a.TypeId, context.Bound++, context.GetGLSL(), a.Id, b.Id, x.Id));
        return new(instruction.ResultId, instruction.ResultType);
    }

    public override SpirvValue CompileLerp(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue a, SpirvValue b, SpirvValue s, TextLocation location = default)
    {
        var instruction = builder.Insert(new GLSLFMix(a.TypeId, context.Bound++, context.GetGLSL(), a.Id, b.Id, s.Id));
        return new(instruction.ResultId, instruction.ResultType);
    }

    public override SpirvValue CompileFmod(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue a, SpirvValue b, TextLocation location = default)

View on GitHub (pinned to 96fad776d2)