]> Cypherpunks.ru repositories - gostls13.git/blobdiff - src/go/types/interface.go
go/types, types2: introduce _Alias type node
[gostls13.git] / src / go / types / interface.go
index fd3fe0ef91eaa4e37525282acf45d38d2701cf0b..01bbb08e0efe5573f205756b2c1d450913b4d066 100644 (file)
 package types
 
 import (
-       "fmt"
        "go/ast"
-       "go/internal/typeparams"
        "go/token"
-       "sort"
+       . "internal/types/errors"
 )
 
-func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, def *Named) {
-       var tlist *ast.Ident // "type" name of first entry in a type list declaration
-       var types []ast.Expr
-       for _, f := range iface.Methods.List {
-               if len(f.Names) > 0 {
-                       // We have a method with name f.Names[0], or a type
-                       // of a type list (name.Name == "type").
-                       // (The parser ensures that there's only one method
-                       // and we don't care if a constructed AST has more.)
-                       name := f.Names[0]
-                       if name.Name == "_" {
-                               check.errorf(name, _BlankIfaceMethod, "invalid method name _")
-                               continue // ignore
-                       }
+// ----------------------------------------------------------------------------
+// API
 
-                       if name.Name == "type" {
-                               // Always collect all type list entries, even from
-                               // different type lists, under the assumption that
-                               // the author intended to include all types.
-                               types = append(types, f.Type)
-                               if tlist != nil && tlist != name {
-                                       check.errorf(name, _Todo, "cannot have multiple type lists in an interface")
-                               }
-                               tlist = name
-                               continue
-                       }
-
-                       typ := check.typ(f.Type)
-                       sig, _ := typ.(*Signature)
-                       if sig == nil {
-                               if typ != Typ[Invalid] {
-                                       check.invalidAST(f.Type, "%s is not a method signature", typ)
-                               }
-                               continue // ignore
-                       }
+// An Interface represents an interface type.
+type Interface struct {
+       check     *Checker     // for error reporting; nil once type set is computed
+       methods   []*Func      // ordered list of explicitly declared methods
+       embeddeds []Type       // ordered list of explicitly embedded elements
+       embedPos  *[]token.Pos // positions of embedded elements; or nil (for error messages) - use pointer to save space
+       implicit  bool         // interface is wrapper for type set literal (non-interface T, ~T, or A|B)
+       complete  bool         // indicates that obj, methods, and embeddeds are set and type set can be computed
 
-                       // Always type-check method type parameters but complain if they are not enabled.
-                       // (This extra check is needed here because interface method signatures don't have
-                       // a receiver specification.)
-                       if sig.tparams != nil {
-                               var at positioner = f.Type
-                               if tparams := typeparams.Get(f.Type); tparams != nil {
-                                       at = tparams
-                               }
-                               check.errorf(at, _Todo, "methods cannot have type parameters")
-                       }
+       tset *_TypeSet // type set described by this interface, computed lazily
+}
 
-                       // use named receiver type if available (for better error messages)
-                       var recvTyp Type = ityp
-                       if def != nil {
-                               recvTyp = def
-                       }
-                       sig.recv = NewVar(name.Pos(), check.pkg, "", recvTyp)
-
-                       m := NewFunc(name.Pos(), check.pkg, name.Name, sig)
-                       check.recordDef(name, m)
-                       ityp.methods = append(ityp.methods, m)
-               } else {
-                       // We have an embedded type. completeInterface will
-                       // eventually verify that we have an interface.
-                       ityp.embeddeds = append(ityp.embeddeds, check.typ(f.Type))
-                       check.posMap[ityp] = append(check.posMap[ityp], f.Type.Pos())
-               }
-       }
+// typeSet returns the type set for interface t.
+func (t *Interface) typeSet() *_TypeSet { return computeInterfaceTypeSet(t.check, nopos, t) }
 
-       // type constraints
-       ityp.types = _NewSum(check.collectTypeConstraints(iface.Pos(), types))
+// emptyInterface represents the empty (completed) interface
+var emptyInterface = Interface{complete: true, tset: &topTypeSet}
 
-       if len(ityp.methods) == 0 && ityp.types == nil && len(ityp.embeddeds) == 0 {
-               // empty interface
-               ityp.allMethods = markComplete
-               return
+// NewInterface returns a new interface for the given methods and embedded types.
+// NewInterface takes ownership of the provided methods and may modify their types
+// by setting missing receivers.
+//
+// Deprecated: Use NewInterfaceType instead which allows arbitrary embedded types.
+func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
+       tnames := make([]Type, len(embeddeds))
+       for i, t := range embeddeds {
+               tnames[i] = t
        }
-
-       // sort for API stability
-       sortMethods(ityp.methods)
-       sortTypes(ityp.embeddeds)
-
-       check.later(func() { check.completeInterface(iface.Pos(), ityp) })
+       return NewInterfaceType(methods, tnames)
 }
 
-func (check *Checker) collectTypeConstraints(pos token.Pos, types []ast.Expr) []Type {
-       list := make([]Type, 0, len(types)) // assume all types are correct
-       for _, texpr := range types {
-               if texpr == nil {
-                       check.invalidAST(atPos(pos), "missing type constraint")
-                       continue
+// NewInterfaceType returns a new interface for the given methods and embedded
+// types. NewInterfaceType takes ownership of the provided methods and may
+// modify their types by setting missing receivers.
+//
+// To avoid race conditions, the interface's type set should be computed before
+// concurrent use of the interface, by explicitly calling Complete.
+func NewInterfaceType(methods []*Func, embeddeds []Type) *Interface {
+       if len(methods) == 0 && len(embeddeds) == 0 {
+               return &emptyInterface
+       }
+
+       // set method receivers if necessary
+       typ := (*Checker)(nil).newInterface()
+       for _, m := range methods {
+               if sig := m.typ.(*Signature); sig.recv == nil {
+                       sig.recv = NewVar(m.pos, m.pkg, "", typ)
                }
-               list = append(list, check.varType(texpr))
        }
 
-       // Ensure that each type is only present once in the type list.  Types may be
-       // interfaces, which may not be complete yet. It's ok to do this check at the
-       // end because it's not a requirement for correctness of the code.
-       // Note: This is a quadratic algorithm, but type lists tend to be short.
-       check.later(func() {
-               for i, t := range list {
-                       if t := asInterface(t); t != nil {
-                               check.completeInterface(types[i].Pos(), t)
-                       }
-                       if includes(list[:i], t) {
-                               check.softErrorf(types[i], _Todo, "duplicate type %s in type list", t)
-                       }
-               }
-       })
+       // sort for API stability
+       sortMethods(methods)
 
-       return list
-}
+       typ.methods = methods
+       typ.embeddeds = embeddeds
+       typ.complete = true
 
-// 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
+       return typ
 }
 
-func (check *Checker) completeInterface(pos token.Pos, ityp *Interface) {
-       if ityp.allMethods != nil {
-               return
+// check may be nil
+func (check *Checker) newInterface() *Interface {
+       typ := &Interface{check: check}
+       if check != nil {
+               check.needsCleanup(typ)
        }
+       return typ
+}
 
-       // completeInterface may be called via the LookupFieldOrMethod,
-       // MissingMethod, Identical, or IdenticalIgnoreTags external API
-       // in which case check will be nil. In this case, type-checking
-       // must be finished and all interfaces should have been completed.
-       if check == nil {
-               panic("internal error: incomplete interface")
-       }
-       completeInterface(check, pos, ityp)
+// MarkImplicit marks the interface t as implicit, meaning this interface
+// corresponds to a constraint literal such as ~T or A|B without explicit
+// interface embedding. MarkImplicit should be called before any concurrent use
+// of implicit interfaces.
+func (t *Interface) MarkImplicit() {
+       t.implicit = true
 }
 
-func completeInterface(check *Checker, pos token.Pos, ityp *Interface) {
-       assert(ityp.allMethods == nil)
+// NumExplicitMethods returns the number of explicitly declared methods of interface t.
+func (t *Interface) NumExplicitMethods() int { return len(t.methods) }
 
-       if check != nil && trace {
-               // 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
-               }
+// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods().
+// The methods are ordered by their unique [Id].
+func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] }
 
-               check.trace(pos, "complete %s", ityp)
-               check.indent++
-               defer func() {
-                       check.indent--
-                       check.trace(pos, "=> %s (methods = %v, types = %v)", ityp, ityp.allMethods, ityp.allTypes)
-               }()
-       }
+// NumEmbeddeds returns the number of embedded types in interface t.
+func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) }
 
-       // An infinitely expanding interface (due to a cycle) is detected
-       // elsewhere (Checker.validType), so here we simply assume we only
-       // have valid interfaces. Mark the interface as complete to avoid
-       // infinite recursion if the validType check occurs later for some
-       // reason.
-       ityp.allMethods = markComplete
-
-       // Methods of embedded interfaces are collected unchanged; i.e., the identity
-       // of a method I.m's Func Object of an interface I is the same as that of
-       // the method m in an interface that embeds interface I. On the other hand,
-       // if a method is embedded via multiple overlapping embedded interfaces, we
-       // don't provide a guarantee which "original m" got chosen for the embedding
-       // interface. See also issue #34421.
-       //
-       // If we don't care to provide this identity guarantee anymore, instead of
-       // reusing the original method in embeddings, we can clone the method's Func
-       // Object and give it the position of a corresponding embedded interface. Then
-       // we can get rid of the mpos map below and simply use the cloned method's
-       // position.
-
-       var todo []*Func
-       var seen objset
-       var methods []*Func
-       mpos := make(map[*Func]token.Pos) // method specification or method embedding position, for good error messages
-       addMethod := func(pos token.Pos, m *Func, explicit bool) {
-               switch other := seen.insert(m); {
-               case other == nil:
-                       methods = append(methods, m)
-                       mpos[m] = pos
-               case explicit:
-                       if check == nil {
-                               panic(fmt.Sprintf("%v: duplicate method %s", m.pos, m.name))
-                       }
-                       check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
-                       check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
-               default:
-                       // We have a duplicate method name in an embedded (not explicitly declared) method.
-                       // Check method signatures after all types are computed (issue #33656).
-                       // If we're pre-go1.14 (overlapping embeddings are not permitted), report that
-                       // error here as well (even though we could do it eagerly) because it's the same
-                       // error message.
-                       if check == nil {
-                               // check method signatures after all locally embedded interfaces are computed
-                               todo = append(todo, m, other.(*Func))
-                               break
-                       }
-                       check.later(func() {
-                               if !check.allowVersion(m.pkg, 1, 14) || !check.identical(m.typ, other.Type()) {
-                                       check.errorf(atPos(pos), _DuplicateDecl, "duplicate method %s", m.name)
-                                       check.errorf(atPos(mpos[other.(*Func)]), _DuplicateDecl, "\tother declaration of %s", m.name) // secondary error, \t indented
-                               }
-                       })
-               }
-       }
+// Embedded returns the i'th embedded defined (*[Named]) type of interface t for 0 <= i < t.NumEmbeddeds().
+// The result is nil if the i'th embedded type is not a defined type.
+//
+// Deprecated: Use [Interface.EmbeddedType] which is not restricted to defined (*[Named]) types.
+func (t *Interface) Embedded(i int) *Named { return asNamed(t.embeddeds[i]) }
 
-       for _, m := range ityp.methods {
-               addMethod(m.pos, m, true)
-       }
+// EmbeddedType returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds().
+func (t *Interface) EmbeddedType(i int) Type { return t.embeddeds[i] }
 
-       // collect types
-       allTypes := ityp.types
+// NumMethods returns the total number of methods of interface t.
+func (t *Interface) NumMethods() int { return t.typeSet().NumMethods() }
 
-       var posList []token.Pos
-       if check != nil {
-               posList = check.posMap[ityp]
-       }
-       for i, typ := range ityp.embeddeds {
-               var pos token.Pos // embedding position
-               if posList != nil {
-                       pos = posList[i]
-               }
-               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"
-                               }
-                               if check != nil {
-                                       // TODO: correct error code.
-                                       check.errorf(atPos(pos), _InvalidIfaceEmbed, format, typ)
-                               } else {
-                                       panic(fmt.Sprintf(format, typ))
-                               }
-                       }
-                       continue
-               }
-               if etyp.allMethods == nil {
-                       completeInterface(check, pos, etyp)
-               }
-               for _, m := range etyp.allMethods {
-                       addMethod(pos, m, false) // use embedding position pos rather than m.pos
-               }
-               allTypes = intersect(allTypes, etyp.allTypes)
-       }
+// Method returns the i'th method of interface t for 0 <= i < t.NumMethods().
+// The methods are ordered by their unique Id.
+func (t *Interface) Method(i int) *Func { return t.typeSet().Method(i) }
 
-       // process todo's (this only happens if check == nil)
-       for i := 0; i < len(todo); i += 2 {
-               m := todo[i]
-               other := todo[i+1]
-               if !Identical(m.typ, other.typ) {
-                       panic(fmt.Sprintf("%v: duplicate method %s", m.pos, m.name))
-               }
-       }
+// Empty reports whether t is the empty interface.
+func (t *Interface) Empty() bool { return t.typeSet().IsAll() }
+
+// IsComparable reports whether each type in interface t's type set is comparable.
+func (t *Interface) IsComparable() bool { return t.typeSet().IsComparable(nil) }
+
+// IsMethodSet reports whether the interface t is fully described by its method
+// set.
+func (t *Interface) IsMethodSet() bool { return t.typeSet().IsMethodSet() }
 
-       if methods != nil {
-               sort.Sort(byUniqueMethodName(methods))
-               ityp.allMethods = methods
+// IsImplicit reports whether the interface t is a wrapper for a type set literal.
+func (t *Interface) IsImplicit() bool { return t.implicit }
+
+// Complete computes the interface's type set. It must be called by users of
+// [NewInterfaceType] and [NewInterface] after the interface's embedded types are
+// fully defined and before using the interface type in any way other than to
+// form other types. The interface must not contain duplicate methods or a
+// panic occurs. Complete returns the receiver.
+//
+// Interface types that have been completed are safe for concurrent use.
+func (t *Interface) Complete() *Interface {
+       if !t.complete {
+               t.complete = true
        }
-       ityp.allTypes = allTypes
+       t.typeSet() // checks if t.tset is already set
+       return t
+}
+
+func (t *Interface) Underlying() Type { return t }
+func (t *Interface) String() string   { return TypeString(t, nil) }
+
+// ----------------------------------------------------------------------------
+// Implementation
+
+func (t *Interface) cleanup() {
+       t.typeSet() // any interface that escapes type checking must be safe for concurrent use
+       t.check = nil
+       t.embedPos = nil
 }
 
-// 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
+func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, def *TypeName) {
+       addEmbedded := func(pos token.Pos, typ Type) {
+               ityp.embeddeds = append(ityp.embeddeds, typ)
+               if ityp.embedPos == nil {
+                       ityp.embedPos = new([]token.Pos)
                }
-       }()
-
-       switch {
-       case x == theBottom || y == theBottom:
-               return theBottom
-       case x == nil || x == theTop:
-               return y
-       case y == nil || x == theTop:
-               return x
+               *ityp.embedPos = append(*ityp.embedPos, pos)
        }
 
-       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)
+       for _, f := range iface.Methods.List {
+               if len(f.Names) == 0 {
+                       addEmbedded(f.Type.Pos(), parseUnion(check, f.Type))
+                       continue
                }
-       }
+               // f.Name != nil
 
-       if rtypes == nil {
-               return theBottom
-       }
-       return _NewSum(rtypes)
-}
+               // We have a method with name f.Names[0].
+               name := f.Names[0]
+               if name.Name == "_" {
+                       check.error(name, BlankIfaceMethod, "methods must have a unique non-blank name")
+                       continue // ignore
+               }
 
-func sortTypes(list []Type) {
-       sort.Stable(byUniqueTypeName(list))
-}
+               typ := check.typ(f.Type)
+               sig, _ := typ.(*Signature)
+               if sig == nil {
+                       if isValid(typ) {
+                               check.errorf(f.Type, InvalidSyntaxTree, "%s is not a method signature", typ)
+                       }
+                       continue // ignore
+               }
 
-// byUniqueTypeName named type lists can be sorted by their unique type names.
-type byUniqueTypeName []Type
+               // The go/parser doesn't accept method type parameters but an ast.FuncType may have them.
+               if sig.tparams != nil {
+                       var at positioner = f.Type
+                       if ftyp, _ := f.Type.(*ast.FuncType); ftyp != nil && ftyp.TypeParams != nil {
+                               at = ftyp.TypeParams
+                       }
+                       check.error(at, InvalidSyntaxTree, "methods cannot have type parameters")
+               }
 
-func (a byUniqueTypeName) Len() int           { return len(a) }
-func (a byUniqueTypeName) Less(i, j int) bool { return sortName(a[i]) < sortName(a[j]) }
-func (a byUniqueTypeName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+               // use named receiver type if available (for better error messages)
+               var recvTyp Type = ityp
+               if def != nil {
+                       if named := asNamed(def.typ); named != nil {
+                               recvTyp = named
+                       }
+               }
+               sig.recv = NewVar(name.Pos(), check.pkg, "", recvTyp)
 
-func sortName(t Type) string {
-       if named := asNamed(t); named != nil {
-               return named.obj.Id()
+               m := NewFunc(name.Pos(), check.pkg, name.Name, sig)
+               check.recordDef(name, m)
+               ityp.methods = append(ityp.methods, m)
        }
-       return ""
-}
 
-func sortMethods(list []*Func) {
-       sort.Sort(byUniqueMethodName(list))
-}
+       // All methods and embedded elements for this interface are collected;
+       // i.e., this interface may be used in a type set computation.
+       ityp.complete = true
 
-func assertSortedMethods(list []*Func) {
-       if !debug {
-               panic("internal error: assertSortedMethods called outside debug mode")
-       }
-       if !sort.IsSorted(byUniqueMethodName(list)) {
-               panic("internal error: methods not sorted")
+       if len(ityp.methods) == 0 && len(ityp.embeddeds) == 0 {
+               // empty interface
+               ityp.tset = &topTypeSet
+               return
        }
-}
 
-// byUniqueMethodName method lists can be sorted by their unique method names.
-type byUniqueMethodName []*Func
+       // sort for API stability
+       sortMethods(ityp.methods)
+       // (don't sort embeddeds: they must correspond to *embedPos entries)
 
-func (a byUniqueMethodName) Len() int           { return len(a) }
-func (a byUniqueMethodName) Less(i, j int) bool { return a[i].Id() < a[j].Id() }
-func (a byUniqueMethodName) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
+       // Compute type set as soon as possible to report any errors.
+       // Subsequent uses of type sets will use this computed type
+       // set and won't need to pass in a *Checker.
+       check.later(func() {
+               computeInterfaceTypeSet(check, iface.Pos(), ityp)
+       }).describef(iface, "compute type set for %s", ityp)
+}