]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/types, types2: substitute for type parameters in signatures when
authorRobert Findley <rfindley@google.com>
Mon, 22 Nov 2021 17:47:53 +0000 (12:47 -0500)
committerRobert Findley <rfindley@google.com>
Mon, 22 Nov 2021 20:32:29 +0000 (20:32 +0000)
comparing type identity

Generic signatures should be considered identical modulo type parameter
renaming. Update Identical to reflect this, by substituting type
parameters.

Fixes #49722

Change-Id: I33743768c72d8aa59c29bf72fcbabc5974f0b805
Reviewed-on: https://go-review.googlesource.com/c/go/+/366178
Trust: Robert Findley <rfindley@google.com>
Run-TryBot: Robert Findley <rfindley@google.com>
Reviewed-by: Robert Griesemer <gri@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>

src/cmd/compile/internal/types2/api_test.go
src/cmd/compile/internal/types2/predicates.go
src/go/types/api_test.go
src/go/types/predicates.go

index ca90e6b97d409fe47904a874becf2dca82501681..9436a4ed970ed6cf666b998831a5f0a710f382d8 100644 (file)
@@ -1675,6 +1675,56 @@ func TestAssignableTo(t *testing.T) {
        }
 }
 
+func TestIdentical(t *testing.T) {
+       // For each test, we compare the types of objects X and Y in the source.
+       tests := []struct {
+               src  string
+               want bool
+       }{
+               // Basic types.
+               {"var X int; var Y int", true},
+               {"var X int; var Y string", false},
+
+               // TODO: add more tests for complex types.
+
+               // Named types.
+               {"type X int; type Y int", false},
+
+               // Aliases.
+               {"type X = int; type Y = int", true},
+
+               // Functions.
+               {`func X(int) string { return "" }; func Y(int) string { return "" }`, true},
+               {`func X() string { return "" }; func Y(int) string { return "" }`, false},
+               {`func X(int) string { return "" }; func Y(int) {}`, false},
+
+               // Generic functions. Type parameters should be considered identical modulo
+               // renaming. See also issue #49722.
+               {`func X[P ~int](){}; func Y[Q ~int]() {}`, true},
+               {`func X[P1 any, P2 ~*P1](){}; func Y[Q1 any, Q2 ~*Q1]() {}`, true},
+               {`func X[P1 any, P2 ~[]P1](){}; func Y[Q1 any, Q2 ~*Q1]() {}`, false},
+               {`func X[P ~int](P){}; func Y[Q ~int](Q) {}`, true},
+               {`func X[P ~string](P){}; func Y[Q ~int](Q) {}`, false},
+               {`func X[P ~int]([]P){}; func Y[Q ~int]([]Q) {}`, true},
+       }
+
+       for _, test := range tests {
+               pkg, err := pkgFor("test", "package p;"+test.src, nil)
+               if err != nil {
+                       t.Errorf("%s: incorrect test case: %s", test.src, err)
+                       continue
+               }
+               X := pkg.Scope().Lookup("X")
+               Y := pkg.Scope().Lookup("Y")
+               if X == nil || Y == nil {
+                       t.Fatal("test must declare both X and Y")
+               }
+               if got := Identical(X.Type(), Y.Type()); got != test.want {
+                       t.Errorf("Identical(%s, %s) = %t, want %t", X.Type(), Y.Type(), got, test.want)
+               }
+       }
+}
+
 func TestIdentical_issue15173(t *testing.T) {
        // Identical should allow nil arguments and be symmetric.
        for _, test := range []struct {
index e7834a0f9e2e0f68233ad4eb365e7f02638d836a..cf2993f68b08b15e0258a8ff367a85a312b85a51 100644 (file)
@@ -235,19 +235,56 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
                }
 
        case *Signature:
-               // Two function types are identical if they have the same number of parameters
-               // and result values, corresponding parameter and result types are identical,
-               // and either both functions are variadic or neither is. Parameter and result
-               // names are not required to match.
-               // Generic functions must also have matching type parameter lists, but for the
-               // parameter names.
-               if y, ok := y.(*Signature); ok {
-                       return x.variadic == y.variadic &&
-                               identicalTParams(x.TypeParams().list(), y.TypeParams().list(), cmpTags, p) &&
-                               identical(x.params, y.params, cmpTags, p) &&
-                               identical(x.results, y.results, cmpTags, p)
+               y, _ := y.(*Signature)
+               if y == nil {
+                       return false
+               }
+
+               // Two function types are identical if they have the same number of
+               // parameters and result values, corresponding parameter and result types
+               // are identical, and either both functions are variadic or neither is.
+               // Parameter and result names are not required to match, and type
+               // parameters are considered identical modulo renaming.
+
+               if x.TypeParams().Len() != y.TypeParams().Len() {
+                       return false
+               }
+
+               // In the case of generic signatures, we will substitute in yparams and
+               // yresults.
+               yparams := y.params
+               yresults := y.results
+
+               if x.TypeParams().Len() > 0 {
+                       // We must ignore type parameter names when comparing x and y. The
+                       // easiest way to do this is to substitute x's type parameters for y's.
+                       xtparams := x.TypeParams().list()
+                       ytparams := y.TypeParams().list()
+
+                       var targs []Type
+                       for i := range xtparams {
+                               targs = append(targs, x.TypeParams().At(i))
+                       }
+                       smap := makeSubstMap(ytparams, targs)
+
+                       var check *Checker // ok to call subst on a nil *Checker
+
+                       // Constraints must be pair-wise identical, after substitution.
+                       for i, xtparam := range xtparams {
+                               ybound := check.subst(nopos, ytparams[i].bound, smap, nil)
+                               if !identical(xtparam.bound, ybound, cmpTags, p) {
+                                       return false
+                               }
+                       }
+
+                       yparams = check.subst(nopos, y.params, smap, nil).(*Tuple)
+                       yresults = check.subst(nopos, y.results, smap, nil).(*Tuple)
                }
 
+               return x.variadic == y.variadic &&
+                       identical(x.params, yparams, cmpTags, p) &&
+                       identical(x.results, yresults, cmpTags, p)
+
        case *Union:
                if y, _ := y.(*Union); y != nil {
                        xset := computeUnionTypeSet(nil, nopos, x)
@@ -389,19 +426,6 @@ func identicalInstance(xorig Type, xargs []Type, yorig Type, yargs []Type) bool
        return Identical(xorig, yorig)
 }
 
-func identicalTParams(x, y []*TypeParam, cmpTags bool, p *ifacePair) bool {
-       if len(x) != len(y) {
-               return false
-       }
-       for i, x := range x {
-               y := y[i]
-               if !identical(x.bound, y.bound, cmpTags, p) {
-                       return false
-               }
-       }
-       return true
-}
-
 // Default returns the default "typed" type for an "untyped" type;
 // it returns the incoming type for all other types. The default type
 // for untyped nil is untyped nil.
index d8ca8ad611d20c2315fdde09e72cdcc6be83c198..c8fda5521ad76c8beb0e96e94ad1b97420759260 100644 (file)
@@ -1661,6 +1661,56 @@ func TestAssignableTo(t *testing.T) {
        }
 }
 
+func TestIdentical(t *testing.T) {
+       // For each test, we compare the types of objects X and Y in the source.
+       tests := []struct {
+               src  string
+               want bool
+       }{
+               // Basic types.
+               {"var X int; var Y int", true},
+               {"var X int; var Y string", false},
+
+               // TODO: add more tests for complex types.
+
+               // Named types.
+               {"type X int; type Y int", false},
+
+               // Aliases.
+               {"type X = int; type Y = int", true},
+
+               // Functions.
+               {`func X(int) string { return "" }; func Y(int) string { return "" }`, true},
+               {`func X() string { return "" }; func Y(int) string { return "" }`, false},
+               {`func X(int) string { return "" }; func Y(int) {}`, false},
+
+               // Generic functions. Type parameters should be considered identical modulo
+               // renaming. See also issue #49722.
+               {`func X[P ~int](){}; func Y[Q ~int]() {}`, true},
+               {`func X[P1 any, P2 ~*P1](){}; func Y[Q1 any, Q2 ~*Q1]() {}`, true},
+               {`func X[P1 any, P2 ~[]P1](){}; func Y[Q1 any, Q2 ~*Q1]() {}`, false},
+               {`func X[P ~int](P){}; func Y[Q ~int](Q) {}`, true},
+               {`func X[P ~string](P){}; func Y[Q ~int](Q) {}`, false},
+               {`func X[P ~int]([]P){}; func Y[Q ~int]([]Q) {}`, true},
+       }
+
+       for _, test := range tests {
+               pkg, err := pkgForMode("test", "package p;"+test.src, nil, 0)
+               if err != nil {
+                       t.Errorf("%s: incorrect test case: %s", test.src, err)
+                       continue
+               }
+               X := pkg.Scope().Lookup("X")
+               Y := pkg.Scope().Lookup("Y")
+               if X == nil || Y == nil {
+                       t.Fatal("test must declare both X and Y")
+               }
+               if got := Identical(X.Type(), Y.Type()); got != test.want {
+                       t.Errorf("Identical(%s, %s) = %t, want %t", X.Type(), Y.Type(), got, test.want)
+               }
+       }
+}
+
 func TestIdentical_issue15173(t *testing.T) {
        // Identical should allow nil arguments and be symmetric.
        for _, test := range []struct {
index 229a616eaccf5e1493a591db870513d177456673..22ccdd7744db6951a825a02aecf5c35f5b05902e 100644 (file)
@@ -237,19 +237,56 @@ func identical(x, y Type, cmpTags bool, p *ifacePair) bool {
                }
 
        case *Signature:
-               // Two function types are identical if they have the same number of parameters
-               // and result values, corresponding parameter and result types are identical,
-               // and either both functions are variadic or neither is. Parameter and result
-               // names are not required to match.
-               // Generic functions must also have matching type parameter lists, but for the
-               // parameter names.
-               if y, ok := y.(*Signature); ok {
-                       return x.variadic == y.variadic &&
-                               identicalTParams(x.TypeParams().list(), y.TypeParams().list(), cmpTags, p) &&
-                               identical(x.params, y.params, cmpTags, p) &&
-                               identical(x.results, y.results, cmpTags, p)
+               y, _ := y.(*Signature)
+               if y == nil {
+                       return false
+               }
+
+               // Two function types are identical if they have the same number of
+               // parameters and result values, corresponding parameter and result types
+               // are identical, and either both functions are variadic or neither is.
+               // Parameter and result names are not required to match, and type
+               // parameters are considered identical modulo renaming.
+
+               if x.TypeParams().Len() != y.TypeParams().Len() {
+                       return false
+               }
+
+               // In the case of generic signatures, we will substitute in yparams and
+               // yresults.
+               yparams := y.params
+               yresults := y.results
+
+               if x.TypeParams().Len() > 0 {
+                       // We must ignore type parameter names when comparing x and y. The
+                       // easiest way to do this is to substitute x's type parameters for y's.
+                       xtparams := x.TypeParams().list()
+                       ytparams := y.TypeParams().list()
+
+                       var targs []Type
+                       for i := range xtparams {
+                               targs = append(targs, x.TypeParams().At(i))
+                       }
+                       smap := makeSubstMap(ytparams, targs)
+
+                       var check *Checker // ok to call subst on a nil *Checker
+
+                       // Constraints must be pair-wise identical, after substitution.
+                       for i, xtparam := range xtparams {
+                               ybound := check.subst(token.NoPos, ytparams[i].bound, smap, nil)
+                               if !identical(xtparam.bound, ybound, cmpTags, p) {
+                                       return false
+                               }
+                       }
+
+                       yparams = check.subst(token.NoPos, y.params, smap, nil).(*Tuple)
+                       yresults = check.subst(token.NoPos, y.results, smap, nil).(*Tuple)
                }
 
+               return x.variadic == y.variadic &&
+                       identical(x.params, yparams, cmpTags, p) &&
+                       identical(x.results, yresults, cmpTags, p)
+
        case *Union:
                if y, _ := y.(*Union); y != nil {
                        xset := computeUnionTypeSet(nil, token.NoPos, x)
@@ -391,19 +428,6 @@ func identicalInstance(xorig Type, xargs []Type, yorig Type, yargs []Type) bool
        return Identical(xorig, yorig)
 }
 
-func identicalTParams(x, y []*TypeParam, cmpTags bool, p *ifacePair) bool {
-       if len(x) != len(y) {
-               return false
-       }
-       for i, x := range x {
-               y := y[i]
-               if !identical(x.bound, y.bound, cmpTags, p) {
-                       return false
-               }
-       }
-       return true
-}
-
 // Default returns the default "typed" type for an "untyped" type;
 // it returns the incoming type for all other types. The default type
 // for untyped nil is untyped nil.