// Copyright 2017 The Go Authors. All rights reserved. // Use of this src code is governed by a BSD-style // license that can be found in the LICENSE file. package types import ( "bytes" "fmt" "go/ast" "go/token" ) // This file implements the collection of an interface's methods // without relying on partially computed types of methods or interfaces // for interface types declared at the package level. // // Because interfaces must not embed themselves, directly or indirectly, // the method set of a valid interface can always be computed independent // of any cycles that might exist via method signatures (see also issue #18395). // // Except for blank method name and interface cycle errors, no errors // are reported. Affected methods or embedded interfaces are silently // dropped. Subsequent type-checking of the interface will check // signatures and embedded interfaces and report errors at that time. // // Only infoFromTypeLit should be called directly from code outside this file // to compute an ifaceInfo. // ifaceInfo describes the method set for an interface. // The zero value for an ifaceInfo is a ready-to-use ifaceInfo representing // the empty interface. type ifaceInfo struct { explicits int // number of explicitly declared methods methods []*methodInfo // all methods, starting with explicitly declared ones in source order } // emptyIfaceInfo represents the ifaceInfo for the empty interface. var emptyIfaceInfo ifaceInfo func (info *ifaceInfo) String() string { var buf bytes.Buffer fmt.Fprintf(&buf, "interface{") for i, m := range info.methods { if i > 0 { fmt.Fprint(&buf, " ") } fmt.Fprint(&buf, m) } fmt.Fprintf(&buf, "}") return buf.String() } // methodInfo represents an interface method. // At least one of src or fun must be non-nil. // (Methods declared in the current package have a non-nil scope // and src, and eventually a non-nil fun field; imported and pre- // declared methods have a nil scope and src, and only a non-nil // fun field.) type methodInfo struct { scope *Scope // scope of interface method; or nil src *ast.Field // syntax tree representation of interface method; or nil fun *Func // corresponding fully type-checked method type; or nil } func (info *methodInfo) String() string { if info.fun != nil { return info.fun.name } return info.src.Names[0].Name } func (info *methodInfo) Pos() token.Pos { if info.fun != nil { return info.fun.Pos() } return info.src.Pos() } func (info *methodInfo) id(pkg *Package) string { if info.fun != nil { return info.fun.Id() } return Id(pkg, info.src.Names[0].Name) } // A methodInfoSet maps method ids to methodInfos. // It is used to determine duplicate declarations. // (A methodInfo set is the equivalent of an objset // but for methodInfos rather than Objects.) type methodInfoSet map[string]*methodInfo // insert attempts to insert an method m into the method set s. // If s already contains an alternative method alt with // the same name, insert leaves s unchanged and returns alt. // Otherwise it inserts m and returns nil. func (s *methodInfoSet) insert(pkg *Package, m *methodInfo) *methodInfo { id := m.id(pkg) if alt := (*s)[id]; alt != nil { return alt } if *s == nil { *s = make(methodInfoSet) } (*s)[id] = m return nil } // like Checker.declareInSet but for method infos. func (check *Checker) declareInMethodSet(mset *methodInfoSet, pos token.Pos, m *methodInfo) bool { if alt := mset.insert(check.pkg, m); alt != nil { check.errorf(pos, "%s redeclared", m) check.reportAltMethod(alt) return false } return true } // like Checker.reportAltDecl but for method infos. func (check *Checker) reportAltMethod(m *methodInfo) { if pos := m.Pos(); pos.IsValid() { // We use "other" rather than "previous" here because // the first declaration seen may not be textually // earlier in the source. check.errorf(pos, "\tother declaration of %s", m) // secondary error, \t indented } } // infoFromTypeLit computes the method set for the given interface iface // declared in scope. // If a corresponding type name exists (tname != nil), it is used for // cycle detection and to cache the method set. // The result is the method set, or nil if there is a cycle via embedded // interfaces. A non-nil result doesn't mean that there were no errors, // but they were either reported (e.g., blank methods), or will be found // (again) when computing the interface's type. // If tname is not nil it must be the last element in path. func (check *Checker) infoFromTypeLit(scope *Scope, iface *ast.InterfaceType, tname *TypeName, path []*TypeName) (info *ifaceInfo) { assert(iface != nil) // lazy-allocate interfaces map if check.interfaces == nil { check.interfaces = make(map[*TypeName]*ifaceInfo) } if trace { check.trace(iface.Pos(), "-- collect methods for %v (path = %s, objPath = %s)", iface, pathString(path), objPathString(check.objPath)) check.indent++ defer func() { check.indent-- check.trace(iface.Pos(), "=> %s", info) }() } // If the interface is named, check if we computed info already. // // This is not simply an optimization; we may run into stack // overflow with recursive interface declarations. Example: // // type T interface { // m() interface { T } // } // // (Since recursive definitions can only be expressed via names, // it is sufficient to track named interfaces here.) // // While at it, use the same mechanism to detect cycles. (We still // have the path-based cycle check because we want to report the // entire cycle if present.) if tname != nil { assert(path[len(path)-1] == tname) // tname must be last path element var found bool if info, found = check.interfaces[tname]; found { if info == nil { // We have a cycle and use check.cycle to report it. // We are guaranteed that check.cycle also finds the // cycle because when infoFromTypeLit is called, any // tname that's already in check.interfaces was also // added to the path. (But the converse is not true: // A non-nil tname is always the last element in path.) ok := check.cycle(tname, path, true) assert(ok) } return } check.interfaces[tname] = nil // computation started but not complete } if iface.Methods.List == nil { // fast track for empty interface info = &emptyIfaceInfo } else { // (syntactically) non-empty interface info = new(ifaceInfo) // collect explicitly declared methods and embedded interfaces var mset methodInfoSet var embeddeds []*ifaceInfo var positions []token.Pos // entries correspond to positions of embeddeds; used for error reporting for _, f := range iface.Methods.List { if len(f.Names) > 0 { // We have a method with name f.Names[0]. // (The parser ensures that there's only one method // and we don't care if a constructed AST has more.) // spec: "As with all method sets, in an interface type, // each method must have a unique non-blank name." if name := f.Names[0]; name.Name == "_" { check.errorf(name.Pos(), "invalid method name _") continue // ignore } m := &methodInfo{scope: scope, src: f} if check.declareInMethodSet(&mset, f.Pos(), m) { info.methods = append(info.methods, m) } } else { // We have an embedded interface and f.Type is its // (possibly qualified) embedded type name. Collect // it if it's a valid interface. var e *ifaceInfo switch ename := f.Type.(type) { case *ast.Ident: e = check.infoFromTypeName(scope, ename, path) case *ast.SelectorExpr: e = check.infoFromQualifiedTypeName(scope, ename) default: // The parser makes sure we only see one of the above. // Constructed ASTs may contain other (invalid) nodes; // we simply ignore them. The full type-checking pass // will report those as errors later. } if e != nil { embeddeds = append(embeddeds, e) positions = append(positions, f.Type.Pos()) } } } info.explicits = len(info.methods) // collect methods of embedded interfaces for i, e := range embeddeds { pos := positions[i] // position of type name of embedded interface for _, m := range e.methods { if check.declareInMethodSet(&mset, pos, m) { info.methods = append(info.methods, m) } } } } // mark check.interfaces as complete assert(info != nil) if tname != nil { check.interfaces[tname] = info } return } // infoFromTypeName computes the method set for the given type name // which must denote a type whose underlying type is an interface. // The same result qualifications apply as for infoFromTypeLit. // infoFromTypeName should only be called from infoFromTypeLit. func (check *Checker) infoFromTypeName(scope *Scope, name *ast.Ident, path []*TypeName) *ifaceInfo { // A single call of infoFromTypeName handles a sequence of (possibly // recursive) type declarations connected via unqualified type names. // Each type declaration leading to another typename causes a "tail call" // (goto) of this function. The general scenario looks like this: // // ... // type Pn T // previous declarations leading to T, path = [..., Pn] // type T interface { T0; ... } // T0 leads to call of infoFromTypeName // // // infoFromTypeName(name = T0, path = [..., Pn, T]) // type T0 T1 // path = [..., Pn, T, T0] // type T1 T2 <-+ // path = [..., Pn, T, T0, T1] // type T2 ... | // path = [..., Pn, T, T0, T1, T2] // type Tn T1 --+ // path = [..., Pn, T, T0, T1, T2, Tn] and T1 is in path => cycle // // infoFromTypeName returns nil when such a cycle is detected. But in // contrast to cycles involving interfaces, we must not report the // error for "type name only" cycles because they will be found again // during type-checking of embedded interfaces. Reporting those cycles // here would lead to double reporting. Cycles involving embedding are // not reported again later because type-checking of interfaces relies // on the ifaceInfos computed here which are cycle-free by design. // // Remember the path length to detect "type name only" cycles. start := len(path) typenameLoop: // name must be a type name denoting a type whose underlying type is an interface _, obj := scope.LookupParent(name.Name, check.pos) if obj == nil { return nil } tname, _ := obj.(*TypeName) if tname == nil { return nil } // We have a type name. It may be predeclared (error type), // imported (dot import), or declared by a type declaration. // It may not be an interface (e.g., predeclared type int). // Resolve it by analyzing each possible case. // Abort but don't report an error if we have a "type name only" // cycle (see big function comment). if check.cycle(tname, path[start:], false) { return nil } // Abort and report an error if we have a general cycle. if check.cycle(tname, path, true) { return nil } path = append(path, tname) // If tname is a package-level type declaration, it must be // in the objMap. Follow the RHS of that declaration if so. // The RHS may be a literal type (likely case), or another // (possibly parenthesized and/or qualified) type name. // (The declaration may be an alias declaration, but it // doesn't matter for the purpose of determining the under- // lying interface.) if decl := check.objMap[tname]; decl != nil { switch typ := unparen(decl.typ).(type) { case *ast.Ident: // type tname T name = typ goto typenameLoop case *ast.SelectorExpr: // type tname p.T return check.infoFromQualifiedTypeName(decl.file, typ) case *ast.InterfaceType: // type tname interface{...} // If tname is fully type-checked at this point (tname.color() == black) // we could use infoFromType here. But in this case, the interface must // be in the check.interfaces cache as well, which will be hit when we // call infoFromTypeLit below, and which will be faster. It is important // that we use that previously computed interface because its methods // have the correct receiver type (for go/types clients). Thus, the // check.interfaces cache must be up-to-date across even across multiple // check.Files calls (was bug - see issue #29029). return check.infoFromTypeLit(decl.file, typ, tname, path) } // type tname X // and X is not an interface type return nil } // If tname is not a package-level declaration, in a well-typed // program it should be a predeclared (error type), imported (dot // import), or function local declaration. Either way, it should // have been fully declared before use, except if there is a direct // cycle, and direct cycles will be caught above. Also, the denoted // type should be an interface (e.g., int is not an interface). if typ := tname.typ; typ != nil { // typ should be an interface if ityp, _ := typ.Underlying().(*Interface); ityp != nil { return infoFromType(ityp) } } // In all other cases we have some error. return nil } // infoFromQualifiedTypeName computes the method set for the given qualified type name, or nil. func (check *Checker) infoFromQualifiedTypeName(scope *Scope, qname *ast.SelectorExpr) *ifaceInfo { // see also Checker.selector name, _ := qname.X.(*ast.Ident) if name == nil { return nil } _, obj1 := scope.LookupParent(name.Name, check.pos) if obj1 == nil { return nil } pname, _ := obj1.(*PkgName) if pname == nil { return nil } assert(pname.pkg == check.pkg) obj2 := pname.imported.scope.Lookup(qname.Sel.Name) if obj2 == nil || !obj2.Exported() { return nil } tname, _ := obj2.(*TypeName) if tname == nil { return nil } ityp, _ := tname.typ.Underlying().(*Interface) if ityp == nil { return nil } return infoFromType(ityp) } // infoFromType computes the method set for the given interface type. // The result is never nil. func infoFromType(typ *Interface) *ifaceInfo { assert(typ.allMethods != nil) // typ must be completely set up // fast track for empty interface n := len(typ.allMethods) if n == 0 { return &emptyIfaceInfo } info := new(ifaceInfo) info.explicits = len(typ.methods) info.methods = make([]*methodInfo, n) // If there are no embedded interfaces, simply collect the // explicitly declared methods (optimization of common case). if len(typ.methods) == n { for i, m := range typ.methods { info.methods[i] = &methodInfo{fun: m} } return info } // Interface types have a separate list for explicitly declared methods // which shares its methods with the list of all (explicitly declared or // embedded) methods. Collect all methods in a set so we can separate // the embedded methods from the explicitly declared ones. all := make(map[*Func]bool, n) for _, m := range typ.allMethods { all[m] = true } assert(len(all) == n) // methods must be unique // collect explicitly declared methods info.methods = make([]*methodInfo, n) for i, m := range typ.methods { info.methods[i] = &methodInfo{fun: m} delete(all, m) } // collect remaining (embedded) methods i := len(typ.methods) for m := range all { info.methods[i] = &methodInfo{fun: m} i++ } assert(i == n) return info }