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

Editorial: describe behavior for algorithms without a "Return" #2397

Closed
wants to merge 3 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -826,8 +826,10 @@ <h1>Implicit Completion Values</h1>
</emu-alg>
<p>A &ldquo;return&rdquo; statement without a value in an algorithm step means the same thing as:</p>
michaelficarra marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Collaborator

@jmdyck jmdyck Apr 30, 2021

Choose a reason for hiding this comment

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

"If all steps in an abstract operation have been executed without returning..."

Well, not necessarily all steps, because if there are If-else-steps, we might have executed only one of the arms. Maybe "If an abstract operation completes execution without returning ..." ?

Also, it's not just abstract operations, there's at least one syntax-directed operation (ForDeclarationBindingInstantiation) that this applies to. (Or maybe you consider SDOs a subset of AOs, which the spec isn't entirely clear on.)


"~unused~ is a special value which is never directly consumed."

So it might be indirectly consumed? What does that even mean?

Maybe "~unused~ is an ad hoc value that is only used for this purpose."


Also maybe "Operations that return ~unused~ are typically invoked in a Perform step." (I only found one exception, where AsyncGeneratorEnqueue has Let _check_ be AsyncGeneratorValidate(...).)

Copy link
Member

Choose a reason for hiding this comment

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

I'd say "Operations that return ~unused~ must only be invoked in a Perform step.`

Copy link
Collaborator

Choose a reason for hiding this comment

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

So then what about the AsyncGeneratorEnqueue case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@jmdyck

Well, not necessarily all steps, because if there are If-else-steps, we might have executed only one of the arms.

I was modeling the if-else as a single step (with the arms being substeps), but I can see how your interpretation is plausible.

Maybe "... completes execution without returning ..." ?

I'd be OK with that, though I don't love it. Open to other phrasings here.

I originally said something like "if control reaches the end of the algorithm", but I felt that was maybe too confusing, since we normally talk about control-flow of the JS being executed rather than of spec steps.

Also, it's not just abstract operations, there's at least one syntax-directed operation (ForDeclarationBindingInstantiation) that this applies to. (Or maybe you consider SDOs a subset of AOs, which the spec isn't entirely clear on.)

For avoidance of doubt I'll list SDOs explicitly.

So it might be indirectly consumed? What does that even mean?

It means the completion containing it is consumed, or it's stored in a binding within a macro but the binding is not used after the macro, etc. I am content with this phrasing personally; does anyone else want to weigh in?

@ljharb

Operations that return ~unused~ must only be invoked in a Perform step

That's not a guarantee I want to make: it is reasonable to have instances of the pattern

1. Let _result_ be SomeProcedure(whatever).
1. If _result_ is an abrupt completion, then
  1. Do some cleanup. 

Copy link
Member

@ljharb ljharb Apr 30, 2021

Choose a reason for hiding this comment

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

Hmm, that's a good question/point about those cases. I don't have a suggestion.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I was modeling the if-else as a single step (with the arms being substeps),

Indeed, that's how structured programming wants us to think of it, but when each Else has a different step-number, it's harder to suggest that they're not different steps.

Maybe "... completes execution without returning ..." ?

I'd be OK with that, though I don't love it.

Yeah, I'm not thrilled with it either. The problem is, the existing spec gives us almost no terminology/model for the execution of algorithms.

I originally said something like "if control reaches the end of the algorithm", but I felt that was maybe too confusing, since we normally talk about control-flow of the JS being executed rather than of spec steps.

Yeah, I don't see any examples of the spec using "control" in the context of performing spec steps. At least for "execute" there are a handful of examples.

<emu-alg>
1. Return NormalCompletion(*undefined*).
1. Return NormalCompletion(~unused~).
</emu-alg>
<p>In the above step, ~unused~ is a special value which is never directly consumed.</p>
<p>If all steps in an abstract operation have been executed without returning, the abstract operation behaves as though there were an additional step reading "Return." after the existing steps.</p>
<p>Any reference to a Completion Record value that is in a context that does not explicitly require a complete Completion Record value is equivalent to an explicit reference to the [[Value]] field of the Completion Record value unless the Completion Record is an abrupt completion.</p>
</emu-clause>

Expand Down Expand Up @@ -3525,7 +3527,7 @@ <h1>The Completion Record Specification Type</h1>
[[Value]]
</td>
<td>
any ECMAScript language value or ~empty~
any ECMAScript language value, ~empty~, or ~unused~
</td>
<td>
The value that was produced.
Expand Down Expand Up @@ -3782,7 +3784,7 @@ <h1>PutValue ( _V_, _W_ )</h1>
1. [id="step-putvalue-toobject"] Let _baseObj_ be ! ToObject(_V_.[[Base]]).
1. Let _succeeded_ be ? _baseObj_.[[Set]](_V_.[[ReferencedName]], _W_, GetThisValue(_V_)).
1. If _succeeded_ is *false* and _V_.[[Strict]] is *true*, throw a *TypeError* exception.
1. Return.
1. Return *undefined*.
1. Else,
1. Let _base_ be _V_.[[Base]].
1. Assert: _base_ is an Environment Record.
Expand Down Expand Up @@ -39484,8 +39486,7 @@ <h1>AsyncFunctionStart ( _promiseCapability_, _asyncFunctionBody_ )</h1>
1. Push _asyncContext_ onto the execution context stack; _asyncContext_ is now the running execution context.
1. Resume the suspended evaluation of _asyncContext_. Let _result_ be the value returned by the resumed computation.
1. Assert: When we return here, _asyncContext_ has already been removed from the execution context stack and _runningContext_ is the currently running execution context.
1. Assert: _result_ is a normal completion with a value of *undefined*. The possible sources of completion values are Await or, if the async function doesn't await anything, step <emu-xref href="#step-asyncfunctionstart-return-undefined"></emu-xref> above.
1. Return.
1. Assert: _result_ is a normal completion with a value of ~unused~. The possible sources of completion values are Await or, if the async function doesn't await anything, step <emu-xref href="#step-asyncfunctionstart-return-undefined"></emu-xref> above.
</emu-alg>
</emu-clause>
</emu-clause>
Expand Down