From: Robert Griesemer Date: Tue, 28 May 2024 17:53:44 +0000 (-0700) Subject: go/types, types2: don't lose alias information during unification X-Git-Tag: go1.23rc1~126 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=ee29dbe43eeb28a2834803e8e1275f1dccb18bc2;p=gostls13.git go/types, types2: don't lose alias information during unification 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 Auto-Submit: Robert Griesemer LUCI-TryBot-Result: Go LUCI Commit-Queue: Robert Griesemer Reviewed-by: Robert Findley --- diff --git a/src/cmd/compile/internal/types2/unify.go b/src/cmd/compile/internal/types2/unify.go index 6838f270c1..8c91294d2b 100644 --- a/src/cmd/compile/internal/types2/unify.go +++ b/src/cmd/compile/internal/types2/unify.go @@ -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 diff --git a/src/go/types/unify.go b/src/go/types/unify.go index b93f9966f8..d8f1b4a5b7 100644 --- a/src/go/types/unify.go +++ b/src/go/types/unify.go @@ -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 index 0000000000..dff1bee4a0 --- /dev/null +++ b/src/internal/types/testdata/fixedbugs/issue67628.go @@ -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" +}