diff --git a/go/types/decl.go b/go/types/decl.go new file mode 100644 index 00000000..5d170862 --- /dev/null +++ b/go/types/decl.go @@ -0,0 +1,369 @@ +// Copyright 2014 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" + "go/token" + + "code.google.com/p/go.tools/go/exact" +) + +func (check *checker) reportAltDecl(obj Object) { + if pos := obj.Pos(); pos.IsValid() { + // We use "other" rather than "previous" here because + // the first declaration seen may not be textually + // earlier in the source. + check.errorf(pos, "\tother declaration of %s", obj.Name()) // secondary error, \t indented + } +} + +func (check *checker) declare(scope *Scope, id *ast.Ident, obj Object) { + if alt := scope.Insert(obj); alt != nil { + check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name()) + check.reportAltDecl(alt) + return + } + if id != nil { + check.recordObject(id, obj) + } +} + +// objDecl type-checks the declaration of obj in its respective file scope. +// See typeDecl for the details on def and cycleOk. +func (check *checker) objDecl(obj Object, def *Named, cycleOk bool) { + d := check.objMap[obj] + + // adjust file scope for current object + oldScope := check.topScope + check.topScope = d.file // for lookup + + // save current iota + oldIota := check.iota + check.iota = nil + + // save current decl + oldDecl := check.decl + check.decl = nil + + switch obj := obj.(type) { + case *Const: + check.constDecl(obj, d.typ, d.init) + case *Var: + check.decl = d // new package-level var decl + check.varDecl(obj, d.lhs, d.typ, d.init) + case *TypeName: + check.typeDecl(obj, d.typ, def, cycleOk) + case *Func: + check.funcDecl(obj, d) + default: + unreachable() + } + + check.decl = oldDecl + check.iota = oldIota + check.topScope = oldScope +} + +func (check *checker) constDecl(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 + + // use the correct value of iota + assert(check.iota == nil) + check.iota = obj.val + + // determine type, if any + if typ != nil { + t := check.typ(typ, nil, false) + if !isConstType(t) { + check.errorf(typ.Pos(), "invalid constant type %s", t) + obj.typ = Typ[Invalid] + check.iota = nil + return + } + obj.typ = t + } + + // check initialization + var x operand + if init != nil { + check.expr(&x, init) + } + check.initConst(obj, &x) + + check.iota = nil +} + +// TODO(gri) document arguments +func (check *checker) varDecl(obj *Var, lhs []*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 + + // var declarations cannot use iota + assert(check.iota == nil) + + // determine type, if any + if typ != nil { + obj.typ = check.typ(typ, nil, false) + } + + // check initialization + if init == nil { + if typ == nil { + // error reported before by arityMatch + obj.typ = Typ[Invalid] + } + return + } + + if lhs == nil || len(lhs) == 1 { + assert(lhs == nil || lhs[0] == obj) + var x operand + check.expr(&x, init) + check.initVar(obj, &x) + return + } + + if debug { + // obj must be one of lhs + found := false + for _, lhs := range lhs { + if obj == lhs { + found = true + break + } + } + if !found { + panic("inconsistent lhs") + } + } + check.initVars(lhs, []ast.Expr{init}, token.NoPos) +} + +func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, cycleOk bool) { + assert(obj.Type() == nil) + + // type declarations cannot use iota + assert(check.iota == nil) + + named := &Named{obj: obj} + obj.typ = named // make sure recursive type declarations terminate + + // If this type (named) defines the type of another (def) type declaration, + // set def's underlying type to this type so that we can resolve the true + // underlying of def later. + if def != nil { + def.underlying = named + } + + // Typecheck typ - it may be a named type that is not yet complete. + // For instance, consider: + // + // type ( + // A B + // B *C + // C A + // ) + // + // When we declare object C, typ is the identifier A which is incomplete. + u := check.typ(typ, named, cycleOk) + + // Determine the unnamed underlying type. + // In the above example, the underlying type of A was (temporarily) set + // to B whose underlying type was set to *C. Such "forward chains" always + // end in an unnamed type (cycles are terminated with an invalid type). + for { + n, _ := u.(*Named) + if n == nil { + break + } + u = n.underlying + } + named.underlying = u + + // the underlying type has been determined + named.complete = true + + // 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 an objset to determine redeclarations + var mset objset + + // spec: "If the base type is a struct type, the non-blank method + // and field names must be distinct." + // => pre-populate the objset to find conflicts + // TODO(gri) consider keeping the objset with the struct instead + if t, _ := named.underlying.(*Struct); t != nil { + for _, fld := range t.fields { + if fld.name != "_" { + assert(mset.insert(fld) == nil) + } + } + } + + // check each method + for _, m := range methods { + if m.name != "_" { + if alt := mset.insert(m); alt != nil { + switch alt.(type) { + case *Var: + check.errorf(m.pos, "field and method with the same name %s", m.name) + case *Func: + check.errorf(m.pos, "method %s already declared for %s", m.name, named) + default: + unreachable() + } + check.reportAltDecl(alt) + continue + } + } + check.recordObject(check.objMap[m].fdecl.Name, m) + check.objDecl(m, nil, true) + // Methods with blank _ names cannot be found. + // Don't add them to the method list. + if m.name != "_" { + named.methods = append(named.methods, m) + } + } + + delete(check.methods, obj.name) // we don't need them anymore +} + +type funcInfo struct { + name string // for tracing only + info *declInfo // for cycle detection + sig *Signature + body *ast.BlockStmt +} + +func (check *checker) funcDecl(obj *Func, info *declInfo) { + // func declarations cannot use iota + assert(check.iota == nil) + + obj.typ = Typ[Invalid] // guard against cycles + fdecl := info.fdecl + 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 + + // function body must be type-checked after global declarations + // (functions implemented elsewhere have no body) + if !check.conf.IgnoreFuncBodies && fdecl.Body != nil { + check.funcList = append(check.funcList, funcInfo{obj.name, info, 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.ValueSpec // last ValueSpec with type or init exprs seen + for iota, spec := range d.Specs { + switch s := spec.(type) { + case *ast.ValueSpec: + switch d.Tok { + case token.CONST: + // determine which init exprs to use + switch { + case s.Type != nil || len(s.Values) > 0: + last = s + case last == nil: + last = new(ast.ValueSpec) // make sure last exists + } + + // declare all constants + lhs := make([]*Const, len(s.Names)) + for i, name := range s.Names { + obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota))) + lhs[i] = obj + + var init ast.Expr + if i < len(last.Values) { + init = last.Values[i] + } + + check.constDecl(obj, last.Type, init) + } + + check.arityMatch(s, last) + + for i, name := range s.Names { + check.declare(check.topScope, name, lhs[i]) + } + + case token.VAR: + lhs0 := make([]*Var, len(s.Names)) + for i, name := range s.Names { + lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil) + } + + // initialize all variables + for i, obj := range lhs0 { + var lhs []*Var + var init ast.Expr + switch len(s.Values) { + case len(s.Names): + // lhs and rhs match + init = s.Values[i] + case 1: + // rhs is expected to be a multi-valued expression + lhs = lhs0 + init = s.Values[0] + default: + if i < len(s.Values) { + init = s.Values[i] + } + } + check.varDecl(obj, lhs, s.Type, init) + } + + check.arityMatch(s, nil) + + // declare all variables + // (only at this point are the variable scopes (parents) set) + for i, name := range s.Names { + check.declare(check.topScope, name, lhs0[i]) + } + + default: + check.invalidAST(s.Pos(), "invalid token %s", d.Tok) + } + + case *ast.TypeSpec: + obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) + check.declare(check.topScope, s.Name, obj) + check.typeDecl(obj, s.Type, nil, false) + + 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/objects.go b/go/types/object.go similarity index 100% rename from go/types/objects.go rename to go/types/object.go diff --git a/go/types/resolver.go b/go/types/resolver.go index 3beea8a5..5846d6d9 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -17,26 +17,6 @@ import ( "code.google.com/p/go.tools/go/exact" ) -func (check *checker) reportAltDecl(obj Object) { - if pos := obj.Pos(); pos.IsValid() { - // We use "other" rather than "previous" here because - // the first declaration seen may not be textually - // earlier in the source. - check.errorf(pos, "\tother declaration of %s", obj.Name()) // secondary error, \t indented - } -} - -func (check *checker) declare(scope *Scope, id *ast.Ident, obj Object) { - if alt := scope.Insert(obj); alt != nil { - check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name()) - check.reportAltDecl(alt) - return - } - if id != nil { - check.recordObject(id, obj) - } -} - // A declInfo describes a package-level const, type, var, or func declaration. type declInfo struct { file *Scope // scope of file containing this declaration @@ -49,13 +29,6 @@ type declInfo struct { mark int // see check.dependencies } -type funcInfo struct { - name string // for tracing only - info *declInfo // for cycle detection - sig *Signature - body *ast.BlockStmt -} - // arityMatch checks that the lhs and rhs of a const or var decl // have the appropriate number of names and init exprs. For const // decls, init is the value spec providing the init exprs; for @@ -564,333 +537,3 @@ func (check *checker) dependencies(obj Object, init *declInfo, path []Object) { check.Info.InitOrder = append(check.Info.InitOrder, &Initializer{initLhs, init.init}) } } - -// objDecl type-checks the declaration of obj in its respective file scope. -// See typeDecl for the details on def and cycleOk. -func (check *checker) objDecl(obj Object, def *Named, cycleOk bool) { - d := check.objMap[obj] - - // adjust file scope for current object - oldScope := check.topScope - check.topScope = d.file // for lookup - - // save current iota - oldIota := check.iota - check.iota = nil - - // save current decl - oldDecl := check.decl - check.decl = nil - - switch obj := obj.(type) { - case *Const: - check.constDecl(obj, d.typ, d.init) - case *Var: - check.decl = d // new package-level var decl - check.varDecl(obj, d.lhs, d.typ, d.init) - case *TypeName: - check.typeDecl(obj, d.typ, def, cycleOk) - case *Func: - check.funcDecl(obj, d) - default: - unreachable() - } - - check.decl = oldDecl - check.iota = oldIota - check.topScope = oldScope -} - -func (check *checker) constDecl(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 - - // use the correct value of iota - assert(check.iota == nil) - check.iota = obj.val - - // determine type, if any - if typ != nil { - t := check.typ(typ, nil, false) - if !isConstType(t) { - check.errorf(typ.Pos(), "invalid constant type %s", t) - obj.typ = Typ[Invalid] - check.iota = nil - return - } - obj.typ = t - } - - // check initialization - var x operand - if init != nil { - check.expr(&x, init) - } - check.initConst(obj, &x) - - check.iota = nil -} - -// TODO(gri) document arguments -func (check *checker) varDecl(obj *Var, lhs []*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 - - // var declarations cannot use iota - assert(check.iota == nil) - - // determine type, if any - if typ != nil { - obj.typ = check.typ(typ, nil, false) - } - - // check initialization - if init == nil { - if typ == nil { - // error reported before by arityMatch - obj.typ = Typ[Invalid] - } - return - } - - if lhs == nil || len(lhs) == 1 { - assert(lhs == nil || lhs[0] == obj) - var x operand - check.expr(&x, init) - check.initVar(obj, &x) - return - } - - if debug { - // obj must be one of lhs - found := false - for _, lhs := range lhs { - if obj == lhs { - found = true - break - } - } - if !found { - panic("inconsistent lhs") - } - } - check.initVars(lhs, []ast.Expr{init}, token.NoPos) -} - -func (check *checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, cycleOk bool) { - assert(obj.Type() == nil) - - // type declarations cannot use iota - assert(check.iota == nil) - - named := &Named{obj: obj} - obj.typ = named // make sure recursive type declarations terminate - - // If this type (named) defines the type of another (def) type declaration, - // set def's underlying type to this type so that we can resolve the true - // underlying of def later. - if def != nil { - def.underlying = named - } - - // Typecheck typ - it may be a named type that is not yet complete. - // For instance, consider: - // - // type ( - // A B - // B *C - // C A - // ) - // - // When we declare object C, typ is the identifier A which is incomplete. - u := check.typ(typ, named, cycleOk) - - // Determine the unnamed underlying type. - // In the above example, the underlying type of A was (temporarily) set - // to B whose underlying type was set to *C. Such "forward chains" always - // end in an unnamed type (cycles are terminated with an invalid type). - for { - n, _ := u.(*Named) - if n == nil { - break - } - u = n.underlying - } - named.underlying = u - - // the underlying type has been determined - named.complete = true - - // 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 an objset to determine redeclarations - var mset objset - - // spec: "If the base type is a struct type, the non-blank method - // and field names must be distinct." - // => pre-populate the objset to find conflicts - // TODO(gri) consider keeping the objset with the struct instead - if t, _ := named.underlying.(*Struct); t != nil { - for _, fld := range t.fields { - if fld.name != "_" { - assert(mset.insert(fld) == nil) - } - } - } - - // check each method - for _, m := range methods { - if m.name != "_" { - if alt := mset.insert(m); alt != nil { - switch alt.(type) { - case *Var: - check.errorf(m.pos, "field and method with the same name %s", m.name) - case *Func: - check.errorf(m.pos, "method %s already declared for %s", m.name, named) - default: - unreachable() - } - check.reportAltDecl(alt) - continue - } - } - check.recordObject(check.objMap[m].fdecl.Name, m) - check.objDecl(m, nil, true) - // Methods with blank _ names cannot be found. - // Don't add them to the method list. - if m.name != "_" { - named.methods = append(named.methods, m) - } - } - - delete(check.methods, obj.name) // we don't need them anymore -} - -func (check *checker) funcDecl(obj *Func, info *declInfo) { - // func declarations cannot use iota - assert(check.iota == nil) - - obj.typ = Typ[Invalid] // guard against cycles - fdecl := info.fdecl - 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 - - // function body must be type-checked after global declarations - // (functions implemented elsewhere have no body) - if !check.conf.IgnoreFuncBodies && fdecl.Body != nil { - check.funcList = append(check.funcList, funcInfo{obj.name, info, 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.ValueSpec // last ValueSpec with type or init exprs seen - for iota, spec := range d.Specs { - switch s := spec.(type) { - case *ast.ValueSpec: - switch d.Tok { - case token.CONST: - // determine which init exprs to use - switch { - case s.Type != nil || len(s.Values) > 0: - last = s - case last == nil: - last = new(ast.ValueSpec) // make sure last exists - } - - // declare all constants - lhs := make([]*Const, len(s.Names)) - for i, name := range s.Names { - obj := NewConst(name.Pos(), pkg, name.Name, nil, exact.MakeInt64(int64(iota))) - lhs[i] = obj - - var init ast.Expr - if i < len(last.Values) { - init = last.Values[i] - } - - check.constDecl(obj, last.Type, init) - } - - check.arityMatch(s, last) - - for i, name := range s.Names { - check.declare(check.topScope, name, lhs[i]) - } - - case token.VAR: - lhs0 := make([]*Var, len(s.Names)) - for i, name := range s.Names { - lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil) - } - - // initialize all variables - for i, obj := range lhs0 { - var lhs []*Var - var init ast.Expr - switch len(s.Values) { - case len(s.Names): - // lhs and rhs match - init = s.Values[i] - case 1: - // rhs is expected to be a multi-valued expression - lhs = lhs0 - init = s.Values[0] - default: - if i < len(s.Values) { - init = s.Values[i] - } - } - check.varDecl(obj, lhs, s.Type, init) - } - - check.arityMatch(s, nil) - - // declare all variables - // (only at this point are the variable scopes (parents) set) - for i, name := range s.Names { - check.declare(check.topScope, name, lhs0[i]) - } - - default: - check.invalidAST(s.Pos(), "invalid token %s", d.Tok) - } - - case *ast.TypeSpec: - obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil) - check.declare(check.topScope, s.Name, obj) - check.typeDecl(obj, s.Type, nil, false) - - 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/types.go b/go/types/type.go similarity index 100% rename from go/types/types.go rename to go/types/type.go