stride3d/stride · error · NotImplementedException

Binary operator not supported for element type

Error message

Binary operator {op} not supported for element type {elementType}

What it means

SelectBinaryOp maps an Operator plus an element type to a concrete SPIR-V comparison/instruction opcode. If the (operator, elementType) pair has no entry in the switch — e.g. a relational operator on a boolean/pointer element type — it throws NotImplementedException because no valid SPIR-V opcode exists for that combination.

Solutions

  1. Ensure the compared operands have integer or floating-point scalar element types before applying relational operators
  2. Cast operands to a numeric type (e.g. int/float) before comparison
  3. Add the missing (Operator, elementType) case to SelectBinaryOp if the operation should be supported
  4. Log elementType at the call site to see what type inference actually produced

Example fix

// before
if (a > b) { ... } // a, b are bool2
// after
if (a.x == b.x && a.y == b.y) { ... } // or compare a numeric projection
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(elementType.IsInteger() || elementType.IsFloating()))
    throw new InvalidOperationException($"Relational operator {op} requires numeric element type, got {elementType}");

Type guard

bool IsComparableType(ScalarType t) => t.IsInteger() || t.IsFloating();

Try / catch

try { opCode = SelectBinaryOp(op, elementType); }
catch (NotImplementedException ex) when (ex.Message.Contains("Binary operator"))
{ /* fall back to manual comparison or fail the pass with a clear diagnostic */ }

Prevention

When it happens

Trigger: Calling spirvOp/BinaryOperation with a comparison operator (Less, Greater, Equal, etc.) on a value whose component type is neither integer nor floating (e.g. bool vectors, or an operator like Modulus on a type with no matching mapping).

Common situations: Comparing bool vectors with < or >; comparing structures/arrays component-wise; type inference producing an unexpected element type after generics instantiation so the switch falls through.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

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

        (Operator.NotEquals, ScalarType { Type: Scalar.Boolean }) => Specification.Op.OpLogicalNotEqual,
        (Operator.NotEquals, _) when elementType.IsInteger() => Specification.Op.OpINotEqual,
        (Operator.NotEquals, _) when elementType.IsFloating() => Specification.Op.OpFOrdNotEqual,

        // Comparison
        (Operator.Lower, _) when elementType.IsUnsignedInteger() => Specification.Op.OpULessThan,
        (Operator.Lower, _) when elementType.IsInteger() => Specification.Op.OpSLessThan,
        (Operator.Lower, _) when elementType.IsFloating() => Specification.Op.OpFOrdLessThan,
        (Operator.LowerOrEqual, _) when elementType.IsUnsignedInteger() => Specification.Op.OpULessThanEqual,
        (Operator.LowerOrEqual, _) when elementType.IsInteger() => Specification.Op.OpSLessThanEqual,
        (Operator.LowerOrEqual, _) when elementType.IsFloating() => Specification.Op.OpFOrdLessThanEqual,
        (Operator.Greater, _) when elementType.IsUnsignedInteger() => Specification.Op.OpUGreaterThan,
        (Operator.Greater, _) when elementType.IsInteger() => Specification.Op.OpSGreaterThan,
        (Operator.Greater, _) when elementType.IsFloating() => Specification.Op.OpFOrdGreaterThan,
        (Operator.GreaterOrEqual, _) when elementType.IsUnsignedInteger() => Specification.Op.OpUGreaterThanEqual,
        (Operator.GreaterOrEqual, _) when elementType.IsInteger() => Specification.Op.OpSGreaterThanEqual,
        (Operator.GreaterOrEqual, _) when elementType.IsFloating() => Specification.Op.OpFOrdGreaterThanEqual,

        _ => throw new NotImplementedException($"Binary operator {op} not supported for element type {elementType}")
    };

    /// <summary>
    /// Check if a type can be converted to another.
    /// </summary>
    /// <param name="valueType"></param>
    /// <param name="castType"></param>
    /// <returns>0 if perfect match, int.MaxValue if failure, a score higher than 0 otherwise.</returns>
    public static int CanConvertScore(SymbolType valueType, SymbolType castType)
    {
        if (castType == valueType)
            return 0;

        if (castType is StructType || valueType is StructType)
            throw new NotImplementedException($"Can't cast between structures (cast from {valueType} to {castType})");

        if (castType is ArrayType a1 && valueType is ArrayType a2)
        {

View on GitHub (pinned to 96fad776d2)