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>
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
}
// 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
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
}
// 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)
}
// 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) {
}
// 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
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
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
// 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
// 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
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
}
// 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
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
}
// 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)
}
// 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) {
}
// 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
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
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
// 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
// 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
--- /dev/null
+// -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"
+}