Skip to content
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

Add normative changes from January 2023 plenary #4

Merged
merged 3 commits into from Feb 9, 2023

Conversation

rbuckton
Copy link

@rbuckton rbuckton commented Feb 5, 2023

This adds the normative changes from the January 2023 plenary:

  • Decorators are now also allowed before export
  • Decorators are not allowed both before and after export or export default
  • context.access.get.call(obj) -> context.access.get(obj)
  • context.access.set.call(obj, value) -> context.access.set(obj, value)
  • Adds context.access.has(obj)

Rather than using a production parameter like, this instead uses early errors to ensure a decorator list prior to export is only allowed for class declarations, and to ensure you do not have decorators both before and after export/export default.

@rbuckton
Copy link
Author

rbuckton commented Feb 5, 2023

@pzuraq, we should have these changes reviewed by the editors before merging.

@@ -28653,9 +28661,9 @@ <h2>Syntax</h2>
`export` ExportFromClause FromClause `;`
`export` NamedExports `;`
`export` VariableStatement[~Yield, +Await]
`export` Declaration[~Yield, +Await]
DecoratorList[~Yield, +Await]? `export` Declaration[~Yield, +Await]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be slightly easier to follow the grammar if it was defined like this:

`export` LexicalDeclaration[~Yield, +Await]
`export` HoistableDeclaration[~Yield, +Await]
`export` ClassDeclaration[~Yield, +Await, +Decorators]
DecoratorList[~Yield, +Await]? `export` ClassDeclaration[~Yield, +Await, ~Decorators]

`export` `default` HoistableDeclaration[~Yield, +Await, +Default]
`export` `default` ClassDeclaration[~Yield, +Await, +Decorators, +Default]
DecoratorList[~Yield, +Await]? `export` `default` ClassDeclaration[~Yield, +Await, +Decorators, ~Default]
`export` `default` [lookahead &notin; { `function`, `async` [no LineTerminator here] `function`, `class`, `@` }] AssignmentExpression[+In, ~Yield, +Await] `;`

because the grammar rules are in a single place and you don't have to read both the grammar and the syntactic early errors. However, the end result is effectively the same so avoiding the new production parameter as you proposed is also ok 👍

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I considered that, and that was what I'd implemented originally in https://github.com/tc39/proposal-decorators/pull/95/files. The early error seemed a little easier to reason over given that a Decorators parameter doesn't go very deep.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's also a bug in esmeta regarding multiple constraints on an RHS as would be needed here. That bug has since been fixed, but hasn't been released yet. I think there's still some work to go on the decorators PR before it will pass esmeta anyways, so it doesn't really matter too much either way.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would prefer to have a new UndecoratedClassDeclaration nonterminal which derives what ClassDeclaration currently does in ecma262, and then have ClassDeclaration derive DecoratorList? UndecoratedClassDeclaration. Then you can use UndecoratedClassDeclaration here, no parameters or early errors necessary.

Still, that's an editorial change which can be made later. This change is correct as-is. Only reason to do it now is if it would cause TS or Babel to use a different representation in their ASTs; I know sometimes people like to have the AST shapes stick close to the spec (though this seems like a case where you would not want that).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know about Babel, but TypeScript won't introduce a new AST node to differentiate. We just store decorators as if they were modifiers now (a change we made in TS 4.9 to support decorators-after-export syntax), so export, default, declare, abstract, and decorators all end up in the same bucket. An UndecoratedClassDeclaration production feels more like a workaround for the grammar, much like cover grammars. I don't expect we would really tie any unique semantics to it, and it might even make handling Function.prototype.toString a bit more complicated.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh, I disagree - using productions which precisely express what you actually want to accept is the normal use of a CFG, and grammar parameters and early errors are the workaround for when that's not practical.

Fair point about Function.prototype.toString, but I'd have to actually write it out to tell if it will actually be a problem.

spec.html Outdated Show resolved Hide resolved
@rbuckton
Copy link
Author

rbuckton commented Feb 8, 2023

@syg, @michaelficarra, @bakkot: Would one of you mind reviewing these changes? I understand the baseline spec in tc39#2417 (which this PR is a diff against) probably needs updating as well, but an editor's review on these individual changes would help so this can be merged into the main PR and unblock microsoft/TypeScript#52582 and babel/babel#15405.

Copy link

@bakkot bakkot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple tiny comments, but LGTM otherwise. Feel free to merge once the ? and assert thing are addressed.

1. Let _getter_ be CreateBuiltinFunction(_getterClosure_, 0, *""*, &laquo; &raquo;).
1. Let _getterClosure_ be a new Abstract Closure with parameter (_obj_) that captures _name_ and performs the following steps when called:
1. If _obj_ is not an Object, throw a *TypeError* exception.
1. If _name_ is either a String or a Symbol, return ? Get(_obj_, _name_).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change to accept Symbols looks good, but just to confirm my understanding, that's a bugfix unrelated to the normative changes presented in committee, right?

(I have no problem with it going in with this PR, I just want to make sure I understand why it's here.)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, its a bugfix. Without it, we'd try to do a PrivateGet using a symbol and not a Private Name, since name is already declared to be a String, Symbol, or Private Name in the AO.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I have no problem with it going in with this PR, I just want to make sure I understand why it's here.)

It's a bit of a drive-by bugfix since I needed to rewrite the algorithm steps for both get and set anyways and noticed the oversight.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, its a bugfix. Without it, we'd try to do a PrivateGet using a symbol and not a Private Name, since name is already declared to be a String, Symbol, or Private Name in the AO.

Correction: name could already be a String, Symbol, or Private Name per the name argument to the CreateDecoratorContextObject AO.

<emu-alg>
1. Return the result of evaluating |Declaration|.
1. If |DecoratorList?| is present, then
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
1. If |DecoratorList?| is present, then
1. If |DecoratorList| is present, then

I personally am neutral on whether the ? should be included in general, but right now the prevailing style is that it is not. (See for example ForLoopEvaluation.)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can change that, but there are other cases of |DecoratorList?| in the main PR we'll need to address at some point as well.

Copy link

@bakkot bakkot Feb 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to leave this as-is and address it as part of the main PR if you'd prefer, then. Either way.

1. Return the result of evaluating |Declaration|.
1. If |DecoratorList?| is present, then
1. Assert: |Declaration| is a |ClassDeclaration|.
1. Let _decorators_ be ? DecoratorListEvaluation of |DecoratorList|.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth adding an assert here that |ClassDeclaration| does not have its own |DecoratorList| (and in the export default case as well, of course).

It's prevented by the early errors, but not by the mere structure of the nonterminals, so if you haven't noticed the early errors it seems like this is just discarding that |DecoratorList|.

@@ -28653,9 +28661,9 @@ <h2>Syntax</h2>
`export` ExportFromClause FromClause `;`
`export` NamedExports `;`
`export` VariableStatement[~Yield, +Await]
`export` Declaration[~Yield, +Await]
DecoratorList[~Yield, +Await]? `export` Declaration[~Yield, +Await]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would prefer to have a new UndecoratedClassDeclaration nonterminal which derives what ClassDeclaration currently does in ecma262, and then have ClassDeclaration derive DecoratorList? UndecoratedClassDeclaration. Then you can use UndecoratedClassDeclaration here, no parameters or early errors necessary.

Still, that's an editorial change which can be made later. This change is correct as-is. Only reason to do it now is if it would cause TS or Babel to use a different representation in their ASTs; I know sometimes people like to have the AST shapes stick close to the spec (though this seems like a case where you would not want that).

@rbuckton
Copy link
Author

rbuckton commented Feb 9, 2023

@pzuraq: I've addressed @bakkot's feedback. Can you merge this into your decorators branch so that it is applied to the main PR?

@pzuraq pzuraq merged commit 7fbb55b into pzuraq:decorators Feb 9, 2023
@pzuraq
Copy link
Owner

pzuraq commented Feb 9, 2023

Yes, thanks again for taking this on @rbuckton, super appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants