-
Notifications
You must be signed in to change notification settings - Fork 18k
cmd/compile: loopvar doesn't trigger nocopy detection #66070
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
Comments
|
CC @dr2chase |
In the following program, package main
import "strings"
//go:noinline
func f(b *strings.Builder) {
println(b.String())
}
func foo() {
for b := (strings.Builder{}); b.Len() < 2; {
b.WriteByte('!')
f(&b)
}
}
var p *strings.Builder
//go:noinline
func g(b *strings.Builder) {
println(b.String())
p = b
}
func bar() {
for b := (strings.Builder{}); b.Len() < 2; {
b.WriteByte('!')
g(&b)
}
}
func main() {
println("------------ foo:")
foo() // not panic
println("------------ bar:")
bar() // panic
} |
The panic I get is
Is that what you get? You could have provided this information in your bug report and it would have been helpful. |
Yes, we should get it for both functions ( |
An example to extend one of my previous comment: package main
import (
"fmt"
"strings"
)
//go:noinline
func globalDebugProcess(pb *strings.Builder) {
// do nothing
}
func foo() string {
for b, i := (strings.Builder{}), byte('a'); ; i++ {
b.WriteByte(i)
globalDebugProcess(&b)
if i == 'z' {
return b.String()
}
}
}
func bar(callback func(*strings.Builder)) string {
for b, i := (strings.Builder{}), byte('a'); ; i++ {
b.WriteByte(i)
callback(&b) // <-- difference here
if i == 'z' {
return b.String()
}
}
}
func main() {
localDebugProcess := func(pb *strings.Builder) {
// do nothing
}
fmt.Println("foo:", foo())
fmt.Println(" foo done.")
fmt.Println("bar:", bar(localDebugProcess))
fmt.Println(" bar done.")
} |
BTW, such cases can never happen in JS world. So this is a fundamental difference from JS. |
Is this causing problems in a real program? Also, in your examples, it is helpful if you provide information about what happened when you ran it. Just because I observe a problem when I run it, does not mean I observe the same problem. |
? The problem is just there. I just expected the behavior doesn't change when adding/removing a no-op line. The example is real. |
I think this is a side effect of #47276. Without the |
That is: because The compiler does not perform that optimization in the |
(To be clear: fixing the bug in |
The original proposal explicitly acknowledged that there would be corner-case programs that might be affected in one way or another. That's why the change is keyed by the language version in go.mod and the //go:build lines, so that you can update your code gradually to be safe for the new semantics, instead of having to convert an entire program all at once. The compiler is doing a safe transformation. The use of unsafe in strings.Builder is the unsafe part, and your example is a casualty. But in this case at least, there is no reason to write the code this way. Basically everyone who writes Go would declare b above the loop. I don't think there's anything to do here. |
It would be great that Go official maintain a list of such so-called corner cases. Doing this will be much helpful to all Go programmers. BTW, I maintain an incomplete one.
This needs much more publicity. Hope the just mentioned article would help some.
I've had bad luck. :D I really didn't know that
My reason is: the syntax allows it and I like the flexibility the old semantics provide.
Prior to Go 1.22, I can't vouch for that myself. This issue was created mainly to make Go compiler more rigorous. |
This corner case, as a source of possible Go errors, is not a large one. I know we have a tendency to view language definition changes as a sort of "broken promise" that somehow makes those potential bug causes loom larger than all the working-as-intended foot-guns ("the programmer should have known") but at scale, what matters is the rate/cost of bugs, no matter their cause. Assume code needs maintenance, I have never seen a piece of code of any significance that lacked a bug. I extended the vet nocopy check to cover For comparison, on this sample, I saw 152 occurrences of any kind of copying a I'm benchmarking an alternate fix for this problem, but even if Builders become copy-able, this same problem would affect the other nocopy types, so vet needs a modification for that (and that small change probably does not require a proposal). |
Change https://go.dev/cl/570137 mentions this issue: |
Go version
go version go1.22.0 linux/amd64
Output of
go env
in your module/workspace:.
What did you do?
What did you see happen?
Consistent behavior between
foo
andbar
with 1.22 compiler.What did you expect to see?
Inconsistent behavior between
foo
andbar
with 1.22 compiler.The text was updated successfully, but these errors were encountered: