stride3d/stride · error · InvalidOperationException

foreach: collection and variable type not matching

Error message

foreach: collection and variable type not matching

What it means

After resolving the foreach collection's element type, ProcessSymbol compares the loop variable's type (variableType.BaseType, the array's element type) with the declared TypeName's resolved type. If they differ, the foreach body would iterate with a mismatched element type, so this InvalidOperationException is thrown.

Solutions

  1. Change the loop variable's declared type to exactly match the array element type.
  2. Use 'var' as the loop variable type so it is inferred from the collection.
  3. Change the collection's element type if the loop variable type is the intended one.

Example fix

// before
float arr[4];
foreach (int v in arr) { ... }
// after
float arr[4];
foreach (float v in arr) { ... }  // or: foreach (var v in arr)
Defensive patterns

Strategy: validation

Validate before calling

if (arrayType.BaseType != declaredVariableType)
    throw new InvalidOperationException("foreach variable type must equal the array element type");

Type guard

bool ElementTypesMatch(ArrayType arr, SymbolType varType) => arr.BaseType == varType;

Try / catch

try { foreachStmt.ProcessSymbol(table); }
catch (InvalidOperationException ex) when (ex.Message == "foreach: collection and variable type not matching") { reportDiagnostic(foreachStmt.Info, ex.Message); }

Prevention

When it happens

Trigger: foreach (Type v in arrayOfOtherType) where the explicit loop-variable type differs from the array's element type, e.g. foreach (int v in floatArray).

Common situations: Implicit-conversion expectations carried over from C# (SDSL does not convert here); arrays retyped after refactoring; generic/link substitution changing the array element type at composition time.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

Thrown at sources/shaders/Stride.Shaders.Parsers/Parsing/SDSL/AST/Statements.Flow.cs:81

    public override void ProcessSymbol(SymbolTable table)
    {
        Collection.ProcessSymbol(table);
        if (!(Collection.Type is PointerType p && p.BaseType is ArrayType arrayType))
            throw new InvalidOperationException("foreach: Array type is expected");


        var variableType = new PointerType(arrayType.BaseType, Specification.StorageClass.Function);
        Variable.Type = variableType;

        if (TypeName.Name != "var")
            TypeName.ProcessSymbol(table, variableType);
        else
            TypeName.Type = arrayType.BaseType;

        // TODO: check conversions
        if (variableType.BaseType != TypeName.Type)
            throw new InvalidOperationException("foreach: collection and variable type not matching");

        table.Push();
        var variableSymbol = new Symbol(new(Variable.Name, SymbolKind.Variable), Variable.Type, 0, OwnerType: table.CurrentShader);
        table.CurrentFrame.Add(Variable.Name, variableSymbol);
        Body.ProcessSymbol(table);
        SymbolFrame = table.Pop();
    }
    public override void Compile(SymbolTable table, CompilerUnit compiler)
    {
        var (builder, context) = compiler;

        var collection = Collection.Compile(table, compiler);
        if (!(Collection.Type is PointerType p && p.BaseType is ArrayType arrayType))
            throw new InvalidOperationException("foreach: Array type is expected");

        var variableType = new PointerType(arrayType.BaseType, Specification.StorageClass.Function);

        // Since foreach need to be processed and expanded later, we use custom opcode

View on GitHub (pinned to 96fad776d2)