diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index a05a5d6397d21..8ed5ca837a017 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -52,7 +52,7 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o // not have found it for T (see also issue 8590). if t := asNamed(T); t != nil { if p, _ := safeUnderlying(t).(*Pointer); p != nil { - obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name) + obj, index, indirect = lookupFieldOrMethod(p, false, false, pkg, name) if _, ok := obj.(*Func); ok { return nil, nil, false } @@ -60,7 +60,7 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o } } - return lookupFieldOrMethod(T, addressable, pkg, name) + return lookupFieldOrMethod(T, addressable, false, pkg, name) } // TODO(gri) The named type consolidation and seen maps below must be @@ -69,7 +69,9 @@ func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o // indirectly via different packages.) // lookupFieldOrMethod should only be called by LookupFieldOrMethod and missingMethod. -func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { +// If checkFold is true, the lookup for methods will include looking for any method +// which case-folds to the same as 'name' (used for giving helpful error messages). +func lookupFieldOrMethod(T Type, addressable, checkFold bool, pkg *Package, name string) (obj Object, index []int, indirect bool) { // WARNING: The code in this function is extremely subtle - do not modify casually! if name == "_" { @@ -127,7 +129,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o seen[named] = true // look for a matching attached method - if i, m := lookupMethod(named.methods, pkg, name); m != nil { + if i, m := lookupMethodFold(named.methods, pkg, name, checkFold); m != nil { // potential match // caution: method may not have a proper signature yet index = concat(e.index, i) @@ -178,7 +180,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o case *Interface: // look for a matching method - if i, m := t.typeSet().LookupMethod(pkg, name); m != nil { + if i, m := lookupMethodFold(t.typeSet().methods, pkg, name, checkFold); m != nil { assert(m.typ != nil) index = concat(e.index, i) if obj != nil || e.multiples { @@ -189,7 +191,7 @@ func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (o } case *TypeParam: - if i, m := t.iface().typeSet().LookupMethod(pkg, name); m != nil { + if i, m := lookupMethodFold(t.iface().typeSet().methods, pkg, name, checkFold); m != nil { assert(m.typ != nil) index = concat(e.index, i) if obj != nil || e.multiples { @@ -315,6 +317,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, if !static { continue } + // We don't do any case-fold check if V is an interface. return m, f } @@ -345,13 +348,20 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // A concrete type implements T if it implements all methods of T. for _, m := range T.typeSet().methods { - // TODO(gri) should this be calling lookupFieldOrMethod instead (and why not)? - obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name) + // TODO(gri) should this be calling LookupFieldOrMethod instead (and why not)? + obj, _, _ := lookupFieldOrMethod(V, false, false, m.pkg, m.name) // Check if *V implements this method of T. if obj == nil { ptr := NewPointer(V) - obj, _, _ = lookupFieldOrMethod(ptr, false, m.pkg, m.name) + obj, _, _ = lookupFieldOrMethod(ptr, false, false, m.pkg, m.name) + if obj != nil { + return m, obj.(*Func) + } + // If we didn't find the exact method (even with pointer + // receiver), look to see if there is a method that + // matches m.name with case-folding. + obj, _, _ := lookupFieldOrMethod(V, false, true, m.pkg, m.name) if obj != nil { return m, obj.(*Func) } @@ -410,7 +420,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, // where m is missing from V, but required by T. It puts the reason in parentheses, // and may include more have/want info after that. If non-nil, wrongType is a relevant // method that matches in some way. It may have the correct name, but wrong type, or -// it may have a pointer receiver. +// it may have a pointer receiver, or it may have the correct name except wrong case. func (check *Checker) missingMethodReason(V, T Type, m, wrongType *Func) string { var r string var mname string @@ -527,3 +537,21 @@ func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) { } return -1, nil } + +// lookupMethodFold is like lookupMethod, but if checkFold is true, it matches a method +// name if the names are equal with case folding. +func lookupMethodFold(methods []*Func, pkg *Package, name string, checkFold bool) (int, *Func) { + if name != "_" { + for i, m := range methods { + if m.name != name && !(checkFold && strings.EqualFold(m.name, name)) { + continue + } + // Use m.name, since we've already checked that m.name and + // name are equal with folding. + if m.sameId(pkg, m.name) { + return i, m + } + } + } + return -1, nil +} diff --git a/test/fixedbugs/issue48471.go b/test/fixedbugs/issue48471.go index 0412d23b99cde..2e00c87c6a936 100644 --- a/test/fixedbugs/issue48471.go +++ b/test/fixedbugs/issue48471.go @@ -29,11 +29,11 @@ func g() { var i I i = new(T) // ERROR "cannot use new\(T\) \(.*type \*T\) as type I in assignment:\n\t\*T does not implement I \(missing M method\)" i = I(new(T)) // ERROR "cannot convert new\(T\) \(.*type \*T\) to type I:\n\t\*T does not implement I \(missing M method\)" - i = new(T2) // ERROR "cannot use new\(T2\) \(.*type \*T2\) as type I in assignment:\n\t\*T2 does not implement I \(missing M method\)" + i = new(T2) // ERROR "cannot use new\(T2\) \(.*type \*T2\) as type I in assignment:\n\t\*T2 does not implement I \(missing M method\)\n\t\thave m\(int\)\n\t\twant M\(int\)" i = new(T3) // ERROR "cannot use new\(T3\) \(.*type \*T3\) as type I in assignment:\n\t\*T3 does not implement I \(wrong type for M method\)\n\t\thave M\(string\)\n\t\twant M\(int\)" i = T4{} // ERROR "cannot use T4\{\} \(.*type T4\) as type I in assignment:\n\tT4 does not implement I \(M method has pointer receiver\)" i = new(I) // ERROR "cannot use new\(I\) \(.*type \*I\) as type I in assignment:\n\t\*I does not implement I \(\*I is pointer to interface, not interface\)" - _ = i.(*T2) // ERROR "impossible type assertion: i.\(\*T2\)\n\t\*T2 does not implement I \(missing M method\)" + _ = i.(*T2) // ERROR "impossible type assertion: i.\(\*T2\)\n\t\*T2 does not implement I \(missing M method\)\n\t\thave m\(int\)\n\t\twant M\(int\)" _ = i.(*T3) // ERROR "impossible type assertion: i.\(\*T3\)\n\t\*T3 does not implement I \(wrong type for M method\)\n\t\thave M\(string\)\n\t\twant M\(int\)" var t *T4 t = i // ERROR "cannot use i \(variable of type I\) as type \*T4 in assignment:\n\tneed type assertion"