stride3d/stride · error · InvalidOperationException

Type of binary operation could not be determined

Error message

Type of binary operation could not be determined

What it means

After handling struct/array composite cases, BinaryOperation calls AnalyzeBinaryOperation to derive operand and result types. If it returns null (the operator/type combination is not analyzable, e.g. unsupported operator on those types), the builder throws InvalidOperationException 'Type of binary operation could not be determined'.

Solutions

  1. Check that the operator is valid for the operand types; cast operands to a supported type
  2. Use explicit type conversions so both sides resolve to a common scalar type
  3. Inspect AnalyzeBinaryOperation's return-null conditions for the given op/type combo
  4. Add shader-side explicit functions to implement unsupported operator semantics

Example fix

// before (HLSL)
float a, b;
var c = a & b;
// after
int ai = (int)a, bi = (int)b;
var c = (float)(ai & bi);
Defensive patterns

Strategy: validation

Validate before calling

// Validate the operator/type combination is analyzable before codegen
if (AnalyzeBinaryOperation(table, leftType, op, rightType, info) is null)
    throw new ShaderSemanticException($"Operator {op} is not defined for {leftType} and {rightType}");

Type guard

bool IsBinaryOpAnalyzable(SymbolTable t, SymbolType l, Operator op, SymbolType r, TextLocation loc) => AnalyzeBinaryOperation(t, l, op, r, loc) is not null;

Try / catch

try { var r = BinaryOperation(table, context, left, op, right, info); }
catch (InvalidOperationException ex) when (ex.Message == "Type of binary operation could not be determined") { throw new ShaderSemanticException($"Unsupported operator {op} for operand types", ex); }

Prevention

When it happens

Trigger: A binary expression whose (leftType, op, rightType) combination makes AnalyzeBinaryOperation return null — e.g. an operator unsupported for the operand types (bitwise op on floats, % on non-integers, comparison on composites).

Common situations: Using an operator invalid for the operand types in HLSL (e.g. bitwise AND on float); applying comparison operators to struct/array types; mismatched scalar kinds the analyzer can't resolve.

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/4c21b92ee42c57ca. Report an issue: GitHub.

Appendix: source

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

            {
                var memberValue = Buffer.Insert(Position++, new OpCompositeExtract(context.GetOrRegister(arrayType.BaseType), context.Bound++, arrayValue.Id, [i])).ToValue();
                var otherMemberValue = otherType switch
                {
                    // If the other value is also a struct of same type, extract its value
                    ArrayType otherArrayType when otherArrayType == arrayType
                        => Buffer.Insert(Position++, new OpCompositeExtract(context.GetOrRegister(arrayType.BaseType), context.Bound++, otherValue.Id, [i])).ToValue(),
                    ScalarType => otherValue,
                    _ => throw new NotSupportedException($"Unsupported type in array binary operation: {otherType}"),
                };
                arrayValues[i] = leftType is StructType
                    ? BinaryOperation(table, context, memberValue, op, otherMemberValue, info, name).Id
                    : BinaryOperation(table, context, otherMemberValue, op, memberValue, info, name).Id;
            }

            return Buffer.Insert(Position++, new OpCompositeConstruct(context.GetOrRegister(arrayType), resultId, [.. arrayValues])).ToValue();
        }

        (var operandType, var resultType) = AnalyzeBinaryOperation(table, leftType, op, rightType, info) ?? throw new InvalidOperationException("Type of binary operation could not be determined");

        // TODO: Some specific cases where one of the operands doesn't need to have exact same type as resultType (such as shift in OpShiftRightLogical, or signedness for some other operations)
        //       We'll need to review those cases
        left = Convert(context, left, operandType);
        right = Convert(context, right, operandType);

        var resultTypeId = context.GetOrRegister(resultType);

        // Refresh types (after convert)
        leftType = context.ReverseTypes[left.TypeId];
        rightType = context.ReverseTypes[right.TypeId];

        // SPIR-V arithmetic ops (OpFAdd, OpFSub, OpFMul, etc.) only accept scalar/vector types.
        // For matrices, decompose into column vectors, apply the op per-column, then reconstruct.
        if (leftType is MatrixType matType)
        {
            if (op is not (Operator.Plus or Operator.Minus or Operator.Mul or Operator.Div))
                throw new InvalidOperationException($"Operator '{op}' is not supported on matrix types");

View on GitHub (pinned to 96fad776d2)