tools: switch to standard go/types at tip
A few files have been forked and tagged "go1.5,!go1.6" to work around
minor API changes between the two types packages:
- constant.Value.String() in oracle/describe.go and its tests;
- constant.ToInt must now be called before constant.Int64Val.
- types.Config{Importer: importer.Default()} in a number of places
- go/types/typeutil/import_test.go uses lowercase names to avoid 'import "C"'.
Files in go/types/typesutil, missing from my previous CL, have been
tagged !go1.5; these files will be deleted in February.
All affected packages were tested using 1.4.1, 1.5, and ~1.6 (tip).
Change-Id: Iec7fd370e1434508149b378438fb37f65b8d2ba8
Reviewed-on: https://go-review.googlesource.com/18207
Reviewed-by: Robert Griesemer <gri@golang.org>
diff --git a/go/callgraph/cha/cha.go b/go/callgraph/cha/cha.go
index 962f919..e016649 100644
--- a/go/callgraph/cha/cha.go
+++ b/go/callgraph/cha/cha.go
@@ -26,10 +26,11 @@
package cha // import "golang.org/x/tools/go/callgraph/cha"
import (
+ "go/types"
+
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/callgraph/cha/cha_test.go b/go/callgraph/cha/cha_test.go
index a558074..332758c 100644
--- a/go/callgraph/cha/cha_test.go
+++ b/go/callgraph/cha/cha_test.go
@@ -16,6 +16,7 @@
"go/ast"
"go/parser"
"go/token"
+ "go/types"
"io/ioutil"
"sort"
"strings"
@@ -25,7 +26,6 @@
"golang.org/x/tools/go/callgraph/cha"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
)
var inputs = []string{
diff --git a/go/callgraph/rta/rta.go b/go/callgraph/rta/rta.go
index 0d0b0d3..7c9379d 100644
--- a/go/callgraph/rta/rta.go
+++ b/go/callgraph/rta/rta.go
@@ -50,10 +50,10 @@
import (
"fmt"
+ "go/types"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/callgraph/rta/rta_test.go b/go/callgraph/rta/rta_test.go
index d37cc5c..5046521 100644
--- a/go/callgraph/rta/rta_test.go
+++ b/go/callgraph/rta/rta_test.go
@@ -16,6 +16,7 @@
"go/ast"
"go/parser"
"go/token"
+ "go/types"
"io/ioutil"
"sort"
"strings"
@@ -26,7 +27,6 @@
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
)
var inputs = []string{
diff --git a/go/loader/loader.go b/go/loader/loader.go
index ecdc85d..41d5957 100644
--- a/go/loader/loader.go
+++ b/go/loader/loader.go
@@ -15,6 +15,7 @@
"go/build"
"go/parser"
"go/token"
+ "go/types"
"os"
"sort"
"strings"
@@ -23,7 +24,6 @@
"golang.org/x/tools/go/ast/astutil"
"golang.org/x/tools/go/buildutil"
- "golang.org/x/tools/go/types"
)
const trace = false // show timing info for type-checking
@@ -1005,9 +1005,7 @@
if f := imp.conf.TypeCheckFuncBodies; f != nil {
tc.IgnoreFuncBodies = !f(path)
}
- tc.Import = func(_ map[string]*types.Package, to string) (*types.Package, error) {
- return imp.doImport(info, to)
- }
+ tc.Importer = closure{imp, info}
tc.Error = info.appendError // appendError wraps the user's Error function
info.checker = types.NewChecker(&tc, imp.conf.fset(), pkg, &info.Info)
@@ -1016,3 +1014,10 @@
imp.progMu.Unlock()
return info
}
+
+type closure struct {
+ imp *importer
+ info *PackageInfo
+}
+
+func (c closure) Import(to string) (*types.Package, error) { return c.imp.doImport(c.info, to) }
diff --git a/go/loader/stdlib_test.go b/go/loader/stdlib_test.go
index 23f6f01..812a4a6 100644
--- a/go/loader/stdlib_test.go
+++ b/go/loader/stdlib_test.go
@@ -16,6 +16,7 @@
"go/ast"
"go/build"
"go/token"
+ "go/types"
"io/ioutil"
"path/filepath"
"runtime"
@@ -25,7 +26,6 @@
"golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader"
- "golang.org/x/tools/go/types"
)
func TestStdlib(t *testing.T) {
diff --git a/go/pointer/analysis.go b/go/pointer/analysis.go
index 4a7439e..9f3476c 100644
--- a/go/pointer/analysis.go
+++ b/go/pointer/analysis.go
@@ -11,6 +11,7 @@
import (
"fmt"
"go/token"
+ "go/types"
"io"
"os"
"reflect"
@@ -20,7 +21,6 @@
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/pointer/constraint.go b/go/pointer/constraint.go
index 5b9265a..ea44287 100644
--- a/go/pointer/constraint.go
+++ b/go/pointer/constraint.go
@@ -6,9 +6,7 @@
package pointer
-import (
- "golang.org/x/tools/go/types"
-)
+import "go/types"
type constraint interface {
// For a complex constraint, returns the nodeid of the pointer
diff --git a/go/pointer/gen.go b/go/pointer/gen.go
index 9d550a7..405a63b 100644
--- a/go/pointer/gen.go
+++ b/go/pointer/gen.go
@@ -15,10 +15,10 @@
import (
"fmt"
"go/token"
+ "go/types"
"golang.org/x/tools/go/callgraph"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
var (
diff --git a/go/pointer/hvn.go b/go/pointer/hvn.go
index c144559..e550bc9 100644
--- a/go/pointer/hvn.go
+++ b/go/pointer/hvn.go
@@ -165,11 +165,11 @@
import (
"fmt"
+ "go/types"
"io"
"reflect"
"golang.org/x/tools/container/intsets"
- "golang.org/x/tools/go/types"
)
// A peLabel is a pointer-equivalence label: two nodes with the same
diff --git a/go/pointer/intrinsics.go b/go/pointer/intrinsics.go
index 69da377..fbfb36d 100644
--- a/go/pointer/intrinsics.go
+++ b/go/pointer/intrinsics.go
@@ -19,9 +19,9 @@
import (
"fmt"
+ "go/types"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
// Instances of 'intrinsic' generate analysis constraints for calls to
diff --git a/go/pointer/labels.go b/go/pointer/labels.go
index 4709272..bfe60d2 100644
--- a/go/pointer/labels.go
+++ b/go/pointer/labels.go
@@ -9,10 +9,10 @@
import (
"fmt"
"go/token"
+ "go/types"
"strings"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
// A Label is an entity that may be pointed to by a pointer, map,
diff --git a/go/pointer/pointer_test.go b/go/pointer/pointer_test.go
index ddfacf7..52af485 100644
--- a/go/pointer/pointer_test.go
+++ b/go/pointer/pointer_test.go
@@ -19,6 +19,7 @@
"errors"
"fmt"
"go/token"
+ "go/types"
"io/ioutil"
"os"
"regexp"
@@ -31,7 +32,6 @@
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/pointer/reflect.go b/go/pointer/reflect.go
index a0bfda6..bdb22cf 100644
--- a/go/pointer/reflect.go
+++ b/go/pointer/reflect.go
@@ -32,11 +32,11 @@
import (
"fmt"
+ exact "go/constant"
+ "go/types"
"reflect"
- "golang.org/x/tools/go/exact"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
func init() {
diff --git a/go/pointer/solve.go b/go/pointer/solve.go
index 8cff32f..3c60685 100644
--- a/go/pointer/solve.go
+++ b/go/pointer/solve.go
@@ -11,8 +11,7 @@
import (
"fmt"
-
- "golang.org/x/tools/go/types"
+ "go/types"
)
type solverState struct {
diff --git a/go/pointer/util.go b/go/pointer/util.go
index 7a43187..4d2fa74 100644
--- a/go/pointer/util.go
+++ b/go/pointer/util.go
@@ -9,6 +9,7 @@
import (
"bytes"
"fmt"
+ "go/types"
"log"
"os"
"os/exec"
@@ -16,7 +17,6 @@
"time"
"golang.org/x/tools/container/intsets"
- "golang.org/x/tools/go/types"
)
// CanPoint reports whether the type T is pointerlike,
diff --git a/go/ssa/builder.go b/go/ssa/builder.go
index 9a0a474..4707ebe 100644
--- a/go/ssa/builder.go
+++ b/go/ssa/builder.go
@@ -34,12 +34,11 @@
import (
"fmt"
"go/ast"
+ exact "go/constant"
"go/token"
+ "go/types"
"os"
"sync"
-
- "golang.org/x/tools/go/exact"
- "golang.org/x/tools/go/types"
)
type opaqueType struct {
@@ -244,7 +243,7 @@
}
if m, ok := m.(*Const); ok {
// treat make([]T, n, m) as new([m]T)[:n]
- cap, _ := exact.Int64Val(m.Value)
+ cap := m.Int64()
at := types.NewArray(typ.Underlying().(*types.Slice).Elem(), cap)
alloc := emitNew(fn, at, pos)
alloc.Comment = "makeslice"
diff --git a/go/ssa/builder_test.go b/go/ssa/builder_test.go
index f055625..fdb58ff 100644
--- a/go/ssa/builder_test.go
+++ b/go/ssa/builder_test.go
@@ -9,8 +9,10 @@
import (
"bytes"
"go/ast"
+ "go/importer"
"go/parser"
"go/token"
+ "go/types"
"reflect"
"sort"
"strings"
@@ -19,9 +21,6 @@
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
-
- _ "golang.org/x/tools/go/gcimporter"
)
func isEmpty(f *ssa.Function) bool { return f.Blocks == nil }
@@ -59,7 +58,7 @@
// Build an SSA program from the parsed file.
// Load its dependencies from gc binary export data.
- mainPkg, _, err := ssautil.BuildPackage(new(types.Config), fset,
+ mainPkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
types.NewPackage("main", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
if err != nil {
t.Error(err)
@@ -227,7 +226,7 @@
// Create a single-file main package.
// Load dependencies from gc binary export data.
- ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset,
+ ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset,
types.NewPackage("p", ""), []*ast.File{f}, ssa.SanityCheckFunctions)
if err != nil {
t.Errorf("test %q: %s", test.input[:15], err)
diff --git a/go/ssa/const.go b/go/ssa/const.go
index 3a1746d..0690463 100644
--- a/go/ssa/const.go
+++ b/go/ssa/const.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// +build go1.5
+// +build go1.6
package ssa
@@ -10,11 +10,10 @@
import (
"fmt"
+ exact "go/constant"
"go/token"
+ "go/types"
"strconv"
-
- "golang.org/x/tools/go/exact"
- "golang.org/x/tools/go/types"
)
// NewConst returns a new constant of the specified value and type.
@@ -118,11 +117,13 @@
return c.Value == nil
}
+// TODO(adonovan): move everything below into golang.org/x/tools/go/ssa/interp.
+
// Int64 returns the numeric value of this constant truncated to fit
// a signed 64-bit integer.
//
func (c *Const) Int64() int64 {
- switch x := c.Value; x.Kind() {
+ switch x := exact.ToInt(c.Value); x.Kind() {
case exact.Int:
if i, ok := exact.Int64Val(x); ok {
return i
@@ -139,7 +140,7 @@
// an unsigned 64-bit integer.
//
func (c *Const) Uint64() uint64 {
- switch x := c.Value; x.Kind() {
+ switch x := exact.ToInt(c.Value); x.Kind() {
case exact.Int:
if u, ok := exact.Uint64Val(x); ok {
return u
diff --git a/go/ssa/const15.go b/go/ssa/const15.go
new file mode 100644
index 0000000..a42b255
--- /dev/null
+++ b/go/ssa/const15.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.
+
+// +build go1.5,!go1.6
+
+package ssa
+
+// This file defines the Const SSA value type.
+
+import (
+ "fmt"
+ exact "go/constant"
+ "go/token"
+ "go/types"
+ "strconv"
+)
+
+// NewConst returns a new constant of the specified value and type.
+// val must be valid according to the specification of Const.Value.
+//
+func NewConst(val exact.Value, typ types.Type) *Const {
+ return &Const{typ, val}
+}
+
+// intConst returns an 'int' constant that evaluates to i.
+// (i is an int64 in case the host is narrower than the target.)
+func intConst(i int64) *Const {
+ return NewConst(exact.MakeInt64(i), tInt)
+}
+
+// nilConst returns a nil constant of the specified type, which may
+// be any reference type, including interfaces.
+//
+func nilConst(typ types.Type) *Const {
+ return NewConst(nil, typ)
+}
+
+// stringConst returns a 'string' constant that evaluates to s.
+func stringConst(s string) *Const {
+ return NewConst(exact.MakeString(s), tString)
+}
+
+// zeroConst returns a new "zero" constant of the specified type,
+// which must not be an array or struct type: the zero values of
+// aggregates are well-defined but cannot be represented by Const.
+//
+func zeroConst(t types.Type) *Const {
+ switch t := t.(type) {
+ case *types.Basic:
+ switch {
+ case t.Info()&types.IsBoolean != 0:
+ return NewConst(exact.MakeBool(false), t)
+ case t.Info()&types.IsNumeric != 0:
+ return NewConst(exact.MakeInt64(0), t)
+ case t.Info()&types.IsString != 0:
+ return NewConst(exact.MakeString(""), t)
+ case t.Kind() == types.UnsafePointer:
+ fallthrough
+ case t.Kind() == types.UntypedNil:
+ return nilConst(t)
+ default:
+ panic(fmt.Sprint("zeroConst for unexpected type:", t))
+ }
+ case *types.Pointer, *types.Slice, *types.Interface, *types.Chan, *types.Map, *types.Signature:
+ return nilConst(t)
+ case *types.Named:
+ return NewConst(zeroConst(t.Underlying()).Value, t)
+ case *types.Array, *types.Struct, *types.Tuple:
+ panic(fmt.Sprint("zeroConst applied to aggregate:", t))
+ }
+ panic(fmt.Sprint("zeroConst: unexpected ", t))
+}
+
+func (c *Const) RelString(from *types.Package) string {
+ var s string
+ if c.Value == nil {
+ s = "nil"
+ } else if c.Value.Kind() == exact.String {
+ s = exact.StringVal(c.Value)
+ const max = 20
+ // TODO(adonovan): don't cut a rune in half.
+ if len(s) > max {
+ s = s[:max-3] + "..." // abbreviate
+ }
+ s = strconv.Quote(s)
+ } else {
+ s = c.Value.String()
+ }
+ return s + ":" + relType(c.Type(), from)
+}
+
+func (c *Const) Name() string {
+ return c.RelString(nil)
+}
+
+func (c *Const) String() string {
+ return c.Name()
+}
+
+func (c *Const) Type() types.Type {
+ return c.typ
+}
+
+func (c *Const) Referrers() *[]Instruction {
+ return nil
+}
+
+func (c *Const) Parent() *Function { return nil }
+
+func (c *Const) Pos() token.Pos {
+ return token.NoPos
+}
+
+// IsNil returns true if this constant represents a typed or untyped nil value.
+func (c *Const) IsNil() bool {
+ return c.Value == nil
+}
+
+// TODO(adonovan): move everything below into golang.org/x/tools/go/ssa/interp.
+
+// Int64 returns the numeric value of this constant truncated to fit
+// a signed 64-bit integer.
+//
+func (c *Const) Int64() int64 {
+ switch x := c.Value; x.Kind() {
+ case exact.Int:
+ if i, ok := exact.Int64Val(x); ok {
+ return i
+ }
+ return 0
+ case exact.Float:
+ f, _ := exact.Float64Val(x)
+ return int64(f)
+ }
+ panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
+}
+
+// Uint64 returns the numeric value of this constant truncated to fit
+// an unsigned 64-bit integer.
+//
+func (c *Const) Uint64() uint64 {
+ switch x := c.Value; x.Kind() {
+ case exact.Int:
+ if u, ok := exact.Uint64Val(x); ok {
+ return u
+ }
+ return 0
+ case exact.Float:
+ f, _ := exact.Float64Val(x)
+ return uint64(f)
+ }
+ panic(fmt.Sprintf("unexpected constant value: %T", c.Value))
+}
+
+// Float64 returns the numeric value of this constant truncated to fit
+// a float64.
+//
+func (c *Const) Float64() float64 {
+ f, _ := exact.Float64Val(c.Value)
+ return f
+}
+
+// Complex128 returns the complex value of this constant truncated to
+// fit a complex128.
+//
+func (c *Const) Complex128() complex128 {
+ re, _ := exact.Float64Val(exact.Real(c.Value))
+ im, _ := exact.Float64Val(exact.Imag(c.Value))
+ return complex(re, im)
+}
diff --git a/go/ssa/create.go b/go/ssa/create.go
index b49a8be..372d1c7 100644
--- a/go/ssa/create.go
+++ b/go/ssa/create.go
@@ -13,10 +13,10 @@
"fmt"
"go/ast"
"go/token"
+ "go/types"
"os"
"sync"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/ssa/emit.go b/go/ssa/emit.go
index dd4ff93..238c070 100644
--- a/go/ssa/emit.go
+++ b/go/ssa/emit.go
@@ -12,8 +12,7 @@
"fmt"
"go/ast"
"go/token"
-
- "golang.org/x/tools/go/types"
+ "go/types"
)
// emitNew emits to f a new (heap Alloc) instruction allocating an
diff --git a/go/ssa/example_test.go b/go/ssa/example_test.go
index f25e31d..718817c 100644
--- a/go/ssa/example_test.go
+++ b/go/ssa/example_test.go
@@ -8,16 +8,16 @@
import (
"fmt"
- "os"
-
"go/ast"
+ "go/importer"
"go/parser"
"go/token"
+ "go/types"
+ "os"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
)
const hello = `
@@ -66,7 +66,7 @@
// Type-check the package, load dependencies.
// Create and build the SSA program.
hello, _, err := ssautil.BuildPackage(
- new(types.Config), fset, pkg, files, ssa.SanityCheckFunctions)
+ &types.Config{Importer: importer.Default()}, fset, pkg, files, ssa.SanityCheckFunctions)
if err != nil {
fmt.Print(err) // type error in some package
return
diff --git a/go/ssa/func.go b/go/ssa/func.go
index 7d9e8f9..88052c3 100644
--- a/go/ssa/func.go
+++ b/go/ssa/func.go
@@ -13,11 +13,10 @@
"fmt"
"go/ast"
"go/token"
+ "go/types"
"io"
"os"
"strings"
-
- "golang.org/x/tools/go/types"
)
// addEdge adds a control-flow graph edge from from to to.
diff --git a/go/ssa/interp/external.go b/go/ssa/interp/external.go
index 2425163..8ae81b2 100644
--- a/go/ssa/interp/external.go
+++ b/go/ssa/interp/external.go
@@ -10,6 +10,7 @@
// external or because they use "unsafe" or "reflect" operations.
import (
+ "go/types"
"math"
"os"
"runtime"
@@ -19,7 +20,6 @@
"unsafe"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
type externalFn func(fr *frame, args []value) value
diff --git a/go/ssa/interp/interp.go b/go/ssa/interp/interp.go
index 87e6b81..b855645 100644
--- a/go/ssa/interp/interp.go
+++ b/go/ssa/interp/interp.go
@@ -49,12 +49,12 @@
import (
"fmt"
"go/token"
+ "go/types"
"os"
"reflect"
"runtime"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
type continuation int
diff --git a/go/ssa/interp/interp_test.go b/go/ssa/interp/interp_test.go
index bcdd81c..5163816 100644
--- a/go/ssa/interp/interp_test.go
+++ b/go/ssa/interp/interp_test.go
@@ -12,6 +12,7 @@
"bytes"
"fmt"
"go/build"
+ "go/types"
"os"
"path/filepath"
"strings"
@@ -22,7 +23,6 @@
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/interp"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
)
// Each line contains a space-separated list of $GOROOT/test/
diff --git a/go/ssa/interp/map.go b/go/ssa/interp/map.go
index 6d4c3ae..4c092b3 100644
--- a/go/ssa/interp/map.go
+++ b/go/ssa/interp/map.go
@@ -14,7 +14,7 @@
// concurrent map access.
import (
- "golang.org/x/tools/go/types"
+ "go/types"
)
type hashable interface {
diff --git a/go/ssa/interp/ops.go b/go/ssa/interp/ops.go
index a76b579..c7a0a40 100644
--- a/go/ssa/interp/ops.go
+++ b/go/ssa/interp/ops.go
@@ -9,14 +9,14 @@
import (
"bytes"
"fmt"
+ exact "go/constant"
"go/token"
+ "go/types"
"strings"
"sync"
"unsafe"
- "golang.org/x/tools/go/exact"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
// If the target program panics, the interpreter panics with this type.
diff --git a/go/ssa/interp/reflect.go b/go/ssa/interp/reflect.go
index 01cd00b..48bb911 100644
--- a/go/ssa/interp/reflect.go
+++ b/go/ssa/interp/reflect.go
@@ -15,11 +15,11 @@
import (
"fmt"
"go/token"
+ "go/types"
"reflect"
"unsafe"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
type opaqueType struct {
diff --git a/go/ssa/interp/value.go b/go/ssa/interp/value.go
index 37be184..2194b01 100644
--- a/go/ssa/interp/value.go
+++ b/go/ssa/interp/value.go
@@ -38,6 +38,7 @@
import (
"bytes"
"fmt"
+ "go/types"
"io"
"reflect"
"strings"
@@ -45,7 +46,6 @@
"unsafe"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/ssa/lift.go b/go/ssa/lift.go
index d0a145d..722d086 100644
--- a/go/ssa/lift.go
+++ b/go/ssa/lift.go
@@ -46,10 +46,9 @@
import (
"fmt"
"go/token"
+ "go/types"
"math/big"
"os"
-
- "golang.org/x/tools/go/types"
)
// If true, perform sanity checking and show diagnostic information at
diff --git a/go/ssa/lvalue.go b/go/ssa/lvalue.go
index 657204d..85e090f 100644
--- a/go/ssa/lvalue.go
+++ b/go/ssa/lvalue.go
@@ -12,8 +12,7 @@
import (
"go/ast"
"go/token"
-
- "golang.org/x/tools/go/types"
+ "go/types"
)
// An lvalue represents an assignable location that may appear on the
diff --git a/go/ssa/methods.go b/go/ssa/methods.go
index ba36bbf..7d1fb42 100644
--- a/go/ssa/methods.go
+++ b/go/ssa/methods.go
@@ -10,8 +10,7 @@
import (
"fmt"
-
- "golang.org/x/tools/go/types"
+ "go/types"
)
// MethodValue returns the Function implementing method sel, building
diff --git a/go/ssa/print.go b/go/ssa/print.go
index c389616..55c9266 100644
--- a/go/ssa/print.go
+++ b/go/ssa/print.go
@@ -12,11 +12,11 @@
import (
"bytes"
"fmt"
+ "go/types"
"io"
"reflect"
"sort"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/ssa/sanity.go b/go/ssa/sanity.go
index 01e97e5..4babb37 100644
--- a/go/ssa/sanity.go
+++ b/go/ssa/sanity.go
@@ -11,11 +11,10 @@
import (
"fmt"
+ "go/types"
"io"
"os"
"strings"
-
- "golang.org/x/tools/go/types"
)
type sanity struct {
diff --git a/go/ssa/source.go b/go/ssa/source.go
index 0926c05..3a6f039 100644
--- a/go/ssa/source.go
+++ b/go/ssa/source.go
@@ -15,8 +15,7 @@
import (
"go/ast"
"go/token"
-
- "golang.org/x/tools/go/types"
+ "go/types"
)
// EnclosingFunction returns the function that contains the syntax
diff --git a/go/ssa/source_test.go b/go/ssa/source_test.go
index 9146907..3fd7ac4 100644
--- a/go/ssa/source_test.go
+++ b/go/ssa/source_test.go
@@ -11,8 +11,10 @@
import (
"fmt"
"go/ast"
+ exact "go/constant"
"go/parser"
"go/token"
+ "go/types"
"os"
"regexp"
"runtime"
@@ -20,11 +22,9 @@
"testing"
"golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/exact"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
)
func TestObjValueLookup(t *testing.T) {
diff --git a/go/ssa/ssa.go b/go/ssa/ssa.go
index 513f87f..c547af8 100644
--- a/go/ssa/ssa.go
+++ b/go/ssa/ssa.go
@@ -12,11 +12,11 @@
import (
"fmt"
"go/ast"
+ exact "go/constant"
"go/token"
+ "go/types"
"sync"
- "golang.org/x/tools/go/exact"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/ssa/ssautil/load.go b/go/ssa/ssautil/load.go
index 4061f49..7c57838 100644
--- a/go/ssa/ssautil/load.go
+++ b/go/ssa/ssautil/load.go
@@ -11,10 +11,10 @@
import (
"go/ast"
"go/token"
+ "go/types"
"golang.org/x/tools/go/loader"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
// CreateProgram returns a new program in SSA form, given a program
diff --git a/go/ssa/ssautil/load_test.go b/go/ssa/ssautil/load_test.go
index 31fe186..8ce73bc 100644
--- a/go/ssa/ssautil/load_test.go
+++ b/go/ssa/ssautil/load_test.go
@@ -8,15 +8,14 @@
import (
"go/ast"
+ "go/importer"
"go/parser"
"go/token"
+ "go/types"
"os"
"testing"
"golang.org/x/tools/go/ssa/ssautil"
- "golang.org/x/tools/go/types"
-
- _ "golang.org/x/tools/go/gcimporter"
)
const hello = `package main
@@ -39,7 +38,7 @@
}
pkg := types.NewPackage("hello", "")
- ssapkg, _, err := ssautil.BuildPackage(new(types.Config), fset, pkg, []*ast.File{f}, 0)
+ ssapkg, _, err := ssautil.BuildPackage(&types.Config{Importer: importer.Default()}, fset, pkg, []*ast.File{f}, 0)
if err != nil {
t.Fatal(err)
}
diff --git a/go/ssa/ssautil/switch.go b/go/ssa/ssautil/switch.go
index 0cbcb3d..2fcc167 100644
--- a/go/ssa/ssautil/switch.go
+++ b/go/ssa/ssautil/switch.go
@@ -24,9 +24,9 @@
"bytes"
"fmt"
"go/token"
+ "go/types"
"golang.org/x/tools/go/ssa"
- "golang.org/x/tools/go/types"
)
// A ConstCase represents a single constant comparison.
diff --git a/go/ssa/testmain.go b/go/ssa/testmain.go
index 13d7ac1..48b184a 100644
--- a/go/ssa/testmain.go
+++ b/go/ssa/testmain.go
@@ -12,13 +12,12 @@
import (
"go/ast"
+ exact "go/constant"
"go/token"
+ "go/types"
"os"
"sort"
"strings"
-
- "golang.org/x/tools/go/exact"
- "golang.org/x/tools/go/types"
)
// FindTests returns the list of packages that define at least one Test,
diff --git a/go/ssa/util.go b/go/ssa/util.go
index e323553..317a013 100644
--- a/go/ssa/util.go
+++ b/go/ssa/util.go
@@ -12,11 +12,11 @@
"fmt"
"go/ast"
"go/token"
+ "go/types"
"io"
"os"
"golang.org/x/tools/go/ast/astutil"
- "golang.org/x/tools/go/types"
)
//// AST utilities
diff --git a/go/ssa/wrappers.go b/go/ssa/wrappers.go
index 75ec596..6ca01ab 100644
--- a/go/ssa/wrappers.go
+++ b/go/ssa/wrappers.go
@@ -24,7 +24,7 @@
import (
"fmt"
- "golang.org/x/tools/go/types"
+ "go/types"
)
// -- wrappers -----------------------------------------------------------
diff --git a/go/types/typeutil/example_test.go b/go/types/typeutil/example_test.go
index 9e3ada7..fe49644 100644
--- a/go/types/typeutil/example_test.go
+++ b/go/types/typeutil/example_test.go
@@ -2,17 +2,18 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package typeutil_test
import (
"fmt"
- "sort"
-
"go/ast"
"go/parser"
"go/token"
+ "go/types"
+ "sort"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/types/typeutil/imports.go b/go/types/typeutil/imports.go
index 967fe1e..4b753f4 100644
--- a/go/types/typeutil/imports.go
+++ b/go/types/typeutil/imports.go
@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package typeutil
-import "golang.org/x/tools/go/types"
+import "go/types"
// Dependencies returns all dependencies of the specified packages.
//
diff --git a/go/types/typeutil/imports14.go b/go/types/typeutil/imports14.go
new file mode 100644
index 0000000..9741df3
--- /dev/null
+++ b/go/types/typeutil/imports14.go
@@ -0,0 +1,33 @@
+// 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.5
+
+package typeutil
+
+import "golang.org/x/tools/go/types"
+
+// Dependencies returns all dependencies of the specified packages.
+//
+// Dependent packages appear in topological order: if package P imports
+// package Q, Q appears earlier than P in the result.
+// The algorithm follows import statements in the order they
+// appear in the source code, so the result is a total order.
+//
+func Dependencies(pkgs ...*types.Package) []*types.Package {
+ var result []*types.Package
+ seen := make(map[*types.Package]bool)
+ var visit func(pkgs []*types.Package)
+ visit = func(pkgs []*types.Package) {
+ for _, p := range pkgs {
+ if !seen[p] {
+ seen[p] = true
+ visit(p.Imports())
+ result = append(result, p)
+ }
+ }
+ }
+ visit(pkgs)
+ return result
+}
diff --git a/go/types/typeutil/imports14_test.go b/go/types/typeutil/imports14_test.go
new file mode 100644
index 0000000..b70f5f0
--- /dev/null
+++ b/go/types/typeutil/imports14_test.go
@@ -0,0 +1,81 @@
+// 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.5
+
+package typeutil_test
+
+import (
+ "fmt"
+ "go/ast"
+ "go/parser"
+ "go/token"
+ "testing"
+
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+func TestDependencies(t *testing.T) {
+ packages := make(map[string]*types.Package)
+ conf := types.Config{
+ Packages: packages,
+ Import: func(_ map[string]*types.Package, path string) (*types.Package, error) {
+ return packages[path], nil
+ },
+ }
+ fset := token.NewFileSet()
+
+ // All edges go to the right.
+ // /--D--B--A
+ // F \_C_/
+ // \__E_/
+ for i, content := range []string{
+ `package a`,
+ `package c; import (_ "a")`,
+ `package b; import (_ "a")`,
+ `package e; import (_ "c")`,
+ `package d; import (_ "b"; _ "c")`,
+ `package f; import (_ "d"; _ "e")`,
+ } {
+ f, err := parser.ParseFile(fset, fmt.Sprintf("%d.go", i), content, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ pkg, err := conf.Check(f.Name.Name, fset, []*ast.File{f}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ packages[pkg.Path()] = pkg
+ }
+
+ for _, test := range []struct {
+ roots, want string
+ }{
+ {"a", "a"},
+ {"b", "ab"},
+ {"c", "ac"},
+ {"d", "abcd"},
+ {"e", "ace"},
+ {"f", "abcdef"},
+
+ {"be", "abce"},
+ {"eb", "aceb"},
+ {"de", "abcde"},
+ {"ed", "acebd"},
+ {"ef", "acebdf"},
+ } {
+ var pkgs []*types.Package
+ for _, r := range test.roots {
+ pkgs = append(pkgs, packages[string(r)])
+ }
+ var got string
+ for _, p := range typeutil.Dependencies(pkgs...) {
+ got += p.Path()
+ }
+ if got != test.want {
+ t.Errorf("Dependencies(%q) = %q, want %q", test.roots, got, test.want)
+ }
+ }
+}
diff --git a/go/types/typeutil/imports_test.go b/go/types/typeutil/imports_test.go
index 8071ae1..b846fbb 100644
--- a/go/types/typeutil/imports_test.go
+++ b/go/types/typeutil/imports_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package typeutil_test
import (
@@ -9,19 +11,20 @@
"go/ast"
"go/parser"
"go/token"
+ "go/types"
"testing"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
+type closure map[string]*types.Package
+
+func (c closure) Import(path string) (*types.Package, error) { return c[path], nil }
+
func TestDependencies(t *testing.T) {
packages := make(map[string]*types.Package)
conf := types.Config{
- Packages: packages,
- Import: func(_ map[string]*types.Package, path string) (*types.Package, error) {
- return packages[path], nil
- },
+ Importer: closure(packages),
}
fset := token.NewFileSet()
@@ -30,12 +33,12 @@
// F \_C_/
// \__E_/
for i, content := range []string{
- `package A`,
- `package C; import (_ "A")`,
- `package B; import (_ "A")`,
- `package E; import (_ "C")`,
- `package D; import (_ "B"; _ "C")`,
- `package F; import (_ "D"; _ "E")`,
+ `package a`,
+ `package c; import (_ "a")`,
+ `package b; import (_ "a")`,
+ `package e; import (_ "c")`,
+ `package d; import (_ "b"; _ "c")`,
+ `package f; import (_ "d"; _ "e")`,
} {
f, err := parser.ParseFile(fset, fmt.Sprintf("%d.go", i), content, 0)
if err != nil {
@@ -51,22 +54,22 @@
for _, test := range []struct {
roots, want string
}{
- {"A", "A"},
- {"B", "AB"},
- {"C", "AC"},
- {"D", "ABCD"},
- {"E", "ACE"},
- {"F", "ABCDEF"},
+ {"a", "a"},
+ {"b", "ab"},
+ {"c", "ac"},
+ {"d", "abcd"},
+ {"e", "ace"},
+ {"f", "abcdef"},
- {"BE", "ABCE"},
- {"EB", "ACEB"},
- {"DE", "ABCDE"},
- {"ED", "ACEBD"},
- {"EF", "ACEBDF"},
+ {"be", "abce"},
+ {"eb", "aceb"},
+ {"de", "abcde"},
+ {"ed", "acebd"},
+ {"ef", "acebdf"},
} {
var pkgs []*types.Package
for _, r := range test.roots {
- pkgs = append(pkgs, conf.Packages[string(r)])
+ pkgs = append(pkgs, packages[string(r)])
}
var got string
for _, p := range typeutil.Dependencies(pkgs...) {
diff --git a/go/types/typeutil/map.go b/go/types/typeutil/map.go
index b3a04cc..81dd556 100644
--- a/go/types/typeutil/map.go
+++ b/go/types/typeutil/map.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// Package typeutil defines various utilities for types, such as Map,
// a mapping from types.Type to interface{} values.
package typeutil // import "golang.org/x/tools/go/types/typeutil"
@@ -9,9 +11,8 @@
import (
"bytes"
"fmt"
+ "go/types"
"reflect"
-
- "golang.org/x/tools/go/types"
)
// Map is a hash-table-based mapping from types (types.Type) to
diff --git a/go/types/typeutil/map14.go b/go/types/typeutil/map14.go
new file mode 100644
index 0000000..16209e3
--- /dev/null
+++ b/go/types/typeutil/map14.go
@@ -0,0 +1,316 @@
+// 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.5
+
+// Package typeutil defines various utilities for types, such as Map,
+// a mapping from types.Type to interface{} values.
+package typeutil // import "golang.org/x/tools/go/types/typeutil"
+
+import (
+ "bytes"
+ "fmt"
+ "reflect"
+
+ "golang.org/x/tools/go/types"
+)
+
+// Map is a hash-table-based mapping from types (types.Type) to
+// arbitrary interface{} values. The concrete types that implement
+// the Type interface are pointers. Since they are not canonicalized,
+// == cannot be used to check for equivalence, and thus we cannot
+// simply use a Go map.
+//
+// Just as with map[K]V, a nil *Map is a valid empty map.
+//
+// Not thread-safe.
+//
+type Map struct {
+ hasher Hasher // shared by many Maps
+ table map[uint32][]entry // maps hash to bucket; entry.key==nil means unused
+ length int // number of map entries
+}
+
+// entry is an entry (key/value association) in a hash bucket.
+type entry struct {
+ key types.Type
+ value interface{}
+}
+
+// SetHasher sets the hasher used by Map.
+//
+// All Hashers are functionally equivalent but contain internal state
+// used to cache the results of hashing previously seen types.
+//
+// A single Hasher created by MakeHasher() may be shared among many
+// Maps. This is recommended if the instances have many keys in
+// common, as it will amortize the cost of hash computation.
+//
+// A Hasher may grow without bound as new types are seen. Even when a
+// type is deleted from the map, the Hasher never shrinks, since other
+// types in the map may reference the deleted type indirectly.
+//
+// Hashers are not thread-safe, and read-only operations such as
+// Map.Lookup require updates to the hasher, so a full Mutex lock (not a
+// read-lock) is require around all Map operations if a shared
+// hasher is accessed from multiple threads.
+//
+// If SetHasher is not called, the Map will create a private hasher at
+// the first call to Insert.
+//
+func (m *Map) SetHasher(hasher Hasher) {
+ m.hasher = hasher
+}
+
+// Delete removes the entry with the given key, if any.
+// It returns true if the entry was found.
+//
+func (m *Map) Delete(key types.Type) bool {
+ if m != nil && m.table != nil {
+ hash := m.hasher.Hash(key)
+ bucket := m.table[hash]
+ for i, e := range bucket {
+ if e.key != nil && types.Identical(key, e.key) {
+ // We can't compact the bucket as it
+ // would disturb iterators.
+ bucket[i] = entry{}
+ m.length--
+ return true
+ }
+ }
+ }
+ return false
+}
+
+// At returns the map entry for the given key.
+// The result is nil if the entry is not present.
+//
+func (m *Map) At(key types.Type) interface{} {
+ if m != nil && m.table != nil {
+ for _, e := range m.table[m.hasher.Hash(key)] {
+ if e.key != nil && types.Identical(key, e.key) {
+ return e.value
+ }
+ }
+ }
+ return nil
+}
+
+// Set sets the map entry for key to val,
+// and returns the previous entry, if any.
+func (m *Map) Set(key types.Type, value interface{}) (prev interface{}) {
+ if m.table != nil {
+ hash := m.hasher.Hash(key)
+ bucket := m.table[hash]
+ var hole *entry
+ for i, e := range bucket {
+ if e.key == nil {
+ hole = &bucket[i]
+ } else if types.Identical(key, e.key) {
+ prev = e.value
+ bucket[i].value = value
+ return
+ }
+ }
+
+ if hole != nil {
+ *hole = entry{key, value} // overwrite deleted entry
+ } else {
+ m.table[hash] = append(bucket, entry{key, value})
+ }
+ } else {
+ if m.hasher.memo == nil {
+ m.hasher = MakeHasher()
+ }
+ hash := m.hasher.Hash(key)
+ m.table = map[uint32][]entry{hash: {entry{key, value}}}
+ }
+
+ m.length++
+ return
+}
+
+// Len returns the number of map entries.
+func (m *Map) Len() int {
+ if m != nil {
+ return m.length
+ }
+ return 0
+}
+
+// Iterate calls function f on each entry in the map in unspecified order.
+//
+// If f should mutate the map, Iterate provides the same guarantees as
+// Go maps: if f deletes a map entry that Iterate has not yet reached,
+// f will not be invoked for it, but if f inserts a map entry that
+// Iterate has not yet reached, whether or not f will be invoked for
+// it is unspecified.
+//
+func (m *Map) Iterate(f func(key types.Type, value interface{})) {
+ if m != nil {
+ for _, bucket := range m.table {
+ for _, e := range bucket {
+ if e.key != nil {
+ f(e.key, e.value)
+ }
+ }
+ }
+ }
+}
+
+// Keys returns a new slice containing the set of map keys.
+// The order is unspecified.
+func (m *Map) Keys() []types.Type {
+ keys := make([]types.Type, 0, m.Len())
+ m.Iterate(func(key types.Type, _ interface{}) {
+ keys = append(keys, key)
+ })
+ return keys
+}
+
+func (m *Map) toString(values bool) string {
+ if m == nil {
+ return "{}"
+ }
+ var buf bytes.Buffer
+ fmt.Fprint(&buf, "{")
+ sep := ""
+ m.Iterate(func(key types.Type, value interface{}) {
+ fmt.Fprint(&buf, sep)
+ sep = ", "
+ fmt.Fprint(&buf, key)
+ if values {
+ fmt.Fprintf(&buf, ": %q", value)
+ }
+ })
+ fmt.Fprint(&buf, "}")
+ return buf.String()
+}
+
+// String returns a string representation of the map's entries.
+// Values are printed using fmt.Sprintf("%v", v).
+// Order is unspecified.
+//
+func (m *Map) String() string {
+ return m.toString(true)
+}
+
+// KeysString returns a string representation of the map's key set.
+// Order is unspecified.
+//
+func (m *Map) KeysString() string {
+ return m.toString(false)
+}
+
+////////////////////////////////////////////////////////////////////////
+// Hasher
+
+// A Hasher maps each type to its hash value.
+// For efficiency, a hasher uses memoization; thus its memory
+// footprint grows monotonically over time.
+// Hashers are not thread-safe.
+// Hashers have reference semantics.
+// Call MakeHasher to create a Hasher.
+type Hasher struct {
+ memo map[types.Type]uint32
+}
+
+// MakeHasher returns a new Hasher instance.
+func MakeHasher() Hasher {
+ return Hasher{make(map[types.Type]uint32)}
+}
+
+// Hash computes a hash value for the given type t such that
+// Identical(t, t') => Hash(t) == Hash(t').
+func (h Hasher) Hash(t types.Type) uint32 {
+ hash, ok := h.memo[t]
+ if !ok {
+ hash = h.hashFor(t)
+ h.memo[t] = hash
+ }
+ return hash
+}
+
+// hashString computes the Fowler–Noll–Vo hash of s.
+func hashString(s string) uint32 {
+ var h uint32
+ for i := 0; i < len(s); i++ {
+ h ^= uint32(s[i])
+ h *= 16777619
+ }
+ return h
+}
+
+// hashFor computes the hash of t.
+func (h Hasher) hashFor(t types.Type) uint32 {
+ // See Identical for rationale.
+ switch t := t.(type) {
+ case *types.Basic:
+ return uint32(t.Kind())
+
+ case *types.Array:
+ return 9043 + 2*uint32(t.Len()) + 3*h.Hash(t.Elem())
+
+ case *types.Slice:
+ return 9049 + 2*h.Hash(t.Elem())
+
+ case *types.Struct:
+ var hash uint32 = 9059
+ for i, n := 0, t.NumFields(); i < n; i++ {
+ f := t.Field(i)
+ if f.Anonymous() {
+ hash += 8861
+ }
+ hash += hashString(t.Tag(i))
+ hash += hashString(f.Name()) // (ignore f.Pkg)
+ hash += h.Hash(f.Type())
+ }
+ return hash
+
+ case *types.Pointer:
+ return 9067 + 2*h.Hash(t.Elem())
+
+ case *types.Signature:
+ var hash uint32 = 9091
+ if t.Variadic() {
+ hash *= 8863
+ }
+ return hash + 3*h.hashTuple(t.Params()) + 5*h.hashTuple(t.Results())
+
+ case *types.Interface:
+ var hash uint32 = 9103
+ for i, n := 0, t.NumMethods(); i < n; i++ {
+ // See go/types.identicalMethods for rationale.
+ // Method order is not significant.
+ // Ignore m.Pkg().
+ m := t.Method(i)
+ hash += 3*hashString(m.Name()) + 5*h.Hash(m.Type())
+ }
+ return hash
+
+ case *types.Map:
+ return 9109 + 2*h.Hash(t.Key()) + 3*h.Hash(t.Elem())
+
+ case *types.Chan:
+ return 9127 + 2*uint32(t.Dir()) + 3*h.Hash(t.Elem())
+
+ case *types.Named:
+ // Not safe with a copying GC; objects may move.
+ return uint32(reflect.ValueOf(t.Obj()).Pointer())
+
+ case *types.Tuple:
+ return h.hashTuple(t)
+ }
+ panic(t)
+}
+
+func (h Hasher) hashTuple(tuple *types.Tuple) uint32 {
+ // See go/types.identicalTypes for rationale.
+ n := tuple.Len()
+ var hash uint32 = 9137 + 2*uint32(n)
+ for i := 0; i < n; i++ {
+ hash += 3 * h.Hash(tuple.At(i).Type())
+ }
+ return hash
+}
diff --git a/go/types/typeutil/map14_test.go b/go/types/typeutil/map14_test.go
new file mode 100644
index 0000000..9043d05
--- /dev/null
+++ b/go/types/typeutil/map14_test.go
@@ -0,0 +1,176 @@
+// 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.5
+
+package typeutil_test
+
+// TODO(adonovan):
+// - test use of explicit hasher across two maps.
+// - test hashcodes are consistent with equals for a range of types
+// (e.g. all types generated by type-checking some body of real code).
+
+import (
+ "testing"
+
+ "golang.org/x/tools/go/types"
+ "golang.org/x/tools/go/types/typeutil"
+)
+
+var (
+ tStr = types.Typ[types.String] // string
+ tPStr1 = types.NewPointer(tStr) // *string
+ tPStr2 = types.NewPointer(tStr) // *string, again
+ tInt = types.Typ[types.Int] // int
+ tChanInt1 = types.NewChan(types.RecvOnly, tInt) // <-chan int
+ tChanInt2 = types.NewChan(types.RecvOnly, tInt) // <-chan int, again
+)
+
+func checkEqualButNotIdentical(t *testing.T, x, y types.Type, comment string) {
+ if !types.Identical(x, y) {
+ t.Errorf("%s: not equal: %s, %s", comment, x, y)
+ }
+ if x == y {
+ t.Errorf("%s: identical: %v, %v", comment, x, y)
+ }
+}
+
+func TestAxioms(t *testing.T) {
+ checkEqualButNotIdentical(t, tPStr1, tPStr2, "tPstr{1,2}")
+ checkEqualButNotIdentical(t, tChanInt1, tChanInt2, "tChanInt{1,2}")
+}
+
+func TestMap(t *testing.T) {
+ var tmap *typeutil.Map
+
+ // All methods but Set are safe on on (*T)(nil).
+ tmap.Len()
+ tmap.At(tPStr1)
+ tmap.Delete(tPStr1)
+ tmap.KeysString()
+ tmap.String()
+
+ tmap = new(typeutil.Map)
+
+ // Length of empty map.
+ if l := tmap.Len(); l != 0 {
+ t.Errorf("Len() on empty Map: got %d, want 0", l)
+ }
+ // At of missing key.
+ if v := tmap.At(tPStr1); v != nil {
+ t.Errorf("At() on empty Map: got %v, want nil", v)
+ }
+ // Deletion of missing key.
+ if tmap.Delete(tPStr1) {
+ t.Errorf("Delete() on empty Map: got true, want false")
+ }
+ // Set of new key.
+ if prev := tmap.Set(tPStr1, "*string"); prev != nil {
+ t.Errorf("Set() on empty Map returned non-nil previous value %s", prev)
+ }
+
+ // Now: {*string: "*string"}
+
+ // Length of non-empty map.
+ if l := tmap.Len(); l != 1 {
+ t.Errorf("Len(): got %d, want 1", l)
+ }
+ // At via insertion key.
+ if v := tmap.At(tPStr1); v != "*string" {
+ t.Errorf("At(): got %q, want \"*string\"", v)
+ }
+ // At via equal key.
+ if v := tmap.At(tPStr2); v != "*string" {
+ t.Errorf("At(): got %q, want \"*string\"", v)
+ }
+ // Iteration over sole entry.
+ tmap.Iterate(func(key types.Type, value interface{}) {
+ if key != tPStr1 {
+ t.Errorf("Iterate: key: got %s, want %s", key, tPStr1)
+ }
+ if want := "*string"; value != want {
+ t.Errorf("Iterate: value: got %s, want %s", value, want)
+ }
+ })
+
+ // Setion with key equal to present one.
+ if prev := tmap.Set(tPStr2, "*string again"); prev != "*string" {
+ t.Errorf("Set() previous value: got %s, want \"*string\"", prev)
+ }
+
+ // Setion of another association.
+ if prev := tmap.Set(tChanInt1, "<-chan int"); prev != nil {
+ t.Errorf("Set() previous value: got %s, want nil", prev)
+ }
+
+ // Now: {*string: "*string again", <-chan int: "<-chan int"}
+
+ want1 := "{*string: \"*string again\", <-chan int: \"<-chan int\"}"
+ want2 := "{<-chan int: \"<-chan int\", *string: \"*string again\"}"
+ if s := tmap.String(); s != want1 && s != want2 {
+ t.Errorf("String(): got %s, want %s", s, want1)
+ }
+
+ want1 = "{*string, <-chan int}"
+ want2 = "{<-chan int, *string}"
+ if s := tmap.KeysString(); s != want1 && s != want2 {
+ t.Errorf("KeysString(): got %s, want %s", s, want1)
+ }
+
+ // Keys().
+ I := types.Identical
+ switch k := tmap.Keys(); {
+ case I(k[0], tChanInt1) && I(k[1], tPStr1): // ok
+ case I(k[1], tChanInt1) && I(k[0], tPStr1): // ok
+ default:
+ t.Errorf("Keys(): got %v, want %s", k, want2)
+ }
+
+ if l := tmap.Len(); l != 2 {
+ t.Errorf("Len(): got %d, want 1", l)
+ }
+ // At via original key.
+ if v := tmap.At(tPStr1); v != "*string again" {
+ t.Errorf("At(): got %q, want \"*string again\"", v)
+ }
+ hamming := 1
+ tmap.Iterate(func(key types.Type, value interface{}) {
+ switch {
+ case I(key, tChanInt1):
+ hamming *= 2 // ok
+ case I(key, tPStr1):
+ hamming *= 3 // ok
+ }
+ })
+ if hamming != 6 {
+ t.Errorf("Iterate: hamming: got %d, want %d", hamming, 6)
+ }
+
+ if v := tmap.At(tChanInt2); v != "<-chan int" {
+ t.Errorf("At(): got %q, want \"<-chan int\"", v)
+ }
+ // Deletion with key equal to present one.
+ if !tmap.Delete(tChanInt2) {
+ t.Errorf("Delete() of existing key: got false, want true")
+ }
+
+ // Now: {*string: "*string again"}
+
+ if l := tmap.Len(); l != 1 {
+ t.Errorf("Len(): got %d, want 1", l)
+ }
+ // Deletion again.
+ if !tmap.Delete(tPStr2) {
+ t.Errorf("Delete() of existing key: got false, want true")
+ }
+
+ // Now: {}
+
+ if l := tmap.Len(); l != 0 {
+ t.Errorf("Len(): got %d, want %d", l, 0)
+ }
+ if s := tmap.String(); s != "{}" {
+ t.Errorf("Len(): got %q, want %q", s, "")
+ }
+}
diff --git a/go/types/typeutil/map_test.go b/go/types/typeutil/map_test.go
index 776b5e2..e5dc4e4 100644
--- a/go/types/typeutil/map_test.go
+++ b/go/types/typeutil/map_test.go
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package typeutil_test
// TODO(adonovan):
@@ -10,9 +12,9 @@
// (e.g. all types generated by type-checking some body of real code).
import (
+ "go/types"
"testing"
- "golang.org/x/tools/go/types"
"golang.org/x/tools/go/types/typeutil"
)
diff --git a/go/types/typeutil/methodsetcache.go b/go/types/typeutil/methodsetcache.go
index daad644..edc3f5b 100644
--- a/go/types/typeutil/methodsetcache.go
+++ b/go/types/typeutil/methodsetcache.go
@@ -2,14 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
// This file implements a cache of method sets.
package typeutil
import (
+ "go/types"
"sync"
-
- "golang.org/x/tools/go/types"
)
// A MethodSetCache records the method set of each type T for which
diff --git a/go/types/typeutil/methodsetcache14.go b/go/types/typeutil/methodsetcache14.go
new file mode 100644
index 0000000..83b5e76
--- /dev/null
+++ b/go/types/typeutil/methodsetcache14.go
@@ -0,0 +1,75 @@
+// 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.5
+
+// This file implements a cache of method sets.
+
+package typeutil
+
+import (
+ "sync"
+
+ "golang.org/x/tools/go/types"
+)
+
+// A MethodSetCache records the method set of each type T for which
+// MethodSet(T) is called so that repeat queries are fast.
+// The zero value is a ready-to-use cache instance.
+type MethodSetCache struct {
+ mu sync.Mutex
+ named map[*types.Named]struct{ value, pointer *types.MethodSet } // method sets for named N and *N
+ others map[types.Type]*types.MethodSet // all other types
+}
+
+// MethodSet returns the method set of type T. It is thread-safe.
+//
+// If cache is nil, this function is equivalent to types.NewMethodSet(T).
+// Utility functions can thus expose an optional *MethodSetCache
+// parameter to clients that care about performance.
+//
+func (cache *MethodSetCache) MethodSet(T types.Type) *types.MethodSet {
+ if cache == nil {
+ return types.NewMethodSet(T)
+ }
+ cache.mu.Lock()
+ defer cache.mu.Unlock()
+
+ switch T := T.(type) {
+ case *types.Named:
+ return cache.lookupNamed(T).value
+
+ case *types.Pointer:
+ if N, ok := T.Elem().(*types.Named); ok {
+ return cache.lookupNamed(N).pointer
+ }
+ }
+
+ // all other types
+ // (The map uses pointer equivalence, not type identity.)
+ mset := cache.others[T]
+ if mset == nil {
+ mset = types.NewMethodSet(T)
+ if cache.others == nil {
+ cache.others = make(map[types.Type]*types.MethodSet)
+ }
+ cache.others[T] = mset
+ }
+ return mset
+}
+
+func (cache *MethodSetCache) lookupNamed(named *types.Named) struct{ value, pointer *types.MethodSet } {
+ if cache.named == nil {
+ cache.named = make(map[*types.Named]struct{ value, pointer *types.MethodSet })
+ }
+ // Avoid recomputing mset(*T) for each distinct Pointer
+ // instance whose underlying type is a named type.
+ msets, ok := cache.named[named]
+ if !ok {
+ msets.value = types.NewMethodSet(named)
+ msets.pointer = types.NewMethodSet(types.NewPointer(named))
+ cache.named[named] = msets
+ }
+ return msets
+}
diff --git a/go/types/typeutil/ui.go b/go/types/typeutil/ui.go
index 20c5249..945fb29 100644
--- a/go/types/typeutil/ui.go
+++ b/go/types/typeutil/ui.go
@@ -2,11 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// +build go1.5
+
package typeutil
// This file defines utilities for user interfaces that display types.
-import "golang.org/x/tools/go/types"
+import "go/types"
// IntuitiveMethodSet returns the intuitive method set of a type, T.
//
diff --git a/go/types/typeutil/ui14.go b/go/types/typeutil/ui14.go
new file mode 100644
index 0000000..bb78e0b
--- /dev/null
+++ b/go/types/typeutil/ui14.go
@@ -0,0 +1,40 @@
+// 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.5
+
+package typeutil
+
+// This file defines utilities for user interfaces that display types.
+
+import "golang.org/x/tools/go/types"
+
+// IntuitiveMethodSet returns the intuitive method set of a type, T.
+//
+// The result contains MethodSet(T) and additionally, if T is a
+// concrete type, methods belonging to *T if there is no identically
+// named method on T itself. This corresponds to user intuition about
+// method sets; this function is intended only for user interfaces.
+//
+// The order of the result is as for types.MethodSet(T).
+//
+func IntuitiveMethodSet(T types.Type, msets *MethodSetCache) []*types.Selection {
+ var result []*types.Selection
+ mset := msets.MethodSet(T)
+ if _, ok := T.Underlying().(*types.Interface); ok {
+ for i, n := 0, mset.Len(); i < n; i++ {
+ result = append(result, mset.At(i))
+ }
+ } else {
+ pmset := msets.MethodSet(types.NewPointer(T))
+ for i, n := 0, pmset.Len(); i < n; i++ {
+ meth := pmset.At(i)
+ if m := mset.Lookup(meth.Obj().Pkg(), meth.Obj().Name()); m != nil {
+ meth = m
+ }
+ result = append(result, meth)
+ }
+ }
+ return result
+}