summaryrefslogtreecommitdiff
path: root/libgo/go/go
diff options
context:
space:
mode:
authorian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2015-10-31 00:59:47 +0000
committerian <ian@138bc75d-0d04-0410-961f-82ee72b054a4>2015-10-31 00:59:47 +0000
commit4a3da3a8a45d5496118798146de1fa4e5798ed5a (patch)
tree13beeaed3698c61903fe93fb1ce70bd9b18d4e7f /libgo/go/go
parentcd529f4d86a17a3e8959f2cb5ac7132a841ab6f1 (diff)
downloadgcc-4a3da3a8a45d5496118798146de1fa4e5798ed5a.tar.gz
runtime: Remove now unnecessary pad field from ParFor.
It is not needed due to the removal of the ctx field. Reviewed-on: https://go-review.googlesource.com/16525 git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@229616 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'libgo/go/go')
-rw-r--r--libgo/go/go/ast/ast.go14
-rw-r--r--libgo/go/go/ast/filter.go21
-rw-r--r--libgo/go/go/ast/scope.go2
-rw-r--r--libgo/go/go/ast/walk.go6
-rw-r--r--libgo/go/go/build/build.go84
-rw-r--r--libgo/go/go/build/build_test.go57
-rw-r--r--libgo/go/go/build/deps_test.go259
-rw-r--r--libgo/go/go/build/doc.go3
-rw-r--r--libgo/go/go/build/read.go20
-rw-r--r--libgo/go/go/build/read_test.go6
-rw-r--r--libgo/go/go/build/syslist.go2
-rw-r--r--libgo/go/go/build/testdata/empty/dummy0
-rw-r--r--libgo/go/go/constant/go13.go24
-rw-r--r--libgo/go/go/constant/go14.go13
-rw-r--r--libgo/go/go/constant/value.go925
-rw-r--r--libgo/go/go/constant/value_test.go375
-rw-r--r--libgo/go/go/doc/doc.go3
-rw-r--r--libgo/go/go/doc/exports.go65
-rw-r--r--libgo/go/go/doc/testdata/blank.0.golden28
-rw-r--r--libgo/go/go/doc/testdata/blank.1.golden31
-rw-r--r--libgo/go/go/doc/testdata/blank.2.golden28
-rw-r--r--libgo/go/go/doc/testdata/blank.go31
-rw-r--r--libgo/go/go/format/format.go157
-rw-r--r--libgo/go/go/format/format_test.go6
-rw-r--r--libgo/go/go/importer/importer.go69
-rw-r--r--libgo/go/go/internal/gccgoimporter/gccgoinstallation.go94
-rw-r--r--libgo/go/go/internal/gccgoimporter/gccgoinstallation_test.go197
-rw-r--r--libgo/go/go/internal/gccgoimporter/importer.go212
-rw-r--r--libgo/go/go/internal/gccgoimporter/importer_test.go171
-rw-r--r--libgo/go/go/internal/gccgoimporter/parser.go855
-rw-r--r--libgo/go/go/internal/gccgoimporter/parser_test.go72
-rw-r--r--libgo/go/go/internal/gccgoimporter/testdata/complexnums.go6
-rw-r--r--libgo/go/go/internal/gccgoimporter/testdata/imports.go5
-rw-r--r--libgo/go/go/internal/gccgoimporter/testdata/pointer.go3
-rw-r--r--libgo/go/go/internal/gcimporter/exportdata.go108
-rw-r--r--libgo/go/go/internal/gcimporter/gcimporter.go991
-rw-r--r--libgo/go/go/internal/gcimporter/gcimporter_test.go225
-rw-r--r--libgo/go/go/internal/gcimporter/testdata/exports.go89
-rw-r--r--libgo/go/go/parser/error_test.go27
-rw-r--r--libgo/go/go/parser/interface.go40
-rw-r--r--libgo/go/go/parser/parser.go82
-rw-r--r--libgo/go/go/parser/parser_test.go106
-rw-r--r--libgo/go/go/parser/short_test.go15
-rw-r--r--libgo/go/go/parser/testdata/issue3106.src2
-rw-r--r--libgo/go/go/printer/nodes.go48
-rw-r--r--libgo/go/go/printer/printer.go32
-rw-r--r--libgo/go/go/printer/testdata/comments.golden62
-rw-r--r--libgo/go/go/printer/testdata/comments.input62
-rw-r--r--libgo/go/go/printer/testdata/declarations.golden9
-rw-r--r--libgo/go/go/printer/testdata/declarations.input9
-rw-r--r--libgo/go/go/printer/testdata/parser.go4
-rw-r--r--libgo/go/go/scanner/errors.go18
-rw-r--r--libgo/go/go/scanner/scanner.go3
-rw-r--r--libgo/go/go/scanner/scanner_test.go35
-rw-r--r--libgo/go/go/token/position.go12
-rw-r--r--libgo/go/go/types/api.go336
-rw-r--r--libgo/go/go/types/api_test.go1044
-rw-r--r--libgo/go/go/types/assignments.go328
-rw-r--r--libgo/go/go/types/builtins.go627
-rw-r--r--libgo/go/go/types/builtins_test.go204
-rw-r--r--libgo/go/go/types/call.go441
-rw-r--r--libgo/go/go/types/check.go358
-rw-r--r--libgo/go/go/types/check_test.go298
-rw-r--r--libgo/go/go/types/conversions.go146
-rw-r--r--libgo/go/go/types/decl.go430
-rw-r--r--libgo/go/go/types/errors.go103
-rw-r--r--libgo/go/go/types/eval.go83
-rw-r--r--libgo/go/go/types/eval_test.go188
-rw-r--r--libgo/go/go/types/example_test.go312
-rw-r--r--libgo/go/go/types/expr.go1496
-rw-r--r--libgo/go/go/types/exprstring.go220
-rw-r--r--libgo/go/go/types/exprstring_test.go94
-rw-r--r--libgo/go/go/types/go11.go17
-rw-r--r--libgo/go/go/types/go12.go17
-rw-r--r--libgo/go/go/types/hilbert_test.go234
-rw-r--r--libgo/go/go/types/initorder.go222
-rw-r--r--libgo/go/go/types/issues_test.go206
-rw-r--r--libgo/go/go/types/labels.go268
-rw-r--r--libgo/go/go/types/lookup.go341
-rw-r--r--libgo/go/go/types/methodset.go268
-rw-r--r--libgo/go/go/types/object.go360
-rw-r--r--libgo/go/go/types/objset.go31
-rw-r--r--libgo/go/go/types/operand.go287
-rw-r--r--libgo/go/go/types/ordering.go127
-rw-r--r--libgo/go/go/types/package.go65
-rw-r--r--libgo/go/go/types/predicates.go309
-rw-r--r--libgo/go/go/types/resolver.go445
-rw-r--r--libgo/go/go/types/resolver_test.go209
-rw-r--r--libgo/go/go/types/return.go185
-rw-r--r--libgo/go/go/types/scope.go190
-rw-r--r--libgo/go/go/types/selection.go143
-rw-r--r--libgo/go/go/types/self_test.go102
-rw-r--r--libgo/go/go/types/sizes.go211
-rw-r--r--libgo/go/go/types/stdlib_test.go279
-rw-r--r--libgo/go/go/types/stmt.go744
-rw-r--r--libgo/go/go/types/token_test.go47
-rw-r--r--libgo/go/go/types/type.go454
-rw-r--r--libgo/go/go/types/typestring.go296
-rw-r--r--libgo/go/go/types/typestring_test.go168
-rw-r--r--libgo/go/go/types/typexpr.go712
-rw-r--r--libgo/go/go/types/universe.go223
101 files changed, 19265 insertions, 426 deletions
diff --git a/libgo/go/go/ast/ast.go b/libgo/go/go/ast/ast.go
index 312e3d1b989..5ab4283826b 100644
--- a/libgo/go/go/ast/ast.go
+++ b/libgo/go/go/ast/ast.go
@@ -486,7 +486,7 @@ func (x *MapType) End() token.Pos { return x.Value.End() }
func (x *ChanType) End() token.Pos { return x.Value.End() }
// exprNode() ensures that only expression/type nodes can be
-// assigned to an ExprNode.
+// assigned to an Expr.
//
func (*BadExpr) exprNode() {}
func (*Ident) exprNode() {}
@@ -562,10 +562,11 @@ type (
// An EmptyStmt node represents an empty statement.
// The "position" of the empty statement is the position
- // of the immediately preceding semicolon.
+ // of the immediately following (explicit or implicit) semicolon.
//
EmptyStmt struct {
- Semicolon token.Pos // position of preceding ";"
+ Semicolon token.Pos // position of following ";"
+ Implicit bool // if set, ";" was omitted in the source
}
// A LabeledStmt node represents a labeled statement.
@@ -734,6 +735,9 @@ func (s *RangeStmt) Pos() token.Pos { return s.For }
func (s *BadStmt) End() token.Pos { return s.To }
func (s *DeclStmt) End() token.Pos { return s.Decl.End() }
func (s *EmptyStmt) End() token.Pos {
+ if s.Implicit {
+ return s.Semicolon
+ }
return s.Semicolon + 1 /* len(";") */
}
func (s *LabeledStmt) End() token.Pos { return s.Stmt.End() }
@@ -783,7 +787,7 @@ func (s *ForStmt) End() token.Pos { return s.Body.End() }
func (s *RangeStmt) End() token.Pos { return s.Body.End() }
// stmtNode() ensures that only statement nodes can be
-// assigned to a StmtNode.
+// assigned to a Stmt.
//
func (*BadStmt) stmtNode() {}
func (*DeclStmt) stmtNode() {}
@@ -947,7 +951,7 @@ func (d *FuncDecl) End() token.Pos {
}
// declNode() ensures that only declaration nodes can be
-// assigned to a DeclNode.
+// assigned to a Decl.
//
func (*BadDecl) declNode() {}
func (*GenDecl) declNode() {}
diff --git a/libgo/go/go/ast/filter.go b/libgo/go/go/ast/filter.go
index fc3eeb4a1db..bb571166f47 100644
--- a/libgo/go/go/ast/filter.go
+++ b/libgo/go/go/ast/filter.go
@@ -23,8 +23,7 @@ func exportFilter(name string) bool {
// body) are removed. Non-exported fields and methods of exported types are
// stripped. The File.Comments list is not changed.
//
-// FileExports returns true if there are exported declarations;
-// it returns false otherwise.
+// FileExports reports whether there are exported declarations.
//
func FileExports(src *File) bool {
return filterFile(src, exportFilter, true)
@@ -34,7 +33,7 @@ func FileExports(src *File) bool {
// only exported nodes remain. The pkg.Files list is not changed, so that
// file names and top-level package comments don't get lost.
//
-// PackageExports returns true if there are exported declarations;
+// PackageExports reports whether there are exported declarations;
// it returns false otherwise.
//
func PackageExports(pkg *Package) bool {
@@ -199,8 +198,8 @@ func filterSpecList(list []Spec, f Filter, export bool) []Spec {
// all names (including struct field and interface method names, but
// not from parameter lists) that don't pass through the filter f.
//
-// FilterDecl returns true if there are any declared names left after
-// filtering; it returns false otherwise.
+// FilterDecl reports whether there are any declared names left after
+// filtering.
//
func FilterDecl(decl Decl, f Filter) bool {
return filterDecl(decl, f, false)
@@ -221,11 +220,11 @@ func filterDecl(decl Decl, f Filter, export bool) bool {
// names from top-level declarations (including struct field and
// interface method names, but not from parameter lists) that don't
// pass through the filter f. If the declaration is empty afterwards,
-// the declaration is removed from the AST. The File.Comments list
-// is not changed.
+// the declaration is removed from the AST. Import declarations are
+// always removed. The File.Comments list is not changed.
//
-// FilterFile returns true if there are any top-level declarations
-// left after filtering; it returns false otherwise.
+// FilterFile reports whether there are any top-level declarations
+// left after filtering.
//
func FilterFile(src *File, f Filter) bool {
return filterFile(src, f, false)
@@ -251,8 +250,8 @@ func filterFile(src *File, f Filter, export bool) bool {
// changed, so that file names and top-level package comments don't get
// lost.
//
-// FilterPackage returns true if there are any top-level declarations
-// left after filtering; it returns false otherwise.
+// FilterPackage reports whether there are any top-level declarations
+// left after filtering.
//
func FilterPackage(pkg *Package, f Filter) bool {
return filterPackage(pkg, f, false)
diff --git a/libgo/go/go/ast/scope.go b/libgo/go/go/ast/scope.go
index df1529d1819..1ce5e2e84b5 100644
--- a/libgo/go/go/ast/scope.go
+++ b/libgo/go/go/ast/scope.go
@@ -38,7 +38,7 @@ func (s *Scope) Lookup(name string) *Object {
// 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."
+// it inserts obj and returns nil.
//
func (s *Scope) Insert(obj *Object) (alt *Object) {
if alt = s.Objects[obj.Name]; alt == nil {
diff --git a/libgo/go/go/ast/walk.go b/libgo/go/go/ast/walk.go
index 73ac38647a2..8ca21959b11 100644
--- a/libgo/go/go/ast/walk.go
+++ b/libgo/go/go/ast/walk.go
@@ -361,8 +361,7 @@ func Walk(v Visitor, node Node) {
}
default:
- fmt.Printf("ast.Walk: unexpected node type %T", n)
- panic("ast.Walk")
+ panic(fmt.Sprintf("ast.Walk: unexpected node type %T", n))
}
v.Visit(nil)
@@ -379,7 +378,8 @@ func (f inspector) Visit(node Node) Visitor {
// Inspect traverses an AST in depth-first order: It starts by calling
// f(node); node must not be nil. If f returns true, Inspect invokes f
-// for all the non-nil children of node, recursively.
+// recursively for each of the non-nil children of node, followed by a
+// call of f(nil).
//
func Inspect(node Node, f func(Node) bool) {
Walk(inspector(f), node)
diff --git a/libgo/go/go/build/build.go b/libgo/go/go/build/build.go
index e9247274b6e..42f11655b39 100644
--- a/libgo/go/go/build/build.go
+++ b/libgo/go/go/build/build.go
@@ -256,10 +256,12 @@ func (ctxt *Context) SrcDirs() []string {
// if set, or else the compiled code's GOARCH, GOOS, and GOROOT.
var Default Context = defaultContext()
+// Also known to cmd/dist/build.go.
var cgoEnabled = map[string]bool{
"darwin/386": true,
"darwin/amd64": true,
- "dragonfly/386": true,
+ "darwin/arm": true,
+ "darwin/arm64": true,
"dragonfly/amd64": true,
"freebsd/386": true,
"freebsd/amd64": true,
@@ -282,6 +284,7 @@ var cgoEnabled = map[string]bool{
"netbsd/arm": true,
"openbsd/386": true,
"openbsd/amd64": true,
+ "solaris/amd64": true,
"windows/386": true,
"windows/amd64": true,
}
@@ -300,11 +303,7 @@ func defaultContext() Context {
// in all releases >= Go 1.x. Code that requires Go 1.x or later should
// say "+build go1.x", and code that should only be built before Go 1.x
// (perhaps it is the stub to use in that case) should say "+build !go1.x".
- //
- // When we reach Go 1.5 the line will read
- // c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5"}
- // and so on.
- c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4"}
+ c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5"}
switch os.Getenv("CGO_ENABLED") {
case "1":
@@ -361,6 +360,7 @@ type Package struct {
Root string // root of Go tree where this package lives
SrcRoot string // package source root directory ("" if unknown)
PkgRoot string // package install root directory ("" if unknown)
+ PkgTargetRoot string // architecture dependent install root directory ("" if unknown)
BinDir string // command install directory ("" if unknown)
Goroot bool // package found in Go root
PkgObj string // installed .a file
@@ -469,18 +469,21 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
return p, fmt.Errorf("import %q: invalid import path", path)
}
+ var pkgtargetroot string
var pkga string
var pkgerr error
+ suffix := ""
+ if ctxt.InstallSuffix != "" {
+ suffix = "_" + ctxt.InstallSuffix
+ }
switch ctxt.Compiler {
case "gccgo":
+ pkgtargetroot = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
dir, elem := pathpkg.Split(p.ImportPath)
- pkga = "pkg/gccgo_" + ctxt.GOOS + "_" + ctxt.GOARCH + "/" + dir + "lib" + elem + ".a"
+ pkga = pkgtargetroot + "/" + dir + "lib" + elem + ".a"
case "gc":
- suffix := ""
- if ctxt.InstallSuffix != "" {
- suffix = "_" + ctxt.InstallSuffix
- }
- pkga = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix + "/" + p.ImportPath + ".a"
+ pkgtargetroot = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + suffix
+ pkga = pkgtargetroot + "/" + p.ImportPath + ".a"
default:
// Save error for end of function.
pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler)
@@ -496,9 +499,13 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
p.Dir = ctxt.joinPath(srcDir, path)
}
// Determine canonical import path, if any.
+ // Exclude results where the import path would include /testdata/.
+ inTestdata := func(sub string) bool {
+ return strings.Contains(sub, "/testdata/") || strings.HasSuffix(sub, "/testdata") || strings.HasPrefix(sub, "testdata/") || sub == "testdata"
+ }
if ctxt.GOROOT != "" {
root := ctxt.joinPath(ctxt.GOROOT, "src")
- if sub, ok := ctxt.hasSubdir(root, p.Dir); ok {
+ if sub, ok := ctxt.hasSubdir(root, p.Dir); ok && !inTestdata(sub) {
p.Goroot = true
p.ImportPath = sub
p.Root = ctxt.GOROOT
@@ -508,7 +515,7 @@ func (ctxt *Context) Import(path string, srcDir string, mode ImportMode) (*Packa
all := ctxt.gopath()
for i, root := range all {
rootsrc := ctxt.joinPath(root, "src")
- if sub, ok := ctxt.hasSubdir(rootsrc, p.Dir); ok {
+ if sub, ok := ctxt.hasSubdir(rootsrc, p.Dir); ok && !inTestdata(sub) {
// We found a potential import path for dir,
// but check that using it wouldn't find something
// else first.
@@ -597,6 +604,7 @@ Found:
p.PkgRoot = ctxt.joinPath(p.Root, "pkg")
p.BinDir = ctxt.joinPath(p.Root, "bin")
if pkga != "" {
+ p.PkgTargetRoot = ctxt.joinPath(p.Root, pkgtargetroot)
p.PkgObj = ctxt.joinPath(p.Root, pkga)
}
}
@@ -695,7 +703,11 @@ Found:
p.Name = pkg
firstFile = name
} else if pkg != p.Name {
- return p, &MultiplePackageError{p.Dir, []string{firstFile, name}, []string{p.Name, pkg}}
+ return p, &MultiplePackageError{
+ Dir: p.Dir,
+ Packages: []string{p.Name, pkg},
+ Files: []string{firstFile, name},
+ }
}
if pf.Doc != nil && p.Doc == "" {
p.Doc = doc.Synopsis(pf.Doc.Text())
@@ -962,7 +974,7 @@ func (ctxt *Context) matchFile(dir, name string, returnImports bool, allTags map
}
if strings.HasSuffix(filename, ".go") {
- data, err = readImports(f, false)
+ data, err = readImports(f, false, nil)
} else {
data, err = readComments(f)
}
@@ -1075,9 +1087,6 @@ func (ctxt *Context) shouldBuild(content []byte, allTags map[string]bool) bool {
// saveCgo saves the information from the #cgo lines in the import "C" comment.
// These lines set CFLAGS, CPPFLAGS, CXXFLAGS and LDFLAGS and pkg-config directives
// that affect the way cgo's C code is built.
-//
-// TODO(rsc): This duplicates code in cgo.
-// Once the dust settles, remove this code from cgo.
func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup) error {
text := cg.Text()
for _, line := range strings.Split(text, "\n") {
@@ -1123,10 +1132,12 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
if err != nil {
return fmt.Errorf("%s: invalid #cgo line: %s", filename, orig)
}
- for _, arg := range args {
+ for i, arg := range args {
+ arg = expandSrcDir(arg, di.Dir)
if !safeCgoName(arg) {
return fmt.Errorf("%s: malformed #cgo argument: %s", filename, arg)
}
+ args[i] = arg
}
switch verb {
@@ -1147,6 +1158,14 @@ func (ctxt *Context) saveCgo(filename string, di *Package, cg *ast.CommentGroup)
return nil
}
+func expandSrcDir(str string, srcdir string) string {
+ // "\" delimited paths cause safeCgoName to fail
+ // so convert native paths with a different delimeter
+ // to "/" before starting (eg: on windows)
+ srcdir = filepath.ToSlash(srcdir)
+ return strings.Replace(str, "${SRCDIR}", srcdir, -1)
+}
+
// NOTE: $ is not safe for the shell, but it is allowed here because of linker options like -Wl,$ORIGIN.
// We never pass these arguments to a shell (just to programs we construct argv for), so this should be okay.
// See golang.org/issue/6038.
@@ -1225,7 +1244,7 @@ func splitQuoted(s string) (r []string, err error) {
return args, err
}
-// match returns true if the name is one of:
+// match reports whether the name is one of:
//
// $GOOS
// $GOARCH
@@ -1316,7 +1335,7 @@ func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
// build tag "linux" in that file. For Go 1.4 and beyond, we require this
// auto-tagging to apply only to files with a non-empty prefix, so
// "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
- // sytems, such as android, to arrive without breaking existing code with
+ // systems, such as android, to arrive without breaking existing code with
// innocuous source code in "android.go". The easiest fix: cut everything
// in the name before the initial _.
i := strings.Index(name, "_")
@@ -1390,20 +1409,11 @@ func IsLocalImport(path string) bool {
strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
}
-// ArchChar returns the architecture character for the given goarch.
-// For example, ArchChar("amd64") returns "6".
+// ArchChar returns "?" and an error.
+// In earlier versions of Go, the returned string was used to derive
+// the compiler and linker tool names, the default object file suffix,
+// and the default linker output name. As of Go 1.5, those strings
+// no longer vary by architecture; they are compile, link, .o, and a.out, respectively.
func ArchChar(goarch string) (string, error) {
- switch goarch {
- case "386":
- return "8", nil
- case "amd64", "amd64p32":
- return "6", nil
- case "arm":
- return "5", nil
- case "arm64":
- return "7", nil
- case "ppc64", "ppc64le":
- return "9", nil
- }
- return "", errors.New("unsupported GOARCH " + goarch)
+ return "?", errors.New("architecture letter no longer used")
}
diff --git a/libgo/go/go/build/build_test.go b/libgo/go/go/build/build_test.go
index a40def0fa0e..2709ca34f54 100644
--- a/libgo/go/go/build/build_test.go
+++ b/libgo/go/go/build/build_test.go
@@ -94,12 +94,29 @@ func TestEmptyFolderImport(t *testing.T) {
func TestMultiplePackageImport(t *testing.T) {
_, err := Import(".", "testdata/multi", 0)
- if _, ok := err.(*MultiplePackageError); !ok {
+ mpe, ok := err.(*MultiplePackageError)
+ if !ok {
t.Fatal(`Import("testdata/multi") did not return MultiplePackageError.`)
}
+ want := &MultiplePackageError{
+ Dir: filepath.FromSlash("testdata/multi"),
+ Packages: []string{"main", "test_package"},
+ Files: []string{"file.go", "file_appengine.go"},
+ }
+ if !reflect.DeepEqual(mpe, want) {
+ t.Errorf("got %#v; want %#v", mpe, want)
+ }
}
func TestLocalDirectory(t *testing.T) {
+ t.Skip("does not work with gccgo")
+ if runtime.GOOS == "darwin" {
+ switch runtime.GOARCH {
+ case "arm", "arm64":
+ t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
+ }
+ }
+
cwd, err := os.Getwd()
if err != nil {
t.Fatal(err)
@@ -214,6 +231,14 @@ func TestMatchFile(t *testing.T) {
}
func TestImportCmd(t *testing.T) {
+ t.Skip("does not work with gccgo")
+ if runtime.GOOS == "darwin" {
+ switch runtime.GOARCH {
+ case "arm", "arm64":
+ t.Skipf("skipping on %s/%s, no valid GOROOT", runtime.GOOS, runtime.GOARCH)
+ }
+ }
+
p, err := Import("cmd/internal/objfile", "", 0)
if err != nil {
t.Fatal(err)
@@ -222,3 +247,33 @@ func TestImportCmd(t *testing.T) {
t.Fatalf("Import cmd/internal/objfile returned Dir=%q, want %q", filepath.ToSlash(p.Dir), ".../src/cmd/internal/objfile")
}
}
+
+var (
+ expandSrcDirPath = filepath.Join(string(filepath.Separator)+"projects", "src", "add")
+)
+
+var expandSrcDirTests = []struct {
+ input, expected string
+}{
+ {"-L ${SRCDIR}/libs -ladd", "-L /projects/src/add/libs -ladd"},
+ {"${SRCDIR}/add_linux_386.a -pthread -lstdc++", "/projects/src/add/add_linux_386.a -pthread -lstdc++"},
+ {"Nothing to expand here!", "Nothing to expand here!"},
+ {"$", "$"},
+ {"$$", "$$"},
+ {"${", "${"},
+ {"$}", "$}"},
+ {"$FOO ${BAR}", "$FOO ${BAR}"},
+ {"Find me the $SRCDIRECTORY.", "Find me the $SRCDIRECTORY."},
+ {"$SRCDIR is missing braces", "$SRCDIR is missing braces"},
+}
+
+func TestExpandSrcDir(t *testing.T) {
+ for _, test := range expandSrcDirTests {
+ output := expandSrcDir(test.input, expandSrcDirPath)
+ if output != test.expected {
+ t.Errorf("%q expands to %q with SRCDIR=%q when %q is expected", test.input, output, expandSrcDirPath, test.expected)
+ } else {
+ t.Logf("%q expands to %q with SRCDIR=%q", test.input, output, expandSrcDirPath)
+ }
+ }
+}
diff --git a/libgo/go/go/build/deps_test.go b/libgo/go/go/build/deps_test.go
index a335effec3a..68969bbcf86 100644
--- a/libgo/go/go/build/deps_test.go
+++ b/libgo/go/go/build/deps_test.go
@@ -8,8 +8,15 @@
package build
import (
+ "bytes"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
"runtime"
"sort"
+ "strconv"
+ "strings"
"testing"
)
@@ -121,9 +128,12 @@ var pkgDeps = map[string][]string{
// End of linear dependency definitions.
// Operating system access.
- "syscall": {"L0", "unicode/utf16"},
- "time": {"L0", "syscall"},
- "os": {"L1", "os", "syscall", "time"},
+ "syscall": {"L0", "unicode/utf16"},
+ "internal/syscall/unix": {"L0", "syscall"},
+ "internal/syscall/windows": {"L0", "syscall"},
+ "internal/syscall/windows/registry": {"L0", "syscall", "unicode/utf16"},
+ "time": {"L0", "syscall", "internal/syscall/windows/registry"},
+ "os": {"L1", "os", "syscall", "time", "internal/syscall/windows"},
"path/filepath": {"L2", "os", "syscall"},
"io/ioutil": {"L2", "os", "path/filepath", "time"},
"os/exec": {"L2", "os", "path/filepath", "syscall"},
@@ -148,11 +158,13 @@ var pkgDeps = map[string][]string{
"regexp/syntax": {"L2"},
"runtime/debug": {"L2", "fmt", "io/ioutil", "os", "time"},
"runtime/pprof": {"L2", "fmt", "text/tabwriter"},
+ "runtime/trace": {"L0"},
"text/tabwriter": {"L2"},
- "testing": {"L2", "flag", "fmt", "os", "runtime/pprof", "time"},
- "testing/iotest": {"L2", "log"},
- "testing/quick": {"L2", "flag", "fmt", "reflect"},
+ "testing": {"L2", "flag", "fmt", "os", "runtime/pprof", "runtime/trace", "time"},
+ "testing/iotest": {"L2", "log"},
+ "testing/quick": {"L2", "flag", "fmt", "reflect"},
+ "internal/testenv": {"L2", "os", "testing"},
// L4 is defined as L3+fmt+log+time, because in general once
// you're using L3 packages, use of fmt, log, or time is not a big deal.
@@ -180,43 +192,60 @@ var pkgDeps = map[string][]string{
"go/token",
},
+ "go/format": {"L4", "GOPARSER", "internal/format"},
+ "internal/format": {"L4", "GOPARSER"},
+
+ // Go type checking.
+ "go/constant": {"L4", "go/token", "math/big"},
+ "go/importer": {"L4", "go/internal/gcimporter", "go/internal/gccgoimporter", "go/types"},
+ "go/internal/gcimporter": {"L4", "OS", "go/build", "go/constant", "go/token", "go/types", "text/scanner"},
+ "go/internal/gccgoimporter": {"L4", "OS", "debug/elf", "go/constant", "go/token", "go/types", "text/scanner"},
+ "go/types": {"L4", "GOPARSER", "container/heap", "go/constant"},
+
// One of a kind.
- "archive/tar": {"L4", "OS", "syscall"},
- "archive/zip": {"L4", "OS", "compress/flate"},
- "compress/bzip2": {"L4"},
- "compress/flate": {"L4"},
- "compress/gzip": {"L4", "compress/flate"},
- "compress/lzw": {"L4"},
- "compress/zlib": {"L4", "compress/flate"},
- "database/sql": {"L4", "container/list", "database/sql/driver"},
- "database/sql/driver": {"L4", "time"},
- "debug/dwarf": {"L4"},
- "debug/elf": {"L4", "OS", "debug/dwarf"},
- "debug/gosym": {"L4"},
- "debug/macho": {"L4", "OS", "debug/dwarf"},
- "debug/pe": {"L4", "OS", "debug/dwarf"},
- "encoding": {"L4"},
- "encoding/ascii85": {"L4"},
- "encoding/asn1": {"L4", "math/big"},
- "encoding/csv": {"L4"},
- "encoding/gob": {"L4", "OS", "encoding"},
- "encoding/hex": {"L4"},
- "encoding/json": {"L4", "encoding"},
- "encoding/pem": {"L4"},
- "encoding/xml": {"L4", "encoding"},
- "flag": {"L4", "OS"},
- "go/build": {"L4", "OS", "GOPARSER"},
- "html": {"L4"},
- "image/draw": {"L4"},
- "image/gif": {"L4", "compress/lzw", "image/color/palette", "image/draw"},
- "image/jpeg": {"L4"},
- "image/png": {"L4", "compress/zlib"},
- "index/suffixarray": {"L4", "regexp"},
- "math/big": {"L4"},
- "mime": {"L4", "OS", "syscall"},
- "net/url": {"L4"},
- "text/scanner": {"L4", "OS"},
- "text/template/parse": {"L4"},
+ "archive/tar": {"L4", "OS", "syscall"},
+ "archive/zip": {"L4", "OS", "compress/flate"},
+ "container/heap": {"sort"},
+ "compress/bzip2": {"L4"},
+ "compress/flate": {"L4"},
+ "compress/gzip": {"L4", "compress/flate"},
+ "compress/lzw": {"L4"},
+ "compress/zlib": {"L4", "compress/flate"},
+ "database/sql": {"L4", "container/list", "database/sql/driver"},
+ "database/sql/driver": {"L4", "time"},
+ "debug/dwarf": {"L4"},
+ "debug/elf": {"L4", "OS", "debug/dwarf"},
+ "debug/gosym": {"L4"},
+ "debug/macho": {"L4", "OS", "debug/dwarf"},
+ "debug/pe": {"L4", "OS", "debug/dwarf"},
+ "debug/plan9obj": {"L4", "OS"},
+ "encoding": {"L4"},
+ "encoding/ascii85": {"L4"},
+ "encoding/asn1": {"L4", "math/big"},
+ "encoding/csv": {"L4"},
+ "encoding/gob": {"L4", "OS", "encoding"},
+ "encoding/hex": {"L4"},
+ "encoding/json": {"L4", "encoding"},
+ "encoding/pem": {"L4"},
+ "encoding/xml": {"L4", "encoding"},
+ "flag": {"L4", "OS"},
+ "go/build": {"L4", "OS", "GOPARSER"},
+ "html": {"L4"},
+ "image/draw": {"L4", "image/internal/imageutil"},
+ "image/gif": {"L4", "compress/lzw", "image/color/palette", "image/draw"},
+ "image/internal/imageutil": {"L4"},
+ "image/jpeg": {"L4", "image/internal/imageutil"},
+ "image/png": {"L4", "compress/zlib"},
+ "index/suffixarray": {"L4", "regexp"},
+ "internal/singleflight": {"sync"},
+ "internal/trace": {"L4", "OS"},
+ "math/big": {"L4"},
+ "mime": {"L4", "OS", "syscall", "internal/syscall/windows/registry"},
+ "mime/quotedprintable": {"L4"},
+ "net/internal/socktest": {"L4", "OS", "syscall"},
+ "net/url": {"L4"},
+ "text/scanner": {"L4", "OS"},
+ "text/template/parse": {"L4"},
"html/template": {
"L4", "OS", "encoding/json", "html", "text/template",
@@ -234,13 +263,16 @@ var pkgDeps = map[string][]string{
// that shows up in programs that use cgo.
"C": {},
+ // Race detector uses cgo.
+ "runtime/race": {"C"},
+
// Plan 9 alone needs io/ioutil and os.
"os/user": {"L4", "CGO", "io/ioutil", "os", "syscall"},
// Basic networking.
// Because net must be used by any package that wants to
// do networking portably, it must have a small dependency set: just L1+basic os.
- "net": {"L1", "CGO", "os", "syscall", "time"},
+ "net": {"L1", "CGO", "os", "syscall", "time", "internal/syscall/windows", "internal/singleflight"},
// NET enables use of basic network-related packages.
"NET": {
@@ -252,7 +284,7 @@ var pkgDeps = map[string][]string{
// Uses of networking.
"log/syslog": {"L4", "OS", "net"},
- "net/mail": {"L4", "NET", "OS"},
+ "net/mail": {"L4", "NET", "OS", "mime"},
"net/textproto": {"L4", "OS", "net"},
// Core crypto.
@@ -279,7 +311,7 @@ var pkgDeps = map[string][]string{
// Random byte, number generation.
// This would be part of core crypto except that it imports
// math/big, which imports fmt.
- "crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall", "internal/syscall"},
+ "crypto/rand": {"L4", "CRYPTO", "OS", "math/big", "syscall", "internal/syscall/unix"},
// Mathematical crypto: dependencies on fmt (L4) and math/big.
// We could avoid some of the fmt, but math/big imports fmt anyway.
@@ -311,7 +343,7 @@ var pkgDeps = map[string][]string{
"crypto/x509/pkix": {"L4", "CRYPTO-MATH"},
// Simple net+crypto-aware packages.
- "mime/multipart": {"L4", "OS", "mime", "crypto/rand", "net/textproto"},
+ "mime/multipart": {"L4", "OS", "mime", "crypto/rand", "net/textproto", "mime/quotedprintable"},
"net/smtp": {"L4", "CRYPTO", "NET", "crypto/tls"},
// HTTP, kingpin of dependencies.
@@ -320,16 +352,18 @@ var pkgDeps = map[string][]string{
"compress/gzip", "crypto/tls", "mime/multipart", "runtime/debug",
"net/http/internal",
},
+ "net/http/internal": {"L4"},
// HTTP-using packages.
- "expvar": {"L4", "OS", "encoding/json", "net/http"},
- "net/http/cgi": {"L4", "NET", "OS", "crypto/tls", "net/http", "regexp"},
- "net/http/fcgi": {"L4", "NET", "OS", "net/http", "net/http/cgi"},
- "net/http/httptest": {"L4", "NET", "OS", "crypto/tls", "flag", "net/http"},
- "net/http/httputil": {"L4", "NET", "OS", "net/http", "net/http/internal"},
- "net/http/pprof": {"L4", "OS", "html/template", "net/http", "runtime/pprof"},
- "net/rpc": {"L4", "NET", "encoding/gob", "html/template", "net/http"},
- "net/rpc/jsonrpc": {"L4", "NET", "encoding/json", "net/rpc"},
+ "expvar": {"L4", "OS", "encoding/json", "net/http"},
+ "net/http/cgi": {"L4", "NET", "OS", "crypto/tls", "net/http", "regexp"},
+ "net/http/cookiejar": {"L4", "NET", "net/http"},
+ "net/http/fcgi": {"L4", "NET", "OS", "net/http", "net/http/cgi"},
+ "net/http/httptest": {"L4", "NET", "OS", "crypto/tls", "flag", "net/http"},
+ "net/http/httputil": {"L4", "NET", "OS", "net/http", "net/http/internal"},
+ "net/http/pprof": {"L4", "OS", "html/template", "net/http", "runtime/pprof", "runtime/trace"},
+ "net/rpc": {"L4", "NET", "encoding/gob", "html/template", "net/http"},
+ "net/rpc/jsonrpc": {"L4", "NET", "encoding/json", "net/rpc"},
}
// isMacro reports whether p is a package dependency macro
@@ -375,69 +409,112 @@ var allowedErrors = map[osPkg]bool{
osPkg{"plan9", "log/syslog"}: true,
}
+// listStdPkgs returns the same list of packages as "go list std".
+func listStdPkgs(goroot string) ([]string, error) {
+ // Based on cmd/go's matchPackages function.
+ var pkgs []string
+
+ src := filepath.Join(goroot, "src") + string(filepath.Separator)
+ walkFn := func(path string, fi os.FileInfo, err error) error {
+ if err != nil || !fi.IsDir() || path == src {
+ return nil
+ }
+
+ base := filepath.Base(path)
+ if strings.HasPrefix(base, ".") || strings.HasPrefix(base, "_") || base == "testdata" {
+ return filepath.SkipDir
+ }
+
+ name := filepath.ToSlash(path[len(src):])
+ if name == "builtin" || name == "cmd" || strings.Contains(name, ".") {
+ return filepath.SkipDir
+ }
+
+ pkgs = append(pkgs, name)
+ return nil
+ }
+ if err := filepath.Walk(src, walkFn); err != nil {
+ return nil, err
+ }
+ return pkgs, nil
+}
+
func TestDependencies(t *testing.T) {
- if runtime.GOOS == "nacl" {
- // NaCl tests run in a limited file system and we do not
+ iOS := runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64")
+ if runtime.GOOS == "nacl" || iOS {
+ // Tests run in a limited file system and we do not
// provide access to every source file.
- t.Skip("skipping on NaCl")
+ t.Skipf("skipping on %s/%s, missing full GOROOT", runtime.GOOS, runtime.GOARCH)
}
- var all []string
- for k := range pkgDeps {
- all = append(all, k)
+ ctxt := Default
+ all, err := listStdPkgs(ctxt.GOROOT)
+ if err != nil {
+ t.Fatal(err)
}
sort.Strings(all)
- ctxt := Default
test := func(mustImport bool) {
for _, pkg := range all {
- if isMacro(pkg) {
- continue
- }
- if pkg == "runtime/cgo" && !ctxt.CgoEnabled {
- continue
- }
- p, err := ctxt.Import(pkg, "", 0)
+ imports, err := findImports(pkg)
if err != nil {
- if allowedErrors[osPkg{ctxt.GOOS, pkg}] {
- continue
- }
- if !ctxt.CgoEnabled && pkg == "runtime/cgo" {
- continue
- }
- // Some of the combinations we try might not
- // be reasonable (like arm,plan9,cgo), so ignore
- // errors for the auto-generated combinations.
- if !mustImport {
- continue
- }
- t.Errorf("%s/%s/cgo=%v %v", ctxt.GOOS, ctxt.GOARCH, ctxt.CgoEnabled, err)
+ t.Error(err)
continue
}
ok := allowed(pkg)
var bad []string
- for _, imp := range p.Imports {
+ for _, imp := range imports {
if !ok[imp] {
bad = append(bad, imp)
}
}
if bad != nil {
- t.Errorf("%s/%s/cgo=%v unexpected dependency: %s imports %v", ctxt.GOOS, ctxt.GOARCH, ctxt.CgoEnabled, pkg, bad)
+ t.Errorf("unexpected dependency: %s imports %v", pkg, bad)
}
}
}
test(true)
+}
- if testing.Short() {
- t.Logf("skipping other systems")
- return
- }
+var buildIgnore = []byte("\n// +build ignore")
- for _, ctxt.GOOS = range geese {
- for _, ctxt.GOARCH = range goarches {
- for _, ctxt.CgoEnabled = range bools {
- test(false)
+func findImports(pkg string) ([]string, error) {
+ dir := filepath.Join(Default.GOROOT, "src", pkg)
+ files, err := ioutil.ReadDir(dir)
+ if err != nil {
+ return nil, err
+ }
+ var imports []string
+ var haveImport = map[string]bool{}
+ for _, file := range files {
+ name := file.Name()
+ if !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") {
+ continue
+ }
+ f, err := os.Open(filepath.Join(dir, name))
+ if err != nil {
+ return nil, err
+ }
+ var imp []string
+ data, err := readImports(f, false, &imp)
+ f.Close()
+ if err != nil {
+ return nil, fmt.Errorf("reading %v: %v", name, err)
+ }
+ if bytes.Contains(data, buildIgnore) {
+ continue
+ }
+ for _, quoted := range imp {
+ path, err := strconv.Unquote(quoted)
+ if err != nil {
+ continue
+ }
+ if !haveImport[path] {
+ haveImport[path] = true
+ imports = append(imports, path)
}
}
}
+ sort.Strings(imports)
+ return imports, nil
}
diff --git a/libgo/go/go/build/doc.go b/libgo/go/go/build/doc.go
index 75a827bb91f..233f8b989d5 100644
--- a/libgo/go/go/build/doc.go
+++ b/libgo/go/go/build/doc.go
@@ -101,6 +101,7 @@
// - "go1.2", from Go version 1.2 onward
// - "go1.3", from Go version 1.3 onward
// - "go1.4", from Go version 1.4 onward
+// - "go1.5", from Go version 1.5 onward
// - any additional words listed in ctxt.BuildTags
//
// If a file's name, after stripping the extension and a possible _test suffix,
@@ -111,7 +112,7 @@
// (example: source_windows_amd64.go) where GOOS and GOARCH represent
// any known operating system and architecture values respectively, then
// the file is considered to have an implicit build constraint requiring
-// those terms.
+// those terms (in addition to any explicit constraints in the file).
//
// To keep a file from being considered for the build:
//
diff --git a/libgo/go/go/build/read.go b/libgo/go/go/build/read.go
index c8079dfd15d..1049ac50d94 100644
--- a/libgo/go/go/build/read.go
+++ b/libgo/go/go/build/read.go
@@ -146,11 +146,15 @@ func (r *importReader) readIdent() {
// readString reads a quoted string literal from the input.
// If an identifier is not present, readString records a syntax error.
-func (r *importReader) readString() {
+func (r *importReader) readString(save *[]string) {
switch r.nextByte(true) {
case '`':
+ start := len(r.buf) - 1
for r.err == nil {
if r.nextByte(false) == '`' {
+ if save != nil {
+ *save = append(*save, string(r.buf[start:]))
+ }
break
}
if r.eof {
@@ -158,9 +162,13 @@ func (r *importReader) readString() {
}
}
case '"':
+ start := len(r.buf) - 1
for r.err == nil {
c := r.nextByte(false)
if c == '"' {
+ if save != nil {
+ *save = append(*save, string(r.buf[start:]))
+ }
break
}
if r.eof || c == '\n' {
@@ -177,14 +185,14 @@ func (r *importReader) readString() {
// readImport reads an import clause - optional identifier followed by quoted string -
// from the input.
-func (r *importReader) readImport() {
+func (r *importReader) readImport(imports *[]string) {
c := r.peekByte(true)
if c == '.' {
r.peek = 0
} else if isIdent(c) {
r.readIdent()
}
- r.readString()
+ r.readString(imports)
}
// readComments is like ioutil.ReadAll, except that it only reads the leading
@@ -201,7 +209,7 @@ func readComments(f io.Reader) ([]byte, error) {
// readImports is like ioutil.ReadAll, except that it expects a Go file as input
// and stops reading the input once the imports have completed.
-func readImports(f io.Reader, reportSyntaxError bool) ([]byte, error) {
+func readImports(f io.Reader, reportSyntaxError bool, imports *[]string) ([]byte, error) {
r := &importReader{b: bufio.NewReader(f)}
r.readKeyword("package")
@@ -211,11 +219,11 @@ func readImports(f io.Reader, reportSyntaxError bool) ([]byte, error) {
if r.peekByte(true) == '(' {
r.nextByte(false)
for r.peekByte(true) != ')' && r.err == nil {
- r.readImport()
+ r.readImport(imports)
}
r.nextByte(false)
} else {
- r.readImport()
+ r.readImport(imports)
}
}
diff --git a/libgo/go/go/build/read_test.go b/libgo/go/go/build/read_test.go
index 2dcc1208f71..326960bdc91 100644
--- a/libgo/go/go/build/read_test.go
+++ b/libgo/go/go/build/read_test.go
@@ -131,7 +131,7 @@ func testRead(t *testing.T, tests []readTest, read func(io.Reader) ([]byte, erro
}
func TestReadImports(t *testing.T) {
- testRead(t, readImportsTests, func(r io.Reader) ([]byte, error) { return readImports(r, true) })
+ testRead(t, readImportsTests, func(r io.Reader) ([]byte, error) { return readImports(r, true, nil) })
}
func TestReadComments(t *testing.T) {
@@ -207,7 +207,7 @@ var readFailuresTests = []readTest{
func TestReadFailures(t *testing.T) {
// Errors should be reported (true arg to readImports).
- testRead(t, readFailuresTests, func(r io.Reader) ([]byte, error) { return readImports(r, true) })
+ testRead(t, readFailuresTests, func(r io.Reader) ([]byte, error) { return readImports(r, true, nil) })
}
func TestReadFailuresIgnored(t *testing.T) {
@@ -222,5 +222,5 @@ func TestReadFailuresIgnored(t *testing.T) {
tt.err = ""
}
}
- testRead(t, tests, func(r io.Reader) ([]byte, error) { return readImports(r, false) })
+ testRead(t, tests, func(r io.Reader) ([]byte, error) { return readImports(r, false, nil) })
}
diff --git a/libgo/go/go/build/syslist.go b/libgo/go/go/build/syslist.go
index 0bf4b1573c9..d800a78f0a1 100644
--- a/libgo/go/go/build/syslist.go
+++ b/libgo/go/go/build/syslist.go
@@ -5,4 +5,4 @@
package build
const goosList = "android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris windows "
-const goarchList = "386 amd64 amd64p32 arm arm64 alpha m68k mipso32 mipsn32 mipsn64 mipso64 ppc ppc64 ppc64le s390 s390x sparc sparc64 "
+const goarchList = "386 amd64 amd64p32 arm armbe arm64 arm64be alpha m68k ppc64 ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le mipso32 mipsn32 mipsn64 mipso64 ppc s390 s390x sparc sparc64 "
diff --git a/libgo/go/go/build/testdata/empty/dummy b/libgo/go/go/build/testdata/empty/dummy
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/libgo/go/go/build/testdata/empty/dummy
diff --git a/libgo/go/go/constant/go13.go b/libgo/go/go/constant/go13.go
new file mode 100644
index 00000000000..a4a838a2908
--- /dev/null
+++ b/libgo/go/go/constant/go13.go
@@ -0,0 +1,24 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.4
+
+package constant
+
+import (
+ "math"
+ "math/big"
+)
+
+func ratToFloat32(x *big.Rat) (float32, bool) {
+ // Before 1.4, there's no Rat.Float32.
+ // Emulate it, albeit at the cost of
+ // imprecision in corner cases.
+ x64, exact := x.Float64()
+ x32 := float32(x64)
+ if math.IsInf(float64(x32), 0) {
+ exact = false
+ }
+ return x32, exact
+}
diff --git a/libgo/go/go/constant/go14.go b/libgo/go/go/constant/go14.go
new file mode 100644
index 00000000000..2ab6da02f67
--- /dev/null
+++ b/libgo/go/go/constant/go14.go
@@ -0,0 +1,13 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.4
+
+package constant
+
+import "math/big"
+
+func ratToFloat32(x *big.Rat) (float32, bool) {
+ return x.Float32()
+}
diff --git a/libgo/go/go/constant/value.go b/libgo/go/go/constant/value.go
new file mode 100644
index 00000000000..79a80af1ab1
--- /dev/null
+++ b/libgo/go/go/constant/value.go
@@ -0,0 +1,925 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package constant implements Values representing untyped
+// Go constants and the corresponding operations. Values
+// and operations may have arbitrary or unlimited precision.
+//
+// A special Unknown value may be used when a value
+// is unknown due to an error. Operations on unknown
+// values produce unknown values unless specified
+// otherwise.
+//
+package constant // import "go/constant"
+
+import (
+ "fmt"
+ "go/token"
+ "math/big"
+ "strconv"
+)
+
+// Kind specifies the kind of value represented by a Value.
+type Kind int
+
+// Implementation note: Kinds must be enumerated in
+// order of increasing "complexity" (used by match).
+
+const (
+ // unknown values
+ Unknown Kind = iota
+
+ // non-numeric values
+ Bool
+ String
+
+ // numeric values
+ Int
+ Float
+ Complex
+)
+
+// A Value represents a mathematically exact value of a given Kind.
+type Value interface {
+ // Kind returns the value kind; it is always the smallest
+ // kind in which the value can be represented exactly.
+ Kind() Kind
+
+ // String returns a human-readable form of the value.
+ String() string
+
+ // Prevent external implementations.
+ implementsValue()
+}
+
+// ----------------------------------------------------------------------------
+// Implementations
+
+type (
+ unknownVal struct{}
+ boolVal bool
+ stringVal string
+ int64Val int64
+ intVal struct{ val *big.Int }
+ floatVal struct{ val *big.Rat }
+ complexVal struct{ re, im *big.Rat }
+)
+
+func (unknownVal) Kind() Kind { return Unknown }
+func (boolVal) Kind() Kind { return Bool }
+func (stringVal) Kind() Kind { return String }
+func (int64Val) Kind() Kind { return Int }
+func (intVal) Kind() Kind { return Int }
+func (floatVal) Kind() Kind { return Float }
+func (complexVal) Kind() Kind { return Complex }
+
+func (unknownVal) String() string { return "unknown" }
+func (x boolVal) String() string { return fmt.Sprintf("%v", bool(x)) }
+func (x stringVal) String() string { return strconv.Quote(string(x)) }
+func (x int64Val) String() string { return strconv.FormatInt(int64(x), 10) }
+func (x intVal) String() string { return x.val.String() }
+func (x floatVal) String() string { return x.val.String() }
+func (x complexVal) String() string { return fmt.Sprintf("(%s + %si)", x.re, x.im) }
+
+func (unknownVal) implementsValue() {}
+func (boolVal) implementsValue() {}
+func (stringVal) implementsValue() {}
+func (int64Val) implementsValue() {}
+func (intVal) implementsValue() {}
+func (floatVal) implementsValue() {}
+func (complexVal) implementsValue() {}
+
+// int64 bounds
+var (
+ minInt64 = big.NewInt(-1 << 63)
+ maxInt64 = big.NewInt(1<<63 - 1)
+)
+
+func normInt(x *big.Int) Value {
+ if minInt64.Cmp(x) <= 0 && x.Cmp(maxInt64) <= 0 {
+ return int64Val(x.Int64())
+ }
+ return intVal{x}
+}
+
+func normFloat(x *big.Rat) Value {
+ if x.IsInt() {
+ return normInt(x.Num())
+ }
+ return floatVal{x}
+}
+
+func normComplex(re, im *big.Rat) Value {
+ if im.Sign() == 0 {
+ return normFloat(re)
+ }
+ return complexVal{re, im}
+}
+
+// ----------------------------------------------------------------------------
+// Factories
+
+// MakeUnknown returns the Unknown value.
+func MakeUnknown() Value { return unknownVal{} }
+
+// MakeBool returns the Bool value for x.
+func MakeBool(b bool) Value { return boolVal(b) }
+
+// MakeString returns the String value for x.
+func MakeString(s string) Value { return stringVal(s) }
+
+// MakeInt64 returns the Int value for x.
+func MakeInt64(x int64) Value { return int64Val(x) }
+
+// MakeUint64 returns the Int value for x.
+func MakeUint64(x uint64) Value { return normInt(new(big.Int).SetUint64(x)) }
+
+// MakeFloat64 returns the numeric value for x.
+// If x is not finite, the result is unknown.
+func MakeFloat64(x float64) Value {
+ if f := new(big.Rat).SetFloat64(x); f != nil {
+ return normFloat(f)
+ }
+ return unknownVal{}
+}
+
+// MakeFromLiteral returns the corresponding integer, floating-point,
+// imaginary, character, or string value for a Go literal string.
+// If prec > 0, prec specifies an upper limit for the precision of
+// a numeric value. If the literal string is invalid, the result is
+// nil.
+// BUG(gri) Only prec == 0 is supported at the moment.
+func MakeFromLiteral(lit string, tok token.Token, prec uint) Value {
+ if prec != 0 {
+ panic("limited precision not supported")
+ }
+ switch tok {
+ case token.INT:
+ if x, err := strconv.ParseInt(lit, 0, 64); err == nil {
+ return int64Val(x)
+ }
+ if x, ok := new(big.Int).SetString(lit, 0); ok {
+ return intVal{x}
+ }
+
+ case token.FLOAT:
+ if x, ok := new(big.Rat).SetString(lit); ok {
+ return normFloat(x)
+ }
+
+ case token.IMAG:
+ if n := len(lit); n > 0 && lit[n-1] == 'i' {
+ if im, ok := new(big.Rat).SetString(lit[0 : n-1]); ok {
+ return normComplex(big.NewRat(0, 1), im)
+ }
+ }
+
+ case token.CHAR:
+ if n := len(lit); n >= 2 {
+ if code, _, _, err := strconv.UnquoteChar(lit[1:n-1], '\''); err == nil {
+ return int64Val(code)
+ }
+ }
+
+ case token.STRING:
+ if s, err := strconv.Unquote(lit); err == nil {
+ return stringVal(s)
+ }
+ }
+
+ return nil
+}
+
+// ----------------------------------------------------------------------------
+// Accessors
+//
+// For unknown arguments the result is the zero value for the respective
+// accessor type, except for Sign, where the result is 1.
+
+// BoolVal returns the Go boolean value of x, which must be a Bool or an Unknown.
+// If x is Unknown, the result is false.
+func BoolVal(x Value) bool {
+ switch x := x.(type) {
+ case boolVal:
+ return bool(x)
+ case unknownVal:
+ return false
+ }
+ panic(fmt.Sprintf("%v not a Bool", x))
+}
+
+// StringVal returns the Go string value of x, which must be a String or an Unknown.
+// If x is Unknown, the result is "".
+func StringVal(x Value) string {
+ switch x := x.(type) {
+ case stringVal:
+ return string(x)
+ case unknownVal:
+ return ""
+ }
+ panic(fmt.Sprintf("%v not a String", x))
+}
+
+// Int64Val returns the Go int64 value of x and whether the result is exact;
+// x must be an Int or an Unknown. If the result is not exact, its value is undefined.
+// If x is Unknown, the result is (0, false).
+func Int64Val(x Value) (int64, bool) {
+ switch x := x.(type) {
+ case int64Val:
+ return int64(x), true
+ case intVal:
+ return x.val.Int64(), x.val.BitLen() <= 63
+ case unknownVal:
+ return 0, false
+ }
+ panic(fmt.Sprintf("%v not an Int", x))
+}
+
+// Uint64Val returns the Go uint64 value of x and whether the result is exact;
+// x must be an Int or an Unknown. If the result is not exact, its value is undefined.
+// If x is Unknown, the result is (0, false).
+func Uint64Val(x Value) (uint64, bool) {
+ switch x := x.(type) {
+ case int64Val:
+ return uint64(x), x >= 0
+ case intVal:
+ return x.val.Uint64(), x.val.Sign() >= 0 && x.val.BitLen() <= 64
+ case unknownVal:
+ return 0, false
+ }
+ panic(fmt.Sprintf("%v not an Int", x))
+}
+
+// Float32Val is like Float64Val but for float32 instead of float64.
+func Float32Val(x Value) (float32, bool) {
+ switch x := x.(type) {
+ case int64Val:
+ f := float32(x)
+ return f, int64Val(f) == x
+ case intVal:
+ return ratToFloat32(new(big.Rat).SetFrac(x.val, int1))
+ case floatVal:
+ return ratToFloat32(x.val)
+ case unknownVal:
+ return 0, false
+ }
+ panic(fmt.Sprintf("%v not a Float", x))
+}
+
+// Float64Val returns the nearest Go float64 value of x and whether the result is exact;
+// x must be numeric but not Complex, or Unknown. For values too small (too close to 0)
+// to represent as float64, Float64Val silently underflows to 0. The result sign always
+// matches the sign of x, even for 0.
+// If x is Unknown, the result is (0, false).
+func Float64Val(x Value) (float64, bool) {
+ switch x := x.(type) {
+ case int64Val:
+ f := float64(int64(x))
+ return f, int64Val(f) == x
+ case intVal:
+ return new(big.Rat).SetFrac(x.val, int1).Float64()
+ case floatVal:
+ return x.val.Float64()
+ case unknownVal:
+ return 0, false
+ }
+ panic(fmt.Sprintf("%v not a Float", x))
+}
+
+// BitLen returns the number of bits required to represent
+// the absolute value x in binary representation; x must be an Int or an Unknown.
+// If x is Unknown, the result is 0.
+func BitLen(x Value) int {
+ switch x := x.(type) {
+ case int64Val:
+ return new(big.Int).SetInt64(int64(x)).BitLen()
+ case intVal:
+ return x.val.BitLen()
+ case unknownVal:
+ return 0
+ }
+ panic(fmt.Sprintf("%v not an Int", x))
+}
+
+// Sign returns -1, 0, or 1 depending on whether x < 0, x == 0, or x > 0;
+// x must be numeric or Unknown. For complex values x, the sign is 0 if x == 0,
+// otherwise it is != 0. If x is Unknown, the result is 1.
+func Sign(x Value) int {
+ switch x := x.(type) {
+ case int64Val:
+ switch {
+ case x < 0:
+ return -1
+ case x > 0:
+ return 1
+ }
+ return 0
+ case intVal:
+ return x.val.Sign()
+ case floatVal:
+ return x.val.Sign()
+ case complexVal:
+ return x.re.Sign() | x.im.Sign()
+ case unknownVal:
+ return 1 // avoid spurious division by zero errors
+ }
+ panic(fmt.Sprintf("%v not numeric", x))
+}
+
+// ----------------------------------------------------------------------------
+// Support for serializing/deserializing integers
+
+const (
+ // Compute the size of a Word in bytes.
+ _m = ^big.Word(0)
+ _log = _m>>8&1 + _m>>16&1 + _m>>32&1
+ wordSize = 1 << _log
+)
+
+// Bytes returns the bytes for the absolute value of x in little-
+// endian binary representation; x must be an Int.
+func Bytes(x Value) []byte {
+ var val *big.Int
+ switch x := x.(type) {
+ case int64Val:
+ val = new(big.Int).SetInt64(int64(x))
+ case intVal:
+ val = x.val
+ default:
+ panic(fmt.Sprintf("%v not an Int", x))
+ }
+
+ words := val.Bits()
+ bytes := make([]byte, len(words)*wordSize)
+
+ i := 0
+ for _, w := range words {
+ for j := 0; j < wordSize; j++ {
+ bytes[i] = byte(w)
+ w >>= 8
+ i++
+ }
+ }
+ // remove leading 0's
+ for i > 0 && bytes[i-1] == 0 {
+ i--
+ }
+
+ return bytes[:i]
+}
+
+// MakeFromBytes returns the Int value given the bytes of its little-endian
+// binary representation. An empty byte slice argument represents 0.
+func MakeFromBytes(bytes []byte) Value {
+ words := make([]big.Word, (len(bytes)+(wordSize-1))/wordSize)
+
+ i := 0
+ var w big.Word
+ var s uint
+ for _, b := range bytes {
+ w |= big.Word(b) << s
+ if s += 8; s == wordSize*8 {
+ words[i] = w
+ i++
+ w = 0
+ s = 0
+ }
+ }
+ // store last word
+ if i < len(words) {
+ words[i] = w
+ i++
+ }
+ // remove leading 0's
+ for i > 0 && words[i-1] == 0 {
+ i--
+ }
+
+ return normInt(new(big.Int).SetBits(words[:i]))
+}
+
+// ----------------------------------------------------------------------------
+// Support for disassembling fractions
+
+// Num returns the numerator of x; x must be Int, Float, or Unknown.
+// If x is Unknown, the result is Unknown, otherwise it is an Int
+// with the same sign as x.
+func Num(x Value) Value {
+ switch x := x.(type) {
+ case unknownVal, int64Val, intVal:
+ return x
+ case floatVal:
+ return normInt(x.val.Num())
+ }
+ panic(fmt.Sprintf("%v not Int or Float", x))
+}
+
+// Denom returns the denominator of x; x must be Int, Float, or Unknown.
+// If x is Unknown, the result is Unknown, otherwise it is an Int >= 1.
+func Denom(x Value) Value {
+ switch x := x.(type) {
+ case unknownVal:
+ return x
+ case int64Val, intVal:
+ return int64Val(1)
+ case floatVal:
+ return normInt(x.val.Denom())
+ }
+ panic(fmt.Sprintf("%v not Int or Float", x))
+}
+
+// ----------------------------------------------------------------------------
+// Support for assembling/disassembling complex numbers
+
+// MakeImag returns the numeric value x*i (possibly 0);
+// x must be Int, Float, or Unknown.
+// If x is Unknown, the result is Unknown.
+func MakeImag(x Value) Value {
+ var im *big.Rat
+ switch x := x.(type) {
+ case unknownVal:
+ return x
+ case int64Val:
+ im = big.NewRat(int64(x), 1)
+ case intVal:
+ im = new(big.Rat).SetFrac(x.val, int1)
+ case floatVal:
+ im = x.val
+ default:
+ panic(fmt.Sprintf("%v not Int or Float", x))
+ }
+ return normComplex(rat0, im)
+}
+
+// Real returns the real part of x, which must be a numeric or unknown value.
+// If x is Unknown, the result is Unknown.
+func Real(x Value) Value {
+ switch x := x.(type) {
+ case unknownVal, int64Val, intVal, floatVal:
+ return x
+ case complexVal:
+ return normFloat(x.re)
+ }
+ panic(fmt.Sprintf("%v not numeric", x))
+}
+
+// Imag returns the imaginary part of x, which must be a numeric or unknown value.
+// If x is Unknown, the result is Unknown.
+func Imag(x Value) Value {
+ switch x := x.(type) {
+ case unknownVal:
+ return x
+ case int64Val, intVal, floatVal:
+ return int64Val(0)
+ case complexVal:
+ return normFloat(x.im)
+ }
+ panic(fmt.Sprintf("%v not numeric", x))
+}
+
+// ----------------------------------------------------------------------------
+// Operations
+
+// is32bit reports whether x can be represented using 32 bits.
+func is32bit(x int64) bool {
+ const s = 32
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+}
+
+// is63bit reports whether x can be represented using 63 bits.
+func is63bit(x int64) bool {
+ const s = 63
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+}
+
+// UnaryOp returns the result of the unary expression op y.
+// The operation must be defined for the operand.
+// If prec > 0 it specifies the ^ (xor) result size in bits.
+// If y is Unknown, the result is Unknown.
+//
+func UnaryOp(op token.Token, y Value, prec uint) Value {
+ switch op {
+ case token.ADD:
+ switch y.(type) {
+ case unknownVal, int64Val, intVal, floatVal, complexVal:
+ return y
+ }
+
+ case token.SUB:
+ switch y := y.(type) {
+ case unknownVal:
+ return y
+ case int64Val:
+ if z := -y; z != y {
+ return z // no overflow
+ }
+ return normInt(new(big.Int).Neg(big.NewInt(int64(y))))
+ case intVal:
+ return normInt(new(big.Int).Neg(y.val))
+ case floatVal:
+ return normFloat(new(big.Rat).Neg(y.val))
+ case complexVal:
+ return normComplex(new(big.Rat).Neg(y.re), new(big.Rat).Neg(y.im))
+ }
+
+ case token.XOR:
+ var z big.Int
+ switch y := y.(type) {
+ case unknownVal:
+ return y
+ case int64Val:
+ z.Not(big.NewInt(int64(y)))
+ case intVal:
+ z.Not(y.val)
+ default:
+ goto Error
+ }
+ // For unsigned types, the result will be negative and
+ // thus "too large": We must limit the result precision
+ // to the type's precision.
+ if prec > 0 {
+ z.AndNot(&z, new(big.Int).Lsh(big.NewInt(-1), prec)) // z &^= (-1)<<prec
+ }
+ return normInt(&z)
+
+ case token.NOT:
+ switch y := y.(type) {
+ case unknownVal:
+ return y
+ case boolVal:
+ return !y
+ }
+ }
+
+Error:
+ panic(fmt.Sprintf("invalid unary operation %s%v", op, y))
+}
+
+var (
+ int1 = big.NewInt(1)
+ rat0 = big.NewRat(0, 1)
+)
+
+func ord(x Value) int {
+ switch x.(type) {
+ default:
+ return 0
+ case boolVal, stringVal:
+ return 1
+ case int64Val:
+ return 2
+ case intVal:
+ return 3
+ case floatVal:
+ return 4
+ case complexVal:
+ return 5
+ }
+}
+
+// match returns the matching representation (same type) with the
+// smallest complexity for two values x and y. If one of them is
+// numeric, both of them must be numeric. If one of them is Unknown,
+// both results are Unknown.
+//
+func match(x, y Value) (_, _ Value) {
+ if ord(x) > ord(y) {
+ y, x = match(y, x)
+ return x, y
+ }
+ // ord(x) <= ord(y)
+
+ switch x := x.(type) {
+ case unknownVal:
+ return x, x
+
+ case boolVal, stringVal, complexVal:
+ return x, y
+
+ case int64Val:
+ switch y := y.(type) {
+ case int64Val:
+ return x, y
+ case intVal:
+ return intVal{big.NewInt(int64(x))}, y
+ case floatVal:
+ return floatVal{big.NewRat(int64(x), 1)}, y
+ case complexVal:
+ return complexVal{big.NewRat(int64(x), 1), rat0}, y
+ }
+
+ case intVal:
+ switch y := y.(type) {
+ case intVal:
+ return x, y
+ case floatVal:
+ return floatVal{new(big.Rat).SetFrac(x.val, int1)}, y
+ case complexVal:
+ return complexVal{new(big.Rat).SetFrac(x.val, int1), rat0}, y
+ }
+
+ case floatVal:
+ switch y := y.(type) {
+ case floatVal:
+ return x, y
+ case complexVal:
+ return complexVal{x.val, rat0}, y
+ }
+ }
+
+ panic("unreachable")
+}
+
+// BinaryOp returns the result of the binary expression x op y.
+// The operation must be defined for the operands. If one of the
+// operands is Unknown, the result is Unknown.
+// To force integer division of Int operands, use op == token.QUO_ASSIGN
+// instead of token.QUO; the result is guaranteed to be Int in this case.
+// Division by zero leads to a run-time panic.
+//
+func BinaryOp(x Value, op token.Token, y Value) Value {
+ x, y = match(x, y)
+
+ switch x := x.(type) {
+ case unknownVal:
+ return x
+
+ case boolVal:
+ y := y.(boolVal)
+ switch op {
+ case token.LAND:
+ return x && y
+ case token.LOR:
+ return x || y
+ }
+
+ case int64Val:
+ a := int64(x)
+ b := int64(y.(int64Val))
+ var c int64
+ switch op {
+ case token.ADD:
+ if !is63bit(a) || !is63bit(b) {
+ return normInt(new(big.Int).Add(big.NewInt(a), big.NewInt(b)))
+ }
+ c = a + b
+ case token.SUB:
+ if !is63bit(a) || !is63bit(b) {
+ return normInt(new(big.Int).Sub(big.NewInt(a), big.NewInt(b)))
+ }
+ c = a - b
+ case token.MUL:
+ if !is32bit(a) || !is32bit(b) {
+ return normInt(new(big.Int).Mul(big.NewInt(a), big.NewInt(b)))
+ }
+ c = a * b
+ case token.QUO:
+ return normFloat(new(big.Rat).SetFrac(big.NewInt(a), big.NewInt(b)))
+ case token.QUO_ASSIGN: // force integer division
+ c = a / b
+ case token.REM:
+ c = a % b
+ case token.AND:
+ c = a & b
+ case token.OR:
+ c = a | b
+ case token.XOR:
+ c = a ^ b
+ case token.AND_NOT:
+ c = a &^ b
+ default:
+ goto Error
+ }
+ return int64Val(c)
+
+ case intVal:
+ a := x.val
+ b := y.(intVal).val
+ var c big.Int
+ switch op {
+ case token.ADD:
+ c.Add(a, b)
+ case token.SUB:
+ c.Sub(a, b)
+ case token.MUL:
+ c.Mul(a, b)
+ case token.QUO:
+ return normFloat(new(big.Rat).SetFrac(a, b))
+ case token.QUO_ASSIGN: // force integer division
+ c.Quo(a, b)
+ case token.REM:
+ c.Rem(a, b)
+ case token.AND:
+ c.And(a, b)
+ case token.OR:
+ c.Or(a, b)
+ case token.XOR:
+ c.Xor(a, b)
+ case token.AND_NOT:
+ c.AndNot(a, b)
+ default:
+ goto Error
+ }
+ return normInt(&c)
+
+ case floatVal:
+ a := x.val
+ b := y.(floatVal).val
+ var c big.Rat
+ switch op {
+ case token.ADD:
+ c.Add(a, b)
+ case token.SUB:
+ c.Sub(a, b)
+ case token.MUL:
+ c.Mul(a, b)
+ case token.QUO:
+ c.Quo(a, b)
+ default:
+ goto Error
+ }
+ return normFloat(&c)
+
+ case complexVal:
+ y := y.(complexVal)
+ a, b := x.re, x.im
+ c, d := y.re, y.im
+ var re, im big.Rat
+ switch op {
+ case token.ADD:
+ // (a+c) + i(b+d)
+ re.Add(a, c)
+ im.Add(b, d)
+ case token.SUB:
+ // (a-c) + i(b-d)
+ re.Sub(a, c)
+ im.Sub(b, d)
+ 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)
+ re.Sub(&ac, &bd)
+ im.Add(&bc, &ad)
+ case token.QUO:
+ // (ac+bd)/s + i(bc-ad)/s, with s = cc + dd
+ var ac, bd, bc, ad, s, cc, dd big.Rat
+ ac.Mul(a, c)
+ bd.Mul(b, d)
+ bc.Mul(b, c)
+ ad.Mul(a, d)
+ cc.Mul(c, c)
+ dd.Mul(d, d)
+ s.Add(&cc, &dd)
+ re.Add(&ac, &bd)
+ re.Quo(&re, &s)
+ im.Sub(&bc, &ad)
+ im.Quo(&im, &s)
+ default:
+ goto Error
+ }
+ return normComplex(&re, &im)
+
+ case stringVal:
+ if op == token.ADD {
+ return x + y.(stringVal)
+ }
+ }
+
+Error:
+ panic(fmt.Sprintf("invalid binary operation %v %s %v", x, op, y))
+}
+
+// Shift returns the result of the shift expression x op s
+// with op == token.SHL or token.SHR (<< or >>). x must be
+// an Int or an Unknown. If x is Unknown, the result is x.
+//
+func Shift(x Value, op token.Token, s uint) Value {
+ switch x := x.(type) {
+ case unknownVal:
+ return x
+
+ case int64Val:
+ if s == 0 {
+ return x
+ }
+ switch op {
+ case token.SHL:
+ z := big.NewInt(int64(x))
+ return normInt(z.Lsh(z, s))
+ case token.SHR:
+ return x >> s
+ }
+
+ case intVal:
+ if s == 0 {
+ return x
+ }
+ var z big.Int
+ switch op {
+ case token.SHL:
+ return normInt(z.Lsh(x.val, s))
+ case token.SHR:
+ return normInt(z.Rsh(x.val, s))
+ }
+ }
+
+ panic(fmt.Sprintf("invalid shift %v %s %d", x, op, s))
+}
+
+func cmpZero(x int, op token.Token) bool {
+ switch op {
+ case token.EQL:
+ return x == 0
+ case token.NEQ:
+ return x != 0
+ case token.LSS:
+ return x < 0
+ case token.LEQ:
+ return x <= 0
+ case token.GTR:
+ return x > 0
+ case token.GEQ:
+ return x >= 0
+ }
+ panic("unreachable")
+}
+
+// Compare returns the result of the comparison x op y.
+// The comparison must be defined for the operands.
+// If one of the operands is Unknown, the result is
+// false.
+//
+func Compare(x Value, op token.Token, y Value) bool {
+ x, y = match(x, y)
+
+ switch x := x.(type) {
+ case unknownVal:
+ return false
+
+ case boolVal:
+ y := y.(boolVal)
+ switch op {
+ case token.EQL:
+ return x == y
+ case token.NEQ:
+ return x != y
+ }
+
+ case int64Val:
+ y := y.(int64Val)
+ switch op {
+ 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
+ }
+
+ case intVal:
+ return cmpZero(x.val.Cmp(y.(intVal).val), op)
+
+ case floatVal:
+ return cmpZero(x.val.Cmp(y.(floatVal).val), op)
+
+ case complexVal:
+ y := y.(complexVal)
+ re := x.re.Cmp(y.re)
+ im := x.im.Cmp(y.im)
+ switch op {
+ case token.EQL:
+ return re == 0 && im == 0
+ case token.NEQ:
+ return re != 0 || im != 0
+ }
+
+ case stringVal:
+ y := y.(stringVal)
+ switch op {
+ 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(fmt.Sprintf("invalid comparison %v %s %v", x, op, y))
+}
diff --git a/libgo/go/go/constant/value_test.go b/libgo/go/go/constant/value_test.go
new file mode 100644
index 00000000000..08cdd5e625c
--- /dev/null
+++ b/libgo/go/go/constant/value_test.go
@@ -0,0 +1,375 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package constant
+
+import (
+ "go/token"
+ "strings"
+ "testing"
+)
+
+// TODO(gri) expand this test framework
+
+var opTests = []string{
+ // unary operations
+ `+ 0 = 0`,
+ `+ ? = ?`,
+ `- 1 = -1`,
+ `- ? = ?`,
+ `^ 0 = -1`,
+ `^ ? = ?`,
+
+ `! true = false`,
+ `! false = true`,
+ `! ? = ?`,
+
+ // etc.
+
+ // binary operations
+ `"" + "" = ""`,
+ `"foo" + "" = "foo"`,
+ `"" + "bar" = "bar"`,
+ `"foo" + "bar" = "foobar"`,
+
+ `0 + 0 = 0`,
+ `0 + 0.1 = 0.1`,
+ `0 + 0.1i = 0.1i`,
+ `0.1 + 0.9 = 1`,
+ `1e100 + 1e100 = 2e100`,
+ `? + 0 = ?`,
+ `0 + ? = ?`,
+
+ `0 - 0 = 0`,
+ `0 - 0.1 = -0.1`,
+ `0 - 0.1i = -0.1i`,
+ `1e100 - 1e100 = 0`,
+ `? - 0 = ?`,
+ `0 - ? = ?`,
+
+ `0 * 0 = 0`,
+ `1 * 0.1 = 0.1`,
+ `1 * 0.1i = 0.1i`,
+ `1i * 1i = -1`,
+ `? * 0 = ?`,
+ `0 * ? = ?`,
+
+ `0 / 0 = "division_by_zero"`,
+ `10 / 2 = 5`,
+ `5 / 3 = 5/3`,
+ `5i / 3i = 5/3`,
+ `? / 0 = ?`,
+ `0 / ? = ?`,
+
+ `0 % 0 = "runtime_error:_integer_divide_by_zero"`, // TODO(gri) should be the same as for /
+ `10 % 3 = 1`,
+ `? % 0 = ?`,
+ `0 % ? = ?`,
+
+ `0 & 0 = 0`,
+ `12345 & 0 = 0`,
+ `0xff & 0xf = 0xf`,
+ `? & 0 = ?`,
+ `0 & ? = ?`,
+
+ `0 | 0 = 0`,
+ `12345 | 0 = 12345`,
+ `0xb | 0xa0 = 0xab`,
+ `? | 0 = ?`,
+ `0 | ? = ?`,
+
+ `0 ^ 0 = 0`,
+ `1 ^ -1 = -2`,
+ `? ^ 0 = ?`,
+ `0 ^ ? = ?`,
+
+ `0 &^ 0 = 0`,
+ `0xf &^ 1 = 0xe`,
+ `1 &^ 0xf = 0`,
+ // etc.
+
+ // shifts
+ `0 << 0 = 0`,
+ `1 << 10 = 1024`,
+ `0 >> 0 = 0`,
+ `1024 >> 10 == 1`,
+ `? << 0 == ?`,
+ `? >> 10 == ?`,
+ // etc.
+
+ // comparisons
+ `false == false = true`,
+ `false == true = false`,
+ `true == false = false`,
+ `true == true = true`,
+
+ `false != false = false`,
+ `false != true = true`,
+ `true != false = true`,
+ `true != true = false`,
+
+ `"foo" == "bar" = false`,
+ `"foo" != "bar" = true`,
+ `"foo" < "bar" = false`,
+ `"foo" <= "bar" = false`,
+ `"foo" > "bar" = true`,
+ `"foo" >= "bar" = true`,
+
+ `0 == 0 = true`,
+ `0 != 0 = false`,
+ `0 < 10 = true`,
+ `10 <= 10 = true`,
+ `0 > 10 = false`,
+ `10 >= 10 = true`,
+
+ `1/123456789 == 1/123456789 == true`,
+ `1/123456789 != 1/123456789 == false`,
+ `1/123456789 < 1/123456788 == true`,
+ `1/123456788 <= 1/123456789 == false`,
+ `0.11 > 0.11 = false`,
+ `0.11 >= 0.11 = true`,
+
+ `? == 0 = false`,
+ `? != 0 = false`,
+ `? < 10 = false`,
+ `? <= 10 = false`,
+ `? > 10 = false`,
+ `? >= 10 = false`,
+
+ `0 == ? = false`,
+ `0 != ? = false`,
+ `0 < ? = false`,
+ `10 <= ? = false`,
+ `0 > ? = false`,
+ `10 >= ? = false`,
+
+ // etc.
+}
+
+func TestOps(t *testing.T) {
+ for _, test := range opTests {
+ a := strings.Split(test, " ")
+ i := 0 // operator index
+
+ var x, x0 Value
+ switch len(a) {
+ case 4:
+ // unary operation
+ case 5:
+ // binary operation
+ x, x0 = val(a[0]), val(a[0])
+ i = 1
+ default:
+ t.Errorf("invalid test case: %s", test)
+ continue
+ }
+
+ op, ok := optab[a[i]]
+ if !ok {
+ panic("missing optab entry for " + a[i])
+ }
+
+ y, y0 := val(a[i+1]), val(a[i+1])
+
+ got := doOp(x, op, y)
+ want := val(a[i+3])
+ if !eql(got, want) {
+ t.Errorf("%s: got %s; want %s", test, got, want)
+ }
+ if x0 != nil && !eql(x, x0) {
+ t.Errorf("%s: x changed to %s", test, x)
+ }
+ if !eql(y, y0) {
+ t.Errorf("%s: y changed to %s", test, y)
+ }
+ }
+}
+
+func eql(x, y Value) bool {
+ _, ux := x.(unknownVal)
+ _, uy := y.(unknownVal)
+ if ux || uy {
+ return ux == uy
+ }
+ return Compare(x, token.EQL, y)
+}
+
+// ----------------------------------------------------------------------------
+// Support functions
+
+func val(lit string) Value {
+ if len(lit) == 0 {
+ return MakeUnknown()
+ }
+
+ switch lit {
+ case "?":
+ return MakeUnknown()
+ case "true":
+ return MakeBool(true)
+ case "false":
+ return MakeBool(false)
+ }
+
+ tok := token.INT
+ switch first, last := lit[0], lit[len(lit)-1]; {
+ case first == '"' || first == '`':
+ tok = token.STRING
+ lit = strings.Replace(lit, "_", " ", -1)
+ case first == '\'':
+ tok = token.CHAR
+ case last == 'i':
+ tok = token.IMAG
+ default:
+ if !strings.HasPrefix(lit, "0x") && strings.ContainsAny(lit, "./Ee") {
+ tok = token.FLOAT
+ }
+ }
+
+ return MakeFromLiteral(lit, tok, 0)
+}
+
+var optab = map[string]token.Token{
+ "!": token.NOT,
+
+ "+": token.ADD,
+ "-": token.SUB,
+ "*": token.MUL,
+ "/": token.QUO,
+ "%": token.REM,
+
+ "<<": token.SHL,
+ ">>": token.SHR,
+
+ "&": token.AND,
+ "|": token.OR,
+ "^": token.XOR,
+ "&^": token.AND_NOT,
+
+ "==": token.EQL,
+ "!=": token.NEQ,
+ "<": token.LSS,
+ "<=": token.LEQ,
+ ">": token.GTR,
+ ">=": token.GEQ,
+}
+
+func panicHandler(v *Value) {
+ switch p := recover().(type) {
+ case nil:
+ // nothing to do
+ case string:
+ *v = MakeString(p)
+ case error:
+ *v = MakeString(p.Error())
+ default:
+ panic(p)
+ }
+}
+
+func doOp(x Value, op token.Token, y Value) (z Value) {
+ defer panicHandler(&z)
+
+ if x == nil {
+ return UnaryOp(op, y, 0)
+ }
+
+ switch op {
+ case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
+ return MakeBool(Compare(x, op, y))
+ case token.SHL, token.SHR:
+ s, _ := Int64Val(y)
+ return Shift(x, op, uint(s))
+ default:
+ return BinaryOp(x, op, y)
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Other tests
+
+var fracTests = []string{
+ "0 0 1",
+ "1 1 1",
+ "-1 -1 1",
+ "1.2 6 5",
+ "-0.991 -991 1000",
+ "1e100 1e100 1",
+}
+
+func TestFractions(t *testing.T) {
+ for _, test := range fracTests {
+ a := strings.Split(test, " ")
+ if len(a) != 3 {
+ t.Errorf("invalid test case: %s", test)
+ continue
+ }
+
+ x := val(a[0])
+ n := val(a[1])
+ d := val(a[2])
+
+ if got := Num(x); !eql(got, n) {
+ t.Errorf("%s: got num = %s; want %s", test, got, n)
+ }
+
+ if got := Denom(x); !eql(got, d) {
+ t.Errorf("%s: got denom = %s; want %s", test, got, d)
+ }
+ }
+}
+
+var bytesTests = []string{
+ "0",
+ "1",
+ "123456789",
+ "123456789012345678901234567890123456789012345678901234567890",
+}
+
+func TestBytes(t *testing.T) {
+ for _, test := range bytesTests {
+ x := val(test)
+ bytes := Bytes(x)
+
+ // special case 0
+ if Sign(x) == 0 && len(bytes) != 0 {
+ t.Errorf("%s: got %v; want empty byte slice", test, bytes)
+ }
+
+ if n := len(bytes); n > 0 && bytes[n-1] == 0 {
+ t.Errorf("%s: got %v; want no leading 0 byte", test, bytes)
+ }
+
+ if got := MakeFromBytes(bytes); !eql(got, x) {
+ t.Errorf("%s: got %s; want %s (bytes = %v)", test, got, x, bytes)
+ }
+ }
+}
+
+func TestUnknown(t *testing.T) {
+ u := MakeUnknown()
+ var values = []Value{
+ u,
+ MakeBool(false), // token.ADD ok below, operation is never considered
+ MakeString(""),
+ MakeInt64(1),
+ MakeFromLiteral("-1234567890123456789012345678901234567890", token.INT, 0),
+ MakeFloat64(1.2),
+ MakeImag(MakeFloat64(1.2)),
+ }
+ for _, val := range values {
+ x, y := val, u
+ for i := range [2]int{} {
+ if i == 1 {
+ x, y = y, x
+ }
+ if got := BinaryOp(x, token.ADD, y); got.Kind() != Unknown {
+ t.Errorf("%s + %s: got %s; want %s", x, y, got, u)
+ }
+ if got := Compare(x, token.EQL, y); got {
+ t.Errorf("%s == %s: got true; want false", x, y)
+ }
+ }
+ }
+}
diff --git a/libgo/go/go/doc/doc.go b/libgo/go/go/doc/doc.go
index 4264940a0cd..3c3e28d48fb 100644
--- a/libgo/go/go/doc/doc.go
+++ b/libgo/go/go/doc/doc.go
@@ -18,7 +18,8 @@ type Package struct {
Imports []string
Filenames []string
Notes map[string][]*Note
- // DEPRECATED. For backward compatibility Bugs is still populated,
+
+ // Deprecated: For backward compatibility Bugs is still populated,
// but all new code should use Notes instead.
Bugs []string
diff --git a/libgo/go/go/doc/exports.go b/libgo/go/go/doc/exports.go
index 1d3b466d8c7..4a12b1e55f8 100644
--- a/libgo/go/go/doc/exports.go
+++ b/libgo/go/go/doc/exports.go
@@ -12,13 +12,12 @@ import (
)
// filterIdentList removes unexported names from list in place
-// and returns the resulting list. If blankOk is set, blank
-// identifiers are considered exported names.
+// and returns the resulting list.
//
-func filterIdentList(list []*ast.Ident, blankOk bool) []*ast.Ident {
+func filterIdentList(list []*ast.Ident) []*ast.Ident {
j := 0
for _, x := range list {
- if ast.IsExported(x.Name) || (blankOk && x.Name == "_") {
+ if ast.IsExported(x.Name) {
list[j] = x
j++
}
@@ -26,6 +25,17 @@ func filterIdentList(list []*ast.Ident, blankOk bool) []*ast.Ident {
return list[0:j]
}
+// hasExportedName reports whether list contains any exported names.
+//
+func hasExportedName(list []*ast.Ident) bool {
+ for _, x := range list {
+ if x.IsExported() {
+ return true
+ }
+ }
+ return false
+}
+
// removeErrorField removes anonymous fields named "error" from an interface.
// This is called when "error" has been determined to be a local name,
// not the predeclared type.
@@ -53,7 +63,7 @@ func removeErrorField(ityp *ast.InterfaceType) {
}
// filterFieldList removes unexported fields (field names) from the field list
-// in place and returns true if fields were removed. Anonymous fields are
+// in place and reports whether fields were removed. Anonymous fields are
// recorded with the parent type. filterType is called with the types of
// all remaining fields.
//
@@ -78,7 +88,7 @@ func (r *reader) filterFieldList(parent *namedType, fields *ast.FieldList, ityp
r.remember(ityp)
}
} else {
- field.Names = filterIdentList(field.Names, false)
+ field.Names = filterIdentList(field.Names)
if len(field.Names) < n {
removedFields = true
}
@@ -146,9 +156,7 @@ func (r *reader) filterSpec(spec ast.Spec, tok token.Token) bool {
// always keep imports so we can collect them
return true
case *ast.ValueSpec:
- // special case: consider blank constants as exported
- // (work-around for issue 5397)
- s.Names = filterIdentList(s.Names, tok == token.CONST)
+ s.Names = filterIdentList(s.Names)
if len(s.Names) > 0 {
r.filterType(nil, s.Type)
return true
@@ -165,7 +173,46 @@ func (r *reader) filterSpec(spec ast.Spec, tok token.Token) bool {
return false
}
+// copyConstType returns a copy of typ with position pos.
+// typ must be a valid constant type.
+// In practice, only (possibly qualified) identifiers are possible.
+//
+func copyConstType(typ ast.Expr, pos token.Pos) ast.Expr {
+ switch typ := typ.(type) {
+ case *ast.Ident:
+ return &ast.Ident{Name: typ.Name, NamePos: pos}
+ case *ast.SelectorExpr:
+ if id, ok := typ.X.(*ast.Ident); ok {
+ // presumably a qualified identifier
+ return &ast.SelectorExpr{
+ Sel: ast.NewIdent(typ.Sel.Name),
+ X: &ast.Ident{Name: id.Name, NamePos: pos},
+ }
+ }
+ }
+ return nil // shouldn't happen, but be conservative and don't panic
+}
+
func (r *reader) filterSpecList(list []ast.Spec, tok token.Token) []ast.Spec {
+ if tok == token.CONST {
+ // Propagate any type information that would get lost otherwise
+ // when unexported constants are filtered.
+ var prevType ast.Expr
+ for _, spec := range list {
+ spec := spec.(*ast.ValueSpec)
+ if spec.Type == nil && prevType != nil {
+ // provide current spec with an explicit type
+ spec.Type = copyConstType(prevType, spec.Pos())
+ }
+ if hasExportedName(spec.Names) {
+ // exported names are preserved so there's no need to propagate the type
+ prevType = nil
+ } else {
+ prevType = spec.Type
+ }
+ }
+ }
+
j := 0
for _, s := range list {
if r.filterSpec(s, tok) {
diff --git a/libgo/go/go/doc/testdata/blank.0.golden b/libgo/go/go/doc/testdata/blank.0.golden
index dae3ab2affa..c2987cf140e 100644
--- a/libgo/go/go/doc/testdata/blank.0.golden
+++ b/libgo/go/go/doc/testdata/blank.0.golden
@@ -4,14 +4,33 @@ PACKAGE blank
IMPORTPATH
testdata/blank
+IMPORTS
+ os
+
FILENAMES
testdata/blank.go
CONSTANTS
+ // T constants counting from unexported constants.
+ const (
+ C1 T
+ C2
+
+ C3
+
+ C4 int
+ )
+
+ // Constants with an imported type that needs to be propagated.
+ const (
+ Default os.FileMode = 0644
+ Useless = 0312
+ WideOpen = 0777
+ )
+
// Package constants.
const (
- _ int = iota
- I1
+ I1 int
I2
)
@@ -28,10 +47,9 @@ TYPES
//
type T int
- // T constants.
+ // T constants counting from a blank constant.
const (
- _ T = iota
- T1
+ T1 T
T2
)
diff --git a/libgo/go/go/doc/testdata/blank.1.golden b/libgo/go/go/doc/testdata/blank.1.golden
index 333d7e5b040..ee5054a4ed6 100644
--- a/libgo/go/go/doc/testdata/blank.1.golden
+++ b/libgo/go/go/doc/testdata/blank.1.golden
@@ -4,10 +4,25 @@ PACKAGE blank
IMPORTPATH
testdata/blank
+IMPORTS
+ os
+
FILENAMES
testdata/blank.go
CONSTANTS
+ // T constants counting from unexported constants.
+ const (
+ tweedledee T = iota
+ tweedledum
+ C1
+ C2
+ alice
+ C3
+ redQueen int = iota
+ C4
+ )
+
// Package constants.
const (
_ int = iota
@@ -15,6 +30,20 @@ CONSTANTS
I2
)
+ // Constants with an imported type that needs to be propagated.
+ const (
+ zero os.FileMode = 0
+ Default = 0644
+ Useless = 0312
+ WideOpen = 0777
+ )
+
+ // Unexported constants counting from blank iota. See issue 9615.
+ const (
+ _ = iota
+ one = iota + 1
+ )
+
VARIABLES
//
@@ -37,7 +66,7 @@ TYPES
//
type T int
- // T constants.
+ // T constants counting from a blank constant.
const (
_ T = iota
T1
diff --git a/libgo/go/go/doc/testdata/blank.2.golden b/libgo/go/go/doc/testdata/blank.2.golden
index dae3ab2affa..c2987cf140e 100644
--- a/libgo/go/go/doc/testdata/blank.2.golden
+++ b/libgo/go/go/doc/testdata/blank.2.golden
@@ -4,14 +4,33 @@ PACKAGE blank
IMPORTPATH
testdata/blank
+IMPORTS
+ os
+
FILENAMES
testdata/blank.go
CONSTANTS
+ // T constants counting from unexported constants.
+ const (
+ C1 T
+ C2
+
+ C3
+
+ C4 int
+ )
+
+ // Constants with an imported type that needs to be propagated.
+ const (
+ Default os.FileMode = 0644
+ Useless = 0312
+ WideOpen = 0777
+ )
+
// Package constants.
const (
- _ int = iota
- I1
+ I1 int
I2
)
@@ -28,10 +47,9 @@ TYPES
//
type T int
- // T constants.
+ // T constants counting from a blank constant.
const (
- _ T = iota
- T1
+ T1 T
T2
)
diff --git a/libgo/go/go/doc/testdata/blank.go b/libgo/go/go/doc/testdata/blank.go
index f812c77b777..419a78f7d51 100644
--- a/libgo/go/go/doc/testdata/blank.go
+++ b/libgo/go/go/doc/testdata/blank.go
@@ -6,15 +6,37 @@
// See issue 5397.
package blank
+import "os"
+
type T int
-// T constants.
+// T constants counting from a blank constant.
const (
_ T = iota
T1
T2
)
+// T constants counting from unexported constants.
+const (
+ tweedledee T = iota
+ tweedledum
+ C1
+ C2
+ alice
+ C3
+ redQueen int = iota
+ C4
+)
+
+// Constants with an imported type that needs to be propagated.
+const (
+ zero os.FileMode = 0
+ Default = 0644
+ Useless = 0312
+ WideOpen = 0777
+)
+
// Package constants.
const (
_ int = iota
@@ -22,6 +44,13 @@ const (
I2
)
+// Unexported constants counting from blank iota.
+// See issue 9615.
+const (
+ _ = iota
+ one = iota + 1
+)
+
// Blanks not in doc output:
// S has a padding field.
diff --git a/libgo/go/go/format/format.go b/libgo/go/go/format/format.go
index 668a42df2df..1adfd7d45e8 100644
--- a/libgo/go/go/format/format.go
+++ b/libgo/go/go/format/format.go
@@ -12,8 +12,8 @@ import (
"go/parser"
"go/printer"
"go/token"
+ "internal/format"
"io"
- "strings"
)
var config = printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8}
@@ -82,7 +82,7 @@ func Node(dst io.Writer, fset *token.FileSet, node interface{}) error {
//
func Source(src []byte) ([]byte, error) {
fset := token.NewFileSet()
- file, sourceAdj, indentAdj, err := parse(fset, "", src, true)
+ file, sourceAdj, indentAdj, err := format.Parse(fset, "", src, true)
if err != nil {
return nil, err
}
@@ -93,7 +93,7 @@ func Source(src []byte) ([]byte, error) {
ast.SortImports(fset, file)
}
- return format(fset, file, sourceAdj, indentAdj, src, config)
+ return format.Format(fset, file, sourceAdj, indentAdj, src, config)
}
func hasUnsortedImports(file *ast.File) bool {
@@ -113,154 +113,3 @@ func hasUnsortedImports(file *ast.File) bool {
}
return false
}
-
-// ----------------------------------------------------------------------------
-// Support functions
-//
-// The functions parse, format, and isSpace below are identical to the
-// respective functions in cmd/gofmt/gofmt.go - keep them in sync!
-//
-// TODO(gri) Factor out this functionality, eventually.
-
-// parse parses src, which was read from the named file,
-// as a Go source file, declaration, or statement list.
-func parse(fset *token.FileSet, filename string, src []byte, fragmentOk bool) (
- file *ast.File,
- sourceAdj func(src []byte, indent int) []byte,
- indentAdj int,
- err error,
-) {
- // Try as whole source file.
- file, err = parser.ParseFile(fset, filename, src, parserMode)
- // If there's no error, return. If the error is that the source file didn't begin with a
- // package line and source fragments are ok, fall through to
- // try as a source fragment. Stop and return on any other error.
- if err == nil || !fragmentOk || !strings.Contains(err.Error(), "expected 'package'") {
- return
- }
-
- // If this is a declaration list, make it a source file
- // by inserting a package clause.
- // Insert using a ;, not a newline, so that the line numbers
- // in psrc match the ones in src.
- psrc := append([]byte("package p;"), src...)
- file, err = parser.ParseFile(fset, filename, psrc, parserMode)
- if err == nil {
- sourceAdj = func(src []byte, indent int) []byte {
- // Remove the package clause.
- // Gofmt has turned the ; into a \n.
- src = src[indent+len("package p\n"):]
- return bytes.TrimSpace(src)
- }
- return
- }
- // If the error is that the source file didn't begin with a
- // declaration, fall through to try as a statement list.
- // Stop and return on any other error.
- if !strings.Contains(err.Error(), "expected declaration") {
- return
- }
-
- // If this is a statement list, make it a source file
- // by inserting a package clause and turning the list
- // into a function body. This handles expressions too.
- // Insert using a ;, not a newline, so that the line numbers
- // in fsrc match the ones in src.
- fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '}')
- file, err = parser.ParseFile(fset, filename, fsrc, parserMode)
- if err == nil {
- sourceAdj = func(src []byte, indent int) []byte {
- // Cap adjusted indent to zero.
- if indent < 0 {
- indent = 0
- }
- // Remove the wrapping.
- // Gofmt has turned the ; into a \n\n.
- // There will be two non-blank lines with indent, hence 2*indent.
- src = src[2*indent+len("package p\n\nfunc _() {"):]
- src = src[:len(src)-(indent+len("\n}\n"))]
- return bytes.TrimSpace(src)
- }
- // Gofmt has also indented the function body one level.
- // Adjust that with indentAdj.
- indentAdj = -1
- }
-
- // Succeeded, or out of options.
- return
-}
-
-// format formats the given package file originally obtained from src
-// and adjusts the result based on the original source via sourceAdj
-// and indentAdj.
-func format(
- fset *token.FileSet,
- file *ast.File,
- sourceAdj func(src []byte, indent int) []byte,
- indentAdj int,
- src []byte,
- cfg printer.Config,
-) ([]byte, error) {
- if sourceAdj == nil {
- // Complete source file.
- var buf bytes.Buffer
- err := cfg.Fprint(&buf, fset, file)
- if err != nil {
- return nil, err
- }
- return buf.Bytes(), nil
- }
-
- // Partial source file.
- // Determine and prepend leading space.
- i, j := 0, 0
- for j < len(src) && isSpace(src[j]) {
- if src[j] == '\n' {
- i = j + 1 // byte offset of last line in leading space
- }
- j++
- }
- var res []byte
- res = append(res, src[:i]...)
-
- // Determine and prepend indentation of first code line.
- // Spaces are ignored unless there are no tabs,
- // in which case spaces count as one tab.
- indent := 0
- hasSpace := false
- for _, b := range src[i:j] {
- switch b {
- case ' ':
- hasSpace = true
- case '\t':
- indent++
- }
- }
- if indent == 0 && hasSpace {
- indent = 1
- }
- for i := 0; i < indent; i++ {
- res = append(res, '\t')
- }
-
- // Format the source.
- // Write it without any leading and trailing space.
- cfg.Indent = indent + indentAdj
- var buf bytes.Buffer
- err := cfg.Fprint(&buf, fset, file)
- if err != nil {
- return nil, err
- }
- res = append(res, sourceAdj(buf.Bytes(), cfg.Indent)...)
-
- // Determine and append trailing space.
- i = len(src)
- for i > 0 && isSpace(src[i-1]) {
- i--
- }
- return append(res, src[i:]...), nil
-}
-
-func isSpace(b byte) bool {
- return b == ' ' || b == '\t' || b == '\n' || b == '\r'
-}
diff --git a/libgo/go/go/format/format_test.go b/libgo/go/go/format/format_test.go
index d7846bec652..000c611aa25 100644
--- a/libgo/go/go/format/format_test.go
+++ b/libgo/go/go/format/format_test.go
@@ -91,7 +91,11 @@ var tests = []string{
"\n\t\t\n\n\t\t\tx := 0\n\t\t\tconst s = `\n\t\tfoo\n`\n\n\n", // no indentation removed inside raw strings
// comments
- "i := 5 /* Comment */", // Issue 5551.
+ "i := 5 /* Comment */", // Issue 5551.
+ "\ta()\n//line :1", // Issue 11276.
+ "\t//xxx\n\ta()\n//line :2", // Issue 11276.
+ "\ta() //line :1\n\tb()\n", // Issue 11276.
+ "x := 0\n//line :1\n//line :2", // Issue 11276.
// erroneous programs
"ERROR1 + 2 +",
diff --git a/libgo/go/go/importer/importer.go b/libgo/go/go/importer/importer.go
new file mode 100644
index 00000000000..4590ca30e60
--- /dev/null
+++ b/libgo/go/go/importer/importer.go
@@ -0,0 +1,69 @@
+// Copyright 2015 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 importer provides access to export data importers.
+package importer
+
+import (
+ "go/internal/gccgoimporter"
+ "go/internal/gcimporter"
+ "go/types"
+ "io"
+ "runtime"
+)
+
+// A Lookup function returns a reader to access package data for
+// a given import path, or an error if no matching package is found.
+type Lookup func(path string) (io.ReadCloser, error)
+
+// For returns an Importer for the given compiler and lookup interface,
+// or nil. Supported compilers are "gc", and "gccgo". If lookup is nil,
+// the default package lookup mechanism for the given compiler is used.
+func For(compiler string, lookup Lookup) types.Importer {
+ switch compiler {
+ case "gc":
+ if lookup == nil {
+ return make(gcimports)
+ }
+ panic("gc importer for custom import path lookup not yet implemented")
+ case "gccgo":
+ if lookup == nil {
+ var inst gccgoimporter.GccgoInstallation
+ if err := inst.InitFromDriver("gccgo"); err != nil {
+ return nil
+ }
+ return &gccgoimports{
+ packages: make(map[string]*types.Package),
+ importer: inst.GetImporter(nil, nil),
+ }
+ }
+ panic("gccgo importer for custom import path lookup not yet implemented")
+ }
+ // compiler not supported
+ return nil
+}
+
+// Default returns an Importer for the compiler that built the running binary.
+func Default() types.Importer {
+ return For(runtime.Compiler, nil)
+}
+
+// gc support
+
+type gcimports map[string]*types.Package
+
+func (m gcimports) Import(path string) (*types.Package, error) {
+ return gcimporter.Import(m, path)
+}
+
+// gccgo support
+
+type gccgoimports struct {
+ packages map[string]*types.Package
+ importer gccgoimporter.Importer
+}
+
+func (m *gccgoimports) Import(path string) (*types.Package, error) {
+ return m.importer(m.packages, path)
+}
diff --git a/libgo/go/go/internal/gccgoimporter/gccgoinstallation.go b/libgo/go/go/internal/gccgoimporter/gccgoinstallation.go
new file mode 100644
index 00000000000..622dfc8b696
--- /dev/null
+++ b/libgo/go/go/internal/gccgoimporter/gccgoinstallation.go
@@ -0,0 +1,94 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gccgoimporter
+
+import (
+ "bufio"
+ "go/types"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+)
+
+// Information about a specific installation of gccgo.
+type GccgoInstallation struct {
+ // Version of gcc (e.g. 4.8.0).
+ GccVersion string
+
+ // Target triple (e.g. x86_64-unknown-linux-gnu).
+ TargetTriple string
+
+ // Built-in library paths used by this installation.
+ LibPaths []string
+}
+
+// Ask the driver at the given path for information for this GccgoInstallation.
+func (inst *GccgoInstallation) InitFromDriver(gccgoPath string) (err error) {
+ cmd := exec.Command(gccgoPath, "-###", "-S", "-x", "go", "-")
+ stderr, err := cmd.StderrPipe()
+ if err != nil {
+ return
+ }
+
+ err = cmd.Start()
+ if err != nil {
+ return
+ }
+
+ scanner := bufio.NewScanner(stderr)
+ for scanner.Scan() {
+ line := scanner.Text()
+ switch {
+ case strings.HasPrefix(line, "Target: "):
+ inst.TargetTriple = line[8:]
+
+ case line[0] == ' ':
+ args := strings.Fields(line)
+ for _, arg := range args[1:] {
+ if strings.HasPrefix(arg, "-L") {
+ inst.LibPaths = append(inst.LibPaths, arg[2:])
+ }
+ }
+ }
+ }
+
+ stdout, err := exec.Command(gccgoPath, "-dumpversion").Output()
+ if err != nil {
+ return
+ }
+ inst.GccVersion = strings.TrimSpace(string(stdout))
+
+ return
+}
+
+// Return the list of export search paths for this GccgoInstallation.
+func (inst *GccgoInstallation) SearchPaths() (paths []string) {
+ for _, lpath := range inst.LibPaths {
+ spath := filepath.Join(lpath, "go", inst.GccVersion)
+ fi, err := os.Stat(spath)
+ if err != nil || !fi.IsDir() {
+ continue
+ }
+ paths = append(paths, spath)
+
+ spath = filepath.Join(spath, inst.TargetTriple)
+ fi, err = os.Stat(spath)
+ if err != nil || !fi.IsDir() {
+ continue
+ }
+ paths = append(paths, spath)
+ }
+
+ paths = append(paths, inst.LibPaths...)
+
+ return
+}
+
+// Return an importer that searches incpaths followed by the gcc installation's
+// built-in search paths and the current directory.
+func (inst *GccgoInstallation) GetImporter(incpaths []string, initmap map[*types.Package]InitData) Importer {
+ return GetImporter(append(append(incpaths, inst.SearchPaths()...), "."), initmap)
+}
diff --git a/libgo/go/go/internal/gccgoimporter/gccgoinstallation_test.go b/libgo/go/go/internal/gccgoimporter/gccgoinstallation_test.go
new file mode 100644
index 00000000000..a2acaf7331a
--- /dev/null
+++ b/libgo/go/go/internal/gccgoimporter/gccgoinstallation_test.go
@@ -0,0 +1,197 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gccgoimporter
+
+import (
+ "go/types"
+ "runtime"
+ "testing"
+)
+
+var importablePackages = [...]string{
+ "archive/tar",
+ "archive/zip",
+ "bufio",
+ "bytes",
+ "compress/bzip2",
+ "compress/flate",
+ "compress/gzip",
+ "compress/lzw",
+ "compress/zlib",
+ "container/heap",
+ "container/list",
+ "container/ring",
+ "crypto/aes",
+ "crypto/cipher",
+ "crypto/des",
+ "crypto/dsa",
+ "crypto/ecdsa",
+ "crypto/elliptic",
+ "crypto",
+ "crypto/hmac",
+ "crypto/md5",
+ "crypto/rand",
+ "crypto/rc4",
+ "crypto/rsa",
+ "crypto/sha1",
+ "crypto/sha256",
+ "crypto/sha512",
+ "crypto/subtle",
+ "crypto/tls",
+ "crypto/x509",
+ "crypto/x509/pkix",
+ "database/sql/driver",
+ "database/sql",
+ "debug/dwarf",
+ "debug/elf",
+ "debug/gosym",
+ "debug/macho",
+ "debug/pe",
+ "encoding/ascii85",
+ "encoding/asn1",
+ "encoding/base32",
+ "encoding/base64",
+ "encoding/binary",
+ "encoding/csv",
+ "encoding/gob",
+ "encoding",
+ "encoding/hex",
+ "encoding/json",
+ "encoding/pem",
+ "encoding/xml",
+ "errors",
+ "exp/proxy",
+ "exp/terminal",
+ "expvar",
+ "flag",
+ "fmt",
+ "go/ast",
+ "go/build",
+ "go/doc",
+ "go/format",
+ "go/parser",
+ "go/printer",
+ "go/scanner",
+ "go/token",
+ "hash/adler32",
+ "hash/crc32",
+ "hash/crc64",
+ "hash/fnv",
+ "hash",
+ "html",
+ "html/template",
+ "image/color",
+ "image/color/palette",
+ "image/draw",
+ "image/gif",
+ "image",
+ "image/jpeg",
+ "image/png",
+ "index/suffixarray",
+ "io",
+ "io/ioutil",
+ "log",
+ "log/syslog",
+ "math/big",
+ "math/cmplx",
+ "math",
+ "math/rand",
+ "mime",
+ "mime/multipart",
+ "net",
+ "net/http/cgi",
+ "net/http/cookiejar",
+ "net/http/fcgi",
+ "net/http",
+ "net/http/httptest",
+ "net/http/httputil",
+ "net/http/pprof",
+ "net/mail",
+ "net/rpc",
+ "net/rpc/jsonrpc",
+ "net/smtp",
+ "net/textproto",
+ "net/url",
+ "old/regexp",
+ "old/template",
+ "os/exec",
+ "os",
+ "os/signal",
+ "os/user",
+ "path/filepath",
+ "path",
+ "reflect",
+ "regexp",
+ "regexp/syntax",
+ "runtime/debug",
+ "runtime",
+ "runtime/pprof",
+ "sort",
+ "strconv",
+ "strings",
+ "sync/atomic",
+ "sync",
+ "syscall",
+ "testing",
+ "testing/iotest",
+ "testing/quick",
+ "text/scanner",
+ "text/tabwriter",
+ "text/template",
+ "text/template/parse",
+ "time",
+ "unicode",
+ "unicode/utf16",
+ "unicode/utf8",
+}
+
+func TestInstallationImporter(t *testing.T) {
+ // This test relies on gccgo being around, which it most likely will be if we
+ // were compiled with gccgo.
+ if runtime.Compiler != "gccgo" {
+ t.Skip("This test needs gccgo")
+ return
+ }
+
+ // Even when we have gccgo, this doesn't work while building
+ // gccgo itself.
+ t.Skip("gccgo is not necessarily available")
+
+ var inst GccgoInstallation
+ err := inst.InitFromDriver("gccgo")
+ if err != nil {
+ t.Fatal(err)
+ }
+ imp := inst.GetImporter(nil, nil)
+
+ // Ensure we don't regress the number of packages we can parse. First import
+ // all packages into the same map and then each individually.
+ pkgMap := make(map[string]*types.Package)
+ for _, pkg := range importablePackages {
+ _, err = imp(pkgMap, pkg)
+ if err != nil {
+ t.Error(err)
+ }
+ }
+
+ for _, pkg := range importablePackages {
+ _, err = imp(make(map[string]*types.Package), pkg)
+ if err != nil {
+ t.Error(err)
+ }
+ }
+
+ // Test for certain specific entities in the imported data.
+ for _, test := range [...]importerTest{
+ {pkgpath: "io", name: "Reader", want: "type Reader interface{Read(p []uint8) (n int, err error)}"},
+ {pkgpath: "io", name: "ReadWriter", want: "type ReadWriter interface{Reader; Writer}"},
+ {pkgpath: "math", name: "Pi", want: "const Pi untyped float"},
+ {pkgpath: "math", name: "Sin", want: "func Sin(x float64) float64"},
+ {pkgpath: "sort", name: "Ints", want: "func Ints(a []int)"},
+ {pkgpath: "unsafe", name: "Pointer", want: "type Pointer unsafe.Pointer"},
+ } {
+ runImporterTest(t, imp, nil, &test)
+ }
+}
diff --git a/libgo/go/go/internal/gccgoimporter/importer.go b/libgo/go/go/internal/gccgoimporter/importer.go
new file mode 100644
index 00000000000..aa0d01afdf3
--- /dev/null
+++ b/libgo/go/go/internal/gccgoimporter/importer.go
@@ -0,0 +1,212 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package gccgoimporter implements Import for gccgo-generated object files.
+package gccgoimporter // import "go/internal/gccgoimporter"
+
+import (
+ "bytes"
+ "debug/elf"
+ "fmt"
+ "go/types"
+ "io"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+)
+
+// A PackageInit describes an imported package that needs initialization.
+type PackageInit struct {
+ Name string // short package name
+ InitFunc string // name of init function
+ Priority int // priority of init function, see InitData.Priority
+}
+
+// The gccgo-specific init data for a package.
+type InitData struct {
+ // Initialization priority of this package relative to other packages.
+ // This is based on the maximum depth of the package's dependency graph;
+ // it is guaranteed to be greater than that of its dependencies.
+ Priority int
+
+ // The list of packages which this package depends on to be initialized,
+ // including itself if needed. This is the subset of the transitive closure of
+ // the package's dependencies that need initialization.
+ Inits []PackageInit
+}
+
+// Locate the file from which to read export data.
+// This is intended to replicate the logic in gofrontend.
+func findExportFile(searchpaths []string, pkgpath string) (string, error) {
+ for _, spath := range searchpaths {
+ pkgfullpath := filepath.Join(spath, pkgpath)
+ pkgdir, name := filepath.Split(pkgfullpath)
+
+ for _, filepath := range [...]string{
+ pkgfullpath,
+ pkgfullpath + ".gox",
+ pkgdir + "lib" + name + ".so",
+ pkgdir + "lib" + name + ".a",
+ pkgfullpath + ".o",
+ } {
+ fi, err := os.Stat(filepath)
+ if err == nil && !fi.IsDir() {
+ return filepath, nil
+ }
+ }
+ }
+
+ return "", fmt.Errorf("%s: could not find export data (tried %s)", pkgpath, strings.Join(searchpaths, ":"))
+}
+
+const (
+ gccgov1Magic = "v1;\n"
+ goimporterMagic = "\n$$ "
+ archiveMagic = "!<ar"
+)
+
+// Opens the export data file at the given path. If this is an ELF file,
+// searches for and opens the .go_export section. If this is an archive,
+// reads the export data from the first member, which is assumed to be an ELF file.
+// This is intended to replicate the logic in gofrontend.
+func openExportFile(fpath string) (reader io.ReadSeeker, closer io.Closer, err error) {
+ f, err := os.Open(fpath)
+ if err != nil {
+ return
+ }
+ closer = f
+ defer func() {
+ if err != nil && closer != nil {
+ f.Close()
+ }
+ }()
+
+ var magic [4]byte
+ _, err = f.ReadAt(magic[:], 0)
+ if err != nil {
+ return
+ }
+ // reset to offset 0 - needed on Plan 9 (see issue #11265)
+ // TODO: remove once issue #11265 has been resolved.
+ _, err = f.Seek(0, 0)
+ if err != nil {
+ return
+ }
+
+ var elfreader io.ReaderAt
+ switch string(magic[:]) {
+ case gccgov1Magic, goimporterMagic:
+ // Raw export data.
+ reader = f
+ return
+
+ case archiveMagic:
+ // TODO(pcc): Read the archive directly instead of using "ar".
+ f.Close()
+ closer = nil
+
+ cmd := exec.Command("ar", "p", fpath)
+ var out []byte
+ out, err = cmd.Output()
+ if err != nil {
+ return
+ }
+
+ elfreader = bytes.NewReader(out)
+
+ default:
+ elfreader = f
+ }
+
+ ef, err := elf.NewFile(elfreader)
+ if err != nil {
+ return
+ }
+
+ sec := ef.Section(".go_export")
+ if sec == nil {
+ err = fmt.Errorf("%s: .go_export section not found", fpath)
+ return
+ }
+
+ reader = sec.Open()
+ return
+}
+
+// An Importer resolves import paths to Packages. The imports map records
+// packages already known, indexed by package path.
+// An importer must determine the canonical package path and check imports
+// to see if it is already present in the map. If so, the Importer can return
+// the map entry. Otherwise, the importer must load the package data for the
+// given path into a new *Package, record it in imports map, and return the
+// package.
+type Importer func(imports map[string]*types.Package, path string) (*types.Package, error)
+
+func GetImporter(searchpaths []string, initmap map[*types.Package]InitData) Importer {
+ return func(imports map[string]*types.Package, pkgpath string) (pkg *types.Package, err error) {
+ if pkgpath == "unsafe" {
+ return types.Unsafe, nil
+ }
+
+ fpath, err := findExportFile(searchpaths, pkgpath)
+ if err != nil {
+ return
+ }
+
+ reader, closer, err := openExportFile(fpath)
+ if err != nil {
+ return
+ }
+ if closer != nil {
+ defer closer.Close()
+ }
+
+ var magic [4]byte
+ _, err = reader.Read(magic[:])
+ if err != nil {
+ return
+ }
+ _, err = reader.Seek(0, 0)
+ if err != nil {
+ return
+ }
+
+ switch string(magic[:]) {
+ case gccgov1Magic:
+ var p parser
+ p.init(fpath, reader, imports)
+ pkg = p.parsePackage()
+ if initmap != nil {
+ initmap[pkg] = p.initdata
+ }
+
+ // Excluded for now: Standard gccgo doesn't support this import format currently.
+ // case goimporterMagic:
+ // var data []byte
+ // data, err = ioutil.ReadAll(reader)
+ // if err != nil {
+ // return
+ // }
+ // var n int
+ // n, pkg, err = importer.ImportData(imports, data)
+ // if err != nil {
+ // return
+ // }
+
+ // if initmap != nil {
+ // suffixreader := bytes.NewReader(data[n:])
+ // var p parser
+ // p.init(fpath, suffixreader, nil)
+ // p.parseInitData()
+ // initmap[pkg] = p.initdata
+ // }
+
+ default:
+ err = fmt.Errorf("unrecognized magic string: %q", string(magic[:]))
+ }
+
+ return
+ }
+}
diff --git a/libgo/go/go/internal/gccgoimporter/importer_test.go b/libgo/go/go/internal/gccgoimporter/importer_test.go
new file mode 100644
index 00000000000..f3bcadbaf77
--- /dev/null
+++ b/libgo/go/go/internal/gccgoimporter/importer_test.go
@@ -0,0 +1,171 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gccgoimporter
+
+import (
+ "go/types"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "testing"
+)
+
+type importerTest struct {
+ pkgpath, name, want, wantval string
+ wantinits []string
+}
+
+func runImporterTest(t *testing.T, imp Importer, initmap map[*types.Package]InitData, test *importerTest) {
+ pkg, err := imp(make(map[string]*types.Package), test.pkgpath)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ if test.name != "" {
+ obj := pkg.Scope().Lookup(test.name)
+ if obj == nil {
+ t.Errorf("%s: object not found", test.name)
+ return
+ }
+
+ got := types.ObjectString(obj, types.RelativeTo(pkg))
+ if got != test.want {
+ t.Errorf("%s: got %q; want %q", test.name, got, test.want)
+ }
+
+ if test.wantval != "" {
+ gotval := obj.(*types.Const).Val().String()
+ if gotval != test.wantval {
+ t.Errorf("%s: got val %q; want val %q", test.name, gotval, test.wantval)
+ }
+ }
+ }
+
+ if len(test.wantinits) > 0 {
+ initdata := initmap[pkg]
+ found := false
+ // Check that the package's own init function has the package's priority
+ for _, pkginit := range initdata.Inits {
+ if pkginit.InitFunc == test.wantinits[0] {
+ if initdata.Priority != pkginit.Priority {
+ t.Errorf("%s: got self priority %d; want %d", test.pkgpath, pkginit.Priority, initdata.Priority)
+ }
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ t.Errorf("%s: could not find expected function %q", test.pkgpath, test.wantinits[0])
+ }
+
+ // Each init function in the list other than the first one is a
+ // dependency of the function immediately before it. Check that
+ // the init functions appear in descending priority order.
+ priority := initdata.Priority
+ for _, wantdepinit := range test.wantinits[1:] {
+ found = false
+ for _, pkginit := range initdata.Inits {
+ if pkginit.InitFunc == wantdepinit {
+ if priority <= pkginit.Priority {
+ t.Errorf("%s: got dep priority %d; want less than %d", test.pkgpath, pkginit.Priority, priority)
+ }
+ found = true
+ priority = pkginit.Priority
+ break
+ }
+ }
+
+ if !found {
+ t.Errorf("%s: could not find expected function %q", test.pkgpath, wantdepinit)
+ }
+ }
+ }
+}
+
+var importerTests = [...]importerTest{
+ {pkgpath: "pointer", name: "Int8Ptr", want: "type Int8Ptr *int8"},
+ {pkgpath: "complexnums", name: "NN", want: "const NN untyped complex", wantval: "(-1/1 + -1/1i)"},
+ {pkgpath: "complexnums", name: "NP", want: "const NP untyped complex", wantval: "(-1/1 + 1/1i)"},
+ {pkgpath: "complexnums", name: "PN", want: "const PN untyped complex", wantval: "(1/1 + -1/1i)"},
+ {pkgpath: "complexnums", name: "PP", want: "const PP untyped complex", wantval: "(1/1 + 1/1i)"},
+ // TODO: enable this entry once bug has been tracked down
+ //{pkgpath: "imports", wantinits: []string{"imports..import", "fmt..import", "math..import"}},
+}
+
+func TestGoxImporter(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ initmap := make(map[*types.Package]InitData)
+ imp := GetImporter([]string{"testdata"}, initmap)
+
+ for _, test := range importerTests {
+ runImporterTest(t, imp, initmap, &test)
+ }
+}
+
+func TestObjImporter(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // This test relies on gccgo being around, which it most likely will be if we
+ // were compiled with gccgo.
+ if runtime.Compiler != "gccgo" {
+ t.Skip("This test needs gccgo")
+ return
+ }
+
+ tmpdir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ initmap := make(map[*types.Package]InitData)
+ imp := GetImporter([]string{tmpdir}, initmap)
+
+ artmpdir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ arinitmap := make(map[*types.Package]InitData)
+ arimp := GetImporter([]string{artmpdir}, arinitmap)
+
+ for _, test := range importerTests {
+ gofile := filepath.Join("testdata", test.pkgpath+".go")
+ ofile := filepath.Join(tmpdir, test.pkgpath+".o")
+ afile := filepath.Join(artmpdir, "lib"+test.pkgpath+".a")
+
+ cmd := exec.Command("gccgo", "-fgo-pkgpath="+test.pkgpath, "-c", "-o", ofile, gofile)
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Logf("%s", out)
+ t.Fatalf("gccgo %s failed: %s", gofile, err)
+ }
+
+ runImporterTest(t, imp, initmap, &test)
+
+ cmd = exec.Command("ar", "cr", afile, ofile)
+ out, err = cmd.CombinedOutput()
+ if err != nil {
+ t.Logf("%s", out)
+ t.Fatalf("ar cr %s %s failed: %s", afile, ofile, err)
+ }
+
+ runImporterTest(t, arimp, arinitmap, &test)
+
+ if err = os.Remove(ofile); err != nil {
+ t.Fatal(err)
+ }
+ if err = os.Remove(afile); err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ if err = os.Remove(tmpdir); err != nil {
+ t.Fatal(err)
+ }
+}
diff --git a/libgo/go/go/internal/gccgoimporter/parser.go b/libgo/go/go/internal/gccgoimporter/parser.go
new file mode 100644
index 00000000000..c06cce435b1
--- /dev/null
+++ b/libgo/go/go/internal/gccgoimporter/parser.go
@@ -0,0 +1,855 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gccgoimporter
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "go/constant"
+ "go/token"
+ "go/types"
+ "io"
+ "strconv"
+ "strings"
+ "text/scanner"
+)
+
+type parser struct {
+ scanner scanner.Scanner
+ tok rune // current token
+ lit string // literal string; only valid for Ident, Int, String tokens
+ pkgpath string // package path of imported package
+ pkgname string // name of imported package
+ pkg *types.Package // reference to imported package
+ imports map[string]*types.Package // package path -> package object
+ typeMap map[int]types.Type // type number -> type
+ initdata InitData // package init priority data
+}
+
+func (p *parser) init(filename string, src io.Reader, imports map[string]*types.Package) {
+ p.scanner.Init(src)
+ p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
+ p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
+ p.scanner.Whitespace = 1<<'\t' | 1<<'\n' | 1<<' '
+ p.scanner.Filename = filename // for good error messages
+ p.next()
+ p.imports = imports
+ p.typeMap = make(map[int]types.Type)
+}
+
+type importError struct {
+ pos scanner.Position
+ err error
+}
+
+func (e importError) Error() string {
+ return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
+}
+
+func (p *parser) error(err interface{}) {
+ if s, ok := err.(string); ok {
+ err = errors.New(s)
+ }
+ // panic with a runtime.Error if err is not an error
+ panic(importError{p.scanner.Pos(), err.(error)})
+}
+
+func (p *parser) errorf(format string, args ...interface{}) {
+ p.error(fmt.Errorf(format, args...))
+}
+
+func (p *parser) expect(tok rune) string {
+ lit := p.lit
+ if p.tok != tok {
+ p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
+ }
+ p.next()
+ return lit
+}
+
+func (p *parser) expectKeyword(keyword string) {
+ lit := p.expect(scanner.Ident)
+ if lit != keyword {
+ p.errorf("expected keyword %s, got %q", keyword, lit)
+ }
+}
+
+func (p *parser) parseString() string {
+ str, err := strconv.Unquote(p.expect(scanner.String))
+ if err != nil {
+ p.error(err)
+ }
+ return str
+}
+
+// unquotedString = { unquotedStringChar } .
+// unquotedStringChar = <neither a whitespace nor a ';' char> .
+func (p *parser) parseUnquotedString() string {
+ if p.tok == scanner.EOF {
+ p.error("unexpected EOF")
+ }
+ var buf bytes.Buffer
+ buf.WriteString(p.scanner.TokenText())
+ // This loop needs to examine each character before deciding whether to consume it. If we see a semicolon,
+ // we need to let it be consumed by p.next().
+ for ch := p.scanner.Peek(); ch != ';' && ch != scanner.EOF && p.scanner.Whitespace&(1<<uint(ch)) == 0; ch = p.scanner.Peek() {
+ buf.WriteRune(ch)
+ p.scanner.Next()
+ }
+ p.next()
+ return buf.String()
+}
+
+func (p *parser) next() {
+ p.tok = p.scanner.Scan()
+ switch p.tok {
+ case scanner.Ident, scanner.Int, scanner.Float, scanner.String, '·':
+ p.lit = p.scanner.TokenText()
+ default:
+ p.lit = ""
+ }
+}
+
+func (p *parser) parseQualifiedName() (path, name string) {
+ return p.parseQualifiedNameStr(p.parseString())
+}
+
+func (p *parser) parseUnquotedQualifiedName() (path, name string) {
+ return p.parseQualifiedNameStr(p.parseUnquotedString())
+}
+
+// qualifiedName = [ ["."] unquotedString "." ] unquotedString .
+//
+// The above production uses greedy matching.
+func (p *parser) parseQualifiedNameStr(unquotedName string) (pkgpath, name string) {
+ parts := strings.Split(unquotedName, ".")
+ if parts[0] == "" {
+ parts = parts[1:]
+ }
+
+ switch len(parts) {
+ case 0:
+ p.errorf("malformed qualified name: %q", unquotedName)
+ case 1:
+ // unqualified name
+ pkgpath = p.pkgpath
+ name = parts[0]
+ default:
+ // qualified name, which may contain periods
+ pkgpath = strings.Join(parts[0:len(parts)-1], ".")
+ name = parts[len(parts)-1]
+ }
+
+ return
+}
+
+// getPkg returns the package for a given path. If the package is
+// not found but we have a package name, create the package and
+// add it to the p.imports map.
+//
+func (p *parser) getPkg(pkgpath, name string) *types.Package {
+ // package unsafe is not in the imports map - handle explicitly
+ if pkgpath == "unsafe" {
+ return types.Unsafe
+ }
+ pkg := p.imports[pkgpath]
+ if pkg == nil && name != "" {
+ pkg = types.NewPackage(pkgpath, name)
+ p.imports[pkgpath] = pkg
+ }
+ return pkg
+}
+
+// parseExportedName is like parseQualifiedName, but
+// the package path is resolved to an imported *types.Package.
+//
+// ExportedName = string [string] .
+func (p *parser) parseExportedName() (pkg *types.Package, name string) {
+ path, name := p.parseQualifiedName()
+ var pkgname string
+ if p.tok == scanner.String {
+ pkgname = p.parseString()
+ }
+ pkg = p.getPkg(path, pkgname)
+ if pkg == nil {
+ p.errorf("package %s (path = %q) not found", name, path)
+ }
+ return
+}
+
+// Name = QualifiedName | "?" .
+func (p *parser) parseName() string {
+ if p.tok == '?' {
+ // Anonymous.
+ p.next()
+ return ""
+ }
+ // The package path is redundant for us. Don't try to parse it.
+ _, name := p.parseUnquotedQualifiedName()
+ return name
+}
+
+func deref(typ types.Type) types.Type {
+ if p, _ := typ.(*types.Pointer); p != nil {
+ typ = p.Elem()
+ }
+ return typ
+}
+
+// Field = Name Type [string] .
+func (p *parser) parseField(pkg *types.Package) (field *types.Var, tag string) {
+ name := p.parseName()
+ typ := p.parseType(pkg)
+ anon := false
+ if name == "" {
+ anon = true
+ switch typ := deref(typ).(type) {
+ case *types.Basic:
+ name = typ.Name()
+ case *types.Named:
+ name = typ.Obj().Name()
+ default:
+ p.error("anonymous field expected")
+ }
+ }
+ field = types.NewField(token.NoPos, pkg, name, typ, anon)
+ if p.tok == scanner.String {
+ tag = p.parseString()
+ }
+ return
+}
+
+// Param = Name ["..."] Type .
+func (p *parser) parseParam(pkg *types.Package) (param *types.Var, isVariadic bool) {
+ name := p.parseName()
+ if p.tok == '.' {
+ p.next()
+ p.expect('.')
+ p.expect('.')
+ isVariadic = true
+ }
+ typ := p.parseType(pkg)
+ if isVariadic {
+ typ = types.NewSlice(typ)
+ }
+ param = types.NewParam(token.NoPos, pkg, name, typ)
+ return
+}
+
+// Var = Name Type .
+func (p *parser) parseVar(pkg *types.Package) *types.Var {
+ name := p.parseName()
+ return types.NewVar(token.NoPos, pkg, name, p.parseType(pkg))
+}
+
+// ConstValue = string | "false" | "true" | ["-"] (int ["'"] | FloatOrComplex) .
+// FloatOrComplex = float ["i" | ("+"|"-") float "i"] .
+func (p *parser) parseConstValue() (val constant.Value, typ types.Type) {
+ switch p.tok {
+ case scanner.String:
+ str := p.parseString()
+ val = constant.MakeString(str)
+ typ = types.Typ[types.UntypedString]
+ return
+
+ case scanner.Ident:
+ b := false
+ switch p.lit {
+ case "false":
+ case "true":
+ b = true
+
+ default:
+ p.errorf("expected const value, got %s (%q)", scanner.TokenString(p.tok), p.lit)
+ }
+
+ p.next()
+ val = constant.MakeBool(b)
+ typ = types.Typ[types.UntypedBool]
+ return
+ }
+
+ sign := ""
+ if p.tok == '-' {
+ p.next()
+ sign = "-"
+ }
+
+ switch p.tok {
+ case scanner.Int:
+ val = constant.MakeFromLiteral(sign+p.lit, token.INT, 0)
+ if val == nil {
+ p.error("could not parse integer literal")
+ }
+
+ p.next()
+ if p.tok == '\'' {
+ p.next()
+ typ = types.Typ[types.UntypedRune]
+ } else {
+ typ = types.Typ[types.UntypedInt]
+ }
+
+ case scanner.Float:
+ re := sign + p.lit
+ p.next()
+
+ var im string
+ switch p.tok {
+ case '+':
+ p.next()
+ im = p.expect(scanner.Float)
+
+ case '-':
+ p.next()
+ im = "-" + p.expect(scanner.Float)
+
+ case scanner.Ident:
+ // re is in fact the imaginary component. Expect "i" below.
+ im = re
+ re = "0"
+
+ default:
+ val = constant.MakeFromLiteral(re, token.FLOAT, 0)
+ if val == nil {
+ p.error("could not parse float literal")
+ }
+ typ = types.Typ[types.UntypedFloat]
+ return
+ }
+
+ p.expectKeyword("i")
+ reval := constant.MakeFromLiteral(re, token.FLOAT, 0)
+ if reval == nil {
+ p.error("could not parse real component of complex literal")
+ }
+ imval := constant.MakeFromLiteral(im+"i", token.IMAG, 0)
+ if imval == nil {
+ p.error("could not parse imag component of complex literal")
+ }
+ val = constant.BinaryOp(reval, token.ADD, imval)
+ typ = types.Typ[types.UntypedComplex]
+
+ default:
+ p.errorf("expected const value, got %s (%q)", scanner.TokenString(p.tok), p.lit)
+ }
+
+ return
+}
+
+// Const = Name [Type] "=" ConstValue .
+func (p *parser) parseConst(pkg *types.Package) *types.Const {
+ name := p.parseName()
+ var typ types.Type
+ if p.tok == '<' {
+ typ = p.parseType(pkg)
+ }
+ p.expect('=')
+ val, vtyp := p.parseConstValue()
+ if typ == nil {
+ typ = vtyp
+ }
+ return types.NewConst(token.NoPos, pkg, name, typ, val)
+}
+
+// TypeName = ExportedName .
+func (p *parser) parseTypeName() *types.TypeName {
+ pkg, name := p.parseExportedName()
+ scope := pkg.Scope()
+ if obj := scope.Lookup(name); obj != nil {
+ return obj.(*types.TypeName)
+ }
+ obj := types.NewTypeName(token.NoPos, pkg, name, nil)
+ // a named type may be referred to before the underlying type
+ // is known - set it up
+ types.NewNamed(obj, nil, nil)
+ scope.Insert(obj)
+ return obj
+}
+
+// NamedType = TypeName Type { Method } .
+// Method = "func" "(" Param ")" Name ParamList ResultList ";" .
+func (p *parser) parseNamedType(n int) types.Type {
+ obj := p.parseTypeName()
+
+ pkg := obj.Pkg()
+ typ := obj.Type()
+ p.typeMap[n] = typ
+
+ nt, ok := typ.(*types.Named)
+ if !ok {
+ // This can happen for unsafe.Pointer, which is a TypeName holding a Basic type.
+ pt := p.parseType(pkg)
+ if pt != typ {
+ p.error("unexpected underlying type for non-named TypeName")
+ }
+ return typ
+ }
+
+ underlying := p.parseType(pkg)
+ if nt.Underlying() == nil {
+ nt.SetUnderlying(underlying.Underlying())
+ }
+
+ for p.tok == scanner.Ident {
+ // collect associated methods
+ p.expectKeyword("func")
+ p.expect('(')
+ receiver, _ := p.parseParam(pkg)
+ p.expect(')')
+ name := p.parseName()
+ params, isVariadic := p.parseParamList(pkg)
+ results := p.parseResultList(pkg)
+ p.expect(';')
+
+ sig := types.NewSignature(receiver, params, results, isVariadic)
+ nt.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
+ }
+
+ return nt
+}
+
+func (p *parser) parseInt() int64 {
+ lit := p.expect(scanner.Int)
+ n, err := strconv.ParseInt(lit, 10, 0)
+ if err != nil {
+ p.error(err)
+ }
+ return n
+}
+
+// ArrayOrSliceType = "[" [ int ] "]" Type .
+func (p *parser) parseArrayOrSliceType(pkg *types.Package) types.Type {
+ p.expect('[')
+ if p.tok == ']' {
+ p.next()
+ return types.NewSlice(p.parseType(pkg))
+ }
+
+ n := p.parseInt()
+ p.expect(']')
+ return types.NewArray(p.parseType(pkg), n)
+}
+
+// MapType = "map" "[" Type "]" Type .
+func (p *parser) parseMapType(pkg *types.Package) types.Type {
+ p.expectKeyword("map")
+ p.expect('[')
+ key := p.parseType(pkg)
+ p.expect(']')
+ elem := p.parseType(pkg)
+ return types.NewMap(key, elem)
+}
+
+// ChanType = "chan" ["<-" | "-<"] Type .
+func (p *parser) parseChanType(pkg *types.Package) types.Type {
+ p.expectKeyword("chan")
+ dir := types.SendRecv
+ switch p.tok {
+ case '-':
+ p.next()
+ p.expect('<')
+ dir = types.SendOnly
+
+ case '<':
+ // don't consume '<' if it belongs to Type
+ if p.scanner.Peek() == '-' {
+ p.next()
+ p.expect('-')
+ dir = types.RecvOnly
+ }
+ }
+
+ return types.NewChan(dir, p.parseType(pkg))
+}
+
+// StructType = "struct" "{" { Field } "}" .
+func (p *parser) parseStructType(pkg *types.Package) types.Type {
+ p.expectKeyword("struct")
+
+ var fields []*types.Var
+ var tags []string
+
+ p.expect('{')
+ for p.tok != '}' && p.tok != scanner.EOF {
+ field, tag := p.parseField(pkg)
+ p.expect(';')
+ fields = append(fields, field)
+ tags = append(tags, tag)
+ }
+ p.expect('}')
+
+ return types.NewStruct(fields, tags)
+}
+
+// ParamList = "(" [ { Parameter "," } Parameter ] ")" .
+func (p *parser) parseParamList(pkg *types.Package) (*types.Tuple, bool) {
+ var list []*types.Var
+ isVariadic := false
+
+ p.expect('(')
+ for p.tok != ')' && p.tok != scanner.EOF {
+ if len(list) > 0 {
+ p.expect(',')
+ }
+ par, variadic := p.parseParam(pkg)
+ list = append(list, par)
+ if variadic {
+ if isVariadic {
+ p.error("... not on final argument")
+ }
+ isVariadic = true
+ }
+ }
+ p.expect(')')
+
+ return types.NewTuple(list...), isVariadic
+}
+
+// ResultList = Type | ParamList .
+func (p *parser) parseResultList(pkg *types.Package) *types.Tuple {
+ switch p.tok {
+ case '<':
+ return types.NewTuple(types.NewParam(token.NoPos, pkg, "", p.parseType(pkg)))
+
+ case '(':
+ params, _ := p.parseParamList(pkg)
+ return params
+
+ default:
+ return nil
+ }
+}
+
+// FunctionType = ParamList ResultList .
+func (p *parser) parseFunctionType(pkg *types.Package) *types.Signature {
+ params, isVariadic := p.parseParamList(pkg)
+ results := p.parseResultList(pkg)
+ return types.NewSignature(nil, params, results, isVariadic)
+}
+
+// Func = Name FunctionType .
+func (p *parser) parseFunc(pkg *types.Package) *types.Func {
+ name := p.parseName()
+ if strings.ContainsRune(name, '$') {
+ // This is a Type$equal or Type$hash function, which we don't want to parse,
+ // except for the types.
+ p.discardDirectiveWhileParsingTypes(pkg)
+ return nil
+ }
+ return types.NewFunc(token.NoPos, pkg, name, p.parseFunctionType(pkg))
+}
+
+// InterfaceType = "interface" "{" { ("?" Type | Func) ";" } "}" .
+func (p *parser) parseInterfaceType(pkg *types.Package) types.Type {
+ p.expectKeyword("interface")
+
+ var methods []*types.Func
+ var typs []*types.Named
+
+ p.expect('{')
+ for p.tok != '}' && p.tok != scanner.EOF {
+ if p.tok == '?' {
+ p.next()
+ typs = append(typs, p.parseType(pkg).(*types.Named))
+ } else {
+ method := p.parseFunc(pkg)
+ methods = append(methods, method)
+ }
+ p.expect(';')
+ }
+ p.expect('}')
+
+ return types.NewInterface(methods, typs)
+}
+
+// PointerType = "*" ("any" | Type) .
+func (p *parser) parsePointerType(pkg *types.Package) types.Type {
+ p.expect('*')
+ if p.tok == scanner.Ident {
+ p.expectKeyword("any")
+ return types.Typ[types.UnsafePointer]
+ }
+ return types.NewPointer(p.parseType(pkg))
+}
+
+// TypeDefinition = NamedType | MapType | ChanType | StructType | InterfaceType | PointerType | ArrayOrSliceType | FunctionType .
+func (p *parser) parseTypeDefinition(pkg *types.Package, n int) types.Type {
+ var t types.Type
+ switch p.tok {
+ case scanner.String:
+ t = p.parseNamedType(n)
+
+ case scanner.Ident:
+ switch p.lit {
+ case "map":
+ t = p.parseMapType(pkg)
+
+ case "chan":
+ t = p.parseChanType(pkg)
+
+ case "struct":
+ t = p.parseStructType(pkg)
+
+ case "interface":
+ t = p.parseInterfaceType(pkg)
+ }
+
+ case '*':
+ t = p.parsePointerType(pkg)
+
+ case '[':
+ t = p.parseArrayOrSliceType(pkg)
+
+ case '(':
+ t = p.parseFunctionType(pkg)
+ }
+
+ p.typeMap[n] = t
+ return t
+}
+
+const (
+ // From gofrontend/go/export.h
+ // Note that these values are negative in the gofrontend and have been made positive
+ // in the gccgoimporter.
+ gccgoBuiltinINT8 = 1
+ gccgoBuiltinINT16 = 2
+ gccgoBuiltinINT32 = 3
+ gccgoBuiltinINT64 = 4
+ gccgoBuiltinUINT8 = 5
+ gccgoBuiltinUINT16 = 6
+ gccgoBuiltinUINT32 = 7
+ gccgoBuiltinUINT64 = 8
+ gccgoBuiltinFLOAT32 = 9
+ gccgoBuiltinFLOAT64 = 10
+ gccgoBuiltinINT = 11
+ gccgoBuiltinUINT = 12
+ gccgoBuiltinUINTPTR = 13
+ gccgoBuiltinBOOL = 15
+ gccgoBuiltinSTRING = 16
+ gccgoBuiltinCOMPLEX64 = 17
+ gccgoBuiltinCOMPLEX128 = 18
+ gccgoBuiltinERROR = 19
+ gccgoBuiltinBYTE = 20
+ gccgoBuiltinRUNE = 21
+)
+
+func lookupBuiltinType(typ int) types.Type {
+ return [...]types.Type{
+ gccgoBuiltinINT8: types.Typ[types.Int8],
+ gccgoBuiltinINT16: types.Typ[types.Int16],
+ gccgoBuiltinINT32: types.Typ[types.Int32],
+ gccgoBuiltinINT64: types.Typ[types.Int64],
+ gccgoBuiltinUINT8: types.Typ[types.Uint8],
+ gccgoBuiltinUINT16: types.Typ[types.Uint16],
+ gccgoBuiltinUINT32: types.Typ[types.Uint32],
+ gccgoBuiltinUINT64: types.Typ[types.Uint64],
+ gccgoBuiltinFLOAT32: types.Typ[types.Float32],
+ gccgoBuiltinFLOAT64: types.Typ[types.Float64],
+ gccgoBuiltinINT: types.Typ[types.Int],
+ gccgoBuiltinUINT: types.Typ[types.Uint],
+ gccgoBuiltinUINTPTR: types.Typ[types.Uintptr],
+ gccgoBuiltinBOOL: types.Typ[types.Bool],
+ gccgoBuiltinSTRING: types.Typ[types.String],
+ gccgoBuiltinCOMPLEX64: types.Typ[types.Complex64],
+ gccgoBuiltinCOMPLEX128: types.Typ[types.Complex128],
+ gccgoBuiltinERROR: types.Universe.Lookup("error").Type(),
+ gccgoBuiltinBYTE: types.Universe.Lookup("byte").Type(),
+ gccgoBuiltinRUNE: types.Universe.Lookup("rune").Type(),
+ }[typ]
+}
+
+// Type = "<" "type" ( "-" int | int [ TypeDefinition ] ) ">" .
+func (p *parser) parseType(pkg *types.Package) (t types.Type) {
+ p.expect('<')
+ p.expectKeyword("type")
+
+ switch p.tok {
+ case scanner.Int:
+ n := p.parseInt()
+
+ if p.tok == '>' {
+ t = p.typeMap[int(n)]
+ } else {
+ t = p.parseTypeDefinition(pkg, int(n))
+ }
+
+ case '-':
+ p.next()
+ n := p.parseInt()
+ t = lookupBuiltinType(int(n))
+
+ default:
+ p.errorf("expected type number, got %s (%q)", scanner.TokenString(p.tok), p.lit)
+ return nil
+ }
+
+ p.expect('>')
+ return
+}
+
+// PackageInit = unquotedString unquotedString int .
+func (p *parser) parsePackageInit() PackageInit {
+ name := p.parseUnquotedString()
+ initfunc := p.parseUnquotedString()
+ priority := int(p.parseInt())
+ return PackageInit{Name: name, InitFunc: initfunc, Priority: priority}
+}
+
+// Throw away tokens until we see a ';'. If we see a '<', attempt to parse as a type.
+func (p *parser) discardDirectiveWhileParsingTypes(pkg *types.Package) {
+ for {
+ switch p.tok {
+ case ';':
+ return
+ case '<':
+ p.parseType(p.pkg)
+ case scanner.EOF:
+ p.error("unexpected EOF")
+ default:
+ p.next()
+ }
+ }
+}
+
+// Create the package if we have parsed both the package path and package name.
+func (p *parser) maybeCreatePackage() {
+ if p.pkgname != "" && p.pkgpath != "" {
+ p.pkg = p.getPkg(p.pkgpath, p.pkgname)
+ }
+}
+
+// InitDataDirective = "v1" ";" |
+// "priority" int ";" |
+// "init" { PackageInit } ";" |
+// "checksum" unquotedString ";" .
+func (p *parser) parseInitDataDirective() {
+ if p.tok != scanner.Ident {
+ // unexpected token kind; panic
+ p.expect(scanner.Ident)
+ }
+
+ switch p.lit {
+ case "v1":
+ p.next()
+ p.expect(';')
+
+ case "priority":
+ p.next()
+ p.initdata.Priority = int(p.parseInt())
+ p.expect(';')
+
+ case "init":
+ p.next()
+ for p.tok != ';' && p.tok != scanner.EOF {
+ p.initdata.Inits = append(p.initdata.Inits, p.parsePackageInit())
+ }
+ p.expect(';')
+
+ case "checksum":
+ // Don't let the scanner try to parse the checksum as a number.
+ defer func(mode uint) {
+ p.scanner.Mode = mode
+ }(p.scanner.Mode)
+ p.scanner.Mode &^= scanner.ScanInts | scanner.ScanFloats
+ p.next()
+ p.parseUnquotedString()
+ p.expect(';')
+
+ default:
+ p.errorf("unexpected identifier: %q", p.lit)
+ }
+}
+
+// Directive = InitDataDirective |
+// "package" unquotedString ";" |
+// "pkgpath" unquotedString ";" |
+// "import" unquotedString unquotedString string ";" |
+// "func" Func ";" |
+// "type" Type ";" |
+// "var" Var ";" |
+// "const" Const ";" .
+func (p *parser) parseDirective() {
+ if p.tok != scanner.Ident {
+ // unexpected token kind; panic
+ p.expect(scanner.Ident)
+ }
+
+ switch p.lit {
+ case "v1", "priority", "init", "checksum":
+ p.parseInitDataDirective()
+
+ case "package":
+ p.next()
+ p.pkgname = p.parseUnquotedString()
+ p.maybeCreatePackage()
+ p.expect(';')
+
+ case "pkgpath":
+ p.next()
+ p.pkgpath = p.parseUnquotedString()
+ p.maybeCreatePackage()
+ p.expect(';')
+
+ case "import":
+ p.next()
+ pkgname := p.parseUnquotedString()
+ pkgpath := p.parseUnquotedString()
+ p.getPkg(pkgpath, pkgname)
+ p.parseString()
+ p.expect(';')
+
+ case "func":
+ p.next()
+ fun := p.parseFunc(p.pkg)
+ if fun != nil {
+ p.pkg.Scope().Insert(fun)
+ }
+ p.expect(';')
+
+ case "type":
+ p.next()
+ p.parseType(p.pkg)
+ p.expect(';')
+
+ case "var":
+ p.next()
+ v := p.parseVar(p.pkg)
+ p.pkg.Scope().Insert(v)
+ p.expect(';')
+
+ case "const":
+ p.next()
+ c := p.parseConst(p.pkg)
+ p.pkg.Scope().Insert(c)
+ p.expect(';')
+
+ default:
+ p.errorf("unexpected identifier: %q", p.lit)
+ }
+}
+
+// Package = { Directive } .
+func (p *parser) parsePackage() *types.Package {
+ for p.tok != scanner.EOF {
+ p.parseDirective()
+ }
+ for _, typ := range p.typeMap {
+ if it, ok := typ.(*types.Interface); ok {
+ it.Complete()
+ }
+ }
+ p.pkg.MarkComplete()
+ return p.pkg
+}
+
+// InitData = { InitDataDirective } .
+func (p *parser) parseInitData() {
+ for p.tok != scanner.EOF {
+ p.parseInitDataDirective()
+ }
+}
diff --git a/libgo/go/go/internal/gccgoimporter/parser_test.go b/libgo/go/go/internal/gccgoimporter/parser_test.go
new file mode 100644
index 00000000000..b96486f20ad
--- /dev/null
+++ b/libgo/go/go/internal/gccgoimporter/parser_test.go
@@ -0,0 +1,72 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package gccgoimporter
+
+import (
+ "bytes"
+ "go/types"
+ "strings"
+ "testing"
+ "text/scanner"
+)
+
+var typeParserTests = []struct {
+ id, typ, want, underlying, methods string
+}{
+ {id: "foo", typ: "<type -1>", want: "int8"},
+ {id: "foo", typ: "<type 1 *<type -19>>", want: "*error"},
+ {id: "foo", typ: "<type 1 *any>", want: "unsafe.Pointer"},
+ {id: "foo", typ: "<type 1 \"Bar\" <type 2 *<type 1>>>", want: "foo.Bar", underlying: "*foo.Bar"},
+ {id: "foo", typ: "<type 1 \"bar.Foo\" \"bar\" <type -1> func (? <type 1>) M (); >", want: "bar.Foo", underlying: "int8", methods: "func (bar.Foo).M()"},
+ {id: "foo", typ: "<type 1 \".bar.foo\" \"bar\" <type -1>>", want: "bar.foo", underlying: "int8"},
+ {id: "foo", typ: "<type 1 []<type -1>>", want: "[]int8"},
+ {id: "foo", typ: "<type 1 [42]<type -1>>", want: "[42]int8"},
+ {id: "foo", typ: "<type 1 map [<type -1>] <type -2>>", want: "map[int8]int16"},
+ {id: "foo", typ: "<type 1 chan <type -1>>", want: "chan int8"},
+ {id: "foo", typ: "<type 1 chan <- <type -1>>", want: "<-chan int8"},
+ {id: "foo", typ: "<type 1 chan -< <type -1>>", want: "chan<- int8"},
+ {id: "foo", typ: "<type 1 struct { I8 <type -1>; I16 <type -2> \"i16\"; }>", want: "struct{I8 int8; I16 int16 \"i16\"}"},
+ {id: "foo", typ: "<type 1 interface { Foo (a <type -1>, b <type -2>) <type -1>; Bar (? <type -2>, ? ...<type -1>) (? <type -2>, ? <type -1>); Baz (); }>", want: "interface{Bar(int16, ...int8) (int16, int8); Baz(); Foo(a int8, b int16) int8}"},
+ {id: "foo", typ: "<type 1 (? <type -1>) <type -2>>", want: "func(int8) int16"},
+}
+
+func TestTypeParser(t *testing.T) {
+ for _, test := range typeParserTests {
+ var p parser
+ p.init("test.gox", strings.NewReader(test.typ), make(map[string]*types.Package))
+ p.pkgname = test.id
+ p.pkgpath = test.id
+ p.maybeCreatePackage()
+ typ := p.parseType(p.pkg)
+
+ if p.tok != scanner.EOF {
+ t.Errorf("expected full parse, stopped at %q", p.lit)
+ }
+
+ got := typ.String()
+ if got != test.want {
+ t.Errorf("got type %q, expected %q", got, test.want)
+ }
+
+ if test.underlying != "" {
+ underlying := typ.Underlying().String()
+ if underlying != test.underlying {
+ t.Errorf("got underlying type %q, expected %q", underlying, test.underlying)
+ }
+ }
+
+ if test.methods != "" {
+ nt := typ.(*types.Named)
+ var buf bytes.Buffer
+ for i := 0; i != nt.NumMethods(); i++ {
+ buf.WriteString(nt.Method(i).String())
+ }
+ methods := buf.String()
+ if methods != test.methods {
+ t.Errorf("got methods %q, expected %q", methods, test.methods)
+ }
+ }
+ }
+}
diff --git a/libgo/go/go/internal/gccgoimporter/testdata/complexnums.go b/libgo/go/go/internal/gccgoimporter/testdata/complexnums.go
new file mode 100644
index 00000000000..a51b6b01c0b
--- /dev/null
+++ b/libgo/go/go/internal/gccgoimporter/testdata/complexnums.go
@@ -0,0 +1,6 @@
+package complexnums
+
+const NN = -1 - 1i
+const NP = -1 + 1i
+const PN = 1 - 1i
+const PP = 1 + 1i
diff --git a/libgo/go/go/internal/gccgoimporter/testdata/imports.go b/libgo/go/go/internal/gccgoimporter/testdata/imports.go
new file mode 100644
index 00000000000..7907316a607
--- /dev/null
+++ b/libgo/go/go/internal/gccgoimporter/testdata/imports.go
@@ -0,0 +1,5 @@
+package imports
+
+import "fmt"
+
+var Hello = fmt.Sprintf("Hello, world")
diff --git a/libgo/go/go/internal/gccgoimporter/testdata/pointer.go b/libgo/go/go/internal/gccgoimporter/testdata/pointer.go
new file mode 100644
index 00000000000..4ebc67137d6
--- /dev/null
+++ b/libgo/go/go/internal/gccgoimporter/testdata/pointer.go
@@ -0,0 +1,3 @@
+package pointer
+
+type Int8Ptr *int8
diff --git a/libgo/go/go/internal/gcimporter/exportdata.go b/libgo/go/go/internal/gcimporter/exportdata.go
new file mode 100644
index 00000000000..657742bb6d7
--- /dev/null
+++ b/libgo/go/go/internal/gcimporter/exportdata.go
@@ -0,0 +1,108 @@
+// 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 FindExportData.
+
+package gcimporter
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "io"
+ "strconv"
+ "strings"
+)
+
+func readGopackHeader(r *bufio.Reader) (name string, size int, err error) {
+ // See $GOROOT/include/ar.h.
+ hdr := make([]byte, 16+12+6+6+8+10+2)
+ _, err = io.ReadFull(r, hdr)
+ if err != nil {
+ return
+ }
+ // leave for debugging
+ if false {
+ fmt.Printf("header: %s", hdr)
+ }
+ s := strings.TrimSpace(string(hdr[16+12+6+6+8:][:10]))
+ size, err = strconv.Atoi(s)
+ if err != nil || hdr[len(hdr)-2] != '`' || hdr[len(hdr)-1] != '\n' {
+ err = errors.New("invalid archive header")
+ return
+ }
+ name = strings.TrimSpace(string(hdr[:16]))
+ return
+}
+
+// FindExportData positions the reader r at the beginning of the
+// export data section of an underlying GC-created object/archive
+// file by reading from it. The reader must be positioned at the
+// start of the file before calling this function.
+//
+func FindExportData(r *bufio.Reader) (err error) {
+ // Read first line to make sure this is an object file.
+ line, err := r.ReadSlice('\n')
+ if err != nil {
+ return
+ }
+ if string(line) == "!<arch>\n" {
+ // Archive file. Scan to __.PKGDEF.
+ var name string
+ var size int
+ if name, size, err = readGopackHeader(r); err != nil {
+ return
+ }
+
+ // Optional leading __.GOSYMDEF or __.SYMDEF.
+ // Read and discard.
+ if name == "__.SYMDEF" || name == "__.GOSYMDEF" {
+ const block = 4096
+ tmp := make([]byte, block)
+ for size > 0 {
+ n := size
+ if n > block {
+ n = block
+ }
+ if _, err = io.ReadFull(r, tmp[:n]); err != nil {
+ return
+ }
+ size -= n
+ }
+
+ if name, size, err = readGopackHeader(r); err != nil {
+ return
+ }
+ }
+
+ // First real entry should be __.PKGDEF.
+ if name != "__.PKGDEF" {
+ err = errors.New("go archive is missing __.PKGDEF")
+ return
+ }
+
+ // Read first line of __.PKGDEF data, so that line
+ // is once again the first line of the input.
+ if line, err = r.ReadSlice('\n'); 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 = errors.New("not a go object file")
+ return
+ }
+
+ // Skip over object header to export data.
+ // Begins after first line with $$.
+ for line[0] != '$' {
+ if line, err = r.ReadSlice('\n'); err != nil {
+ return
+ }
+ }
+
+ return
+}
diff --git a/libgo/go/go/internal/gcimporter/gcimporter.go b/libgo/go/go/internal/gcimporter/gcimporter.go
new file mode 100644
index 00000000000..1d485cf9cbf
--- /dev/null
+++ b/libgo/go/go/internal/gcimporter/gcimporter.go
@@ -0,0 +1,991 @@
+// 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 gcimporter implements Import for gc-generated object files.
+package gcimporter // import "go/internal/gcimporter"
+
+import (
+ "bufio"
+ "errors"
+ "fmt"
+ "go/build"
+ "go/token"
+ "io"
+ "os"
+ "path/filepath"
+ "sort"
+ "strconv"
+ "strings"
+ "text/scanner"
+
+ exact "go/constant"
+ "go/types"
+)
+
+// debugging/development support
+const debug = false
+
+var pkgExts = [...]string{".a", ".o"}
+
+// FindPkg returns the filename and unique package id for an import
+// path based on package information provided by build.Import (using
+// the build.Default build.Context).
+// If no file was found, an empty filename is returned.
+//
+func FindPkg(path, srcDir string) (filename, id string) {
+ if len(path) == 0 {
+ return
+ }
+
+ id = path
+ var noext string
+ switch {
+ default:
+ // "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
+ // Don't require the source files to be present.
+ bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
+ if bp.PkgObj == "" {
+ return
+ }
+ noext = strings.TrimSuffix(bp.PkgObj, ".a")
+
+ case build.IsLocalImport(path):
+ // "./x" -> "/this/directory/x.ext", "/this/directory/x"
+ noext = filepath.Join(srcDir, path)
+ id = noext
+
+ case filepath.IsAbs(path):
+ // for completeness only - go/build.Import
+ // does not support absolute imports
+ // "/x" -> "/x.ext", "/x"
+ noext = path
+ }
+
+ // try extensions
+ for _, ext := range pkgExts {
+ filename = noext + ext
+ if f, err := os.Stat(filename); err == nil && !f.IsDir() {
+ return
+ }
+ }
+
+ filename = "" // not found
+ return
+}
+
+// ImportData imports a package by reading the gc-generated export data,
+// adds the corresponding package object to the packages map indexed by id,
+// and returns the object.
+//
+// The packages map must contains all packages already imported. The data
+// reader position must be the beginning of the export data section. The
+// filename is only used in error messages.
+//
+// If packages[id] contains the completely imported package, that package
+// can be used directly, and there is no need to call this function (but
+// there is also no harm but for extra time used).
+//
+func ImportData(packages map[string]*types.Package, filename, id string, data io.Reader) (pkg *types.Package, err error) {
+ // support for parser error handling
+ defer func() {
+ switch r := recover().(type) {
+ case nil:
+ // nothing to do
+ case importError:
+ err = r
+ default:
+ panic(r) // internal error
+ }
+ }()
+
+ var p parser
+ p.init(filename, id, data, packages)
+ pkg = p.parseExport()
+
+ return
+}
+
+// Import imports a gc-generated package given its import path, adds the
+// corresponding package object to the packages map, and returns the object.
+// Local import paths are interpreted relative to the current working directory.
+// The packages map must contain all packages already imported.
+//
+func Import(packages map[string]*types.Package, path string) (pkg *types.Package, err error) {
+ // package "unsafe" is handled by the type checker
+ if path == "unsafe" {
+ panic(`gcimporter.Import called for package "unsafe"`)
+ }
+
+ srcDir := "."
+ if build.IsLocalImport(path) {
+ srcDir, err = os.Getwd()
+ if err != nil {
+ return
+ }
+ }
+
+ filename, id := FindPkg(path, srcDir)
+ if filename == "" {
+ err = fmt.Errorf("can't find import: %s", id)
+ return
+ }
+
+ // no need to re-import if the package was imported completely before
+ if pkg = packages[id]; pkg != nil && pkg.Complete() {
+ return
+ }
+
+ // open file
+ f, err := os.Open(filename)
+ if err != nil {
+ return
+ }
+ defer func() {
+ f.Close()
+ if err != nil {
+ // add file name to error
+ err = fmt.Errorf("reading export data: %s: %v", filename, err)
+ }
+ }()
+
+ buf := bufio.NewReader(f)
+ if err = FindExportData(buf); err != nil {
+ return
+ }
+
+ pkg, err = ImportData(packages, filename, id, buf)
+
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Parser
+
+// TODO(gri) Imported objects don't have position information.
+// Ideally use the debug table line info; alternatively
+// create some fake position (or the position of the
+// import). That way error messages referring to imported
+// objects can print meaningful information.
+
+// parser parses the exports inside a gc compiler-produced
+// object/archive file and populates its scope with the results.
+type parser struct {
+ scanner scanner.Scanner
+ tok rune // current token
+ lit string // literal string; only valid for Ident, Int, String tokens
+ id string // package id of imported package
+ sharedPkgs map[string]*types.Package // package id -> package object (across importer)
+ localPkgs map[string]*types.Package // package id -> package object (just this package)
+}
+
+func (p *parser) init(filename, id string, src io.Reader, packages map[string]*types.Package) {
+ p.scanner.Init(src)
+ p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
+ p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanChars | 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.sharedPkgs = packages
+ if debug {
+ // check consistency of packages map
+ for _, pkg := range packages {
+ if pkg.Name() == "" {
+ fmt.Printf("no package name for %s\n", pkg.Path())
+ }
+ }
+ }
+}
+
+func (p *parser) next() {
+ p.tok = p.scanner.Scan()
+ switch p.tok {
+ case scanner.Ident, scanner.Int, scanner.Char, scanner.String, '·':
+ p.lit = p.scanner.TokenText()
+ default:
+ p.lit = ""
+ }
+ if debug {
+ fmt.Printf("%s: %q -> %q\n", scanner.TokenString(p.tok), p.scanner.TokenText(), p.lit)
+ }
+}
+
+func declTypeName(pkg *types.Package, name string) *types.TypeName {
+ scope := pkg.Scope()
+ if obj := scope.Lookup(name); obj != nil {
+ return obj.(*types.TypeName)
+ }
+ obj := types.NewTypeName(token.NoPos, pkg, name, nil)
+ // a named type may be referred to before the underlying type
+ // is known - set it up
+ types.NewNamed(obj, nil, nil)
+ scope.Insert(obj)
+ return obj
+}
+
+// ----------------------------------------------------------------------------
+// Error handling
+
+// Internal errors are boxed as importErrors.
+type importError struct {
+ pos scanner.Position
+ err error
+}
+
+func (e importError) Error() string {
+ return fmt.Sprintf("import error %s (byte offset = %d): %s", e.pos, e.pos.Offset, e.err)
+}
+
+func (p *parser) error(err interface{}) {
+ if s, ok := err.(string); ok {
+ err = errors.New(s)
+ }
+ // panic with a runtime.Error if err is not an error
+ panic(importError{p.scanner.Pos(), err.(error)})
+}
+
+func (p *parser) errorf(format string, args ...interface{}) {
+ p.error(fmt.Sprintf(format, args...))
+}
+
+func (p *parser) expect(tok rune) string {
+ lit := p.lit
+ if p.tok != tok {
+ p.errorf("expected %s, got %s (%s)", scanner.TokenString(tok), scanner.TokenString(p.tok), lit)
+ }
+ p.next()
+ return lit
+}
+
+func (p *parser) expectSpecial(tok string) {
+ sep := 'x' // not white space
+ i := 0
+ for i < len(tok) && p.tok == rune(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 *parser) expectKeyword(keyword string) {
+ lit := p.expect(scanner.Ident)
+ if lit != keyword {
+ p.errorf("expected keyword %s, got %q", keyword, lit)
+ }
+}
+
+// ----------------------------------------------------------------------------
+// Qualified and unqualified names
+
+// PackageId = string_lit .
+//
+func (p *parser) parsePackageId() string {
+ id, err := strconv.Unquote(p.expect(scanner.String))
+ if err != nil {
+ p.error(err)
+ }
+ // id == "" stands for the imported package id
+ // (only known at time of package installation)
+ if id == "" {
+ id = p.id
+ }
+ return id
+}
+
+// PackageName = ident .
+//
+func (p *parser) parsePackageName() string {
+ return p.expect(scanner.Ident)
+}
+
+// dotIdentifier = ( ident | '·' ) { ident | int | '·' } .
+func (p *parser) 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
+}
+
+// QualifiedName = "@" PackageId "." ( "?" | dotIdentifier ) .
+//
+func (p *parser) parseQualifiedName() (id, name string) {
+ p.expect('@')
+ id = p.parsePackageId()
+ p.expect('.')
+ // Per rev f280b8a485fd (10/2/2013), qualified names may be used for anonymous fields.
+ if p.tok == '?' {
+ p.next()
+ } else {
+ name = p.parseDotIdent()
+ }
+ return
+}
+
+// getPkg returns the package for a given id. If the package is
+// not found but we have a package name, create the package and
+// add it to the p.localPkgs and p.sharedPkgs maps.
+//
+// id identifies a package, usually by a canonical package path like
+// "encoding/json" but possibly by a non-canonical import path like
+// "./json".
+//
+func (p *parser) getPkg(id, name string) *types.Package {
+ // package unsafe is not in the packages maps - handle explicitly
+ if id == "unsafe" {
+ return types.Unsafe
+ }
+
+ pkg := p.localPkgs[id]
+ if pkg == nil && name != "" {
+ // first import of id from this package
+ pkg = p.sharedPkgs[id]
+ if pkg == nil {
+ // first import of id by this importer
+ pkg = types.NewPackage(id, name)
+ p.sharedPkgs[id] = pkg
+ }
+
+ if p.localPkgs == nil {
+ p.localPkgs = make(map[string]*types.Package)
+ }
+ p.localPkgs[id] = pkg
+ }
+ return pkg
+}
+
+// parseExportedName is like parseQualifiedName, but
+// the package id is resolved to an imported *types.Package.
+//
+func (p *parser) parseExportedName() (pkg *types.Package, name string) {
+ id, name := p.parseQualifiedName()
+ pkg = p.getPkg(id, "")
+ if pkg == nil {
+ p.errorf("%s package not found", id)
+ }
+ return
+}
+
+// ----------------------------------------------------------------------------
+// Types
+
+// BasicType = identifier .
+//
+func (p *parser) parseBasicType() types.Type {
+ id := p.expect(scanner.Ident)
+ obj := types.Universe.Lookup(id)
+ if obj, ok := obj.(*types.TypeName); ok {
+ return obj.Type()
+ }
+ p.errorf("not a basic type: %s", id)
+ return nil
+}
+
+// ArrayType = "[" int_lit "]" Type .
+//
+func (p *parser) parseArrayType() types.Type {
+ // "[" already consumed and lookahead known not to be "]"
+ lit := p.expect(scanner.Int)
+ p.expect(']')
+ elem := p.parseType()
+ n, err := strconv.ParseInt(lit, 10, 64)
+ if err != nil {
+ p.error(err)
+ }
+ return types.NewArray(elem, n)
+}
+
+// MapType = "map" "[" Type "]" Type .
+//
+func (p *parser) parseMapType() types.Type {
+ p.expectKeyword("map")
+ p.expect('[')
+ key := p.parseType()
+ p.expect(']')
+ elem := p.parseType()
+ return types.NewMap(key, elem)
+}
+
+// Name = identifier | "?" | QualifiedName .
+//
+// If materializePkg is set, the returned package is guaranteed to be set.
+// For fully qualified names, the returned package may be a fake package
+// (without name, scope, and not in the p.sharedPkgs map), created for the
+// sole purpose of providing a package path. Fake packages are created
+// when the package id is not found in the p.sharedPkgs map; in that case
+// we cannot create a real package because we don't have a package name.
+// For non-qualified names, the returned package is the imported package.
+//
+func (p *parser) parseName(materializePkg bool) (pkg *types.Package, name string) {
+ switch p.tok {
+ case scanner.Ident:
+ pkg = p.sharedPkgs[p.id]
+ name = p.lit
+ p.next()
+ case '?':
+ // anonymous
+ pkg = p.sharedPkgs[p.id]
+ p.next()
+ case '@':
+ // exported name prefixed with package path
+ var id string
+ id, name = p.parseQualifiedName()
+ if materializePkg {
+ // we don't have a package name - if the package
+ // doesn't exist yet, create a fake package instead
+ pkg = p.getPkg(id, "")
+ if pkg == nil {
+ pkg = types.NewPackage(id, "")
+ }
+ }
+ default:
+ p.error("name expected")
+ }
+ return
+}
+
+func deref(typ types.Type) types.Type {
+ if p, _ := typ.(*types.Pointer); p != nil {
+ return p.Elem()
+ }
+ return typ
+}
+
+// Field = Name Type [ string_lit ] .
+//
+func (p *parser) parseField() (*types.Var, string) {
+ pkg, name := p.parseName(true)
+ typ := p.parseType()
+ anonymous := false
+ if name == "" {
+ // anonymous field - typ must be T or *T and T must be a type name
+ switch typ := deref(typ).(type) {
+ case *types.Basic: // basic types are named types
+ pkg = nil
+ name = typ.Name()
+ case *types.Named:
+ name = typ.Obj().Name()
+ default:
+ p.errorf("anonymous field expected")
+ }
+ anonymous = true
+ }
+ tag := ""
+ if p.tok == scanner.String {
+ s := p.expect(scanner.String)
+ var err error
+ tag, err = strconv.Unquote(s)
+ if err != nil {
+ p.errorf("invalid struct tag %s: %s", s, err)
+ }
+ }
+ return types.NewField(token.NoPos, pkg, name, typ, anonymous), tag
+}
+
+// StructType = "struct" "{" [ FieldList ] "}" .
+// FieldList = Field { ";" Field } .
+//
+func (p *parser) parseStructType() types.Type {
+ var fields []*types.Var
+ var tags []string
+
+ p.expectKeyword("struct")
+ p.expect('{')
+ for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ {
+ if i > 0 {
+ p.expect(';')
+ }
+ fld, tag := p.parseField()
+ if tag != "" && tags == nil {
+ tags = make([]string, i)
+ }
+ if tags != nil {
+ tags = append(tags, tag)
+ }
+ fields = append(fields, fld)
+ }
+ p.expect('}')
+
+ return types.NewStruct(fields, tags)
+}
+
+// Parameter = ( identifier | "?" ) [ "..." ] Type [ string_lit ] .
+//
+func (p *parser) parseParameter() (par *types.Var, isVariadic bool) {
+ _, name := p.parseName(false)
+ // remove gc-specific parameter numbering
+ if i := strings.Index(name, "·"); i >= 0 {
+ name = name[:i]
+ }
+ if p.tok == '.' {
+ p.expectSpecial("...")
+ isVariadic = true
+ }
+ typ := p.parseType()
+ if isVariadic {
+ typ = types.NewSlice(typ)
+ }
+ // ignore argument tag (e.g. "noescape")
+ if p.tok == scanner.String {
+ p.next()
+ }
+ // TODO(gri) should we provide a package?
+ par = types.NewVar(token.NoPos, nil, name, typ)
+ return
+}
+
+// Parameters = "(" [ ParameterList ] ")" .
+// ParameterList = { Parameter "," } Parameter .
+//
+func (p *parser) parseParameters() (list []*types.Var, isVariadic bool) {
+ p.expect('(')
+ for p.tok != ')' && p.tok != scanner.EOF {
+ if len(list) > 0 {
+ p.expect(',')
+ }
+ par, variadic := p.parseParameter()
+ list = append(list, par)
+ if variadic {
+ if isVariadic {
+ p.error("... not on final argument")
+ }
+ isVariadic = true
+ }
+ }
+ p.expect(')')
+
+ return
+}
+
+// Signature = Parameters [ Result ] .
+// Result = Type | Parameters .
+//
+func (p *parser) parseSignature(recv *types.Var) *types.Signature {
+ params, isVariadic := p.parseParameters()
+
+ // optional result type
+ var results []*types.Var
+ if p.tok == '(' {
+ var variadic bool
+ results, variadic = p.parseParameters()
+ if variadic {
+ p.error("... not permitted on result type")
+ }
+ }
+
+ return types.NewSignature(recv, types.NewTuple(params...), types.NewTuple(results...), isVariadic)
+}
+
+// InterfaceType = "interface" "{" [ MethodList ] "}" .
+// MethodList = Method { ";" Method } .
+// Method = Name Signature .
+//
+// The methods of embedded interfaces are always "inlined"
+// by the compiler and thus embedded interfaces are never
+// visible in the export data.
+//
+func (p *parser) parseInterfaceType() types.Type {
+ var methods []*types.Func
+
+ p.expectKeyword("interface")
+ p.expect('{')
+ for i := 0; p.tok != '}' && p.tok != scanner.EOF; i++ {
+ if i > 0 {
+ p.expect(';')
+ }
+ pkg, name := p.parseName(true)
+ sig := p.parseSignature(nil)
+ methods = append(methods, types.NewFunc(token.NoPos, pkg, name, sig))
+ }
+ p.expect('}')
+
+ // Complete requires the type's embedded interfaces to be fully defined,
+ // but we do not define any
+ return types.NewInterface(methods, nil).Complete()
+}
+
+// ChanType = ( "chan" [ "<-" ] | "<-" "chan" ) Type .
+//
+func (p *parser) parseChanType() types.Type {
+ dir := types.SendRecv
+ if p.tok == scanner.Ident {
+ p.expectKeyword("chan")
+ if p.tok == '<' {
+ p.expectSpecial("<-")
+ dir = types.SendOnly
+ }
+ } else {
+ p.expectSpecial("<-")
+ p.expectKeyword("chan")
+ dir = types.RecvOnly
+ }
+ elem := p.parseType()
+ return types.NewChan(dir, elem)
+}
+
+// Type =
+// BasicType | TypeName | ArrayType | SliceType | StructType |
+// PointerType | FuncType | InterfaceType | MapType | ChanType |
+// "(" Type ")" .
+//
+// BasicType = ident .
+// TypeName = ExportedName .
+// SliceType = "[" "]" Type .
+// PointerType = "*" Type .
+// FuncType = "func" Signature .
+//
+func (p *parser) parseType() types.Type {
+ switch p.tok {
+ case scanner.Ident:
+ switch p.lit {
+ default:
+ return p.parseBasicType()
+ case "struct":
+ return p.parseStructType()
+ case "func":
+ // FuncType
+ p.next()
+ return p.parseSignature(nil)
+ case "interface":
+ return p.parseInterfaceType()
+ case "map":
+ return p.parseMapType()
+ case "chan":
+ return p.parseChanType()
+ }
+ case '@':
+ // TypeName
+ pkg, name := p.parseExportedName()
+ return declTypeName(pkg, name).Type()
+ case '[':
+ p.next() // look ahead
+ if p.tok == ']' {
+ // SliceType
+ p.next()
+ return types.NewSlice(p.parseType())
+ }
+ return p.parseArrayType()
+ case '*':
+ // PointerType
+ p.next()
+ return types.NewPointer(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" PackageName PackageId .
+//
+func (p *parser) parseImportDecl() {
+ p.expectKeyword("import")
+ name := p.parsePackageName()
+ p.getPkg(p.parsePackageId(), name)
+}
+
+// int_lit = [ "+" | "-" ] { "0" ... "9" } .
+//
+func (p *parser) parseInt() string {
+ s := ""
+ switch p.tok {
+ case '-':
+ s = "-"
+ p.next()
+ case '+':
+ p.next()
+ }
+ return s + p.expect(scanner.Int)
+}
+
+// number = int_lit [ "p" int_lit ] .
+//
+func (p *parser) parseNumber() (typ *types.Basic, val exact.Value) {
+ // mantissa
+ mant := exact.MakeFromLiteral(p.parseInt(), token.INT, 0)
+ if mant == nil {
+ panic("invalid mantissa")
+ }
+
+ if p.lit == "p" {
+ // exponent (base 2)
+ p.next()
+ exp, err := strconv.ParseInt(p.parseInt(), 10, 0)
+ if err != nil {
+ p.error(err)
+ }
+ if exp < 0 {
+ denom := exact.MakeInt64(1)
+ denom = exact.Shift(denom, token.SHL, uint(-exp))
+ typ = types.Typ[types.UntypedFloat]
+ val = exact.BinaryOp(mant, token.QUO, denom)
+ return
+ }
+ if exp > 0 {
+ mant = exact.Shift(mant, token.SHL, uint(exp))
+ }
+ typ = types.Typ[types.UntypedFloat]
+ val = mant
+ return
+ }
+
+ typ = types.Typ[types.UntypedInt]
+ val = mant
+ return
+}
+
+// ConstDecl = "const" ExportedName [ Type ] "=" Literal .
+// Literal = bool_lit | int_lit | float_lit | complex_lit | rune_lit | string_lit .
+// bool_lit = "true" | "false" .
+// complex_lit = "(" float_lit "+" float_lit "i" ")" .
+// rune_lit = "(" int_lit "+" int_lit ")" .
+// string_lit = `"` { unicode_char } `"` .
+//
+func (p *parser) parseConstDecl() {
+ p.expectKeyword("const")
+ pkg, name := p.parseExportedName()
+
+ var typ0 types.Type
+ if p.tok != '=' {
+ typ0 = p.parseType()
+ }
+
+ p.expect('=')
+ var typ types.Type
+ var val exact.Value
+ switch p.tok {
+ case scanner.Ident:
+ // bool_lit
+ if p.lit != "true" && p.lit != "false" {
+ p.error("expected true or false")
+ }
+ typ = types.Typ[types.UntypedBool]
+ val = exact.MakeBool(p.lit == "true")
+ p.next()
+
+ case '-', scanner.Int:
+ // int_lit
+ typ, val = p.parseNumber()
+
+ case '(':
+ // complex_lit or rune_lit
+ p.next()
+ if p.tok == scanner.Char {
+ p.next()
+ p.expect('+')
+ typ = types.Typ[types.UntypedRune]
+ _, val = p.parseNumber()
+ p.expect(')')
+ break
+ }
+ _, re := p.parseNumber()
+ p.expect('+')
+ _, im := p.parseNumber()
+ p.expectKeyword("i")
+ p.expect(')')
+ typ = types.Typ[types.UntypedComplex]
+ val = exact.BinaryOp(re, token.ADD, exact.MakeImag(im))
+
+ case scanner.Char:
+ // rune_lit
+ typ = types.Typ[types.UntypedRune]
+ val = exact.MakeFromLiteral(p.lit, token.CHAR, 0)
+ p.next()
+
+ case scanner.String:
+ // string_lit
+ typ = types.Typ[types.UntypedString]
+ val = exact.MakeFromLiteral(p.lit, token.STRING, 0)
+ p.next()
+
+ default:
+ p.errorf("expected literal got %s", scanner.TokenString(p.tok))
+ }
+
+ if typ0 == nil {
+ typ0 = typ
+ }
+
+ pkg.Scope().Insert(types.NewConst(token.NoPos, pkg, name, typ0, val))
+}
+
+// TypeDecl = "type" ExportedName Type .
+//
+func (p *parser) parseTypeDecl() {
+ p.expectKeyword("type")
+ pkg, name := p.parseExportedName()
+ obj := declTypeName(pkg, name)
+
+ // The type object may have been imported before and thus already
+ // have a type associated with it. We still need to parse the type
+ // structure, but throw it away if the object already has a type.
+ // This ensures that all imports refer to the same type object for
+ // a given type declaration.
+ typ := p.parseType()
+
+ if name := obj.Type().(*types.Named); name.Underlying() == nil {
+ name.SetUnderlying(typ)
+ }
+}
+
+// VarDecl = "var" ExportedName Type .
+//
+func (p *parser) parseVarDecl() {
+ p.expectKeyword("var")
+ pkg, name := p.parseExportedName()
+ typ := p.parseType()
+ pkg.Scope().Insert(types.NewVar(token.NoPos, pkg, name, typ))
+}
+
+// Func = Signature [ Body ] .
+// Body = "{" ... "}" .
+//
+func (p *parser) parseFunc(recv *types.Var) *types.Signature {
+ sig := p.parseSignature(recv)
+ if p.tok == '{' {
+ p.next()
+ for i := 1; i > 0; p.next() {
+ switch p.tok {
+ case '{':
+ i++
+ case '}':
+ i--
+ }
+ }
+ }
+ return sig
+}
+
+// MethodDecl = "func" Receiver Name Func .
+// Receiver = "(" ( identifier | "?" ) [ "*" ] ExportedName ")" .
+//
+func (p *parser) parseMethodDecl() {
+ // "func" already consumed
+ p.expect('(')
+ recv, _ := p.parseParameter() // receiver
+ p.expect(')')
+
+ // determine receiver base type object
+ base := deref(recv.Type()).(*types.Named)
+
+ // parse method name, signature, and possibly inlined body
+ _, name := p.parseName(true)
+ sig := p.parseFunc(recv)
+
+ // methods always belong to the same package as the base type object
+ pkg := base.Obj().Pkg()
+
+ // add method to type unless type was imported before
+ // and method exists already
+ // TODO(gri) This leads to a quadratic algorithm - ok for now because method counts are small.
+ base.AddMethod(types.NewFunc(token.NoPos, pkg, name, sig))
+}
+
+// FuncDecl = "func" ExportedName Func .
+//
+func (p *parser) parseFuncDecl() {
+ // "func" already consumed
+ pkg, name := p.parseExportedName()
+ typ := p.parseFunc(nil)
+ pkg.Scope().Insert(types.NewFunc(token.NoPos, pkg, name, typ))
+}
+
+// Decl = [ ImportDecl | ConstDecl | TypeDecl | VarDecl | FuncDecl | MethodDecl ] "\n" .
+//
+func (p *parser) parseDecl() {
+ if p.tok == scanner.Ident {
+ 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" PackageName [ "safe" ] "\n" .
+//
+func (p *parser) parseExport() *types.Package {
+ p.expectKeyword("package")
+ name := p.parsePackageName()
+ if p.tok == scanner.Ident && p.lit == "safe" {
+ // package was compiled with -u option - ignore
+ p.next()
+ }
+ p.expect('\n')
+
+ pkg := p.getPkg(p.id, name)
+
+ 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)
+ }
+
+ // Record all referenced packages as imports.
+ var imports []*types.Package
+ for id, pkg2 := range p.localPkgs {
+ if id == p.id {
+ continue // avoid self-edge
+ }
+ imports = append(imports, pkg2)
+ }
+ sort.Sort(byPath(imports))
+ pkg.SetImports(imports)
+
+ // package was imported completely and without errors
+ pkg.MarkComplete()
+
+ return pkg
+}
+
+type byPath []*types.Package
+
+func (a byPath) Len() int { return len(a) }
+func (a byPath) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a byPath) Less(i, j int) bool { return a[i].Path() < a[j].Path() }
diff --git a/libgo/go/go/internal/gcimporter/gcimporter_test.go b/libgo/go/go/internal/gcimporter/gcimporter_test.go
new file mode 100644
index 00000000000..07993a801f7
--- /dev/null
+++ b/libgo/go/go/internal/gcimporter/gcimporter_test.go
@@ -0,0 +1,225 @@
+// 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 gcimporter
+
+import (
+ "fmt"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+
+ "go/types"
+)
+
+// skipSpecialPlatforms causes the test to be skipped for platforms where
+// builders (build.golang.org) don't have access to compiled packages for
+// import.
+func skipSpecialPlatforms(t *testing.T) {
+ switch platform := runtime.GOOS + "-" + runtime.GOARCH; platform {
+ case "nacl-amd64p32",
+ "nacl-386",
+ "nacl-arm",
+ "darwin-arm",
+ "darwin-arm64":
+ t.Skipf("no compiled packages available for import on %s", platform)
+ }
+}
+
+func compile(t *testing.T, dirname, filename string) string {
+ testenv.MustHaveGoBuild(t)
+ cmd := exec.Command("go", "tool", "compile", filename)
+ cmd.Dir = dirname
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Logf("%s", out)
+ t.Fatalf("go tool compile %s failed: %s", filename, err)
+ }
+ // filename should end with ".go"
+ return filepath.Join(dirname, filename[:len(filename)-2]+"o")
+}
+
+// Use the same global imports map for all tests. The effect is
+// as if all tested packages were imported into a single package.
+var imports = make(map[string]*types.Package)
+
+func testPath(t *testing.T, path string) *types.Package {
+ t0 := time.Now()
+ pkg, err := Import(imports, path)
+ if err != nil {
+ t.Errorf("testPath(%s): %s", path, err)
+ return nil
+ }
+ t.Logf("testPath(%s): %v", path, time.Since(t0))
+ return pkg
+}
+
+const maxTime = 30 * time.Second
+
+func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
+ dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)
+ list, err := ioutil.ReadDir(dirname)
+ if err != nil {
+ t.Fatalf("testDir(%s): %s", dirname, err)
+ }
+ for _, f := range list {
+ if time.Now().After(endTime) {
+ t.Log("testing time used up")
+ return
+ }
+ switch {
+ case !f.IsDir():
+ // 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)) != nil {
+ nimports++
+ }
+ }
+ }
+ case f.IsDir():
+ nimports += testDir(t, filepath.Join(dir, f.Name()), endTime)
+ }
+ }
+ return
+}
+
+func TestImport(t *testing.T) {
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ if outFn := compile(t, "testdata", "exports.go"); outFn != "" {
+ defer os.Remove(outFn)
+ }
+
+ nimports := 0
+ if pkg := testPath(t, "./testdata/exports"); pkg != nil {
+ nimports++
+ // The package's Imports should include all the types
+ // referenced by the exportdata, which may be more than
+ // the import statements in the package's source, but
+ // fewer than the transitive closure of dependencies.
+ want := `[package ast ("go/ast") package token ("go/token") package runtime ("runtime")]`
+ got := fmt.Sprint(pkg.Imports())
+ if got != want {
+ t.Errorf(`Package("exports").Imports() = %s, want %s`, got, want)
+ }
+ }
+ nimports += testDir(t, "", time.Now().Add(maxTime)) // installed packages
+ t.Logf("tested %d imports", nimports)
+}
+
+var importedObjectTests = []struct {
+ name string
+ want string
+}{
+ {"math.Pi", "const Pi untyped float"},
+ {"io.Reader", "type Reader interface{Read(p []byte) (n int, err error)}"},
+ {"io.ReadWriter", "type ReadWriter interface{Read(p []byte) (n int, err error); Write(p []byte) (n int, err error)}"},
+ {"math.Sin", "func Sin(x float64) float64"},
+ // TODO(gri) add more tests
+}
+
+func TestImportedTypes(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ for _, test := range importedObjectTests {
+ s := strings.Split(test.name, ".")
+ if len(s) != 2 {
+ t.Fatal("inconsistent test data")
+ }
+ importPath := s[0]
+ objName := s[1]
+
+ pkg, err := Import(imports, importPath)
+ if err != nil {
+ t.Error(err)
+ continue
+ }
+
+ obj := pkg.Scope().Lookup(objName)
+ if obj == nil {
+ t.Errorf("%s: object not found", test.name)
+ continue
+ }
+
+ got := types.ObjectString(obj, types.RelativeTo(pkg))
+ if got != test.want {
+ t.Errorf("%s: got %q; want %q", test.name, got, test.want)
+ }
+ }
+}
+
+func TestIssue5815(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ pkg, err := Import(make(map[string]*types.Package), "strings")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ scope := pkg.Scope()
+ for _, name := range scope.Names() {
+ obj := scope.Lookup(name)
+ if obj.Pkg() == nil {
+ t.Errorf("no pkg for %s", obj)
+ }
+ if tname, _ := obj.(*types.TypeName); tname != nil {
+ named := tname.Type().(*types.Named)
+ for i := 0; i < named.NumMethods(); i++ {
+ m := named.Method(i)
+ if m.Pkg() == nil {
+ t.Errorf("no pkg for %s", m)
+ }
+ }
+ }
+ }
+}
+
+// Smoke test to ensure that imported methods get the correct package.
+func TestCorrectMethodPackage(t *testing.T) {
+ skipSpecialPlatforms(t)
+
+ // This package only handles gc export data.
+ if runtime.Compiler != "gc" {
+ t.Skipf("gc-built packages not available (compiler = %s)", runtime.Compiler)
+ return
+ }
+
+ imports := make(map[string]*types.Package)
+ _, err := Import(imports, "net/http")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ mutex := imports["sync"].Scope().Lookup("Mutex").(*types.TypeName).Type()
+ mset := types.NewMethodSet(types.NewPointer(mutex)) // methods of *sync.Mutex
+ sel := mset.Lookup(nil, "Lock")
+ lock := sel.Obj().(*types.Func)
+ if got, want := lock.Pkg().Path(), "sync"; got != want {
+ t.Errorf("got package path %q; want %q", got, want)
+ }
+}
diff --git a/libgo/go/go/internal/gcimporter/testdata/exports.go b/libgo/go/go/internal/gcimporter/testdata/exports.go
new file mode 100644
index 00000000000..8ee28b0942b
--- /dev/null
+++ b/libgo/go/go/internal/gcimporter/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 an object file which
+// serves as test file for gcimporter_test.go.
+
+package exports
+
+import (
+ "go/ast"
+)
+
+// Issue 3682: Correctly read dotted identifiers from export data.
+const init1 = 0
+
+func init() {}
+
+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 `go:"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/libgo/go/go/parser/error_test.go b/libgo/go/go/parser/error_test.go
index 48fb53e5b0d..1a08d5a6b11 100644
--- a/libgo/go/go/parser/error_test.go
+++ b/libgo/go/go/parser/error_test.go
@@ -34,11 +34,9 @@ import (
const testdata = "testdata"
-var fsetErrs = token.NewFileSet()
-
// getFile assumes that each filename occurs at most once
-func getFile(filename string) (file *token.File) {
- fsetErrs.Iterate(func(f *token.File) bool {
+func getFile(fset *token.FileSet, filename string) (file *token.File) {
+ fset.Iterate(func(f *token.File) bool {
if f.Name() == filename {
if file != nil {
panic(filename + " used multiple times")
@@ -50,8 +48,8 @@ func getFile(filename string) (file *token.File) {
return file
}
-func getPos(filename string, offset int) token.Pos {
- if f := getFile(filename); f != nil {
+func getPos(fset *token.FileSet, filename string, offset int) token.Pos {
+ if f := getFile(fset, filename); f != nil {
return f.Pos(offset)
}
return token.NoPos
@@ -68,14 +66,14 @@ var errRx = regexp.MustCompile(`^/\* *ERROR *(HERE)? *"([^"]*)" *\*/$`)
// expectedErrors collects the regular expressions of ERROR comments found
// in files and returns them as a map of error positions to error messages.
//
-func expectedErrors(t *testing.T, filename string, src []byte) map[token.Pos]string {
+func expectedErrors(t *testing.T, fset *token.FileSet, filename string, src []byte) map[token.Pos]string {
errors := make(map[token.Pos]string)
var s scanner.Scanner
// file was parsed already - do not add it again to the file
// set otherwise the position information returned here will
// not match the position information collected by the parser
- s.Init(getFile(filename), src, nil, scanner.ScanComments)
+ s.Init(getFile(fset, filename), src, nil, scanner.ScanComments)
var prev token.Pos // position of last non-comment, non-semicolon token
var here token.Pos // position immediately after the token at position prev
@@ -109,11 +107,11 @@ func expectedErrors(t *testing.T, filename string, src []byte) map[token.Pos]str
// compareErrors compares the map of expected error messages with the list
// of found errors and reports discrepancies.
//
-func compareErrors(t *testing.T, expected map[token.Pos]string, found scanner.ErrorList) {
+func compareErrors(t *testing.T, fset *token.FileSet, expected map[token.Pos]string, found scanner.ErrorList) {
for _, error := range found {
// error.Pos is a token.Position, but we want
// a token.Pos so we can do a map lookup
- pos := getPos(error.Pos.Filename, error.Pos.Offset)
+ pos := getPos(fset, error.Pos.Filename, error.Pos.Offset)
if msg, found := expected[pos]; found {
// we expect a message at pos; check if it matches
rx, err := regexp.Compile(msg)
@@ -140,7 +138,7 @@ func compareErrors(t *testing.T, expected map[token.Pos]string, found scanner.Er
if len(expected) > 0 {
t.Errorf("%d errors not reported:", len(expected))
for pos, msg := range expected {
- t.Errorf("%s: %s\n", fsetErrs.Position(pos), msg)
+ t.Errorf("%s: %s\n", fset.Position(pos), msg)
}
}
}
@@ -152,7 +150,8 @@ func checkErrors(t *testing.T, filename string, input interface{}) {
return
}
- _, err = ParseFile(fsetErrs, filename, src, DeclarationErrors|AllErrors)
+ fset := token.NewFileSet()
+ _, err = ParseFile(fset, filename, src, DeclarationErrors|AllErrors)
found, ok := err.(scanner.ErrorList)
if err != nil && !ok {
t.Error(err)
@@ -162,10 +161,10 @@ func checkErrors(t *testing.T, filename string, input interface{}) {
// we are expecting the following errors
// (collect these after parsing a file so that it is found in the file set)
- expected := expectedErrors(t, filename, src)
+ expected := expectedErrors(t, fset, filename, src)
// verify errors returned by the parser
- compareErrors(t, expected, found)
+ compareErrors(t, fset, expected, found)
}
func TestErrors(t *testing.T) {
diff --git a/libgo/go/go/parser/interface.go b/libgo/go/go/parser/interface.go
index 49103058b59..c6fd93240af 100644
--- a/libgo/go/go/parser/interface.go
+++ b/libgo/go/go/parser/interface.go
@@ -91,7 +91,10 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode)
var p parser
defer func() {
if e := recover(); e != nil {
- _ = e.(bailout) // re-panics if it's not a bailout
+ // resume same panic if it's not a bailout
+ if _, ok := e.(bailout); !ok {
+ panic(e)
+ }
}
// set result values
@@ -164,14 +167,31 @@ func ParseDir(fset *token.FileSet, path string, filter func(os.FileInfo) bool, m
return
}
-// ParseExpr is a convenience function for obtaining the AST of an expression x.
-// The position information recorded in the AST is undefined. The filename used
-// in error messages is the empty string.
+// ParseExprFrom is a convenience function for parsing an expression.
+// The arguments have the same meaning as for Parse, but the source must
+// be a valid Go (type or value) expression.
//
-func ParseExpr(x string) (ast.Expr, error) {
+func ParseExprFrom(fset *token.FileSet, filename string, src interface{}, mode Mode) (ast.Expr, error) {
+ // get source
+ text, err := readSource(filename, src)
+ if err != nil {
+ return nil, err
+ }
+
var p parser
- p.init(token.NewFileSet(), "", []byte(x), 0)
+ defer func() {
+ if e := recover(); e != nil {
+ // resume same panic if it's not a bailout
+ if _, ok := e.(bailout); !ok {
+ panic(e)
+ }
+ }
+ p.errors.Sort()
+ err = p.errors.Err()
+ }()
+ // parse expr
+ p.init(fset, filename, text, mode)
// Set up pkg-level scopes to avoid nil-pointer errors.
// This is not needed for a correct expression x as the
// parser will be ok with a nil topScope, but be cautious
@@ -196,3 +216,11 @@ func ParseExpr(x string) (ast.Expr, error) {
return e, nil
}
+
+// ParseExpr is a convenience function for obtaining the AST of an expression x.
+// The position information recorded in the AST is undefined. The filename used
+// in error messages is the empty string.
+//
+func ParseExpr(x string) (ast.Expr, error) {
+ return ParseExprFrom(token.NewFileSet(), "", []byte(x), 0)
+}
diff --git a/libgo/go/go/parser/parser.go b/libgo/go/go/parser/parser.go
index 4a005d8ffa3..e82c0bd1223 100644
--- a/libgo/go/go/parser/parser.go
+++ b/libgo/go/go/parser/parser.go
@@ -7,6 +7,13 @@
// output is an abstract syntax tree (AST) representing the Go source. The
// parser is invoked through one of the Parse* functions.
//
+// The parser accepts a larger language than is syntactically permitted by
+// the Go spec, for simplicity, and for improved robustness in the presence
+// of syntax errors. For instance, in method declarations, the receiver is
+// treated like an ordinary parameter list and thus may contain multiple
+// entries where the spec permits exactly one. Consequently, the corresponding
+// field in the AST (ast.FuncDecl.Recv) field is not restricted to one entry.
+//
package parser
import (
@@ -412,14 +419,17 @@ func (p *parser) expectSemi() {
}
}
-func (p *parser) atComma(context string) bool {
+func (p *parser) atComma(context string, follow token.Token) bool {
if p.tok == token.COMMA {
return true
}
- if p.tok == token.SEMICOLON && p.lit == "\n" {
- p.error(p.pos, "missing ',' before newline in "+context)
- return true // "insert" the comma and continue
-
+ if p.tok != follow {
+ msg := "missing ','"
+ if p.tok == token.SEMICOLON && p.lit == "\n" {
+ msg += " before newline"
+ }
+ p.error(p.pos, msg+" in "+context)
+ return true // "insert" comma and continue
}
return false
}
@@ -825,7 +835,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
// parameter or result variable is the function body.
p.declare(field, nil, scope, ast.Var, idents...)
p.resolve(typ)
- if !p.atComma("parameter list") {
+ if !p.atComma("parameter list", token.RPAREN) {
return
}
p.next()
@@ -838,7 +848,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
// parameter or result variable is the function body.
p.declare(field, nil, scope, ast.Var, idents...)
p.resolve(typ)
- if !p.atComma("parameter list") {
+ if !p.atComma("parameter list", token.RPAREN) {
break
}
p.next()
@@ -1248,7 +1258,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
ellipsis = p.pos
p.next()
}
- if !p.atComma("argument list") {
+ if !p.atComma("argument list", token.RPAREN) {
break
}
p.next()
@@ -1259,7 +1269,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
return &ast.CallExpr{Fun: fun, Lparen: lparen, Args: list, Ellipsis: ellipsis, Rparen: rparen}
}
-func (p *parser) parseElement(keyOk bool) ast.Expr {
+func (p *parser) parseValue(keyOk bool) ast.Expr {
if p.trace {
defer un(trace(p, "Element"))
}
@@ -1287,16 +1297,30 @@ func (p *parser) parseElement(keyOk bool) ast.Expr {
x := p.checkExpr(p.parseExpr(keyOk))
if keyOk {
if p.tok == token.COLON {
- colon := p.pos
- p.next()
// Try to resolve the key but don't collect it
// as unresolved identifier if it fails so that
// we don't get (possibly false) errors about
// undeclared names.
p.tryResolve(x, false)
- return &ast.KeyValueExpr{Key: x, Colon: colon, Value: p.parseElement(false)}
+ } else {
+ // not a key
+ p.resolve(x)
}
- p.resolve(x) // not a key
+ }
+
+ return x
+}
+
+func (p *parser) parseElement() ast.Expr {
+ if p.trace {
+ defer un(trace(p, "Element"))
+ }
+
+ x := p.parseValue(true)
+ if p.tok == token.COLON {
+ colon := p.pos
+ p.next()
+ x = &ast.KeyValueExpr{Key: x, Colon: colon, Value: p.parseValue(false)}
}
return x
@@ -1308,8 +1332,8 @@ func (p *parser) parseElementList() (list []ast.Expr) {
}
for p.tok != token.RBRACE && p.tok != token.EOF {
- list = append(list, p.parseElement(true))
- if !p.atComma("composite literal") {
+ list = append(list, p.parseElement())
+ if !p.atComma("composite literal", token.RBRACE) {
break
}
p.next()
@@ -1365,7 +1389,7 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr {
return x
}
-// isTypeName returns true iff x is a (qualified) TypeName.
+// isTypeName reports whether x is a (qualified) TypeName.
func isTypeName(x ast.Expr) bool {
switch t := x.(type) {
case *ast.BadExpr:
@@ -1379,7 +1403,7 @@ func isTypeName(x ast.Expr) bool {
return true
}
-// isLiteralType returns true iff x is a legal composite literal type.
+// isLiteralType reports whether x is a legal composite literal type.
func isLiteralType(x ast.Expr) bool {
switch t := x.(type) {
case *ast.BadExpr:
@@ -1455,7 +1479,8 @@ L:
pos := p.pos
p.errorExpected(pos, "selector or type assertion")
p.next() // make progress
- x = &ast.BadExpr{From: pos, To: p.pos}
+ sel := &ast.Ident{NamePos: pos, Name: "_"}
+ x = &ast.SelectorExpr{X: x, Sel: sel}
}
case token.LBRACK:
if lhs {
@@ -2123,7 +2148,7 @@ func (p *parser) parseStmt() (s ast.Stmt) {
case
// tokens that may start an expression
token.IDENT, token.INT, token.FLOAT, token.IMAG, token.CHAR, token.STRING, token.FUNC, token.LPAREN, // operands
- token.LBRACK, token.STRUCT, // composite types
+ token.LBRACK, token.STRUCT, token.MAP, token.CHAN, token.INTERFACE, // composite types
token.ADD, token.SUB, token.MUL, token.AND, token.XOR, token.ARROW, token.NOT: // unary operators
s, _ = p.parseSimpleStmt(labelOk)
// because of the required look-ahead, labeled statements are
@@ -2152,11 +2177,14 @@ func (p *parser) parseStmt() (s ast.Stmt) {
case token.FOR:
s = p.parseForStmt()
case token.SEMICOLON:
- s = &ast.EmptyStmt{Semicolon: p.pos}
+ // Is it ever possible to have an implicit semicolon
+ // producing an empty statement in a valid program?
+ // (handle correctly anyway)
+ s = &ast.EmptyStmt{Semicolon: p.pos, Implicit: p.lit == "\n"}
p.next()
case token.RBRACE:
// a semicolon may be omitted before a closing "}"
- s = &ast.EmptyStmt{Semicolon: p.pos}
+ s = &ast.EmptyStmt{Semicolon: p.pos, Implicit: true}
default:
// no statement found
pos := p.pos
@@ -2228,6 +2256,7 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota
defer un(trace(p, keyword.String()+"Spec"))
}
+ pos := p.pos
idents := p.parseIdentList()
typ := p.tryType()
var values []ast.Expr
@@ -2238,6 +2267,17 @@ func (p *parser) parseValueSpec(doc *ast.CommentGroup, keyword token.Token, iota
}
p.expectSemi() // call before accessing p.linecomment
+ switch keyword {
+ case token.VAR:
+ if typ == nil && values == nil {
+ p.error(pos, "missing variable type or initialization")
+ }
+ case token.CONST:
+ if values == nil && (iota == 0 || typ != nil) {
+ p.error(pos, "missing constant value")
+ }
+ }
+
// 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.
diff --git a/libgo/go/go/parser/parser_test.go b/libgo/go/go/parser/parser_test.go
index 85065fd1829..c7bb36d789d 100644
--- a/libgo/go/go/parser/parser_test.go
+++ b/libgo/go/go/parser/parser_test.go
@@ -14,8 +14,6 @@ import (
"testing"
)
-var fset = token.NewFileSet()
-
var validFiles = []string{
"parser.go",
"parser_test.go",
@@ -25,7 +23,7 @@ var validFiles = []string{
func TestParse(t *testing.T) {
for _, filename := range validFiles {
- _, err := ParseFile(fset, filename, nil, DeclarationErrors)
+ _, err := ParseFile(token.NewFileSet(), filename, nil, DeclarationErrors)
if err != nil {
t.Fatalf("ParseFile(%s): %v", filename, err)
}
@@ -46,7 +44,7 @@ func dirFilter(f os.FileInfo) bool { return nameFilter(f.Name()) }
func TestParseDir(t *testing.T) {
path := "."
- pkgs, err := ParseDir(fset, path, dirFilter, 0)
+ pkgs, err := ParseDir(token.NewFileSet(), path, dirFilter, 0)
if err != nil {
t.Fatalf("ParseDir(%s): %v", path, err)
}
@@ -131,7 +129,7 @@ func TestParseExpr(t *testing.T) {
}
func TestColonEqualsScope(t *testing.T) {
- f, err := ParseFile(fset, "", `package p; func f() { x, y, z := x, y, z }`, 0)
+ f, err := ParseFile(token.NewFileSet(), "", `package p; func f() { x, y, z := x, y, z }`, 0)
if err != nil {
t.Fatal(err)
}
@@ -153,7 +151,7 @@ func TestColonEqualsScope(t *testing.T) {
}
func TestVarScope(t *testing.T) {
- f, err := ParseFile(fset, "", `package p; func f() { var x, y, z = x, y, z }`, 0)
+ f, err := ParseFile(token.NewFileSet(), "", `package p; func f() { var x, y, z = x, y, z }`, 0)
if err != nil {
t.Fatal(err)
}
@@ -183,7 +181,7 @@ var x int
func f() { L: }
`
- f, err := ParseFile(fset, "", src, 0)
+ f, err := ParseFile(token.NewFileSet(), "", src, 0)
if err != nil {
t.Fatal(err)
}
@@ -221,7 +219,7 @@ func f() { L: }
}
func TestUnresolved(t *testing.T) {
- f, err := ParseFile(fset, "", `
+ f, err := ParseFile(token.NewFileSet(), "", `
package p
//
func f1a(int)
@@ -316,7 +314,7 @@ var imports = map[string]bool{
func TestImports(t *testing.T) {
for path, isValid := range imports {
src := fmt.Sprintf("package p; import %s", path)
- _, err := ParseFile(fset, "", src, 0)
+ _, err := ParseFile(token.NewFileSet(), "", src, 0)
switch {
case err != nil && isValid:
t.Errorf("ParseFile(%s): got %v; expected no error", src, err)
@@ -327,7 +325,7 @@ func TestImports(t *testing.T) {
}
func TestCommentGroups(t *testing.T) {
- f, err := ParseFile(fset, "", `
+ f, err := ParseFile(token.NewFileSet(), "", `
package p /* 1a */ /* 1b */ /* 1c */ // 1d
/* 2a
*/
@@ -421,7 +419,7 @@ func checkFieldComments(t *testing.T, file *ast.File, fieldname, lead, line stri
}
func TestLeadAndLineComments(t *testing.T) {
- f, err := ParseFile(fset, "", `
+ f, err := ParseFile(token.NewFileSet(), "", `
package p
type T struct {
/* F1 lead comment */
@@ -447,3 +445,89 @@ type T struct {
t.Error("not expected to find T.f3")
}
}
+
+// TestIssue9979 verifies that empty statements are contained within their enclosing blocks.
+func TestIssue9979(t *testing.T) {
+ for _, src := range []string{
+ "package p; func f() {;}",
+ "package p; func f() {L:}",
+ "package p; func f() {L:;}",
+ "package p; func f() {L:\n}",
+ "package p; func f() {L:\n;}",
+ "package p; func f() { ; }",
+ "package p; func f() { L: }",
+ "package p; func f() { L: ; }",
+ "package p; func f() { L: \n}",
+ "package p; func f() { L: \n; }",
+ } {
+ fset := token.NewFileSet()
+ f, err := ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var pos, end token.Pos
+ ast.Inspect(f, func(x ast.Node) bool {
+ switch s := x.(type) {
+ case *ast.BlockStmt:
+ pos, end = s.Pos()+1, s.End()-1 // exclude "{", "}"
+ case *ast.LabeledStmt:
+ pos, end = s.Pos()+2, s.End() // exclude "L:"
+ case *ast.EmptyStmt:
+ // check containment
+ if s.Pos() < pos || s.End() > end {
+ t.Errorf("%s: %T[%d, %d] not inside [%d, %d]", src, s, s.Pos(), s.End(), pos, end)
+ }
+ // check semicolon
+ offs := fset.Position(s.Pos()).Offset
+ if ch := src[offs]; ch != ';' != s.Implicit {
+ want := "want ';'"
+ if s.Implicit {
+ want = "but ';' is implicit"
+ }
+ t.Errorf("%s: found %q at offset %d; %s", src, ch, offs, want)
+ }
+ }
+ return true
+ })
+ }
+}
+
+// TestIncompleteSelection ensures that an incomplete selector
+// expression is parsed as a (blank) *ast.SelectorExpr, not a
+// *ast.BadExpr.
+func TestIncompleteSelection(t *testing.T) {
+ for _, src := range []string{
+ "package p; var _ = fmt.", // at EOF
+ "package p; var _ = fmt.\ntype X int", // not at EOF
+ } {
+ fset := token.NewFileSet()
+ f, err := ParseFile(fset, "", src, 0)
+ if err == nil {
+ t.Errorf("ParseFile(%s) succeeded unexpectedly", src)
+ continue
+ }
+
+ const wantErr = "expected selector or type assertion"
+ if !strings.Contains(err.Error(), wantErr) {
+ t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr)
+ }
+
+ var sel *ast.SelectorExpr
+ ast.Inspect(f, func(n ast.Node) bool {
+ if n, ok := n.(*ast.SelectorExpr); ok {
+ sel = n
+ }
+ return true
+ })
+ if sel == nil {
+ t.Error("found no *ast.SelectorExpr")
+ continue
+ }
+ const wantSel = "&{fmt _}"
+ if fmt.Sprint(sel) != wantSel {
+ t.Errorf("found selector %s, want %s", sel, wantSel)
+ continue
+ }
+ }
+}
diff --git a/libgo/go/go/parser/short_test.go b/libgo/go/go/parser/short_test.go
index 05e44de28a7..ef2ffadbd98 100644
--- a/libgo/go/go/parser/short_test.go
+++ b/libgo/go/go/parser/short_test.go
@@ -40,6 +40,12 @@ var valids = []string{
`package p; func (*(T),) m() {}`,
`package p; func _(x []int) { for range x {} }`,
`package p; func _() { if [T{}.n]int{} {} }`,
+ `package p; func _() { map[int]int{}[0]++; map[int]int{}[0] += 1 }`,
+ `package p; func _(x interface{f()}) { interface{f()}(x).f() }`,
+ `package p; func _(x chan int) { chan int(x) <- 0 }`,
+ `package p; const (x = 0; y; z)`, // issue 9639
+ `package p; var _ = map[P]int{P{}:0, {}:1}`,
+ `package p; var _ = map[*P]int{&P{}:0, {}:1}`,
}
func TestValid(t *testing.T) {
@@ -93,8 +99,13 @@ var invalids = []string{
`package p; func f() { for i /* ERROR "boolean or range expression" */ , x := []string {} }`,
`package p; func f() { go f /* ERROR HERE "function must be invoked" */ }`,
`package p; func f() { defer func() {} /* ERROR HERE "function must be invoked" */ }`,
- `package p; func f() { go func() { func() { f(x func /* ERROR "expected '\)'" */ (){}) } } }`,
- `package p; func f() (a b string /* ERROR "expected '\)'" */ , ok bool) // issue 8656`,
+ `package p; func f() { go func() { func() { f(x func /* ERROR "missing ','" */ (){}) } } }`,
+ `package p; func f(x func(), u v func /* ERROR "missing ','" */ ()){}`,
+ `package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`, // issue 8656
+ `package p; var x /* ERROR "missing variable type or initialization" */ , y, z;`, // issue 9639
+ `package p; const x /* ERROR "missing constant value" */ ;`, // issue 9639
+ `package p; const x /* ERROR "missing constant value" */ int;`, // issue 9639
+ `package p; const (x = 0; y; z /* ERROR "missing constant value" */ int);`, // issue 9639
}
func TestInvalid(t *testing.T) {
diff --git a/libgo/go/go/parser/testdata/issue3106.src b/libgo/go/go/parser/testdata/issue3106.src
index 82796c8ceb6..2db10be2354 100644
--- a/libgo/go/go/parser/testdata/issue3106.src
+++ b/libgo/go/go/parser/testdata/issue3106.src
@@ -19,7 +19,7 @@ func f() {
time.Sleep(1e8)
m.Lock()
defer
- if /* ERROR "expected operand, found 'if'" */ percent == 100 {
+ if /* ERROR "expected ';', found 'if'" */ percent == 100 {
m.Unlock()
break
}
diff --git a/libgo/go/go/printer/nodes.go b/libgo/go/go/printer/nodes.go
index d5a69349beb..fe047053afb 100644
--- a/libgo/go/go/printer/nodes.go
+++ b/libgo/go/go/printer/nodes.go
@@ -12,6 +12,9 @@ import (
"bytes"
"go/ast"
"go/token"
+ "strconv"
+ "strings"
+ "unicode"
"unicode/utf8"
)
@@ -1334,6 +1337,49 @@ func (p *printer) valueSpec(s *ast.ValueSpec, keepType bool) {
}
}
+func sanitizeImportPath(lit *ast.BasicLit) *ast.BasicLit {
+ // Note: An unmodified AST generated by go/parser will already
+ // contain a backward- or double-quoted path string that does
+ // not contain any invalid characters, and most of the work
+ // here is not needed. However, a modified or generated AST
+ // may possibly contain non-canonical paths. Do the work in
+ // all cases since it's not too hard and not speed-critical.
+
+ // if we don't have a proper string, be conservative and return whatever we have
+ if lit.Kind != token.STRING {
+ return lit
+ }
+ s, err := strconv.Unquote(lit.Value)
+ if err != nil {
+ return lit
+ }
+
+ // if the string is an invalid path, return whatever we have
+ //
+ // spec: "Implementation restriction: A compiler may restrict
+ // ImportPaths to non-empty strings using only characters belonging
+ // to Unicode's L, M, N, P, and S general categories (the Graphic
+ // characters without spaces) and may also exclude the characters
+ // !"#$%&'()*,:;<=>?[\]^`{|} and the Unicode replacement character
+ // U+FFFD."
+ if s == "" {
+ return lit
+ }
+ const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
+ for _, r := range s {
+ if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
+ return lit
+ }
+ }
+
+ // otherwise, return the double-quoted path
+ s = strconv.Quote(s)
+ if s == lit.Value {
+ return lit // nothing wrong with lit
+ }
+ return &ast.BasicLit{ValuePos: lit.ValuePos, Kind: token.STRING, Value: s}
+}
+
// The parameter n is the number of specs in the group. If doIndent is set,
// multi-line identifier lists in the spec are indented when the first
// linebreak is encountered.
@@ -1346,7 +1392,7 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool) {
p.expr(s.Name)
p.print(blank)
}
- p.expr(s.Path)
+ p.expr(sanitizeImportPath(s.Path))
p.setComment(s.Comment)
p.print(s.EndPos)
diff --git a/libgo/go/go/printer/printer.go b/libgo/go/go/printer/printer.go
index 280c697a0dd..f9343d3af0d 100644
--- a/libgo/go/go/printer/printer.go
+++ b/libgo/go/go/printer/printer.go
@@ -144,7 +144,7 @@ func (p *printer) nextComment() {
p.commentOffset = infinity
}
-// commentBefore returns true iff the current comment group occurs
+// commentBefore reports whether the current comment group occurs
// before the next position in the source code and printing it does
// not introduce implicit semicolons.
//
@@ -496,29 +496,33 @@ func stripCommonPrefix(lines []string) {
// Compute maximum common white prefix of all but the first,
// last, and blank lines, and replace blank lines with empty
// lines (the first line starts with /* and has no prefix).
- // In case of two-line comments, consider the last line for
- // the prefix computation since otherwise the prefix would
- // be empty.
+ // In cases where only the first and last lines are not blank,
+ // such as two-line comments, or comments where all inner lines
+ // are blank, consider the last line for the prefix computation
+ // since otherwise the prefix would be empty.
//
// Note that the first and last line are never empty (they
// contain the opening /* and closing */ respectively) and
// thus they can be ignored by the blank line check.
- var prefix string
+ prefix := ""
+ prefixSet := false
if len(lines) > 2 {
- first := true
for i, line := range lines[1 : len(lines)-1] {
- switch {
- case isBlank(line):
+ if isBlank(line) {
lines[1+i] = "" // range starts with lines[1]
- case first:
- prefix = commonPrefix(line, line)
- first = false
- default:
+ } else {
+ if !prefixSet {
+ prefix = line
+ prefixSet = true
+ }
prefix = commonPrefix(prefix, line)
}
+
}
- } else { // len(lines) == 2, lines cannot be blank (contain /* and */)
- line := lines[1]
+ }
+ // If we don't have a prefix yet, consider the last line.
+ if !prefixSet {
+ line := lines[len(lines)-1]
prefix = commonPrefix(line, line)
}
diff --git a/libgo/go/go/printer/testdata/comments.golden b/libgo/go/go/printer/testdata/comments.golden
index b1af7958a96..849fa624489 100644
--- a/libgo/go/go/printer/testdata/comments.golden
+++ b/libgo/go/go/printer/testdata/comments.golden
@@ -413,6 +413,68 @@ func _() {
aligned line */
}
+// Issue 9751.
+func _() {
+ /*a string
+
+ b string*/
+
+ /*A string
+
+
+
+ Z string*/
+
+ /*a string
+
+ b string
+
+ c string*/
+
+ {
+ /*a string
+ b string*/
+
+ /*a string
+
+ b string*/
+
+ /*a string
+
+ b string
+
+ c string*/
+ }
+
+ {
+ /*a string
+ b string*/
+
+ /*a string
+
+ b string*/
+
+ /*a string
+
+ b string
+
+ c string*/
+ }
+
+ /*
+ */
+
+ /*
+
+ */
+
+ /*
+
+ * line
+
+ */
+}
+
/*
* line
* of
diff --git a/libgo/go/go/printer/testdata/comments.input b/libgo/go/go/printer/testdata/comments.input
index 983e2b2c97e..30cd23c6dd4 100644
--- a/libgo/go/go/printer/testdata/comments.input
+++ b/libgo/go/go/printer/testdata/comments.input
@@ -418,6 +418,68 @@ func _() {
aligned line */
}
+// Issue 9751.
+func _() {
+ /*a string
+
+ b string*/
+
+ /*A string
+
+
+
+ Z string*/
+
+ /*a string
+
+ b string
+
+ c string*/
+
+ {
+ /*a string
+b string*/
+
+ /*a string
+
+b string*/
+
+ /*a string
+
+b string
+
+c string*/
+ }
+
+ {
+ /*a string
+ b string*/
+
+ /*a string
+
+ b string*/
+
+ /*a string
+
+ b string
+
+ c string*/
+ }
+
+ /*
+ */
+
+ /*
+
+ */
+
+ /*
+
+ * line
+
+ */
+}
+
/*
* line
* of
diff --git a/libgo/go/go/printer/testdata/declarations.golden b/libgo/go/go/printer/testdata/declarations.golden
index 9acd41b7d28..82f5e0f9147 100644
--- a/libgo/go/go/printer/testdata/declarations.golden
+++ b/libgo/go/go/printer/testdata/declarations.golden
@@ -110,6 +110,15 @@ import (
"package_dddd" // comment
)
+// print import paths as double-quoted strings
+// (we would like more test cases but the go/parser
+// already excludes most incorrect paths, and we don't
+// bother setting up test-ASTs manually)
+import (
+ "fmt"
+ "math"
+)
+
// at least one empty line between declarations of different kind
import _ "io"
diff --git a/libgo/go/go/printer/testdata/declarations.input b/libgo/go/go/printer/testdata/declarations.input
index 45beec25fc7..a0a3783b846 100644
--- a/libgo/go/go/printer/testdata/declarations.input
+++ b/libgo/go/go/printer/testdata/declarations.input
@@ -111,6 +111,15 @@ import (
"package_dddd" // comment
)
+// print import paths as double-quoted strings
+// (we would like more test cases but the go/parser
+// already excludes most incorrect paths, and we don't
+// bother setting up test-ASTs manually)
+import (
+ `fmt`
+ "math"
+)
+
// at least one empty line between declarations of different kind
import _ "io"
var _ int
diff --git a/libgo/go/go/printer/testdata/parser.go b/libgo/go/go/printer/testdata/parser.go
index dba8bbd4351..44dfa19ff35 100644
--- a/libgo/go/go/printer/testdata/parser.go
+++ b/libgo/go/go/printer/testdata/parser.go
@@ -1165,7 +1165,7 @@ func (p *parser) checkExpr(x ast.Expr) ast.Expr {
return x
}
-// isTypeName returns true iff x is a (qualified) TypeName.
+// isTypeName reports whether x is a (qualified) TypeName.
func isTypeName(x ast.Expr) bool {
switch t := x.(type) {
case *ast.BadExpr:
@@ -1179,7 +1179,7 @@ func isTypeName(x ast.Expr) bool {
return true
}
-// isLiteralType returns true iff x is a legal composite literal type.
+// isLiteralType reports whether x is a legal composite literal type.
func isLiteralType(x ast.Expr) bool {
switch t := x.(type) {
case *ast.BadExpr:
diff --git a/libgo/go/go/scanner/errors.go b/libgo/go/go/scanner/errors.go
index 22de69c3c1c..bf7bfa30e4b 100644
--- a/libgo/go/go/scanner/errors.go
+++ b/libgo/go/go/scanner/errors.go
@@ -54,18 +54,16 @@ func (p ErrorList) Less(i, j int) bool {
// Note that it is not sufficient to simply compare file offsets because
// the offsets do not reflect modified line information (through //line
// comments).
- if e.Filename < f.Filename {
- return true
+ if e.Filename != f.Filename {
+ return e.Filename < f.Filename
}
- if e.Filename == f.Filename {
- if e.Line < f.Line {
- return true
- }
- if e.Line == f.Line {
- return e.Column < f.Column
- }
+ if e.Line != f.Line {
+ return e.Line < f.Line
+ }
+ if e.Column != f.Column {
+ return e.Column < f.Column
}
- return false
+ return p[i].Msg < p[j].Msg
}
// Sort sorts an ErrorList. *Error entries are sorted by position,
diff --git a/libgo/go/go/scanner/scanner.go b/libgo/go/go/scanner/scanner.go
index cec82ea10ef..e9476c4dee4 100644
--- a/libgo/go/go/scanner/scanner.go
+++ b/libgo/go/go/scanner/scanner.go
@@ -706,13 +706,14 @@ scanAgain:
s.insertSemi = false // newline consumed
return pos, token.SEMICOLON, "\n"
}
- lit = s.scanComment()
+ comment := s.scanComment()
if s.mode&ScanComments == 0 {
// skip comment
s.insertSemi = false // newline consumed
goto scanAgain
}
tok = token.COMMENT
+ lit = comment
} else {
tok = s.switch2(token.QUO, token.QUO_ASSIGN)
}
diff --git a/libgo/go/go/scanner/scanner_test.go b/libgo/go/go/scanner/scanner_test.go
index fc450d8a6eb..0d21905166c 100644
--- a/libgo/go/go/scanner/scanner_test.go
+++ b/libgo/go/go/scanner/scanner_test.go
@@ -734,6 +734,41 @@ func TestScanErrors(t *testing.T) {
}
}
+// Verify that no comments show up as literal values when skipping comments.
+func TestIssue10213(t *testing.T) {
+ var src = `
+ var (
+ A = 1 // foo
+ )
+
+ var (
+ B = 2
+ // foo
+ )
+
+ var C = 3 // foo
+
+ var D = 4
+ // foo
+
+ func anycode() {
+ // foo
+ }
+ `
+ var s Scanner
+ s.Init(fset.AddFile("", fset.Base(), len(src)), []byte(src), nil, 0)
+ for {
+ pos, tok, lit := s.Scan()
+ class := tokenclass(tok)
+ if lit != "" && class != keyword && class != literal && tok != token.SEMICOLON {
+ t.Errorf("%s: tok = %s, lit = %q", fset.Position(pos), tok, lit)
+ }
+ if tok <= token.EOF {
+ break
+ }
+ }
+}
+
func BenchmarkScan(b *testing.B) {
b.StopTimer()
fset := token.NewFileSet()
diff --git a/libgo/go/go/token/position.go b/libgo/go/go/token/position.go
index 82d90eeb728..33751779a3d 100644
--- a/libgo/go/go/token/position.go
+++ b/libgo/go/go/token/position.go
@@ -21,10 +21,10 @@ type Position struct {
Filename string // filename, if any
Offset int // offset, starting at 0
Line int // line number, starting at 1
- Column int // column number, starting at 1 (character count)
+ Column int // column number, starting at 1 (byte count)
}
-// IsValid returns true if the position is valid.
+// IsValid reports whether the position is valid.
func (pos *Position) IsValid() bool { return pos.Line > 0 }
// String returns a string in one of several forms:
@@ -56,8 +56,8 @@ func (pos Position) String() string {
// where base and size are specified when adding the file to the file set via
// AddFile.
//
-// To create the Pos value for a specific source offset, first add
-// the respective file to the current file set (via FileSet.AddFile)
+// To create the Pos value for a specific source offset (measured in bytes),
+// first add the respective file to the current file set using FileSet.AddFile
// and then call File.Pos(offset) for that file. Given a Pos value p
// for a specific file set fset, the corresponding Position value is
// obtained by calling fset.Position(p).
@@ -77,7 +77,7 @@ type Pos int
//
const NoPos Pos = 0
-// IsValid returns true if the position is valid.
+// IsValid reports whether the position is valid.
func (p Pos) IsValid() bool {
return p != NoPos
}
@@ -157,7 +157,7 @@ func (f *File) MergeLine(line int) {
f.lines = f.lines[:len(f.lines)-1]
}
-// SetLines sets the line offsets for a file and returns true if successful.
+// SetLines sets the line offsets for a file and reports whether it succeeded.
// The line offsets are the offsets of the first character of each line;
// for instance for the content "ab\nc\n" the line offsets are {0, 3}.
// An empty file has an empty line offset table.
diff --git a/libgo/go/go/types/api.go b/libgo/go/go/types/api.go
new file mode 100644
index 00000000000..b3bf6f01476
--- /dev/null
+++ b/libgo/go/go/types/api.go
@@ -0,0 +1,336 @@
+// Copyright 2012 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 declares the data types and implements
+// the algorithms for type-checking of Go packages. Use
+// Config.Check to invoke the type checker for a package.
+// Alternatively, create a new type checked with NewChecker
+// and invoke it incrementally by calling Checker.Files.
+//
+// Type-checking consists of several interdependent phases:
+//
+// Name resolution maps each identifier (ast.Ident) in the program to the
+// language object (Object) it denotes.
+// Use Info.{Defs,Uses,Implicits} for the results of name resolution.
+//
+// Constant folding computes the exact constant value (constant.Value)
+// for every expression (ast.Expr) that is a compile-time constant.
+// Use Info.Types[expr].Value for the results of constant folding.
+//
+// Type inference computes the type (Type) of every expression (ast.Expr)
+// and checks for compliance with the language specification.
+// Use Info.Types[expr].Type for the results of type inference.
+//
+package types // import "go/types"
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "go/token"
+)
+
+// An Error describes a type-checking error; it implements the error interface.
+// A "soft" error is an error that still permits a valid interpretation of a
+// package (such as "unused variable"); "hard" errors may lead to unpredictable
+// behavior if ignored.
+type Error struct {
+ Fset *token.FileSet // file set for interpretation of Pos
+ Pos token.Pos // error position
+ Msg string // error message
+ Soft bool // if set, error is "soft"
+}
+
+// Error returns an error string formatted as follows:
+// filename:line:column: message
+func (err Error) Error() string {
+ return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg)
+}
+
+// An importer resolves import paths to Packages.
+// See go/importer for existing implementations.
+type Importer interface {
+ // Import returns the imported package for the given import
+ // path, or an error if the package couldn't be imported.
+ // Import is responsible for returning the same package for
+ // matching import paths.
+ Import(path string) (*Package, error)
+}
+
+// A Config specifies the configuration for type checking.
+// The zero value for Config is a ready-to-use default configuration.
+type Config struct {
+ // If IgnoreFuncBodies is set, function bodies are not
+ // type-checked.
+ IgnoreFuncBodies bool
+
+ // If FakeImportC is set, `import "C"` (for packages requiring Cgo)
+ // declares an empty "C" package and errors are omitted for qualified
+ // identifiers referring to package C (which won't find an object).
+ // This feature is intended for the standard library cmd/api tool.
+ //
+ // Caution: Effects may be unpredictable due to follow-up errors.
+ // Do not use casually!
+ FakeImportC bool
+
+ // If Error != nil, it is called with each error found
+ // during type checking; err has dynamic type Error.
+ // Secondary errors (for instance, to enumerate all types
+ // involved in an invalid recursive type declaration) have
+ // error strings that start with a '\t' character.
+ // If Error == nil, type-checking stops with the first
+ // error found.
+ Error func(err error)
+
+ // Importer is called for each import declaration except when
+ // importing package "unsafe". An error is reported if an
+ // importer is needed but none was installed.
+ Importer Importer
+
+ // If Sizes != nil, it provides the sizing functions for package unsafe.
+ // Otherwise &StdSizes{WordSize: 8, MaxAlign: 8} is used instead.
+ Sizes Sizes
+
+ // If DisableUnusedImportCheck is set, packages are not checked
+ // for unused imports.
+ DisableUnusedImportCheck bool
+}
+
+// Info holds result type information for a type-checked package.
+// Only the information for which a map is provided is collected.
+// If the package has type errors, the collected information may
+// be incomplete.
+type Info struct {
+ // Types maps expressions to their types, and for constant
+ // expressions, their values. Invalid expressions are omitted.
+ //
+ // For (possibly parenthesized) identifiers denoting built-in
+ // functions, the recorded signatures are call-site specific:
+ // if the call result is not a constant, the recorded type is
+ // an argument-specific signature. Otherwise, the recorded type
+ // is invalid.
+ //
+ // Identifiers on the lhs of declarations (i.e., the identifiers
+ // which are being declared) are collected in the Defs map.
+ // Identifiers denoting packages are collected in the Uses maps.
+ Types map[ast.Expr]TypeAndValue
+
+ // Defs maps identifiers to the objects they define (including
+ // package names, dots "." of dot-imports, and blank "_" identifiers).
+ // For identifiers that do not denote objects (e.g., the package name
+ // in package clauses, or symbolic variables t in t := x.(type) of
+ // type switch headers), the corresponding objects are nil.
+ //
+ // For an anonymous field, Defs returns the field *Var it defines.
+ //
+ // Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
+ Defs map[*ast.Ident]Object
+
+ // Uses maps identifiers to the objects they denote.
+ //
+ // For an anonymous field, Uses returns the *TypeName it denotes.
+ //
+ // Invariant: Uses[id].Pos() != id.Pos()
+ Uses map[*ast.Ident]Object
+
+ // Implicits maps nodes to their implicitly declared objects, if any.
+ // The following node and object types may appear:
+ //
+ // node declared object
+ //
+ // *ast.ImportSpec *PkgName for dot-imports and imports without renames
+ // *ast.CaseClause type-specific *Var for each type switch case clause (incl. default)
+ // *ast.Field anonymous struct field or parameter *Var
+ //
+ Implicits map[ast.Node]Object
+
+ // Selections maps selector expressions (excluding qualified identifiers)
+ // to their corresponding selections.
+ Selections map[*ast.SelectorExpr]*Selection
+
+ // Scopes maps ast.Nodes to the scopes they define. Package scopes are not
+ // associated with a specific node but with all files belonging to a package.
+ // Thus, the package scope can be found in the type-checked Package object.
+ // Scopes nest, with the Universe scope being the outermost scope, enclosing
+ // the package scope, which contains (one or more) files scopes, which enclose
+ // function scopes which in turn enclose statement and function literal scopes.
+ // Note that even though package-level functions are declared in the package
+ // scope, the function scopes are embedded in the file scope of the file
+ // containing the function declaration.
+ //
+ // The following node types may appear in Scopes:
+ //
+ // *ast.File
+ // *ast.FuncType
+ // *ast.BlockStmt
+ // *ast.IfStmt
+ // *ast.SwitchStmt
+ // *ast.TypeSwitchStmt
+ // *ast.CaseClause
+ // *ast.CommClause
+ // *ast.ForStmt
+ // *ast.RangeStmt
+ //
+ Scopes map[ast.Node]*Scope
+
+ // InitOrder is the list of package-level initializers in the order in which
+ // they must be executed. Initializers referring to variables related by an
+ // initialization dependency appear in topological order, the others appear
+ // in source order. Variables without an initialization expression do not
+ // appear in this list.
+ InitOrder []*Initializer
+}
+
+// TypeOf returns the type of expression e, or nil if not found.
+// Precondition: the Types, Uses and Defs maps are populated.
+//
+func (info *Info) TypeOf(e ast.Expr) Type {
+ if t, ok := info.Types[e]; ok {
+ return t.Type
+ }
+ if id, _ := e.(*ast.Ident); id != nil {
+ if obj := info.ObjectOf(id); obj != nil {
+ return obj.Type()
+ }
+ }
+ return nil
+}
+
+// ObjectOf returns the object denoted by the specified id,
+// or nil if not found.
+//
+// If id is an anonymous struct field, ObjectOf returns the field (*Var)
+// it uses, not the type (*TypeName) it defines.
+//
+// Precondition: the Uses and Defs maps are populated.
+//
+func (info *Info) ObjectOf(id *ast.Ident) Object {
+ if obj, _ := info.Defs[id]; obj != nil {
+ return obj
+ }
+ return info.Uses[id]
+}
+
+// TypeAndValue reports the type and value (for constants)
+// of the corresponding expression.
+type TypeAndValue struct {
+ mode operandMode
+ Type Type
+ Value constant.Value
+}
+
+// TODO(gri) Consider eliminating the IsVoid predicate. Instead, report
+// "void" values as regular values but with the empty tuple type.
+
+// IsVoid reports whether the corresponding expression
+// is a function call without results.
+func (tv TypeAndValue) IsVoid() bool {
+ return tv.mode == novalue
+}
+
+// IsType reports whether the corresponding expression specifies a type.
+func (tv TypeAndValue) IsType() bool {
+ return tv.mode == typexpr
+}
+
+// IsBuiltin reports whether the corresponding expression denotes
+// a (possibly parenthesized) built-in function.
+func (tv TypeAndValue) IsBuiltin() bool {
+ return tv.mode == builtin
+}
+
+// IsValue reports whether the corresponding expression is a value.
+// Builtins are not considered values. Constant values have a non-
+// nil Value.
+func (tv TypeAndValue) IsValue() bool {
+ switch tv.mode {
+ case constant_, variable, mapindex, value, commaok:
+ return true
+ }
+ return false
+}
+
+// IsNil reports whether the corresponding expression denotes the
+// predeclared value nil.
+func (tv TypeAndValue) IsNil() bool {
+ return tv.mode == value && tv.Type == Typ[UntypedNil]
+}
+
+// Addressable reports whether the corresponding expression
+// is addressable (https://golang.org/ref/spec#Address_operators).
+func (tv TypeAndValue) Addressable() bool {
+ return tv.mode == variable
+}
+
+// Assignable reports whether the corresponding expression
+// is assignable to (provided a value of the right type).
+func (tv TypeAndValue) Assignable() bool {
+ return tv.mode == variable || tv.mode == mapindex
+}
+
+// HasOk reports whether the corresponding expression may be
+// used on the lhs of a comma-ok assignment.
+func (tv TypeAndValue) HasOk() bool {
+ return tv.mode == commaok || tv.mode == mapindex
+}
+
+// An Initializer describes a package-level variable, or a list of variables in case
+// of a multi-valued initialization expression, and the corresponding initialization
+// expression.
+type Initializer struct {
+ Lhs []*Var // var Lhs = Rhs
+ Rhs ast.Expr
+}
+
+func (init *Initializer) String() string {
+ var buf bytes.Buffer
+ for i, lhs := range init.Lhs {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ buf.WriteString(lhs.Name())
+ }
+ buf.WriteString(" = ")
+ WriteExpr(&buf, init.Rhs)
+ return buf.String()
+}
+
+// Check type-checks a package and returns the resulting package object,
+// the first error if any, and if info != nil, additional type information.
+// The package is marked as complete if no errors occurred, otherwise it is
+// incomplete. See Config.Error for controlling behavior in the presence of
+// errors.
+//
+// The package is specified by a list of *ast.Files and corresponding
+// file set, and the package path the package is identified with.
+// The clean path must not be empty or dot (".").
+func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) {
+ pkg := NewPackage(path, "")
+ return pkg, NewChecker(conf, fset, pkg, info).Files(files)
+}
+
+// AssertableTo reports whether a value of type V can be asserted to have type T.
+func AssertableTo(V *Interface, T Type) bool {
+ m, _ := assertableTo(V, T)
+ return m == nil
+}
+
+// AssignableTo reports whether a value of type V is assignable to a variable of type T.
+func AssignableTo(V, T Type) bool {
+ x := operand{mode: value, typ: V}
+ return x.assignableTo(nil, T) // config not needed for non-constant x
+}
+
+// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
+func ConvertibleTo(V, T Type) bool {
+ x := operand{mode: value, typ: V}
+ return x.convertibleTo(nil, T) // config not needed for non-constant x
+}
+
+// Implements reports whether type V implements interface T.
+func Implements(V Type, T *Interface) bool {
+ f, _ := MissingMethod(V, T, true)
+ return f == nil
+}
diff --git a/libgo/go/go/types/api_test.go b/libgo/go/go/types/api_test.go
new file mode 100644
index 00000000000..eeda0d847c3
--- /dev/null
+++ b/libgo/go/go/types/api_test.go
@@ -0,0 +1,1044 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types_test
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "internal/testenv"
+ "reflect"
+ "regexp"
+ "strings"
+ "testing"
+
+ . "go/types"
+)
+
+func pkgFor(path, source string, info *Info) (*Package, error) {
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, path, source, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ conf := Config{Importer: importer.Default()}
+ return conf.Check(f.Name.Name, fset, []*ast.File{f}, info)
+}
+
+func mustTypecheck(t *testing.T, path, source string, info *Info) string {
+ pkg, err := pkgFor(path, source, info)
+ if err != nil {
+ name := path
+ if pkg != nil {
+ name = "package " + pkg.Name()
+ }
+ t.Fatalf("%s: didn't type-check (%s)", name, err)
+ }
+ return pkg.Name()
+}
+
+func TestValuesInfo(t *testing.T) {
+ var tests = []struct {
+ src string
+ expr string // constant expression
+ typ string // constant type
+ val string // constant value
+ }{
+ {`package a0; const _ = false`, `false`, `untyped bool`, `false`},
+ {`package a1; const _ = 0`, `0`, `untyped int`, `0`},
+ {`package a2; const _ = 'A'`, `'A'`, `untyped rune`, `65`},
+ {`package a3; const _ = 0.`, `0.`, `untyped float`, `0`},
+ {`package a4; const _ = 0i`, `0i`, `untyped complex`, `0`},
+ {`package a5; const _ = "foo"`, `"foo"`, `untyped string`, `"foo"`},
+
+ {`package b0; var _ = false`, `false`, `bool`, `false`},
+ {`package b1; var _ = 0`, `0`, `int`, `0`},
+ {`package b2; var _ = 'A'`, `'A'`, `rune`, `65`},
+ {`package b3; var _ = 0.`, `0.`, `float64`, `0`},
+ {`package b4; var _ = 0i`, `0i`, `complex128`, `0`},
+ {`package b5; var _ = "foo"`, `"foo"`, `string`, `"foo"`},
+
+ {`package c0a; var _ = bool(false)`, `false`, `bool`, `false`},
+ {`package c0b; var _ = bool(false)`, `bool(false)`, `bool`, `false`},
+ {`package c0c; type T bool; var _ = T(false)`, `T(false)`, `c0c.T`, `false`},
+
+ {`package c1a; var _ = int(0)`, `0`, `int`, `0`},
+ {`package c1b; var _ = int(0)`, `int(0)`, `int`, `0`},
+ {`package c1c; type T int; var _ = T(0)`, `T(0)`, `c1c.T`, `0`},
+
+ {`package c2a; var _ = rune('A')`, `'A'`, `rune`, `65`},
+ {`package c2b; var _ = rune('A')`, `rune('A')`, `rune`, `65`},
+ {`package c2c; type T rune; var _ = T('A')`, `T('A')`, `c2c.T`, `65`},
+
+ {`package c3a; var _ = float32(0.)`, `0.`, `float32`, `0`},
+ {`package c3b; var _ = float32(0.)`, `float32(0.)`, `float32`, `0`},
+ {`package c3c; type T float32; var _ = T(0.)`, `T(0.)`, `c3c.T`, `0`},
+
+ {`package c4a; var _ = complex64(0i)`, `0i`, `complex64`, `0`},
+ {`package c4b; var _ = complex64(0i)`, `complex64(0i)`, `complex64`, `0`},
+ {`package c4c; type T complex64; var _ = T(0i)`, `T(0i)`, `c4c.T`, `0`},
+
+ {`package c5a; var _ = string("foo")`, `"foo"`, `string`, `"foo"`},
+ {`package c5b; var _ = string("foo")`, `string("foo")`, `string`, `"foo"`},
+ {`package c5c; type T string; var _ = T("foo")`, `T("foo")`, `c5c.T`, `"foo"`},
+
+ {`package d0; var _ = []byte("foo")`, `"foo"`, `string`, `"foo"`},
+ {`package d1; var _ = []byte(string("foo"))`, `"foo"`, `string`, `"foo"`},
+ {`package d2; var _ = []byte(string("foo"))`, `string("foo")`, `string`, `"foo"`},
+ {`package d3; type T []byte; var _ = T("foo")`, `"foo"`, `string`, `"foo"`},
+
+ {`package e0; const _ = float32( 1e-200)`, `float32(1e-200)`, `float32`, `0`},
+ {`package e1; const _ = float32(-1e-200)`, `float32(-1e-200)`, `float32`, `0`},
+ {`package e2; const _ = float64( 1e-2000)`, `float64(1e-2000)`, `float64`, `0`},
+ {`package e3; const _ = float64(-1e-2000)`, `float64(-1e-2000)`, `float64`, `0`},
+ {`package e4; const _ = complex64( 1e-200)`, `complex64(1e-200)`, `complex64`, `0`},
+ {`package e5; const _ = complex64(-1e-200)`, `complex64(-1e-200)`, `complex64`, `0`},
+ {`package e6; const _ = complex128( 1e-2000)`, `complex128(1e-2000)`, `complex128`, `0`},
+ {`package e7; const _ = complex128(-1e-2000)`, `complex128(-1e-2000)`, `complex128`, `0`},
+
+ {`package f0 ; var _ float32 = 1e-200`, `1e-200`, `float32`, `0`},
+ {`package f1 ; var _ float32 = -1e-200`, `-1e-200`, `float32`, `0`},
+ {`package f2a; var _ float64 = 1e-2000`, `1e-2000`, `float64`, `0`},
+ {`package f3a; var _ float64 = -1e-2000`, `-1e-2000`, `float64`, `0`},
+ {`package f2b; var _ = 1e-2000`, `1e-2000`, `float64`, `0`},
+ {`package f3b; var _ = -1e-2000`, `-1e-2000`, `float64`, `0`},
+ {`package f4 ; var _ complex64 = 1e-200 `, `1e-200`, `complex64`, `0`},
+ {`package f5 ; var _ complex64 = -1e-200 `, `-1e-200`, `complex64`, `0`},
+ {`package f6a; var _ complex128 = 1e-2000i`, `1e-2000i`, `complex128`, `0`},
+ {`package f7a; var _ complex128 = -1e-2000i`, `-1e-2000i`, `complex128`, `0`},
+ {`package f6b; var _ = 1e-2000i`, `1e-2000i`, `complex128`, `0`},
+ {`package f7b; var _ = -1e-2000i`, `-1e-2000i`, `complex128`, `0`},
+ }
+
+ for _, test := range tests {
+ info := Info{
+ Types: make(map[ast.Expr]TypeAndValue),
+ }
+ name := mustTypecheck(t, "ValuesInfo", test.src, &info)
+
+ // look for constant expression
+ var expr ast.Expr
+ for e := range info.Types {
+ if ExprString(e) == test.expr {
+ expr = e
+ break
+ }
+ }
+ if expr == nil {
+ t.Errorf("package %s: no expression found for %s", name, test.expr)
+ continue
+ }
+ tv := info.Types[expr]
+
+ // check that type is correct
+ if got := tv.Type.String(); got != test.typ {
+ t.Errorf("package %s: got type %s; want %s", name, got, test.typ)
+ continue
+ }
+
+ // check that value is correct
+ if got := tv.Value.String(); got != test.val {
+ t.Errorf("package %s: got value %s; want %s", name, got, test.val)
+ }
+ }
+}
+
+func TestTypesInfo(t *testing.T) {
+ var tests = []struct {
+ src string
+ expr string // expression
+ typ string // value type
+ }{
+ // single-valued expressions of untyped constants
+ {`package b0; var x interface{} = false`, `false`, `bool`},
+ {`package b1; var x interface{} = 0`, `0`, `int`},
+ {`package b2; var x interface{} = 0.`, `0.`, `float64`},
+ {`package b3; var x interface{} = 0i`, `0i`, `complex128`},
+ {`package b4; var x interface{} = "foo"`, `"foo"`, `string`},
+
+ // comma-ok expressions
+ {`package p0; var x interface{}; var _, _ = x.(int)`,
+ `x.(int)`,
+ `(int, bool)`,
+ },
+ {`package p1; var x interface{}; func _() { _, _ = x.(int) }`,
+ `x.(int)`,
+ `(int, bool)`,
+ },
+ // TODO(gri): uncomment if we accept issue 8189.
+ // {`package p2; type mybool bool; var m map[string]complex128; var b mybool; func _() { _, b = m["foo"] }`,
+ // `m["foo"]`,
+ // `(complex128, p2.mybool)`,
+ // },
+ // TODO(gri): remove if we accept issue 8189.
+ {`package p2; var m map[string]complex128; var b bool; func _() { _, b = m["foo"] }`,
+ `m["foo"]`,
+ `(complex128, bool)`,
+ },
+ {`package p3; var c chan string; var _, _ = <-c`,
+ `<-c`,
+ `(string, bool)`,
+ },
+
+ // issue 6796
+ {`package issue6796_a; var x interface{}; var _, _ = (x.(int))`,
+ `x.(int)`,
+ `(int, bool)`,
+ },
+ {`package issue6796_b; var c chan string; var _, _ = (<-c)`,
+ `(<-c)`,
+ `(string, bool)`,
+ },
+ {`package issue6796_c; var c chan string; var _, _ = (<-c)`,
+ `<-c`,
+ `(string, bool)`,
+ },
+ {`package issue6796_d; var c chan string; var _, _ = ((<-c))`,
+ `(<-c)`,
+ `(string, bool)`,
+ },
+ {`package issue6796_e; func f(c chan string) { _, _ = ((<-c)) }`,
+ `(<-c)`,
+ `(string, bool)`,
+ },
+
+ // issue 7060
+ {`package issue7060_a; var ( m map[int]string; x, ok = m[0] )`,
+ `m[0]`,
+ `(string, bool)`,
+ },
+ {`package issue7060_b; var ( m map[int]string; x, ok interface{} = m[0] )`,
+ `m[0]`,
+ `(string, bool)`,
+ },
+ {`package issue7060_c; func f(x interface{}, ok bool, m map[int]string) { x, ok = m[0] }`,
+ `m[0]`,
+ `(string, bool)`,
+ },
+ {`package issue7060_d; var ( ch chan string; x, ok = <-ch )`,
+ `<-ch`,
+ `(string, bool)`,
+ },
+ {`package issue7060_e; var ( ch chan string; x, ok interface{} = <-ch )`,
+ `<-ch`,
+ `(string, bool)`,
+ },
+ {`package issue7060_f; func f(x interface{}, ok bool, ch chan string) { x, ok = <-ch }`,
+ `<-ch`,
+ `(string, bool)`,
+ },
+ }
+
+ for _, test := range tests {
+ info := Info{Types: make(map[ast.Expr]TypeAndValue)}
+ name := mustTypecheck(t, "TypesInfo", test.src, &info)
+
+ // look for expression type
+ var typ Type
+ for e, tv := range info.Types {
+ if ExprString(e) == test.expr {
+ typ = tv.Type
+ break
+ }
+ }
+ if typ == nil {
+ t.Errorf("package %s: no type found for %s", name, test.expr)
+ continue
+ }
+
+ // check that type is correct
+ if got := typ.String(); got != test.typ {
+ t.Errorf("package %s: got %s; want %s", name, got, test.typ)
+ }
+ }
+}
+
+func predString(tv TypeAndValue) string {
+ var buf bytes.Buffer
+ pred := func(b bool, s string) {
+ if b {
+ if buf.Len() > 0 {
+ buf.WriteString(", ")
+ }
+ buf.WriteString(s)
+ }
+ }
+
+ pred(tv.IsVoid(), "void")
+ pred(tv.IsType(), "type")
+ pred(tv.IsBuiltin(), "builtin")
+ pred(tv.IsValue() && tv.Value != nil, "const")
+ pred(tv.IsValue() && tv.Value == nil, "value")
+ pred(tv.IsNil(), "nil")
+ pred(tv.Addressable(), "addressable")
+ pred(tv.Assignable(), "assignable")
+ pred(tv.HasOk(), "hasOk")
+
+ if buf.Len() == 0 {
+ return "invalid"
+ }
+ return buf.String()
+}
+
+func TestPredicatesInfo(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ var tests = []struct {
+ src string
+ expr string
+ pred string
+ }{
+ // void
+ {`package n0; func f() { f() }`, `f()`, `void`},
+
+ // types
+ {`package t0; type _ int`, `int`, `type`},
+ {`package t1; type _ []int`, `[]int`, `type`},
+ {`package t2; type _ func()`, `func()`, `type`},
+
+ // built-ins
+ {`package b0; var _ = len("")`, `len`, `builtin`},
+ {`package b1; var _ = (len)("")`, `(len)`, `builtin`},
+
+ // constants
+ {`package c0; var _ = 42`, `42`, `const`},
+ {`package c1; var _ = "foo" + "bar"`, `"foo" + "bar"`, `const`},
+ {`package c2; const (i = 1i; _ = i)`, `i`, `const`},
+
+ // values
+ {`package v0; var (a, b int; _ = a + b)`, `a + b`, `value`},
+ {`package v1; var _ = &[]int{1}`, `([]int literal)`, `value`},
+ {`package v2; var _ = func(){}`, `(func() literal)`, `value`},
+ {`package v4; func f() { _ = f }`, `f`, `value`},
+ {`package v3; var _ *int = nil`, `nil`, `value, nil`},
+ {`package v3; var _ *int = (nil)`, `(nil)`, `value, nil`},
+
+ // addressable (and thus assignable) operands
+ {`package a0; var (x int; _ = x)`, `x`, `value, addressable, assignable`},
+ {`package a1; var (p *int; _ = *p)`, `*p`, `value, addressable, assignable`},
+ {`package a2; var (s []int; _ = s[0])`, `s[0]`, `value, addressable, assignable`},
+ {`package a3; var (s struct{f int}; _ = s.f)`, `s.f`, `value, addressable, assignable`},
+ {`package a4; var (a [10]int; _ = a[0])`, `a[0]`, `value, addressable, assignable`},
+ {`package a5; func _(x int) { _ = x }`, `x`, `value, addressable, assignable`},
+ {`package a6; func _()(x int) { _ = x; return }`, `x`, `value, addressable, assignable`},
+ {`package a7; type T int; func (x T) _() { _ = x }`, `x`, `value, addressable, assignable`},
+ // composite literals are not addressable
+
+ // assignable but not addressable values
+ {`package s0; var (m map[int]int; _ = m[0])`, `m[0]`, `value, assignable, hasOk`},
+ {`package s1; var (m map[int]int; _, _ = m[0])`, `m[0]`, `value, assignable, hasOk`},
+
+ // hasOk expressions
+ {`package k0; var (ch chan int; _ = <-ch)`, `<-ch`, `value, hasOk`},
+ {`package k1; var (ch chan int; _, _ = <-ch)`, `<-ch`, `value, hasOk`},
+
+ // missing entries
+ // - package names are collected in the Uses map
+ // - identifiers being declared are collected in the Defs map
+ {`package m0; import "os"; func _() { _ = os.Stdout }`, `os`, `<missing>`},
+ {`package m1; import p "os"; func _() { _ = p.Stdout }`, `p`, `<missing>`},
+ {`package m2; const c = 0`, `c`, `<missing>`},
+ {`package m3; type T int`, `T`, `<missing>`},
+ {`package m4; var v int`, `v`, `<missing>`},
+ {`package m5; func f() {}`, `f`, `<missing>`},
+ {`package m6; func _(x int) {}`, `x`, `<missing>`},
+ {`package m6; func _()(x int) { return }`, `x`, `<missing>`},
+ {`package m6; type T int; func (x T) _() {}`, `x`, `<missing>`},
+ }
+
+ for _, test := range tests {
+ info := Info{Types: make(map[ast.Expr]TypeAndValue)}
+ name := mustTypecheck(t, "PredicatesInfo", test.src, &info)
+
+ // look for expression predicates
+ got := "<missing>"
+ for e, tv := range info.Types {
+ //println(name, ExprString(e))
+ if ExprString(e) == test.expr {
+ got = predString(tv)
+ break
+ }
+ }
+
+ if got != test.pred {
+ t.Errorf("package %s: got %s; want %s", name, got, test.pred)
+ }
+ }
+}
+
+func TestScopesInfo(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ var tests = []struct {
+ src string
+ scopes []string // list of scope descriptors of the form kind:varlist
+ }{
+ {`package p0`, []string{
+ "file:",
+ }},
+ {`package p1; import ( "fmt"; m "math"; _ "os" ); var ( _ = fmt.Println; _ = m.Pi )`, []string{
+ "file:fmt m",
+ }},
+ {`package p2; func _() {}`, []string{
+ "file:", "func:",
+ }},
+ {`package p3; func _(x, y int) {}`, []string{
+ "file:", "func:x y",
+ }},
+ {`package p4; func _(x, y int) { x, z := 1, 2; _ = z }`, []string{
+ "file:", "func:x y z", // redeclaration of x
+ }},
+ {`package p5; func _(x, y int) (u, _ int) { return }`, []string{
+ "file:", "func:u x y",
+ }},
+ {`package p6; func _() { { var x int; _ = x } }`, []string{
+ "file:", "func:", "block:x",
+ }},
+ {`package p7; func _() { if true {} }`, []string{
+ "file:", "func:", "if:", "block:",
+ }},
+ {`package p8; func _() { if x := 0; x < 0 { y := x; _ = y } }`, []string{
+ "file:", "func:", "if:x", "block:y",
+ }},
+ {`package p9; func _() { switch x := 0; x {} }`, []string{
+ "file:", "func:", "switch:x",
+ }},
+ {`package p10; func _() { switch x := 0; x { case 1: y := x; _ = y; default: }}`, []string{
+ "file:", "func:", "switch:x", "case:y", "case:",
+ }},
+ {`package p11; func _(t interface{}) { switch t.(type) {} }`, []string{
+ "file:", "func:t", "type switch:",
+ }},
+ {`package p12; func _(t interface{}) { switch t := t; t.(type) {} }`, []string{
+ "file:", "func:t", "type switch:t",
+ }},
+ {`package p13; func _(t interface{}) { switch x := t.(type) { case int: _ = x } }`, []string{
+ "file:", "func:t", "type switch:", "case:x", // x implicitly declared
+ }},
+ {`package p14; func _() { select{} }`, []string{
+ "file:", "func:",
+ }},
+ {`package p15; func _(c chan int) { select{ case <-c: } }`, []string{
+ "file:", "func:c", "comm:",
+ }},
+ {`package p16; func _(c chan int) { select{ case i := <-c: x := i; _ = x} }`, []string{
+ "file:", "func:c", "comm:i x",
+ }},
+ {`package p17; func _() { for{} }`, []string{
+ "file:", "func:", "for:", "block:",
+ }},
+ {`package p18; func _(n int) { for i := 0; i < n; i++ { _ = i } }`, []string{
+ "file:", "func:n", "for:i", "block:",
+ }},
+ {`package p19; func _(a []int) { for i := range a { _ = i} }`, []string{
+ "file:", "func:a", "range:i", "block:",
+ }},
+ {`package p20; var s int; func _(a []int) { for i, x := range a { s += x; _ = i } }`, []string{
+ "file:", "func:a", "range:i x", "block:",
+ }},
+ }
+
+ for _, test := range tests {
+ info := Info{Scopes: make(map[ast.Node]*Scope)}
+ name := mustTypecheck(t, "ScopesInfo", test.src, &info)
+
+ // number of scopes must match
+ if len(info.Scopes) != len(test.scopes) {
+ t.Errorf("package %s: got %d scopes; want %d", name, len(info.Scopes), len(test.scopes))
+ }
+
+ // scope descriptions must match
+ for node, scope := range info.Scopes {
+ kind := "<unknown node kind>"
+ switch node.(type) {
+ case *ast.File:
+ kind = "file"
+ case *ast.FuncType:
+ kind = "func"
+ case *ast.BlockStmt:
+ kind = "block"
+ case *ast.IfStmt:
+ kind = "if"
+ case *ast.SwitchStmt:
+ kind = "switch"
+ case *ast.TypeSwitchStmt:
+ kind = "type switch"
+ case *ast.CaseClause:
+ kind = "case"
+ case *ast.CommClause:
+ kind = "comm"
+ case *ast.ForStmt:
+ kind = "for"
+ case *ast.RangeStmt:
+ kind = "range"
+ }
+
+ // look for matching scope description
+ desc := kind + ":" + strings.Join(scope.Names(), " ")
+ found := false
+ for _, d := range test.scopes {
+ if desc == d {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Errorf("package %s: no matching scope found for %s", name, desc)
+ }
+ }
+ }
+}
+
+func TestInitOrderInfo(t *testing.T) {
+ var tests = []struct {
+ src string
+ inits []string
+ }{
+ {`package p0; var (x = 1; y = x)`, []string{
+ "x = 1", "y = x",
+ }},
+ {`package p1; var (a = 1; b = 2; c = 3)`, []string{
+ "a = 1", "b = 2", "c = 3",
+ }},
+ {`package p2; var (a, b, c = 1, 2, 3)`, []string{
+ "a = 1", "b = 2", "c = 3",
+ }},
+ {`package p3; var _ = f(); func f() int { return 1 }`, []string{
+ "_ = f()", // blank var
+ }},
+ {`package p4; var (a = 0; x = y; y = z; z = 0)`, []string{
+ "a = 0", "z = 0", "y = z", "x = y",
+ }},
+ {`package p5; var (a, _ = m[0]; m map[int]string)`, []string{
+ "a, _ = m[0]", // blank var
+ }},
+ {`package p6; var a, b = f(); func f() (_, _ int) { return z, z }; var z = 0`, []string{
+ "z = 0", "a, b = f()",
+ }},
+ {`package p7; var (a = func() int { return b }(); b = 1)`, []string{
+ "b = 1", "a = (func() int literal)()",
+ }},
+ {`package p8; var (a, b = func() (_, _ int) { return c, c }(); c = 1)`, []string{
+ "c = 1", "a, b = (func() (_, _ int) literal)()",
+ }},
+ {`package p9; type T struct{}; func (T) m() int { _ = y; return 0 }; var x, y = T.m, 1`, []string{
+ "y = 1", "x = T.m",
+ }},
+ {`package p10; var (d = c + b; a = 0; b = 0; c = 0)`, []string{
+ "a = 0", "b = 0", "c = 0", "d = c + b",
+ }},
+ {`package p11; var (a = e + c; b = d + c; c = 0; d = 0; e = 0)`, []string{
+ "c = 0", "d = 0", "b = d + c", "e = 0", "a = e + c",
+ }},
+ // emit an initializer for n:1 initializations only once (not for each node
+ // on the lhs which may appear in different order in the dependency graph)
+ {`package p12; var (a = x; b = 0; x, y = m[0]; m map[int]int)`, []string{
+ "b = 0", "x, y = m[0]", "a = x",
+ }},
+ // test case from spec section on package initialization
+ {`package p12
+
+ var (
+ a = c + b
+ b = f()
+ c = f()
+ d = 3
+ )
+
+ func f() int {
+ d++
+ return d
+ }`, []string{
+ "d = 3", "b = f()", "c = f()", "a = c + b",
+ }},
+ // test case for issue 7131
+ {`package main
+
+ var counter int
+ func next() int { counter++; return counter }
+
+ var _ = makeOrder()
+ func makeOrder() []int { return []int{f, b, d, e, c, a} }
+
+ var a = next()
+ var b, c = next(), next()
+ var d, e, f = next(), next(), next()
+ `, []string{
+ "a = next()", "b = next()", "c = next()", "d = next()", "e = next()", "f = next()", "_ = makeOrder()",
+ }},
+ }
+
+ for _, test := range tests {
+ info := Info{}
+ name := mustTypecheck(t, "InitOrderInfo", test.src, &info)
+
+ // number of initializers must match
+ if len(info.InitOrder) != len(test.inits) {
+ t.Errorf("package %s: got %d initializers; want %d", name, len(info.InitOrder), len(test.inits))
+ continue
+ }
+
+ // initializers must match
+ for i, want := range test.inits {
+ got := info.InitOrder[i].String()
+ if got != want {
+ t.Errorf("package %s, init %d: got %s; want %s", name, i, got, want)
+ continue
+ }
+ }
+ }
+}
+
+func TestMultiFileInitOrder(t *testing.T) {
+ fset := token.NewFileSet()
+ mustParse := func(src string) *ast.File {
+ f, err := parser.ParseFile(fset, "main", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return f
+ }
+
+ fileA := mustParse(`package main; var a = 1`)
+ fileB := mustParse(`package main; var b = 2`)
+
+ // The initialization order must not depend on the parse
+ // order of the files, only on the presentation order to
+ // the type-checker.
+ for _, test := range []struct {
+ files []*ast.File
+ want string
+ }{
+ {[]*ast.File{fileA, fileB}, "[a = 1 b = 2]"},
+ {[]*ast.File{fileB, fileA}, "[b = 2 a = 1]"},
+ } {
+ var info Info
+ if _, err := new(Config).Check("main", fset, test.files, &info); err != nil {
+ t.Fatal(err)
+ }
+ if got := fmt.Sprint(info.InitOrder); got != test.want {
+ t.Fatalf("got %s; want %s", got, test.want)
+ }
+ }
+}
+
+func TestFiles(t *testing.T) {
+ var sources = []string{
+ "package p; type T struct{}; func (T) m1() {}",
+ "package p; func (T) m2() {}; var x interface{ m1(); m2() } = T{}",
+ "package p; func (T) m3() {}; var y interface{ m1(); m2(); m3() } = T{}",
+ "package p",
+ }
+
+ var conf Config
+ fset := token.NewFileSet()
+ pkg := NewPackage("p", "p")
+ var info Info
+ check := NewChecker(&conf, fset, pkg, &info)
+
+ for i, src := range sources {
+ filename := fmt.Sprintf("sources%d", i)
+ f, err := parser.ParseFile(fset, filename, src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := check.Files([]*ast.File{f}); err != nil {
+ t.Error(err)
+ }
+ }
+
+ // check InitOrder is [x y]
+ var vars []string
+ for _, init := range info.InitOrder {
+ for _, v := range init.Lhs {
+ vars = append(vars, v.Name())
+ }
+ }
+ if got, want := fmt.Sprint(vars), "[x y]"; got != want {
+ t.Errorf("InitOrder == %s, want %s", got, want)
+ }
+}
+
+type testImporter map[string]*Package
+
+func (m testImporter) Import(path string) (*Package, error) {
+ if pkg := m[path]; pkg != nil {
+ return pkg, nil
+ }
+ return nil, fmt.Errorf("package %q not found", path)
+}
+
+func TestSelection(t *testing.T) {
+ selections := make(map[*ast.SelectorExpr]*Selection)
+
+ fset := token.NewFileSet()
+ imports := make(testImporter)
+ conf := Config{Importer: imports}
+ makePkg := func(path, src string) {
+ f, err := parser.ParseFile(fset, path+".go", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ pkg, err := conf.Check(path, fset, []*ast.File{f}, &Info{Selections: selections})
+ if err != nil {
+ t.Fatal(err)
+ }
+ imports[path] = pkg
+ }
+
+ const libSrc = `
+package lib
+type T float64
+const C T = 3
+var V T
+func F() {}
+func (T) M() {}
+`
+ const mainSrc = `
+package main
+import "lib"
+
+type A struct {
+ *B
+ C
+}
+
+type B struct {
+ b int
+}
+
+func (B) f(int)
+
+type C struct {
+ c int
+}
+
+func (C) g()
+func (*C) h()
+
+func main() {
+ // qualified identifiers
+ var _ lib.T
+ _ = lib.C
+ _ = lib.F
+ _ = lib.V
+ _ = lib.T.M
+
+ // fields
+ _ = A{}.B
+ _ = new(A).B
+
+ _ = A{}.C
+ _ = new(A).C
+
+ _ = A{}.b
+ _ = new(A).b
+
+ _ = A{}.c
+ _ = new(A).c
+
+ // methods
+ _ = A{}.f
+ _ = new(A).f
+ _ = A{}.g
+ _ = new(A).g
+ _ = new(A).h
+
+ _ = B{}.f
+ _ = new(B).f
+
+ _ = C{}.g
+ _ = new(C).g
+ _ = new(C).h
+
+ // method expressions
+ _ = A.f
+ _ = (*A).f
+ _ = B.f
+ _ = (*B).f
+}`
+
+ wantOut := map[string][2]string{
+ "lib.T.M": {"method expr (lib.T) M(lib.T)", ".[0]"},
+
+ "A{}.B": {"field (main.A) B *main.B", ".[0]"},
+ "new(A).B": {"field (*main.A) B *main.B", "->[0]"},
+ "A{}.C": {"field (main.A) C main.C", ".[1]"},
+ "new(A).C": {"field (*main.A) C main.C", "->[1]"},
+ "A{}.b": {"field (main.A) b int", "->[0 0]"},
+ "new(A).b": {"field (*main.A) b int", "->[0 0]"},
+ "A{}.c": {"field (main.A) c int", ".[1 0]"},
+ "new(A).c": {"field (*main.A) c int", "->[1 0]"},
+
+ "A{}.f": {"method (main.A) f(int)", "->[0 0]"},
+ "new(A).f": {"method (*main.A) f(int)", "->[0 0]"},
+ "A{}.g": {"method (main.A) g()", ".[1 0]"},
+ "new(A).g": {"method (*main.A) g()", "->[1 0]"},
+ "new(A).h": {"method (*main.A) h()", "->[1 1]"}, // TODO(gri) should this report .[1 1] ?
+ "B{}.f": {"method (main.B) f(int)", ".[0]"},
+ "new(B).f": {"method (*main.B) f(int)", "->[0]"},
+ "C{}.g": {"method (main.C) g()", ".[0]"},
+ "new(C).g": {"method (*main.C) g()", "->[0]"},
+ "new(C).h": {"method (*main.C) h()", "->[1]"}, // TODO(gri) should this report .[1] ?
+
+ "A.f": {"method expr (main.A) f(main.A, int)", "->[0 0]"},
+ "(*A).f": {"method expr (*main.A) f(*main.A, int)", "->[0 0]"},
+ "B.f": {"method expr (main.B) f(main.B, int)", ".[0]"},
+ "(*B).f": {"method expr (*main.B) f(*main.B, int)", "->[0]"},
+ }
+
+ makePkg("lib", libSrc)
+ makePkg("main", mainSrc)
+
+ for e, sel := range selections {
+ sel.String() // assertion: must not panic
+
+ start := fset.Position(e.Pos()).Offset
+ end := fset.Position(e.End()).Offset
+ syntax := mainSrc[start:end] // (all SelectorExprs are in main, not lib)
+
+ direct := "."
+ if sel.Indirect() {
+ direct = "->"
+ }
+ got := [2]string{
+ sel.String(),
+ fmt.Sprintf("%s%v", direct, sel.Index()),
+ }
+ want := wantOut[syntax]
+ if want != got {
+ t.Errorf("%s: got %q; want %q", syntax, got, want)
+ }
+ delete(wantOut, syntax)
+
+ // We must explicitly assert properties of the
+ // Signature's receiver since it doesn't participate
+ // in Identical() or String().
+ sig, _ := sel.Type().(*Signature)
+ if sel.Kind() == MethodVal {
+ got := sig.Recv().Type()
+ want := sel.Recv()
+ if !Identical(got, want) {
+ t.Errorf("%s: Recv() = %s, want %s", syntax, got, want)
+ }
+ } else if sig != nil && sig.Recv() != nil {
+ t.Errorf("%s: signature has receiver %s", sig, sig.Recv().Type())
+ }
+ }
+ // Assert that all wantOut entries were used exactly once.
+ for syntax := range wantOut {
+ t.Errorf("no ast.Selection found with syntax %q", syntax)
+ }
+}
+
+func TestIssue8518(t *testing.T) {
+ fset := token.NewFileSet()
+ imports := make(testImporter)
+ conf := Config{
+ Error: func(err error) { t.Log(err) }, // don't exit after first error
+ Importer: imports,
+ }
+ makePkg := func(path, src string) {
+ f, err := parser.ParseFile(fset, path, src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ pkg, _ := conf.Check(path, fset, []*ast.File{f}, nil) // errors logged via conf.Error
+ imports[path] = pkg
+ }
+
+ const libSrc = `
+package a
+import "missing"
+const C1 = foo
+const C2 = missing.C
+`
+
+ const mainSrc = `
+package main
+import "a"
+var _ = a.C1
+var _ = a.C2
+`
+
+ makePkg("a", libSrc)
+ makePkg("main", mainSrc) // don't crash when type-checking this package
+}
+
+func TestLookupFieldOrMethod(t *testing.T) {
+ // Test cases assume a lookup of the form a.f or x.f, where a stands for an
+ // addressable value, and x for a non-addressable value (even though a variable
+ // for ease of test case writing).
+ var tests = []struct {
+ src string
+ found bool
+ index []int
+ indirect bool
+ }{
+ // field lookups
+ {"var x T; type T struct{}", false, nil, false},
+ {"var x T; type T struct{ f int }", true, []int{0}, false},
+ {"var x T; type T struct{ a, b, f, c int }", true, []int{2}, false},
+
+ // method lookups
+ {"var a T; type T struct{}; func (T) f() {}", true, []int{0}, false},
+ {"var a *T; type T struct{}; func (T) f() {}", true, []int{0}, true},
+ {"var a T; type T struct{}; func (*T) f() {}", true, []int{0}, false},
+ {"var a *T; type T struct{}; func (*T) f() {}", true, []int{0}, true}, // TODO(gri) should this report indirect = false?
+
+ // collisions
+ {"type ( E1 struct{ f int }; E2 struct{ f int }; x struct{ E1; *E2 })", false, []int{1, 0}, false},
+ {"type ( E1 struct{ f int }; E2 struct{}; x struct{ E1; *E2 }); func (E2) f() {}", false, []int{1, 0}, false},
+
+ // outside methodset
+ // (*T).f method exists, but value of type T is not addressable
+ {"var x T; type T struct{}; func (*T) f() {}", false, nil, true},
+ }
+
+ for _, test := range tests {
+ pkg, err := pkgFor("test", "package p;"+test.src, nil)
+ if err != nil {
+ t.Errorf("%s: incorrect test case: %s", test.src, err)
+ continue
+ }
+
+ obj := pkg.Scope().Lookup("a")
+ if obj == nil {
+ if obj = pkg.Scope().Lookup("x"); obj == nil {
+ t.Errorf("%s: incorrect test case - no object a or x", test.src)
+ continue
+ }
+ }
+
+ f, index, indirect := LookupFieldOrMethod(obj.Type(), obj.Name() == "a", pkg, "f")
+ if (f != nil) != test.found {
+ if f == nil {
+ t.Errorf("%s: got no object; want one", test.src)
+ } else {
+ t.Errorf("%s: got object = %v; want none", test.src, f)
+ }
+ }
+ if !sameSlice(index, test.index) {
+ t.Errorf("%s: got index = %v; want %v", test.src, index, test.index)
+ }
+ if indirect != test.indirect {
+ t.Errorf("%s: got indirect = %v; want %v", test.src, indirect, test.indirect)
+ }
+ }
+}
+
+func sameSlice(a, b []int) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ for i, x := range a {
+ if x != b[i] {
+ return false
+ }
+ }
+ return true
+}
+
+// TestScopeLookupParent ensures that (*Scope).LookupParent returns
+// the correct result at various positions with the source.
+func TestScopeLookupParent(t *testing.T) {
+ fset := token.NewFileSet()
+ imports := make(testImporter)
+ conf := Config{Importer: imports}
+ mustParse := func(src string) *ast.File {
+ f, err := parser.ParseFile(fset, "dummy.go", src, parser.ParseComments)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return f
+ }
+ var info Info
+ makePkg := func(path string, files ...*ast.File) {
+ imports[path], _ = conf.Check(path, fset, files, &info)
+ }
+
+ makePkg("lib", mustParse("package lib; var X int"))
+ // Each /*name=kind:line*/ comment makes the test look up the
+ // name at that point and checks that it resolves to a decl of
+ // the specified kind and line number. "undef" means undefined.
+ mainSrc := `
+package main
+import "lib"
+var Y = lib.X
+func f() {
+ print(Y) /*Y=var:4*/
+ z /*z=undef*/ := /*z=undef*/ 1 /*z=var:7*/
+ print(z)
+ /*f=func:5*/ /*lib=pkgname:3*/
+ type /*T=undef*/ T /*T=typename:10*/ *T
+}
+`
+ info.Uses = make(map[*ast.Ident]Object)
+ f := mustParse(mainSrc)
+ makePkg("main", f)
+ mainScope := imports["main"].Scope()
+ rx := regexp.MustCompile(`^/\*(\w*)=([\w:]*)\*/$`)
+ for _, group := range f.Comments {
+ for _, comment := range group.List {
+ // Parse the assertion in the comment.
+ m := rx.FindStringSubmatch(comment.Text)
+ if m == nil {
+ t.Errorf("%s: bad comment: %s",
+ fset.Position(comment.Pos()), comment.Text)
+ continue
+ }
+ name, want := m[1], m[2]
+
+ // Look up the name in the innermost enclosing scope.
+ inner := mainScope.Innermost(comment.Pos())
+ if inner == nil {
+ t.Errorf("%s: at %s: can't find innermost scope",
+ fset.Position(comment.Pos()), comment.Text)
+ continue
+ }
+ got := "undef"
+ if _, obj := inner.LookupParent(name, comment.Pos()); obj != nil {
+ kind := strings.ToLower(strings.TrimPrefix(reflect.TypeOf(obj).String(), "*types."))
+ got = fmt.Sprintf("%s:%d", kind, fset.Position(obj.Pos()).Line)
+ }
+ if got != want {
+ t.Errorf("%s: at %s: %s resolved to %s, want %s",
+ fset.Position(comment.Pos()), comment.Text, name, got, want)
+ }
+ }
+ }
+
+ // Check that for each referring identifier,
+ // a lookup of its name on the innermost
+ // enclosing scope returns the correct object.
+
+ for id, wantObj := range info.Uses {
+ inner := mainScope.Innermost(id.Pos())
+ if inner == nil {
+ t.Errorf("%s: can't find innermost scope enclosing %q",
+ fset.Position(id.Pos()), id.Name)
+ continue
+ }
+
+ // Exclude selectors and qualified identifiers---lexical
+ // refs only. (Ideally, we'd see if the AST parent is a
+ // SelectorExpr, but that requires PathEnclosingInterval
+ // from golang.org/x/tools/go/ast/astutil.)
+ if id.Name == "X" {
+ continue
+ }
+
+ _, gotObj := inner.LookupParent(id.Name, id.Pos())
+ if gotObj != wantObj {
+ t.Errorf("%s: got %v, want %v",
+ fset.Position(id.Pos()), gotObj, wantObj)
+ continue
+ }
+ }
+}
diff --git a/libgo/go/go/types/assignments.go b/libgo/go/go/types/assignments.go
new file mode 100644
index 00000000000..e88de56a0da
--- /dev/null
+++ b/libgo/go/go/types/assignments.go
@@ -0,0 +1,328 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements initialization and assignment checks.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// assignment reports whether x can be assigned to a variable of type T,
+// if necessary by attempting to convert untyped values to the appropriate
+// type. If x.mode == invalid upon return, then assignment has already
+// issued an error message and the caller doesn't have to report another.
+// Use T == nil to indicate assignment to an untyped blank identifier.
+//
+// TODO(gri) Should find a better way to handle in-band errors.
+//
+func (check *Checker) assignment(x *operand, T Type) bool {
+ switch x.mode {
+ case invalid:
+ return true // error reported before
+ case constant_, variable, mapindex, value, commaok:
+ // ok
+ default:
+ unreachable()
+ }
+
+ // x must be a single value
+ // (tuple types are never named - no need for underlying type)
+ if t, _ := x.typ.(*Tuple); t != nil {
+ assert(t.Len() > 1)
+ check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x)
+ x.mode = invalid
+ return false
+ }
+
+ if isUntyped(x.typ) {
+ target := T
+ // spec: "If an untyped constant is assigned to a variable of interface
+ // type or the blank identifier, the constant is first converted to type
+ // bool, rune, int, float64, complex128 or string respectively, depending
+ // on whether the value is a boolean, rune, integer, floating-point, complex,
+ // or string constant."
+ if T == nil || IsInterface(T) {
+ if T == nil && x.typ == Typ[UntypedNil] {
+ check.errorf(x.pos(), "use of untyped nil")
+ x.mode = invalid
+ return false
+ }
+ target = defaultType(x.typ)
+ }
+ check.convertUntyped(x, target)
+ if x.mode == invalid {
+ return false
+ }
+ }
+
+ // spec: "If a left-hand side is the blank identifier, any typed or
+ // non-constant value except for the predeclared identifier nil may
+ // be assigned to it."
+ return T == nil || x.assignableTo(check.conf, T)
+}
+
+func (check *Checker) initConst(lhs *Const, x *operand) {
+ if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
+ if lhs.typ == nil {
+ lhs.typ = Typ[Invalid]
+ }
+ return
+ }
+
+ // rhs must be a constant
+ if x.mode != constant_ {
+ check.errorf(x.pos(), "%s is not constant", x)
+ if lhs.typ == nil {
+ lhs.typ = Typ[Invalid]
+ }
+ return
+ }
+ assert(isConstType(x.typ))
+
+ // If the lhs doesn't have a type yet, use the type of x.
+ if lhs.typ == nil {
+ lhs.typ = x.typ
+ }
+
+ if !check.assignment(x, lhs.typ) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot define constant %s (type %s) as %s", lhs.Name(), lhs.typ, x)
+ }
+ return
+ }
+
+ lhs.val = x.val
+}
+
+// If result is set, lhs is a function result parameter and x is a return result.
+func (check *Checker) initVar(lhs *Var, x *operand, result bool) Type {
+ if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
+ if lhs.typ == nil {
+ lhs.typ = Typ[Invalid]
+ }
+ return nil
+ }
+
+ // If the lhs doesn't have a type yet, use the type of x.
+ if lhs.typ == nil {
+ typ := x.typ
+ if isUntyped(typ) {
+ // convert untyped types to default types
+ if typ == Typ[UntypedNil] {
+ check.errorf(x.pos(), "use of untyped nil")
+ lhs.typ = Typ[Invalid]
+ return nil
+ }
+ typ = defaultType(typ)
+ }
+ lhs.typ = typ
+ }
+
+ if !check.assignment(x, lhs.typ) {
+ if x.mode != invalid {
+ if result {
+ // don't refer to lhs.name because it may be an anonymous result parameter
+ check.errorf(x.pos(), "cannot return %s as value of type %s", x, lhs.typ)
+ } else {
+ check.errorf(x.pos(), "cannot initialize %s with %s", lhs, x)
+ }
+ }
+ return nil
+ }
+
+ return x.typ
+}
+
+func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
+ if x.mode == invalid || x.typ == Typ[Invalid] {
+ return nil
+ }
+
+ // Determine if the lhs is a (possibly parenthesized) identifier.
+ ident, _ := unparen(lhs).(*ast.Ident)
+
+ // Don't evaluate lhs if it is the blank identifier.
+ if ident != nil && ident.Name == "_" {
+ check.recordDef(ident, nil)
+ if !check.assignment(x, nil) {
+ assert(x.mode == invalid)
+ x.typ = nil
+ }
+ return x.typ
+ }
+
+ // If the lhs is an identifier denoting a variable v, this assignment
+ // is not a 'use' of v. Remember current value of v.used and restore
+ // after evaluating the lhs via check.expr.
+ var v *Var
+ var v_used bool
+ if ident != nil {
+ if _, obj := check.scope.LookupParent(ident.Name, token.NoPos); obj != nil {
+ v, _ = obj.(*Var)
+ if v != nil {
+ v_used = v.used
+ }
+ }
+ }
+
+ var z operand
+ check.expr(&z, lhs)
+ if v != nil {
+ v.used = v_used // restore v.used
+ }
+
+ if z.mode == invalid || z.typ == Typ[Invalid] {
+ return nil
+ }
+
+ // spec: "Each left-hand side operand must be addressable, a map index
+ // expression, or the blank identifier. Operands may be parenthesized."
+ switch z.mode {
+ case invalid:
+ return nil
+ case variable, mapindex:
+ // ok
+ default:
+ check.errorf(z.pos(), "cannot assign to %s", &z)
+ return nil
+ }
+
+ if !check.assignment(x, z.typ) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot assign %s to %s", x, &z)
+ }
+ return nil
+ }
+
+ return x.typ
+}
+
+// If returnPos is valid, initVars is called to type-check the assignment of
+// return expressions, and returnPos is the position of the return statement.
+func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) {
+ l := len(lhs)
+ get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid())
+ if get == nil || l != r {
+ // invalidate lhs and use rhs
+ for _, obj := range lhs {
+ if obj.typ == nil {
+ obj.typ = Typ[Invalid]
+ }
+ }
+ if get == nil {
+ return // error reported by unpack
+ }
+ check.useGetter(get, r)
+ if returnPos.IsValid() {
+ check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r)
+ return
+ }
+ check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
+ return
+ }
+
+ var x operand
+ if commaOk {
+ var a [2]Type
+ for i := range a {
+ get(&x, i)
+ a[i] = check.initVar(lhs[i], &x, returnPos.IsValid())
+ }
+ check.recordCommaOkTypes(rhs[0], a)
+ return
+ }
+
+ for i, lhs := range lhs {
+ get(&x, i)
+ check.initVar(lhs, &x, returnPos.IsValid())
+ }
+}
+
+func (check *Checker) assignVars(lhs, rhs []ast.Expr) {
+ l := len(lhs)
+ get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2)
+ if get == nil {
+ return // error reported by unpack
+ }
+ if l != r {
+ check.useGetter(get, r)
+ check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
+ return
+ }
+
+ var x operand
+ if commaOk {
+ var a [2]Type
+ for i := range a {
+ get(&x, i)
+ a[i] = check.assignVar(lhs[i], &x)
+ }
+ check.recordCommaOkTypes(rhs[0], a)
+ return
+ }
+
+ for i, lhs := range lhs {
+ get(&x, i)
+ check.assignVar(lhs, &x)
+ }
+}
+
+func (check *Checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) {
+ scope := check.scope
+
+ // collect lhs variables
+ var newVars []*Var
+ var lhsVars = make([]*Var, len(lhs))
+ for i, lhs := range lhs {
+ var obj *Var
+ if ident, _ := lhs.(*ast.Ident); ident != nil {
+ // Use the correct obj if the ident is redeclared. The
+ // variable's scope starts after the declaration; so we
+ // must use Scope.Lookup here and call Scope.Insert
+ // (via check.declare) later.
+ name := ident.Name
+ if alt := scope.Lookup(name); alt != nil {
+ // redeclared object must be a variable
+ if alt, _ := alt.(*Var); alt != nil {
+ obj = alt
+ } else {
+ check.errorf(lhs.Pos(), "cannot assign to %s", lhs)
+ }
+ check.recordUse(ident, alt)
+ } else {
+ // declare new variable, possibly a blank (_) variable
+ obj = NewVar(ident.Pos(), check.pkg, name, nil)
+ if name != "_" {
+ newVars = append(newVars, obj)
+ }
+ check.recordDef(ident, obj)
+ }
+ } else {
+ check.errorf(lhs.Pos(), "cannot declare %s", lhs)
+ }
+ if obj == nil {
+ obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
+ }
+ lhsVars[i] = obj
+ }
+
+ check.initVars(lhsVars, rhs, token.NoPos)
+
+ // declare new variables
+ if len(newVars) > 0 {
+ // spec: "The scope of a constant or variable identifier declared inside
+ // a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
+ // for short variable declarations) and ends at the end of the innermost
+ // containing block."
+ scopePos := rhs[len(rhs)-1].End()
+ for _, obj := range newVars {
+ check.declare(scope, nil, obj, scopePos) // recordObject already called
+ }
+ } else {
+ check.softErrorf(pos, "no new variables on left side of :=")
+ }
+}
diff --git a/libgo/go/go/types/builtins.go b/libgo/go/go/types/builtins.go
new file mode 100644
index 00000000000..47295914d00
--- /dev/null
+++ b/libgo/go/go/types/builtins.go
@@ -0,0 +1,627 @@
+// Copyright 2012 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 typechecking of builtin function calls.
+
+package types
+
+import (
+ "go/ast"
+ "go/constant"
+ "go/token"
+)
+
+// builtin type-checks a call to the built-in specified by id and
+// returns true if the call is valid, with *x holding the result;
+// but x.expr is not set. If the call is invalid, the result is
+// false, and *x is undefined.
+//
+func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ bool) {
+ // append is the only built-in that permits the use of ... for the last argument
+ bin := predeclaredFuncs[id]
+ if call.Ellipsis.IsValid() && id != _Append {
+ check.invalidOp(call.Ellipsis, "invalid use of ... with built-in %s", bin.name)
+ check.use(call.Args...)
+ return
+ }
+
+ // For len(x) and cap(x) we need to know if x contains any function calls or
+ // receive operations. Save/restore current setting and set hasCallOrRecv to
+ // false for the evaluation of x so that we can check it afterwards.
+ // Note: We must do this _before_ calling unpack because unpack evaluates the
+ // first argument before we even call arg(x, 0)!
+ if id == _Len || id == _Cap {
+ defer func(b bool) {
+ check.hasCallOrRecv = b
+ }(check.hasCallOrRecv)
+ check.hasCallOrRecv = false
+ }
+
+ // determine actual arguments
+ var arg getter
+ nargs := len(call.Args)
+ switch id {
+ default:
+ // make argument getter
+ arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false)
+ if arg == nil {
+ return
+ }
+ // evaluate first argument, if present
+ if nargs > 0 {
+ arg(x, 0)
+ if x.mode == invalid {
+ return
+ }
+ }
+ case _Make, _New, _Offsetof, _Trace:
+ // arguments require special handling
+ }
+
+ // check argument count
+ {
+ msg := ""
+ if nargs < bin.nargs {
+ msg = "not enough"
+ } else if !bin.variadic && nargs > bin.nargs {
+ msg = "too many"
+ }
+ if msg != "" {
+ check.invalidOp(call.Rparen, "%s arguments for %s (expected %d, found %d)", msg, call, bin.nargs, nargs)
+ return
+ }
+ }
+
+ switch id {
+ case _Append:
+ // append(s S, x ...T) S, where T is the element type of S
+ // spec: "The variadic function append appends zero or more values x to s of type
+ // S, which must be a slice type, and returns the resulting slice, also of type S.
+ // The values x are passed to a parameter of type ...T where T is the element type
+ // of S and the respective parameter passing rules apply."
+ S := x.typ
+ var T Type
+ if s, _ := S.Underlying().(*Slice); s != nil {
+ T = s.elem
+ } else {
+ check.invalidArg(x.pos(), "%s is not a slice", x)
+ return
+ }
+
+ // remember arguments that have been evaluated already
+ alist := []operand{*x}
+
+ // spec: "As a special case, append also accepts a first argument assignable
+ // to type []byte with a second argument of string type followed by ... .
+ // This form appends the bytes of the string.
+ if nargs == 2 && call.Ellipsis.IsValid() && x.assignableTo(check.conf, NewSlice(universeByte)) {
+ arg(x, 1)
+ if x.mode == invalid {
+ return
+ }
+ if isString(x.typ) {
+ if check.Types != nil {
+ sig := makeSig(S, S, x.typ)
+ sig.variadic = true
+ check.recordBuiltinType(call.Fun, sig)
+ }
+ x.mode = value
+ x.typ = S
+ break
+ }
+ alist = append(alist, *x)
+ // fallthrough
+ }
+
+ // check general case by creating custom signature
+ sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature
+ sig.variadic = true
+ check.arguments(x, call, sig, func(x *operand, i int) {
+ // only evaluate arguments that have not been evaluated before
+ if i < len(alist) {
+ *x = alist[i]
+ return
+ }
+ arg(x, i)
+ }, nargs)
+ // ok to continue even if check.arguments reported errors
+
+ x.mode = value
+ x.typ = S
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, sig)
+ }
+
+ case _Cap, _Len:
+ // cap(x)
+ // len(x)
+ mode := invalid
+ var typ Type
+ var val constant.Value
+ switch typ = implicitArrayDeref(x.typ.Underlying()); t := typ.(type) {
+ case *Basic:
+ if isString(t) && id == _Len {
+ if x.mode == constant_ {
+ mode = constant_
+ val = constant.MakeInt64(int64(len(constant.StringVal(x.val))))
+ } else {
+ mode = value
+ }
+ }
+
+ case *Array:
+ mode = value
+ // spec: "The expressions len(s) and cap(s) are constants
+ // if the type of s is an array or pointer to an array and
+ // the expression s does not contain channel receives or
+ // function calls; in this case s is not evaluated."
+ if !check.hasCallOrRecv {
+ mode = constant_
+ val = constant.MakeInt64(t.len)
+ }
+
+ case *Slice, *Chan:
+ mode = value
+
+ case *Map:
+ if id == _Len {
+ mode = value
+ }
+ }
+
+ if mode == invalid {
+ check.invalidArg(x.pos(), "%s for %s", x, bin.name)
+ return
+ }
+
+ x.mode = mode
+ x.typ = Typ[Int]
+ x.val = val
+ if check.Types != nil && mode != constant_ {
+ check.recordBuiltinType(call.Fun, makeSig(x.typ, typ))
+ }
+
+ case _Close:
+ // close(c)
+ c, _ := x.typ.Underlying().(*Chan)
+ if c == nil {
+ check.invalidArg(x.pos(), "%s is not a channel", x)
+ return
+ }
+ if c.dir == RecvOnly {
+ check.invalidArg(x.pos(), "%s must not be a receive-only channel", x)
+ return
+ }
+
+ x.mode = novalue
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(nil, c))
+ }
+
+ case _Complex:
+ // complex(x, y realT) complexT
+ if !check.complexArg(x) {
+ return
+ }
+
+ var y operand
+ arg(&y, 1)
+ if y.mode == invalid {
+ return
+ }
+ if !check.complexArg(&y) {
+ return
+ }
+
+ check.convertUntyped(x, y.typ)
+ if x.mode == invalid {
+ return
+ }
+ check.convertUntyped(&y, x.typ)
+ if y.mode == invalid {
+ return
+ }
+
+ if !Identical(x.typ, y.typ) {
+ check.invalidArg(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
+ return
+ }
+
+ if x.mode == constant_ && y.mode == constant_ {
+ x.val = constant.BinaryOp(x.val, token.ADD, constant.MakeImag(y.val))
+ } else {
+ x.mode = value
+ }
+
+ realT := x.typ
+ complexT := Typ[Invalid]
+ switch realT.Underlying().(*Basic).kind {
+ case Float32:
+ complexT = Typ[Complex64]
+ case Float64:
+ complexT = Typ[Complex128]
+ case UntypedInt, UntypedRune, UntypedFloat:
+ if x.mode == constant_ {
+ realT = defaultType(realT).(*Basic)
+ complexT = Typ[UntypedComplex]
+ } else {
+ // untyped but not constant; probably because one
+ // operand is a non-constant shift of untyped lhs
+ realT = Typ[Float64]
+ complexT = Typ[Complex128]
+ }
+ default:
+ check.invalidArg(x.pos(), "float32 or float64 arguments expected")
+ return
+ }
+
+ x.typ = complexT
+ if check.Types != nil && x.mode != constant_ {
+ check.recordBuiltinType(call.Fun, makeSig(complexT, realT, realT))
+ }
+
+ if x.mode != constant_ {
+ // The arguments have now their final types, which at run-
+ // time will be materialized. Update the expression trees.
+ // If the current types are untyped, the materialized type
+ // is the respective default type.
+ // (If the result is constant, the arguments are never
+ // materialized and there is nothing to do.)
+ check.updateExprType(x.expr, realT, true)
+ check.updateExprType(y.expr, realT, true)
+ }
+
+ case _Copy:
+ // copy(x, y []T) int
+ var dst Type
+ if t, _ := x.typ.Underlying().(*Slice); t != nil {
+ dst = t.elem
+ }
+
+ var y operand
+ arg(&y, 1)
+ if y.mode == invalid {
+ return
+ }
+ var src Type
+ switch t := y.typ.Underlying().(type) {
+ case *Basic:
+ if isString(y.typ) {
+ src = universeByte
+ }
+ case *Slice:
+ src = t.elem
+ }
+
+ if dst == nil || src == nil {
+ check.invalidArg(x.pos(), "copy expects slice arguments; found %s and %s", x, &y)
+ return
+ }
+
+ if !Identical(dst, src) {
+ check.invalidArg(x.pos(), "arguments to copy %s and %s have different element types %s and %s", x, &y, dst, src)
+ return
+ }
+
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(Typ[Int], x.typ, y.typ))
+ }
+ x.mode = value
+ x.typ = Typ[Int]
+
+ case _Delete:
+ // delete(m, k)
+ m, _ := x.typ.Underlying().(*Map)
+ if m == nil {
+ check.invalidArg(x.pos(), "%s is not a map", x)
+ return
+ }
+ arg(x, 1) // k
+ if x.mode == invalid {
+ return
+ }
+
+ if !x.assignableTo(check.conf, m.key) {
+ check.invalidArg(x.pos(), "%s is not assignable to %s", x, m.key)
+ return
+ }
+
+ x.mode = novalue
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(nil, m, m.key))
+ }
+
+ case _Imag, _Real:
+ // imag(complexT) realT
+ // real(complexT) realT
+ if !isComplex(x.typ) {
+ check.invalidArg(x.pos(), "%s must be a complex number", x)
+ return
+ }
+ if x.mode == constant_ {
+ if id == _Real {
+ x.val = constant.Real(x.val)
+ } else {
+ x.val = constant.Imag(x.val)
+ }
+ } else {
+ x.mode = value
+ }
+ var k BasicKind
+ switch x.typ.Underlying().(*Basic).kind {
+ case Complex64:
+ k = Float32
+ case Complex128:
+ k = Float64
+ case UntypedComplex:
+ k = UntypedFloat
+ default:
+ unreachable()
+ }
+
+ if check.Types != nil && x.mode != constant_ {
+ check.recordBuiltinType(call.Fun, makeSig(Typ[k], x.typ))
+ }
+ x.typ = Typ[k]
+
+ case _Make:
+ // make(T, n)
+ // make(T, n, m)
+ // (no argument evaluated yet)
+ arg0 := call.Args[0]
+ T := check.typ(arg0)
+ if T == Typ[Invalid] {
+ return
+ }
+
+ var min int // minimum number of arguments
+ switch T.Underlying().(type) {
+ case *Slice:
+ min = 2
+ case *Map, *Chan:
+ min = 1
+ default:
+ check.invalidArg(arg0.Pos(), "cannot make %s; type must be slice, map, or channel", arg0)
+ return
+ }
+ if nargs < min || min+1 < nargs {
+ check.errorf(call.Pos(), "%s expects %d or %d arguments; found %d", call, min, min+1, nargs)
+ return
+ }
+ var sizes []int64 // constant integer arguments, if any
+ for _, arg := range call.Args[1:] {
+ if s, ok := check.index(arg, -1); ok && s >= 0 {
+ sizes = append(sizes, s)
+ }
+ }
+ if len(sizes) == 2 && sizes[0] > sizes[1] {
+ check.invalidArg(call.Args[1].Pos(), "length and capacity swapped")
+ // safe to continue
+ }
+ x.mode = value
+ x.typ = T
+ if check.Types != nil {
+ params := [...]Type{T, Typ[Int], Typ[Int]}
+ check.recordBuiltinType(call.Fun, makeSig(x.typ, params[:1+len(sizes)]...))
+ }
+
+ case _New:
+ // new(T)
+ // (no argument evaluated yet)
+ T := check.typ(call.Args[0])
+ if T == Typ[Invalid] {
+ return
+ }
+
+ x.mode = value
+ x.typ = &Pointer{base: T}
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(x.typ, T))
+ }
+
+ case _Panic:
+ // panic(x)
+ T := new(Interface)
+ if !check.assignment(x, T) {
+ assert(x.mode == invalid)
+ return
+ }
+
+ x.mode = novalue
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(nil, T))
+ }
+
+ case _Print, _Println:
+ // print(x, y, ...)
+ // println(x, y, ...)
+ var params []Type
+ if nargs > 0 {
+ params = make([]Type, nargs)
+ for i := 0; i < nargs; i++ {
+ if i > 0 {
+ arg(x, i) // first argument already evaluated
+ }
+ if !check.assignment(x, nil) {
+ assert(x.mode == invalid)
+ return
+ }
+ params[i] = x.typ
+ }
+ }
+
+ x.mode = novalue
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(nil, params...))
+ }
+
+ case _Recover:
+ // recover() interface{}
+ x.mode = value
+ x.typ = new(Interface)
+ if check.Types != nil {
+ check.recordBuiltinType(call.Fun, makeSig(x.typ))
+ }
+
+ case _Alignof:
+ // unsafe.Alignof(x T) uintptr
+ if !check.assignment(x, nil) {
+ assert(x.mode == invalid)
+ return
+ }
+
+ x.mode = constant_
+ x.val = constant.MakeInt64(check.conf.alignof(x.typ))
+ x.typ = Typ[Uintptr]
+ // result is constant - no need to record signature
+
+ case _Offsetof:
+ // unsafe.Offsetof(x T) uintptr, where x must be a selector
+ // (no argument evaluated yet)
+ arg0 := call.Args[0]
+ selx, _ := unparen(arg0).(*ast.SelectorExpr)
+ if selx == nil {
+ check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0)
+ check.use(arg0)
+ return
+ }
+
+ check.expr(x, selx.X)
+ if x.mode == invalid {
+ return
+ }
+
+ base := derefStructPtr(x.typ)
+ sel := selx.Sel.Name
+ obj, index, indirect := LookupFieldOrMethod(base, false, check.pkg, sel)
+ switch obj.(type) {
+ case nil:
+ check.invalidArg(x.pos(), "%s has no single field %s", base, sel)
+ return
+ case *Func:
+ // TODO(gri) Using derefStructPtr may result in methods being found
+ // that don't actually exist. An error either way, but the error
+ // message is confusing. See: https://play.golang.org/p/al75v23kUy ,
+ // but go/types reports: "invalid argument: x.m is a method value".
+ check.invalidArg(arg0.Pos(), "%s is a method value", arg0)
+ return
+ }
+ if indirect {
+ check.invalidArg(x.pos(), "field %s is embedded via a pointer in %s", sel, base)
+ return
+ }
+
+ // TODO(gri) Should we pass x.typ instead of base (and indirect report if derefStructPtr indirected)?
+ check.recordSelection(selx, FieldVal, base, obj, index, false)
+
+ offs := check.conf.offsetof(base, index)
+ x.mode = constant_
+ x.val = constant.MakeInt64(offs)
+ x.typ = Typ[Uintptr]
+ // result is constant - no need to record signature
+
+ case _Sizeof:
+ // unsafe.Sizeof(x T) uintptr
+ if !check.assignment(x, nil) {
+ assert(x.mode == invalid)
+ return
+ }
+
+ x.mode = constant_
+ x.val = constant.MakeInt64(check.conf.sizeof(x.typ))
+ x.typ = Typ[Uintptr]
+ // result is constant - no need to record signature
+
+ case _Assert:
+ // assert(pred) causes a typechecker error if pred is false.
+ // The result of assert is the value of pred if there is no error.
+ // Note: assert is only available in self-test mode.
+ if x.mode != constant_ || !isBoolean(x.typ) {
+ check.invalidArg(x.pos(), "%s is not a boolean constant", x)
+ return
+ }
+ if x.val.Kind() != constant.Bool {
+ check.errorf(x.pos(), "internal error: value of %s should be a boolean constant", x)
+ return
+ }
+ if !constant.BoolVal(x.val) {
+ check.errorf(call.Pos(), "%s failed", call)
+ // compile-time assertion failure - safe to continue
+ }
+ // result is constant - no need to record signature
+
+ case _Trace:
+ // trace(x, y, z, ...) dumps the positions, expressions, and
+ // values of its arguments. The result of trace is the value
+ // of the first argument.
+ // Note: trace is only available in self-test mode.
+ // (no argument evaluated yet)
+ if nargs == 0 {
+ check.dump("%s: trace() without arguments", call.Pos())
+ x.mode = novalue
+ break
+ }
+ var t operand
+ x1 := x
+ for _, arg := range call.Args {
+ check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T))
+ check.dump("%s: %s", x1.pos(), x1)
+ x1 = &t // use incoming x only for first argument
+ }
+ // trace is only available in test mode - no need to record signature
+
+ default:
+ unreachable()
+ }
+
+ return true
+}
+
+// makeSig makes a signature for the given argument and result types.
+// Default types are used for untyped arguments, and res may be nil.
+func makeSig(res Type, args ...Type) *Signature {
+ list := make([]*Var, len(args))
+ for i, param := range args {
+ list[i] = NewVar(token.NoPos, nil, "", defaultType(param))
+ }
+ params := NewTuple(list...)
+ var result *Tuple
+ if res != nil {
+ assert(!isUntyped(res))
+ result = NewTuple(NewVar(token.NoPos, nil, "", res))
+ }
+ return &Signature{params: params, results: result}
+}
+
+// implicitArrayDeref returns A if typ is of the form *A and A is an array;
+// otherwise it returns typ.
+//
+func implicitArrayDeref(typ Type) Type {
+ if p, ok := typ.(*Pointer); ok {
+ if a, ok := p.base.Underlying().(*Array); ok {
+ return a
+ }
+ }
+ return typ
+}
+
+// unparen returns e with any enclosing parentheses stripped.
+func unparen(e ast.Expr) ast.Expr {
+ for {
+ p, ok := e.(*ast.ParenExpr)
+ if !ok {
+ return e
+ }
+ e = p.X
+ }
+}
+
+func (check *Checker) complexArg(x *operand) bool {
+ t, _ := x.typ.Underlying().(*Basic)
+ if t != nil && (t.info&IsFloat != 0 || t.kind == UntypedInt || t.kind == UntypedRune) {
+ return true
+ }
+ check.invalidArg(x.pos(), "%s must be a float32, float64, or an untyped non-complex numeric constant", x)
+ return false
+}
diff --git a/libgo/go/go/types/builtins_test.go b/libgo/go/go/types/builtins_test.go
new file mode 100644
index 00000000000..9835a482670
--- /dev/null
+++ b/libgo/go/go/types/builtins_test.go
@@ -0,0 +1,204 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types_test
+
+import (
+ "fmt"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "testing"
+
+ . "go/types"
+)
+
+var builtinCalls = []struct {
+ name, src, sig string
+}{
+ {"append", `var s []int; _ = append(s)`, `func([]int, ...int) []int`},
+ {"append", `var s []int; _ = append(s, 0)`, `func([]int, ...int) []int`},
+ {"append", `var s []int; _ = (append)(s, 0)`, `func([]int, ...int) []int`},
+ {"append", `var s []byte; _ = ((append))(s, 0)`, `func([]byte, ...byte) []byte`},
+ {"append", `var s []byte; _ = append(s, "foo"...)`, `func([]byte, string...) []byte`},
+ {"append", `type T []byte; var s T; var str string; _ = append(s, str...)`, `func(p.T, string...) p.T`},
+ {"append", `type T []byte; type U string; var s T; var str U; _ = append(s, str...)`, `func(p.T, p.U...) p.T`},
+
+ {"cap", `var s [10]int; _ = cap(s)`, `invalid type`}, // constant
+ {"cap", `var s [10]int; _ = cap(&s)`, `invalid type`}, // constant
+ {"cap", `var s []int64; _ = cap(s)`, `func([]int64) int`},
+ {"cap", `var c chan<-bool; _ = cap(c)`, `func(chan<- bool) int`},
+
+ {"len", `_ = len("foo")`, `invalid type`}, // constant
+ {"len", `var s string; _ = len(s)`, `func(string) int`},
+ {"len", `var s [10]int; _ = len(s)`, `invalid type`}, // constant
+ {"len", `var s [10]int; _ = len(&s)`, `invalid type`}, // constant
+ {"len", `var s []int64; _ = len(s)`, `func([]int64) int`},
+ {"len", `var c chan<-bool; _ = len(c)`, `func(chan<- bool) int`},
+ {"len", `var m map[string]float32; _ = len(m)`, `func(map[string]float32) int`},
+
+ {"close", `var c chan int; close(c)`, `func(chan int)`},
+ {"close", `var c chan<- chan string; close(c)`, `func(chan<- chan string)`},
+
+ {"complex", `_ = complex(1, 0)`, `invalid type`}, // constant
+ {"complex", `var re float32; _ = complex(re, 1.0)`, `func(float32, float32) complex64`},
+ {"complex", `var im float64; _ = complex(1, im)`, `func(float64, float64) complex128`},
+ {"complex", `type F32 float32; var re, im F32; _ = complex(re, im)`, `func(p.F32, p.F32) complex64`},
+ {"complex", `type F64 float64; var re, im F64; _ = complex(re, im)`, `func(p.F64, p.F64) complex128`},
+
+ {"copy", `var src, dst []byte; copy(dst, src)`, `func([]byte, []byte) int`},
+ {"copy", `type T [][]int; var src, dst T; _ = copy(dst, src)`, `func(p.T, p.T) int`},
+ {"copy", `var src string; var dst []byte; copy(dst, src)`, `func([]byte, string) int`},
+ {"copy", `type T string; type U []byte; var src T; var dst U; copy(dst, src)`, `func(p.U, p.T) int`},
+ {"copy", `var dst []byte; copy(dst, "hello")`, `func([]byte, string) int`},
+
+ {"delete", `var m map[string]bool; delete(m, "foo")`, `func(map[string]bool, string)`},
+ {"delete", `type (K string; V int); var m map[K]V; delete(m, "foo")`, `func(map[p.K]p.V, p.K)`},
+
+ {"imag", `_ = imag(1i)`, `invalid type`}, // constant
+ {"imag", `var c complex64; _ = imag(c)`, `func(complex64) float32`},
+ {"imag", `var c complex128; _ = imag(c)`, `func(complex128) float64`},
+ {"imag", `type C64 complex64; var c C64; _ = imag(c)`, `func(p.C64) float32`},
+ {"imag", `type C128 complex128; var c C128; _ = imag(c)`, `func(p.C128) float64`},
+
+ {"real", `_ = real(1i)`, `invalid type`}, // constant
+ {"real", `var c complex64; _ = real(c)`, `func(complex64) float32`},
+ {"real", `var c complex128; _ = real(c)`, `func(complex128) float64`},
+ {"real", `type C64 complex64; var c C64; _ = real(c)`, `func(p.C64) float32`},
+ {"real", `type C128 complex128; var c C128; _ = real(c)`, `func(p.C128) float64`},
+
+ {"make", `_ = make([]int, 10)`, `func([]int, int) []int`},
+ {"make", `type T []byte; _ = make(T, 10, 20)`, `func(p.T, int, int) p.T`},
+
+ {"new", `_ = new(int)`, `func(int) *int`},
+ {"new", `type T struct{}; _ = new(T)`, `func(p.T) *p.T`},
+
+ {"panic", `panic(0)`, `func(interface{})`},
+ {"panic", `panic("foo")`, `func(interface{})`},
+
+ {"print", `print()`, `func()`},
+ {"print", `print(0)`, `func(int)`},
+ {"print", `print(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
+
+ {"println", `println()`, `func()`},
+ {"println", `println(0)`, `func(int)`},
+ {"println", `println(1, 2.0, "foo", true)`, `func(int, float64, string, bool)`},
+
+ {"recover", `recover()`, `func() interface{}`},
+ {"recover", `_ = recover()`, `func() interface{}`},
+
+ {"Alignof", `_ = unsafe.Alignof(0)`, `invalid type`}, // constant
+ {"Alignof", `var x struct{}; _ = unsafe.Alignof(x)`, `invalid type`}, // constant
+
+ {"Offsetof", `var x struct{f bool}; _ = unsafe.Offsetof(x.f)`, `invalid type`}, // constant
+ {"Offsetof", `var x struct{_ int; f bool}; _ = unsafe.Offsetof((&x).f)`, `invalid type`}, // constant
+
+ {"Sizeof", `_ = unsafe.Sizeof(0)`, `invalid type`}, // constant
+ {"Sizeof", `var x struct{}; _ = unsafe.Sizeof(x)`, `invalid type`}, // constant
+
+ {"assert", `assert(true)`, `invalid type`}, // constant
+ {"assert", `type B bool; const pred B = 1 < 2; assert(pred)`, `invalid type`}, // constant
+
+ // no tests for trace since it produces output as a side-effect
+}
+
+func TestBuiltinSignatures(t *testing.T) {
+ DefPredeclaredTestFuncs()
+
+ seen := map[string]bool{"trace": true} // no test for trace built-in; add it manually
+ for _, call := range builtinCalls {
+ testBuiltinSignature(t, call.name, call.src, call.sig)
+ seen[call.name] = true
+ }
+
+ // make sure we didn't miss one
+ for _, name := range Universe.Names() {
+ if _, ok := Universe.Lookup(name).(*Builtin); ok && !seen[name] {
+ t.Errorf("missing test for %s", name)
+ }
+ }
+ for _, name := range Unsafe.Scope().Names() {
+ if _, ok := Unsafe.Scope().Lookup(name).(*Builtin); ok && !seen[name] {
+ t.Errorf("missing test for unsafe.%s", name)
+ }
+ }
+}
+
+func testBuiltinSignature(t *testing.T, name, src0, want string) {
+ src := fmt.Sprintf(`package p; import "unsafe"; type _ unsafe.Pointer /* use unsafe */; func _() { %s }`, src0)
+ f, err := parser.ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Errorf("%s: %s", src0, err)
+ return
+ }
+
+ conf := Config{Importer: importer.Default()}
+ uses := make(map[*ast.Ident]Object)
+ types := make(map[ast.Expr]TypeAndValue)
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Uses: uses, Types: types})
+ if err != nil {
+ t.Errorf("%s: %s", src0, err)
+ return
+ }
+
+ // find called function
+ n := 0
+ var fun ast.Expr
+ for x := range types {
+ if call, _ := x.(*ast.CallExpr); call != nil {
+ fun = call.Fun
+ n++
+ }
+ }
+ if n != 1 {
+ t.Errorf("%s: got %d CallExprs; want 1", src0, n)
+ return
+ }
+
+ // check recorded types for fun and descendents (may be parenthesized)
+ for {
+ // the recorded type for the built-in must match the wanted signature
+ typ := types[fun].Type
+ if typ == nil {
+ t.Errorf("%s: no type recorded for %s", src0, ExprString(fun))
+ return
+ }
+ if got := typ.String(); got != want {
+ t.Errorf("%s: got type %s; want %s", src0, got, want)
+ return
+ }
+
+ // called function must be a (possibly parenthesized, qualified)
+ // identifier denoting the expected built-in
+ switch p := fun.(type) {
+ case *ast.Ident:
+ obj := uses[p]
+ if obj == nil {
+ t.Errorf("%s: no object found for %s", src0, p)
+ return
+ }
+ bin, _ := obj.(*Builtin)
+ if bin == nil {
+ t.Errorf("%s: %s does not denote a built-in", src0, p)
+ return
+ }
+ if bin.Name() != name {
+ t.Errorf("%s: got built-in %s; want %s", src0, bin.Name(), name)
+ return
+ }
+ return // we're done
+
+ case *ast.ParenExpr:
+ fun = p.X // unpack
+
+ case *ast.SelectorExpr:
+ // built-in from package unsafe - ignore details
+ return // we're done
+
+ default:
+ t.Errorf("%s: invalid function call", src0)
+ return
+ }
+ }
+}
diff --git a/libgo/go/go/types/call.go b/libgo/go/go/types/call.go
new file mode 100644
index 00000000000..62cefc047ef
--- /dev/null
+++ b/libgo/go/go/types/call.go
@@ -0,0 +1,441 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements typechecking of call and selector expressions.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
+ check.exprOrType(x, e.Fun)
+
+ switch x.mode {
+ case invalid:
+ check.use(e.Args...)
+ x.mode = invalid
+ x.expr = e
+ return statement
+
+ case typexpr:
+ // conversion
+ T := x.typ
+ x.mode = invalid
+ switch n := len(e.Args); n {
+ case 0:
+ check.errorf(e.Rparen, "missing argument in conversion to %s", T)
+ case 1:
+ check.expr(x, e.Args[0])
+ if x.mode != invalid {
+ check.conversion(x, T)
+ }
+ default:
+ check.errorf(e.Args[n-1].Pos(), "too many arguments in conversion to %s", T)
+ }
+ x.expr = e
+ return conversion
+
+ case builtin:
+ id := x.id
+ if !check.builtin(x, e, id) {
+ x.mode = invalid
+ }
+ x.expr = e
+ // a non-constant result implies a function call
+ if x.mode != invalid && x.mode != constant_ {
+ check.hasCallOrRecv = true
+ }
+ return predeclaredFuncs[id].kind
+
+ default:
+ // function/method call
+ sig, _ := x.typ.Underlying().(*Signature)
+ if sig == nil {
+ check.invalidOp(x.pos(), "cannot call non-function %s", x)
+ x.mode = invalid
+ x.expr = e
+ return statement
+ }
+
+ arg, n, _ := unpack(func(x *operand, i int) { check.expr(x, e.Args[i]) }, len(e.Args), false)
+ if arg == nil {
+ x.mode = invalid
+ x.expr = e
+ return statement
+ }
+
+ check.arguments(x, e, sig, arg, n)
+
+ // determine result
+ switch sig.results.Len() {
+ case 0:
+ x.mode = novalue
+ case 1:
+ x.mode = value
+ x.typ = sig.results.vars[0].typ // unpack tuple
+ default:
+ x.mode = value
+ x.typ = sig.results
+ }
+ x.expr = e
+ check.hasCallOrRecv = true
+
+ return statement
+ }
+}
+
+// use type-checks each argument.
+// Useful to make sure expressions are evaluated
+// (and variables are "used") in the presence of other errors.
+func (check *Checker) use(arg ...ast.Expr) {
+ var x operand
+ for _, e := range arg {
+ check.rawExpr(&x, e, nil)
+ }
+}
+
+// useGetter is like use, but takes a getter instead of a list of expressions.
+// It should be called instead of use if a getter is present to avoid repeated
+// evaluation of the first argument (since the getter was likely obtained via
+// unpack, which may have evaluated the first argument already).
+func (check *Checker) useGetter(get getter, n int) {
+ var x operand
+ for i := 0; i < n; i++ {
+ get(&x, i)
+ }
+}
+
+// A getter sets x as the i'th operand, where 0 <= i < n and n is the total
+// number of operands (context-specific, and maintained elsewhere). A getter
+// type-checks the i'th operand; the details of the actual check are getter-
+// specific.
+type getter func(x *operand, i int)
+
+// unpack takes a getter get and a number of operands n. If n == 1, unpack
+// calls the incoming getter for the first operand. If that operand is
+// invalid, unpack returns (nil, 0, false). Otherwise, if that operand is a
+// function call, or a comma-ok expression and allowCommaOk is set, the result
+// is a new getter and operand count providing access to the function results,
+// or comma-ok values, respectively. The third result value reports if it
+// is indeed the comma-ok case. In all other cases, the incoming getter and
+// operand count are returned unchanged, and the third result value is false.
+//
+// In other words, if there's exactly one operand that - after type-checking
+// by calling get - stands for multiple operands, the resulting getter provides
+// access to those operands instead.
+//
+// If the returned getter is called at most once for a given operand index i
+// (including i == 0), that operand is guaranteed to cause only one call of
+// the incoming getter with that i.
+//
+func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) {
+ if n == 1 {
+ // possibly result of an n-valued function call or comma,ok value
+ var x0 operand
+ get(&x0, 0)
+ if x0.mode == invalid {
+ return nil, 0, false
+ }
+
+ if t, ok := x0.typ.(*Tuple); ok {
+ // result of an n-valued function call
+ return func(x *operand, i int) {
+ x.mode = value
+ x.expr = x0.expr
+ x.typ = t.At(i).typ
+ }, t.Len(), false
+ }
+
+ if x0.mode == mapindex || x0.mode == commaok {
+ // comma-ok value
+ if allowCommaOk {
+ a := [2]Type{x0.typ, Typ[UntypedBool]}
+ return func(x *operand, i int) {
+ x.mode = value
+ x.expr = x0.expr
+ x.typ = a[i]
+ }, 2, true
+ }
+ x0.mode = value
+ }
+
+ // single value
+ return func(x *operand, i int) {
+ if i != 0 {
+ unreachable()
+ }
+ *x = x0
+ }, 1, false
+ }
+
+ // zero or multiple values
+ return get, n, false
+}
+
+// arguments checks argument passing for the call with the given signature.
+// The arg function provides the operand for the i'th argument.
+func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, arg getter, n int) {
+ if call.Ellipsis.IsValid() {
+ // last argument is of the form x...
+ if len(call.Args) == 1 && n > 1 {
+ // f()... is not permitted if f() is multi-valued
+ check.errorf(call.Ellipsis, "cannot use ... with %d-valued expression %s", n, call.Args[0])
+ check.useGetter(arg, n)
+ return
+ }
+ if !sig.variadic {
+ check.errorf(call.Ellipsis, "cannot use ... in call to non-variadic %s", call.Fun)
+ check.useGetter(arg, n)
+ return
+ }
+ }
+
+ // evaluate arguments
+ for i := 0; i < n; i++ {
+ arg(x, i)
+ if x.mode != invalid {
+ var ellipsis token.Pos
+ if i == n-1 && call.Ellipsis.IsValid() {
+ ellipsis = call.Ellipsis
+ }
+ check.argument(sig, i, x, ellipsis)
+ }
+ }
+
+ // check argument count
+ if sig.variadic {
+ // a variadic function accepts an "empty"
+ // last argument: count one extra
+ n++
+ }
+ if n < sig.params.Len() {
+ check.errorf(call.Rparen, "too few arguments in call to %s", call.Fun)
+ // ok to continue
+ }
+}
+
+// argument checks passing of argument x to the i'th parameter of the given signature.
+// If ellipsis is valid, the argument is followed by ... at that position in the call.
+func (check *Checker) argument(sig *Signature, i int, x *operand, ellipsis token.Pos) {
+ n := sig.params.Len()
+
+ // determine parameter type
+ var typ Type
+ switch {
+ case i < n:
+ typ = sig.params.vars[i].typ
+ case sig.variadic:
+ typ = sig.params.vars[n-1].typ
+ if debug {
+ if _, ok := typ.(*Slice); !ok {
+ check.dump("%s: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), typ)
+ }
+ }
+ default:
+ check.errorf(x.pos(), "too many arguments")
+ return
+ }
+
+ if ellipsis.IsValid() {
+ // argument is of the form x...
+ if i != n-1 {
+ check.errorf(ellipsis, "can only use ... with matching parameter")
+ return
+ }
+ switch t := x.typ.Underlying().(type) {
+ case *Slice:
+ // ok
+ case *Tuple:
+ check.errorf(ellipsis, "cannot use ... with %d-valued expression %s", t.Len(), x)
+ return
+ default:
+ check.errorf(x.pos(), "cannot use %s as parameter of type %s", x, typ)
+ return
+ }
+ } else if sig.variadic && i >= n-1 {
+ // use the variadic parameter slice's element type
+ typ = typ.(*Slice).elem
+ }
+
+ if !check.assignment(x, typ) && x.mode != invalid {
+ check.errorf(x.pos(), "cannot pass argument %s to parameter of type %s", x, typ)
+ }
+}
+
+func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
+ // these must be declared before the "goto Error" statements
+ var (
+ obj Object
+ index []int
+ indirect bool
+ )
+
+ sel := e.Sel.Name
+ // If the identifier refers to a package, handle everything here
+ // so we don't need a "package" mode for operands: package names
+ // can only appear in qualified identifiers which are mapped to
+ // selector expressions.
+ if ident, ok := e.X.(*ast.Ident); ok {
+ _, obj := check.scope.LookupParent(ident.Name, check.pos)
+ if pkg, _ := obj.(*PkgName); pkg != nil {
+ assert(pkg.pkg == check.pkg)
+ check.recordUse(ident, pkg)
+ pkg.used = true
+ exp := pkg.imported.scope.Lookup(sel)
+ if exp == nil {
+ if !pkg.imported.fake {
+ check.errorf(e.Pos(), "%s not declared by package %s", sel, ident)
+ }
+ goto Error
+ }
+ if !exp.Exported() {
+ check.errorf(e.Pos(), "%s not exported by package %s", sel, ident)
+ // ok to continue
+ }
+ check.recordUse(e.Sel, exp)
+ // Simplified version of the code for *ast.Idents:
+ // - imported objects are always fully initialized
+ switch exp := exp.(type) {
+ case *Const:
+ assert(exp.Val() != nil)
+ x.mode = constant_
+ x.typ = exp.typ
+ x.val = exp.val
+ case *TypeName:
+ x.mode = typexpr
+ x.typ = exp.typ
+ case *Var:
+ x.mode = variable
+ x.typ = exp.typ
+ case *Func:
+ x.mode = value
+ x.typ = exp.typ
+ case *Builtin:
+ x.mode = builtin
+ x.typ = exp.typ
+ x.id = exp.id
+ default:
+ unreachable()
+ }
+ x.expr = e
+ return
+ }
+ }
+
+ check.exprOrType(x, e.X)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ obj, index, indirect = LookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
+ if obj == nil {
+ switch {
+ case index != nil:
+ // TODO(gri) should provide actual type where the conflict happens
+ check.invalidOp(e.Pos(), "ambiguous selector %s", sel)
+ case indirect:
+ check.invalidOp(e.Pos(), "%s is not in method set of %s", sel, x.typ)
+ default:
+ check.invalidOp(e.Pos(), "%s has no field or method %s", x, sel)
+ }
+ goto Error
+ }
+
+ if x.mode == typexpr {
+ // method expression
+ m, _ := obj.(*Func)
+ if m == nil {
+ check.invalidOp(e.Pos(), "%s has no method %s", x, sel)
+ goto Error
+ }
+
+ check.recordSelection(e, MethodExpr, x.typ, m, index, indirect)
+
+ // the receiver type becomes the type of the first function
+ // argument of the method expression's function type
+ var params []*Var
+ sig := m.typ.(*Signature)
+ if sig.params != nil {
+ params = sig.params.vars
+ }
+ x.mode = value
+ x.typ = &Signature{
+ params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "", x.typ)}, params...)...),
+ results: sig.results,
+ variadic: sig.variadic,
+ }
+
+ check.addDeclDep(m)
+
+ } else {
+ // regular selector
+ switch obj := obj.(type) {
+ case *Var:
+ check.recordSelection(e, FieldVal, x.typ, obj, index, indirect)
+ if x.mode == variable || indirect {
+ x.mode = variable
+ } else {
+ x.mode = value
+ }
+ x.typ = obj.typ
+
+ case *Func:
+ // TODO(gri) If we needed to take into account the receiver's
+ // addressability, should we report the type &(x.typ) instead?
+ check.recordSelection(e, MethodVal, x.typ, obj, index, indirect)
+
+ if debug {
+ // Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
+ typ := x.typ
+ if x.mode == variable {
+ // If typ is not an (unnamed) pointer or an interface,
+ // use *typ instead, because the method set of *typ
+ // includes the methods of typ.
+ // Variables are addressable, so we can always take their
+ // address.
+ if _, ok := typ.(*Pointer); !ok && !IsInterface(typ) {
+ typ = &Pointer{base: typ}
+ }
+ }
+ // If we created a synthetic pointer type above, we will throw
+ // away the method set computed here after use.
+ // TODO(gri) Method set computation should probably always compute
+ // both, the value and the pointer receiver method set and represent
+ // them in a single structure.
+ // TODO(gri) Consider also using a method set cache for the lifetime
+ // of checker once we rely on MethodSet lookup instead of individual
+ // lookup.
+ mset := NewMethodSet(typ)
+ if m := mset.Lookup(check.pkg, sel); m == nil || m.obj != obj {
+ check.dump("%s: (%s).%v -> %s", e.Pos(), typ, obj.name, m)
+ check.dump("%s\n", mset)
+ panic("method sets and lookup don't agree")
+ }
+ }
+
+ x.mode = value
+
+ // remove receiver
+ sig := *obj.typ.(*Signature)
+ sig.recv = nil
+ x.typ = &sig
+
+ check.addDeclDep(obj)
+
+ default:
+ unreachable()
+ }
+ }
+
+ // everything went well
+ x.expr = e
+ return
+
+Error:
+ x.mode = invalid
+ x.expr = e
+}
diff --git a/libgo/go/go/types/check.go b/libgo/go/go/types/check.go
new file mode 100644
index 00000000000..bb0b07415e8
--- /dev/null
+++ b/libgo/go/go/types/check.go
@@ -0,0 +1,358 @@
+// 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 the Check function, which drives type-checking.
+
+package types
+
+import (
+ "go/ast"
+ "go/constant"
+ "go/token"
+)
+
+// debugging/development support
+const (
+ debug = false // leave on during development
+ trace = false // turn on for detailed type resolution traces
+)
+
+// If Strict is set, the type-checker enforces additional
+// rules not specified by the Go 1 spec, but which will
+// catch guaranteed run-time errors if the respective
+// code is executed. In other words, programs passing in
+// Strict mode are Go 1 compliant, but not all Go 1 programs
+// will pass in Strict mode. The additional rules are:
+//
+// - A type assertion x.(T) where T is an interface type
+// is invalid if any (statically known) method that exists
+// for both x and T have different signatures.
+//
+const strict = false
+
+// exprInfo stores information about an untyped expression.
+type exprInfo struct {
+ isLhs bool // expression is lhs operand of a shift with delayed type-check
+ mode operandMode
+ typ *Basic
+ val constant.Value // constant value; or nil (if not a constant)
+}
+
+// funcInfo stores the information required for type-checking a function.
+type funcInfo struct {
+ name string // for debugging/tracing only
+ decl *declInfo // for cycle detection
+ sig *Signature
+ body *ast.BlockStmt
+}
+
+// A context represents the context within which an object is type-checked.
+type context struct {
+ decl *declInfo // package-level declaration whose init expression/function body is checked
+ scope *Scope // top-most scope for lookups
+ iota constant.Value // value of iota in a constant declaration; nil otherwise
+ sig *Signature // function signature if inside a function; nil otherwise
+ hasLabel bool // set if a function makes use of labels (only ~1% of functions); unused outside functions
+ hasCallOrRecv bool // set if an expression contains a function call or channel receive operation
+}
+
+// A Checker maintains the state of the type checker.
+// It must be created with NewChecker.
+type Checker struct {
+ // package information
+ // (initialized by NewChecker, valid for the life-time of checker)
+ conf *Config
+ fset *token.FileSet
+ pkg *Package
+ *Info
+ objMap map[Object]*declInfo // maps package-level object to declaration info
+
+ // information collected during type-checking of a set of package files
+ // (initialized by Files, valid only for the duration of check.Files;
+ // maps and lists are allocated on demand)
+ files []*ast.File // package files
+ unusedDotImports map[*Scope]map[*Package]token.Pos // positions of unused dot-imported packages for each file scope
+
+ firstErr error // first error encountered
+ methods map[string][]*Func // maps type names to associated methods
+ untyped map[ast.Expr]exprInfo // map of expressions without final type
+ funcs []funcInfo // list of functions to type-check
+ delayed []func() // delayed checks requiring fully setup types
+
+ // context within which the current object is type-checked
+ // (valid only for the duration of type-checking a specific object)
+ context
+ pos token.Pos // if valid, identifiers are looked up as if at position pos (used by Eval)
+
+ // debugging
+ indent int // indentation for tracing
+}
+
+// addUnusedImport adds the position of a dot-imported package
+// pkg to the map of dot imports for the given file scope.
+func (check *Checker) addUnusedDotImport(scope *Scope, pkg *Package, pos token.Pos) {
+ mm := check.unusedDotImports
+ if mm == nil {
+ mm = make(map[*Scope]map[*Package]token.Pos)
+ check.unusedDotImports = mm
+ }
+ m := mm[scope]
+ if m == nil {
+ m = make(map[*Package]token.Pos)
+ mm[scope] = m
+ }
+ m[pkg] = pos
+}
+
+// addDeclDep adds the dependency edge (check.decl -> to) if check.decl exists
+func (check *Checker) addDeclDep(to Object) {
+ from := check.decl
+ if from == nil {
+ return // not in a package-level init expression
+ }
+ if _, found := check.objMap[to]; !found {
+ return // to is not a package-level object
+ }
+ from.addDep(to)
+}
+
+func (check *Checker) assocMethod(tname string, meth *Func) {
+ m := check.methods
+ if m == nil {
+ m = make(map[string][]*Func)
+ check.methods = m
+ }
+ m[tname] = append(m[tname], meth)
+}
+
+func (check *Checker) rememberUntyped(e ast.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
+ m := check.untyped
+ if m == nil {
+ m = make(map[ast.Expr]exprInfo)
+ check.untyped = m
+ }
+ m[e] = exprInfo{lhs, mode, typ, val}
+}
+
+func (check *Checker) later(name string, decl *declInfo, sig *Signature, body *ast.BlockStmt) {
+ check.funcs = append(check.funcs, funcInfo{name, decl, sig, body})
+}
+
+func (check *Checker) delay(f func()) {
+ check.delayed = append(check.delayed, f)
+}
+
+// NewChecker returns a new Checker instance for a given package.
+// Package files may be added incrementally via checker.Files.
+func NewChecker(conf *Config, fset *token.FileSet, pkg *Package, info *Info) *Checker {
+ // make sure we have a configuration
+ if conf == nil {
+ conf = new(Config)
+ }
+
+ // make sure we have an info struct
+ if info == nil {
+ info = new(Info)
+ }
+
+ return &Checker{
+ conf: conf,
+ fset: fset,
+ pkg: pkg,
+ Info: info,
+ objMap: make(map[Object]*declInfo),
+ }
+}
+
+// initFiles initializes the files-specific portion of checker.
+// The provided files must all belong to the same package.
+func (check *Checker) initFiles(files []*ast.File) {
+ // start with a clean slate (check.Files may be called multiple times)
+ check.files = nil
+ check.unusedDotImports = nil
+
+ check.firstErr = nil
+ check.methods = nil
+ check.untyped = nil
+ check.funcs = nil
+ check.delayed = nil
+
+ // determine package name and collect valid files
+ pkg := check.pkg
+ for _, file := range files {
+ switch name := file.Name.Name; pkg.name {
+ case "":
+ if name != "_" {
+ pkg.name = name
+ } else {
+ check.errorf(file.Name.Pos(), "invalid package name _")
+ }
+ fallthrough
+
+ case name:
+ check.files = append(check.files, file)
+
+ default:
+ check.errorf(file.Package, "package %s; expected %s", name, pkg.name)
+ // ignore this file
+ }
+ }
+}
+
+// A bailout panic is used for early termination.
+type bailout struct{}
+
+func (check *Checker) handleBailout(err *error) {
+ switch p := recover().(type) {
+ case nil, bailout:
+ // normal return or early exit
+ *err = check.firstErr
+ default:
+ // re-panic
+ panic(p)
+ }
+}
+
+// Files checks the provided files as part of the checker's package.
+func (check *Checker) Files(files []*ast.File) (err error) {
+ defer check.handleBailout(&err)
+
+ check.initFiles(files)
+
+ check.collectObjects()
+
+ check.packageObjects(check.resolveOrder())
+
+ check.functionBodies()
+
+ check.initOrder()
+
+ if !check.conf.DisableUnusedImportCheck {
+ check.unusedImports()
+ }
+
+ // perform delayed checks
+ for _, f := range check.delayed {
+ f()
+ }
+
+ check.recordUntyped()
+
+ check.pkg.complete = true
+ return
+}
+
+func (check *Checker) recordUntyped() {
+ if !debug && check.Types == nil {
+ return // nothing to do
+ }
+
+ for x, info := range check.untyped {
+ if debug && isTyped(info.typ) {
+ check.dump("%s: %s (type %s) is typed", x.Pos(), x, info.typ)
+ unreachable()
+ }
+ check.recordTypeAndValue(x, info.mode, info.typ, info.val)
+ }
+}
+
+func (check *Checker) recordTypeAndValue(x ast.Expr, mode operandMode, typ Type, val constant.Value) {
+ assert(x != nil)
+ assert(typ != nil)
+ if mode == invalid {
+ return // omit
+ }
+ assert(typ != nil)
+ if mode == constant_ {
+ assert(val != nil)
+ assert(typ == Typ[Invalid] || isConstType(typ))
+ }
+ if m := check.Types; m != nil {
+ m[x] = TypeAndValue{mode, typ, val}
+ }
+}
+
+func (check *Checker) recordBuiltinType(f ast.Expr, sig *Signature) {
+ // f must be a (possibly parenthesized) identifier denoting a built-in
+ // (built-ins in package unsafe always produce a constant result and
+ // we don't record their signatures, so we don't see qualified idents
+ // here): record the signature for f and possible children.
+ for {
+ check.recordTypeAndValue(f, builtin, sig, nil)
+ switch p := f.(type) {
+ case *ast.Ident:
+ return // we're done
+ case *ast.ParenExpr:
+ f = p.X
+ default:
+ unreachable()
+ }
+ }
+}
+
+func (check *Checker) recordCommaOkTypes(x ast.Expr, a [2]Type) {
+ assert(x != nil)
+ if a[0] == nil || a[1] == nil {
+ return
+ }
+ assert(isTyped(a[0]) && isTyped(a[1]) && isBoolean(a[1]))
+ if m := check.Types; m != nil {
+ for {
+ tv := m[x]
+ assert(tv.Type != nil) // should have been recorded already
+ pos := x.Pos()
+ tv.Type = NewTuple(
+ NewVar(pos, check.pkg, "", a[0]),
+ NewVar(pos, check.pkg, "", a[1]),
+ )
+ m[x] = tv
+ // if x is a parenthesized expression (p.X), update p.X
+ p, _ := x.(*ast.ParenExpr)
+ if p == nil {
+ break
+ }
+ x = p.X
+ }
+ }
+}
+
+func (check *Checker) recordDef(id *ast.Ident, obj Object) {
+ assert(id != nil)
+ if m := check.Defs; m != nil {
+ m[id] = obj
+ }
+}
+
+func (check *Checker) recordUse(id *ast.Ident, obj Object) {
+ assert(id != nil)
+ assert(obj != nil)
+ if m := check.Uses; m != nil {
+ m[id] = obj
+ }
+}
+
+func (check *Checker) recordImplicit(node ast.Node, obj Object) {
+ assert(node != nil)
+ assert(obj != nil)
+ if m := check.Implicits; m != nil {
+ m[node] = obj
+ }
+}
+
+func (check *Checker) recordSelection(x *ast.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
+ assert(obj != nil && (recv == nil || len(index) > 0))
+ check.recordUse(x.Sel, obj)
+ // TODO(gri) Should we also call recordTypeAndValue?
+ if m := check.Selections; m != nil {
+ m[x] = &Selection{kind, recv, obj, index, indirect}
+ }
+}
+
+func (check *Checker) recordScope(node ast.Node, scope *Scope) {
+ assert(node != nil)
+ assert(scope != nil)
+ if m := check.Scopes; m != nil {
+ m[node] = scope
+ }
+}
diff --git a/libgo/go/go/types/check_test.go b/libgo/go/go/types/check_test.go
new file mode 100644
index 00000000000..5e34c65b636
--- /dev/null
+++ b/libgo/go/go/types/check_test.go
@@ -0,0 +1,298 @@
+// 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 a typechecker test harness. The packages specified
+// in tests are typechecked. Error messages reported by the typechecker are
+// compared against the error messages expected in the test files.
+//
+// Expected errors are indicated in the test files by putting a comment
+// of the form /* ERROR "rx" */ immediately following an offending token.
+// The harness will verify that an error matching the regular expression
+// rx is reported at that source position. Consecutive comments may be
+// used to indicate multiple errors for the same token position.
+//
+// For instance, the following test file indicates that a "not declared"
+// error should be reported for the undeclared variable x:
+//
+// package p
+// func f() {
+// _ = x /* ERROR "not declared" */ + 1
+// }
+
+// TODO(gri) Also collect strict mode errors of the form /* STRICT ... */
+// and test against strict mode.
+
+package types_test
+
+import (
+ "flag"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/scanner"
+ "go/token"
+ "internal/testenv"
+ "io/ioutil"
+ "regexp"
+ "strings"
+ "testing"
+
+ . "go/types"
+)
+
+var (
+ listErrors = flag.Bool("list", false, "list errors")
+ testFiles = flag.String("files", "", "space-separated list of test files")
+)
+
+// The test filenames do not end in .go so that they are invisible
+// to gofmt since they contain comments that must not change their
+// positions relative to surrounding tokens.
+
+// Each tests entry is list of files belonging to the same package.
+var tests = [][]string{
+ {"testdata/errors.src"},
+ {"testdata/importdecl0a.src", "testdata/importdecl0b.src"},
+ {"testdata/importdecl1a.src", "testdata/importdecl1b.src"},
+ {"testdata/cycles.src"},
+ {"testdata/cycles1.src"},
+ {"testdata/cycles2.src"},
+ {"testdata/cycles3.src"},
+ {"testdata/cycles4.src"},
+ {"testdata/init0.src"},
+ {"testdata/init1.src"},
+ {"testdata/init2.src"},
+ {"testdata/decls0.src"},
+ {"testdata/decls1.src"},
+ {"testdata/decls2a.src", "testdata/decls2b.src"},
+ {"testdata/decls3.src"},
+ {"testdata/const0.src"},
+ {"testdata/const1.src"},
+ {"testdata/constdecl.src"},
+ {"testdata/vardecl.src"},
+ {"testdata/expr0.src"},
+ {"testdata/expr1.src"},
+ {"testdata/expr2.src"},
+ {"testdata/expr3.src"},
+ {"testdata/methodsets.src"},
+ {"testdata/shifts.src"},
+ {"testdata/builtins.src"},
+ {"testdata/conversions.src"},
+ {"testdata/stmt0.src"},
+ {"testdata/stmt1.src"},
+ {"testdata/gotos.src"},
+ {"testdata/labels.src"},
+ {"testdata/issues.src"},
+ {"testdata/blank.src"},
+}
+
+var fset = token.NewFileSet()
+
+// Positioned errors are of the form filename:line:column: message .
+var posMsgRx = regexp.MustCompile(`^(.*:[0-9]+:[0-9]+): *(.*)`)
+
+// splitError splits an error's error message into a position string
+// and the actual error message. If there's no position information,
+// pos is the empty string, and msg is the entire error message.
+//
+func splitError(err error) (pos, msg string) {
+ msg = err.Error()
+ if m := posMsgRx.FindStringSubmatch(msg); len(m) == 3 {
+ pos = m[1]
+ msg = m[2]
+ }
+ return
+}
+
+func parseFiles(t *testing.T, filenames []string) ([]*ast.File, []error) {
+ var files []*ast.File
+ var errlist []error
+ for _, filename := range filenames {
+ file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
+ if file == nil {
+ t.Fatalf("%s: %s", filename, err)
+ }
+ files = append(files, file)
+ if err != nil {
+ if list, _ := err.(scanner.ErrorList); len(list) > 0 {
+ for _, err := range list {
+ errlist = append(errlist, err)
+ }
+ } else {
+ errlist = append(errlist, err)
+ }
+ }
+ }
+ return files, errlist
+}
+
+// ERROR comments must start with text `ERROR "rx"` or `ERROR rx` where
+// rx is a regular expression that matches the expected error message.
+// Space around "rx" or rx is ignored. Use the form `ERROR HERE "rx"`
+// for error messages that are located immediately after rather than
+// at a token's position.
+//
+var errRx = regexp.MustCompile(`^ *ERROR *(HERE)? *"?([^"]*)"?`)
+
+// errMap collects the regular expressions of ERROR comments found
+// in files and returns them as a map of error positions to error messages.
+//
+func errMap(t *testing.T, testname string, files []*ast.File) map[string][]string {
+ // map of position strings to lists of error message patterns
+ errmap := make(map[string][]string)
+
+ for _, file := range files {
+ filename := fset.Position(file.Package).Filename
+ src, err := ioutil.ReadFile(filename)
+ if err != nil {
+ t.Fatalf("%s: could not read %s", testname, filename)
+ }
+
+ var s scanner.Scanner
+ s.Init(fset.AddFile(filename, -1, len(src)), src, nil, scanner.ScanComments)
+ var prev token.Pos // position of last non-comment, non-semicolon token
+ var here token.Pos // position immediately after the token at position prev
+
+ scanFile:
+ for {
+ pos, tok, lit := s.Scan()
+ switch tok {
+ case token.EOF:
+ break scanFile
+ case token.COMMENT:
+ if lit[1] == '*' {
+ lit = lit[:len(lit)-2] // strip trailing */
+ }
+ if s := errRx.FindStringSubmatch(lit[2:]); len(s) == 3 {
+ pos := prev
+ if s[1] == "HERE" {
+ pos = here
+ }
+ p := fset.Position(pos).String()
+ errmap[p] = append(errmap[p], strings.TrimSpace(s[2]))
+ }
+ case token.SEMICOLON:
+ // ignore automatically inserted semicolon
+ if lit == "\n" {
+ continue scanFile
+ }
+ fallthrough
+ default:
+ prev = pos
+ var l int // token length
+ if tok.IsLiteral() {
+ l = len(lit)
+ } else {
+ l = len(tok.String())
+ }
+ here = prev + token.Pos(l)
+ }
+ }
+ }
+
+ return errmap
+}
+
+func eliminate(t *testing.T, errmap map[string][]string, errlist []error) {
+ for _, err := range errlist {
+ pos, gotMsg := splitError(err)
+ list := errmap[pos]
+ index := -1 // list index of matching message, if any
+ // we expect one of the messages in list to match the error at pos
+ for i, wantRx := range list {
+ rx, err := regexp.Compile(wantRx)
+ if err != nil {
+ t.Errorf("%s: %v", pos, err)
+ continue
+ }
+ if rx.MatchString(gotMsg) {
+ index = i
+ break
+ }
+ }
+ if index >= 0 {
+ // eliminate from list
+ if n := len(list) - 1; n > 0 {
+ // not the last entry - swap in last element and shorten list by 1
+ list[index] = list[n]
+ errmap[pos] = list[:n]
+ } else {
+ // last entry - remove list from map
+ delete(errmap, pos)
+ }
+ } else {
+ t.Errorf("%s: no error expected: %q", pos, gotMsg)
+ }
+ }
+}
+
+func checkFiles(t *testing.T, testfiles []string) {
+ // parse files and collect parser errors
+ files, errlist := parseFiles(t, testfiles)
+
+ pkgName := "<no package>"
+ if len(files) > 0 {
+ pkgName = files[0].Name.Name
+ }
+
+ if *listErrors && len(errlist) > 0 {
+ t.Errorf("--- %s:", pkgName)
+ for _, err := range errlist {
+ t.Error(err)
+ }
+ }
+
+ // typecheck and collect typechecker errors
+ var conf Config
+ conf.Importer = importer.Default()
+ conf.Error = func(err error) {
+ if *listErrors {
+ t.Error(err)
+ return
+ }
+ // Ignore secondary error messages starting with "\t";
+ // they are clarifying messages for a primary error.
+ if !strings.Contains(err.Error(), ": \t") {
+ errlist = append(errlist, err)
+ }
+ }
+ conf.Check(pkgName, fset, files, nil)
+
+ if *listErrors {
+ return
+ }
+
+ // match and eliminate errors;
+ // we are expecting the following errors
+ errmap := errMap(t, pkgName, files)
+ eliminate(t, errmap, errlist)
+
+ // there should be no expected errors left
+ if len(errmap) > 0 {
+ t.Errorf("--- %s: %d source positions with expected (but not reported) errors:", pkgName, len(errmap))
+ for pos, list := range errmap {
+ for _, rx := range list {
+ t.Errorf("%s: %q", pos, rx)
+ }
+ }
+ }
+}
+
+func TestCheck(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // Declare builtins for testing.
+ DefPredeclaredTestFuncs()
+
+ // If explicit test files are specified, only check those.
+ if files := *testFiles; files != "" {
+ checkFiles(t, strings.Split(files, " "))
+ return
+ }
+
+ // Otherwise, run all the tests.
+ for _, files := range tests {
+ checkFiles(t, files)
+ }
+}
diff --git a/libgo/go/go/types/conversions.go b/libgo/go/go/types/conversions.go
new file mode 100644
index 00000000000..74826ce9345
--- /dev/null
+++ b/libgo/go/go/types/conversions.go
@@ -0,0 +1,146 @@
+// Copyright 2012 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 typechecking of conversions.
+
+package types
+
+import "go/constant"
+
+// Conversion type-checks the conversion T(x).
+// The result is in x.
+func (check *Checker) conversion(x *operand, T Type) {
+ constArg := x.mode == constant_
+
+ var ok bool
+ switch {
+ case constArg && isConstType(T):
+ // constant conversion
+ switch t := T.Underlying().(*Basic); {
+ case representableConst(x.val, check.conf, t.kind, &x.val):
+ ok = true
+ case isInteger(x.typ) && isString(t):
+ codepoint := int64(-1)
+ if i, ok := constant.Int64Val(x.val); ok {
+ codepoint = i
+ }
+ // If codepoint < 0 the absolute value is too large (or unknown) for
+ // conversion. This is the same as converting any other out-of-range
+ // value - let string(codepoint) do the work.
+ x.val = constant.MakeString(string(codepoint))
+ ok = true
+ }
+ case x.convertibleTo(check.conf, T):
+ // non-constant conversion
+ x.mode = value
+ ok = true
+ }
+
+ if !ok {
+ check.errorf(x.pos(), "cannot convert %s to %s", x, T)
+ x.mode = invalid
+ return
+ }
+
+ // The conversion argument types are final. For untyped values the
+ // conversion provides the type, per the spec: "A constant may be
+ // given a type explicitly by a constant declaration or conversion,...".
+ final := x.typ
+ if isUntyped(x.typ) {
+ final = T
+ // - For conversions to interfaces, use the argument's default type.
+ // - For conversions of untyped constants to non-constant types, also
+ // use the default type (e.g., []byte("foo") should report string
+ // not []byte as type for the constant "foo").
+ // - Keep untyped nil for untyped nil arguments.
+ if IsInterface(T) || constArg && !isConstType(T) {
+ final = defaultType(x.typ)
+ }
+ check.updateExprType(x.expr, final, true)
+ }
+
+ x.typ = T
+}
+
+func (x *operand) convertibleTo(conf *Config, T Type) bool {
+ // "x is assignable to T"
+ if x.assignableTo(conf, T) {
+ return true
+ }
+
+ // "x's type and T have identical underlying types"
+ V := x.typ
+ Vu := V.Underlying()
+ Tu := T.Underlying()
+ if Identical(Vu, Tu) {
+ return true
+ }
+
+ // "x's type and T are unnamed pointer types and their pointer base types have identical underlying types"
+ if V, ok := V.(*Pointer); ok {
+ if T, ok := T.(*Pointer); ok {
+ if Identical(V.base.Underlying(), T.base.Underlying()) {
+ return true
+ }
+ }
+ }
+
+ // "x's type and T are both integer or floating point types"
+ if (isInteger(V) || isFloat(V)) && (isInteger(T) || isFloat(T)) {
+ return true
+ }
+
+ // "x's type and T are both complex types"
+ if isComplex(V) && isComplex(T) {
+ return true
+ }
+
+ // "x is an integer or a slice of bytes or runes and T is a string type"
+ if (isInteger(V) || isBytesOrRunes(Vu)) && isString(T) {
+ return true
+ }
+
+ // "x is a string and T is a slice of bytes or runes"
+ if isString(V) && isBytesOrRunes(Tu) {
+ return true
+ }
+
+ // package unsafe:
+ // "any pointer or value of underlying type uintptr can be converted into a unsafe.Pointer"
+ if (isPointer(Vu) || isUintptr(Vu)) && isUnsafePointer(T) {
+ return true
+ }
+ // "and vice versa"
+ if isUnsafePointer(V) && (isPointer(Tu) || isUintptr(Tu)) {
+ return true
+ }
+
+ return false
+}
+
+func isUintptr(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.kind == Uintptr
+}
+
+func isUnsafePointer(typ Type) bool {
+ // TODO(gri): Is this (typ.Underlying() instead of just typ) correct?
+ // The spec does not say so, but gc claims it is. See also
+ // issue 6326.
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.kind == UnsafePointer
+}
+
+func isPointer(typ Type) bool {
+ _, ok := typ.Underlying().(*Pointer)
+ return ok
+}
+
+func isBytesOrRunes(typ Type) bool {
+ if s, ok := typ.(*Slice); ok {
+ t, ok := s.elem.Underlying().(*Basic)
+ return ok && (t.kind == Byte || t.kind == Rune)
+ }
+ return false
+}
diff --git a/libgo/go/go/types/decl.go b/libgo/go/go/types/decl.go
new file mode 100644
index 00000000000..8e9e5f36de4
--- /dev/null
+++ b/libgo/go/go/types/decl.go
@@ -0,0 +1,430 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import (
+ "go/ast"
+ "go/constant"
+ "go/token"
+)
+
+func (check *Checker) reportAltDecl(obj Object) {
+ if pos := obj.Pos(); pos.IsValid() {
+ // We use "other" rather than "previous" here because
+ // the first declaration seen may not be textually
+ // earlier in the source.
+ check.errorf(pos, "\tother declaration of %s", obj.Name()) // secondary error, \t indented
+ }
+}
+
+func (check *Checker) declare(scope *Scope, id *ast.Ident, obj Object, pos token.Pos) {
+ // spec: "The blank identifier, represented by the underscore
+ // character _, may be used in a declaration like any other
+ // identifier but the declaration does not introduce a new
+ // binding."
+ if obj.Name() != "_" {
+ if alt := scope.Insert(obj); alt != nil {
+ check.errorf(obj.Pos(), "%s redeclared in this block", obj.Name())
+ check.reportAltDecl(alt)
+ return
+ }
+ obj.setScopePos(pos)
+ }
+ if id != nil {
+ check.recordDef(id, obj)
+ }
+}
+
+// objDecl type-checks the declaration of obj in its respective (file) context.
+// See check.typ for the details on def and path.
+func (check *Checker) objDecl(obj Object, def *Named, path []*TypeName) {
+ if obj.Type() != nil {
+ return // already checked - nothing to do
+ }
+
+ if trace {
+ check.trace(obj.Pos(), "-- declaring %s", obj.Name())
+ check.indent++
+ defer func() {
+ check.indent--
+ check.trace(obj.Pos(), "=> %s", obj)
+ }()
+ }
+
+ d := check.objMap[obj]
+ if d == nil {
+ check.dump("%s: %s should have been declared", obj.Pos(), obj.Name())
+ unreachable()
+ }
+
+ // save/restore current context and setup object context
+ defer func(ctxt context) {
+ check.context = ctxt
+ }(check.context)
+ check.context = context{
+ scope: d.file,
+ }
+
+ // Const and var declarations must not have initialization
+ // cycles. We track them by remembering the current declaration
+ // in check.decl. Initialization expressions depending on other
+ // consts, vars, or functions, add dependencies to the current
+ // check.decl.
+ switch obj := obj.(type) {
+ case *Const:
+ check.decl = d // new package-level const decl
+ check.constDecl(obj, d.typ, d.init)
+ case *Var:
+ check.decl = d // new package-level var decl
+ check.varDecl(obj, d.lhs, d.typ, d.init)
+ case *TypeName:
+ // invalid recursive types are detected via path
+ check.typeDecl(obj, d.typ, def, path)
+ case *Func:
+ // functions may be recursive - no need to track dependencies
+ check.funcDecl(obj, d)
+ default:
+ unreachable()
+ }
+}
+
+func (check *Checker) constDecl(obj *Const, typ, init ast.Expr) {
+ assert(obj.typ == nil)
+
+ if obj.visited {
+ obj.typ = Typ[Invalid]
+ return
+ }
+ obj.visited = true
+
+ // use the correct value of iota
+ assert(check.iota == nil)
+ check.iota = obj.val
+ defer func() { check.iota = nil }()
+
+ // provide valid constant value under all circumstances
+ obj.val = constant.MakeUnknown()
+
+ // determine type, if any
+ if typ != nil {
+ t := check.typ(typ)
+ if !isConstType(t) {
+ check.errorf(typ.Pos(), "invalid constant type %s", t)
+ obj.typ = Typ[Invalid]
+ return
+ }
+ obj.typ = t
+ }
+
+ // check initialization
+ var x operand
+ if init != nil {
+ check.expr(&x, init)
+ }
+ check.initConst(obj, &x)
+}
+
+func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init ast.Expr) {
+ assert(obj.typ == nil)
+
+ if obj.visited {
+ obj.typ = Typ[Invalid]
+ return
+ }
+ obj.visited = true
+
+ // var declarations cannot use iota
+ assert(check.iota == nil)
+
+ // determine type, if any
+ if typ != nil {
+ obj.typ = check.typ(typ)
+ }
+
+ // check initialization
+ if init == nil {
+ if typ == nil {
+ // error reported before by arityMatch
+ obj.typ = Typ[Invalid]
+ }
+ return
+ }
+
+ if lhs == nil || len(lhs) == 1 {
+ assert(lhs == nil || lhs[0] == obj)
+ var x operand
+ check.expr(&x, init)
+ check.initVar(obj, &x, false)
+ return
+ }
+
+ if debug {
+ // obj must be one of lhs
+ found := false
+ for _, lhs := range lhs {
+ if obj == lhs {
+ found = true
+ break
+ }
+ }
+ if !found {
+ panic("inconsistent lhs")
+ }
+ }
+ check.initVars(lhs, []ast.Expr{init}, token.NoPos)
+}
+
+// underlying returns the underlying type of typ; possibly by following
+// forward chains of named types. Such chains only exist while named types
+// are incomplete.
+func underlying(typ Type) Type {
+ for {
+ n, _ := typ.(*Named)
+ if n == nil {
+ break
+ }
+ typ = n.underlying
+ }
+ return typ
+}
+
+func (n *Named) setUnderlying(typ Type) {
+ if n != nil {
+ n.underlying = typ
+ }
+}
+
+func (check *Checker) typeDecl(obj *TypeName, typ ast.Expr, def *Named, path []*TypeName) {
+ assert(obj.typ == nil)
+
+ // type declarations cannot use iota
+ assert(check.iota == nil)
+
+ named := &Named{obj: obj}
+ def.setUnderlying(named)
+ obj.typ = named // make sure recursive type declarations terminate
+
+ // determine underlying type of named
+ check.typExpr(typ, named, append(path, obj))
+
+ // The underlying type of named may be itself a named type that is
+ // incomplete:
+ //
+ // type (
+ // A B
+ // B *C
+ // C A
+ // )
+ //
+ // The type of C is the (named) type of A which is incomplete,
+ // and which has as its underlying type the named type B.
+ // Determine the (final, unnamed) underlying type by resolving
+ // any forward chain (they always end in an unnamed type).
+ named.underlying = underlying(named.underlying)
+
+ // check and add associated methods
+ // TODO(gri) It's easy to create pathological cases where the
+ // current approach is incorrect: In general we need to know
+ // and add all methods _before_ type-checking the type.
+ // See https://play.golang.org/p/WMpE0q2wK8
+ check.addMethodDecls(obj)
+}
+
+func (check *Checker) addMethodDecls(obj *TypeName) {
+ // get associated methods
+ methods := check.methods[obj.name]
+ if len(methods) == 0 {
+ return // no methods
+ }
+ delete(check.methods, obj.name)
+
+ // use an objset to check for name conflicts
+ var mset objset
+
+ // spec: "If the base type is a struct type, the non-blank method
+ // and field names must be distinct."
+ base := obj.typ.(*Named)
+ if t, _ := base.underlying.(*Struct); t != nil {
+ for _, fld := range t.fields {
+ if fld.name != "_" {
+ assert(mset.insert(fld) == nil)
+ }
+ }
+ }
+
+ // Checker.Files may be called multiple times; additional package files
+ // may add methods to already type-checked types. Add pre-existing methods
+ // so that we can detect redeclarations.
+ for _, m := range base.methods {
+ assert(m.name != "_")
+ assert(mset.insert(m) == nil)
+ }
+
+ // type-check methods
+ for _, m := range methods {
+ // spec: "For a base type, the non-blank names of methods bound
+ // to it must be unique."
+ if m.name != "_" {
+ if alt := mset.insert(m); alt != nil {
+ switch alt.(type) {
+ case *Var:
+ check.errorf(m.pos, "field and method with the same name %s", m.name)
+ case *Func:
+ check.errorf(m.pos, "method %s already declared for %s", m.name, base)
+ default:
+ unreachable()
+ }
+ check.reportAltDecl(alt)
+ continue
+ }
+ }
+ check.objDecl(m, nil, nil)
+ // methods with blank _ names cannot be found - don't keep them
+ if m.name != "_" {
+ base.methods = append(base.methods, m)
+ }
+ }
+}
+
+func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
+ assert(obj.typ == nil)
+
+ // func declarations cannot use iota
+ assert(check.iota == nil)
+
+ sig := new(Signature)
+ obj.typ = sig // guard against cycles
+ fdecl := decl.fdecl
+ check.funcType(sig, fdecl.Recv, fdecl.Type)
+ if sig.recv == nil && obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) {
+ check.errorf(fdecl.Pos(), "func init must have no arguments and no return values")
+ // ok to continue
+ }
+
+ // function body must be type-checked after global declarations
+ // (functions implemented elsewhere have no body)
+ if !check.conf.IgnoreFuncBodies && fdecl.Body != nil {
+ check.later(obj.name, decl, sig, fdecl.Body)
+ }
+}
+
+func (check *Checker) declStmt(decl ast.Decl) {
+ pkg := check.pkg
+
+ switch d := decl.(type) {
+ case *ast.BadDecl:
+ // ignore
+
+ case *ast.GenDecl:
+ var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
+ for iota, spec := range d.Specs {
+ switch s := spec.(type) {
+ case *ast.ValueSpec:
+ switch d.Tok {
+ case token.CONST:
+ // determine which init exprs to use
+ switch {
+ case s.Type != nil || len(s.Values) > 0:
+ last = s
+ case last == nil:
+ last = new(ast.ValueSpec) // make sure last exists
+ }
+
+ // declare all constants
+ lhs := make([]*Const, len(s.Names))
+ for i, name := range s.Names {
+ obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(iota)))
+ lhs[i] = obj
+
+ var init ast.Expr
+ if i < len(last.Values) {
+ init = last.Values[i]
+ }
+
+ check.constDecl(obj, last.Type, init)
+ }
+
+ check.arityMatch(s, last)
+
+ // spec: "The scope of a constant or variable identifier declared
+ // inside a function begins at the end of the ConstSpec or VarSpec
+ // (ShortVarDecl for short variable declarations) and ends at the
+ // end of the innermost containing block."
+ scopePos := s.End()
+ for i, name := range s.Names {
+ check.declare(check.scope, name, lhs[i], scopePos)
+ }
+
+ case token.VAR:
+ lhs0 := make([]*Var, len(s.Names))
+ for i, name := range s.Names {
+ lhs0[i] = NewVar(name.Pos(), pkg, name.Name, nil)
+ }
+
+ // initialize all variables
+ for i, obj := range lhs0 {
+ var lhs []*Var
+ var init ast.Expr
+ switch len(s.Values) {
+ case len(s.Names):
+ // lhs and rhs match
+ init = s.Values[i]
+ case 1:
+ // rhs is expected to be a multi-valued expression
+ lhs = lhs0
+ init = s.Values[0]
+ default:
+ if i < len(s.Values) {
+ init = s.Values[i]
+ }
+ }
+ check.varDecl(obj, lhs, s.Type, init)
+ if len(s.Values) == 1 {
+ // If we have a single lhs variable we are done either way.
+ // If we have a single rhs expression, it must be a multi-
+ // valued expression, in which case handling the first lhs
+ // variable will cause all lhs variables to have a type
+ // assigned, and we are done as well.
+ if debug {
+ for _, obj := range lhs0 {
+ assert(obj.typ != nil)
+ }
+ }
+ break
+ }
+ }
+
+ check.arityMatch(s, nil)
+
+ // declare all variables
+ // (only at this point are the variable scopes (parents) set)
+ scopePos := s.End() // see constant declarations
+ for i, name := range s.Names {
+ // see constant declarations
+ check.declare(check.scope, name, lhs0[i], scopePos)
+ }
+
+ default:
+ check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
+ }
+
+ case *ast.TypeSpec:
+ obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
+ // 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."
+ scopePos := s.Name.Pos()
+ check.declare(check.scope, s.Name, obj, scopePos)
+ check.typeDecl(obj, s.Type, nil, nil)
+
+ default:
+ check.invalidAST(s.Pos(), "const, type, or var declaration expected")
+ }
+ }
+
+ default:
+ check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
+ }
+}
diff --git a/libgo/go/go/types/errors.go b/libgo/go/go/types/errors.go
new file mode 100644
index 00000000000..0c0049b1f3e
--- /dev/null
+++ b/libgo/go/go/types/errors.go
@@ -0,0 +1,103 @@
+// Copyright 2012 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 various error reporters.
+
+package types
+
+import (
+ "fmt"
+ "go/ast"
+ "go/token"
+ "strings"
+)
+
+func assert(p bool) {
+ if !p {
+ panic("assertion failed")
+ }
+}
+
+func unreachable() {
+ panic("unreachable")
+}
+
+func (check *Checker) qualifier(pkg *Package) string {
+ if pkg != check.pkg {
+ return pkg.path
+ }
+ return ""
+}
+
+func (check *Checker) sprintf(format string, args ...interface{}) string {
+ for i, arg := range args {
+ switch a := arg.(type) {
+ case nil:
+ arg = "<nil>"
+ case operand:
+ panic("internal error: should always pass *operand")
+ case *operand:
+ arg = operandString(a, check.qualifier)
+ case token.Pos:
+ arg = check.fset.Position(a).String()
+ case ast.Expr:
+ arg = ExprString(a)
+ case Object:
+ arg = ObjectString(a, check.qualifier)
+ case Type:
+ arg = TypeString(a, check.qualifier)
+ }
+ args[i] = arg
+ }
+ return fmt.Sprintf(format, args...)
+}
+
+func (check *Checker) trace(pos token.Pos, format string, args ...interface{}) {
+ fmt.Printf("%s:\t%s%s\n",
+ check.fset.Position(pos),
+ strings.Repeat(". ", check.indent),
+ check.sprintf(format, args...),
+ )
+}
+
+// dump is only needed for debugging
+func (check *Checker) dump(format string, args ...interface{}) {
+ fmt.Println(check.sprintf(format, args...))
+}
+
+func (check *Checker) err(pos token.Pos, msg string, soft bool) {
+ err := Error{check.fset, pos, msg, soft}
+ if check.firstErr == nil {
+ check.firstErr = err
+ }
+ f := check.conf.Error
+ if f == nil {
+ panic(bailout{}) // report only first error
+ }
+ f(err)
+}
+
+func (check *Checker) error(pos token.Pos, msg string) {
+ check.err(pos, msg, false)
+}
+
+func (check *Checker) errorf(pos token.Pos, format string, args ...interface{}) {
+ check.err(pos, check.sprintf(format, args...), false)
+}
+
+func (check *Checker) softErrorf(pos token.Pos, format string, args ...interface{}) {
+ check.err(pos, check.sprintf(format, args...), true)
+}
+
+func (check *Checker) invalidAST(pos token.Pos, format string, args ...interface{}) {
+ check.errorf(pos, "invalid AST: "+format, args...)
+}
+
+func (check *Checker) invalidArg(pos token.Pos, format string, args ...interface{}) {
+ check.errorf(pos, "invalid argument: "+format, args...)
+}
+
+func (check *Checker) invalidOp(pos token.Pos, format string, args ...interface{}) {
+ check.errorf(pos, "invalid operation: "+format, args...)
+}
diff --git a/libgo/go/go/types/eval.go b/libgo/go/go/types/eval.go
new file mode 100644
index 00000000000..7b42ff1a9d0
--- /dev/null
+++ b/libgo/go/go/types/eval.go
@@ -0,0 +1,83 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import (
+ "fmt"
+ "go/parser"
+ "go/token"
+)
+
+// Eval returns the type and, if constant, the value for the
+// expression expr, evaluated at position pos of package pkg,
+// which must have been derived from type-checking an AST with
+// complete position information relative to the provided file
+// set.
+//
+// If the expression contains function literals, their bodies
+// are ignored (i.e., the bodies are not type-checked).
+//
+// If pkg == nil, the Universe scope is used and the provided
+// position pos is ignored. If pkg != nil, and pos is invalid,
+// the package scope is used. Otherwise, pos must belong to the
+// package.
+//
+// An error is returned if pos is not within the package or
+// if the node cannot be evaluated.
+//
+// Note: Eval should not be used instead of running Check to compute
+// types and values, but in addition to Check. Eval will re-evaluate
+// its argument each time, and it also does not know about the context
+// in which an expression is used (e.g., an assignment). Thus, top-
+// level untyped constants will return an untyped type rather then the
+// respective context-specific type.
+//
+func Eval(fset *token.FileSet, pkg *Package, pos token.Pos, expr string) (tv TypeAndValue, err error) {
+ // determine scope
+ var scope *Scope
+ if pkg == nil {
+ scope = Universe
+ pos = token.NoPos
+ } else if !pos.IsValid() {
+ scope = pkg.scope
+ } else {
+ // The package scope extent (position information) may be
+ // incorrect (files spread accross a wide range of fset
+ // positions) - ignore it and just consider its children
+ // (file scopes).
+ for _, fscope := range pkg.scope.children {
+ if scope = fscope.Innermost(pos); scope != nil {
+ break
+ }
+ }
+ if scope == nil || debug {
+ s := scope
+ for s != nil && s != pkg.scope {
+ s = s.parent
+ }
+ // s == nil || s == pkg.scope
+ if s == nil {
+ return TypeAndValue{}, fmt.Errorf("no position %s found in package %s", fset.Position(pos), pkg.name)
+ }
+ }
+ }
+
+ // parse expressions
+ node, err := parser.ParseExprFrom(fset, "eval", expr, 0)
+ if err != nil {
+ return TypeAndValue{}, err
+ }
+
+ // initialize checker
+ check := NewChecker(nil, fset, pkg, nil)
+ check.scope = scope
+ check.pos = pos
+ defer check.handleBailout(&err)
+
+ // evaluate node
+ var x operand
+ check.rawExpr(&x, node, nil)
+ return TypeAndValue{x.mode, x.typ, x.val}, err
+}
diff --git a/libgo/go/go/types/eval_test.go b/libgo/go/go/types/eval_test.go
new file mode 100644
index 00000000000..7e0be43e723
--- /dev/null
+++ b/libgo/go/go/types/eval_test.go
@@ -0,0 +1,188 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file contains tests for Eval.
+
+package types_test
+
+import (
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "internal/testenv"
+ "strings"
+ "testing"
+
+ . "go/types"
+)
+
+func testEval(t *testing.T, fset *token.FileSet, pkg *Package, pos token.Pos, expr string, typ Type, typStr, valStr string) {
+ gotTv, err := Eval(fset, pkg, pos, expr)
+ if err != nil {
+ t.Errorf("Eval(%q) failed: %s", expr, err)
+ return
+ }
+ if gotTv.Type == nil {
+ t.Errorf("Eval(%q) got nil type but no error", expr)
+ return
+ }
+
+ // compare types
+ if typ != nil {
+ // we have a type, check identity
+ if !Identical(gotTv.Type, typ) {
+ t.Errorf("Eval(%q) got type %s, want %s", expr, gotTv.Type, typ)
+ return
+ }
+ } else {
+ // we have a string, compare type string
+ gotStr := gotTv.Type.String()
+ if gotStr != typStr {
+ t.Errorf("Eval(%q) got type %s, want %s", expr, gotStr, typStr)
+ return
+ }
+ }
+
+ // compare values
+ gotStr := ""
+ if gotTv.Value != nil {
+ gotStr = gotTv.Value.String()
+ }
+ if gotStr != valStr {
+ t.Errorf("Eval(%q) got value %s, want %s", expr, gotStr, valStr)
+ }
+}
+
+func TestEvalBasic(t *testing.T) {
+ fset := token.NewFileSet()
+ for _, typ := range Typ[Bool : String+1] {
+ testEval(t, fset, nil, token.NoPos, typ.Name(), typ, "", "")
+ }
+}
+
+func TestEvalComposite(t *testing.T) {
+ fset := token.NewFileSet()
+ for _, test := range independentTestTypes {
+ testEval(t, fset, nil, token.NoPos, test.src, nil, test.str, "")
+ }
+}
+
+func TestEvalArith(t *testing.T) {
+ var tests = []string{
+ `true`,
+ `false == false`,
+ `12345678 + 87654321 == 99999999`,
+ `10 * 20 == 200`,
+ `(1<<1000)*2 >> 100 == 2<<900`,
+ `"foo" + "bar" == "foobar"`,
+ `"abc" <= "bcd"`,
+ `len([10]struct{}{}) == 2*5`,
+ }
+ fset := token.NewFileSet()
+ for _, test := range tests {
+ testEval(t, fset, nil, token.NoPos, test, Typ[UntypedBool], "", "true")
+ }
+}
+
+func TestEvalPos(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // The contents of /*-style comments are of the form
+ // expr => value, type
+ // where value may be the empty string.
+ // Each expr is evaluated at the position of the comment
+ // and the result is compared with the expected value
+ // and type.
+ var sources = []string{
+ `
+ package p
+ import "fmt"
+ import m "math"
+ const c = 3.0
+ type T []int
+ func f(a int, s string) float64 {
+ fmt.Println("calling f")
+ _ = m.Pi // use package math
+ const d int = c + 1
+ var x int
+ x = a + len(s)
+ return float64(x)
+ /* true => true, untyped bool */
+ /* fmt.Println => , func(a ...interface{}) (n int, err error) */
+ /* c => 3, untyped float */
+ /* T => , p.T */
+ /* a => , int */
+ /* s => , string */
+ /* d => 4, int */
+ /* x => , int */
+ /* d/c => 1, int */
+ /* c/2 => 3/2, untyped float */
+ /* m.Pi < m.E => false, untyped bool */
+ }
+ `,
+ `
+ package p
+ /* c => 3, untyped float */
+ type T1 /* T1 => , p.T1 */ struct {}
+ var v1 /* v1 => , int */ = 42
+ func /* f1 => , func(v1 float64) */ f1(v1 float64) {
+ /* f1 => , func(v1 float64) */
+ /* v1 => , float64 */
+ var c /* c => 3, untyped float */ = "foo" /* c => , string */
+ {
+ var c struct {
+ c /* c => , string */ int
+ }
+ /* c => , struct{c int} */
+ _ = c
+ }
+ _ = func(a, b, c int) /* c => , string */ {
+ /* c => , int */
+ }
+ _ = c
+ type FT /* FT => , p.FT */ interface{}
+ }
+ `,
+ `
+ package p
+ /* T => , p.T */
+ `,
+ }
+
+ fset := token.NewFileSet()
+ var files []*ast.File
+ for i, src := range sources {
+ file, err := parser.ParseFile(fset, "p", src, parser.ParseComments)
+ if err != nil {
+ t.Fatalf("could not parse file %d: %s", i, err)
+ }
+ files = append(files, file)
+ }
+
+ conf := Config{Importer: importer.Default()}
+ pkg, err := conf.Check("p", fset, files, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for _, file := range files {
+ for _, group := range file.Comments {
+ for _, comment := range group.List {
+ s := comment.Text
+ if len(s) >= 4 && s[:2] == "/*" && s[len(s)-2:] == "*/" {
+ str, typ := split(s[2:len(s)-2], ", ")
+ str, val := split(str, "=>")
+ testEval(t, fset, pkg, comment.Pos(), str, nil, typ, val)
+ }
+ }
+ }
+ }
+}
+
+// split splits string s at the first occurrence of s.
+func split(s, sep string) (string, string) {
+ i := strings.Index(s, sep)
+ return strings.TrimSpace(s[:i]), strings.TrimSpace(s[i+len(sep):])
+}
diff --git a/libgo/go/go/types/example_test.go b/libgo/go/go/types/example_test.go
new file mode 100644
index 00000000000..9daad8765a8
--- /dev/null
+++ b/libgo/go/go/types/example_test.go
@@ -0,0 +1,312 @@
+// Copyright 2015 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.
+
+// Only run where builders (build.golang.org) have
+// access to compiled packages for import.
+//
+// +build ignore,!arm,!arm64,!nacl
+
+package types_test
+
+// This file shows examples of basic usage of the go/types API.
+//
+// To locate a Go package, use (*go/build.Context).Import.
+// To load, parse, and type-check a complete Go program
+// from source, use golang.org/x/tools/go/loader.
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/format"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "go/types"
+ "log"
+ "regexp"
+ "sort"
+ "strings"
+)
+
+// ExampleScope prints the tree of Scopes of a package created from a
+// set of parsed files.
+func ExampleScope() {
+ // Parse the source files for a package.
+ fset := token.NewFileSet()
+ var files []*ast.File
+ for _, file := range []struct{ name, input string }{
+ {"main.go", `
+package main
+import "fmt"
+func main() {
+ freezing := FToC(-18)
+ fmt.Println(freezing, Boiling) }
+`},
+ {"celsius.go", `
+package main
+import "fmt"
+type Celsius float64
+func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
+func FToC(f float64) Celsius { return Celsius(f - 32 / 9 * 5) }
+const Boiling Celsius = 100
+`},
+ } {
+ f, err := parser.ParseFile(fset, file.name, file.input, 0)
+ if err != nil {
+ log.Fatal(err)
+ }
+ files = append(files, f)
+ }
+
+ // Type-check a package consisting of these files.
+ // Type information for the imported "fmt" package
+ // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a.
+ conf := types.Config{Importer: importer.Default()}
+ pkg, err := conf.Check("temperature", fset, files, nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Print the tree of scopes.
+ // For determinism, we redact addresses.
+ var buf bytes.Buffer
+ pkg.Scope().WriteTo(&buf, 0, true)
+ rx := regexp.MustCompile(` 0x[a-fA-F0-9]*`)
+ fmt.Println(rx.ReplaceAllString(buf.String(), ""))
+
+ // Output:
+ // package "temperature" scope {
+ // . const temperature.Boiling temperature.Celsius
+ // . type temperature.Celsius float64
+ // . func temperature.FToC(f float64) temperature.Celsius
+ // . func temperature.main()
+ //
+ // . main.go scope {
+ // . . package fmt
+ //
+ // . . function scope {
+ // . . . var freezing temperature.Celsius
+ // . . }. }
+ // . celsius.go scope {
+ // . . package fmt
+ //
+ // . . function scope {
+ // . . . var c temperature.Celsius
+ // . . }
+ // . . function scope {
+ // . . . var f float64
+ // . . }. }}
+}
+
+// ExampleMethodSet prints the method sets of various types.
+func ExampleMethodSet() {
+ // Parse a single source file.
+ const input = `
+package temperature
+import "fmt"
+type Celsius float64
+func (c Celsius) String() string { return fmt.Sprintf("%g°C", c) }
+func (c *Celsius) SetF(f float64) { *c = Celsius(f - 32 / 9 * 5) }
+`
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "celsius.go", input, 0)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Type-check a package consisting of this file.
+ // Type information for the imported packages
+ // comes from $GOROOT/pkg/$GOOS_$GOOARCH/fmt.a.
+ conf := types.Config{Importer: importer.Default()}
+ pkg, err := conf.Check("temperature", fset, []*ast.File{f}, nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Print the method sets of Celsius and *Celsius.
+ celsius := pkg.Scope().Lookup("Celsius").Type()
+ for _, t := range []types.Type{celsius, types.NewPointer(celsius)} {
+ fmt.Printf("Method set of %s:\n", t)
+ mset := types.NewMethodSet(t)
+ for i := 0; i < mset.Len(); i++ {
+ fmt.Println(mset.At(i))
+ }
+ fmt.Println()
+ }
+
+ // Output:
+ // Method set of temperature.Celsius:
+ // method (temperature.Celsius) String() string
+ //
+ // Method set of *temperature.Celsius:
+ // method (*temperature.Celsius) SetF(f float64)
+ // method (*temperature.Celsius) String() string
+}
+
+// ExampleInfo prints various facts recorded by the type checker in a
+// types.Info struct: definitions of and references to each named object,
+// and the type, value, and mode of every expression in the package.
+func ExampleInfo() {
+ // Parse a single source file.
+ const input = `
+package fib
+
+type S string
+
+var a, b, c = len(b), S(c), "hello"
+
+func fib(x int) int {
+ if x < 2 {
+ return x
+ }
+ return fib(x-1) - fib(x-2)
+}`
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "fib.go", input, 0)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Type-check the package.
+ // We create an empty map for each kind of input
+ // we're interested in, and Check populates them.
+ info := types.Info{
+ Types: make(map[ast.Expr]types.TypeAndValue),
+ Defs: make(map[*ast.Ident]types.Object),
+ Uses: make(map[*ast.Ident]types.Object),
+ }
+ var conf types.Config
+ pkg, err := conf.Check("fib", fset, []*ast.File{f}, &info)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Print package-level variables in initialization order.
+ fmt.Printf("InitOrder: %v\n\n", info.InitOrder)
+
+ // For each named object, print the line and
+ // column of its definition and each of its uses.
+ fmt.Println("Defs and Uses of each named object:")
+ usesByObj := make(map[types.Object][]string)
+ for id, obj := range info.Uses {
+ posn := fset.Position(id.Pos())
+ lineCol := fmt.Sprintf("%d:%d", posn.Line, posn.Column)
+ usesByObj[obj] = append(usesByObj[obj], lineCol)
+ }
+ var items []string
+ for obj, uses := range usesByObj {
+ sort.Strings(uses)
+ item := fmt.Sprintf("%s:\n defined at %s\n used at %s",
+ types.ObjectString(obj, types.RelativeTo(pkg)),
+ fset.Position(obj.Pos()),
+ strings.Join(uses, ", "))
+ items = append(items, item)
+ }
+ sort.Strings(items) // sort by line:col, in effect
+ fmt.Println(strings.Join(items, "\n"))
+ fmt.Println()
+
+ fmt.Println("Types and Values of each expression:")
+ items = nil
+ for expr, tv := range info.Types {
+ var buf bytes.Buffer
+ posn := fset.Position(expr.Pos())
+ tvstr := tv.Type.String()
+ if tv.Value != nil {
+ tvstr += " = " + tv.Value.String()
+ }
+ // line:col | expr | mode : type = value
+ fmt.Fprintf(&buf, "%2d:%2d | %-19s | %-7s : %s",
+ posn.Line, posn.Column, exprString(fset, expr),
+ mode(tv), tvstr)
+ items = append(items, buf.String())
+ }
+ sort.Strings(items)
+ fmt.Println(strings.Join(items, "\n"))
+
+ // Output:
+ // InitOrder: [c = "hello" b = S(c) a = len(b)]
+ //
+ // Defs and Uses of each named object:
+ // builtin len:
+ // defined at -
+ // used at 6:15
+ // func fib(x int) int:
+ // defined at fib.go:8:6
+ // used at 12:20, 12:9
+ // type S string:
+ // defined at fib.go:4:6
+ // used at 6:23
+ // type int int:
+ // defined at -
+ // used at 8:12, 8:17
+ // type string string:
+ // defined at -
+ // used at 4:8
+ // var b S:
+ // defined at fib.go:6:8
+ // used at 6:19
+ // var c string:
+ // defined at fib.go:6:11
+ // used at 6:25
+ // var x int:
+ // defined at fib.go:8:10
+ // used at 10:10, 12:13, 12:24, 9:5
+ //
+ // Types and Values of each expression:
+ // 4: 8 | string | type : string
+ // 6:15 | len | builtin : func(string) int
+ // 6:15 | len(b) | value : int
+ // 6:19 | b | var : fib.S
+ // 6:23 | S | type : fib.S
+ // 6:23 | S(c) | value : fib.S
+ // 6:25 | c | var : string
+ // 6:29 | "hello" | value : string = "hello"
+ // 8:12 | int | type : int
+ // 8:17 | int | type : int
+ // 9: 5 | x | var : int
+ // 9: 5 | x < 2 | value : untyped bool
+ // 9: 9 | 2 | value : int = 2
+ // 10:10 | x | var : int
+ // 12: 9 | fib | value : func(x int) int
+ // 12: 9 | fib(x - 1) | value : int
+ // 12: 9 | fib(x-1) - fib(x-2) | value : int
+ // 12:13 | x | var : int
+ // 12:13 | x - 1 | value : int
+ // 12:15 | 1 | value : int = 1
+ // 12:20 | fib | value : func(x int) int
+ // 12:20 | fib(x - 2) | value : int
+ // 12:24 | x | var : int
+ // 12:24 | x - 2 | value : int
+ // 12:26 | 2 | value : int = 2
+}
+
+func mode(tv types.TypeAndValue) string {
+ switch {
+ case tv.IsVoid():
+ return "void"
+ case tv.IsType():
+ return "type"
+ case tv.IsBuiltin():
+ return "builtin"
+ case tv.IsNil():
+ return "nil"
+ case tv.Assignable():
+ if tv.Addressable() {
+ return "var"
+ }
+ return "mapindex"
+ case tv.IsValue():
+ return "value"
+ default:
+ return "unknown"
+ }
+}
+
+func exprString(fset *token.FileSet, expr ast.Expr) string {
+ var buf bytes.Buffer
+ format.Node(&buf, fset, expr)
+ return buf.String()
+}
diff --git a/libgo/go/go/types/expr.go b/libgo/go/go/types/expr.go
new file mode 100644
index 00000000000..7d00dd5fa5d
--- /dev/null
+++ b/libgo/go/go/types/expr.go
@@ -0,0 +1,1496 @@
+// Copyright 2012 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 typechecking of expressions.
+
+package types
+
+import (
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "go/token"
+ "math"
+)
+
+/*
+Basic algorithm:
+
+Expressions are checked recursively, top down. Expression checker functions
+are generally of the form:
+
+ func f(x *operand, e *ast.Expr, ...)
+
+where e is the expression to be checked, and x is the result of the check.
+The check performed by f may fail in which case x.mode == invalid, and
+related error messages will have been issued by f.
+
+If a hint argument is present, it is the composite literal element type
+of an outer composite literal; it is used to type-check composite literal
+elements that have no explicit type specification in the source
+(e.g.: []T{{...}, {...}}, the hint is the type T in this case).
+
+All expressions are checked via rawExpr, which dispatches according
+to expression kind. Upon returning, rawExpr is recording the types and
+constant values for all expressions that have an untyped type (those types
+may change on the way up in the expression tree). Usually these are constants,
+but the results of comparisons or non-constant shifts of untyped constants
+may also be untyped, but not constant.
+
+Untyped expressions may eventually become fully typed (i.e., not untyped),
+typically when the value is assigned to a variable, or is used otherwise.
+The updateExprType method is used to record this final type and update
+the recorded types: the type-checked expression tree is again traversed down,
+and the new type is propagated as needed. Untyped constant expression values
+that become fully typed must now be representable by the full type (constant
+sub-expression trees are left alone except for their roots). This mechanism
+ensures that a client sees the actual (run-time) type an untyped value would
+have. It also permits type-checking of lhs shift operands "as if the shift
+were not present": when updateExprType visits an untyped lhs shift operand
+and assigns it it's final type, that type must be an integer type, and a
+constant lhs must be representable as an integer.
+
+When an expression gets its final type, either on the way out from rawExpr,
+on the way down in updateExprType, or at the end of the type checker run,
+the type (and constant value, if any) is recorded via Info.Types, if present.
+*/
+
+type opPredicates map[token.Token]func(Type) bool
+
+var unaryOpPredicates = opPredicates{
+ token.ADD: isNumeric,
+ token.SUB: isNumeric,
+ token.XOR: isInteger,
+ token.NOT: isBoolean,
+}
+
+func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool {
+ if pred := m[op]; pred != nil {
+ if !pred(x.typ) {
+ check.invalidOp(x.pos(), "operator %s not defined for %s", op, x)
+ return false
+ }
+ } else {
+ check.invalidAST(x.pos(), "unknown operator %s", op)
+ return false
+ }
+ return true
+}
+
+// The unary expression e may be nil. It's passed in for better error messages only.
+func (check *Checker) unary(x *operand, e *ast.UnaryExpr, op token.Token) {
+ switch op {
+ case token.AND:
+ // spec: "As an exception to the addressability
+ // requirement x may also be a composite literal."
+ if _, ok := unparen(x.expr).(*ast.CompositeLit); !ok && x.mode != variable {
+ check.invalidOp(x.pos(), "cannot take address of %s", x)
+ x.mode = invalid
+ return
+ }
+ x.mode = value
+ x.typ = &Pointer{base: x.typ}
+ return
+
+ case token.ARROW:
+ typ, ok := x.typ.Underlying().(*Chan)
+ if !ok {
+ check.invalidOp(x.pos(), "cannot receive from non-channel %s", x)
+ x.mode = invalid
+ return
+ }
+ if typ.dir == SendOnly {
+ check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x)
+ x.mode = invalid
+ return
+ }
+ x.mode = commaok
+ x.typ = typ.elem
+ check.hasCallOrRecv = true
+ return
+ }
+
+ if !check.op(unaryOpPredicates, x, op) {
+ x.mode = invalid
+ return
+ }
+
+ if x.mode == constant_ {
+ typ := x.typ.Underlying().(*Basic)
+ var prec uint
+ if isUnsigned(typ) {
+ prec = uint(check.conf.sizeof(typ) * 8)
+ }
+ x.val = constant.UnaryOp(op, x.val, prec)
+ // Typed constants must be representable in
+ // their type after each constant operation.
+ if isTyped(typ) {
+ if e != nil {
+ x.expr = e // for better error message
+ }
+ check.representable(x, typ)
+ }
+ return
+ }
+
+ x.mode = value
+ // x.typ remains unchanged
+}
+
+func isShift(op token.Token) bool {
+ return op == token.SHL || op == token.SHR
+}
+
+func isComparison(op token.Token) bool {
+ // Note: tokens are not ordered well to make this much easier
+ switch op {
+ case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
+ return true
+ }
+ return false
+}
+
+func fitsFloat32(x constant.Value) bool {
+ f32, _ := constant.Float32Val(x)
+ f := float64(f32)
+ return !math.IsInf(f, 0)
+}
+
+func roundFloat32(x constant.Value) constant.Value {
+ f32, _ := constant.Float32Val(x)
+ f := float64(f32)
+ if !math.IsInf(f, 0) {
+ return constant.MakeFloat64(f)
+ }
+ return nil
+}
+
+func fitsFloat64(x constant.Value) bool {
+ f, _ := constant.Float64Val(x)
+ return !math.IsInf(f, 0)
+}
+
+func roundFloat64(x constant.Value) constant.Value {
+ f, _ := constant.Float64Val(x)
+ if !math.IsInf(f, 0) {
+ return constant.MakeFloat64(f)
+ }
+ return nil
+}
+
+// representableConst reports whether x can be represented as
+// value of the given basic type kind and for the configuration
+// provided (only needed for int/uint sizes).
+//
+// If rounded != nil, *rounded is set to the rounded value of x for
+// representable floating-point values; it is left alone otherwise.
+// It is ok to provide the addressof the first argument for rounded.
+func representableConst(x constant.Value, conf *Config, as BasicKind, rounded *constant.Value) bool {
+ switch x.Kind() {
+ case constant.Unknown:
+ return true
+
+ case constant.Bool:
+ return as == Bool || as == UntypedBool
+
+ case constant.Int:
+ if x, ok := constant.Int64Val(x); ok {
+ switch as {
+ case Int:
+ var s = uint(conf.sizeof(Typ[as])) * 8
+ return int64(-1)<<(s-1) <= x && x <= int64(1)<<(s-1)-1
+ case Int8:
+ const s = 8
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+ case Int16:
+ const s = 16
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+ case Int32:
+ const s = 32
+ return -1<<(s-1) <= x && x <= 1<<(s-1)-1
+ case Int64:
+ return true
+ case Uint, Uintptr:
+ if s := uint(conf.sizeof(Typ[as])) * 8; s < 64 {
+ return 0 <= x && x <= int64(1)<<s-1
+ }
+ return 0 <= x
+ case Uint8:
+ const s = 8
+ return 0 <= x && x <= 1<<s-1
+ case Uint16:
+ const s = 16
+ return 0 <= x && x <= 1<<s-1
+ case Uint32:
+ const s = 32
+ return 0 <= x && x <= 1<<s-1
+ case Uint64:
+ return 0 <= x
+ case Float32, Float64, Complex64, Complex128,
+ UntypedInt, UntypedFloat, UntypedComplex:
+ return true
+ }
+ }
+
+ n := constant.BitLen(x)
+ switch as {
+ case Uint, Uintptr:
+ var s = uint(conf.sizeof(Typ[as])) * 8
+ return constant.Sign(x) >= 0 && n <= int(s)
+ case Uint64:
+ return constant.Sign(x) >= 0 && n <= 64
+ case Float32, Complex64:
+ if rounded == nil {
+ return fitsFloat32(x)
+ }
+ r := roundFloat32(x)
+ if r != nil {
+ *rounded = r
+ return true
+ }
+ case Float64, Complex128:
+ if rounded == nil {
+ return fitsFloat64(x)
+ }
+ r := roundFloat64(x)
+ if r != nil {
+ *rounded = r
+ return true
+ }
+ case UntypedInt, UntypedFloat, UntypedComplex:
+ return true
+ }
+
+ case constant.Float:
+ switch as {
+ case Float32, Complex64:
+ if rounded == nil {
+ return fitsFloat32(x)
+ }
+ r := roundFloat32(x)
+ if r != nil {
+ *rounded = r
+ return true
+ }
+ case Float64, Complex128:
+ if rounded == nil {
+ return fitsFloat64(x)
+ }
+ r := roundFloat64(x)
+ if r != nil {
+ *rounded = r
+ return true
+ }
+ case UntypedFloat, UntypedComplex:
+ return true
+ }
+
+ case constant.Complex:
+ switch as {
+ case Complex64:
+ if rounded == nil {
+ return fitsFloat32(constant.Real(x)) && fitsFloat32(constant.Imag(x))
+ }
+ re := roundFloat32(constant.Real(x))
+ im := roundFloat32(constant.Imag(x))
+ if re != nil && im != nil {
+ *rounded = constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
+ return true
+ }
+ case Complex128:
+ if rounded == nil {
+ return fitsFloat64(constant.Real(x)) && fitsFloat64(constant.Imag(x))
+ }
+ re := roundFloat64(constant.Real(x))
+ im := roundFloat64(constant.Imag(x))
+ if re != nil && im != nil {
+ *rounded = constant.BinaryOp(re, token.ADD, constant.MakeImag(im))
+ return true
+ }
+ case UntypedComplex:
+ return true
+ }
+
+ case constant.String:
+ return as == String || as == UntypedString
+
+ default:
+ unreachable()
+ }
+
+ return false
+}
+
+// representable checks that a constant operand is representable in the given basic type.
+func (check *Checker) representable(x *operand, typ *Basic) {
+ assert(x.mode == constant_)
+ if !representableConst(x.val, check.conf, typ.kind, &x.val) {
+ var msg string
+ if isNumeric(x.typ) && isNumeric(typ) {
+ // numeric conversion : error msg
+ //
+ // integer -> integer : overflows
+ // integer -> float : overflows (actually not possible)
+ // float -> integer : truncated
+ // float -> float : overflows
+ //
+ if !isInteger(x.typ) && isInteger(typ) {
+ msg = "%s truncated to %s"
+ } else {
+ msg = "%s overflows %s"
+ }
+ } else {
+ msg = "cannot convert %s to %s"
+ }
+ check.errorf(x.pos(), msg, x, typ)
+ x.mode = invalid
+ }
+}
+
+// updateExprType updates the type of x to typ and invokes itself
+// recursively for the operands of x, depending on expression kind.
+// If typ is still an untyped and not the final type, updateExprType
+// only updates the recorded untyped type for x and possibly its
+// operands. Otherwise (i.e., typ is not an untyped type anymore,
+// or it is the final type for x), the type and value are recorded.
+// Also, if x is a constant, it must be representable as a value of typ,
+// and if x is the (formerly untyped) lhs operand of a non-constant
+// shift, it must be an integer value.
+//
+func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) {
+ old, found := check.untyped[x]
+ if !found {
+ return // nothing to do
+ }
+
+ // update operands of x if necessary
+ switch x := x.(type) {
+ case *ast.BadExpr,
+ *ast.FuncLit,
+ *ast.CompositeLit,
+ *ast.IndexExpr,
+ *ast.SliceExpr,
+ *ast.TypeAssertExpr,
+ *ast.StarExpr,
+ *ast.KeyValueExpr,
+ *ast.ArrayType,
+ *ast.StructType,
+ *ast.FuncType,
+ *ast.InterfaceType,
+ *ast.MapType,
+ *ast.ChanType:
+ // These expression are never untyped - nothing to do.
+ // The respective sub-expressions got their final types
+ // upon assignment or use.
+ if debug {
+ check.dump("%s: found old type(%s): %s (new: %s)", x.Pos(), x, old.typ, typ)
+ unreachable()
+ }
+ return
+
+ case *ast.CallExpr:
+ // Resulting in an untyped constant (e.g., built-in complex).
+ // The respective calls take care of calling updateExprType
+ // for the arguments if necessary.
+
+ case *ast.Ident, *ast.BasicLit, *ast.SelectorExpr:
+ // An identifier denoting a constant, a constant literal,
+ // or a qualified identifier (imported untyped constant).
+ // No operands to take care of.
+
+ case *ast.ParenExpr:
+ check.updateExprType(x.X, typ, final)
+
+ case *ast.UnaryExpr:
+ // If x is a constant, the operands were constants.
+ // They don't need to be updated since they never
+ // get "materialized" into a typed value; and they
+ // will be processed at the end of the type check.
+ if old.val != nil {
+ break
+ }
+ check.updateExprType(x.X, typ, final)
+
+ case *ast.BinaryExpr:
+ if old.val != nil {
+ break // see comment for unary expressions
+ }
+ if isComparison(x.Op) {
+ // The result type is independent of operand types
+ // and the operand types must have final types.
+ } else if isShift(x.Op) {
+ // The result type depends only on lhs operand.
+ // The rhs type was updated when checking the shift.
+ check.updateExprType(x.X, typ, final)
+ } else {
+ // The operand types match the result type.
+ check.updateExprType(x.X, typ, final)
+ check.updateExprType(x.Y, typ, final)
+ }
+
+ default:
+ unreachable()
+ }
+
+ // If the new type is not final and still untyped, just
+ // update the recorded type.
+ if !final && isUntyped(typ) {
+ old.typ = typ.Underlying().(*Basic)
+ check.untyped[x] = old
+ return
+ }
+
+ // Otherwise we have the final (typed or untyped type).
+ // Remove it from the map of yet untyped expressions.
+ delete(check.untyped, x)
+
+ // If x is the lhs of a shift, its final type must be integer.
+ // We already know from the shift check that it is representable
+ // as an integer if it is a constant.
+ if old.isLhs && !isInteger(typ) {
+ check.invalidOp(x.Pos(), "shifted operand %s (type %s) must be integer", x, typ)
+ return
+ }
+
+ // Everything's fine, record final type and value for x.
+ check.recordTypeAndValue(x, old.mode, typ, old.val)
+}
+
+// updateExprVal updates the value of x to val.
+func (check *Checker) updateExprVal(x ast.Expr, val constant.Value) {
+ if info, ok := check.untyped[x]; ok {
+ info.val = val
+ check.untyped[x] = info
+ }
+}
+
+// convertUntyped attempts to set the type of an untyped value to the target type.
+func (check *Checker) convertUntyped(x *operand, target Type) {
+ if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] {
+ return
+ }
+
+ // TODO(gri) Sloppy code - clean up. This function is central
+ // to assignment and expression checking.
+
+ if isUntyped(target) {
+ // both x and target are untyped
+ xkind := x.typ.(*Basic).kind
+ tkind := target.(*Basic).kind
+ if isNumeric(x.typ) && isNumeric(target) {
+ if xkind < tkind {
+ x.typ = target
+ check.updateExprType(x.expr, target, false)
+ }
+ } else if xkind != tkind {
+ goto Error
+ }
+ return
+ }
+
+ // typed target
+ switch t := target.Underlying().(type) {
+ case *Basic:
+ if x.mode == constant_ {
+ check.representable(x, t)
+ if x.mode == invalid {
+ return
+ }
+ // expression value may have been rounded - update if needed
+ // TODO(gri) A floating-point value may silently underflow to
+ // zero. If it was negative, the sign is lost. See issue 6898.
+ check.updateExprVal(x.expr, x.val)
+ } else {
+ // Non-constant untyped values may appear as the
+ // result of comparisons (untyped bool), intermediate
+ // (delayed-checked) rhs operands of shifts, and as
+ // the value nil.
+ switch x.typ.(*Basic).kind {
+ case UntypedBool:
+ if !isBoolean(target) {
+ goto Error
+ }
+ case UntypedInt, UntypedRune, UntypedFloat, UntypedComplex:
+ if !isNumeric(target) {
+ goto Error
+ }
+ case UntypedString:
+ // Non-constant untyped string values are not
+ // permitted by the spec and should not occur.
+ unreachable()
+ case UntypedNil:
+ // Unsafe.Pointer is a basic type that includes nil.
+ if !hasNil(target) {
+ goto Error
+ }
+ default:
+ goto Error
+ }
+ }
+ case *Interface:
+ if !x.isNil() && !t.Empty() /* empty interfaces are ok */ {
+ goto Error
+ }
+ // Update operand types to the default type rather then
+ // the target (interface) type: values must have concrete
+ // dynamic types. If the value is nil, keep it untyped
+ // (this is important for tools such as go vet which need
+ // the dynamic type for argument checking of say, print
+ // functions)
+ if x.isNil() {
+ target = Typ[UntypedNil]
+ } else {
+ // cannot assign untyped values to non-empty interfaces
+ if !t.Empty() {
+ goto Error
+ }
+ target = defaultType(x.typ)
+ }
+ case *Pointer, *Signature, *Slice, *Map, *Chan:
+ if !x.isNil() {
+ goto Error
+ }
+ // keep nil untyped - see comment for interfaces, above
+ target = Typ[UntypedNil]
+ default:
+ goto Error
+ }
+
+ x.typ = target
+ check.updateExprType(x.expr, target, true) // UntypedNils are final
+ return
+
+Error:
+ check.errorf(x.pos(), "cannot convert %s to %s", x, target)
+ x.mode = invalid
+}
+
+func (check *Checker) comparison(x, y *operand, op token.Token) {
+ // spec: "In any comparison, the first operand must be assignable
+ // to the type of the second operand, or vice versa."
+ err := ""
+ if x.assignableTo(check.conf, y.typ) || y.assignableTo(check.conf, x.typ) {
+ defined := false
+ switch op {
+ case token.EQL, token.NEQ:
+ // spec: "The equality operators == and != apply to operands that are comparable."
+ defined = Comparable(x.typ) || x.isNil() && hasNil(y.typ) || y.isNil() && hasNil(x.typ)
+ case token.LSS, token.LEQ, token.GTR, token.GEQ:
+ // spec: The ordering operators <, <=, >, and >= apply to operands that are ordered."
+ defined = isOrdered(x.typ)
+ default:
+ unreachable()
+ }
+ if !defined {
+ typ := x.typ
+ if x.isNil() {
+ typ = y.typ
+ }
+ err = check.sprintf("operator %s not defined for %s", op, typ)
+ }
+ } else {
+ err = check.sprintf("mismatched types %s and %s", x.typ, y.typ)
+ }
+
+ if err != "" {
+ check.errorf(x.pos(), "cannot compare %s %s %s (%s)", x.expr, op, y.expr, err)
+ x.mode = invalid
+ return
+ }
+
+ if x.mode == constant_ && y.mode == constant_ {
+ x.val = constant.MakeBool(constant.Compare(x.val, op, y.val))
+ // The operands are never materialized; no need to update
+ // their types.
+ } else {
+ x.mode = value
+ // The operands have now their final types, which at run-
+ // time will be materialized. Update the expression trees.
+ // If the current types are untyped, the materialized type
+ // is the respective default type.
+ check.updateExprType(x.expr, defaultType(x.typ), true)
+ check.updateExprType(y.expr, defaultType(y.typ), true)
+ }
+
+ // spec: "Comparison operators compare two operands and yield
+ // an untyped boolean value."
+ x.typ = Typ[UntypedBool]
+}
+
+func (check *Checker) shift(x, y *operand, op token.Token) {
+ untypedx := isUntyped(x.typ)
+
+ // The lhs must be of integer type or be representable
+ // as an integer; otherwise the shift has no chance.
+ if !x.isInteger() {
+ check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
+ x.mode = invalid
+ return
+ }
+
+ // spec: "The right operand in a shift expression must have unsigned
+ // integer type or be an untyped constant that can be converted to
+ // unsigned integer type."
+ switch {
+ case isInteger(y.typ) && isUnsigned(y.typ):
+ // nothing to do
+ case isUntyped(y.typ):
+ check.convertUntyped(y, Typ[UntypedInt])
+ if y.mode == invalid {
+ x.mode = invalid
+ return
+ }
+ default:
+ check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y)
+ x.mode = invalid
+ return
+ }
+
+ if x.mode == constant_ {
+ if y.mode == constant_ {
+ // rhs must be an integer value
+ if !y.isInteger() {
+ check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y)
+ x.mode = invalid
+ return
+ }
+ // rhs must be within reasonable bounds
+ const stupidShift = 1023 - 1 + 52 // so we can express smallestFloat64
+ s, ok := constant.Uint64Val(y.val)
+ if !ok || s > stupidShift {
+ check.invalidOp(y.pos(), "stupid shift count %s", y)
+ x.mode = invalid
+ return
+ }
+ // The lhs is representable as an integer but may not be an integer
+ // (e.g., 2.0, an untyped float) - this can only happen for untyped
+ // non-integer numeric constants. Correct the type so that the shift
+ // result is of integer type.
+ if !isInteger(x.typ) {
+ x.typ = Typ[UntypedInt]
+ }
+ x.val = constant.Shift(x.val, op, uint(s))
+ return
+ }
+
+ // non-constant shift with constant lhs
+ if untypedx {
+ // spec: "If the left operand of a non-constant shift
+ // expression is an untyped constant, the type of the
+ // constant is what it would be if the shift expression
+ // were replaced by its left operand alone.".
+ //
+ // Delay operand checking until we know the final type:
+ // The lhs expression must be in the untyped map, mark
+ // the entry as lhs shift operand.
+ info, found := check.untyped[x.expr]
+ assert(found)
+ info.isLhs = true
+ check.untyped[x.expr] = info
+ // keep x's type
+ x.mode = value
+ return
+ }
+ }
+
+ // constant rhs must be >= 0
+ if y.mode == constant_ && constant.Sign(y.val) < 0 {
+ check.invalidOp(y.pos(), "shift count %s must not be negative", y)
+ }
+
+ // non-constant shift - lhs must be an integer
+ if !isInteger(x.typ) {
+ check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
+ x.mode = invalid
+ return
+ }
+
+ x.mode = value
+}
+
+var binaryOpPredicates = opPredicates{
+ token.ADD: func(typ Type) bool { return isNumeric(typ) || isString(typ) },
+ token.SUB: isNumeric,
+ token.MUL: isNumeric,
+ token.QUO: isNumeric,
+ token.REM: isInteger,
+
+ token.AND: isInteger,
+ token.OR: isInteger,
+ token.XOR: isInteger,
+ token.AND_NOT: isInteger,
+
+ token.LAND: isBoolean,
+ token.LOR: isBoolean,
+}
+
+// The binary expression e may be nil. It's passed in for better error messages only.
+func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, op token.Token) {
+ var y operand
+
+ check.expr(x, lhs)
+ check.expr(&y, rhs)
+
+ if x.mode == invalid {
+ return
+ }
+ if y.mode == invalid {
+ x.mode = invalid
+ x.expr = y.expr
+ return
+ }
+
+ if isShift(op) {
+ check.shift(x, &y, op)
+ return
+ }
+
+ check.convertUntyped(x, y.typ)
+ if x.mode == invalid {
+ return
+ }
+ check.convertUntyped(&y, x.typ)
+ if y.mode == invalid {
+ x.mode = invalid
+ return
+ }
+
+ if isComparison(op) {
+ check.comparison(x, &y, op)
+ return
+ }
+
+ if !Identical(x.typ, y.typ) {
+ // only report an error if we have valid types
+ // (otherwise we had an error reported elsewhere already)
+ if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] {
+ check.invalidOp(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
+ }
+ x.mode = invalid
+ return
+ }
+
+ if !check.op(binaryOpPredicates, x, op) {
+ x.mode = invalid
+ return
+ }
+
+ if (op == token.QUO || op == token.REM) && (x.mode == constant_ || isInteger(x.typ)) && y.mode == constant_ && constant.Sign(y.val) == 0 {
+ check.invalidOp(y.pos(), "division by zero")
+ x.mode = invalid
+ return
+ }
+
+ if x.mode == constant_ && y.mode == constant_ {
+ typ := x.typ.Underlying().(*Basic)
+ // force integer division of integer operands
+ if op == token.QUO && isInteger(typ) {
+ op = token.QUO_ASSIGN
+ }
+ x.val = constant.BinaryOp(x.val, op, y.val)
+ // Typed constants must be representable in
+ // their type after each constant operation.
+ if isTyped(typ) {
+ if e != nil {
+ x.expr = e // for better error message
+ }
+ check.representable(x, typ)
+ }
+ return
+ }
+
+ x.mode = value
+ // x.typ is unchanged
+}
+
+// index checks an index expression for validity.
+// If max >= 0, it is the upper bound for index.
+// If index is valid and the result i >= 0, then i is the constant value of index.
+func (check *Checker) index(index ast.Expr, max int64) (i int64, valid bool) {
+ var x operand
+ check.expr(&x, index)
+ if x.mode == invalid {
+ return
+ }
+
+ // an untyped constant must be representable as Int
+ check.convertUntyped(&x, Typ[Int])
+ if x.mode == invalid {
+ return
+ }
+
+ // the index must be of integer type
+ if !isInteger(x.typ) {
+ check.invalidArg(x.pos(), "index %s must be integer", &x)
+ return
+ }
+
+ // a constant index i must be in bounds
+ if x.mode == constant_ {
+ if constant.Sign(x.val) < 0 {
+ check.invalidArg(x.pos(), "index %s must not be negative", &x)
+ return
+ }
+ i, valid = constant.Int64Val(x.val)
+ if !valid || max >= 0 && i >= max {
+ check.errorf(x.pos(), "index %s is out of bounds", &x)
+ return i, false
+ }
+ // 0 <= i [ && i < max ]
+ return i, true
+ }
+
+ return -1, true
+}
+
+// indexElts checks the elements (elts) of an array or slice composite literal
+// against the literal's element type (typ), and the element indices against
+// the literal length if known (length >= 0). It returns the length of the
+// literal (maximum index value + 1).
+//
+func (check *Checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64 {
+ visited := make(map[int64]bool, len(elts))
+ var index, max int64
+ for _, e := range elts {
+ // determine and check index
+ validIndex := false
+ eval := e
+ if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
+ if i, ok := check.index(kv.Key, length); ok {
+ if i >= 0 {
+ index = i
+ validIndex = true
+ } else {
+ check.errorf(e.Pos(), "index %s must be integer constant", kv.Key)
+ }
+ }
+ eval = kv.Value
+ } else if length >= 0 && index >= length {
+ check.errorf(e.Pos(), "index %d is out of bounds (>= %d)", index, length)
+ } else {
+ validIndex = true
+ }
+
+ // if we have a valid index, check for duplicate entries
+ if validIndex {
+ if visited[index] {
+ check.errorf(e.Pos(), "duplicate index %d in array or slice literal", index)
+ }
+ visited[index] = true
+ }
+ index++
+ if index > max {
+ max = index
+ }
+
+ // check element against composite literal element type
+ var x operand
+ check.exprWithHint(&x, eval, typ)
+ if !check.assignment(&x, typ) && x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s value in array or slice literal", &x, typ)
+ }
+ }
+ return max
+}
+
+// exprKind describes the kind of an expression; the kind
+// determines if an expression is valid in 'statement context'.
+type exprKind int
+
+const (
+ conversion exprKind = iota
+ expression
+ statement
+)
+
+// rawExpr typechecks expression e and initializes x with the expression
+// value or type. If an error occurred, x.mode is set to invalid.
+// If hint != nil, it is the type of a composite literal element.
+//
+func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind {
+ if trace {
+ check.trace(e.Pos(), "%s", e)
+ check.indent++
+ defer func() {
+ check.indent--
+ check.trace(e.Pos(), "=> %s", x)
+ }()
+ }
+
+ kind := check.exprInternal(x, e, hint)
+
+ // convert x into a user-friendly set of values
+ // TODO(gri) this code can be simplified
+ var typ Type
+ var val constant.Value
+ switch x.mode {
+ case invalid:
+ typ = Typ[Invalid]
+ case novalue:
+ typ = (*Tuple)(nil)
+ case constant_:
+ typ = x.typ
+ val = x.val
+ default:
+ typ = x.typ
+ }
+ assert(x.expr != nil && typ != nil)
+
+ if isUntyped(typ) {
+ // delay type and value recording until we know the type
+ // or until the end of type checking
+ check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val)
+ } else {
+ check.recordTypeAndValue(e, x.mode, typ, val)
+ }
+
+ return kind
+}
+
+// exprInternal contains the core of type checking of expressions.
+// Must only be called by rawExpr.
+//
+func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
+ // make sure x has a valid state in case of bailout
+ // (was issue 5770)
+ x.mode = invalid
+ x.typ = Typ[Invalid]
+
+ switch e := e.(type) {
+ case *ast.BadExpr:
+ goto Error // error was reported before
+
+ case *ast.Ident:
+ check.ident(x, e, nil, nil)
+
+ case *ast.Ellipsis:
+ // ellipses are handled explicitly where they are legal
+ // (array composite literals and parameter lists)
+ check.error(e.Pos(), "invalid use of '...'")
+ goto Error
+
+ case *ast.BasicLit:
+ x.setConst(e.Kind, e.Value)
+ if x.mode == invalid {
+ check.invalidAST(e.Pos(), "invalid literal %v", e.Value)
+ goto Error
+ }
+
+ case *ast.FuncLit:
+ if sig, ok := check.typ(e.Type).(*Signature); ok {
+ // Anonymous functions are considered part of the
+ // init expression/func declaration which contains
+ // them: use existing package-level declaration info.
+ check.funcBody(check.decl, "", sig, e.Body)
+ x.mode = value
+ x.typ = sig
+ } else {
+ check.invalidAST(e.Pos(), "invalid function literal %s", e)
+ goto Error
+ }
+
+ case *ast.CompositeLit:
+ typ := hint
+ openArray := false
+ if e.Type != nil {
+ // [...]T array types may only appear with composite literals.
+ // Check for them here so we don't have to handle ... in general.
+ typ = nil
+ if atyp, _ := e.Type.(*ast.ArrayType); atyp != nil && atyp.Len != nil {
+ if ellip, _ := atyp.Len.(*ast.Ellipsis); ellip != nil && ellip.Elt == nil {
+ // We have an "open" [...]T array type.
+ // Create a new ArrayType with unknown length (-1)
+ // and finish setting it up after analyzing the literal.
+ typ = &Array{len: -1, elem: check.typ(atyp.Elt)}
+ openArray = true
+ }
+ }
+ if typ == nil {
+ typ = check.typ(e.Type)
+ }
+ }
+ if typ == nil {
+ // TODO(gri) provide better error messages depending on context
+ check.error(e.Pos(), "missing type in composite literal")
+ goto Error
+ }
+
+ switch typ, _ := deref(typ); utyp := typ.Underlying().(type) {
+ case *Struct:
+ if len(e.Elts) == 0 {
+ break
+ }
+ fields := utyp.fields
+ if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok {
+ // all elements must have keys
+ visited := make([]bool, len(fields))
+ for _, e := range e.Elts {
+ kv, _ := e.(*ast.KeyValueExpr)
+ if kv == nil {
+ check.error(e.Pos(), "mixture of field:value and value elements in struct literal")
+ continue
+ }
+ key, _ := kv.Key.(*ast.Ident)
+ if key == nil {
+ check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key)
+ continue
+ }
+ i := fieldIndex(utyp.fields, check.pkg, key.Name)
+ if i < 0 {
+ check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name)
+ continue
+ }
+ fld := fields[i]
+ check.recordUse(key, fld)
+ // 0 <= i < len(fields)
+ if visited[i] {
+ check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name)
+ continue
+ }
+ visited[i] = true
+ check.expr(x, kv.Value)
+ etyp := fld.typ
+ if !check.assignment(x, etyp) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp)
+ }
+ continue
+ }
+ }
+ } else {
+ // no element must have a key
+ for i, e := range e.Elts {
+ if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
+ check.error(kv.Pos(), "mixture of field:value and value elements in struct literal")
+ continue
+ }
+ check.expr(x, e)
+ if i >= len(fields) {
+ check.error(x.pos(), "too many values in struct literal")
+ break // cannot continue
+ }
+ // i < len(fields)
+ fld := fields[i]
+ if !fld.Exported() && fld.pkg != check.pkg {
+ check.errorf(x.pos(), "implicit assignment to unexported field %s in %s literal", fld.name, typ)
+ continue
+ }
+ etyp := fld.typ
+ if !check.assignment(x, etyp) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp)
+ }
+ continue
+ }
+ }
+ if len(e.Elts) < len(fields) {
+ check.error(e.Rbrace, "too few values in struct literal")
+ // ok to continue
+ }
+ }
+
+ case *Array:
+ n := check.indexedElts(e.Elts, utyp.elem, utyp.len)
+ // if we have an "open" [...]T array, set the length now that we know it
+ if openArray {
+ utyp.len = n
+ }
+
+ case *Slice:
+ check.indexedElts(e.Elts, utyp.elem, -1)
+
+ case *Map:
+ visited := make(map[interface{}][]Type, len(e.Elts))
+ for _, e := range e.Elts {
+ kv, _ := e.(*ast.KeyValueExpr)
+ if kv == nil {
+ check.error(e.Pos(), "missing key in map literal")
+ continue
+ }
+ check.exprWithHint(x, kv.Key, utyp.key)
+ if !check.assignment(x, utyp.key) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.key)
+ }
+ continue
+ }
+ if x.mode == constant_ {
+ duplicate := false
+ // if the key is of interface type, the type is also significant when checking for duplicates
+ if _, ok := utyp.key.Underlying().(*Interface); ok {
+ for _, vtyp := range visited[x.val] {
+ if Identical(vtyp, x.typ) {
+ duplicate = true
+ break
+ }
+ }
+ visited[x.val] = append(visited[x.val], x.typ)
+ } else {
+ _, duplicate = visited[x.val]
+ visited[x.val] = nil
+ }
+ if duplicate {
+ check.errorf(x.pos(), "duplicate key %s in map literal", x.val)
+ continue
+ }
+ }
+ check.exprWithHint(x, kv.Value, utyp.elem)
+ if !check.assignment(x, utyp.elem) {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.elem)
+ }
+ continue
+ }
+ }
+
+ default:
+ // if utyp is invalid, an error was reported before
+ if utyp != Typ[Invalid] {
+ check.errorf(e.Pos(), "invalid composite literal type %s", typ)
+ goto Error
+ }
+ }
+
+ x.mode = value
+ x.typ = typ
+
+ case *ast.ParenExpr:
+ kind := check.rawExpr(x, e.X, nil)
+ x.expr = e
+ return kind
+
+ case *ast.SelectorExpr:
+ check.selector(x, e)
+
+ case *ast.IndexExpr:
+ check.expr(x, e.X)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ valid := false
+ length := int64(-1) // valid if >= 0
+ switch typ := x.typ.Underlying().(type) {
+ case *Basic:
+ if isString(typ) {
+ valid = true
+ if x.mode == constant_ {
+ length = int64(len(constant.StringVal(x.val)))
+ }
+ // an indexed string always yields a byte value
+ // (not a constant) even if the string and the
+ // index are constant
+ x.mode = value
+ x.typ = universeByte // use 'byte' name
+ }
+
+ case *Array:
+ valid = true
+ length = typ.len
+ if x.mode != variable {
+ x.mode = value
+ }
+ x.typ = typ.elem
+
+ case *Pointer:
+ if typ, _ := typ.base.Underlying().(*Array); typ != nil {
+ valid = true
+ length = typ.len
+ x.mode = variable
+ x.typ = typ.elem
+ }
+
+ case *Slice:
+ valid = true
+ x.mode = variable
+ x.typ = typ.elem
+
+ case *Map:
+ var key operand
+ check.expr(&key, e.Index)
+ if !check.assignment(&key, typ.key) {
+ if key.mode != invalid {
+ check.invalidOp(key.pos(), "cannot use %s as map index of type %s", &key, typ.key)
+ }
+ goto Error
+ }
+ x.mode = mapindex
+ x.typ = typ.elem
+ x.expr = e
+ return expression
+ }
+
+ if !valid {
+ check.invalidOp(x.pos(), "cannot index %s", x)
+ goto Error
+ }
+
+ if e.Index == nil {
+ check.invalidAST(e.Pos(), "missing index for %s", x)
+ goto Error
+ }
+
+ check.index(e.Index, length)
+ // ok to continue
+
+ case *ast.SliceExpr:
+ check.expr(x, e.X)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ valid := false
+ length := int64(-1) // valid if >= 0
+ switch typ := x.typ.Underlying().(type) {
+ case *Basic:
+ if isString(typ) {
+ if slice3(e) {
+ check.invalidOp(x.pos(), "3-index slice of string")
+ goto Error
+ }
+ valid = true
+ if x.mode == constant_ {
+ length = int64(len(constant.StringVal(x.val)))
+ }
+ // spec: "For untyped string operands the result
+ // is a non-constant value of type string."
+ if typ.kind == UntypedString {
+ x.typ = Typ[String]
+ }
+ }
+
+ case *Array:
+ valid = true
+ length = typ.len
+ if x.mode != variable {
+ check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x)
+ goto Error
+ }
+ x.typ = &Slice{elem: typ.elem}
+
+ case *Pointer:
+ if typ, _ := typ.base.Underlying().(*Array); typ != nil {
+ valid = true
+ length = typ.len
+ x.typ = &Slice{elem: typ.elem}
+ }
+
+ case *Slice:
+ valid = true
+ // x.typ doesn't change
+ }
+
+ if !valid {
+ check.invalidOp(x.pos(), "cannot slice %s", x)
+ goto Error
+ }
+
+ x.mode = value
+
+ // spec: "Only the first index may be omitted; it defaults to 0."
+ if slice3(e) && (e.High == nil || sliceMax(e) == nil) {
+ check.error(e.Rbrack, "2nd and 3rd index required in 3-index slice")
+ goto Error
+ }
+
+ // check indices
+ var ind [3]int64
+ for i, expr := range []ast.Expr{e.Low, e.High, sliceMax(e)} {
+ x := int64(-1)
+ switch {
+ case expr != nil:
+ // The "capacity" is only known statically for strings, arrays,
+ // and pointers to arrays, and it is the same as the length for
+ // those types.
+ max := int64(-1)
+ if length >= 0 {
+ max = length + 1
+ }
+ if t, ok := check.index(expr, max); ok && t >= 0 {
+ x = t
+ }
+ case i == 0:
+ // default is 0 for the first index
+ x = 0
+ case length >= 0:
+ // default is length (== capacity) otherwise
+ x = length
+ }
+ ind[i] = x
+ }
+
+ // constant indices must be in range
+ // (check.index already checks that existing indices >= 0)
+ L:
+ for i, x := range ind[:len(ind)-1] {
+ if x > 0 {
+ for _, y := range ind[i+1:] {
+ if y >= 0 && x > y {
+ check.errorf(e.Rbrack, "invalid slice indices: %d > %d", x, y)
+ break L // only report one error, ok to continue
+ }
+ }
+ }
+ }
+
+ case *ast.TypeAssertExpr:
+ check.expr(x, e.X)
+ if x.mode == invalid {
+ goto Error
+ }
+ xtyp, _ := x.typ.Underlying().(*Interface)
+ if xtyp == nil {
+ check.invalidOp(x.pos(), "%s is not an interface", x)
+ goto Error
+ }
+ // x.(type) expressions are handled explicitly in type switches
+ if e.Type == nil {
+ check.invalidAST(e.Pos(), "use of .(type) outside type switch")
+ goto Error
+ }
+ T := check.typ(e.Type)
+ if T == Typ[Invalid] {
+ goto Error
+ }
+ check.typeAssertion(x.pos(), x, xtyp, T)
+ x.mode = commaok
+ x.typ = T
+
+ case *ast.CallExpr:
+ return check.call(x, e)
+
+ case *ast.StarExpr:
+ check.exprOrType(x, e.X)
+ switch x.mode {
+ case invalid:
+ goto Error
+ case typexpr:
+ x.typ = &Pointer{base: x.typ}
+ default:
+ if typ, ok := x.typ.Underlying().(*Pointer); ok {
+ x.mode = variable
+ x.typ = typ.base
+ } else {
+ check.invalidOp(x.pos(), "cannot indirect %s", x)
+ goto Error
+ }
+ }
+
+ case *ast.UnaryExpr:
+ check.expr(x, e.X)
+ if x.mode == invalid {
+ goto Error
+ }
+ check.unary(x, e, e.Op)
+ if x.mode == invalid {
+ goto Error
+ }
+ if e.Op == token.ARROW {
+ x.expr = e
+ return statement // receive operations may appear in statement context
+ }
+
+ case *ast.BinaryExpr:
+ check.binary(x, e, e.X, e.Y, e.Op)
+ if x.mode == invalid {
+ goto Error
+ }
+
+ case *ast.KeyValueExpr:
+ // key:value expressions are handled in composite literals
+ check.invalidAST(e.Pos(), "no key:value expected")
+ goto Error
+
+ case *ast.ArrayType, *ast.StructType, *ast.FuncType,
+ *ast.InterfaceType, *ast.MapType, *ast.ChanType:
+ x.mode = typexpr
+ x.typ = check.typ(e)
+ // Note: rawExpr (caller of exprInternal) will call check.recordTypeAndValue
+ // even though check.typ has already called it. This is fine as both
+ // times the same expression and type are recorded. It is also not a
+ // performance issue because we only reach here for composite literal
+ // types, which are comparatively rare.
+
+ default:
+ panic(fmt.Sprintf("%s: unknown expression type %T", check.fset.Position(e.Pos()), e))
+ }
+
+ // everything went well
+ x.expr = e
+ return expression
+
+Error:
+ x.mode = invalid
+ x.expr = e
+ return statement // avoid follow-up errors
+}
+
+// typeAssertion checks that x.(T) is legal; xtyp must be the type of x.
+func (check *Checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface, T Type) {
+ method, wrongType := assertableTo(xtyp, T)
+ if method == nil {
+ return
+ }
+
+ var msg string
+ if wrongType {
+ msg = "wrong type for method"
+ } else {
+ msg = "missing method"
+ }
+ check.errorf(pos, "%s cannot have dynamic type %s (%s %s)", x, T, msg, method.name)
+}
+
+// expr typechecks expression e and initializes x with the expression value.
+// If an error occurred, x.mode is set to invalid.
+//
+func (check *Checker) expr(x *operand, e ast.Expr) {
+ check.rawExpr(x, e, nil)
+ var msg string
+ switch x.mode {
+ default:
+ return
+ case novalue:
+ msg = "used as value"
+ case builtin:
+ msg = "must be called"
+ case typexpr:
+ msg = "is not an expression"
+ }
+ check.errorf(x.pos(), "%s %s", x, msg)
+ x.mode = invalid
+}
+
+// exprWithHint typechecks expression e and initializes x with the expression value.
+// If an error occurred, x.mode is set to invalid.
+// If hint != nil, it is the type of a composite literal element.
+//
+func (check *Checker) exprWithHint(x *operand, e ast.Expr, hint Type) {
+ assert(hint != nil)
+ check.rawExpr(x, e, hint)
+ var msg string
+ switch x.mode {
+ default:
+ return
+ case novalue:
+ msg = "used as value"
+ case builtin:
+ msg = "must be called"
+ case typexpr:
+ msg = "is not an expression"
+ }
+ check.errorf(x.pos(), "%s %s", x, msg)
+ x.mode = invalid
+}
+
+// exprOrType typechecks expression or type e and initializes x with the expression value or type.
+// If an error occurred, x.mode is set to invalid.
+//
+func (check *Checker) exprOrType(x *operand, e ast.Expr) {
+ check.rawExpr(x, e, nil)
+ if x.mode == novalue {
+ check.errorf(x.pos(), "%s used as value or type", x)
+ x.mode = invalid
+ }
+}
diff --git a/libgo/go/go/types/exprstring.go b/libgo/go/go/types/exprstring.go
new file mode 100644
index 00000000000..370bdf35324
--- /dev/null
+++ b/libgo/go/go/types/exprstring.go
@@ -0,0 +1,220 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements printing of expressions.
+
+package types
+
+import (
+ "bytes"
+ "go/ast"
+)
+
+// ExprString returns the (possibly simplified) string representation for x.
+func ExprString(x ast.Expr) string {
+ var buf bytes.Buffer
+ WriteExpr(&buf, x)
+ return buf.String()
+}
+
+// WriteExpr writes the (possibly simplified) string representation for x to buf.
+func WriteExpr(buf *bytes.Buffer, x ast.Expr) {
+ // The AST preserves source-level parentheses so there is
+ // no need to introduce them here to correct for different
+ // operator precedences. (This assumes that the AST was
+ // generated by a Go parser.)
+
+ switch x := x.(type) {
+ default:
+ buf.WriteString("(bad expr)") // nil, ast.BadExpr, ast.KeyValueExpr
+
+ case *ast.Ident:
+ buf.WriteString(x.Name)
+
+ case *ast.Ellipsis:
+ buf.WriteString("...")
+ if x.Elt != nil {
+ WriteExpr(buf, x.Elt)
+ }
+
+ case *ast.BasicLit:
+ buf.WriteString(x.Value)
+
+ case *ast.FuncLit:
+ buf.WriteByte('(')
+ WriteExpr(buf, x.Type)
+ buf.WriteString(" literal)") // simplified
+
+ case *ast.CompositeLit:
+ buf.WriteByte('(')
+ WriteExpr(buf, x.Type)
+ buf.WriteString(" literal)") // simplified
+
+ case *ast.ParenExpr:
+ buf.WriteByte('(')
+ WriteExpr(buf, x.X)
+ buf.WriteByte(')')
+
+ case *ast.SelectorExpr:
+ WriteExpr(buf, x.X)
+ buf.WriteByte('.')
+ buf.WriteString(x.Sel.Name)
+
+ case *ast.IndexExpr:
+ WriteExpr(buf, x.X)
+ buf.WriteByte('[')
+ WriteExpr(buf, x.Index)
+ buf.WriteByte(']')
+
+ case *ast.SliceExpr:
+ WriteExpr(buf, x.X)
+ buf.WriteByte('[')
+ if x.Low != nil {
+ WriteExpr(buf, x.Low)
+ }
+ buf.WriteByte(':')
+ if x.High != nil {
+ WriteExpr(buf, x.High)
+ }
+ if x.Slice3 {
+ buf.WriteByte(':')
+ if x.Max != nil {
+ WriteExpr(buf, x.Max)
+ }
+ }
+ buf.WriteByte(']')
+
+ case *ast.TypeAssertExpr:
+ WriteExpr(buf, x.X)
+ buf.WriteString(".(")
+ WriteExpr(buf, x.Type)
+ buf.WriteByte(')')
+
+ case *ast.CallExpr:
+ WriteExpr(buf, x.Fun)
+ buf.WriteByte('(')
+ for i, arg := range x.Args {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ WriteExpr(buf, arg)
+ }
+ if x.Ellipsis.IsValid() {
+ buf.WriteString("...")
+ }
+ buf.WriteByte(')')
+
+ case *ast.StarExpr:
+ buf.WriteByte('*')
+ WriteExpr(buf, x.X)
+
+ case *ast.UnaryExpr:
+ buf.WriteString(x.Op.String())
+ WriteExpr(buf, x.X)
+
+ case *ast.BinaryExpr:
+ WriteExpr(buf, x.X)
+ buf.WriteByte(' ')
+ buf.WriteString(x.Op.String())
+ buf.WriteByte(' ')
+ WriteExpr(buf, x.Y)
+
+ case *ast.ArrayType:
+ buf.WriteByte('[')
+ if x.Len != nil {
+ WriteExpr(buf, x.Len)
+ }
+ buf.WriteByte(']')
+ WriteExpr(buf, x.Elt)
+
+ case *ast.StructType:
+ buf.WriteString("struct{")
+ writeFieldList(buf, x.Fields, "; ", false)
+ buf.WriteByte('}')
+
+ case *ast.FuncType:
+ buf.WriteString("func")
+ writeSigExpr(buf, x)
+
+ case *ast.InterfaceType:
+ buf.WriteString("interface{")
+ writeFieldList(buf, x.Methods, "; ", true)
+ buf.WriteByte('}')
+
+ case *ast.MapType:
+ buf.WriteString("map[")
+ WriteExpr(buf, x.Key)
+ buf.WriteByte(']')
+ WriteExpr(buf, x.Value)
+
+ case *ast.ChanType:
+ var s string
+ switch x.Dir {
+ case ast.SEND:
+ s = "chan<- "
+ case ast.RECV:
+ s = "<-chan "
+ default:
+ s = "chan "
+ }
+ buf.WriteString(s)
+ WriteExpr(buf, x.Value)
+ }
+}
+
+func writeSigExpr(buf *bytes.Buffer, sig *ast.FuncType) {
+ buf.WriteByte('(')
+ writeFieldList(buf, sig.Params, ", ", false)
+ buf.WriteByte(')')
+
+ res := sig.Results
+ n := res.NumFields()
+ if n == 0 {
+ // no result
+ return
+ }
+
+ buf.WriteByte(' ')
+ if n == 1 && len(res.List[0].Names) == 0 {
+ // single unnamed result
+ WriteExpr(buf, res.List[0].Type)
+ return
+ }
+
+ // multiple or named result(s)
+ buf.WriteByte('(')
+ writeFieldList(buf, res, ", ", false)
+ buf.WriteByte(')')
+}
+
+func writeFieldList(buf *bytes.Buffer, fields *ast.FieldList, sep string, iface bool) {
+ for i, f := range fields.List {
+ if i > 0 {
+ buf.WriteString(sep)
+ }
+
+ // field list names
+ for i, name := range f.Names {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ buf.WriteString(name.Name)
+ }
+
+ // types of interface methods consist of signatures only
+ if sig, _ := f.Type.(*ast.FuncType); sig != nil && iface {
+ writeSigExpr(buf, sig)
+ continue
+ }
+
+ // named fields are separated with a blank from the field type
+ if len(f.Names) > 0 {
+ buf.WriteByte(' ')
+ }
+
+ WriteExpr(buf, f.Type)
+
+ // ignore tag
+ }
+}
diff --git a/libgo/go/go/types/exprstring_test.go b/libgo/go/go/types/exprstring_test.go
new file mode 100644
index 00000000000..51102881c9f
--- /dev/null
+++ b/libgo/go/go/types/exprstring_test.go
@@ -0,0 +1,94 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types_test
+
+import (
+ "go/parser"
+ "testing"
+
+ . "go/types"
+)
+
+var testExprs = []testEntry{
+ // basic type literals
+ dup("x"),
+ dup("true"),
+ dup("42"),
+ dup("3.1415"),
+ dup("2.71828i"),
+ dup(`'a'`),
+ dup(`"foo"`),
+ dup("`bar`"),
+
+ // func and composite literals
+ {"func(){}", "(func() literal)"},
+ {"func(x int) complex128 {}", "(func(x int) complex128 literal)"},
+ {"[]int{1, 2, 3}", "([]int literal)"},
+
+ // non-type expressions
+ dup("(x)"),
+ dup("x.f"),
+ dup("a[i]"),
+
+ dup("s[:]"),
+ dup("s[i:]"),
+ dup("s[:j]"),
+ dup("s[i:j]"),
+ dup("s[:j:k]"),
+ dup("s[i:j:k]"),
+
+ dup("x.(T)"),
+
+ dup("x.([10]int)"),
+ dup("x.([...]int)"),
+
+ dup("x.(struct{})"),
+ dup("x.(struct{x int; y, z float32; E})"),
+
+ dup("x.(func())"),
+ dup("x.(func(x int))"),
+ dup("x.(func() int)"),
+ dup("x.(func(x, y int, z float32) (r int))"),
+ dup("x.(func(a, b, c int))"),
+ dup("x.(func(x ...T))"),
+
+ dup("x.(interface{})"),
+ dup("x.(interface{m(); n(x int); E})"),
+ dup("x.(interface{m(); n(x int) T; E; F})"),
+
+ dup("x.(map[K]V)"),
+
+ dup("x.(chan E)"),
+ dup("x.(<-chan E)"),
+ dup("x.(chan<- chan int)"),
+ dup("x.(chan<- <-chan int)"),
+ dup("x.(<-chan chan int)"),
+ dup("x.(chan (<-chan int))"),
+
+ dup("f()"),
+ dup("f(x)"),
+ dup("int(x)"),
+ dup("f(x, x + y)"),
+ dup("f(s...)"),
+ dup("f(a, s...)"),
+
+ dup("*x"),
+ dup("&x"),
+ dup("x + y"),
+ dup("x + y << (2 * s)"),
+}
+
+func TestExprString(t *testing.T) {
+ for _, test := range testExprs {
+ x, err := parser.ParseExpr(test.src)
+ if err != nil {
+ t.Errorf("%s: %s", test.src, err)
+ continue
+ }
+ if got := ExprString(x); got != test.str {
+ t.Errorf("%s: got %s, want %s", test.src, got, test.str)
+ }
+ }
+}
diff --git a/libgo/go/go/types/go11.go b/libgo/go/go/types/go11.go
new file mode 100644
index 00000000000..cf41cabeeac
--- /dev/null
+++ b/libgo/go/go/types/go11.go
@@ -0,0 +1,17 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !go1.2
+
+package types
+
+import "go/ast"
+
+func slice3(x *ast.SliceExpr) bool {
+ return false
+}
+
+func sliceMax(x *ast.SliceExpr) ast.Expr {
+ return nil
+}
diff --git a/libgo/go/go/types/go12.go b/libgo/go/go/types/go12.go
new file mode 100644
index 00000000000..20174421540
--- /dev/null
+++ b/libgo/go/go/types/go12.go
@@ -0,0 +1,17 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build go1.2
+
+package types
+
+import "go/ast"
+
+func slice3(x *ast.SliceExpr) bool {
+ return x.Slice3
+}
+
+func sliceMax(x *ast.SliceExpr) ast.Expr {
+ return x.Max
+}
diff --git a/libgo/go/go/types/hilbert_test.go b/libgo/go/go/types/hilbert_test.go
new file mode 100644
index 00000000000..cfd51b1d649
--- /dev/null
+++ b/libgo/go/go/types/hilbert_test.go
@@ -0,0 +1,234 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types_test
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "io/ioutil"
+ "testing"
+
+ . "go/types"
+)
+
+var (
+ H = flag.Int("H", 5, "Hilbert matrix size")
+ out = flag.String("out", "", "write generated program to out")
+)
+
+func TestHilbert(t *testing.T) {
+ // generate source
+ src := program(*H, *out)
+ if *out != "" {
+ ioutil.WriteFile(*out, src, 0666)
+ return
+ }
+
+ // parse source
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "hilbert.go", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // type-check file
+ DefPredeclaredTestFuncs() // define assert built-in
+ conf := Config{Importer: importer.Default()}
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func program(n int, out string) []byte {
+ var g gen
+
+ g.p(`// WARNING: GENERATED FILE - DO NOT MODIFY MANUALLY!
+// (To generate, in go/types directory: go test -run=Hilbert -H=%d -out=%q)
+
+// This program tests arbitrary precision constant arithmetic
+// by generating the constant elements of a Hilbert matrix H,
+// its inverse I, and the product P = H*I. The product should
+// be the identity matrix.
+package main
+
+func main() {
+ if !ok {
+ printProduct()
+ return
+ }
+ println("PASS")
+}
+
+`, n, out)
+ g.hilbert(n)
+ g.inverse(n)
+ g.product(n)
+ g.verify(n)
+ g.printProduct(n)
+ g.binomials(2*n - 1)
+ g.factorials(2*n - 1)
+
+ return g.Bytes()
+}
+
+type gen struct {
+ bytes.Buffer
+}
+
+func (g *gen) p(format string, args ...interface{}) {
+ fmt.Fprintf(&g.Buffer, format, args...)
+}
+
+func (g *gen) hilbert(n int) {
+ g.p(`// Hilbert matrix, n = %d
+const (
+`, n)
+ for i := 0; i < n; i++ {
+ g.p("\t")
+ for j := 0; j < n; j++ {
+ if j > 0 {
+ g.p(", ")
+ }
+ g.p("h%d_%d", i, j)
+ }
+ if i == 0 {
+ g.p(" = ")
+ for j := 0; j < n; j++ {
+ if j > 0 {
+ g.p(", ")
+ }
+ g.p("1.0/(iota + %d)", j+1)
+ }
+ }
+ g.p("\n")
+ }
+ g.p(")\n\n")
+}
+
+func (g *gen) inverse(n int) {
+ g.p(`// Inverse Hilbert matrix
+const (
+`)
+ for i := 0; i < n; i++ {
+ for j := 0; j < n; j++ {
+ s := "+"
+ if (i+j)&1 != 0 {
+ s = "-"
+ }
+ g.p("\ti%d_%d = %s%d * b%d_%d * b%d_%d * b%d_%d * b%d_%d\n",
+ i, j, s, i+j+1, n+i, n-j-1, n+j, n-i-1, i+j, i, i+j, i)
+ }
+ g.p("\n")
+ }
+ g.p(")\n\n")
+}
+
+func (g *gen) product(n int) {
+ g.p(`// Product matrix
+const (
+`)
+ for i := 0; i < n; i++ {
+ for j := 0; j < n; j++ {
+ g.p("\tp%d_%d = ", i, j)
+ for k := 0; k < n; k++ {
+ if k > 0 {
+ g.p(" + ")
+ }
+ g.p("h%d_%d*i%d_%d", i, k, k, j)
+ }
+ g.p("\n")
+ }
+ g.p("\n")
+ }
+ g.p(")\n\n")
+}
+
+func (g *gen) verify(n int) {
+ g.p(`// Verify that product is the identity matrix
+const ok =
+`)
+ for i := 0; i < n; i++ {
+ for j := 0; j < n; j++ {
+ if j == 0 {
+ g.p("\t")
+ } else {
+ g.p(" && ")
+ }
+ v := 0
+ if i == j {
+ v = 1
+ }
+ g.p("p%d_%d == %d", i, j, v)
+ }
+ g.p(" &&\n")
+ }
+ g.p("\ttrue\n\n")
+
+ // verify ok at type-check time
+ if *out == "" {
+ g.p("const _ = assert(ok)\n\n")
+ }
+}
+
+func (g *gen) printProduct(n int) {
+ g.p("func printProduct() {\n")
+ for i := 0; i < n; i++ {
+ g.p("\tprintln(")
+ for j := 0; j < n; j++ {
+ if j > 0 {
+ g.p(", ")
+ }
+ g.p("p%d_%d", i, j)
+ }
+ g.p(")\n")
+ }
+ g.p("}\n\n")
+}
+
+func (g *gen) mulRange(a, b int) {
+ if a > b {
+ g.p("1")
+ return
+ }
+ for i := a; i <= b; i++ {
+ if i > a {
+ g.p("*")
+ }
+ g.p("%d", i)
+ }
+}
+
+func (g *gen) binomials(n int) {
+ g.p(`// Binomials
+const (
+`)
+ for j := 0; j <= n; j++ {
+ if j > 0 {
+ g.p("\n")
+ }
+ for k := 0; k <= j; k++ {
+ g.p("\tb%d_%d = f%d / (f%d*f%d)\n", j, k, j, k, j-k)
+ }
+ }
+ g.p(")\n\n")
+}
+
+func (g *gen) factorials(n int) {
+ g.p(`// Factorials
+const (
+ f0 = 1
+ f1 = 1
+`)
+ for i := 2; i <= n; i++ {
+ g.p("\tf%d = f%d * %d\n", i, i-1, i)
+ }
+ g.p(")\n\n")
+}
diff --git a/libgo/go/go/types/initorder.go b/libgo/go/go/types/initorder.go
new file mode 100644
index 00000000000..0fd567b2691
--- /dev/null
+++ b/libgo/go/go/types/initorder.go
@@ -0,0 +1,222 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import (
+ "container/heap"
+ "fmt"
+)
+
+// initOrder computes the Info.InitOrder for package variables.
+func (check *Checker) initOrder() {
+ // An InitOrder may already have been computed if a package is
+ // built from several calls to (*Checker).Files. Clear it.
+ check.Info.InitOrder = check.Info.InitOrder[:0]
+
+ // compute the object dependency graph and
+ // initialize a priority queue with the list
+ // of graph nodes
+ pq := nodeQueue(dependencyGraph(check.objMap))
+ heap.Init(&pq)
+
+ const debug = false
+ if debug {
+ fmt.Printf("package %s: object dependency graph\n", check.pkg.Name())
+ for _, n := range pq {
+ for _, o := range n.out {
+ fmt.Printf("\t%s -> %s\n", n.obj.Name(), o.obj.Name())
+ }
+ }
+ fmt.Println()
+ fmt.Printf("package %s: initialization order\n", check.pkg.Name())
+ }
+
+ // determine initialization order by removing the highest priority node
+ // (the one with the fewest dependencies) and its edges from the graph,
+ // repeatedly, until there are no nodes left.
+ // In a valid Go program, those nodes always have zero dependencies (after
+ // removing all incoming dependencies), otherwise there are initialization
+ // cycles.
+ mark := 0
+ emitted := make(map[*declInfo]bool)
+ for len(pq) > 0 {
+ // get the next node
+ n := heap.Pop(&pq).(*objNode)
+
+ // if n still depends on other nodes, we have a cycle
+ if n.in > 0 {
+ mark++ // mark nodes using a different value each time
+ cycle := findPath(n, n, mark)
+ if i := valIndex(cycle); i >= 0 {
+ check.reportCycle(cycle, i)
+ }
+ // ok to continue, but the variable initialization order
+ // will be incorrect at this point since it assumes no
+ // cycle errors
+ }
+
+ // reduce dependency count of all dependent nodes
+ // and update priority queue
+ for _, out := range n.out {
+ out.in--
+ heap.Fix(&pq, out.index)
+ }
+
+ // record the init order for variables with initializers only
+ v, _ := n.obj.(*Var)
+ info := check.objMap[v]
+ if v == nil || !info.hasInitializer() {
+ continue
+ }
+
+ // n:1 variable declarations such as: a, b = f()
+ // introduce a node for each lhs variable (here: a, b);
+ // but they all have the same initializer - emit only
+ // one, for the first variable seen
+ if emitted[info] {
+ continue // initializer already emitted, if any
+ }
+ emitted[info] = true
+
+ infoLhs := info.lhs // possibly nil (see declInfo.lhs field comment)
+ if infoLhs == nil {
+ infoLhs = []*Var{v}
+ }
+ init := &Initializer{infoLhs, info.init}
+ check.Info.InitOrder = append(check.Info.InitOrder, init)
+
+ if debug {
+ fmt.Printf("\t%s\n", init)
+ }
+ }
+
+ if debug {
+ fmt.Println()
+ }
+}
+
+// findPath returns the (reversed) list of nodes z, ... c, b, a,
+// such that there is a path (list of edges) from a to z.
+// If there is no such path, the result is nil.
+// Nodes marked with the value mark are considered "visited";
+// unvisited nodes are marked during the graph search.
+func findPath(a, z *objNode, mark int) []*objNode {
+ if a.mark == mark {
+ return nil // node already seen
+ }
+ a.mark = mark
+
+ for _, n := range a.out {
+ if n == z {
+ return []*objNode{z}
+ }
+ if P := findPath(n, z, mark); P != nil {
+ return append(P, n)
+ }
+ }
+
+ return nil
+}
+
+// valIndex returns the index of the first constant or variable in a,
+// if any; or a value < 0.
+func valIndex(a []*objNode) int {
+ for i, n := range a {
+ switch n.obj.(type) {
+ case *Const, *Var:
+ return i
+ }
+ }
+ return -1
+}
+
+// reportCycle reports an error for the cycle starting at i.
+func (check *Checker) reportCycle(cycle []*objNode, i int) {
+ obj := cycle[i].obj
+ check.errorf(obj.Pos(), "initialization cycle for %s", obj.Name())
+ // print cycle
+ for _ = range cycle {
+ check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
+ i++
+ if i >= len(cycle) {
+ i = 0
+ }
+ obj = cycle[i].obj
+ }
+ check.errorf(obj.Pos(), "\t%s", obj.Name())
+}
+
+// An objNode represents a node in the object dependency graph.
+// Each node b in a.out represents an edge a->b indicating that
+// b depends on a.
+// Nodes may be marked for cycle detection. A node n is marked
+// if n.mark corresponds to the current mark value.
+type objNode struct {
+ obj Object // object represented by this node
+ in int // number of nodes this node depends on
+ out []*objNode // list of nodes that depend on this node
+ index int // node index in list of nodes
+ mark int // for cycle detection
+}
+
+// dependencyGraph computes the transposed object dependency graph
+// from the given objMap. The transposed graph is returned as a list
+// of nodes; an edge d->n indicates that node n depends on node d.
+func dependencyGraph(objMap map[Object]*declInfo) []*objNode {
+ // M maps each object to its corresponding node
+ M := make(map[Object]*objNode, len(objMap))
+ for obj := range objMap {
+ M[obj] = &objNode{obj: obj}
+ }
+
+ // G is the graph of nodes n
+ G := make([]*objNode, len(M))
+ i := 0
+ for obj, n := range M {
+ deps := objMap[obj].deps
+ n.in = len(deps)
+ for d := range deps {
+ d := M[d] // node n depends on node d
+ d.out = append(d.out, n) // add edge d->n
+ }
+
+ G[i] = n
+ n.index = i
+ i++
+ }
+
+ return G
+}
+
+// nodeQueue implements the container/heap interface;
+// a nodeQueue may be used as a priority queue.
+type nodeQueue []*objNode
+
+func (a nodeQueue) Len() int { return len(a) }
+
+func (a nodeQueue) Swap(i, j int) {
+ x, y := a[i], a[j]
+ a[i], a[j] = y, x
+ x.index, y.index = j, i
+}
+
+func (a nodeQueue) Less(i, j int) bool {
+ x, y := a[i], a[j]
+ // nodes are prioritized by number of incoming dependencies (1st key)
+ // and source order (2nd key)
+ return x.in < y.in || x.in == y.in && x.obj.order() < y.obj.order()
+}
+
+func (a *nodeQueue) Push(x interface{}) {
+ panic("unreachable")
+}
+
+func (a *nodeQueue) Pop() interface{} {
+ n := len(*a)
+ x := (*a)[n-1]
+ x.index = -1 // for safety
+ *a = (*a)[:n-1]
+ return x
+}
diff --git a/libgo/go/go/types/issues_test.go b/libgo/go/go/types/issues_test.go
new file mode 100644
index 00000000000..672c78dfc20
--- /dev/null
+++ b/libgo/go/go/types/issues_test.go
@@ -0,0 +1,206 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements tests for various issues.
+
+package types_test
+
+import (
+ "fmt"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "sort"
+ "strings"
+ "testing"
+
+ . "go/types"
+)
+
+func TestIssue5770(t *testing.T) {
+ src := `package p; type S struct{T}`
+ f, err := parser.ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ conf := Config{Importer: importer.Default()}
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, nil) // do not crash
+ want := "undeclared name: T"
+ if err == nil || !strings.Contains(err.Error(), want) {
+ t.Errorf("got: %v; want: %s", err, want)
+ }
+}
+
+func TestIssue5849(t *testing.T) {
+ src := `
+package p
+var (
+ s uint
+ _ = uint8(8)
+ _ = uint16(16) << s
+ _ = uint32(32 << s)
+ _ = uint64(64 << s + s)
+ _ = (interface{})("foo")
+ _ = (interface{})(nil)
+)`
+ f, err := parser.ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var conf Config
+ types := make(map[ast.Expr]TypeAndValue)
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ for x, tv := range types {
+ var want Type
+ switch x := x.(type) {
+ case *ast.BasicLit:
+ switch x.Value {
+ case `8`:
+ want = Typ[Uint8]
+ case `16`:
+ want = Typ[Uint16]
+ case `32`:
+ want = Typ[Uint32]
+ case `64`:
+ want = Typ[Uint] // because of "+ s", s is of type uint
+ case `"foo"`:
+ want = Typ[String]
+ }
+ case *ast.Ident:
+ if x.Name == "nil" {
+ want = Typ[UntypedNil]
+ }
+ }
+ if want != nil && !Identical(tv.Type, want) {
+ t.Errorf("got %s; want %s", tv.Type, want)
+ }
+ }
+}
+
+func TestIssue6413(t *testing.T) {
+ src := `
+package p
+func f() int {
+ defer f()
+ go f()
+ return 0
+}
+`
+ f, err := parser.ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var conf Config
+ types := make(map[ast.Expr]TypeAndValue)
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Types: types})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ want := Typ[Int]
+ n := 0
+ for x, tv := range types {
+ if _, ok := x.(*ast.CallExpr); ok {
+ if tv.Type != want {
+ t.Errorf("%s: got %s; want %s", fset.Position(x.Pos()), tv.Type, want)
+ }
+ n++
+ }
+ }
+
+ if n != 2 {
+ t.Errorf("got %d CallExprs; want 2", n)
+ }
+}
+
+func TestIssue7245(t *testing.T) {
+ src := `
+package p
+func (T) m() (res bool) { return }
+type T struct{} // receiver type after method declaration
+`
+ f, err := parser.ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var conf Config
+ defs := make(map[*ast.Ident]Object)
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ m := f.Decls[0].(*ast.FuncDecl)
+ res1 := defs[m.Name].(*Func).Type().(*Signature).Results().At(0)
+ res2 := defs[m.Type.Results.List[0].Names[0]].(*Var)
+
+ if res1 != res2 {
+ t.Errorf("got %s (%p) != %s (%p)", res1, res2, res1, res2)
+ }
+}
+
+// This tests that uses of existing vars on the LHS of an assignment
+// are Uses, not Defs; and also that the (illegal) use of a non-var on
+// the LHS of an assignment is a Use nonetheless.
+func TestIssue7827(t *testing.T) {
+ const src = `
+package p
+func _() {
+ const w = 1 // defs w
+ x, y := 2, 3 // defs x, y
+ w, x, z := 4, 5, 6 // uses w, x, defs z; error: cannot assign to w
+ _, _, _ = x, y, z // uses x, y, z
+}
+`
+ const want = `L3 defs func p._()
+L4 defs const w untyped int
+L5 defs var x int
+L5 defs var y int
+L6 defs var z int
+L6 uses const w untyped int
+L6 uses var x int
+L7 uses var x int
+L7 uses var y int
+L7 uses var z int`
+
+ f, err := parser.ParseFile(fset, "", src, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // don't abort at the first error
+ conf := Config{Error: func(err error) { t.Log(err) }}
+ defs := make(map[*ast.Ident]Object)
+ uses := make(map[*ast.Ident]Object)
+ _, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Defs: defs, Uses: uses})
+ if s := fmt.Sprint(err); !strings.HasSuffix(s, "cannot assign to w") {
+ t.Errorf("Check: unexpected error: %s", s)
+ }
+
+ var facts []string
+ for id, obj := range defs {
+ if obj != nil {
+ fact := fmt.Sprintf("L%d defs %s", fset.Position(id.Pos()).Line, obj)
+ facts = append(facts, fact)
+ }
+ }
+ for id, obj := range uses {
+ fact := fmt.Sprintf("L%d uses %s", fset.Position(id.Pos()).Line, obj)
+ facts = append(facts, fact)
+ }
+ sort.Strings(facts)
+
+ got := strings.Join(facts, "\n")
+ if got != want {
+ t.Errorf("Unexpected defs/uses\ngot:\n%s\nwant:\n%s", got, want)
+ }
+}
diff --git a/libgo/go/go/types/labels.go b/libgo/go/go/types/labels.go
new file mode 100644
index 00000000000..7364d4dbe68
--- /dev/null
+++ b/libgo/go/go/types/labels.go
@@ -0,0 +1,268 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// labels checks correct label use in body.
+func (check *Checker) labels(body *ast.BlockStmt) {
+ // set of all labels in this body
+ all := NewScope(nil, body.Pos(), body.End(), "label")
+
+ fwdJumps := check.blockBranches(all, nil, nil, body.List)
+
+ // If there are any forward jumps left, no label was found for
+ // the corresponding goto statements. Either those labels were
+ // never defined, or they are inside blocks and not reachable
+ // for the respective gotos.
+ for _, jmp := range fwdJumps {
+ var msg string
+ name := jmp.Label.Name
+ if alt := all.Lookup(name); alt != nil {
+ msg = "goto %s jumps into block"
+ alt.(*Label).used = true // avoid another error
+ } else {
+ msg = "label %s not declared"
+ }
+ check.errorf(jmp.Label.Pos(), msg, name)
+ }
+
+ // spec: "It is illegal to define a label that is never used."
+ for _, obj := range all.elems {
+ if lbl := obj.(*Label); !lbl.used {
+ check.softErrorf(lbl.pos, "label %s declared but not used", lbl.name)
+ }
+ }
+}
+
+// A block tracks label declarations in a block and its enclosing blocks.
+type block struct {
+ parent *block // enclosing block
+ lstmt *ast.LabeledStmt // labeled statement to which this block belongs, or nil
+ labels map[string]*ast.LabeledStmt // allocated lazily
+}
+
+// insert records a new label declaration for the current block.
+// The label must not have been declared before in any block.
+func (b *block) insert(s *ast.LabeledStmt) {
+ name := s.Label.Name
+ if debug {
+ assert(b.gotoTarget(name) == nil)
+ }
+ labels := b.labels
+ if labels == nil {
+ labels = make(map[string]*ast.LabeledStmt)
+ b.labels = labels
+ }
+ labels[name] = s
+}
+
+// gotoTarget returns the labeled statement in the current
+// or an enclosing block with the given label name, or nil.
+func (b *block) gotoTarget(name string) *ast.LabeledStmt {
+ for s := b; s != nil; s = s.parent {
+ if t := s.labels[name]; t != nil {
+ return t
+ }
+ }
+ return nil
+}
+
+// enclosingTarget returns the innermost enclosing labeled
+// statement with the given label name, or nil.
+func (b *block) enclosingTarget(name string) *ast.LabeledStmt {
+ for s := b; s != nil; s = s.parent {
+ if t := s.lstmt; t != nil && t.Label.Name == name {
+ return t
+ }
+ }
+ return nil
+}
+
+// blockBranches processes a block's statement list and returns the set of outgoing forward jumps.
+// all is the scope of all declared labels, parent the set of labels declared in the immediately
+// enclosing block, and lstmt is the labeled statement this block is associated with (or nil).
+func (check *Checker) blockBranches(all *Scope, parent *block, lstmt *ast.LabeledStmt, list []ast.Stmt) []*ast.BranchStmt {
+ b := &block{parent: parent, lstmt: lstmt}
+
+ var (
+ varDeclPos token.Pos
+ fwdJumps, badJumps []*ast.BranchStmt
+ )
+
+ // All forward jumps jumping over a variable declaration are possibly
+ // invalid (they may still jump out of the block and be ok).
+ // recordVarDecl records them for the given position.
+ recordVarDecl := func(pos token.Pos) {
+ varDeclPos = pos
+ badJumps = append(badJumps[:0], fwdJumps...) // copy fwdJumps to badJumps
+ }
+
+ jumpsOverVarDecl := func(jmp *ast.BranchStmt) bool {
+ if varDeclPos.IsValid() {
+ for _, bad := range badJumps {
+ if jmp == bad {
+ return true
+ }
+ }
+ }
+ return false
+ }
+
+ blockBranches := func(lstmt *ast.LabeledStmt, list []ast.Stmt) {
+ // Unresolved forward jumps inside the nested block
+ // become forward jumps in the current block.
+ fwdJumps = append(fwdJumps, check.blockBranches(all, b, lstmt, list)...)
+ }
+
+ var stmtBranches func(ast.Stmt)
+ stmtBranches = func(s ast.Stmt) {
+ switch s := s.(type) {
+ case *ast.DeclStmt:
+ if d, _ := s.Decl.(*ast.GenDecl); d != nil && d.Tok == token.VAR {
+ recordVarDecl(d.Pos())
+ }
+
+ case *ast.LabeledStmt:
+ // declare non-blank label
+ if name := s.Label.Name; name != "_" {
+ lbl := NewLabel(s.Label.Pos(), check.pkg, name)
+ if alt := all.Insert(lbl); alt != nil {
+ check.softErrorf(lbl.pos, "label %s already declared", name)
+ check.reportAltDecl(alt)
+ // ok to continue
+ } else {
+ b.insert(s)
+ check.recordDef(s.Label, lbl)
+ }
+ // resolve matching forward jumps and remove them from fwdJumps
+ i := 0
+ for _, jmp := range fwdJumps {
+ if jmp.Label.Name == name {
+ // match
+ lbl.used = true
+ check.recordUse(jmp.Label, lbl)
+ if jumpsOverVarDecl(jmp) {
+ check.softErrorf(
+ jmp.Label.Pos(),
+ "goto %s jumps over variable declaration at line %d",
+ name,
+ check.fset.Position(varDeclPos).Line,
+ )
+ // ok to continue
+ }
+ } else {
+ // no match - record new forward jump
+ fwdJumps[i] = jmp
+ i++
+ }
+ }
+ fwdJumps = fwdJumps[:i]
+ lstmt = s
+ }
+ stmtBranches(s.Stmt)
+
+ case *ast.BranchStmt:
+ if s.Label == nil {
+ return // checked in 1st pass (check.stmt)
+ }
+
+ // determine and validate target
+ name := s.Label.Name
+ switch s.Tok {
+ case token.BREAK:
+ // spec: "If there is a label, it must be that of an enclosing
+ // "for", "switch", or "select" statement, and that is the one
+ // whose execution terminates."
+ valid := false
+ if t := b.enclosingTarget(name); t != nil {
+ switch t.Stmt.(type) {
+ case *ast.SwitchStmt, *ast.TypeSwitchStmt, *ast.SelectStmt, *ast.ForStmt, *ast.RangeStmt:
+ valid = true
+ }
+ }
+ if !valid {
+ check.errorf(s.Label.Pos(), "invalid break label %s", name)
+ return
+ }
+
+ case token.CONTINUE:
+ // spec: "If there is a label, it must be that of an enclosing
+ // "for" statement, and that is the one whose execution advances."
+ valid := false
+ if t := b.enclosingTarget(name); t != nil {
+ switch t.Stmt.(type) {
+ case *ast.ForStmt, *ast.RangeStmt:
+ valid = true
+ }
+ }
+ if !valid {
+ check.errorf(s.Label.Pos(), "invalid continue label %s", name)
+ return
+ }
+
+ case token.GOTO:
+ if b.gotoTarget(name) == nil {
+ // label may be declared later - add branch to forward jumps
+ fwdJumps = append(fwdJumps, s)
+ return
+ }
+
+ default:
+ check.invalidAST(s.Pos(), "branch statement: %s %s", s.Tok, name)
+ return
+ }
+
+ // record label use
+ obj := all.Lookup(name)
+ obj.(*Label).used = true
+ check.recordUse(s.Label, obj)
+
+ case *ast.AssignStmt:
+ if s.Tok == token.DEFINE {
+ recordVarDecl(s.Pos())
+ }
+
+ case *ast.BlockStmt:
+ blockBranches(lstmt, s.List)
+
+ case *ast.IfStmt:
+ stmtBranches(s.Body)
+ if s.Else != nil {
+ stmtBranches(s.Else)
+ }
+
+ case *ast.CaseClause:
+ blockBranches(nil, s.Body)
+
+ case *ast.SwitchStmt:
+ stmtBranches(s.Body)
+
+ case *ast.TypeSwitchStmt:
+ stmtBranches(s.Body)
+
+ case *ast.CommClause:
+ blockBranches(nil, s.Body)
+
+ case *ast.SelectStmt:
+ stmtBranches(s.Body)
+
+ case *ast.ForStmt:
+ stmtBranches(s.Body)
+
+ case *ast.RangeStmt:
+ stmtBranches(s.Body)
+ }
+ }
+
+ for _, s := range list {
+ stmtBranches(s)
+ }
+
+ return fwdJumps
+}
diff --git a/libgo/go/go/types/lookup.go b/libgo/go/go/types/lookup.go
new file mode 100644
index 00000000000..3caca5519b6
--- /dev/null
+++ b/libgo/go/go/types/lookup.go
@@ -0,0 +1,341 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements various field and method lookup functions.
+
+package types
+
+// LookupFieldOrMethod looks up a field or method with given package and name
+// in T and returns the corresponding *Var or *Func, an index sequence, and a
+// bool indicating if there were any pointer indirections on the path to the
+// field or method. If addressable is set, T is the type of an addressable
+// variable (only matters for method lookups).
+//
+// The last index entry is the field or method index in the (possibly embedded)
+// type where the entry was found, either:
+//
+// 1) the list of declared methods of a named type; or
+// 2) the list of all methods (method set) of an interface type; or
+// 3) the list of fields of a struct type.
+//
+// The earlier index entries are the indices of the anonymous struct fields
+// traversed to get to the found entry, starting at depth 0.
+//
+// If no entry is found, a nil object is returned. In this case, the returned
+// index and indirect values have the following meaning:
+//
+// - If index != nil, the index sequence points to an ambiguous entry
+// (the same name appeared more than once at the same embedding level).
+//
+// - If indirect is set, a method with a pointer receiver type was found
+// but there was no pointer on the path from the actual receiver type to
+// the method's formal receiver base type, nor was the receiver addressable.
+//
+func LookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
+ // Methods cannot be associated to a named pointer type
+ // (spec: "The type denoted by T is called the receiver base type;
+ // it must not be a pointer or interface type and it must be declared
+ // in the same package as the method.").
+ // Thus, if we have a named pointer type, proceed with the underlying
+ // pointer type but discard the result if it is a method since we would
+ // not have found it for T (see also issue 8590).
+ if t, _ := T.(*Named); t != nil {
+ if p, _ := t.underlying.(*Pointer); p != nil {
+ obj, index, indirect = lookupFieldOrMethod(p, false, pkg, name)
+ if _, ok := obj.(*Func); ok {
+ return nil, nil, false
+ }
+ return
+ }
+ }
+
+ return lookupFieldOrMethod(T, addressable, pkg, name)
+}
+
+// TODO(gri) The named type consolidation and seen maps below must be
+// indexed by unique keys for a given type. Verify that named
+// types always have only one representation (even when imported
+// indirectly via different packages.)
+
+func lookupFieldOrMethod(T Type, addressable bool, pkg *Package, name string) (obj Object, index []int, indirect bool) {
+ // WARNING: The code in this function is extremely subtle - do not modify casually!
+ // This function and NewMethodSet should be kept in sync.
+
+ if name == "_" {
+ return // blank fields/methods are never found
+ }
+
+ typ, isPtr := deref(T)
+ named, _ := typ.(*Named)
+
+ // *typ where typ is an interface has no methods.
+ if isPtr {
+ utyp := typ
+ if named != nil {
+ utyp = named.underlying
+ }
+ if _, ok := utyp.(*Interface); ok {
+ return
+ }
+ }
+
+ // Start with typ as single entry at shallowest depth.
+ // If typ is not a named type, insert a nil type instead.
+ current := []embeddedType{{named, nil, isPtr, false}}
+
+ // named types that we have seen already, allocated lazily
+ var seen map[*Named]bool
+
+ // search current depth
+ for len(current) > 0 {
+ var next []embeddedType // embedded types found at current depth
+
+ // look for (pkg, name) in all types at current depth
+ for _, e := range current {
+ // The very first time only, e.typ may be nil.
+ // In this case, we don't have a named type and
+ // we simply continue with the underlying type.
+ if e.typ != nil {
+ if seen[e.typ] {
+ // We have seen this type before, at a more shallow depth
+ // (note that multiples of this type at the current depth
+ // were consolidated before). The type at that depth shadows
+ // this same type at the current depth, so we can ignore
+ // this one.
+ continue
+ }
+ if seen == nil {
+ seen = make(map[*Named]bool)
+ }
+ seen[e.typ] = true
+
+ // look for a matching attached method
+ if i, m := lookupMethod(e.typ.methods, pkg, name); m != nil {
+ // potential match
+ assert(m.typ != nil)
+ index = concat(e.index, i)
+ if obj != nil || e.multiples {
+ return nil, index, false // collision
+ }
+ obj = m
+ indirect = e.indirect
+ continue // we can't have a matching field or interface method
+ }
+
+ // continue with underlying type
+ typ = e.typ.underlying
+ }
+
+ switch t := typ.(type) {
+ case *Struct:
+ // look for a matching field and collect embedded types
+ for i, f := range t.fields {
+ if f.sameId(pkg, name) {
+ assert(f.typ != nil)
+ index = concat(e.index, i)
+ if obj != nil || e.multiples {
+ return nil, index, false // collision
+ }
+ obj = f
+ indirect = e.indirect
+ continue // we can't have a matching interface method
+ }
+ // Collect embedded struct fields for searching the next
+ // lower depth, but only if we have not seen a match yet
+ // (if we have a match it is either the desired field or
+ // we have a name collision on the same depth; in either
+ // case we don't need to look further).
+ // Embedded fields are always of the form T or *T where
+ // T is a named type. If e.typ appeared multiple times at
+ // this depth, f.typ appears multiple times at the next
+ // depth.
+ if obj == nil && f.anonymous {
+ // Ignore embedded basic types - only user-defined
+ // named types can have methods or struct fields.
+ typ, isPtr := deref(f.typ)
+ if t, _ := typ.(*Named); t != nil {
+ next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples})
+ }
+ }
+ }
+
+ case *Interface:
+ // look for a matching method
+ // TODO(gri) t.allMethods is sorted - use binary search
+ if i, m := lookupMethod(t.allMethods, pkg, name); m != nil {
+ assert(m.typ != nil)
+ index = concat(e.index, i)
+ if obj != nil || e.multiples {
+ return nil, index, false // collision
+ }
+ obj = m
+ indirect = e.indirect
+ }
+ }
+ }
+
+ if obj != nil {
+ // found a potential match
+ // spec: "A method call x.m() is valid if the method set of (the type of) x
+ // contains m and the argument list can be assigned to the parameter
+ // list of m. If x is addressable and &x's method set contains m, x.m()
+ // is shorthand for (&x).m()".
+ if f, _ := obj.(*Func); f != nil && ptrRecv(f) && !indirect && !addressable {
+ return nil, nil, true // pointer/addressable receiver required
+ }
+ return
+ }
+
+ current = consolidateMultiples(next)
+ }
+
+ return nil, nil, false // not found
+}
+
+// embeddedType represents an embedded named type
+type embeddedType struct {
+ typ *Named // nil means use the outer typ variable instead
+ index []int // embedded field indices, starting with index at depth 0
+ indirect bool // if set, there was a pointer indirection on the path to this field
+ multiples bool // if set, typ appears multiple times at this depth
+}
+
+// consolidateMultiples collects multiple list entries with the same type
+// into a single entry marked as containing multiples. The result is the
+// consolidated list.
+func consolidateMultiples(list []embeddedType) []embeddedType {
+ if len(list) <= 1 {
+ return list // at most one entry - nothing to do
+ }
+
+ n := 0 // number of entries w/ unique type
+ prev := make(map[*Named]int) // index at which type was previously seen
+ for _, e := range list {
+ if i, found := prev[e.typ]; found {
+ list[i].multiples = true
+ // ignore this entry
+ } else {
+ prev[e.typ] = n
+ list[n] = e
+ n++
+ }
+ }
+ return list[:n]
+}
+
+// MissingMethod returns (nil, false) if V implements T, otherwise it
+// returns a missing method required by T and whether it is missing or
+// just has the wrong type.
+//
+// For non-interface types V, or if static is set, V implements T if all
+// methods of T are present in V. Otherwise (V is an interface and static
+// is not set), MissingMethod only checks that methods of T which are also
+// present in V have matching types (e.g., for a type assertion x.(T) where
+// x is of interface type V).
+//
+func MissingMethod(V Type, T *Interface, static bool) (method *Func, wrongType bool) {
+ // fast path for common case
+ if T.Empty() {
+ return
+ }
+
+ // TODO(gri) Consider using method sets here. Might be more efficient.
+
+ if ityp, _ := V.Underlying().(*Interface); ityp != nil {
+ // TODO(gri) allMethods is sorted - can do this more efficiently
+ for _, m := range T.allMethods {
+ _, obj := lookupMethod(ityp.allMethods, m.pkg, m.name)
+ switch {
+ case obj == nil:
+ if static {
+ return m, false
+ }
+ case !Identical(obj.Type(), m.typ):
+ return m, true
+ }
+ }
+ return
+ }
+
+ // A concrete type implements T if it implements all methods of T.
+ for _, m := range T.allMethods {
+ obj, _, _ := lookupFieldOrMethod(V, false, m.pkg, m.name)
+
+ f, _ := obj.(*Func)
+ if f == nil {
+ return m, false
+ }
+
+ if !Identical(f.typ, m.typ) {
+ return m, true
+ }
+ }
+
+ return
+}
+
+// assertableTo reports whether a value of type V can be asserted to have type T.
+// It returns (nil, false) as affirmative answer. Otherwise it returns a missing
+// method required by V and whether it is missing or just has the wrong type.
+func assertableTo(V *Interface, T Type) (method *Func, wrongType bool) {
+ // no static check is required if T is an interface
+ // spec: "If T is an interface type, x.(T) asserts that the
+ // dynamic type of x implements the interface T."
+ if _, ok := T.Underlying().(*Interface); ok && !strict {
+ return
+ }
+ return MissingMethod(T, V, false)
+}
+
+// deref dereferences typ if it is a *Pointer and returns its base and true.
+// Otherwise it returns (typ, false).
+func deref(typ Type) (Type, bool) {
+ if p, _ := typ.(*Pointer); p != nil {
+ return p.base, true
+ }
+ return typ, false
+}
+
+// derefStructPtr dereferences typ if it is a (named or unnamed) pointer to a
+// (named or unnamed) struct and returns its base. Otherwise it returns typ.
+func derefStructPtr(typ Type) Type {
+ if p, _ := typ.Underlying().(*Pointer); p != nil {
+ if _, ok := p.base.Underlying().(*Struct); ok {
+ return p.base
+ }
+ }
+ return typ
+}
+
+// concat returns the result of concatenating list and i.
+// The result does not share its underlying array with list.
+func concat(list []int, i int) []int {
+ var t []int
+ t = append(t, list...)
+ return append(t, i)
+}
+
+// fieldIndex returns the index for the field with matching package and name, or a value < 0.
+func fieldIndex(fields []*Var, pkg *Package, name string) int {
+ if name != "_" {
+ for i, f := range fields {
+ if f.sameId(pkg, name) {
+ return i
+ }
+ }
+ }
+ return -1
+}
+
+// lookupMethod returns the index of and method with matching package and name, or (-1, nil).
+func lookupMethod(methods []*Func, pkg *Package, name string) (int, *Func) {
+ if name != "_" {
+ for i, m := range methods {
+ if m.sameId(pkg, name) {
+ return i, m
+ }
+ }
+ }
+ return -1, nil
+}
diff --git a/libgo/go/go/types/methodset.go b/libgo/go/go/types/methodset.go
new file mode 100644
index 00000000000..b27f2dac34a
--- /dev/null
+++ b/libgo/go/go/types/methodset.go
@@ -0,0 +1,268 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements method sets.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+ "sort"
+)
+
+// A MethodSet is an ordered set of concrete or abstract (interface) methods;
+// a method is a MethodVal selection, and they are ordered by ascending m.Obj().Id().
+// The zero value for a MethodSet is a ready-to-use empty method set.
+type MethodSet struct {
+ list []*Selection
+}
+
+func (s *MethodSet) String() string {
+ if s.Len() == 0 {
+ return "MethodSet {}"
+ }
+
+ var buf bytes.Buffer
+ fmt.Fprintln(&buf, "MethodSet {")
+ for _, f := range s.list {
+ fmt.Fprintf(&buf, "\t%s\n", f)
+ }
+ fmt.Fprintln(&buf, "}")
+ return buf.String()
+}
+
+// Len returns the number of methods in s.
+func (s *MethodSet) Len() int { return len(s.list) }
+
+// At returns the i'th method in s for 0 <= i < s.Len().
+func (s *MethodSet) At(i int) *Selection { return s.list[i] }
+
+// Lookup returns the method with matching package and name, or nil if not found.
+func (s *MethodSet) Lookup(pkg *Package, name string) *Selection {
+ if s.Len() == 0 {
+ return nil
+ }
+
+ key := Id(pkg, name)
+ i := sort.Search(len(s.list), func(i int) bool {
+ m := s.list[i]
+ return m.obj.Id() >= key
+ })
+ if i < len(s.list) {
+ m := s.list[i]
+ if m.obj.Id() == key {
+ return m
+ }
+ }
+ return nil
+}
+
+// Shared empty method set.
+var emptyMethodSet MethodSet
+
+// NewMethodSet returns the method set for the given type T.
+// It always returns a non-nil method set, even if it is empty.
+func NewMethodSet(T Type) *MethodSet {
+ // WARNING: The code in this function is extremely subtle - do not modify casually!
+ // This function and lookupFieldOrMethod should be kept in sync.
+
+ // method set up to the current depth, allocated lazily
+ var base methodSet
+
+ typ, isPtr := deref(T)
+ named, _ := typ.(*Named)
+
+ // *typ where typ is an interface has no methods.
+ if isPtr {
+ utyp := typ
+ if named != nil {
+ utyp = named.underlying
+ }
+ if _, ok := utyp.(*Interface); ok {
+ return &emptyMethodSet
+ }
+ }
+
+ // Start with typ as single entry at shallowest depth.
+ // If typ is not a named type, insert a nil type instead.
+ current := []embeddedType{{named, nil, isPtr, false}}
+
+ // named types that we have seen already, allocated lazily
+ var seen map[*Named]bool
+
+ // collect methods at current depth
+ for len(current) > 0 {
+ var next []embeddedType // embedded types found at current depth
+
+ // field and method sets at current depth, allocated lazily
+ var fset fieldSet
+ var mset methodSet
+
+ for _, e := range current {
+ // The very first time only, e.typ may be nil.
+ // In this case, we don't have a named type and
+ // we simply continue with the underlying type.
+ if e.typ != nil {
+ if seen[e.typ] {
+ // We have seen this type before, at a more shallow depth
+ // (note that multiples of this type at the current depth
+ // were consolidated before). The type at that depth shadows
+ // this same type at the current depth, so we can ignore
+ // this one.
+ continue
+ }
+ if seen == nil {
+ seen = make(map[*Named]bool)
+ }
+ seen[e.typ] = true
+
+ mset = mset.add(e.typ.methods, e.index, e.indirect, e.multiples)
+
+ // continue with underlying type
+ typ = e.typ.underlying
+ }
+
+ switch t := typ.(type) {
+ case *Struct:
+ for i, f := range t.fields {
+ fset = fset.add(f, e.multiples)
+
+ // Embedded fields are always of the form T or *T where
+ // T is a named type. If typ appeared multiple times at
+ // this depth, f.Type appears multiple times at the next
+ // depth.
+ if f.anonymous {
+ // Ignore embedded basic types - only user-defined
+ // named types can have methods or struct fields.
+ typ, isPtr := deref(f.typ)
+ if t, _ := typ.(*Named); t != nil {
+ next = append(next, embeddedType{t, concat(e.index, i), e.indirect || isPtr, e.multiples})
+ }
+ }
+ }
+
+ case *Interface:
+ mset = mset.add(t.allMethods, e.index, true, e.multiples)
+ }
+ }
+
+ // Add methods and collisions at this depth to base if no entries with matching
+ // names exist already.
+ for k, m := range mset {
+ if _, found := base[k]; !found {
+ // Fields collide with methods of the same name at this depth.
+ if _, found := fset[k]; found {
+ m = nil // collision
+ }
+ if base == nil {
+ base = make(methodSet)
+ }
+ base[k] = m
+ }
+ }
+
+ // Multiple fields with matching names collide at this depth and shadow all
+ // entries further down; add them as collisions to base if no entries with
+ // matching names exist already.
+ for k, f := range fset {
+ if f == nil {
+ if _, found := base[k]; !found {
+ if base == nil {
+ base = make(methodSet)
+ }
+ base[k] = nil // collision
+ }
+ }
+ }
+
+ current = consolidateMultiples(next)
+ }
+
+ if len(base) == 0 {
+ return &emptyMethodSet
+ }
+
+ // collect methods
+ var list []*Selection
+ for _, m := range base {
+ if m != nil {
+ m.recv = T
+ list = append(list, m)
+ }
+ }
+ sort.Sort(byUniqueName(list))
+ return &MethodSet{list}
+}
+
+// A fieldSet is a set of fields and name collisions.
+// A collision indicates that multiple fields with the
+// same unique id appeared.
+type fieldSet map[string]*Var // a nil entry indicates a name collision
+
+// Add adds field f to the field set s.
+// If multiples is set, f appears multiple times
+// and is treated as a collision.
+func (s fieldSet) add(f *Var, multiples bool) fieldSet {
+ if s == nil {
+ s = make(fieldSet)
+ }
+ key := f.Id()
+ // if f is not in the set, add it
+ if !multiples {
+ if _, found := s[key]; !found {
+ s[key] = f
+ return s
+ }
+ }
+ s[key] = nil // collision
+ return s
+}
+
+// A methodSet is a set of methods and name collisions.
+// A collision indicates that multiple methods with the
+// same unique id appeared.
+type methodSet map[string]*Selection // a nil entry indicates a name collision
+
+// Add adds all functions in list to the method set s.
+// If multiples is set, every function in list appears multiple times
+// and is treated as a collision.
+func (s methodSet) add(list []*Func, index []int, indirect bool, multiples bool) methodSet {
+ if len(list) == 0 {
+ return s
+ }
+ if s == nil {
+ s = make(methodSet)
+ }
+ for i, f := range list {
+ key := f.Id()
+ // if f is not in the set, add it
+ if !multiples {
+ // TODO(gri) A found method may not be added because it's not in the method set
+ // (!indirect && ptrRecv(f)). A 2nd method on the same level may be in the method
+ // set and may not collide with the first one, thus leading to a false positive.
+ // Is that possible? Investigate.
+ if _, found := s[key]; !found && (indirect || !ptrRecv(f)) {
+ s[key] = &Selection{MethodVal, nil, f, concat(index, i), indirect}
+ continue
+ }
+ }
+ s[key] = nil // collision
+ }
+ return s
+}
+
+// ptrRecv reports whether the receiver is of the form *T.
+// The receiver must exist.
+func ptrRecv(f *Func) bool {
+ _, isPtr := deref(f.typ.(*Signature).recv.typ)
+ return isPtr
+}
+
+// byUniqueName function lists can be sorted by their unique names.
+type byUniqueName []*Selection
+
+func (a byUniqueName) Len() int { return len(a) }
+func (a byUniqueName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() }
+func (a byUniqueName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
diff --git a/libgo/go/go/types/object.go b/libgo/go/go/types/object.go
new file mode 100644
index 00000000000..b835c6e53e8
--- /dev/null
+++ b/libgo/go/go/types/object.go
@@ -0,0 +1,360 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "go/token"
+)
+
+// TODO(gri) Document factory, accessor methods, and fields. General clean-up.
+
+// An Object describes a named language entity such as a package,
+// constant, type, variable, function (incl. methods), or label.
+// All objects implement the Object interface.
+//
+type Object interface {
+ Parent() *Scope // scope in which this object is declared
+ Pos() token.Pos // position of object identifier in declaration
+ Pkg() *Package // nil for objects in the Universe scope and labels
+ Name() string // package local object name
+ Type() Type // object type
+ Exported() bool // reports whether the name starts with a capital letter
+ Id() string // object id (see Id below)
+
+ // String returns a human-readable string of the object.
+ String() string
+
+ // order reflects a package-level object's source order: if object
+ // a is before object b in the source, then a.order() < b.order().
+ // order returns a value > 0 for package-level objects; it returns
+ // 0 for all other objects (including objects in file scopes).
+ order() uint32
+
+ // setOrder sets the order number of the object. It must be > 0.
+ setOrder(uint32)
+
+ // setParent sets the parent scope of the object.
+ setParent(*Scope)
+
+ // sameId reports whether obj.Id() and Id(pkg, name) are the same.
+ sameId(pkg *Package, name string) bool
+
+ // scopePos returns the start position of the scope of this Object
+ scopePos() token.Pos
+
+ // setScopePos sets the start position of the scope for this Object.
+ setScopePos(pos token.Pos)
+}
+
+// Id returns name if it is exported, otherwise it
+// returns the name qualified with the package path.
+func Id(pkg *Package, name string) string {
+ if ast.IsExported(name) {
+ return name
+ }
+ // unexported names need the package path for differentiation
+ // (if there's no package, make sure we don't start with '.'
+ // as that may change the order of methods between a setup
+ // inside a package and outside a package - which breaks some
+ // tests)
+ path := "_"
+ // TODO(gri): shouldn't !ast.IsExported(name) => pkg != nil be an precondition?
+ // if pkg == nil {
+ // panic("nil package in lookup of unexported name")
+ // }
+ if pkg != nil {
+ path = pkg.path
+ if path == "" {
+ path = "_"
+ }
+ }
+ return path + "." + name
+}
+
+// An object implements the common parts of an Object.
+type object struct {
+ parent *Scope
+ pos token.Pos
+ pkg *Package
+ name string
+ typ Type
+ order_ uint32
+ scopePos_ token.Pos
+}
+
+func (obj *object) Parent() *Scope { return obj.parent }
+func (obj *object) Pos() token.Pos { return obj.pos }
+func (obj *object) Pkg() *Package { return obj.pkg }
+func (obj *object) Name() string { return obj.name }
+func (obj *object) Type() Type { return obj.typ }
+func (obj *object) Exported() bool { return ast.IsExported(obj.name) }
+func (obj *object) Id() string { return Id(obj.pkg, obj.name) }
+func (obj *object) String() string { panic("abstract") }
+func (obj *object) order() uint32 { return obj.order_ }
+func (obj *object) scopePos() token.Pos { return obj.scopePos_ }
+
+func (obj *object) setParent(parent *Scope) { obj.parent = parent }
+func (obj *object) setOrder(order uint32) { assert(order > 0); obj.order_ = order }
+func (obj *object) setScopePos(pos token.Pos) { obj.scopePos_ = pos }
+
+func (obj *object) sameId(pkg *Package, name string) bool {
+ // spec:
+ // "Two identifiers are different if they are spelled differently,
+ // or if they appear in different packages and are not exported.
+ // Otherwise, they are the same."
+ if name != obj.name {
+ return false
+ }
+ // obj.Name == name
+ if obj.Exported() {
+ return true
+ }
+ // not exported, so packages must be the same (pkg == nil for
+ // fields in Universe scope; this can only happen for types
+ // introduced via Eval)
+ if pkg == nil || obj.pkg == nil {
+ return pkg == obj.pkg
+ }
+ // pkg != nil && obj.pkg != nil
+ return pkg.path == obj.pkg.path
+}
+
+// A PkgName represents an imported Go package.
+type PkgName struct {
+ object
+ imported *Package
+ used bool // set if the package was used
+}
+
+func NewPkgName(pos token.Pos, pkg *Package, name string, imported *Package) *PkgName {
+ return &PkgName{object{nil, pos, pkg, name, Typ[Invalid], 0, token.NoPos}, imported, false}
+}
+
+// Imported returns the package that was imported.
+// It is distinct from Pkg(), which is the package containing the import statement.
+func (obj *PkgName) Imported() *Package { return obj.imported }
+
+// A Const represents a declared constant.
+type Const struct {
+ object
+ val constant.Value
+ visited bool // for initialization cycle detection
+}
+
+func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val constant.Value) *Const {
+ return &Const{object{nil, pos, pkg, name, typ, 0, token.NoPos}, val, false}
+}
+
+func (obj *Const) Val() constant.Value { return obj.val }
+
+// A TypeName represents a declared type.
+type TypeName struct {
+ object
+}
+
+func NewTypeName(pos token.Pos, pkg *Package, name string, typ Type) *TypeName {
+ return &TypeName{object{nil, pos, pkg, name, typ, 0, token.NoPos}}
+}
+
+// A Variable represents a declared variable (including function parameters and results, and struct fields).
+type Var struct {
+ object
+ anonymous bool // if set, the variable is an anonymous struct field, and name is the type name
+ visited bool // for initialization cycle detection
+ isField bool // var is struct field
+ used bool // set if the variable was used
+}
+
+func NewVar(pos token.Pos, pkg *Package, name string, typ Type) *Var {
+ return &Var{object: object{nil, pos, pkg, name, typ, 0, token.NoPos}}
+}
+
+func NewParam(pos token.Pos, pkg *Package, name string, typ Type) *Var {
+ return &Var{object: object{nil, pos, pkg, name, typ, 0, token.NoPos}, used: true} // parameters are always 'used'
+}
+
+func NewField(pos token.Pos, pkg *Package, name string, typ Type, anonymous bool) *Var {
+ return &Var{object: object{nil, pos, pkg, name, typ, 0, token.NoPos}, anonymous: anonymous, isField: true}
+}
+
+func (obj *Var) Anonymous() bool { return obj.anonymous }
+
+func (obj *Var) IsField() bool { return obj.isField }
+
+// A Func represents a declared function, concrete method, or abstract
+// (interface) method. Its Type() is always a *Signature.
+// An abstract method may belong to many interfaces due to embedding.
+type Func struct {
+ object
+}
+
+func NewFunc(pos token.Pos, pkg *Package, name string, sig *Signature) *Func {
+ // don't store a nil signature
+ var typ Type
+ if sig != nil {
+ typ = sig
+ }
+ return &Func{object{nil, pos, pkg, name, typ, 0, token.NoPos}}
+}
+
+// FullName returns the package- or receiver-type-qualified name of
+// function or method obj.
+func (obj *Func) FullName() string {
+ var buf bytes.Buffer
+ writeFuncName(&buf, obj, nil)
+ return buf.String()
+}
+
+func (obj *Func) Scope() *Scope {
+ return obj.typ.(*Signature).scope
+}
+
+// A Label represents a declared label.
+type Label struct {
+ object
+ used bool // set if the label was used
+}
+
+func NewLabel(pos token.Pos, pkg *Package, name string) *Label {
+ return &Label{object{pos: pos, pkg: pkg, name: name, typ: Typ[Invalid]}, false}
+}
+
+// A Builtin represents a built-in function.
+// Builtins don't have a valid type.
+type Builtin struct {
+ object
+ id builtinId
+}
+
+func newBuiltin(id builtinId) *Builtin {
+ return &Builtin{object{name: predeclaredFuncs[id].name, typ: Typ[Invalid]}, id}
+}
+
+// Nil represents the predeclared value nil.
+type Nil struct {
+ object
+}
+
+func writeObject(buf *bytes.Buffer, obj Object, qf Qualifier) {
+ typ := obj.Type()
+ switch obj := obj.(type) {
+ case *PkgName:
+ fmt.Fprintf(buf, "package %s", obj.Name())
+ if path := obj.imported.path; path != "" && path != obj.name {
+ fmt.Fprintf(buf, " (%q)", path)
+ }
+ return
+
+ case *Const:
+ buf.WriteString("const")
+
+ case *TypeName:
+ buf.WriteString("type")
+ typ = typ.Underlying()
+
+ case *Var:
+ if obj.isField {
+ buf.WriteString("field")
+ } else {
+ buf.WriteString("var")
+ }
+
+ case *Func:
+ buf.WriteString("func ")
+ writeFuncName(buf, obj, qf)
+ if typ != nil {
+ WriteSignature(buf, typ.(*Signature), qf)
+ }
+ return
+
+ case *Label:
+ buf.WriteString("label")
+ typ = nil
+
+ case *Builtin:
+ buf.WriteString("builtin")
+ typ = nil
+
+ case *Nil:
+ buf.WriteString("nil")
+ return
+
+ default:
+ panic(fmt.Sprintf("writeObject(%T)", obj))
+ }
+
+ buf.WriteByte(' ')
+
+ // For package-level objects, qualify the name.
+ if obj.Pkg() != nil && obj.Pkg().scope.Lookup(obj.Name()) == obj {
+ writePackage(buf, obj.Pkg(), qf)
+ }
+ buf.WriteString(obj.Name())
+ if typ != nil {
+ buf.WriteByte(' ')
+ WriteType(buf, typ, qf)
+ }
+}
+
+func writePackage(buf *bytes.Buffer, pkg *Package, qf Qualifier) {
+ if pkg == nil {
+ return
+ }
+ var s string
+ if qf != nil {
+ s = qf(pkg)
+ } else {
+ s = pkg.Path()
+ }
+ if s != "" {
+ buf.WriteString(s)
+ buf.WriteByte('.')
+ }
+}
+
+// ObjectString returns the string form of obj.
+// The Qualifier controls the printing of
+// package-level objects, and may be nil.
+func ObjectString(obj Object, qf Qualifier) string {
+ var buf bytes.Buffer
+ writeObject(&buf, obj, qf)
+ return buf.String()
+}
+
+func (obj *PkgName) String() string { return ObjectString(obj, nil) }
+func (obj *Const) String() string { return ObjectString(obj, nil) }
+func (obj *TypeName) String() string { return ObjectString(obj, nil) }
+func (obj *Var) String() string { return ObjectString(obj, nil) }
+func (obj *Func) String() string { return ObjectString(obj, nil) }
+func (obj *Label) String() string { return ObjectString(obj, nil) }
+func (obj *Builtin) String() string { return ObjectString(obj, nil) }
+func (obj *Nil) String() string { return ObjectString(obj, nil) }
+
+func writeFuncName(buf *bytes.Buffer, f *Func, qf Qualifier) {
+ if f.typ != nil {
+ sig := f.typ.(*Signature)
+ if recv := sig.Recv(); recv != nil {
+ buf.WriteByte('(')
+ if _, ok := recv.Type().(*Interface); ok {
+ // gcimporter creates abstract methods of
+ // named interfaces using the interface type
+ // (not the named type) as the receiver.
+ // Don't print it in full.
+ buf.WriteString("interface")
+ } else {
+ WriteType(buf, recv.Type(), qf)
+ }
+ buf.WriteByte(')')
+ buf.WriteByte('.')
+ } else if f.pkg != nil {
+ writePackage(buf, f.pkg, qf)
+ }
+ }
+ buf.WriteString(f.name)
+}
diff --git a/libgo/go/go/types/objset.go b/libgo/go/go/types/objset.go
new file mode 100644
index 00000000000..55eb74addba
--- /dev/null
+++ b/libgo/go/go/types/objset.go
@@ -0,0 +1,31 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements objsets.
+//
+// An objset is similar to a Scope but objset elements
+// are identified by their unique id, instead of their
+// object name.
+
+package types
+
+// An objset is a set of objects identified by their unique id.
+// The zero value for objset is a ready-to-use empty objset.
+type objset map[string]Object // initialized lazily
+
+// insert attempts to insert an object obj into objset s.
+// If s already contains an alternative object alt with
+// the same name, insert leaves s unchanged and returns alt.
+// Otherwise it inserts obj and returns nil.
+func (s *objset) insert(obj Object) Object {
+ id := obj.Id()
+ if alt := (*s)[id]; alt != nil {
+ return alt
+ }
+ if *s == nil {
+ *s = make(map[string]Object)
+ }
+ (*s)[id] = obj
+ return nil
+}
diff --git a/libgo/go/go/types/operand.go b/libgo/go/go/types/operand.go
new file mode 100644
index 00000000000..d3bab51b04a
--- /dev/null
+++ b/libgo/go/go/types/operand.go
@@ -0,0 +1,287 @@
+// Copyright 2012 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 defines operands and associated operations.
+
+package types
+
+import (
+ "bytes"
+ "go/ast"
+ "go/constant"
+ "go/token"
+)
+
+// An operandMode specifies the (addressing) mode of an operand.
+type operandMode byte
+
+const (
+ invalid operandMode = iota // operand is invalid
+ novalue // operand represents no value (result of a function call w/o result)
+ builtin // operand is a built-in function
+ typexpr // operand is a type
+ constant_ // operand is a constant; the operand's typ is a Basic type
+ variable // operand is an addressable variable
+ mapindex // operand is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)
+ value // operand is a computed value
+ commaok // like value, but operand may be used in a comma,ok expression
+)
+
+var operandModeString = [...]string{
+ invalid: "invalid operand",
+ novalue: "no value",
+ builtin: "built-in",
+ typexpr: "type",
+ constant_: "constant",
+ variable: "variable",
+ mapindex: "map index expression",
+ value: "value",
+ commaok: "comma, ok expression",
+}
+
+// An operand represents an intermediate value during type checking.
+// Operands have an (addressing) mode, the expression evaluating to
+// the operand, the operand's type, a value for constants, and an id
+// for built-in functions.
+// The zero value of operand is a ready to use invalid operand.
+//
+type operand struct {
+ mode operandMode
+ expr ast.Expr
+ typ Type
+ val constant.Value
+ id builtinId
+}
+
+// pos returns the position of the expression corresponding to x.
+// If x is invalid the position is token.NoPos.
+//
+func (x *operand) pos() token.Pos {
+ // x.expr may not be set if x is invalid
+ if x.expr == nil {
+ return token.NoPos
+ }
+ return x.expr.Pos()
+}
+
+// Operand string formats
+// (not all "untyped" cases can appear due to the type system,
+// but they fall out naturally here)
+//
+// mode format
+//
+// invalid <expr> ( <mode> )
+// novalue <expr> ( <mode> )
+// builtin <expr> ( <mode> )
+// typexpr <expr> ( <mode> )
+//
+// constant <expr> (<untyped kind> <mode> )
+// constant <expr> ( <mode> of type <typ>)
+// constant <expr> (<untyped kind> <mode> <val> )
+// constant <expr> ( <mode> <val> of type <typ>)
+//
+// variable <expr> (<untyped kind> <mode> )
+// variable <expr> ( <mode> of type <typ>)
+//
+// mapindex <expr> (<untyped kind> <mode> )
+// mapindex <expr> ( <mode> of type <typ>)
+//
+// value <expr> (<untyped kind> <mode> )
+// value <expr> ( <mode> of type <typ>)
+//
+// commaok <expr> (<untyped kind> <mode> )
+// commaok <expr> ( <mode> of type <typ>)
+//
+func operandString(x *operand, qf Qualifier) string {
+ var buf bytes.Buffer
+
+ var expr string
+ if x.expr != nil {
+ expr = ExprString(x.expr)
+ } else {
+ switch x.mode {
+ case builtin:
+ expr = predeclaredFuncs[x.id].name
+ case typexpr:
+ expr = TypeString(x.typ, qf)
+ case constant_:
+ expr = x.val.String()
+ }
+ }
+
+ // <expr> (
+ if expr != "" {
+ buf.WriteString(expr)
+ buf.WriteString(" (")
+ }
+
+ // <untyped kind>
+ hasType := false
+ switch x.mode {
+ case invalid, novalue, builtin, typexpr:
+ // no type
+ default:
+ // has type
+ if isUntyped(x.typ) {
+ buf.WriteString(x.typ.(*Basic).name)
+ buf.WriteByte(' ')
+ break
+ }
+ hasType = true
+ }
+
+ // <mode>
+ buf.WriteString(operandModeString[x.mode])
+
+ // <val>
+ if x.mode == constant_ {
+ if s := x.val.String(); s != expr {
+ buf.WriteByte(' ')
+ buf.WriteString(s)
+ }
+ }
+
+ // <typ>
+ if hasType {
+ if x.typ != Typ[Invalid] {
+ buf.WriteString(" of type ")
+ WriteType(&buf, x.typ, qf)
+ } else {
+ buf.WriteString(" with invalid type")
+ }
+ }
+
+ // )
+ if expr != "" {
+ buf.WriteByte(')')
+ }
+
+ return buf.String()
+}
+
+func (x *operand) String() string {
+ return operandString(x, nil)
+}
+
+// setConst sets x to the untyped constant for literal lit.
+func (x *operand) setConst(tok token.Token, lit string) {
+ val := constant.MakeFromLiteral(lit, tok, 0)
+ if val == nil {
+ // TODO(gri) Should we make it an unknown constant instead?
+ x.mode = invalid
+ return
+ }
+
+ var kind BasicKind
+ switch tok {
+ case token.INT:
+ kind = UntypedInt
+ case token.FLOAT:
+ kind = UntypedFloat
+ case token.IMAG:
+ kind = UntypedComplex
+ case token.CHAR:
+ kind = UntypedRune
+ case token.STRING:
+ kind = UntypedString
+ }
+
+ x.mode = constant_
+ x.typ = Typ[kind]
+ x.val = val
+}
+
+// isNil reports whether x is the nil value.
+func (x *operand) isNil() bool {
+ return x.mode == value && x.typ == Typ[UntypedNil]
+}
+
+// TODO(gri) The functions operand.assignableTo, checker.convertUntyped,
+// checker.representable, and checker.assignment are
+// overlapping in functionality. Need to simplify and clean up.
+
+// assignableTo reports whether x is assignable to a variable of type T.
+func (x *operand) assignableTo(conf *Config, T Type) bool {
+ if x.mode == invalid || T == Typ[Invalid] {
+ return true // avoid spurious errors
+ }
+
+ V := x.typ
+
+ // x's type is identical to T
+ if Identical(V, T) {
+ return true
+ }
+
+ Vu := V.Underlying()
+ Tu := T.Underlying()
+
+ // T is an interface type and x implements T
+ // (Do this check first as it might succeed early.)
+ if Ti, ok := Tu.(*Interface); ok {
+ if Implements(x.typ, Ti) {
+ return true
+ }
+ }
+
+ // x's type V and T have identical underlying types
+ // and at least one of V or T is not a named type
+ if Identical(Vu, Tu) && (!isNamed(V) || !isNamed(T)) {
+ return true
+ }
+
+ // x is a bidirectional channel value, T is a channel
+ // type, x's type V and T have identical element types,
+ // and at least one of V or T is not a named type
+ if Vc, ok := Vu.(*Chan); ok && Vc.dir == SendRecv {
+ if Tc, ok := Tu.(*Chan); ok && Identical(Vc.elem, Tc.elem) {
+ return !isNamed(V) || !isNamed(T)
+ }
+ }
+
+ // x is the predeclared identifier nil and T is a pointer,
+ // function, slice, map, channel, or interface type
+ if x.isNil() {
+ switch t := Tu.(type) {
+ case *Basic:
+ if t.kind == UnsafePointer {
+ return true
+ }
+ case *Pointer, *Signature, *Slice, *Map, *Chan, *Interface:
+ return true
+ }
+ return false
+ }
+
+ // x is an untyped constant representable by a value of type T
+ // TODO(gri) This is borrowing from checker.convertUntyped and
+ // checker.representable. Need to clean up.
+ if isUntyped(Vu) {
+ switch t := Tu.(type) {
+ case *Basic:
+ if x.mode == constant_ {
+ return representableConst(x.val, conf, t.kind, nil)
+ }
+ // The result of a comparison is an untyped boolean,
+ // but may not be a constant.
+ if Vb, _ := Vu.(*Basic); Vb != nil {
+ return Vb.kind == UntypedBool && isBoolean(Tu)
+ }
+ case *Interface:
+ return x.isNil() || t.Empty()
+ case *Pointer, *Signature, *Slice, *Map, *Chan:
+ return x.isNil()
+ }
+ }
+
+ return false
+}
+
+// isInteger reports whether x is value of integer type
+// or an untyped constant representable as an integer.
+func (x *operand) isInteger() bool {
+ return x.mode == invalid ||
+ isInteger(x.typ) ||
+ isUntyped(x.typ) && x.mode == constant_ && representableConst(x.val, nil, UntypedInt, nil) // no *Config required for UntypedInt
+}
diff --git a/libgo/go/go/types/ordering.go b/libgo/go/go/types/ordering.go
new file mode 100644
index 00000000000..6bb98f2dc10
--- /dev/null
+++ b/libgo/go/go/types/ordering.go
@@ -0,0 +1,127 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements resolveOrder.
+
+package types
+
+import (
+ "go/ast"
+ "sort"
+)
+
+// resolveOrder computes the order in which package-level objects
+// must be type-checked.
+//
+// Interface types appear first in the list, sorted topologically
+// by dependencies on embedded interfaces that are also declared
+// in this package, followed by all other objects sorted in source
+// order.
+//
+// TODO(gri) Consider sorting all types by dependencies here, and
+// in the process check _and_ report type cycles. This may simplify
+// the full type-checking phase.
+//
+func (check *Checker) resolveOrder() []Object {
+ var ifaces, others []Object
+
+ // collect interface types with their dependencies, and all other objects
+ for obj := range check.objMap {
+ if ityp := check.interfaceFor(obj); ityp != nil {
+ ifaces = append(ifaces, obj)
+ // determine dependencies on embedded interfaces
+ for _, f := range ityp.Methods.List {
+ if len(f.Names) == 0 {
+ // Embedded interface: The type must be a (possibly
+ // qualified) identifier denoting another interface.
+ // Imported interfaces are already fully resolved,
+ // so we can ignore qualified identifiers.
+ if ident, _ := f.Type.(*ast.Ident); ident != nil {
+ embedded := check.pkg.scope.Lookup(ident.Name)
+ if check.interfaceFor(embedded) != nil {
+ check.objMap[obj].addDep(embedded)
+ }
+ }
+ }
+ }
+ } else {
+ others = append(others, obj)
+ }
+ }
+
+ // final object order
+ var order []Object
+
+ // sort interface types topologically by dependencies,
+ // and in source order if there are no dependencies
+ sort.Sort(inSourceOrder(ifaces))
+ if debug {
+ for _, obj := range ifaces {
+ assert(check.objMap[obj].mark == 0)
+ }
+ }
+ for _, obj := range ifaces {
+ check.appendInPostOrder(&order, obj)
+ }
+
+ // sort everything else in source order
+ sort.Sort(inSourceOrder(others))
+
+ return append(order, others...)
+}
+
+// interfaceFor returns the AST interface denoted by obj, or nil.
+func (check *Checker) interfaceFor(obj Object) *ast.InterfaceType {
+ tname, _ := obj.(*TypeName)
+ if tname == nil {
+ return nil // not a type
+ }
+ d := check.objMap[obj]
+ if d == nil {
+ check.dump("%s: %s should have been declared", obj.Pos(), obj.Name())
+ unreachable()
+ }
+ if d.typ == nil {
+ return nil // invalid AST - ignore (will be handled later)
+ }
+ ityp, _ := d.typ.(*ast.InterfaceType)
+ return ityp
+}
+
+func (check *Checker) appendInPostOrder(order *[]Object, obj Object) {
+ d := check.objMap[obj]
+ if d.mark != 0 {
+ // We've already seen this object; either because it's
+ // already added to order, or because we have a cycle.
+ // In both cases we stop. Cycle errors are reported
+ // when type-checking types.
+ return
+ }
+ d.mark = 1
+
+ for _, obj := range orderedSetObjects(d.deps) {
+ check.appendInPostOrder(order, obj)
+ }
+
+ *order = append(*order, obj)
+}
+
+func orderedSetObjects(set map[Object]bool) []Object {
+ list := make([]Object, len(set))
+ i := 0
+ for obj := range set {
+ // we don't care about the map element value
+ list[i] = obj
+ i++
+ }
+ sort.Sort(inSourceOrder(list))
+ return list
+}
+
+// inSourceOrder implements the sort.Sort interface.
+type inSourceOrder []Object
+
+func (a inSourceOrder) Len() int { return len(a) }
+func (a inSourceOrder) Less(i, j int) bool { return a[i].order() < a[j].order() }
+func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
diff --git a/libgo/go/go/types/package.go b/libgo/go/go/types/package.go
new file mode 100644
index 00000000000..48fe8398fe6
--- /dev/null
+++ b/libgo/go/go/types/package.go
@@ -0,0 +1,65 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import (
+ "fmt"
+ "go/token"
+)
+
+// A Package describes a Go package.
+type Package struct {
+ path string
+ name string
+ scope *Scope
+ complete bool
+ imports []*Package
+ fake bool // scope lookup errors are silently dropped if package is fake (internal use only)
+}
+
+// NewPackage returns a new Package for the given package path and name;
+// the name must not be the blank identifier.
+// The package is not complete and contains no explicit imports.
+func NewPackage(path, name string) *Package {
+ if name == "_" {
+ panic("invalid package name _")
+ }
+ scope := NewScope(Universe, token.NoPos, token.NoPos, fmt.Sprintf("package %q", path))
+ return &Package{path: path, name: name, scope: scope}
+}
+
+// Path returns the package path.
+func (pkg *Package) Path() string { return pkg.path }
+
+// Name returns the package name.
+func (pkg *Package) Name() string { return pkg.name }
+
+// Scope returns the (complete or incomplete) package scope
+// holding the objects declared at package level (TypeNames,
+// Consts, Vars, and Funcs).
+func (pkg *Package) Scope() *Scope { return pkg.scope }
+
+// A package is complete if its scope contains (at least) all
+// exported objects; otherwise it is incomplete.
+func (pkg *Package) Complete() bool { return pkg.complete }
+
+// MarkComplete marks a package as complete.
+func (pkg *Package) MarkComplete() { pkg.complete = true }
+
+// Imports returns the list of packages directly imported by
+// pkg; the list is in source order. Package unsafe is excluded.
+//
+// If pkg was loaded from export data, Imports includes packages that
+// provide package-level objects referenced by pkg. This may be more or
+// less than the set of packages directly imported by pkg's source code.
+func (pkg *Package) Imports() []*Package { return pkg.imports }
+
+// SetImports sets the list of explicitly imported packages to list.
+// It is the caller's responsibility to make sure list elements are unique.
+func (pkg *Package) SetImports(list []*Package) { pkg.imports = list }
+
+func (pkg *Package) String() string {
+ return fmt.Sprintf("package %s (%q)", pkg.name, pkg.path)
+}
diff --git a/libgo/go/go/types/predicates.go b/libgo/go/go/types/predicates.go
new file mode 100644
index 00000000000..993c6d290b1
--- /dev/null
+++ b/libgo/go/go/types/predicates.go
@@ -0,0 +1,309 @@
+// Copyright 2012 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 commonly used type predicates.
+
+package types
+
+import "sort"
+
+func isNamed(typ Type) bool {
+ if _, ok := typ.(*Basic); ok {
+ return ok
+ }
+ _, ok := typ.(*Named)
+ return ok
+}
+
+func isBoolean(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsBoolean != 0
+}
+
+func isInteger(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsInteger != 0
+}
+
+func isUnsigned(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsUnsigned != 0
+}
+
+func isFloat(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsFloat != 0
+}
+
+func isComplex(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsComplex != 0
+}
+
+func isNumeric(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsNumeric != 0
+}
+
+func isString(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsString != 0
+}
+
+func isTyped(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return !ok || t.info&IsUntyped == 0
+}
+
+func isUntyped(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsUntyped != 0
+}
+
+func isOrdered(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsOrdered != 0
+}
+
+func isConstType(typ Type) bool {
+ t, ok := typ.Underlying().(*Basic)
+ return ok && t.info&IsConstType != 0
+}
+
+// IsInterface reports whether typ is an interface type.
+func IsInterface(typ Type) bool {
+ _, ok := typ.Underlying().(*Interface)
+ return ok
+}
+
+// Comparable reports whether values of type T are comparable.
+func Comparable(T Type) bool {
+ switch t := T.Underlying().(type) {
+ case *Basic:
+ // assume invalid types to be comparable
+ // to avoid follow-up errors
+ return t.kind != UntypedNil
+ case *Pointer, *Interface, *Chan:
+ return true
+ case *Struct:
+ for _, f := range t.fields {
+ if !Comparable(f.typ) {
+ return false
+ }
+ }
+ return true
+ case *Array:
+ return Comparable(t.elem)
+ }
+ return false
+}
+
+// hasNil reports whether a type includes the nil value.
+func hasNil(typ Type) bool {
+ switch t := typ.Underlying().(type) {
+ case *Basic:
+ return t.kind == UnsafePointer
+ case *Slice, *Pointer, *Signature, *Interface, *Map, *Chan:
+ return true
+ }
+ return false
+}
+
+// Identical reports whether x and y are identical.
+func Identical(x, y Type) bool {
+ return identical(x, y, nil)
+}
+
+// An ifacePair is a node in a stack of interface type pairs compared for identity.
+type ifacePair struct {
+ x, y *Interface
+ prev *ifacePair
+}
+
+func (p *ifacePair) identical(q *ifacePair) bool {
+ return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x
+}
+
+func identical(x, y Type, p *ifacePair) bool {
+ if x == y {
+ return true
+ }
+
+ switch x := x.(type) {
+ case *Basic:
+ // Basic types are singletons except for the rune and byte
+ // aliases, thus we cannot solely rely on the x == y check
+ // above.
+ if y, ok := y.(*Basic); ok {
+ return x.kind == y.kind
+ }
+
+ case *Array:
+ // Two array types are identical if they have identical element types
+ // and the same array length.
+ if y, ok := y.(*Array); ok {
+ return x.len == y.len && identical(x.elem, y.elem, p)
+ }
+
+ case *Slice:
+ // Two slice types are identical if they have identical element types.
+ if y, ok := y.(*Slice); ok {
+ return identical(x.elem, y.elem, p)
+ }
+
+ case *Struct:
+ // Two struct types are identical if they have the same sequence of fields,
+ // and if corresponding fields have the same names, and identical types,
+ // and identical tags. Two anonymous fields are considered to have the same
+ // name. Lower-case field names from different packages are always different.
+ if y, ok := y.(*Struct); ok {
+ if x.NumFields() == y.NumFields() {
+ for i, f := range x.fields {
+ g := y.fields[i]
+ if f.anonymous != g.anonymous ||
+ x.Tag(i) != y.Tag(i) ||
+ !f.sameId(g.pkg, g.name) ||
+ !identical(f.typ, g.typ, p) {
+ return false
+ }
+ }
+ return true
+ }
+ }
+
+ case *Pointer:
+ // Two pointer types are identical if they have identical base types.
+ if y, ok := y.(*Pointer); ok {
+ return identical(x.base, y.base, p)
+ }
+
+ case *Tuple:
+ // Two tuples types are identical if they have the same number of elements
+ // and corresponding elements have identical types.
+ if y, ok := y.(*Tuple); ok {
+ if x.Len() == y.Len() {
+ if x != nil {
+ for i, v := range x.vars {
+ w := y.vars[i]
+ if !identical(v.typ, w.typ, p) {
+ return false
+ }
+ }
+ }
+ return true
+ }
+ }
+
+ case *Signature:
+ // Two function types are identical if they have the same number of parameters
+ // and result values, corresponding parameter and result types are identical,
+ // and either both functions are variadic or neither is. Parameter and result
+ // names are not required to match.
+ if y, ok := y.(*Signature); ok {
+ return x.variadic == y.variadic &&
+ identical(x.params, y.params, p) &&
+ identical(x.results, y.results, p)
+ }
+
+ case *Interface:
+ // Two interface types are identical if they have the same set of methods with
+ // the same names and identical function types. Lower-case method names from
+ // different packages are always different. The order of the methods is irrelevant.
+ if y, ok := y.(*Interface); ok {
+ a := x.allMethods
+ b := y.allMethods
+ if len(a) == len(b) {
+ // Interface types are the only types where cycles can occur
+ // that are not "terminated" via named types; and such cycles
+ // can only be created via method parameter types that are
+ // anonymous interfaces (directly or indirectly) embedding
+ // the current interface. Example:
+ //
+ // type T interface {
+ // m() interface{T}
+ // }
+ //
+ // If two such (differently named) interfaces are compared,
+ // endless recursion occurs if the cycle is not detected.
+ //
+ // If x and y were compared before, they must be equal
+ // (if they were not, the recursion would have stopped);
+ // search the ifacePair stack for the same pair.
+ //
+ // This is a quadratic algorithm, but in practice these stacks
+ // are extremely short (bounded by the nesting depth of interface
+ // type declarations that recur via parameter types, an extremely
+ // rare occurrence). An alternative implementation might use a
+ // "visited" map, but that is probably less efficient overall.
+ q := &ifacePair{x, y, p}
+ for p != nil {
+ if p.identical(q) {
+ return true // same pair was compared before
+ }
+ p = p.prev
+ }
+ if debug {
+ assert(sort.IsSorted(byUniqueMethodName(a)))
+ assert(sort.IsSorted(byUniqueMethodName(b)))
+ }
+ for i, f := range a {
+ g := b[i]
+ if f.Id() != g.Id() || !identical(f.typ, g.typ, q) {
+ return false
+ }
+ }
+ return true
+ }
+ }
+
+ case *Map:
+ // Two map types are identical if they have identical key and value types.
+ if y, ok := y.(*Map); ok {
+ return identical(x.key, y.key, p) && identical(x.elem, y.elem, p)
+ }
+
+ case *Chan:
+ // Two channel types are identical if they have identical value types
+ // and the same direction.
+ if y, ok := y.(*Chan); ok {
+ return x.dir == y.dir && identical(x.elem, y.elem, p)
+ }
+
+ case *Named:
+ // Two named types are identical if their type names originate
+ // in the same type declaration.
+ if y, ok := y.(*Named); ok {
+ return x.obj == y.obj
+ }
+
+ default:
+ unreachable()
+ }
+
+ return false
+}
+
+// defaultType returns the default "typed" type for an "untyped" type;
+// it returns the incoming type for all other types. The default type
+// for untyped nil is untyped nil.
+//
+func defaultType(typ Type) Type {
+ if t, ok := typ.(*Basic); ok {
+ switch t.kind {
+ case UntypedBool:
+ return Typ[Bool]
+ case UntypedInt:
+ return Typ[Int]
+ case UntypedRune:
+ return universeRune // use 'rune' name
+ case UntypedFloat:
+ return Typ[Float64]
+ case UntypedComplex:
+ return Typ[Complex128]
+ case UntypedString:
+ return Typ[String]
+ }
+ }
+ return typ
+}
diff --git a/libgo/go/go/types/resolver.go b/libgo/go/go/types/resolver.go
new file mode 100644
index 00000000000..c31ef423d9a
--- /dev/null
+++ b/libgo/go/go/types/resolver.go
@@ -0,0 +1,445 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types
+
+import (
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "go/token"
+ pathLib "path"
+ "strconv"
+ "strings"
+ "unicode"
+)
+
+// A declInfo describes a package-level const, type, var, or func declaration.
+type declInfo struct {
+ file *Scope // scope of file containing this declaration
+ lhs []*Var // lhs of n:1 variable declarations, or nil
+ typ ast.Expr // type, or nil
+ init ast.Expr // init expression, or nil
+ fdecl *ast.FuncDecl // func declaration, or nil
+
+ deps map[Object]bool // type and init dependencies; lazily allocated
+ mark int // for dependency analysis
+}
+
+// hasInitializer reports whether the declared object has an initialization
+// expression or function body.
+func (d *declInfo) hasInitializer() bool {
+ return d.init != nil || d.fdecl != nil && d.fdecl.Body != nil
+}
+
+// addDep adds obj as a dependency to d.
+func (d *declInfo) addDep(obj Object) {
+ m := d.deps
+ if m == nil {
+ m = make(map[Object]bool)
+ d.deps = m
+ }
+ m[obj] = true
+}
+
+// arityMatch checks that the lhs and rhs of a const or var decl
+// have the appropriate number of names and init exprs. For const
+// decls, init is the value spec providing the init exprs; for
+// var decls, init is nil (the init exprs are in s in this case).
+func (check *Checker) arityMatch(s, init *ast.ValueSpec) {
+ l := len(s.Names)
+ r := len(s.Values)
+ if init != nil {
+ r = len(init.Values)
+ }
+
+ switch {
+ case init == nil && r == 0:
+ // var decl w/o init expr
+ if s.Type == nil {
+ check.errorf(s.Pos(), "missing type or init expr")
+ }
+ case l < r:
+ if l < len(s.Values) {
+ // init exprs from s
+ n := s.Values[l]
+ check.errorf(n.Pos(), "extra init expr %s", n)
+ // TODO(gri) avoid declared but not used error here
+ } else {
+ // init exprs "inherited"
+ check.errorf(s.Pos(), "extra init expr at %s", init.Pos())
+ // TODO(gri) avoid declared but not used error here
+ }
+ case l > r && (init != nil || r != 1):
+ n := s.Names[r]
+ check.errorf(n.Pos(), "missing init expr for %s", n)
+ }
+}
+
+func validatedImportPath(path string) (string, error) {
+ s, err := strconv.Unquote(path)
+ if err != nil {
+ return "", err
+ }
+ if s == "" {
+ return "", fmt.Errorf("empty string")
+ }
+ const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
+ for _, r := range s {
+ if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
+ return s, fmt.Errorf("invalid character %#U", r)
+ }
+ }
+ return s, nil
+}
+
+// declarePkgObj declares obj in the package scope, records its ident -> obj mapping,
+// and updates check.objMap. The object must not be a function or method.
+func (check *Checker) declarePkgObj(ident *ast.Ident, obj Object, d *declInfo) {
+ assert(ident.Name == obj.Name())
+
+ // spec: "A package-scope or file-scope identifier with name init
+ // may only be declared to be a function with this (func()) signature."
+ if ident.Name == "init" {
+ check.errorf(ident.Pos(), "cannot declare init - must be func")
+ return
+ }
+
+ check.declare(check.pkg.scope, ident, obj, token.NoPos)
+ check.objMap[obj] = d
+ obj.setOrder(uint32(len(check.objMap)))
+}
+
+// filename returns a filename suitable for debugging output.
+func (check *Checker) filename(fileNo int) string {
+ file := check.files[fileNo]
+ if pos := file.Pos(); pos.IsValid() {
+ return check.fset.File(pos).Name()
+ }
+ return fmt.Sprintf("file[%d]", fileNo)
+}
+
+// collectObjects collects all file and package objects and inserts them
+// into their respective scopes. It also performs imports and associates
+// methods with receiver base type names.
+func (check *Checker) collectObjects() {
+ pkg := check.pkg
+
+ // pkgImports is the set of packages already imported by any package file seen
+ // so far. Used to avoid duplicate entries in pkg.imports. Allocate and populate
+ // it (pkg.imports may not be empty if we are checking test files incrementally).
+ var pkgImports = make(map[*Package]bool)
+ for _, imp := range pkg.imports {
+ pkgImports[imp] = true
+ }
+
+ for fileNo, file := range check.files {
+ // The package identifier denotes the current package,
+ // but there is no corresponding package object.
+ check.recordDef(file.Name, nil)
+
+ // Use the actual source file extent rather than *ast.File extent since the
+ // latter doesn't include comments which appear at the start or end of the file.
+ // Be conservative and use the *ast.File extent if we don't have a *token.File.
+ pos, end := file.Pos(), file.End()
+ if f := check.fset.File(file.Pos()); f != nil {
+ pos, end = token.Pos(f.Base()), token.Pos(f.Base()+f.Size())
+ }
+ fileScope := NewScope(check.pkg.scope, pos, end, check.filename(fileNo))
+ check.recordScope(file, fileScope)
+
+ for _, decl := range file.Decls {
+ switch d := decl.(type) {
+ case *ast.BadDecl:
+ // ignore
+
+ case *ast.GenDecl:
+ var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
+ for iota, spec := range d.Specs {
+ switch s := spec.(type) {
+ case *ast.ImportSpec:
+ // import package
+ var imp *Package
+ path, err := validatedImportPath(s.Path.Value)
+ if err != nil {
+ check.errorf(s.Path.Pos(), "invalid import path (%s)", err)
+ continue
+ }
+ if path == "C" && check.conf.FakeImportC {
+ // TODO(gri) shouldn't create a new one each time
+ imp = NewPackage("C", "C")
+ imp.fake = true
+ } else if path == "unsafe" {
+ // package "unsafe" is known to the language
+ imp = Unsafe
+ } else {
+ if importer := check.conf.Importer; importer != nil {
+ imp, err = importer.Import(path)
+ if imp == nil && err == nil {
+ err = fmt.Errorf("Config.Importer.Import(%s) returned nil but no error", path)
+ }
+ } else {
+ err = fmt.Errorf("Config.Importer not installed")
+ }
+ if err != nil {
+ check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
+ continue
+ }
+ }
+
+ // add package to list of explicit imports
+ // (this functionality is provided as a convenience
+ // for clients; it is not needed for type-checking)
+ if !pkgImports[imp] {
+ pkgImports[imp] = true
+ if imp != Unsafe {
+ pkg.imports = append(pkg.imports, imp)
+ }
+ }
+
+ // local name overrides imported package name
+ name := imp.name
+ if s.Name != nil {
+ name = s.Name.Name
+ if name == "init" {
+ check.errorf(s.Name.Pos(), "cannot declare init - must be func")
+ continue
+ }
+ }
+
+ obj := NewPkgName(s.Pos(), pkg, name, imp)
+ if s.Name != nil {
+ // in a dot-import, the dot represents the package
+ check.recordDef(s.Name, obj)
+ } else {
+ check.recordImplicit(s, obj)
+ }
+
+ // add import to file scope
+ if name == "." {
+ // merge imported scope with file scope
+ for _, obj := range imp.scope.elems {
+ // A package scope may contain non-exported objects,
+ // do not import them!
+ if obj.Exported() {
+ // TODO(gri) When we import a package, we create
+ // a new local package object. We should do the
+ // same for each dot-imported object. That way
+ // they can have correct position information.
+ // (We must not modify their existing position
+ // information because the same package - found
+ // via Config.Packages - may be dot-imported in
+ // another package!)
+ check.declare(fileScope, nil, obj, token.NoPos)
+ check.recordImplicit(s, obj)
+ }
+ }
+ // add position to set of dot-import positions for this file
+ // (this is only needed for "imported but not used" errors)
+ check.addUnusedDotImport(fileScope, imp, s.Pos())
+ } else {
+ // declare imported package object in file scope
+ check.declare(fileScope, nil, obj, token.NoPos)
+ }
+
+ case *ast.ValueSpec:
+ switch d.Tok {
+ case token.CONST:
+ // determine which initialization expressions to use
+ switch {
+ case s.Type != nil || len(s.Values) > 0:
+ last = s
+ case last == nil:
+ last = new(ast.ValueSpec) // make sure last exists
+ }
+
+ // declare all constants
+ for i, name := range s.Names {
+ obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(iota)))
+
+ var init ast.Expr
+ if i < len(last.Values) {
+ init = last.Values[i]
+ }
+
+ d := &declInfo{file: fileScope, typ: last.Type, init: init}
+ check.declarePkgObj(name, obj, d)
+ }
+
+ check.arityMatch(s, last)
+
+ case token.VAR:
+ lhs := make([]*Var, len(s.Names))
+ // If there's exactly one rhs initializer, use
+ // the same declInfo d1 for all lhs variables
+ // so that each lhs variable depends on the same
+ // rhs initializer (n:1 var declaration).
+ var d1 *declInfo
+ if len(s.Values) == 1 {
+ // The lhs elements are only set up after the for loop below,
+ // but that's ok because declareVar only collects the declInfo
+ // for a later phase.
+ d1 = &declInfo{file: fileScope, lhs: lhs, typ: s.Type, init: s.Values[0]}
+ }
+
+ // declare all variables
+ for i, name := range s.Names {
+ obj := NewVar(name.Pos(), pkg, name.Name, nil)
+ lhs[i] = obj
+
+ d := d1
+ if d == nil {
+ // individual assignments
+ var init ast.Expr
+ if i < len(s.Values) {
+ init = s.Values[i]
+ }
+ d = &declInfo{file: fileScope, typ: s.Type, init: init}
+ }
+
+ check.declarePkgObj(name, obj, d)
+ }
+
+ check.arityMatch(s, nil)
+
+ default:
+ check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
+ }
+
+ case *ast.TypeSpec:
+ obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
+ check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type})
+
+ default:
+ check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
+ }
+ }
+
+ case *ast.FuncDecl:
+ name := d.Name.Name
+ obj := NewFunc(d.Name.Pos(), pkg, name, nil)
+ if d.Recv == nil {
+ // regular function
+ if name == "init" {
+ // don't declare init functions in the package scope - they are invisible
+ obj.parent = pkg.scope
+ check.recordDef(d.Name, obj)
+ // init functions must have a body
+ if d.Body == nil {
+ check.softErrorf(obj.pos, "missing function body")
+ }
+ } else {
+ check.declare(pkg.scope, d.Name, obj, token.NoPos)
+ }
+ } else {
+ // method
+ check.recordDef(d.Name, obj)
+ // Associate method with receiver base type name, if possible.
+ // Ignore methods that have an invalid receiver, or a blank _
+ // receiver name. They will be type-checked later, with regular
+ // functions.
+ if list := d.Recv.List; len(list) > 0 {
+ typ := list[0].Type
+ if ptr, _ := typ.(*ast.StarExpr); ptr != nil {
+ typ = ptr.X
+ }
+ if base, _ := typ.(*ast.Ident); base != nil && base.Name != "_" {
+ check.assocMethod(base.Name, obj)
+ }
+ }
+ }
+ info := &declInfo{file: fileScope, fdecl: d}
+ check.objMap[obj] = info
+ obj.setOrder(uint32(len(check.objMap)))
+
+ default:
+ check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
+ }
+ }
+ }
+
+ // verify that objects in package and file scopes have different names
+ for _, scope := range check.pkg.scope.children /* file scopes */ {
+ for _, obj := range scope.elems {
+ if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
+ if pkg, ok := obj.(*PkgName); ok {
+ check.errorf(alt.Pos(), "%s already declared through import of %s", alt.Name(), pkg.Imported())
+ check.reportAltDecl(pkg)
+ } else {
+ check.errorf(alt.Pos(), "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())
+ // TODO(gri) dot-imported objects don't have a position; reportAltDecl won't print anything
+ check.reportAltDecl(obj)
+ }
+ }
+ }
+ }
+}
+
+// packageObjects typechecks all package objects in objList, but not function bodies.
+func (check *Checker) packageObjects(objList []Object) {
+ // add new methods to already type-checked types (from a prior Checker.Files call)
+ for _, obj := range objList {
+ if obj, _ := obj.(*TypeName); obj != nil && obj.typ != nil {
+ check.addMethodDecls(obj)
+ }
+ }
+
+ // pre-allocate space for type declaration paths so that the underlying array is reused
+ typePath := make([]*TypeName, 0, 8)
+
+ for _, obj := range objList {
+ check.objDecl(obj, nil, typePath)
+ }
+
+ // At this point we may have a non-empty check.methods map; this means that not all
+ // entries were deleted at the end of typeDecl because the respective receiver base
+ // types were not found. In that case, an error was reported when declaring those
+ // methods. We can now safely discard this map.
+ check.methods = nil
+}
+
+// functionBodies typechecks all function bodies.
+func (check *Checker) functionBodies() {
+ for _, f := range check.funcs {
+ check.funcBody(f.decl, f.name, f.sig, f.body)
+ }
+}
+
+// unusedImports checks for unused imports.
+func (check *Checker) unusedImports() {
+ // if function bodies are not checked, packages' uses are likely missing - don't check
+ if check.conf.IgnoreFuncBodies {
+ return
+ }
+
+ // spec: "It is illegal (...) to directly import a package without referring to
+ // any of its exported identifiers. To import a package solely for its side-effects
+ // (initialization), use the blank identifier as explicit package name."
+
+ // check use of regular imported packages
+ for _, scope := range check.pkg.scope.children /* file scopes */ {
+ for _, obj := range scope.elems {
+ if obj, ok := obj.(*PkgName); ok {
+ // Unused "blank imports" are automatically ignored
+ // since _ identifiers are not entered into scopes.
+ if !obj.used {
+ path := obj.imported.path
+ base := pathLib.Base(path)
+ if obj.name == base {
+ check.softErrorf(obj.pos, "%q imported but not used", path)
+ } else {
+ check.softErrorf(obj.pos, "%q imported but not used as %s", path, obj.name)
+ }
+ }
+ }
+ }
+ }
+
+ // check use of dot-imported packages
+ for _, unusedDotImports := range check.unusedDotImports {
+ for pkg, pos := range unusedDotImports {
+ check.softErrorf(pos, "%q imported but not used", pkg.path)
+ }
+ }
+}
diff --git a/libgo/go/go/types/resolver_test.go b/libgo/go/go/types/resolver_test.go
new file mode 100644
index 00000000000..34deae268e3
--- /dev/null
+++ b/libgo/go/go/types/resolver_test.go
@@ -0,0 +1,209 @@
+// 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_test
+
+import (
+ "fmt"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "internal/testenv"
+ "sort"
+ "testing"
+
+ . "go/types"
+)
+
+type resolveTestImporter struct {
+ importer Importer
+ imported map[string]bool
+}
+
+func (imp *resolveTestImporter) Import(path string) (*Package, error) {
+ if imp.importer == nil {
+ imp.importer = importer.Default()
+ imp.imported = make(map[string]bool)
+ }
+ pkg, err := imp.importer.Import(path)
+ if err != nil {
+ return nil, err
+ }
+ imp.imported[path] = true
+ return pkg, nil
+}
+
+func TestResolveIdents(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ sources := []string{
+ `
+ package p
+ import "fmt"
+ import "math"
+ const pi = math.Pi
+ func sin(x float64) float64 {
+ return math.Sin(x)
+ }
+ var Println = fmt.Println
+ `,
+ `
+ package p
+ import "fmt"
+ type errorStringer struct { fmt.Stringer; error }
+ func f() string {
+ _ = "foo"
+ return fmt.Sprintf("%d", g())
+ }
+ func g() (x int) { return }
+ `,
+ `
+ package p
+ import . "go/parser"
+ import "sync"
+ func h() Mode { return ImportsOnly }
+ var _, x int = 1, 2
+ func init() {}
+ type T struct{ *sync.Mutex; a, b, c int}
+ type I interface{ m() }
+ var _ = T{a: 1, b: 2, c: 3}
+ func (_ T) m() {}
+ func (T) _() {}
+ var i I
+ var _ = i.m
+ func _(s []int) { for i, x := range s { _, _ = i, x } }
+ func _(x interface{}) {
+ switch x := x.(type) {
+ case int:
+ _ = x
+ }
+ switch {} // implicit 'true' tag
+ }
+ `,
+ `
+ package p
+ type S struct{}
+ func (T) _() {}
+ func (T) _() {}
+ `,
+ `
+ package p
+ func _() {
+ L0:
+ L1:
+ goto L0
+ for {
+ goto L1
+ }
+ if true {
+ goto L2
+ }
+ L2:
+ }
+ `,
+ }
+
+ pkgnames := []string{
+ "fmt",
+ "math",
+ }
+
+ // parse package files
+ fset := token.NewFileSet()
+ var files []*ast.File
+ for i, src := range sources {
+ f, err := parser.ParseFile(fset, fmt.Sprintf("sources[%d]", i), src, parser.DeclarationErrors)
+ if err != nil {
+ t.Fatal(err)
+ }
+ files = append(files, f)
+ }
+
+ // resolve and type-check package AST
+ importer := new(resolveTestImporter)
+ conf := Config{Importer: importer}
+ uses := make(map[*ast.Ident]Object)
+ defs := make(map[*ast.Ident]Object)
+ _, err := conf.Check("testResolveIdents", fset, files, &Info{Defs: defs, Uses: uses})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // check that all packages were imported
+ for _, name := range pkgnames {
+ if !importer.imported[name] {
+ t.Errorf("package %s not imported", name)
+ }
+ }
+
+ // check that qualified identifiers are resolved
+ for _, f := range files {
+ ast.Inspect(f, func(n ast.Node) bool {
+ if s, ok := n.(*ast.SelectorExpr); ok {
+ if x, ok := s.X.(*ast.Ident); ok {
+ obj := uses[x]
+ if obj == nil {
+ t.Errorf("%s: unresolved qualified identifier %s", fset.Position(x.Pos()), x.Name)
+ return false
+ }
+ if _, ok := obj.(*PkgName); ok && uses[s.Sel] == nil {
+ t.Errorf("%s: unresolved selector %s", fset.Position(s.Sel.Pos()), s.Sel.Name)
+ return false
+ }
+ return false
+ }
+ return false
+ }
+ return true
+ })
+ }
+
+ for id, obj := range uses {
+ if obj == nil {
+ t.Errorf("%s: Uses[%s] == nil", fset.Position(id.Pos()), id.Name)
+ }
+ }
+
+ // check that each identifier in the source is found in uses or defs or both
+ var both []string
+ for _, f := range files {
+ ast.Inspect(f, func(n ast.Node) bool {
+ if x, ok := n.(*ast.Ident); ok {
+ var objects int
+ if _, found := uses[x]; found {
+ objects |= 1
+ delete(uses, x)
+ }
+ if _, found := defs[x]; found {
+ objects |= 2
+ delete(defs, x)
+ }
+ if objects == 0 {
+ t.Errorf("%s: unresolved identifier %s", fset.Position(x.Pos()), x.Name)
+ } else if objects == 3 {
+ both = append(both, x.Name)
+ }
+ return false
+ }
+ return true
+ })
+ }
+
+ // check the expected set of idents that are simultaneously uses and defs
+ sort.Strings(both)
+ if got, want := fmt.Sprint(both), "[Mutex Stringer error]"; got != want {
+ t.Errorf("simultaneous uses/defs = %s, want %s", got, want)
+ }
+
+ // any left-over identifiers didn't exist in the source
+ for x := range uses {
+ t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name)
+ }
+ for x := range defs {
+ t.Errorf("%s: identifier %s not present in source", fset.Position(x.Pos()), x.Name)
+ }
+
+ // TODO(gri) add tests to check ImplicitObj callbacks
+}
diff --git a/libgo/go/go/types/return.go b/libgo/go/go/types/return.go
new file mode 100644
index 00000000000..66289852147
--- /dev/null
+++ b/libgo/go/go/types/return.go
@@ -0,0 +1,185 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements isTerminating.
+
+package types
+
+import (
+ "go/ast"
+ "go/token"
+)
+
+// isTerminating reports if s is a terminating statement.
+// If s is labeled, label is the label name; otherwise s
+// is "".
+func (check *Checker) isTerminating(s ast.Stmt, label string) bool {
+ switch s := s.(type) {
+ default:
+ unreachable()
+
+ case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.SendStmt,
+ *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt, *ast.DeferStmt,
+ *ast.RangeStmt:
+ // no chance
+
+ case *ast.LabeledStmt:
+ return check.isTerminating(s.Stmt, s.Label.Name)
+
+ case *ast.ExprStmt:
+ // the predeclared (possibly parenthesized) panic() function is terminating
+ if call, _ := unparen(s.X).(*ast.CallExpr); call != nil {
+ if id, _ := call.Fun.(*ast.Ident); id != nil {
+ if _, obj := check.scope.LookupParent(id.Name, token.NoPos); obj != nil {
+ if b, _ := obj.(*Builtin); b != nil && b.id == _Panic {
+ return true
+ }
+ }
+ }
+ }
+
+ case *ast.ReturnStmt:
+ return true
+
+ case *ast.BranchStmt:
+ if s.Tok == token.GOTO || s.Tok == token.FALLTHROUGH {
+ return true
+ }
+
+ case *ast.BlockStmt:
+ return check.isTerminatingList(s.List, "")
+
+ case *ast.IfStmt:
+ if s.Else != nil &&
+ check.isTerminating(s.Body, "") &&
+ check.isTerminating(s.Else, "") {
+ return true
+ }
+
+ case *ast.SwitchStmt:
+ return check.isTerminatingSwitch(s.Body, label)
+
+ case *ast.TypeSwitchStmt:
+ return check.isTerminatingSwitch(s.Body, label)
+
+ case *ast.SelectStmt:
+ for _, s := range s.Body.List {
+ cc := s.(*ast.CommClause)
+ if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) {
+ return false
+ }
+
+ }
+ return true
+
+ case *ast.ForStmt:
+ if s.Cond == nil && !hasBreak(s.Body, label, true) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func (check *Checker) isTerminatingList(list []ast.Stmt, label string) bool {
+ n := len(list)
+ return n > 0 && check.isTerminating(list[n-1], label)
+}
+
+func (check *Checker) isTerminatingSwitch(body *ast.BlockStmt, label string) bool {
+ hasDefault := false
+ for _, s := range body.List {
+ cc := s.(*ast.CaseClause)
+ if cc.List == nil {
+ hasDefault = true
+ }
+ if !check.isTerminatingList(cc.Body, "") || hasBreakList(cc.Body, label, true) {
+ return false
+ }
+ }
+ return hasDefault
+}
+
+// TODO(gri) For nested breakable statements, the current implementation of hasBreak
+// will traverse the same subtree repeatedly, once for each label. Replace
+// with a single-pass label/break matching phase.
+
+// hasBreak reports if s is or contains a break statement
+// referring to the label-ed statement or implicit-ly the
+// closest outer breakable statement.
+func hasBreak(s ast.Stmt, label string, implicit bool) bool {
+ switch s := s.(type) {
+ default:
+ unreachable()
+
+ case *ast.BadStmt, *ast.DeclStmt, *ast.EmptyStmt, *ast.ExprStmt,
+ *ast.SendStmt, *ast.IncDecStmt, *ast.AssignStmt, *ast.GoStmt,
+ *ast.DeferStmt, *ast.ReturnStmt:
+ // no chance
+
+ case *ast.LabeledStmt:
+ return hasBreak(s.Stmt, label, implicit)
+
+ case *ast.BranchStmt:
+ if s.Tok == token.BREAK {
+ if s.Label == nil {
+ return implicit
+ }
+ if s.Label.Name == label {
+ return true
+ }
+ }
+
+ case *ast.BlockStmt:
+ return hasBreakList(s.List, label, implicit)
+
+ case *ast.IfStmt:
+ if hasBreak(s.Body, label, implicit) ||
+ s.Else != nil && hasBreak(s.Else, label, implicit) {
+ return true
+ }
+
+ case *ast.CaseClause:
+ return hasBreakList(s.Body, label, implicit)
+
+ case *ast.SwitchStmt:
+ if label != "" && hasBreak(s.Body, label, false) {
+ return true
+ }
+
+ case *ast.TypeSwitchStmt:
+ if label != "" && hasBreak(s.Body, label, false) {
+ return true
+ }
+
+ case *ast.CommClause:
+ return hasBreakList(s.Body, label, implicit)
+
+ case *ast.SelectStmt:
+ if label != "" && hasBreak(s.Body, label, false) {
+ return true
+ }
+
+ case *ast.ForStmt:
+ if label != "" && hasBreak(s.Body, label, false) {
+ return true
+ }
+
+ case *ast.RangeStmt:
+ if label != "" && hasBreak(s.Body, label, false) {
+ return true
+ }
+ }
+
+ return false
+}
+
+func hasBreakList(list []ast.Stmt, label string, implicit bool) bool {
+ for _, s := range list {
+ if hasBreak(s, label, implicit) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/libgo/go/go/types/scope.go b/libgo/go/go/types/scope.go
new file mode 100644
index 00000000000..3502840225f
--- /dev/null
+++ b/libgo/go/go/types/scope.go
@@ -0,0 +1,190 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements Scopes.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+ "go/token"
+ "io"
+ "sort"
+ "strings"
+)
+
+// TODO(gri) Provide scopes with a name or other mechanism so that
+// objects can use that information for better printing.
+
+// A Scope maintains a set of objects and links to its containing
+// (parent) and contained (children) scopes. Objects may be inserted
+// and looked up by name. The zero value for Scope is a ready-to-use
+// empty scope.
+type Scope struct {
+ parent *Scope
+ children []*Scope
+ elems map[string]Object // lazily allocated
+ pos, end token.Pos // scope extent; may be invalid
+ comment string // for debugging only
+}
+
+// NewScope returns a new, empty scope contained in the given parent
+// scope, if any. The comment is for debugging only.
+func NewScope(parent *Scope, pos, end token.Pos, comment string) *Scope {
+ s := &Scope{parent, nil, nil, pos, end, comment}
+ // don't add children to Universe scope!
+ if parent != nil && parent != Universe {
+ parent.children = append(parent.children, s)
+ }
+ return s
+}
+
+// Parent returns the scope's containing (parent) scope.
+func (s *Scope) Parent() *Scope { return s.parent }
+
+// Len() returns the number of scope elements.
+func (s *Scope) Len() int { return len(s.elems) }
+
+// Names returns the scope's element names in sorted order.
+func (s *Scope) Names() []string {
+ names := make([]string, len(s.elems))
+ i := 0
+ for name := range s.elems {
+ names[i] = name
+ i++
+ }
+ sort.Strings(names)
+ return names
+}
+
+// NumChildren() returns the number of scopes nested in s.
+func (s *Scope) NumChildren() int { return len(s.children) }
+
+// Child returns the i'th child scope for 0 <= i < NumChildren().
+func (s *Scope) Child(i int) *Scope { return s.children[i] }
+
+// Lookup returns the object in scope s with the given name if such an
+// object exists; otherwise the result is nil.
+func (s *Scope) Lookup(name string) Object {
+ return s.elems[name]
+}
+
+// LookupParent follows the parent chain of scopes starting with s until
+// it finds a scope where Lookup(name) returns a non-nil object, and then
+// returns that scope and object. If a valid position pos is provided,
+// only objects that were declared at or before pos are considered.
+// If no such scope and object exists, the result is (nil, nil).
+//
+// Note that obj.Parent() may be different from the returned scope if the
+// object was inserted into the scope and already had a parent at that
+// time (see Insert, below). This can only happen for dot-imported objects
+// whose scope is the scope of the package that exported them.
+func (s *Scope) LookupParent(name string, pos token.Pos) (*Scope, Object) {
+ for ; s != nil; s = s.parent {
+ if obj := s.elems[name]; obj != nil && (!pos.IsValid() || obj.scopePos() <= pos) {
+ return s, obj
+ }
+ }
+ return nil, nil
+}
+
+// Insert attempts to insert an object obj into scope s.
+// If s already contains an alternative object alt with
+// the same name, Insert leaves s unchanged and returns alt.
+// Otherwise it inserts obj, sets the object's parent scope
+// if not already set, and returns nil.
+func (s *Scope) Insert(obj Object) Object {
+ name := obj.Name()
+ if alt := s.elems[name]; alt != nil {
+ return alt
+ }
+ if s.elems == nil {
+ s.elems = make(map[string]Object)
+ }
+ s.elems[name] = obj
+ if obj.Parent() == nil {
+ obj.setParent(s)
+ }
+ return nil
+}
+
+// Pos and End describe the scope's source code extent [pos, end).
+// The results are guaranteed to be valid only if the type-checked
+// AST has complete position information. The extent is undefined
+// for Universe and package scopes.
+func (s *Scope) Pos() token.Pos { return s.pos }
+func (s *Scope) End() token.Pos { return s.end }
+
+// Contains returns true if pos is within the scope's extent.
+// The result is guaranteed to be valid only if the type-checked
+// AST has complete position information.
+func (s *Scope) Contains(pos token.Pos) bool {
+ return s.pos <= pos && pos < s.end
+}
+
+// Innermost returns the innermost (child) scope containing
+// pos. If pos is not within any scope, the result is nil.
+// The result is also nil for the Universe scope.
+// The result is guaranteed to be valid only if the type-checked
+// AST has complete position information.
+func (s *Scope) Innermost(pos token.Pos) *Scope {
+ // Package scopes do not have extents since they may be
+ // discontiguous, so iterate over the package's files.
+ if s.parent == Universe {
+ for _, s := range s.children {
+ if inner := s.Innermost(pos); inner != nil {
+ return inner
+ }
+ }
+ }
+
+ if s.Contains(pos) {
+ for _, s := range s.children {
+ if s.Contains(pos) {
+ return s.Innermost(pos)
+ }
+ }
+ return s
+ }
+ return nil
+}
+
+// WriteTo writes a string representation of the scope to w,
+// with the scope elements sorted by name.
+// The level of indentation is controlled by n >= 0, with
+// n == 0 for no indentation.
+// If recurse is set, it also writes nested (children) scopes.
+func (s *Scope) WriteTo(w io.Writer, n int, recurse bool) {
+ const ind = ". "
+ indn := strings.Repeat(ind, n)
+
+ fmt.Fprintf(w, "%s%s scope %p {", indn, s.comment, s)
+ if len(s.elems) == 0 {
+ fmt.Fprintf(w, "}\n")
+ return
+ }
+
+ fmt.Fprintln(w)
+ indn1 := indn + ind
+ for _, name := range s.Names() {
+ fmt.Fprintf(w, "%s%s\n", indn1, s.elems[name])
+ }
+
+ if recurse {
+ for _, s := range s.children {
+ fmt.Fprintln(w)
+ s.WriteTo(w, n+1, recurse)
+ }
+ }
+
+ fmt.Fprintf(w, "%s}", indn)
+}
+
+// String returns a string representation of the scope, for debugging.
+func (s *Scope) String() string {
+ var buf bytes.Buffer
+ s.WriteTo(&buf, 0, false)
+ return buf.String()
+}
diff --git a/libgo/go/go/types/selection.go b/libgo/go/go/types/selection.go
new file mode 100644
index 00000000000..124e0d39f02
--- /dev/null
+++ b/libgo/go/go/types/selection.go
@@ -0,0 +1,143 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements Selections.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+)
+
+// SelectionKind describes the kind of a selector expression x.f
+// (excluding qualified identifiers).
+type SelectionKind int
+
+const (
+ FieldVal SelectionKind = iota // x.f is a struct field selector
+ MethodVal // x.f is a method selector
+ MethodExpr // x.f is a method expression
+)
+
+// A Selection describes a selector expression x.f.
+// For the declarations:
+//
+// type T struct{ x int; E }
+// type E struct{}
+// func (e E) m() {}
+// var p *T
+//
+// the following relations exist:
+//
+// Selector Kind Recv Obj Type Index Indirect
+//
+// p.x FieldVal T x int {0} true
+// p.m MethodVal *T m func (e *T) m() {1, 0} true
+// T.m MethodExpr T m func m(_ T) {1, 0} false
+//
+type Selection struct {
+ kind SelectionKind
+ recv Type // type of x
+ obj Object // object denoted by x.f
+ index []int // path from x to x.f
+ indirect bool // set if there was any pointer indirection on the path
+}
+
+// Kind returns the selection kind.
+func (s *Selection) Kind() SelectionKind { return s.kind }
+
+// Recv returns the type of x in x.f.
+func (s *Selection) Recv() Type { return s.recv }
+
+// Obj returns the object denoted by x.f; a *Var for
+// a field selection, and a *Func in all other cases.
+func (s *Selection) Obj() Object { return s.obj }
+
+// Type returns the type of x.f, which may be different from the type of f.
+// See Selection for more information.
+func (s *Selection) Type() Type {
+ switch s.kind {
+ case MethodVal:
+ // The type of x.f is a method with its receiver type set
+ // to the type of x.
+ sig := *s.obj.(*Func).typ.(*Signature)
+ recv := *sig.recv
+ recv.typ = s.recv
+ sig.recv = &recv
+ return &sig
+
+ case MethodExpr:
+ // The type of x.f is a function (without receiver)
+ // and an additional first argument with the same type as x.
+ // TODO(gri) Similar code is already in call.go - factor!
+ // TODO(gri) Compute this eagerly to avoid allocations.
+ sig := *s.obj.(*Func).typ.(*Signature)
+ arg0 := *sig.recv
+ sig.recv = nil
+ arg0.typ = s.recv
+ var params []*Var
+ if sig.params != nil {
+ params = sig.params.vars
+ }
+ sig.params = NewTuple(append([]*Var{&arg0}, params...)...)
+ return &sig
+ }
+
+ // In all other cases, the type of x.f is the type of x.
+ return s.obj.Type()
+}
+
+// Index describes the path from x to f in x.f.
+// The last index entry is the field or method index of the type declaring f;
+// either:
+//
+// 1) the list of declared methods of a named type; or
+// 2) the list of methods of an interface type; or
+// 3) the list of fields of a struct type.
+//
+// The earlier index entries are the indices of the embedded fields implicitly
+// traversed to get from (the type of) x to f, starting at embedding depth 0.
+func (s *Selection) Index() []int { return s.index }
+
+// Indirect reports whether any pointer indirection was required to get from
+// x to f in x.f.
+func (s *Selection) Indirect() bool { return s.indirect }
+
+func (s *Selection) String() string { return SelectionString(s, nil) }
+
+// SelectionString returns the string form of s.
+// The Qualifier controls the printing of
+// package-level objects, and may be nil.
+//
+// Examples:
+// "field (T) f int"
+// "method (T) f(X) Y"
+// "method expr (T) f(X) Y"
+//
+func SelectionString(s *Selection, qf Qualifier) string {
+ var k string
+ switch s.kind {
+ case FieldVal:
+ k = "field "
+ case MethodVal:
+ k = "method "
+ case MethodExpr:
+ k = "method expr "
+ default:
+ unreachable()
+ }
+ var buf bytes.Buffer
+ buf.WriteString(k)
+ buf.WriteByte('(')
+ WriteType(&buf, s.Recv(), qf)
+ fmt.Fprintf(&buf, ") %s", s.obj.Name())
+ if T := s.Type(); s.kind == FieldVal {
+ buf.WriteByte(' ')
+ WriteType(&buf, T, qf)
+ } else {
+ WriteSignature(&buf, T.(*Signature), qf)
+ }
+ return buf.String()
+}
diff --git a/libgo/go/go/types/self_test.go b/libgo/go/go/types/self_test.go
new file mode 100644
index 00000000000..10ad06fbca1
--- /dev/null
+++ b/libgo/go/go/types/self_test.go
@@ -0,0 +1,102 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package types_test
+
+import (
+ "flag"
+ "fmt"
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "path/filepath"
+ "testing"
+ "time"
+
+ . "go/types"
+)
+
+var benchmark = flag.Bool("b", false, "run benchmarks")
+
+func TestSelf(t *testing.T) {
+ fset := token.NewFileSet()
+ files, err := pkgFiles(fset, ".")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ conf := Config{Importer: importer.Default()}
+ _, err = conf.Check("go/types", fset, files, nil)
+ if err != nil {
+ // Importing go/constant doesn't work in the
+ // build dashboard environment. Don't report an error
+ // for now so that the build remains green.
+ // TODO(gri) fix this
+ t.Log(err) // replace w/ t.Fatal eventually
+ return
+ }
+}
+
+func TestBenchmark(t *testing.T) {
+ if !*benchmark {
+ return
+ }
+
+ // We're not using testing's benchmarking mechanism directly
+ // because we want custom output.
+
+ for _, p := range []string{"types", "constant", filepath.Join("internal", "gcimporter")} {
+ path := filepath.Join("..", p)
+ runbench(t, path, false)
+ runbench(t, path, true)
+ fmt.Println()
+ }
+}
+
+func runbench(t *testing.T, path string, ignoreFuncBodies bool) {
+ fset := token.NewFileSet()
+ files, err := pkgFiles(fset, path)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ b := testing.Benchmark(func(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ conf := Config{IgnoreFuncBodies: ignoreFuncBodies}
+ conf.Check(path, fset, files, nil)
+ }
+ })
+
+ // determine line count
+ lines := 0
+ fset.Iterate(func(f *token.File) bool {
+ lines += f.LineCount()
+ return true
+ })
+
+ d := time.Duration(b.NsPerOp())
+ fmt.Printf(
+ "%s: %s for %d lines (%d lines/s), ignoreFuncBodies = %v\n",
+ filepath.Base(path), d, lines, int64(float64(lines)/d.Seconds()), ignoreFuncBodies,
+ )
+}
+
+func pkgFiles(fset *token.FileSet, path string) ([]*ast.File, error) {
+ filenames, err := pkgFilenames(path) // from stdlib_test.go
+ if err != nil {
+ return nil, err
+ }
+
+ var files []*ast.File
+ for _, filename := range filenames {
+ file, err := parser.ParseFile(fset, filename, nil, 0)
+ if err != nil {
+ return nil, err
+ }
+ files = append(files, file)
+ }
+
+ return files, nil
+}
diff --git a/libgo/go/go/types/sizes.go b/libgo/go/go/types/sizes.go
new file mode 100644
index 00000000000..56fb310c294
--- /dev/null
+++ b/libgo/go/go/types/sizes.go
@@ -0,0 +1,211 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements Sizes.
+
+package types
+
+// Sizes defines the sizing functions for package unsafe.
+type Sizes interface {
+ // Alignof returns the alignment of a variable of type T.
+ // Alignof must implement the alignment guarantees required by the spec.
+ Alignof(T Type) int64
+
+ // Offsetsof returns the offsets of the given struct fields, in bytes.
+ // Offsetsof must implement the offset guarantees required by the spec.
+ Offsetsof(fields []*Var) []int64
+
+ // Sizeof returns the size of a variable of type T.
+ // Sizeof must implement the size guarantees required by the spec.
+ Sizeof(T Type) int64
+}
+
+// StdSizes is a convenience type for creating commonly used Sizes.
+// It makes the following simplifying assumptions:
+//
+// - The size of explicitly sized basic types (int16, etc.) is the
+// specified size.
+// - The size of strings and interfaces is 2*WordSize.
+// - The size of slices is 3*WordSize.
+// - The size of an array of n elements corresponds to the size of
+// a struct of n consecutive fields of the array's element type.
+// - The size of a struct is the offset of the last field plus that
+// field's size. As with all element types, if the struct is used
+// in an array its size must first be aligned to a multiple of the
+// struct's alignment.
+// - All other types have size WordSize.
+// - Arrays and structs are aligned per spec definition; all other
+// types are naturally aligned with a maximum alignment MaxAlign.
+//
+// *StdSizes implements Sizes.
+//
+type StdSizes struct {
+ WordSize int64 // word size in bytes - must be >= 4 (32bits)
+ MaxAlign int64 // maximum alignment in bytes - must be >= 1
+}
+
+func (s *StdSizes) Alignof(T Type) int64 {
+ // For arrays and structs, alignment is defined in terms
+ // of alignment of the elements and fields, respectively.
+ switch t := T.Underlying().(type) {
+ case *Array:
+ // spec: "For a variable x of array type: unsafe.Alignof(x)
+ // is the same as unsafe.Alignof(x[0]), but at least 1."
+ return s.Alignof(t.elem)
+ case *Struct:
+ // spec: "For a variable x of struct type: unsafe.Alignof(x)
+ // is the largest of the values unsafe.Alignof(x.f) for each
+ // field f of x, but at least 1."
+ max := int64(1)
+ for _, f := range t.fields {
+ if a := s.Alignof(f.typ); a > max {
+ max = a
+ }
+ }
+ return max
+ }
+ a := s.Sizeof(T) // may be 0
+ // spec: "For a variable x of any type: unsafe.Alignof(x) is at least 1."
+ if a < 1 {
+ return 1
+ }
+ if a > s.MaxAlign {
+ return s.MaxAlign
+ }
+ return a
+}
+
+func (s *StdSizes) Offsetsof(fields []*Var) []int64 {
+ offsets := make([]int64, len(fields))
+ var o int64
+ for i, f := range fields {
+ a := s.Alignof(f.typ)
+ o = align(o, a)
+ offsets[i] = o
+ o += s.Sizeof(f.typ)
+ }
+ return offsets
+}
+
+var basicSizes = [...]byte{
+ Bool: 1,
+ Int8: 1,
+ Int16: 2,
+ Int32: 4,
+ Int64: 8,
+ Uint8: 1,
+ Uint16: 2,
+ Uint32: 4,
+ Uint64: 8,
+ Float32: 4,
+ Float64: 8,
+ Complex64: 8,
+ Complex128: 16,
+}
+
+func (s *StdSizes) Sizeof(T Type) int64 {
+ switch t := T.Underlying().(type) {
+ case *Basic:
+ assert(isTyped(T))
+ k := t.kind
+ if int(k) < len(basicSizes) {
+ if s := basicSizes[k]; s > 0 {
+ return int64(s)
+ }
+ }
+ if k == String {
+ return s.WordSize * 2
+ }
+ case *Array:
+ n := t.len
+ if n == 0 {
+ return 0
+ }
+ a := s.Alignof(t.elem)
+ z := s.Sizeof(t.elem)
+ return align(z, a)*(n-1) + z
+ case *Slice:
+ return s.WordSize * 3
+ case *Struct:
+ n := t.NumFields()
+ if n == 0 {
+ return 0
+ }
+ offsets := t.offsets
+ if t.offsets == nil {
+ // compute offsets on demand
+ offsets = s.Offsetsof(t.fields)
+ t.offsets = offsets
+ }
+ return offsets[n-1] + s.Sizeof(t.fields[n-1].typ)
+ case *Interface:
+ return s.WordSize * 2
+ }
+ return s.WordSize // catch-all
+}
+
+// stdSizes is used if Config.Sizes == nil.
+var stdSizes = StdSizes{8, 8}
+
+func (conf *Config) alignof(T Type) int64 {
+ if s := conf.Sizes; s != nil {
+ if a := s.Alignof(T); a >= 1 {
+ return a
+ }
+ panic("Config.Sizes.Alignof returned an alignment < 1")
+ }
+ return stdSizes.Alignof(T)
+}
+
+func (conf *Config) offsetsof(T *Struct) []int64 {
+ offsets := T.offsets
+ if offsets == nil && T.NumFields() > 0 {
+ // compute offsets on demand
+ if s := conf.Sizes; s != nil {
+ offsets = s.Offsetsof(T.fields)
+ // sanity checks
+ if len(offsets) != T.NumFields() {
+ panic("Config.Sizes.Offsetsof returned the wrong number of offsets")
+ }
+ for _, o := range offsets {
+ if o < 0 {
+ panic("Config.Sizes.Offsetsof returned an offset < 0")
+ }
+ }
+ } else {
+ offsets = stdSizes.Offsetsof(T.fields)
+ }
+ T.offsets = offsets
+ }
+ return offsets
+}
+
+// offsetof returns the offset of the field specified via
+// the index sequence relative to typ. All embedded fields
+// must be structs (rather than pointer to structs).
+func (conf *Config) offsetof(typ Type, index []int) int64 {
+ var o int64
+ for _, i := range index {
+ s := typ.Underlying().(*Struct)
+ o += conf.offsetsof(s)[i]
+ typ = s.fields[i].typ
+ }
+ return o
+}
+
+func (conf *Config) sizeof(T Type) int64 {
+ if s := conf.Sizes; s != nil {
+ if z := s.Sizeof(T); z >= 0 {
+ return z
+ }
+ panic("Config.Sizes.Sizeof returned a size < 0")
+ }
+ return stdSizes.Sizeof(T)
+}
+
+// align returns the smallest y >= x such that y % a == 0.
+func align(x, a int64) int64 {
+ y := x + a - 1
+ return y - y%a
+}
diff --git a/libgo/go/go/types/stdlib_test.go b/libgo/go/go/types/stdlib_test.go
new file mode 100644
index 00000000000..c6c946e976a
--- /dev/null
+++ b/libgo/go/go/types/stdlib_test.go
@@ -0,0 +1,279 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file tests types.Check by using it to
+// typecheck the standard library and tests.
+
+package types_test
+
+import (
+ "fmt"
+ "go/ast"
+ "go/build"
+ "go/importer"
+ "go/parser"
+ "go/scanner"
+ "go/token"
+ "internal/testenv"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "runtime"
+ "strings"
+ "testing"
+ "time"
+
+ . "go/types"
+)
+
+var (
+ pkgCount int // number of packages processed
+ start time.Time
+
+ // Use the same importer for all std lib tests to
+ // avoid repeated importing of the same packages.
+ stdLibImporter = importer.Default()
+)
+
+func TestStdlib(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ start = time.Now()
+ walkDirs(t, filepath.Join(runtime.GOROOT(), "src"))
+ if testing.Verbose() {
+ fmt.Println(pkgCount, "packages typechecked in", time.Since(start))
+ }
+}
+
+// firstComment returns the contents of the first comment in
+// the given file, assuming there's one within the first KB.
+func firstComment(filename string) string {
+ f, err := os.Open(filename)
+ if err != nil {
+ return ""
+ }
+ defer f.Close()
+
+ var src [1 << 10]byte // read at most 1KB
+ n, _ := f.Read(src[:])
+
+ var s scanner.Scanner
+ s.Init(fset.AddFile("", fset.Base(), n), src[:n], nil, scanner.ScanComments)
+ for {
+ _, tok, lit := s.Scan()
+ switch tok {
+ case token.COMMENT:
+ // remove trailing */ of multi-line comment
+ if lit[1] == '*' {
+ lit = lit[:len(lit)-2]
+ }
+ return strings.TrimSpace(lit[2:])
+ case token.EOF:
+ return ""
+ }
+ }
+}
+
+func testTestDir(t *testing.T, path string, ignore ...string) {
+ files, err := ioutil.ReadDir(path)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ excluded := make(map[string]bool)
+ for _, filename := range ignore {
+ excluded[filename] = true
+ }
+
+ fset := token.NewFileSet()
+ for _, f := range files {
+ // filter directory contents
+ if f.IsDir() || !strings.HasSuffix(f.Name(), ".go") || excluded[f.Name()] {
+ continue
+ }
+
+ // get per-file instructions
+ expectErrors := false
+ filename := filepath.Join(path, f.Name())
+ if cmd := firstComment(filename); cmd != "" {
+ switch cmd {
+ case "skip", "compiledir":
+ continue // ignore this file
+ case "errorcheck":
+ expectErrors = true
+ }
+ }
+
+ // parse and type-check file
+ file, err := parser.ParseFile(fset, filename, nil, 0)
+ if err == nil {
+ conf := Config{Importer: stdLibImporter}
+ _, err = conf.Check(filename, fset, []*ast.File{file}, nil)
+ }
+
+ if expectErrors {
+ if err == nil {
+ t.Errorf("expected errors but found none in %s", filename)
+ }
+ } else {
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ }
+}
+
+func TestStdTest(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ // test/recover4.go is only built for Linux and Darwin.
+ // TODO(gri) Remove once tests consider +build tags (issue 10370).
+ if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
+ return
+ }
+
+ testTestDir(t, filepath.Join(runtime.GOROOT(), "test"),
+ "cmplxdivide.go", // also needs file cmplxdivide1.go - ignore
+ "sigchld.go", // don't work on Windows; testTestDir should consult build tags
+ )
+}
+
+func TestStdFixed(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "fixedbugs"),
+ "bug248.go", "bug302.go", "bug369.go", // complex test instructions - ignore
+ "bug459.go", // possibly incorrect test - see issue 6703 (pending spec clarification)
+ "issue3924.go", // possibly incorrect test - see issue 6671 (pending spec clarification)
+ "issue6889.go", // gc-specific test
+ "issue7746.go", // large constants - consumes too much memory
+ "issue11326.go", // large constants
+ "issue11326b.go", // large constants
+ )
+}
+
+func TestStdKen(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ testTestDir(t, filepath.Join(runtime.GOROOT(), "test", "ken"))
+}
+
+// Package paths of excluded packages.
+var excluded = map[string]bool{
+ "builtin": true,
+}
+
+// typecheck typechecks the given package files.
+func typecheck(t *testing.T, path string, filenames []string) {
+ fset := token.NewFileSet()
+
+ // parse package files
+ var files []*ast.File
+ for _, filename := range filenames {
+ file, err := parser.ParseFile(fset, filename, nil, parser.AllErrors)
+ if err != nil {
+ // the parser error may be a list of individual errors; report them all
+ if list, ok := err.(scanner.ErrorList); ok {
+ for _, err := range list {
+ t.Error(err)
+ }
+ return
+ }
+ t.Error(err)
+ return
+ }
+
+ if testing.Verbose() {
+ if len(files) == 0 {
+ fmt.Println("package", file.Name.Name)
+ }
+ fmt.Println("\t", filename)
+ }
+
+ files = append(files, file)
+ }
+
+ // typecheck package files
+ conf := Config{
+ Error: func(err error) { t.Error(err) },
+ Importer: stdLibImporter,
+ }
+ info := Info{Uses: make(map[*ast.Ident]Object)}
+ conf.Check(path, fset, files, &info)
+ pkgCount++
+
+ // Perform checks of API invariants.
+
+ // All Objects have a package, except predeclared ones.
+ errorError := Universe.Lookup("error").Type().Underlying().(*Interface).ExplicitMethod(0) // (error).Error
+ for id, obj := range info.Uses {
+ predeclared := obj == Universe.Lookup(obj.Name()) || obj == errorError
+ if predeclared == (obj.Pkg() != nil) {
+ posn := fset.Position(id.Pos())
+ if predeclared {
+ t.Errorf("%s: predeclared object with package: %s", posn, obj)
+ } else {
+ t.Errorf("%s: user-defined object without package: %s", posn, obj)
+ }
+ }
+ }
+}
+
+// pkgFilenames returns the list of package filenames for the given directory.
+func pkgFilenames(dir string) ([]string, error) {
+ ctxt := build.Default
+ ctxt.CgoEnabled = false
+ pkg, err := ctxt.ImportDir(dir, 0)
+ if err != nil {
+ if _, nogo := err.(*build.NoGoError); nogo {
+ return nil, nil // no *.go files, not an error
+ }
+ return nil, err
+ }
+ if excluded[pkg.ImportPath] {
+ return nil, nil
+ }
+ var filenames []string
+ for _, name := range pkg.GoFiles {
+ filenames = append(filenames, filepath.Join(pkg.Dir, name))
+ }
+ for _, name := range pkg.TestGoFiles {
+ filenames = append(filenames, filepath.Join(pkg.Dir, name))
+ }
+ return filenames, nil
+}
+
+// Note: Could use filepath.Walk instead of walkDirs but that wouldn't
+// necessarily be shorter or clearer after adding the code to
+// terminate early for -short tests.
+
+func walkDirs(t *testing.T, dir string) {
+ // limit run time for short tests
+ if testing.Short() && time.Since(start) >= 750*time.Millisecond {
+ return
+ }
+
+ fis, err := ioutil.ReadDir(dir)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ // typecheck package in directory
+ files, err := pkgFilenames(dir)
+ if err != nil {
+ t.Error(err)
+ return
+ }
+ if files != nil {
+ typecheck(t, dir, files)
+ }
+
+ // traverse subdirectories, but don't walk into testdata
+ for _, fi := range fis {
+ if fi.IsDir() && fi.Name() != "testdata" {
+ walkDirs(t, filepath.Join(dir, fi.Name()))
+ }
+ }
+}
diff --git a/libgo/go/go/types/stmt.go b/libgo/go/go/types/stmt.go
new file mode 100644
index 00000000000..88a1d9b8668
--- /dev/null
+++ b/libgo/go/go/types/stmt.go
@@ -0,0 +1,744 @@
+// Copyright 2012 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 typechecking of statements.
+
+package types
+
+import (
+ "fmt"
+ "go/ast"
+ "go/constant"
+ "go/token"
+)
+
+func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body *ast.BlockStmt) {
+ if trace {
+ if name == "" {
+ name = "<function literal>"
+ }
+ fmt.Printf("--- %s: %s {\n", name, sig)
+ defer fmt.Println("--- <end>")
+ }
+
+ // set function scope extent
+ sig.scope.pos = body.Pos()
+ sig.scope.end = body.End()
+
+ // save/restore current context and setup function context
+ // (and use 0 indentation at function start)
+ defer func(ctxt context, indent int) {
+ check.context = ctxt
+ check.indent = indent
+ }(check.context, check.indent)
+ check.context = context{
+ decl: decl,
+ scope: sig.scope,
+ sig: sig,
+ }
+ check.indent = 0
+
+ check.stmtList(0, body.List)
+
+ if check.hasLabel {
+ check.labels(body)
+ }
+
+ if sig.results.Len() > 0 && !check.isTerminating(body, "") {
+ check.error(body.Rbrace, "missing return")
+ }
+
+ // spec: "Implementation restriction: A compiler may make it illegal to
+ // declare a variable inside a function body if the variable is never used."
+ // (One could check each scope after use, but that distributes this check
+ // over several places because CloseScope is not always called explicitly.)
+ check.usage(sig.scope)
+}
+
+func (check *Checker) usage(scope *Scope) {
+ for _, obj := range scope.elems {
+ if v, _ := obj.(*Var); v != nil && !v.used {
+ check.softErrorf(v.pos, "%s declared but not used", v.name)
+ }
+ }
+ for _, scope := range scope.children {
+ check.usage(scope)
+ }
+}
+
+// stmtContext is a bitset describing which
+// control-flow statements are permissible.
+type stmtContext uint
+
+const (
+ breakOk stmtContext = 1 << iota
+ continueOk
+ fallthroughOk
+)
+
+func (check *Checker) simpleStmt(s ast.Stmt) {
+ if s != nil {
+ check.stmt(0, s)
+ }
+}
+
+func (check *Checker) stmtList(ctxt stmtContext, list []ast.Stmt) {
+ ok := ctxt&fallthroughOk != 0
+ inner := ctxt &^ fallthroughOk
+ for i, s := range list {
+ inner := inner
+ if ok && i+1 == len(list) {
+ inner |= fallthroughOk
+ }
+ check.stmt(inner, s)
+ }
+}
+
+func (check *Checker) multipleDefaults(list []ast.Stmt) {
+ var first ast.Stmt
+ for _, s := range list {
+ var d ast.Stmt
+ switch c := s.(type) {
+ case *ast.CaseClause:
+ if len(c.List) == 0 {
+ d = s
+ }
+ case *ast.CommClause:
+ if c.Comm == nil {
+ d = s
+ }
+ default:
+ check.invalidAST(s.Pos(), "case/communication clause expected")
+ }
+ if d != nil {
+ if first != nil {
+ check.errorf(d.Pos(), "multiple defaults (first at %s)", first.Pos())
+ } else {
+ first = d
+ }
+ }
+ }
+}
+
+func (check *Checker) openScope(s ast.Stmt, comment string) {
+ scope := NewScope(check.scope, s.Pos(), s.End(), comment)
+ check.recordScope(s, scope)
+ check.scope = scope
+}
+
+func (check *Checker) closeScope() {
+ check.scope = check.scope.Parent()
+}
+
+func assignOp(op token.Token) token.Token {
+ // token_test.go verifies the token ordering this function relies on
+ if token.ADD_ASSIGN <= op && op <= token.AND_NOT_ASSIGN {
+ return op + (token.ADD - token.ADD_ASSIGN)
+ }
+ return token.ILLEGAL
+}
+
+func (check *Checker) suspendedCall(keyword string, call *ast.CallExpr) {
+ var x operand
+ var msg string
+ switch check.rawExpr(&x, call, nil) {
+ case conversion:
+ msg = "requires function call, not conversion"
+ case expression:
+ msg = "discards result of"
+ case statement:
+ return
+ default:
+ unreachable()
+ }
+ check.errorf(x.pos(), "%s %s %s", keyword, msg, &x)
+}
+
+func (check *Checker) caseValues(x operand /* copy argument (not *operand!) */, values []ast.Expr) {
+ // No duplicate checking for now. See issue 4524.
+ for _, e := range values {
+ var y operand
+ check.expr(&y, e)
+ if y.mode == invalid {
+ return
+ }
+ // TODO(gri) The convertUntyped call pair below appears in other places. Factor!
+ // Order matters: By comparing y against x, error positions are at the case values.
+ check.convertUntyped(&y, x.typ)
+ if y.mode == invalid {
+ return
+ }
+ check.convertUntyped(&x, y.typ)
+ if x.mode == invalid {
+ return
+ }
+ check.comparison(&y, &x, token.EQL)
+ }
+}
+
+func (check *Checker) caseTypes(x *operand, xtyp *Interface, types []ast.Expr, seen map[Type]token.Pos) (T Type) {
+L:
+ for _, e := range types {
+ T = check.typOrNil(e)
+ if T == Typ[Invalid] {
+ continue
+ }
+ // complain about duplicate types
+ // TODO(gri) use a type hash to avoid quadratic algorithm
+ for t, pos := range seen {
+ if T == nil && t == nil || T != nil && t != nil && Identical(T, t) {
+ // talk about "case" rather than "type" because of nil case
+ check.error(e.Pos(), "duplicate case in type switch")
+ check.errorf(pos, "\tprevious case %s", T) // secondary error, \t indented
+ continue L
+ }
+ }
+ seen[T] = e.Pos()
+ if T != nil {
+ check.typeAssertion(e.Pos(), x, xtyp, T)
+ }
+ }
+ return
+}
+
+// stmt typechecks statement s.
+func (check *Checker) stmt(ctxt stmtContext, s ast.Stmt) {
+ // statements cannot use iota in general
+ // (constant declarations set it explicitly)
+ assert(check.iota == nil)
+
+ // statements must end with the same top scope as they started with
+ if debug {
+ defer func(scope *Scope) {
+ // don't check if code is panicking
+ if p := recover(); p != nil {
+ panic(p)
+ }
+ assert(scope == check.scope)
+ }(check.scope)
+ }
+
+ inner := ctxt &^ fallthroughOk
+ switch s := s.(type) {
+ case *ast.BadStmt, *ast.EmptyStmt:
+ // ignore
+
+ case *ast.DeclStmt:
+ check.declStmt(s.Decl)
+
+ case *ast.LabeledStmt:
+ check.hasLabel = true
+ check.stmt(ctxt, s.Stmt)
+
+ case *ast.ExprStmt:
+ // spec: "With the exception of specific built-in functions,
+ // function and method calls and receive operations can appear
+ // in statement context. Such statements may be parenthesized."
+ var x operand
+ kind := check.rawExpr(&x, s.X, nil)
+ var msg string
+ switch x.mode {
+ default:
+ if kind == statement {
+ return
+ }
+ msg = "is not used"
+ case builtin:
+ msg = "must be called"
+ case typexpr:
+ msg = "is not an expression"
+ }
+ check.errorf(x.pos(), "%s %s", &x, msg)
+
+ case *ast.SendStmt:
+ var ch, x operand
+ check.expr(&ch, s.Chan)
+ check.expr(&x, s.Value)
+ if ch.mode == invalid || x.mode == invalid {
+ return
+ }
+ if tch, ok := ch.typ.Underlying().(*Chan); !ok || tch.dir == RecvOnly || !check.assignment(&x, tch.elem) {
+ if x.mode != invalid {
+ check.invalidOp(ch.pos(), "cannot send %s to channel %s", &x, &ch)
+ }
+ }
+
+ case *ast.IncDecStmt:
+ var op token.Token
+ switch s.Tok {
+ case token.INC:
+ op = token.ADD
+ case token.DEC:
+ op = token.SUB
+ default:
+ check.invalidAST(s.TokPos, "unknown inc/dec operation %s", s.Tok)
+ return
+ }
+ var x operand
+ Y := &ast.BasicLit{ValuePos: s.X.Pos(), Kind: token.INT, Value: "1"} // use x's position
+ check.binary(&x, nil, s.X, Y, op)
+ if x.mode == invalid {
+ return
+ }
+ check.assignVar(s.X, &x)
+
+ case *ast.AssignStmt:
+ switch s.Tok {
+ case token.ASSIGN, token.DEFINE:
+ if len(s.Lhs) == 0 {
+ check.invalidAST(s.Pos(), "missing lhs in assignment")
+ return
+ }
+ if s.Tok == token.DEFINE {
+ check.shortVarDecl(s.TokPos, s.Lhs, s.Rhs)
+ } else {
+ // regular assignment
+ check.assignVars(s.Lhs, s.Rhs)
+ }
+
+ default:
+ // assignment operations
+ if len(s.Lhs) != 1 || len(s.Rhs) != 1 {
+ check.errorf(s.TokPos, "assignment operation %s requires single-valued expressions", s.Tok)
+ return
+ }
+ op := assignOp(s.Tok)
+ if op == token.ILLEGAL {
+ check.invalidAST(s.TokPos, "unknown assignment operation %s", s.Tok)
+ return
+ }
+ var x operand
+ check.binary(&x, nil, s.Lhs[0], s.Rhs[0], op)
+ if x.mode == invalid {
+ return
+ }
+ check.assignVar(s.Lhs[0], &x)
+ }
+
+ case *ast.GoStmt:
+ check.suspendedCall("go", s.Call)
+
+ case *ast.DeferStmt:
+ check.suspendedCall("defer", s.Call)
+
+ case *ast.ReturnStmt:
+ res := check.sig.results
+ if res.Len() > 0 {
+ // function returns results
+ // (if one, say the first, result parameter is named, all of them are named)
+ if len(s.Results) == 0 && res.vars[0].name != "" {
+ // spec: "Implementation restriction: A compiler may disallow an empty expression
+ // list in a "return" statement if a different entity (constant, type, or variable)
+ // with the same name as a result parameter is in scope at the place of the return."
+ for _, obj := range res.vars {
+ if _, alt := check.scope.LookupParent(obj.name, check.pos); alt != nil && alt != obj {
+ check.errorf(s.Pos(), "result parameter %s not in scope at return", obj.name)
+ check.errorf(alt.Pos(), "\tinner declaration of %s", obj)
+ // ok to continue
+ }
+ }
+ } else {
+ // return has results or result parameters are unnamed
+ check.initVars(res.vars, s.Results, s.Return)
+ }
+ } else if len(s.Results) > 0 {
+ check.error(s.Results[0].Pos(), "no result values expected")
+ check.use(s.Results...)
+ }
+
+ case *ast.BranchStmt:
+ if s.Label != nil {
+ check.hasLabel = true
+ return // checked in 2nd pass (check.labels)
+ }
+ switch s.Tok {
+ case token.BREAK:
+ if ctxt&breakOk == 0 {
+ check.error(s.Pos(), "break not in for, switch, or select statement")
+ }
+ case token.CONTINUE:
+ if ctxt&continueOk == 0 {
+ check.error(s.Pos(), "continue not in for statement")
+ }
+ case token.FALLTHROUGH:
+ if ctxt&fallthroughOk == 0 {
+ check.error(s.Pos(), "fallthrough statement out of place")
+ }
+ default:
+ check.invalidAST(s.Pos(), "branch statement: %s", s.Tok)
+ }
+
+ case *ast.BlockStmt:
+ check.openScope(s, "block")
+ defer check.closeScope()
+
+ check.stmtList(inner, s.List)
+
+ case *ast.IfStmt:
+ check.openScope(s, "if")
+ defer check.closeScope()
+
+ check.simpleStmt(s.Init)
+ var x operand
+ check.expr(&x, s.Cond)
+ if x.mode != invalid && !isBoolean(x.typ) {
+ check.error(s.Cond.Pos(), "non-boolean condition in if statement")
+ }
+ check.stmt(inner, s.Body)
+ if s.Else != nil {
+ check.stmt(inner, s.Else)
+ }
+
+ case *ast.SwitchStmt:
+ inner |= breakOk
+ check.openScope(s, "switch")
+ defer check.closeScope()
+
+ check.simpleStmt(s.Init)
+ var x operand
+ if s.Tag != nil {
+ check.expr(&x, s.Tag)
+ } else {
+ // spec: "A missing switch expression is
+ // equivalent to the boolean value true."
+ x.mode = constant_
+ x.typ = Typ[Bool]
+ x.val = constant.MakeBool(true)
+ x.expr = &ast.Ident{NamePos: s.Body.Lbrace, Name: "true"}
+ }
+
+ check.multipleDefaults(s.Body.List)
+
+ for i, c := range s.Body.List {
+ clause, _ := c.(*ast.CaseClause)
+ if clause == nil {
+ check.invalidAST(c.Pos(), "incorrect expression switch case")
+ continue
+ }
+ if x.mode != invalid {
+ check.caseValues(x, clause.List)
+ }
+ check.openScope(clause, "case")
+ inner := inner
+ if i+1 < len(s.Body.List) {
+ inner |= fallthroughOk
+ }
+ check.stmtList(inner, clause.Body)
+ check.closeScope()
+ }
+
+ case *ast.TypeSwitchStmt:
+ inner |= breakOk
+ check.openScope(s, "type switch")
+ defer check.closeScope()
+
+ check.simpleStmt(s.Init)
+
+ // A type switch guard must be of the form:
+ //
+ // TypeSwitchGuard = [ identifier ":=" ] PrimaryExpr "." "(" "type" ")" .
+ //
+ // The parser is checking syntactic correctness;
+ // remaining syntactic errors are considered AST errors here.
+ // TODO(gri) better factoring of error handling (invalid ASTs)
+ //
+ var lhs *ast.Ident // lhs identifier or nil
+ var rhs ast.Expr
+ switch guard := s.Assign.(type) {
+ case *ast.ExprStmt:
+ rhs = guard.X
+ case *ast.AssignStmt:
+ if len(guard.Lhs) != 1 || guard.Tok != token.DEFINE || len(guard.Rhs) != 1 {
+ check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ return
+ }
+
+ lhs, _ = guard.Lhs[0].(*ast.Ident)
+ if lhs == nil {
+ check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ return
+ }
+
+ if lhs.Name == "_" {
+ // _ := x.(type) is an invalid short variable declaration
+ check.softErrorf(lhs.Pos(), "no new variable on left side of :=")
+ lhs = nil // avoid declared but not used error below
+ } else {
+ check.recordDef(lhs, nil) // lhs variable is implicitly declared in each cause clause
+ }
+
+ rhs = guard.Rhs[0]
+
+ default:
+ check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ return
+ }
+
+ // rhs must be of the form: expr.(type) and expr must be an interface
+ expr, _ := rhs.(*ast.TypeAssertExpr)
+ if expr == nil || expr.Type != nil {
+ check.invalidAST(s.Pos(), "incorrect form of type switch guard")
+ return
+ }
+ var x operand
+ check.expr(&x, expr.X)
+ if x.mode == invalid {
+ return
+ }
+ xtyp, _ := x.typ.Underlying().(*Interface)
+ if xtyp == nil {
+ check.errorf(x.pos(), "%s is not an interface", &x)
+ return
+ }
+
+ check.multipleDefaults(s.Body.List)
+
+ var lhsVars []*Var // list of implicitly declared lhs variables
+ seen := make(map[Type]token.Pos) // map of seen types to positions
+ for _, s := range s.Body.List {
+ clause, _ := s.(*ast.CaseClause)
+ if clause == nil {
+ check.invalidAST(s.Pos(), "incorrect type switch case")
+ continue
+ }
+ // Check each type in this type switch case.
+ T := check.caseTypes(&x, xtyp, clause.List, seen)
+ check.openScope(clause, "case")
+ // If lhs exists, declare a corresponding variable in the case-local scope.
+ if lhs != nil {
+ // spec: "The TypeSwitchGuard may include a short variable declaration.
+ // When that form is used, the variable is declared at the beginning of
+ // the implicit block in each clause. In clauses with a case listing
+ // exactly one type, the variable has that type; otherwise, the variable
+ // has the type of the expression in the TypeSwitchGuard."
+ if len(clause.List) != 1 || T == nil {
+ T = x.typ
+ }
+ obj := NewVar(lhs.Pos(), check.pkg, lhs.Name, T)
+ scopePos := clause.End()
+ if len(clause.Body) > 0 {
+ scopePos = clause.Body[0].Pos()
+ }
+ check.declare(check.scope, nil, obj, scopePos)
+ check.recordImplicit(clause, obj)
+ // For the "declared but not used" error, all lhs variables act as
+ // one; i.e., if any one of them is 'used', all of them are 'used'.
+ // Collect them for later analysis.
+ lhsVars = append(lhsVars, obj)
+ }
+ check.stmtList(inner, clause.Body)
+ check.closeScope()
+ }
+
+ // If lhs exists, we must have at least one lhs variable that was used.
+ if lhs != nil {
+ var used bool
+ for _, v := range lhsVars {
+ if v.used {
+ used = true
+ }
+ v.used = true // avoid usage error when checking entire function
+ }
+ if !used {
+ check.softErrorf(lhs.Pos(), "%s declared but not used", lhs.Name)
+ }
+ }
+
+ case *ast.SelectStmt:
+ inner |= breakOk
+
+ check.multipleDefaults(s.Body.List)
+
+ for _, s := range s.Body.List {
+ clause, _ := s.(*ast.CommClause)
+ if clause == nil {
+ continue // error reported before
+ }
+
+ // clause.Comm must be a SendStmt, RecvStmt, or default case
+ valid := false
+ var rhs ast.Expr // rhs of RecvStmt, or nil
+ switch s := clause.Comm.(type) {
+ case nil, *ast.SendStmt:
+ valid = true
+ case *ast.AssignStmt:
+ if len(s.Rhs) == 1 {
+ rhs = s.Rhs[0]
+ }
+ case *ast.ExprStmt:
+ rhs = s.X
+ }
+
+ // if present, rhs must be a receive operation
+ if rhs != nil {
+ if x, _ := unparen(rhs).(*ast.UnaryExpr); x != nil && x.Op == token.ARROW {
+ valid = true
+ }
+ }
+
+ if !valid {
+ check.error(clause.Comm.Pos(), "select case must be send or receive (possibly with assignment)")
+ continue
+ }
+
+ check.openScope(s, "case")
+ if clause.Comm != nil {
+ check.stmt(inner, clause.Comm)
+ }
+ check.stmtList(inner, clause.Body)
+ check.closeScope()
+ }
+
+ case *ast.ForStmt:
+ inner |= breakOk | continueOk
+ check.openScope(s, "for")
+ defer check.closeScope()
+
+ check.simpleStmt(s.Init)
+ if s.Cond != nil {
+ var x operand
+ check.expr(&x, s.Cond)
+ if x.mode != invalid && !isBoolean(x.typ) {
+ check.error(s.Cond.Pos(), "non-boolean condition in for statement")
+ }
+ }
+ check.simpleStmt(s.Post)
+ // spec: "The init statement may be a short variable
+ // declaration, but the post statement must not."
+ if s, _ := s.Post.(*ast.AssignStmt); s != nil && s.Tok == token.DEFINE {
+ check.softErrorf(s.Pos(), "cannot declare in post statement")
+ check.use(s.Lhs...) // avoid follow-up errors
+ }
+ check.stmt(inner, s.Body)
+
+ case *ast.RangeStmt:
+ inner |= breakOk | continueOk
+ check.openScope(s, "for")
+ defer check.closeScope()
+
+ // check expression to iterate over
+ var x operand
+ check.expr(&x, s.X)
+
+ // determine key/value types
+ var key, val Type
+ if x.mode != invalid {
+ switch typ := x.typ.Underlying().(type) {
+ case *Basic:
+ if isString(typ) {
+ key = Typ[Int]
+ val = universeRune // use 'rune' name
+ }
+ case *Array:
+ key = Typ[Int]
+ val = typ.elem
+ case *Slice:
+ key = Typ[Int]
+ val = typ.elem
+ case *Pointer:
+ if typ, _ := typ.base.Underlying().(*Array); typ != nil {
+ key = Typ[Int]
+ val = typ.elem
+ }
+ case *Map:
+ key = typ.key
+ val = typ.elem
+ case *Chan:
+ key = typ.elem
+ val = Typ[Invalid]
+ if typ.dir == SendOnly {
+ check.errorf(x.pos(), "cannot range over send-only channel %s", &x)
+ // ok to continue
+ }
+ if s.Value != nil {
+ check.errorf(s.Value.Pos(), "iteration over %s permits only one iteration variable", &x)
+ // ok to continue
+ }
+ }
+ }
+
+ if key == nil {
+ check.errorf(x.pos(), "cannot range over %s", &x)
+ // ok to continue
+ }
+
+ // check assignment to/declaration of iteration variables
+ // (irregular assignment, cannot easily map to existing assignment checks)
+
+ // lhs expressions and initialization value (rhs) types
+ lhs := [2]ast.Expr{s.Key, s.Value}
+ rhs := [2]Type{key, val} // key, val may be nil
+
+ if s.Tok == token.DEFINE {
+ // short variable declaration; variable scope starts after the range clause
+ // (the for loop opens a new scope, so variables on the lhs never redeclare
+ // previously declared variables)
+ var vars []*Var
+ for i, lhs := range lhs {
+ if lhs == nil {
+ continue
+ }
+
+ // determine lhs variable
+ var obj *Var
+ if ident, _ := lhs.(*ast.Ident); ident != nil {
+ // declare new variable
+ name := ident.Name
+ obj = NewVar(ident.Pos(), check.pkg, name, nil)
+ check.recordDef(ident, obj)
+ // _ variables don't count as new variables
+ if name != "_" {
+ vars = append(vars, obj)
+ }
+ } else {
+ check.errorf(lhs.Pos(), "cannot declare %s", lhs)
+ obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
+ }
+
+ // initialize lhs variable
+ if typ := rhs[i]; typ != nil {
+ x.mode = value
+ x.expr = lhs // we don't have a better rhs expression to use here
+ x.typ = typ
+ check.initVar(obj, &x, false)
+ } else {
+ obj.typ = Typ[Invalid]
+ obj.used = true // don't complain about unused variable
+ }
+ }
+
+ // declare variables
+ if len(vars) > 0 {
+ for _, obj := range vars {
+ // spec: "The scope of a constant or variable identifier declared inside
+ // a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl
+ // for short variable declarations) and ends at the end of the innermost
+ // containing block."
+ scopePos := s.End()
+ check.declare(check.scope, nil /* recordDef already called */, obj, scopePos)
+ }
+ } else {
+ check.error(s.TokPos, "no new variables on left side of :=")
+ }
+ } else {
+ // ordinary assignment
+ for i, lhs := range lhs {
+ if lhs == nil {
+ continue
+ }
+ if typ := rhs[i]; typ != nil {
+ x.mode = value
+ x.expr = lhs // we don't have a better rhs expression to use here
+ x.typ = typ
+ check.assignVar(lhs, &x)
+ }
+ }
+ }
+
+ check.stmt(inner, s.Body)
+
+ default:
+ check.error(s.Pos(), "invalid statement")
+ }
+}
diff --git a/libgo/go/go/types/token_test.go b/libgo/go/go/types/token_test.go
new file mode 100644
index 00000000000..705bb295a16
--- /dev/null
+++ b/libgo/go/go/types/token_test.go
@@ -0,0 +1,47 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file checks invariants of token.Token ordering that we rely on
+// since package go/token doesn't provide any guarantees at the moment.
+
+package types
+
+import (
+ "go/token"
+ "testing"
+)
+
+var assignOps = map[token.Token]token.Token{
+ token.ADD_ASSIGN: token.ADD,
+ token.SUB_ASSIGN: token.SUB,
+ token.MUL_ASSIGN: token.MUL,
+ token.QUO_ASSIGN: token.QUO,
+ token.REM_ASSIGN: token.REM,
+ token.AND_ASSIGN: token.AND,
+ token.OR_ASSIGN: token.OR,
+ token.XOR_ASSIGN: token.XOR,
+ token.SHL_ASSIGN: token.SHL,
+ token.SHR_ASSIGN: token.SHR,
+ token.AND_NOT_ASSIGN: token.AND_NOT,
+}
+
+func TestZeroTok(t *testing.T) {
+ // zero value for token.Token must be token.ILLEGAL
+ var zero token.Token
+ if token.ILLEGAL != zero {
+ t.Errorf("%s == %d; want 0", token.ILLEGAL, zero)
+ }
+}
+
+func TestAssignOp(t *testing.T) {
+ // there are fewer than 256 tokens
+ for i := 0; i < 256; i++ {
+ tok := token.Token(i)
+ got := assignOp(tok)
+ want := assignOps[tok]
+ if got != want {
+ t.Errorf("for assignOp(%s): got %s; want %s", tok, got, want)
+ }
+ }
+}
diff --git a/libgo/go/go/types/type.go b/libgo/go/go/types/type.go
new file mode 100644
index 00000000000..1df8b45b285
--- /dev/null
+++ b/libgo/go/go/types/type.go
@@ -0,0 +1,454 @@
+// 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 "sort"
+
+// TODO(gri) Revisit factory functions - make sure they have all relevant parameters.
+
+// A Type represents a type of Go.
+// All types implement the Type interface.
+type Type interface {
+ // Underlying returns the underlying type of a type.
+ Underlying() Type
+
+ // String returns a string representation of a type.
+ String() string
+}
+
+// BasicKind describes the kind of basic type.
+type BasicKind int
+
+const (
+ Invalid BasicKind = iota // type is invalid
+
+ // predeclared types
+ Bool
+ Int
+ Int8
+ Int16
+ Int32
+ Int64
+ Uint
+ Uint8
+ Uint16
+ Uint32
+ Uint64
+ Uintptr
+ Float32
+ Float64
+ Complex64
+ Complex128
+ String
+ UnsafePointer
+
+ // types for untyped values
+ UntypedBool
+ UntypedInt
+ UntypedRune
+ UntypedFloat
+ UntypedComplex
+ UntypedString
+ UntypedNil
+
+ // aliases
+ Byte = Uint8
+ Rune = Int32
+)
+
+// BasicInfo is a set of flags describing properties of a basic type.
+type BasicInfo int
+
+// Properties of basic types.
+const (
+ IsBoolean BasicInfo = 1 << iota
+ IsInteger
+ IsUnsigned
+ IsFloat
+ IsComplex
+ IsString
+ IsUntyped
+
+ IsOrdered = IsInteger | IsFloat | IsString
+ IsNumeric = IsInteger | IsFloat | IsComplex
+ IsConstType = IsBoolean | IsNumeric | IsString
+)
+
+// A Basic represents a basic type.
+type Basic struct {
+ kind BasicKind
+ info BasicInfo
+ name string
+}
+
+// Kind returns the kind of basic type b.
+func (b *Basic) Kind() BasicKind { return b.kind }
+
+// Info returns information about properties of basic type b.
+func (b *Basic) Info() BasicInfo { return b.info }
+
+// Name returns the name of basic type b.
+func (b *Basic) Name() string { return b.name }
+
+// An Array represents an array type.
+type Array struct {
+ len int64
+ elem Type
+}
+
+// NewArray returns a new array type for the given element type and length.
+func NewArray(elem Type, len int64) *Array { return &Array{len, elem} }
+
+// Len returns the length of array a.
+func (a *Array) Len() int64 { return a.len }
+
+// Elem returns element type of array a.
+func (a *Array) Elem() Type { return a.elem }
+
+// A Slice represents a slice type.
+type Slice struct {
+ elem Type
+}
+
+// NewSlice returns a new slice type for the given element type.
+func NewSlice(elem Type) *Slice { return &Slice{elem} }
+
+// Elem returns the element type of slice s.
+func (s *Slice) Elem() Type { return s.elem }
+
+// A Struct represents a struct type.
+type Struct struct {
+ fields []*Var
+ tags []string // field tags; nil if there are no tags
+ // TODO(gri) access to offsets is not threadsafe - fix this
+ offsets []int64 // field offsets in bytes, lazily initialized
+}
+
+// NewStruct returns a new struct with the given fields and corresponding field tags.
+// If a field with index i has a tag, tags[i] must be that tag, but len(tags) may be
+// only as long as required to hold the tag with the largest index i. Consequently,
+// if no field has a tag, tags may be nil.
+func NewStruct(fields []*Var, tags []string) *Struct {
+ var fset objset
+ for _, f := range fields {
+ if f.name != "_" && fset.insert(f) != nil {
+ panic("multiple fields with the same name")
+ }
+ }
+ if len(tags) > len(fields) {
+ panic("more tags than fields")
+ }
+ return &Struct{fields: fields, tags: tags}
+}
+
+// NumFields returns the number of fields in the struct (including blank and anonymous fields).
+func (s *Struct) NumFields() int { return len(s.fields) }
+
+// Field returns the i'th field for 0 <= i < NumFields().
+func (s *Struct) Field(i int) *Var { return s.fields[i] }
+
+// Tag returns the i'th field tag for 0 <= i < NumFields().
+func (s *Struct) Tag(i int) string {
+ if i < len(s.tags) {
+ return s.tags[i]
+ }
+ return ""
+}
+
+// A Pointer represents a pointer type.
+type Pointer struct {
+ base Type // element type
+}
+
+// NewPointer returns a new pointer type for the given element (base) type.
+func NewPointer(elem Type) *Pointer { return &Pointer{base: elem} }
+
+// Elem returns the element type for the given pointer p.
+func (p *Pointer) Elem() Type { return p.base }
+
+// A Tuple represents an ordered list of variables; a nil *Tuple is a valid (empty) tuple.
+// Tuples are used as components of signatures and to represent the type of multiple
+// assignments; they are not first class types of Go.
+type Tuple struct {
+ vars []*Var
+}
+
+// NewTuple returns a new tuple for the given variables.
+func NewTuple(x ...*Var) *Tuple {
+ if len(x) > 0 {
+ return &Tuple{x}
+ }
+ return nil
+}
+
+// Len returns the number variables of tuple t.
+func (t *Tuple) Len() int {
+ if t != nil {
+ return len(t.vars)
+ }
+ return 0
+}
+
+// At returns the i'th variable of tuple t.
+func (t *Tuple) At(i int) *Var { return t.vars[i] }
+
+// A Signature represents a (non-builtin) function or method type.
+type Signature struct {
+ // We need to keep the scope in Signature (rather than passing it around
+ // and store it in the Func Object) because when type-checking a function
+ // literal we call the general type checker which returns a general Type.
+ // We then unpack the *Signature and use the scope for the literal body.
+ scope *Scope // function scope, present for package-local signatures
+ recv *Var // nil if not a method
+ params *Tuple // (incoming) parameters from left to right; or nil
+ results *Tuple // (outgoing) results from left to right; or nil
+ variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only)
+}
+
+// NewSignature returns a new function type for the given receiver, parameters,
+// and results, either of which may be nil. If variadic is set, the function
+// is variadic, it must have at least one parameter, and the last parameter
+// must be of unnamed slice type.
+func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature {
+ if variadic {
+ n := params.Len()
+ if n == 0 {
+ panic("types.NewSignature: variadic function must have at least one parameter")
+ }
+ if _, ok := params.At(n - 1).typ.(*Slice); !ok {
+ panic("types.NewSignature: variadic parameter must be of unnamed slice type")
+ }
+ }
+ return &Signature{nil, recv, params, results, variadic}
+}
+
+// Recv returns the receiver of signature s (if a method), or nil if a
+// function.
+//
+// For an abstract method, Recv returns the enclosing interface either
+// as a *Named or an *Interface. Due to embedding, an interface may
+// contain methods whose receiver type is a different interface.
+func (s *Signature) Recv() *Var { return s.recv }
+
+// Params returns the parameters of signature s, or nil.
+func (s *Signature) Params() *Tuple { return s.params }
+
+// Results returns the results of signature s, or nil.
+func (s *Signature) Results() *Tuple { return s.results }
+
+// Variadic reports whether the signature s is variadic.
+func (s *Signature) Variadic() bool { return s.variadic }
+
+// An Interface represents an interface type.
+type Interface struct {
+ methods []*Func // ordered list of explicitly declared methods
+ embeddeds []*Named // ordered list of explicitly embedded types
+
+ allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
+}
+
+// NewInterface returns a new interface for the given methods and embedded types.
+func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
+ typ := new(Interface)
+
+ var mset objset
+ for _, m := range methods {
+ if mset.insert(m) != nil {
+ panic("multiple methods with the same name")
+ }
+ // set receiver
+ // TODO(gri) Ideally, we should use a named type here instead of
+ // typ, for less verbose printing of interface method signatures.
+ m.typ.(*Signature).recv = NewVar(m.pos, m.pkg, "", typ)
+ }
+ sort.Sort(byUniqueMethodName(methods))
+
+ if embeddeds == nil {
+ sort.Sort(byUniqueTypeName(embeddeds))
+ }
+
+ typ.methods = methods
+ typ.embeddeds = embeddeds
+ return typ
+}
+
+// NumExplicitMethods returns the number of explicitly declared methods of interface t.
+func (t *Interface) NumExplicitMethods() int { return len(t.methods) }
+
+// ExplicitMethod returns the i'th explicitly declared method of interface t for 0 <= i < t.NumExplicitMethods().
+// The methods are ordered by their unique Id.
+func (t *Interface) ExplicitMethod(i int) *Func { return t.methods[i] }
+
+// NumEmbeddeds returns the number of embedded types in interface t.
+func (t *Interface) NumEmbeddeds() int { return len(t.embeddeds) }
+
+// Embedded returns the i'th embedded type of interface t for 0 <= i < t.NumEmbeddeds().
+// The types are ordered by the corresponding TypeName's unique Id.
+func (t *Interface) Embedded(i int) *Named { return t.embeddeds[i] }
+
+// NumMethods returns the total number of methods of interface t.
+func (t *Interface) NumMethods() int { return len(t.allMethods) }
+
+// Method returns the i'th method of interface t for 0 <= i < t.NumMethods().
+// The methods are ordered by their unique Id.
+func (t *Interface) Method(i int) *Func { return t.allMethods[i] }
+
+// Empty returns true if t is the empty interface.
+func (t *Interface) Empty() bool { return len(t.allMethods) == 0 }
+
+// Complete computes the interface's method set. It must be called by users of
+// NewInterface after the interface's embedded types are fully defined and
+// before using the interface type in any way other than to form other types.
+// Complete returns the receiver.
+func (t *Interface) Complete() *Interface {
+ if t.allMethods != nil {
+ return t
+ }
+
+ var allMethods []*Func
+ if t.embeddeds == nil {
+ if t.methods == nil {
+ allMethods = make([]*Func, 0, 1)
+ } else {
+ allMethods = t.methods
+ }
+ } else {
+ allMethods = append(allMethods, t.methods...)
+ for _, et := range t.embeddeds {
+ it := et.Underlying().(*Interface)
+ it.Complete()
+ for _, tm := range it.allMethods {
+ // Make a copy of the method and adjust its receiver type.
+ newm := *tm
+ newmtyp := *tm.typ.(*Signature)
+ newm.typ = &newmtyp
+ newmtyp.recv = NewVar(newm.pos, newm.pkg, "", t)
+ allMethods = append(allMethods, &newm)
+ }
+ }
+ sort.Sort(byUniqueMethodName(allMethods))
+ }
+ t.allMethods = allMethods
+
+ return t
+}
+
+// A Map represents a map type.
+type Map struct {
+ key, elem Type
+}
+
+// NewMap returns a new map for the given key and element types.
+func NewMap(key, elem Type) *Map {
+ return &Map{key, elem}
+}
+
+// Key returns the key type of map m.
+func (m *Map) Key() Type { return m.key }
+
+// Elem returns the element type of map m.
+func (m *Map) Elem() Type { return m.elem }
+
+// A Chan represents a channel type.
+type Chan struct {
+ dir ChanDir
+ elem Type
+}
+
+// A ChanDir value indicates a channel direction.
+type ChanDir int
+
+// The direction of a channel is indicated by one of the following constants.
+const (
+ SendRecv ChanDir = iota
+ SendOnly
+ RecvOnly
+)
+
+// NewChan returns a new channel type for the given direction and element type.
+func NewChan(dir ChanDir, elem Type) *Chan {
+ return &Chan{dir, elem}
+}
+
+// Dir returns the direction of channel c.
+func (c *Chan) Dir() ChanDir { return c.dir }
+
+// Elem returns the element type of channel c.
+func (c *Chan) Elem() Type { return c.elem }
+
+// A Named represents a named type.
+type Named struct {
+ obj *TypeName // corresponding declared object
+ underlying Type // possibly a *Named during setup; never a *Named once set up completely
+ methods []*Func // methods declared for this type (not the method set of this type)
+}
+
+// NewNamed returns a new named type for the given type name, underlying type, and associated methods.
+// The underlying type must not be a *Named.
+func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
+ if _, ok := underlying.(*Named); ok {
+ panic("types.NewNamed: underlying type must not be *Named")
+ }
+ typ := &Named{obj: obj, underlying: underlying, methods: methods}
+ if obj.typ == nil {
+ obj.typ = typ
+ }
+ return typ
+}
+
+// TypeName returns the type name for the named type t.
+func (t *Named) Obj() *TypeName { return t.obj }
+
+// NumMethods returns the number of explicit methods whose receiver is named type t.
+func (t *Named) NumMethods() int { return len(t.methods) }
+
+// Method returns the i'th method of named type t for 0 <= i < t.NumMethods().
+func (t *Named) Method(i int) *Func { return t.methods[i] }
+
+// SetUnderlying sets the underlying type and marks t as complete.
+// TODO(gri) determine if there's a better solution rather than providing this function
+func (t *Named) SetUnderlying(underlying Type) {
+ if underlying == nil {
+ panic("types.Named.SetUnderlying: underlying type must not be nil")
+ }
+ if _, ok := underlying.(*Named); ok {
+ panic("types.Named.SetUnderlying: underlying type must not be *Named")
+ }
+ t.underlying = underlying
+}
+
+// AddMethod adds method m unless it is already in the method list.
+// TODO(gri) find a better solution instead of providing this function
+func (t *Named) AddMethod(m *Func) {
+ if i, _ := lookupMethod(t.methods, m.pkg, m.name); i < 0 {
+ t.methods = append(t.methods, m)
+ }
+}
+
+// Implementations for Type methods.
+
+func (t *Basic) Underlying() Type { return t }
+func (t *Array) Underlying() Type { return t }
+func (t *Slice) Underlying() Type { return t }
+func (t *Struct) Underlying() Type { return t }
+func (t *Pointer) Underlying() Type { return t }
+func (t *Tuple) Underlying() Type { return t }
+func (t *Signature) Underlying() Type { return t }
+func (t *Interface) Underlying() Type { return t }
+func (t *Map) Underlying() Type { return t }
+func (t *Chan) Underlying() Type { return t }
+func (t *Named) Underlying() Type { return t.underlying }
+
+func (t *Basic) String() string { return TypeString(t, nil) }
+func (t *Array) String() string { return TypeString(t, nil) }
+func (t *Slice) String() string { return TypeString(t, nil) }
+func (t *Struct) String() string { return TypeString(t, nil) }
+func (t *Pointer) String() string { return TypeString(t, nil) }
+func (t *Tuple) String() string { return TypeString(t, nil) }
+func (t *Signature) String() string { return TypeString(t, nil) }
+func (t *Interface) String() string { return TypeString(t, nil) }
+func (t *Map) String() string { return TypeString(t, nil) }
+func (t *Chan) String() string { return TypeString(t, nil) }
+func (t *Named) String() string { return TypeString(t, nil) }
diff --git a/libgo/go/go/types/typestring.go b/libgo/go/go/types/typestring.go
new file mode 100644
index 00000000000..bd62f4dc229
--- /dev/null
+++ b/libgo/go/go/types/typestring.go
@@ -0,0 +1,296 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements printing of types.
+
+package types
+
+import (
+ "bytes"
+ "fmt"
+)
+
+// A Qualifier controls how named package-level objects are printed in
+// calls to TypeString, ObjectString, and SelectionString.
+//
+// These three formatting routines call the Qualifier for each
+// package-level object O, and if the Qualifier returns a non-empty
+// string p, the object is printed in the form p.O.
+// If it returns an empty string, only the object name O is printed.
+//
+// Using a nil Qualifier is equivalent to using (*Package).Path: the
+// object is qualified by the import path, e.g., "encoding/json.Marshal".
+//
+type Qualifier func(*Package) string
+
+// RelativeTo(pkg) returns a Qualifier that fully qualifies members of
+// all packages other than pkg.
+func RelativeTo(pkg *Package) Qualifier {
+ if pkg == nil {
+ return nil
+ }
+ return func(other *Package) string {
+ if pkg == other {
+ return "" // same package; unqualified
+ }
+ return other.Path()
+ }
+}
+
+// If gcCompatibilityMode is set, printing of types is modified
+// to match the representation of some types in the gc compiler:
+//
+// - byte and rune lose their alias name and simply stand for
+// uint8 and int32 respectively
+// - embedded interfaces get flattened (the embedding info is lost,
+// and certain recursive interface types cannot be printed anymore)
+//
+// This makes it easier to compare packages computed with the type-
+// checker vs packages imported from gc export data.
+//
+// Caution: This flag affects all uses of WriteType, globally.
+// It is only provided for testing in conjunction with
+// gc-generated data.
+//
+// This flag is exported in the x/tools/go/types package. We don't
+// need it at the moment in the std repo and so we don't export it
+// anymore. We should eventually try to remove it altogether.
+var gcCompatibilityMode bool
+
+// TypeString returns the string representation of typ.
+// The Qualifier controls the printing of
+// package-level objects, and may be nil.
+func TypeString(typ Type, qf Qualifier) string {
+ var buf bytes.Buffer
+ WriteType(&buf, typ, qf)
+ return buf.String()
+}
+
+// WriteType writes the string representation of typ to buf.
+// The Qualifier controls the printing of
+// package-level objects, and may be nil.
+func WriteType(buf *bytes.Buffer, typ Type, qf Qualifier) {
+ writeType(buf, typ, qf, make([]Type, 8))
+}
+
+func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
+ // Theoretically, this is a quadratic lookup algorithm, but in
+ // practice deeply nested composite types with unnamed component
+ // types are uncommon. This code is likely more efficient than
+ // using a map.
+ for _, t := range visited {
+ if t == typ {
+ fmt.Fprintf(buf, "â—‹%T", typ) // cycle to typ
+ return
+ }
+ }
+ visited = append(visited, typ)
+
+ switch t := typ.(type) {
+ case nil:
+ buf.WriteString("<nil>")
+
+ case *Basic:
+ if t.kind == UnsafePointer {
+ buf.WriteString("unsafe.")
+ }
+ if gcCompatibilityMode {
+ // forget the alias names
+ switch t.kind {
+ case Byte:
+ t = Typ[Uint8]
+ case Rune:
+ t = Typ[Int32]
+ }
+ }
+ buf.WriteString(t.name)
+
+ case *Array:
+ fmt.Fprintf(buf, "[%d]", t.len)
+ writeType(buf, t.elem, qf, visited)
+
+ case *Slice:
+ buf.WriteString("[]")
+ writeType(buf, t.elem, qf, visited)
+
+ case *Struct:
+ buf.WriteString("struct{")
+ for i, f := range t.fields {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ if !f.anonymous {
+ buf.WriteString(f.name)
+ buf.WriteByte(' ')
+ }
+ writeType(buf, f.typ, qf, visited)
+ if tag := t.Tag(i); tag != "" {
+ fmt.Fprintf(buf, " %q", tag)
+ }
+ }
+ buf.WriteByte('}')
+
+ case *Pointer:
+ buf.WriteByte('*')
+ writeType(buf, t.base, qf, visited)
+
+ case *Tuple:
+ writeTuple(buf, t, false, qf, visited)
+
+ case *Signature:
+ buf.WriteString("func")
+ writeSignature(buf, t, qf, visited)
+
+ case *Interface:
+ // We write the source-level methods and embedded types rather
+ // than the actual method set since resolved method signatures
+ // may have non-printable cycles if parameters have anonymous
+ // interface types that (directly or indirectly) embed the
+ // current interface. For instance, consider the result type
+ // of m:
+ //
+ // type T interface{
+ // m() interface{ T }
+ // }
+ //
+ buf.WriteString("interface{")
+ if gcCompatibilityMode {
+ // print flattened interface
+ // (useful to compare against gc-generated interfaces)
+ for i, m := range t.allMethods {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ buf.WriteString(m.name)
+ writeSignature(buf, m.typ.(*Signature), qf, visited)
+ }
+ } else {
+ // print explicit interface methods and embedded types
+ for i, m := range t.methods {
+ if i > 0 {
+ buf.WriteString("; ")
+ }
+ buf.WriteString(m.name)
+ writeSignature(buf, m.typ.(*Signature), qf, visited)
+ }
+ for i, typ := range t.embeddeds {
+ if i > 0 || len(t.methods) > 0 {
+ buf.WriteString("; ")
+ }
+ writeType(buf, typ, qf, visited)
+ }
+ }
+ buf.WriteByte('}')
+
+ case *Map:
+ buf.WriteString("map[")
+ writeType(buf, t.key, qf, visited)
+ buf.WriteByte(']')
+ writeType(buf, t.elem, qf, visited)
+
+ case *Chan:
+ var s string
+ var parens bool
+ switch t.dir {
+ case SendRecv:
+ s = "chan "
+ // chan (<-chan T) requires parentheses
+ if c, _ := t.elem.(*Chan); c != nil && c.dir == RecvOnly {
+ parens = true
+ }
+ case SendOnly:
+ s = "chan<- "
+ case RecvOnly:
+ s = "<-chan "
+ default:
+ panic("unreachable")
+ }
+ buf.WriteString(s)
+ if parens {
+ buf.WriteByte('(')
+ }
+ writeType(buf, t.elem, qf, visited)
+ if parens {
+ buf.WriteByte(')')
+ }
+
+ case *Named:
+ s := "<Named w/o object>"
+ if obj := t.obj; obj != nil {
+ if obj.pkg != nil {
+ writePackage(buf, obj.pkg, qf)
+ }
+ // TODO(gri): function-local named types should be displayed
+ // differently from named types at package level to avoid
+ // ambiguity.
+ s = obj.name
+ }
+ buf.WriteString(s)
+
+ default:
+ // For externally defined implementations of Type.
+ buf.WriteString(t.String())
+ }
+}
+
+func writeTuple(buf *bytes.Buffer, tup *Tuple, variadic bool, qf Qualifier, visited []Type) {
+ buf.WriteByte('(')
+ if tup != nil {
+ for i, v := range tup.vars {
+ if i > 0 {
+ buf.WriteString(", ")
+ }
+ if v.name != "" {
+ buf.WriteString(v.name)
+ buf.WriteByte(' ')
+ }
+ typ := v.typ
+ if variadic && i == len(tup.vars)-1 {
+ if s, ok := typ.(*Slice); ok {
+ buf.WriteString("...")
+ typ = s.elem
+ } else {
+ // special case:
+ // append(s, "foo"...) leads to signature func([]byte, string...)
+ if t, ok := typ.Underlying().(*Basic); !ok || t.kind != String {
+ panic("internal error: string type expected")
+ }
+ writeType(buf, typ, qf, visited)
+ buf.WriteString("...")
+ continue
+ }
+ }
+ writeType(buf, typ, qf, visited)
+ }
+ }
+ buf.WriteByte(')')
+}
+
+// WriteSignature writes the representation of the signature sig to buf,
+// without a leading "func" keyword.
+// The Qualifier controls the printing of
+// package-level objects, and may be nil.
+func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) {
+ writeSignature(buf, sig, qf, make([]Type, 8))
+}
+
+func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) {
+ writeTuple(buf, sig.params, sig.variadic, qf, visited)
+
+ n := sig.results.Len()
+ if n == 0 {
+ // no result
+ return
+ }
+
+ buf.WriteByte(' ')
+ if n == 1 && sig.results.vars[0].name == "" {
+ // single unnamed result
+ writeType(buf, sig.results.vars[0].typ, qf, visited)
+ return
+ }
+
+ // multiple or named result(s)
+ writeTuple(buf, sig.results, false, qf, visited)
+}
diff --git a/libgo/go/go/types/typestring_test.go b/libgo/go/go/types/typestring_test.go
new file mode 100644
index 00000000000..913e6c735cc
--- /dev/null
+++ b/libgo/go/go/types/typestring_test.go
@@ -0,0 +1,168 @@
+// Copyright 2012 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_test
+
+import (
+ "go/ast"
+ "go/importer"
+ "go/parser"
+ "go/token"
+ "internal/testenv"
+ "testing"
+
+ . "go/types"
+)
+
+const filename = "<src>"
+
+func makePkg(t *testing.T, src string) (*Package, error) {
+ fset := token.NewFileSet()
+ file, err := parser.ParseFile(fset, filename, src, parser.DeclarationErrors)
+ if err != nil {
+ return nil, err
+ }
+ // use the package name as package path
+ conf := Config{Importer: importer.Default()}
+ return conf.Check(file.Name.Name, fset, []*ast.File{file}, nil)
+}
+
+type testEntry struct {
+ src, str string
+}
+
+// dup returns a testEntry where both src and str are the same.
+func dup(s string) testEntry {
+ return testEntry{s, s}
+}
+
+// types that don't depend on any other type declarations
+var independentTestTypes = []testEntry{
+ // basic types
+ dup("int"),
+ dup("float32"),
+ dup("string"),
+
+ // arrays
+ dup("[10]int"),
+
+ // slices
+ dup("[]int"),
+ dup("[][]int"),
+
+ // structs
+ dup("struct{}"),
+ dup("struct{x int}"),
+ {`struct {
+ x, y int
+ z float32 "foo"
+ }`, `struct{x int; y int; z float32 "foo"}`},
+ {`struct {
+ string
+ elems []complex128
+ }`, `struct{string; elems []complex128}`},
+
+ // pointers
+ dup("*int"),
+ dup("***struct{}"),
+ dup("*struct{a int; b float32}"),
+
+ // functions
+ dup("func()"),
+ dup("func(x int)"),
+ {"func(x, y int)", "func(x int, y int)"},
+ {"func(x, y int, z string)", "func(x int, y int, z string)"},
+ dup("func(int)"),
+ {"func(int, string, byte)", "func(int, string, byte)"},
+
+ dup("func() int"),
+ {"func() (string)", "func() string"},
+ dup("func() (u int)"),
+ {"func() (u, v int, w string)", "func() (u int, v int, w string)"},
+
+ dup("func(int) string"),
+ dup("func(x int) string"),
+ dup("func(x int) (u string)"),
+ {"func(x, y int) (u string)", "func(x int, y int) (u string)"},
+
+ dup("func(...int) string"),
+ dup("func(x ...int) string"),
+ dup("func(x ...int) (u string)"),
+ {"func(x, y ...int) (u string)", "func(x int, y ...int) (u string)"},
+
+ // interfaces
+ dup("interface{}"),
+ dup("interface{m()}"),
+ dup(`interface{String() string; m(int) float32}`),
+
+ // maps
+ dup("map[string]int"),
+ {"map[struct{x, y int}][]byte", "map[struct{x int; y int}][]byte"},
+
+ // channels
+ dup("chan<- chan int"),
+ dup("chan<- <-chan int"),
+ dup("<-chan <-chan int"),
+ dup("chan (<-chan int)"),
+ dup("chan<- func()"),
+ dup("<-chan []func() int"),
+}
+
+// types that depend on other type declarations (src in TestTypes)
+var dependentTestTypes = []testEntry{
+ // interfaces
+ dup(`interface{io.Reader; io.Writer}`),
+ dup(`interface{m() int; io.Writer}`),
+ {`interface{m() interface{T}}`, `interface{m() interface{p.T}}`},
+}
+
+func TestTypeString(t *testing.T) {
+ testenv.MustHaveGoBuild(t)
+
+ var tests []testEntry
+ tests = append(tests, independentTestTypes...)
+ tests = append(tests, dependentTestTypes...)
+
+ for _, test := range tests {
+ src := `package p; import "io"; type _ io.Writer; type T ` + test.src
+ pkg, err := makePkg(t, src)
+ if err != nil {
+ t.Errorf("%s: %s", src, err)
+ continue
+ }
+ typ := pkg.Scope().Lookup("T").Type().Underlying()
+ if got := typ.String(); got != test.str {
+ t.Errorf("%s: got %s, want %s", test.src, got, test.str)
+ }
+ }
+}
+
+func TestQualifiedTypeString(t *testing.T) {
+ p, _ := pkgFor("p.go", "package p; type T int", nil)
+ q, _ := pkgFor("q.go", "package q", nil)
+
+ pT := p.Scope().Lookup("T").Type()
+ for _, test := range []struct {
+ typ Type
+ this *Package
+ want string
+ }{
+ {pT, nil, "p.T"},
+ {pT, p, "T"},
+ {pT, q, "p.T"},
+ {NewPointer(pT), p, "*T"},
+ {NewPointer(pT), q, "*p.T"},
+ } {
+ qualifier := func(pkg *Package) string {
+ if pkg != test.this {
+ return pkg.Name()
+ }
+ return ""
+ }
+ if got := TypeString(test.typ, qualifier); got != test.want {
+ t.Errorf("TypeString(%s, %s) = %s, want %s",
+ test.this, test.typ, got, test.want)
+ }
+ }
+}
diff --git a/libgo/go/go/types/typexpr.go b/libgo/go/go/types/typexpr.go
new file mode 100644
index 00000000000..c744eeaa0c7
--- /dev/null
+++ b/libgo/go/go/types/typexpr.go
@@ -0,0 +1,712 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements type-checking of identifiers and type expressions.
+
+package types
+
+import (
+ "go/ast"
+ "go/constant"
+ "go/token"
+ "sort"
+ "strconv"
+)
+
+// ident type-checks identifier e and initializes x with the value or type of e.
+// If an error occurred, x.mode is set to invalid.
+// For the meaning of def and path, see check.typ, below.
+//
+func (check *Checker) ident(x *operand, e *ast.Ident, def *Named, path []*TypeName) {
+ x.mode = invalid
+ x.expr = e
+
+ scope, obj := check.scope.LookupParent(e.Name, check.pos)
+ if obj == nil {
+ if e.Name == "_" {
+ check.errorf(e.Pos(), "cannot use _ as value or type")
+ } else {
+ check.errorf(e.Pos(), "undeclared name: %s", e.Name)
+ }
+ return
+ }
+ check.recordUse(e, obj)
+
+ check.objDecl(obj, def, path)
+ typ := obj.Type()
+ assert(typ != nil)
+
+ // The object may be dot-imported: If so, remove its package from
+ // the map of unused dot imports for the respective file scope.
+ // (This code is only needed for dot-imports. Without them,
+ // we only have to mark variables, see *Var case below).
+ if pkg := obj.Pkg(); pkg != check.pkg && pkg != nil {
+ delete(check.unusedDotImports[scope], pkg)
+ }
+
+ switch obj := obj.(type) {
+ case *PkgName:
+ check.errorf(e.Pos(), "use of package %s not in selector", obj.name)
+ return
+
+ case *Const:
+ check.addDeclDep(obj)
+ if typ == Typ[Invalid] {
+ return
+ }
+ if obj == universeIota {
+ if check.iota == nil {
+ check.errorf(e.Pos(), "cannot use iota outside constant declaration")
+ return
+ }
+ x.val = check.iota
+ } else {
+ x.val = obj.val
+ }
+ assert(x.val != nil)
+ x.mode = constant_
+
+ case *TypeName:
+ x.mode = typexpr
+ // check for cycle
+ // (it's ok to iterate forward because each named type appears at most once in path)
+ for i, prev := range path {
+ if prev == obj {
+ check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name)
+ // print cycle
+ for _, obj := range path[i:] {
+ check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
+ }
+ check.errorf(obj.Pos(), "\t%s", obj.Name())
+ // maintain x.mode == typexpr despite error
+ typ = Typ[Invalid]
+ break
+ }
+ }
+
+ case *Var:
+ if obj.pkg == check.pkg {
+ obj.used = true
+ }
+ check.addDeclDep(obj)
+ if typ == Typ[Invalid] {
+ return
+ }
+ x.mode = variable
+
+ case *Func:
+ check.addDeclDep(obj)
+ x.mode = value
+
+ case *Builtin:
+ x.id = obj.id
+ x.mode = builtin
+
+ case *Nil:
+ x.mode = value
+
+ default:
+ unreachable()
+ }
+
+ x.typ = typ
+}
+
+// typExpr type-checks the type expression e and returns its type, or Typ[Invalid].
+// If def != nil, e is the type specification for the named type def, declared
+// in a type declaration, and def.underlying will be set to the type of e before
+// any components of e are type-checked. Path contains the path of named types
+// referring to this type.
+//
+func (check *Checker) typExpr(e ast.Expr, def *Named, path []*TypeName) (T Type) {
+ if trace {
+ check.trace(e.Pos(), "%s", e)
+ check.indent++
+ defer func() {
+ check.indent--
+ check.trace(e.Pos(), "=> %s", T)
+ }()
+ }
+
+ T = check.typExprInternal(e, def, path)
+ assert(isTyped(T))
+ check.recordTypeAndValue(e, typexpr, T, nil)
+
+ return
+}
+
+func (check *Checker) typ(e ast.Expr) Type {
+ return check.typExpr(e, nil, nil)
+}
+
+// funcType type-checks a function or method type.
+func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast.FuncType) {
+ scope := NewScope(check.scope, token.NoPos, token.NoPos, "function")
+ check.recordScope(ftyp, scope)
+
+ recvList, _ := check.collectParams(scope, recvPar, false)
+ params, variadic := check.collectParams(scope, ftyp.Params, true)
+ results, _ := check.collectParams(scope, ftyp.Results, false)
+
+ if recvPar != nil {
+ // recv parameter list present (may be empty)
+ // spec: "The receiver is specified via an extra parameter section preceding the
+ // method name. That parameter section must declare a single parameter, the receiver."
+ var recv *Var
+ switch len(recvList) {
+ case 0:
+ check.error(recvPar.Pos(), "method is missing receiver")
+ recv = NewParam(0, nil, "", Typ[Invalid]) // ignore recv below
+ default:
+ // more than one receiver
+ check.error(recvList[len(recvList)-1].Pos(), "method must have exactly one receiver")
+ fallthrough // continue with first receiver
+ case 1:
+ recv = recvList[0]
+ }
+ // spec: "The receiver type must be of the form T or *T where T is a type name."
+ // (ignore invalid types - error was reported before)
+ if t, _ := deref(recv.typ); t != Typ[Invalid] {
+ var err string
+ if T, _ := t.(*Named); T != nil {
+ // spec: "The type denoted by T is called the receiver base type; it must not
+ // be a pointer or interface type and it must be declared in the same package
+ // as the method."
+ if T.obj.pkg != check.pkg {
+ err = "type not defined in this package"
+ } else {
+ // TODO(gri) This is not correct if the underlying type is unknown yet.
+ switch u := T.underlying.(type) {
+ case *Basic:
+ // unsafe.Pointer is treated like a regular pointer
+ if u.kind == UnsafePointer {
+ err = "unsafe.Pointer"
+ }
+ case *Pointer, *Interface:
+ err = "pointer or interface type"
+ }
+ }
+ } else {
+ err = "basic or unnamed type"
+ }
+ if err != "" {
+ check.errorf(recv.pos, "invalid receiver %s (%s)", recv.typ, err)
+ // ok to continue
+ }
+ }
+ sig.recv = recv
+ }
+
+ sig.scope = scope
+ sig.params = NewTuple(params...)
+ sig.results = NewTuple(results...)
+ sig.variadic = variadic
+}
+
+// typExprInternal drives type checking of types.
+// Must only be called by typExpr.
+//
+func (check *Checker) typExprInternal(e ast.Expr, def *Named, path []*TypeName) Type {
+ switch e := e.(type) {
+ case *ast.BadExpr:
+ // ignore - error reported before
+
+ case *ast.Ident:
+ var x operand
+ check.ident(&x, e, def, path)
+
+ switch x.mode {
+ case typexpr:
+ typ := x.typ
+ def.setUnderlying(typ)
+ return typ
+ case invalid:
+ // ignore - error reported before
+ case novalue:
+ check.errorf(x.pos(), "%s used as type", &x)
+ default:
+ check.errorf(x.pos(), "%s is not a type", &x)
+ }
+
+ case *ast.SelectorExpr:
+ var x operand
+ check.selector(&x, e)
+
+ switch x.mode {
+ case typexpr:
+ typ := x.typ
+ def.setUnderlying(typ)
+ return typ
+ case invalid:
+ // ignore - error reported before
+ case novalue:
+ check.errorf(x.pos(), "%s used as type", &x)
+ default:
+ check.errorf(x.pos(), "%s is not a type", &x)
+ }
+
+ case *ast.ParenExpr:
+ return check.typExpr(e.X, def, path)
+
+ case *ast.ArrayType:
+ if e.Len != nil {
+ typ := new(Array)
+ def.setUnderlying(typ)
+ typ.len = check.arrayLength(e.Len)
+ typ.elem = check.typExpr(e.Elt, nil, path)
+ return typ
+
+ } else {
+ typ := new(Slice)
+ def.setUnderlying(typ)
+ typ.elem = check.typ(e.Elt)
+ return typ
+ }
+
+ case *ast.StructType:
+ typ := new(Struct)
+ def.setUnderlying(typ)
+ check.structType(typ, e, path)
+ return typ
+
+ case *ast.StarExpr:
+ typ := new(Pointer)
+ def.setUnderlying(typ)
+ typ.base = check.typ(e.X)
+ return typ
+
+ case *ast.FuncType:
+ typ := new(Signature)
+ def.setUnderlying(typ)
+ check.funcType(typ, nil, e)
+ return typ
+
+ case *ast.InterfaceType:
+ typ := new(Interface)
+ def.setUnderlying(typ)
+ check.interfaceType(typ, e, def, path)
+ return typ
+
+ case *ast.MapType:
+ typ := new(Map)
+ def.setUnderlying(typ)
+
+ typ.key = check.typ(e.Key)
+ typ.elem = check.typ(e.Value)
+
+ // spec: "The comparison operators == and != must be fully defined
+ // for operands of the key type; thus the key type must not be a
+ // function, map, or slice."
+ //
+ // Delay this check because it requires fully setup types;
+ // it is safe to continue in any case (was issue 6667).
+ check.delay(func() {
+ if !Comparable(typ.key) {
+ check.errorf(e.Key.Pos(), "invalid map key type %s", typ.key)
+ }
+ })
+
+ return typ
+
+ case *ast.ChanType:
+ typ := new(Chan)
+ def.setUnderlying(typ)
+
+ dir := SendRecv
+ switch e.Dir {
+ case ast.SEND | ast.RECV:
+ // nothing to do
+ case ast.SEND:
+ dir = SendOnly
+ case ast.RECV:
+ dir = RecvOnly
+ default:
+ check.invalidAST(e.Pos(), "unknown channel direction %d", e.Dir)
+ // ok to continue
+ }
+
+ typ.dir = dir
+ typ.elem = check.typ(e.Value)
+ return typ
+
+ default:
+ check.errorf(e.Pos(), "%s is not a type", e)
+ }
+
+ typ := Typ[Invalid]
+ def.setUnderlying(typ)
+ return typ
+}
+
+// typeOrNil type-checks the type expression (or nil value) e
+// and returns the typ of e, or nil.
+// If e is neither a type nor nil, typOrNil returns Typ[Invalid].
+//
+func (check *Checker) typOrNil(e ast.Expr) Type {
+ var x operand
+ check.rawExpr(&x, e, nil)
+ switch x.mode {
+ case invalid:
+ // ignore - error reported before
+ case novalue:
+ check.errorf(x.pos(), "%s used as type", &x)
+ case typexpr:
+ return x.typ
+ case value:
+ if x.isNil() {
+ return nil
+ }
+ fallthrough
+ default:
+ check.errorf(x.pos(), "%s is not a type", &x)
+ }
+ return Typ[Invalid]
+}
+
+func (check *Checker) arrayLength(e ast.Expr) int64 {
+ var x operand
+ check.expr(&x, e)
+ if x.mode != constant_ {
+ if x.mode != invalid {
+ check.errorf(x.pos(), "array length %s must be constant", &x)
+ }
+ return 0
+ }
+ if !x.isInteger() {
+ check.errorf(x.pos(), "array length %s must be integer", &x)
+ return 0
+ }
+ n, ok := constant.Int64Val(x.val)
+ if !ok || n < 0 {
+ check.errorf(x.pos(), "invalid array length %s", &x)
+ return 0
+ }
+ return n
+}
+
+func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) {
+ if list == nil {
+ return
+ }
+
+ var named, anonymous bool
+ for i, field := range list.List {
+ ftype := field.Type
+ if t, _ := ftype.(*ast.Ellipsis); t != nil {
+ ftype = t.Elt
+ if variadicOk && i == len(list.List)-1 {
+ variadic = true
+ } else {
+ check.invalidAST(field.Pos(), "... not permitted")
+ // ignore ... and continue
+ }
+ }
+ typ := check.typ(ftype)
+ // The parser ensures that f.Tag is nil and we don't
+ // care if a constructed AST contains a non-nil tag.
+ if len(field.Names) > 0 {
+ // named parameter
+ for _, name := range field.Names {
+ if name.Name == "" {
+ check.invalidAST(name.Pos(), "anonymous parameter")
+ // ok to continue
+ }
+ par := NewParam(name.Pos(), check.pkg, name.Name, typ)
+ check.declare(scope, name, par, scope.pos)
+ params = append(params, par)
+ }
+ named = true
+ } else {
+ // anonymous parameter
+ par := NewParam(ftype.Pos(), check.pkg, "", typ)
+ check.recordImplicit(field, par)
+ params = append(params, par)
+ anonymous = true
+ }
+ }
+
+ if named && anonymous {
+ check.invalidAST(list.Pos(), "list contains both named and anonymous parameters")
+ // ok to continue
+ }
+
+ // For a variadic function, change the last parameter's type from T to []T.
+ if variadic && len(params) > 0 {
+ last := params[len(params)-1]
+ last.typ = &Slice{elem: last.typ}
+ }
+
+ return
+}
+
+func (check *Checker) declareInSet(oset *objset, pos token.Pos, obj Object) bool {
+ if alt := oset.insert(obj); alt != nil {
+ check.errorf(pos, "%s redeclared", obj.Name())
+ check.reportAltDecl(alt)
+ return false
+ }
+ return true
+}
+
+func (check *Checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, def *Named, path []*TypeName) {
+ // empty interface: common case
+ if ityp.Methods == nil {
+ return
+ }
+
+ // The parser ensures that field tags are nil and we don't
+ // care if a constructed AST contains non-nil tags.
+
+ // use named receiver type if available (for better error messages)
+ var recvTyp Type = iface
+ if def != nil {
+ recvTyp = def
+ }
+
+ // Phase 1: Collect explicitly declared methods, the corresponding
+ // signature (AST) expressions, and the list of embedded
+ // type (AST) expressions. Do not resolve signatures or
+ // embedded types yet to avoid cycles referring to this
+ // interface.
+
+ var (
+ mset objset
+ signatures []ast.Expr // list of corresponding method signatures
+ embedded []ast.Expr // list of embedded types
+ )
+ for _, f := range ityp.Methods.List {
+ if len(f.Names) > 0 {
+ // The parser ensures that there's only one method
+ // and we don't care if a constructed AST has more.
+ name := f.Names[0]
+ pos := name.Pos()
+ // spec: "As with all method sets, in an interface type,
+ // each method must have a unique non-blank name."
+ if name.Name == "_" {
+ check.errorf(pos, "invalid method name _")
+ continue
+ }
+ // Don't type-check signature yet - use an
+ // empty signature now and update it later.
+ // Since we know the receiver, set it up now
+ // (required to avoid crash in ptrRecv; see
+ // e.g. test case for issue 6638).
+ // TODO(gri) Consider marking methods signatures
+ // as incomplete, for better error messages. See
+ // also the T4 and T5 tests in testdata/cycles2.src.
+ sig := new(Signature)
+ sig.recv = NewVar(pos, check.pkg, "", recvTyp)
+ m := NewFunc(pos, check.pkg, name.Name, sig)
+ if check.declareInSet(&mset, pos, m) {
+ iface.methods = append(iface.methods, m)
+ iface.allMethods = append(iface.allMethods, m)
+ signatures = append(signatures, f.Type)
+ check.recordDef(name, m)
+ }
+ } else {
+ // embedded type
+ embedded = append(embedded, f.Type)
+ }
+ }
+
+ // Phase 2: Resolve embedded interfaces. Because an interface must not
+ // embed itself (directly or indirectly), each embedded interface
+ // can be fully resolved without depending on any method of this
+ // interface (if there is a cycle or another error, the embedded
+ // type resolves to an invalid type and is ignored).
+ // In particular, the list of methods for each embedded interface
+ // must be complete (it cannot depend on this interface), and so
+ // those methods can be added to the list of all methods of this
+ // interface.
+
+ for _, e := range embedded {
+ pos := e.Pos()
+ typ := check.typExpr(e, nil, path)
+ // Determine underlying embedded (possibly incomplete) type
+ // by following its forward chain.
+ named, _ := typ.(*Named)
+ under := underlying(named)
+ embed, _ := under.(*Interface)
+ if embed == nil {
+ if typ != Typ[Invalid] {
+ check.errorf(pos, "%s is not an interface", typ)
+ }
+ continue
+ }
+ iface.embeddeds = append(iface.embeddeds, named)
+ // collect embedded methods
+ for _, m := range embed.allMethods {
+ if check.declareInSet(&mset, pos, m) {
+ iface.allMethods = append(iface.allMethods, m)
+ }
+ }
+ }
+
+ // Phase 3: At this point all methods have been collected for this interface.
+ // It is now safe to type-check the signatures of all explicitly
+ // declared methods, even if they refer to this interface via a cycle
+ // and embed the methods of this interface in a parameter of interface
+ // type.
+
+ for i, m := range iface.methods {
+ expr := signatures[i]
+ typ := check.typ(expr)
+ sig, _ := typ.(*Signature)
+ if sig == nil {
+ if typ != Typ[Invalid] {
+ check.invalidAST(expr.Pos(), "%s is not a method signature", typ)
+ }
+ continue // keep method with empty method signature
+ }
+ // update signature, but keep recv that was set up before
+ old := m.typ.(*Signature)
+ sig.recv = old.recv
+ *old = *sig // update signature (don't replace it!)
+ }
+
+ // TODO(gri) The list of explicit methods is only sorted for now to
+ // produce the same Interface as NewInterface. We may be able to
+ // claim source order in the future. Revisit.
+ sort.Sort(byUniqueMethodName(iface.methods))
+
+ // TODO(gri) The list of embedded types is only sorted for now to
+ // produce the same Interface as NewInterface. We may be able to
+ // claim source order in the future. Revisit.
+ sort.Sort(byUniqueTypeName(iface.embeddeds))
+
+ sort.Sort(byUniqueMethodName(iface.allMethods))
+}
+
+// byUniqueTypeName named type lists can be sorted by their unique type names.
+type byUniqueTypeName []*Named
+
+func (a byUniqueTypeName) Len() int { return len(a) }
+func (a byUniqueTypeName) Less(i, j int) bool { return a[i].obj.Id() < a[j].obj.Id() }
+func (a byUniqueTypeName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+// byUniqueMethodName method lists can be sorted by their unique method names.
+type byUniqueMethodName []*Func
+
+func (a byUniqueMethodName) Len() int { return len(a) }
+func (a byUniqueMethodName) Less(i, j int) bool { return a[i].Id() < a[j].Id() }
+func (a byUniqueMethodName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+
+func (check *Checker) tag(t *ast.BasicLit) string {
+ if t != nil {
+ if t.Kind == token.STRING {
+ if val, err := strconv.Unquote(t.Value); err == nil {
+ return val
+ }
+ }
+ check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value)
+ }
+ return ""
+}
+
+func (check *Checker) structType(styp *Struct, e *ast.StructType, path []*TypeName) {
+ list := e.Fields
+ if list == nil {
+ return
+ }
+
+ // struct fields and tags
+ var fields []*Var
+ var tags []string
+
+ // for double-declaration checks
+ var fset objset
+
+ // current field typ and tag
+ var typ Type
+ var tag string
+ // anonymous != nil indicates an anonymous field.
+ add := func(field *ast.Field, ident *ast.Ident, anonymous *TypeName, pos token.Pos) {
+ if tag != "" && tags == nil {
+ tags = make([]string, len(fields))
+ }
+ if tags != nil {
+ tags = append(tags, tag)
+ }
+
+ name := ident.Name
+ fld := NewField(pos, check.pkg, name, typ, anonymous != nil)
+ // spec: "Within a struct, non-blank field names must be unique."
+ if name == "_" || check.declareInSet(&fset, pos, fld) {
+ fields = append(fields, fld)
+ check.recordDef(ident, fld)
+ }
+ if anonymous != nil {
+ check.recordUse(ident, anonymous)
+ }
+ }
+
+ for _, f := range list.List {
+ typ = check.typExpr(f.Type, nil, path)
+ tag = check.tag(f.Tag)
+ if len(f.Names) > 0 {
+ // named fields
+ for _, name := range f.Names {
+ add(f, name, nil, name.Pos())
+ }
+ } else {
+ // anonymous field
+ name := anonymousFieldIdent(f.Type)
+ pos := f.Type.Pos()
+ t, isPtr := deref(typ)
+ switch t := t.(type) {
+ case *Basic:
+ if t == Typ[Invalid] {
+ // error was reported before
+ continue
+ }
+ // unsafe.Pointer is treated like a regular pointer
+ if t.kind == UnsafePointer {
+ check.errorf(pos, "anonymous field type cannot be unsafe.Pointer")
+ continue
+ }
+ add(f, name, Universe.Lookup(t.name).(*TypeName), pos)
+
+ case *Named:
+ // spec: "An embedded type must be specified as a type name
+ // T or as a pointer to a non-interface type name *T, and T
+ // itself may not be a pointer type."
+ switch u := t.underlying.(type) {
+ case *Basic:
+ // unsafe.Pointer is treated like a regular pointer
+ if u.kind == UnsafePointer {
+ check.errorf(pos, "anonymous field type cannot be unsafe.Pointer")
+ continue
+ }
+ case *Pointer:
+ check.errorf(pos, "anonymous field type cannot be a pointer")
+ continue
+ case *Interface:
+ if isPtr {
+ check.errorf(pos, "anonymous field type cannot be a pointer to an interface")
+ continue
+ }
+ }
+ add(f, name, t.obj, pos)
+
+ default:
+ check.invalidAST(pos, "anonymous field type %s must be named", typ)
+ }
+ }
+ }
+
+ styp.fields = fields
+ styp.tags = tags
+}
+
+func anonymousFieldIdent(e ast.Expr) *ast.Ident {
+ switch e := e.(type) {
+ case *ast.Ident:
+ return e
+ case *ast.StarExpr:
+ return anonymousFieldIdent(e.X)
+ case *ast.SelectorExpr:
+ return e.Sel
+ }
+ return nil // invalid anonymous field
+}
diff --git a/libgo/go/go/types/universe.go b/libgo/go/go/types/universe.go
new file mode 100644
index 00000000000..40185c1ad42
--- /dev/null
+++ b/libgo/go/go/types/universe.go
@@ -0,0 +1,223 @@
+// 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 sets up the universe scope and the unsafe package.
+
+package types
+
+import (
+ "go/constant"
+ "go/token"
+ "strings"
+)
+
+var (
+ Universe *Scope
+ Unsafe *Package
+ universeIota *Const
+ universeByte *Basic // uint8 alias, but has name "byte"
+ universeRune *Basic // int32 alias, but has name "rune"
+)
+
+var Typ = []*Basic{
+ Invalid: {Invalid, 0, "invalid type"},
+
+ Bool: {Bool, IsBoolean, "bool"},
+ Int: {Int, IsInteger, "int"},
+ Int8: {Int8, IsInteger, "int8"},
+ Int16: {Int16, IsInteger, "int16"},
+ Int32: {Int32, IsInteger, "int32"},
+ Int64: {Int64, IsInteger, "int64"},
+ Uint: {Uint, IsInteger | IsUnsigned, "uint"},
+ Uint8: {Uint8, IsInteger | IsUnsigned, "uint8"},
+ Uint16: {Uint16, IsInteger | IsUnsigned, "uint16"},
+ Uint32: {Uint32, IsInteger | IsUnsigned, "uint32"},
+ Uint64: {Uint64, IsInteger | IsUnsigned, "uint64"},
+ Uintptr: {Uintptr, IsInteger | IsUnsigned, "uintptr"},
+ Float32: {Float32, IsFloat, "float32"},
+ Float64: {Float64, IsFloat, "float64"},
+ Complex64: {Complex64, IsComplex, "complex64"},
+ Complex128: {Complex128, IsComplex, "complex128"},
+ String: {String, IsString, "string"},
+ UnsafePointer: {UnsafePointer, 0, "Pointer"},
+
+ UntypedBool: {UntypedBool, IsBoolean | IsUntyped, "untyped bool"},
+ UntypedInt: {UntypedInt, IsInteger | IsUntyped, "untyped int"},
+ UntypedRune: {UntypedRune, IsInteger | IsUntyped, "untyped rune"},
+ UntypedFloat: {UntypedFloat, IsFloat | IsUntyped, "untyped float"},
+ UntypedComplex: {UntypedComplex, IsComplex | IsUntyped, "untyped complex"},
+ UntypedString: {UntypedString, IsString | IsUntyped, "untyped string"},
+ UntypedNil: {UntypedNil, IsUntyped, "untyped nil"},
+}
+
+var aliases = [...]*Basic{
+ {Byte, IsInteger | IsUnsigned, "byte"},
+ {Rune, IsInteger, "rune"},
+}
+
+func defPredeclaredTypes() {
+ for _, t := range Typ {
+ def(NewTypeName(token.NoPos, nil, t.name, t))
+ }
+ for _, t := range aliases {
+ def(NewTypeName(token.NoPos, nil, t.name, t))
+ }
+
+ // Error has a nil package in its qualified name since it is in no package
+ res := NewVar(token.NoPos, nil, "", Typ[String])
+ sig := &Signature{results: NewTuple(res)}
+ err := NewFunc(token.NoPos, nil, "Error", sig)
+ typ := &Named{underlying: NewInterface([]*Func{err}, nil).Complete()}
+ sig.recv = NewVar(token.NoPos, nil, "", typ)
+ def(NewTypeName(token.NoPos, nil, "error", typ))
+}
+
+var predeclaredConsts = [...]struct {
+ name string
+ kind BasicKind
+ val constant.Value
+}{
+ {"true", UntypedBool, constant.MakeBool(true)},
+ {"false", UntypedBool, constant.MakeBool(false)},
+ {"iota", UntypedInt, constant.MakeInt64(0)},
+}
+
+func defPredeclaredConsts() {
+ for _, c := range predeclaredConsts {
+ def(NewConst(token.NoPos, nil, c.name, Typ[c.kind], c.val))
+ }
+}
+
+func defPredeclaredNil() {
+ def(&Nil{object{name: "nil", typ: Typ[UntypedNil]}})
+}
+
+// A builtinId is the id of a builtin function.
+type builtinId int
+
+const (
+ // universe scope
+ _Append builtinId = iota
+ _Cap
+ _Close
+ _Complex
+ _Copy
+ _Delete
+ _Imag
+ _Len
+ _Make
+ _New
+ _Panic
+ _Print
+ _Println
+ _Real
+ _Recover
+
+ // package unsafe
+ _Alignof
+ _Offsetof
+ _Sizeof
+
+ // testing support
+ _Assert
+ _Trace
+)
+
+var predeclaredFuncs = [...]struct {
+ name string
+ nargs int
+ variadic bool
+ kind exprKind
+}{
+ _Append: {"append", 1, true, expression},
+ _Cap: {"cap", 1, false, expression},
+ _Close: {"close", 1, false, statement},
+ _Complex: {"complex", 2, false, expression},
+ _Copy: {"copy", 2, false, statement},
+ _Delete: {"delete", 2, false, statement},
+ _Imag: {"imag", 1, false, expression},
+ _Len: {"len", 1, false, expression},
+ _Make: {"make", 1, true, expression},
+ _New: {"new", 1, false, expression},
+ _Panic: {"panic", 1, false, statement},
+ _Print: {"print", 0, true, statement},
+ _Println: {"println", 0, true, statement},
+ _Real: {"real", 1, false, expression},
+ _Recover: {"recover", 0, false, statement},
+
+ _Alignof: {"Alignof", 1, false, expression},
+ _Offsetof: {"Offsetof", 1, false, expression},
+ _Sizeof: {"Sizeof", 1, false, expression},
+
+ _Assert: {"assert", 1, false, statement},
+ _Trace: {"trace", 0, true, statement},
+}
+
+func defPredeclaredFuncs() {
+ for i := range predeclaredFuncs {
+ id := builtinId(i)
+ if id == _Assert || id == _Trace {
+ continue // only define these in testing environment
+ }
+ def(newBuiltin(id))
+ }
+}
+
+// DefPredeclaredTestFuncs defines the assert and trace built-ins.
+// These built-ins are intended for debugging and testing of this
+// package only.
+func DefPredeclaredTestFuncs() {
+ if Universe.Lookup("assert") != nil {
+ return // already defined
+ }
+ def(newBuiltin(_Assert))
+ def(newBuiltin(_Trace))
+}
+
+func init() {
+ Universe = NewScope(nil, token.NoPos, token.NoPos, "universe")
+ Unsafe = NewPackage("unsafe", "unsafe")
+ Unsafe.complete = true
+
+ defPredeclaredTypes()
+ defPredeclaredConsts()
+ defPredeclaredNil()
+ defPredeclaredFuncs()
+
+ universeIota = Universe.Lookup("iota").(*Const)
+ universeByte = Universe.Lookup("byte").(*TypeName).typ.(*Basic)
+ universeRune = Universe.Lookup("rune").(*TypeName).typ.(*Basic)
+}
+
+// Objects with names containing blanks are internal and not entered into
+// a scope. Objects with exported names are inserted in the unsafe package
+// scope; other objects are inserted in the universe scope.
+//
+func def(obj Object) {
+ name := obj.Name()
+ if strings.Index(name, " ") >= 0 {
+ return // nothing to do
+ }
+ // fix Obj link for named types
+ if typ, ok := obj.Type().(*Named); ok {
+ typ.obj = obj.(*TypeName)
+ }
+ // exported identifiers go into package unsafe
+ scope := Universe
+ if obj.Exported() {
+ scope = Unsafe.scope
+ // set Pkg field
+ switch obj := obj.(type) {
+ case *TypeName:
+ obj.pkg = Unsafe
+ case *Builtin:
+ obj.pkg = Unsafe
+ default:
+ unreachable()
+ }
+ }
+ if scope.Insert(obj) != nil {
+ panic("internal error: double declaration")
+ }
+}