stride3d/stride · error · InvalidOperationException

Unexpected type for asdouble

Error message

Unexpected type {inputType} for asdouble

What it means

CompileAsdouble implements the asdouble intrinsic by packing two 32-bit integers into a uint2 and bitcasting to double. If the input value's type is not one of the supported integer forms (uint/uint2 from asuint pairs), the method exhausts its branches and throws InvalidOperationException at IntrinsicImplementations.cs:55.

Solutions

  1. Pass the two 32-bit halves as uints: `asdouble(asuint(low), asuint(high))` or a uint2 pair.
  2. Do not pass float/double inputs to asdouble; convert to uint first with asuint.
  3. If other input types should be supported, add a branch in CompileAsdouble before the final throw.

Example fix

// before
double d = asdouble(myFloat);
// after
uint2 packed = asuint(doubleValue); double d = asdouble(packed.x, packed.y);
Defensive patterns

Strategy: type-guard

Validate before calling

// asdouble requires uint (or uint2) input produced by asuint
if (input is not (ScalarType { Type: Scalar.UInt } or VectorType { BaseType: ScalarType { Type: Scalar.UInt } }))
    throw new ShaderCompileHint("asdouble expects uint low/high pair");

Type guard

static bool IsUintValue(BaseType t) => t.GetElementType() is ScalarType { Type: Scalar.UInt };

Try / catch

try { result = CompileIntrinsic("asdouble", args, table, context, builder); }
catch (InvalidOperationException ex) when (ex.Message.Contains("for asdouble"))
{ diagnostics.Report(location, ex.Message); }

Prevention

When it happens

Trigger: Calling asdouble(x) with a float, double, int, vector-of-float, or any type other than the expected uint / uint2 low-high pair.

Common situations: Calling asdouble on a float variable, passing a scalar when a two-component pair is required, or on int inputs instead of the uint pair produced by asuint.

Related errors


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

Appendix: source

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

            var packed = builder.Insert(new OpCompositeConstruct(uint2Type, context.Bound++, [x.Id, y.Id]));
            var result = builder.Insert(new OpBitcast(doubleType, context.Bound++, packed.ResultId));
            return new(result.ResultId, result.ResultType);
        }
        else if (inputType is VectorType v)
        {
            var uintType = context.GetOrRegister(ScalarType.UInt);
            var components = new int[v.Size];
            for (int i = 0; i < v.Size; i++)
            {
                var xi = builder.Insert(new OpCompositeExtract(uintType, context.Bound++, x.Id, [i]));
                var yi = builder.Insert(new OpCompositeExtract(uintType, context.Bound++, y.Id, [i]));
                var packed = builder.Insert(new OpCompositeConstruct(uint2Type, context.Bound++, [xi.ResultId, yi.ResultId]));
                components[i] = builder.Insert(new OpBitcast(doubleType, context.Bound++, packed.ResultId)).ResultId;
            }
            var result = new SpirvValue(builder.InsertData(new OpCompositeConstruct(context.GetOrRegister(functionType.ReturnType), context.Bound++, [.. components])));
            return result;
        }
        throw new InvalidOperationException($"Unexpected type {inputType} for asdouble");
    }
    public override SpirvValue CompileAsfloat16(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileBitcastCall(table, context, builder, functionType.ReturnType, x);
    public override SpirvValue CompileAsint16(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileBitcastCall(table, context, builder, functionType.ReturnType, x);
    public override SpirvValue CompileAsuint16(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileBitcastCall(table, context, builder, functionType.ReturnType, x);

    // Trigo
    public override SpirvValue CompileSin(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLSin, x);
    public override SpirvValue CompileSinh(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLSinh, x);
    public override SpirvValue CompileAsin(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLAsin, x);
    public override SpirvValue CompileCos(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLCos, x);
    public override SpirvValue CompileCosh(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLCosh, x);
    public override SpirvValue CompileAcos(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLAcos, x);
    public override SpirvValue CompileTan(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLTan, x);
    public override SpirvValue CompileTanh(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLTanh, x);
    public override SpirvValue CompileAtan(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, TextLocation location = default) => CompileGLSLFloatUnaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLAtan, x);
    public override SpirvValue CompileAtan2(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, SpirvValue y, TextLocation location = default) => CompileGLSLFloatBinaryCall(table, context, builder, functionType.ReturnType, Specification.GLSLOp.GLSLAtan2, x, y);
    public override SpirvValue CompileSincos(SymbolTable table, SpirvContext context, SpirvBuilder builder, FunctionType functionType, SpirvValue x, SpirvValue s, SpirvValue c, TextLocation location = default)
    {

View on GitHub (pinned to 96fad776d2)