{"record":{"id":"08073e7ba2cb4d3e","repo":"stride3d/stride","slug":"unsupported-type-in-struct-binary-operation-othertype","errorCode":null,"errorMessage":"Unsupported type in struct binary operation: {otherType}","messagePattern":"Unsupported type in struct binary operation: (.+?)","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Expressions.cs","lineNumber":243,"sourceCode":"            // If there is a struct, we simply apply the operation for each member\n            // (this is SDSL-specific)\n            var structValue = leftType is StructType ? left : right;\n            var otherType = leftType is StructType ? rightType : leftType;\n            var otherValue = leftType is StructType ? right : left;\n\n            Span<int> structValues = stackalloc int[structType.Members.Count];\n            for (var i = 0; i < structType.Members.Count; i++)\n            {\n                var member = structType.Members[i];\n                var memberValue = Buffer.Insert(Position++, new OpCompositeExtract(context.GetOrRegister(member.Type), context.Bound++, structValue.Id, [i])).ToValue();\n\n                var otherMemberValue = otherType switch\n                {\n                    // If the other value is also a struct of same type, extract its value\n                    StructType otherStructType when otherStructType == structType\n                        => Buffer.Insert(Position++, new OpCompositeExtract(context.GetOrRegister(member.Type), context.Bound++, otherValue.Id, [i])).ToValue(),\n                    ScalarType => otherValue,\n                    _ => throw new NotSupportedException($\"Unsupported type in struct binary operation: {otherType}\"),\n                };\n                memberValue = leftType is StructType\n                    ? BinaryOperation(table, context, memberValue, op, otherMemberValue, info, name)\n                    : BinaryOperation(table, context, otherMemberValue, op, memberValue, info, name);\n                structValues[i] = memberValue.Id;\n            }\n\n            return Buffer.Insert(Position++, new OpCompositeConstruct(context.GetOrRegister(structType), resultId, [.. structValues])).ToValue();\n        }\n\n        // Can indirectly happen inside struct (SDSL specific)\n        var arrayType = leftType as ArrayType ?? rightType as ArrayType;\n        if (arrayType != null)\n        {\n            var arrayValue = leftType is ArrayType ? left : right;\n            var otherType = leftType is ArrayType ? rightType : leftType;\n            var otherValue = leftType is ArrayType ? right : left;\n","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/stride3d/stride/blob/96fad776d210c221682aac1ccdf4c79dc046fc38/sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Expressions.cs#L225-L261","documentation":"BinaryOperation on struct-typed values decomposes the operation member-by-member. For each member, the 'other' operand must be either a struct of the same type (to extract the matching member) or a scalar (broadcast). Any other type for the right-hand side is rejected with NotSupportedException.","triggerScenarios":"Applying +,-,*,/ etc. between a struct value and a value of an incompatible type (different struct type, array, vector) via BinaryOperation when leftType is StructType.","commonSituations":"Shader code adding a struct to a vector or to a differently-shaped struct; operator overloads/typedefs changed so the two structs no longer match; API returning struct vs scalar inconsistently.","solutions":["Make both operands the same struct type, or convert one to a scalar/vector before the operation","Perform the operation member-by-member explicitly in the shader","Add an operator overload / conversion function for the mismatched combination","Verify the struct definitions (fields, order, types) match exactly on both sides"],"exampleFix":"// before (HLSL)\nstruct S { float a; float b; };\nS s; float3 v;\nvar r = s + v;\n// after\nS s2; S s3;\nvar r = s2 + s3; // or r.a = s.a + v.x; etc.","handlingStrategy":"type-guard","validationCode":"// Only allow struct+same-struct or struct+scalar binary ops\nif (left is StructType st && right is not (StructType eq when eq == st) && right is not ScalarType)\n    throw new ShaderSemanticException($\"Cannot apply operator between {left} and {right}\");","typeGuard":"bool IsStructOpCompatible(StructType s, SymbolType other) => other is StructType o && o == s || other is ScalarType;","tryCatchPattern":"try { var r = BinaryOperation(table, context, left, op, right, info); }\ncatch (NotSupportedException ex) when (ex.Message.StartsWith(\"Unsupported type in struct binary operation\")) { throw new ShaderSemanticException(\"Struct binary operations require same-type struct or scalar operand\", ex); }","preventionTips":["Apply operators only between identical struct types or struct/scalar","Implement explicit member-wise operations or operator overloads in shaders","Keep struct definitions in sync across modules"],"tags":["shader","struct","binary-operation","spirv"],"backgroundTag":"incompatible-source-type","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"}