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

spec: defer statement specification is easily misread #27802

Closed
seebs opened this issue Sep 21, 2018 · 6 comments
Closed

spec: defer statement specification is easily misread #27802

seebs opened this issue Sep 21, 2018 · 6 comments
Labels
FrozenDueToAge NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Milestone

Comments

@seebs
Copy link
Contributor

seebs commented Sep 21, 2018

What version of Go are you using (go version)?

1.11 but NA

Does this issue reproduce with the latest release?

Sure.

What operating system and processor architecture are you using (go env)?

Human brain, some kind of godawful biological monstrosity. "go env" apparently says "i'd like some pizza now".

What did you do?

Tried to explain defer statements to a reasonably competent programmer not familiar with go's spec enough to fully understand the semantics of named return variables.

https://golang.org/ref/spec#Defer_statements

https://play.golang.org/p/qwKgqdXIc4W

(note: I understand and expected this, I'm not saying the language's behavior is wrong)

What did you expect to see?

Slightly clearer spec.

What did you see instead?

The problem here is the spec's phrasing "deferred functions are invoked immediately before the surrounding function returns". Even knowing how it works, I read this as strongly suggesting that the fails() function should work, at least in the case where the return statement is hit; after all, the deferred function should happen immediately before the return.

In fact, deferred functions appear to happen roughly as the function returns. They are clearly after any return statements in the function itself, but they are clearly before stack unwinding. But if I had only the spec, and not experience with the language, I'd expect them to take place before the return, and thus, I'd expect a return foo to end up yielding the value of foo as modified by a deferred function.

Suggested alternative: "immediately after function execution completes, but before returning control to the caller". The example does make the intent more clear, but it doesn't rule out the expectation that returning a named variable would work the same way.

Similarly, the example's "unlock happens before surrounding function returns" could be misleading. Say my return statement uses the locked thing. Does the unlock happening "before" the return make that dangerous? No, but it sounds like it does.

@ALTree ALTree changed the title spec for defer is easily misread spec: defer statement specification is easily misread Sep 21, 2018
@bcmills
Copy link
Contributor

bcmills commented Sep 22, 2018

CC @griesemer

@bcmills bcmills added the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label Sep 22, 2018
@bcmills bcmills added this to the Unplanned milestone Sep 22, 2018
@griesemer
Copy link
Contributor

griesemer commented Sep 22, 2018

My reading of your write-up is that one could reasonably (w/o prior Go knowledge) interpret the sentence "immediately before the surrounding function returns" as "immediately before the surrounding function executes a return statement" - would that be an accurate summary, @seebs ?

I think the crucial part here is that that first part of the sentence (which is actually written slightly differently in the spec) is followed by a clarifying 2nd part of the sentence:

... either because the surrounding function executed a return statement, reached the end of its function body, or because the corresponding goroutine is panicking.

It says pretty clearly that (for instance), the surrounding function already executed a return statement, that is the deferred function execution does happen after the return statement. But perhaps, given the subtleties involved here, this is not clear enough. There's also an example to that effect, but the associated comment is perhaps a bit too subdued.

Leaving this open for now. It's probably worthwhile being a bit more explicit.

@griesemer griesemer self-assigned this Sep 22, 2018
@griesemer griesemer modified the milestones: Unplanned, Go1.12 Sep 22, 2018
@seebs
Copy link
Contributor Author

seebs commented Sep 22, 2018

That's a really good question. I think I would agree that this at least strongly implies that it has to be happening after the return "executed", but I could also see someone inferring that the sequence of events is:

  1. control flow reaches a return statement, and begins executing it.
  2. at this point, it has "executed a return statement" and defers start happening.
  3. defers start happening.
  4. the return actually happens.

in particular, if you haven't run into the "named return values" special syntax, it's pretty easy to think that the term probably refers to "the values given to the return statement, if they are named objects that the deferred function literals could modify".

I did a writeup explaining this (which i eventually plan to do a thing with other than stash it in a repo somewhere) for the benefit of new users:

https://github.com/seebs/notes/blob/master/defer_return.md

The key thing missing, i think, is the distinction between "the return causes the return values to become set" and "the return causes control flow to transfer back to the caller". If you think of those as a single "return" operation, than defer basically has to happen before the return statement changes the values-that-will-be-returned. (Note, "missing" in the mental model people might have when coming to this, I think the spec basically communicates this, it's just easy to have a mental model which makes it plausibly seem to say something else.)

@griesemer
Copy link
Contributor

@seebs Thanks for the clarification; I agree with the key thing that's missing (per the mental model you are referring to). I think this shouldn't be too hard to make crystal clear in the spec.

@seebs
Copy link
Contributor Author

seebs commented Sep 22, 2018

Thanks!

Amusingly, the genesis of this is that someone was being confused by this, and I was going to try to write an explanation of why the spec's adequately clear and he just wasn't reading carefully enough, and then after a couple of tries at this I realized that the spec's wording was making it unduly difficult to express the thing without apparently contradicting the spec, or at least requiring a large digression into why what I was saying didn't contradict it.

@gopherbot
Copy link

Change https://golang.org/cl/136758 mentions this issue: spec: be more precise about moment deferred functions are executed

@golang golang locked and limited conversation to collaborators Sep 24, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made.
Projects
None yet
Development

No branches or pull requests

4 participants