go/types, types2: wrap Named.fromRHS into Named.rhs

In debug mode, the Named.rhs method asserts that Named is in a state
with Named.fromRHS populated.

This caught a missing call to Named.unpack in validtype, which has been
added.

Change-Id: Id3f95f78f03d98a6efe87af6ac24f2ac2e285f96
Reviewed-on: https://go-review.googlesource.com/c/go/+/714242
Auto-Submit: Mark Freeman <markfreeman@google.com>
Reviewed-by: Robert Griesemer <gri@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Mark Freeman 2025-10-23 12:15:21 -04:00 committed by Gopher Robot
parent b2af92270f
commit d8a32f3d4b
4 changed files with 36 additions and 14 deletions

View file

@ -562,6 +562,16 @@ func (t *Named) methodIndex(name string, foldCase bool) int {
return -1
}
// rhs returns [Named.fromRHS].
//
// In debug mode, it also asserts that n is in an appropriate state.
func (n *Named) rhs() Type {
if debug {
assert(n.stateHas(lazyLoaded | unpacked))
}
return n.fromRHS
}
// Underlying returns the [underlying type] of the named type t, resolving all
// forwarding declarations. Underlying types are never Named, TypeParam, or
// Alias types.
@ -573,7 +583,7 @@ func (n *Named) Underlying() Type {
// The gccimporter depends on writing a nil underlying via NewNamed and
// immediately reading it back. Rather than putting that in Named.under
// and complicating things there, we just check for that special case here.
if n.fromRHS == nil {
if n.rhs() == nil {
assert(n.allowNilRHS)
if n.allowNilUnderlying {
return nil
@ -649,8 +659,8 @@ func (n *Named) resolveUnderlying() {
t.mu.Lock()
defer t.mu.Unlock()
assert(t.fromRHS != nil || t.allowNilRHS)
rhs = t.fromRHS
assert(t.rhs() != nil || t.allowNilRHS)
rhs = t.rhs()
default:
u = rhs // any type literal works
@ -735,7 +745,7 @@ func (n *Named) expandRHS() (rhs Type) {
}
assert(!n.stateHas(unpacked))
assert(n.inst.orig.stateHas(unpacked | lazyLoaded))
assert(n.inst.orig.stateHas(lazyLoaded | unpacked))
if n.inst.ctxt == nil {
n.inst.ctxt = NewContext()
@ -760,7 +770,7 @@ func (n *Named) expandRHS() (rhs Type) {
ctxt = check.context()
}
rhs = check.subst(n.obj.pos, orig.fromRHS, m, n, ctxt)
rhs = check.subst(n.obj.pos, orig.rhs(), m, n, ctxt)
// TODO(markfreeman): Can we handle this in substitution?
// If the RHS is an interface, we must set the receiver of interface methods
@ -769,7 +779,7 @@ func (n *Named) expandRHS() (rhs Type) {
if methods, copied := replaceRecvType(iface.methods, orig, n); copied {
// If the RHS doesn't use type parameters, it may not have been
// substituted; we need to craft a new interface first.
if iface == orig.fromRHS {
if iface == orig.rhs() {
assert(iface.complete) // otherwise we are copying incomplete data
crafted := check.newInterface()

View file

@ -141,7 +141,8 @@ func (check *Checker) validType0(pos syntax.Pos, typ Type, nest, path []*Named)
// Every type added to nest is also added to path; thus every type that is in nest
// must also be in path (invariant). But not every type in path is in nest, since
// nest may be pruned (see below, *TypeParam case).
if !check.validType0(pos, t.Origin().fromRHS, append(nest, t), append(path, t)) {
t.Origin().unpack()
if !check.validType0(pos, t.Origin().rhs(), append(nest, t), append(path, t)) {
return false
}

View file

@ -565,6 +565,16 @@ func (t *Named) methodIndex(name string, foldCase bool) int {
return -1
}
// rhs returns [Named.fromRHS].
//
// In debug mode, it also asserts that n is in an appropriate state.
func (n *Named) rhs() Type {
if debug {
assert(n.stateHas(lazyLoaded | unpacked))
}
return n.fromRHS
}
// Underlying returns the [underlying type] of the named type t, resolving all
// forwarding declarations. Underlying types are never Named, TypeParam, or
// Alias types.
@ -576,7 +586,7 @@ func (n *Named) Underlying() Type {
// The gccimporter depends on writing a nil underlying via NewNamed and
// immediately reading it back. Rather than putting that in Named.under
// and complicating things there, we just check for that special case here.
if n.fromRHS == nil {
if n.rhs() == nil {
assert(n.allowNilRHS)
if n.allowNilUnderlying {
return nil
@ -652,8 +662,8 @@ func (n *Named) resolveUnderlying() {
t.mu.Lock()
defer t.mu.Unlock()
assert(t.fromRHS != nil || t.allowNilRHS)
rhs = t.fromRHS
assert(t.rhs() != nil || t.allowNilRHS)
rhs = t.rhs()
default:
u = rhs // any type literal works
@ -738,7 +748,7 @@ func (n *Named) expandRHS() (rhs Type) {
}
assert(!n.stateHas(unpacked))
assert(n.inst.orig.stateHas(unpacked | lazyLoaded))
assert(n.inst.orig.stateHas(lazyLoaded | unpacked))
if n.inst.ctxt == nil {
n.inst.ctxt = NewContext()
@ -763,7 +773,7 @@ func (n *Named) expandRHS() (rhs Type) {
ctxt = check.context()
}
rhs = check.subst(n.obj.pos, orig.fromRHS, m, n, ctxt)
rhs = check.subst(n.obj.pos, orig.rhs(), m, n, ctxt)
// TODO(markfreeman): Can we handle this in substitution?
// If the RHS is an interface, we must set the receiver of interface methods
@ -772,7 +782,7 @@ func (n *Named) expandRHS() (rhs Type) {
if methods, copied := replaceRecvType(iface.methods, orig, n); copied {
// If the RHS doesn't use type parameters, it may not have been
// substituted; we need to craft a new interface first.
if iface == orig.fromRHS {
if iface == orig.rhs() {
assert(iface.complete) // otherwise we are copying incomplete data
crafted := check.newInterface()

View file

@ -144,7 +144,8 @@ func (check *Checker) validType0(pos token.Pos, typ Type, nest, path []*Named) b
// Every type added to nest is also added to path; thus every type that is in nest
// must also be in path (invariant). But not every type in path is in nest, since
// nest may be pruned (see below, *TypeParam case).
if !check.validType0(pos, t.Origin().fromRHS, append(nest, t), append(path, t)) {
t.Origin().unpack()
if !check.validType0(pos, t.Origin().rhs(), append(nest, t), append(path, t)) {
return false
}