-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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
cmd/compile: incorrect package initialization order for spec example #22326
Comments
Thanks for the issue. The spec example is correct but cmd/compile is wrong; and I'm pretty sure we have an issue for it, but I can't find it at the moment. go/types also agrees with the spec, and there is a test case for this exact example here). The spec says:
At first, By introducing a little helper function, we can have the code print out the initialization order explicitly: https://play.golang.org/p/yiajBYfoSG . As you can see, the order is clearly wrong. The reason it is wrong for cmd/compile is that the compiler doesn't sort the "ready to initialize" variables in declaration order for some reason. |
Hmm ... something related to go/src/cmd/compile/internal/gc/sinit.go Lines 243 to 254 in 2c1d2e0
|
Yes, somewhere in that file. Haven't investigated. |
@griesemer Cool, no worries. Verbose dive log on chadwhitacre#1, will report back here if I find anything. :) |
@griesemer I've got the test running. It passes, which seems odd. My work plan:
|
For the record, gccgo gets this right. |
@whit537 I'm not sure what you mean with the test passes. If you're referring to |
@griesemer @ianlancetaylor I've been working on writing a test and thought I'd provide an update. I don't find any existing tests for package initialization ordering in the compiler. If I'm missing them by all means please point them out. I'm writing a new // Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gc
import (
"cmd/compile/internal/syntax"
"testing"
)
func TestInitOrder(t *testing.T) {
tests := []struct {
src string
}{
{`package foo; var (bar = 537)`},
}
for i, test := range tests {
parsed, err := syntax.ParseBytes(nil, []byte(test.src), nil, nil, nil, 0)
initfix(parsed)
if test.src != `Greetings, program!` {
t.Errorf("%d: y u no greet? :(", i)
}
}
} I was hoping to use writing a test or three to comprehend the code so I can find the bug, and I'm trying to find a good place in the call chain at which to test even simple package initialization behavior. The call chain I see is: That's my current status. I would be happy for feedback if you have it, otherwise I will keep plugging away as I can find the time. :) |
@whit537 For historical reasons (the compiler used to be written in C, so it couldn't use Go's testing package), the compiler is mostly tested through tests in $GOROOT/test. You can run those tests by cd'ing into $GOROOT/test and running It looks like there are some initialization related tests in there (e.g., $GOROOT/test/*init*.go). |
I've created
Now I'm trying to see a |
Figured out to rebuild Go with Lessee here ... 🤔 |
Here are:
My hunch is that the first traversal of A naive attempt to move the |
This issue was present in Go 1.9, so not a regression nor release critical. Bumping to 1.11. |
Going over this in person with @DeadHeadRussell ... looks like options are:
|
Not obvious that parent is available on Node so (2) might not be a ready option. |
(1) is pretty clearly the longer-term solution, but also pretty clearly a significant refactor. |
sinit.go could use a substantial rewrite. However, that's pretty non-trivial. Last time I looked at adding state to some sinit.go methods, I found it pretty hard to work with, because walk and sinit call each other recursively, and it gets gnarly quickly. Maybe your needs will be more easily met. Also, long term, we'd like to get rid of the current Node AST entirely, so any major refactoring to sinit.go would hopefully move in the direction of simplification and elimination of code, e.g. by shifting it into the SSA backend. That's not really a precise answer to your implicit question, but I hope it helps a little. |
The call graphs in
Here's a sketch of data structures and an algorithm to add to
|
Ftr, my first patch was buggy. I submitted a second patch, which passes the test suite. |
Bump. Does anybody want https://go-review.googlesource.com/c/go/+/156328/? |
Change https://golang.org/cl/169898 mentions this issue: |
Change https://golang.org/cl/169899 mentions this issue: |
This will make it easier for subsequent CLs to track additional state during package initialization scheduling. Passes toolstash-check. Updates #22326. Change-Id: I528792ad34f41a4be52951531eb7525a94c9f350 Reviewed-on: https://go-review.googlesource.com/c/go/+/169898 Run-TryBot: Matthew Dempsky <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
Eliminates global state from sinit.go. Passes toolstash-check. Updates #22326. Change-Id: Ie3cb14bff625baa20134d1488962ab02d24f0c15 Reviewed-on: https://go-review.googlesource.com/c/go/+/169899 Run-TryBot: Matthew Dempsky <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
What's the correct initialization order for this program?
cmd/compile outputs Though notably, changing @griesemer @ianlancetaylor What do you think? |
I agree that |
Change https://golang.org/cl/170062 mentions this issue: |
Regarding the example here, I believe
According to this (specifically the 2nd sentence), for var (
_ = f("_", b)
a = f("a")
b = f("b")
) the next earliest (in decl. order) variable that has no dependencies is Analogously, for the 2nd example (changing Thus I believe both The first sentence in this section is perhaps misleading and should be clarified. I filed #31292 for now. As an aside, whether a variable is named |
After chatting with @griesemer about this, I've changed my mind and agree that |
I'm willing to believe that |
Change https://golang.org/cl/175980 mentions this issue: |
The very first paragraph on "Package initialization" stated that "variables are initialized in declaration order, but after any variables they might depend on". This phrasing was easily misread as "declaration order is the first sorting criteria" and then contradicted what the subsequent paragraphs spelled out in precise detail. Instead, variable initialization proceeds by repeatedly determining a set of ready to initialize variables, and then selecting from that set the variable declared earliest. That is, declaration order is the second sorting criteria. Also, for the purpose of variable initialization, declarations introducing blank (_) variables are considered like any other variables (their initialization expressions may have side-effects and affect initialization order), even though blank identifiers are not "declared". This CL adds clarifying language regarding these two issues and the supporting example. Both gccgo and go/types implement this behavior. cmd/compile has a long-standing issue (#22326). The spec also did not state in which order multiple variables initialized by a single (multi-value) initialization expression are handled. This CL adds a clarifying paragraph: If any such variable is initialized, all that declaration's variables are initialized at the same time. This behavior matches user expectation: We are not expecting to observe partially initialized sets of variables in declarations such as "var a, b, c = f()". It also matches existing cmd/compile and go/types (but not gccgo) behavior. Finally, cmd/compile, gccgo, and go/types produce different initialization orders in (esoteric) cases where hidden (not detected with existing rules) dependencies exist. Added a sentence and example clarifying how much leeway compilers have in those situations. The goal is to preserve the ability to use static initialization while at the same time maintain the relative initialization order of variables with detected dependencies. Fixes #31292. Updates #22326. Change-Id: I0a369abff8cfce27afc975998db875f5c580caa2 Reviewed-on: https://go-review.googlesource.com/c/go/+/175980 Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Matthew Dempsky <[email protected]>
@griesemer, what remains here? |
CL 170062 is ready for review, if it's not too late in the cycle. |
Yes, aware of it. Thanks.
- gri
…On Mon, May 20, 2019, 5:33 PM Matthew Dempsky ***@***.***> wrote:
CL 170062 is ready for review, if it's not too late in the cycle.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#22326?email_source=notifications&email_token=ACBCIT2IELKMAVQMTCWBWWLPWMKKPA5CNFSM4D7X4AKKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODV2ECVQ#issuecomment-494158166>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/ACBCIT3VMZEZYB6SWOUXDWTPWMKKPANCNFSM4D7X4AKA>
.
|
Change https://golang.org/cl/179238 mentions this issue: |
Change https://golang.org/cl/179398 mentions this issue: |
Updates #22326. Change-Id: Ia9173b6eb29b2a4f90f4ba39bf53b6e9b7a6d6bf Reviewed-on: https://go-review.googlesource.com/c/go/+/179398 Run-TryBot: Matthew Dempsky <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
From https://golang.org/ref/spec#Package_initialization:
If Go initializes
b
beforec
, then after initialization I'd expect the value ofb
to be 4 andc
to be 5. However, this test outputsb
as 5 andc
as 4. Swapping theb
andc
declarations doesn't change the output, but swapping the order in the addition in the declaration ofa
does change the output. Does this mean that the initialization order in the example is actuallyd
,c
,b
,a
? And that both LHS and RHS are in scope in the phrase "earliest in declaration order"? Or (more likely) am I missing something about what it means to declare and initialize a variable?P.S. Location in current
master
:go/doc/go_spec.html
Line 6341 in a9afa4e
The text was updated successfully, but these errors were encountered: