mirror of
https://github.com/golang/go
synced 2024-11-18 23:14:43 -07:00
go.tools/go/types: various cleanups
- use unpack also for assignments - run benchmarks only requested (-b flag) R=adonovan CC=golang-dev https://golang.org/cl/31190044
This commit is contained in:
parent
60954257d5
commit
8c9bb9c1e3
@ -13,7 +13,7 @@ import (
|
||||
"code.google.com/p/go.tools/go/exact"
|
||||
)
|
||||
|
||||
// assignment reports whether x can be assigned to a variable of type 'T',
|
||||
// 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.
|
||||
@ -148,8 +148,6 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) Type {
|
||||
if ident != nil && ident.Name == "_" {
|
||||
check.recordObject(ident, nil)
|
||||
// If the lhs is untyped, determine the default type.
|
||||
// The spec is unclear about this, but gc appears to
|
||||
// do this.
|
||||
// TODO(gri) This is still not correct (_ = 1<<1e3)
|
||||
typ := x.typ
|
||||
if isUntyped(typ) {
|
||||
@ -188,8 +186,8 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) Type {
|
||||
return nil
|
||||
}
|
||||
|
||||
// spec: Each left-hand side operand must be addressable, a map index
|
||||
// expression, or the blank identifier. Operands may be parenthesized.
|
||||
// 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
|
||||
@ -214,137 +212,57 @@ func (check *checker) assignVar(lhs ast.Expr, x *operand) Type {
|
||||
// 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)
|
||||
r := len(rhs)
|
||||
assert(l > 0)
|
||||
|
||||
// If the lhs and rhs have corresponding expressions,
|
||||
// treat each matching pair as an individual pair.
|
||||
if l == r {
|
||||
var x operand
|
||||
for i, e := range rhs {
|
||||
check.expr(&x, e)
|
||||
check.initVar(lhs[i], &x)
|
||||
get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid())
|
||||
if l != r {
|
||||
invalidateVars(lhs)
|
||||
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
|
||||
}
|
||||
|
||||
// Otherwise, the rhs must be a single expression (possibly
|
||||
// a function call returning multiple values, or a comma-ok
|
||||
// expression).
|
||||
if r == 1 {
|
||||
// l > 1
|
||||
// Start with rhs so we have expression types
|
||||
// for declarations with implicit types.
|
||||
var x operand
|
||||
rhs := rhs[0]
|
||||
check.expr(&x, rhs)
|
||||
if x.mode == invalid {
|
||||
invalidateVars(lhs)
|
||||
return
|
||||
var x operand
|
||||
if commaOk {
|
||||
var a [2]Type
|
||||
for i := range a {
|
||||
get(&x, i)
|
||||
a[i] = check.initVar(lhs[i], &x)
|
||||
}
|
||||
|
||||
if t, ok := x.typ.(*Tuple); ok {
|
||||
// function result
|
||||
r = t.Len()
|
||||
if l == r {
|
||||
for i, lhs := range lhs {
|
||||
x.mode = value
|
||||
x.expr = rhs
|
||||
x.typ = t.At(i).typ
|
||||
check.initVar(lhs, &x)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !returnPos.IsValid() && (x.mode == mapindex || x.mode == commaok) && l == 2 {
|
||||
// comma-ok expression (not permitted with return statements)
|
||||
x.mode = value
|
||||
t1 := check.initVar(lhs[0], &x)
|
||||
|
||||
x.mode = value
|
||||
x.expr = rhs
|
||||
x.typ = Typ[UntypedBool]
|
||||
t2 := check.initVar(lhs[1], &x)
|
||||
|
||||
if t1 != nil && t2 != nil {
|
||||
check.recordCommaOkTypes(rhs, t1, t2)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
invalidateVars(lhs)
|
||||
|
||||
// lhs variables may be function result parameters (return statement);
|
||||
// use rhs position for properly located error messages
|
||||
if returnPos.IsValid() {
|
||||
check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r)
|
||||
check.recordCommaOkTypes(rhs[0], a)
|
||||
return
|
||||
}
|
||||
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
|
||||
|
||||
for i, lhs := range lhs {
|
||||
get(&x, i)
|
||||
check.initVar(lhs, &x)
|
||||
}
|
||||
}
|
||||
|
||||
func (check *checker) assignVars(lhs, rhs []ast.Expr) {
|
||||
l := len(lhs)
|
||||
r := len(rhs)
|
||||
assert(l > 0)
|
||||
|
||||
// If the lhs and rhs have corresponding expressions,
|
||||
// treat each matching pair as an individual pair.
|
||||
if l == r {
|
||||
var x operand
|
||||
for i, e := range rhs {
|
||||
check.expr(&x, e)
|
||||
check.assignVar(lhs[i], &x)
|
||||
}
|
||||
get, r, commaOk := unpack(func(x *operand, i int) { check.expr(x, rhs[i]) }, len(rhs), l == 2)
|
||||
if l != r {
|
||||
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise, the rhs must be a single expression (possibly
|
||||
// a function call returning multiple values, or a comma-ok
|
||||
// expression).
|
||||
if r == 1 {
|
||||
// l > 1
|
||||
var x operand
|
||||
rhs := rhs[0]
|
||||
check.expr(&x, rhs)
|
||||
if x.mode == invalid {
|
||||
return
|
||||
}
|
||||
|
||||
if t, ok := x.typ.(*Tuple); ok {
|
||||
// function result
|
||||
r = t.Len()
|
||||
if l == r {
|
||||
for i, lhs := range lhs {
|
||||
x.mode = value
|
||||
x.expr = rhs
|
||||
x.typ = t.At(i).typ
|
||||
check.assignVar(lhs, &x)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (x.mode == mapindex || x.mode == commaok) && l == 2 {
|
||||
// comma-ok expression
|
||||
x.mode = value
|
||||
t1 := check.assignVar(lhs[0], &x)
|
||||
|
||||
x.mode = value
|
||||
x.expr = rhs
|
||||
x.typ = Typ[UntypedBool]
|
||||
t2 := check.assignVar(lhs[1], &x)
|
||||
|
||||
if t1 != nil && t2 != nil {
|
||||
check.recordCommaOkTypes(rhs, t1, t2)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
|
||||
for i, lhs := range lhs {
|
||||
get(&x, i)
|
||||
check.assignVar(lhs, &x)
|
||||
}
|
||||
}
|
||||
|
||||
func (check *checker) shortVarDecl(pos token.Pos, lhs, rhs []ast.Expr) {
|
||||
|
@ -33,7 +33,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
||||
switch id {
|
||||
default:
|
||||
// make argument getter
|
||||
arg, nargs = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false)
|
||||
arg, nargs, _ = unpack(func(x *operand, i int) { check.expr(x, call.Args[i]) }, nargs, false)
|
||||
// evaluate first argument, if present
|
||||
if nargs > 0 {
|
||||
arg(x, 0)
|
||||
|
@ -60,7 +60,7 @@ func (check *checker) call(x *operand, e *ast.CallExpr) exprKind {
|
||||
return statement
|
||||
}
|
||||
|
||||
arg, n := unpack(func(x *operand, i int) { check.expr(x, e.Args[i]) }, len(e.Args), false)
|
||||
arg, n, _ := unpack(func(x *operand, i int) { check.expr(x, e.Args[i]) }, len(e.Args), false)
|
||||
check.arguments(x, e, sig, arg, n)
|
||||
|
||||
// determine result
|
||||
@ -90,8 +90,6 @@ func (check *checker) use(list []ast.Expr) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(gri) use unpack for assignment checking as well.
|
||||
|
||||
// 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-
|
||||
@ -101,17 +99,20 @@ type getter func(x *operand, i int)
|
||||
// unpack takes a getter get and a number of operands n. If n == 1 and the
|
||||
// first 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. In all other cases, the
|
||||
// incoming getter and operand count are returned unchanged. 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.
|
||||
// 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.
|
||||
//
|
||||
// Note that unpack may call get(..., 0); but if the result 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) {
|
||||
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
|
||||
@ -121,9 +122,8 @@ func unpack(get getter, n int, allowCommaOk bool) (getter, int) {
|
||||
if i != 0 {
|
||||
unreachable()
|
||||
}
|
||||
// i == 0
|
||||
x.mode = invalid
|
||||
}, 1
|
||||
}, 1, false
|
||||
}
|
||||
|
||||
if t, ok := x0.typ.(*Tuple); ok {
|
||||
@ -132,26 +132,18 @@ func unpack(get getter, n int, allowCommaOk bool) (getter, int) {
|
||||
x.mode = value
|
||||
x.expr = x0.expr
|
||||
x.typ = t.At(i).typ
|
||||
}, t.Len()
|
||||
}, 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) {
|
||||
switch i {
|
||||
case 0:
|
||||
x.mode = value
|
||||
x.expr = x0.expr
|
||||
x.typ = x0.typ
|
||||
case 1:
|
||||
x.mode = value
|
||||
x.expr = x0.expr
|
||||
x.typ = Typ[UntypedBool]
|
||||
default:
|
||||
unreachable()
|
||||
}
|
||||
}, 2
|
||||
x.mode = value
|
||||
x.expr = x0.expr
|
||||
x.typ = a[i]
|
||||
}, 2, true
|
||||
}
|
||||
x0.mode = value
|
||||
}
|
||||
@ -162,16 +154,16 @@ func unpack(get getter, n int, allowCommaOk bool) (getter, int) {
|
||||
unreachable()
|
||||
}
|
||||
*x = x0
|
||||
}, 1
|
||||
}, 1, false
|
||||
}
|
||||
|
||||
// zero or multiple values
|
||||
return get, n
|
||||
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 func(*operand, int), n int) {
|
||||
func (check *checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, arg getter, n int) {
|
||||
passSlice := false
|
||||
if call.Ellipsis.IsValid() {
|
||||
// last argument is of the form x...
|
||||
|
@ -121,15 +121,19 @@ func (check *checker) recordBuiltinType(f ast.Expr, sig *Signature) {
|
||||
}
|
||||
}
|
||||
|
||||
func (check *checker) recordCommaOkTypes(x ast.Expr, t1, t2 Type) {
|
||||
assert(x != nil && isTyped(t1) && isTyped(t2) && isBoolean(t2))
|
||||
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 {
|
||||
assert(m[x] != nil) // should have been recorded already
|
||||
pos := x.Pos()
|
||||
m[x] = NewTuple(
|
||||
NewVar(pos, check.pkg, "", t1),
|
||||
NewVar(pos, check.pkg, "", t2),
|
||||
NewVar(pos, check.pkg, "", a[0]),
|
||||
NewVar(pos, check.pkg, "", a[1]),
|
||||
)
|
||||
// if x is a parenthesized expression (p.X), update p.X
|
||||
p, _ := x.(*ast.ParenExpr)
|
||||
|
@ -5,6 +5,7 @@
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
@ -17,6 +18,8 @@ import (
|
||||
. "code.google.com/p/go.tools/go/types"
|
||||
)
|
||||
|
||||
var benchmark = flag.Bool("b", false, "run benchmarks")
|
||||
|
||||
func TestSelf(t *testing.T) {
|
||||
fset := token.NewFileSet()
|
||||
files, err := pkgFiles(fset, ".")
|
||||
@ -27,9 +30,8 @@ func TestSelf(t *testing.T) {
|
||||
_, err = Check("go/types", fset, files)
|
||||
if err != nil {
|
||||
// Importing go.tools/go/exact doensn't work in the
|
||||
// build dashboard environment at the moment. Don't
|
||||
// report an error for now so that the build remains
|
||||
// green.
|
||||
// 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
|
||||
@ -37,8 +39,8 @@ func TestSelf(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBenchmark(t *testing.T) {
|
||||
if testing.Short() {
|
||||
return // skip benchmark in short mode
|
||||
if !*benchmark {
|
||||
return
|
||||
}
|
||||
|
||||
// We're not using testing's benchmarking mechanism directly
|
||||
@ -46,13 +48,13 @@ func TestBenchmark(t *testing.T) {
|
||||
|
||||
for _, p := range []string{"types", "exact", "gcimporter"} {
|
||||
path := filepath.Join("..", p)
|
||||
benchmark(t, path, false)
|
||||
benchmark(t, path, true)
|
||||
runbench(t, path, false)
|
||||
runbench(t, path, true)
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
func benchmark(t *testing.T, path string, ignoreFuncBodies bool) {
|
||||
func runbench(t *testing.T, path string, ignoreFuncBodies bool) {
|
||||
fset := token.NewFileSet()
|
||||
files, err := pkgFiles(fset, path)
|
||||
if err != nil {
|
||||
|
2
go/types/testdata/stmt0.src
vendored
2
go/types/testdata/stmt0.src
vendored
@ -21,7 +21,7 @@ func assignments0() (int, int) {
|
||||
|
||||
a = f0 /* ERROR "used as value" */ ()
|
||||
a = f1()
|
||||
a = f2 /* ERROR "used as single value" */ ()
|
||||
a = f2 /* ERROR "assignment count mismatch" */ ()
|
||||
a, b = f2()
|
||||
a, b, c = f2 /* ERROR "assignment count mismatch" */ ()
|
||||
a, b, c = f3()
|
||||
|
Loading…
Reference in New Issue
Block a user