Source File
named.go
Belonging Package
go/types
// 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 types
import (
)
// A Named represents a named (defined) type.
type Named struct {
check *Checker
obj *TypeName // corresponding declared object for declared types; placeholder for instantiated types
orig *Named // original, uninstantiated type
fromRHS Type // type (on RHS of declaration) this *Named type is derived of (for cycle reporting)
underlying Type // possibly a *Named during setup; never a *Named once set up completely
tparams *TypeParamList // type parameters, or nil
targs *TypeList // type arguments (after instantiation), 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, this is a 'lazy' list, and methods are instantiated
// when they are first accessed.
methods *methodList
// resolver may be provided to lazily resolve type parameters, underlying, and methods.
resolver func(*Context, *Named) (tparams *TypeParamList, underlying Type, methods *methodList)
once sync.Once // ensures that tparams, underlying, and methods are resolved before accessing
}
// 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 , := .(*Named); {
panic("underlying type must not be *Named")
}
return (*Checker)(nil).newNamed(, nil, , nil, newMethodList())
}
func ( *Named) ( *Context) *Named {
if .resolver == nil {
return
}
.once.Do(func() {
// TODO(mdempsky): Since we're passing t to the resolver 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
// (like SetTypeParams).
.tparams, .underlying, .methods = .resolver(, )
.fromRHS = .underlying // for cycle detection
})
return
}
// newNamed is like NewNamed but with a *Checker receiver and additional orig argument.
func ( *Checker) ( *TypeName, *Named, Type, *TypeParamList, *methodList) *Named {
:= &Named{check: , obj: , orig: , fromRHS: , underlying: , tparams: , methods: }
if .orig == nil {
.orig =
}
if .typ == nil {
.typ =
}
// Ensure that typ is always expanded and sanity-checked.
if != nil {
.needsCleanup()
}
return
}
func ( *Named) () {
// Ensure that every defined type created in the course of type-checking has
// either non-*Named underlying, or is unresolved.
//
// This guarantees that we don't leak any types whose underlying is *Named,
// because any unresolved instances will lazily compute their underlying by
// substituting in the underlying of their origin. The origin must have
// either been imported or type-checked and expanded here, and in either case
// its underlying will be fully expanded.
switch .underlying.(type) {
case nil:
if .resolver == nil {
panic("nil underlying")
}
case *Named:
.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 {
return .orig.obj // for non-instances this is the same as t.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 { return .orig }
// TODO(gri) Come up with a better representation and API to distinguish
// between parameterized instantiated and non-instantiated types.
// 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(nil).tparams }
// SetTypeParams sets the type parameters of the named type t.
// t must not have type arguments.
func ( *Named) ( []*TypeParam) {
assert(.targs.Len() == 0)
.resolve(nil).tparams = bindTParams()
}
// TypeArgs returns the type arguments used to instantiate the named type t.
func ( *Named) () *TypeList { return .targs }
// NumMethods returns the number of explicit methods defined for t.
//
// For an ordinary or instantiated type t, the receiver base type of these
// methods will be the named type t. For an uninstantiated generic type t, each
// method receiver will be instantiated with its receiver type parameters.
func ( *Named) () int { return .resolve(nil).methods.Len() }
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
func ( *Named) ( int) *Func {
.resolve(nil)
return .methods.At(, func() *Func {
return .instantiateMethod()
})
}
// instiateMethod instantiates the i'th method for an instantiated receiver.
func ( *Named) ( int) *Func {
assert(.TypeArgs().Len() > 0) // t must be an instance
// t.orig.methods is not lazy. origm is the method instantiated with its
// receiver type parameters (the "origin" method).
:= .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() == .targs.Len() {
:= .bestContext(nil)
:= makeSubstMap(.RecvTypeParams().list(), .targs.list())
= .subst(.pos, , , ).(*Signature)
}
if == {
// No substitution occurred, but we still need to create a new signature to
// hold the instantiated receiver.
:= *
= &
}
var Type
if .hasPtrRecv() {
= NewPointer()
} else {
=
}
.recv = substVar(.recv, )
return NewFunc(.pos, .pkg, .name, )
}
// SetUnderlying sets the underlying type and marks t as complete.
// t must not have type arguments.
func ( *Named) ( Type) {
assert(.targs.Len() == 0)
if == nil {
panic("underlying type must not be nil")
}
if , := .(*Named); {
panic("underlying type must not be *Named")
}
.resolve(nil).underlying =
if .fromRHS == nil {
.fromRHS = // for cycle detection
}
}
// AddMethod adds method m unless it is already in the method list.
// t must not have type arguments.
func ( *Named) ( *Func) {
assert(.targs.Len() == 0)
.resolve(nil)
if .methods == nil {
.methods = newMethodList(nil)
}
.methods.Add()
}
func ( *Named) () Type { return .resolve(nil).underlying }
func ( *Named) () string { return TypeString(, nil) }
// ----------------------------------------------------------------------------
// Implementation
// 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 *Named
switch u1 := .(type) {
case nil:
// After expansion via Underlying(), we should never encounter a nil
// underlying.
panic("nil underlying")
default:
// common case
return
case *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 resolved
var []Object // objects encountered, for cycle reporting
:
for {
[] = len()
= append(, .obj)
=
if , := []; {
// cycle
.cycleError([:])
= Typ[Invalid]
break
}
= .Underlying()
switch u1 := .(type) {
case nil:
= Typ[Invalid]
break
default:
break
case *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 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) ( Type) {
if != nil {
.underlying =
}
}
func ( *Named) ( *Package, string, bool) (int, *Func) {
.resolve(nil)
// 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).
, := .orig.methods.Lookup(, , )
if < 0 {
return -1, nil
}
// For instances, m.Method(i) will be different from the orig method.
return , .Method()
}
// bestContext returns the best available context. In order of preference:
// - the given ctxt, if non-nil
// - check.ctxt, if check is non-nil
// - a new Context
func ( *Checker) ( *Context) *Context {
if != nil {
return
}
if != nil {
if .ctxt == nil {
.ctxt = NewContext()
}
return .ctxt
}
return NewContext()
}
// expandNamed ensures that the underlying type of n is instantiated.
// The underlying type will be Typ[Invalid] if there was an error.
func ( *Context, *Named, token.Pos) ( *TypeParamList, Type, *methodList) {
.orig.resolve()
assert(.orig.underlying != nil)
:= .check
if , := .orig.underlying.(*Named); {
// We should only get an unexpanded underlying here during type checking
// (for example, in recursive type declarations).
assert( != nil)
}
// Mismatching arg and tparam length may be checked elsewhere.
if .orig.tparams.Len() == .targs.Len() {
// We must always have a context, to avoid infinite recursion.
= .bestContext()
:= .instanceHash(.orig, .targs.list())
// ensure that an instance is recorded for h to avoid infinite recursion.
.update(, .orig, .TypeArgs().list(), )
:= makeSubstMap(.orig.tparams.list(), .targs.list())
= .check.subst(, .orig.underlying, , )
// If the underlying 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, .orig, ); {
// If the underlying 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 == .orig.underlying {
:=
= .newInterface()
.embeddeds = .embeddeds
.complete = .complete
.implicit = .implicit // should be false but be conservative
=
}
.methods =
}
}
} else {
= Typ[Invalid]
}
return .orig.tparams, , newLazyMethodList(.orig.methods.Len())
}
// safeUnderlying returns the underlying of typ without expanding instances, to
// avoid infinite recursion.
//
// TODO(rfindley): eliminate this function or give it a better name.
func ( Type) Type {
if , := .(*Named); != nil {
return .underlying
}
return .Underlying()
}
The pages are generated with Golds v0.4.9. (GOOS=linux GOARCH=amd64)