go/types, types: flip field/method selection logic inside out (cleanup)

By selecting on *Var/*Func first, the field/method selection logic
becomes a bit cleaner, clearer, and a little bit simpler.

No functional changes.

Change-Id: I9860f98d52779b0f7ce4484ea77e74a3667d9e6b
Reviewed-on: https://go-review.googlesource.com/c/go/+/738080
Reviewed-by: Alan Donovan <adonovan@google.com>
Commit-Queue: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Robert Griesemer <gri@google.com>
This commit is contained in:
Robert Griesemer 2026-01-21 15:11:13 -08:00 committed by Gopher Robot
parent 021d5ca042
commit 6d89ab44cc
2 changed files with 108 additions and 120 deletions

View file

@ -837,71 +837,67 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr, wantType bool
}
// obj != nil
// methods may not have a fully set up signature yet
if m, _ := obj.(*Func); m != nil {
check.objDecl(m)
}
if x.mode == typexpr {
// method expression
m, _ := obj.(*Func)
if m == nil {
switch obj := obj.(type) {
case *Var:
if x.mode == typexpr {
check.errorf(e.X, MissingFieldOrMethod, "operand for field selector %s must be value of type %s", sel, x.typ)
goto Error
}
check.recordSelection(e, MethodExpr, x.typ, m, index, indirect)
sig := m.typ.(*Signature)
if sig.recv == nil {
check.error(e, InvalidDeclCycle, "illegal cycle in method declaration")
goto Error
// field value
check.recordSelection(e, FieldVal, x.typ, obj, index, indirect)
if x.mode == variable || indirect {
x.mode = variable
} else {
x.mode = value
}
x.typ = obj.typ
// The receiver type becomes the type of the first function
// argument of the method expression's function type.
var params []*Var
if sig.params != nil {
params = sig.params.vars
}
// Be consistent about named/unnamed parameters. This is not needed
// for type-checking, but the newly constructed signature may appear
// in an error message and then have mixed named/unnamed parameters.
// (An alternative would be to not print parameter names in errors,
// but it's useful to see them; this is cheap and method expressions
// are rare.)
name := ""
if len(params) > 0 && params[0].name != "" {
// name needed
name = sig.recv.name
if name == "" {
name = "_"
case *Func:
check.objDecl(obj) // ensure fully set-up signature
check.addDeclDep(obj)
if x.mode == typexpr {
// method expression
check.recordSelection(e, MethodExpr, x.typ, obj, index, indirect)
sig := obj.typ.(*Signature)
if sig.recv == nil {
check.error(e, InvalidDeclCycle, "illegal cycle in method declaration")
goto Error
}
}
params = append([]*Var{NewParam(sig.recv.pos, sig.recv.pkg, name, x.typ)}, params...)
x.mode = value
x.typ = &Signature{
tparams: sig.tparams,
params: NewTuple(params...),
results: sig.results,
variadic: sig.variadic,
}
check.addDeclDep(m)
} else {
// regular selector
switch obj := obj.(type) {
case *Var:
check.recordSelection(e, FieldVal, x.typ, obj, index, indirect)
if x.mode == variable || indirect {
x.mode = variable
} else {
x.mode = value
// The receiver type becomes the type of the first function
// argument of the method expression's function type.
var params []*Var
if sig.params != nil {
params = sig.params.vars
}
x.typ = obj.typ
// Be consistent about named/unnamed parameters. This is not needed
// for type-checking, but the newly constructed signature may appear
// in an error message and then have mixed named/unnamed parameters.
// (An alternative would be to not print parameter names in errors,
// but it's useful to see them; this is cheap and method expressions
// are rare.)
name := ""
if len(params) > 0 && params[0].name != "" {
// name needed
name = sig.recv.name
if name == "" {
name = "_"
}
}
params = append([]*Var{NewParam(sig.recv.pos, sig.recv.pkg, name, x.typ)}, params...)
x.mode = value
x.typ = &Signature{
tparams: sig.tparams,
params: NewTuple(params...),
results: sig.results,
variadic: sig.variadic,
}
} else {
// method value
case *Func:
// TODO(gri) If we needed to take into account the receiver's
// addressability, should we report the type &(x.typ) instead?
check.recordSelection(e, MethodVal, x.typ, obj, index, indirect)
@ -912,12 +908,10 @@ func (check *Checker) selector(x *operand, e *syntax.SelectorExpr, wantType bool
sig := *obj.typ.(*Signature)
sig.recv = nil
x.typ = &sig
check.addDeclDep(obj)
default:
panic("unreachable")
}
default:
panic("unreachable")
}
// everything went well

View file

@ -840,71 +840,67 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr, wantType bool) {
}
// obj != nil
// methods may not have a fully set up signature yet
if m, _ := obj.(*Func); m != nil {
check.objDecl(m)
}
if x.mode == typexpr {
// method expression
m, _ := obj.(*Func)
if m == nil {
switch obj := obj.(type) {
case *Var:
if x.mode == typexpr {
check.errorf(e.X, MissingFieldOrMethod, "operand for field selector %s must be value of type %s", sel, x.typ)
goto Error
}
check.recordSelection(e, MethodExpr, x.typ, m, index, indirect)
sig := m.typ.(*Signature)
if sig.recv == nil {
check.error(e, InvalidDeclCycle, "illegal cycle in method declaration")
goto Error
// field value
check.recordSelection(e, FieldVal, x.typ, obj, index, indirect)
if x.mode == variable || indirect {
x.mode = variable
} else {
x.mode = value
}
x.typ = obj.typ
// the receiver type becomes the type of the first function
// argument of the method expression's function type
var params []*Var
if sig.params != nil {
params = sig.params.vars
}
// Be consistent about named/unnamed parameters. This is not needed
// for type-checking, but the newly constructed signature may appear
// in an error message and then have mixed named/unnamed parameters.
// (An alternative would be to not print parameter names in errors,
// but it's useful to see them; this is cheap and method expressions
// are rare.)
name := ""
if len(params) > 0 && params[0].name != "" {
// name needed
name = sig.recv.name
if name == "" {
name = "_"
case *Func:
check.objDecl(obj) // ensure fully set-up signature
check.addDeclDep(obj)
if x.mode == typexpr {
// method expression
check.recordSelection(e, MethodExpr, x.typ, obj, index, indirect)
sig := obj.typ.(*Signature)
if sig.recv == nil {
check.error(e, InvalidDeclCycle, "illegal cycle in method declaration")
goto Error
}
}
params = append([]*Var{NewParam(sig.recv.pos, sig.recv.pkg, name, x.typ)}, params...)
x.mode = value
x.typ = &Signature{
tparams: sig.tparams,
params: NewTuple(params...),
results: sig.results,
variadic: sig.variadic,
}
check.addDeclDep(m)
} else {
// regular selector
switch obj := obj.(type) {
case *Var:
check.recordSelection(e, FieldVal, x.typ, obj, index, indirect)
if x.mode == variable || indirect {
x.mode = variable
} else {
x.mode = value
// The receiver type becomes the type of the first function
// argument of the method expression's function type.
var params []*Var
if sig.params != nil {
params = sig.params.vars
}
x.typ = obj.typ
// Be consistent about named/unnamed parameters. This is not needed
// for type-checking, but the newly constructed signature may appear
// in an error message and then have mixed named/unnamed parameters.
// (An alternative would be to not print parameter names in errors,
// but it's useful to see them; this is cheap and method expressions
// are rare.)
name := ""
if len(params) > 0 && params[0].name != "" {
// name needed
name = sig.recv.name
if name == "" {
name = "_"
}
}
params = append([]*Var{NewParam(sig.recv.pos, sig.recv.pkg, name, x.typ)}, params...)
x.mode = value
x.typ = &Signature{
tparams: sig.tparams,
params: NewTuple(params...),
results: sig.results,
variadic: sig.variadic,
}
} else {
// method value
case *Func:
// TODO(gri) If we needed to take into account the receiver's
// addressability, should we report the type &(x.typ) instead?
check.recordSelection(e, MethodVal, x.typ, obj, index, indirect)
@ -960,12 +956,10 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr, wantType bool) {
sig := *obj.typ.(*Signature)
sig.recv = nil
x.typ = &sig
check.addDeclDep(obj)
default:
panic("unreachable")
}
default:
panic("unreachable")
}
// everything went well