stride3d/stride · error · InvalidOperationException

Unexpected type for f32tof16

Error message

Unexpected type {inputType} for f32tof16

What it means

CompileF32tof16 packs float values into 16-bit halves using GLSLstd450 PackHalf2x16 and expects the input to resolve to a float scalar or float vector; the per-element results are recomposed into the uint return type. If the input type is not a supported float type/vector, it throws InvalidOperationException naming the resolved input type.

Solutions

  1. Cast the argument to float (or floatN) before f32tof16, e.g. f32tof16((float)x).
  2. Use asuint/asint for pure bit reinterpretation instead of f32tof16.
  3. Ensure generic shader code constrains T to float types for this call path.
  4. If another input kind should be supported, extend the type dispatch in CompileF32tof16.

Example fix

// before (SDSL)
int i = 3;
uint p = f32tof16(i); // throws: Unexpected type int
// after
uint p = f32tof16((float)i);
Defensive patterns

Strategy: validation

Validate before calling

bool IsFloatInput(TypeBase t) =>
    t is ScalarType { Type: Scalar.Float } or VectorType { BaseType: ScalarType { Type: Scalar.Float } };

Type guard

bool IsFloatOrFloatVec(TypeBase t) => t.GetElementType() is ScalarType { Type: Scalar.Float };

Try / catch

try { result = intrinsics.CompileF32tof16(table, ctx, builder, fnType, x); }
catch (InvalidOperationException ex) { /* report that input must be float/floatN */ }

Prevention

When it happens

Trigger: Calling f32tof16(x) where x resolves to int, uint, double, or a non-float vector — only float scalars/vectors can be packed with PackHalf2x16 in the generated code.

Common situations: Passing an int assuming bit-level reinterpretation; passing a double from double-precision math; generic T functions instantiating to non-float types.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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

Appendix: source

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

        }
        else if (inputType is VectorType v)
        {
            var components = new int[v.Size];
            for (int i = 0; i < v.Size; i++)
            {
                // Extract float component
                var comp = new SpirvValue(builder.InsertData(new OpCompositeExtract(floatType, context.Bound++, x.Id, [i])));
                // Construct float2(comp, 0.0)
                var float2Val = new SpirvValue(builder.InsertData(new OpCompositeConstruct(float2Type, context.Bound++, [comp.Id, zero])));
                // PackHalf2x16 -> uint
                var pack = builder.Insert(new GLSLExp(uintType, context.Bound++, context.GetGLSL(), float2Val.Id));
                pack.InstructionMemory.Span[4] = 58; // GLSLstd450 PackHalf2x16
                components[i] = pack.ResultId;
            }
            var result = new SpirvValue(builder.InsertData(new OpCompositeConstruct(context.GetOrRegister(returnType), context.Bound++, [.. components])));
            return result;
        }
        throw new InvalidOperationException($"Unexpected type {inputType} for f32tof16");
    }
    public override SpirvValue CompileFirstbitlow(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLFindILsb, x);
    public override SpirvValue CompileFma(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue a, SpirvValue b, SpirvValue c, TextLocation location = default)
    {
        var instruction = builder.Insert(new GLSLFma(a.TypeId, context.Bound++, context.GetGLSL(), a.Id, b.Id, c.Id));
        return new(instruction.ResultId, instruction.ResultType);
    }
    public override SpirvValue CompileFrexp(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, SpirvValue exp, TextLocation location = default)
    {
        var instruction = builder.Insert(new GLSLFrexp(context.GetOrRegister(functionType.ReturnType), context.Bound++, context.GetGLSL(), x.Id, exp.Id));
        return new(instruction.ResultId, instruction.ResultType);
    }
    public override SpirvValue CompileIsfinite(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default)
    {
        // isfinite(x) = !(isinf(x) || isnan(x))
        var boolType = context.GetOrRegister(functionType.ReturnType);
        var isInf = builder.Insert(new OpIsInf(boolType, context.Bound++, x.Id));
        var isNan = builder.Insert(new OpIsNan(boolType, context.Bound++, x.Id));

View on GitHub (pinned to 96fad776d2)