go.tools/go/types: combine Info.{Types,Values} maps.
This results in significant improvement to type-checking time:
it reduces by 4% the entire running time of ssa/stdlib_test
(GOMAXPROCS=8, n=7).
LGTM=gri
R=gri
CC=golang-codereviews
https://golang.org/cl/57770043
diff --git a/go/loader/loader.go b/go/loader/loader.go
index cf5d914..06c2603 100644
--- a/go/loader/loader.go
+++ b/go/loader/loader.go
@@ -107,7 +107,6 @@
"strings"
"code.google.com/p/go.tools/astutil"
- "code.google.com/p/go.tools/go/exact"
"code.google.com/p/go.tools/go/gcimporter"
"code.google.com/p/go.tools/go/types"
)
@@ -620,8 +619,7 @@
info := &PackageInfo{
Files: files,
Info: types.Info{
- Types: make(map[ast.Expr]types.Type),
- Values: make(map[ast.Expr]exact.Value),
+ Types: make(map[ast.Expr]types.TypeAndValue),
Objects: make(map[*ast.Ident]types.Object),
Implicits: make(map[ast.Node]types.Object),
Scopes: make(map[ast.Node]*types.Scope),
diff --git a/go/loader/pkginfo.go b/go/loader/pkginfo.go
index ca25057..bb43ff1 100644
--- a/go/loader/pkginfo.go
+++ b/go/loader/pkginfo.go
@@ -34,7 +34,7 @@
//
func (info *PackageInfo) TypeOf(e ast.Expr) types.Type {
if t, ok := info.Types[e]; ok {
- return t
+ return t.Type
}
// Defining ast.Idents (id := expr) get only Ident callbacks
// but not Expr callbacks.
@@ -49,7 +49,7 @@
// Precondition: e belongs to the package's ASTs.
//
func (info *PackageInfo) ValueOf(e ast.Expr) exact.Value {
- return info.Values[e]
+ return info.Types[e].Value
}
// ObjectOf returns the typechecker object denoted by the specified id.
diff --git a/go/types/api.go b/go/types/api.go
index 6403bf4..99cf5d2 100644
--- a/go/types/api.go
+++ b/go/types/api.go
@@ -14,11 +14,11 @@
//
// Constant folding computes the exact constant value (exact.Value) for
// every expression (ast.Expr) that is a compile-time constant.
-// Use Info.Values for the results of constant folding.
+// 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 for the results of type evaluation.
+// Use Info.Types[expr].Type for the results of type inference.
//
package types
@@ -115,22 +115,26 @@
// in a client of go/types will initialize DefaultImport to gcimporter.Import.
var DefaultImport Importer
+type TypeAndValue struct {
+ Type Type
+ Value exact.Value
+}
+
// 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. Identifiers on the
- // lhs of declarations are collected in Objects, not Types.
+ // Types maps expressions to their types, and for constant
+ // expressions, their values.
+ // Identifiers on the lhs of declarations are collected in
+ // Objects, not Types.
//
// For an expression denoting a predeclared built-in function
// the recorded signature is 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.
- Types map[ast.Expr]Type
-
- // Values maps constant expressions to their values.
- Values map[ast.Expr]exact.Value
+ Types map[ast.Expr]TypeAndValue
// Objects maps identifiers to their corresponding objects (including
// package names, dots "." of dot-imports, and blank "_" identifiers).
diff --git a/go/types/api_test.go b/go/types/api_test.go
index 6742a3e..147282b 100644
--- a/go/types/api_test.go
+++ b/go/types/api_test.go
@@ -13,7 +13,6 @@
"strings"
"testing"
- "code.google.com/p/go.tools/go/exact"
_ "code.google.com/p/go.tools/go/gcimporter"
. "code.google.com/p/go.tools/go/types"
)
@@ -94,14 +93,13 @@
for _, test := range tests {
info := Info{
- Types: make(map[ast.Expr]Type),
- Values: make(map[ast.Expr]exact.Value),
+ 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.Values {
+ for e := range info.Types {
if ExprString(e) == test.expr {
expr = e
break
@@ -111,15 +109,16 @@
t.Errorf("package %s: no expression found for %s", name, test.expr)
continue
}
+ tv := info.Types[expr]
// check that type is correct
- if got := info.Types[expr].String(); got != test.typ {
+ 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 := info.Values[expr].String(); got != test.val {
+ if got := tv.Value.String(); got != test.val {
t.Errorf("package %s: got value %s; want %s", name, got, test.val)
}
}
@@ -206,14 +205,14 @@
}
for _, test := range tests {
- info := Info{Types: make(map[ast.Expr]Type)}
+ info := Info{Types: make(map[ast.Expr]TypeAndValue)}
name := mustTypecheck(t, "TypesInfo", test.src, &info)
// look for expression type
var typ Type
- for e, t := range info.Types {
+ for e, tv := range info.Types {
if ExprString(e) == test.expr {
- typ = t
+ typ = tv.Type
break
}
}
diff --git a/go/types/builtins_test.go b/go/types/builtins_test.go
index fecc531..c33f63c 100644
--- a/go/types/builtins_test.go
+++ b/go/types/builtins_test.go
@@ -134,7 +134,7 @@
var conf Config
objects := make(map[*ast.Ident]Object)
- types := make(map[ast.Expr]Type)
+ types := make(map[ast.Expr]TypeAndValue)
_, err = conf.Check(f.Name.Name, fset, []*ast.File{f}, &Info{Objects: objects, Types: types})
if err != nil {
t.Errorf("%s: %s", src0, err)
@@ -144,7 +144,7 @@
// find called function
n := 0
var fun ast.Expr
- for x, _ := range types {
+ for x := range types {
if call, _ := x.(*ast.CallExpr); call != nil {
fun = call.Fun
n++
@@ -158,7 +158,7 @@
// 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]
+ typ := types[fun].Type
if typ == nil {
t.Errorf("%s: no type recorded for %s", src0, ExprString(fun))
return
diff --git a/go/types/check.go b/go/types/check.go
index 734cbf6..c4694be 100644
--- a/go/types/check.go
+++ b/go/types/check.go
@@ -92,14 +92,11 @@
func (check *checker) recordTypeAndValue(x ast.Expr, typ Type, val exact.Value) {
assert(x != nil && typ != nil)
- if m := check.Types; m != nil {
- m[x] = typ
- }
if val != nil {
assert(isConstType(typ))
- if m := check.Values; m != nil {
- m[x] = val
- }
+ }
+ if m := check.Types; m != nil {
+ m[x] = TypeAndValue{typ, val}
}
}
@@ -129,12 +126,14 @@
assert(isTyped(a[0]) && isTyped(a[1]) && isBoolean(a[1]))
if m := check.Types; m != nil {
for {
- assert(m[x] != nil) // should have been recorded already
+ tv := m[x]
+ assert(tv.Type != nil) // should have been recorded already
pos := x.Pos()
- m[x] = NewTuple(
+ 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 {
@@ -248,7 +247,7 @@
// TODO(gri) Consider doing this before and
// after function body checking for smaller
// map size and more immediate feedback.
- if check.Types != nil || check.Values != nil {
+ if check.Types != nil {
for x, info := range check.untyped {
check.recordTypeAndValue(x, info.typ, info.val)
}
diff --git a/go/types/expr.go b/go/types/expr.go
index f7d1ed9..6b102d0 100644
--- a/go/types/expr.go
+++ b/go/types/expr.go
@@ -53,8 +53,7 @@
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 and Values,
-if present.
+the type (and constant value, if any) is recorded via Info.Types, if present.
*/
type opPredicates map[token.Token]func(Type) bool
diff --git a/go/types/issues_test.go b/go/types/issues_test.go
index 67d9e13..e61f0ef 100644
--- a/go/types/issues_test.go
+++ b/go/types/issues_test.go
@@ -48,13 +48,13 @@
}
var conf Config
- types := make(map[ast.Expr]Type)
+ 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, typ := range types {
+ for x, tv := range types {
var want Type
switch x := x.(type) {
case *ast.BasicLit:
@@ -75,8 +75,8 @@
want = Typ[UntypedNil]
}
}
- if want != nil && !Identical(typ, want) {
- t.Errorf("got %s; want %s", typ, want)
+ if want != nil && !Identical(tv.Type, want) {
+ t.Errorf("got %s; want %s", tv.Type, want)
}
}
}
@@ -96,7 +96,7 @@
}
var conf Config
- types := make(map[ast.Expr]Type)
+ 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)
@@ -104,10 +104,10 @@
want := Typ[Int]
n := 0
- for x, got := range types {
+ for x, tv := range types {
if _, ok := x.(*ast.CallExpr); ok {
- if got != want {
- t.Errorf("%s: got %s; want %s", fset.Position(x.Pos()), got, want)
+ if tv.Type != want {
+ t.Errorf("%s: got %s; want %s", fset.Position(x.Pos()), tv.Type, want)
}
n++
}