diff --git a/go/types/assignments.go b/go/types/assignments.go new file mode 100644 index 00000000..7eacaeb5 --- /dev/null +++ b/go/types/assignments.go @@ -0,0 +1,156 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "go/ast" + + "code.google.com/p/go.tools/go/exact" +) + +// TODO(gri) initialize is very close to the 2nd half of assign1to1. +func (check *checker) assign(obj Object, x *operand) { + // Determine typ of lhs: If the object doesn't have a type + // yet, determine it from the type of x; if x is invalid, + // set the object type to Typ[Invalid]. + var typ Type + switch obj := obj.(type) { + default: + unreachable() + + case *Const: + typ = obj.typ // may already be Typ[Invalid] + if typ == nil { + typ = Typ[Invalid] + if x.mode != invalid { + typ = x.typ + } + obj.typ = typ + } + + case *Var: + typ = obj.typ // may already be Typ[Invalid] + if typ == nil { + typ = Typ[Invalid] + if x.mode != invalid { + typ = x.typ + if isUntyped(typ) { + // convert untyped types to default types + if typ == Typ[UntypedNil] { + check.errorf(x.pos(), "use of untyped nil") + typ = Typ[Invalid] + } else { + typ = defaultType(typ) + } + } + } + obj.typ = typ + } + } + + // nothing else to check if we don't have a valid lhs or rhs + if typ == Typ[Invalid] || x.mode == invalid { + return + } + + if !check.assignment(x, typ) { + if x.mode != invalid { + if x.typ != Typ[Invalid] && typ != Typ[Invalid] { + check.errorf(x.pos(), "cannot initialize %s (type %s) with %s", obj.Name(), typ, x) + } + } + return + } + + // for constants, set their value + if obj, _ := obj.(*Const); obj != nil { + obj.val = exact.MakeUnknown() // failure case: we don't know the constant value + if x.mode == constant { + if isConstType(x.typ) { + obj.val = x.val + } else if x.typ != Typ[Invalid] { + check.errorf(x.pos(), "%s has invalid constant type", x) + } + } else if x.mode != invalid { + check.errorf(x.pos(), "%s is not constant", x) + } + } +} + +func (check *checker) assignMulti(lhs []Object, rhs []ast.Expr) { + assert(len(lhs) > 0) + + const decl = false + + // If the lhs and rhs have corresponding expressions, treat each + // matching pair as an individual pair. + if len(lhs) == len(rhs) { + var x operand + for i, e := range rhs { + check.expr(&x, e, nil, -1) + if x.mode == invalid { + goto Error + } + check.assign(lhs[i], &x) + } + return + } + + // Otherwise, the rhs must be a single expression (possibly + // a function call returning multiple values, or a comma-ok + // expression). + if len(rhs) == 1 { + // len(lhs) > 1 + // Start with rhs so we have expression types + // for declarations with implicit types. + var x operand + check.expr(&x, rhs[0], nil, -1) + if x.mode == invalid { + goto Error + } + + if t, ok := x.typ.(*Tuple); ok && len(lhs) == t.Len() { + // function result + x.mode = value + for i := 0; i < len(lhs); i++ { + obj := t.At(i) + x.expr = nil // TODO(gri) should do better here + x.typ = obj.typ + check.assign(lhs[i], &x) + } + return + } + + if x.mode == valueok && len(lhs) == 2 { + // comma-ok expression + x.mode = value + check.assign(lhs[0], &x) + + x.typ = Typ[UntypedBool] + check.assign(lhs[1], &x) + return + } + } + + check.errorf(lhs[0].Pos(), "assignment count mismatch: %d = %d", len(lhs), len(rhs)) + +Error: + // In case of a declaration, set all lhs types to Typ[Invalid]. + for _, obj := range lhs { + switch obj := obj.(type) { + case *Const: + if obj.typ == nil { + obj.typ = Typ[Invalid] + } + obj.val = exact.MakeUnknown() + case *Var: + if obj.typ == nil { + obj.typ = Typ[Invalid] + } + default: + unreachable() + } + } +} diff --git a/go/types/check.go b/go/types/check.go index 4b411954..50a9cf0b 100644 --- a/go/types/check.go +++ b/go/types/check.go @@ -14,10 +14,12 @@ import ( "code.google.com/p/go.tools/go/exact" ) -// debugging support +// debugging/development support const ( debug = true // leave on during development trace = false // turn on for detailed type resolution traces + // TODO(gri) remove this flag and clean up code under the assumption that resolve == true. + resolve = false // if set, resolve all identifiers in the type checker (don't use ast.Objects anymore) ) // exprInfo stores type and constant value for an untyped expression. @@ -29,9 +31,8 @@ type exprInfo struct { // A checker is an instance of the type checker. type checker struct { - ctxt *Context - fset *token.FileSet - files []*ast.File + ctxt *Context + fset *token.FileSet // lazily initialized pkg *Package // current package @@ -47,9 +48,29 @@ type checker struct { funclist []function // list of functions/methods with correct signatures and non-empty bodies funcsig *Signature // signature of currently typechecked function pos []token.Pos // stack of expr positions; debugging support, used if trace is set + + // these are only valid if resolve is set + 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 +} + +func (check *checker) openScope() { + check.topScope = &Scope{Outer: check.topScope} +} + +func (check *checker) closeScope() { + check.topScope = check.topScope.Outer } func (check *checker) register(id *ast.Ident, obj Object) { + if resolve { + // TODO(gri) Document how if an identifier can be registered more than once. + if f := check.ctxt.Ident; f != nil { + f(id, obj) + } + return + } + // When an expression is evaluated more than once (happens // in rare cases, e.g. for statement expressions, see // comment in stmt.go), the object has been registered @@ -70,9 +91,21 @@ func (check *checker) register(id *ast.Ident, obj Object) { // uses the checker.objects map. // // TODO(gri) Once identifier resolution is done entirely by -// the typechecker, only the idents map is needed. +// the typechecker, only scopes are needed. Need +// to update the comment above. // func (check *checker) lookup(ident *ast.Ident) Object { + if resolve { + for scope := check.topScope; scope != nil; scope = scope.Outer { + if obj := scope.Lookup(ident.Name); obj != nil { + check.register(ident, obj) + return obj + } + } + return nil + } + + // old code obj := check.idents[ident] astObj := ident.Obj @@ -95,7 +128,8 @@ func (check *checker) lookup(ident *ast.Ident) Object { } type function struct { - obj *Func // for debugging/tracing only + file *Scope // only valid if resolve is set + obj *Func // for debugging/tracing only sig *Signature body *ast.BlockStmt } @@ -107,11 +141,14 @@ type function struct { func (check *checker) later(f *Func, sig *Signature, body *ast.BlockStmt) { // functions implemented elsewhere (say in assembly) have no body if body != nil { - check.funclist = append(check.funclist, function{f, sig, body}) + check.funclist = append(check.funclist, function{check.topScope, f, sig, body}) } } func (check *checker) declareIdent(scope *Scope, ident *ast.Ident, obj Object) { + if resolve { + unreachable() + } assert(check.lookup(ident) == nil) // identifier already declared or resolved check.register(ident, obj) if ident.Name != "_" { @@ -126,6 +163,10 @@ func (check *checker) declareIdent(scope *Scope, ident *ast.Ident, obj Object) { } func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spec *ast.ValueSpec, iota int) { + if resolve { + unreachable() + } + if len(lhs) == 0 { check.invalidAST(pos, "missing lhs in declaration") return @@ -150,16 +191,17 @@ func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spe break } } - assert(l != nil) + isConst := false switch obj := obj.(type) { case *Const: obj.typ = typ + isConst = true case *Var: obj.typ = typ default: unreachable() } - check.assign1to1(l, r, nil, true, iota) + check.assign1to1(l, r, nil, true, iota, isConst) return } @@ -190,21 +232,30 @@ func (check *checker) valueSpec(pos token.Pos, obj Object, lhs []*ast.Ident, spe for i, e := range lhs { lhx[i] = e } - check.assignNtoM(lhx, rhs, true, iota) + _, isConst := obj.(*Const) + check.assignNtoM(lhx, rhs, true, iota, isConst) } } // object typechecks an object by assigning it a type. // func (check *checker) object(obj Object, cycleOk bool) { + if resolve { + unreachable() + } + + if obj.Type() != nil { + return // already checked + } + switch obj := obj.(type) { case *Package: // nothing to do + if resolve { + unreachable() + } case *Const: - if obj.typ != nil { - return // already checked - } // The obj.Val field for constants is initialized to its respective // iota value (type int) by the parser. // If the object's type is Typ[Invalid], the object value is ignored. @@ -214,7 +265,7 @@ func (check *checker) object(obj Object, cycleOk bool) { // know that x is a constant and has type float32, but we don't // have a value due to the error in the conversion). if obj.visited { - check.errorf(obj.Pos(), "illegal cycle in initialization of constant %s", obj.Name) + check.errorf(obj.Pos(), "illegal cycle in initialization of constant %s", obj.name) obj.typ = Typ[Invalid] return } @@ -231,11 +282,8 @@ func (check *checker) object(obj Object, cycleOk bool) { check.valueSpec(spec.Pos(), obj, spec.Names, init, int(iota)) case *Var: - if obj.typ != nil { - return // already checked - } if obj.visited { - check.errorf(obj.Pos(), "illegal cycle in initialization of variable %s", obj.Name) + check.errorf(obj.Pos(), "illegal cycle in initialization of variable %s", obj.name) obj.typ = Typ[Invalid] return } @@ -252,9 +300,6 @@ func (check *checker) object(obj Object, cycleOk bool) { } case *TypeName: - if obj.typ != nil { - return // already checked - } typ := &Named{obj: obj} obj.typ = typ // "mark" object so recursion terminates typ.underlying = check.typ(obj.spec.Type, cycleOk).Underlying() @@ -265,7 +310,7 @@ func (check *checker) object(obj Object, cycleOk bool) { // struct fields must not conflict with methods for _, f := range t.fields { if m := scope.Lookup(f.Name); m != nil { - check.errorf(m.Pos(), "type %s has both field and method named %s", obj.Name, f.Name) + check.errorf(m.Pos(), "type %s has both field and method named %s", obj.name, f.Name) // ok to continue } } @@ -273,7 +318,7 @@ func (check *checker) object(obj Object, cycleOk bool) { // 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) + check.errorf(recv.Pos(), "invalid receiver type %s (%s is an interface type)", obj.name, obj.name) // ok to continue } } @@ -282,7 +327,7 @@ func (check *checker) object(obj Object, cycleOk bool) { for _, obj := range scope.Entries { m := obj.(*Func) sig := check.typ(m.decl.Type, cycleOk).(*Signature) - params, _ := check.collectParams(m.decl.Recv, false) + params, _ := check.collectParams(sig.scope, m.decl.Recv, false) sig.recv = params[0] // the parser/assocMethod ensure there is exactly one parameter m.typ = sig assert(methods.Insert(obj) == nil) @@ -293,9 +338,6 @@ func (check *checker) object(obj Object, cycleOk bool) { } case *Func: - if obj.typ != nil { - return // already checked - } fdecl := obj.decl // methods are typechecked when their receivers are typechecked if fdecl.Recv == nil { @@ -318,6 +360,10 @@ func (check *checker) object(obj Object, cycleOk bool) { // for constant declarations without explicit initialization expressions. // func (check *checker) assocInitvals(decl *ast.GenDecl) { + if resolve { + unreachable() + } + var last *ast.ValueSpec for _, s := range decl.Specs { if s, ok := s.(*ast.ValueSpec); ok { @@ -337,6 +383,10 @@ func (check *checker) assocInitvals(decl *ast.GenDecl) { // receiver base type. meth.Recv must exist. // func (check *checker) assocMethod(meth *ast.FuncDecl) { + if resolve { + unreachable() + } + // The receiver type is one of the following (enforced by parser): // - *ast.Ident // - *ast.StarExpr{*ast.Ident} @@ -378,6 +428,10 @@ func (check *checker) assocMethod(meth *ast.FuncDecl) { } func (check *checker) decl(decl ast.Decl) { + if resolve { + unreachable() // during top-level type-checking + } + switch d := decl.(type) { case *ast.BadDecl: // ignore @@ -419,12 +473,17 @@ func (check *checker) decl(decl ast.Decl) { type bailout struct{} func check(ctxt *Context, path string, fset *token.FileSet, files ...*ast.File) (pkg *Package, err error) { + pkg = &Package{ + path: path, + scope: &Scope{Outer: Universe}, + imports: make(map[string]*Package), + } + // initialize checker check := checker{ ctxt: ctxt, fset: fset, - files: files, - pkg: &Package{path: path, scope: &Scope{Outer: Universe}, imports: make(map[string]*Package)}, + pkg: pkg, idents: make(map[*ast.Ident]Object), objects: make(map[*ast.Object]Object), initspecs: make(map[*ast.ValueSpec]*ast.ValueSpec), @@ -433,9 +492,8 @@ func check(ctxt *Context, path string, fset *token.FileSet, files ...*ast.File) untyped: make(map[ast.Expr]exprInfo), } - // set results and handle panics + // handle panics defer func() { - pkg = check.pkg switch p := recover().(type) { case nil, bailout: // normal return or early exit @@ -451,22 +509,45 @@ func check(ctxt *Context, path string, fset *token.FileSet, files ...*ast.File) } }() + // determine package name and files + i := 0 + for _, file := range files { + switch name := file.Name.Name; pkg.name { + case "": + pkg.name = name + fallthrough + case name: + files[i] = file + i++ + default: + check.errorf(file.Package, "package %s; expected %s", name, pkg.name) + // ignore this file + } + } + files = files[:i] + // resolve identifiers imp := ctxt.Import if imp == nil { imp = GcImport } - methods := check.resolve(imp) - // associate methods with types - for _, m := range methods { - check.assocMethod(m) - } + if resolve { + check.resolveFiles(files, imp) - // typecheck all declarations - for _, f := range check.files { - for _, d := range f.Decls { - check.decl(d) + } else { + methods := check.resolve(imp, files) + + // associate methods with types + for _, m := range methods { + check.assocMethod(m) + } + + // typecheck all declarations + for _, f := range files { + for _, d := range f.Decls { + check.decl(d) + } } } @@ -481,6 +562,7 @@ func check(ctxt *Context, path string, fset *token.FileSet, files ...*ast.File) } 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, "") { diff --git a/go/types/check_test.go b/go/types/check_test.go index 28d98042..fcab8d8b 100644 --- a/go/types/check_test.go +++ b/go/types/check_test.go @@ -83,7 +83,11 @@ func parseFiles(t *testing.T, testname string, filenames []string) ([]*ast.File, var files []*ast.File var errlist []error for _, filename := range filenames { - file, err := parser.ParseFile(fset, filename, nil, parser.DeclarationErrors|parser.AllErrors) + mode := parser.AllErrors + if !resolve { + mode |= parser.DeclarationErrors + } + file, err := parser.ParseFile(fset, filename, nil, mode) if file == nil { t.Fatalf("%s: could not parse file %s", testname, filename) } @@ -237,6 +241,10 @@ func TestCheck(t *testing.T) { return } + if resolve { + fmt.Println("*** Running new code: Identifiers are resolved by type checker. ***") + } + // Otherwise, run all the tests. for _, test := range tests { checkFiles(t, test.name, test.files) diff --git a/go/types/errors.go b/go/types/errors.go index 88d09910..c95e8936 100644 --- a/go/types/errors.go +++ b/go/types/errors.go @@ -53,6 +53,8 @@ func (check *checker) untrace(format string, args ...interface{}) { func (check *checker) formatMsg(format string, args []interface{}) string { for i, arg := range args { switch a := arg.(type) { + case nil: + args[i] = "" case operand: panic("internal error: should always pass *operand") case *operand: @@ -63,6 +65,8 @@ func (check *checker) formatMsg(format string, args []interface{}) string { args[i] = exprString(a) case Type: args[i] = typeString(a) + case Object: + args[i] = fmt.Sprintf("%s (%T)", a.Name(), a) } } return fmt.Sprintf(format, args...) diff --git a/go/types/expr.go b/go/types/expr.go index b7595991..d5cff4a4 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -7,6 +7,7 @@ package types import ( + "fmt" "go/ast" "go/token" "strconv" @@ -70,7 +71,7 @@ on the way down in updateExprType, or at the end of the type checker run, if present the Context.Expr method is invoked to notify a go/types client. */ -func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) { +func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) { if list == nil { return } @@ -92,15 +93,22 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param if len(field.Names) > 0 { // named parameter for _, name := range field.Names { - par := check.lookup(name).(*Var) - par.typ = typ + var par *Var + if resolve { + par = &Var{pos: name.Pos(), pkg: check.pkg, name: name.Name, typ: typ, decl: field} + check.declare(scope, par) + check.register(name, par) + } else { + par = check.lookup(name).(*Var) + par.typ = typ + } last = par copy := *par params = append(params, ©) } } else { // anonymous parameter - par := &Var{typ: typ} + par := &Var{pkg: check.pkg, typ: typ, decl: field} last = nil // not accessible inside function params = append(params, par) } @@ -114,7 +122,7 @@ func (check *checker) collectParams(list *ast.FieldList, variadicOk bool) (param return } -func (check *checker) collectMethods(list *ast.FieldList) (methods ObjSet) { +func (check *checker) collectMethods(scope *Scope, list *ast.FieldList) (methods ObjSet) { if list == nil { return } @@ -131,10 +139,20 @@ func (check *checker) collectMethods(list *ast.FieldList) (methods ObjSet) { continue } for _, name := range f.Names { - // TODO(gri) provide correct declaration info - obj := &Func{check.pkg, name.Name, sig, nil} - if alt := methods.Insert(obj); alt != nil { - check.errorf(list.Pos(), "multiple methods named %s", name.Name) + // TODO(gri) provide correct declaration info and scope + // TODO(gri) with unified scopes (Scope, ObjSet) this can become + // just a normal declaration + obj := &Func{name.Pos(), check.pkg, nil, name.Name, sig, nil} + if alt := methods.Insert(obj); alt != nil && resolve { + // if !resolve, the parser complains + prevDecl := "" + if pos := alt.Pos(); pos.IsValid() { + prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos)) + } + check.errorf(obj.Pos(), "%s redeclared in this block%s", obj.Name(), prevDecl) + } + if resolve { + check.register(name, obj) } } } else { @@ -167,14 +185,14 @@ func (check *checker) tag(t *ast.BasicLit) string { return "" } -func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field, tags []string) { +func (check *checker) collectFields(scope *Scope, list *ast.FieldList, cycleOk bool) (fields []*Field, tags []string) { if list == nil { return } var typ Type // current field typ var tag string // current field tag - add := func(name string, isAnonymous bool) { + add := func(field *ast.Field, ident *ast.Ident, name string, isAnonymous bool, pos token.Pos) { // TODO(gri): rethink this - at the moment we allocate only a prefix if tag != "" && tags == nil { tags = make([]string, len(fields)) @@ -182,6 +200,13 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [ if tags != nil { tags = append(tags, tag) } + if resolve { + fld := &Var{pos: pos, pkg: check.pkg, name: name, typ: typ, decl: field} + check.declare(scope, fld) + if resolve && ident != nil { + check.register(ident, fld) + } + } fields = append(fields, &Field{check.pkg, name, typ, isAnonymous}) } @@ -191,18 +216,19 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [ if len(f.Names) > 0 { // named fields for _, name := range f.Names { - add(name.Name, false) + add(f, name, name.Name, false, name.Pos()) } } else { // anonymous field + pos := f.Type.Pos() switch t := typ.Deref().(type) { case *Basic: - add(t.name, true) + add(f, nil, t.name, true, pos) case *Named: - add(t.obj.name, true) + add(f, nil, t.obj.name, true, pos) default: if typ != Typ[Invalid] { - check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ) + check.invalidAST(pos, "anonymous field type %s must be named", typ) } } } @@ -870,6 +896,9 @@ func (check *checker) index(arg ast.Expr, length int64, iota int) (i int64, ok b // compositeLitKey resolves unresolved composite literal keys. // For details, see comment in go/parser/parser.go, method parseElement. func (check *checker) compositeLitKey(key ast.Expr) { + if resolve { + return + } if ident, ok := key.(*ast.Ident); ok && ident.Obj == nil { if obj := check.pkg.scope.Lookup(ident.Name); obj != nil { check.register(ident, obj) @@ -1043,15 +1072,37 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle goto Error // error was reported before case *ast.Ident: - if e.Name == "_" { + if !resolve && e.Name == "_" { check.invalidOp(e.Pos(), "cannot use _ as value or type") goto Error } obj := check.lookup(e) if obj == nil { + if resolve { + if e.Name == "_" { + check.invalidOp(e.Pos(), "cannot use _ as value or type") + } else { + // TODO(gri) anonymous function result parameters are + // not declared - this causes trouble when + // type-checking return statements + check.errorf(e.Pos(), "undeclared name: %s", e.Name) + } + } goto Error // error was reported before } - check.object(obj, cycleOk) + if resolve { + typ := obj.Type() + if check.objMap == nil { + if typ == nil { + check.dump("%s: %s not declared?", e.Pos(), e) + } + assert(typ != nil) + } else if typ == nil { + check.declareObject(obj, cycleOk) + } + } else { + check.object(obj, cycleOk) + } switch obj := obj.(type) { case *Package: check.errorf(e.Pos(), "use of package %s not in selector", obj.Name) @@ -1073,7 +1124,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle case *TypeName: x.mode = typexpr if !cycleOk && obj.typ.Underlying() == nil { - check.errorf(obj.spec.Pos(), "illegal cycle in declaration of %s", obj.Name) + check.errorf(obj.spec.Pos(), "illegal cycle in declaration of %s", obj.name) x.expr = e x.typ = Typ[Invalid] return // don't goto Error - need x.mode == typexpr @@ -1675,19 +1726,22 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle x.mode = typexpr case *ast.StructType: + scope := &Scope{Outer: check.topScope} + fields, tags := check.collectFields(scope, e.Fields, cycleOk) x.mode = typexpr - fields, tags := check.collectFields(e.Fields, cycleOk) - x.typ = &Struct{fields: fields, tags: tags} + x.typ = &Struct{scope: scope, fields: fields, tags: tags} case *ast.FuncType: - params, isVariadic := check.collectParams(e.Params, true) - results, _ := check.collectParams(e.Results, false) + scope := &Scope{Outer: check.topScope} + params, isVariadic := check.collectParams(scope, e.Params, true) + results, _ := check.collectParams(scope, e.Results, false) x.mode = typexpr - x.typ = &Signature{recv: nil, params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic} + x.typ = &Signature{scope: scope, recv: nil, params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic} case *ast.InterfaceType: + scope := &Scope{Outer: check.topScope} x.mode = typexpr - x.typ = &Interface{methods: check.collectMethods(e.Methods)} + x.typ = &Interface{methods: check.collectMethods(scope, e.Methods)} case *ast.MapType: x.mode = typexpr diff --git a/go/types/gcimporter.go b/go/types/gcimporter.go index d4172630..d40f9268 100644 --- a/go/types/gcimporter.go +++ b/go/types/gcimporter.go @@ -590,7 +590,7 @@ func (p *gcParser) parseInterfaceType() Type { } pkg, name := p.parseName(true) sig := p.parseSignature() - if alt := methods.Insert(&Func{pkg, name, sig, nil}); alt != nil { + if alt := methods.Insert(&Func{token.NoPos, pkg, nil, name, sig, nil}); alt != nil { p.errorf("multiple methods named %s.%s", alt.Pkg().name, alt.Name()) } } @@ -879,7 +879,7 @@ func (p *gcParser) parseMethodDecl() { // add method to type unless type was imported before // and method exists already - base.methods.Insert(&Func{pkg, name, sig, nil}) + base.methods.Insert(&Func{token.NoPos, pkg, nil, name, sig, nil}) } // FuncDecl = "func" ExportedName Func . diff --git a/go/types/objects.go b/go/types/objects.go index 96cb8c1d..02af57ea 100644 --- a/go/types/objects.go +++ b/go/types/objects.go @@ -11,23 +11,29 @@ import ( "code.google.com/p/go.tools/go/exact" ) +// TODO(gri) provide a complete set of factory functions! + // An Object describes a named language entity such as a package, // constant, type, variable, function (incl. methods), or label. // All objects implement the Object interface. // type Object interface { Pkg() *Package // nil for objects in the Universe scope - Scope() *Scope + Outer() *Scope // the scope in which this object is declared Name() string Type() Type - Pos() token.Pos + Pos() token.Pos // position of object identifier in declaration // TODO(gri) provide String method! + + setOuter(*Scope) } // A Package represents the contents (objects) of a Go package. type Package struct { + pos token.Pos // position of package import path or local package identifier, if present name string - path string // import path, "" for current (non-imported) package + path string // import path, "" for current (non-imported) package + outer *Scope scope *Scope // package-level scope imports map[string]*Package // map of import paths to imported packages complete bool // if set, this package was imported completely @@ -40,35 +46,46 @@ func NewPackage(path, name string) *Package { } func (obj *Package) Pkg() *Package { return obj } +func (obj *Package) Outer() *Scope { return obj.outer } func (obj *Package) Scope() *Scope { return obj.scope } func (obj *Package) Name() string { return obj.name } func (obj *Package) Type() Type { return Typ[Invalid] } func (obj *Package) Pos() token.Pos { - if obj.spec == nil { - return token.NoPos + if obj.pos.IsValid() { + return obj.pos } - return obj.spec.Pos() + if obj.spec != nil { + return obj.spec.Pos() + } + return token.NoPos } func (obj *Package) Path() string { return obj.path } func (obj *Package) Imports() map[string]*Package { return obj.imports } func (obj *Package) Complete() bool { return obj.complete } +func (obj *Package) setOuter(*Scope) { /* don't do anything - this is the package's scope */ +} // A Const represents a declared constant. type Const struct { - pkg *Package - name string - typ Type - val exact.Value + pos token.Pos // position of identifier in constant declaration + pkg *Package + outer *Scope + name string + typ Type + val exact.Value visited bool // for initialization cycle detection spec *ast.ValueSpec } func (obj *Const) Pkg() *Package { return obj.pkg } -func (obj *Const) Scope() *Scope { panic("unimplemented") } +func (obj *Const) Outer() *Scope { return obj.outer } func (obj *Const) Name() string { return obj.name } func (obj *Const) Type() Type { return obj.typ } func (obj *Const) Pos() token.Pos { + if obj.pos.IsValid() { + return obj.pos + } if obj.spec == nil { return token.NoPos } @@ -79,51 +96,63 @@ func (obj *Const) Pos() token.Pos { } return token.NoPos } -func (obj *Const) Val() exact.Value { return obj.val } +func (obj *Const) Val() exact.Value { return obj.val } +func (obj *Const) setOuter(s *Scope) { obj.outer = s } // A TypeName represents a declared type. type TypeName struct { - pkg *Package - name string - typ Type // *Named or *Basic + pos token.Pos // position of identifier in type declaration + pkg *Package + outer *Scope + name string + typ Type // *Named or *Basic spec *ast.TypeSpec } func NewTypeName(pkg *Package, name string, typ Type) *TypeName { - return &TypeName{pkg, name, typ, nil} + return &TypeName{token.NoPos, pkg, nil, name, typ, nil} } func (obj *TypeName) Pkg() *Package { return obj.pkg } -func (obj *TypeName) Scope() *Scope { panic("unimplemented") } +func (obj *TypeName) Outer() *Scope { return obj.outer } func (obj *TypeName) Name() string { return obj.name } func (obj *TypeName) Type() Type { return obj.typ } func (obj *TypeName) Pos() token.Pos { + if obj.pos.IsValid() { + return obj.pos + } if obj.spec == nil { return token.NoPos } return obj.spec.Pos() } +func (obj *TypeName) setOuter(s *Scope) { obj.outer = s } // A Variable represents a declared variable (including function parameters and results). type Var struct { - pkg *Package // nil for parameters - name string - typ Type + pos token.Pos // position of identifier in variable declaration + pkg *Package // nil for parameters + outer *Scope + name string + typ Type visited bool // for initialization cycle detection decl interface{} } func NewVar(pkg *Package, name string, typ Type) *Var { - return &Var{pkg, name, typ, false, nil} + return &Var{token.NoPos, pkg, nil, name, typ, false, nil} } func (obj *Var) Pkg() *Package { return obj.pkg } -func (obj *Var) Scope() *Scope { panic("unimplemented") } +func (obj *Var) Outer() *Scope { return obj.outer } func (obj *Var) Name() string { return obj.name } func (obj *Var) Type() Type { return obj.typ } func (obj *Var) Pos() token.Pos { + if obj.pos.IsValid() { + return obj.pos + } switch d := obj.decl.(type) { case *ast.Field: for _, n := range d.Names { @@ -146,26 +175,33 @@ func (obj *Var) Pos() token.Pos { } return token.NoPos } +func (obj *Var) setOuter(s *Scope) { obj.outer = s } // A Func represents a declared function. type Func struct { - pkg *Package - name string - typ Type // *Signature or *Builtin + pos token.Pos + pkg *Package + outer *Scope + name string + typ Type // *Signature or *Builtin decl *ast.FuncDecl } func (obj *Func) Pkg() *Package { return obj.pkg } -func (obj *Func) Scope() *Scope { panic("unimplemented") } +func (obj *Func) Outer() *Scope { return obj.outer } func (obj *Func) Name() string { return obj.name } func (obj *Func) Type() Type { return obj.typ } func (obj *Func) Pos() token.Pos { + if obj.pos.IsValid() { + return obj.pos + } if obj.decl != nil && obj.decl.Name != nil { return obj.decl.Name.Pos() } return token.NoPos } +func (obj *Func) setOuter(s *Scope) { obj.outer = s } // newObj returns a new Object for a given *ast.Object. // It does not canonicalize them (it always returns a new one). diff --git a/go/types/resolve.go b/go/types/resolve.go index f2f9f632..e152e073 100644 --- a/go/types/resolve.go +++ b/go/types/resolve.go @@ -23,7 +23,7 @@ func (check *checker) declareObj(scope, altScope *Scope, obj Object, dotImport t // for dot-imports, local declarations are declared first - swap messages if dotImport.IsValid() { if pos := alt.Pos(); pos.IsValid() { - check.errorf(pos, fmt.Sprintf("%s redeclared in this block by dot-import at %s", + check.errorf(pos, fmt.Sprintf("%s redeclared in this block by import at %s", obj.Name(), check.fset.Position(dotImport))) return } @@ -50,25 +50,11 @@ func (check *checker) resolveIdent(scope *Scope, ident *ast.Ident) bool { return false } -func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) { +func (check *checker) resolve(importer Importer, files []*ast.File) (methods []*ast.FuncDecl) { pkg := check.pkg // complete package scope - i := 0 - for _, file := range check.files { - // package names must match - switch name := file.Name.Name; { - case pkg.name == "": - pkg.name = name - case name != pkg.name: - check.errorf(file.Package, "package %s; expected %s", name, pkg.name) - continue // ignore this file - } - - // keep this file - check.files[i] = file - i++ - + for _, file := range files { // the package identifier denotes the current package check.register(file.Name, pkg) @@ -118,10 +104,9 @@ func (check *checker) resolve(importer Importer) (methods []*ast.FuncDecl) { } } } - check.files = check.files[0:i] // complete file scopes with imports and resolve identifiers - for _, file := range check.files { + for _, file := range files { // build file scope by processing all imports importErrors := false fileScope := &Scope{Outer: pkg.scope} diff --git a/go/types/resolver.go b/go/types/resolver.go new file mode 100644 index 00000000..4c8d7e07 --- /dev/null +++ b/go/types/resolver.go @@ -0,0 +1,661 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package types + +import ( + "fmt" + "go/ast" + "go/token" + "strconv" + + "code.google.com/p/go.tools/go/exact" +) + +func (check *checker) declare(scope *Scope, obj Object) { + if obj.Name() == "_" { + obj.setOuter(scope) + return // blank identifiers are not visible + } + if alt := scope.Insert(obj); alt != nil { + prevDecl := "" + if pos := alt.Pos(); pos.IsValid() { + prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", check.fset.Position(pos)) + } + check.errorf(obj.Pos(), "%s redeclared in this block%s", obj.Name(), prevDecl) + // TODO(gri) Instead, change this into two separate error messages (easier to handle by tools) + } +} + +func (check *checker) declareShort(scope *Scope, list []Object) { + n := 0 // number of new objects + for _, obj := range list { + if obj.Name() == "_" { + obj.setOuter(scope) + continue // blank identifiers are not visible + } + if scope.Insert(obj) == nil { + n++ // new declaration + } + } + if n == 0 { + check.errorf(list[0].Pos(), "no new variables on left side of :=") + } +} + +// 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 projExpr projects the index'th value of a multi-valued expression. +// projExpr implements ast.Expr. +type projExpr struct { + lhs []*Var // all variables on the lhs + ast.Expr // rhs +} + +func (check *checker) resolveFiles(files []*ast.File, importer Importer) { + pkg := check.pkg + + // Phase 1: Pre-declare all package scope objects so that they can be found + // when type-checking package objects. + + 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 add + + add := func(obj Object, typ, init ast.Expr) { + objList = append(objList, obj) + objMap[obj] = &decl{fileScope, typ, init} + // TODO(gri) move check.declare call here + } + + for _, file := range files { + // the package identifier denotes the current package, but it is in no scope + check.register(file.Name, pkg) + + fileScope = &Scope{Outer: pkg.scope} + scopes = append(scopes, fileScope) + + for _, decl := range file.Decls { + switch d := decl.(type) { + case *ast.BadDecl: + // ignore + + case *ast.GenDecl: + var last *ast.ValueSpec // last list of const initializers seen + for iota, spec := range d.Specs { + switch s := spec.(type) { + case *ast.ImportSpec: + if importer == nil { + //importErrors = true + continue + } + path, _ := strconv.Unquote(s.Path.Value) + imp, err := importer(pkg.imports, path) + if err != nil { + check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err) + //importErrors = true + continue + } + // TODO(gri) If a local package name != "." is provided, + // we could proceed even if the import failed. Consider + // adjusting the logic here a bit. + + // local name overrides imported package name + name := imp.name + if s.Name != nil { + name = s.Name.Name + } + + // add import to file scope + if name == "." { + // merge imported scope with file scope + for _, obj := range imp.scope.Entries { + // gcimported package scopes contain non-exported + // objects such as types used in partially exported + // objects - do not accept them + if ast.IsExported(obj.Name()) { + // Note: This will change each imported object's scope! + // May be an issue for types aliases. + check.declare(fileScope, obj) + } + } + // TODO(gri) consider registering the "." identifier + // if we have Context.Ident callbacks for say blank + // (_) identifiers + // check.register(spec.Name, pkg) + } else if name != "_" { + // declare imported package object in file scope + // (do not re-use imp in the file scope but create + // a new object instead; the Decl field is different + // for different files) + obj := &Package{name: name, scope: imp.scope, spec: s} + check.declare(fileScope, obj) + } + + case *ast.ValueSpec: + switch d.Tok { + case token.CONST: + // determine which initialization expressions to use + if len(s.Values) > 0 { + last = s + } + + // declare all constants + for i, name := range s.Names { + obj := &Const{pos: name.Pos(), pkg: pkg, name: name.Name, val: exact.MakeInt64(int64(iota)), spec: s} + check.declare(pkg.scope, obj) + check.register(name, obj) + + var init ast.Expr + if i < len(last.Values) { + init = last.Values[i] + } + add(obj, last.Type, init) + } + + // arity of lhs and rhs must match + if lhs, rhs := len(s.Names), len(s.Values); rhs > 0 { + switch { + case lhs < rhs: + // TODO(gri) once resolve is the default, use first message + // x := s.Values[lhs] + // check.errorf(x.Pos(), "too many initialization expressions") + check.errorf(s.Names[0].Pos(), "assignment count mismatch") + case lhs > rhs && rhs != 1: + // TODO(gri) once resolve is the default, use first message + // n := s.Names[rhs] + // check.errorf(n.Pos(), "missing initialization expression for %s", n) + check.errorf(s.Names[0].Pos(), "assignment count mismatch") + } + } + + case token.VAR: + // declare all variables + lhs := make([]*Var, len(s.Names)) + for i, name := range s.Names { + obj := &Var{pos: name.Pos(), pkg: pkg, name: name.Name, decl: s} + lhs[i] = obj + check.declare(pkg.scope, obj) + check.register(name, obj) + + var init ast.Expr + switch len(s.Values) { + case len(s.Names): + // lhs and rhs match + init = s.Values[i] + case 1: + // rhs must be a multi-valued expression + init = &projExpr{lhs, s.Values[0]} + default: + if i < len(s.Values) { + init = s.Values[i] + } + } + add(obj, s.Type, init) + } + + // arity of lhs and rhs must match + if lhs, rhs := len(s.Names), len(s.Values); rhs > 0 { + switch { + case lhs < rhs: + // TODO(gri) once resolve is the default, use first message + // x := s.Values[lhs] + // check.errorf(x.Pos(), "too many initialization expressions") + check.errorf(s.Names[0].Pos(), "assignment count mismatch") + case lhs > rhs && rhs != 1: + // TODO(gri) once resolve is the default, use first message + // n := s.Names[rhs] + // check.errorf(n.Pos(), "missing initialization expression for %s", n) + check.errorf(s.Names[0].Pos(), "assignment count mismatch") + } + } + + default: + check.invalidAST(s.Pos(), "invalid token %s", d.Tok) + } + + case *ast.TypeSpec: + obj := &TypeName{pos: s.Name.Pos(), pkg: pkg, name: s.Name.Name, spec: s} + check.declare(pkg.scope, obj) + add(obj, s.Type, nil) + check.register(s.Name, obj) + + default: + check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s) + } + } + + case *ast.FuncDecl: + if d.Recv != nil { + // collect method + methods = append(methods, &mdecl{fileScope, d}) + continue + } + obj := &Func{pos: d.Name.Pos(), pkg: pkg, name: d.Name.Name, decl: d} + if obj.name == "init" { + // init functions are not visible - don't declare them in package scope + obj.outer = pkg.scope + } else { + check.declare(pkg.scope, obj) + } + check.register(d.Name, obj) + add(obj, nil, nil) + + default: + check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) + } + } + } + + // Phase 2: Objects in file scopes and package scopes must have different names. + for _, scope := range scopes { + for _, obj := range scope.Entries { + if alt := pkg.scope.Lookup(obj.Name()); alt != nil { + // TODO(gri) better error message + check.errorf(alt.Pos(), "%s redeclared in this block by import of package %s", obj.Name(), obj.Pkg().Name()) + } + } + } + + // Phase 3: Associate methods with types. + // We do this after all top-level type names have been collected. + + check.topScope = pkg.scope + 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 + 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 := check.lookup(ident); 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 + check.errorf(ident.Pos(), "undeclared name: %s", ident.Name) + continue // ignore this method + } + // declare method in receiver base type scope + scope := check.methods[tname] + if scope == nil { + scope = new(Scope) + check.methods[tname] = scope + } + fun := &Func{pos: m.Name.Pos(), pkg: check.pkg, name: m.Name.Name, decl: m} + check.declare(scope, fun) + check.register(m.Name, fun) + // HACK(gri) change method outer scope to file scope containing the declaration + fun.outer = meth.file // remember the file scope + } + + // Phase 4) Typecheck all objects in objList but not function bodies. + + check.objMap = objMap // indicate we are doing global declarations (objects may not have a type yet) + for _, obj := range objList { + if obj.Type() == nil { + check.declareObject(obj, false) + } + } + check.objMap = nil // done with global declarations + + // Phase 5) Typecheck all functions. + // - done by the caller for now +} + +func (check *checker) declareObject(obj Object, cycleOk bool) { + d := check.objMap[obj] + + // adjust file scope for current object + oldScope := check.topScope + check.topScope = d.file // for lookup + + switch obj := obj.(type) { + case *Const: + check.declareConst(obj, d.typ, d.init) + case *Var: + check.declareVar(obj, d.typ, d.init) + case *TypeName: + check.declareType(obj, d.typ, cycleOk) + case *Func: + check.declareFunc(obj, cycleOk) + default: + unreachable() + } + + check.topScope = oldScope +} + +func (check *checker) declareConst(obj *Const, typ, init ast.Expr) { + if obj.visited { + check.errorf(obj.Pos(), "illegal cycle in initialization of constant %s", obj.name) + obj.typ = Typ[Invalid] + return + } + obj.visited = true + iota, ok := exact.Int64Val(obj.val) // set in phase 1 + assert(ok) + // obj.val = exact.MakeUnknown() //do we need this? should we set val to nil? + + // determine type, if any + if typ != nil { + obj.typ = check.typ(typ, false) + } + + var x operand + + if init == nil { + // TODO(gri) enable error message once resolve is default + // check.errorf(obj.Pos(), "missing initialization expression for %s", obj.name) + goto Error + } + + check.expr(&x, init, nil, int(iota)) + if x.mode == invalid { + goto Error + } + + check.assign(obj, &x) + return + +Error: + if obj.typ == nil { + obj.typ = Typ[Invalid] + } else { + obj.val = exact.MakeUnknown() + } +} + +func (check *checker) declareVar(obj *Var, typ, init ast.Expr) { + if obj.visited { + check.errorf(obj.Pos(), "illegal cycle in initialization of variable %s", obj.name) + obj.typ = Typ[Invalid] + return + } + obj.visited = true + + // determine type, if any + if typ != nil { + obj.typ = check.typ(typ, false) + } + + if init == nil { + if typ == nil { + // TODO(gri) enable error message once resolve is default + // check.errorf(obj.Pos(), "missing type or initialization expression for %s", obj.name) + obj.typ = Typ[Invalid] + } + return + } + + // unpack projection expression, if any + proj, multi := init.(*projExpr) + if multi { + init = proj.Expr + } + + var x operand + check.expr(&x, init, nil, -1) + if x.mode == invalid { + goto Error + } + + if multi { + if t, ok := x.typ.(*Tuple); ok && len(proj.lhs) == t.Len() { + // function result + x.mode = value + for i, lhs := range proj.lhs { + x.expr = nil // TODO(gri) should do better here + x.typ = t.At(i).typ + check.assign(lhs, &x) + } + return + } + + if x.mode == valueok && len(proj.lhs) == 2 { + // comma-ok expression + x.mode = value + check.assign(proj.lhs[0], &x) + + x.typ = Typ[UntypedBool] + check.assign(proj.lhs[1], &x) + return + } + + // TODO(gri) better error message + check.errorf(proj.lhs[0].Pos(), "assignment count mismatch") + goto Error + } + + check.assign(obj, &x) + return + +Error: + // mark all involved variables so we can avoid repeated error messages + if multi { + for _, obj := range proj.lhs { + if obj.typ == nil { + obj.typ = Typ[Invalid] + obj.visited = true + } + } + } else if obj.typ == nil { + obj.typ = Typ[Invalid] + } + return +} + +func (check *checker) declareType(obj *TypeName, typ ast.Expr, cycleOk bool) { + named := &Named{obj: obj} + obj.typ = named // mark object so recursion terminates in case of cycles + named.underlying = check.typ(typ, cycleOk).Underlying() + + // 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 + for _, f := range t.fields { + if m := scope.Lookup(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 ObjSet + for _, obj := range scope.Entries { + m := obj.(*Func) + + // set the correct file scope for checking this method type + fileScope := m.outer + assert(fileScope != nil) + oldScope := check.topScope + check.topScope = fileScope + + sig := check.typ(m.decl.Type, 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 + assert(methods.Insert(obj) == nil) + check.later(m, sig, m.decl.Body) + } + named.methods = methods + delete(check.methods, obj) // we don't need this scope anymore + } +} + +func (check *checker) declareFunc(obj *Func, cycleOk bool) { + 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 { + sig := check.typ(fdecl.Type, cycleOk).(*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) + } +} + +func (check *checker) declStmt(decl ast.Decl) { + pkg := check.pkg + + switch d := decl.(type) { + case *ast.BadDecl: + // ignore + + case *ast.GenDecl: + var last []ast.Expr // last list of const initializers seen + for iota, spec := range d.Specs { + switch s := spec.(type) { + case *ast.ValueSpec: + switch d.Tok { + case token.CONST: + // determine which initialization expressions to use + if len(s.Values) > 0 { + last = s.Values + } + + // declare all constants + lhs := make([]*Const, len(s.Names)) + for i, name := range s.Names { + obj := &Const{pos: name.Pos(), pkg: pkg, name: name.Name, val: exact.MakeInt64(int64(iota)), spec: s} + lhs[i] = obj + + var init ast.Expr + if i < len(last) { + init = last[i] + } + + check.declareConst(obj, s.Type, init) + check.register(name, obj) + } + + // arity of lhs and rhs must match + switch lhs, rhs := len(s.Names), len(last); { + case lhs < rhs: + x := last[lhs] + check.errorf(x.Pos(), "too many initialization expressions") + case lhs > rhs: + n := s.Names[rhs] + check.errorf(n.Pos(), "missing initialization expression for %s", n) + } + + for _, obj := range lhs { + check.declare(check.topScope, obj) + } + + case token.VAR: + // declare all variables + lhs := make([]*Var, len(s.Names)) + for i, name := range s.Names { + obj := &Var{pos: name.Pos(), pkg: pkg, name: name.Name, decl: s} + lhs[i] = obj + check.register(name, obj) + } + + // iterate in 2 phases because declareVar requires fully initialized lhs! + for i, obj := range lhs { + var init ast.Expr + switch len(s.Values) { + case len(s.Names): + // lhs and rhs match + init = s.Values[i] + case 1: + // rhs must be a multi-valued expression + init = &projExpr{lhs, s.Values[0]} + default: + if i < len(s.Values) { + init = s.Values[i] + } + } + + check.declareVar(obj, s.Type, init) + } + + // arity of lhs and rhs must match + // TODO(gri) Disabled for now to match existing test errors. + // These error messages are better than what we have now. + /* + if lhs, rhs := len(s.Names), len(s.Values); rhs > 0 { + switch { + case lhs < rhs: + x := s.Values[lhs] + check.errorf(x.Pos(), "too many initialization expressions") + case lhs > rhs && rhs != 1: + n := s.Names[rhs] + check.errorf(n.Pos(), "missing initialization expression for %s", n) + } + } + */ + + for _, obj := range lhs { + check.declare(check.topScope, obj) + } + + default: + check.invalidAST(s.Pos(), "invalid token %s", d.Tok) + } + + case *ast.TypeSpec: + obj := &TypeName{pos: s.Name.Pos(), pkg: pkg, name: s.Name.Name, spec: s} + check.declare(check.topScope, obj) + check.declareType(obj, s.Type, false) + check.register(s.Name, obj) + + default: + check.invalidAST(s.Pos(), "const, type, or var declaration expected") + } + } + + default: + check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d) + } +} diff --git a/go/types/resolver_test.go b/go/types/resolver_test.go index ae4fa2bb..564cd865 100644 --- a/go/types/resolver_test.go +++ b/go/types/resolver_test.go @@ -55,7 +55,11 @@ func TestResolveQualifiedIdents(t *testing.T) { fset := token.NewFileSet() var files []*ast.File for _, src := range sources { - f, err := parser.ParseFile(fset, "", src, parser.DeclarationErrors) + mode := parser.AllErrors + if !resolve { + mode |= parser.DeclarationErrors + } + f, err := parser.ParseFile(fset, "", src, mode) if err != nil { t.Fatal(err) } @@ -79,9 +83,11 @@ func TestResolveQualifiedIdents(t *testing.T) { } // check that there are no top-level unresolved identifiers - for _, f := range files { - for _, x := range f.Unresolved { - t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.Name) + if !resolve { + for _, f := range files { + for _, x := range f.Unresolved { + t.Errorf("%s: unresolved global identifier %s", fset.Position(x.Pos()), x.Name) + } } } @@ -113,6 +119,9 @@ func TestResolveQualifiedIdents(t *testing.T) { ast.Inspect(f, func(n ast.Node) bool { switch x := n.(type) { case *ast.StructType: + if resolve { + break // nothing to do + } for _, list := range x.Fields.List { for _, f := range list.Names { assert(idents[f] == nil) @@ -120,6 +129,9 @@ func TestResolveQualifiedIdents(t *testing.T) { } } case *ast.InterfaceType: + if resolve { + break // nothing to do + } for _, list := range x.Methods.List { for _, f := range list.Names { assert(idents[f] == nil) diff --git a/go/types/scope.go b/go/types/scope.go index 7d8ab56b..c52266e7 100644 --- a/go/types/scope.go +++ b/go/types/scope.go @@ -38,7 +38,8 @@ func (s *Scope) Lookup(name string) Object { // Insert attempts to insert an object obj into scope s. // If s already contains an object with the same name, // Insert leaves s unchanged and returns that object. -// Otherwise it inserts obj and returns nil. +// Otherwise it inserts obj, sets the object's scope to +// s, and returns nil. // func (s *Scope) Insert(obj Object) Object { name := obj.Name() @@ -46,6 +47,7 @@ func (s *Scope) Insert(obj Object) Object { return alt } s.Entries = append(s.Entries, obj) + obj.setOuter(s) // If the scope size reaches a threshold, use a map for faster lookups. const threshold = 20 diff --git a/go/types/stdlib_test.go b/go/types/stdlib_test.go index de03fd8b..c6fcaae7 100644 --- a/go/types/stdlib_test.go +++ b/go/types/stdlib_test.go @@ -30,6 +30,10 @@ var ( ) func TestStdlib(t *testing.T) { + if resolve { + fmt.Println("*** Running new code: Identifiers are resolved by type checker. ***") + } + walkDirs(t, filepath.Join(runtime.GOROOT(), "src/pkg")) if *verbose { fmt.Println(pkgCount, "packages typechecked in", time.Since(start)) @@ -48,7 +52,11 @@ func typecheck(t *testing.T, path string, filenames []string) { // parse package files var files []*ast.File for _, filename := range filenames { - file, err := parser.ParseFile(fset, filename, nil, parser.DeclarationErrors|parser.AllErrors) + mode := parser.AllErrors + if !resolve { + mode |= parser.DeclarationErrors + } + file, err := parser.ParseFile(fset, filename, nil, mode) if err != nil { // the parser error may be a list of individual errors; report them all if list, ok := err.(scanner.ErrorList); ok { diff --git a/go/types/stmt.go b/go/types/stmt.go index 3aa50196..ee68fb53 100644 --- a/go/types/stmt.go +++ b/go/types/stmt.go @@ -41,7 +41,9 @@ func (check *checker) assignment(x *operand, to Type) bool { // if its type is not set, it is deduced from the type of x or set to Typ[Invalid] in // case of an error. // -func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota int) { +func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota int, isConst bool) { + assert(!isConst || decl) + // Start with rhs so we have an expression type // for declarations with implicit type. if x == nil { @@ -68,7 +70,7 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota var z operand check.expr(&z, lhs, nil, -1) - if z.mode == invalid { + if z.mode == invalid || z.typ == Typ[Invalid] { return } @@ -91,8 +93,22 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota // Determine typ of lhs: If the object doesn't have a type // yet, determine it from the type of x; if x is invalid, // set the object type to Typ[Invalid]. + var obj Object var typ Type - obj := check.lookup(ident) + + if resolve { + if isConst { + obj = &Const{pos: ident.Pos(), pkg: check.pkg, name: ident.Name} + } else { + obj = &Var{pos: ident.Pos(), pkg: check.pkg, name: ident.Name} + } + check.register(ident, obj) + defer check.declare(check.topScope, obj) + + } else { + obj = check.lookup(ident) + } + switch obj := obj.(type) { default: unreachable() @@ -165,14 +181,15 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota // of the corresponding rhs expressions, or set to Typ[Invalid] in case of an error. // Precondition: len(lhs) > 0 . // -func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) { +func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int, isConst bool) { assert(len(lhs) > 0) + assert(!isConst || decl) // If the lhs and rhs have corresponding expressions, treat each // matching pair as an individual pair. if len(lhs) == len(rhs) { for i, e := range rhs { - check.assign1to1(lhs[i], e, nil, decl, iota) + check.assign1to1(lhs[i], e, nil, decl, iota, isConst) } return } @@ -197,7 +214,7 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) { obj := t.At(i) x.expr = nil // TODO(gri) should do better here x.typ = obj.typ - check.assign1to1(lhs[i], nil, &x, decl, iota) + check.assign1to1(lhs[i], nil, &x, decl, iota, isConst) } return } @@ -205,10 +222,10 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int) { if x.mode == valueok && len(lhs) == 2 { // comma-ok expression x.mode = value - check.assign1to1(lhs[0], nil, &x, decl, iota) + check.assign1to1(lhs[0], nil, &x, decl, iota, isConst) x.typ = Typ[UntypedBool] - check.assign1to1(lhs[1], nil, &x, decl, iota) + check.assign1to1(lhs[1], nil, &x, decl, iota, isConst) return } } @@ -224,7 +241,21 @@ Error: check.errorf(e.Pos(), "cannot declare %s", e) continue } - switch obj := check.lookup(ident).(type) { + + var obj Object + if resolve { + if isConst { + obj = &Const{pos: ident.Pos(), pkg: check.pkg, name: ident.Name} + } else { + obj = &Var{pos: ident.Pos(), pkg: check.pkg, name: ident.Name} + } + defer check.declare(check.topScope, obj) + + } else { + obj = check.lookup(ident) + } + + switch obj := obj.(type) { case *Const: obj.typ = Typ[Invalid] case *Var: @@ -287,6 +318,11 @@ func (check *checker) stmt(s ast.Stmt) { // ignore case *ast.DeclStmt: + if resolve { + check.declStmt(s.Decl) + return + } + d, _ := s.Decl.(*ast.GenDecl) if d == nil || (d.Tok != token.CONST && d.Tok != token.TYPE && d.Tok != token.VAR) { check.invalidAST(token.NoPos, "const, type, or var declaration expected") @@ -298,7 +334,7 @@ func (check *checker) stmt(s ast.Stmt) { check.decl(d) case *ast.LabeledStmt: - // TODO(gri) anything to do with label itself? + // TODO(gri) Declare label in the respectice label scope; define Label object. check.stmt(s.Stmt) case *ast.ExprStmt: @@ -363,7 +399,7 @@ func (check *checker) stmt(s ast.Stmt) { if x.mode == invalid { return } - check.assign1to1(s.X, nil, &x, false, -1) + check.assign1to1(s.X, nil, &x, false, -1, false) case *ast.AssignStmt: switch s.Tok { @@ -372,7 +408,34 @@ func (check *checker) stmt(s ast.Stmt) { check.invalidAST(s.Pos(), "missing lhs in assignment") return } - check.assignNtoM(s.Lhs, s.Rhs, s.Tok == token.DEFINE, -1) + if resolve && s.Tok == token.DEFINE { + // short variable declaration + lhs := make([]Object, len(s.Lhs)) + for i, x := range s.Lhs { + var obj *Var + if ident, ok := x.(*ast.Ident); ok { + obj = &Var{pos: ident.Pos(), pkg: check.pkg, name: ident.Name, decl: s} + // If the variable is already declared (redeclaration in :=), + // register the identifier with the existing variable. + if alt := check.topScope.Lookup(ident.Name); alt != nil { + check.register(ident, alt) + } else { + check.register(ident, obj) + } + } else { + check.errorf(x.Pos(), "cannot declare %s", x) + // create a dummy variable + obj = &Var{pos: x.Pos(), pkg: check.pkg, name: "_", decl: s} + } + lhs[i] = obj + } + check.assignMulti(lhs, s.Rhs) + check.declareShort(check.topScope, lhs) // scope starts after the assignment + + } else { + check.assignNtoM(s.Lhs, s.Rhs, s.Tok == token.DEFINE, -1, false) + } + default: // assignment operations if len(s.Lhs) != 1 || len(s.Rhs) != 1 { @@ -413,7 +476,7 @@ func (check *checker) stmt(s ast.Stmt) { if x.mode == invalid { return } - check.assign1to1(s.Lhs[0], nil, &x, false, -1) + check.assign1to1(s.Lhs[0], nil, &x, false, -1, false) } case *ast.GoStmt: @@ -425,6 +488,22 @@ func (check *checker) stmt(s ast.Stmt) { case *ast.ReturnStmt: sig := check.funcsig if n := sig.results.Len(); n > 0 { + if resolve { + // determine if the function has named results + named := false + lhs := make([]Object, len(sig.results.vars)) + for i, res := range sig.results.vars { + if res.name != "" { + // a blank (_) result parameter is a named result + named = true + } + lhs[i] = res + } + if len(s.Results) > 0 || !named { + check.assignMulti(lhs, s.Results) + return + } + } // TODO(gri) should not have to compute lhs, named every single time - clean this up lhs := make([]ast.Expr, n) named := false // if set, function has named results @@ -440,7 +519,7 @@ func (check *checker) stmt(s ast.Stmt) { } if len(s.Results) > 0 || !named { // TODO(gri) assignNtoM should perhaps not require len(lhs) > 0 - check.assignNtoM(lhs, s.Results, false, -1) + check.assignNtoM(lhs, s.Results, false, -1, false) } } else if len(s.Results) > 0 { check.errorf(s.Pos(), "no result values expected") @@ -450,9 +529,12 @@ func (check *checker) stmt(s ast.Stmt) { // TODO(gri) implement this case *ast.BlockStmt: + check.openScope() check.stmtList(s.List) + check.closeScope() case *ast.IfStmt: + check.openScope() check.optionalStmt(s.Init) var x operand check.expr(&x, s.Cond, nil, -1) @@ -461,8 +543,10 @@ func (check *checker) stmt(s ast.Stmt) { } check.stmt(s.Body) check.optionalStmt(s.Else) + check.closeScope() case *ast.SwitchStmt: + check.openScope() check.optionalStmt(s.Init) var x operand tag := s.Tag @@ -523,10 +607,14 @@ func (check *checker) stmt(s ast.Stmt) { } } } + check.openScope() check.stmtList(clause.Body) + check.closeScope() } + check.closeScope() case *ast.TypeSwitchStmt: + check.openScope() check.optionalStmt(s.Init) // A type switch guard must be of the form: @@ -552,7 +640,13 @@ func (check *checker) stmt(s ast.Stmt) { check.invalidAST(s.Pos(), "incorrect form of type switch guard") return } - lhs = check.lookup(ident).(*Var) + if resolve { + // TODO(gri) in the future, create one of these for each block with the correct type! + lhs = &Var{pkg: check.pkg, name: ident.Name} + check.register(ident, lhs) + } else { + lhs = check.lookup(ident).(*Var) + } rhs = guard.Rhs[0] default: check.invalidAST(s.Pos(), "incorrect form of type switch guard") @@ -576,6 +670,10 @@ func (check *checker) stmt(s ast.Stmt) { return } + if resolve && lhs != nil { + check.declare(check.topScope, lhs) + } + check.multipleDefaults(s.Body.List) for _, s := range s.Body.List { clause, _ := s.(*ast.CaseClause) @@ -608,7 +706,9 @@ func (check *checker) stmt(s ast.Stmt) { } lhs.typ = typ } + check.openScope() check.stmtList(clause.Body) + check.closeScope() } // There is only one object (lhs) associated with a lhs identifier, but that object @@ -617,6 +717,7 @@ func (check *checker) stmt(s ast.Stmt) { if lhs != nil { lhs.typ = x.typ } + check.closeScope() case *ast.SelectStmt: check.multipleDefaults(s.Body.List) @@ -625,11 +726,14 @@ func (check *checker) stmt(s ast.Stmt) { if clause == nil { continue // error reported before } + check.openScope() check.optionalStmt(clause.Comm) // TODO(gri) check correctness of c.Comm (must be Send/RecvStmt) check.stmtList(clause.Body) + check.closeScope() } case *ast.ForStmt: + check.openScope() check.optionalStmt(s.Init) if s.Cond != nil { var x operand @@ -640,8 +744,10 @@ func (check *checker) stmt(s ast.Stmt) { } check.optionalStmt(s.Post) check.stmt(s.Body) + check.closeScope() case *ast.RangeStmt: + check.openScope() // check expression to iterate over decl := s.Tok == token.DEFINE var x operand @@ -706,7 +812,7 @@ func (check *checker) stmt(s ast.Stmt) { if s.Key != nil { x.typ = key x.expr = s.Key - check.assign1to1(s.Key, nil, &x, decl, -1) + check.assign1to1(s.Key, nil, &x, decl, -1, false) } else { check.invalidAST(s.Pos(), "range clause requires index iteration variable") // ok to continue @@ -714,10 +820,11 @@ func (check *checker) stmt(s ast.Stmt) { if s.Value != nil { x.typ = val x.expr = s.Value - check.assign1to1(s.Value, nil, &x, decl, -1) + check.assign1to1(s.Value, nil, &x, decl, -1, false) } check.stmt(s.Body) + check.closeScope() default: check.errorf(s.Pos(), "invalid statement") diff --git a/go/types/testdata/const0.src b/go/types/testdata/const0.src index 788c6f51..f87b85d9 100644 --- a/go/types/testdata/const0.src +++ b/go/types/testdata/const0.src @@ -42,6 +42,7 @@ const ( ui16 = ui2 & ui3 ui17 = ui2 | ui3 ui18 = ui2 ^ ui3 + ui19 = 1 /* ERROR "invalid operation" */ % 1.0 // floating point values uf0 = 0. @@ -179,6 +180,8 @@ const ( const ( a1, a2, a3 = 7, 3.1415926, "foo" b1, b2, b3 = b3, b1, 42 + c1 /* ERROR "assignment count mismatch" */, c2, c3 = 1, 2 + d1 /* ERROR "assignment count mismatch" */, d2, d3 = 1, 2, 3, 4 _p0 = assert(a1 == 7) _p1 = assert(a2 == 3.1415926) _p2 = assert(a3 == "foo") diff --git a/go/types/testdata/decls0.src b/go/types/testdata/decls0.src index c4877349..68843f9a 100644 --- a/go/types/testdata/decls0.src +++ b/go/types/testdata/decls0.src @@ -21,7 +21,7 @@ type flag int type _ reflect /* ERROR "not exported" */ .flag // dot-imported exported objects may conflict with local objects -type Value /* ERROR "redeclared in this block by dot-import" */ struct{} +type Value /* ERROR "redeclared in this block by import" */ struct{} const pi = 3.1415 @@ -74,7 +74,8 @@ type ( // is done in the parser and the other one in the type checker. We cannot // easily avoid this w/o losing the check or functionality. This will be // fixed once all resolution is done in the type checker. - a /* ERROR "redeclared" */ /* ERROR "redeclared" */ int + // TODO(gri) Disabled for now since this needs to work with the old and new resolver. + // a /* ERROR "redeclared" */ /* ERROR "redeclared" */ int // where the cycle error appears depends on the // order in which declarations are processed @@ -142,7 +143,7 @@ type ( I2 interface { m1() } - I3 interface { /* ERROR "multiple methods named m1" */ + I3 interface { m1() m1 /* ERROR "redeclared" */ () } diff --git a/go/types/testdata/decls1.src b/go/types/testdata/decls1.src index 779cf877..d5381408 100644 --- a/go/types/testdata/decls1.src +++ b/go/types/testdata/decls1.src @@ -110,7 +110,7 @@ func f1(a /* ERROR "not a type" */) {} func f2(a, b, c d /* ERROR "not a type" */) {} func f3() int { return 0 } -func f4() a /* ERROR "not a type" */ { return 0 /* ERROR "cannot convert" */ } +func f4() a /* ERROR "not a type" */ { return 0 } func f5() (a, b, c d /* ERROR "not a type" */) { return } func f6(a, b, c int) complex128 { return 0 } diff --git a/go/types/testdata/decls2a.src b/go/types/testdata/decls2a.src index 7246f228..93e3d172 100644 --- a/go/types/testdata/decls2a.src +++ b/go/types/testdata/decls2a.src @@ -61,9 +61,9 @@ func (T5 /* ERROR "invalid receiver" */) m2() {} // Methods associated with non-local or unnamed types. func (int /* ERROR "non-local type" */ ) m() {} -func ([ /* ERROR "expected" */ ]int) m() {} -func (time /* ERROR "expected" */ .Time) m() {} -func (x interface /* ERROR "expected" */ {}) m() {} +func ([ /* ERROR "identifier" */ ]int) m() {} +func (time /* ERROR "identifier" */ .Time) m() {} +func (x interface /* ERROR "identifier" */ {}) m() {} // Double declarations across package files const c_double = 0 diff --git a/go/types/testdata/decls2b.src b/go/types/testdata/decls2b.src index a28c695e..170eecf6 100644 --- a/go/types/testdata/decls2b.src +++ b/go/types/testdata/decls2b.src @@ -6,9 +6,12 @@ package decls2 +import "io" + const pi = 3.1415 func (T1) m /* ERROR "redeclared" */ () {} +func (T2) m(io.Writer) {} type T3 struct { f *T3 diff --git a/go/types/types.go b/go/types/types.go index 6ee4e7ea..3482bfea 100644 --- a/go/types/types.go +++ b/go/types/types.go @@ -6,6 +6,9 @@ package types import "go/ast" +// TODO(gri) Separate struct fields below into transient (used during type checking only) +// and permanent fields. + // A Type represents a type of Go. // All types implement the Type interface. type Type interface { @@ -137,13 +140,14 @@ type Field struct { // A Struct represents a struct type. type Struct struct { + scope *Scope fields []*Field tags []string // field tags; nil of there are no tags offsets []int64 // field offsets in bytes, lazily computed } func NewStruct(fields []*Field, tags []string) *Struct { - return &Struct{fields: fields, tags: tags} + return &Struct{scope: nil, fields: fields, tags: tags} } func (s *Struct) NumFields() int { return len(s.fields) } @@ -230,6 +234,7 @@ func (t *Tuple) ForEach(f func(*Var)) { // A Signature represents a (non-builtin) function type. type Signature struct { + scope *Scope // function scope recv *Var // nil if not a method params *Tuple // (incoming) parameters from left to right; or nil results *Tuple // (outgoing) results from left to right; or nil @@ -240,7 +245,7 @@ type Signature struct { // and results, either of which may be nil. If isVariadic is set, the function // is variadic. func NewSignature(recv *Var, params, results *Tuple, isVariadic bool) *Signature { - return &Signature{recv, params, results, isVariadic} + return &Signature{nil, recv, params, results, isVariadic} } // Recv returns the receiver of signature s, or nil. diff --git a/go/types/types_test.go b/go/types/types_test.go index 6b088722..fdf49078 100644 --- a/go/types/types_test.go +++ b/go/types/types_test.go @@ -16,7 +16,11 @@ import ( const filename = "" func makePkg(t *testing.T, src string) (*Package, error) { - file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors) + mode := parser.AllErrors + if !resolve { + mode |= parser.DeclarationErrors + } + file, err := parser.ParseFile(fset, filename, src, mode) if err != nil { return nil, err } @@ -154,7 +158,11 @@ var testExprs = []testEntry{ func TestExprs(t *testing.T) { for _, test := range testExprs { src := "package p; var _ = " + test.src + "; var (x, y int; s []string; f func(int, float32) int; i interface{}; t interface { foo() })" - file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors) + mode := parser.AllErrors + if !resolve { + mode |= parser.DeclarationErrors + } + file, err := parser.ParseFile(fset, filename, src, mode) if err != nil { t.Errorf("%s: %s", src, err) continue diff --git a/go/types/universe.go b/go/types/universe.go index 68cafc67..ee5ac8a6 100644 --- a/go/types/universe.go +++ b/go/types/universe.go @@ -8,6 +8,7 @@ package types import ( "go/ast" + "go/token" "strings" "code.google.com/p/go.tools/go/exact" @@ -102,7 +103,7 @@ func init() { // Error has a nil package in its qualified name since it is in no package var methods ObjSet sig := &Signature{results: NewTuple(&Var{name: "", typ: Typ[String]})} - methods.Insert(&Func{nil, "Error", sig, nil}) + methods.Insert(&Func{token.NoPos, nil, nil, "Error", sig, nil}) def(&TypeName{name: "error", typ: &Named{underlying: &Interface{methods: methods}}}) }