From 7b15ed9ef455b6b66c6b376898a88aef5d6a9970 Mon Sep 17 00:00:00 2001 From: Ondřej Surý Date: Tue, 26 Apr 2011 09:55:32 +0200 Subject: Imported Upstream version 2011.04.13 --- src/pkg/go/ast/Makefile | 1 + src/pkg/go/ast/ast.go | 58 +- src/pkg/go/ast/filter.go | 6 +- src/pkg/go/ast/print.go | 94 +-- src/pkg/go/ast/print_test.go | 80 +++ src/pkg/go/ast/resolve.go | 188 ++++++ src/pkg/go/ast/scope.go | 268 +++----- src/pkg/go/ast/walk.go | 8 +- src/pkg/go/doc/comment.go | 2 +- src/pkg/go/doc/doc.go | 8 +- src/pkg/go/parser/interface.go | 7 +- src/pkg/go/parser/parser.go | 696 +++++++++++++++------ src/pkg/go/parser/parser_test.go | 3 +- src/pkg/go/printer/nodes.go | 114 ++-- src/pkg/go/printer/printer.go | 196 +++--- src/pkg/go/printer/printer_test.go | 37 +- src/pkg/go/printer/testdata/declarations.golden | 77 ++- src/pkg/go/printer/testdata/declarations.input | 29 + src/pkg/go/printer/testdata/expressions.golden | 34 +- src/pkg/go/printer/testdata/expressions.input | 20 +- src/pkg/go/printer/testdata/expressions.raw | 37 +- src/pkg/go/printer/testdata/slow.golden | 85 +++ src/pkg/go/printer/testdata/slow.input | 85 +++ src/pkg/go/scanner/scanner.go | 26 +- src/pkg/go/scanner/scanner_test.go | 60 +- src/pkg/go/token/token.go | 15 +- src/pkg/go/typechecker/Makefile | 1 + src/pkg/go/typechecker/scope.go | 72 +-- src/pkg/go/typechecker/testdata/test0.go | 94 --- src/pkg/go/typechecker/testdata/test0.src | 94 +++ src/pkg/go/typechecker/testdata/test1.go | 13 - src/pkg/go/typechecker/testdata/test1.src | 13 + src/pkg/go/typechecker/testdata/test3.go | 38 -- src/pkg/go/typechecker/testdata/test3.src | 41 ++ src/pkg/go/typechecker/testdata/test4.go | 11 - src/pkg/go/typechecker/testdata/test4.src | 11 + src/pkg/go/typechecker/type.go | 125 ++++ src/pkg/go/typechecker/typechecker.go | 82 +-- src/pkg/go/typechecker/typechecker_test.go | 4 +- src/pkg/go/typechecker/universe.go | 6 +- src/pkg/go/types/Makefile | 15 + src/pkg/go/types/const.go | 347 +++++++++++ src/pkg/go/types/exportdata.go | 135 ++++ src/pkg/go/types/gcimporter.go | 786 ++++++++++++++++++++++++ src/pkg/go/types/gcimporter_test.go | 111 ++++ src/pkg/go/types/testdata/exports.go | 89 +++ src/pkg/go/types/types.go | 122 ++++ src/pkg/go/types/universe.go | 113 ++++ 48 files changed, 3548 insertions(+), 1009 deletions(-) create mode 100644 src/pkg/go/ast/print_test.go create mode 100644 src/pkg/go/ast/resolve.go create mode 100644 src/pkg/go/printer/testdata/slow.golden create mode 100644 src/pkg/go/printer/testdata/slow.input delete mode 100644 src/pkg/go/typechecker/testdata/test0.go create mode 100644 src/pkg/go/typechecker/testdata/test0.src delete mode 100644 src/pkg/go/typechecker/testdata/test1.go create mode 100644 src/pkg/go/typechecker/testdata/test1.src delete mode 100644 src/pkg/go/typechecker/testdata/test3.go create mode 100644 src/pkg/go/typechecker/testdata/test3.src delete mode 100644 src/pkg/go/typechecker/testdata/test4.go create mode 100644 src/pkg/go/typechecker/testdata/test4.src create mode 100644 src/pkg/go/typechecker/type.go create mode 100644 src/pkg/go/types/Makefile create mode 100644 src/pkg/go/types/const.go create mode 100644 src/pkg/go/types/exportdata.go create mode 100644 src/pkg/go/types/gcimporter.go create mode 100644 src/pkg/go/types/gcimporter_test.go create mode 100644 src/pkg/go/types/testdata/exports.go create mode 100644 src/pkg/go/types/types.go create mode 100644 src/pkg/go/types/universe.go (limited to 'src/pkg/go') diff --git a/src/pkg/go/ast/Makefile b/src/pkg/go/ast/Makefile index e9b885c70..40be10208 100644 --- a/src/pkg/go/ast/Makefile +++ b/src/pkg/go/ast/Makefile @@ -9,6 +9,7 @@ GOFILES=\ ast.go\ filter.go\ print.go\ + resolve.go\ scope.go\ walk.go\ diff --git a/src/pkg/go/ast/ast.go b/src/pkg/go/ast/ast.go index abafb5663..ed3e2cdd9 100644 --- a/src/pkg/go/ast/ast.go +++ b/src/pkg/go/ast/ast.go @@ -66,7 +66,7 @@ type Decl interface { // A Comment node represents a single //-style or /*-style comment. type Comment struct { Slash token.Pos // position of "/" starting the comment - Text []byte // comment text (excluding '\n' for //-style comments) + Text string // comment text (excluding '\n' for //-style comments) } @@ -199,7 +199,7 @@ type ( BasicLit struct { ValuePos token.Pos // literal position Kind token.Token // token.INT, token.FLOAT, token.IMAG, token.CHAR, or token.STRING - Value []byte // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o` + Value string // literal string; e.g. 42, 0x7f, 3.14, 1e-9, 2.4i, 'a', '\x7f', "foo" or `\m\n\o` } // A FuncLit node represents a function literal. @@ -602,12 +602,12 @@ type ( Else Stmt // else branch; or nil } - // A CaseClause represents a case of an expression switch statement. + // A CaseClause represents a case of an expression or type switch statement. CaseClause struct { - Case token.Pos // position of "case" or "default" keyword - Values []Expr // nil means default case - Colon token.Pos // position of ":" - Body []Stmt // statement list; or nil + Case token.Pos // position of "case" or "default" keyword + List []Expr // list of expressions or types; nil means default case + Colon token.Pos // position of ":" + Body []Stmt // statement list; or nil } // A SwitchStmt node represents an expression switch statement. @@ -618,20 +618,12 @@ type ( Body *BlockStmt // CaseClauses only } - // A TypeCaseClause represents a case of a type switch statement. - TypeCaseClause struct { - Case token.Pos // position of "case" or "default" keyword - Types []Expr // nil means default case - Colon token.Pos // position of ":" - Body []Stmt // statement list; or nil - } - // An TypeSwitchStmt node represents a type switch statement. TypeSwitchStmt struct { Switch token.Pos // position of "switch" keyword Init Stmt // initalization statement; or nil - Assign Stmt // x := y.(type) - Body *BlockStmt // TypeCaseClauses only + Assign Stmt // x := y.(type) or y.(type) + Body *BlockStmt // CaseClauses only } // A CommClause node represents a case of a select statement. @@ -687,7 +679,6 @@ func (s *BlockStmt) Pos() token.Pos { return s.Lbrace } func (s *IfStmt) Pos() token.Pos { return s.If } func (s *CaseClause) Pos() token.Pos { return s.Case } func (s *SwitchStmt) Pos() token.Pos { return s.Switch } -func (s *TypeCaseClause) Pos() token.Pos { return s.Case } func (s *TypeSwitchStmt) Pos() token.Pos { return s.Switch } func (s *CommClause) Pos() token.Pos { return s.Case } func (s *SelectStmt) Pos() token.Pos { return s.Select } @@ -734,13 +725,7 @@ func (s *CaseClause) End() token.Pos { } return s.Colon + 1 } -func (s *SwitchStmt) End() token.Pos { return s.Body.End() } -func (s *TypeCaseClause) End() token.Pos { - if n := len(s.Body); n > 0 { - return s.Body[n-1].End() - } - return s.Colon + 1 -} +func (s *SwitchStmt) End() token.Pos { return s.Body.End() } func (s *TypeSwitchStmt) End() token.Pos { return s.Body.End() } func (s *CommClause) End() token.Pos { if n := len(s.Body); n > 0 { @@ -772,7 +757,6 @@ func (s *BlockStmt) stmtNode() {} func (s *IfStmt) stmtNode() {} func (s *CaseClause) stmtNode() {} func (s *SwitchStmt) stmtNode() {} -func (s *TypeCaseClause) stmtNode() {} func (s *TypeSwitchStmt) stmtNode() {} func (s *CommClause) stmtNode() {} func (s *SelectStmt) stmtNode() {} @@ -797,7 +781,7 @@ type ( ImportSpec struct { Doc *CommentGroup // associated documentation; or nil Name *Ident // local package name (including "."); or nil - Path *BasicLit // package path + Path *BasicLit // import path Comment *CommentGroup // line comments; or nil } @@ -937,11 +921,14 @@ func (d *FuncDecl) declNode() {} // via Doc and Comment fields. // type File struct { - Doc *CommentGroup // associated documentation; or nil - Package token.Pos // position of "package" keyword - Name *Ident // package name - Decls []Decl // top-level declarations; or nil - Comments []*CommentGroup // list of all comments in the source file + Doc *CommentGroup // associated documentation; or nil + Package token.Pos // position of "package" keyword + Name *Ident // package name + Decls []Decl // top-level declarations; or nil + Scope *Scope // package scope (this file only) + Imports []*ImportSpec // imports in this file + Unresolved []*Ident // unresolved identifiers in this file + Comments []*CommentGroup // list of all comments in the source file } @@ -958,9 +945,10 @@ func (f *File) End() token.Pos { // collectively building a Go package. // type Package struct { - Name string // package name - Scope *Scope // package scope; or nil - Files map[string]*File // Go source files by filename + Name string // package name + Scope *Scope // package scope + Imports map[string]*Scope // map of import path -> package scope across all files + Files map[string]*File // Go source files by filename } diff --git a/src/pkg/go/ast/filter.go b/src/pkg/go/ast/filter.go index 0c3cef4b2..090d08d34 100644 --- a/src/pkg/go/ast/filter.go +++ b/src/pkg/go/ast/filter.go @@ -304,7 +304,7 @@ const ( // separator is an empty //-style comment that is interspersed between // different comment groups when they are concatenated into a single group // -var separator = &Comment{noPos, []byte("//")} +var separator = &Comment{noPos, "//"} // MergePackageFiles creates a file AST by merging the ASTs of the @@ -425,5 +425,7 @@ func MergePackageFiles(pkg *Package, mode MergeMode) *File { } } - return &File{doc, pos, NewIdent(pkg.Name), decls, comments} + // TODO(gri) need to compute pkgScope and unresolved identifiers! + // TODO(gri) need to compute imports! + return &File{doc, pos, NewIdent(pkg.Name), decls, nil, nil, nil, comments} } diff --git a/src/pkg/go/ast/print.go b/src/pkg/go/ast/print.go index d71490d4a..e6d4e838d 100644 --- a/src/pkg/go/ast/print.go +++ b/src/pkg/go/ast/print.go @@ -21,24 +21,29 @@ type FieldFilter func(name string, value reflect.Value) bool // NotNilFilter returns true for field values that are not nil; // it returns false otherwise. -func NotNilFilter(_ string, value reflect.Value) bool { - v, ok := value.(interface { - IsNil() bool - }) - return !ok || !v.IsNil() +func NotNilFilter(_ string, v reflect.Value) bool { + switch v.Kind() { + case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice: + return !v.IsNil() + } + return true } // Fprint prints the (sub-)tree starting at AST node x to w. +// If fset != nil, position information is interpreted relative +// to that file set. Otherwise positions are printed as integer +// values (file set specific offsets). // // A non-nil FieldFilter f may be provided to control the output: // struct fields for which f(fieldname, fieldvalue) is true are // are printed; all others are filtered from the output. // -func Fprint(w io.Writer, x interface{}, f FieldFilter) (n int, err os.Error) { +func Fprint(w io.Writer, fset *token.FileSet, x interface{}, f FieldFilter) (n int, err os.Error) { // setup printer p := printer{ output: w, + fset: fset, filter: f, ptrmap: make(map[interface{}]int), last: '\n', // force printing of line number on first line @@ -65,16 +70,17 @@ func Fprint(w io.Writer, x interface{}, f FieldFilter) (n int, err os.Error) { // Print prints x to standard output, skipping nil fields. -// Print(x) is the same as Fprint(os.Stdout, x, NotNilFilter). -func Print(x interface{}) (int, os.Error) { - return Fprint(os.Stdout, x, NotNilFilter) +// Print(fset, x) is the same as Fprint(os.Stdout, fset, x, NotNilFilter). +func Print(fset *token.FileSet, x interface{}) (int, os.Error) { + return Fprint(os.Stdout, fset, x, NotNilFilter) } type printer struct { output io.Writer + fset *token.FileSet filter FieldFilter - ptrmap map[interface{}]int // *reflect.PtrValue -> line number + ptrmap map[interface{}]int // *T -> line number written int // number of bytes written to output indent int // current indentation level last byte // the last byte processed by Write @@ -135,73 +141,69 @@ func (p *printer) printf(format string, args ...interface{}) { // Implementation note: Print is written for AST nodes but could be // used to print arbitrary data structures; such a version should // probably be in a different package. +// +// Note: This code detects (some) cycles created via pointers but +// not cycles that are created via slices or maps containing the +// same slice or map. Code for general data structures probably +// should catch those as well. func (p *printer) print(x reflect.Value) { - // Note: This test is only needed because AST nodes - // embed a token.Position, and thus all of them - // understand the String() method (but it only - // applies to the Position field). - // TODO: Should reconsider this AST design decision. - if pos, ok := x.Interface().(token.Position); ok { - p.printf("%s", pos) - return - } - if !NotNilFilter("", x) { p.printf("nil") return } - switch v := x.(type) { - case *reflect.InterfaceValue: - p.print(v.Elem()) + switch x.Kind() { + case reflect.Interface: + p.print(x.Elem()) - case *reflect.MapValue: - p.printf("%s (len = %d) {\n", x.Type().String(), v.Len()) + case reflect.Map: + p.printf("%s (len = %d) {\n", x.Type().String(), x.Len()) p.indent++ - for _, key := range v.Keys() { + for _, key := range x.MapKeys() { p.print(key) p.printf(": ") - p.print(v.Elem(key)) + p.print(x.MapIndex(key)) + p.printf("\n") } p.indent-- p.printf("}") - case *reflect.PtrValue: + case reflect.Ptr: p.printf("*") // type-checked ASTs may contain cycles - use ptrmap // to keep track of objects that have been printed // already and print the respective line number instead - ptr := v.Interface() + ptr := x.Interface() if line, exists := p.ptrmap[ptr]; exists { p.printf("(obj @ %d)", line) } else { p.ptrmap[ptr] = p.line - p.print(v.Elem()) + p.print(x.Elem()) } - case *reflect.SliceValue: - if s, ok := v.Interface().([]byte); ok { + case reflect.Slice: + if s, ok := x.Interface().([]byte); ok { p.printf("%#q", s) return } - p.printf("%s (len = %d) {\n", x.Type().String(), v.Len()) + p.printf("%s (len = %d) {\n", x.Type().String(), x.Len()) p.indent++ - for i, n := 0, v.Len(); i < n; i++ { + for i, n := 0, x.Len(); i < n; i++ { p.printf("%d: ", i) - p.print(v.Elem(i)) + p.print(x.Index(i)) p.printf("\n") } p.indent-- p.printf("}") - case *reflect.StructValue: + case reflect.Struct: p.printf("%s {\n", x.Type().String()) p.indent++ - t := v.Type().(*reflect.StructType) + t := x.Type() for i, n := 0, t.NumField(); i < n; i++ { name := t.Field(i).Name - value := v.Field(i) + value := x.Field(i) if p.filter == nil || p.filter(name, value) { p.printf("%s: ", name) p.print(value) @@ -212,6 +214,20 @@ func (p *printer) print(x reflect.Value) { p.printf("}") default: - p.printf("%v", x.Interface()) + v := x.Interface() + switch v := v.(type) { + case string: + // print strings in quotes + p.printf("%q", v) + return + case token.Pos: + // position values can be printed nicely if we have a file set + if p.fset != nil { + p.printf("%s", p.fset.Position(v)) + return + } + } + // default + p.printf("%v", v) } } diff --git a/src/pkg/go/ast/print_test.go b/src/pkg/go/ast/print_test.go new file mode 100644 index 000000000..0820dcfce --- /dev/null +++ b/src/pkg/go/ast/print_test.go @@ -0,0 +1,80 @@ +// Copyright 2011 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 ast + +import ( + "bytes" + "strings" + "testing" +) + + +var tests = []struct { + x interface{} // x is printed as s + s string +}{ + // basic types + {nil, "0 nil"}, + {true, "0 true"}, + {42, "0 42"}, + {3.14, "0 3.14"}, + {1 + 2.718i, "0 (1+2.718i)"}, + {"foobar", "0 \"foobar\""}, + + // maps + {map[string]int{"a": 1, "b": 2}, + `0 map[string] int (len = 2) { + 1 . "a": 1 + 2 . "b": 2 + 3 }`}, + + // pointers + {new(int), "0 *0"}, + + // slices + {[]int{1, 2, 3}, + `0 []int (len = 3) { + 1 . 0: 1 + 2 . 1: 2 + 3 . 2: 3 + 4 }`}, + + // structs + {struct{ x, y int }{42, 991}, + `0 struct { x int; y int } { + 1 . x: 42 + 2 . y: 991 + 3 }`}, +} + + +// Split s into lines, trim whitespace from all lines, and return +// the concatenated non-empty lines. +func trim(s string) string { + lines := strings.Split(s, "\n", -1) + i := 0 + for _, line := range lines { + line = strings.TrimSpace(line) + if line != "" { + lines[i] = line + i++ + } + } + return strings.Join(lines[0:i], "\n") +} + + +func TestPrint(t *testing.T) { + var buf bytes.Buffer + for _, test := range tests { + buf.Reset() + if _, err := Fprint(&buf, nil, test.x, nil); err != nil { + t.Errorf("Fprint failed: %s", err) + } + if s, ts := trim(buf.String()), trim(test.s); s != ts { + t.Errorf("got:\n%s\nexpected:\n%s\n", s, ts) + } + } +} diff --git a/src/pkg/go/ast/resolve.go b/src/pkg/go/ast/resolve.go new file mode 100644 index 000000000..fddc3baab --- /dev/null +++ b/src/pkg/go/ast/resolve.go @@ -0,0 +1,188 @@ +// Copyright 2011 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. + +// This file implements NewPackage. + +package ast + +import ( + "fmt" + "go/scanner" + "go/token" + "os" +) + + +type pkgBuilder struct { + scanner.ErrorVector + fset *token.FileSet +} + + +func (p *pkgBuilder) error(pos token.Pos, msg string) { + p.Error(p.fset.Position(pos), msg) +} + + +func (p *pkgBuilder) errorf(pos token.Pos, format string, args ...interface{}) { + p.error(pos, fmt.Sprintf(format, args...)) +} + + +func (p *pkgBuilder) declare(scope, altScope *Scope, obj *Object) { + alt := scope.Insert(obj) + if alt == nil && altScope != nil { + // see if there is a conflicting declaration in altScope + alt = altScope.Lookup(obj.Name) + } + if alt != nil { + prevDecl := "" + if pos := alt.Pos(); pos.IsValid() { + prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.fset.Position(pos)) + } + p.error(obj.Pos(), fmt.Sprintf("%s redeclared in this block%s", obj.Name, prevDecl)) + } +} + + +func resolve(scope *Scope, ident *Ident) bool { + for ; scope != nil; scope = scope.Outer { + if obj := scope.Lookup(ident.Name); obj != nil { + ident.Obj = obj + return true + } + } + return false +} + + +// NewPackage uses an Importer to resolve imports. Given an importPath, +// an importer returns the imported package's name, its scope of exported +// objects, and an error, if any. +// +type Importer func(path string) (name string, scope *Scope, err os.Error) + + +// NewPackage creates a new Package node from a set of File nodes. It resolves +// unresolved identifiers across files and updates each file's Unresolved list +// accordingly. If a non-nil importer and universe scope are provided, they are +// used to resolve identifiers not declared in any of the package files. Any +// remaining unresolved identifiers are reported as undeclared. If the files +// belong to different packages, one package name is selected and files with +// different package name are reported and then ignored. +// The result is a package node and a scanner.ErrorList if there were errors. +// +func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer, universe *Scope) (*Package, os.Error) { + var p pkgBuilder + p.fset = fset + + // complete package scope + pkgName := "" + pkgScope := NewScope(universe) + for _, file := range files { + // package names must match + switch name := file.Name.Name; { + case pkgName == "": + pkgName = name + case name != pkgName: + p.errorf(file.Package, "package %s; expected %s", name, pkgName) + continue // ignore this file + } + + // collect top-level file objects in package scope + for _, obj := range file.Scope.Objects { + p.declare(pkgScope, nil, obj) + } + } + + // imports maps import paths to package names and scopes + // TODO(gri): Eventually we like to get to the import scope from + // a package object. Then we can have a map path -> Obj. + type importedPkg struct { + name string + scope *Scope + } + imports := make(map[string]*importedPkg) + + // complete file scopes with imports and resolve identifiers + for _, file := range files { + // ignore file if it belongs to a different package + // (error has already been reported) + if file.Name.Name != pkgName { + continue + } + + // build file scope by processing all imports + importErrors := false + fileScope := NewScope(pkgScope) + for _, spec := range file.Imports { + // add import to global map of imports + path := string(spec.Path.Value) + path = path[1 : len(path)-1] // strip ""'s + pkg := imports[path] + if pkg == nil { + if importer == nil { + importErrors = true + continue + } + name, scope, err := importer(path) + if err != nil { + p.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err) + importErrors = true + continue + } + pkg = &importedPkg{name, scope} + imports[path] = pkg + // TODO(gri) If a local package name != "." is provided, + // global identifier resolution could proceed even if the + // import failed. Consider adjusting the logic here a bit. + } + // local name overrides imported package name + name := pkg.name + if spec.Name != nil { + name = spec.Name.Name + } + // add import to file scope + if name == "." { + // merge imported scope with file scope + for _, obj := range pkg.scope.Objects { + p.declare(fileScope, pkgScope, obj) + } + } else { + // declare imported package object in file scope + obj := NewObj(Pkg, name) + obj.Decl = spec + p.declare(fileScope, pkgScope, obj) + } + } + + // resolve identifiers + if importErrors { + // don't use the universe scope without correct imports + // (objects in the universe may be shadowed by imports; + // with missing imports identifiers might get resolved + // wrongly) + pkgScope.Outer = nil + } + i := 0 + for _, ident := range file.Unresolved { + if !resolve(fileScope, ident) { + p.errorf(ident.Pos(), "undeclared name: %s", ident.Name) + file.Unresolved[i] = ident + i++ + } + + } + file.Unresolved = file.Unresolved[0:i] + pkgScope.Outer = universe // reset universe scope + } + + // collect all import paths and respective package scopes + importedScopes := make(map[string]*Scope) + for path, pkg := range imports { + importedScopes[path] = pkg.scope + } + + return &Package{pkgName, pkgScope, importedScopes, files}, p.GetError(scanner.Sorted) +} diff --git a/src/pkg/go/ast/scope.go b/src/pkg/go/ast/scope.go index 956a208ae..830d88aef 100644 --- a/src/pkg/go/ast/scope.go +++ b/src/pkg/go/ast/scope.go @@ -2,31 +2,31 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file implements scopes, the objects they contain, -// and object types. +// This file implements scopes and the objects they contain. package ast +import ( + "bytes" + "fmt" + "go/token" +) + + // A Scope maintains the set of named language entities declared // in the scope and a link to the immediately surrounding (outer) // scope. // type Scope struct { Outer *Scope - Objects []*Object // in declaration order - // Implementation note: In some cases (struct fields, - // function parameters) we need the source order of - // variables. Thus for now, we store scope entries - // in a linear list. If scopes become very large - // (say, for packages), we may need to change this - // to avoid slow lookups. + Objects map[string]*Object } // NewScope creates a new scope nested in the outer scope. func NewScope(outer *Scope) *Scope { - const n = 4 // initial scope capacity, must be > 0 - return &Scope{outer, make([]*Object, 0, n)} + const n = 4 // initial scope capacity + return &Scope{outer, make(map[string]*Object, n)} } @@ -34,73 +34,111 @@ func NewScope(outer *Scope) *Scope { // found in scope s, otherwise it returns nil. Outer scopes // are ignored. // -// Lookup always returns nil if name is "_", even if the scope -// contains objects with that name. -// func (s *Scope) Lookup(name string) *Object { - if name != "_" { - for _, obj := range s.Objects { - if obj.Name == name { - return obj - } - } - } - return nil + return s.Objects[name] } -// Insert attempts to insert a named object into the scope s. -// If the scope does not contain an object with that name yet -// or if the object is named "_", Insert inserts the object -// and returns it. Otherwise, Insert leaves the scope unchanged -// and returns the object found in the scope instead. +// Insert attempts to insert a named object obj into the scope s. +// If the scope already contains an object alt with the same name, +// Insert leaves the scope unchanged and returns alt. Otherwise +// it inserts obj and returns nil." // -func (s *Scope) Insert(obj *Object) *Object { - alt := s.Lookup(obj.Name) - if alt == nil { - s.append(obj) - alt = obj +func (s *Scope) Insert(obj *Object) (alt *Object) { + if alt = s.Objects[obj.Name]; alt == nil { + s.Objects[obj.Name] = obj } - return alt + return } -func (s *Scope) append(obj *Object) { - s.Objects = append(s.Objects, obj) +// Debugging support +func (s *Scope) String() string { + var buf bytes.Buffer + fmt.Fprintf(&buf, "scope %p {", s) + if s != nil && len(s.Objects) > 0 { + fmt.Fprintln(&buf) + for _, obj := range s.Objects { + fmt.Fprintf(&buf, "\t%s %s\n", obj.Kind, obj.Name) + } + } + fmt.Fprintf(&buf, "}\n") + return buf.String() } + // ---------------------------------------------------------------------------- // Objects -// An Object describes a language entity such as a package, -// constant, type, variable, or function (incl. methods). +// An Object describes a named language entity such as a package, +// constant, type, variable, function (incl. methods), or label. // type Object struct { - Kind Kind - Name string // declared name - Type *Type - Decl interface{} // corresponding Field, XxxSpec or FuncDecl - N int // value of iota for this declaration + Kind ObjKind + Name string // declared name + Decl interface{} // corresponding Field, XxxSpec, FuncDecl, or LabeledStmt; or nil + Type interface{} // place holder for type information; may be nil } // NewObj creates a new object of a given kind and name. -func NewObj(kind Kind, name string) *Object { +func NewObj(kind ObjKind, name string) *Object { return &Object{Kind: kind, Name: name} } -// Kind describes what an object represents. -type Kind int +// Pos computes the source position of the declaration of an object name. +// The result may be an invalid position if it cannot be computed +// (obj.Decl may be nil or not correct). +func (obj *Object) Pos() token.Pos { + name := obj.Name + switch d := obj.Decl.(type) { + case *Field: + for _, n := range d.Names { + if n.Name == name { + return n.Pos() + } + } + case *ImportSpec: + if d.Name != nil && d.Name.Name == name { + return d.Name.Pos() + } + return d.Path.Pos() + case *ValueSpec: + for _, n := range d.Names { + if n.Name == name { + return n.Pos() + } + } + case *TypeSpec: + if d.Name.Name == name { + return d.Name.Pos() + } + case *FuncDecl: + if d.Name.Name == name { + return d.Name.Pos() + } + case *LabeledStmt: + if d.Label.Name == name { + return d.Label.Pos() + } + } + return token.NoPos +} + + +// ObKind describes what an object represents. +type ObjKind int // The list of possible Object kinds. const ( - Bad Kind = iota // for error handling - Pkg // package - Con // constant - Typ // type - Var // variable - Fun // function or method + Bad ObjKind = iota // for error handling + Pkg // package + Con // constant + Typ // type + Var // variable + Fun // function or method + Lbl // label ) @@ -111,132 +149,8 @@ var objKindStrings = [...]string{ Typ: "type", Var: "var", Fun: "func", + Lbl: "label", } -func (kind Kind) String() string { return objKindStrings[kind] } - - -// IsExported returns whether obj is exported. -func (obj *Object) IsExported() bool { return IsExported(obj.Name) } - - -// ---------------------------------------------------------------------------- -// Types - -// A Type represents a Go type. -type Type struct { - Form Form - Obj *Object // corresponding type name, or nil - Scope *Scope // fields and methods, always present - N uint // basic type id, array length, number of function results, or channel direction - Key, Elt *Type // map key and array, pointer, slice, map or channel element - Params *Scope // function (receiver, input and result) parameters, tuple expressions (results of function calls), or nil - Expr Expr // corresponding AST expression -} - - -// NewType creates a new type of a given form. -func NewType(form Form) *Type { - return &Type{Form: form, Scope: NewScope(nil)} -} - - -// Form describes the form of a type. -type Form int - -// The list of possible type forms. -const ( - BadType Form = iota // for error handling - Unresolved // type not fully setup - Basic - Array - Struct - Pointer - Function - Method - Interface - Slice - Map - Channel - Tuple -) - - -var formStrings = [...]string{ - BadType: "badType", - Unresolved: "unresolved", - Basic: "basic", - Array: "array", - Struct: "struct", - Pointer: "pointer", - Function: "function", - Method: "method", - Interface: "interface", - Slice: "slice", - Map: "map", - Channel: "channel", - Tuple: "tuple", -} - - -func (form Form) String() string { return formStrings[form] } - - -// The list of basic type id's. -const ( - Bool = iota - Byte - Uint - Int - Float - Complex - Uintptr - String - - Uint8 - Uint16 - Uint32 - Uint64 - - Int8 - Int16 - Int32 - Int64 - - Float32 - Float64 - - Complex64 - Complex128 - - // TODO(gri) ideal types are missing -) - - -var BasicTypes = map[uint]string{ - Bool: "bool", - Byte: "byte", - Uint: "uint", - Int: "int", - Float: "float", - Complex: "complex", - Uintptr: "uintptr", - String: "string", - - Uint8: "uint8", - Uint16: "uint16", - Uint32: "uint32", - Uint64: "uint64", - - Int8: "int8", - Int16: "int16", - Int32: "int32", - Int64: "int64", - - Float32: "float32", - Float64: "float64", - - Complex64: "complex64", - Complex128: "complex128", -} +func (kind ObjKind) String() string { return objKindStrings[kind] } diff --git a/src/pkg/go/ast/walk.go b/src/pkg/go/ast/walk.go index 20c337c3b..95c4b3a35 100644 --- a/src/pkg/go/ast/walk.go +++ b/src/pkg/go/ast/walk.go @@ -234,7 +234,7 @@ func Walk(v Visitor, node Node) { } case *CaseClause: - walkExprList(v, n.Values) + walkExprList(v, n.List) walkStmtList(v, n.Body) case *SwitchStmt: @@ -246,12 +246,6 @@ func Walk(v Visitor, node Node) { } Walk(v, n.Body) - case *TypeCaseClause: - for _, x := range n.Types { - Walk(v, x) - } - walkStmtList(v, n.Body) - case *TypeSwitchStmt: if n.Init != nil { Walk(v, n.Init) diff --git a/src/pkg/go/doc/comment.go b/src/pkg/go/doc/comment.go index 9ff0bd536..f1ebfa97b 100644 --- a/src/pkg/go/doc/comment.go +++ b/src/pkg/go/doc/comment.go @@ -286,7 +286,7 @@ func unindent(block [][]byte) { // nor to have trailing spaces at the end of lines. // The comment markers have already been removed. // -// Turn each run of multiple \n into

+// Turn each run of multiple \n into

. // Turn each run of indented lines into a

 block without indent.
 //
 // URLs in the comment text are converted into links; if the URL also appears
diff --git a/src/pkg/go/doc/doc.go b/src/pkg/go/doc/doc.go
index e46857cb8..e7a8d3f63 100644
--- a/src/pkg/go/doc/doc.go
+++ b/src/pkg/go/doc/doc.go
@@ -66,7 +66,7 @@ func (doc *docReader) addDoc(comments *ast.CommentGroup) {
 	n2 := len(comments.List)
 	list := make([]*ast.Comment, n1+1+n2) // + 1 for separator line
 	copy(list, doc.doc.List)
-	list[n1] = &ast.Comment{token.NoPos, []byte("//")} // separator line
+	list[n1] = &ast.Comment{token.NoPos, "//"} // separator line
 	copy(list[n1+1:], comments.List)
 	doc.doc = &ast.CommentGroup{list}
 }
@@ -105,7 +105,7 @@ func baseTypeName(typ ast.Expr) string {
 		// if the type is not exported, the effect to
 		// a client is as if there were no type name
 		if t.IsExported() {
-			return string(t.Name)
+			return t.Name
 		}
 	case *ast.StarExpr:
 		return baseTypeName(t.X)
@@ -300,9 +300,9 @@ func (doc *docReader) addFile(src *ast.File) {
 	// collect BUG(...) comments
 	for _, c := range src.Comments {
 		text := c.List[0].Text
-		if m := bug_markers.FindIndex(text); m != nil {
+		if m := bug_markers.FindStringIndex(text); m != nil {
 			// found a BUG comment; maybe empty
-			if btxt := text[m[1]:]; bug_content.Match(btxt) {
+			if btxt := text[m[1]:]; bug_content.MatchString(btxt) {
 				// non-empty BUG comment; collect comment without BUG prefix
 				list := copyCommentList(c.List)
 				list[0].Text = text[m[1]:]
diff --git a/src/pkg/go/parser/interface.go b/src/pkg/go/parser/interface.go
index 6f35b495e..b4780e057 100644
--- a/src/pkg/go/parser/interface.go
+++ b/src/pkg/go/parser/interface.go
@@ -69,7 +69,7 @@ func ParseExpr(fset *token.FileSet, filename string, src interface{}) (ast.Expr,
 
 	var p parser
 	p.init(fset, filename, data, 0)
-	x := p.parseExpr()
+	x := p.parseRhs()
 	if p.tok == token.SEMICOLON {
 		p.next() // consume automatically inserted semicolon, if any
 	}
@@ -159,7 +159,8 @@ func ParseFiles(fset *token.FileSet, filenames []string, mode uint) (pkgs map[st
 			name := src.Name.Name
 			pkg, found := pkgs[name]
 			if !found {
-				pkg = &ast.Package{name, nil, make(map[string]*ast.File)}
+				// TODO(gri) Use NewPackage here; reconsider ParseFiles API.
+				pkg = &ast.Package{name, nil, nil, make(map[string]*ast.File)}
 				pkgs[name] = pkg
 			}
 			pkg.Files[filename] = src
@@ -182,7 +183,7 @@ func ParseFiles(fset *token.FileSet, filenames []string, mode uint) (pkgs map[st
 // error are returned.
 //
 func ParseDir(fset *token.FileSet, path string, filter func(*os.FileInfo) bool, mode uint) (map[string]*ast.Package, os.Error) {
-	fd, err := os.Open(path, os.O_RDONLY, 0)
+	fd, err := os.Open(path)
 	if err != nil {
 		return nil, err
 	}
diff --git a/src/pkg/go/parser/parser.go b/src/pkg/go/parser/parser.go
index 7c5843f36..84a0da6ae 100644
--- a/src/pkg/go/parser/parser.go
+++ b/src/pkg/go/parser/parser.go
@@ -17,10 +17,6 @@ import (
 )
 
 
-// noPos is used when there is no corresponding source position for a token.
-var noPos token.Position
-
-
 // The mode parameter to the Parse* functions is a set of flags (or 0).
 // They control the amount of source code parsed and other optional
 // parser functionality.
@@ -30,6 +26,7 @@ const (
 	ImportsOnly                        // parsing stops after import declarations
 	ParseComments                      // parse comments and add them to AST
 	Trace                              // print a trace of parsed productions
+	DeclarationErrors                  // report declaration errors
 )
 
 
@@ -46,16 +43,27 @@ type parser struct {
 
 	// Comments
 	comments    []*ast.CommentGroup
-	leadComment *ast.CommentGroup // the last lead comment
-	lineComment *ast.CommentGroup // the last line comment
+	leadComment *ast.CommentGroup // last lead comment
+	lineComment *ast.CommentGroup // last line comment
 
 	// Next token
 	pos token.Pos   // token position
 	tok token.Token // one token look-ahead
-	lit []byte      // token literal
+	lit string      // token literal
 
 	// Non-syntactic parser control
 	exprLev int // < 0: in control clause, >= 0: in expression
+
+	// Ordinary identifer scopes
+	pkgScope   *ast.Scope        // pkgScope.Outer == nil
+	topScope   *ast.Scope        // top-most scope; may be pkgScope
+	unresolved []*ast.Ident      // unresolved identifiers
+	imports    []*ast.ImportSpec // list of imports
+
+	// Label scope
+	// (maintained by open/close LabelScope)
+	labelScope  *ast.Scope     // label scope for current function
+	targetStack [][]*ast.Ident // stack of unresolved labels
 }
 
 
@@ -72,9 +80,133 @@ func scannerMode(mode uint) uint {
 func (p *parser) init(fset *token.FileSet, filename string, src []byte, mode uint) {
 	p.file = fset.AddFile(filename, fset.Base(), len(src))
 	p.scanner.Init(p.file, src, p, scannerMode(mode))
+
 	p.mode = mode
 	p.trace = mode&Trace != 0 // for convenience (p.trace is used frequently)
+
 	p.next()
+
+	// set up the pkgScope here (as opposed to in parseFile) because
+	// there are other parser entry points (ParseExpr, etc.)
+	p.openScope()
+	p.pkgScope = p.topScope
+
+	// for the same reason, set up a label scope
+	p.openLabelScope()
+}
+
+
+// ----------------------------------------------------------------------------
+// Scoping support
+
+func (p *parser) openScope() {
+	p.topScope = ast.NewScope(p.topScope)
+}
+
+
+func (p *parser) closeScope() {
+	p.topScope = p.topScope.Outer
+}
+
+
+func (p *parser) openLabelScope() {
+	p.labelScope = ast.NewScope(p.labelScope)
+	p.targetStack = append(p.targetStack, nil)
+}
+
+
+func (p *parser) closeLabelScope() {
+	// resolve labels
+	n := len(p.targetStack) - 1
+	scope := p.labelScope
+	for _, ident := range p.targetStack[n] {
+		ident.Obj = scope.Lookup(ident.Name)
+		if ident.Obj == nil && p.mode&DeclarationErrors != 0 {
+			p.error(ident.Pos(), fmt.Sprintf("label %s undefined", ident.Name))
+		}
+	}
+	// pop label scope
+	p.targetStack = p.targetStack[0:n]
+	p.labelScope = p.labelScope.Outer
+}
+
+
+func (p *parser) declare(decl interface{}, scope *ast.Scope, kind ast.ObjKind, idents ...*ast.Ident) {
+	for _, ident := range idents {
+		assert(ident.Obj == nil, "identifier already declared or resolved")
+		if ident.Name != "_" {
+			obj := ast.NewObj(kind, ident.Name)
+			// remember the corresponding declaration for redeclaration
+			// errors and global variable resolution/typechecking phase
+			obj.Decl = decl
+			if alt := scope.Insert(obj); alt != nil && p.mode&DeclarationErrors != 0 {
+				prevDecl := ""
+				if pos := alt.Pos(); pos.IsValid() {
+					prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.file.Position(pos))
+				}
+				p.error(ident.Pos(), fmt.Sprintf("%s redeclared in this block%s", ident.Name, prevDecl))
+			}
+			ident.Obj = obj
+		}
+	}
+}
+
+
+func (p *parser) shortVarDecl(idents []*ast.Ident) {
+	// Go spec: A short variable declaration may redeclare variables
+	// provided they were originally declared in the same block with
+	// the same type, and at least one of the non-blank variables is new.
+	n := 0 // number of new variables
+	for _, ident := range idents {
+		assert(ident.Obj == nil, "identifier already declared or resolved")
+		if ident.Name != "_" {
+			obj := ast.NewObj(ast.Var, ident.Name)
+			// short var declarations cannot have redeclaration errors
+			// and are not global => no need to remember the respective
+			// declaration
+			alt := p.topScope.Insert(obj)
+			if alt == nil {
+				n++ // new declaration
+				alt = obj
+			}
+			ident.Obj = alt
+		}
+	}
+	if n == 0 && p.mode&DeclarationErrors != 0 {
+		p.error(idents[0].Pos(), "no new variables on left side of :=")
+	}
+}
+
+
+// The unresolved object is a sentinel to mark identifiers that have been added
+// to the list of unresolved identifiers. The sentinel is only used for verifying
+// internal consistency.
+var unresolved = new(ast.Object)
+
+
+func (p *parser) resolve(x ast.Expr) {
+	// nothing to do if x is not an identifier or the blank identifier
+	ident, _ := x.(*ast.Ident)
+	if ident == nil {
+		return
+	}
+	assert(ident.Obj == nil, "identifier already declared or resolved")
+	if ident.Name == "_" {
+		return
+	}
+	// try to resolve the identifier
+	for s := p.topScope; s != nil; s = s.Outer {
+		if obj := s.Lookup(ident.Name); obj != nil {
+			ident.Obj = obj
+			return
+		}
+	}
+	// all local scopes are known, so any unresolved identifier
+	// must be found either in the file scope, package scope
+	// (perhaps in another file), or universe scope --- collect
+	// them so that they can be resolved later
+	ident.Obj = unresolved
+	p.unresolved = append(p.unresolved, ident)
 }
 
 
@@ -120,7 +252,7 @@ func (p *parser) next0() {
 		s := p.tok.String()
 		switch {
 		case p.tok.IsLiteral():
-			p.printTrace(s, string(p.lit))
+			p.printTrace(s, p.lit)
 		case p.tok.IsOperator(), p.tok.IsKeyword():
 			p.printTrace("\"" + s + "\"")
 		default:
@@ -137,8 +269,9 @@ func (p *parser) consumeComment() (comment *ast.Comment, endline int) {
 	// Scan the comment for '\n' chars and adjust endline accordingly.
 	endline = p.file.Line(p.pos)
 	if p.lit[1] == '*' {
-		for _, b := range p.lit {
-			if b == '\n' {
+		// don't use range here - no need to decode Unicode code points
+		for i := 0; i < len(p.lit); i++ {
+			if p.lit[i] == '\n' {
 				endline++
 			}
 		}
@@ -199,7 +332,7 @@ func (p *parser) next() {
 		var endline int
 
 		if p.file.Line(p.pos) == line {
-			// The comment is on same line as previous token; it
+			// The comment is on same line as the previous token; it
 			// cannot be a lead comment but may be a line comment.
 			comment, endline = p.consumeCommentGroup()
 			if p.file.Line(p.pos) != endline {
@@ -239,7 +372,7 @@ func (p *parser) errorExpected(pos token.Pos, msg string) {
 		} else {
 			msg += ", found '" + p.tok.String() + "'"
 			if p.tok.IsLiteral() {
-				msg += " " + string(p.lit)
+				msg += " " + p.lit
 			}
 		}
 	}
@@ -264,6 +397,13 @@ func (p *parser) expectSemi() {
 }
 
 
+func assert(cond bool, msg string) {
+	if !cond {
+		panic("go/parser internal error: " + msg)
+	}
+}
+
+
 // ----------------------------------------------------------------------------
 // Identifiers
 
@@ -271,7 +411,7 @@ func (p *parser) parseIdent() *ast.Ident {
 	pos := p.pos
 	name := "_"
 	if p.tok == token.IDENT {
-		name = string(p.lit)
+		name = p.lit
 		p.next()
 	} else {
 		p.expect(token.IDENT) // use expect() error handling
@@ -298,21 +438,51 @@ func (p *parser) parseIdentList() (list []*ast.Ident) {
 // ----------------------------------------------------------------------------
 // Common productions
 
-func (p *parser) parseExprList() (list []ast.Expr) {
+// If lhs is set, result list elements which are identifiers are not resolved.
+func (p *parser) parseExprList(lhs bool) (list []ast.Expr) {
 	if p.trace {
 		defer un(trace(p, "ExpressionList"))
 	}
 
-	list = append(list, p.parseExpr())
+	list = append(list, p.parseExpr(lhs))
 	for p.tok == token.COMMA {
 		p.next()
-		list = append(list, p.parseExpr())
+		list = append(list, p.parseExpr(lhs))
 	}
 
 	return
 }
 
 
+func (p *parser) parseLhsList() []ast.Expr {
+	list := p.parseExprList(true)
+	switch p.tok {
+	case token.DEFINE:
+		// lhs of a short variable declaration
+		p.shortVarDecl(p.makeIdentList(list))
+	case token.COLON:
+		// lhs of a label declaration or a communication clause of a select
+		// statement (parseLhsList is not called when parsing the case clause
+		// of a switch statement):
+		// - labels are declared by the caller of parseLhsList
+		// - for communication clauses, if there is a stand-alone identifier
+		//   followed by a colon, we have a syntax error; there is no need
+		//   to resolve the identifier in that case
+	default:
+		// identifiers must be declared elsewhere
+		for _, x := range list {
+			p.resolve(x)
+		}
+	}
+	return list
+}
+
+
+func (p *parser) parseRhsList() []ast.Expr {
+	return p.parseExprList(false)
+}
+
+
 // ----------------------------------------------------------------------------
 // Types
 
@@ -334,28 +504,24 @@ func (p *parser) parseType() ast.Expr {
 }
 
 
-func (p *parser) parseQualifiedIdent() ast.Expr {
+// If the result is an identifier, it is not resolved.
+func (p *parser) parseTypeName() ast.Expr {
 	if p.trace {
-		defer un(trace(p, "QualifiedIdent"))
+		defer un(trace(p, "TypeName"))
 	}
 
-	var x ast.Expr = p.parseIdent()
+	ident := p.parseIdent()
+	// don't resolve ident yet - it may be a parameter or field name
+
 	if p.tok == token.PERIOD {
-		// first identifier is a package identifier
+		// ident is a package name
 		p.next()
+		p.resolve(ident)
 		sel := p.parseIdent()
-		x = &ast.SelectorExpr{x, sel}
-	}
-	return x
-}
-
-
-func (p *parser) parseTypeName() ast.Expr {
-	if p.trace {
-		defer un(trace(p, "TypeName"))
+		return &ast.SelectorExpr{ident, sel}
 	}
 
-	return p.parseQualifiedIdent()
+	return ident
 }
 
 
@@ -370,7 +536,7 @@ func (p *parser) parseArrayType(ellipsisOk bool) ast.Expr {
 		len = &ast.Ellipsis{p.pos, nil}
 		p.next()
 	} else if p.tok != token.RBRACK {
-		len = p.parseExpr()
+		len = p.parseRhs()
 	}
 	p.expect(token.RBRACK)
 	elt := p.parseType()
@@ -394,7 +560,7 @@ func (p *parser) makeIdentList(list []ast.Expr) []*ast.Ident {
 }
 
 
-func (p *parser) parseFieldDecl() *ast.Field {
+func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
 	if p.trace {
 		defer un(trace(p, "FieldDecl"))
 	}
@@ -419,6 +585,7 @@ func (p *parser) parseFieldDecl() *ast.Field {
 	} else {
 		// ["*"] TypeName (AnonymousField)
 		typ = list[0] // we always have at least one element
+		p.resolve(typ)
 		if n := len(list); n > 1 || !isTypeName(deref(typ)) {
 			pos := typ.Pos()
 			p.errorExpected(pos, "anonymous field")
@@ -426,9 +593,12 @@ func (p *parser) parseFieldDecl() *ast.Field {
 		}
 	}
 
-	p.expectSemi()
+	p.expectSemi() // call before accessing p.linecomment
+
+	field := &ast.Field{doc, idents, typ, tag, p.lineComment}
+	p.declare(field, scope, ast.Var, idents...)
 
-	return &ast.Field{doc, idents, typ, tag, p.lineComment}
+	return field
 }
 
 
@@ -439,15 +609,17 @@ func (p *parser) parseStructType() *ast.StructType {
 
 	pos := p.expect(token.STRUCT)
 	lbrace := p.expect(token.LBRACE)
+	scope := ast.NewScope(nil) // struct scope
 	var list []*ast.Field
 	for p.tok == token.IDENT || p.tok == token.MUL || p.tok == token.LPAREN {
 		// a field declaration cannot start with a '(' but we accept
 		// it here for more robust parsing and better error messages
 		// (parseFieldDecl will check and complain if necessary)
-		list = append(list, p.parseFieldDecl())
+		list = append(list, p.parseFieldDecl(scope))
 	}
 	rbrace := p.expect(token.RBRACE)
 
+	// TODO(gri): store struct scope in AST
 	return &ast.StructType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
 }
 
@@ -468,7 +640,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
 	if isParam && p.tok == token.ELLIPSIS {
 		pos := p.pos
 		p.next()
-		typ := p.tryType() // don't use parseType so we can provide better error message
+		typ := p.tryIdentOrType(isParam) // don't use parseType so we can provide better error message
 		if typ == nil {
 			p.error(pos, "'...' parameter is missing type")
 			typ = &ast.BadExpr{pos, p.pos}
@@ -478,7 +650,7 @@ func (p *parser) tryVarType(isParam bool) ast.Expr {
 		}
 		return &ast.Ellipsis{pos, typ}
 	}
-	return p.tryType()
+	return p.tryIdentOrType(false)
 }
 
 
@@ -514,12 +686,15 @@ func (p *parser) parseVarList(isParam bool) (list []ast.Expr, typ ast.Expr) {
 
 	// if we had a list of identifiers, it must be followed by a type
 	typ = p.tryVarType(isParam)
+	if typ != nil {
+		p.resolve(typ)
+	}
 
 	return
 }
 
 
-func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
+func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params []*ast.Field) {
 	if p.trace {
 		defer un(trace(p, "ParameterList"))
 	}
@@ -528,7 +703,11 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
 	if typ != nil {
 		// IdentifierList Type
 		idents := p.makeIdentList(list)
-		params = append(params, &ast.Field{nil, idents, typ, nil, nil})
+		field := &ast.Field{nil, idents, typ, nil, nil}
+		params = append(params, field)
+		// Go spec: The scope of an identifier denoting a function
+		// parameter or result variable is the function body.
+		p.declare(field, scope, ast.Var, idents...)
 		if p.tok == token.COMMA {
 			p.next()
 		}
@@ -536,7 +715,11 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
 		for p.tok != token.RPAREN && p.tok != token.EOF {
 			idents := p.parseIdentList()
 			typ := p.parseVarType(ellipsisOk)
-			params = append(params, &ast.Field{nil, idents, typ, nil, nil})
+			field := &ast.Field{nil, idents, typ, nil, nil}
+			params = append(params, field)
+			// Go spec: The scope of an identifier denoting a function
+			// parameter or result variable is the function body.
+			p.declare(field, scope, ast.Var, idents...)
 			if p.tok != token.COMMA {
 				break
 			}
@@ -547,6 +730,7 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
 		// Type { "," Type } (anonymous parameters)
 		params = make([]*ast.Field, len(list))
 		for i, x := range list {
+			p.resolve(x)
 			params[i] = &ast.Field{Type: x}
 		}
 	}
@@ -555,7 +739,7 @@ func (p *parser) parseParameterList(ellipsisOk bool) (params []*ast.Field) {
 }
 
 
-func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList {
+func (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) *ast.FieldList {
 	if p.trace {
 		defer un(trace(p, "Parameters"))
 	}
@@ -563,7 +747,7 @@ func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList {
 	var params []*ast.Field
 	lparen := p.expect(token.LPAREN)
 	if p.tok != token.RPAREN {
-		params = p.parseParameterList(ellipsisOk)
+		params = p.parseParameterList(scope, ellipsisOk)
 	}
 	rparen := p.expect(token.RPAREN)
 
@@ -571,13 +755,13 @@ func (p *parser) parseParameters(ellipsisOk bool) *ast.FieldList {
 }
 
 
-func (p *parser) parseResult() *ast.FieldList {
+func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList {
 	if p.trace {
 		defer un(trace(p, "Result"))
 	}
 
 	if p.tok == token.LPAREN {
-		return p.parseParameters(false)
+		return p.parseParameters(scope, false)
 	}
 
 	typ := p.tryType()
@@ -591,31 +775,32 @@ func (p *parser) parseResult() *ast.FieldList {
 }
 
 
-func (p *parser) parseSignature() (params, results *ast.FieldList) {
+func (p *parser) parseSignature(scope *ast.Scope) (params, results *ast.FieldList) {
 	if p.trace {
 		defer un(trace(p, "Signature"))
 	}
 
-	params = p.parseParameters(true)
-	results = p.parseResult()
+	params = p.parseParameters(scope, true)
+	results = p.parseResult(scope)
 
 	return
 }
 
 
-func (p *parser) parseFuncType() *ast.FuncType {
+func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) {
 	if p.trace {
 		defer un(trace(p, "FuncType"))
 	}
 
 	pos := p.expect(token.FUNC)
-	params, results := p.parseSignature()
+	scope := ast.NewScope(p.topScope) // function scope
+	params, results := p.parseSignature(scope)
 
-	return &ast.FuncType{pos, params, results}
+	return &ast.FuncType{pos, params, results}, scope
 }
 
 
-func (p *parser) parseMethodSpec() *ast.Field {
+func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {
 	if p.trace {
 		defer un(trace(p, "MethodSpec"))
 	}
@@ -623,19 +808,23 @@ func (p *parser) parseMethodSpec() *ast.Field {
 	doc := p.leadComment
 	var idents []*ast.Ident
 	var typ ast.Expr
-	x := p.parseQualifiedIdent()
+	x := p.parseTypeName()
 	if ident, isIdent := x.(*ast.Ident); isIdent && p.tok == token.LPAREN {
 		// method
 		idents = []*ast.Ident{ident}
-		params, results := p.parseSignature()
+		scope := ast.NewScope(nil) // method scope
+		params, results := p.parseSignature(scope)
 		typ = &ast.FuncType{token.NoPos, params, results}
 	} else {
 		// embedded interface
 		typ = x
 	}
-	p.expectSemi()
+	p.expectSemi() // call before accessing p.linecomment
 
-	return &ast.Field{doc, idents, typ, nil, p.lineComment}
+	spec := &ast.Field{doc, idents, typ, nil, p.lineComment}
+	p.declare(spec, scope, ast.Fun, idents...)
+
+	return spec
 }
 
 
@@ -646,12 +835,14 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType {
 
 	pos := p.expect(token.INTERFACE)
 	lbrace := p.expect(token.LBRACE)
+	scope := ast.NewScope(nil) // interface scope
 	var list []*ast.Field
 	for p.tok == token.IDENT {
-		list = append(list, p.parseMethodSpec())
+		list = append(list, p.parseMethodSpec(scope))
 	}
 	rbrace := p.expect(token.RBRACE)
 
+	// TODO(gri): store interface scope in AST
 	return &ast.InterfaceType{pos, &ast.FieldList{lbrace, list, rbrace}, false}
 }
 
@@ -695,7 +886,8 @@ func (p *parser) parseChanType() *ast.ChanType {
 }
 
 
-func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
+// If the result is an identifier, it is not resolved.
+func (p *parser) tryIdentOrType(ellipsisOk bool) ast.Expr {
 	switch p.tok {
 	case token.IDENT:
 		return p.parseTypeName()
@@ -706,7 +898,8 @@ func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
 	case token.MUL:
 		return p.parsePointerType()
 	case token.FUNC:
-		return p.parseFuncType()
+		typ, _ := p.parseFuncType()
+		return typ
 	case token.INTERFACE:
 		return p.parseInterfaceType()
 	case token.MAP:
@@ -726,7 +919,13 @@ func (p *parser) tryRawType(ellipsisOk bool) ast.Expr {
 }
 
 
-func (p *parser) tryType() ast.Expr { return p.tryRawType(false) }
+func (p *parser) tryType() ast.Expr {
+	typ := p.tryIdentOrType(false)
+	if typ != nil {
+		p.resolve(typ)
+	}
+	return typ
+}
 
 
 // ----------------------------------------------------------------------------
@@ -745,13 +944,17 @@ func (p *parser) parseStmtList() (list []ast.Stmt) {
 }
 
 
-func (p *parser) parseBody() *ast.BlockStmt {
+func (p *parser) parseBody(scope *ast.Scope) *ast.BlockStmt {
 	if p.trace {
 		defer un(trace(p, "Body"))
 	}
 
 	lbrace := p.expect(token.LBRACE)
+	p.topScope = scope // open function scope
+	p.openLabelScope()
 	list := p.parseStmtList()
+	p.closeLabelScope()
+	p.closeScope()
 	rbrace := p.expect(token.RBRACE)
 
 	return &ast.BlockStmt{lbrace, list, rbrace}
@@ -764,7 +967,9 @@ func (p *parser) parseBlockStmt() *ast.BlockStmt {
 	}
 
 	lbrace := p.expect(token.LBRACE)
+	p.openScope()
 	list := p.parseStmtList()
+	p.closeScope()
 	rbrace := p.expect(token.RBRACE)
 
 	return &ast.BlockStmt{lbrace, list, rbrace}
@@ -779,14 +984,14 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr {
 		defer un(trace(p, "FuncTypeOrLit"))
 	}
 
-	typ := p.parseFuncType()
+	typ, scope := p.parseFuncType()
 	if p.tok != token.LBRACE {
 		// function type only
 		return typ
 	}
 
 	p.exprLev++
-	body := p.parseBody()
+	body := p.parseBody(scope)
 	p.exprLev--
 
 	return &ast.FuncLit{typ, body}
@@ -795,15 +1000,20 @@ func (p *parser) parseFuncTypeOrLit() ast.Expr {
 
 // parseOperand may return an expression or a raw type (incl. array
 // types of the form [...]T. Callers must verify the result.
+// If lhs is set and the result is an identifier, it is not resolved.
 //
-func (p *parser) parseOperand() ast.Expr {
+func (p *parser) parseOperand(lhs bool) ast.Expr {
 	if p.trace {
 		defer un(trace(p, "Operand"))
 	}
 
 	switch p.tok {
 	case token.IDENT:
-		return p.parseIdent()
+		x := p.parseIdent()
+		if !lhs {
+			p.resolve(x)
+		}
+		return x
 
 	case token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING:
 		x := &ast.BasicLit{p.pos, p.tok, p.lit}
@@ -814,7 +1024,7 @@ func (p *parser) parseOperand() ast.Expr {
 		lparen := p.pos
 		p.next()
 		p.exprLev++
-		x := p.parseExpr()
+		x := p.parseRhs()
 		p.exprLev--
 		rparen := p.expect(token.RPAREN)
 		return &ast.ParenExpr{lparen, x, rparen}
@@ -823,9 +1033,11 @@ func (p *parser) parseOperand() ast.Expr {
 		return p.parseFuncTypeOrLit()
 
 	default:
-		t := p.tryRawType(true) // could be type for composite literal or conversion
-		if t != nil {
-			return t
+		if typ := p.tryIdentOrType(true); typ != nil {
+			// could be type for composite literal or conversion
+			_, isIdent := typ.(*ast.Ident)
+			assert(!isIdent, "type cannot be identifier")
+			return typ
 		}
 	}
 
@@ -836,19 +1048,22 @@ func (p *parser) parseOperand() ast.Expr {
 }
 
 
-func (p *parser) parseSelectorOrTypeAssertion(x ast.Expr) ast.Expr {
+func (p *parser) parseSelector(x ast.Expr) ast.Expr {
 	if p.trace {
-		defer un(trace(p, "SelectorOrTypeAssertion"))
+		defer un(trace(p, "Selector"))
 	}
 
-	p.expect(token.PERIOD)
-	if p.tok == token.IDENT {
-		// selector
-		sel := p.parseIdent()
-		return &ast.SelectorExpr{x, sel}
+	sel := p.parseIdent()
+
+	return &ast.SelectorExpr{x, sel}
+}
+
+
+func (p *parser) parseTypeAssertion(x ast.Expr) ast.Expr {
+	if p.trace {
+		defer un(trace(p, "TypeAssertion"))
 	}
 
-	// type assertion
 	p.expect(token.LPAREN)
 	var typ ast.Expr
 	if p.tok == token.TYPE {
@@ -873,13 +1088,13 @@ func (p *parser) parseIndexOrSlice(x ast.Expr) ast.Expr {
 	var low, high ast.Expr
 	isSlice := false
 	if p.tok != token.COLON {
-		low = p.parseExpr()
+		low = p.parseRhs()
 	}
 	if p.tok == token.COLON {
 		isSlice = true
 		p.next()
 		if p.tok != token.RBRACK {
-			high = p.parseExpr()
+			high = p.parseRhs()
 		}
 	}
 	p.exprLev--
@@ -902,7 +1117,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
 	var list []ast.Expr
 	var ellipsis token.Pos
 	for p.tok != token.RPAREN && p.tok != token.EOF && !ellipsis.IsValid() {
-		list = append(list, p.parseExpr())
+		list = append(list, p.parseRhs())
 		if p.tok == token.ELLIPSIS {
 			ellipsis = p.pos
 			p.next()
@@ -928,12 +1143,16 @@ func (p *parser) parseElement(keyOk bool) ast.Expr {
 		return p.parseLiteralValue(nil)
 	}
 
-	x := p.parseExpr()
-	if keyOk && p.tok == token.COLON {
-		colon := p.pos
-		p.next()
-		x = &ast.KeyValueExpr{x, colon, p.parseElement(false)}
+	x := p.parseExpr(keyOk) // don't resolve if map key
+	if keyOk {
+		if p.tok == token.COLON {
+			colon := p.pos
+			p.next()
+			return &ast.KeyValueExpr{x, colon, p.parseElement(false)}
+		}
+		p.resolve(x) // not a map key
 	}
+
 	return x
 }
 
@@ -1085,23 +1304,47 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
 }
 
 
-func (p *parser) parsePrimaryExpr() ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parsePrimaryExpr(lhs bool) ast.Expr {
 	if p.trace {
 		defer un(trace(p, "PrimaryExpr"))
 	}
 
-	x := p.parseOperand()
+	x := p.parseOperand(lhs)
 L:
 	for {
 		switch p.tok {
 		case token.PERIOD:
-			x = p.parseSelectorOrTypeAssertion(p.checkExpr(x))
+			p.next()
+			if lhs {
+				p.resolve(x)
+			}
+			switch p.tok {
+			case token.IDENT:
+				x = p.parseSelector(p.checkExpr(x))
+			case token.LPAREN:
+				x = p.parseTypeAssertion(p.checkExpr(x))
+			default:
+				pos := p.pos
+				p.next() // make progress
+				p.errorExpected(pos, "selector or type assertion")
+				x = &ast.BadExpr{pos, p.pos}
+			}
 		case token.LBRACK:
+			if lhs {
+				p.resolve(x)
+			}
 			x = p.parseIndexOrSlice(p.checkExpr(x))
 		case token.LPAREN:
+			if lhs {
+				p.resolve(x)
+			}
 			x = p.parseCallOrConversion(p.checkExprOrType(x))
 		case token.LBRACE:
 			if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
+				if lhs {
+					p.resolve(x)
+				}
 				x = p.parseLiteralValue(x)
 			} else {
 				break L
@@ -1109,13 +1352,15 @@ L:
 		default:
 			break L
 		}
+		lhs = false // no need to try to resolve again
 	}
 
 	return x
 }
 
 
-func (p *parser) parseUnaryExpr() ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parseUnaryExpr(lhs bool) ast.Expr {
 	if p.trace {
 		defer un(trace(p, "UnaryExpr"))
 	}
@@ -1124,7 +1369,7 @@ func (p *parser) parseUnaryExpr() ast.Expr {
 	case token.ADD, token.SUB, token.NOT, token.XOR, token.AND, token.RANGE:
 		pos, op := p.pos, p.tok
 		p.next()
-		x := p.parseUnaryExpr()
+		x := p.parseUnaryExpr(false)
 		return &ast.UnaryExpr{pos, op, p.checkExpr(x)}
 
 	case token.ARROW:
@@ -1137,32 +1382,37 @@ func (p *parser) parseUnaryExpr() ast.Expr {
 			return &ast.ChanType{pos, ast.RECV, value}
 		}
 
-		x := p.parseUnaryExpr()
+		x := p.parseUnaryExpr(false)
 		return &ast.UnaryExpr{pos, token.ARROW, p.checkExpr(x)}
 
 	case token.MUL:
 		// pointer type or unary "*" expression
 		pos := p.pos
 		p.next()
-		x := p.parseUnaryExpr()
+		x := p.parseUnaryExpr(false)
 		return &ast.StarExpr{pos, p.checkExprOrType(x)}
 	}
 
-	return p.parsePrimaryExpr()
+	return p.parsePrimaryExpr(lhs)
 }
 
 
-func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
+// If lhs is set and the result is an identifier, it is not resolved.
+func (p *parser) parseBinaryExpr(lhs bool, prec1 int) ast.Expr {
 	if p.trace {
 		defer un(trace(p, "BinaryExpr"))
 	}
 
-	x := p.parseUnaryExpr()
+	x := p.parseUnaryExpr(lhs)
 	for prec := p.tok.Precedence(); prec >= prec1; prec-- {
 		for p.tok.Precedence() == prec {
 			pos, op := p.pos, p.tok
 			p.next()
-			y := p.parseBinaryExpr(prec + 1)
+			if lhs {
+				p.resolve(x)
+				lhs = false
+			}
+			y := p.parseBinaryExpr(false, prec+1)
 			x = &ast.BinaryExpr{p.checkExpr(x), pos, op, p.checkExpr(y)}
 		}
 	}
@@ -1171,14 +1421,20 @@ func (p *parser) parseBinaryExpr(prec1 int) ast.Expr {
 }
 
 
+// If lhs is set and the result is an identifier, it is not resolved.
 // TODO(gri): parseExpr may return a type or even a raw type ([..]int) -
 //            should reject when a type/raw type is obviously not allowed
-func (p *parser) parseExpr() ast.Expr {
+func (p *parser) parseExpr(lhs bool) ast.Expr {
 	if p.trace {
 		defer un(trace(p, "Expression"))
 	}
 
-	return p.parseBinaryExpr(token.LowestPrec + 1)
+	return p.parseBinaryExpr(lhs, token.LowestPrec+1)
+}
+
+
+func (p *parser) parseRhs() ast.Expr {
+	return p.parseExpr(false)
 }
 
 
@@ -1190,7 +1446,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
 		defer un(trace(p, "SimpleStmt"))
 	}
 
-	x := p.parseExprList()
+	x := p.parseLhsList()
 
 	switch p.tok {
 	case
@@ -1201,7 +1457,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
 		// assignment statement
 		pos, tok := p.pos, p.tok
 		p.next()
-		y := p.parseExprList()
+		y := p.parseRhsList()
 		return &ast.AssignStmt{x, pos, tok, y}
 	}
 
@@ -1216,7 +1472,12 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
 		colon := p.pos
 		p.next()
 		if label, isIdent := x[0].(*ast.Ident); labelOk && isIdent {
-			return &ast.LabeledStmt{label, colon, p.parseStmt()}
+			// Go spec: The scope of a label is the body of the function
+			// in which it is declared and excludes the body of any nested
+			// function.
+			stmt := &ast.LabeledStmt{label, colon, p.parseStmt()}
+			p.declare(stmt, p.labelScope, ast.Lbl, label)
+			return stmt
 		}
 		p.error(x[0].Pos(), "illegal label declaration")
 		return &ast.BadStmt{x[0].Pos(), colon + 1}
@@ -1225,7 +1486,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
 		// send statement
 		arrow := p.pos
 		p.next() // consume "<-"
-		y := p.parseExpr()
+		y := p.parseRhs()
 		return &ast.SendStmt{x[0], arrow, y}
 
 	case token.INC, token.DEC:
@@ -1241,7 +1502,7 @@ func (p *parser) parseSimpleStmt(labelOk bool) ast.Stmt {
 
 
 func (p *parser) parseCallExpr() *ast.CallExpr {
-	x := p.parseExpr()
+	x := p.parseRhs()
 	if call, isCall := x.(*ast.CallExpr); isCall {
 		return call
 	}
@@ -1291,7 +1552,7 @@ func (p *parser) parseReturnStmt() *ast.ReturnStmt {
 	p.expect(token.RETURN)
 	var x []ast.Expr
 	if p.tok != token.SEMICOLON && p.tok != token.RBRACE {
-		x = p.parseExprList()
+		x = p.parseRhsList()
 	}
 	p.expectSemi()
 
@@ -1304,14 +1565,17 @@ func (p *parser) parseBranchStmt(tok token.Token) *ast.BranchStmt {
 		defer un(trace(p, "BranchStmt"))
 	}
 
-	s := &ast.BranchStmt{p.pos, tok, nil}
-	p.expect(tok)
+	pos := p.expect(tok)
+	var label *ast.Ident
 	if tok != token.FALLTHROUGH && p.tok == token.IDENT {
-		s.Label = p.parseIdent()
+		label = p.parseIdent()
+		// add to list of unresolved targets
+		n := len(p.targetStack) - 1
+		p.targetStack[n] = append(p.targetStack[n], label)
 	}
 	p.expectSemi()
 
-	return s
+	return &ast.BranchStmt{pos, tok, label}
 }
 
 
@@ -1333,6 +1597,8 @@ func (p *parser) parseIfStmt() *ast.IfStmt {
 	}
 
 	pos := p.expect(token.IF)
+	p.openScope()
+	defer p.closeScope()
 
 	var s ast.Stmt
 	var x ast.Expr
@@ -1341,12 +1607,12 @@ func (p *parser) parseIfStmt() *ast.IfStmt {
 		p.exprLev = -1
 		if p.tok == token.SEMICOLON {
 			p.next()
-			x = p.parseExpr()
+			x = p.parseRhs()
 		} else {
 			s = p.parseSimpleStmt(false)
 			if p.tok == token.SEMICOLON {
 				p.next()
-				x = p.parseExpr()
+				x = p.parseRhs()
 			} else {
 				x = p.makeExpr(s)
 				s = nil
@@ -1368,28 +1634,6 @@ func (p *parser) parseIfStmt() *ast.IfStmt {
 }
 
 
-func (p *parser) parseCaseClause() *ast.CaseClause {
-	if p.trace {
-		defer un(trace(p, "CaseClause"))
-	}
-
-	// SwitchCase
-	pos := p.pos
-	var x []ast.Expr
-	if p.tok == token.CASE {
-		p.next()
-		x = p.parseExprList()
-	} else {
-		p.expect(token.DEFAULT)
-	}
-
-	colon := p.expect(token.COLON)
-	body := p.parseStmtList()
-
-	return &ast.CaseClause{pos, x, colon, body}
-}
-
-
 func (p *parser) parseTypeList() (list []ast.Expr) {
 	if p.trace {
 		defer un(trace(p, "TypeList"))
@@ -1405,25 +1649,30 @@ func (p *parser) parseTypeList() (list []ast.Expr) {
 }
 
 
-func (p *parser) parseTypeCaseClause() *ast.TypeCaseClause {
+func (p *parser) parseCaseClause(exprSwitch bool) *ast.CaseClause {
 	if p.trace {
-		defer un(trace(p, "TypeCaseClause"))
+		defer un(trace(p, "CaseClause"))
 	}
 
-	// TypeSwitchCase
 	pos := p.pos
-	var types []ast.Expr
+	var list []ast.Expr
 	if p.tok == token.CASE {
 		p.next()
-		types = p.parseTypeList()
+		if exprSwitch {
+			list = p.parseRhsList()
+		} else {
+			list = p.parseTypeList()
+		}
 	} else {
 		p.expect(token.DEFAULT)
 	}
 
 	colon := p.expect(token.COLON)
+	p.openScope()
 	body := p.parseStmtList()
+	p.closeScope()
 
-	return &ast.TypeCaseClause{pos, types, colon, body}
+	return &ast.CaseClause{pos, list, colon, body}
 }
 
 
@@ -1447,6 +1696,8 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
 	}
 
 	pos := p.expect(token.SWITCH)
+	p.openScope()
+	defer p.closeScope()
 
 	var s1, s2 ast.Stmt
 	if p.tok != token.LBRACE {
@@ -1466,28 +1717,21 @@ func (p *parser) parseSwitchStmt() ast.Stmt {
 		p.exprLev = prevLev
 	}
 
-	if isExprSwitch(s2) {
-		lbrace := p.expect(token.LBRACE)
-		var list []ast.Stmt
-		for p.tok == token.CASE || p.tok == token.DEFAULT {
-			list = append(list, p.parseCaseClause())
-		}
-		rbrace := p.expect(token.RBRACE)
-		body := &ast.BlockStmt{lbrace, list, rbrace}
-		p.expectSemi()
-		return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body}
-	}
-
-	// type switch
-	// TODO(gri): do all the checks!
+	exprSwitch := isExprSwitch(s2)
 	lbrace := p.expect(token.LBRACE)
 	var list []ast.Stmt
 	for p.tok == token.CASE || p.tok == token.DEFAULT {
-		list = append(list, p.parseTypeCaseClause())
+		list = append(list, p.parseCaseClause(exprSwitch))
 	}
 	rbrace := p.expect(token.RBRACE)
 	p.expectSemi()
 	body := &ast.BlockStmt{lbrace, list, rbrace}
+
+	if exprSwitch {
+		return &ast.SwitchStmt{pos, s1, p.makeExpr(s2), body}
+	}
+	// type switch
+	// TODO(gri): do all the checks!
 	return &ast.TypeSwitchStmt{pos, s1, s2, body}
 }
 
@@ -1497,12 +1741,12 @@ func (p *parser) parseCommClause() *ast.CommClause {
 		defer un(trace(p, "CommClause"))
 	}
 
-	// CommCase
+	p.openScope()
 	pos := p.pos
 	var comm ast.Stmt
 	if p.tok == token.CASE {
 		p.next()
-		lhs := p.parseExprList()
+		lhs := p.parseLhsList()
 		if p.tok == token.ARROW {
 			// SendStmt
 			if len(lhs) > 1 {
@@ -1511,14 +1755,14 @@ func (p *parser) parseCommClause() *ast.CommClause {
 			}
 			arrow := p.pos
 			p.next()
-			rhs := p.parseExpr()
+			rhs := p.parseRhs()
 			comm = &ast.SendStmt{lhs[0], arrow, rhs}
 		} else {
 			// RecvStmt
 			pos := p.pos
 			tok := p.tok
 			var rhs ast.Expr
-			if p.tok == token.ASSIGN || p.tok == token.DEFINE {
+			if tok == token.ASSIGN || tok == token.DEFINE {
 				// RecvStmt with assignment
 				if len(lhs) > 2 {
 					p.errorExpected(lhs[0].Pos(), "1 or 2 expressions")
@@ -1526,7 +1770,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
 					lhs = lhs[0:2]
 				}
 				p.next()
-				rhs = p.parseExpr()
+				rhs = p.parseRhs()
 			} else {
 				// rhs must be single receive operation
 				if len(lhs) > 1 {
@@ -1552,6 +1796,7 @@ func (p *parser) parseCommClause() *ast.CommClause {
 
 	colon := p.expect(token.COLON)
 	body := p.parseStmtList()
+	p.closeScope()
 
 	return &ast.CommClause{pos, comm, colon, body}
 }
@@ -1582,6 +1827,8 @@ func (p *parser) parseForStmt() ast.Stmt {
 	}
 
 	pos := p.expect(token.FOR)
+	p.openScope()
+	defer p.closeScope()
 
 	var s1, s2, s3 ast.Stmt
 	if p.tok != token.LBRACE {
@@ -1631,18 +1878,16 @@ func (p *parser) parseForStmt() ast.Stmt {
 			return &ast.BadStmt{pos, body.End()}
 		}
 		if rhs, isUnary := as.Rhs[0].(*ast.UnaryExpr); isUnary && rhs.Op == token.RANGE {
-			// rhs is range expression; check lhs
+			// rhs is range expression
+			// (any short variable declaration was handled by parseSimpleStat above)
 			return &ast.RangeStmt{pos, key, value, as.TokPos, as.Tok, rhs.X, body}
-		} else {
-			p.errorExpected(s2.Pos(), "range clause")
-			return &ast.BadStmt{pos, body.End()}
 		}
-	} else {
-		// regular for statement
-		return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body}
+		p.errorExpected(s2.Pos(), "range clause")
+		return &ast.BadStmt{pos, body.End()}
 	}
 
-	panic("unreachable")
+	// regular for statement
+	return &ast.ForStmt{pos, s1, p.makeExpr(s2), s3, body}
 }
 
 
@@ -1706,19 +1951,20 @@ func (p *parser) parseStmt() (s ast.Stmt) {
 // ----------------------------------------------------------------------------
 // Declarations
 
-type parseSpecFunction func(p *parser, doc *ast.CommentGroup) ast.Spec
+type parseSpecFunction func(p *parser, doc *ast.CommentGroup, iota int) ast.Spec
 
 
-func parseImportSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
+func parseImportSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
 	if p.trace {
 		defer un(trace(p, "ImportSpec"))
 	}
 
 	var ident *ast.Ident
-	if p.tok == token.PERIOD {
+	switch p.tok {
+	case token.PERIOD:
 		ident = &ast.Ident{p.pos, ".", nil}
 		p.next()
-	} else if p.tok == token.IDENT {
+	case token.IDENT:
 		ident = p.parseIdent()
 	}
 
@@ -1729,13 +1975,17 @@ func parseImportSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
 	} else {
 		p.expect(token.STRING) // use expect() error handling
 	}
-	p.expectSemi()
+	p.expectSemi() // call before accessing p.linecomment
 
-	return &ast.ImportSpec{doc, ident, path, p.lineComment}
+	// collect imports
+	spec := &ast.ImportSpec{doc, ident, path, p.lineComment}
+	p.imports = append(p.imports, spec)
+
+	return spec
 }
 
 
-func parseConstSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
+func parseConstSpec(p *parser, doc *ast.CommentGroup, iota int) ast.Spec {
 	if p.trace {
 		defer un(trace(p, "ConstSpec"))
 	}
@@ -1743,30 +1993,46 @@ func parseConstSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
 	idents := p.parseIdentList()
 	typ := p.tryType()
 	var values []ast.Expr
-	if typ != nil || p.tok == token.ASSIGN {
+	if typ != nil || p.tok == token.ASSIGN || iota == 0 {
 		p.expect(token.ASSIGN)
-		values = p.parseExprList()
+		values = p.parseRhsList()
 	}
-	p.expectSemi()
+	p.expectSemi() // call before accessing p.linecomment
+
+	// Go spec: The scope of a constant or variable identifier declared inside
+	// a function begins at the end of the ConstSpec or VarSpec and ends at
+	// the end of the innermost containing block.
+	// (Global identifiers are resolved in a separate phase after parsing.)
+	spec := &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
+	p.declare(spec, p.topScope, ast.Con, idents...)
 
-	return &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
+	return spec
 }
 
 
-func parseTypeSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
+func parseTypeSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
 	if p.trace {
 		defer un(trace(p, "TypeSpec"))
 	}
 
 	ident := p.parseIdent()
-	typ := p.parseType()
-	p.expectSemi()
 
-	return &ast.TypeSpec{doc, ident, typ, p.lineComment}
+	// Go spec: The scope of a type identifier declared inside a function begins
+	// at the identifier in the TypeSpec and ends at the end of the innermost
+	// containing block.
+	// (Global identifiers are resolved in a separate phase after parsing.)
+	spec := &ast.TypeSpec{doc, ident, nil, nil}
+	p.declare(spec, p.topScope, ast.Typ, ident)
+
+	spec.Type = p.parseType()
+	p.expectSemi() // call before accessing p.linecomment
+	spec.Comment = p.lineComment
+
+	return spec
 }
 
 
-func parseVarSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
+func parseVarSpec(p *parser, doc *ast.CommentGroup, _ int) ast.Spec {
 	if p.trace {
 		defer un(trace(p, "VarSpec"))
 	}
@@ -1776,11 +2042,18 @@ func parseVarSpec(p *parser, doc *ast.CommentGroup) ast.Spec {
 	var values []ast.Expr
 	if typ == nil || p.tok == token.ASSIGN {
 		p.expect(token.ASSIGN)
-		values = p.parseExprList()
+		values = p.parseRhsList()
 	}
-	p.expectSemi()
+	p.expectSemi() // call before accessing p.linecomment
+
+	// Go spec: The scope of a constant or variable identifier declared inside
+	// a function begins at the end of the ConstSpec or VarSpec and ends at
+	// the end of the innermost containing block.
+	// (Global identifiers are resolved in a separate phase after parsing.)
+	spec := &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
+	p.declare(spec, p.topScope, ast.Var, idents...)
 
-	return &ast.ValueSpec{doc, idents, typ, values, p.lineComment}
+	return spec
 }
 
 
@@ -1796,26 +2069,26 @@ func (p *parser) parseGenDecl(keyword token.Token, f parseSpecFunction) *ast.Gen
 	if p.tok == token.LPAREN {
 		lparen = p.pos
 		p.next()
-		for p.tok != token.RPAREN && p.tok != token.EOF {
-			list = append(list, f(p, p.leadComment))
+		for iota := 0; p.tok != token.RPAREN && p.tok != token.EOF; iota++ {
+			list = append(list, f(p, p.leadComment, iota))
 		}
 		rparen = p.expect(token.RPAREN)
 		p.expectSemi()
 	} else {
-		list = append(list, f(p, nil))
+		list = append(list, f(p, nil, 0))
 	}
 
 	return &ast.GenDecl{doc, pos, keyword, lparen, list, rparen}
 }
 
 
-func (p *parser) parseReceiver() *ast.FieldList {
+func (p *parser) parseReceiver(scope *ast.Scope) *ast.FieldList {
 	if p.trace {
 		defer un(trace(p, "Receiver"))
 	}
 
 	pos := p.pos
-	par := p.parseParameters(false)
+	par := p.parseParameters(scope, false)
 
 	// must have exactly one receiver
 	if par.NumFields() != 1 {
@@ -1844,22 +2117,37 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
 
 	doc := p.leadComment
 	pos := p.expect(token.FUNC)
+	scope := ast.NewScope(p.topScope) // function scope
 
 	var recv *ast.FieldList
 	if p.tok == token.LPAREN {
-		recv = p.parseReceiver()
+		recv = p.parseReceiver(scope)
 	}
 
 	ident := p.parseIdent()
-	params, results := p.parseSignature()
+
+	params, results := p.parseSignature(scope)
 
 	var body *ast.BlockStmt
 	if p.tok == token.LBRACE {
-		body = p.parseBody()
+		body = p.parseBody(scope)
 	}
 	p.expectSemi()
 
-	return &ast.FuncDecl{doc, recv, ident, &ast.FuncType{pos, params, results}, body}
+	decl := &ast.FuncDecl{doc, recv, ident, &ast.FuncType{pos, params, results}, body}
+	if recv == nil {
+		// Go spec: The scope of an identifier denoting a constant, type,
+		// variable, or function (but not method) declared at top level
+		// (outside any function) is the package block.
+		//
+		// init() functions cannot be referred to and there may
+		// be more than one - don't put them in the pkgScope
+		if ident.Name != "init" {
+			p.declare(decl, p.pkgScope, ast.Fun, ident)
+		}
+	}
+
+	return decl
 }
 
 
@@ -1918,7 +2206,12 @@ func (p *parser) parseFile() *ast.File {
 	// package clause
 	doc := p.leadComment
 	pos := p.expect(token.PACKAGE)
+	// Go spec: The package clause is not a declaration;
+	// the package name does not appear in any scope.
 	ident := p.parseIdent()
+	if ident.Name == "_" {
+		p.error(p.pos, "invalid package name _")
+	}
 	p.expectSemi()
 
 	var decls []ast.Decl
@@ -1940,5 +2233,20 @@ func (p *parser) parseFile() *ast.File {
 		}
 	}
 
-	return &ast.File{doc, pos, ident, decls, p.comments}
+	assert(p.topScope == p.pkgScope, "imbalanced scopes")
+
+	// resolve global identifiers within the same file
+	i := 0
+	for _, ident := range p.unresolved {
+		// i <= index for current ident
+		assert(ident.Obj == unresolved, "object already resolved")
+		ident.Obj = p.pkgScope.Lookup(ident.Name) // also removes unresolved sentinel
+		if ident.Obj == nil {
+			p.unresolved[i] = ident
+			i++
+		}
+	}
+
+	// TODO(gri): store p.imports in AST
+	return &ast.File{doc, pos, ident, decls, p.pkgScope, p.imports, p.unresolved[0:i], p.comments}
 }
diff --git a/src/pkg/go/parser/parser_test.go b/src/pkg/go/parser/parser_test.go
index 38535627a..2f1ee6bfc 100644
--- a/src/pkg/go/parser/parser_test.go
+++ b/src/pkg/go/parser/parser_test.go
@@ -21,6 +21,7 @@ var illegalInputs = []interface{}{
 	`package p; func f() { if /* should have condition */ {} };`,
 	`package p; func f() { if ; /* should have condition */ {} };`,
 	`package p; func f() { if f(); /* should have condition */ {} };`,
+	`package p; const c; /* should have constant value */`,
 }
 
 
@@ -73,7 +74,7 @@ var validFiles = []string{
 
 func TestParse3(t *testing.T) {
 	for _, filename := range validFiles {
-		_, err := ParseFile(fset, filename, nil, 0)
+		_, err := ParseFile(fset, filename, nil, DeclarationErrors)
 		if err != nil {
 			t.Errorf("ParseFile(%s): %v", filename, err)
 		}
diff --git a/src/pkg/go/printer/nodes.go b/src/pkg/go/printer/nodes.go
index 7933c2f18..86c327930 100644
--- a/src/pkg/go/printer/nodes.go
+++ b/src/pkg/go/printer/nodes.go
@@ -108,17 +108,6 @@ func (p *printer) identList(list []*ast.Ident, indent bool, multiLine *bool) {
 }
 
 
-// Compute the key size of a key:value expression.
-// Returns 0 if the expression doesn't fit onto a single line.
-func (p *printer) keySize(pair *ast.KeyValueExpr) int {
-	if p.nodeSize(pair, infinity) <= infinity {
-		// entire expression fits on one line - return key size
-		return p.nodeSize(pair.Key, infinity)
-	}
-	return 0
-}
-
-
 // Print a list of expressions. If the list spans multiple
 // source lines, the original line breaks are respected between
 // expressions. Sets multiLine to true if the list spans multiple
@@ -171,19 +160,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
 	// the first linebreak is always a formfeed since this section must not
 	// depend on any previous formatting
 	prevBreak := -1 // index of last expression that was followed by a linebreak
-	linebreakMin := 1
-	if mode&periodSep != 0 {
-		// Make fragments like
-		//
-		// a.Bar(1,
-		//   2).Foo
-		//
-		// format correctly (a linebreak shouldn't be added before Foo) when
-		// doing period-separated expr lists by setting minimum linebreak to 0
-		// lines for them.
-		linebreakMin = 0
-	}
-	if prev.IsValid() && prev.Line < line && p.linebreak(line, linebreakMin, ws, true) {
+	if prev.IsValid() && prev.Line < line && p.linebreak(line, 0, ws, true) {
 		ws = ignore
 		*multiLine = true
 		prevBreak = 0
@@ -204,17 +181,21 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
 		//           the key and the node size into the decision process
 		useFF := true
 
-		// determine size
+		// determine element size: all bets are off if we don't have
+		// position information for the previous and next token (likely
+		// generated code - simply ignore the size in this case by setting
+		// it to 0)
 		prevSize := size
 		const infinity = 1e6 // larger than any source line
 		size = p.nodeSize(x, infinity)
 		pair, isPair := x.(*ast.KeyValueExpr)
-		if size <= infinity {
+		if size <= infinity && prev.IsValid() && next.IsValid() {
 			// x fits on a single line
 			if isPair {
 				size = p.nodeSize(pair.Key, infinity) // size <= infinity
 			}
 		} else {
+			// size too large or we don't have good layout information
 			size = 0
 		}
 
@@ -244,8 +225,7 @@ func (p *printer) exprList(prev0 token.Pos, list []ast.Expr, depth int, mode exp
 				// lines are broken using newlines so comments remain aligned
 				// unless forceFF is set or there are multiple expressions on
 				// the same line in which case formfeed is used
-				// broken with a formfeed
-				if p.linebreak(line, linebreakMin, ws, useFF || prevBreak+1 < i) {
+				if p.linebreak(line, 0, ws, useFF || prevBreak+1 < i) {
 					ws = ignore
 					*multiLine = true
 					prevBreak = i
@@ -371,11 +351,11 @@ func (p *printer) isOneLineFieldList(list []*ast.Field) bool {
 
 
 func (p *printer) setLineComment(text string) {
-	p.setComment(&ast.CommentGroup{[]*ast.Comment{&ast.Comment{token.NoPos, []byte(text)}}})
+	p.setComment(&ast.CommentGroup{[]*ast.Comment{&ast.Comment{token.NoPos, text}}})
 }
 
 
-func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprContext) {
+func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool) {
 	p.nesting++
 	defer func() {
 		p.nesting--
@@ -384,15 +364,15 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC
 	lbrace := fields.Opening
 	list := fields.List
 	rbrace := fields.Closing
+	srcIsOneLine := lbrace.IsValid() && rbrace.IsValid() && p.fset.Position(lbrace).Line == p.fset.Position(rbrace).Line
 
-	if !isIncomplete && !p.commentBefore(p.fset.Position(rbrace)) {
+	if !isIncomplete && !p.commentBefore(p.fset.Position(rbrace)) && srcIsOneLine {
 		// possibly a one-line struct/interface
 		if len(list) == 0 {
 			// no blank between keyword and {} in this case
 			p.print(lbrace, token.LBRACE, rbrace, token.RBRACE)
 			return
-		} else if ctxt&(compositeLit|structType) == compositeLit|structType &&
-			p.isOneLineFieldList(list) { // for now ignore interfaces
+		} else if isStruct && p.isOneLineFieldList(list) { // for now ignore interfaces
 			// small enough - print on one line
 			// (don't use identList and ignore source line breaks)
 			p.print(lbrace, token.LBRACE, blank)
@@ -414,7 +394,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC
 
 	// at least one entry or incomplete
 	p.print(blank, lbrace, token.LBRACE, indent, formfeed)
-	if ctxt&structType != 0 {
+	if isStruct {
 
 		sep := vtab
 		if len(list) == 1 {
@@ -497,15 +477,6 @@ func (p *printer) fieldList(fields *ast.FieldList, isIncomplete bool, ctxt exprC
 // ----------------------------------------------------------------------------
 // Expressions
 
-// exprContext describes the syntactic environment in which an expression node is printed.
-type exprContext uint
-
-const (
-	compositeLit exprContext = 1 << iota
-	structType
-)
-
-
 func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
 	switch e.Op.Precedence() {
 	case 4:
@@ -544,7 +515,7 @@ func walkBinary(e *ast.BinaryExpr) (has4, has5 bool, maxProblem int) {
 		}
 
 	case *ast.StarExpr:
-		if e.Op.String() == "/" {
+		if e.Op == token.QUO { // `*/`
 			maxProblem = 5
 		}
 
@@ -650,7 +621,7 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiL
 	printBlank := prec < cutoff
 
 	ws := indent
-	p.expr1(x.X, prec, depth+diffPrec(x.X, prec), 0, multiLine)
+	p.expr1(x.X, prec, depth+diffPrec(x.X, prec), multiLine)
 	if printBlank {
 		p.print(blank)
 	}
@@ -669,7 +640,7 @@ func (p *printer) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int, multiL
 	if printBlank {
 		p.print(blank)
 	}
-	p.expr1(x.Y, prec+1, depth+1, 0, multiLine)
+	p.expr1(x.Y, prec+1, depth+1, multiLine)
 	if ws == ignore {
 		p.print(unindent)
 	}
@@ -742,7 +713,7 @@ func selectorExprList(expr ast.Expr) (list []ast.Expr) {
 
 
 // Sets multiLine to true if the expression spans multiple lines.
-func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multiLine *bool) {
+func (p *printer) expr1(expr ast.Expr, prec1, depth int, multiLine *bool) {
 	p.print(expr.Pos())
 
 	switch x := expr.(type) {
@@ -792,7 +763,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
 				// TODO(gri) Remove this code if it cannot be reached.
 				p.print(blank)
 			}
-			p.expr1(x.X, prec, depth, 0, multiLine)
+			p.expr1(x.X, prec, depth, multiLine)
 		}
 
 	case *ast.BasicLit:
@@ -818,7 +789,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
 		p.exprList(token.NoPos, parts, depth, periodSep, multiLine, token.NoPos)
 
 	case *ast.TypeAssertExpr:
-		p.expr1(x.X, token.HighestPrec, depth, 0, multiLine)
+		p.expr1(x.X, token.HighestPrec, depth, multiLine)
 		p.print(token.PERIOD, token.LPAREN)
 		if x.Type != nil {
 			p.expr(x.Type, multiLine)
@@ -829,14 +800,14 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
 
 	case *ast.IndexExpr:
 		// TODO(gri): should treat[] like parentheses and undo one level of depth
-		p.expr1(x.X, token.HighestPrec, 1, 0, multiLine)
+		p.expr1(x.X, token.HighestPrec, 1, multiLine)
 		p.print(x.Lbrack, token.LBRACK)
 		p.expr0(x.Index, depth+1, multiLine)
 		p.print(x.Rbrack, token.RBRACK)
 
 	case *ast.SliceExpr:
 		// TODO(gri): should treat[] like parentheses and undo one level of depth
-		p.expr1(x.X, token.HighestPrec, 1, 0, multiLine)
+		p.expr1(x.X, token.HighestPrec, 1, multiLine)
 		p.print(x.Lbrack, token.LBRACK)
 		if x.Low != nil {
 			p.expr0(x.Low, depth+1, multiLine)
@@ -856,7 +827,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
 		if len(x.Args) > 1 {
 			depth++
 		}
-		p.expr1(x.Fun, token.HighestPrec, depth, 0, multiLine)
+		p.expr1(x.Fun, token.HighestPrec, depth, multiLine)
 		p.print(x.Lparen, token.LPAREN)
 		p.exprList(x.Lparen, x.Args, depth, commaSep|commaTerm, multiLine, x.Rparen)
 		if x.Ellipsis.IsValid() {
@@ -867,7 +838,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
 	case *ast.CompositeLit:
 		// composite literal elements that are composite literals themselves may have the type omitted
 		if x.Type != nil {
-			p.expr1(x.Type, token.HighestPrec, depth, compositeLit, multiLine)
+			p.expr1(x.Type, token.HighestPrec, depth, multiLine)
 		}
 		p.print(x.Lbrace, token.LBRACE)
 		p.exprList(x.Lbrace, x.Elts, 1, commaSep|commaTerm, multiLine, x.Rbrace)
@@ -892,7 +863,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
 
 	case *ast.StructType:
 		p.print(token.STRUCT)
-		p.fieldList(x.Fields, x.Incomplete, ctxt|structType)
+		p.fieldList(x.Fields, true, x.Incomplete)
 
 	case *ast.FuncType:
 		p.print(token.FUNC)
@@ -900,7 +871,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
 
 	case *ast.InterfaceType:
 		p.print(token.INTERFACE)
-		p.fieldList(x.Methods, x.Incomplete, ctxt)
+		p.fieldList(x.Methods, false, x.Incomplete)
 
 	case *ast.MapType:
 		p.print(token.MAP, token.LBRACK)
@@ -929,14 +900,14 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int, ctxt exprContext, multi
 
 
 func (p *printer) expr0(x ast.Expr, depth int, multiLine *bool) {
-	p.expr1(x, token.LowestPrec, depth, 0, multiLine)
+	p.expr1(x, token.LowestPrec, depth, multiLine)
 }
 
 
 // Sets multiLine to true if the expression spans multiple lines.
 func (p *printer) expr(x ast.Expr, multiLine *bool) {
 	const depth = 1
-	p.expr1(x, token.LowestPrec, depth, 0, multiLine)
+	p.expr1(x, token.LowestPrec, depth, multiLine)
 }
 
 
@@ -1145,9 +1116,9 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
 		}
 
 	case *ast.CaseClause:
-		if s.Values != nil {
+		if s.List != nil {
 			p.print(token.CASE)
-			p.exprList(s.Pos(), s.Values, 1, blankStart|commaSep, multiLine, s.Colon)
+			p.exprList(s.Pos(), s.List, 1, blankStart|commaSep, multiLine, s.Colon)
 		} else {
 			p.print(token.DEFAULT)
 		}
@@ -1160,16 +1131,6 @@ func (p *printer) stmt(stmt ast.Stmt, nextIsRBrace bool, multiLine *bool) {
 		p.block(s.Body, 0)
 		*multiLine = true
 
-	case *ast.TypeCaseClause:
-		if s.Types != nil {
-			p.print(token.CASE)
-			p.exprList(s.Pos(), s.Types, 1, blankStart|commaSep, multiLine, s.Colon)
-		} else {
-			p.print(token.DEFAULT)
-		}
-		p.print(s.Colon, token.COLON)
-		p.stmtList(s.Body, 1, nextIsRBrace)
-
 	case *ast.TypeSwitchStmt:
 		p.print(token.SWITCH)
 		if s.Init != nil {
@@ -1239,7 +1200,7 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool, multiLine *bool) {
 		p.setComment(s.Doc)
 		if s.Name != nil {
 			p.expr(s.Name, multiLine)
-			p.print(vtab)
+			p.print(blank)
 		}
 		p.expr(s.Path, multiLine)
 		p.setComment(s.Comment)
@@ -1331,13 +1292,23 @@ func (p *printer) genDecl(d *ast.GenDecl, multiLine *bool) {
 // any control chars. Otherwise, the result is > maxSize.
 //
 func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
+	// nodeSize invokes the printer, which may invoke nodeSize
+	// recursively. For deep composite literal nests, this can
+	// lead to an exponential algorithm. Remember previous
+	// results to prune the recursion (was issue 1628).
+	if size, found := p.nodeSizes[n]; found {
+		return size
+	}
+
 	size = maxSize + 1 // assume n doesn't fit
+	p.nodeSizes[n] = size
+
 	// nodeSize computation must be indendent of particular
 	// style so that we always get the same decision; print
 	// in RawFormat
 	cfg := Config{Mode: RawFormat}
 	var buf bytes.Buffer
-	if _, err := cfg.Fprint(&buf, p.fset, n); err != nil {
+	if _, err := cfg.fprint(&buf, p.fset, n, p.nodeSizes); err != nil {
 		return
 	}
 	if buf.Len() <= maxSize {
@@ -1347,6 +1318,7 @@ func (p *printer) nodeSize(n ast.Node, maxSize int) (size int) {
 			}
 		}
 		size = buf.Len() // n fits
+		p.nodeSizes[n] = size
 	}
 	return
 }
diff --git a/src/pkg/go/printer/printer.go b/src/pkg/go/printer/printer.go
index 90d9784ac..697a83fa8 100644
--- a/src/pkg/go/printer/printer.go
+++ b/src/pkg/go/printer/printer.go
@@ -34,12 +34,6 @@ const (
 )
 
 
-const (
-	esc2 = '\xfe'                        // an escape byte that cannot occur in regular UTF-8
-	_    = 1 / (esc2 - tabwriter.Escape) // cause compiler error if esc2 == tabwriter.Escape
-)
-
-
 var (
 	esc       = []byte{tabwriter.Escape}
 	htab      = []byte{'\t'}
@@ -81,8 +75,9 @@ type printer struct {
 	mode    pmode       // current printer mode
 	lastTok token.Token // the last token printed (token.ILLEGAL if it's whitespace)
 
-	// Buffered whitespace
-	buffer []whiteSpace
+	// Reused buffers
+	wsbuf  []whiteSpace // delayed white space
+	litbuf bytes.Buffer // for creation of escaped literals and comments
 
 	// The (possibly estimated) position in the generated output;
 	// in AST space (i.e., pos is set whenever a token position is
@@ -94,22 +89,23 @@ type printer struct {
 	// written using writeItem.
 	last token.Position
 
-	// HTML support
-	lastTaggedLine int // last line for which a line tag was written
-
 	// The list of all source comments, in order of appearance.
 	comments        []*ast.CommentGroup // may be nil
 	cindex          int                 // current comment index
 	useNodeComments bool                // if not set, ignore lead and line comments of nodes
+
+	// Cache of already computed node sizes.
+	nodeSizes map[ast.Node]int
 }
 
 
-func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet) {
+func (p *printer) init(output io.Writer, cfg *Config, fset *token.FileSet, nodeSizes map[ast.Node]int) {
 	p.output = output
 	p.Config = *cfg
 	p.fset = fset
 	p.errors = make(chan os.Error)
-	p.buffer = make([]whiteSpace, 0, 16) // whitespace sequences are short
+	p.wsbuf = make([]whiteSpace, 0, 16) // whitespace sequences are short
+	p.nodeSizes = nodeSizes
 }
 
 
@@ -122,6 +118,20 @@ func (p *printer) internalError(msg ...interface{}) {
 }
 
 
+// escape escapes string s by bracketing it with tabwriter.Escape.
+// Escaped strings pass through tabwriter unchanged. (Note that
+// valid Go programs cannot contain tabwriter.Escape bytes since
+// they do not appear in legal UTF-8 sequences).
+//
+func (p *printer) escape(s string) string {
+	p.litbuf.Reset()
+	p.litbuf.WriteByte(tabwriter.Escape)
+	p.litbuf.WriteString(s)
+	p.litbuf.WriteByte(tabwriter.Escape)
+	return p.litbuf.String()
+}
+
+
 // nlines returns the adjusted number of linebreaks given the desired number
 // of breaks n such that min <= result <= max where max depends on the current
 // nesting level.
@@ -229,7 +239,7 @@ func (p *printer) writeNewlines(n int, useFF bool) {
 // source text. writeItem updates p.last to the position immediately following
 // the data.
 //
-func (p *printer) writeItem(pos token.Position, data []byte) {
+func (p *printer) writeItem(pos token.Position, data string) {
 	if pos.IsValid() {
 		// continue with previous position if we don't have a valid pos
 		if p.last.IsValid() && p.last.Filename != pos.Filename {
@@ -238,7 +248,7 @@ func (p *printer) writeItem(pos token.Position, data []byte) {
 			// e.g., the result of ast.MergePackageFiles)
 			p.indent = 0
 			p.mode = 0
-			p.buffer = p.buffer[0:0]
+			p.wsbuf = p.wsbuf[0:0]
 		}
 		p.pos = pos
 	}
@@ -247,7 +257,7 @@ func (p *printer) writeItem(pos token.Position, data []byte) {
 		_, filename := filepath.Split(pos.Filename)
 		p.write0([]byte(fmt.Sprintf("[%s:%d:%d]", filename, pos.Line, pos.Column)))
 	}
-	p.write(data)
+	p.write([]byte(data))
 	p.last = p.pos
 }
 
@@ -279,11 +289,11 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
 		if prev == nil {
 			// first comment of a comment group
 			j := 0
-			for i, ch := range p.buffer {
+			for i, ch := range p.wsbuf {
 				switch ch {
 				case blank:
 					// ignore any blanks before a comment
-					p.buffer[i] = ignore
+					p.wsbuf[i] = ignore
 					continue
 				case vtab:
 					// respect existing tabs - important
@@ -317,11 +327,11 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
 		if prev == nil {
 			// first comment of a comment group
 			j := 0
-			for i, ch := range p.buffer {
+			for i, ch := range p.wsbuf {
 				switch ch {
 				case blank, vtab:
 					// ignore any horizontal whitespace before line breaks
-					p.buffer[i] = ignore
+					p.wsbuf[i] = ignore
 					continue
 				case indent:
 					// apply pending indentation
@@ -338,7 +348,7 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
 					}
 				case newline, formfeed:
 					// TODO(gri): may want to keep formfeed info in some cases
-					p.buffer[i] = ignore
+					p.wsbuf[i] = ignore
 				}
 				j = i
 				break
@@ -359,12 +369,8 @@ func (p *printer) writeCommentPrefix(pos, next token.Position, prev *ast.Comment
 }
 
 
-func (p *printer) writeCommentLine(comment *ast.Comment, pos token.Position, line []byte) {
-	// line must pass through unchanged, bracket it with tabwriter.Escape
-	line = bytes.Join([][]byte{esc, line, esc}, nil)
-	p.writeItem(pos, line)
-}
-
+// TODO(gri): It should be possible to convert the code below from using
+//            []byte to string and in the process eliminate some conversions.
 
 // Split comment text into lines
 func split(text []byte) [][]byte {
@@ -545,13 +551,13 @@ func (p *printer) writeComment(comment *ast.Comment) {
 
 	// shortcut common case of //-style comments
 	if text[1] == '/' {
-		p.writeCommentLine(comment, p.fset.Position(comment.Pos()), text)
+		p.writeItem(p.fset.Position(comment.Pos()), p.escape(text))
 		return
 	}
 
 	// for /*-style comments, print line by line and let the
 	// write function take care of the proper indentation
-	lines := split(text)
+	lines := split([]byte(text))
 	stripCommonPrefix(lines)
 
 	// write comment lines, separated by formfeed,
@@ -564,7 +570,7 @@ func (p *printer) writeComment(comment *ast.Comment) {
 			pos = p.pos
 		}
 		if len(line) > 0 {
-			p.writeCommentLine(comment, pos, line)
+			p.writeItem(pos, p.escape(string(line)))
 		}
 	}
 }
@@ -577,11 +583,11 @@ func (p *printer) writeComment(comment *ast.Comment) {
 // formfeed was dropped from the whitespace buffer.
 //
 func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
-	for i, ch := range p.buffer {
+	for i, ch := range p.wsbuf {
 		switch ch {
 		case blank, vtab:
 			// ignore trailing whitespace
-			p.buffer[i] = ignore
+			p.wsbuf[i] = ignore
 		case indent, unindent:
 			// don't loose indentation information
 		case newline, formfeed:
@@ -593,11 +599,11 @@ func (p *printer) writeCommentSuffix(needsLinebreak bool) (droppedFF bool) {
 				if ch == formfeed {
 					droppedFF = true
 				}
-				p.buffer[i] = ignore
+				p.wsbuf[i] = ignore
 			}
 		}
 	}
-	p.writeWhitespace(len(p.buffer))
+	p.writeWhitespace(len(p.wsbuf))
 
 	// make sure we have a line break
 	if needsLinebreak {
@@ -651,7 +657,7 @@ func (p *printer) writeWhitespace(n int) {
 	// write entries
 	var data [1]byte
 	for i := 0; i < n; i++ {
-		switch ch := p.buffer[i]; ch {
+		switch ch := p.wsbuf[i]; ch {
 		case ignore:
 			// ignore!
 		case indent:
@@ -669,13 +675,13 @@ func (p *printer) writeWhitespace(n int) {
 			// the line break and the label, the unindent is not
 			// part of the comment whitespace prefix and the comment
 			// will be positioned correctly indented.
-			if i+1 < n && p.buffer[i+1] == unindent {
+			if i+1 < n && p.wsbuf[i+1] == unindent {
 				// Use a formfeed to terminate the current section.
 				// Otherwise, a long label name on the next line leading
 				// to a wide column may increase the indentation column
 				// of lines before the label; effectively leading to wrong
 				// indentation.
-				p.buffer[i], p.buffer[i+1] = unindent, formfeed
+				p.wsbuf[i], p.wsbuf[i+1] = unindent, formfeed
 				i-- // do it again
 				continue
 			}
@@ -688,11 +694,11 @@ func (p *printer) writeWhitespace(n int) {
 
 	// shift remaining entries down
 	i := 0
-	for ; n < len(p.buffer); n++ {
-		p.buffer[i] = p.buffer[n]
+	for ; n < len(p.wsbuf); n++ {
+		p.wsbuf[i] = p.wsbuf[n]
 		i++
 	}
-	p.buffer = p.buffer[0:i]
+	p.wsbuf = p.wsbuf[0:i]
 }
 
 
@@ -733,7 +739,7 @@ func mayCombine(prev token.Token, next byte) (b bool) {
 func (p *printer) print(args ...interface{}) {
 	for _, f := range args {
 		next := p.pos // estimated position of next item
-		var data []byte
+		var data string
 		var tok token.Token
 
 		switch x := f.(type) {
@@ -747,42 +753,22 @@ func (p *printer) print(args ...interface{}) {
 				// LabeledStmt)
 				break
 			}
-			i := len(p.buffer)
-			if i == cap(p.buffer) {
+			i := len(p.wsbuf)
+			if i == cap(p.wsbuf) {
 				// Whitespace sequences are very short so this should
 				// never happen. Handle gracefully (but possibly with
 				// bad comment placement) if it does happen.
 				p.writeWhitespace(i)
 				i = 0
 			}
-			p.buffer = p.buffer[0 : i+1]
-			p.buffer[i] = x
+			p.wsbuf = p.wsbuf[0 : i+1]
+			p.wsbuf[i] = x
 		case *ast.Ident:
-			data = []byte(x.Name)
+			data = x.Name
 			tok = token.IDENT
 		case *ast.BasicLit:
-			// escape all literals so they pass through unchanged
-			// (note that valid Go programs cannot contain
-			// tabwriter.Escape bytes since they do not appear in
-			// legal UTF-8 sequences)
-			data = make([]byte, 0, len(x.Value)+2)
-			data = append(data, tabwriter.Escape)
-			data = append(data, x.Value...)
-			data = append(data, tabwriter.Escape)
+			data = p.escape(x.Value)
 			tok = x.Kind
-			// If we have a raw string that spans multiple lines and
-			// the opening quote (`) is on a line preceded only by
-			// indentation, we don't want to write that indentation
-			// because the following lines of the raw string are not
-			// indented. It's easiest to correct the output at the end
-			// via the trimmer (because of the complex handling of
-			// white space).
-			// Mark multi-line raw strings by replacing the opening
-			// quote with esc2 and have the trimmer take care of fixing
-			// it up. (Do this _after_ making a copy of data!)
-			if data[1] == '`' && bytes.IndexByte(data, '\n') > 0 {
-				data[1] = esc2
-			}
 		case token.Token:
 			s := x.String()
 			if mayCombine(p.lastTok, s[0]) {
@@ -792,13 +778,13 @@ func (p *printer) print(args ...interface{}) {
 				// (except for token.INT followed by a '.' this
 				// should never happen because it is taken care
 				// of via binary expression formatting)
-				if len(p.buffer) != 0 {
+				if len(p.wsbuf) != 0 {
 					p.internalError("whitespace buffer not empty")
 				}
-				p.buffer = p.buffer[0:1]
-				p.buffer[0] = ' '
+				p.wsbuf = p.wsbuf[0:1]
+				p.wsbuf[0] = ' '
 			}
-			data = []byte(s)
+			data = s
 			tok = x
 		case token.Pos:
 			if x.IsValid() {
@@ -812,7 +798,7 @@ func (p *printer) print(args ...interface{}) {
 		p.lastTok = tok
 		p.pos = next
 
-		if data != nil {
+		if data != "" {
 			droppedFF := p.flush(next, tok)
 
 			// intersperse extra newlines if present in the source
@@ -847,7 +833,7 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) {
 		droppedFF = p.intersperseComments(next, tok)
 	} else {
 		// otherwise, write any leftover whitespace
-		p.writeWhitespace(len(p.buffer))
+		p.writeWhitespace(len(p.wsbuf))
 	}
 	return
 }
@@ -863,10 +849,9 @@ func (p *printer) flush(next token.Position, tok token.Token) (droppedFF bool) {
 // through unchanged.
 //
 type trimmer struct {
-	output  io.Writer
-	state   int
-	space   bytes.Buffer
-	hasText bool
+	output io.Writer
+	state  int
+	space  bytes.Buffer
 }
 
 
@@ -874,15 +859,11 @@ type trimmer struct {
 // It can be in one of the following states:
 const (
 	inSpace  = iota // inside space
-	atEscape        // inside space and the last char was an opening tabwriter.Escape
 	inEscape        // inside text bracketed by tabwriter.Escapes
 	inText          // inside text
 )
 
 
-var backquote = []byte{'`'}
-
-
 // Design note: It is tempting to eliminate extra blanks occurring in
 //              whitespace in this function as it could simplify some
 //              of the blanks logic in the node printing functions.
@@ -891,9 +872,8 @@ var backquote = []byte{'`'}
 
 func (p *trimmer) Write(data []byte) (n int, err os.Error) {
 	// invariants:
-	// p.state == inSpace, atEscape:
+	// p.state == inSpace:
 	//	p.space is unwritten
-	//	p.hasText indicates if there is any text on this line
 	// p.state == inEscape, inText:
 	//	data[m:n] is unwritten
 	m := 0
@@ -910,32 +890,20 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) {
 			case '\n', '\f':
 				p.space.Reset()                        // discard trailing space
 				_, err = p.output.Write(newlines[0:1]) // write newline
-				p.hasText = false
 			case tabwriter.Escape:
-				p.state = atEscape
+				_, err = p.output.Write(p.space.Bytes())
+				p.state = inEscape
+				m = n + 1 // +1: skip tabwriter.Escape
 			default:
 				_, err = p.output.Write(p.space.Bytes())
 				p.state = inText
 				m = n
 			}
-		case atEscape:
-			// discard indentation if we have a multi-line raw string
-			// (see printer.print for details)
-			if b != esc2 || p.hasText {
-				_, err = p.output.Write(p.space.Bytes())
-			}
-			p.state = inEscape
-			m = n
-			if b == esc2 {
-				_, err = p.output.Write(backquote) // convert back
-				m++
-			}
 		case inEscape:
 			if b == tabwriter.Escape {
 				_, err = p.output.Write(data[m:n])
 				p.state = inSpace
 				p.space.Reset()
-				p.hasText = true
 			}
 		case inText:
 			switch b {
@@ -944,19 +912,18 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) {
 				p.state = inSpace
 				p.space.Reset()
 				p.space.WriteByte(b) // WriteByte returns no errors
-				p.hasText = true
 			case '\n', '\f':
 				_, err = p.output.Write(data[m:n])
 				p.state = inSpace
 				p.space.Reset()
 				_, err = p.output.Write(newlines[0:1]) // write newline
-				p.hasText = false
 			case tabwriter.Escape:
 				_, err = p.output.Write(data[m:n])
-				p.state = atEscape
-				p.space.Reset()
-				p.hasText = true
+				p.state = inEscape
+				m = n + 1 // +1: skip tabwriter.Escape
 			}
+		default:
+			panic("unreachable")
 		}
 		if err != nil {
 			return
@@ -969,7 +936,6 @@ func (p *trimmer) Write(data []byte) (n int, err os.Error) {
 		_, err = p.output.Write(data[m:n])
 		p.state = inSpace
 		p.space.Reset()
-		p.hasText = true
 	}
 
 	return
@@ -994,13 +960,8 @@ type Config struct {
 }
 
 
-// Fprint "pretty-prints" an AST node to output and returns the number
-// of bytes written and an error (if any) for a given configuration cfg.
-// Position information is interpreted relative to the file set fset.
-// The node type must be *ast.File, or assignment-compatible to ast.Expr,
-// ast.Decl, ast.Spec, or ast.Stmt.
-//
-func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) (int, os.Error) {
+// fprint implements Fprint and takes a nodesSizes map for setting up the printer state.
+func (cfg *Config) fprint(output io.Writer, fset *token.FileSet, node interface{}, nodeSizes map[ast.Node]int) (int, os.Error) {
 	// redirect output through a trimmer to eliminate trailing whitespace
 	// (Input to a tabwriter must be untrimmed since trailing tabs provide
 	// formatting information. The tabwriter could provide trimming
@@ -1029,7 +990,7 @@ func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{
 
 	// setup printer and print node
 	var p printer
-	p.init(output, cfg, fset)
+	p.init(output, cfg, fset, nodeSizes)
 	go func() {
 		switch n := node.(type) {
 		case ast.Expr:
@@ -1076,6 +1037,17 @@ func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{
 }
 
 
+// Fprint "pretty-prints" an AST node to output and returns the number
+// of bytes written and an error (if any) for a given configuration cfg.
+// Position information is interpreted relative to the file set fset.
+// The node type must be *ast.File, or assignment-compatible to ast.Expr,
+// ast.Decl, ast.Spec, or ast.Stmt.
+//
+func (cfg *Config) Fprint(output io.Writer, fset *token.FileSet, node interface{}) (int, os.Error) {
+	return cfg.fprint(output, fset, node, make(map[ast.Node]int))
+}
+
+
 // Fprint "pretty-prints" an AST node to output.
 // It calls Config.Fprint with default settings.
 //
diff --git a/src/pkg/go/printer/printer_test.go b/src/pkg/go/printer/printer_test.go
index 62b726913..090f92af1 100644
--- a/src/pkg/go/printer/printer_test.go
+++ b/src/pkg/go/printer/printer_test.go
@@ -13,6 +13,7 @@ import (
 	"go/token"
 	"path/filepath"
 	"testing"
+	"time"
 )
 
 
@@ -45,7 +46,7 @@ const (
 )
 
 
-func check(t *testing.T, source, golden string, mode checkMode) {
+func runcheck(t *testing.T, source, golden string, mode checkMode) {
 	// parse source
 	prog, err := parser.ParseFile(fset, source, nil, parser.ParseComments)
 	if err != nil {
@@ -109,6 +110,32 @@ func check(t *testing.T, source, golden string, mode checkMode) {
 }
 
 
+func check(t *testing.T, source, golden string, mode checkMode) {
+	// start a timer to produce a time-out signal
+	tc := make(chan int)
+	go func() {
+		time.Sleep(10e9) // plenty of a safety margin, even for very slow machines
+		tc <- 0
+	}()
+
+	// run the test
+	cc := make(chan int)
+	go func() {
+		runcheck(t, source, golden, mode)
+		cc <- 0
+	}()
+
+	// wait for the first finisher
+	select {
+	case <-tc:
+		// test running past time out
+		t.Errorf("%s: running too slowly", source)
+	case <-cc:
+		// test finished within alloted time margin
+	}
+}
+
+
 type entry struct {
 	source, golden string
 	mode           checkMode
@@ -124,16 +151,20 @@ var data = []entry{
 	{"expressions.input", "expressions.raw", rawFormat},
 	{"declarations.input", "declarations.golden", 0},
 	{"statements.input", "statements.golden", 0},
+	{"slow.input", "slow.golden", 0},
 }
 
 
 func TestFiles(t *testing.T) {
-	for _, e := range data {
+	for i, e := range data {
 		source := filepath.Join(dataDir, e.source)
 		golden := filepath.Join(dataDir, e.golden)
 		check(t, source, golden, e.mode)
 		// TODO(gri) check that golden is idempotent
-		//check(t, golden, golden, e.mode);
+		//check(t, golden, golden, e.mode)
+		if testing.Short() && i >= 3 {
+			break
+		}
 	}
 }
 
diff --git a/src/pkg/go/printer/testdata/declarations.golden b/src/pkg/go/printer/testdata/declarations.golden
index 1c091b929..c1b255842 100644
--- a/src/pkg/go/printer/testdata/declarations.golden
+++ b/src/pkg/go/printer/testdata/declarations.golden
@@ -7,10 +7,10 @@ package imports
 import "io"
 
 import (
-	_	"io"
+	_ "io"
 )
 
-import _	"io"
+import _ "io"
 
 import (
 	"io"
@@ -20,40 +20,40 @@ import (
 
 import (
 	"io"
-	aLongRename	"io"
+	aLongRename "io"
 
-	b	"io"
+	b "io"
 )
 
 import (
 	"unrenamed"
-	renamed	"renameMe"
-	.	"io"
-	_	"io"
+	renamed "renameMe"
+	. "io"
+	_ "io"
 	"io"
-	.	"os"
+	. "os"
 )
 
 // no newlines between consecutive single imports, but
 // respect extra line breaks in the source (at most one empty line)
-import _	"io"
-import _	"io"
-import _	"io"
+import _ "io"
+import _ "io"
+import _ "io"
 
-import _	"os"
-import _	"os"
-import _	"os"
+import _ "os"
+import _ "os"
+import _ "os"
 
 
-import _	"fmt"
-import _	"fmt"
-import _	"fmt"
+import _ "fmt"
+import _ "fmt"
+import _ "fmt"
 
 import "foo"	// a comment
 import "bar"	// a comment
 
 import (
-	_	"foo"
+	_ "foo"
 	// a comment
 	"bar"
 	"foo"	// a comment
@@ -63,17 +63,17 @@ import (
 // comments + renames
 import (
 	"unrenamed"	// a comment
-	renamed		"renameMe"
-	.		"io"		/* a comment */
-	_		"io/ioutil"	// a comment
+	renamed "renameMe"
+	. "io"		/* a comment */
+	_ "io/ioutil"	// a comment
 	"io"		// testing alignment
-	.		"os"
+	. "os"
 	// a comment
 )
 
 // a case that caused problems in the past (comment placement)
 import (
-	.	"fmt"
+	. "fmt"
 	"io"
 	"malloc"	// for the malloc count test only
 	"math"
@@ -81,9 +81,38 @@ import (
 	"testing"
 )
 
+// more import examples
+import (
+	"xxx"
+	"much longer name"	// comment
+	"short name"		// comment
+)
+
+import (
+	_ "xxx"
+	"much longer name"	// comment
+)
+
+import (
+	mymath "math"
+	"/foo/bar/long_package_path"	// a comment
+)
+
+import (
+	"package_a"	// comment
+	"package_b"
+	my_better_c "package_c"	// comment
+	"package_d"		// comment
+	my_e "package_e"	// comment
+
+	"package_a"	// comment
+	"package_bb"
+	"package_ccc"	// comment
+	"package_dddd"	// comment
+)
 
 // at least one empty line between declarations of different kind
-import _	"io"
+import _ "io"
 
 var _ int
 
diff --git a/src/pkg/go/printer/testdata/declarations.input b/src/pkg/go/printer/testdata/declarations.input
index c826462f9..c8b37e12b 100644
--- a/src/pkg/go/printer/testdata/declarations.input
+++ b/src/pkg/go/printer/testdata/declarations.input
@@ -81,6 +81,35 @@ import (
 	"testing"
 )
 
+// more import examples
+import (
+	"xxx"
+	"much longer name" // comment
+	"short name" // comment
+)
+
+import (
+	_ "xxx"
+	"much longer name" // comment
+)
+
+import (
+	mymath "math"
+	"/foo/bar/long_package_path" // a comment
+)
+
+import (
+	"package_a" // comment
+	"package_b"
+	my_better_c "package_c" // comment
+	"package_d" // comment
+	my_e "package_e" // comment
+
+	"package_a"    // comment
+	"package_bb"
+	"package_ccc"  // comment
+	"package_dddd" // comment
+)
 
 // at least one empty line between declarations of different kind
 import _ "io"
diff --git a/src/pkg/go/printer/testdata/expressions.golden b/src/pkg/go/printer/testdata/expressions.golden
index 7f18f338a..c1a7e970b 100644
--- a/src/pkg/go/printer/testdata/expressions.golden
+++ b/src/pkg/go/printer/testdata/expressions.golden
@@ -224,11 +224,7 @@ func _() {
 	_ = struct{ x int }{0}
 	_ = struct{ x, y, z int }{0, 1, 2}
 	_ = struct{ int }{0}
-	_ = struct {
-		s struct {
-			int
-		}
-	}{struct{ int }{0}}	// compositeLit context not propagated => multiLine result
+	_ = struct{ s struct{ int } }{struct{ int }{0}}
 }
 
 
@@ -257,8 +253,8 @@ bar`
 	var _ = ``
 	var _ = `foo`
 	var _ =
-	// the next line should not be indented
-`foo
+	// the next line should remain indented
+	`foo
 bar`
 
 	var _ =	// comment
@@ -266,8 +262,8 @@ bar`
 	var _ =	// comment
 	`foo`
 	var _ =	// comment
-	// the next line should not be indented
-`foo
+	// the next line should remain indented
+	`foo
 bar`
 
 	var _ = /* comment */ ``
@@ -280,12 +276,12 @@ bar`
 	var _ =	/* comment */
 	`foo`
 	var _ =	/* comment */
-	// the next line should not be indented
-`foo
+	// the next line should remain indented
+	`foo
 bar`
 
 	var board = []int(
-`...........
+		`...........
 ...........
 ....●●●....
 ....●●●....
@@ -300,8 +296,8 @@ bar`
 
 	var state = S{
 		"foo",
-		// the next line should not be indented
-`...........
+		// the next line should remain indented
+		`...........
 ...........
 ....●●●....
 ....●●●....
@@ -623,3 +619,13 @@ func _() {
 		b.(T).
 		c
 }
+
+
+// Don't introduce extra newlines in strangely formatted expression lists.
+func f() {
+	// os.Open parameters should remain on two lines
+	if writer, err = os.Open(outfile, s.O_WRONLY|os.O_CREATE|
+		os.O_TRUNC,0666); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/src/pkg/go/printer/testdata/expressions.input b/src/pkg/go/printer/testdata/expressions.input
index 6bcd9b5f8..b87381198 100644
--- a/src/pkg/go/printer/testdata/expressions.input
+++ b/src/pkg/go/printer/testdata/expressions.input
@@ -224,7 +224,7 @@ func _() {
 	_ = struct{ x int }{0}
 	_ = struct{ x, y, z int }{0, 1, 2}
 	_ = struct{ int }{0}
-	_ = struct{ s struct { int } }{struct{ int}{0}}  // compositeLit context not propagated => multiLine result
+	_ = struct{ s struct { int } }{struct{ int}{0} }
 }
 
 
@@ -256,7 +256,7 @@ var _ =
 var _ =
 	`foo`
 var _ =
-	// the next line should not be indented
+	// the next line should remain indented
 	`foo
 bar`
 
@@ -266,7 +266,7 @@ bar`
 	var _ = // comment
 		`foo`
 	var _ = // comment
-		// the next line should not be indented
+		// the next line should remain indented
 		`foo
 bar`
 
@@ -282,7 +282,7 @@ bar`
 	var _ = /* comment */
 		`foo`
 	var _ = /* comment */
-		// the next line should not be indented
+		// the next line should remain indented
 		`foo
 bar`
 
@@ -304,7 +304,7 @@ var board = []int(
 
 	var state = S{
 		"foo",
-		// the next line should not be indented
+		// the next line should remain indented
 		`...........
 ...........
 ....●●●....
@@ -625,3 +625,13 @@ baz()
 	(T).
 	c
 }
+
+
+// Don't introduce extra newlines in strangely formatted expression lists.
+func f() {
+	// os.Open parameters should remain on two lines
+	if writer, err = os.Open(outfile, s.O_WRONLY|os.O_CREATE|
+		os.O_TRUNC, 0666); err != nil {
+	    log.Fatal(err)
+	}
+}
diff --git a/src/pkg/go/printer/testdata/expressions.raw b/src/pkg/go/printer/testdata/expressions.raw
index f1944c94b..735cd943e 100644
--- a/src/pkg/go/printer/testdata/expressions.raw
+++ b/src/pkg/go/printer/testdata/expressions.raw
@@ -224,11 +224,7 @@ func _() {
 	_ = struct{ x int }{0}
 	_ = struct{ x, y, z int }{0, 1, 2}
 	_ = struct{ int }{0}
-	_ = struct {
-		s struct {
-			int
-		}
-	}{struct{ int }{0}}	// compositeLit context not propagated => multiLine result
+	_ = struct{ s struct{ int } }{struct{ int }{0}}
 }
 
 
@@ -243,7 +239,8 @@ func _() {
 	_ = `foo
 		bar`
 	_ = `three spaces before the end of the line starting here:   
-they must not be removed`}
+they must not be removed`
+}
 
 
 func _() {
@@ -256,8 +253,8 @@ bar`
 	var _ = ``
 	var _ = `foo`
 	var _ =
-	// the next line should not be indented
-`foo
+	// the next line should remain indented
+	`foo
 bar`
 
 	var _ =	// comment
@@ -265,8 +262,8 @@ bar`
 	var _ =	// comment
 	`foo`
 	var _ =	// comment
-	// the next line should not be indented
-`foo
+	// the next line should remain indented
+	`foo
 bar`
 
 	var _ = /* comment */ ``
@@ -279,12 +276,12 @@ bar`
 	var _ =	/* comment */
 	`foo`
 	var _ =	/* comment */
-	// the next line should not be indented
-`foo
+	// the next line should remain indented
+	`foo
 bar`
 
 	var board = []int(
-`...........
+		`...........
 ...........
 ....●●●....
 ....●●●....
@@ -299,8 +296,8 @@ bar`
 
 	var state = S{
 		"foo",
-		// the next line should not be indented
-`...........
+		// the next line should remain indented
+		`...........
 ...........
 ....●●●....
 ....●●●....
@@ -622,3 +619,13 @@ func _() {
 		b.(T).
 		c
 }
+
+
+// Don't introduce extra newlines in strangely formatted expression lists.
+func f() {
+	// os.Open parameters should remain on two lines
+	if writer, err = os.Open(outfile, s.O_WRONLY|os.O_CREATE|
+		os.O_TRUNC,0666); err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/src/pkg/go/printer/testdata/slow.golden b/src/pkg/go/printer/testdata/slow.golden
new file mode 100644
index 000000000..43a15cb1d
--- /dev/null
+++ b/src/pkg/go/printer/testdata/slow.golden
@@ -0,0 +1,85 @@
+// Copyright 2011 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 deepequal_test
+
+import (
+	"testing"
+	"google3/spam/archer/frontend/deepequal"
+)
+
+func TestTwoNilValues(t *testing.T) {
+	if err := deepequal.Check(nil, nil); err != nil {
+		t.Errorf("expected nil, saw %v", err)
+	}
+}
+
+type Foo struct {
+	bar	*Bar
+	bang	*Bar
+}
+
+type Bar struct {
+	baz	*Baz
+	foo	[]*Foo
+}
+
+type Baz struct {
+	entries		map[int]interface{}
+	whatever	string
+}
+
+func newFoo() *Foo {
+	return &Foo{bar: &Bar{baz: &Baz{
+		entries: map[int]interface{}{
+			42:	&Foo{},
+			21:	&Bar{},
+			11:	&Baz{whatever: "it's just a test"}}}},
+		bang: &Bar{foo: []*Foo{
+			&Foo{bar: &Bar{baz: &Baz{
+				entries: map[int]interface{}{
+					43:	&Foo{},
+					22:	&Bar{},
+					13:	&Baz{whatever: "this is nuts"}}}},
+				bang: &Bar{foo: []*Foo{
+					&Foo{bar: &Bar{baz: &Baz{
+						entries: map[int]interface{}{
+							61:	&Foo{},
+							71:	&Bar{},
+							11:	&Baz{whatever: "no, it's Go"}}}},
+						bang: &Bar{foo: []*Foo{
+							&Foo{bar: &Bar{baz: &Baz{
+								entries: map[int]interface{}{
+									0:	&Foo{},
+									-2:	&Bar{},
+									-11:	&Baz{whatever: "we need to go deeper"}}}},
+								bang: &Bar{foo: []*Foo{
+									&Foo{bar: &Bar{baz: &Baz{
+										entries: map[int]interface{}{
+											-2:	&Foo{},
+											-5:	&Bar{},
+											-7:	&Baz{whatever: "are you serious?"}}}},
+										bang:	&Bar{foo: []*Foo{}}},
+									&Foo{bar: &Bar{baz: &Baz{
+										entries: map[int]interface{}{
+											-100:	&Foo{},
+											50:	&Bar{},
+											20:	&Baz{whatever: "na, not really ..."}}}},
+										bang:	&Bar{foo: []*Foo{}}}}}}}}},
+					&Foo{bar: &Bar{baz: &Baz{
+						entries: map[int]interface{}{
+							2:	&Foo{},
+							1:	&Bar{},
+							-1:	&Baz{whatever: "... it's just a test."}}}},
+						bang:	&Bar{foo: []*Foo{}}}}}}}}}
+}
+
+func TestElaborate(t *testing.T) {
+	a := newFoo()
+	b := newFoo()
+
+	if err := deepequal.Check(a, b); err != nil {
+		t.Errorf("expected nil, saw %v", err)
+	}
+}
diff --git a/src/pkg/go/printer/testdata/slow.input b/src/pkg/go/printer/testdata/slow.input
new file mode 100644
index 000000000..0e5a23d88
--- /dev/null
+++ b/src/pkg/go/printer/testdata/slow.input
@@ -0,0 +1,85 @@
+// Copyright 2011 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 deepequal_test
+
+import (
+        "testing"
+        "google3/spam/archer/frontend/deepequal"
+)
+
+func TestTwoNilValues(t *testing.T) {
+        if err := deepequal.Check(nil, nil); err != nil {
+                t.Errorf("expected nil, saw %v", err)
+        }
+}
+
+type Foo struct {
+        bar *Bar
+        bang *Bar
+}
+
+type Bar struct {
+        baz *Baz
+        foo []*Foo
+}
+
+type Baz struct {
+        entries  map[int]interface{}
+        whatever string
+}
+
+func newFoo() (*Foo) {
+return &Foo{bar: &Bar{ baz: &Baz{
+entries: map[int]interface{}{
+42: &Foo{},
+21: &Bar{},
+11: &Baz{ whatever: "it's just a test" }}}},
+        bang: &Bar{foo: []*Foo{
+&Foo{bar: &Bar{ baz: &Baz{
+entries: map[int]interface{}{
+43: &Foo{},
+22: &Bar{},
+13: &Baz{ whatever: "this is nuts" }}}},
+        bang: &Bar{foo: []*Foo{
+&Foo{bar: &Bar{ baz: &Baz{
+entries: map[int]interface{}{
+61: &Foo{},
+71: &Bar{},
+11: &Baz{ whatever: "no, it's Go" }}}},
+        bang: &Bar{foo: []*Foo{
+&Foo{bar: &Bar{ baz: &Baz{
+entries: map[int]interface{}{
+0: &Foo{},
+-2: &Bar{},
+-11: &Baz{ whatever: "we need to go deeper" }}}},
+        bang: &Bar{foo: []*Foo{
+&Foo{bar: &Bar{ baz: &Baz{
+entries: map[int]interface{}{
+-2: &Foo{},
+-5: &Bar{},
+-7: &Baz{ whatever: "are you serious?" }}}},
+        bang: &Bar{foo: []*Foo{}}},
+&Foo{bar: &Bar{ baz: &Baz{
+entries: map[int]interface{}{
+-100: &Foo{},
+50: &Bar{},
+20: &Baz{ whatever: "na, not really ..." }}}},
+        bang: &Bar{foo: []*Foo{}}}}}}}}},
+&Foo{bar: &Bar{ baz: &Baz{
+entries: map[int]interface{}{
+2: &Foo{},
+1: &Bar{},
+-1: &Baz{ whatever: "... it's just a test." }}}},
+        bang: &Bar{foo: []*Foo{}}}}}}}}}
+}
+
+func TestElaborate(t *testing.T) {
+        a := newFoo()
+        b := newFoo()
+
+        if err := deepequal.Check(a, b); err != nil {
+                t.Errorf("expected nil, saw %v", err)
+        }
+}
diff --git a/src/pkg/go/scanner/scanner.go b/src/pkg/go/scanner/scanner.go
index 153707f59..2f949ad25 100644
--- a/src/pkg/go/scanner/scanner.go
+++ b/src/pkg/go/scanner/scanner.go
@@ -177,11 +177,11 @@ var prefix = []byte("//line ")
 func (S *Scanner) interpretLineComment(text []byte) {
 	if bytes.HasPrefix(text, prefix) {
 		// get filename and line number, if any
-		if i := bytes.Index(text, []byte{':'}); i > 0 {
+		if i := bytes.LastIndex(text, []byte{':'}); i > 0 {
 			if line, err := strconv.Atoi(string(text[i+1:])); err == nil && line > 0 {
 				// valid //line filename:line comment;
 				filename := filepath.Clean(string(text[len(prefix):i]))
-				if filename[0] != '/' {
+				if !filepath.IsAbs(filename) {
 					// make filename relative to current directory
 					filename = filepath.Join(S.dir, filename)
 				}
@@ -538,14 +538,12 @@ func (S *Scanner) switch4(tok0, tok1 token.Token, ch2 int, tok2, tok3 token.Toke
 }
 
 
-var newline = []byte{'\n'}
-
-// Scan scans the next token and returns the token position pos,
-// the token tok, and the literal text lit corresponding to the
+// Scan scans the next token and returns the token position,
+// the token, and the literal string corresponding to the
 // token. The source end is indicated by token.EOF.
 //
 // If the returned token is token.SEMICOLON, the corresponding
-// literal value is ";" if the semicolon was present in the source,
+// literal string is ";" if the semicolon was present in the source,
 // and "\n" if the semicolon was inserted because of a newline or
 // at EOF.
 //
@@ -560,7 +558,7 @@ var newline = []byte{'\n'}
 // set with Init. Token positions are relative to that file
 // and thus relative to the file set.
 //
-func (S *Scanner) Scan() (token.Pos, token.Token, []byte) {
+func (S *Scanner) Scan() (token.Pos, token.Token, string) {
 scanAgain:
 	S.skipWhitespace()
 
@@ -586,7 +584,7 @@ scanAgain:
 		case -1:
 			if S.insertSemi {
 				S.insertSemi = false // EOF consumed
-				return S.file.Pos(offs), token.SEMICOLON, newline
+				return S.file.Pos(offs), token.SEMICOLON, "\n"
 			}
 			tok = token.EOF
 		case '\n':
@@ -594,7 +592,7 @@ scanAgain:
 			// set in the first place and exited early
 			// from S.skipWhitespace()
 			S.insertSemi = false // newline consumed
-			return S.file.Pos(offs), token.SEMICOLON, newline
+			return S.file.Pos(offs), token.SEMICOLON, "\n"
 		case '"':
 			insertSemi = true
 			tok = token.STRING
@@ -662,7 +660,7 @@ scanAgain:
 					S.offset = offs
 					S.rdOffset = offs + 1
 					S.insertSemi = false // newline consumed
-					return S.file.Pos(offs), token.SEMICOLON, newline
+					return S.file.Pos(offs), token.SEMICOLON, "\n"
 				}
 				S.scanComment()
 				if S.mode&ScanComments == 0 {
@@ -711,5 +709,9 @@ scanAgain:
 	if S.mode&InsertSemis != 0 {
 		S.insertSemi = insertSemi
 	}
-	return S.file.Pos(offs), tok, S.src[offs:S.offset]
+
+	// TODO(gri): The scanner API should change such that the literal string
+	//            is only valid if an actual literal was scanned. This will
+	//            permit a more efficient implementation.
+	return S.file.Pos(offs), tok, string(S.src[offs:S.offset])
 }
diff --git a/src/pkg/go/scanner/scanner_test.go b/src/pkg/go/scanner/scanner_test.go
index c622ff482..8afb00ee5 100644
--- a/src/pkg/go/scanner/scanner_test.go
+++ b/src/pkg/go/scanner/scanner_test.go
@@ -7,6 +7,8 @@ package scanner
 import (
 	"go/token"
 	"os"
+	"path/filepath"
+	"runtime"
 	"testing"
 )
 
@@ -232,12 +234,11 @@ func TestScan(t *testing.T) {
 	index := 0
 	epos := token.Position{"", 0, 1, 1} // expected position
 	for {
-		pos, tok, litb := s.Scan()
+		pos, tok, lit := s.Scan()
 		e := elt{token.EOF, "", special}
 		if index < len(tokens) {
 			e = tokens[index]
 		}
-		lit := string(litb)
 		if tok == token.EOF {
 			lit = ""
 			epos.Line = src_linecount
@@ -255,7 +256,7 @@ func TestScan(t *testing.T) {
 		}
 		epos.Offset += len(lit) + len(whitespace)
 		epos.Line += newlineCount(lit) + whitespace_linecount
-		if tok == token.COMMENT && litb[1] == '/' {
+		if tok == token.COMMENT && lit[1] == '/' {
 			// correct for unaccounted '/n' in //-style comment
 			epos.Offset++
 			epos.Line++
@@ -290,7 +291,7 @@ func checkSemi(t *testing.T, line string, mode uint) {
 			semiPos.Column++
 			pos, tok, lit = S.Scan()
 			if tok == token.SEMICOLON {
-				if string(lit) != semiLit {
+				if lit != semiLit {
 					t.Errorf(`bad literal for %q: got %q, expected %q`, line, lit, semiLit)
 				}
 				checkPos(t, line, pos, semiPos)
@@ -443,32 +444,41 @@ func TestSemis(t *testing.T) {
 	}
 }
 
-
-var segments = []struct {
+type segment struct {
 	srcline  string // a line of source text
 	filename string // filename for current token
 	line     int    // line number for current token
-}{
+}
+
+var segments = []segment{
 	// exactly one token per line since the test consumes one token per segment
-	{"  line1", "dir/TestLineComments", 1},
-	{"\nline2", "dir/TestLineComments", 2},
-	{"\nline3  //line File1.go:100", "dir/TestLineComments", 3}, // bad line comment, ignored
-	{"\nline4", "dir/TestLineComments", 4},
-	{"\n//line File1.go:100\n  line100", "dir/File1.go", 100},
-	{"\n//line File2.go:200\n  line200", "dir/File2.go", 200},
+	{"  line1", filepath.Join("dir", "TestLineComments"), 1},
+	{"\nline2", filepath.Join("dir", "TestLineComments"), 2},
+	{"\nline3  //line File1.go:100", filepath.Join("dir", "TestLineComments"), 3}, // bad line comment, ignored
+	{"\nline4", filepath.Join("dir", "TestLineComments"), 4},
+	{"\n//line File1.go:100\n  line100", filepath.Join("dir", "File1.go"), 100},
+	{"\n//line File2.go:200\n  line200", filepath.Join("dir", "File2.go"), 200},
 	{"\n//line :1\n  line1", "dir", 1},
-	{"\n//line foo:42\n  line42", "dir/foo", 42},
-	{"\n //line foo:42\n  line44", "dir/foo", 44},           // bad line comment, ignored
-	{"\n//line foo 42\n  line46", "dir/foo", 46},            // bad line comment, ignored
-	{"\n//line foo:42 extra text\n  line48", "dir/foo", 48}, // bad line comment, ignored
-	{"\n//line /bar:42\n  line42", "/bar", 42},
-	{"\n//line ./foo:42\n  line42", "dir/foo", 42},
-	{"\n//line a/b/c/File1.go:100\n  line100", "dir/a/b/c/File1.go", 100},
+	{"\n//line foo:42\n  line42", filepath.Join("dir", "foo"), 42},
+	{"\n //line foo:42\n  line44", filepath.Join("dir", "foo"), 44},           // bad line comment, ignored
+	{"\n//line foo 42\n  line46", filepath.Join("dir", "foo"), 46},            // bad line comment, ignored
+	{"\n//line foo:42 extra text\n  line48", filepath.Join("dir", "foo"), 48}, // bad line comment, ignored
+	{"\n//line /bar:42\n  line42", string(filepath.Separator) + "bar", 42},
+	{"\n//line ./foo:42\n  line42", filepath.Join("dir", "foo"), 42},
+	{"\n//line a/b/c/File1.go:100\n  line100", filepath.Join("dir", "a", "b", "c", "File1.go"), 100},
+}
+
+var winsegments = []segment{
+	{"\n//line c:\\dir\\File1.go:100\n  line100", "c:\\dir\\File1.go", 100},
 }
 
 
 // Verify that comments of the form "//line filename:line" are interpreted correctly.
 func TestLineComments(t *testing.T) {
+	if runtime.GOOS == "windows" {
+		segments = append(segments, winsegments...)
+	}
+
 	// make source
 	var src string
 	for _, e := range segments {
@@ -477,12 +487,12 @@ func TestLineComments(t *testing.T) {
 
 	// verify scan
 	var S Scanner
-	file := fset.AddFile("dir/TestLineComments", fset.Base(), len(src))
+	file := fset.AddFile(filepath.Join("dir", "TestLineComments"), fset.Base(), len(src))
 	S.Init(file, []byte(src), nil, 0)
 	for _, s := range segments {
 		p, _, lit := S.Scan()
 		pos := file.Position(p)
-		checkPos(t, string(lit), p, token.Position{s.filename, pos.Offset, s.line, pos.Column})
+		checkPos(t, lit, p, token.Position{s.filename, pos.Offset, s.line, pos.Column})
 	}
 
 	if S.ErrorCount != 0 {
@@ -536,10 +546,10 @@ func TestIllegalChars(t *testing.T) {
 	for offs, ch := range src {
 		pos, tok, lit := s.Scan()
 		if poffs := file.Offset(pos); poffs != offs {
-			t.Errorf("bad position for %s: got %d, expected %d", string(lit), poffs, offs)
+			t.Errorf("bad position for %s: got %d, expected %d", lit, poffs, offs)
 		}
-		if tok == token.ILLEGAL && string(lit) != string(ch) {
-			t.Errorf("bad token: got %s, expected %s", string(lit), string(ch))
+		if tok == token.ILLEGAL && lit != string(ch) {
+			t.Errorf("bad token: got %s, expected %s", lit, string(ch))
 		}
 	}
 
diff --git a/src/pkg/go/token/token.go b/src/pkg/go/token/token.go
index 2a2d3ecc4..a5f21df16 100644
--- a/src/pkg/go/token/token.go
+++ b/src/pkg/go/token/token.go
@@ -126,10 +126,7 @@ const (
 )
 
 
-// At the moment we have no array literal syntax that lets us describe
-// the index for each element - use a map for now to make sure they are
-// in sync.
-var tokens = map[Token]string{
+var tokens = [...]string{
 	ILLEGAL: "ILLEGAL",
 
 	EOF:     "EOF",
@@ -237,10 +234,14 @@ var tokens = map[Token]string{
 // constant name (e.g. for the token IDENT, the string is "IDENT").
 //
 func (tok Token) String() string {
-	if str, exists := tokens[tok]; exists {
-		return str
+	s := ""
+	if 0 <= tok && tok < Token(len(tokens)) {
+		s = tokens[tok]
 	}
-	return "token(" + strconv.Itoa(int(tok)) + ")"
+	if s == "" {
+		s = "token(" + strconv.Itoa(int(tok)) + ")"
+	}
+	return s
 }
 
 
diff --git a/src/pkg/go/typechecker/Makefile b/src/pkg/go/typechecker/Makefile
index 62b2aa7fe..83af3ef4e 100644
--- a/src/pkg/go/typechecker/Makefile
+++ b/src/pkg/go/typechecker/Makefile
@@ -7,6 +7,7 @@ include ../../../Make.inc
 TARG=go/typechecker
 GOFILES=\
 	scope.go\
+	type.go\
 	typechecker.go\
 	universe.go\
 
diff --git a/src/pkg/go/typechecker/scope.go b/src/pkg/go/typechecker/scope.go
index 114c93ea8..a4bee6e69 100644
--- a/src/pkg/go/typechecker/scope.go
+++ b/src/pkg/go/typechecker/scope.go
@@ -2,15 +2,15 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// This file implements scope support functions.
+// DEPRECATED FILE - WILL GO AWAY EVENTUALLY.
+//
+// Scope handling is now done in go/parser.
+// The functionality here is only present to
+// keep the typechecker running for now.
 
 package typechecker
 
-import (
-	"fmt"
-	"go/ast"
-	"go/token"
-)
+import "go/ast"
 
 
 func (tc *typechecker) openScope() *ast.Scope {
@@ -24,52 +24,25 @@ func (tc *typechecker) closeScope() {
 }
 
 
-// objPos computes the source position of the declaration of an object name.
-// Only required for error reporting, so doesn't have to be fast.
-func objPos(obj *ast.Object) (pos token.Pos) {
-	switch d := obj.Decl.(type) {
-	case *ast.Field:
-		for _, n := range d.Names {
-			if n.Name == obj.Name {
-				return n.Pos()
-			}
-		}
-	case *ast.ValueSpec:
-		for _, n := range d.Names {
-			if n.Name == obj.Name {
-				return n.Pos()
-			}
-		}
-	case *ast.TypeSpec:
-		return d.Name.Pos()
-	case *ast.FuncDecl:
-		return d.Name.Pos()
-	}
-	if debug {
-		fmt.Printf("decl = %T\n", obj.Decl)
-	}
-	panic("unreachable")
-}
-
-
 // declInScope declares an object of a given kind and name in scope and sets the object's Decl and N fields.
 // It returns the newly allocated object. If an object with the same name already exists in scope, an error
 // is reported and the object is not inserted.
-// (Objects with _ name are always inserted into a scope without errors, but they cannot be found.)
-func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.Kind, name *ast.Ident, decl interface{}, n int) *ast.Object {
+func (tc *typechecker) declInScope(scope *ast.Scope, kind ast.ObjKind, name *ast.Ident, decl interface{}, n int) *ast.Object {
 	obj := ast.NewObj(kind, name.Name)
 	obj.Decl = decl
-	obj.N = n
+	//obj.N = n
 	name.Obj = obj
-	if alt := scope.Insert(obj); alt != obj {
-		tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, objPos(alt))
+	if name.Name != "_" {
+		if alt := scope.Insert(obj); alt != nil {
+			tc.Errorf(name.Pos(), "%s already declared at %s", name.Name, tc.fset.Position(alt.Pos()).String())
+		}
 	}
 	return obj
 }
 
 
 // decl is the same as declInScope(tc.topScope, ...)
-func (tc *typechecker) decl(kind ast.Kind, name *ast.Ident, decl interface{}, n int) *ast.Object {
+func (tc *typechecker) decl(kind ast.ObjKind, name *ast.Ident, decl interface{}, n int) *ast.Object {
 	return tc.declInScope(tc.topScope, kind, name, decl, n)
 }
 
@@ -91,7 +64,7 @@ func (tc *typechecker) find(name *ast.Ident) (obj *ast.Object) {
 
 // findField returns the object with the given name if visible in the type's scope.
 // If no such object is found, an error is reported and a bad object is returned instead.
-func (tc *typechecker) findField(typ *ast.Type, name *ast.Ident) (obj *ast.Object) {
+func (tc *typechecker) findField(typ *Type, name *ast.Ident) (obj *ast.Object) {
 	// TODO(gri) This is simplistic at the moment and ignores anonymous fields.
 	obj = typ.Scope.Lookup(name.Name)
 	if obj == nil {
@@ -100,20 +73,3 @@ func (tc *typechecker) findField(typ *ast.Type, name *ast.Ident) (obj *ast.Objec
 	}
 	return
 }
-
-
-// printScope prints the objects in a scope.
-func printScope(scope *ast.Scope) {
-	fmt.Printf("scope %p {", scope)
-	if scope != nil && len(scope.Objects) > 0 {
-		fmt.Println()
-		for _, obj := range scope.Objects {
-			form := "void"
-			if obj.Type != nil {
-				form = obj.Type.Form.String()
-			}
-			fmt.Printf("\t%s\t%s\n", obj.Name, form)
-		}
-	}
-	fmt.Printf("}\n")
-}
diff --git a/src/pkg/go/typechecker/testdata/test0.go b/src/pkg/go/typechecker/testdata/test0.go
deleted file mode 100644
index 4e317f214..000000000
--- a/src/pkg/go/typechecker/testdata/test0.go
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2010 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.
-
-// type declarations
-
-package P0
-
-type (
-	B bool
-	I int32
-	A [10]P
-	T struct {
-		x, y P
-	}
-	P *T
-	R *R
-	F func(A) I
-	Y interface {
-		f(A) I
-	}
-	S []P
-	M map[I]F
-	C chan<- I
-)
-
-type (
-	a/* ERROR "illegal cycle" */ a
-	a/* ERROR "already declared" */ int
-
-	b/* ERROR "illegal cycle" */ c
-	c d
-	d e
-	e b /* ERROR "not a type" */
-
-	t *t
-
-	U V
-	V W
-	W *U
-
-	P1 *S2
-	P2 P1
-
-	S1 struct {
-		a, b, c int
-		u, v, a/* ERROR "already declared" */ float
-	}
-	S2/* ERROR "illegal cycle" */ struct {
-		x S2
-	}
-
-	L1 []L1
-	L2 []int
-
-	A1 [10]int
-	A2/* ERROR "illegal cycle" */ [10]A2
-	A3/* ERROR "illegal cycle" */ [10]struct {
-		x A4
-	}
-	A4 [10]A3
-
-	F1 func()
-	F2 func(x, y, z float)
-	F3 func(x, y, x /* ERROR "already declared" */ float)
-	F4 func() (x, y, x /* ERROR "already declared" */ float)
-	F5 func(x int) (x /* ERROR "already declared" */ float)
-
-	I1 interface{}
-	I2 interface {
-		m1()
-	}
-	I3 interface {
-		m1()
-		m1 /* ERROR "already declared" */ ()
-	}
-	I4 interface {
-		m1(x, y, x /* ERROR "already declared" */ float)
-		m2() (x, y, x /* ERROR "already declared" */ float)
-		m3(x int) (x /* ERROR "already declared" */ float)
-	}
-	I5 interface {
-		m1(I5)
-	}
-
-	C1 chan int
-	C2 <-chan int
-	C3 chan<- C3
-
-	M1 map[Last]string
-	M2 map[string]M2
-
-	Last int
-)
diff --git a/src/pkg/go/typechecker/testdata/test0.src b/src/pkg/go/typechecker/testdata/test0.src
new file mode 100644
index 000000000..4e317f214
--- /dev/null
+++ b/src/pkg/go/typechecker/testdata/test0.src
@@ -0,0 +1,94 @@
+// Copyright 2010 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.
+
+// type declarations
+
+package P0
+
+type (
+	B bool
+	I int32
+	A [10]P
+	T struct {
+		x, y P
+	}
+	P *T
+	R *R
+	F func(A) I
+	Y interface {
+		f(A) I
+	}
+	S []P
+	M map[I]F
+	C chan<- I
+)
+
+type (
+	a/* ERROR "illegal cycle" */ a
+	a/* ERROR "already declared" */ int
+
+	b/* ERROR "illegal cycle" */ c
+	c d
+	d e
+	e b /* ERROR "not a type" */
+
+	t *t
+
+	U V
+	V W
+	W *U
+
+	P1 *S2
+	P2 P1
+
+	S1 struct {
+		a, b, c int
+		u, v, a/* ERROR "already declared" */ float
+	}
+	S2/* ERROR "illegal cycle" */ struct {
+		x S2
+	}
+
+	L1 []L1
+	L2 []int
+
+	A1 [10]int
+	A2/* ERROR "illegal cycle" */ [10]A2
+	A3/* ERROR "illegal cycle" */ [10]struct {
+		x A4
+	}
+	A4 [10]A3
+
+	F1 func()
+	F2 func(x, y, z float)
+	F3 func(x, y, x /* ERROR "already declared" */ float)
+	F4 func() (x, y, x /* ERROR "already declared" */ float)
+	F5 func(x int) (x /* ERROR "already declared" */ float)
+
+	I1 interface{}
+	I2 interface {
+		m1()
+	}
+	I3 interface {
+		m1()
+		m1 /* ERROR "already declared" */ ()
+	}
+	I4 interface {
+		m1(x, y, x /* ERROR "already declared" */ float)
+		m2() (x, y, x /* ERROR "already declared" */ float)
+		m3(x int) (x /* ERROR "already declared" */ float)
+	}
+	I5 interface {
+		m1(I5)
+	}
+
+	C1 chan int
+	C2 <-chan int
+	C3 chan<- C3
+
+	M1 map[Last]string
+	M2 map[string]M2
+
+	Last int
+)
diff --git a/src/pkg/go/typechecker/testdata/test1.go b/src/pkg/go/typechecker/testdata/test1.go
deleted file mode 100644
index b0808ee7a..000000000
--- a/src/pkg/go/typechecker/testdata/test1.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2010 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.
-
-// const and var declarations
-
-package P1
-
-const (
-	c1         /* ERROR "missing initializer" */
-	c2     int = 0
-	c3, c4 = 0
-)
diff --git a/src/pkg/go/typechecker/testdata/test1.src b/src/pkg/go/typechecker/testdata/test1.src
new file mode 100644
index 000000000..b5531fb9f
--- /dev/null
+++ b/src/pkg/go/typechecker/testdata/test1.src
@@ -0,0 +1,13 @@
+// Copyright 2010 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.
+
+// const and var declarations
+
+package P1
+
+const (
+	c1 = 0
+	c2     int = 0
+	c3, c4 = 0
+)
diff --git a/src/pkg/go/typechecker/testdata/test3.go b/src/pkg/go/typechecker/testdata/test3.go
deleted file mode 100644
index ea35808a0..000000000
--- a/src/pkg/go/typechecker/testdata/test3.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright 2010 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 P3
-
-// function and method signatures
-
-func _()                                        {}
-func _()                                        {}
-func _(x, x /* ERROR "already declared" */ int) {}
-
-func f()                                 {}
-func f /* ERROR "already declared" */ () {}
-
-func (*foo /* ERROR "invalid receiver" */ ) m() {}
-func (bar /* ERROR "not a type" */ ) m()        {}
-
-func f1(x, _, _ int) (_, _ float)                              {}
-func f2(x, y, x /* ERROR "already declared" */ int)            {}
-func f3(x, y int) (a, b, x /* ERROR "already declared" */ int) {}
-
-func (x *T) m1()                                 {}
-func (x *T) m1 /* ERROR "already declared" */ () {}
-func (x T) m1 /* ERROR "already declared" */ ()  {}
-func (T) m1 /* ERROR "already declared" */ ()    {}
-
-func (x *T) m2(u, x /* ERROR "already declared" */ int)               {}
-func (x *T) m3(a, b, c int) (u, x /* ERROR "already declared" */ int) {}
-func (T) _(x, x /* ERROR "already declared" */ int)                   {}
-func (T) _() (x, x /* ERROR "already declared" */ int)                {}
-
-//func (PT) _() {}
-
-var bar int
-
-type T struct{}
-type PT (T)
diff --git a/src/pkg/go/typechecker/testdata/test3.src b/src/pkg/go/typechecker/testdata/test3.src
new file mode 100644
index 000000000..2e1a9fa8f
--- /dev/null
+++ b/src/pkg/go/typechecker/testdata/test3.src
@@ -0,0 +1,41 @@
+// Copyright 2010 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 P3
+
+// function and method signatures
+
+func _()                                        {}
+func _()                                        {}
+func _(x, x /* ERROR "already declared" */ int) {}
+
+func f()                                 {}
+func f /* ERROR "already declared" */ () {}
+
+func (*foo /* ERROR "invalid receiver" */ ) m() {}
+func (bar /* ERROR "not a type" */ ) m()        {}
+
+func f1(x, _, _ int) (_, _ float)                              {}
+func f2(x, y, x /* ERROR "already declared" */ int)            {}
+func f3(x, y int) (a, b, x /* ERROR "already declared" */ int) {}
+
+func (x *T) m1()                                 {}
+func (x *T) m1 /* ERROR "already declared" */ () {}
+func (x T) m1 /* ERROR "already declared" */ ()  {}
+func (T) m1 /* ERROR "already declared" */ ()    {}
+
+func (x *T) m2(u, x /* ERROR "already declared" */ int)               {}
+func (x *T) m3(a, b, c int) (u, x /* ERROR "already declared" */ int) {}
+// The following are disabled for now because the typechecker
+// in in the process of being rewritten and cannot handle them
+// at the moment
+//func (T) _(x, x /* "already declared" */ int)                   {}
+//func (T) _() (x, x /* "already declared" */ int)                {}
+
+//func (PT) _() {}
+
+var bar int
+
+type T struct{}
+type PT (T)
diff --git a/src/pkg/go/typechecker/testdata/test4.go b/src/pkg/go/typechecker/testdata/test4.go
deleted file mode 100644
index bb9aee3ad..000000000
--- a/src/pkg/go/typechecker/testdata/test4.go
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2010 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.
-
-// Constant declarations
-
-package P4
-
-const (
-	c0 /* ERROR "missing initializer" */
-)
diff --git a/src/pkg/go/typechecker/testdata/test4.src b/src/pkg/go/typechecker/testdata/test4.src
new file mode 100644
index 000000000..94d3558f9
--- /dev/null
+++ b/src/pkg/go/typechecker/testdata/test4.src
@@ -0,0 +1,11 @@
+// Copyright 2010 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.
+
+// Constant declarations
+
+package P4
+
+const (
+	c0 = 0
+)
diff --git a/src/pkg/go/typechecker/type.go b/src/pkg/go/typechecker/type.go
new file mode 100644
index 000000000..62b4e9d3e
--- /dev/null
+++ b/src/pkg/go/typechecker/type.go
@@ -0,0 +1,125 @@
+// Copyright 2010 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 typechecker
+
+import "go/ast"
+
+
+// A Type represents a Go type.
+type Type struct {
+	Form     Form
+	Obj      *ast.Object // corresponding type name, or nil
+	Scope    *ast.Scope  // fields and methods, always present
+	N        uint        // basic type id, array length, number of function results, or channel direction
+	Key, Elt *Type       // map key and array, pointer, slice, map or channel element
+	Params   *ast.Scope  // function (receiver, input and result) parameters, tuple expressions (results of function calls), or nil
+	Expr     ast.Expr    // corresponding AST expression
+}
+
+
+// NewType creates a new type of a given form.
+func NewType(form Form) *Type {
+	return &Type{Form: form, Scope: ast.NewScope(nil)}
+}
+
+
+// Form describes the form of a type.
+type Form int
+
+// The list of possible type forms.
+const (
+	BadType    Form = iota // for error handling
+	Unresolved             // type not fully setup
+	Basic
+	Array
+	Struct
+	Pointer
+	Function
+	Method
+	Interface
+	Slice
+	Map
+	Channel
+	Tuple
+)
+
+
+var formStrings = [...]string{
+	BadType:    "badType",
+	Unresolved: "unresolved",
+	Basic:      "basic",
+	Array:      "array",
+	Struct:     "struct",
+	Pointer:    "pointer",
+	Function:   "function",
+	Method:     "method",
+	Interface:  "interface",
+	Slice:      "slice",
+	Map:        "map",
+	Channel:    "channel",
+	Tuple:      "tuple",
+}
+
+
+func (form Form) String() string { return formStrings[form] }
+
+
+// The list of basic type id's.
+const (
+	Bool = iota
+	Byte
+	Uint
+	Int
+	Float
+	Complex
+	Uintptr
+	String
+
+	Uint8
+	Uint16
+	Uint32
+	Uint64
+
+	Int8
+	Int16
+	Int32
+	Int64
+
+	Float32
+	Float64
+
+	Complex64
+	Complex128
+
+	// TODO(gri) ideal types are missing
+)
+
+
+var BasicTypes = map[uint]string{
+	Bool:    "bool",
+	Byte:    "byte",
+	Uint:    "uint",
+	Int:     "int",
+	Float:   "float",
+	Complex: "complex",
+	Uintptr: "uintptr",
+	String:  "string",
+
+	Uint8:  "uint8",
+	Uint16: "uint16",
+	Uint32: "uint32",
+	Uint64: "uint64",
+
+	Int8:  "int8",
+	Int16: "int16",
+	Int32: "int32",
+	Int64: "int64",
+
+	Float32: "float32",
+	Float64: "float64",
+
+	Complex64:  "complex64",
+	Complex128: "complex128",
+}
diff --git a/src/pkg/go/typechecker/typechecker.go b/src/pkg/go/typechecker/typechecker.go
index e9aefa240..b151f5834 100644
--- a/src/pkg/go/typechecker/typechecker.go
+++ b/src/pkg/go/typechecker/typechecker.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.
 
-// INCOMPLETE PACKAGE.
+// DEPRECATED PACKAGE - SEE go/types INSTEAD.
 // This package implements typechecking of a Go AST.
 // The result of the typecheck is an augmented AST
 // with object and type information for each identifier.
@@ -53,7 +53,7 @@ func CheckPackage(fset *token.FileSet, pkg *ast.Package, importer Importer) os.E
 //
 func CheckFile(fset *token.FileSet, file *ast.File, importer Importer) os.Error {
 	// create a single-file dummy package
-	pkg := &ast.Package{file.Name.Name, nil, map[string]*ast.File{fset.Position(file.Name.NamePos).Filename: file}}
+	pkg := &ast.Package{file.Name.Name, nil, nil, map[string]*ast.File{fset.Position(file.Name.NamePos).Filename: file}}
 	return CheckPackage(fset, pkg, importer)
 }
 
@@ -65,6 +65,7 @@ type typechecker struct {
 	fset *token.FileSet
 	scanner.ErrorVector
 	importer Importer
+	globals  []*ast.Object        // list of global objects
 	topScope *ast.Scope           // current top-most scope
 	cyclemap map[*ast.Object]bool // for cycle detection
 	iota     int                  // current value of iota
@@ -94,7 +95,7 @@ phase 1: declare all global objects; also collect all function and method declar
 	- report global double declarations
 
 phase 2: bind methods to their receiver base types
-	- received base types must be declared in the package, thus for
+	- receiver base types must be declared in the package, thus for
 	  each method a corresponding (unresolved) type must exist
 	- report method double declarations and errors with base types
 
@@ -142,16 +143,16 @@ func (tc *typechecker) checkPackage(pkg *ast.Package) {
 	}
 
 	// phase 3: resolve all global objects
-	// (note that objects with _ name are also in the scope)
 	tc.cyclemap = make(map[*ast.Object]bool)
-	for _, obj := range tc.topScope.Objects {
+	for _, obj := range tc.globals {
 		tc.resolve(obj)
 	}
 	assert(len(tc.cyclemap) == 0)
 
 	// 4: sequentially typecheck function and method bodies
 	for _, f := range funcs {
-		tc.checkBlock(f.Body.List, f.Name.Obj.Type)
+		ftype, _ := f.Name.Obj.Type.(*Type)
+		tc.checkBlock(f.Body.List, ftype)
 	}
 
 	pkg.Scope = tc.topScope
@@ -183,11 +184,11 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
 						}
 					}
 					for _, name := range s.Names {
-						tc.decl(ast.Con, name, s, iota)
+						tc.globals = append(tc.globals, tc.decl(ast.Con, name, s, iota))
 					}
 				case token.VAR:
 					for _, name := range s.Names {
-						tc.decl(ast.Var, name, s, 0)
+						tc.globals = append(tc.globals, tc.decl(ast.Var, name, s, 0))
 					}
 				default:
 					panic("unreachable")
@@ -196,9 +197,10 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
 				iota++
 			case *ast.TypeSpec:
 				obj := tc.decl(ast.Typ, s.Name, s, 0)
+				tc.globals = append(tc.globals, obj)
 				// give all type objects an unresolved type so
 				// that we can collect methods in the type scope
-				typ := ast.NewType(ast.Unresolved)
+				typ := NewType(Unresolved)
 				obj.Type = typ
 				typ.Obj = obj
 			default:
@@ -208,7 +210,7 @@ func (tc *typechecker) declGlobal(global ast.Decl) {
 
 	case *ast.FuncDecl:
 		if d.Recv == nil {
-			tc.decl(ast.Fun, d.Name, d, 0)
+			tc.globals = append(tc.globals, tc.decl(ast.Fun, d.Name, d, 0))
 		}
 
 	default:
@@ -239,8 +241,8 @@ func (tc *typechecker) bindMethod(method *ast.FuncDecl) {
 		} else if obj.Kind != ast.Typ {
 			tc.Errorf(name.Pos(), "invalid receiver: %s is not a type", name.Name)
 		} else {
-			typ := obj.Type
-			assert(typ.Form == ast.Unresolved)
+			typ := obj.Type.(*Type)
+			assert(typ.Form == Unresolved)
 			scope = typ.Scope
 		}
 	}
@@ -261,7 +263,7 @@ func (tc *typechecker) bindMethod(method *ast.FuncDecl) {
 func (tc *typechecker) resolve(obj *ast.Object) {
 	// check for declaration cycles
 	if tc.cyclemap[obj] {
-		tc.Errorf(objPos(obj), "illegal cycle in declaration of %s", obj.Name)
+		tc.Errorf(obj.Pos(), "illegal cycle in declaration of %s", obj.Name)
 		obj.Kind = ast.Bad
 		return
 	}
@@ -271,7 +273,7 @@ func (tc *typechecker) resolve(obj *ast.Object) {
 	}()
 
 	// resolve non-type objects
-	typ := obj.Type
+	typ, _ := obj.Type.(*Type)
 	if typ == nil {
 		switch obj.Kind {
 		case ast.Bad:
@@ -282,12 +284,12 @@ func (tc *typechecker) resolve(obj *ast.Object) {
 
 		case ast.Var:
 			tc.declVar(obj)
-			//obj.Type = tc.typeFor(nil, obj.Decl.(*ast.ValueSpec).Type, false)
+			obj.Type = tc.typeFor(nil, obj.Decl.(*ast.ValueSpec).Type, false)
 
 		case ast.Fun:
-			obj.Type = ast.NewType(ast.Function)
+			obj.Type = NewType(Function)
 			t := obj.Decl.(*ast.FuncDecl).Type
-			tc.declSignature(obj.Type, nil, t.Params, t.Results)
+			tc.declSignature(obj.Type.(*Type), nil, t.Params, t.Results)
 
 		default:
 			// type objects have non-nil types when resolve is called
@@ -300,32 +302,34 @@ func (tc *typechecker) resolve(obj *ast.Object) {
 	}
 
 	// resolve type objects
-	if typ.Form == ast.Unresolved {
+	if typ.Form == Unresolved {
 		tc.typeFor(typ, typ.Obj.Decl.(*ast.TypeSpec).Type, false)
 
 		// provide types for all methods
 		for _, obj := range typ.Scope.Objects {
 			if obj.Kind == ast.Fun {
 				assert(obj.Type == nil)
-				obj.Type = ast.NewType(ast.Method)
+				obj.Type = NewType(Method)
 				f := obj.Decl.(*ast.FuncDecl)
 				t := f.Type
-				tc.declSignature(obj.Type, f.Recv, t.Params, t.Results)
+				tc.declSignature(obj.Type.(*Type), f.Recv, t.Params, t.Results)
 			}
 		}
 	}
 }
 
 
-func (tc *typechecker) checkBlock(body []ast.Stmt, ftype *ast.Type) {
+func (tc *typechecker) checkBlock(body []ast.Stmt, ftype *Type) {
 	tc.openScope()
 	defer tc.closeScope()
 
 	// inject function/method parameters into block scope, if any
 	if ftype != nil {
 		for _, par := range ftype.Params.Objects {
-			obj := tc.topScope.Insert(par)
-			assert(obj == par) // ftype has no double declarations
+			if par.Name != "_" {
+				alt := tc.topScope.Insert(par)
+				assert(alt == nil) // ftype has no double declarations
+			}
 		}
 	}
 
@@ -362,8 +366,8 @@ func (tc *typechecker) declFields(scope *ast.Scope, fields *ast.FieldList, ref b
 }
 
 
-func (tc *typechecker) declSignature(typ *ast.Type, recv, params, results *ast.FieldList) {
-	assert((typ.Form == ast.Method) == (recv != nil))
+func (tc *typechecker) declSignature(typ *Type, recv, params, results *ast.FieldList) {
+	assert((typ.Form == Method) == (recv != nil))
 	typ.Params = ast.NewScope(nil)
 	tc.declFields(typ.Params, recv, true)
 	tc.declFields(typ.Params, params, true)
@@ -371,7 +375,7 @@ func (tc *typechecker) declSignature(typ *ast.Type, recv, params, results *ast.F
 }
 
 
-func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Type) {
+func (tc *typechecker) typeFor(def *Type, x ast.Expr, ref bool) (typ *Type) {
 	x = unparen(x)
 
 	// type name
@@ -381,10 +385,10 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
 		if obj.Kind != ast.Typ {
 			tc.Errorf(t.Pos(), "%s is not a type", t.Name)
 			if def == nil {
-				typ = ast.NewType(ast.BadType)
+				typ = NewType(BadType)
 			} else {
 				typ = def
-				typ.Form = ast.BadType
+				typ.Form = BadType
 			}
 			typ.Expr = x
 			return
@@ -393,7 +397,7 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
 		if !ref {
 			tc.resolve(obj) // check for cycles even if type resolved
 		}
-		typ = obj.Type
+		typ = obj.Type.(*Type)
 
 		if def != nil {
 			// new type declaration: copy type structure
@@ -410,7 +414,7 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
 	// type literal
 	typ = def
 	if typ == nil {
-		typ = ast.NewType(ast.BadType)
+		typ = NewType(BadType)
 	}
 	typ.Expr = x
 
@@ -419,42 +423,42 @@ func (tc *typechecker) typeFor(def *ast.Type, x ast.Expr, ref bool) (typ *ast.Ty
 		if debug {
 			fmt.Println("qualified identifier unimplemented")
 		}
-		typ.Form = ast.BadType
+		typ.Form = BadType
 
 	case *ast.StarExpr:
-		typ.Form = ast.Pointer
+		typ.Form = Pointer
 		typ.Elt = tc.typeFor(nil, t.X, true)
 
 	case *ast.ArrayType:
 		if t.Len != nil {
-			typ.Form = ast.Array
+			typ.Form = Array
 			// TODO(gri) compute the real length
 			// (this may call resolve recursively)
 			(*typ).N = 42
 		} else {
-			typ.Form = ast.Slice
+			typ.Form = Slice
 		}
 		typ.Elt = tc.typeFor(nil, t.Elt, t.Len == nil)
 
 	case *ast.StructType:
-		typ.Form = ast.Struct
+		typ.Form = Struct
 		tc.declFields(typ.Scope, t.Fields, false)
 
 	case *ast.FuncType:
-		typ.Form = ast.Function
+		typ.Form = Function
 		tc.declSignature(typ, nil, t.Params, t.Results)
 
 	case *ast.InterfaceType:
-		typ.Form = ast.Interface
+		typ.Form = Interface
 		tc.declFields(typ.Scope, t.Methods, true)
 
 	case *ast.MapType:
-		typ.Form = ast.Map
+		typ.Form = Map
 		typ.Key = tc.typeFor(nil, t.Key, true)
 		typ.Elt = tc.typeFor(nil, t.Value, true)
 
 	case *ast.ChanType:
-		typ.Form = ast.Channel
+		typ.Form = Channel
 		typ.N = uint(t.Dir)
 		typ.Elt = tc.typeFor(nil, t.Value, true)
 
diff --git a/src/pkg/go/typechecker/typechecker_test.go b/src/pkg/go/typechecker/typechecker_test.go
index 33f4a6223..d16e06921 100644
--- a/src/pkg/go/typechecker/typechecker_test.go
+++ b/src/pkg/go/typechecker/typechecker_test.go
@@ -78,7 +78,7 @@ func expectedErrors(t *testing.T, pkg *ast.Package) (list scanner.ErrorList) {
 			case token.EOF:
 				break loop
 			case token.COMMENT:
-				s := errRx.FindSubmatch(lit)
+				s := errRx.FindStringSubmatch(lit)
 				if len(s) == 2 {
 					list = append(list, &scanner.Error{fset.Position(prev), string(s[1])})
 				}
@@ -93,7 +93,7 @@ func expectedErrors(t *testing.T, pkg *ast.Package) (list scanner.ErrorList) {
 
 
 func testFilter(f *os.FileInfo) bool {
-	return strings.HasSuffix(f.Name, ".go") && f.Name[0] != '.'
+	return strings.HasSuffix(f.Name, ".src") && f.Name[0] != '.'
 }
 
 
diff --git a/src/pkg/go/typechecker/universe.go b/src/pkg/go/typechecker/universe.go
index db950737f..abc8bbbd4 100644
--- a/src/pkg/go/typechecker/universe.go
+++ b/src/pkg/go/typechecker/universe.go
@@ -14,7 +14,7 @@ var Universe *ast.Scope
 
 func def(obj *ast.Object) {
 	alt := Universe.Insert(obj)
-	if alt != obj {
+	if alt != nil {
 		panic("object declared twice")
 	}
 }
@@ -24,8 +24,8 @@ func init() {
 	Universe = ast.NewScope(nil)
 
 	// basic types
-	for n, name := range ast.BasicTypes {
-		typ := ast.NewType(ast.Basic)
+	for n, name := range BasicTypes {
+		typ := NewType(Basic)
 		typ.N = n
 		obj := ast.NewObj(ast.Typ, name)
 		obj.Type = typ
diff --git a/src/pkg/go/types/Makefile b/src/pkg/go/types/Makefile
new file mode 100644
index 000000000..54e762b36
--- /dev/null
+++ b/src/pkg/go/types/Makefile
@@ -0,0 +1,15 @@
+# Copyright 2010 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.
+
+include ../../../Make.inc
+
+TARG=go/types
+GOFILES=\
+	const.go\
+	exportdata.go\
+	gcimporter.go\
+	types.go\
+	universe.go\
+
+include ../../../Make.pkg
diff --git a/src/pkg/go/types/const.go b/src/pkg/go/types/const.go
new file mode 100644
index 000000000..6fdc22f6b
--- /dev/null
+++ b/src/pkg/go/types/const.go
@@ -0,0 +1,347 @@
+// Copyright 2011 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.
+
+// This file implements operations on ideal constants.
+
+package types
+
+import (
+	"big"
+	"go/token"
+	"strconv"
+)
+
+
+// TODO(gri) Consider changing the API so Const is an interface
+//           and operations on consts don't have to type switch.
+
+// A Const implements an ideal constant Value.
+// The zero value z for a Const is not a valid constant value.
+type Const struct {
+	// representation of constant values:
+	// ideal bool     ->  bool
+	// ideal int      ->  *big.Int
+	// ideal float    ->  *big.Rat
+	// ideal complex  ->  cmplx
+	// ideal string   ->  string
+	val interface{}
+}
+
+
+// Representation of complex values.
+type cmplx struct {
+	re, im *big.Rat
+}
+
+
+func assert(cond bool) {
+	if !cond {
+		panic("go/types internal error: assertion failed")
+	}
+}
+
+
+// MakeConst makes an ideal constant from a literal
+// token and the corresponding literal string.
+func MakeConst(tok token.Token, lit string) Const {
+	switch tok {
+	case token.INT:
+		var x big.Int
+		_, ok := x.SetString(lit, 0)
+		assert(ok)
+		return Const{&x}
+	case token.FLOAT:
+		var y big.Rat
+		_, ok := y.SetString(lit)
+		assert(ok)
+		return Const{&y}
+	case token.IMAG:
+		assert(lit[len(lit)-1] == 'i')
+		var im big.Rat
+		_, ok := im.SetString(lit[0 : len(lit)-1])
+		assert(ok)
+		return Const{cmplx{big.NewRat(0, 1), &im}}
+	case token.CHAR:
+		assert(lit[0] == '\'' && lit[len(lit)-1] == '\'')
+		code, _, _, err := strconv.UnquoteChar(lit[1:len(lit)-1], '\'')
+		assert(err == nil)
+		return Const{big.NewInt(int64(code))}
+	case token.STRING:
+		s, err := strconv.Unquote(lit)
+		assert(err == nil)
+		return Const{s}
+	}
+	panic("unreachable")
+}
+
+
+// MakeZero returns the zero constant for the given type.
+func MakeZero(typ *Type) Const {
+	// TODO(gri) fix this
+	return Const{0}
+}
+
+
+// Match attempts to match the internal constant representations of x and y.
+// If the attempt is successful, the result is the values of x and y,
+// if necessary converted to have the same internal representation; otherwise
+// the results are invalid.
+func (x Const) Match(y Const) (u, v Const) {
+	switch a := x.val.(type) {
+	case bool:
+		if _, ok := y.val.(bool); ok {
+			u, v = x, y
+		}
+	case *big.Int:
+		switch y.val.(type) {
+		case *big.Int:
+			u, v = x, y
+		case *big.Rat:
+			var z big.Rat
+			z.SetInt(a)
+			u, v = Const{&z}, y
+		case cmplx:
+			var z big.Rat
+			z.SetInt(a)
+			u, v = Const{cmplx{&z, big.NewRat(0, 1)}}, y
+		}
+	case *big.Rat:
+		switch y.val.(type) {
+		case *big.Int:
+			v, u = y.Match(x)
+		case *big.Rat:
+			u, v = x, y
+		case cmplx:
+			u, v = Const{cmplx{a, big.NewRat(0, 0)}}, y
+		}
+	case cmplx:
+		switch y.val.(type) {
+		case *big.Int, *big.Rat:
+			v, u = y.Match(x)
+		case cmplx:
+			u, v = x, y
+		}
+	case string:
+		if _, ok := y.val.(string); ok {
+			u, v = x, y
+		}
+	default:
+		panic("unreachable")
+	}
+	return
+}
+
+
+// Convert attempts to convert the constant x to a given type.
+// If the attempt is successful, the result is the new constant;
+// otherwise the result is invalid.
+func (x Const) Convert(typ *Type) Const {
+	// TODO(gri) implement this
+	switch x := x.val.(type) {
+	case bool:
+	case *big.Int:
+	case *big.Rat:
+	case cmplx:
+	case string:
+	}
+	return x
+}
+
+
+func (x Const) String() string {
+	switch x := x.val.(type) {
+	case bool:
+		if x {
+			return "true"
+		}
+		return "false"
+	case *big.Int:
+		return x.String()
+	case *big.Rat:
+		return x.FloatString(10) // 10 digits of precision after decimal point seems fine
+	case cmplx:
+		// TODO(gri) don't print 0 components
+		return x.re.FloatString(10) + " + " + x.im.FloatString(10) + "i"
+	case string:
+		return x
+	}
+	panic("unreachable")
+}
+
+
+func (x Const) UnaryOp(op token.Token) Const {
+	panic("unimplemented")
+}
+
+
+func (x Const) BinaryOp(op token.Token, y Const) Const {
+	var z interface{}
+	switch x := x.val.(type) {
+	case bool:
+		z = binaryBoolOp(x, op, y.val.(bool))
+	case *big.Int:
+		z = binaryIntOp(x, op, y.val.(*big.Int))
+	case *big.Rat:
+		z = binaryFloatOp(x, op, y.val.(*big.Rat))
+	case cmplx:
+		z = binaryCmplxOp(x, op, y.val.(cmplx))
+	case string:
+		z = binaryStringOp(x, op, y.val.(string))
+	default:
+		panic("unreachable")
+	}
+	return Const{z}
+}
+
+
+func binaryBoolOp(x bool, op token.Token, y bool) interface{} {
+	switch op {
+	case token.EQL:
+		return x == y
+	case token.NEQ:
+		return x != y
+	}
+	panic("unreachable")
+}
+
+
+func binaryIntOp(x *big.Int, op token.Token, y *big.Int) interface{} {
+	var z big.Int
+	switch op {
+	case token.ADD:
+		return z.Add(x, y)
+	case token.SUB:
+		return z.Sub(x, y)
+	case token.MUL:
+		return z.Mul(x, y)
+	case token.QUO:
+		return z.Quo(x, y)
+	case token.REM:
+		return z.Rem(x, y)
+	case token.AND:
+		return z.And(x, y)
+	case token.OR:
+		return z.Or(x, y)
+	case token.XOR:
+		return z.Xor(x, y)
+	case token.AND_NOT:
+		return z.AndNot(x, y)
+	case token.SHL:
+		panic("unimplemented")
+	case token.SHR:
+		panic("unimplemented")
+	case token.EQL:
+		return x.Cmp(y) == 0
+	case token.NEQ:
+		return x.Cmp(y) != 0
+	case token.LSS:
+		return x.Cmp(y) < 0
+	case token.LEQ:
+		return x.Cmp(y) <= 0
+	case token.GTR:
+		return x.Cmp(y) > 0
+	case token.GEQ:
+		return x.Cmp(y) >= 0
+	}
+	panic("unreachable")
+}
+
+
+func binaryFloatOp(x *big.Rat, op token.Token, y *big.Rat) interface{} {
+	var z big.Rat
+	switch op {
+	case token.ADD:
+		return z.Add(x, y)
+	case token.SUB:
+		return z.Sub(x, y)
+	case token.MUL:
+		return z.Mul(x, y)
+	case token.QUO:
+		return z.Quo(x, y)
+	case token.EQL:
+		return x.Cmp(y) == 0
+	case token.NEQ:
+		return x.Cmp(y) != 0
+	case token.LSS:
+		return x.Cmp(y) < 0
+	case token.LEQ:
+		return x.Cmp(y) <= 0
+	case token.GTR:
+		return x.Cmp(y) > 0
+	case token.GEQ:
+		return x.Cmp(y) >= 0
+	}
+	panic("unreachable")
+}
+
+
+func binaryCmplxOp(x cmplx, op token.Token, y cmplx) interface{} {
+	a, b := x.re, x.im
+	c, d := y.re, y.im
+	switch op {
+	case token.ADD:
+		// (a+c) + i(b+d)
+		var re, im big.Rat
+		re.Add(a, c)
+		im.Add(b, d)
+		return cmplx{&re, &im}
+	case token.SUB:
+		// (a-c) + i(b-d)
+		var re, im big.Rat
+		re.Sub(a, c)
+		im.Sub(b, d)
+		return cmplx{&re, &im}
+	case token.MUL:
+		// (ac-bd) + i(bc+ad)
+		var ac, bd, bc, ad big.Rat
+		ac.Mul(a, c)
+		bd.Mul(b, d)
+		bc.Mul(b, c)
+		ad.Mul(a, d)
+		var re, im big.Rat
+		re.Sub(&ac, &bd)
+		im.Add(&bc, &ad)
+		return cmplx{&re, &im}
+	case token.QUO:
+		// (ac+bd)/s + i(bc-ad)/s, with s = cc + dd
+		var ac, bd, bc, ad, s big.Rat
+		ac.Mul(a, c)
+		bd.Mul(b, d)
+		bc.Mul(b, c)
+		ad.Mul(a, d)
+		s.Add(c.Mul(c, c), d.Mul(d, d))
+		var re, im big.Rat
+		re.Add(&ac, &bd)
+		re.Quo(&re, &s)
+		im.Sub(&bc, &ad)
+		im.Quo(&im, &s)
+		return cmplx{&re, &im}
+	case token.EQL:
+		return a.Cmp(c) == 0 && b.Cmp(d) == 0
+	case token.NEQ:
+		return a.Cmp(c) != 0 || b.Cmp(d) != 0
+	}
+	panic("unreachable")
+}
+
+
+func binaryStringOp(x string, op token.Token, y string) interface{} {
+	switch op {
+	case token.ADD:
+		return x + y
+	case token.EQL:
+		return x == y
+	case token.NEQ:
+		return x != y
+	case token.LSS:
+		return x < y
+	case token.LEQ:
+		return x <= y
+	case token.GTR:
+		return x > y
+	case token.GEQ:
+		return x >= y
+	}
+	panic("unreachable")
+}
diff --git a/src/pkg/go/types/exportdata.go b/src/pkg/go/types/exportdata.go
new file mode 100644
index 000000000..cb08ffe18
--- /dev/null
+++ b/src/pkg/go/types/exportdata.go
@@ -0,0 +1,135 @@
+// Copyright 2011 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.
+
+// This file implements ExportData.
+
+package types
+
+import (
+	"bufio"
+	"fmt"
+	"io"
+	"os"
+	"strconv"
+	"strings"
+)
+
+
+func readGopackHeader(buf *bufio.Reader) (name string, size int, err os.Error) {
+	// See $GOROOT/include/ar.h.
+	hdr := make([]byte, 64+12+6+6+8+10+2)
+	_, err = io.ReadFull(buf, hdr)
+	if err != nil {
+		return
+	}
+	if trace {
+		fmt.Printf("header: %s", hdr)
+	}
+	s := strings.TrimSpace(string(hdr[64+12+6+6+8:][:10]))
+	size, err = strconv.Atoi(s)
+	if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
+		err = os.ErrorString("invalid archive header")
+		return
+	}
+	name = strings.TrimSpace(string(hdr[:64]))
+	return
+}
+
+
+type dataReader struct {
+	*bufio.Reader
+	io.Closer
+}
+
+
+// ExportData returns a readCloser positioned at the beginning of the
+// export data section of the given object/archive file, or an error.
+// It is the caller's responsibility to close the readCloser.
+//
+func ExportData(filename string) (rc io.ReadCloser, err os.Error) {
+	file, err := os.Open(filename)
+	if err != nil {
+		return
+	}
+
+	defer func() {
+		if err != nil {
+			file.Close()
+			// Add file name to error.
+			err = fmt.Errorf("reading export data: %s: %v", filename, err)
+		}
+	}()
+
+	buf := bufio.NewReader(file)
+
+	// Read first line to make sure this is an object file.
+	line, err := buf.ReadSlice('\n')
+	if err != nil {
+		return
+	}
+	if string(line) == "!\n" {
+		// Archive file.  Scan to __.PKGDEF, which should
+		// be second archive entry.
+		var name string
+		var size int
+
+		// First entry should be __.SYMDEF.
+		// Read and discard.
+		if name, size, err = readGopackHeader(buf); err != nil {
+			return
+		}
+		if name != "__.SYMDEF" {
+			err = os.ErrorString("go archive does not begin with __.SYMDEF")
+			return
+		}
+		const block = 4096
+		tmp := make([]byte, block)
+		for size > 0 {
+			n := size
+			if n > block {
+				n = block
+			}
+			_, err = io.ReadFull(buf, tmp[:n])
+			if err != nil {
+				return
+			}
+			size -= n
+		}
+
+		// Second entry should be __.PKGDEF.
+		if name, size, err = readGopackHeader(buf); err != nil {
+			return
+		}
+		if name != "__.PKGDEF" {
+			err = os.ErrorString("go archive is missing __.PKGDEF")
+			return
+		}
+
+		// Read first line of __.PKGDEF data, so that line
+		// is once again the first line of the input.
+		line, err = buf.ReadSlice('\n')
+		if err != nil {
+			return
+		}
+	}
+
+	// Now at __.PKGDEF in archive or still at beginning of file.
+	// Either way, line should begin with "go object ".
+	if !strings.HasPrefix(string(line), "go object ") {
+		err = os.ErrorString("not a go object file")
+		return
+	}
+
+	// Skip over object header to export data.
+	// Begins after first line with $$.
+	for line[0] != '$' {
+		line, err = buf.ReadSlice('\n')
+		if err != nil {
+			return
+		}
+	}
+
+	rc = &dataReader{buf, file}
+	return
+}
diff --git a/src/pkg/go/types/gcimporter.go b/src/pkg/go/types/gcimporter.go
new file mode 100644
index 000000000..9e0ae6285
--- /dev/null
+++ b/src/pkg/go/types/gcimporter.go
@@ -0,0 +1,786 @@
+// Copyright 2011 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.
+
+// This file implements an ast.Importer for gc generated object files.
+// TODO(gri) Eventually move this into a separate package outside types.
+
+package types
+
+import (
+	"big"
+	"fmt"
+	"go/ast"
+	"go/token"
+	"io"
+	"os"
+	"path/filepath"
+	"runtime"
+	"scanner"
+	"strconv"
+)
+
+
+const trace = false // set to true for debugging
+
+var (
+	pkgRoot = filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH)
+	pkgExts = [...]string{".a", ".5", ".6", ".8"}
+)
+
+
+// findPkg returns the filename and package id for an import path.
+// If no file was found, an empty filename is returned.
+func findPkg(path string) (filename, id string) {
+	if len(path) == 0 {
+		return
+	}
+
+	id = path
+	var noext string
+	switch path[0] {
+	default:
+		// "x" -> "$GOROOT/pkg/$GOOS_$GOARCH/x.ext", "x"
+		noext = filepath.Join(pkgRoot, path)
+
+	case '.':
+		// "./x" -> "/this/directory/x.ext", "/this/directory/x"
+		cwd, err := os.Getwd()
+		if err != nil {
+			return
+		}
+		noext = filepath.Join(cwd, path)
+		id = noext
+
+	case '/':
+		// "/x" -> "/x.ext", "/x"
+		noext = path
+	}
+
+	// try extensions
+	for _, ext := range pkgExts {
+		filename = noext + ext
+		if f, err := os.Stat(filename); err == nil && f.IsRegular() {
+			return
+		}
+	}
+
+	filename = "" // not found
+	return
+}
+
+
+// gcParser parses the exports inside a gc compiler-produced
+// object/archive file and populates its scope with the results.
+type gcParser struct {
+	scanner scanner.Scanner
+	tok     int                   // current token
+	lit     string                // literal string; only valid for Ident, Int, String tokens
+	id      string                // package id of imported package
+	scope   *ast.Scope            // scope of imported package; alias for deps[id]
+	deps    map[string]*ast.Scope // package id -> package scope
+}
+
+
+func (p *gcParser) init(filename, id string, src io.Reader) {
+	p.scanner.Init(src)
+	p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
+	p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
+	p.scanner.Whitespace = 1<<'\t' | 1<<' '
+	p.scanner.Filename = filename // for good error messages
+	p.next()
+	p.id = id
+	p.scope = ast.NewScope(nil)
+	p.deps = map[string]*ast.Scope{"unsafe": Unsafe, id: p.scope}
+}
+
+
+func (p *gcParser) next() {
+	p.tok = p.scanner.Scan()
+	switch p.tok {
+	case scanner.Ident, scanner.Int, scanner.String:
+		p.lit = p.scanner.TokenText()
+	default:
+		p.lit = ""
+	}
+	if trace {
+		fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
+	}
+}
+
+
+// GcImporter implements the ast.Importer signature.
+func GcImporter(path string) (name string, scope *ast.Scope, err os.Error) {
+	if path == "unsafe" {
+		return path, Unsafe, nil
+	}
+
+	defer func() {
+		if r := recover(); r != nil {
+			err = r.(importError) // will re-panic if r is not an importError
+			if trace {
+				panic(err) // force a stack trace
+			}
+		}
+	}()
+
+	filename, id := findPkg(path)
+	if filename == "" {
+		err = os.ErrorString("can't find import: " + id)
+		return
+	}
+
+	buf, err := ExportData(filename)
+	if err != nil {
+		return
+	}
+	defer buf.Close()
+
+	if trace {
+		fmt.Printf("importing %s\n", filename)
+	}
+
+	var p gcParser
+	p.init(filename, id, buf)
+	name, scope = p.parseExport()
+
+	return
+}
+
+
+// ----------------------------------------------------------------------------
+// Error handling
+
+// Internal errors are boxed as importErrors.
+type importError struct {
+	pos scanner.Position
+	err os.Error
+}
+
+
+func (e importError) String() string {
+	return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
+}
+
+
+func (p *gcParser) error(err interface{}) {
+	if s, ok := err.(string); ok {
+		err = os.ErrorString(s)
+	}
+	// panic with a runtime.Error if err is not an os.Error
+	panic(importError{p.scanner.Pos(), err.(os.Error)})
+}
+
+
+func (p *gcParser) errorf(format string, args ...interface{}) {
+	p.error(fmt.Sprintf(format, args...))
+}
+
+
+func (p *gcParser) expect(tok int) string {
+	lit := p.lit
+	if p.tok != tok {
+		p.errorf("expected %q, got %q (%q)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
+	}
+	p.next()
+	return lit
+}
+
+
+func (p *gcParser) expectSpecial(tok string) {
+	sep := 'x' // not white space
+	i := 0
+	for i < len(tok) && p.tok == int(tok[i]) && sep > ' ' {
+		sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
+		p.next()
+		i++
+	}
+	if i < len(tok) {
+		p.errorf("expected %q, got %q", tok, tok[0:i])
+	}
+}
+
+
+func (p *gcParser) expectKeyword(keyword string) {
+	lit := p.expect(scanner.Ident)
+	if lit != keyword {
+		p.errorf("expected keyword %s, got %q", keyword, lit)
+	}
+}
+
+
+// ----------------------------------------------------------------------------
+// Import declarations
+
+// ImportPath = string_lit .
+//
+func (p *gcParser) parsePkgId() *ast.Scope {
+	id, err := strconv.Unquote(p.expect(scanner.String))
+	if err != nil {
+		p.error(err)
+	}
+
+	scope := p.scope // id == "" stands for the imported package id
+	if id != "" {
+		if scope = p.deps[id]; scope == nil {
+			scope = ast.NewScope(nil)
+			p.deps[id] = scope
+		}
+	}
+
+	return scope
+}
+
+
+// dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
+func (p *gcParser) parseDotIdent() string {
+	ident := ""
+	if p.tok != scanner.Int {
+		sep := 'x' // not white space
+		for (p.tok == scanner.Ident || p.tok == scanner.Int || p.tok == '·') && sep > ' ' {
+			ident += p.lit
+			sep = p.scanner.Peek() // if sep <= ' ', there is white space before the next token
+			p.next()
+		}
+	}
+	if ident == "" {
+		p.expect(scanner.Ident) // use expect() for error handling
+	}
+	return ident
+}
+
+
+// ExportedName = ImportPath "." dotIdentifier .
+//
+func (p *gcParser) parseExportedName(kind ast.ObjKind) *ast.Object {
+	scope := p.parsePkgId()
+	p.expect('.')
+	name := p.parseDotIdent()
+
+	// a type may have been declared before - if it exists
+	// already in the respective package scope, return that
+	// type
+	if kind == ast.Typ {
+		if obj := scope.Lookup(name); obj != nil {
+			assert(obj.Kind == ast.Typ)
+			return obj
+		}
+	}
+
+	// any other object must be a newly declared object -
+	// create it and insert it into the package scope
+	obj := ast.NewObj(kind, name)
+	if scope.Insert(obj) != nil {
+		p.errorf("already declared: %s", obj.Name)
+	}
+
+	// a new type object is a named type and may be referred
+	// to before the underlying type is known - set it up
+	if kind == ast.Typ {
+		obj.Type = &Name{Obj: obj}
+	}
+
+	return obj
+}
+
+
+// ----------------------------------------------------------------------------
+// Types
+
+// BasicType = identifier .
+//
+func (p *gcParser) parseBasicType() Type {
+	obj := Universe.Lookup(p.expect(scanner.Ident))
+	if obj == nil || obj.Kind != ast.Typ {
+		p.errorf("not a basic type: %s", obj.Name)
+	}
+	return obj.Type.(Type)
+}
+
+
+// ArrayType = "[" int_lit "]" Type .
+//
+func (p *gcParser) parseArrayType() Type {
+	// "[" already consumed and lookahead known not to be "]"
+	lit := p.expect(scanner.Int)
+	p.expect(']')
+	elt := p.parseType()
+	n, err := strconv.Atoui64(lit)
+	if err != nil {
+		p.error(err)
+	}
+	return &Array{Len: n, Elt: elt}
+}
+
+
+// MapType = "map" "[" Type "]" Type .
+//
+func (p *gcParser) parseMapType() Type {
+	p.expectKeyword("map")
+	p.expect('[')
+	key := p.parseType()
+	p.expect(']')
+	elt := p.parseType()
+	return &Map{Key: key, Elt: elt}
+}
+
+
+// Name = identifier | "?" .
+//
+func (p *gcParser) parseName() (name string) {
+	switch p.tok {
+	case scanner.Ident:
+		name = p.lit
+		p.next()
+	case '?':
+		// anonymous
+		p.next()
+	default:
+		p.error("name expected")
+	}
+	return
+}
+
+
+// Field = Name Type [ ":" string_lit ] .
+//
+func (p *gcParser) parseField(scope *ast.Scope) {
+	// TODO(gri) The code below is not correct for anonymous fields:
+	//           The name is the type name; it should not be empty.
+	name := p.parseName()
+	ftyp := p.parseType()
+	if name == "" {
+		// anonymous field - ftyp must be T or *T and T must be a type name
+		ftyp = Deref(ftyp)
+		if ftyp, ok := ftyp.(*Name); ok {
+			name = ftyp.Obj.Name
+		} else {
+			p.errorf("anonymous field expected")
+		}
+	}
+	if p.tok == ':' {
+		p.next()
+		tag := p.expect(scanner.String)
+		_ = tag // TODO(gri) store tag somewhere
+	}
+	fld := ast.NewObj(ast.Var, name)
+	fld.Type = ftyp
+	scope.Insert(fld)
+}
+
+
+// StructType = "struct" "{" [ FieldList ] "}" .
+// FieldList  = Field { ";" Field } .
+//
+func (p *gcParser) parseStructType() Type {
+	p.expectKeyword("struct")
+	p.expect('{')
+	scope := ast.NewScope(nil)
+	if p.tok != '}' {
+		p.parseField(scope)
+		for p.tok == ';' {
+			p.next()
+			p.parseField(scope)
+		}
+	}
+	p.expect('}')
+	return &Struct{}
+}
+
+
+// Parameter = ( identifier | "?" ) [ "..." ] Type .
+//
+func (p *gcParser) parseParameter(scope *ast.Scope, isVariadic *bool) {
+	name := p.parseName()
+	if name == "" {
+		name = "_" // cannot access unnamed identifiers
+	}
+	if isVariadic != nil {
+		if *isVariadic {
+			p.error("... not on final argument")
+		}
+		if p.tok == '.' {
+			p.expectSpecial("...")
+			*isVariadic = true
+		}
+	}
+	ptyp := p.parseType()
+	par := ast.NewObj(ast.Var, name)
+	par.Type = ptyp
+	scope.Insert(par)
+}
+
+
+// Parameters    = "(" [ ParameterList ] ")" .
+// ParameterList = { Parameter "," } Parameter .
+//
+func (p *gcParser) parseParameters(scope *ast.Scope, isVariadic *bool) {
+	p.expect('(')
+	if p.tok != ')' {
+		p.parseParameter(scope, isVariadic)
+		for p.tok == ',' {
+			p.next()
+			p.parseParameter(scope, isVariadic)
+		}
+	}
+	p.expect(')')
+}
+
+
+// Signature = Parameters [ Result ] .
+// Result    = Type | Parameters .
+//
+func (p *gcParser) parseSignature(scope *ast.Scope, isVariadic *bool) {
+	p.parseParameters(scope, isVariadic)
+
+	// optional result type
+	switch p.tok {
+	case scanner.Ident, scanner.String, '[', '*', '<':
+		// single, unnamed result
+		result := ast.NewObj(ast.Var, "_")
+		result.Type = p.parseType()
+		scope.Insert(result)
+	case '(':
+		// named or multiple result(s)
+		p.parseParameters(scope, nil)
+	}
+}
+
+
+// FuncType = "func" Signature .
+//
+func (p *gcParser) parseFuncType() Type {
+	// "func" already consumed
+	scope := ast.NewScope(nil)
+	isVariadic := false
+	p.parseSignature(scope, &isVariadic)
+	return &Func{IsVariadic: isVariadic}
+}
+
+
+// MethodSpec = identifier Signature .
+//
+func (p *gcParser) parseMethodSpec(scope *ast.Scope) {
+	p.expect(scanner.Ident)
+	isVariadic := false
+	p.parseSignature(scope, &isVariadic)
+}
+
+
+// InterfaceType = "interface" "{" [ MethodList ] "}" .
+// MethodList    = MethodSpec { ";" MethodSpec } .
+//
+func (p *gcParser) parseInterfaceType() Type {
+	p.expectKeyword("interface")
+	p.expect('{')
+	scope := ast.NewScope(nil)
+	if p.tok != '}' {
+		p.parseMethodSpec(scope)
+		for p.tok == ';' {
+			p.next()
+			p.parseMethodSpec(scope)
+		}
+	}
+	p.expect('}')
+	return &Interface{}
+}
+
+
+// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
+//
+func (p *gcParser) parseChanType() Type {
+	dir := ast.SEND | ast.RECV
+	if p.tok == scanner.Ident {
+		p.expectKeyword("chan")
+		if p.tok == '<' {
+			p.expectSpecial("<-")
+			dir = ast.SEND
+		}
+	} else {
+		p.expectSpecial("<-")
+		p.expectKeyword("chan")
+		dir = ast.RECV
+	}
+	elt := p.parseType()
+	return &Chan{Dir: dir, Elt: elt}
+}
+
+
+// Type =
+//	BasicType | TypeName | ArrayType | SliceType | StructType |
+//      PointerType | FuncType | InterfaceType | MapType | ChanType |
+//      "(" Type ")" .
+// BasicType = ident .
+// TypeName = ExportedName .
+// SliceType = "[" "]" Type .
+// PointerType = "*" Type .
+//
+func (p *gcParser) parseType() Type {
+	switch p.tok {
+	case scanner.Ident:
+		switch p.lit {
+		default:
+			return p.parseBasicType()
+		case "struct":
+			return p.parseStructType()
+		case "func":
+			p.next() // parseFuncType assumes "func" is already consumed
+			return p.parseFuncType()
+		case "interface":
+			return p.parseInterfaceType()
+		case "map":
+			return p.parseMapType()
+		case "chan":
+			return p.parseChanType()
+		}
+	case scanner.String:
+		// TypeName
+		return p.parseExportedName(ast.Typ).Type.(Type)
+	case '[':
+		p.next() // look ahead
+		if p.tok == ']' {
+			// SliceType
+			p.next()
+			return &Slice{Elt: p.parseType()}
+		}
+		return p.parseArrayType()
+	case '*':
+		// PointerType
+		p.next()
+		return &Pointer{Base: p.parseType()}
+	case '<':
+		return p.parseChanType()
+	case '(':
+		// "(" Type ")"
+		p.next()
+		typ := p.parseType()
+		p.expect(')')
+		return typ
+	}
+	p.errorf("expected type, got %s (%q)", scanner.TokenString(p.tok), p.lit)
+	return nil
+}
+
+
+// ----------------------------------------------------------------------------
+// Declarations
+
+// ImportDecl = "import" identifier string_lit .
+//
+func (p *gcParser) parseImportDecl() {
+	p.expectKeyword("import")
+	// The identifier has no semantic meaning in the import data.
+	// It exists so that error messages can print the real package
+	// name: binary.ByteOrder instead of "encoding/binary".ByteOrder.
+	// TODO(gri): Save package id -> package name mapping.
+	p.expect(scanner.Ident)
+	p.parsePkgId()
+}
+
+
+// int_lit = [ "+" | "-" ] { "0" ... "9" } .
+//
+func (p *gcParser) parseInt() (sign, val string) {
+	switch p.tok {
+	case '-':
+		p.next()
+		sign = "-"
+	case '+':
+		p.next()
+	}
+	val = p.expect(scanner.Int)
+	return
+}
+
+
+// number = int_lit [ "p" int_lit ] .
+//
+func (p *gcParser) parseNumber() Const {
+	// mantissa
+	sign, val := p.parseInt()
+	mant, ok := new(big.Int).SetString(sign+val, 10)
+	assert(ok)
+
+	if p.lit == "p" {
+		// exponent (base 2)
+		p.next()
+		sign, val = p.parseInt()
+		exp, err := strconv.Atoui(val)
+		if err != nil {
+			p.error(err)
+		}
+		if sign == "-" {
+			denom := big.NewInt(1)
+			denom.Lsh(denom, exp)
+			return Const{new(big.Rat).SetFrac(mant, denom)}
+		}
+		if exp > 0 {
+			mant.Lsh(mant, exp)
+		}
+		return Const{new(big.Rat).SetInt(mant)}
+	}
+
+	return Const{mant}
+}
+
+
+// ConstDecl   = "const" ExportedName [ Type ] "=" Literal .
+// Literal     = bool_lit | int_lit | float_lit | complex_lit | string_lit .
+// bool_lit    = "true" | "false" .
+// complex_lit = "(" float_lit "+" float_lit ")" .
+// string_lit  = `"` { unicode_char } `"` .
+//
+func (p *gcParser) parseConstDecl() {
+	p.expectKeyword("const")
+	obj := p.parseExportedName(ast.Con)
+	var x Const
+	var typ Type
+	if p.tok != '=' {
+		obj.Type = p.parseType()
+	}
+	p.expect('=')
+	switch p.tok {
+	case scanner.Ident:
+		// bool_lit
+		if p.lit != "true" && p.lit != "false" {
+			p.error("expected true or false")
+		}
+		x = Const{p.lit == "true"}
+		typ = Bool.Underlying
+		p.next()
+	case '-', scanner.Int:
+		// int_lit
+		x = p.parseNumber()
+		typ = Int.Underlying
+		if _, ok := x.val.(*big.Rat); ok {
+			typ = Float64.Underlying
+		}
+	case '(':
+		// complex_lit
+		p.next()
+		re := p.parseNumber()
+		p.expect('+')
+		im := p.parseNumber()
+		p.expect(')')
+		x = Const{cmplx{re.val.(*big.Rat), im.val.(*big.Rat)}}
+		typ = Complex128.Underlying
+	case scanner.String:
+		// string_lit
+		x = MakeConst(token.STRING, p.lit)
+		p.next()
+		typ = String.Underlying
+	default:
+		p.error("expected literal")
+	}
+	if obj.Type == nil {
+		obj.Type = typ
+	}
+	_ = x // TODO(gri) store x somewhere
+}
+
+
+// TypeDecl = "type" ExportedName Type .
+//
+func (p *gcParser) parseTypeDecl() {
+	p.expectKeyword("type")
+	obj := p.parseExportedName(ast.Typ)
+	typ := p.parseType()
+
+	name := obj.Type.(*Name)
+	assert(name.Underlying == nil)
+	assert(Underlying(typ) == typ)
+	name.Underlying = typ
+}
+
+
+// VarDecl = "var" ExportedName Type .
+//
+func (p *gcParser) parseVarDecl() {
+	p.expectKeyword("var")
+	obj := p.parseExportedName(ast.Var)
+	obj.Type = p.parseType()
+}
+
+
+// FuncDecl = "func" ExportedName Signature .
+//
+func (p *gcParser) parseFuncDecl() {
+	// "func" already consumed
+	obj := p.parseExportedName(ast.Fun)
+	obj.Type = p.parseFuncType()
+}
+
+
+// MethodDecl = "func" Receiver identifier Signature .
+// Receiver   = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
+//
+func (p *gcParser) parseMethodDecl() {
+	// "func" already consumed
+	scope := ast.NewScope(nil) // method scope
+	p.expect('(')
+	p.parseParameter(scope, nil) // receiver
+	p.expect(')')
+	p.expect(scanner.Ident)
+	isVariadic := false
+	p.parseSignature(scope, &isVariadic)
+
+}
+
+
+// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
+//
+func (p *gcParser) parseDecl() {
+	switch p.lit {
+	case "import":
+		p.parseImportDecl()
+	case "const":
+		p.parseConstDecl()
+	case "type":
+		p.parseTypeDecl()
+	case "var":
+		p.parseVarDecl()
+	case "func":
+		p.next() // look ahead
+		if p.tok == '(' {
+			p.parseMethodDecl()
+		} else {
+			p.parseFuncDecl()
+		}
+	}
+	p.expect('\n')
+}
+
+
+// ----------------------------------------------------------------------------
+// Export
+
+// Export        = "PackageClause { Decl } "$$" .
+// PackageClause = "package" identifier [ "safe" ] "\n" .
+//
+func (p *gcParser) parseExport() (string, *ast.Scope) {
+	p.expectKeyword("package")
+	name := p.expect(scanner.Ident)
+	if p.tok != '\n' {
+		// A package is safe if it was compiled with the -u flag,
+		// which disables the unsafe package.
+		// TODO(gri) remember "safe" package
+		p.expectKeyword("safe")
+	}
+	p.expect('\n')
+
+	for p.tok != '$' && p.tok != scanner.EOF {
+		p.parseDecl()
+	}
+
+	if ch := p.scanner.Peek(); p.tok != '$' || ch != '$' {
+		// don't call next()/expect() since reading past the
+		// export data may cause scanner errors (e.g. NUL chars)
+		p.errorf("expected '$$', got %s %c", scanner.TokenString(p.tok), ch)
+	}
+
+	if n := p.scanner.ErrorCount; n != 0 {
+		p.errorf("expected no scanner errors, got %d", n)
+	}
+
+	return name, p.scope
+}
diff --git a/src/pkg/go/types/gcimporter_test.go b/src/pkg/go/types/gcimporter_test.go
new file mode 100644
index 000000000..556e761df
--- /dev/null
+++ b/src/pkg/go/types/gcimporter_test.go
@@ -0,0 +1,111 @@
+// Copyright 2011 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 (
+	"exec"
+	"io/ioutil"
+	"path/filepath"
+	"runtime"
+	"strings"
+	"testing"
+	"time"
+)
+
+
+var gcName, gcPath string // compiler name and path
+
+func init() {
+	// determine compiler
+	switch runtime.GOARCH {
+	case "386":
+		gcName = "8g"
+	case "amd64":
+		gcName = "6g"
+	case "arm":
+		gcName = "5g"
+	default:
+		gcName = "unknown-GOARCH-compiler"
+		gcPath = gcName
+		return
+	}
+	gcPath, _ = exec.LookPath(gcName)
+}
+
+
+func compile(t *testing.T, dirname, filename string) {
+	cmd, err := exec.Run(gcPath, []string{gcPath, filename}, nil, dirname, exec.DevNull, exec.Pipe, exec.MergeWithStdout)
+	if err != nil {
+		t.Errorf("%s %s failed: %s", gcName, filename, err)
+		return
+	}
+	defer cmd.Close()
+
+	msg, err := cmd.Wait(0)
+	if err != nil {
+		t.Errorf("%s %s failed: %s", gcName, filename, err)
+		return
+	}
+
+	if !msg.Exited() || msg.ExitStatus() != 0 {
+		t.Errorf("%s %s failed: exit status = %d", gcName, filename, msg.ExitStatus())
+		output, _ := ioutil.ReadAll(cmd.Stdout)
+		t.Log(string(output))
+	}
+}
+
+
+func testPath(t *testing.T, path string) bool {
+	_, _, err := GcImporter(path)
+	if err != nil {
+		t.Errorf("testPath(%s): %s", path, err)
+		return false
+	}
+	return true
+}
+
+
+const maxTime = 3e9 // maximum allotted testing time in ns
+
+func testDir(t *testing.T, dir string, endTime int64) (nimports int) {
+	dirname := filepath.Join(pkgRoot, dir)
+	list, err := ioutil.ReadDir(dirname)
+	if err != nil {
+		t.Errorf("testDir(%s): %s", dirname, err)
+	}
+	for _, f := range list {
+		if time.Nanoseconds() >= endTime {
+			t.Log("testing time used up")
+			return
+		}
+		switch {
+		case f.IsRegular():
+			// try extensions
+			for _, ext := range pkgExts {
+				if strings.HasSuffix(f.Name, ext) {
+					name := f.Name[0 : len(f.Name)-len(ext)] // remove extension
+					if testPath(t, filepath.Join(dir, name)) {
+						nimports++
+					}
+				}
+			}
+		case f.IsDirectory():
+			nimports += testDir(t, filepath.Join(dir, f.Name), endTime)
+		}
+	}
+	return
+}
+
+
+func TestGcImport(t *testing.T) {
+	compile(t, "testdata", "exports.go")
+
+	nimports := 0
+	if testPath(t, "./testdata/exports") {
+		nimports++
+	}
+	nimports += testDir(t, "", time.Nanoseconds()+maxTime) // installed packages
+	t.Logf("tested %d imports", nimports)
+}
diff --git a/src/pkg/go/types/testdata/exports.go b/src/pkg/go/types/testdata/exports.go
new file mode 100644
index 000000000..13efe012a
--- /dev/null
+++ b/src/pkg/go/types/testdata/exports.go
@@ -0,0 +1,89 @@
+// Copyright 2011 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.
+
+// This file is used to generate a .6 object file which
+// serves as test file for gcimporter_test.go.
+
+package exports
+
+import (
+	"go/ast"
+)
+
+
+const (
+	C0 int = 0
+	C1 = 3.14159265
+	C2 = 2.718281828i
+	C3 = -123.456e-789
+	C4 = +123.456E+789
+	C5 = 1234i
+	C6 = "foo\n"
+	C7 = `bar\n`
+)
+
+
+type (
+	T1  int
+	T2  [10]int
+	T3  []int
+	T4  *int
+	T5  chan int
+	T6a chan<- int
+	T6b chan (<-chan int)
+	T6c chan<- (chan int)
+	T7  <-chan *ast.File
+	T8  struct{}
+	T9  struct {
+		a    int
+		b, c float32
+		d    []string "tag"
+	}
+	T10 struct {
+		T8
+		T9
+		_ *T10
+	}
+	T11 map[int]string
+	T12 interface{}
+	T13 interface {
+		m1()
+		m2(int) float32
+	}
+	T14 interface {
+		T12
+		T13
+		m3(x ...struct{}) []T9
+	}
+	T15 func()
+	T16 func(int)
+	T17 func(x int)
+	T18 func() float32
+	T19 func() (x float32)
+	T20 func(...interface{})
+	T21 struct{ next *T21 }
+	T22 struct{ link *T23 }
+	T23 struct{ link *T22 }
+	T24 *T24
+	T25 *T26
+	T26 *T27
+	T27 *T25
+	T28 func(T28) T28
+)
+
+
+var (
+	V0 int
+	V1 = -991.0
+)
+
+
+func F1()         {}
+func F2(x int)    {}
+func F3() int     { return 0 }
+func F4() float32 { return 0 }
+func F5(a, b, c int, u, v, w struct{ x, y T1 }, more ...interface{}) (p, q, r chan<- T10)
+
+
+func (p *T1) M1()
diff --git a/src/pkg/go/types/types.go b/src/pkg/go/types/types.go
new file mode 100644
index 000000000..72384e121
--- /dev/null
+++ b/src/pkg/go/types/types.go
@@ -0,0 +1,122 @@
+// Copyright 2011 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 UNDER CONSTRUCTION. ANY AND ALL PARTS MAY CHANGE.
+// The types package declares the types used to represent Go types.
+//
+package types
+
+import "go/ast"
+
+
+// All types implement the Type interface.
+type Type interface {
+	isType()
+}
+
+
+// All concrete types embed ImplementsType which
+// ensures that all types implement the Type interface.
+type ImplementsType struct{}
+
+func (t *ImplementsType) isType() {}
+
+
+// A Basic represents a (unnamed) basic type.
+type Basic struct {
+	ImplementsType
+	// TODO(gri) need a field specifying the exact basic type
+}
+
+
+// An Array represents an array type [Len]Elt.
+type Array struct {
+	ImplementsType
+	Len uint64
+	Elt Type
+}
+
+
+// A Slice represents a slice type []Elt.
+type Slice struct {
+	ImplementsType
+	Elt Type
+}
+
+
+// A Struct represents a struct type struct{...}.
+type Struct struct {
+	ImplementsType
+	// TODO(gri) need to remember fields.
+}
+
+
+// A Pointer represents a pointer type *Base.
+type Pointer struct {
+	ImplementsType
+	Base Type
+}
+
+
+// A Func represents a function type func(...) (...).
+type Func struct {
+	ImplementsType
+	IsVariadic bool
+	// TODO(gri) need to remember parameters.
+}
+
+
+// An Interface represents an interface type interface{...}.
+type Interface struct {
+	ImplementsType
+	// TODO(gri) need to remember methods.
+}
+
+
+// A Map represents a map type map[Key]Elt.
+type Map struct {
+	ImplementsType
+	Key, Elt Type
+}
+
+
+// A Chan represents a channel type chan Elt, <-chan Elt, or chan<-Elt.
+type Chan struct {
+	ImplementsType
+	Dir ast.ChanDir
+	Elt Type
+}
+
+
+// A Name represents a named type as declared in a type declaration.
+type Name struct {
+	ImplementsType
+	Underlying Type        // nil if not fully declared
+	Obj        *ast.Object // corresponding declared object
+	// TODO(gri) need to remember fields and methods.
+}
+
+
+// If typ is a pointer type, Deref returns the pointer's base type;
+// otherwise it returns typ.
+func Deref(typ Type) Type {
+	if typ, ok := typ.(*Pointer); ok {
+		return typ.Base
+	}
+	return typ
+}
+
+
+// Underlying returns the underlying type of a type.
+func Underlying(typ Type) Type {
+	if typ, ok := typ.(*Name); ok {
+		utyp := typ.Underlying
+		if _, ok := utyp.(*Basic); ok {
+			return typ
+		}
+		return utyp
+
+	}
+	return typ
+}
diff --git a/src/pkg/go/types/universe.go b/src/pkg/go/types/universe.go
new file mode 100644
index 000000000..2a54a8ac1
--- /dev/null
+++ b/src/pkg/go/types/universe.go
@@ -0,0 +1,113 @@
+// Copyright 2011 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.
+
+// FILE UNDER CONSTRUCTION. ANY AND ALL PARTS MAY CHANGE.
+// This file implements the universe and unsafe package scopes.
+
+package types
+
+import "go/ast"
+
+
+var (
+	scope, // current scope to use for initialization
+	Universe,
+	Unsafe *ast.Scope
+)
+
+
+func define(kind ast.ObjKind, name string) *ast.Object {
+	obj := ast.NewObj(kind, name)
+	if scope.Insert(obj) != nil {
+		panic("types internal error: double declaration")
+	}
+	return obj
+}
+
+
+func defType(name string) *Name {
+	obj := define(ast.Typ, name)
+	typ := &Name{Underlying: &Basic{}, Obj: obj}
+	obj.Type = typ
+	return typ
+}
+
+
+func defConst(name string) {
+	obj := define(ast.Con, name)
+	_ = obj // TODO(gri) fill in other properties
+}
+
+
+func defFun(name string) {
+	obj := define(ast.Fun, name)
+	_ = obj // TODO(gri) fill in other properties
+}
+
+
+var (
+	Bool,
+	Int,
+	Float64,
+	Complex128,
+	String *Name
+)
+
+
+func init() {
+	Universe = ast.NewScope(nil)
+	scope = Universe
+
+	Bool = defType("bool")
+	defType("byte") // TODO(gri) should be an alias for uint8
+	defType("complex64")
+	Complex128 = defType("complex128")
+	defType("float32")
+	Float64 = defType("float64")
+	defType("int8")
+	defType("int16")
+	defType("int32")
+	defType("int64")
+	String = defType("string")
+	defType("uint8")
+	defType("uint16")
+	defType("uint32")
+	defType("uint64")
+	Int = defType("int")
+	defType("uint")
+	defType("uintptr")
+
+	defConst("true")
+	defConst("false")
+	defConst("iota")
+	defConst("nil")
+
+	defFun("append")
+	defFun("cap")
+	defFun("close")
+	defFun("complex")
+	defFun("copy")
+	defFun("imag")
+	defFun("len")
+	defFun("make")
+	defFun("new")
+	defFun("panic")
+	defFun("print")
+	defFun("println")
+	defFun("real")
+	defFun("recover")
+
+	Unsafe = ast.NewScope(nil)
+	scope = Unsafe
+	defType("Pointer")
+
+	defFun("Alignof")
+	defFun("New")
+	defFun("NewArray")
+	defFun("Offsetof")
+	defFun("Reflect")
+	defFun("Sizeof")
+	defFun("Typeof")
+	defFun("Unreflect")
+}
-- 
cgit v1.2.3