1
0
mirror of https://github.com/golang/go synced 2024-11-26 22:51:23 -07:00

[dev.typeparams] go/types: import the Type API from dev.go2go

Import the Type changes from the dev.go2go branch, as well as a minimal
set of related changes to the Checker code so that tests pass. This
involved making some decisions about which functionality to import, with
some parts of the Type API (for example instance.expand) carved out as
they pulled in too many additional changes.

Minimal changes were made from the dev.go2go version. Notably:
 + The aType helper struct is removed. It could have been removed in CL
   274852 but was missed.
 + writeTParamList was cleaned up for the bracketed parameter style.
 + Some interface iteration functions were simplified.

One observation along the way is that, especially with converter methods
removed from the Type interface, we should probably avoid storing the
Checker on Type instances for lazy evaluation. If a function is only
valid during a checking pass, we should make it a method on Checker
instead of a method on the Type instance. Rather than do this now, which
would make later changes harder to import, I left a couple TODOs to
address this later.

Change-Id: Ie669224614269874474d87e46c68216cb06b6d0e
Reviewed-on: https://go-review.googlesource.com/c/go/+/275441
Reviewed-by: Robert Griesemer <gri@golang.org>
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Robert Findley <rfindley@google.com>
This commit is contained in:
Rob Findley 2020-12-04 12:39:38 -05:00 committed by Robert Findley
parent cd15a48036
commit 14dc2d2345
10 changed files with 784 additions and 103 deletions

View File

@ -95,7 +95,7 @@ var importerTests = [...]importerTest{
{pkgpath: "nointerface", name: "I", want: "type I int"},
{pkgpath: "issue29198", name: "FooServer", gccgoVersion: 7, want: "type FooServer struct{FooServer *FooServer; user string; ctx context.Context}"},
{pkgpath: "issue30628", name: "Apple", want: "type Apple struct{hey sync.RWMutex; x int; RQ [517]struct{Count uintptr; NumBytes uintptr; Last uintptr}}"},
{pkgpath: "issue31540", name: "S", gccgoVersion: 7, want: "type S struct{b int; map[Y]Z}"},
{pkgpath: "issue31540", name: "S", gccgoVersion: 7, want: "type S struct{b int; A2 /* = map[Y]Z */}"},
{pkgpath: "issue34182", name: "T1", want: "type T1 struct{f *T2}"},
{pkgpath: "notinheap", name: "S", want: "type S struct{}"},
}

View File

@ -77,6 +77,7 @@ type Checker struct {
fset *token.FileSet
pkg *Package
*Info
nextId uint64 // unique Id for type parameters (first valid Id is 1)
objMap map[Object]*declInfo // maps package-level objects and (non-interface) methods to declaration info
impMap map[importKey]*Package // maps (import path, source directory) to (complete or fake) package
posMap map[*Interface][]token.Pos // maps interface types to lists of embedded interface positions

View File

@ -544,36 +544,35 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) {
check.initVars(lhs, []ast.Expr{init}, token.NoPos)
}
// underlying returns the underlying type of typ; possibly by following
// forward chains of named types. Such chains only exist while named types
// are incomplete. 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, a cycle error is reported and Typ[Invalid]
// is used as underlying type for each defined type in the chain and returned
// as result.
func (check *Checker) underlying(typ Type) Type {
// If typ is not a defined type, its underlying type is itself.
n0, _ := typ.(*Named)
if n0 == nil {
return typ // nothing to do
// 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.
func (n0 *Named) under() Type {
u := n0.underlying
if u == nil {
return Typ[Invalid]
}
// If the underlying type of a defined type is not a defined
// type, then that is the desired underlying type.
typ = n0.underlying
n, _ := typ.(*Named)
n := asNamed(u)
if n == nil {
return typ // common case
return u // common case
}
// Otherwise, follow the forward chain.
seen := map[*Named]int{n0: 0}
path := []Object{n0.obj}
for {
typ = n.underlying
n1, _ := typ.(*Named)
u = n.underlying
if u == nil {
u = Typ[Invalid]
break
}
n1 := asNamed(u)
if n1 == nil {
break // end of chain
}
@ -584,8 +583,12 @@ func (check *Checker) underlying(typ Type) Type {
if i, ok := seen[n]; ok {
// cycle
check.cycleError(path[i:])
typ = Typ[Invalid]
// TODO(rFindley) revert this to a method on Checker. Having a possibly
// nil Checker on Named and TypeParam is too subtle.
if n0.check != nil {
n0.check.cycleError(path[i:])
}
u = Typ[Invalid]
break
}
}
@ -594,13 +597,14 @@ func (check *Checker) underlying(typ Type) Type {
// 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).
if n.obj.pkg != check.pkg {
// Do this check always, not just in debug more (it's cheap).
if n0.check != nil && n.obj.pkg != n0.check.pkg {
panic("internal error: imported type with unresolved underlying type")
}
n.underlying = typ
n.underlying = u
}
return typ
return u
}
func (n *Named) setUnderlying(typ Type) {
@ -623,7 +627,7 @@ func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, alias bo
} else {
named := &Named{obj: obj}
named := &Named{check: check, obj: obj}
def.setUnderlying(named)
obj.typ = named // make sure recursive type declarations terminate
@ -643,8 +647,7 @@ func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, alias bo
// and which has as its underlying type the named type B.
// Determine the (final, unnamed) underlying type by resolving
// any forward chain.
named.underlying = check.underlying(named)
named.underlying = under(named)
}
check.addMethodDecls(obj)

View File

@ -591,7 +591,7 @@ func (check *Checker) implicitType(x *operand, target Type) Type {
return Typ[UntypedNil]
}
// cannot assign untyped values to non-empty interfaces
check.completeInterface(t)
check.completeInterface(token.NoPos, t)
if !t.Empty() {
return nil
}

View File

@ -6,6 +6,8 @@
package types
import "go/token"
// LookupFieldOrMethod looks up a field or method with given package and name
// in T and returns the corresponding *Var or *Func, an index sequence, and a
// bool indicating if there were any pointer indirections on the path to the
@ -175,7 +177,7 @@ func (check *Checker) rawLookupFieldOrMethod(T Type, addressable bool, pkg *Pack
case *Interface:
// look for a matching method
// TODO(gri) t.allMethods is sorted - use binary search
check.completeInterface(t)
check.completeInterface(token.NoPos, t)
if i, m := lookupMethod(t.allMethods, pkg, name); m != nil {
assert(m.typ != nil)
index = concat(e.index, i)
@ -276,7 +278,7 @@ func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType b
// To improve error messages, also report the wrong signature
// when the method exists on *V instead of V.
func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method, wrongType *Func) {
check.completeInterface(T)
check.completeInterface(token.NoPos, T)
// fast path for common case
if T.Empty() {
@ -284,7 +286,7 @@ func (check *Checker) missingMethod(V Type, T *Interface, static bool) (method,
}
if ityp, _ := V.Underlying().(*Interface); ityp != nil {
check.completeInterface(ityp)
check.completeInterface(token.NoPos, ityp)
// TODO(gri) allMethods is sorted - can do this more efficiently
for _, m := range T.allMethods {
_, obj := lookupMethod(ityp.allMethods, m.pkg, m.name)

View File

@ -6,7 +6,10 @@
package types
import "sort"
import (
"go/token"
"sort"
)
func isNamed(typ Type) bool {
if _, ok := typ.(*Basic); ok {
@ -225,8 +228,8 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool {
// that case, interfaces are expected to be complete and lazy completion
// here is not needed.
if check != nil {
check.completeInterface(x)
check.completeInterface(y)
check.completeInterface(token.NoPos, x)
check.completeInterface(token.NoPos, y)
}
a := x.allMethods
b := y.allMethods

View File

@ -4,12 +4,18 @@
package types
import "sort"
import (
"fmt"
"go/token"
"sort"
)
// A Type represents a type of Go.
// All types implement the Type interface.
type Type interface {
// Underlying returns the underlying type of a type.
// Underlying returns the underlying type of a type
// w/o following forwarding chains. Only used by
// client packages (here for backward-compatibility).
Underlying() Type
// String returns a string representation of a type.
@ -98,7 +104,7 @@ type Array struct {
// NewArray returns a new array type for the given element type and length.
// A negative length indicates an unknown length.
func NewArray(elem Type, len int64) *Array { return &Array{len, elem} }
func NewArray(elem Type, len int64) *Array { return &Array{len: len, elem: elem} }
// Len returns the length of array a.
// A negative result indicates an unknown length.
@ -113,7 +119,7 @@ type Slice struct {
}
// NewSlice returns a new slice type for the given element type.
func NewSlice(elem Type) *Slice { return &Slice{elem} }
func NewSlice(elem Type) *Slice { return &Slice{elem: elem} }
// Elem returns the element type of slice s.
func (s *Slice) Elem() Type { return s.elem }
@ -176,8 +182,10 @@ type Tuple struct {
// NewTuple returns a new tuple for the given variables.
func NewTuple(x ...*Var) *Tuple {
if len(x) > 0 {
return &Tuple{x}
return &Tuple{vars: x}
}
// TODO(gri) Don't represent empty tuples with a (*Tuple)(nil) pointer;
// it's too subtle and causes problems.
return nil
}
@ -199,6 +207,8 @@ type Signature struct {
// and store it in the Func Object) because when type-checking a function
// literal we call the general type checker which returns a general Type.
// We then unpack the *Signature and use the scope for the literal body.
rparams []*TypeName // receiver type parameters from left to right, or nil
tparams []*TypeName // type parameters from left to right, or nil
scope *Scope // function scope, present for package-local signatures
recv *Var // nil if not a method
params *Tuple // (incoming) parameters from left to right; or nil
@ -220,7 +230,7 @@ func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature {
panic("types.NewSignature: variadic parameter must be of unnamed slice type")
}
}
return &Signature{nil, recv, params, results, variadic}
return &Signature{recv: recv, params: params, results: results, variadic: variadic}
}
// Recv returns the receiver of signature s (if a method), or nil if a
@ -231,6 +241,12 @@ func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature {
// contain methods whose receiver type is a different interface.
func (s *Signature) Recv() *Var { return s.recv }
// TParams returns the type parameters of signature s, or nil.
func (s *Signature) TParams() []*TypeName { return s.tparams }
// SetTParams sets the type parameters of signature s.
func (s *Signature) SetTParams(tparams []*TypeName) { s.tparams = tparams }
// Params returns the parameters of signature s, or nil.
func (s *Signature) Params() *Tuple { return s.params }
@ -240,12 +256,89 @@ func (s *Signature) Results() *Tuple { return s.results }
// Variadic reports whether the signature s is variadic.
func (s *Signature) Variadic() bool { return s.variadic }
// A Sum represents a set of possible types.
// Sums are currently used to represent type lists of interfaces
// and thus the underlying types of type parameters; they are not
// first class types of Go.
type Sum struct {
types []Type // types are unique
}
// NewSum returns a new Sum type consisting of the provided
// types if there are more than one. If there is exactly one
// type, it returns that type. If the list of types is empty
// the result is nil.
func NewSum(types []Type) Type {
if len(types) == 0 {
return nil
}
// What should happen if types contains a sum type?
// Do we flatten the types list? For now we check
// and panic. This should not be possible for the
// current use case of type lists.
// TODO(gri) Come up with the rules for sum types.
for _, t := range types {
if _, ok := t.(*Sum); ok {
panic("sum type contains sum type - unimplemented")
}
}
if len(types) == 1 {
return types[0]
}
return &Sum{types: types}
}
// is reports whether all types in t satisfy pred.
func (s *Sum) is(pred func(Type) bool) bool {
if s == nil {
return false
}
for _, t := range s.types {
if !pred(t) {
return false
}
}
return true
}
// An Interface represents an interface type.
type Interface struct {
methods []*Func // ordered list of explicitly declared methods
types Type // (possibly a Sum) type declared with a type list (TODO(gri) need better field name)
embeddeds []Type // ordered list of explicitly embedded types
allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
allTypes Type // intersection of all embedded and locally declared types (TODO(gri) need better field name)
obj Object // type declaration defining this interface; or nil (for better error messages)
}
// unpack unpacks a type into a list of types.
// TODO(gri) Try to eliminate the need for this function.
func unpackType(typ Type) []Type {
if typ == nil {
return nil
}
if sum := asSum(typ); sum != nil {
return sum.types
}
return []Type{typ}
}
// is reports whether interface t represents types that all satisfy pred.
func (t *Interface) is(pred func(Type) bool) bool {
if t.allTypes == nil {
return false // we must have at least one type! (was bug)
}
for _, t := range unpackType(t.allTypes) {
if !pred(t) {
return false
}
}
return true
}
// emptyInterface represents the empty (completed) interface
@ -344,8 +437,101 @@ func (t *Interface) assertCompleteness() {
func (t *Interface) Method(i int) *Func { t.assertCompleteness(); return t.allMethods[i] }
// Empty reports whether t is the empty interface.
// The interface must have been completed.
func (t *Interface) Empty() bool { t.assertCompleteness(); return len(t.allMethods) == 0 }
func (t *Interface) Empty() bool {
if t.allMethods != nil {
// interface is complete - quick test
// A non-nil allTypes may still be empty and represents the bottom type.
return len(t.allMethods) == 0 && t.allTypes == nil
}
return !t.iterate(func(t *Interface) bool {
return len(t.methods) > 0 || t.types != nil
}, nil)
}
// HasTypeList reports whether interface t has a type list, possibly from an embedded type.
func (t *Interface) HasTypeList() bool {
if t.allMethods != nil {
// interface is complete - quick test
return t.allTypes != nil
}
return t.iterate(func(t *Interface) bool {
return t.types != nil
}, nil)
}
// IsComparable reports whether interface t is or embeds the predeclared interface "comparable".
func (t *Interface) IsComparable() bool {
if t.allMethods != nil {
// interface is complete - quick test
_, m := lookupMethod(t.allMethods, nil, "==")
return m != nil
}
return t.iterate(func(t *Interface) bool {
_, m := lookupMethod(t.methods, nil, "==")
return m != nil
}, nil)
}
// IsConstraint reports t.HasTypeList() || t.IsComparable().
func (t *Interface) IsConstraint() bool {
if t.allMethods != nil {
// interface is complete - quick test
if t.allTypes != nil {
return true
}
_, m := lookupMethod(t.allMethods, nil, "==")
return m != nil
}
return t.iterate(func(t *Interface) bool {
if t.types != nil {
return true
}
_, m := lookupMethod(t.methods, nil, "==")
return m != nil
}, nil)
}
// iterate calls f with t and then with any embedded interface of t, recursively, until f returns true.
// iterate reports whether any call to f returned true.
func (t *Interface) iterate(f func(*Interface) bool, seen map[*Interface]bool) bool {
if f(t) {
return true
}
for _, e := range t.embeddeds {
// e should be an interface but be careful (it may be invalid)
if e := asInterface(e); e != nil {
// Cyclic interfaces such as "type E interface { E }" are not permitted
// but they are still constructed and we need to detect such cycles.
if seen[e] {
continue
}
if seen == nil {
seen = make(map[*Interface]bool)
}
seen[e] = true
if e.iterate(f, seen) {
return true
}
}
}
return false
}
// isSatisfiedBy reports whether interface t's type list is satisfied by the type typ.
// If the the type list is empty (absent), typ trivially satisfies the interface.
// TODO(gri) This is not a great name. Eventually, we should have a more comprehensive
// "implements" predicate.
func (t *Interface) isSatisfiedBy(typ Type) bool {
t.Complete()
if t.allTypes == nil {
return true
}
types := unpackType(t.allTypes)
return includes(types, typ) || includes(types, under(typ))
}
// Complete computes the interface's method set. It must be called by users of
// NewInterfaceType and NewInterface after the interface's embedded types are
@ -379,12 +565,22 @@ func (t *Interface) Complete() *Interface {
addMethod(m, true)
}
allTypes := t.types
for _, typ := range t.embeddeds {
typ := typ.Underlying().(*Interface)
typ.Complete()
for _, m := range typ.allMethods {
utyp := under(typ)
etyp := asInterface(utyp)
if etyp == nil {
if utyp != Typ[Invalid] {
panic(fmt.Sprintf("%s is not an interface", typ))
}
continue
}
etyp.Complete()
for _, m := range etyp.allMethods {
addMethod(m, false)
}
allTypes = intersect(allTypes, etyp.allTypes)
}
for i := 0; i < len(todo); i += 2 {
@ -399,6 +595,7 @@ func (t *Interface) Complete() *Interface {
sort.Sort(byUniqueMethodName(methods))
t.allMethods = methods
}
t.allTypes = allTypes
return t
}
@ -410,7 +607,7 @@ type Map struct {
// NewMap returns a new map for the given key and element types.
func NewMap(key, elem Type) *Map {
return &Map{key, elem}
return &Map{key: key, elem: elem}
}
// Key returns the key type of map m.
@ -437,7 +634,7 @@ const (
// NewChan returns a new channel type for the given direction and element type.
func NewChan(dir ChanDir, elem Type) *Chan {
return &Chan{dir, elem}
return &Chan{dir: dir, elem: elem}
}
// Dir returns the direction of channel c.
@ -446,12 +643,15 @@ func (c *Chan) Dir() ChanDir { return c.dir }
// Elem returns the element type of channel c.
func (c *Chan) Elem() Type { return c.elem }
// A Named represents a named type.
// A Named represents a named (defined) type.
type Named struct {
check *Checker // for Named.under implementation
info typeInfo // for cycle detection
obj *TypeName // corresponding declared object
orig 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 []*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
}
@ -472,6 +672,19 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
// Obj returns the type name for the named type t.
func (t *Named) Obj() *TypeName { return t.obj }
// TODO(gri) Come up with a better representation and API to distinguish
// between parameterized instantiated and non-instantiated types.
// 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 }
// 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 }
// SetTArgs sets the type arguments of Named.
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) }
@ -496,28 +709,251 @@ func (t *Named) AddMethod(m *Func) {
}
}
// Implementations for Type methods.
// A TypeParam represents a type parameter type.
type TypeParam struct {
check *Checker // for lazy type bound completion
id uint64 // unique id
ptr bool // pointer designation
obj *TypeName // corresponding type name
index int // parameter index
bound Type // *Named or *Interface; underlying type is always *Interface
}
func (b *Basic) Underlying() Type { return b }
func (a *Array) Underlying() Type { return a }
func (s *Slice) Underlying() Type { return s }
func (s *Struct) Underlying() Type { return s }
func (p *Pointer) Underlying() Type { return p }
// NewTypeParam returns a new TypeParam.
func (check *Checker) NewTypeParam(ptr bool, obj *TypeName, index int, bound Type) *TypeParam {
assert(bound != nil)
typ := &TypeParam{check: check, id: check.nextId, ptr: ptr, obj: obj, index: index, bound: bound}
check.nextId++
if obj.typ == nil {
obj.typ = typ
}
return typ
}
func (t *TypeParam) Bound() *Interface {
iface := asInterface(t.bound)
// use the type bound position if we have one
pos := token.NoPos
if n, _ := t.bound.(*Named); n != nil {
pos = n.obj.pos
}
// TODO(rFindley) switch this to an unexported method on Checker.
t.check.completeInterface(pos, iface)
return iface
}
// optype returns a type's operational type. Except for
// type parameters, the operational type is the same
// as the underlying type (as returned by under). For
// Type parameters, the operational type is determined
// by the corresponding type bound's type list. The
// result may be the bottom or top type, but it is never
// the incoming type parameter.
func optype(typ Type) Type {
if t := asTypeParam(typ); t != nil {
// If the optype is typ, return the top type as we have
// no information. It also prevents infinite recursion
// via the asTypeParam converter function. This can happen
// for a type parameter list of the form:
// (type T interface { type T }).
// See also issue #39680.
if u := t.Bound().allTypes; u != nil && u != typ {
// u != typ and u is a type parameter => under(u) != typ, so this is ok
return under(u)
}
return theTop
}
return under(typ)
}
// An instance represents an instantiated generic type syntactically
// (without expanding the instantiation). Type instances appear only
// during type-checking and are replaced by their fully instantiated
// (expanded) types before the end of type-checking.
type instance struct {
check *Checker // for lazy instantiation
pos token.Pos // position of type instantiation; for error reporting only
base *Named // parameterized type to be instantiated
targs []Type // type arguments
poslist []token.Pos // position of each targ; for error reporting only
value Type // base(targs...) after instantiation or Typ[Invalid]; nil if not yet set
}
// expand returns the instantiated (= expanded) type of t.
// The result is either an instantiated *Named type, or
// Typ[Invalid] if there was an error.
func (t *instance) expand() Type {
// TODO(rFindley) add this in a follow-up CL.
panic("not implemented")
}
// expand expands a type instance into its instantiated
// type and leaves all other types alone. expand does
// not recurse.
func expand(typ Type) Type {
if t, _ := typ.(*instance); t != nil {
return t.expand()
}
return typ
}
// expandf is set to expand.
// Call expandf when calling expand causes compile-time cycle error.
var expandf func(Type) Type
func init() { expandf = expand }
// bottom represents the bottom of the type lattice.
// It is the underlying type of a type parameter that
// cannot be satisfied by any type, usually because
// the intersection of type constraints left nothing).
type bottom struct{}
// theBottom is the singleton bottom type.
var theBottom = &bottom{}
// top represents the top of the type lattice.
// It is the underlying type of a type parameter that
// can be satisfied by any type (ignoring methods),
// usually because the type constraint has no type
// list.
type top struct{}
// theTop is the singleton top type.
var theTop = &top{}
// Type-specific implementations of Underlying.
func (t *Basic) Underlying() Type { return t }
func (t *Array) Underlying() Type { return t }
func (t *Slice) Underlying() Type { return t }
func (t *Struct) Underlying() Type { return t }
func (t *Pointer) Underlying() Type { return t }
func (t *Tuple) Underlying() Type { return t }
func (s *Signature) Underlying() Type { return s }
func (t *Signature) Underlying() Type { return t }
func (t *Sum) Underlying() Type { return t }
func (t *Interface) Underlying() Type { return t }
func (m *Map) Underlying() Type { return m }
func (c *Chan) Underlying() Type { return c }
func (t *Map) Underlying() Type { return t }
func (t *Chan) Underlying() Type { return t }
func (t *Named) Underlying() Type { return t.underlying }
func (t *TypeParam) Underlying() Type { return t }
func (t *instance) Underlying() Type { return t }
func (t *bottom) Underlying() Type { return t }
func (t *top) Underlying() Type { return t }
func (b *Basic) String() string { return TypeString(b, nil) }
func (a *Array) String() string { return TypeString(a, nil) }
func (s *Slice) String() string { return TypeString(s, nil) }
func (s *Struct) String() string { return TypeString(s, nil) }
func (p *Pointer) String() string { return TypeString(p, nil) }
// Type-specific implementations of String.
func (t *Basic) String() string { return TypeString(t, nil) }
func (t *Array) String() string { return TypeString(t, nil) }
func (t *Slice) String() string { return TypeString(t, nil) }
func (t *Struct) String() string { return TypeString(t, nil) }
func (t *Pointer) String() string { return TypeString(t, nil) }
func (t *Tuple) String() string { return TypeString(t, nil) }
func (s *Signature) String() string { return TypeString(s, nil) }
func (t *Signature) String() string { return TypeString(t, nil) }
func (t *Sum) String() string { return TypeString(t, nil) }
func (t *Interface) String() string { return TypeString(t, nil) }
func (m *Map) String() string { return TypeString(m, nil) }
func (c *Chan) String() string { return TypeString(c, nil) }
func (t *Map) String() string { return TypeString(t, nil) }
func (t *Chan) String() string { return TypeString(t, nil) }
func (t *Named) String() string { return TypeString(t, nil) }
func (t *TypeParam) String() string { return TypeString(t, nil) }
func (t *instance) String() string { return TypeString(t, nil) }
func (t *bottom) String() string { return TypeString(t, nil) }
func (t *top) String() string { return TypeString(t, nil) }
// under returns the true expanded underlying type.
// If it doesn't exist, the result is Typ[Invalid].
// under must only be called when a type is known
// to be fully set up.
func under(t Type) Type {
// TODO(gri) is this correct for *Sum?
if n := asNamed(t); n != nil {
return n.under()
}
return t
}
// Converters
//
// A converter must only be called when a type is
// known to be fully set up. A converter returns
// a type's operational type (see comment for optype)
// or nil if the type argument is not of the
// respective type.
func asBasic(t Type) *Basic {
op, _ := optype(t).(*Basic)
return op
}
func asArray(t Type) *Array {
op, _ := optype(t).(*Array)
return op
}
func asSlice(t Type) *Slice {
op, _ := optype(t).(*Slice)
return op
}
// TODO (rFindley) delete this on the dev.typeparams branch. This is only
// exported in the prototype for legacy compatibility.
func AsStruct(t Type) *Struct {
return asStruct(t)
}
func asStruct(t Type) *Struct {
op, _ := optype(t).(*Struct)
return op
}
// TODO(rFindley) delete this on the dev.typeparams branch (see ToStruct).
func AsPointer(t Type) *Pointer {
return asPointer(t)
}
func asPointer(t Type) *Pointer {
op, _ := optype(t).(*Pointer)
return op
}
func asTuple(t Type) *Tuple {
op, _ := optype(t).(*Tuple)
return op
}
func asSignature(t Type) *Signature {
op, _ := optype(t).(*Signature)
return op
}
func asSum(t Type) *Sum {
op, _ := optype(t).(*Sum)
return op
}
func asInterface(t Type) *Interface {
op, _ := optype(t).(*Interface)
return op
}
func asMap(t Type) *Map {
op, _ := optype(t).(*Map)
return op
}
func asChan(t Type) *Chan {
op, _ := optype(t).(*Chan)
return op
}
// If the argument to asNamed and asTypeParam is of the respective types
// (possibly after expanding an instance type), these methods return that type.
// Otherwise the result is nil.
func asNamed(t Type) *Named {
e, _ := expand(t).(*Named)
return e
}
func asTypeParam(t Type) *TypeParam {
u, _ := under(t).(*TypeParam)
return u
}

View File

@ -9,6 +9,7 @@ package types
import (
"bytes"
"fmt"
"unicode/utf8"
)
// A Qualifier controls how named package-level objects are printed in
@ -75,6 +76,10 @@ func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
writeType(buf, typ, qf, make([]Type, 0, 8))
}
// instanceMarker is the prefix for an instantiated type
// in "non-evaluated" instance form.
const instanceMarker = '#'
func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
// Theoretically, this is a quadratic lookup algorithm, but in
// practice deeply nested composite types with unnamed component
@ -82,7 +87,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
// using a map.
for _, t := range visited {
if t == typ {
fmt.Fprintf(buf, "○%T", typ) // cycle to typ
fmt.Fprintf(buf, "○%T", goTypeName(typ)) // cycle to typ
return
}
}
@ -121,11 +126,19 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
if i > 0 {
buf.WriteString("; ")
}
if !f.embedded {
buf.WriteString(f.name)
buf.WriteByte(' ')
}
if f.embedded {
// emphasize that the embedded field's name
// doesn't match the field's type name
if f.name != embeddedFieldName(f.typ) {
buf.WriteString(" /* = ")
writeType(buf, f.typ, qf, visited)
buf.WriteString(" */")
}
} else {
buf.WriteByte(' ')
writeType(buf, f.typ, qf, visited)
}
if tag := t.Tag(i); tag != "" {
fmt.Fprintf(buf, " %q", tag)
}
@ -143,6 +156,14 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
buf.WriteString("func")
writeSignature(buf, t, qf, visited)
case *Sum:
for i, t := range t.types {
if i > 0 {
buf.WriteString(", ")
}
writeType(buf, t, qf, visited)
}
case *Interface:
// We write the source-level methods and embedded types rather
// than the actual method set since resolved method signatures
@ -168,6 +189,13 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
writeSignature(buf, m.typ.(*Signature), qf, visited)
empty = false
}
if !empty && t.allTypes != nil {
buf.WriteString("; ")
}
if t.allTypes != nil {
buf.WriteString("type ")
writeType(buf, t.allTypes, qf, visited)
}
} else {
// print explicit interface methods and embedded types
for i, m := range t.methods {
@ -178,8 +206,19 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
writeSignature(buf, m.typ.(*Signature), qf, visited)
empty = false
}
if !empty && t.types != nil {
buf.WriteString("; ")
}
if t.types != nil {
buf.WriteString("type ")
writeType(buf, t.types, qf, visited)
empty = false
}
if !empty && len(t.embeddeds) > 0 {
buf.WriteString("; ")
}
for i, typ := range t.embeddeds {
if i > 0 || len(t.methods) > 0 {
if i > 0 {
buf.WriteString("; ")
}
writeType(buf, typ, qf, visited)
@ -227,8 +266,90 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
}
case *Named:
writeTypeName(buf, t.obj, qf)
if t.targs != nil {
// instantiated type
buf.WriteByte('[')
writeTypeList(buf, t.targs, qf, visited)
buf.WriteByte(']')
} else if t.tparams != nil {
// parameterized type
writeTParamList(buf, t.tparams, qf, visited)
}
case *TypeParam:
s := "?"
if t.obj != nil {
s = t.obj.name
}
buf.WriteString(s + subscript(t.id))
case *instance:
buf.WriteByte(instanceMarker) // indicate "non-evaluated" syntactic instance
writeTypeName(buf, t.base.obj, qf)
buf.WriteByte('[')
writeTypeList(buf, t.targs, qf, visited)
buf.WriteByte(']')
case *bottom:
buf.WriteString("⊥")
case *top:
buf.WriteString("")
default:
// For externally defined implementations of Type.
buf.WriteString(t.String())
}
}
func writeTypeList(buf *bytes.Buffer, list []Type, qf Qualifier, visited []Type) {
for i, typ := range list {
if i > 0 {
buf.WriteString(", ")
}
writeType(buf, typ, qf, visited)
}
}
func writeTParamList(buf *bytes.Buffer, list []*TypeName, qf Qualifier, visited []Type) {
buf.WriteString("[")
var prev Type
for i, p := range list {
// TODO(rFindley) support 'any' sugar here.
var b Type = &emptyInterface
if t, _ := p.typ.(*TypeParam); t != nil && t.bound != nil {
b = t.bound
}
if i > 0 {
if b != prev {
// type bound changed - write previous one before advancing
buf.WriteByte(' ')
writeType(buf, prev, qf, visited)
}
buf.WriteString(", ")
}
prev = b
if t, _ := p.typ.(*TypeParam); t != nil {
if t.ptr {
buf.WriteByte('*')
}
writeType(buf, t, qf, visited)
} else {
buf.WriteString(p.name)
}
}
if prev != nil {
buf.WriteByte(' ')
writeType(buf, prev, qf, visited)
}
buf.WriteByte(']')
}
func writeTypeName(buf *bytes.Buffer, obj *TypeName, qf Qualifier) {
s := "<Named w/o object>"
if obj := t.obj; obj != nil {
if obj != nil {
if obj.pkg != nil {
writePackage(buf, obj.pkg, qf)
}
@ -238,11 +359,6 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
s = obj.name
}
buf.WriteString(s)
default:
// For externally defined implementations of Type.
buf.WriteString(t.String())
}
}
func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) {
@ -264,7 +380,7 @@ func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visi
} else {
// special case:
// append(s, "foo"...) leads to signature func([]byte, string...)
if t, ok := typ.Underlying().(*Basic); !ok || t.kind != String {
if t := asBasic(typ); t == nil || t.kind != String {
panic("internal error: string type expected")
}
writeType(buf, typ, qf, visited)
@ -287,6 +403,10 @@ func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
}
func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) {
if sig.tparams != nil {
writeTParamList(buf, sig.tparams, qf, visited)
}
writeTuple(buf, sig.params, sig.variadic, qf, visited)
n := sig.results.Len()
@ -305,3 +425,38 @@ func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []T
// multiple or named result(s)
writeTuple(buf, sig.results, false, qf, visited)
}
// embeddedFieldName returns an embedded field's name given its type.
// The result is "" if the type doesn't have an embedded field name.
func embeddedFieldName(typ Type) string {
switch t := typ.(type) {
case *Basic:
return t.name
case *Named:
return t.obj.name
case *Pointer:
// *T is ok, but **T is not
if _, ok := t.base.(*Pointer); !ok {
return embeddedFieldName(t.base)
}
case *instance:
return t.base.obj.name
}
return "" // not a (pointer to) a defined type
}
// subscript returns the decimal (utf8) representation of x using subscript digits.
func subscript(x uint64) string {
const w = len("₀") // all digits 0...9 have the same utf8 width
var buf [32 * w]byte
i := len(buf)
for {
i -= w
utf8.EncodeRune(buf[i:], '₀'+rune(x%10)) // '₀' == U+2080
x /= 10
if x == 0 {
break
}
}
return string(buf[i:])
}

View File

@ -96,6 +96,10 @@ var independentTestTypes = []testEntry{
dup("interface{m()}"),
dup(`interface{String() string; m(int) float32}`),
// TODO(rFindley) uncomment this once this AST is accepted, and add more test
// cases.
// dup(`interface{type int, float32, complex128}`),
// maps
dup("map[string]int"),
{"map[struct{x, y int}][]byte", "map[struct{x int; y int}][]byte"},

View File

@ -7,11 +7,13 @@
package types
import (
"fmt"
"go/ast"
"go/constant"
"go/token"
"sort"
"strconv"
"strings"
)
// ident type-checks identifier e and initializes x with the value or type of e.
@ -207,6 +209,12 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast
sig.variadic = variadic
}
// goTypeName returns the Go type name for typ and
// removes any occurences of "types." from that name.
func goTypeName(typ Type) string {
return strings.ReplaceAll(fmt.Sprintf("%T", typ), "types.", "")
}
// typInternal drives type checking of types.
// Must only be called by definedType.
//
@ -498,7 +506,7 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
// it if it's a valid interface.
typ := check.typ(f.Type)
utyp := check.underlying(typ)
utyp := under(typ)
if _, ok := utyp.(*Interface); !ok {
if utyp != Typ[Invalid] {
check.errorf(f.Type, _InvalidIfaceEmbed, "%s is not an interface", typ)
@ -521,10 +529,10 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d
sort.Sort(byUniqueMethodName(ityp.methods))
sort.Stable(byUniqueTypeName(ityp.embeddeds))
check.later(func() { check.completeInterface(ityp) })
check.later(func() { check.completeInterface(iface.Pos(), ityp) })
}
func (check *Checker) completeInterface(ityp *Interface) {
func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
if ityp.allMethods != nil {
return
}
@ -538,11 +546,18 @@ func (check *Checker) completeInterface(ityp *Interface) {
}
if trace {
check.trace(token.NoPos, "complete %s", ityp)
// Types don't generally have position information.
// If we don't have a valid pos provided, try to use
// one close enough.
if !pos.IsValid() && len(ityp.methods) > 0 {
pos = ityp.methods[0].pos
}
check.trace(pos, "complete %s", ityp)
check.indent++
defer func() {
check.indent--
check.trace(token.NoPos, "=> %s", ityp)
check.trace(pos, "=> %s (methods = %v, types = %v)", ityp, ityp.allMethods, ityp.allTypes)
}()
}
@ -592,25 +607,77 @@ func (check *Checker) completeInterface(ityp *Interface) {
addMethod(m.pos, m, true)
}
// collect types
allTypes := ityp.types
posList := check.posMap[ityp]
for i, typ := range ityp.embeddeds {
pos := posList[i] // embedding position
typ, ok := check.underlying(typ).(*Interface)
if !ok {
// An error was reported when collecting the embedded types.
// Ignore it.
utyp := under(typ)
etyp := asInterface(utyp)
if etyp == nil {
if utyp != Typ[Invalid] {
var format string
if _, ok := utyp.(*TypeParam); ok {
format = "%s is a type parameter, not an interface"
} else {
format = "%s is not an interface"
}
// TODO: correct error code.
check.errorf(atPos(pos), 0, format, typ)
}
continue
}
check.completeInterface(typ)
for _, m := range typ.allMethods {
check.completeInterface(pos, etyp)
for _, m := range etyp.allMethods {
addMethod(pos, m, false) // use embedding position pos rather than m.pos
}
allTypes = intersect(allTypes, etyp.allTypes)
}
if methods != nil {
sort.Sort(byUniqueMethodName(methods))
ityp.allMethods = methods
}
ityp.allTypes = allTypes
}
// intersect computes the intersection of the types x and y.
// Note: A incomming nil type stands for the top type. A top
// type result is returned as nil.
func intersect(x, y Type) (r Type) {
defer func() {
if r == theTop {
r = nil
}
}()
switch {
case x == theBottom || y == theBottom:
return theBottom
case x == nil || x == theTop:
return y
case y == nil || x == theTop:
return x
}
xtypes := unpackType(x)
ytypes := unpackType(y)
// Compute the list rtypes which includes only
// types that are in both xtypes and ytypes.
// Quadratic algorithm, but good enough for now.
// TODO(gri) fix this
var rtypes []Type
for _, x := range xtypes {
if includes(ytypes, x) {
rtypes = append(rtypes, x)
}
}
if rtypes == nil {
return theBottom
}
return NewSum(rtypes)
}
// byUniqueTypeName named type lists can be sorted by their unique type names.
@ -762,3 +829,13 @@ func embeddedFieldIdent(e ast.Expr) *ast.Ident {
}
return nil // invalid embedded field
}
// includes reports whether typ is in list.
func includes(list []Type, typ Type) bool {
for _, e := range list {
if Identical(typ, e) {
return true
}
}
return false
}