From: Robert Griesemer Date: Fri, 4 Feb 2022 01:34:43 +0000 (-0800) Subject: go/types, types2: use identical missingMethod in both type checkers X-Git-Tag: go1.18rc1~92 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=7c9885def52a408532085a566eea107f31ad1556;p=gostls13.git go/types, types2: use identical missingMethod in both type checkers Further simplify and regularize Checker.missingMethod and use the same code in both type checkers. This enables case-folding lookup for go/types. Adjusted test case that looks for alternative methods. Change-Id: I5b8cc598c295c329ff93b1c65787cc6140f0900e Reviewed-on: https://go-review.googlesource.com/c/go/+/382858 Trust: Robert Griesemer Reviewed-by: Robert Findley --- diff --git a/src/cmd/compile/internal/types2/lookup.go b/src/cmd/compile/internal/types2/lookup.go index 7e528fb1aa..b8ddd94cd7 100644 --- a/src/cmd/compile/internal/types2/lookup.go +++ b/src/cmd/compile/internal/types2/lookup.go @@ -296,21 +296,21 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b // on T when looked up with case-folding, this alternative method is returned // as the second result. func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, alt *Func) { - // fast path for common case if T.NumMethods() == 0 { return } - if ityp, _ := under(V).(*Interface); ityp != nil { + // V is an interface + if u, _ := under(V).(*Interface); u != nil { + tset := u.typeSet() for _, m := range T.typeSet().methods { - _, f := ityp.typeSet().LookupMethod(m.pkg, m.name, false) + _, f := tset.LookupMethod(m.pkg, m.name, false) if f == nil { if !static { continue } - // We don't do any case-fold check if V is an interface. - return m, f + return m, nil } if !Identical(f.typ, m.typ) { @@ -321,30 +321,22 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, return } - // A concrete type implements T if it implements all methods of T. + // V is not an interface 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, false) - // Check if *V implements this method of T. - if obj == nil { - ptr := NewPointer(V) - obj, _, _ = lookupFieldOrMethod(ptr, false, m.pkg, m.name, false) + // check if m is on *V, or on V with case-folding + found := obj != nil + if !found { + // TODO(gri) Instead of NewPointer(V) below, can we just set the "addressable" argument? + obj, _, _ = lookupFieldOrMethod(NewPointer(V), false, m.pkg, m.name, false) if obj == nil { - // If we didn't find the exact method (even with pointer receiver), - // check if there is a matching method using case-folding. - obj, _, _ = lookupFieldOrMethod(V, false, m.pkg, m.name, true) - } - if obj != nil { - // methods may not have a fully set up signature yet - if check != nil { - check.objDecl(obj, nil) - } - return m, obj.(*Func) + obj, _, _ = lookupFieldOrMethod(V, false, m.pkg, m.name, true /* fold case */) } } - // we must have a method (not a field of matching function type) + // we must have a method (not a struct field) f, _ := obj.(*Func) if f == nil { return m, nil @@ -355,7 +347,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, check.objDecl(f, nil) } - if !Identical(f.typ, m.typ) { + if !found || !Identical(f.typ, m.typ) { return m, f } } diff --git a/src/go/types/lookup.go b/src/go/types/lookup.go index ad5438aefb..f2f38be266 100644 --- a/src/go/types/lookup.go +++ b/src/go/types/lookup.go @@ -295,23 +295,22 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b // If a method is missing on T but is found on *T, or if a method is found // on T when looked up with case-folding, this alternative method is returned // as the second result. -// Note: case-folding lookup is currently disabled func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, alt *Func) { - // fast path for common case if T.NumMethods() == 0 { return } - if ityp, _ := under(V).(*Interface); ityp != nil { + // V is an interface + if u, _ := under(V).(*Interface); u != nil { + tset := u.typeSet() for _, m := range T.typeSet().methods { - _, f := ityp.typeSet().LookupMethod(m.pkg, m.name, false) + _, f := tset.LookupMethod(m.pkg, m.name, false) if f == nil { if !static { continue } - // We don't do any case-fold check if V is an interface. - return m, f + return m, nil } if !Identical(f.typ, m.typ) { @@ -322,31 +321,22 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, return } - // A concrete type implements T if it implements all methods of T. + // V is not an interface 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, false) - // Check if *V implements this method of T. - if obj == nil { - ptr := NewPointer(V) - obj, _, _ = lookupFieldOrMethod(ptr, false, m.pkg, m.name, false) + // check if m is on *V, or on V with case-folding + found := obj != nil + if !found { + // TODO(gri) Instead of NewPointer(V) below, can we just set the "addressable" argument? + obj, _, _ = lookupFieldOrMethod(NewPointer(V), false, m.pkg, m.name, false) if obj == nil { - // TODO(gri) enable this code - // If we didn't find the exact method (even with pointer receiver), - // check if there is a matching method using case-folding. - // obj, _, _ = lookupFieldOrMethod(V, false, m.pkg, m.name, true) - } - if obj != nil { - // methods may not have a fully set up signature yet - if check != nil { - check.objDecl(obj, nil) - } - return m, obj.(*Func) + obj, _, _ = lookupFieldOrMethod(V, false, m.pkg, m.name, true /* fold case */) } } - // we must have a method (not a field of matching function type) + // we must have a method (not a struct field) f, _ := obj.(*Func) if f == nil { return m, nil @@ -357,7 +347,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, check.objDecl(f, nil) } - if !Identical(f.typ, m.typ) { + if !found || !Identical(f.typ, m.typ) { return m, f } } diff --git a/src/go/types/testdata/fixedbugs/issue50816.go2 b/src/go/types/testdata/fixedbugs/issue50816.go2 index 025a338184..e7e31d9192 100644 --- a/src/go/types/testdata/fixedbugs/issue50816.go2 +++ b/src/go/types/testdata/fixedbugs/issue50816.go2 @@ -18,6 +18,6 @@ func (T2) foo() string { return "" } func _() { var i I - _ = i /* ERROR impossible type assertion: i\.\(T1\)\n\tT1 does not implement I \(missing method Foo\) */ .(T1) - _ = i /* ERROR impossible type assertion: i\.\(T2\)\n\tT2 does not implement I \(missing method Foo\) */ .(T2) + _ = i /* ERROR impossible type assertion: i\.\(T1\)\n\tT1 does not implement I \(missing method Foo\)\n\t\thave foo\(\)\n\t\twant Foo\(\) */ .(T1) + _ = i /* ERROR impossible type assertion: i\.\(T2\)\n\tT2 does not implement I \(missing method Foo\)\n\t\thave foo\(\) string\n\t\twant Foo\(\) */ .(T2) }