mirror of
https://github.com/golang/go
synced 2024-11-26 05:07:59 -07:00
[dev.typeparams] go/types: port lazy import resolution from types2
This is a straightforward port of CL 323569 to go/types. It is line-for-line identical, except where names are unexported to preserve the current go/types API. Change-Id: I4c78211bff90f982ca2e90ed224946716118ee31 Reviewed-on: https://go-review.googlesource.com/c/go/+/334893 Trust: Robert Findley <rfindley@google.com> Run-TryBot: Robert Findley <rfindley@google.com> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
parent
10c8b7c1d7
commit
b296e54618
@ -73,7 +73,7 @@ type importKey struct {
|
||||
// A dotImportKey describes a dot-imported object in the given scope.
|
||||
type dotImportKey struct {
|
||||
scope *Scope
|
||||
obj Object
|
||||
name string
|
||||
}
|
||||
|
||||
// A Checker maintains the state of the type checker.
|
||||
|
@ -576,7 +576,7 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) {
|
||||
// is detected, the result is Typ[Invalid]. If a cycle is detected and
|
||||
// n0.check != nil, the cycle is reported.
|
||||
func (n0 *Named) under() Type {
|
||||
u := n0.underlying
|
||||
u := n0.Underlying()
|
||||
|
||||
if u == Typ[Invalid] {
|
||||
return u
|
||||
@ -614,7 +614,7 @@ func (n0 *Named) under() Type {
|
||||
seen := map[*Named]int{n0: 0}
|
||||
path := []Object{n0.obj}
|
||||
for {
|
||||
u = n.underlying
|
||||
u = n.Underlying()
|
||||
if u == nil {
|
||||
u = Typ[Invalid]
|
||||
break
|
||||
@ -814,7 +814,7 @@ func (check *Checker) collectMethods(obj *TypeName) {
|
||||
// and field names must be distinct."
|
||||
base := asNamed(obj.typ) // shouldn't fail but be conservative
|
||||
if base != nil {
|
||||
if t, _ := base.underlying.(*Struct); t != nil {
|
||||
if t, _ := base.Underlying().(*Struct); t != nil {
|
||||
for _, fld := range t.fields {
|
||||
if fld.name != "_" {
|
||||
assert(mset.insert(fld) == nil)
|
||||
@ -850,6 +850,7 @@ func (check *Checker) collectMethods(obj *TypeName) {
|
||||
}
|
||||
|
||||
if base != nil {
|
||||
base.expand() // TODO(mdempsky): Probably unnecessary.
|
||||
base.methods = append(base.methods, m)
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ func Instantiate(pos token.Pos, typ Type, targs []Type) (res Type) {
|
||||
var tparams []*TypeName
|
||||
switch t := typ.(type) {
|
||||
case *Named:
|
||||
tparams = t.tparams
|
||||
tparams = t.TParams()
|
||||
case *Signature:
|
||||
tparams = t.tparams
|
||||
defer func() {
|
||||
@ -61,3 +61,19 @@ func Instantiate(pos token.Pos, typ Type, targs []Type) (res Type) {
|
||||
smap := makeSubstMap(tparams, targs)
|
||||
return (*Checker)(nil).subst(pos, typ, smap)
|
||||
}
|
||||
|
||||
// InstantiateLazy is like Instantiate, but avoids actually
|
||||
// instantiating the type until needed.
|
||||
func (check *Checker) InstantiateLazy(pos token.Pos, typ Type, targs []Type) (res Type) {
|
||||
base := asNamed(typ)
|
||||
if base == nil {
|
||||
panic(fmt.Sprintf("%v: cannot instantiate %v", pos, typ))
|
||||
}
|
||||
|
||||
return &instance{
|
||||
check: check,
|
||||
pos: pos,
|
||||
base: base,
|
||||
targs: targs,
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,8 @@ func (check *Checker) labels(body *ast.BlockStmt) {
|
||||
}
|
||||
|
||||
// spec: "It is illegal to define a label that is never used."
|
||||
for _, obj := range all.elems {
|
||||
for name, obj := range all.elems {
|
||||
obj = resolve(name, obj)
|
||||
if lbl := obj.(*Label); !lbl.used {
|
||||
check.softErrorf(lbl, _UnusedLabel, "label %s declared but not used", lbl.name)
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ func (check *Checker) lookupFieldOrMethod(T Type, addressable bool, pkg *Package
|
||||
// pointer type but discard the result if it is a method since we would
|
||||
// not have found it for T (see also issue 8590).
|
||||
if t := asNamed(T); t != nil {
|
||||
if p, _ := t.underlying.(*Pointer); p != nil {
|
||||
if p, _ := t.Underlying().(*Pointer); p != nil {
|
||||
obj, index, indirect = check.rawLookupFieldOrMethod(p, false, pkg, name)
|
||||
if _, ok := obj.(*Func); ok {
|
||||
return nil, nil, false
|
||||
@ -128,6 +128,7 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
|
||||
seen[named] = true
|
||||
|
||||
// look for a matching attached method
|
||||
named.expand()
|
||||
if i, m := lookupMethod(named.methods, pkg, name); m != nil {
|
||||
// potential match
|
||||
// caution: method may not have a proper signature yet
|
||||
@ -400,7 +401,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
|
||||
// In order to compare the signatures, substitute the receiver
|
||||
// type parameters of ftyp with V's instantiation type arguments.
|
||||
// This lazily instantiates the signature of method f.
|
||||
if Vn != nil && len(Vn.tparams) > 0 {
|
||||
if Vn != nil && len(Vn.TParams()) > 0 {
|
||||
// Be careful: The number of type arguments may not match
|
||||
// the number of receiver parameters. If so, an error was
|
||||
// reported earlier but the length discrepancy is still
|
||||
|
@ -230,6 +230,14 @@ func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
|
||||
return &TypeName{object{nil, pos, pkg, name, typ, 0, colorFor(typ), token.NoPos}}
|
||||
}
|
||||
|
||||
// _NewTypeNameLazy returns a new defined type like NewTypeName, but it
|
||||
// lazily calls resolve to finish constructing the Named object.
|
||||
func _NewTypeNameLazy(pos token.Pos, pkg *Package, name string, resolve func(named *Named) (tparams []*TypeName, underlying Type, methods []*Func)) *TypeName {
|
||||
obj := NewTypeName(pos, pkg, name, nil)
|
||||
NewNamed(obj, nil, nil).resolve = resolve
|
||||
return obj
|
||||
}
|
||||
|
||||
// IsAlias reports whether obj is an alias name for a type.
|
||||
func (obj *TypeName) IsAlias() bool {
|
||||
switch t := obj.typ.(type) {
|
||||
|
@ -25,7 +25,7 @@ func isNamed(typ Type) bool {
|
||||
func isGeneric(typ Type) bool {
|
||||
// A parameterized type is only instantiated if it doesn't have an instantiation already.
|
||||
named, _ := typ.(*Named)
|
||||
return named != nil && named.obj != nil && named.tparams != nil && named.targs == nil
|
||||
return named != nil && named.obj != nil && named.TParams() != nil && named.targs == nil
|
||||
}
|
||||
|
||||
func is(typ Type, what BasicInfo) bool {
|
||||
|
@ -309,20 +309,24 @@ func (check *Checker) collectObjects() {
|
||||
check.dotImportMap = make(map[dotImportKey]*PkgName)
|
||||
}
|
||||
// merge imported scope with file scope
|
||||
for _, obj := range imp.scope.elems {
|
||||
for name, obj := range imp.scope.elems {
|
||||
// Note: Avoid eager resolve(name, obj) here, so we only
|
||||
// resolve dot-imported objects as needed.
|
||||
|
||||
// A package scope may contain non-exported objects,
|
||||
// do not import them!
|
||||
if obj.Exported() {
|
||||
if token.IsExported(name) {
|
||||
// declare dot-imported object
|
||||
// (Do not use check.declare because it modifies the object
|
||||
// via Object.setScopePos, which leads to a race condition;
|
||||
// the object may be imported into more than one file scope
|
||||
// concurrently. See issue #32154.)
|
||||
if alt := fileScope.Insert(obj); alt != nil {
|
||||
check.errorf(d.spec.Name, _DuplicateDecl, "%s redeclared in this block", obj.Name())
|
||||
if alt := fileScope.Lookup(name); alt != nil {
|
||||
check.errorf(d.spec.Name, _DuplicateDecl, "%s redeclared in this block", alt.Name())
|
||||
check.reportAltDecl(alt)
|
||||
} else {
|
||||
check.dotImportMap[dotImportKey{fileScope, obj}] = pkgName
|
||||
fileScope.insert(name, obj)
|
||||
check.dotImportMap[dotImportKey{fileScope, name}] = pkgName
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -443,8 +447,9 @@ func (check *Checker) collectObjects() {
|
||||
|
||||
// verify that objects in package and file scopes have different names
|
||||
for _, scope := range fileScopes {
|
||||
for _, obj := range scope.elems {
|
||||
if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
|
||||
for name, obj := range scope.elems {
|
||||
if alt := pkg.scope.Lookup(name); alt != nil {
|
||||
obj = resolve(name, obj)
|
||||
if pkg, ok := obj.(*PkgName); ok {
|
||||
check.errorf(alt, _DuplicateDecl, "%s already declared through import of %s", alt.Name(), pkg.Imported())
|
||||
check.reportAltDecl(pkg)
|
||||
|
@ -135,6 +135,7 @@ func (s sanitizer) typ(typ Type) Type {
|
||||
if debug && t.check != nil {
|
||||
panic("internal error: Named.check != nil")
|
||||
}
|
||||
t.expand()
|
||||
if orig := s.typ(t.fromRHS); orig != t.fromRHS {
|
||||
t.fromRHS = orig
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import (
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A Scope maintains a set of objects and links to its containing
|
||||
@ -66,7 +67,7 @@ func (s *Scope) Child(i int) *Scope { return s.children[i] }
|
||||
// Lookup returns the object in scope s with the given name if such an
|
||||
// object exists; otherwise the result is nil.
|
||||
func (s *Scope) Lookup(name string) Object {
|
||||
return s.elems[name]
|
||||
return resolve(name, s.elems[name])
|
||||
}
|
||||
|
||||
// LookupParent follows the parent chain of scopes starting with s until
|
||||
@ -81,7 +82,7 @@ func (s *Scope) Lookup(name string) Object {
|
||||
// whose scope is the scope of the package that exported them.
|
||||
func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) {
|
||||
for ; s != nil; s = s.parent {
|
||||
if obj := s.elems[name]; obj != nil && (!pos.IsValid() || obj.scopePos() <= pos) {
|
||||
if obj := s.Lookup(name); obj != nil && (!pos.IsValid() || obj.scopePos() <= pos) {
|
||||
return s, obj
|
||||
}
|
||||
}
|
||||
@ -95,19 +96,38 @@ func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) {
|
||||
// if not already set, and returns nil.
|
||||
func (s *Scope) Insert(obj Object) Object {
|
||||
name := obj.Name()
|
||||
if alt := s.elems[name]; alt != nil {
|
||||
if alt := s.Lookup(name); alt != nil {
|
||||
return alt
|
||||
}
|
||||
if s.elems == nil {
|
||||
s.elems = make(map[string]Object)
|
||||
}
|
||||
s.elems[name] = obj
|
||||
s.insert(name, obj)
|
||||
if obj.Parent() == nil {
|
||||
obj.setParent(s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// _InsertLazy is like Insert, but allows deferring construction of the
|
||||
// inserted object until it's accessed with Lookup. The Object
|
||||
// returned by resolve must have the same name as given to _InsertLazy.
|
||||
// If s already contains an alternative object with the same name,
|
||||
// _InsertLazy leaves s unchanged and returns false. Otherwise it
|
||||
// records the binding and returns true. The object's parent scope
|
||||
// will be set to s after resolve is called.
|
||||
func (s *Scope) _InsertLazy(name string, resolve func() Object) bool {
|
||||
if s.elems[name] != nil {
|
||||
return false
|
||||
}
|
||||
s.insert(name, &lazyObject{parent: s, resolve: resolve})
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Scope) insert(name string, obj Object) {
|
||||
if s.elems == nil {
|
||||
s.elems = make(map[string]Object)
|
||||
}
|
||||
s.elems[name] = obj
|
||||
}
|
||||
|
||||
// squash merges s with its parent scope p by adding all
|
||||
// objects of s to p, adding all children of s to the
|
||||
// children of p, and removing s from p's children.
|
||||
@ -117,7 +137,8 @@ func (s *Scope) Insert(obj Object) Object {
|
||||
func (s *Scope) squash(err func(obj, alt Object)) {
|
||||
p := s.parent
|
||||
assert(p != nil)
|
||||
for _, obj := range s.elems {
|
||||
for name, obj := range s.elems {
|
||||
obj = resolve(name, obj)
|
||||
obj.setParent(nil)
|
||||
if alt := p.Insert(obj); alt != nil {
|
||||
err(obj, alt)
|
||||
@ -196,7 +217,7 @@ func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) {
|
||||
|
||||
indn1 := indn + ind
|
||||
for _, name := range s.Names() {
|
||||
fmt.Fprintf(w, "%s%s\n", indn1, s.elems[name])
|
||||
fmt.Fprintf(w, "%s%s\n", indn1, s.Lookup(name))
|
||||
}
|
||||
|
||||
if recurse {
|
||||
@ -214,3 +235,57 @@ func (s *Scope) String() string {
|
||||
s.WriteTo(&buf, 0, false)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// A lazyObject represents an imported Object that has not been fully
|
||||
// resolved yet by its importer.
|
||||
type lazyObject struct {
|
||||
parent *Scope
|
||||
resolve func() Object
|
||||
obj Object
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// resolve returns the Object represented by obj, resolving lazy
|
||||
// objects as appropriate.
|
||||
func resolve(name string, obj Object) Object {
|
||||
if lazy, ok := obj.(*lazyObject); ok {
|
||||
lazy.once.Do(func() {
|
||||
obj := lazy.resolve()
|
||||
|
||||
if _, ok := obj.(*lazyObject); ok {
|
||||
panic("recursive lazy object")
|
||||
}
|
||||
if obj.Name() != name {
|
||||
panic("lazy object has unexpected name")
|
||||
}
|
||||
|
||||
if obj.Parent() == nil {
|
||||
obj.setParent(lazy.parent)
|
||||
}
|
||||
lazy.obj = obj
|
||||
})
|
||||
|
||||
obj = lazy.obj
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
// stub implementations so *lazyObject implements Object and we can
|
||||
// store them directly into Scope.elems.
|
||||
func (*lazyObject) Parent() *Scope { panic("unreachable") }
|
||||
func (*lazyObject) Pos() token.Pos { panic("unreachable") }
|
||||
func (*lazyObject) Pkg() *Package { panic("unreachable") }
|
||||
func (*lazyObject) Name() string { panic("unreachable") }
|
||||
func (*lazyObject) Type() Type { panic("unreachable") }
|
||||
func (*lazyObject) Exported() bool { panic("unreachable") }
|
||||
func (*lazyObject) Id() string { panic("unreachable") }
|
||||
func (*lazyObject) String() string { panic("unreachable") }
|
||||
func (*lazyObject) order() uint32 { panic("unreachable") }
|
||||
func (*lazyObject) color() color { panic("unreachable") }
|
||||
func (*lazyObject) setType(Type) { panic("unreachable") }
|
||||
func (*lazyObject) setOrder(uint32) { panic("unreachable") }
|
||||
func (*lazyObject) setColor(color color) { panic("unreachable") }
|
||||
func (*lazyObject) setParent(*Scope) { panic("unreachable") }
|
||||
func (*lazyObject) sameId(pkg *Package, name string) bool { panic("unreachable") }
|
||||
func (*lazyObject) scopePos() token.Pos { panic("unreachable") }
|
||||
func (*lazyObject) setScopePos(pos token.Pos) { panic("unreachable") }
|
||||
|
@ -57,7 +57,7 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
|
||||
// again when we type-check the signature.
|
||||
// TODO(gri) maybe the receiver should be marked as invalid instead?
|
||||
if recv := asNamed(check.genericType(rname, false)); recv != nil {
|
||||
recvTParams = recv.tparams
|
||||
recvTParams = recv.TParams()
|
||||
}
|
||||
}
|
||||
// provide type parameter bounds
|
||||
|
@ -30,7 +30,7 @@ func TestSizeof(t *testing.T) {
|
||||
{Interface{}, 52, 104},
|
||||
{Map{}, 16, 32},
|
||||
{Chan{}, 12, 24},
|
||||
{Named{}, 68, 136},
|
||||
{Named{}, 84, 160},
|
||||
{_TypeParam{}, 28, 48},
|
||||
{instance{}, 44, 88},
|
||||
{top{}, 0, 0},
|
||||
|
@ -65,7 +65,8 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body
|
||||
|
||||
func (check *Checker) usage(scope *Scope) {
|
||||
var unused []*Var
|
||||
for _, elem := range scope.elems {
|
||||
for name, elem := range scope.elems {
|
||||
elem = resolve(name, elem)
|
||||
if v, _ := elem.(*Var); v != nil && !v.used {
|
||||
unused = append(unused, v)
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ func (check *Checker) instantiate(pos token.Pos, typ Type, targs []Type, poslist
|
||||
var tparams []*TypeName
|
||||
switch t := typ.(type) {
|
||||
case *Named:
|
||||
tparams = t.tparams
|
||||
tparams = t.TParams()
|
||||
case *Signature:
|
||||
tparams = t.tparams
|
||||
defer func() {
|
||||
@ -351,7 +351,7 @@ func (subst *subster) typ(typ Type) Type {
|
||||
}
|
||||
}
|
||||
|
||||
if t.tparams == nil {
|
||||
if t.TParams() == nil {
|
||||
dump(">>> %s is not parameterized", t)
|
||||
return t // type is not parameterized
|
||||
}
|
||||
@ -361,7 +361,7 @@ func (subst *subster) typ(typ Type) Type {
|
||||
if len(t.targs) > 0 {
|
||||
// already instantiated
|
||||
dump(">>> %s already instantiated", t)
|
||||
assert(len(t.targs) == len(t.tparams))
|
||||
assert(len(t.targs) == len(t.TParams()))
|
||||
// For each (existing) type argument targ, determine if it needs
|
||||
// to be substituted; i.e., if it is or contains a type parameter
|
||||
// that has a type argument for it.
|
||||
@ -371,7 +371,7 @@ func (subst *subster) typ(typ Type) Type {
|
||||
if newTarg != targ {
|
||||
dump(">>> substituted %d targ %s => %s", i, targ, newTarg)
|
||||
if newTargs == nil {
|
||||
newTargs = make([]Type, len(t.tparams))
|
||||
newTargs = make([]Type, len(t.TParams()))
|
||||
copy(newTargs, t.targs)
|
||||
}
|
||||
newTargs[i] = newTarg
|
||||
@ -402,7 +402,7 @@ func (subst *subster) typ(typ Type) Type {
|
||||
|
||||
// create a new named type and populate caches to avoid endless recursion
|
||||
tname := NewTypeName(subst.pos, t.obj.pkg, t.obj.name, nil)
|
||||
named := subst.check.newNamed(tname, t, t.underlying, t.tparams, t.methods) // method signatures are updated lazily
|
||||
named := subst.check.newNamed(tname, t, t.Underlying(), t.TParams(), t.methods) // method signatures are updated lazily
|
||||
named.targs = newTargs
|
||||
if subst.check != nil {
|
||||
subst.check.typMap[h] = named
|
||||
@ -411,7 +411,7 @@ func (subst *subster) typ(typ Type) Type {
|
||||
|
||||
// do the substitution
|
||||
dump(">>> subst %s with %s (new: %s)", t.underlying, subst.smap, newTargs)
|
||||
named.underlying = subst.typOrNil(t.underlying)
|
||||
named.underlying = subst.typOrNil(t.Underlying())
|
||||
named.fromRHS = named.underlying // for cycle detection (Checker.validType)
|
||||
|
||||
return named
|
||||
|
@ -6,6 +6,7 @@ package types
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
@ -504,6 +505,9 @@ type Named struct {
|
||||
tparams []*TypeName // type parameters, or nil
|
||||
targs []Type // type arguments (after instantiation), or nil
|
||||
methods []*Func // methods declared for this type (not the method set of this type); signatures are type-checked lazily
|
||||
|
||||
resolve func(*Named) ([]*TypeName, Type, []*Func)
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
|
||||
@ -516,6 +520,35 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
|
||||
return (*Checker)(nil).newNamed(obj, nil, underlying, nil, methods)
|
||||
}
|
||||
|
||||
func (t *Named) expand() *Named {
|
||||
if t.resolve == nil {
|
||||
return t
|
||||
}
|
||||
|
||||
t.once.Do(func() {
|
||||
// TODO(mdempsky): Since we're passing t to resolve 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
|
||||
// SetTParams, 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 SetTParams).
|
||||
|
||||
tparams, underlying, methods := t.resolve(t)
|
||||
|
||||
switch underlying.(type) {
|
||||
case nil, *Named:
|
||||
panic("invalid underlying type")
|
||||
}
|
||||
|
||||
t.tparams = tparams
|
||||
t.underlying = underlying
|
||||
t.methods = methods
|
||||
})
|
||||
return t
|
||||
}
|
||||
|
||||
func (check *Checker) newNamed(obj *TypeName, orig *Named, underlying Type, tparams []*TypeName, methods []*Func) *Named {
|
||||
typ := &Named{check: check, obj: obj, orig: orig, fromRHS: underlying, underlying: underlying, tparams: tparams, methods: methods}
|
||||
if typ.orig == nil {
|
||||
@ -556,10 +589,10 @@ func (t *Named) _Orig() *Named { return t.orig }
|
||||
|
||||
// _TParams returns the type parameters of the named type t, or nil.
|
||||
// The result is non-nil for an (originally) parameterized type even if it is instantiated.
|
||||
func (t *Named) _TParams() []*TypeName { return t.tparams }
|
||||
func (t *Named) _TParams() []*TypeName { return t.expand().tparams }
|
||||
|
||||
// _SetTParams sets the type parameters of the named type t.
|
||||
func (t *Named) _SetTParams(tparams []*TypeName) { t.tparams = tparams }
|
||||
func (t *Named) _SetTParams(tparams []*TypeName) { t.expand().tparams = tparams }
|
||||
|
||||
// _TArgs returns the type arguments after instantiation of the named type t, or nil if not instantiated.
|
||||
func (t *Named) _TArgs() []Type { return t.targs }
|
||||
@ -568,10 +601,10 @@ func (t *Named) _TArgs() []Type { return t.targs }
|
||||
func (t *Named) _SetTArgs(args []Type) { t.targs = args }
|
||||
|
||||
// NumMethods returns the number of explicit methods whose receiver is named type t.
|
||||
func (t *Named) NumMethods() int { return len(t.methods) }
|
||||
func (t *Named) NumMethods() int { return len(t.expand().methods) }
|
||||
|
||||
// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
|
||||
func (t *Named) Method(i int) *Func { return t.methods[i] }
|
||||
func (t *Named) Method(i int) *Func { return t.expand().methods[i] }
|
||||
|
||||
// SetUnderlying sets the underlying type and marks t as complete.
|
||||
func (t *Named) SetUnderlying(underlying Type) {
|
||||
@ -581,11 +614,12 @@ func (t *Named) SetUnderlying(underlying Type) {
|
||||
if _, ok := underlying.(*Named); ok {
|
||||
panic("types.Named.SetUnderlying: underlying type must not be *Named")
|
||||
}
|
||||
t.underlying = underlying
|
||||
t.expand().underlying = underlying
|
||||
}
|
||||
|
||||
// AddMethod adds method m unless it is already in the method list.
|
||||
func (t *Named) AddMethod(m *Func) {
|
||||
t.expand()
|
||||
if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
|
||||
t.methods = append(t.methods, m)
|
||||
}
|
||||
@ -736,7 +770,7 @@ func (t *Signature) Underlying() Type { return t }
|
||||
func (t *Interface) Underlying() Type { return t }
|
||||
func (t *Map) Underlying() Type { return t }
|
||||
func (t *Chan) Underlying() Type { return t }
|
||||
func (t *Named) Underlying() Type { return t.underlying }
|
||||
func (t *Named) Underlying() Type { return t.expand().underlying }
|
||||
func (t *_TypeParam) Underlying() Type { return t }
|
||||
func (t *instance) Underlying() Type { return t }
|
||||
func (t *top) Underlying() Type { return t }
|
||||
|
@ -273,9 +273,9 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
|
||||
buf.WriteByte('[')
|
||||
writeTypeList(buf, t.targs, qf, visited)
|
||||
buf.WriteByte(']')
|
||||
} else if t.tparams != nil {
|
||||
} else if t.TParams() != nil {
|
||||
// parameterized type
|
||||
writeTParamList(buf, t.tparams, qf, visited)
|
||||
writeTParamList(buf, t.TParams(), qf, visited)
|
||||
}
|
||||
|
||||
case *_TypeParam:
|
||||
|
@ -56,7 +56,7 @@ func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, wantType bool)
|
||||
// If so, mark the respective package as used.
|
||||
// (This code is only needed for dot-imports. Without them,
|
||||
// we only have to mark variables, see *Var case below).
|
||||
if pkgName := check.dotImportMap[dotImportKey{scope, obj}]; pkgName != nil {
|
||||
if pkgName := check.dotImportMap[dotImportKey{scope, obj.Name()}]; pkgName != nil {
|
||||
pkgName.used = true
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user