-
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: diagnose unused variable even in closure #3059
Comments
Labels changed: added priority-later, removed priority-triage. Owner changed to builder@golang.org. Status changed to Accepted. |
Issue #6414 has been merged into this issue. |
I ran into this problem today when code that was compiling fine under cmd/gc was rejected during godoc's pointer analysis by go/types. It would be nice to have this fixed so that I didn't have to worry and find all the spots we do this in our code base and keep track of if it's happening during code review. |
…w gccgo gccgo (in xenial at least) is stricter than gc when it comes to a certain class of declared but not used variable, see: golang/go#6415 golang/go#3059
fix api/metricsadder & apiserver/environment test compilation with ne… …w gccgo gccgo (in xenial at least) is stricter than gc when it comes to a certain class of declared but not used variable, see: golang/go#6415 golang/go#3059
Change https://golang.org/cl/121455 mentions this issue: |
Since Go1.10, go test runs vet on the tests before executing them. Moreover, the vet tool typechecks the package under analysis with go/types before running. In Go1.10, a typechecking failure just caused a warning to be printed. In Go1.11, a typechecking failure will cause vet to exit with a fatal error (see Issue #21287). This means that starting with Go1.11, tests that don't typecheck will fail immediately. This would not normally be an issue, since a test that doesn't typecheck shouldn't even compile, and it should already be broken. Unfortunately, there's a bug in gc that makes it accept programs with unused variables inside a closure (Issue #3059). This means that a test with an unused variable inside a closure, that compiled and passed in Go1.10, will fail in the typechecking step of vet starting with Go1.11. Explain this in the 1.11 release notes. Fixes #26109 Change-Id: I970c1033ab6bc985d8c64bd24f56e854af155f96 Reviewed-on: https://go-review.googlesource.com/121455 Reviewed-by: Ian Lance Taylor <iant@golang.org>
Without having fully dug into this I am hypothesizing this might just be a consequence of us taking the address of higher scope variables to make a pointer then technically "assigned" to it var i int
*(&i) = 1 thus when used in the closure got rewritten as var i int
go func(i *int) {
*i = 1
}() which if true would then mean that a fix for this would involve specially marking as used scoped-by-manually-address-of'd variables that were previously just values in their original scope
Kindly paging @griesemer @randall77 @mdempsky for discussion and perhaps to help me check my hypothesis and then I can mail out a fix if plausible. |
Yes, we're effectively doing |
It needs to be done independently of (the implementation detail) whether an address was taken. go/types does this right, but it also does it earlier, during type-checking. Not urgent. |
It's not urgent, but it is an ongoing pain point, because different compilers behave differently. That causes people to write code that works with one compiler and complain when it fails with a different one. So it really ought to be fixed. |
func (s *AnaServer) VisualizationEvent(ctx context.Context, in *pb.VisualizationEventReq) (*pb.VisualizationEventRes, error) {
...
internal_response := &ana.VisualizationEventRes{}
...
if err := customcopier.CopyRecommendedDashPageReq(request.DashPage, in.GetDashPage()); err != nil {
return nil, err
}
if err := s.AnaRpc(func(client ana.GateClient) error {
internal_response, err = client.VisualizationEvent(ctx, request)
return err
}); err != nil {
return nil, err
}
return &pb.VisualizationEventRes{}, nil
} |
Change https://golang.org/cl/238317 mentions this issue: |
Change https://golang.org/cl/238538 mentions this issue: |
Change https://golang.org/cl/238537 mentions this issue: |
I have CLs uploaded to fix this for Go 1.16 (CL 238537, CL 238317). @ianlancetaylor points out this might break programs that currently build, and so maybe we should only apply impose the new, stricter behavior when gccgo, go/types, and cmd/vet all diagnose the original test case as an error. I'm hopeful that we can avoid introducing conditional behavior in cmd/compile. But if we need to make it conditional, I think that's reasonable. Relatedly, if all major Go compilers/type-checkers now require all declared variables to be used, we should consider changing the "implementation restriction" into a plain restriction (like how directly imported packages have to be used). |
Any comments on whether the stricter behavior should be decided by |
If my program pulls in third party packages as many programs do, and those third party packages don't use vet, then it's possible that my program will suddenly stop building if we make this change, and that the only way for me to fix the build will be to edit third party packages. That seems undesirable. Therefore, unfortunately, I think we do have to condition this on a language flag. In effect I think we are removing a language feature as described at https://go.googlesource.com/proposal/+/refs/heads/master/design/28221-go2-transitions.md#language-removals, the language feature in this case being something like "the ability to only set a variable and never use it as long as one of the sets is in a closure." Since we are removing a feature, we should use a language flag. Sorry. Just my opinion, of course, happy to hear other opinions. |
I believe it's even more restrictive than that in practice. Test setup w/ package
Here
Pedantically, the general language feature ("ability to only set a variable and never use it") is already optional, and not supported by most implementations (including cmd/compile, most of the time). I think that distinguishes it from integer-to-string conversions like discussed in that section, as those conversions are a mandatory feature. I would argue the example from https://go.googlesource.com/proposal/+/refs/heads/master/design/28221-go2-transitions.md#language-redefinitions is more applicable: "For example, in Go 1.1 the size of the type int on 64-bit hosts changed from 32 bits to 64 bits. This change was relatively harmless, as the language does not specify the exact size of int. Potentially, though, some Go 1.0 programs continued to compile with Go 1.1 but stopped working." Similar to not specifying the size of |
Thanks for looking into it. I'll let someone else adjudicate. |
I think I'd rather use the go1.16 flag. There's no point in breaking people's programs unnecessarily. Unlike, say, the |
We're running into this on dev.typeparams, because the race detector has some test code that only compiles with cmd/compile because of this bug. (The tests are obviously trivially fixable with just three lines.) I'm still of the opinion that 0 real-world programs depend on this today, because of the We routinely fix compiler bugs without guaranteeing backwards compatibility. For example, this program builds with Go 1.16 and prints "1", due to a compiler bug that fails to detect overflow from the unary If we need a language flag to control taking away the "ability to only set a variable and never use it as long as one of the sets is in a closure" feature, do we need one to control taking away the "ability to construct 513-bit integers by using bitwise complement to bypass overflow detection" feature too? If #23116 hadn't been fixed until after the introduction of What about #28085? If gccgo starts rejecting case 3 to match cmd/compile and go/types, would it continue supporting the "ability to use duplicate constant values in interface-typed switch statements" feature when compiled with Or order of evaluation? This program prints 0 with cmd/compile. If we were to modify SSA construction so it instead prints 1 like gccgo, will we continue supporting the "slice expressions are handled like function calls for order-of-evaluation purposes" feature for backwards compatibility? Where does it end? I don't think we should be bound to maintain particular, unspecified behavior. I think we specify expected and allowable language behavior for a reason. |
The program below relies on cmd/compile's historical "ability to assume pointers to zero-size objects allocated in and returned by functions that contain for loops will always compare equal" feature, which has been supported since at least Go 1.1. However, with Go 1.16 beta 1, even with
Should we file a release-blocker issue because Go 1.16 will break programs that depend on this behavior? I argue no. The Go spec says "Pointers to distinct zero-size variables may or may not be equal." and consistent with that, we've allowed whether any particular such comparison compares equal or not to change over time. And notably, this change is silent, arbitrary, and largely unpredictable. It's also a recurring source of user issue reports (e.g., #43755 was filed just 4 days ago; see https://github.com/golang/go/issues?q=is%3Aissue+%22zero-size+variables%22 for past instances). There's not even a vet warning that these comparisons are unspecified. Meanwhile, the Go spec also says "A compiler may make it illegal to declare a variable inside a function body if the variable is never used." And to contrast with the above, presumably acceptable change in pointer-comparison behavior, the change in question for this issue would align cmd/compile's behavior with gccgo and go/types' existing, long-term behavior (i.e., wholly predictable and understandable), and programs that are impacted (whose existence I still doubt) will fail loudly rather than silently. |
Change https://golang.org/cl/325030 mentions this issue: |
types2 correctly distinguishes variable assignment from use even within function literals. Whatever the outcome of #3059, the test cases in runtime/race need to be fixed to accomodate that. Change-Id: Ibe3547f07b681ff41225caabaf050872a48c98d1 Reviewed-on: https://go-review.googlesource.com/c/go/+/325030 Trust: Matthew Dempsky <mdempsky@google.com> Run-TryBot: Matthew Dempsky <mdempsky@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
The declaration `var err` is unused and it causes some static analysis tools (go guru) to throw an error on this repository. I'm not sure what ought to be done here, but consider this a start. In fact, this ought to be a compile time error. See this issue for the go runtime golang/go#3059
Since switching to the types2 type checker in Go 1.18, cmd/compile unconditionally rejects the sample code with "i declared but not used". I think this issue is fixed. |
The text was updated successfully, but these errors were encountered: