go/types, types2: rename complete namedState to hasMethods

The complete namedState tracks whether an instantiated named type has
expanded all of its methods. In the past, this was the terminal state
of a linear lifecycle, and so a term like "complete" made sense.

Now that we've expanded the lifecycle of named types to a tree structure,
the complete namedState is no longer a terminal state, and so the term
"complete" is now a bit confusing.

This change a) makes the expansion aspect of the complete namedState more
explicit and b) removes a misleading suggestion of terminality by changing
the name from complete to hasMethods.

To take a similar naming convention with the underlying namedState, which
signals presence of Named.underlying, we rename the underlying namedState
to hasUnder.

Change-Id: I29fee26efea3de88c7c1240f2dc53df218acf8b4
Reviewed-on: https://go-review.googlesource.com/c/go/+/713280
Reviewed-by: Robert Griesemer <gri@google.com>
Auto-Submit: Mark Freeman <markfreeman@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Mark Freeman 2025-10-20 15:58:03 -04:00 committed by Gopher Robot
parent cf826bfcb4
commit 8401512a9b
2 changed files with 46 additions and 48 deletions

View file

@ -68,7 +68,6 @@ import (
// arguments from the instantiation. A type may be partially expanded if some // arguments from the instantiation. A type may be partially expanded if some
// but not all of these details have been substituted. Similarly, we refer to // but not all of these details have been substituted. Similarly, we refer to
// these individual details (RHS or method) as being "expanded". // these individual details (RHS or method) as being "expanded".
// - When all information is known for a named type, we say it is "complete".
// //
// Some invariants to keep in mind: each declared Named type has a single // Some invariants to keep in mind: each declared Named type has a single
// corresponding object, and that object's type is the (possibly generic) Named // corresponding object, and that object's type is the (possibly generic) Named
@ -85,8 +84,8 @@ import (
// presence of a cycle of named types, expansion will eventually find an // presence of a cycle of named types, expansion will eventually find an
// existing instance in the Context and short-circuit the expansion. // existing instance in the Context and short-circuit the expansion.
// //
// Once an instance is complete, we can nil out this shared Context to unpin // Once an instance is fully expanded, we can nil out this shared Context to unpin
// memory, though this Context may still be held by other incomplete instances // memory, though the Context may still be held by other incomplete instances
// in its "lineage". // in its "lineage".
// A Named represents a named (defined) type. // A Named represents a named (defined) type.
@ -145,11 +144,11 @@ type instance struct {
// unresolved // unresolved
// loaded // loaded
// resolved // resolved
// └── complete // └── hasMethods
// └── underlying // └── hasUnder
// //
// That is, descent down the tree is mostly linear (unresolved through resolved), except upon // That is, descent down the tree is mostly linear (unresolved through resolved), except upon
// reaching the leaves (complete and underlying). A type may occupy any combination of the // reaching the leaves (hasMethods and hasUnder). A type may occupy any combination of the
// leaf states at once (they are independent states). // leaf states at once (they are independent states).
// //
// To represent this independence, the set of active states is represented with a bit set. State // To represent this independence, the set of active states is represented with a bit set. State
@ -161,9 +160,9 @@ type instance struct {
// 0000 | unresolved // 0000 | unresolved
// 1000 | loaded // 1000 | loaded
// 1100 | resolved, which implies loaded // 1100 | resolved, which implies loaded
// 1110 | completed, which implies resolved (which in turn implies loaded) // 1110 | hasMethods, which implies resolved (which in turn implies loaded)
// 1101 | underlying, which implies resolved ... // 1101 | hasUnder, which implies resolved ...
// 1111 | both completed and underlying which implies resolved ... // 1111 | both hasMethods and hasUnder which implies resolved ...
// //
// To read the state of a named type, use [Named.stateHas]; to write, use [Named.setState]. // To read the state of a named type, use [Named.stateHas]; to write, use [Named.setState].
type stateMask uint32 type stateMask uint32
@ -171,9 +170,9 @@ type stateMask uint32
const ( const (
// before resolved, type parameters, RHS, underlying, and methods might be unavailable // before resolved, type parameters, RHS, underlying, and methods might be unavailable
resolved stateMask = 1 << iota // methods might be unexpanded (for instances) resolved stateMask = 1 << iota // methods might be unexpanded (for instances)
complete // methods are all expanded (for instances) hasMethods // methods are all expanded (for instances)
loaded // methods are available, but constraints might be unexpanded (for generic types) loaded // methods are available, but constraints might be unexpanded (for generic types)
underlying // underlying type is available hasUnder // underlying type is available
) )
// NewNamed returns a new named type for the given type name, underlying type, and associated methods. // NewNamed returns a new named type for the given type name, underlying type, and associated methods.
@ -229,7 +228,7 @@ func (n *Named) resolve() *Named {
} }
// underlying comes after resolving, do not set it // underlying comes after resolving, do not set it
defer (func() { assert(!n.stateHas(underlying)) })() defer (func() { assert(!n.stateHas(hasUnder)) })()
if n.inst != nil { if n.inst != nil {
assert(n.fromRHS == nil) // instantiated types are not declared types assert(n.fromRHS == nil) // instantiated types are not declared types
@ -242,7 +241,7 @@ func (n *Named) resolve() *Named {
n.tparams = orig.tparams n.tparams = orig.tparams
if len(orig.methods) == 0 { if len(orig.methods) == 0 {
n.setState(resolved | complete) // nothing further to do n.setState(resolved | hasMethods) // nothing further to do
n.inst.ctxt = nil n.inst.ctxt = nil
} else { } else {
n.setState(resolved) n.setState(resolved)
@ -274,7 +273,7 @@ func (n *Named) resolve() *Named {
} }
} }
n.setState(resolved | complete) n.setState(resolved | hasMethods)
return n return n
} }
@ -396,11 +395,11 @@ func (t *Named) NumMethods() int {
func (t *Named) Method(i int) *Func { func (t *Named) Method(i int) *Func {
t.resolve() t.resolve()
if t.stateHas(complete) { if t.stateHas(hasMethods) {
return t.methods[i] return t.methods[i]
} }
assert(t.inst != nil) // only instances should have incomplete methods assert(t.inst != nil) // only instances should have unexpanded methods
orig := t.inst.orig orig := t.inst.orig
t.mu.Lock() t.mu.Lock()
@ -417,9 +416,9 @@ func (t *Named) Method(i int) *Func {
t.inst.expandedMethods++ t.inst.expandedMethods++
// Check if we've created all methods at this point. If we have, mark the // Check if we've created all methods at this point. If we have, mark the
// type as fully expanded. // type as having all of its methods.
if t.inst.expandedMethods == len(orig.methods) { if t.inst.expandedMethods == len(orig.methods) {
t.setState(complete) t.setState(hasMethods)
t.inst.ctxt = nil // no need for a context anymore t.inst.ctxt = nil // no need for a context anymore
} }
} }
@ -502,11 +501,11 @@ func (t *Named) SetUnderlying(u Type) {
t.fromRHS = u t.fromRHS = u
t.allowNilRHS = false t.allowNilRHS = false
t.setState(resolved | complete) // TODO(markfreeman): Why complete? t.setState(resolved | hasMethods) // TODO(markfreeman): Why hasMethods?
t.underlying = u t.underlying = u
t.allowNilUnderlying = false t.allowNilUnderlying = false
t.setState(underlying) t.setState(hasUnder)
} }
// AddMethod adds method m unless it is already in the method list. // AddMethod adds method m unless it is already in the method list.
@ -562,7 +561,7 @@ func (n *Named) Underlying() Type {
} }
} }
if !n.stateHas(underlying) { if !n.stateHas(hasUnder) {
n.resolveUnderlying() n.resolveUnderlying()
} }
@ -617,7 +616,7 @@ func (n *Named) resolveUnderlying() {
} }
// avoid acquiring the lock if we can // avoid acquiring the lock if we can
if t.stateHas(underlying) { if t.stateHas(hasUnder) {
u = t.underlying u = t.underlying
break break
} }
@ -644,11 +643,11 @@ func (n *Named) resolveUnderlying() {
// Careful, t.underlying has lock-free readers. Since we might be racing // Careful, t.underlying has lock-free readers. Since we might be racing
// another call to resolveUnderlying, we have to avoid overwriting // another call to resolveUnderlying, we have to avoid overwriting
// t.underlying. Otherwise, the race detector will be tripped. // t.underlying. Otherwise, the race detector will be tripped.
if t.stateHas(underlying) { if t.stateHas(hasUnder) {
continue continue
} }
t.underlying = u t.underlying = u
t.setState(underlying) t.setState(hasUnder)
} }
} }

View file

@ -71,7 +71,6 @@ import (
// arguments from the instantiation. A type may be partially expanded if some // arguments from the instantiation. A type may be partially expanded if some
// but not all of these details have been substituted. Similarly, we refer to // but not all of these details have been substituted. Similarly, we refer to
// these individual details (RHS or method) as being "expanded". // these individual details (RHS or method) as being "expanded".
// - When all information is known for a named type, we say it is "complete".
// //
// Some invariants to keep in mind: each declared Named type has a single // Some invariants to keep in mind: each declared Named type has a single
// corresponding object, and that object's type is the (possibly generic) Named // corresponding object, and that object's type is the (possibly generic) Named
@ -88,8 +87,8 @@ import (
// presence of a cycle of named types, expansion will eventually find an // presence of a cycle of named types, expansion will eventually find an
// existing instance in the Context and short-circuit the expansion. // existing instance in the Context and short-circuit the expansion.
// //
// Once an instance is complete, we can nil out this shared Context to unpin // Once an instance is fully expanded, we can nil out this shared Context to unpin
// memory, though this Context may still be held by other incomplete instances // memory, though the Context may still be held by other incomplete instances
// in its "lineage". // in its "lineage".
// A Named represents a named (defined) type. // A Named represents a named (defined) type.
@ -148,11 +147,11 @@ type instance struct {
// unresolved // unresolved
// loaded // loaded
// resolved // resolved
// └── complete // └── hasMethods
// └── underlying // └── hasUnder
// //
// That is, descent down the tree is mostly linear (unresolved through resolved), except upon // That is, descent down the tree is mostly linear (unresolved through resolved), except upon
// reaching the leaves (complete and underlying). A type may occupy any combination of the // reaching the leaves (hasMethods and hasUnder). A type may occupy any combination of the
// leaf states at once (they are independent states). // leaf states at once (they are independent states).
// //
// To represent this independence, the set of active states is represented with a bit set. State // To represent this independence, the set of active states is represented with a bit set. State
@ -164,9 +163,9 @@ type instance struct {
// 0000 | unresolved // 0000 | unresolved
// 1000 | loaded // 1000 | loaded
// 1100 | resolved, which implies loaded // 1100 | resolved, which implies loaded
// 1110 | completed, which implies resolved (which in turn implies loaded) // 1110 | hasMethods, which implies resolved (which in turn implies loaded)
// 1101 | underlying, which implies resolved ... // 1101 | hasUnder, which implies resolved ...
// 1111 | both completed and underlying which implies resolved ... // 1111 | both hasMethods and hasUnder which implies resolved ...
// //
// To read the state of a named type, use [Named.stateHas]; to write, use [Named.setState]. // To read the state of a named type, use [Named.stateHas]; to write, use [Named.setState].
type stateMask uint32 type stateMask uint32
@ -174,9 +173,9 @@ type stateMask uint32
const ( const (
// before resolved, type parameters, RHS, underlying, and methods might be unavailable // before resolved, type parameters, RHS, underlying, and methods might be unavailable
resolved stateMask = 1 << iota // methods might be unexpanded (for instances) resolved stateMask = 1 << iota // methods might be unexpanded (for instances)
complete // methods are all expanded (for instances) hasMethods // methods are all expanded (for instances)
loaded // methods are available, but constraints might be unexpanded (for generic types) loaded // methods are available, but constraints might be unexpanded (for generic types)
underlying // underlying type is available hasUnder // underlying type is available
) )
// NewNamed returns a new named type for the given type name, underlying type, and associated methods. // NewNamed returns a new named type for the given type name, underlying type, and associated methods.
@ -232,7 +231,7 @@ func (n *Named) resolve() *Named {
} }
// underlying comes after resolving, do not set it // underlying comes after resolving, do not set it
defer (func() { assert(!n.stateHas(underlying)) })() defer (func() { assert(!n.stateHas(hasUnder)) })()
if n.inst != nil { if n.inst != nil {
assert(n.fromRHS == nil) // instantiated types are not declared types assert(n.fromRHS == nil) // instantiated types are not declared types
@ -245,7 +244,7 @@ func (n *Named) resolve() *Named {
n.tparams = orig.tparams n.tparams = orig.tparams
if len(orig.methods) == 0 { if len(orig.methods) == 0 {
n.setState(resolved | complete) // nothing further to do n.setState(resolved | hasMethods) // nothing further to do
n.inst.ctxt = nil n.inst.ctxt = nil
} else { } else {
n.setState(resolved) n.setState(resolved)
@ -277,7 +276,7 @@ func (n *Named) resolve() *Named {
} }
} }
n.setState(resolved | complete) n.setState(resolved | hasMethods)
return n return n
} }
@ -399,11 +398,11 @@ func (t *Named) NumMethods() int {
func (t *Named) Method(i int) *Func { func (t *Named) Method(i int) *Func {
t.resolve() t.resolve()
if t.stateHas(complete) { if t.stateHas(hasMethods) {
return t.methods[i] return t.methods[i]
} }
assert(t.inst != nil) // only instances should have incomplete methods assert(t.inst != nil) // only instances should have unexpanded methods
orig := t.inst.orig orig := t.inst.orig
t.mu.Lock() t.mu.Lock()
@ -420,9 +419,9 @@ func (t *Named) Method(i int) *Func {
t.inst.expandedMethods++ t.inst.expandedMethods++
// Check if we've created all methods at this point. If we have, mark the // Check if we've created all methods at this point. If we have, mark the
// type as fully expanded. // type as having all of its methods.
if t.inst.expandedMethods == len(orig.methods) { if t.inst.expandedMethods == len(orig.methods) {
t.setState(complete) t.setState(hasMethods)
t.inst.ctxt = nil // no need for a context anymore t.inst.ctxt = nil // no need for a context anymore
} }
} }
@ -505,11 +504,11 @@ func (t *Named) SetUnderlying(u Type) {
t.fromRHS = u t.fromRHS = u
t.allowNilRHS = false t.allowNilRHS = false
t.setState(resolved | complete) // TODO(markfreeman): Why complete? t.setState(resolved | hasMethods) // TODO(markfreeman): Why hasMethods?
t.underlying = u t.underlying = u
t.allowNilUnderlying = false t.allowNilUnderlying = false
t.setState(underlying) t.setState(hasUnder)
} }
// AddMethod adds method m unless it is already in the method list. // AddMethod adds method m unless it is already in the method list.
@ -565,7 +564,7 @@ func (n *Named) Underlying() Type {
} }
} }
if !n.stateHas(underlying) { if !n.stateHas(hasUnder) {
n.resolveUnderlying() n.resolveUnderlying()
} }
@ -620,7 +619,7 @@ func (n *Named) resolveUnderlying() {
} }
// avoid acquiring the lock if we can // avoid acquiring the lock if we can
if t.stateHas(underlying) { if t.stateHas(hasUnder) {
u = t.underlying u = t.underlying
break break
} }
@ -647,11 +646,11 @@ func (n *Named) resolveUnderlying() {
// Careful, t.underlying has lock-free readers. Since we might be racing // Careful, t.underlying has lock-free readers. Since we might be racing
// another call to resolveUnderlying, we have to avoid overwriting // another call to resolveUnderlying, we have to avoid overwriting
// t.underlying. Otherwise, the race detector will be tripped. // t.underlying. Otherwise, the race detector will be tripped.
if t.stateHas(underlying) { if t.stateHas(hasUnder) {
continue continue
} }
t.underlying = u t.underlying = u
t.setState(underlying) t.setState(hasUnder)
} }
} }