]> Cypherpunks repositories - gostls13.git/commitdiff
go/types, types2: don't lose alias information during unification
authorRobert Griesemer <gri@golang.org>
Tue, 28 May 2024 17:53:44 +0000 (10:53 -0700)
committerGopher Robot <gobot@golang.org>
Wed, 29 May 2024 02:09:51 +0000 (02:09 +0000)
While at it, rename asTypeParam to asBoundTypeParam for clarity.

For #67547.
Fixes #67628.

Change-Id: I2f447c4cd4d72f5315fe9323d82fcb9bf33657c6
Reviewed-on: https://go-review.googlesource.com/c/go/+/588797
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Commit-Queue: Robert Griesemer <gri@google.com>
Reviewed-by: Robert Findley <rfindley@google.com>
src/cmd/compile/internal/types2/unify.go
src/go/types/unify.go
src/internal/types/testdata/fixedbugs/issue67628.go [new file with mode: 0644]

index 6838f270c15a389934cbdf0000002116754d4eb6..8c91294d2b3b6b3420c4ea3327707ed65b739bdb 100644 (file)
@@ -205,10 +205,10 @@ func (u *unifier) join(x, y *TypeParam) bool {
        return true
 }
 
-// asTypeParam returns x.(*TypeParam) if x is a type parameter recorded with u.
+// asBoundTypeParam returns x.(*TypeParam) if x is a type parameter recorded with u.
 // Otherwise, the result is nil.
-func (u *unifier) asTypeParam(x Type) *TypeParam {
-       if x, _ := x.(*TypeParam); x != nil {
+func (u *unifier) asBoundTypeParam(x Type) *TypeParam {
+       if x, _ := Unalias(x).(*TypeParam); x != nil {
                if _, found := u.handles[x]; found {
                        return x
                }
@@ -269,7 +269,7 @@ func (u *unifier) inferred(tparams []*TypeParam) []Type {
 // asInterface returns the underlying type of x as an interface if
 // it is a non-type parameter interface. Otherwise it returns nil.
 func asInterface(x Type) (i *Interface) {
-       if _, ok := x.(*TypeParam); !ok {
+       if _, ok := Unalias(x).(*TypeParam); !ok {
                i, _ = under(x).(*Interface)
        }
        return i
@@ -291,11 +291,8 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
                u.depth--
        }()
 
-       x = Unalias(x)
-       y = Unalias(y)
-
        // nothing to do if x == y
-       if x == y {
+       if x == y || Unalias(x) == Unalias(y) {
                return true
        }
 
@@ -314,7 +311,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
        // Ensure that if we have at least one
        // - defined type, make sure one is in y
        // - type parameter recorded with u, make sure one is in x
-       if asNamed(x) != nil || u.asTypeParam(y) != nil {
+       if asNamed(x) != nil || u.asBoundTypeParam(y) != nil {
                if traceInference {
                        u.tracef("%s ≡ %s\t// swap", y, x)
                }
@@ -358,7 +355,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
        // isTypeLit(x) is false and y was not changed above. In other
        // words, if y was a defined type, it is still a defined type
        // (relevant for the logic below).
-       switch px, py := u.asTypeParam(x), u.asTypeParam(y); {
+       switch px, py := u.asBoundTypeParam(x), u.asBoundTypeParam(y); {
        case px != nil && py != nil:
                // both x and y are type parameters
                if u.join(px, py) {
@@ -449,7 +446,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
        }
 
        // x != y if we get here
-       assert(x != y)
+       assert(x != y && Unalias(x) != Unalias(y))
 
        // If u.EnableInterfaceInference is set and we don't require exact unification,
        // if both types are interfaces, one interface must have a subset of the
@@ -573,6 +570,10 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
                emode |= exact
        }
 
+       // Continue with unaliased types but don't lose original alias names, if any (go.dev/issue/67628).
+       xorig, x := x, Unalias(x)
+       yorig, y := y, Unalias(y)
+
        switch x := x.(type) {
        case *Basic:
                // Basic types are singletons except for the rune and byte
@@ -751,7 +752,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
        case *TypeParam:
                // x must be an unbound type parameter (see comment above).
                if debug {
-                       assert(u.asTypeParam(x) == nil)
+                       assert(u.asBoundTypeParam(x) == nil)
                }
                // By definition, a valid type argument must be in the type set of
                // the respective type constraint. Therefore, the type argument's
@@ -774,13 +775,13 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
                        // need to take care of that case separately.
                        if cx := coreType(x); cx != nil {
                                if traceInference {
-                                       u.tracef("core %s ≡ %s", x, y)
+                                       u.tracef("core %s ≡ %s", xorig, yorig)
                                }
                                // If y is a defined type, it may not match against cx which
                                // is an underlying type (incl. int, string, etc.). Use assign
                                // mode here so that the unifier automatically takes under(y)
                                // if necessary.
-                               return u.nify(cx, y, assign, p)
+                               return u.nify(cx, yorig, assign, p)
                        }
                }
                // x != y and there's nothing to do
@@ -789,7 +790,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
                // avoid a crash in case of nil type
 
        default:
-               panic(sprintf(nil, true, "u.nify(%s, %s, %d)", x, y, mode))
+               panic(sprintf(nil, true, "u.nify(%s, %s, %d)", xorig, yorig, mode))
        }
 
        return false
index b93f9966f87dc975ee27bb8cedef5dbffbb868ae..d8f1b4a5b7b8036c92800a3da9e1b5c1e412664f 100644 (file)
@@ -208,10 +208,10 @@ func (u *unifier) join(x, y *TypeParam) bool {
        return true
 }
 
-// asTypeParam returns x.(*TypeParam) if x is a type parameter recorded with u.
+// asBoundTypeParam returns x.(*TypeParam) if x is a type parameter recorded with u.
 // Otherwise, the result is nil.
-func (u *unifier) asTypeParam(x Type) *TypeParam {
-       if x, _ := x.(*TypeParam); x != nil {
+func (u *unifier) asBoundTypeParam(x Type) *TypeParam {
+       if x, _ := Unalias(x).(*TypeParam); x != nil {
                if _, found := u.handles[x]; found {
                        return x
                }
@@ -272,7 +272,7 @@ func (u *unifier) inferred(tparams []*TypeParam) []Type {
 // asInterface returns the underlying type of x as an interface if
 // it is a non-type parameter interface. Otherwise it returns nil.
 func asInterface(x Type) (i *Interface) {
-       if _, ok := x.(*TypeParam); !ok {
+       if _, ok := Unalias(x).(*TypeParam); !ok {
                i, _ = under(x).(*Interface)
        }
        return i
@@ -294,11 +294,8 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
                u.depth--
        }()
 
-       x = Unalias(x)
-       y = Unalias(y)
-
        // nothing to do if x == y
-       if x == y {
+       if x == y || Unalias(x) == Unalias(y) {
                return true
        }
 
@@ -317,7 +314,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
        // Ensure that if we have at least one
        // - defined type, make sure one is in y
        // - type parameter recorded with u, make sure one is in x
-       if asNamed(x) != nil || u.asTypeParam(y) != nil {
+       if asNamed(x) != nil || u.asBoundTypeParam(y) != nil {
                if traceInference {
                        u.tracef("%s ≡ %s\t// swap", y, x)
                }
@@ -361,7 +358,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
        // isTypeLit(x) is false and y was not changed above. In other
        // words, if y was a defined type, it is still a defined type
        // (relevant for the logic below).
-       switch px, py := u.asTypeParam(x), u.asTypeParam(y); {
+       switch px, py := u.asBoundTypeParam(x), u.asBoundTypeParam(y); {
        case px != nil && py != nil:
                // both x and y are type parameters
                if u.join(px, py) {
@@ -452,7 +449,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
        }
 
        // x != y if we get here
-       assert(x != y)
+       assert(x != y && Unalias(x) != Unalias(y))
 
        // If u.EnableInterfaceInference is set and we don't require exact unification,
        // if both types are interfaces, one interface must have a subset of the
@@ -576,6 +573,10 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
                emode |= exact
        }
 
+       // Continue with unaliased types but don't lose original alias names, if any (go.dev/issue/67628).
+       xorig, x := x, Unalias(x)
+       yorig, y := y, Unalias(y)
+
        switch x := x.(type) {
        case *Basic:
                // Basic types are singletons except for the rune and byte
@@ -754,7 +755,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
        case *TypeParam:
                // x must be an unbound type parameter (see comment above).
                if debug {
-                       assert(u.asTypeParam(x) == nil)
+                       assert(u.asBoundTypeParam(x) == nil)
                }
                // By definition, a valid type argument must be in the type set of
                // the respective type constraint. Therefore, the type argument's
@@ -777,13 +778,13 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
                        // need to take care of that case separately.
                        if cx := coreType(x); cx != nil {
                                if traceInference {
-                                       u.tracef("core %s ≡ %s", x, y)
+                                       u.tracef("core %s ≡ %s", xorig, yorig)
                                }
                                // If y is a defined type, it may not match against cx which
                                // is an underlying type (incl. int, string, etc.). Use assign
                                // mode here so that the unifier automatically takes under(y)
                                // if necessary.
-                               return u.nify(cx, y, assign, p)
+                               return u.nify(cx, yorig, assign, p)
                        }
                }
                // x != y and there's nothing to do
@@ -792,7 +793,7 @@ func (u *unifier) nify(x, y Type, mode unifyMode, p *ifacePair) (result bool) {
                // avoid a crash in case of nil type
 
        default:
-               panic(sprintf(nil, nil, true, "u.nify(%s, %s, %d)", x, y, mode))
+               panic(sprintf(nil, nil, true, "u.nify(%s, %s, %d)", xorig, yorig, mode))
        }
 
        return false
diff --git a/src/internal/types/testdata/fixedbugs/issue67628.go b/src/internal/types/testdata/fixedbugs/issue67628.go
new file mode 100644 (file)
index 0000000..dff1bee
--- /dev/null
@@ -0,0 +1,17 @@
+// -gotypesalias=1
+
+// Copyright 2024 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+func f[P any](x P) P { return x }
+
+func _() {
+       type A = int
+       var a A
+       b := f(a) // type of b is A
+       // error should report type of b as A, not int
+       _ = b /* ERROR "mismatched types A and untyped string" */ + "foo"
+}