From c4c348a231a36ea443a59bf3f6e03130556fa7fa Mon Sep 17 00:00:00 2001 From: Andrew Morris Date: Fri, 31 Mar 2023 14:35:34 +1100 Subject: [PATCH] Fix captureShadowed.ts --- .../{failing => passing}/captureShadowed.ts | 3 +- .../src/expression_compiler.rs | 79 +++++++++++++------ 2 files changed, 57 insertions(+), 25 deletions(-) rename inputs/{failing => passing}/captureShadowed.ts (71%) diff --git a/inputs/failing/captureShadowed.ts b/inputs/passing/captureShadowed.ts similarity index 71% rename from inputs/failing/captureShadowed.ts rename to inputs/passing/captureShadowed.ts index 97fadae..21dabcd 100644 --- a/inputs/failing/captureShadowed.ts +++ b/inputs/passing/captureShadowed.ts @@ -1,5 +1,4 @@ -// // test_output! 7 -// Regression: TDZ rules incorrectly applied +// test_output! 7 export default function main() { function foo() { diff --git a/valuescript_compiler/src/expression_compiler.rs b/valuescript_compiler/src/expression_compiler.rs index afbf978..38601f5 100644 --- a/valuescript_compiler/src/expression_compiler.rs +++ b/valuescript_compiler/src/expression_compiler.rs @@ -1121,31 +1121,64 @@ impl<'a> ExpressionCompiler<'a> { let mut bind_values = Array::default(); for cap in captures { - let cap_name = self - .fnc - .scope_analysis - .names - .get(cap) - .expect("Failed to find name"); - - if let Some(tdz_end) = cap_name.tdz_end { - if span.lo() <= tdz_end { + let cap_reg = match self.fnc.lookup_by_name_id(cap) { + Some(v) => match v { + Value::Register(r) => r, + _ => continue, + }, + None => { self.fnc.diagnostics.push(Diagnostic { - level: DiagnosticLevel::Error, - message: match &fn_name { - Some(name) => format!( - "Referencing {} is invalid because it binds {} before its declaration (temporal \ - dead zone)", - name, cap_name.sym, - ), - None => format!( - "Expression is invalid because capturing {} binds its value before its declaration \ - (temporal dead zone)", - cap_name.sym, - ), - }, - span, + level: DiagnosticLevel::InternalError, + message: format!( + "Failed to find capture {:?} for scope {:?}", + cap, self.fnc.owner_id + ), + span: cap.span(), }); + + continue; + } + }; + + // If the capture is a parameter, it's excluded from TDZ checking. This is because TDZ applies + // to let/const, and finding a parameter match means that the capture was already bound + // elsewhere, so it's already been TDZ checked (checking here isn't just duplication, it's can + // produce incorrect results, see captureShadowed.ts). + let mut is_param = false; + + for p in &self.fnc.current.parameters { + if p == &cap_reg { + is_param = true; + } + } + + if !is_param { + let cap_name = self + .fnc + .scope_analysis + .names + .get(cap) + .expect("Failed to find name"); + + if let Some(tdz_end) = cap_name.tdz_end { + if span.lo() <= tdz_end { + self.fnc.diagnostics.push(Diagnostic { + level: DiagnosticLevel::Error, + message: match &fn_name { + Some(name) => format!( + "Referencing {} is invalid because it binds {} before its declaration (temporal \ + dead zone)", + name, cap_name.sym, + ), + None => format!( + "Expression is invalid because capturing {} binds its value before its \ + declaration (temporal dead zone)", + cap_name.sym, + ), + }, + span, + }); + } } }