stride3d/stride · error · NotSupportedException

Unsupported type in struct binary operation

Error message

Unsupported type in struct binary operation: {otherType}

What it means

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.

Solutions

  1. Make both operands the same struct type, or convert one to a scalar/vector before the operation
  2. Perform the operation member-by-member explicitly in the shader
  3. Add an operator overload / conversion function for the mismatched combination
  4. Verify the struct definitions (fields, order, types) match exactly on both sides

Example fix

// before (HLSL)
struct S { float a; float b; };
S s; float3 v;
var r = s + v;
// after
S s2; S s3;
var r = s2 + s3; // or r.a = s.a + v.x; etc.
Defensive patterns

Strategy: type-guard

Validate before calling

// Only allow struct+same-struct or struct+scalar binary ops
if (left is StructType st && right is not (StructType eq when eq == st) && right is not ScalarType)
    throw new ShaderSemanticException($"Cannot apply operator between {left} and {right}");

Type guard

bool IsStructOpCompatible(StructType s, SymbolType other) => other is StructType o && o == s || other is ScalarType;

Try / catch

try { var r = BinaryOperation(table, context, left, op, right, info); }
catch (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); }

Prevention

When it happens

Trigger: Applying +,-,*,/ etc. between a struct value and a value of an incompatible type (different struct type, array, vector) via BinaryOperation when leftType is StructType.

Common situations: 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.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/08073e7ba2cb4d3e. Report an issue: GitHub.

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Spirv/Building/Builder.Expressions.cs:243

            // If there is a struct, we simply apply the operation for each member
            // (this is SDSL-specific)
            var structValue = leftType is StructType ? left : right;
            var otherType = leftType is StructType ? rightType : leftType;
            var otherValue = leftType is StructType ? right : left;

            Span<int> structValues = stackalloc int[structType.Members.Count];
            for (var i = 0; i < structType.Members.Count; i++)
            {
                var member = structType.Members[i];
                var memberValue = Buffer.Insert(Position++, new OpCompositeExtract(context.GetOrRegister(member.Type), context.Bound++, structValue.Id, [i])).ToValue();

                var otherMemberValue = otherType switch
                {
                    // If the other value is also a struct of same type, extract its value
                    StructType otherStructType when otherStructType == structType
                        => Buffer.Insert(Position++, new OpCompositeExtract(context.GetOrRegister(member.Type), context.Bound++, otherValue.Id, [i])).ToValue(),
                    ScalarType => otherValue,
                    _ => throw new NotSupportedException($"Unsupported type in struct binary operation: {otherType}"),
                };
                memberValue = leftType is StructType
                    ? BinaryOperation(table, context, memberValue, op, otherMemberValue, info, name)
                    : BinaryOperation(table, context, otherMemberValue, op, memberValue, info, name);
                structValues[i] = memberValue.Id;
            }

            return Buffer.Insert(Position++, new OpCompositeConstruct(context.GetOrRegister(structType), resultId, [.. structValues])).ToValue();
        }

        // Can indirectly happen inside struct (SDSL specific)
        var arrayType = leftType as ArrayType ?? rightType as ArrayType;
        if (arrayType != null)
        {
            var arrayValue = leftType is ArrayType ? left : right;
            var otherType = leftType is ArrayType ? rightType : leftType;
            var otherValue = leftType is ArrayType ? right : left;

View on GitHub (pinned to 96fad776d2)