Skip to content

Map/Set's for...of loop doesn't work for class instance field #302

Description

@codehz

What happened

I found Map/Set inside class instance field doesn't work in for...of loop

What you expected

It should works like module level Map/Set do

Minimal reproduction

/**
 * Minimal reproduction for Perry bug:
 * `for...of` loop body is silently skipped when the iterable is a class instance field Map or Set.
 *
 * Tested with: ~/Projects/perry/target/release/perry (latest local build)
 * Reproduces with: perry test_minimal_repro.ts && ./test_minimal_repro
 *
 * Expected output (Bun / Node.js):
 *   class Map size: 2
 *   class Map for...of:
 *     entry 1 -> a
 *     entry 2 -> b       <-- Perry: MISSING
 *   class Set size: 2
 *   class Set for...of:
 *     elem x             <-- Perry: MISSING
 *     elem y             <-- Perry: MISSING
 *   module-level Map (works in Perry):
 *     entry 1 -> x
 *     entry 2 -> y
 *
 * Actual Perry output:
 *   class Map size: 2
 *   class Map for...of:       <-- loop body never executes
 *   class Set size: 2
 *   class Set for...of:       <-- loop body never executes
 *   module-level Map (works in Perry):
 *     entry 1 -> x
 *     entry 2 -> y
 *
 * Notes:
 *   - .size, .get(), .has(), .set(), .add(), .clear() all work correctly on class field Map/Set
 *   - for...of on class field Array works correctly
 *   - for...of on module-level Map/Set works correctly
 *   - Affects both `private` and `public` class fields
 *   - Affects both same-file and imported classes
 */

class Example {
  public classMap: Map<number, string> = new Map();
  public classSet: Set<string> = new Set();

  run(): void {
    this.classMap.set(1, "a");
    this.classMap.set(2, "b");
    this.classSet.add("x");
    this.classSet.add("y");

    // .size works correctly
    console.log("class Map size:", this.classMap.size); // 2 ✓
    console.log("class Map for...of:");
    for (const [k, v] of this.classMap) {
      console.log("  entry", k, "->", v); // Perry: never printed ✗
    }

    console.log("class Set size:", this.classSet.size); // 2 ✓
    console.log("class Set for...of:");
    for (const v of this.classSet) {
      console.log("  elem", v); // Perry: never printed ✗
    }
  }
}

new Example().run();

// Module-level Map/Set: for...of works correctly in Perry
const m = new Map<number, string>([
  [1, "x"],
  [2, "y"],
]);
console.log("module-level Map (works in Perry):");
for (const [k, v] of m) {
  console.log("  entry", k, "->", v); // Perry: works ✓
}

Command you ran:

perry test.ts && ./test

Environment

  • Perry version: 0.5.386
  • Host OS: ArchLinux
  • Target: native
  • Rust toolchain (if building from source): rustc 1.95.0 (59807616e 2026-04-14)
  • Installed via: from source

Diagnostic output

Perry Doctor

Environment Checks
──────────────────
  ✓ perry version: 0.5.386
  ✓ update status: up to date
  ✓ clang (LLVM codegen): clang version 22.1.3
  ✓ system linker (cc): cc (GCC) 15.2.1 20260209
  ✓ runtime library: /home/codehz/Projects/perry/target/release/libperry_runtime.a
  ⚠ project config (perry.toml): not found - run: perry init

All critical checks passed with some warnings.

Anything else

I found it in my ecs demo, it hit infinite loop, so I create a minimal repro it using AI

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regression

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions