Skip to content

refactor(semantic): classify redeclared functions without reading their AST node#22934

Closed
Boshen wants to merge 1 commit into
mainfrom
refactor/decouple-function-redeclaration-from-nodes
Closed

refactor(semantic): classify redeclared functions without reading their AST node#22934
Boshen wants to merge 1 commit into
mainfrom
refactor/decouple-function-redeclaration-from-nodes

Conversation

@Boshen

@Boshen Boshen commented Jun 3, 2026

Copy link
Copy Markdown
Member

What

check_function_redeclaration (the Annex B.3.3 legacy duplicate-function rule) distinguished a plain FunctionDeclaration from an async/generator one by reading the previous declaration's AST node through its stored NodeId:

let prev_function = ctx.nodes.kind(prev.declaration).as_function();
if prev_function.is_some_and(|f| !(f.r#async || f.generator)) { return; }

That node is a popped sibling (not an ancestor of the current node), so it's the last build-time read that would prevent making AST node storage optional.

How

The async/generator shape is now recorded at bind time, where the node is current and free to read off the Function (self.r#async || self.generator):

  • Function::bind passes it down via a new declare_function_symbol.
  • Each Redeclaration record carries is_async_or_generator. When the first declaration's record is reconstructed on the first redeclaration, its value comes from build-only SemanticBuilder scratch (an IndexVec<SymbolId, bool> populated at symbol creation and discarded after the build) — not from the node.
  • check_function_redeclaration reads prev.flags.contains(Function) && !prev.is_async_or_generator — no node access.

Notes

  • The scratch is bind-time-only: it never reaches the transformer and isn't persisted on Scoping, so there's nothing to keep in sync — no SymbolFlags bit and no transform_checker mask are needed (redeclarations are compared by span only).
  • Behaviour is unchanged — conformance is zero snapshot diff.
  • check_redeclaration's named-function-expression read is intentionally left on the node: that node is the enclosing function (an ancestor), so it stays reachable in parent-pointer mode.

Prerequisite for making AST node storage optional (#22859).

🤖 Generated with Claude Code

@Boshen Boshen requested a review from Dunqing as a code owner June 3, 2026 08:19
@github-actions github-actions Bot added the A-semantic Area - Semantic label Jun 3, 2026
@Boshen Boshen marked this pull request as draft June 3, 2026 08:21
@codspeed-hq

codspeed-hq Bot commented Jun 3, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 57 untouched benchmarks
⏩ 9 skipped benchmarks1


Comparing refactor/decouple-function-redeclaration-from-nodes (8c90905) with main (93f4494)

Open in CodSpeed

Footnotes

  1. 9 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

…ir AST node

`check_function_redeclaration` distinguished a plain `FunctionDeclaration`
from an `async`/generator one (Annex B.3.3) by reading the previous
declaration's AST node via its stored `NodeId`. That node is a popped sibling,
so the read is incompatible with making AST node storage optional.

Record the `async`/generator shape at bind time instead:

- `Function::bind` passes it down (it already holds the node).
- New `Redeclaration` records carry it; the first declaration's value is held
  in build-only `SemanticBuilder` scratch (discarded after the build) and read
  when its record is reconstructed.
- The check reads `prev.is_async_or_generator`.

No `SymbolFlags` change and no `transform_checker` mask are needed:
redeclarations are compared by span only and the scratch never reaches the
transformer. Conformance is unchanged (zero snapshot diff).
@overlookmotel

Copy link
Copy Markdown
Member

#22972 is an alternative version of the same idea, but IMO simpler and more performant.

@graphite-app graphite-app Bot closed this in 594ce58 Jun 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-semantic Area - Semantic

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants