Skip to content
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

reflect: add Value.Equal, Value.Comparable #46746

Open
ianlancetaylor opened this issue Jun 14, 2021 · 13 comments
Open

reflect: add Value.Equal, Value.Comparable #46746

ianlancetaylor opened this issue Jun 14, 2021 · 13 comments

Comments

@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Jun 14, 2021

In Go 1.17 we have introduced a conversion that can panic (#395). This is the first case in which a conversion can panic. This means that code that calls reflect.Type.ConvertibleTo and then, if that returns true, calls reflect.Value.Convert, can see an unexpected panic. (See #46730.)

Separately, for a long time now it has been possible for a comparison to panic, when comparing two interface values that have the same dynamic type but for which the dynamic type is not comparable. Therefore, for a long time code that calls reflect.Type.Comparable and then, if that returns true, uses the == operator can see an unexpected panic. (This is a fairly uncommon case as the problem only arises when working with indirectly accessed interface types, such as pointers to interfaces.)

I propose adding two new methods to reflect.Value.

// ConvertibleTo reports whether v can be converted to type t.
// If this reports true then v.Convert(t) will not panic.
func (v Value) ConvertibleTo(t Type) bool

// Comparable reports whether the type of v is comparable.
// If the type of v is an interface, this checks the dynamic type.
// If this reports true then v.Interface() == x will not panic for any x.
func (v Value) Comparable() bool
@josharian
Copy link
Contributor

@josharian josharian commented Jun 14, 2021

One very minor observation:

// If this reports true then v.Interface() == x will not panic for any x.

I'm not sure this is quite right. x could be an interface containing a dynamic type that isn't comparable. In the common case, comparing two reflect.Values, you need to call Comparable on both of them. I don't have better wording to suggest.

@ianlancetaylor
Copy link
Contributor Author

@ianlancetaylor ianlancetaylor commented Jun 14, 2021

I think the statement is still true, because if the dynamic types of v.Interface() and x are different, then the comparison is false, and it doesn't matter whether either or both of the dynamic types are not comparable. In other words, if the dynamic type of v.Interface() is comparable, then either x has a different dynamic type and the result of v.Interface() == x is false, or x has the same dynamic type and the comparison will be run without panicking.

@josharian
Copy link
Contributor

@josharian josharian commented Jun 14, 2021

Ah, indeed. Thanks. The relevant sentence from the spec is:

A comparison of two interface values with identical dynamic types causes a run-time panic if values of that type are not comparable.

@rsc
Copy link
Contributor

@rsc rsc commented Jun 16, 2021

We have:

func (v Value) Addr() Value
func (v Value) CanAddr() bool

func (v Value) Interface() interface{}
func (v Value) CanInterface() bool

So it sounds like we want to add the second one of these:

func (v Value) Convert(t Type) Value
func (v Value) CanConvert(t Type) bool

And maybe:

func (v Value) Equal(u Value) bool
func (v Value) Comparable(u Value) bool

Comparable seems like a better name than CanEqual here, but Equal seems better than Compare (compare bytes.Compare, bytes.Equal).

We probably want CanConvert at least for Go 1.17.

@rsc rsc moved this from Incoming to Active in Proposals Jun 16, 2021
@rsc
Copy link
Contributor

@rsc rsc commented Jun 16, 2021

This proposal has been added to the active column of the proposals project
and will now be reviewed at the weekly proposal review meetings.
— rsc for the proposal review group

@rsc rsc changed the title proposal: reflect: add Value.ConvertibleTo(Value) and Value.Comparable(Value) proposal: reflect: add Value.Convert, Value.CanConvert, Value.Equal, Value.Comparable Jul 14, 2021
@rsc rsc changed the title proposal: reflect: add Value.Convert, Value.CanConvert, Value.Equal, Value.Comparable proposal: reflect: add Value.CanConvert, Value.Equal, Value.Comparable Jul 14, 2021
@rsc
Copy link
Contributor

@rsc rsc commented Jul 14, 2021

Based on the discussion above, this proposal seems like a likely accept.
— rsc for the proposal review group

@rsc rsc moved this from Active to Likely Accept in Proposals Jul 14, 2021
@twmb
Copy link
Contributor

@twmb twmb commented Jul 14, 2021

If a Value is Comparable, does this mean the return from Interface() can be used as a key in a map? I don't think reflect currently has a way to answer this question.

(this is just a clarifying question, not a comment against the proposal)

@josharian
Copy link
Contributor

@josharian josharian commented Jul 14, 2021

We probably want CanConvert at least for Go 1.17.

Is this still true? The window for doing this is pretty small now.

@ianlancetaylor
Copy link
Contributor Author

@ianlancetaylor ianlancetaylor commented Jul 14, 2021

If a Value is Comparable, does this mean the return from Interface() can be used as a key in a map? I don't think reflect currently has a way to answer this question.

Assuming you have a map[interface{}]T (for some value type T), then yes: if v.Comparable() returns true, you can use v.Interface() as a key value for that map, and no panic will occur.

@gopherbot
Copy link

@gopherbot gopherbot commented Jul 14, 2021

Change https://golang.org/cl/334669 mentions this issue: reflect: add Value.CanConvert

@ianlancetaylor
Copy link
Contributor Author

@ianlancetaylor ianlancetaylor commented Jul 14, 2021

I sent https://golang.org/cl/334669 in case we do want this in 1.17.

@rsc
Copy link
Contributor

@rsc rsc commented Jul 21, 2021

No change in consensus, so accepted. 🎉
This issue now tracks the work of implementing the proposal.
— rsc for the proposal review group

@rsc rsc changed the title proposal: reflect: add Value.CanConvert, Value.Equal, Value.Comparable reflect: add Value.CanConvert, Value.Equal, Value.Comparable Jul 21, 2021
@rsc rsc removed this from the Proposal milestone Jul 21, 2021
@rsc rsc added this to the Backlog milestone Jul 21, 2021
gopherbot pushed a commit that referenced this issue Jul 21, 2021
For #395
For #46746

Change-Id: I4bfc094cf1cecd27ce48e31f92384cf470f371a6
Reviewed-on: https://go-review.googlesource.com/c/go/+/334669
Trust: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
@ianlancetaylor
Copy link
Contributor Author

@ianlancetaylor ianlancetaylor commented Jul 21, 2021

CanConvert has been added for 1.17. This issue remains open for Equal and Comparable in a later release.

@ianlancetaylor ianlancetaylor changed the title reflect: add Value.CanConvert, Value.Equal, Value.Comparable reflect: add Value.Equal, Value.Comparable Jul 21, 2021
steeve pushed a commit to znly/go that referenced this issue Aug 19, 2021
For golang#395
For golang#46746

Change-Id: I4bfc094cf1cecd27ce48e31f92384cf470f371a6
Reviewed-on: https://go-review.googlesource.com/c/go/+/334669
Trust: Ian Lance Taylor <iant@golang.org>
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Joe Tsai <thebrokentoaster@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Proposals
Accepted
Development

No branches or pull requests

5 participants