]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types: consider structural restrictions in Implements
authorRobert Findley <rfindley@google.com>
Mon, 29 Nov 2021 17:21:46 +0000 (12:21 -0500)
committerRobert Findley <rfindley@google.com>
Mon, 29 Nov 2021 22:24:47 +0000 (22:24 +0000)
Fixes #49786

Change-Id: I4559d013399deda48bcb97aef3427ecf87a3ef26
Reviewed-on: https://go-review.googlesource.com/c/go/+/367515
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
src/cmd/compile/internal/types2/api.go
src/cmd/compile/internal/types2/api_test.go
src/go/types/api.go
src/go/types/api_test.go

index 367cb8f700e9585062cbb09e38d7db80d0126592..4ea3989c39577c4df32a72755e01a045bd3c3937 100644 (file)
@@ -440,8 +440,16 @@ func ConvertibleTo(V, T Type) bool {
 
 // Implements reports whether type V implements interface T.
 func Implements(V Type, T *Interface) bool {
-       f, _ := MissingMethod(V, T, true)
-       return f == nil
+       if T.Empty() {
+               // All types (even Typ[Invalid]) implement the empty interface.
+               return true
+       }
+       // Checker.implements suppresses errors for invalid types, so we need special
+       // handling here.
+       if V.Underlying() == Typ[Invalid] {
+               return false
+       }
+       return (*Checker)(nil).implements(V, T, nil) == nil
 }
 
 // Identical reports whether x and y are identical types.
index 9436a4ed970ed6cf666b998831a5f0a710f382d8..4227397df9f7a18eb5b9e2ec68ad64b7e6d46f64 100644 (file)
@@ -2102,3 +2102,106 @@ func TestInstanceIdentity(t *testing.T) {
                t.Errorf("mismatching types: a.A: %s, b.B: %s", a.Type(), b.Type())
        }
 }
+
+func TestImplements(t *testing.T) {
+       const src = `
+package p
+
+type EmptyIface interface{}
+
+type I interface {
+       m()
+}
+
+type C interface {
+       m()
+       ~int
+}
+
+type Integer interface{
+       int8 | int16 | int32 | int64
+}
+
+type EmptyTypeSet interface{
+       Integer
+       ~string
+}
+
+type N1 int
+func (N1) m() {}
+
+type N2 int
+func (*N2) m() {}
+
+type N3 int
+func (N3) m(int) {}
+
+type N4 string
+func (N4) m()
+
+type Bad Bad // invalid type
+`
+
+       f, err := parseSrc("p.go", src)
+       if err != nil {
+               t.Fatal(err)
+       }
+       conf := Config{Error: func(error) {}}
+       pkg, _ := conf.Check(f.PkgName.Value, []*syntax.File{f}, nil)
+
+       scope := pkg.Scope()
+       var (
+               EmptyIface   = scope.Lookup("EmptyIface").Type().Underlying().(*Interface)
+               I            = scope.Lookup("I").Type().(*Named)
+               II           = I.Underlying().(*Interface)
+               C            = scope.Lookup("C").Type().(*Named)
+               CI           = C.Underlying().(*Interface)
+               Integer      = scope.Lookup("Integer").Type().Underlying().(*Interface)
+               EmptyTypeSet = scope.Lookup("EmptyTypeSet").Type().Underlying().(*Interface)
+               N1           = scope.Lookup("N1").Type()
+               N1p          = NewPointer(N1)
+               N2           = scope.Lookup("N2").Type()
+               N2p          = NewPointer(N2)
+               N3           = scope.Lookup("N3").Type()
+               N4           = scope.Lookup("N4").Type()
+               Bad          = scope.Lookup("Bad").Type()
+       )
+
+       tests := []struct {
+               t    Type
+               i    *Interface
+               want bool
+       }{
+               {I, II, true},
+               {I, CI, false},
+               {C, II, true},
+               {C, CI, true},
+               {Typ[Int8], Integer, true},
+               {Typ[Int64], Integer, true},
+               {Typ[String], Integer, false},
+               {EmptyTypeSet, II, true},
+               {EmptyTypeSet, EmptyTypeSet, true},
+               {Typ[Int], EmptyTypeSet, false},
+               {N1, II, true},
+               {N1, CI, true},
+               {N1p, II, true},
+               {N1p, CI, false},
+               {N2, II, false},
+               {N2, CI, false},
+               {N2p, II, true},
+               {N2p, CI, false},
+               {N3, II, false},
+               {N3, CI, false},
+               {N4, II, true},
+               {N4, CI, false},
+               {Bad, II, false},
+               {Bad, CI, false},
+               {Bad, EmptyIface, true},
+       }
+
+       for _, test := range tests {
+               if got := Implements(test.t, test.i); got != test.want {
+                       t.Errorf("Implements(%s, %s) = %t, want %t", test.t, test.i, got, test.want)
+               }
+       }
+}
index c115d07b4182d080df7d7b59263d6cf2c34edca3..51d58c49aab1336795450882554a2b4ba44a4e4b 100644 (file)
@@ -436,8 +436,16 @@ func ConvertibleTo(V, T Type) bool {
 
 // Implements reports whether type V implements interface T.
 func Implements(V Type, T *Interface) bool {
-       f, _ := MissingMethod(V, T, true)
-       return f == nil
+       if T.Empty() {
+               // All types (even Typ[Invalid]) implement the empty interface.
+               return true
+       }
+       // Checker.implements suppresses errors for invalid types, so we need special
+       // handling here.
+       if V.Underlying() == Typ[Invalid] {
+               return false
+       }
+       return (*Checker)(nil).implements(V, T, nil) == nil
 }
 
 // Identical reports whether x and y are identical types.
index c8fda5521ad76c8beb0e96e94ad1b97420759260..7b7baa76042b8a9669f5e8257842ea5a7ba7b9eb 100644 (file)
@@ -2093,3 +2093,107 @@ func TestInstanceIdentity(t *testing.T) {
                t.Errorf("mismatching types: a.A: %s, b.B: %s", a.Type(), b.Type())
        }
 }
+
+func TestImplements(t *testing.T) {
+       const src = `
+package p
+
+type EmptyIface interface{}
+
+type I interface {
+       m()
+}
+
+type C interface {
+       m()
+       ~int
+}
+
+type Integer interface{
+       int8 | int16 | int32 | int64
+}
+
+type EmptyTypeSet interface{
+       Integer
+       ~string
+}
+
+type N1 int
+func (N1) m() {}
+
+type N2 int
+func (*N2) m() {}
+
+type N3 int
+func (N3) m(int) {}
+
+type N4 string
+func (N4) m()
+
+type Bad Bad // invalid type
+`
+
+       fset := token.NewFileSet()
+       f, err := parser.ParseFile(fset, "p.go", src, 0)
+       if err != nil {
+               t.Fatal(err)
+       }
+       conf := Config{Error: func(error) {}}
+       pkg, _ := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil)
+
+       scope := pkg.Scope()
+       var (
+               EmptyIface   = scope.Lookup("EmptyIface").Type().Underlying().(*Interface)
+               I            = scope.Lookup("I").Type().(*Named)
+               II           = I.Underlying().(*Interface)
+               C            = scope.Lookup("C").Type().(*Named)
+               CI           = C.Underlying().(*Interface)
+               Integer      = scope.Lookup("Integer").Type().Underlying().(*Interface)
+               EmptyTypeSet = scope.Lookup("EmptyTypeSet").Type().Underlying().(*Interface)
+               N1           = scope.Lookup("N1").Type()
+               N1p          = NewPointer(N1)
+               N2           = scope.Lookup("N2").Type()
+               N2p          = NewPointer(N2)
+               N3           = scope.Lookup("N3").Type()
+               N4           = scope.Lookup("N4").Type()
+               Bad          = scope.Lookup("Bad").Type()
+       )
+
+       tests := []struct {
+               t    Type
+               i    *Interface
+               want bool
+       }{
+               {I, II, true},
+               {I, CI, false},
+               {C, II, true},
+               {C, CI, true},
+               {Typ[Int8], Integer, true},
+               {Typ[Int64], Integer, true},
+               {Typ[String], Integer, false},
+               {EmptyTypeSet, II, true},
+               {EmptyTypeSet, EmptyTypeSet, true},
+               {Typ[Int], EmptyTypeSet, false},
+               {N1, II, true},
+               {N1, CI, true},
+               {N1p, II, true},
+               {N1p, CI, false},
+               {N2, II, false},
+               {N2, CI, false},
+               {N2p, II, true},
+               {N2p, CI, false},
+               {N3, II, false},
+               {N3, CI, false},
+               {N4, II, true},
+               {N4, CI, false},
+               {Bad, II, false},
+               {Bad, CI, false},
+               {Bad, EmptyIface, true},
+       }
+
+       for _, test := range tests {
+               if got := Implements(test.t, test.i); got != test.want {
+                       t.Errorf("Implements(%s, %s) = %t, want %t", test.t, test.i, got, test.want)
+               }
+       }
+}