FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Implement `for...of` and `for await...of` loops by cknitt · Pull Request #7887 · rescript-lang/rescript · GitHub

Implement for...of and for await...of loops - #7887

Merged
cknitt merged 3 commits into
masterfrom
for-of
Apr 19, 2026
Merged

Implement for...of and for await...of loops#7887
cknitt merged 3 commits into
masterfrom
for-of

Conversation

cknitt commented Sep 13, 2025
edited
Loading

Copy link
Copy Markdown
Member

Summary

This PR adds native for...of and for await...of loop support to ReScript across the full compiler pipeline.

It introduces parser, AST, typedtree, lambda IR, and JS backend support for both loop forms, preserves break / continue semantics inside nested control-flow, and adds syntax, type-error, analysis, and end-to-end test coverage for the new feature.

What changed

  • Add parsing and printing support for:
    • for item of items { ... }
    • for await item of items { ... }
  • Extend the parsetree, typedtree, lambda IR, and JS IR with dedicated for...of / for await...of nodes instead of lowering them too early.
  • Type-check for...of against iterable<'a> with a convenience fallback for raw array<'a> values, and type-check for await...of against asyncIterable<'a>.
  • Restrict loop bindings to variables or _, and report a targeted error for unsupported destructuring patterns on the left-hand side.
  • Compile the new loop forms to native JavaScript for...of and for await...of statements.
  • Preserve labeled loop behavior so break and continue still target the enclosing loop correctly, including when emitted through nested JS switch statements.
  • Update reanalyze and dependency traversal so the new loop forms participate correctly in dead-code and side-effect analysis.

Coverage

  • Syntax tests for parsing, printing, AST mapping, and loop-control formatting.
  • Build tests for type errors, including non-iterables, strings, invalid async iteration targets, and element type mismatches.
  • Integration tests covering:
    • basic iteration
    • empty and single-element loops
    • wildcard and unused bindings
    • shadowing inside loop bodies
    • break / continue in plain loops and nested switches
    • realistic async iteration scenarios
  • Analysis tests covering dead-code handling for for...of and for await...of.

cknitt marked this pull request as draft September 13, 2025 06:56
Comment thread compiler/ml/ast_mapper_to0.ml Outdated

pkg-pr-new Bot commented Sep 13, 2025
edited
Loading

Copy link
Copy Markdown

Open in StackBlitz

rescript

npm i https://pkg.pr.new/rescript@7887

@rescript/darwin-arm64

npm i https://pkg.pr.new/@rescript/darwin-arm64@7887

@rescript/darwin-x64

npm i https://pkg.pr.new/@rescript/darwin-x64@7887

@rescript/linux-arm64

npm i https://pkg.pr.new/@rescript/linux-arm64@7887

@rescript/linux-x64

npm i https://pkg.pr.new/@rescript/linux-x64@7887

@rescript/runtime

npm i https://pkg.pr.new/@rescript/runtime@7887

@rescript/win32-x64

npm i https://pkg.pr.new/@rescript/win32-x64@7887

commit: ee5708e

cknitt requested a review from cristianoc September 13, 2025 12:51
cknitt changed the title Very basic implementation of for..of loops [Poc] for..of loops Sep 17, 2025
cknitt changed the title [Poc] for..of loops [PoC] for..of loops Sep 17, 2025
cknitt force-pushed the for-of branch 3 times, most recently from 77f91d8 to 8b779af Compare September 18, 2025 19:07

Copy link
Copy Markdown
Collaborator

The big questions were language design related ones, rather than implementation.
There are many ways to go about this.
@cometkim still got some design notes from the retreat? This could be the right time to do some more text-based discussion before the code-based one.

cometkim commented Sep 19, 2025
edited
Loading

Copy link
Copy Markdown
Member

Frankly, importing the JavaScript for-of syntax as-is doesn't sound all that appealing. It's not much different from embedding %raw.

Here are my thoughts on designing for loops:

  1. Since iterators are essential in JavaScript, we need to define iterator semantics before design the synyax. The syntax follows.
  2. Without control flow support, the for-of syntax itself isn't very useful. With good control semantics, the simple iterator protocol and even the MoonBit style loop syntax become useful.
  3. The current for x into y syntax is valid for index-based access, but the JS' for-of doesn't. To combine the benefits of both style, generator (for sequencing) support will be needed. Zig's for loop design is a good example.
  4. Are for statements enough? To maintain a robust type-based approach, it would be better to design them based on expressions.

Copy link
Copy Markdown
Member

@cometkim still got some design notes from the retreat?

https://github.com/JonoPrest/iterator-rescript/blob/retreat-note/RETREAT_NOTE.md

On the last retreat, we only discussed iterator semantics and simulating break/continue; we also need to deal with the early-return or jump (for escaping nested loops) semantics.

Copy link
Copy Markdown
Collaborator

Early return keeps on coming up as a topic.
Perhaps it makes sense to explore that as a prerequisite to this.

cknitt commented Sep 19, 2025

Copy link
Copy Markdown
Member Author

For me, the for..of feature as implemented here would already be useful in and of itself because it

  1. allows the usage of await like in this example from the tests which would not be possible using arr->Array.forEach(...)
let asyncProcess = async () => {
  let results = []
  for item of arr {
    let result = await processData(item)
    results->Array.push(result)
  }
  results
}
  1. is more concise
for item for arr {
# vs.
arr->Array.forEach(item =>
  1. is more performant

  2. Increases our coverage of JS features that JS developer may expect to have

I agree that early return would be nice to have, but not sure if we should tie this to for..of.

I also agree that it would be a good idea to look into how iterators should work.
OTOH having this for arrays would be a low-hanging fruit that could hopefully be extended to iterators later.

Copy link
Copy Markdown
Member

OTOH having this for arrays would be a low-hanging fruit that could hopefully be extended to iterators later.

This is already a dangerous assumption. for-of loops are all about iterators, not arrays.

It should be designed to interact with iterator types. When it works with array types, it leads users to copy arbitrary iterators into arrays unnecessarily.

cknitt commented Apr 17, 2026

Copy link
Copy Markdown
Member Author

@codex review

Comment thread tests/tests/src/test_for_of.mjs Fixed
Comment thread tests/tests/src/test_for_of.mjs Fixed

chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2a96c795ce

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread compiler/core/js_dump.ml Outdated
Comment thread compiler/syntax/src/res_core.ml Outdated
cknitt changed the title [PoC] for..of loops Implement for...of and for await...of loops Apr 18, 2026

cknitt commented Apr 18, 2026

Copy link
Copy Markdown
Member Author

It should be designed to interact with iterator types. When it works with array types, it leads users to copy arbitrary iterators into arrays unnecessarily.

Done! 😄

cknitt commented Apr 18, 2026

Copy link
Copy Markdown
Member Author

@codex review

chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4389e9c1c4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread compiler/core/js_analyzer.ml Outdated
Comment on lines +138 to +139
| Int_switch _ | String_switch _ | ForRange _ | ForOf _ | ForAwaitOf _
| If _ | While _ | Block _ | Return _ | Try _ ->

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Treat iterator loops as side-effecting in JS analyzer

no_side_effect_obj.statement now classifies ForOf and ForAwaitOf via structural traversal, so loops with a pure-looking iterable expression/body are considered removable. Unlike ForRange, these loops invoke iterator protocol methods (Symbol.iterator/next or async variants), which can execute arbitrary user code even when the iterable is just an identifier; this feeds into js_shake's statement pruning and can incorrectly drop runtime-visible effects.

Useful? React with 👍 / 👎.

Comment thread analysis/reanalyze/src/SideEffects.ml Outdated
Comment on lines +65 to +68
| Texp_for_of (_id, _pat, e1, e2) ->
e1 |> exprNoSideEffects && e2 |> exprNoSideEffects
| Texp_for_await_of (_id, _pat, e1, e2) ->
e1 |> exprNoSideEffects && e2 |> exprNoSideEffects

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Mark iterator loops effectful in reanalyze side-effects

exprNoSideEffects now returns true for Texp_for_of/Texp_for_await_of when only the loop expression/body look pure, but iteration itself can run effectful iterator callbacks on custom iterables. Since SideEffects.checkExpr is used to set declaration side-effect metadata in dead-value reporting, this can misclassify effectful values as effect-free and produce incorrect dead-code diagnostics.

Useful? React with 👍 / 👎.

cknitt commented Apr 18, 2026

Copy link
Copy Markdown
Member Author

@codex review

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

cknitt marked this pull request as ready for review April 18, 2026 17:27
cknitt enabled auto-merge (squash) April 19, 2026 12:22
cknitt merged commit f976093 into master Apr 19, 2026
31 checks passed
cknitt deleted the for-of branch April 19, 2026 12:38
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants


Back | FazBrowse Home | New Git URL