{"record":{"id":"4c21b92ee42c57ca","repo":"stride3d/stride","slug":"type-of-binary-operation-could-not-be-determined","errorCode":null,"errorMessage":"Type of binary operation could not be determined","messagePattern":"Type of binary operation could not be determined","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Expressions.cs","lineNumber":282,"sourceCode":"            {\n                var memberValue = Buffer.Insert(Position++, new OpCompositeExtract(context.GetOrRegister(arrayType.BaseType), context.Bound++, arrayValue.Id, [i])).ToValue();\n                var otherMemberValue = otherType switch\n                {\n                    // If the other value is also a struct of same type, extract its value\n                    ArrayType otherArrayType when otherArrayType == arrayType\n                        => Buffer.Insert(Position++, new OpCompositeExtract(context.GetOrRegister(arrayType.BaseType), context.Bound++, otherValue.Id, [i])).ToValue(),\n                    ScalarType => otherValue,\n                    _ => throw new NotSupportedException($\"Unsupported type in array binary operation: {otherType}\"),\n                };\n                arrayValues[i] = leftType is StructType\n                    ? BinaryOperation(table, context, memberValue, op, otherMemberValue, info, name).Id\n                    : BinaryOperation(table, context, otherMemberValue, op, memberValue, info, name).Id;\n            }\n\n            return Buffer.Insert(Position++, new OpCompositeConstruct(context.GetOrRegister(arrayType), resultId, [.. arrayValues])).ToValue();\n        }\n\n        (var operandType, var resultType) = AnalyzeBinaryOperation(table, leftType, op, rightType, info) ?? throw new InvalidOperationException(\"Type of binary operation could not be determined\");\n\n        // 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)\n        //       We'll need to review those cases\n        left = Convert(context, left, operandType);\n        right = Convert(context, right, operandType);\n\n        var resultTypeId = context.GetOrRegister(resultType);\n\n        // Refresh types (after convert)\n        leftType = context.ReverseTypes[left.TypeId];\n        rightType = context.ReverseTypes[right.TypeId];\n\n        // SPIR-V arithmetic ops (OpFAdd, OpFSub, OpFMul, etc.) only accept scalar/vector types.\n        // For matrices, decompose into column vectors, apply the op per-column, then reconstruct.\n        if (leftType is MatrixType matType)\n        {\n            if (op is not (Operator.Plus or Operator.Minus or Operator.Mul or Operator.Div))\n                throw new InvalidOperationException($\"Operator '{op}' is not supported on matrix types\");","sourceCodeStart":264,"sourceCodeEnd":300,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Expressions.cs#L264-L300","documentation":"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'.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Check that the operator is valid for the operand types; cast operands to a supported type","Use explicit type conversions so both sides resolve to a common scalar type","Inspect AnalyzeBinaryOperation's return-null conditions for the given op/type combo","Add shader-side explicit functions to implement unsupported operator semantics"],"exampleFix":"// before (HLSL)\nfloat a, b;\nvar c = a & b;\n// after\nint ai = (int)a, bi = (int)b;\nvar c = (float)(ai & bi);","handlingStrategy":"validation","validationCode":"// Validate the operator/type combination is analyzable before codegen\nif (AnalyzeBinaryOperation(table, leftType, op, rightType, info) is null)\n    throw new ShaderSemanticException($\"Operator {op} is not defined for {leftType} and {rightType}\");","typeGuard":"bool IsBinaryOpAnalyzable(SymbolTable t, SymbolType l, Operator op, SymbolType r, TextLocation loc) => AnalyzeBinaryOperation(t, l, op, r, loc) is not null;","tryCatchPattern":"try { var r = BinaryOperation(table, context, left, op, right, info); }\ncatch (InvalidOperationException ex) when (ex.Message == \"Type of binary operation could not be determined\") { throw new ShaderSemanticException($\"Unsupported operator {op} for operand types\", ex); }","preventionTips":["Use operators only where HLSL defines them for the operand types","Insert explicit casts so operand types resolve to a common scalar","Add analyzer tests for every operator/type pairing used in your shaders"],"tags":["shader","binary-operation","type-inference","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-23T08:17:48.524Z"}