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
This commit is contained in:
parent
e5e20e3af5
commit
64ec206bfd
|
|
@ -8,9 +8,10 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
// checkCopyLocks checks whether a function might
|
||||
|
|
@ -24,7 +25,7 @@ func (f *File) checkCopyLocks(d *ast.FuncDecl) {
|
|||
|
||||
if d.Recv != nil && len(d.Recv.List) > 0 {
|
||||
expr := d.Recv.List[0].Type
|
||||
if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr]); path != nil {
|
||||
if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
|
||||
f.Warnf(expr.Pos(), "%s passes Lock by value: %v", d.Name.Name, path)
|
||||
}
|
||||
}
|
||||
|
|
@ -32,7 +33,7 @@ func (f *File) checkCopyLocks(d *ast.FuncDecl) {
|
|||
if d.Type.Params != nil {
|
||||
for _, field := range d.Type.Params.List {
|
||||
expr := field.Type
|
||||
if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr]); path != nil {
|
||||
if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
|
||||
f.Warnf(expr.Pos(), "%s passes Lock by value: %v", d.Name.Name, path)
|
||||
}
|
||||
}
|
||||
|
|
@ -41,7 +42,7 @@ func (f *File) checkCopyLocks(d *ast.FuncDecl) {
|
|||
if d.Type.Results != nil {
|
||||
for _, field := range d.Type.Results.List {
|
||||
expr := field.Type
|
||||
if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr]); path != nil {
|
||||
if path := lockPath(f.pkg.typesPkg, f.pkg.types[expr].Type); path != nil {
|
||||
f.Warnf(expr.Pos(), "%s returns Lock by value: %v", d.Name.Name, path)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
_ "code.google.com/p/go.tools/go/gcimporter"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
|
@ -203,8 +202,7 @@ func doPackageDir(directory string) {
|
|||
type Package struct {
|
||||
path string
|
||||
idents map[*ast.Ident]types.Object
|
||||
types map[ast.Expr]types.Type
|
||||
values map[ast.Expr]exact.Value
|
||||
types map[ast.Expr]types.TypeAndValue
|
||||
spans map[types.Object]Span
|
||||
files []*File
|
||||
typesPkg *types.Package
|
||||
|
|
@ -459,7 +457,7 @@ func (f *File) prepStringerReceiver(d *ast.FuncDecl) {
|
|||
func (f *File) isStringer(d *ast.FuncDecl) bool {
|
||||
return d.Recv != nil && d.Name.Name == "String" &&
|
||||
len(d.Type.Params.List) == 0 && len(d.Type.Results.List) == 1 &&
|
||||
f.pkg.types[d.Type.Results.List[0].Type] == types.Typ[types.String]
|
||||
f.pkg.types[d.Type.Results.List[0].Type].Type == types.Typ[types.String]
|
||||
}
|
||||
|
||||
// walkGenDecl walks a general declaration.
|
||||
|
|
|
|||
|
|
@ -59,5 +59,5 @@ func (f *File) checkNilFuncComparison(e *ast.BinaryExpr) {
|
|||
// isNil reports whether the provided expression is the built-in nil
|
||||
// identifier.
|
||||
func (f *File) isNil(e ast.Expr) bool {
|
||||
return f.pkg.types[e] == types.Typ[types.UntypedNil]
|
||||
return f.pkg.types[e].Type == types.Typ[types.UntypedNil]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ func (f *File) checkPrintf(call *ast.CallExpr, name string, formatIndex int) {
|
|||
f.Warn(call.Pos(), "too few arguments in call to", name)
|
||||
return
|
||||
}
|
||||
lit := f.pkg.values[call.Args[formatIndex]]
|
||||
lit := f.pkg.types[call.Args[formatIndex]].Value
|
||||
if lit == nil {
|
||||
if *verbose {
|
||||
f.Warn(call.Pos(), "can't check non-constant format in call to", name)
|
||||
|
|
@ -380,7 +380,7 @@ func (f *File) okPrintfArg(call *ast.CallExpr, state *formatState) (ok bool) {
|
|||
arg := call.Args[argNum]
|
||||
if !f.matchArgType(v.typ, nil, arg) {
|
||||
typeString := ""
|
||||
if typ := f.pkg.types[arg]; typ != nil {
|
||||
if typ := f.pkg.types[arg].Type; typ != nil {
|
||||
typeString = typ.String()
|
||||
}
|
||||
f.Badf(call.Pos(), "arg %s for printf verb %%%c of wrong type: %s", f.gofmt(arg), state.verb, typeString)
|
||||
|
|
|
|||
|
|
@ -10,15 +10,13 @@ import (
|
|||
"go/ast"
|
||||
"go/token"
|
||||
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
"code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
|
||||
pkg.idents = make(map[*ast.Ident]types.Object)
|
||||
pkg.spans = make(map[types.Object]Span)
|
||||
pkg.types = make(map[ast.Expr]types.Type)
|
||||
pkg.values = make(map[ast.Expr]exact.Value)
|
||||
pkg.types = make(map[ast.Expr]types.TypeAndValue)
|
||||
// By providing a Config with our own error function, it will continue
|
||||
// past the first error. There is no need for that function to do anything.
|
||||
config := types.Config{
|
||||
|
|
@ -26,7 +24,6 @@ func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
|
|||
}
|
||||
info := &types.Info{
|
||||
Types: pkg.types,
|
||||
Values: pkg.values,
|
||||
Objects: pkg.idents,
|
||||
}
|
||||
typesPkg, err := config.Check(pkg.path, fs, astFiles, info)
|
||||
|
|
@ -42,7 +39,7 @@ func (pkg *Package) check(fs *token.FileSet, astFiles []*ast.File) error {
|
|||
// If it is not (probably a struct), it returns a printable form of the type.
|
||||
func (pkg *Package) isStruct(c *ast.CompositeLit) (bool, string) {
|
||||
// Check that the CompositeLit's type is a slice or array (which needs no field keys), if possible.
|
||||
typ := pkg.types[c]
|
||||
typ := pkg.types[c].Type
|
||||
// If it's a named type, pull out the underlying type. If it's not, the Underlying
|
||||
// method returns the type itself.
|
||||
actual := typ
|
||||
|
|
@ -91,7 +88,7 @@ func (f *File) matchArgTypeInternal(t printfArgType, typ types.Type, arg ast.Exp
|
|||
}
|
||||
if typ == nil {
|
||||
// external call
|
||||
typ = f.pkg.types[arg]
|
||||
typ = f.pkg.types[arg].Type
|
||||
if typ == nil {
|
||||
return true // probably a type check problem
|
||||
}
|
||||
|
|
@ -250,7 +247,7 @@ func (f *File) matchStructArgType(t printfArgType, typ *types.Struct, arg ast.Ex
|
|||
// being called has.
|
||||
func (f *File) numArgsInSignature(call *ast.CallExpr) int {
|
||||
// Check the type of the function or method declaration
|
||||
typ := f.pkg.types[call.Fun]
|
||||
typ := f.pkg.types[call.Fun].Type
|
||||
if typ == nil {
|
||||
return 0
|
||||
}
|
||||
|
|
@ -266,10 +263,10 @@ func (f *File) numArgsInSignature(call *ast.CallExpr) int {
|
|||
// func Error() string
|
||||
// where "string" is the universe's string type. We know the method is called "Error".
|
||||
func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
|
||||
typ := f.pkg.types[call]
|
||||
typ := f.pkg.types[call].Type
|
||||
if typ != nil {
|
||||
// We know it's called "Error", so just check the function signature.
|
||||
return types.Identical(f.pkg.types[call.Fun], stringerMethodType)
|
||||
return types.Identical(f.pkg.types[call.Fun].Type, stringerMethodType)
|
||||
}
|
||||
// Without types, we can still check by hand.
|
||||
// Is it a selector expression? Otherwise it's a function call, not a method call.
|
||||
|
|
@ -282,7 +279,7 @@ func (f *File) isErrorMethodCall(call *ast.CallExpr) bool {
|
|||
return false
|
||||
}
|
||||
// Check the type of the method declaration
|
||||
typ = f.pkg.types[sel]
|
||||
typ = f.pkg.types[sel].Type
|
||||
if typ == nil {
|
||||
return false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,6 @@ import (
|
|||
"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 @@ func (imp *importer) createPackage(path string, files ...*ast.File) *PackageInfo
|
|||
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),
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ func (info *PackageInfo) String() string {
|
|||
//
|
||||
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 @@ func (info *PackageInfo) TypeOf(e ast.Expr) types.Type {
|
|||
// 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.
|
||||
|
|
|
|||
|
|
@ -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 @@ type Config struct {
|
|||
// 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).
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import (
|
|||
"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 @@ func TestValuesInfo(t *testing.T) {
|
|||
|
||||
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 @@ func TestValuesInfo(t *testing.T) {
|
|||
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 @@ func TestTypesInfo(t *testing.T) {
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ func testBuiltinSignature(t *testing.T, name, src0, want string) {
|
|||
|
||||
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 @@ func testBuiltinSignature(t *testing.T, name, src0, want string) {
|
|||
// 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 @@ func testBuiltinSignature(t *testing.T, name, src0, want string) {
|
|||
// 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
|
||||
|
|
|
|||
|
|
@ -92,14 +92,11 @@ func (check *checker) delay(f func()) {
|
|||
|
||||
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 @@ func (check *checker) recordCommaOkTypes(x ast.Expr, a [2]Type) {
|
|||
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 @@ func (conf *Config) check(pkgPath string, fset *token.FileSet, files []*ast.File
|
|||
// 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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,8 +53,7 @@ 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 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
|
||||
|
|
|
|||
|
|
@ -48,13 +48,13 @@ var (
|
|||
}
|
||||
|
||||
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 @@ var (
|
|||
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 @@ func f() int {
|
|||
}
|
||||
|
||||
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 @@ func f() int {
|
|||
|
||||
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++
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue