stride3d/stride · error · NotImplementedException

Cast only work between numeral types

Error message

Cast only work between numeral types (cast from {valueType} to {castType})

What it means

CanConvertScore only supports conversion scoring between numeral types (ScalarType, VectorType, MatrixType). If either the source or target type is anything else (e.g. StreamsType, ArrayType, object), the function throws because the library deliberately filters casts to numeric types.

Solutions

  1. Restrict the cast to scalar/vector/matrix types on both sides
  2. Convert via an explicit operation appropriate for the type (e.g. sample a stream into a vector before casting)
  3. Remove the cast if the value is already the correct type and fix the surrounding type inference
  4. Extend the conversion logic if this numeric/non-numeric cast should be legitimate

Example fix

// before
float x = (float)someStream; // cast from StreamsType
// after
float x = someStream.Load(0); // or bind stream values via proper stream access
Defensive patterns

Strategy: type-guard

Validate before calling

static bool IsNumeral(SymbolType t) => t is ScalarType or VectorType or MatrixType;
if (!IsNumeral(valueType) || !IsNumeral(castType)) throw new InvalidOperationException("Cast requires numeral types");

Type guard

bool IsNumeralType(SymbolType t) => t is ScalarType or VectorType or MatrixType;

Try / catch

try { score = CanConvertScore(valueType, castType); }
catch (NotImplementedException ex) when (ex.Message.Contains("numeral types"))
{ /* reject the overload or pick a non-cast path */ }

Prevention

When it happens

Trigger: Requesting a conversion score where valueType or castType is not a ScalarType/VectorType/MatrixType — e.g. casting a stream or array to a numeral type or vice versa.

Common situations: Casting between streams and vectors in shader code; accidentally casting an array to a scalar; overload resolution reaching CanConvertScore with non-numeric candidate types after generics instantiation.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Expressions.cs:418

                // TODO: do a "check pass" later to make sure this won't happen after generics instantiation?
                return 0;
            }
            else
            {
                return int.MaxValue;
            }
        }

        if (castType is StreamsType { Kind: var targetKind }
            && valueType is StreamsType { Kind: var sourceKind }
            // Note: order is Input, Streams, Output
            && targetKind > sourceKind)
            return 1;

        // We don't support cast with object yet, filter for numeral types
        if ((castType is not ScalarType && castType is not VectorType && castType is not MatrixType)
            || (valueType is not ScalarType && valueType is not VectorType && valueType is not MatrixType))
            throw new NotImplementedException($"Cast only work between numeral types (cast from {valueType} to {castType})");

        var conversionScore = (valueType, castType) switch
        {
            // Same size
            (ScalarType, ScalarType) => 0,
            (VectorType v1, VectorType v2) when v1.Size == v2.Size => 0,
            (MatrixType m1, MatrixType m2) when m1.Rows == m2.Rows && m1.Columns == m2.Columns => 0,

            // Promotion scalar to vector or matrix (replicate value) — more expensive than scalar-to-scalar conversion
            (ScalarType, VectorType or MatrixType) => 5,

            // Truncation
            // Shape-changing truncation (vector/matrix to scalar) costs more than dimension-reducing truncation
            // This ensures e.g. mul(float3, float4x4) picks mul_vm(float3, float3x4) over mul_sm(float, float4x4)
            (VectorType or MatrixType, ScalarType) => 20,
            (VectorType v1, VectorType v2) when v1.Size > v2.Size => 10 + (v1.Size - v2.Size),
            (MatrixType m1, MatrixType m2) when (m1.Rows > m2.Rows && m1.Columns >= m2.Columns) || (m1.Rows >= m2.Rows && m1.Columns > m2.Columns) => 10 + (m1.Rows - m2.Rows) + (m1.Columns - m2.Columns),

View on GitHub (pinned to 96fad776d2)