Skip to content

language/runtime: enforce strict-mode unresolvable assignment and lexical declaration errors #3571

Closed

Description

@andrewtdiz

Summary

When a real strict directive is present, unresolvable assignments and forbidden declarations throw the same ReferenceError/TypeError Node/Test262 expects; directives that appear after non-directive statements do not retroactively enable strict mode.

Affected Test262 rows

Perry actual behavior from report

  • 12 x runtime-fail: Uncaught exception: Expected a to be thrown but no exception was thrown at all
  • 3 x runtime-fail: Uncaught exception: Expected SameValue(«0», «null») to be true
  • 1 x runtime-fail: Uncaught exception: const binding is strictly immutable Expected a to be thrown but no exception was thrown at all

Node / ECMAScript expected behavior

When a real strict directive is present, unresolvable assignments and forbidden declarations throw the same ReferenceError/TypeError Node/Test262 expects; directives that appear after non-directive statements do not retroactively enable strict mode.

Likely subsystem

strict-mode flag propagation / global and function environment writes

Acceptance criteria

  • Strict function declaration/expression/accessor rows throw when expected and run clean when directive placement is non-prologue.
  • Global strict block/switch declaration negative rows reject instead of running clean.
  • Const assignment throws while let/class/global lexical bindings keep the expected global-object visibility.

Validation

Run the Test262 differential against the affected area and confirm these rows leave the gap report:

scripts/test262_subset.py --root vendor/test262 --dir language/directive-prologue language/global-code --sample-cap 99999 --report /tmp/STRICT_RUNTIME_ERRORS.json

This is Perry language/runtime semantics triage, not a Node core API compatibility issue.

Activity

  1. andrewtdiz commented on Jun 3, 2026

    @andrewtdiz
    ContributorAuthor

    I am taking a focused strict-mode runtime error slice here.

    Planned scope:

    • propagate real strict directive state into function/global execution paths that currently miss strict-only runtime errors;
    • make strict unresolvable assignments throw Node/Test262-shaped errors instead of creating globals or silently succeeding;
    • cover lexical/global declaration error cases local to the same strict-mode environment path if they are reachable without broad parser changes;
    • add focused parity/regression coverage for strict vs non-prologue directive placement.

    Non-goals for this cut: raw directive escape classification already covered by the separate strict-directive PR, reserved/contextual-word parsing, and broad Test262 harness import work.

  2. andrewtdiz commented on Jun 3, 2026

    @andrewtdiz
    ContributorAuthor

    Opened draft PR #4266 for the strict switch lexical-scope slice: #4266

    Scope covered:

    • adds one shared HIR block scope around lowered switch case blocks in both module and function-body lowering paths
    • prevents strict let and function declarations inside switch cases from leaking after the switch
    • preserves var function-scoping through the existing block-scope pop/hoist behavior
    • adds focused Node parity coverage for strict unresolvable assignment, strict block declarations, strict switch declarations, and switch var hoisting

    Validation:

    • npm exec --yes --package=node@26 -- node --experimental-strip-types test-parity/node-suite/globals/strict-runtime-errors.ts
    • CARGO_TARGET_DIR=/tmp/perry-strict-runtime-errors cargo check -p perry-hir
    • PERRY_NO_AUTO_OPTIMIZE=1 CARGO_TARGET_DIR=/tmp/perry-strict-runtime-errors npm exec --yes --package=node@26 -- ./run_parity_tests.sh --suite node-suite --module globals --filter strict-runtime-errors
      • report: /root/perry-worktrees/perry-node-strict-runtime-errors/test-parity/reports/parity_report_20260603_152435.json
    • existing test-files/test_issue_3585_bindings.ts regression recompiled and passed
    • cargo fmt --all -- --check
    • git diff --check
    • ./scripts/check_file_size.sh
  3. andrewtdiz commented on Jun 4, 2026

    @andrewtdiz
    ContributorAuthor

    Current-main validation update: the checked-in strict runtime fixture is green on 2e0551cb9 (2026-06-04) with Node 26.

    Command:

    CARGO_TARGET_DIR=/root/perry-worktrees/perry-node-probe-20260604/target PERRY_NO_AUTO_OPTIMIZE=1 npm exec --yes --package=node@26 -- bash -lc './run_parity_tests.sh --suite node-suite --module globals --filter strict'\n```\n\nResult: 3 parity pass / 0 parity fail / 0 compile fail / 0 skipped, including `globals/strict-runtime-errors`.\nReport: `test-parity/reports/parity_report_20260604_232300.json`.\n\nThe strict runtime/switch-scope slice is merged, and I do not see a remaining checked-in strict-mode runtime failure on current main, so this looks stale-open unless a new reproduction appears.
  4. proggeramlug commented on Jun 13, 2026

    @proggeramlug
    Contributor

    Partial progress update via the TC39 test262 differential (corpus @ pinned 4249661388):

    language/directive-prologue  pass=56  diff=0  (none of this issue's func-decl-*/func-expr-*/get-accsr-*/set-accsr-* rows fail)
    language/global-code         pass=27  diff=0  rt-fail=1 (decl-lex.js)
    

    Resolved (no longer in the failure set): all the directive-prologue/func-decl-*-runtime, func-expr-*-runtime, get-accsr-*-runtime, set-accsr-* rows, plus global-code/block-decl-strict, switch-case-decl-strict, and switch-dflt-decl-strict. Direct strict-mode const reassignment also throws correctly (TypeError: Assignment to constant variable).

    Still failing — 1 row: global-code/decl-lex.js. The remaining assertion is top-level lexical-binding semantics at global scope (a top-level class X {} binding's reassignment doesn't take effect — class binding is mutable: Expected SameValue(1, 5)), i.e. a codegen issue in how top-level let/const/class names are bound at module scope, not the unresolvable-assignment path this issue opened on.

  5. added
    bugConfirmed defect or regression
    parityCompatibility gap with Node.js, ECMAScript, or the supported ecosystem
    triagedMaintainer reviewed; type, scope, and next step are clear
    on Jul 26, 2026
  6. proggeramlug commented on Jul 30, 2026

    @proggeramlug
    Contributor

    This is now fully covered by the merged strict-runtime/switch work and #5853. The last recorded failure, test262 language/global-code/decl-lex.js, is explicitly fixed by the mutable top-level class-binding path in b830bfb, with the restricted-global declaration cases covered in the same regression suite. I reran cargo test -p perry-hir --test issue_5833_global_code_cluster on current main: 6 passed, 0 failed. Closing as fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regressionparityCompatibility gap with Node.js, ECMAScript, or the supported ecosystemtriagedMaintainer reviewed; type, scope, and next step are clear

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions