diff --git a/go/types/api.go b/go/types/api.go index d87a98cf..34bd3c83 100644 --- a/go/types/api.go +++ b/go/types/api.go @@ -32,7 +32,8 @@ import ( // Check type-checks a package and returns the resulting package object, // or a nil package and the first error. The package is specified by a // list of *ast.Files and corresponding file set, and the import path -// the package is identified with. The path must not be empty or dot ("."). +// the package is identified with. The clean path must not be empty or +// dot ("."). // // For more control over type-checking and results, use Config.Check. func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error) { @@ -106,10 +107,11 @@ type Info struct { // If Implicits != nil, it records the object for each node that implicitly // declares objects. The following node and object types may appear: // - // node obj + // node declared object + // // *ast.ImportSpec *Package (imports w/o renames), or imported objects (dot-imports) - // *ast.CaseClause type-specific variable introduced for each single-type type switch clause - // *ast.Field anonymous struct field or parameter, embedded interface + // *ast.CaseClause type-specific *Var introduced for each single-type type switch clause + // *ast.Field anonymous struct field or parameter *Var // Implicits map[ast.Node]Object } @@ -117,7 +119,7 @@ type Info struct { // Check type-checks a package and returns the resulting package object, the first // error if any, and if info != nil, additional type information. The package is // specified by a list of *ast.Files and corresponding file set, and the import -// path the package is identified with. The path must not be empty or dot ("."). +// path the package is identified with. The clean path must not be empty or dot ("."). func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) { return conf.check(path, fset, files, info) } diff --git a/go/types/check.go b/go/types/check.go index 449b5e65..84af263b 100644 --- a/go/types/check.go +++ b/go/types/check.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file implements the Check function, which typechecks a package. +// This file implements the Check function, which drives type-checking. package types @@ -29,31 +29,31 @@ const retainASTLinks = true // exprInfo stores type and constant value for an untyped expression. type exprInfo struct { - isLhs bool // expression is lhs operand of a shift with delayed type check + isLhs bool // expression is lhs operand of a shift with delayed type-check typ *Basic val exact.Value // constant value; or nil (if not a constant) } -// A checker is an instance of the type checker. +// A checker is an instance of the type-checker. type checker struct { conf *Config fset *token.FileSet - Info + pkg *Package // current package - // lazily initialized - pkg *Package // current package - firsterr error // first error encountered - methods map[*TypeName]*Scope // maps type names to associated methods + methods map[string][]*Func // maps type names to associated methods conversions map[*ast.CallExpr]bool // set of type-checked conversions (to distinguish from calls) untyped map[ast.Expr]exprInfo // map of expressions without final type - objMap map[Object]*decl // if set we are in the package-global declaration phase (otherwise all objects seen must be declared) - topScope *Scope // topScope for lookups, non-global declarations - iota exact.Value // value of iota in a constant declaration; nil otherwise + firsterr error // first error encountered + Info // collected type info + + objMap map[Object]*declInfo // if set we are in the package-level declaration phase (otherwise all objects seen must be declared) + topScope *Scope // topScope for lookups, non-global declarations + iota exact.Value // value of iota in a constant declaration; nil otherwise // functions funclist []function // list of functions/methods with correct signatures and non-empty bodies - funcsig *Signature // signature of currently typechecked function + funcsig *Signature // signature of currently type-checked function // debugging indent int // indentation for tracing @@ -64,7 +64,7 @@ func newChecker(conf *Config, fset *token.FileSet, pkg *Package) *checker { conf: conf, fset: fset, pkg: pkg, - methods: make(map[*TypeName]*Scope), + methods: make(map[string][]*Func), conversions: make(map[*ast.CallExpr]bool), untyped: make(map[ast.Expr]exprInfo), } @@ -97,15 +97,15 @@ func (check *checker) recordImplicit(node ast.Node, obj Object) { } type function struct { - file *Scope // only valid if resolve is set - obj *Func // for debugging/tracing only + file *Scope + obj *Func // for debugging/tracing only sig *Signature body *ast.BlockStmt } // later adds a function with non-empty body to the list of functions // that need to be processed after all package-level declarations -// are typechecked. +// are type-checked. // func (check *checker) later(f *Func, sig *Signature, body *ast.BlockStmt) { // functions implemented elsewhere (say in assembly) have no body @@ -170,28 +170,8 @@ func (conf *Config) check(pkgPath string, fset *token.FileSet, files []*ast.File check.Info = *info } - // TODO(gri) resolveFiles needs to be split up and renamed (cleanup) check.resolveFiles(files[:i]) - // typecheck all function/method bodies - // (funclist may grow when checking statements - do not use range clause!) - for i := 0; i < len(check.funclist); i++ { - f := check.funclist[i] - if trace { - s := "" - if f.obj != nil { - s = f.obj.name - } - fmt.Println("---", s) - } - check.topScope = f.sig.scope // open the function scope - check.funcsig = f.sig - check.stmtList(f.body.List) - if f.sig.results.Len() > 0 && f.body != nil && !check.isTerminating(f.body, "") { - check.errorf(f.body.Rbrace, "missing return") - } - } - // remaining untyped expressions must indeed be untyped if debug { for x, info := range check.untyped { diff --git a/go/types/objects.go b/go/types/objects.go index 7c86271a..ccaa1778 100644 --- a/go/types/objects.go +++ b/go/types/objects.go @@ -181,12 +181,10 @@ func (obj *Var) String() string { return obj.toString("var", obj.typ) } // A Func represents a declared function. type Func struct { object - - decl *ast.FuncDecl // TODO(gri) can we get rid of this field? } func NewFunc(pos token.Pos, pkg *Package, name string, typ Type) *Func { - return &Func{object{nil, pos, pkg, name, typ}, nil} + return &Func{object{nil, pos, pkg, name, typ}} } func (obj *Func) String() string { return obj.toString("func", obj.typ) } diff --git a/go/types/resolver.go b/go/types/resolver.go index acbf4fc1..166bbe2e 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -6,6 +6,7 @@ package types import ( "errors" + "fmt" "go/ast" "go/token" "strconv" @@ -29,17 +30,12 @@ func (check *checker) declare(scope *Scope, id *ast.Ident, obj Object) { } } -// A decl describes a package-level const, type, var, or func declaration. -type decl struct { - file *Scope // scope of file containing this declaration - typ ast.Expr // type, or nil - init ast.Expr // initialization expression, or nil -} - -// An mdecl describes a method declaration. -type mdecl struct { - file *Scope // scope of file containing this declaration - meth *ast.FuncDecl +// A declInfo describes a package-level const, type, var, or func declaration. +type declInfo struct { + file *Scope // scope of file containing this declaration + typ ast.Expr // type, or nil + init ast.Expr // initialization expression, or nil + fdecl *ast.FuncDecl // function declaration, or nil } // A multiExpr describes the lhs variables and a single but @@ -83,19 +79,22 @@ func (check *checker) arityMatch(s, init *ast.ValueSpec) { } } +// TODO(gri) Split resolveFiles into smaller components. + func (check *checker) resolveFiles(files []*ast.File) { pkg := check.pkg // Phase 1: Pre-declare all package-level objects so that they can be found - // independent of source order. + // independent of source order. Collect methods for a later phase. - var scopes []*Scope // corresponding file scope per file - var objList []Object - var objMap = make(map[Object]*decl) - var methods []*mdecl - var fileScope *Scope // current file scope, used by declare + var ( + fileScope *Scope // current file scope, used by declare + scopes []*Scope // corresponding file scope per file + objList []Object // list of objects to type-check + objMap = make(map[Object]*declInfo) // declaration info for each object (incl. methods) + ) - declare := func(ident *ast.Ident, obj Object, typ, init ast.Expr) { + declare := func(ident *ast.Ident, obj Object, typ, init ast.Expr, fdecl *ast.FuncDecl) { assert(ident.Name == obj.Name()) // spec: "A package-scope or file-scope identifier with name init @@ -115,7 +114,7 @@ func (check *checker) resolveFiles(files []*ast.File) { } objList = append(objList, obj) - objMap[obj] = &decl{fileScope, typ, init} + objMap[obj] = &declInfo{fileScope, typ, init, fdecl} } importer := check.conf.Import @@ -209,7 +208,7 @@ func (check *checker) resolveFiles(files []*ast.File) { init = last.Values[i] } - declare(name, obj, last.Type, init) + declare(name, obj, last.Type, init, nil) } check.arityMatch(s, last) @@ -238,7 +237,7 @@ func (check *checker) resolveFiles(files []*ast.File) { } } - declare(name, obj, s.Type, init) + declare(name, obj, s.Type, init, nil) } check.arityMatch(s, nil) @@ -249,7 +248,7 @@ func (check *checker) resolveFiles(files []*ast.File) { case *ast.TypeSpec: obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) - declare(s.Name, obj, s.Type, nil) + declare(s.Name, obj, s.Type, nil, nil) default: check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) @@ -257,14 +256,39 @@ func (check *checker) resolveFiles(files []*ast.File) { } case *ast.FuncDecl: - if d.Recv != nil { - // collect method - methods = append(methods, &mdecl{fileScope, d}) + obj := NewFunc(d.Name.Pos(), pkg, d.Name.Name, nil) + if d.Recv == nil { + // regular function + declare(d.Name, obj, nil, nil, d) continue } - obj := NewFunc(d.Name.Pos(), pkg, d.Name.Name, nil) - obj.decl = d - declare(d.Name, obj, nil, nil) + + // Methods are associated with the receiver base type + // which we don't have yet. Instead, collect methods + // with receiver base type name so that they are known + // when the receiver base type is type-checked. + + // The receiver type must be one of the following + // - *ast.Ident + // - *ast.StarExpr{*ast.Ident} + // - *ast.BadExpr (parser error) + typ := d.Recv.List[0].Type + if ptr, _ := typ.(*ast.StarExpr); ptr != nil { + typ = ptr.X + } + + // Associate method with receiver base type name, if possible. + // Methods with receiver types that are not type names, that + // are blank _ names, or methods with blank names are ignored; + // the respective error will be reported when the method signature + // is type-checked. + if ident, _ := typ.(*ast.Ident); ident != nil && ident.Name != "_" && obj.name != "_" { + check.methods[ident.Name] = append(check.methods[ident.Name], obj) + } + + // Collect methods like other objects. + objList = append(objList, obj) + objMap[obj] = &declInfo{fileScope, nil, nil, d} default: check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) @@ -272,7 +296,8 @@ func (check *checker) resolveFiles(files []*ast.File) { } } - // Phase 2: Objects in file scopes and package scopes must have different names. + // Phase 2: Verify that objects in package and file scopes have different names. + for _, scope := range scopes { for _, obj := range scope.entries { if alt := pkg.scope.Lookup(nil, obj.Name()); alt != nil { @@ -281,64 +306,7 @@ func (check *checker) resolveFiles(files []*ast.File) { } } - // Phase 3: Associate methods with types. - // We do this after all top-level type names have been collected. - - for _, meth := range methods { - m := meth.meth - // The receiver type must be one of the following: - // - *ast.Ident - // - *ast.StarExpr{*ast.Ident} - // - *ast.BadExpr (parser error) - typ := m.Recv.List[0].Type - if ptr, ok := typ.(*ast.StarExpr); ok { - typ = ptr.X - } - // determine receiver base type name - // Note: We cannot simply call check.typ because this will require - // check.objMap to be usable, which it isn't quite yet. - ident, ok := typ.(*ast.Ident) - if !ok { - // Disabled for now since the parser reports this error. - // check.errorf(typ.Pos(), "receiver base type must be an (unqualified) identifier") - continue // ignore this method - } - // determine receiver base type object - var tname *TypeName - if obj := pkg.scope.LookupParent(ident.Name); obj != nil { - obj, ok := obj.(*TypeName) - if !ok { - check.errorf(ident.Pos(), "%s is not a type", ident.Name) - continue // ignore this method - } - if obj.pkg != pkg { - check.errorf(ident.Pos(), "cannot define method on non-local type %s", ident.Name) - continue // ignore this method - } - tname = obj - } else { - // identifier not declared/resolved - if ident.Name == "_" { - check.errorf(ident.Pos(), "cannot use _ as value or type") - } else { - check.errorf(ident.Pos(), "undeclared name: %s", ident.Name) - } - continue // ignore this method - } - // declare method in receiver base type scope - scope := check.methods[tname] // lazily allocated - if scope == nil { - scope = new(Scope) - check.methods[tname] = scope - } - fun := NewFunc(m.Name.Pos(), check.pkg, m.Name.Name, nil) - fun.decl = m - check.declare(scope, m.Name, fun) - // HACK(gri) change method parent scope to file scope containing the declaration - fun.parent = meth.file // remember the file scope - } - - // Phase 4) Typecheck all objects in objList but not function bodies. + // Phase 3: Typecheck all objects in objList, but not function bodies. check.objMap = objMap // indicate that we are checking global declarations (objects may not have a type yet) for _, obj := range objList { @@ -348,8 +316,31 @@ func (check *checker) resolveFiles(files []*ast.File) { } check.objMap = nil // done with global declarations - // Phase 5) Typecheck all functions. - // - done by the caller for now + // At this point we may have a non-empty check.methods map; this means that not all + // entries were deleted at the end of declareType, because the respective receiver + // base types were not declared. In that case, an error was reported when declaring + // those methods. We can now safely discard this map. + check.methods = nil + + // Phase 4: Typecheck all functions bodies. + + // (funclist may grow when checking statements - cannot use range clause) + for i := 0; i < len(check.funclist); i++ { + f := check.funclist[i] + if trace { + s := "" + if f.obj != nil { + s = f.obj.name + } + fmt.Println("---", s) + } + check.topScope = f.sig.scope // open the function scope + check.funcsig = f.sig + check.stmtList(f.body.List) + if f.sig.results.Len() > 0 && f.body != nil && !check.isTerminating(f.body, "") { + check.errorf(f.body.Rbrace, "missing return") + } + } } // declareObject completes the declaration of obj in its respective file scope. @@ -373,7 +364,7 @@ func (check *checker) declareObject(obj Object, def *Named, cycleOk bool) { case *TypeName: check.declareType(obj, d.typ, def, cycleOk) case *Func: - check.declareFunc(obj) + check.declareFunc(obj, d.fdecl) default: unreachable() } @@ -488,73 +479,77 @@ func (check *checker) declareType(obj *TypeName, typ ast.Expr, def *Named, cycle // the underlying type has been determined named.complete = true - // typecheck associated method signatures - if scope := check.methods[obj]; scope != nil { - switch t := named.underlying.(type) { - case *Struct: - // struct fields must not conflict with methods - if t.fields == nil { - break - } - for _, f := range t.fields { - if m := scope.Lookup(nil, f.name); m != nil { - check.errorf(m.Pos(), "type %s has both field and method named %s", obj.name, f.name) - // ok to continue - } - } - case *Interface: - // methods cannot be associated with an interface type - for _, m := range scope.entries { - recv := m.(*Func).decl.Recv.List[0].Type - check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.name, obj.name) - // ok to continue - } - } - // typecheck method signatures - var methods []*Func - if scope.NumEntries() > 0 { - for _, obj := range scope.entries { - m := obj.(*Func) - - // set the correct file scope for checking this method type - fileScope := m.parent - assert(fileScope != nil) - oldScope := check.topScope - check.topScope = fileScope - - sig := check.typ(m.decl.Type, nil, cycleOk).(*Signature) - params, _ := check.collectParams(sig.scope, m.decl.Recv, false) - - check.topScope = oldScope // reset topScope - - sig.recv = params[0] // the parser/assocMethod ensure there is exactly one parameter - m.typ = sig - methods = append(methods, m) - check.later(m, sig, m.decl.Body) - } - } - named.methods = methods - delete(check.methods, obj) // we don't need this scope anymore + // type-check signatures of associated methods + methods := check.methods[obj.name] + if len(methods) == 0 { + return // no methods } + + // spec: "For a base type, the non-blank names of methods bound + // to it must be unique." + // => use a scope to determine redeclarations + scope := NewScope(nil) + + // spec: "If the base type is a struct type, the non-blank method + // and field names must be distinct." + // => pre-populate the scope to find conflicts + if t, _ := named.underlying.(*Struct); t != nil { + for _, fld := range t.fields { + if fld.name != "_" { + scope.Insert(fld) + } + } + } + + // check each method + for _, m := range methods { + assert(m.name != "_") // _ methods were excluded before + mdecl := check.objMap[m] + alt := scope.Insert(m) + m.parent = mdecl.file // correct parent scope (scope.Insert used scope) + + if alt != nil { + switch alt := alt.(type) { + case *Var: + check.errorf(m.pos, "field and method with the same name %s", m.name) + if pos := alt.pos; pos.IsValid() { + check.errorf(pos, "previous declaration of %s", m.name) + } + m = nil + case *Func: + check.errorf(m.pos, "method %s already declared for %s", m.name, named) + if pos := alt.pos; pos.IsValid() { + check.errorf(pos, "previous declaration of %s", m.name) + } + m = nil + } + } + + check.recordObject(mdecl.fdecl.Name, m) + + // If the method is valid, type-check its signature, + // and collect it with the named base type. + if m != nil { + check.declareObject(m, nil, true) + named.methods = append(named.methods, m) + } + } + + delete(check.methods, obj.name) // we don't need them anymore } -func (check *checker) declareFunc(obj *Func) { +func (check *checker) declareFunc(obj *Func, fdecl *ast.FuncDecl) { // func declarations cannot use iota assert(check.iota == nil) - fdecl := obj.decl - // methods are typechecked when their receivers are typechecked - // TODO(gri) there is no reason to make this a special case: receivers are simply parameters - if fdecl.Recv == nil { - obj.typ = Typ[Invalid] // guard against cycles - sig := check.typ(fdecl.Type, nil, false).(*Signature) - if obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) { - check.errorf(fdecl.Pos(), "func init must have no arguments and no return values") - // ok to continue - } - obj.typ = sig - check.later(obj, sig, fdecl.Body) + obj.typ = Typ[Invalid] // guard against cycles + sig := check.funcType(fdecl.Recv, fdecl.Type, nil) + if sig.recv == nil && obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) { + check.errorf(fdecl.Pos(), "func init must have no arguments and no return values") + // ok to continue } + obj.typ = sig + check.later(obj, sig, fdecl.Body) } func (check *checker) declStmt(decl ast.Decl) { diff --git a/go/types/testdata/decls2a.src b/go/types/testdata/decls2a.src index a46af773..962b5c43 100644 --- a/go/types/testdata/decls2a.src +++ b/go/types/testdata/decls2a.src @@ -15,7 +15,7 @@ type T1 struct{ } func (T1) m() {} -func (T1) m /* ERROR "redeclared" */ () {} +func (T1) m /* ERROR "already declared" */ () {} func (x *T1) f /* ERROR "field and method" */ () {} // Conflict between embedded field and method name, @@ -77,11 +77,11 @@ type T5 interface { m() int } -func (T5 /* ERROR "invalid receiver" */) m1() {} -func (T5 /* ERROR "invalid receiver" */) m2() {} +func (T5 /* ERROR "invalid receiver" */ ) m1() {} +func (T5 /* ERROR "invalid receiver" */ ) m2() {} // Methods associated with non-local or unnamed types. -func (int /* ERROR "non-local type" */ ) m() {} +func (int /* ERROR "invalid receiver" */ ) m() {} func ([ /* ERROR "identifier" */ ]int) m() {} func (time /* ERROR "identifier" */ .Time) m() {} func (x interface /* ERROR "identifier" */ {}) m() {} diff --git a/go/types/testdata/decls2b.src b/go/types/testdata/decls2b.src index 170eecf6..f0e0351e 100644 --- a/go/types/testdata/decls2b.src +++ b/go/types/testdata/decls2b.src @@ -10,7 +10,7 @@ import "io" const pi = 3.1415 -func (T1) m /* ERROR "redeclared" */ () {} +func (T1) m /* ERROR "already declared" */ () {} func (T2) m(io.Writer) {} type T3 struct { @@ -35,3 +35,9 @@ const c_double /* ERROR "redeclared" */ = 0 type t_double /* ERROR "redeclared" */ int var v_double /* ERROR "redeclared" */ int func f_double /* ERROR "redeclared" */ () {} + +// Blank methods need to be type-checked. +// Verify by checking that errors are reported. +func (T /* ERROR "undeclared" */ ) _() {} +func (T1) _(undeclared /* ERROR "undeclared" */ ) {} +func (T1) _() int { return "foo" /* ERROR "cannot convert" */ } diff --git a/go/types/typexpr.go b/go/types/typexpr.go index b831727b..dbb56ba5 100644 --- a/go/types/typexpr.go +++ b/go/types/typexpr.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file implements typechecking of identifiers and type expressions. +// This file implements type-checking of identifiers and type expressions. package types @@ -14,7 +14,7 @@ import ( "code.google.com/p/go.tools/go/exact" ) -// ident typechecks identifier e and initializes x with the value or type of e. +// ident type-checks identifier e and initializes x with the value or type of e. // If an error occurred, x.mode is set to invalid. // For the meaning of def and cycleOk, see check.typ, below. // @@ -90,11 +90,10 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool) x.typ = typ } -// typ typechecks the type expression e and initializes x with the type of e. -// If an error occurred, x.mode is set to invalid. +// typ type-checks the type expression e and returns its type, or Typ[Invalid]. // If def != nil, e is the type specification for the named type def, declared // in a type declaration, and def.underlying will be set to the type of e before -// any components of e are typechecked. +// any components of e are type-checked. // If cycleOk is set, e (or elements of e) may refer to a named type that is not // yet completely set up. // @@ -117,11 +116,71 @@ func (check *checker) typ(e ast.Expr, def *Named, cycleOk bool) Type { return t } +// funcType type-checks a function or method type and returns its signature. +func (check *checker) funcType(recv *ast.FieldList, ftyp *ast.FuncType, def *Named) *Signature { + sig := new(Signature) + if def != nil { + def.underlying = sig + } + + scope := NewScope(check.topScope) + if retainASTLinks { + scope.node = ftyp + } + recv_, _ := check.collectParams(scope, recv, false) + params, isVariadic := check.collectParams(scope, ftyp.Params, true) + results, _ := check.collectParams(scope, ftyp.Results, false) + + if len(recv_) > 0 { + // There must be exactly one receiver. + if len(recv_) > 1 { + check.invalidAST(recv_[1].Pos(), "method must have exactly one receiver") + // ok to continue + } + recv := recv_[0] + // spec: "The receiver type must be of the form T or *T where T is a type name." + // (ignore invalid types - error was reported before) + if t, _ := deref(recv.typ); t != Typ[Invalid] { + ok := true + if T, _ := t.(*Named); T != nil { + // spec: "The type denoted by T is called the receiver base type; it must not + // be a pointer or interface type and it must be declared in the same package + // as the method." + switch T.underlying.(type) { + case *Pointer, *Interface: + ok = false + } + if T.obj.pkg != check.pkg { + ok = false + } + } else { + ok = false + } + if !ok { + // TODO(gri) provide better error message depending on error + check.errorf(recv.pos, "invalid receiver %s", recv) + // ok to continue + } + } + sig.recv = recv + } + + sig.scope = scope + sig.params = NewTuple(params...) + sig.results = NewTuple(results...) + sig.isVariadic = isVariadic + + return sig +} + // typ0 contains the core of type checking of types. // Must only be called by typ. // func (check *checker) typ0(e ast.Expr, def *Named, cycleOk bool) Type { switch e := e.(type) { + case *ast.BadExpr: + // ignore - error reported before + case *ast.Ident: var x operand check.ident(&x, e, def, cycleOk) @@ -213,22 +272,7 @@ func (check *checker) typ0(e ast.Expr, def *Named, cycleOk bool) Type { return typ case *ast.FuncType: - typ := new(Signature) - if def != nil { - def.underlying = typ - } - - scope := NewScope(check.topScope) - if retainASTLinks { - scope.node = e - } - typ.scope = scope - params, isVariadic := check.collectParams(scope, e.Params, true) - results, _ := check.collectParams(scope, e.Results, false) - typ.params = NewTuple(params...) - typ.results = NewTuple(results...) - typ.isVariadic = isVariadic - return typ + return check.funcType(nil, e, def) case *ast.InterfaceType: typ := new(Interface) @@ -268,7 +312,7 @@ func (check *checker) typ0(e ast.Expr, def *Named, cycleOk bool) Type { return Typ[Invalid] } -// typeOrNil typechecks the type expression (or nil value) e +// typeOrNil type-checks the type expression (or nil value) e // and returns the typ of e, or nil. // If e is neither a type nor nil, typOrNil returns Typ[Invalid]. // @@ -343,6 +387,7 @@ func (check *checker) collectMethods(recv Type, list *ast.FieldList, cycleOk boo scope := NewScope(nil) for _, f := range list.List { + // TODO(gri) Consider calling funcType here. typ := check.typ(f.Type, nil, cycleOk) // the parser ensures that f.Tag is nil and we don't // care if a constructed AST contains a non-nil tag