Repository navigation
language/runtime: enforce strict-mode unresolvable assignment and lexical declaration errors #3571
Description
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
- Button state does not update in Web/Wasm builds #104 language/directive-prologue/func-decl-final-runtime.js (runtime-fail: Uncaught exception: Expected SameValue(«0», «null») to be true)
- Add watchOS compile target (Digital Crown input) #105 language/directive-prologue/func-decl-inside-func-decl-runtime.js (runtime-fail: Uncaught exception: Expected a to be thrown but no exception was thrown at all)
- Add --features watchos-game-loop (Metal-surface apps on watchOS) #106 language/directive-prologue/func-decl-no-semi-runtime.js (runtime-fail: Uncaught exception: Expected a to be thrown but no exception was thrown at all)
- find_native_library target_key missing watchos mapping #107 language/directive-prologue/func-decl-not-first-runtime.js (runtime-fail: Uncaught exception: Expected SameValue(«0», «null») to be true)
- 【BUG】In the windows platform,
console.logcannot output in terminal! #108 language/directive-prologue/func-decl-runtime.js (runtime-fail: Uncaught exception: Expected a to be thrown but no exception was thrown at all) - Bug:
animateOpacityhas no effect on Web/WASM target #109 language/directive-prologue/func-expr-inside-func-decl-runtime.js (runtime-fail: Uncaught exception: Expected a to be thrown but no exception was thrown at all) - First experience: 0 for 4 examples broken – website examples must work #110 language/directive-prologue/func-expr-no-semi-runtime.js (runtime-fail: Uncaught exception: Expected a to be thrown but no exception was thrown at all)
- Error compiling for Windows- expression UrlSearchParamsGet not yet supported #111 language/directive-prologue/func-expr-runtime.js (runtime-fail: Uncaught exception: Expected a to be thrown but no exception was thrown at all)
- Supports WebGPU or WebGL or wgpu? #112 language/directive-prologue/get-accsr-inside-func-expr-runtime.js (runtime-fail: Uncaught exception: Expected a to be thrown but no exception was thrown at all)
- Support HarmonyOS #113 language/directive-prologue/get-accsr-not-first-runtime.js (runtime-fail: Uncaught exception: Expected SameValue(«0», «null») to be true)
- Styling example code is not working in windows #114 language/directive-prologue/get-accsr-runtime.js (runtime-fail: Uncaught exception: Expected a to be thrown but no exception was thrown at all)
- Provide an AI tool that can quickly check and convert npm packages into packages compatible with Perry? #115 language/directive-prologue/set-accsr-inside-func-expr-runtime.js (runtime-fail: Uncaught exception: Expected a to be thrown but no exception was thrown at all)
- ci: capture compile stderr in parity + compile-smoke jobs #239 language/global-code/block-decl-strict.js (runtime-fail: Uncaught exception: Expected a to be thrown but no exception was thrown at all)
- Cross-module interface dispatch: methods called on an interface-typed parameter return undefined unless the consumer module value-imports the impl class #240 language/global-code/decl-lex.js (runtime-fail: Uncaught exception:
constbinding is strictly immutable Expected a to be thrown but no exception was thrown at all) - fix(codegen): declare @llvm.assume intrinsic in IR module prelude #241 language/global-code/switch-case-decl-strict.js (runtime-fail: Uncaught exception: Expected a to be thrown but no exception was thrown at all)
- Android: geisterhand apply_style dispatcher (#185 Phase D follow-up) #243 language/global-code/switch-dflt-decl-strict.js (runtime-fail: Uncaught exception: Expected a to be thrown but no exception was thrown at all)
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:
constbinding 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.jsonThis is Perry language/runtime semantics triage, not a Node core API compatibility issue.
Activity
andrewtdiz commentedon Jun 3, 2026
I am taking a focused strict-mode runtime error slice here.
Planned scope:
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.
andrewtdiz commentedon Jun 3, 2026
Opened draft PR #4266 for the strict switch lexical-scope slice: #4266
Scope covered:
switchcase blocks in both module and function-body lowering pathsletand function declarations inside switch cases from leaking after the switchvarfunction-scoping through the existing block-scope pop/hoist behaviorvarhoistingValidation:
npm exec --yes --package=node@26 -- node --experimental-strip-types test-parity/node-suite/globals/strict-runtime-errors.tsCARGO_TARGET_DIR=/tmp/perry-strict-runtime-errors cargo check -p perry-hirPERRY_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/root/perry-worktrees/perry-node-strict-runtime-errors/test-parity/reports/parity_report_20260603_152435.jsontest-files/test_issue_3585_bindings.tsregression recompiled and passedcargo fmt --all -- --checkgit diff --check./scripts/check_file_size.shandrewtdiz commentedon Jun 4, 2026
Current-main validation update: the checked-in strict runtime fixture is green on
2e0551cb9(2026-06-04) with Node 26.Command:
proggeramlug commentedon Jun 13, 2026
Partial progress update via the TC39 test262 differential (corpus @ pinned 4249661388):
Resolved (no longer in the failure set): all the
directive-prologue/func-decl-*-runtime,func-expr-*-runtime,get-accsr-*-runtime,set-accsr-*rows, plusglobal-code/block-decl-strict,switch-case-decl-strict, andswitch-dflt-decl-strict. Direct strict-modeconstreassignment 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-levelclass X {}binding's reassignment doesn't take effect —class binding is mutable: Expected SameValue(1, 5)), i.e. a codegen issue in how top-levellet/const/classnames are bound at module scope, not the unresolvable-assignment path this issue opened on.proggeramlug commentedon Jul 30, 2026
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.