Commit b2b5e779 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: ignore receiver parameters in Eqtype

Receiver parameters generally aren't relevant to the function
signature type. In particular:

  1. When checking whether a type's method implements an interface's
     method, we specifically want to ignore the receiver parameters,
     because they'll be different.

  2. When checking interface type equality, interface methods always
     use the same "fakethis" *struct{} type as their receiver.

  3. Finally, method expressions and method values degenerate into
     receiver-less function types.

The only case where we care about receiver types matching is in
addmethod, which is easily handled by adding an extra Eqtype check of
the receiver parameters. Also, added a test for this, since
(surprisingly) there weren't any.

As precedence, go/types.Identical ignores receiver parameters when
comparing go/types.Signature values.

Notably, this allows us to slightly simplify the "implements"
function, which is used for checking whether type/interface t
implements interface iface. Currently, cmd/compile actually works
around Eqtype's receiver parameter checking by creating new throwaway
TFUNC Types without the receiver parameter.

(Worse, the compiler currently only provides APIs to build TFUNC Types
from Nod syntax trees, so building those throwaway types also involves
first building throwaway syntax trees.)

Passes toolstash -cmp.

Change-Id: Ib07289c66feacee284e016bc312e8c5ff674714f
Reviewed-on: https://go-review.googlesource.com/20602Reviewed-by: 's avatarRobert Griesemer <gri@golang.org>
parent d33e37a7
...@@ -1275,7 +1275,9 @@ func addmethod(msym *Sym, t *Type, tpkg *Pkg, local, nointerface bool) { ...@@ -1275,7 +1275,9 @@ func addmethod(msym *Sym, t *Type, tpkg *Pkg, local, nointerface bool) {
if msym.Name != f.Sym.Name { if msym.Name != f.Sym.Name {
continue continue
} }
if !Eqtype(t, f.Type) { // Eqtype only checks that incoming and result parameters match,
// so explicitly check that the receiver parameters match too.
if !Eqtype(t, f.Type) || !Eqtype(t.Recvs(), f.Type.Recvs()) {
Yyerror("method redeclared: %v.%v\n\t%v\n\t%v", pa, msym, f.Type, t) Yyerror("method redeclared: %v.%v\n\t%v\n\t%v", pa, msym, f.Type, t)
} }
return return
......
...@@ -726,9 +726,11 @@ func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool { ...@@ -726,9 +726,11 @@ func eqtype1(t1, t2 *Type, assumedEqual map[typePair]struct{}) bool {
} }
return false return false
// Loop over structs: receiver, in, out.
case TFUNC: case TFUNC:
for _, f := range recvsParamsResults { // Check parameters and result parameters for type equality.
// We intentionally ignore receiver parameters for type
// equality, because they're never relevant.
for _, f := range paramsResults {
// Loop over fields in structs, ignoring argument names. // Loop over fields in structs, ignoring argument names.
ta, ia := IterFields(f(t1)) ta, ia := IterFields(f(t1))
tb, ib := IterFields(f(t2)) tb, ib := IterFields(f(t2))
...@@ -2127,10 +2129,9 @@ func implements(t, iface *Type, m, samename **Field, ptr *int) bool { ...@@ -2127,10 +2129,9 @@ func implements(t, iface *Type, m, samename **Field, ptr *int) bool {
if im.Broke { if im.Broke {
continue continue
} }
imtype := methodfunc(im.Type, nil)
var followptr bool var followptr bool
tm := ifacelookdot(im.Sym, t, &followptr, false) tm := ifacelookdot(im.Sym, t, &followptr, false)
if tm == nil || tm.Nointerface || !Eqtype(methodfunc(tm.Type, nil), imtype) { if tm == nil || tm.Nointerface || !Eqtype(tm.Type, im.Type) {
if tm == nil { if tm == nil {
tm = ifacelookdot(im.Sym, t, &followptr, true) tm = ifacelookdot(im.Sym, t, &followptr, true)
} }
......
...@@ -300,6 +300,11 @@ var recvsParamsResults = [3]func(*Type) *Type{ ...@@ -300,6 +300,11 @@ var recvsParamsResults = [3]func(*Type) *Type{
(*Type).Recvs, (*Type).Params, (*Type).Results, (*Type).Recvs, (*Type).Params, (*Type).Results,
} }
// paramsResults is like recvsParamsResults, but omits receiver parameters.
var paramsResults = [2]func(*Type) *Type{
(*Type).Params, (*Type).Results,
}
// Key returns the key type of map type t. // Key returns the key type of map type t.
func (t *Type) Key() *Type { func (t *Type) Key() *Type {
t.wantEtype(TMAP) t.wantEtype(TMAP)
......
...@@ -9,12 +9,16 @@ ...@@ -9,12 +9,16 @@
package main package main
type T struct { } type T struct{}
func (t *T) M(int, string) // GCCGO_ERROR "previous"
func (t *T) M(int, float64) { } // ERROR "redeclared|redefinition"
func f(int, string) // GCCGO_ERROR "previous" func (t *T) M(int, string) // GCCGO_ERROR "previous"
func f(int, float64) { } // ERROR "redeclared|redefinition" func (t *T) M(int, float64) {} // ERROR "redeclared|redefinition"
func g(a int, b string) // GCCGO_ERROR "previous" func (t T) H() // GCCGO_ERROR "previous"
func g(a int, c string) // ERROR "redeclared|redefinition" func (t *T) H() {} // ERROR "redeclared|redefinition"
func f(int, string) // GCCGO_ERROR "previous"
func f(int, float64) {} // ERROR "redeclared|redefinition"
func g(a int, b string) // GCCGO_ERROR "previous"
func g(a int, c string) // ERROR "redeclared|redefinition"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment