Source File
named.go
Belonging Package
go/types
// Code generated by "go test -run=Generate -write=all"; DO NOT EDIT.// Source: ../../cmd/compile/internal/types2/named.go// Copyright 2011 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 typesimport ()// Type-checking Named types is subtle, because they may be recursively// defined, and because their full details may be spread across multiple// declarations (via methods). For this reason they are type-checked lazily,// to avoid information being accessed before it is complete.//// Conceptually, it is helpful to think of named types as having two distinct// sets of information:// - "LHS" information, defining their identity: Obj() and TypeArgs()// - "RHS" information, defining their details: TypeParams(), Underlying(),// and methods.//// In this taxonomy, LHS information is available immediately, but RHS// information is lazy. Specifically, a named type N may be constructed in any// of the following ways:// 1. type-checked from the source// 2. loaded eagerly from export data// 3. loaded lazily from export data (when using unified IR)// 4. instantiated from a generic type//// In cases 1, 3, and 4, it is possible that the underlying type or methods of// N may not be immediately available.// - During type-checking, we allocate N before type-checking its underlying// type or methods, so that we may resolve recursive references.// - When loading from export data, we may load its methods and underlying// type lazily using a provided load function.// - After instantiating, we lazily expand the underlying type and methods// (note that instances may be created while still in the process of// type-checking the original type declaration).//// In cases 3 and 4 this lazy construction may also occur concurrently, due to// concurrent use of the type checker API (after type checking or importing has// finished). It is critical that we keep track of state, so that Named types// are constructed exactly once and so that we do not access their details too// soon.//// We achieve this by tracking state with an atomic state variable, and// guarding potentially concurrent calculations with a mutex. At any point in// time this state variable determines which data on N may be accessed. As// state monotonically progresses, any data available at state M may be// accessed without acquiring the mutex at state N, provided N >= M.//// GLOSSARY: Here are a few terms used in this file to describe Named types:// - We say that a Named type is "instantiated" if it has been constructed by// instantiating a generic named type with type arguments.// - We say that a Named type is "declared" if it corresponds to a type// declaration in the source. Instantiated named types correspond to a type// instantiation in the source, not a declaration. But their Origin type is// a declared type.// - We say that a Named type is "resolved" if its RHS information has been// loaded or fully type-checked. For Named types constructed from export// data, this may involve invoking a loader function to extract information// from export data. For instantiated named types this involves reading// information from their origin.// - We say that a Named type is "expanded" if it is an instantiated type and// type parameters in its underlying type and methods have been substituted// with the type 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 these individual details (underlying type 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// corresponding object, and that object's type is the (possibly generic) Named// type. Declared Named types are identical if and only if their pointers are// identical. On the other hand, multiple instantiated Named types may be// identical even though their pointers are not identical. One has to use// Identical to compare them. For instantiated named types, their obj is a// synthetic placeholder that records their position of the corresponding// instantiation in the source (if they were constructed during type checking).//// To prevent infinite expansion of named instances that are created outside of// type-checking, instances share a Context with other instances created during// their expansion. Via the pidgeonhole principle, this guarantees that in the// presence of a cycle of named types, expansion will eventually find an// existing instance in the Context and short-circuit the expansion.//// Once an instance is complete, we can nil out this shared Context to unpin// memory, though this Context may still be held by other incomplete instances// in its "lineage".// A Named represents a named (defined) type.//// A declaration such as://// type S struct { ... }//// creates a defined type whose underlying type is a struct,// and binds this type to the object S, a [TypeName].// Use [Named.Underlying] to access the underlying type.// Use [Named.Obj] to obtain the object S.//// Before type aliases (Go 1.9), the spec called defined types "named types".type Named struct {check *Checker // non-nil during type-checking; nil otherwiseobj *TypeName // corresponding declared object for declared types; see above for instantiated types// fromRHS holds the type (on RHS of declaration) this *Named type is derived// from (for cycle reporting). Only used by validType, and therefore does not// require synchronization.fromRHS Type// information for instantiated types; nil otherwiseinst *instancemu sync.Mutex // guards all fields belowstate_ uint32 // the current state of this type; must only be accessed atomicallyunderlying Type // possibly a *Named during setup; never a *Named once set up completelytparams *TypeParamList // type parameters, or nil// methods declared for this type (not the method set of this type)// Signatures are type-checked lazily.// For non-instantiated types, this is a fully populated list of methods. For// instantiated types, methods are individually expanded when they are first// accessed.methods []*Func// loader may be provided to lazily load type parameters, underlying type, and methods.loader func(*Named) (tparams []*TypeParam, underlying Type, methods []*Func)}// instance holds information that is only necessary for instantiated named// types.type instance struct {orig *Named // original, uninstantiated typetargs *TypeList // type argumentsexpandedMethods int // number of expanded methods; expandedMethods <= len(orig.methods)ctxt *Context // local Context; set to nil after full expansion}// namedState represents the possible states that a named type may assume.type namedState uint32const (unresolved namedState = iota // tparams, underlying type and methods might be unavailableresolved // resolve has run; methods might be incomplete (for instances)complete // all data is known)// NewNamed returns a new named type for the given type name, underlying type, and associated methods.// If the given type name obj doesn't have a type yet, its type is set to the returned named type.// The underlying type must not be a *Named.func ( *TypeName, Type, []*Func) *Named {if asNamed() != nil {panic("underlying type must not be *Named")}return (*Checker)(nil).newNamed(, , )}// resolve resolves the type parameters, methods, and underlying type of n.// This information may be loaded from a provided loader function, or computed// from an origin type (in the case of instances).//// After resolution, the type parameters, methods, and underlying type of n are// accessible; but if n is an instantiated type, its methods may still be// unexpanded.func ( *Named) () *Named {if .state() >= resolved { // avoid locking belowreturn}// TODO(rfindley): if n.check is non-nil we can avoid locking here, since// type-checking is not concurrent. Evaluate if this is worth doing..mu.Lock()defer .mu.Unlock()if .state() >= resolved {return}if .inst != nil {assert(.underlying == nil) // n is an unresolved instanceassert(.loader == nil) // instances are created by instantiation, in which case n.loader is nil:= .inst.orig.():= .expandUnderlying().tparams = .tparams.underlying =.fromRHS = .fromRHS // for cycle detectionif len(.methods) == 0 {.setState(complete) // nothing further to do.inst.ctxt = nil} else {.setState(resolved)}return}// TODO(mdempsky): Since we're passing n to the loader anyway// (necessary because types2 expects the receiver type for methods// on defined interface types to be the Named rather than the// underlying Interface), maybe it should just handle calling// SetTypeParams, SetUnderlying, and AddMethod instead? Those// methods would need to support reentrant calls though. It would// also make the API more future-proof towards further extensions.if .loader != nil {assert(.underlying == nil)assert(.TypeArgs().Len() == 0) // instances are created by instantiation, in which case n.loader is nil, , := .loader().tparams = bindTParams().underlying =.fromRHS = // for cycle detection.methods =.loader = nil}.setState(complete)return}// state atomically accesses the current state of the receiver.func ( *Named) () namedState {return namedState(atomic.LoadUint32(&.state_))}// setState atomically stores the given state for n.// Must only be called while holding n.mu.func ( *Named) ( namedState) {atomic.StoreUint32(&.state_, uint32())}// newNamed is like NewNamed but with a *Checker receiver.func ( *Checker) ( *TypeName, Type, []*Func) *Named {:= &Named{check: , obj: , fromRHS: , underlying: , methods: }if .typ == nil {.typ =}// Ensure that typ is always sanity-checked.if != nil {.needsCleanup()}return}// newNamedInstance creates a new named instance for the given origin and type// arguments, recording pos as the position of its synthetic object (for error// reporting).//// If set, expanding is the named type instance currently being expanded, that// led to the creation of this instance.func ( *Checker) ( token.Pos, *Named, []Type, *Named) *Named {assert(len() > 0):= NewTypeName(, .obj.pkg, .obj.name, nil):= &instance{orig: , targs: newTypeList()}// Only pass the expanding context to the new instance if their packages// match. Since type reference cycles are only possible within a single// package, this is sufficient for the purposes of short-circuiting cycles.// Avoiding passing the context in other cases prevents unnecessary coupling// of types across packages.if != nil && .Obj().pkg == .pkg {.ctxt = .inst.ctxt}:= &Named{check: , obj: , inst: }.typ =// Ensure that typ is always sanity-checked.if != nil {.needsCleanup()}return}func ( *Named) () {assert(.inst == nil || .inst.orig.inst == nil)// Ensure that every defined type created in the course of type-checking has// either non-*Named underlying type, or is unexpanded.//// This guarantees that we don't leak any types whose underlying type is// *Named, because any unexpanded instances will lazily compute their// underlying type by substituting in the underlying type of their origin.// The origin must have either been imported or type-checked and expanded// here, and in either case its underlying type will be fully expanded.switch .underlying.(type) {case nil:if .TypeArgs().Len() == 0 {panic("nil underlying")}case *Named, *Alias:.under() // t.under may add entries to check.cleaners}.check = nil}// Obj returns the type name for the declaration defining the named type t. For// instantiated types, this is same as the type name of the origin type.func ( *Named) () *TypeName {if .inst == nil {return .obj}return .inst.orig.obj}// Origin returns the generic type from which the named type t is// instantiated. If t is not an instantiated type, the result is t.func ( *Named) () *Named {if .inst == nil {return}return .inst.orig}// TypeParams returns the type parameters of the named type t, or nil.// The result is non-nil for an (originally) generic type even if it is instantiated.func ( *Named) () *TypeParamList { return .resolve().tparams }// SetTypeParams sets the type parameters of the named type t.// t must not have type arguments.func ( *Named) ( []*TypeParam) {assert(.inst == nil).resolve().tparams = bindTParams()}// TypeArgs returns the type arguments used to instantiate the named type t.func ( *Named) () *TypeList {if .inst == nil {return nil}return .inst.targs}// NumMethods returns the number of explicit methods defined for t.func ( *Named) () int {return len(.Origin().resolve().methods)}// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().//// For an ordinary or instantiated type t, the receiver base type of this// method is the named type t. For an uninstantiated generic type t, each// method receiver is instantiated with its receiver type parameters.//// Methods are numbered deterministically: given the same list of source files// presented to the type checker, or the same sequence of NewMethod and AddMethod// calls, the mapping from method index to corresponding method remains the same.// But the specific ordering is not specified and must not be relied on as it may// change in the future.func ( *Named) ( int) *Func {.resolve()if .state() >= complete {return .methods[]}assert(.inst != nil) // only instances should have incomplete methods:= .inst.orig.mu.Lock()defer .mu.Unlock()if len(.methods) != len(.methods) {assert(len(.methods) == 0).methods = make([]*Func, len(.methods))}if .methods[] == nil {assert(.inst.ctxt != nil) // we should still have a context remaining from the resolution phase.methods[] = .expandMethod().inst.expandedMethods++// Check if we've created all methods at this point. If we have, mark the// type as fully expanded.if .inst.expandedMethods == len(.methods) {.setState(complete).inst.ctxt = nil // no need for a context anymore}}return .methods[]}// expandMethod substitutes type arguments in the i'th method for an// instantiated receiver.func ( *Named) ( int) *Func {// t.orig.methods is not lazy. origm is the method instantiated with its// receiver type parameters (the "origin" method).:= .inst.orig.Method()assert( != nil):= .check// Ensure that the original method is type-checked.if != nil {.objDecl(, nil)}:= .typ.(*Signature), := deref(.Recv().Type())// If rbase is t, then origm is already the instantiated method we're looking// for. In this case, we return origm to preserve the invariant that// traversing Method->Receiver Type->Method should get back to the same// method.//// This occurs if t is instantiated with the receiver type parameters, as in// the use of m in func (r T[_]) m() { r.m() }.if == {return}:=// We can only substitute if we have a correspondence between type arguments// and type parameters. This check is necessary in the presence of invalid// code.if .RecvTypeParams().Len() == .inst.targs.Len() {:= makeSubstMap(.RecvTypeParams().list(), .inst.targs.list())var *Contextif != nil {= .context()}= .subst(.pos, , , , ).(*Signature)}if == {// No substitution occurred, but we still need to create a new signature to// hold the instantiated receiver.:= *= &}var Typeif .hasPtrRecv() {= NewPointer()} else {=}.recv = cloneVar(.recv, )return cloneFunc(, )}// SetUnderlying sets the underlying type and marks t as complete.// t must not have type arguments.func ( *Named) ( Type) {assert(.inst == nil)if == nil {panic("underlying type must not be nil")}if asNamed() != nil {panic("underlying type must not be *Named")}.resolve().underlying =if .fromRHS == nil {.fromRHS = // for cycle detection}}// AddMethod adds method m unless it is already in the method list.// The method must be in the same package as t, and t must not have// type arguments.func ( *Named) ( *Func) {assert(samePkg(.obj.pkg, .pkg))assert(.inst == nil).resolve()if .methodIndex(.name, false) < 0 {.methods = append(.methods, )}}// methodIndex returns the index of the method with the given name.// If foldCase is set, capitalization in the name is ignored.// The result is negative if no such method exists.func ( *Named) ( string, bool) int {if == "_" {return -1}if {for , := range .methods {if strings.EqualFold(.name, ) {return}}} else {for , := range .methods {if .name == {return}}}return -1}// Underlying returns the [underlying type] of the named type t, resolving all// forwarding declarations. Underlying types are never Named, TypeParam, or// Alias types.//// [underlying type]: https://go.dev/ref/spec#Underlying_types.func ( *Named) () Type {// TODO(gri) Investigate if Unalias can be moved to where underlying is set.return Unalias(.resolve().underlying)}func ( *Named) () string { return TypeString(, nil) }// ----------------------------------------------------------------------------// Implementation//// TODO(rfindley): reorganize the loading and expansion methods under this// heading.// under returns the expanded underlying type of n0; possibly by following// forward chains of named types. If an underlying type is found, resolve// the chain by setting the underlying type for each defined type in the// chain before returning it. If no underlying type is found or a cycle// is detected, the result is Typ[Invalid]. If a cycle is detected and// n0.check != nil, the cycle is reported.//// This is necessary because the underlying type of named may be itself a// named type that is incomplete://// type (// A B// B *C// C A// )//// The type of C is the (named) type of A which is incomplete,// and which has as its underlying type the named type B.func ( *Named) () Type {:= .Underlying()// If the underlying type of a defined type is not a defined// (incl. instance) type, then that is the desired underlying// type.var *Namedswitch u1 := .(type) {case nil:// After expansion via Underlying(), we should never encounter a nil// underlying.panic("nil underlying")default:// common casereturncase *Named:// handled below=}if .check == nil {panic("Named.check == nil but type is incomplete")}// Invariant: after this point n0 as well as any named types in its// underlying chain should be set up when this function exits.:= .check:=:= make(map[*Named]int) // types that need their underlying type resolvedvar []Object // objects encountered, for cycle reporting:for {[] = len()= append(, .obj)=if , := []; {// cycle.cycleError([:], firstInSrc([:]))= Typ[Invalid]break}= .Underlying()switch u1 := .(type) {case nil:= Typ[Invalid]breakdefault:breakcase *Named:// Continue collecting *Named types in the chain.=}}for := range {// We should never have to update the underlying type of an imported type;// those underlying types should have been resolved during the import.// Also, doing so would lead to a race condition (was go.dev/issue/31749).// Do this check always, not just in debug mode (it's cheap).if .obj.pkg != .pkg {panic("imported type with unresolved underlying type")}.underlying =}return}func ( *Named) ( *Package, string, bool) (int, *Func) {.resolve()if samePkg(.obj.pkg, ) || isExported() || {// If n is an instance, we may not have yet instantiated all of its methods.// Look up the method index in orig, and only instantiate method at the// matching index (if any).if := .Origin().methodIndex(, ); >= 0 {// For instances, m.Method(i) will be different from the orig method.return , .Method()}}return -1, nil}// context returns the type-checker context.func ( *Checker) () *Context {if .ctxt == nil {.ctxt = NewContext()}return .ctxt}// expandUnderlying substitutes type arguments in the underlying type n.orig,// returning the result. Returns Typ[Invalid] if there was an error.func ( *Named) () Type {:= .checkif != nil && .conf._Trace {.trace(.obj.pos, "-- Named.expandUnderlying %s", ).indent++defer func() {.indent--.trace(.obj.pos, "=> %s (tparams = %s, under = %s)", , .tparams.list(), .underlying)}()}assert(.inst.orig.underlying != nil)if .inst.ctxt == nil {.inst.ctxt = NewContext()}:= .inst.orig:= .inst.targsif asNamed(.underlying) != nil {// We should only get a Named underlying type here during type checking// (for example, in recursive type declarations).assert( != nil)}if .tparams.Len() != .Len() {// Mismatching arg and tparam length may be checked elsewhere.return Typ[Invalid]}// Ensure that an instance is recorded before substituting, so that we// resolve n for any recursive references.:= .inst.ctxt.instanceHash(, .list()):= .inst.ctxt.update(, , .TypeArgs().list(), )assert( == ):= makeSubstMap(.tparams.list(), .list())var *Contextif != nil {= .context()}:= .check.subst(.obj.pos, .underlying, , , )// If the underlying type of n is an interface, we need to set the receiver of// its methods accurately -- we set the receiver of interface methods on// the RHS of a type declaration to the defined type.if , := .(*Interface); != nil {if , := replaceRecvType(.methods, , ); {// If the underlying type doesn't actually use type parameters, it's// possible that it wasn't substituted. In this case we need to create// a new *Interface before modifying receivers.if == .underlying {:== .newInterface().embeddeds = .embeddedsassert(.complete) // otherwise we are copying incomplete data.complete = .complete.implicit = .implicit // should be false but be conservative=}.methods =.tset = nil // recompute type set with new methods// If check != nil, check.newInterface will have saved the interface for later completion.if == nil { // golang/go#61561: all newly created interfaces must be fully evaluated.typeSet()}}}return}// safeUnderlying returns the underlying type of typ without expanding// instances, to avoid infinite recursion.//// TODO(rfindley): eliminate this function or give it a better name.func ( Type) Type {if := asNamed(); != nil {return .underlying}return .Underlying()}
The pages are generated with Golds v0.7.6. (GOOS=linux GOARCH=amd64)