stride3d/stride · error · NotImplementedException

Can't cast between structures

Error message

Can't cast between structures (cast from {valueType} to {castType})

What it means

CanConvertScore computes an implicit-conversion score for overload/cast resolution. Struct types are excluded from conversion scoring because the builder does not implement struct-to-struct casts, so encountering a StructType on either side is treated as an unimplemented path and throws immediately.

Solutions

  1. Make the source and target struct types identical (same SymbolType)
  2. Explicitly convert field-by-field: construct the target struct from the source struct's members
  3. Reinterpret via a helper that builds the new struct value instead of relying on cast
  4. If this should be supported, implement struct conversion logic in CanConvertScore instead of throwing

Example fix

// before
TargetStruct t = sourceStruct; // cast between structures
// after
TargetStruct t = new TargetStruct { A = sourceStruct.A, B = sourceStruct.B };
Defensive patterns

Strategy: type-guard

Validate before calling

if (valueType is StructType || castType is StructType)
    return; // skip conversion scoring; require exact type match

Type guard

bool IsCastable(SymbolType v, SymbolType c) => v is not StructType && c is not StructType;

Try / catch

try { score = CanConvertScore(valueType, castType); }
catch (NotImplementedException ex) when (ex.Message.Contains("cast between structures"))
{ /* require exact struct type or convert field-by-field */ }

Prevention

When it happens

Trigger: Implicit conversion scoring where either the source or target SymbolType is a StructType and the types differ (e.g. passing a struct where another struct type is expected, triggering conversion ranking).

Common situations: Passing one user-defined struct type where a different (though field-compatible) struct is expected; API changes where a method parameter changed struct type; generics instantiation producing two distinct struct types.

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/6f0e74c996c919ef. Report an issue: GitHub.

Appendix: source

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

        (Operator.GreaterOrEqual, _) when elementType.IsInteger() => Specification.Op.OpSGreaterThanEqual,
        (Operator.GreaterOrEqual, _) when elementType.IsFloating() => Specification.Op.OpFOrdGreaterThanEqual,

        _ => throw new NotImplementedException($"Binary operator {op} not supported for element type {elementType}")
    };

    /// <summary>
    /// Check if a type can be converted to another.
    /// </summary>
    /// <param name="valueType"></param>
    /// <param name="castType"></param>
    /// <returns>0 if perfect match, int.MaxValue if failure, a score higher than 0 otherwise.</returns>
    public static int CanConvertScore(SymbolType valueType, SymbolType castType)
    {
        if (castType == valueType)
            return 0;

        if (castType is StructType || valueType is StructType)
            throw new NotImplementedException($"Can't cast between structures (cast from {valueType} to {castType})");

        if (castType is ArrayType a1 && valueType is ArrayType a2)
        {
            if (a1.BaseType == a2.BaseType)
            {
                if (a1.Size != -1 && a1.Size != a2.Size)
                    return int.MaxValue;
                // Some sizes are undetermined; this can only happen during compilation due to incomplete generics type
                // TODO: do a "check pass" later to make sure this won't happen after generics instantiation?
                return 0;
            }
            else
            {
                return int.MaxValue;
            }
        }

        if (castType is StreamsType { Kind: var targetKind }

View on GitHub (pinned to 96fad776d2)