stride3d/stride · error · NotImplementedException
Couldn't figure out element type for binary operation…
Error message
Couldn't figure out element type for binary operation between {leftElementType} and {rightElementType} What it means
FindCommonBaseTypeForBinaryOperation's switch has no matching promotion rule for the pair of operand element types, so it throws NotImplementedException naming both types. This happens for type combinations not covered by the promotion table (e.g. mixing doubles/halves with unsupported combos, or non-scalar element types leaking in).
Solutions
- Make both operands the same scalar type with explicit casts (e.g. (float)a + (float)b)
- Avoid mixing half and double in a single expression; normalize to float
- Inspect the message's two type names to see which combination lacks a promotion rule
- Extend the promotion switch if the combination is a valid HLSL case
Example fix
// before (HLSL) double d = ...; half h = ...; var r = d + h; // after double d = ...; half h = ...; var r = d + (double)h;
Defensive patterns
Strategy: validation
Validate before calling
// Normalize operand element types before binary ops
if (l is ScalarType sl && r is ScalarType sr && !IsPromotable(sl, sr))
throw new ShaderSemanticException($"No promotion rule for {sl} and {sr}; add explicit casts"); Type guard
bool IsPromotable(ScalarType a, ScalarType b) =>
!(a.Type == Scalar.Int64 || b.Type == Scalar.Int64) &&
!( (a.Type == Scalar.Double && b.Type == Scalar.Half) || (a.Type == Scalar.Half && b.Type == Scalar.Double) ); Try / catch
try { var r = AnalyzeBinaryOperation(table, l, op, r, loc); }
catch (NotImplementedException ex) when (ex.Message.StartsWith("Couldn't figure out element type")) { throw new ShaderSemanticException($"Unsupported operand combination: {ex.Message}", ex); } Prevention
- Cast operands to a common scalar type before mixing half/double/int
- Avoid exotic scalar combinations in one expression
- Extend the promotion table for combinations your shaders need
When it happens
Trigger: Binary operation whose left/right element types fall through all promotion cases, e.g. Double with Half, or mismatched composite element types reaching the default arm of the switch.
Common situations: Mixing float16 (half) with double in one expression; unusual implicit conversions in shader code; frontend producing unexpected scalar kinds.
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
- unknown accessor on type in expression
- Unsupported interlocked operation
- 64bit integers
- Unsupported float width
- Unsupported constant type
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/183ba5339a51f180.
Report an issue: GitHub.
Appendix: source
Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Expressions.cs:126
(ScalarType { Type: Scalar.Int64 }, _) or (_, ScalarType { Type: Scalar.Int64 }) => throw new NotImplementedException("64bit integers"),
// Matching types
(ScalarType { Type: Scalar.Int or Scalar.UInt or Scalar.Half or Scalar.Float or Scalar.Double or Scalar.Boolean } l, ScalarType r) when l == r => l,
// If one side is float/double and other is integer, promote to floating
(ScalarType { Type: Scalar.Int or Scalar.UInt } l, ScalarType { Type: Scalar.Float or Scalar.Double } r) => r,
(ScalarType { Type: Scalar.Float or Scalar.Double } l, ScalarType { Type: Scalar.Int or Scalar.UInt } r) => l,
// Half mixed with float/double: HLSL narrows to half
(ScalarType { Type: Scalar.Half } l, ScalarType { Type: Scalar.Float or Scalar.Double }) => l,
(ScalarType { Type: Scalar.Float or Scalar.Double }, ScalarType { Type: Scalar.Half } r) => r,
// Half mixed with integer promotes to half
(ScalarType { Type: Scalar.Int or Scalar.UInt } l, ScalarType { Type: Scalar.Half } r) => r,
(ScalarType { Type: Scalar.Half } l, ScalarType { Type: Scalar.Int or Scalar.UInt } r) => l,
// If one side is unsigned, promote to unsigned (bitcast)
(ScalarType { Type: Scalar.Int } l, ScalarType { Type: Scalar.UInt } r) => r,
(ScalarType { Type: Scalar.UInt } l, ScalarType { Type: Scalar.Int } r) => l,
// Bool promotes to int/uint/float in arithmetic contexts (HLSL implicit conversion)
(ScalarType { Type: Scalar.Boolean }, ScalarType { Type: Scalar.Int or Scalar.UInt or Scalar.Half or Scalar.Float or Scalar.Double } r) => r,
(ScalarType { Type: Scalar.Int or Scalar.UInt or Scalar.Half or Scalar.Float or Scalar.Double } l, ScalarType { Type: Scalar.Boolean }) => l,
_ => throw new NotImplementedException($"Couldn't figure out element type for binary operation between {leftElementType} and {rightElementType}"),
};
}
public static (SymbolType OperandType, SymbolType ResultType)? AnalyzeBinaryOperation(SymbolTable table, SymbolType leftType, Operator op, SymbolType rightType, TextLocation info)
{
static bool IsComplexType(SymbolType type) => type is StreamsType or StructType;
// struct or streams types
var complexType = IsComplexType(leftType) ? leftType : (IsComplexType(rightType) ? rightType : null);
if (complexType != null)
{
// Only simple operations are allowed (they will be applied on each member)
var otherType = IsComplexType(leftType) ? rightType : leftType;
if (otherType is not ScalarType { Type: Scalar.Float } and not StreamsType
|| (op != Operator.Plus && op != Operator.Minus && op != Operator.Mul && op != Operator.Div))
{
table.AddError(new(info, string.Format(SDSLErrorMessages.SDSL0108, leftType, rightType)));
return null;View on GitHub (pinned to 96fad776d2)