]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types, types2: use identical missingMethod in both type checkers
authorRobert Griesemer <gri@golang.org>
Fri, 4 Feb 2022 01:34:43 +0000 (17:34 -0800)
committerRobert Griesemer <gri@golang.org>
Fri, 4 Feb 2022 23:42:58 +0000 (23:42 +0000)
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 <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/types2/lookup.go
src/go/types/lookup.go
src/go/types/testdata/fixedbugs/issue50816.go2

index 7e528fb1aab7acd6c0478cb022f1c0885446a1fc..b8ddd94cd78a46cafaec1449a903eff423669d5e 100644 (file)
@@ -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
                }
        }
index ad5438aefbf2b723189c9cc291b47ef1cf6962be..f2f38be266059112410caef1d57f8d0859b8fb3f 100644 (file)
@@ -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
                }
        }
index 025a338184dd20d8e8061f94a4011000a5e06754..e7e31d91922ee4ef5ef23cf8739894e8e2559fe2 100644 (file)
@@ -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)
 }