diff --git a/go/types/builtins.go b/go/types/builtins.go index 3be050c9..71e9753c 100644 --- a/go/types/builtins.go +++ b/go/types/builtins.go @@ -405,8 +405,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, id builtinId) { } default: - check.invalidAST(call.Pos(), "unknown builtin id %d", id) - goto Error + panic("unreachable") } x.expr = call diff --git a/go/types/call.go b/go/types/call.go index 005ded75..d57172f0 100644 --- a/go/types/call.go +++ b/go/types/call.go @@ -9,19 +9,6 @@ package types import ( "go/ast" "go/token" - - "code.google.com/p/go.tools/go/exact" -) - -// 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 ) func (check *checker) call(x *operand, e *ast.CallExpr) exprKind { @@ -61,8 +48,8 @@ func (check *checker) call(x *operand, e *ast.CallExpr) exprKind { return conversion case builtin: - id, _ := exact.Int64Val(x.val) - check.builtin(x, e, builtinId(id)) + id := x.id + check.builtin(x, e, id) return predeclaredFuncs[id].kind default: @@ -239,7 +226,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr) { case *Builtin: x.mode = builtin x.typ = exp.typ - x.val = exact.MakeInt64(int64(exp.id)) + x.id = exp.id default: unreachable() } diff --git a/go/types/check_test.go b/go/types/check_test.go index a4dd555c..a199f7e9 100644 --- a/go/types/check_test.go +++ b/go/types/check_test.go @@ -45,6 +45,7 @@ var ( // 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/cycles.src"}, {"testdata/decls0.src"}, diff --git a/go/types/expr.go b/go/types/expr.go index 096d556f..971e58e5 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -14,15 +14,6 @@ import ( "code.google.com/p/go.tools/go/exact" ) -// TODO(gri) Internal cleanups -// - don't print error messages referring to invalid types (they are likely spurious errors) -// - simplify invalid handling: maybe just use Typ[Invalid] as marker, get rid of invalid Mode for values? -// - rethink error handling: should all callers check if x.mode == valid after making a call? -// - consider storing error messages in invalid operands for better error messages/debugging output - -// TODO(gri) Test issues -// - API tests are missing (e.g., identifiers should be handled as expressions in callbacks) - /* Basic algorithm: @@ -840,6 +831,16 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64 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. diff --git a/go/types/operand.go b/go/types/operand.go index b53a3807..6e494752 100644 --- a/go/types/operand.go +++ b/go/types/operand.go @@ -8,7 +8,6 @@ package types import ( "bytes" - "fmt" "go/ast" "go/token" @@ -30,20 +29,20 @@ const ( ) var operandModeString = [...]string{ - invalid: "invalid", + invalid: "invalid operand", novalue: "no value", - builtin: "builtin", + builtin: "built-in", typexpr: "type", constant: "constant", variable: "variable", value: "value", - valueok: "value,ok", + valueok: "value, ok", } // An operand represents an intermediate value during type checking. // Operands have an (addressing) mode, the expression evaluating to -// the operand, the operand's type, and a value if mode == constant -// or builtin. The built-in id is encoded as an exact Int64 in val. +// 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 { @@ -51,6 +50,7 @@ type operand struct { expr ast.Expr typ Type val exact.Value + id builtinId } // pos returns the position of the expression corresponding to x. @@ -64,29 +64,95 @@ func (x *operand) pos() token.Pos { 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 ( ) +// novalue ( ) +// builtin ( ) +// typexpr ( ) +// +// constant ( ) +// constant ( of type ) +// constant ( ) +// constant ( of type ) +// +// variable ( ) +// variable ( of type ) +// +// value ( ) +// value ( of type ) +// +// valueok ( ) +// valueok ( of type ) +// func (x *operand) String() string { - if x.mode == invalid { - return "invalid operand" - } var buf bytes.Buffer + + var expr string if x.expr != nil { - buf.WriteString(exprString(x.expr)) + expr = exprString(x.expr) + } else { + switch x.mode { + case builtin: + expr = predeclaredFuncs[x.id].name + case typexpr: + expr = typeString(x.typ) + case constant: + expr = x.val.String() + } + } + + // ( + if expr != "" { + buf.WriteString(expr) buf.WriteString(" (") } - buf.WriteString(operandModeString[x.mode]) - if x.mode == constant { - format := " %v" - if isString(x.typ) { - format = " %q" + + // + 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 } - fmt.Fprintf(&buf, format, x.val) + hasType = true } - if x.mode != novalue && (x.mode != constant || !isUntyped(x.typ)) { - fmt.Fprintf(&buf, " of type %s", typeString(x.typ)) + + // + buf.WriteString(operandModeString[x.mode]) + + // + if x.mode == constant { + if s := x.val.String(); s != expr { + buf.WriteByte(' ') + buf.WriteString(s) + } } - if x.expr != nil { + + // + if hasType { + if x.typ != Typ[Invalid] { + buf.WriteString(" of type ") + writeType(&buf, x.typ) + } else { + buf.WriteString(" with invalid type") + } + } + + // ) + if expr != "" { buf.WriteByte(')') } + return buf.String() } diff --git a/go/types/testdata/errors.src b/go/types/testdata/errors.src new file mode 100644 index 00000000..3e4bc50e --- /dev/null +++ b/go/types/testdata/errors.src @@ -0,0 +1,38 @@ +// 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. + +// testing precise operand formatting in error messages +// (matching messages are regular expressions, hence the \'s) + +package errors + +func f(x int, m map[string]int) { + // no values + _ = f /* ERROR "f\(0, m\) \(no value\) used as value" */ (0, m) + + // built-ins + _ = println /* ERROR "println \(built-in\) must be called" */ + + // types + _ = complex128 /* ERROR "complex128 \(type\) is not an expression" */ + + // constants + const c1 = 991 + const c2 float32 = 0.5 + 0 /* ERROR "0 \(untyped integer constant\) is not used" */ + c1 /* ERROR "c1 \(untyped integer constant 991\) is not used" */ + c2 /* ERROR "c2 \(constant 1/2 of type float32\) is not used" */ + c1 /* ERROR "c1 \+ c2 \(constant 1983/2 of type float32\) is not used" */ + c2 + + // variables + x /* ERROR "x \(variable of type int\) is not used" */ + + // values + x /* ERROR "x != x \(untyped boolean value\) is not used" */ != x + x /* ERROR "x \+ x \(value of type int\) is not used" */ + x + + // value, ok's + const s = "foo" + m /* ERROR "m\[s\] \(value, ok of type int\) is not used" */ [s] +} diff --git a/go/types/typexpr.go b/go/types/typexpr.go index 201897a9..639a2cdc 100644 --- a/go/types/typexpr.go +++ b/go/types/typexpr.go @@ -95,7 +95,7 @@ func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool) case *Builtin: obj.used = true // for built-ins defined by package unsafe x.mode = builtin - x.val = exact.MakeInt64(int64(obj.id)) + x.id = obj.id default: unreachable()