{"record":{"id":"862fa496f58cf489","repo":"stride3d/stride","slug":"unsupported-ternary-operand-types-condition-condition","errorCode":null,"errorMessage":"Unsupported ternary operand types: condition={Condition.ValueType}, left={Left.ValueType}, right={Right.ValueType}","messagePattern":"Unsupported ternary operand types: condition=(.+?), left=(.+?), right=(.+?)","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/Expression.cs","lineNumber":1630,"sourceCode":"    {\n        Condition.ProcessSymbol(table);\n\n        if (Condition.ValueType!.GetElementType() is not ScalarType)\n            table.AddError(new(Condition.Info, SDSLErrorMessages.SDSL0106));\n\n        Left.ProcessSymbol(table);\n        Right.ProcessSymbol(table);\n\n        var scalarType = SpirvBuilder.FindCommonBaseTypeForBinaryOperation(Left.ValueType!.GetElementType(), Right.ValueType!.GetElementType());\n        Type = (Condition.ValueType, Left.ValueType, Right.ValueType) switch\n        {\n            // If condition is a vector, we need to use this vector size instead\n            (VectorType c, _, _) => new VectorType(scalarType, c.Size),\n            (ScalarType c, ScalarType, ScalarType) => scalarType,\n            (ScalarType c, VectorType v1, ScalarType s2) => v1.WithElementType(scalarType),\n            (ScalarType c, ScalarType s1, VectorType v2) => v2.WithElementType(scalarType),\n            (ScalarType c, VectorType v1, VectorType v2) => new VectorType(scalarType, Math.Min(v1.Size, v2.Size)),\n            _ => throw new NotSupportedException($\"Unsupported ternary operand types: condition={Condition.ValueType}, left={Left.ValueType}, right={Right.ValueType}\"),\n        };\n    }\n\n    public override SpirvValue CompileImpl(SymbolTable table, CompilerUnit compiler)\n    {\n        var (builder, context) = compiler;\n\n        var conditionValue = Condition.CompileAsValue(table, compiler);\n\n        // Might need implicit conversion from float/int to bool, preserving vector shape\n        if (Condition.ValueType is VectorType condVec)\n            conditionValue = builder.Convert(context, conditionValue, new VectorType(ScalarType.Boolean, condVec.Size));\n        else\n            conditionValue = builder.Convert(context, conditionValue, ScalarType.Boolean);\n\n        // TODO: Review choice between if/else like branch (OpBranchConditional) which evaluate only one side, or select (OpSelect) which evaluate both side but can work per component but is limited to specific types\n        //       It seems HLSL 2021 changed the behavior to align it with C-style short-circuiting.\n        // For now, we use OpSelect only with per-component, otherwise we use if/else branching","sourceCodeStart":1612,"sourceCodeEnd":1648,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/Expression.cs#L1612-L1648","documentation":"Type inference for the ternary (?:) operator only supports a fixed set of operand combinations: vector condition, scalar/scalar, scalar+vector mixes, and vector/vector pairs, all requiring a valid scalar element type. Any combination outside these patterns — e.g. matrix or struct operands, or a non-scalar element type — throws this NotSupportedException at Expression.cs:1630.","triggerScenarios":"Evaluating `cond ? a : b` where the condition is not scalar/vector or where Left/Right are matrices, structs, samplers, or other non-vector/scalar types; also when scalarType inference (the element type of the branches) fails.","commonSituations":"Selecting between two matrices with ?:, applying ?: to struct/sampler operands, or writing `matrixA ? m1 : m2` expecting component-wise selection like HLSL allows.","solutions":["Ensure the condition is a scalar or vector bool and both branches are scalars or vectors of compatible element types.","For matrices/structs, replace ?: with an explicit if/else or per-component ternary on vectors.","If a new valid combination should be supported, add a pattern arm to the switch at Expression.cs:1630."],"exampleFix":"// before\nfloat3x3 m = b ? m1 : m2;   // matrix operands\n// after\nfloat3x3 m;\nif (b) m = m1; else m = m2;","handlingStrategy":"validation","validationCode":"// check ternary operand kinds before compiling\nbool ok = cond is ScalarType or VectorType && l is (ScalarType or VectorType) && r is (ScalarType or VectorType);\nif (!ok) throw new ShaderCompileHint(\"Ternary requires scalar/vector condition and branches\");","typeGuard":"static bool IsTernaryCompatible(BaseType c, BaseType l, BaseType r) => c is ScalarType or VectorType && l is ScalarType or VectorType && r is ScalarType or VectorType;","tryCatchPattern":"try { result = CompileExpression(expr, table, compiler); }\ncatch (NotSupportedException ex) when (ex.Message.Contains(\"Unsupported ternary operand types\"))\n{ diagnostics.Report(expr.Info, ex.Message); }","preventionTips":["Keep ternary conditions scalar or vector bool.","Use if/else instead of ?: for matrices and structs.","Test type inference for every operand combination you rely on."],"tags":["shader","sdsl","type-inference","ternary"],"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"}