{"record":{"id":"183ba5339a51f180","repo":"stride3d/stride","slug":"couldn-t-figure-out-element-type-for-binary-operation","errorCode":null,"errorMessage":"Couldn't figure out element type for binary operation between {leftElementType} and {rightElementType}","messagePattern":"Couldn't figure out element type for binary operation between (.+?) and (.+?)","errorType":"exception","errorClass":"NotImplementedException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Expressions.cs","lineNumber":126,"sourceCode":"            (ScalarType { Type: Scalar.Int64 }, _) or (_, ScalarType { Type: Scalar.Int64 }) => throw new NotImplementedException(\"64bit integers\"),\n            // Matching types\n            (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,\n            // If one side is float/double and other is integer, promote to floating\n            (ScalarType { Type: Scalar.Int or Scalar.UInt } l, ScalarType { Type: Scalar.Float or Scalar.Double } r) => r,\n            (ScalarType { Type: Scalar.Float or Scalar.Double } l, ScalarType { Type: Scalar.Int or Scalar.UInt } r) => l,\n            // Half mixed with float/double: HLSL narrows to half\n            (ScalarType { Type: Scalar.Half } l, ScalarType { Type: Scalar.Float or Scalar.Double }) => l,\n            (ScalarType { Type: Scalar.Float or Scalar.Double }, ScalarType { Type: Scalar.Half } r) => r,\n            // Half mixed with integer promotes to half\n            (ScalarType { Type: Scalar.Int or Scalar.UInt } l, ScalarType { Type: Scalar.Half } r) => r,\n            (ScalarType { Type: Scalar.Half } l, ScalarType { Type: Scalar.Int or Scalar.UInt } r) => l,\n            // If one side is unsigned, promote to unsigned (bitcast)\n            (ScalarType { Type: Scalar.Int } l, ScalarType { Type: Scalar.UInt } r) => r,\n            (ScalarType { Type: Scalar.UInt } l, ScalarType { Type: Scalar.Int } r) => l,\n            // Bool promotes to int/uint/float in arithmetic contexts (HLSL implicit conversion)\n            (ScalarType { Type: Scalar.Boolean }, ScalarType { Type: Scalar.Int or Scalar.UInt or Scalar.Half or Scalar.Float or Scalar.Double } r) => r,\n            (ScalarType { Type: Scalar.Int or Scalar.UInt or Scalar.Half or Scalar.Float or Scalar.Double } l, ScalarType { Type: Scalar.Boolean }) => l,\n            _ => throw new NotImplementedException($\"Couldn't figure out element type for binary operation between {leftElementType} and {rightElementType}\"),\n        };\n    }\n\n    public static (SymbolType OperandType, SymbolType ResultType)? AnalyzeBinaryOperation(SymbolTable table, SymbolType leftType, Operator op, SymbolType rightType, TextLocation info)\n    {\n        static bool IsComplexType(SymbolType type) => type is StreamsType or StructType;\n\n        // struct or streams types\n        var complexType = IsComplexType(leftType) ? leftType : (IsComplexType(rightType) ? rightType : null);\n        if (complexType != null)\n        {\n            // Only simple operations are allowed (they will be applied on each member)\n            var otherType = IsComplexType(leftType) ? rightType : leftType;\n            if (otherType is not ScalarType { Type: Scalar.Float } and not StreamsType\n                || (op != Operator.Plus && op != Operator.Minus && op != Operator.Mul && op != Operator.Div))\n            {\n                table.AddError(new(info, string.Format(SDSLErrorMessages.SDSL0108, leftType, rightType)));\n                return null;","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Expressions.cs#L108-L144","documentation":"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).","triggerScenarios":"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.","commonSituations":"Mixing float16 (half) with double in one expression; unusual implicit conversions in shader code; frontend producing unexpected scalar kinds.","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"],"exampleFix":"// before (HLSL)\ndouble d = ...;\nhalf h = ...;\nvar r = d + h;\n// after\ndouble d = ...;\nhalf h = ...;\nvar r = d + (double)h;","handlingStrategy":"validation","validationCode":"// Normalize operand element types before binary ops\nif (l is ScalarType sl && r is ScalarType sr && !IsPromotable(sl, sr))\n    throw new ShaderSemanticException($\"No promotion rule for {sl} and {sr}; add explicit casts\");","typeGuard":"bool IsPromotable(ScalarType a, ScalarType b) =>\n    !(a.Type == Scalar.Int64 || b.Type == Scalar.Int64) &&\n    !( (a.Type == Scalar.Double && b.Type == Scalar.Half) || (a.Type == Scalar.Half && b.Type == Scalar.Double) );","tryCatchPattern":"try { var r = AnalyzeBinaryOperation(table, l, op, r, loc); }\ncatch (NotImplementedException ex) when (ex.Message.StartsWith(\"Couldn't figure out element type\")) { throw new ShaderSemanticException($\"Unsupported operand combination: {ex.Message}\", ex); }","preventionTips":["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"],"tags":["shader","type-promotion","not-implemented","spirv"],"backgroundTag":"type-mismatch","analyzedSha":"96fad776d210c221682aac1ccdf4c79dc046fc38","analyzedAt":"2026-09-14T02:59:31.279Z","contentChangedAt":"2026-09-14T02:59:31.279Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}