# Why do non-strict functions need a separate lexical env for direct eval calls?

**URL:** <https://es.discourse.group/t/why-do-non-strict-functions-need-a-separate-lexical-env-for-direct-eval-calls/857>\
**Category:** I have questions\
**Created:** [July 14, 2021, 9:11am UTC](https://es.discourse.group/t/why-do-non-strict-functions-need-a-separate-lexical-env-for-direct-eval-calls/857 "2021-07-14T09:11:50Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![monica](https://avatars.discourse-cdn.com/v4/letter/m/a183cd/32.png) [@monica](https://es.discourse.group/u/monica)\
**Post date:** [July 14, 2021, 9:11am UTC](https://es.discourse.group/t/why-do-non-strict-functions-need-a-separate-lexical-env-for-direct-eval-calls/857/1 "2021-07-14T09:11:50Z")

</div>

Note 30.b of FunctionDeclarationInstantiation says:

> **Non-strict functions use a separate Environment Record for top-level lexical declarations so that a direct eval can determine whether any var scoped declarations introduced by the eval code conflict with pre-existing top-level lexically scoped declarations.** This is not needed for strict functions because a strict direct eval always places all declarations into a new Environment Record.

I'm not sure I understand why a _separate_ lexical environment (LE) is needed to determine if this top-level-let / eval-var conflict exists. Why can't we keep the caller function's LE and variable environment (VE) the same and then when it comes time to perform the eval call, use `varEnv.HasLexicalDeclaration(name)` (as in [step 3.a.i.1 of EvalDeclarationInstantiation](http://262.ecma-international.org/#sec-evaldeclarationinstantiation)) to resolve any conflict instead of the loop in [step 3.d](http://262.ecma-international.org/#sec-evaldeclarationinstantiation)?

Also, when this new LE is created, why is its `outer` property set to the function's variable environment (see step 30.b of FunctionDeclarationInstantiation)?

---

<div class="post-metadata">

**Author:** ![bakkot](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bakkot/32/22_2.png) [@bakkot](https://es.discourse.group/u/bakkot)\
**Post date:** [July 14, 2021, 4:42pm UTC](https://es.discourse.group/t/why-do-non-strict-functions-need-a-separate-lexical-env-for-direct-eval-calls/857/2 "2021-07-14T16:42:09Z")

</div>

> Why can't we [...] use `varEnv.HasLexicalDeclaration(name)`

`HasLexicalDeclaration` is only a method on global environment records, not arbitrary environment records, so it's not available for use at that point.

> Also, when this new LE is created, why is its `outer` property set to the function's variable environment

What else could it be?

---

<div class="post-metadata">

**Author:** ![monica](https://avatars.discourse-cdn.com/v4/letter/m/a183cd/32.png) [@monica](https://es.discourse.group/u/monica)\
**Post date:** [July 14, 2021, 6:49pm UTC](https://es.discourse.group/t/why-do-non-strict-functions-need-a-separate-lexical-env-for-direct-eval-calls/857/3 "2021-07-14T18:49:42Z")

</div>

Thanks @bakkot, is there a reason why a similar method isn't available for declarative environment records?

> [@bakkot](#):
>
> What else could it be?

I had read in the [ES5 spec](https://262.ecma-international.org/5.1/#sec-10.3) that when an execution context is created, both its LE and VE point to the same environment record (as in [step 31 of ES2021](http://262.ecma-international.org/#sec-functiondeclarationinstantiation)). So I didn't quite understand why a non-strict function's LE must be put in front of its VE in the scope chain in order to be able to resolve top-level-let / eval-var conflicts. Is it simply because declarative environment records don't have a method like `HasLexicalDeclaration` to be able to differentiate between `var` and `let` declarations?

---

<div class="post-metadata">

**Author:** ![bakkot](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bakkot/32/22_2.png) [@bakkot](https://es.discourse.group/u/bakkot)\
**Post date:** [July 14, 2021, 7:17pm UTC](https://es.discourse.group/t/why-do-non-strict-functions-need-a-separate-lexical-env-for-direct-eval-calls/857/4 "2021-07-14T19:17:46Z")

</div>

> [@monica](#):
>
> is there a reason why a similar method isn't available for declarative environment records?

It wouldn't really make sense given how they're structured. Global environment records are actually a wrapper holding two other environment records, whereas regular environment records hold bindings directly and do not differentiate among the different kind of binding they hold. global.HasLexicalDeclaration works by querying the one of the two associated records which holds lexical bindings.

In other words, the only reason `HasLexicalDeclaration` can exist on global records is by having two environment records, and that's what's happening here as well. It's just tracked in a different way, by having two environment records directly associated with a function context, rather than (as with global environment records) having a single environment record which wraps two records.

> So I didn't quite understand why a non-strict function's LE must be put in front of its VE in the scope chain in order to be able to resolve top-level-let / eval-var conflicts.

It's not specifically in order to be able to resolve top-level-let / eval-var conflicts; it's just how environment records work in general: every environment record must have an outer record (except the global environment record at the top of the chain).

When [resolving a binding](https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-getidentifierreference) you first look in the current LexicalEnvironment, and then in its [[OuterEnv]], and then in _its_ [[OuterEnv]], and so on until you reach the top of the chain. So the new lexical environment needs to have the function's variable environment as its outer context to ensure that lookups go through the variable environment.

---

<div class="post-metadata">

**Author:** ![monica](https://avatars.discourse-cdn.com/v4/letter/m/a183cd/32.png) [@monica](https://es.discourse.group/u/monica)\
**Post date:** [July 15, 2021, 12:06am UTC](https://es.discourse.group/t/why-do-non-strict-functions-need-a-separate-lexical-env-for-direct-eval-calls/857/5 "2021-07-15T00:06:34Z")

</div>

I see, since program-level var declarations are added to the object portion of the global environment record while program-level let declarations are added to the declarative portion, `HasLexicalBinding` can identify the latter by just searching the declarative record.

> [@bakkot](#):
>
> it's just how environment records work in general: every environment record must have an outer record (except the global environment record at the top of the chain)

Yes, what I meant was that in the case of non-strict functions, the reason for the divergence between the LE and VE at the context _creation_ phase is to allow us to keep track of top-level lexically scoped declarations and prevent an eval call from hoisting like-named var declarations in the context execution phase.

Prior to this I had incorrectly assumed that the VE and LE could only diverge during the execution phase based on the [ES5](https://262.ecma-international.org/5.1/#sec-10.3) description:

> The value of the VariableEnvironment component never changes while the value of the LexicalEnvironment component **may change during execution of code** within an execution context.

That may have been true in ES5 but only because let and const didn't exist back then. Whereas with their introduction in ES6, the value of the LE may change during the context creation phase as well - is this the correct interpretation?
