Skip to content

reflect: can Set to unexported embedded pointer types #21353

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

Closed
dsnet opened this issue Aug 8, 2017 · 5 comments
Closed

reflect: can Set to unexported embedded pointer types #21353

dsnet opened this issue Aug 8, 2017 · 5 comments
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done. release-blocker
Milestone

Comments

@dsnet
Copy link
Member

dsnet commented Aug 8, 2017

Consider the following snippet:

type embed struct{ X int }
type S1 struct{ embed }
type S2 struct{ *embed }

v1 := reflect.ValueOf(&S1{}).Elem().Field(0)
v1.Set(reflect.ValueOf(embed{}))  // OK on Go1.0..Go1.5; Panics on Go1.6..present
v2 := reflect.ValueOf(&S2{}).Elem().Field(0)
v2.Set(reflect.ValueOf(&embed{})) // OK on Go1.0..present

We have two different structs S1 and S2 that differ only in that one has a embedded pointer, while the other does not. Note that embed is an unexported type, and so acts an unexported field in S1 and S2.

From Go1.0 until (and including) Go1.5, you could call Set on the unexported field embed on both the embedded value and embedded pointer versions.

From Go1.6 until (the present day) Go1.9, you could call Set only on the embed field only if it were pointer version, but reflect would panic if you tried to call Set on the pointer version. This is a result of a change to the policy mentioned here.

Outside of reflect, the Go language forbids you from directly setting S1.embed or S2.embed from an external package. For this reason, I'm inclined to believe that the current behavior of reflect is a bug since it permits you to do something that the language forbids.

However, there is an implication to this change. It means that JSON cannot be round-trip marshaled and unmarshaled. For example:

in := &S2{&embed{5}}
out := &S2{}

b, _ := json.Marshal(in) // string(b) == `{"X": 5}`

// Panics, since out.embed is nil and it cannot initialize that field.
json.Unmarshal(b, out)

// Manually allocated embed would allow this to work:
out.embed = &embed{}
json.Unmarshal(b, out)

There would need to be a fix to the json package to avoid a panic in this situation.

If we accept my fix to #21122, then this will also be fixed.

\cc @ianlancetaylor

@dsnet dsnet added NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. release-blocker labels Aug 8, 2017
@dsnet dsnet added this to the Go1.10 milestone Aug 8, 2017
@dsnet
Copy link
Member Author

dsnet commented Aug 8, 2017

\cc @zombiezen @adams-sarah @cybrcodr @jba @pongad

cloud.google.com/go/internal/fields was the only package I found that may be affected by this change. Global testing inside Google revealed no other issues.

@ianlancetaylor
Copy link
Member

gccgo panics on both calls to Set, so any code that relied on the current gc behavior was already broken with gccgo.

@ianlancetaylor
Copy link
Member

I think this is simply a bug, so moving from NeedsDecision to NeedsFix.

@ianlancetaylor ianlancetaylor added the NeedsFix The path to resolution is known, but the work has not been done. label Aug 8, 2017
@gopherbot gopherbot removed the NeedsDecision Feedback is required from experts, contributors, and/or the community before a change can be made. label Aug 8, 2017
@gopherbot
Copy link
Contributor

Change https://golang.org/cl/53643 mentions this issue: reflect: populate StructField.PkgPath on unexported embedded pointer types

@gopherbot
Copy link
Contributor

Change https://golang.org/cl/60410 mentions this issue: cmd/compile: fix and improve struct field reflect information

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge NeedsFix The path to resolution is known, but the work has not been done. release-blocker
Projects
None yet
Development

No branches or pull requests

3 participants