-
Notifications
You must be signed in to change notification settings - Fork 18k
reflect: regression in Value.Addr().Elem() roundtrip #24153
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
Git bisect blames https://golang.org/cl/66331. The CL description seems to indicate that this is intended behavior:
If this is intended, then for #24152 we'll need to fix the |
I would argue that reflect is working correctly in Go 1.10, and that json will need to fix its Addr/Elem roundtrip. Rationale: Given
it's legal to write
which maps to the reflect operations However, it's not legal to write
because |
I argue that: // case 1
(*p).Field = "hello"
v.Elem().FieldByName("Field").SetString("hello")
// case 2
(*p).child.Field = "hello"
v.Elem().FieldByName("child").FieldByName("Field").SetString("hello")
// case 3
(*&(*p).child).Field = "hello"
v.Elem().FieldByName("child").Addr().Elem().FieldByName("Field").SetString("hello") Case 1 is clearly fine. Case 2 is permitted (but violates normal visibility rules). If case 2 is allowed, I don't see why case 3 is forbidden. You could say that we should forbid case 2, but I surmise that would break a lot of existing Go code. |
I think case 1 is clearly okay. Case 2 is tricky though, and shouldn't be used as a precedent to justify case 3. Earlier, we were talking about Value.Field, not Value.FieldByName. Field doesn't support directly accessing promoted fields. This means It seems FieldByName is acting consistent with Field, even though it doesn't need to: since we can directly allow Maybe we need to continue allowing it for backwards compatibility, or so that FieldByName parallels Field. In either case, I don't think it's a good argument for allowing case 3. |
Bleh, the whole situation is funky. Back-to-back use of type child1 struct{ Conflict string }
type child2 struct{ Conflict string }
type parent struct {
child1
child2
}
p := new(parent)
reflect.ValueOf(p).Elem().Field(0).Field(0).SetString("hello")
reflect.ValueOf(p).Elem().Field(1).Field(0).SetString("goodbye")
fmt.Println(p) // &{{hello} {goodbye}} Here, the existence of a conflict on |
Yeah, it's kind of a mess. :( I'd argue that should be disallowed too, but I'm not sure how to do that efficiently. |
Change https://golang.org/cl/97796 mentions this issue: |
…lem() Consider the following: type child struct{ Field string } type parent struct{ child } p := new(parent) v := reflect.ValueOf(p).Elem().Field(0) v.Field(0).SetString("hello") // v.Field = "hello" v = v.Addr().Elem() // v = *(&v) v.Field(0).SetString("goodbye") // v.Field = "goodbye" It would appear that v.Addr().Elem() should have the same value, and that it would be safe to set "goodbye". However, after CL 66331, any interspersed calls between Field calls causes the RO flag to be set. Thus, setting to "goodbye" actually causes a panic. That CL affects decodeState.indirect which assumes that back-to-back Value.Addr().Elem() is side-effect free. We fix that logic to keep track of the Addr() and Elem() calls and set v back to the original after a full round-trip has occured. Fixes #24152 Updates #24153 Change-Id: Ie50f8fe963f00cef8515d89d1d5cbc43b76d9f9c Reviewed-on: https://go-review.googlesource.com/97796 Reviewed-by: Matthew Dempsky <[email protected]> Run-TryBot: Matthew Dempsky <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
The problem described by this issue is working as intended. |
Change https://golang.org/cl/102784 mentions this issue: |
…e reflect.Value.Addr().Elem() Consider the following: type child struct{ Field string } type parent struct{ child } p := new(parent) v := reflect.ValueOf(p).Elem().Field(0) v.Field(0).SetString("hello") // v.Field = "hello" v = v.Addr().Elem() // v = *(&v) v.Field(0).SetString("goodbye") // v.Field = "goodbye" It would appear that v.Addr().Elem() should have the same value, and that it would be safe to set "goodbye". However, after CL 66331, any interspersed calls between Field calls causes the RO flag to be set. Thus, setting to "goodbye" actually causes a panic. That CL affects decodeState.indirect which assumes that back-to-back Value.Addr().Elem() is side-effect free. We fix that logic to keep track of the Addr() and Elem() calls and set v back to the original after a full round-trip has occured. Fixes #24152 Updates #24153 Change-Id: Ie50f8fe963f00cef8515d89d1d5cbc43b76d9f9c Reviewed-on: https://go-review.googlesource.com/97796 Reviewed-by: Matthew Dempsky <[email protected]> Run-TryBot: Matthew Dempsky <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-on: https://go-review.googlesource.com/102784 Run-TryBot: Andrew Bonventre <[email protected]> Reviewed-by: Joe Tsai <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
Consider the following:
On go1.9, this printed:
On go1.10, this printed:
I find it very surprising that a round-trip reference/derefence would cause the exported Field to no longer be settable. It's not clear to me whether this is intended behavior or not.
\cc @mdempsky @ianlancetaylor
The text was updated successfully, but these errors were encountered: