What is the URL of the page with the issue?
https://go.dev/ref/spec#Method_values
What is your user agent?
Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
Screenshot
What did you do?
I have read the specification and I think the part related to the behavior (evaluation) of method values is not well written. I tried to write an issue quite thoroughly and step-by-step, while extensively quoting the specification and providing examples. For this reason, there is a bit more text. I didn't want the issue to be misinterpreted or for there to be any confusion about what I'm alluding to.
What did you see happen?
Selector expression is in the spec explained as the way to select the field or the method with identifier f
of the value x
.
For a primary expression x that is not a package name, the selector expression
x.f
denotes the field or method f of the value x (or sometimes *x; see below). The identifier f is called the (field or method) selector; it must not be the blank identifier. The type of the selector expression is the type of f. If x is a package name, see the section on qualified identifiers.
According to spec, selection is done by traversing the types of the value x
and its embedded fields and looking for the method or field with identifier f
.
A selector f may denote a field or method f of a type T, or it may refer to a field or method f of a nested embedded field of T. The number of embedded fields traversed to reach f is called its depth in T. The depth of a field or method f declared in T is zero. The depth of a field or method f declared in an embedded field A in T is the depth of f in A plus one.
Next, the spec specifies some rules for selectors. The most important for what I'm talking about is the first:
For a value x of type T or T where T is not a pointer or interface type, x.f denotes the field or method* at the shallowest depth in T where there is such an f. If there is not exactly one f with shallowest depth, the selector expression is illegal.
Thus, it only talks about selecting ("taking") some concrete method or field f
. Nothing else than that. Selectors just denote method or field (as bolded in all three cites). Any other logic related to the process of evaluation, creating method sets, implicit dereferencing of x
, implicit taking address of x
etc. is, according to the spec, out of scope for the selectors.
Consider the following example:
type T0 struct {
x int
}
func (*T0) M0() {}
type T1 struct {
x int
T0
}
func (T1) M1() {}
func (*T1) M2() {}
Following are all valid selectors for x1
of type T1
and x2
of type *T1
(I am talking here only about valid selectors according to the previously cited rule for them):
x1.M0
x2.M0
x1.M1
x1.M2
x2.M1
x2.M2
x1.x // from T1
x2.x // from T1
Further rules are then applied based on the "context" of the selected (obtained with the selector). Thus:
1. if we select field x
with either x1.x
or x2.x
, then the rules related to the evaluation of the expression denoting identifier of the variable is considered (since fields of the struct are considered as variables according to spec)
Structured variables of array, slice, and struct types have elements and fields that may be addressed individually. Each such element acts like a variable. 2. if we select method with
x1.M1
and call itx1.M1()
, then the rules related to calls are applied: > A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m() - these rules include implicit dereferencing if the method is in the method set, sox2.M1()
is shorthand for(*x2).M1()
and implicit address taking if the receiver is addressable, sox1.M2()
is shorthand for(&x1).M2()
) 3. based on selectors, promotions and method sets are defined 4. if we select method withx1.M1
, then we selected method value and the rules related to the evaluation of the method value are applied 5. and so on...
So, selector just selects/denotes field or method and then the corresponding rules are applied based on further context. For example:
type T1 struct{}
func (T1) M1() {}
func (*T1) M2() {}
func R() T1 { return T1{} }
func main() {
R().M1()
R().M2() // error
}
In this example, both selector expressions R().M1
and R().M2
are valid according to the rule for selectors. Further rules apply because of method call and then R().M2()
fails to compile since return value of the function is not addressable (implicit &R()
is not possible; rule 2).
Now, what I found to be incorrect in the specification.
It states the following for the method values:
As with selectors, a reference to a non-interface method with a value receiver using a pointer will automatically dereference that pointer: pt.Mv is equivalent to (*pt).Mv.
As with method calls, a reference to a non-interface method with a pointer receiver using an addressable value will automatically take the address of that value: t.Mp is equivalent to (&t).Mp.
I agree with the second paragraph, but the first one seems incorrect to me. Selector just selects/denotes some method and then dereference is done "as part of other rules" (for example, if it is method call or method value).
Nowhere in the selectors section does it state that pt.Mv is equivalent to (*pt).Mv
, or looking at the first example, that x2.M1() is equivalent to (*x2).M1()
and that this implicit dereferencing is done. It only states that pt.Mv
(x2.M1
) denotes a method, and that's it. Moreover, if we follow this logic of implicitness, then for the second rule we could also reference selectors (instead of calls) as t.Mp being equivalent to (&t).Mp
, or looking at the first example, x1.M2() being equivalent to (&x1).M2()
. Here, the address is implicitly taken (just as in the first case where we have implicitly dereferencing).
Whether selectors automatically dereference (or take address) is not stated anywhere (and I think it shouldn't be since they just denote method or field), so selectors can't be used as reference here. Both cases are correct by the rules for the selectors!
What did you expect to see?
At the end:
For a primary expression x that is not a package name, the selector expression
x.f
denotes the field or method f of the value x (or sometimes *x; see below).A selector f may denote a field or method f of a type T, or it may refer to a field or method f of a nested embedded field of T.
Selectors just select/denote field or method, and what will further happen is part of other rules.
In my opinion, issue could be solved by making the first paragraph also reference "calls" rather than selectors.
Additionally, since selector expression x.f
where f
is a field and x
is a pointer type implicitly dereferences x
first, it should be noted in selector section. For example, something as this:
- if the selector expression x.f
denotes/selects field f
and x
is of pointer type, dereferencing is automatically done first, and then the selected field is treated as the variable; if the selector expression x.f
denotes/selects method f
, see the method value.
Comment From: gabyhelp
Related Issues and Documentation
- The Go Programming Language Specification > Expressions > Selectors
- The Go Programming Language Specification > Expressions > Selectors
- spec: add example for method value in case of embedded method #47863 (closed)
- The Go Programming Language Specification > Expressions > Method values
- The Go Programming Language Specification > Expressions > Method values
- x/spec: the selector rules list states that certain legal expressions are illegal #67099 (closed)
- spec: ambiguity in "selectors automatically dereference pointers to structs" #8713 (closed)
- cmd/compile: selector expression resolves incorrectly for defined pointer types #21934 (closed)
- go/types: document when selectors have types #11944 (closed)
- spec: comments on Selector example unclear #49141 (closed)
(Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.)
Comment From: ianlancetaylor
CC @griesemer
Comment From: Muerte-here
Hi @griesemer (@ianlancetaylor),
any updates about this?
Comment From: ianlancetaylor
But the selectors section also says
As an exception, if the type of x is a defined pointer type and (x).f is a valid selector expression denoting a field (but not a method), x.f is shorthand for (x).f.
The mention of selectors in the method value section is referring to that.
And in general I don't think that anybody misunderstands what the spec is saying here.
That said, if you think the spec can be improved, please feel free to send a patch; see https://go.dev/doc/contribute.