-
Notifications
You must be signed in to change notification settings - Fork 192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lay groundwork for lexical scope & improve errors #972
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Might want to delete the |
rwjblue
reviewed
Sep 27, 2019
54740f0
to
768817b
Compare
First, this PR lays the groundwork for Ember RFC #432, which allows components, helpers and modifiers to be passed around as first-class values. Second, it improves the compiler infrastructure to enable errors to be reported (rather than thrown). Reported errors can now include a source location to aid in reporting the error to the user. *First-class components, helpers, and modifiers* Currently, helpers and modifiers have the syntactic form `{{some-helper some parameters}}`, and `some-helper` is required to be a single literal name. That name is then resolved (globally) into a helper by the environment, and the compiler emits a call to that resolved helper into the compiled program. Notably, the helper must be fully resolved at compile-time, and the following does not work as expected: ``` {{#let x as |hello|}} {{hello world}} {{/let}} ``` Glimmer does not currently have any way to construct a first-class helper, so this gap is not particularly observable. On the other hand: ``` {{#let (component "my-tab") as |tab|}} {{tab some=args}} {{/let}} ``` This *does* work in Ember, but before this commit, it was not directly supported in Glimmer. Instead, Ember does an AST transformation on the above syntax to turn `{{tab some=args}}` in this situation into `{{component tab some=args}}`. The same problems exist in Glimmer VM for modifiers, which also have no first-class representation yet. All of these problems are addressed by changing the wire format (the compilation IR) in Glimmer so that these positions are now just expressions rather strings. Additionally, this commit changes a number of disparate wire format representations into a single "free variable" representation. A free variable is a variable reference that refers to a name that is not declared in the current scope. After this PR, the name `hello` is a free variable in all of the following contexts: - `{{hello}}` - `{{hello.world}}` - `{{hello world}}` - `{{helper hello}}` - `{{#hello}}{{/hello}}` - `<hello />` To maintain compatibility, free variables in the wire format include a context. For example in `{{hello world}}`, the `hello` is a free variable whose context is a helper call, which means that the compiler will try to find a helper named `hello`, and otherwise produce an error. In `{{hello}}`, the context is `AppendSingleId`. That means that the compiler will try to find a helper named `hello`, and if it fails to find one, it will fall back to `{{this.hello}}`. These behaviors are present in compatibility mode, which is the only mode currently implemented. In strict mode (described in in-progress RFC 496), all helpers, components and modifiers must be imported, so free variables have no semantics at all. Finally, while this PR lays the groundwork for RFC 432, it does not implement contextual modifiers or helpers, nor does it implement the necessary compilation work to invoke a contextual helper. However, that work is relatively straight forward now that the groundwork is laid. *Better Errors* In addition, this PR introduces improved error reporting. Previously, the template compiler and opcode compiler threw exceptions if they encountered compilation problems. Now, the compilation stages can produce errors alongside the compilation output, which can be reported to users. For example, if the opcode compiler fails to resolve a helper, it emits an `undefined` value instead of a helper invocation, and records the error. The entire compilation process produces a runnable program as well as a list of encountered errors. In addition to improving error reporting, this PR also allows errors to include a source location, and plumbs the source locations from the parser all the way through to the opcode compiler. This PR makes a first use of that infrastructure by reporting the source location of a helper that could not be resolved when an error occurred. It does not fully eliminate all thrown exceptions. Next steps would include reporting the source location of unresolved modifiers and components, as well as beefing up existing error cases to go through the reporting infrastructure and have source locations. Once that work is done, we should plumb the locations further, so that opcodes could associate failures with source locations, and eventually associating references themselves with their original source locations. To accomplish those goals, we will need a good strategy for avoiding overhead in production.
Replaces amd-name-resolver and broccoli-typescript-compiler forked GitHub dependencies with released versions
73b6654
to
e0a5d2d
Compare
This breaks the IE11 tests
We can use Infinity instead for this purpose
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
First, this PR lays the groundwork for Ember RFC #432, which allows
components, helpers and modifiers to be passed around as first-class
values.
Second, it improves the compiler infrastructure to enable errors to be
reported (rather than thrown). Reported errors can now include a source
location to aid in reporting the error to the user.
First-class components, helpers, and modifiers
Currently, helpers and modifiers have the syntactic form
{{some-helper some parameters}}
, andsome-helper
is required to be a single literalname.
That name is then resolved (globally) into a helper by the environment,
and the compiler emits a call to that resolved helper into the compiled
program. Notably, the helper must be fully resolved at compile-time, and
the following does not work as expected:
Glimmer does not currently have any way to construct a first-class
helper, so this gap is not particularly observable.
On the other hand:
This does work in Ember, but before this commit, it was not directly
supported in Glimmer. Instead, Ember does an AST transformation on the
above syntax to turn
{{tab some=args}}
in this situation into{{component tab some=args}}
.The same problems exist in Glimmer VM for modifiers, which also have no
first-class representation yet.
All of these problems are addressed by changing the wire format (the
compilation IR) in Glimmer so that these positions are now just
expressions rather strings.
Additionally, this commit changes a number of disparate wire format
representations into a single "free variable" representation. A free
variable is a variable reference that refers to a name that is not
declared in the current scope.
After this PR, the name
hello
is a free variable in all of thefollowing contexts:
{{hello}}
{{hello.world}}
{{hello world}}
{{helper hello}}
{{#hello}}{{/hello}}
<hello />
To maintain compatibility, free variables in the wire format include a
context. For example in
{{hello world}}
, thehello
is a freevariable whose context is a helper call, which means that the compiler
will try to find a helper named
hello
, and otherwise produce an error.In
{{hello}}
, the context isAppendSingleId
. That means that thecompiler will try to find a helper named
hello
, and if it fails tofind one, it will fall back to
{{this.hello}}
.These behaviors are present in compatibility mode, which is the only
mode currently implemented. In strict mode (described in in-progress RFC
496), all helpers, components and modifiers must be imported, so free
variables have no semantics at all.
Finally, while this PR lays the groundwork for RFC 432, it does not
implement contextual modifiers or helpers, nor does it implement the
necessary compilation work to invoke a contextual helper. However, that
work is relatively straight forward now that the groundwork is laid.
Better Errors
In addition, this PR introduces improved error reporting.
Previously, the template compiler and opcode compiler threw exceptions
if they encountered compilation problems.
Now, the compilation stages can produce errors alongside the compilation
output, which can be reported to users.
For example, if the opcode compiler fails to resolve a helper, it emits
an
undefined
value instead of a helper invocation, and records theerror. The entire compilation process produces a runnable program as
well as a list of encountered errors.
In addition to improving error reporting, this PR also allows errors to
include a source location, and plumbs the source locations from the
parser all the way through to the opcode compiler.
This PR makes a first use of that infrastructure by reporting the
source location of a helper that could not be resolved when an error
occurred. It does not fully eliminate all thrown exceptions.
Next steps would include reporting the source location of unresolved
modifiers and components, as well as beefing up existing error cases to
go through the reporting infrastructure and have source locations.
Once that work is done, we should plumb the locations further, so that
opcodes could associate failures with source locations, and eventually
associating references themselves with their original source locations.
To accomplish those goals, we will need a good strategy for avoiding
overhead in production.