Is it allowed for the [[Call]] internal method of a function object to return an abrupt completion whose [[Type]] is continue, break, or return? (I'm pretty sure that no [[Call]] algorithm defined in the spec has this behavior, so it would have to be an implementation-defined function.)
Allowing it would mean that a function-call could have the same effect as a continue, break, or return statement, which seems undesirable.
However, I can't find anywhere that it's disallowed. In fact, both 6.1.7.2 Object Internal Methods and Internal Slots and 6.1.7.3 Invariants of the Essential Internal Methods are remarkably quiet about the possibility of any internal method returning an abrupt completion (even though it's clear elsewhere that they all can).
https://tc39.github.io/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist seems to indicate that the result type can either be "return", which produces a NormalCompletion, "abrupt", in which case the abrupt completion is produced, or else the completion record's [[Value]] is extracted and wrapped in a NormalCompletion.
So, even if break or continue completion types were allowed, the result would either be abrupt or normal.
Separately, it also kind of seems like the various EvaluateBody steps:
[9.2.1] seems to indicate that the
resulttype can either be "return", which produces a NormalCompletion, "abrupt", in which case the abrupt completion is produced, or else the completion record's[[Value]]is extracted and wrapped in a NormalCompletion.So, even if
breakorcontinuecompletion types were allowed, the result would either be abrupt or normal.
Not sure what you're saying. break and continue are abrupt, so if _result_ managed to be a completion with [[Type]] = break or continue, that's what [[Call]] would return.
Separately, it also kind of seems like the various
EvaluateBodysteps:
[...]
all explicitly indicate a type of "return" or "abrupt"?
[[Type]] = return is abrupt, so saying "return or abrupt" doesn't make sense. And (as above) abrupt includes break and continue, so it's not clear to me if you're saying that they are allowed or aren't.
I think, as long as you only deal with ordinary objects (and spec-defined exotics), the only abrupt completions that any internal method can return are throw completions. But once you add implementation-defined exotics to the mix, all bets appear to be off, as there's no restriction on what abrupt completions they can return.
cc @allenwb for background.
It seems reasonable to explicitly restrict that, unless there鈥檚 a reason not to.
break and continue statements are the only things in the specification that generate [[Type]]=break|continue completion records. Early error restrictions prevent them from syntactically occurring in any context where their completion values could be returned from a function call.
See, for example:
https://tc39.github.io/ecma262/#sec-continue-statement-static-semantics-early-errors
https://tc39.github.io/ecma262/#sec-break-statement-static-semantics-early-errors
https://tc39.github.io/ecma262/#sec-function-definitions-static-semantics-early-errors
(early errors for _FunctionBody_ : _FunctionStatementList_)
https://tc39.github.io/ecma262/#sec-scripts-static-semantics-early-errors
(early errors for _ScriptBody_ : _StatementList_)
https://tc39.github.io/ecma262/#sec-module-semantics-static-semantics-early-errors
Because of these early errors, a break or continue abrupt completion can never reach https://tc39.github.io/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist so it doesn't have to deal with it.
Remember that this document is only a specification, not an implementation of an extensible virtual machine. As a specification it must be internally consistent but only needs to deal with what is actually allowed by other parts of the specification. Adding normative steps to deal with things that are not otherwise specified would just complicate the specification and place unnecessary normative requirements on implementations.
Note that if a new feature (eg, do expressions) was added that allowed break/continue statements to used in new context then the specification of that feature would also need to either provide additional early error rules or runtime errors to ensure that break/continue abrupt completions did not escape from function bodies. Or they would need to define the full semantics of break/continue propagation across function calls (this is non-trivial. It's sometime I extensively explored for the "Block Lambda" proposals. Based upon that I don't recommend every adding it to the language).
Regarding, implementation defined exotic functions. The spec. does not defined an general extension point for them other than https://tc39.github.io/ecma262/#sec-built-in-function-objects-call-thisargument-argumentslist which is only mandatory for implementation defined exotic functions that correspond to a "built function" . Step 10 of 9.3.1 should probably be elaborated to state that the evaluation of _F_ must not be a break/continue/return abrupt completion. To cover the more general issue of implementation defined exotic functions we might add an similar essential invariant for [[Call]] in https://tc39.github.io/ecma262/#sec-invariants-of-the-essential-internal-methods
Perhaps a non normative note that break/continue can鈥檛 be produced from a function call?
Yes, almost suggested that. But it isn't clear where you would put that note. Really applies to the early errors for FunctionBody, ScriptBody, and ModuleBody rather than BreakStatement and ContinueStatement.
For those who are concerned about exotic implementation defined functions I think adding the [[Call]] invariant is what we should do.
As a specification it must be internally consistent but only needs to deal with what is actually allowed by other parts of the specification.
Indeed. This is a question about what's allowed.
Adding normative steps to deal with things that are not otherwise specified would just complicate the specification and place unnecessary normative requirements on implementations.
Not quite sure what you're responding to there: I don't think anyone was proposing to add normative steps.
Regarding, implementation defined exotic functions. The spec. does not defined an general extension point for them other than [9.3.1] which is only mandatory for implementation defined exotic functions that correspond to a "built function" .
Right, so it doesn't apply to implementation-defined exotic functions that aren't built-ins.
Step 10 of 9.3.1 should probably be elaborated to state that the evaluation of _F_ must not be a break/continue/return abrupt completion.
On the other hand, if we add the invariant, we don't have to elaborate, since the evaluation of _F_ goes to _result_, which is what gets returned by the algorithm, which would thus be subject to the invarant.
To cover the more general issue of implementation defined exotic functions we might add an similar essential invariant for [[Call]] in https://tc39.github.io/ecma262/#sec-invariants-of-the-essential-internal-methods
Okay, so it sounds like the answer to my original question is that, while it's disallowed 'by construction' in the common cases, it's not currently disallowed in general, but should be.
For those who are concerned about exotic implementation defined functions I think adding the [[Call]] invariant is what we should do.
I'll propose some text.