go.tools/go/types: remove iota from all parameter lists (cleanup)

Instead of passing around iota everywhere, keep track of the
current value in the checker, and update it when entering
different declarations. This is less explicit, but the improvement
over all code is so significant that it is worth it.

R=adonovan
CC=golang-dev
https://golang.org/cl/10814044
This commit is contained in:
Robert Griesemer 2013-07-03 10:06:09 -07:00
parent b58f98e9c2
commit f052654314
10 changed files with 187 additions and 141 deletions

View File

@ -89,7 +89,7 @@ func (check *checker) assignMulti(lhs []Object, rhs []ast.Expr) {
if len(lhs) == len(rhs) {
var x operand
for i, e := range rhs {
check.expr(&x, e, -1)
check.expr(&x, e)
if x.mode == invalid {
goto Error
}
@ -106,7 +106,7 @@ func (check *checker) assignMulti(lhs []Object, rhs []ast.Expr) {
// Start with rhs so we have expression types
// for declarations with implicit types.
var x operand
check.expr(&x, rhs[0], -1)
check.expr(&x, rhs[0])
if x.mode == invalid {
goto Error
}

View File

@ -16,12 +16,11 @@ import (
// TODO(gri): Several built-ins are missing assignment checks. As a result,
// non-constant shift arguments may not be properly type-checked.
// builtin typechecks a built-in call. The built-in type is bin, and iota is the current
// value of iota or -1 if iota doesn't have a value in the current context. The result
// of the call is returned via x. If the call has type errors, the returned x is marked
// as invalid (x.mode == invalid).
// builtin typechecks a built-in call. The built-in type is bin, and the result
// of the call is returned via x. If the call has type errors, the returned x is
// marked as invalid (x.mode == invalid).
//
func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota int) {
func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin) {
args := call.Args
id := bin.id
@ -50,7 +49,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
// respective cases below do the work
default:
// argument must be an expression
check.expr(x, arg0, iota)
check.expr(x, arg0)
if x.mode == invalid {
goto Error
}
@ -65,7 +64,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
}
resultTyp := x.typ
for _, arg := range args[1:] {
check.expr(x, arg, iota)
check.expr(x, arg)
if x.mode == invalid {
goto Error
}
@ -134,7 +133,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
}
var y operand
check.expr(&y, args[1], iota)
check.expr(&y, args[1])
if y.mode == invalid {
goto Error
}
@ -196,7 +195,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
case _Copy:
var y operand
check.expr(&y, args[1], iota)
check.expr(&y, args[1])
if y.mode == invalid {
goto Error
}
@ -233,7 +232,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
check.invalidArg(x.pos(), "%s is not a map", x)
goto Error
}
check.expr(x, args[1], iota)
check.expr(x, args[1])
if x.mode == invalid {
goto Error
}
@ -271,7 +270,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
x.typ = Typ[k]
case _Make:
resultTyp := check.typ(arg0, iota, nil, false)
resultTyp := check.typ(arg0, nil, false)
if resultTyp == Typ[Invalid] {
goto Error
}
@ -291,7 +290,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
}
var sizes []int64 // constant integer arguments, if any
for _, arg := range args[1:] {
if s, ok := check.index(arg, -1, iota); ok && s >= 0 {
if s, ok := check.index(arg, -1); ok && s >= 0 {
sizes = append(sizes, s)
}
}
@ -303,7 +302,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
x.typ = resultTyp
case _New:
resultTyp := check.typ(arg0, iota, nil, false)
resultTyp := check.typ(arg0, nil, false)
if resultTyp == Typ[Invalid] {
goto Error
}
@ -315,7 +314,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
case _Print, _Println:
for _, arg := range args {
check.expr(x, arg, iota)
check.expr(x, arg)
if x.mode == invalid {
goto Error
}
@ -337,7 +336,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
check.invalidArg(arg0.Pos(), "%s is not a selector expression", arg0)
goto Error
}
check.expr(x, arg.X, iota)
check.expr(x, arg.X)
if x.mode == invalid {
goto Error
}
@ -397,7 +396,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
var t operand
x1 := x
for _, arg := range args {
check.rawExpr(x1, arg, nil, iota) // permit trace for types, e.g.: new(trace(T))
check.rawExpr(x1, arg, nil) // permit trace for types, e.g.: new(trace(T))
check.dump("%s: %s", x1.pos(), x1)
x1 = &t // use incoming x only for first argument
}

View File

@ -11,19 +11,19 @@ import (
"go/token"
)
func (check *checker) call(x *operand, e *ast.CallExpr, iota int) {
check.exprOrType(x, e.Fun, iota)
func (check *checker) call(x *operand, e *ast.CallExpr) {
check.exprOrType(x, e.Fun)
if x.mode == invalid {
// We don't have a valid call or conversion but we have list of arguments.
// Typecheck them independently for better partial type information in
// the presence of type errors.
for _, arg := range e.Args {
check.expr(x, arg, iota)
check.expr(x, arg)
}
goto Error
} else if x.mode == typexpr {
check.conversion(x, e, x.typ, iota)
check.conversion(x, e, x.typ)
} else if sig, ok := x.typ.Underlying().(*Signature); ok {
// check parameters
@ -52,7 +52,7 @@ func (check *checker) call(x *operand, e *ast.CallExpr, iota int) {
n := 0 // parameter count
if call != nil {
// We have a single argument that is a function call.
check.expr(x, call, iota)
check.expr(x, call)
if x.mode == invalid {
goto Error // TODO(gri): we can do better
}
@ -104,7 +104,7 @@ func (check *checker) call(x *operand, e *ast.CallExpr, iota int) {
}
} else if bin, ok := x.typ.(*Builtin); ok {
check.builtin(x, e, bin, iota)
check.builtin(x, e, bin)
} else {
check.invalidOp(x.pos(), "cannot call non-function %s", x)
@ -153,7 +153,7 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand,
z.typ = par.typ
if arg != nil {
check.expr(x, arg, -1)
check.expr(x, arg)
}
if x.mode == invalid {
return // ignore this argument
@ -176,7 +176,7 @@ func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand,
}
}
func (check *checker) selector(x *operand, e *ast.SelectorExpr, iota int) {
func (check *checker) selector(x *operand, e *ast.SelectorExpr) {
// these must be declared before the "goto Error" statements
var (
obj Object
@ -230,7 +230,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr, iota int) {
}
}
check.exprOrType(x, e.X, iota)
check.exprOrType(x, e.X)
if x.mode == invalid {
goto Error
}

View File

@ -48,6 +48,7 @@ type checker struct {
objMap map[Object]*decl // if set we are in the package-global declaration phase (otherwise all objects seen must be declared)
topScope *Scope // topScope for lookups, non-global declarations
iota exact.Value // value of iota in a constant declaration; nil otherwise
// functions
funclist []function // list of functions/methods with correct signatures and non-empty bodies

View File

@ -12,12 +12,12 @@ import (
"code.google.com/p/go.tools/go/exact"
)
// conversion typechecks the type conversion conv to type typ. iota is the current
// value of iota or -1 if iota doesn't have a value in the current context. The result
// of the conversion is returned via x. If the conversion has type errors, the returned
// conversion typechecks the type conversion conv to type typ.
// The result of the conversion is returned via x.
// If the conversion has type errors, the returned
// x is marked as invalid (x.mode == invalid).
//
func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota int) {
func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type) {
// all conversions have one argument
if len(conv.Args) != 1 {
check.invalidOp(conv.Pos(), "%s conversion requires exactly one argument", conv)
@ -25,7 +25,7 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota
}
// evaluate argument
check.expr(x, conv.Args[0], iota)
check.expr(x, conv.Args[0])
if x.mode == invalid {
goto Error
}

View File

@ -17,7 +17,6 @@ import (
// - 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?
// - at the moment, iota is passed around almost everywhere - in many places we know it cannot be used
// - consider storing error messages in invalid operands for better error messages/debugging output
// TODO(gri) Test issues
@ -40,9 +39,6 @@ of an outer composite literal; it is used to type-check composite literal
elements that have no explicit type specification in the source
(e.g.: []T{{...}, {...}}, the hint is the type T in this case).
If an iota argument >= 0 is present, it is the value of iota for the
specific expression.
All expressions are checked via rawExpr, which dispatches according
to expression kind. Upon returning, rawExpr is recording the types and
constant values for all expressions that have an untyped type (those types
@ -611,11 +607,11 @@ var binaryOpPredicates = opPredicates{
token.LOR: isBoolean,
}
func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token, iota int) {
func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token) {
var y operand
check.expr(x, lhs, iota)
check.expr(&y, rhs, iota)
check.expr(x, lhs)
check.expr(&y, rhs)
if x.mode == invalid {
return
@ -686,10 +682,9 @@ func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token, iota
// index checks an index/size expression arg for validity.
// If length >= 0, it is the upper bound for arg.
// TODO(gri): Do we need iota?
func (check *checker) index(arg ast.Expr, length int64, iota int) (i int64, ok bool) {
func (check *checker) index(arg ast.Expr, length int64) (i int64, ok bool) {
var x operand
check.expr(&x, arg, iota)
check.expr(&x, arg)
// an untyped constant must be representable as Int
check.convertUntyped(&x, Typ[Int])
@ -726,7 +721,7 @@ func (check *checker) index(arg ast.Expr, length int64, iota int) (i int64, ok b
// the literal length if known (length >= 0). It returns the length of the
// literal (maximum index value + 1).
//
func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota int) int64 {
func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64) int64 {
visited := make(map[int64]bool, len(elts))
var index, max int64
for _, e := range elts {
@ -734,7 +729,7 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota
validIndex := false
eval := e
if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
if i, ok := check.index(kv.Key, length, iota); ok {
if i, ok := check.index(kv.Key, length); ok {
if i >= 0 {
index = i
}
@ -761,7 +756,7 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota
// check element against composite literal element type
var x operand
check.exprWithHint(&x, eval, typ, iota)
check.exprWithHint(&x, eval, typ)
if !check.assignment(&x, typ) && x.mode != invalid {
check.errorf(x.pos(), "cannot use %s as %s value in array or slice literal", &x, typ)
}
@ -810,9 +805,8 @@ func (check *checker) callExpr(x *operand, ignore *bool) {
// 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.
// iota >= 0 indicates that the expression is part of a constant declaration.
//
func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type) {
// make sure x has a valid state for deferred functions in case of bailout
// (was issue 5770)
x.mode = invalid
@ -832,7 +826,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
goto Error // error was reported before
case *ast.Ident:
check.ident(x, e, iota, nil, false)
check.ident(x, e, nil, false)
case *ast.Ellipsis:
// ellipses are handled explicitly where they are legal
@ -848,7 +842,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
}
case *ast.FuncLit:
if sig, ok := check.typ(e.Type, iota, nil, false).(*Signature); ok {
if sig, ok := check.typ(e.Type, nil, false).(*Signature); ok {
x.mode = value
x.typ = sig
check.later(nil, sig, e.Body)
@ -869,12 +863,12 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
// We have an "open" [...]T array type.
// Create a new ArrayType with unknown length (-1)
// and finish setting it up after analyzing the literal.
typ = &Array{len: -1, elt: check.typ(atyp.Elt, iota, nil, false)}
typ = &Array{len: -1, elt: check.typ(atyp.Elt, nil, false)}
openArray = true
}
}
if typ == nil {
typ = check.typ(e.Type, iota, nil, false)
typ = check.typ(e.Type, nil, false)
}
}
if typ == nil {
@ -915,7 +909,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
continue
}
visited[i] = true
check.expr(x, kv.Value, iota)
check.expr(x, kv.Value)
etyp := fld.typ
if !check.assignment(x, etyp) {
if x.mode != invalid {
@ -931,7 +925,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
check.errorf(kv.Pos(), "mixture of field:value and value elements in struct literal")
continue
}
check.expr(x, e, iota)
check.expr(x, e)
if i >= len(fields) {
check.errorf(x.pos(), "too many values in struct literal")
break // cannot continue
@ -952,14 +946,14 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
}
case *Array:
n := check.indexedElts(e.Elts, utyp.elt, utyp.len, iota)
n := check.indexedElts(e.Elts, utyp.elt, utyp.len)
// if we have an "open" [...]T array, set the length now that we know it
if openArray {
utyp.len = n
}
case *Slice:
check.indexedElts(e.Elts, utyp.elt, -1, iota)
check.indexedElts(e.Elts, utyp.elt, -1)
case *Map:
visited := make(map[interface{}]bool, len(e.Elts))
@ -969,7 +963,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
check.errorf(e.Pos(), "missing key in map literal")
continue
}
check.expr(x, kv.Key, iota)
check.expr(x, kv.Key)
if !check.assignment(x, utyp.key) {
if x.mode != invalid {
check.errorf(x.pos(), "cannot use %s as %s key in map literal", x, utyp.key)
@ -983,7 +977,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
}
visited[x.val] = true
}
check.exprWithHint(x, kv.Value, utyp.elt, iota)
check.exprWithHint(x, kv.Value, utyp.elt)
if !check.assignment(x, utyp.elt) {
if x.mode != invalid {
check.errorf(x.pos(), "cannot use %s as %s value in map literal", x, utyp.elt)
@ -1001,13 +995,13 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
x.typ = typ
case *ast.ParenExpr:
check.rawExpr(x, e.X, nil, iota)
check.rawExpr(x, e.X, nil)
case *ast.SelectorExpr:
check.selector(x, e, iota)
check.selector(x, e)
case *ast.IndexExpr:
check.expr(x, e.X, iota)
check.expr(x, e.X)
if x.mode == invalid {
goto Error
}
@ -1051,7 +1045,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
case *Map:
var key operand
check.expr(&key, e.Index, iota)
check.expr(&key, e.Index)
if !check.assignment(&key, typ.key) {
if key.mode != invalid {
check.invalidOp(key.pos(), "cannot use %s as map index of type %s", &key, typ.key)
@ -1074,11 +1068,11 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
return
}
check.index(e.Index, length, iota)
check.index(e.Index, length)
// ok to continue
case *ast.SliceExpr:
check.expr(x, e.X, iota)
check.expr(x, e.X)
if x.mode == invalid {
goto Error
}
@ -1134,14 +1128,14 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
lo := int64(0)
if e.Low != nil {
if i, ok := check.index(e.Low, length, iota); ok && i >= 0 {
if i, ok := check.index(e.Low, length); ok && i >= 0 {
lo = i
}
}
hi := int64(-1)
if e.High != nil {
if i, ok := check.index(e.High, length, iota); ok && i >= 0 {
if i, ok := check.index(e.High, length); ok && i >= 0 {
hi = i
}
} else if length >= 0 {
@ -1154,7 +1148,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
}
case *ast.TypeAssertExpr:
check.expr(x, e.X, iota)
check.expr(x, e.X)
if x.mode == invalid {
goto Error
}
@ -1168,7 +1162,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
check.invalidAST(e.Pos(), "use of .(type) outside type switch")
goto Error
}
typ := check.typ(e.Type, iota, nil, false)
typ := check.typ(e.Type, nil, false)
if typ == Typ[Invalid] {
goto Error
}
@ -1192,10 +1186,10 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
x.typ = typ
case *ast.CallExpr:
check.call(x, e, iota)
check.call(x, e)
case *ast.StarExpr:
check.exprOrType(x, e.X, iota)
check.exprOrType(x, e.X)
switch x.mode {
case invalid:
goto Error
@ -1212,7 +1206,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
}
case *ast.UnaryExpr:
check.expr(x, e.X, iota)
check.expr(x, e.X)
if x.mode == invalid {
goto Error
}
@ -1222,7 +1216,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
}
case *ast.BinaryExpr:
check.binary(x, e.X, e.Y, e.Op, iota)
check.binary(x, e.X, e.Y, e.Op)
if x.mode == invalid {
goto Error
}
@ -1235,7 +1229,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
case *ast.ArrayType, *ast.StructType, *ast.FuncType,
*ast.InterfaceType, *ast.MapType, *ast.ChanType:
x.mode = typexpr
x.typ = check.typ(e, iota, nil, false)
x.typ = check.typ(e, nil, false)
// check.typ is already notifying clients
// of e's type; don't do it a 2nd time
ignore = true
@ -1258,10 +1252,9 @@ Error:
// expr typechecks expression e and initializes x with the expression value.
// If an error occurred, x.mode is set to invalid.
// iota >= 0 indicates that the expression is part of a constant declaration.
//
func (check *checker) expr(x *operand, e ast.Expr, iota int) {
check.rawExpr(x, e, nil, iota)
func (check *checker) expr(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
switch x.mode {
case novalue:
check.errorf(x.pos(), "%s used as value", x)
@ -1275,11 +1268,10 @@ func (check *checker) expr(x *operand, e ast.Expr, iota int) {
// exprWithHint typechecks expression e and initializes x with the expression value.
// If an error occurred, x.mode is set to invalid.
// If hint != nil, it is the type of a composite literal element.
// iota >= 0 indicates that the expression is part of a constant declaration.
//
func (check *checker) exprWithHint(x *operand, e ast.Expr, hint Type, iota int) {
func (check *checker) exprWithHint(x *operand, e ast.Expr, hint Type) {
assert(hint != nil)
check.rawExpr(x, e, hint, iota)
check.rawExpr(x, e, hint)
switch x.mode {
case novalue:
check.errorf(x.pos(), "%s used as value", x)
@ -1292,10 +1284,9 @@ func (check *checker) exprWithHint(x *operand, e ast.Expr, hint Type, iota int)
// exprOrType typechecks expression or type e and initializes x with the expression value or type.
// If an error occurred, x.mode is set to invalid.
// iota >= 0 indicates that the expression is part of a constant declaration.
//
func (check *checker) exprOrType(x *operand, e ast.Expr, iota int) {
check.rawExpr(x, e, nil, iota)
func (check *checker) exprOrType(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
if x.mode == novalue {
check.errorf(x.pos(), "%s used as value or type", x)
x.mode = invalid

View File

@ -335,7 +335,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
// - done by the caller for now
}
// declareObject declares obj in the top-most scope.
// declareObject completes the declaration of obj in its respective file scope.
// See declareType for the details on def and cycleOk.
func (check *checker) declareObject(obj Object, def *Named, cycleOk bool) {
d := check.objMap[obj]
@ -344,6 +344,10 @@ func (check *checker) declareObject(obj Object, def *Named, cycleOk bool) {
oldScope := check.topScope
check.topScope = d.file // for lookup
// save current iota
oldIota := check.iota
check.iota = nil
switch obj := obj.(type) {
case *Const:
check.declareConst(obj, d.typ, d.init)
@ -357,6 +361,7 @@ func (check *checker) declareObject(obj Object, def *Named, cycleOk bool) {
unreachable()
}
check.iota = oldIota
check.topScope = oldScope
}
@ -367,12 +372,14 @@ func (check *checker) declareConst(obj *Const, typ, init ast.Expr) {
return
}
obj.visited = true
iota, ok := exact.Int64Val(obj.val) // set in phase 1
assert(ok)
// use the correct value of iota
assert(check.iota == nil)
check.iota = obj.val
// determine type, if any
if typ != nil {
obj.typ = check.typ(typ, int(iota), nil, false)
obj.typ = check.typ(typ, nil, false)
}
var x operand
@ -381,12 +388,13 @@ func (check *checker) declareConst(obj *Const, typ, init ast.Expr) {
goto Error // error reported before
}
check.expr(&x, init, int(iota))
check.expr(&x, init)
if x.mode == invalid {
goto Error
}
check.assign(obj, &x)
check.iota = nil
return
Error:
@ -395,6 +403,7 @@ Error:
} else {
obj.val = exact.MakeUnknown()
}
check.iota = nil
}
func (check *checker) declareVar(obj *Var, typ, init ast.Expr) {
@ -405,9 +414,12 @@ func (check *checker) declareVar(obj *Var, typ, init ast.Expr) {
}
obj.visited = true
// var declarations cannot use iota
assert(check.iota == nil)
// determine type, if any
if typ != nil {
obj.typ = check.typ(typ, -1, nil, false)
obj.typ = check.typ(typ, nil, false)
}
if init == nil {
@ -424,7 +436,7 @@ func (check *checker) declareVar(obj *Var, typ, init ast.Expr) {
}
var x operand
check.expr(&x, init, -1)
check.expr(&x, init)
if x.mode == invalid {
goto Error
}
@ -477,6 +489,9 @@ Error:
func (check *checker) declareType(obj *TypeName, typ ast.Expr, def *Named, cycleOk bool) {
assert(obj.Type() == nil)
// type declarations cannot use iota
assert(check.iota == nil)
named := &Named{obj: obj}
obj.typ = named // make sure recursive type declarations terminate
@ -497,7 +512,7 @@ func (check *checker) declareType(obj *TypeName, typ ast.Expr, def *Named, cycle
// )
//
// When we declare obj = C, typ is the identifier A which is incomplete.
u := check.typ(typ, -1, named, cycleOk)
u := check.typ(typ, named, cycleOk)
// Determine the unnamed underlying type.
// In the above example, the underlying type of A was (temporarily) set
@ -549,7 +564,7 @@ func (check *checker) declareType(obj *TypeName, typ ast.Expr, def *Named, cycle
oldScope := check.topScope
check.topScope = fileScope
sig := check.typ(m.decl.Type, -1, nil, cycleOk).(*Signature)
sig := check.typ(m.decl.Type, nil, cycleOk).(*Signature)
params, _ := check.collectParams(sig.scope, m.decl.Recv, false)
check.topScope = oldScope // reset topScope
@ -566,12 +581,15 @@ func (check *checker) declareType(obj *TypeName, typ ast.Expr, def *Named, cycle
}
func (check *checker) declareFunc(obj *Func) {
// func declarations cannot use iota
assert(check.iota == nil)
fdecl := obj.decl
// methods are typechecked when their receivers are typechecked
// TODO(gri) there is no reason to make this a special case: receivers are simply parameters
if fdecl.Recv == nil {
obj.typ = Typ[Invalid] // guard against cycles
sig := check.typ(fdecl.Type, -1, nil, false).(*Signature)
sig := check.typ(fdecl.Type, nil, false).(*Signature)
if obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) {
check.errorf(fdecl.Pos(), "func init must have no arguments and no return values")
// ok to continue

View File

@ -41,14 +41,14 @@ func (check *checker) assignment(x *operand, to Type) bool {
// if its type is not set, it is deduced from the type of x or set to Typ[Invalid] in
// case of an error.
//
func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota int, isConst bool) {
func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, isConst bool) {
assert(!isConst || decl)
// Start with rhs so we have an expression type
// for declarations with implicit type.
if x == nil {
x = new(operand)
check.expr(x, rhs, iota)
check.expr(x, rhs)
// don't exit for declarations - we need the lhs first
if x.mode == invalid && !decl {
return
@ -70,7 +70,7 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
}
var z operand
check.expr(&z, lhs, -1)
check.expr(&z, lhs)
if z.mode == invalid || z.typ == Typ[Invalid] {
return
}
@ -174,7 +174,7 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
// of the corresponding rhs expressions, or set to Typ[Invalid] in case of an error.
// Precondition: len(lhs) > 0 .
//
func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int, isConst bool) {
func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, isConst bool) {
assert(len(lhs) > 0)
assert(!isConst || decl)
@ -182,7 +182,7 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int, isCon
// matching pair as an individual pair.
if len(lhs) == len(rhs) {
for i, e := range rhs {
check.assign1to1(lhs[i], e, nil, decl, iota, isConst)
check.assign1to1(lhs[i], e, nil, decl, isConst)
}
return
}
@ -195,7 +195,7 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int, isCon
// Start with rhs so we have expression types
// for declarations with implicit types.
var x operand
check.expr(&x, rhs[0], iota)
check.expr(&x, rhs[0])
if x.mode == invalid {
goto Error
}
@ -207,7 +207,7 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int, isCon
obj := t.At(i)
x.expr = nil // TODO(gri) should do better here
x.typ = obj.typ
check.assign1to1(lhs[i], nil, &x, decl, iota, isConst)
check.assign1to1(lhs[i], nil, &x, decl, isConst)
}
return
}
@ -215,10 +215,10 @@ func (check *checker) assignNtoM(lhs, rhs []ast.Expr, decl bool, iota int, isCon
if x.mode == valueok && len(lhs) == 2 {
// comma-ok expression
x.mode = value
check.assign1to1(lhs[0], nil, &x, decl, iota, isConst)
check.assign1to1(lhs[0], nil, &x, decl, isConst)
x.typ = Typ[UntypedBool]
check.assign1to1(lhs[1], nil, &x, decl, iota, isConst)
check.assign1to1(lhs[1], nil, &x, decl, isConst)
return
}
}
@ -307,6 +307,10 @@ func (check *checker) closeScope() {
// stmt typechecks statement s.
func (check *checker) stmt(s ast.Stmt) {
// statements cannot use iota in general
// (constant declarations set it explicitly)
assert(check.iota == nil)
switch s := s.(type) {
case *ast.BadStmt, *ast.EmptyStmt:
// ignore
@ -335,7 +339,7 @@ func (check *checker) stmt(s ast.Stmt) {
// (Caution: This evaluates e.Fun twice, once here and once
// below as part of s.X. This has consequences for
// check.callIdent. Perhaps this can be avoided.)
check.expr(&x, e.Fun, -1)
check.expr(&x, e.Fun)
if x.mode != invalid {
if b, ok := x.typ.(*Builtin); ok && !b.isStatement {
used = false
@ -351,15 +355,15 @@ func (check *checker) stmt(s ast.Stmt) {
check.errorf(s.Pos(), "%s not used", s.X)
// ok to continue
}
check.rawExpr(&x, s.X, nil, -1)
check.rawExpr(&x, s.X, nil)
if x.mode == typexpr {
check.errorf(x.pos(), "%s is not an expression", &x)
}
case *ast.SendStmt:
var ch, x operand
check.expr(&ch, s.Chan, -1)
check.expr(&x, s.Value, -1)
check.expr(&ch, s.Chan)
check.expr(&x, s.Value)
if ch.mode == invalid || x.mode == invalid {
return
}
@ -382,11 +386,11 @@ func (check *checker) stmt(s ast.Stmt) {
}
var x operand
Y := &ast.BasicLit{ValuePos: s.X.Pos(), Kind: token.INT, Value: "1"} // use x's position
check.binary(&x, s.X, Y, op, -1)
check.binary(&x, s.X, Y, op)
if x.mode == invalid {
return
}
check.assign1to1(s.X, nil, &x, false, -1, false)
check.assign1to1(s.X, nil, &x, false, false)
case *ast.AssignStmt:
switch s.Tok {
@ -418,7 +422,7 @@ func (check *checker) stmt(s ast.Stmt) {
check.declareShort(check.topScope, lhs) // scope starts after the assignment
} else {
check.assignNtoM(s.Lhs, s.Rhs, s.Tok == token.DEFINE, -1, false)
check.assignNtoM(s.Lhs, s.Rhs, s.Tok == token.DEFINE, false)
}
default:
@ -457,21 +461,21 @@ func (check *checker) stmt(s ast.Stmt) {
return
}
var x operand
check.binary(&x, s.Lhs[0], s.Rhs[0], op, -1)
check.binary(&x, s.Lhs[0], s.Rhs[0], op)
if x.mode == invalid {
return
}
check.assign1to1(s.Lhs[0], nil, &x, false, -1, false)
check.assign1to1(s.Lhs[0], nil, &x, false, false)
}
case *ast.GoStmt:
var x operand
check.call(&x, s.Call, -1)
check.call(&x, s.Call)
// TODO(gri) If a builtin is called, the builtin must be valid this context.
case *ast.DeferStmt:
var x operand
check.call(&x, s.Call, -1)
check.call(&x, s.Call)
// TODO(gri) If a builtin is called, the builtin must be valid this context.
case *ast.ReturnStmt:
@ -508,7 +512,7 @@ func (check *checker) stmt(s ast.Stmt) {
}
if len(s.Results) > 0 || !named {
// TODO(gri) assignNtoM should perhaps not require len(lhs) > 0
check.assignNtoM(lhs, s.Results, false, -1, false)
check.assignNtoM(lhs, s.Results, false, false)
}
} else if len(s.Results) > 0 {
check.errorf(s.Pos(), "no result values expected")
@ -526,7 +530,7 @@ func (check *checker) stmt(s ast.Stmt) {
check.openScope(s)
check.optionalStmt(s.Init)
var x operand
check.expr(&x, s.Cond, -1)
check.expr(&x, s.Cond)
if x.mode != invalid && !isBoolean(x.typ) {
check.errorf(s.Cond.Pos(), "non-boolean condition in if statement")
}
@ -545,7 +549,7 @@ func (check *checker) stmt(s ast.Stmt) {
check.callIdent(ident, Universe.Lookup(nil, "true"))
tag = ident
}
check.expr(&x, tag, -1)
check.expr(&x, tag)
check.multipleDefaults(s.Body.List)
// TODO(gri) check also correct use of fallthrough
@ -559,7 +563,7 @@ func (check *checker) stmt(s ast.Stmt) {
for _, expr := range clause.List {
x := x // copy of x (don't modify original)
var y operand
check.expr(&y, expr, -1)
check.expr(&y, expr)
if y.mode == invalid {
continue // error reported before
}
@ -645,7 +649,7 @@ func (check *checker) stmt(s ast.Stmt) {
return
}
var x operand
check.expr(&x, expr.X, -1)
check.expr(&x, expr.X)
if x.mode == invalid {
return
}
@ -717,7 +721,7 @@ func (check *checker) stmt(s ast.Stmt) {
check.optionalStmt(s.Init)
if s.Cond != nil {
var x operand
check.expr(&x, s.Cond, -1)
check.expr(&x, s.Cond)
if x.mode != invalid && !isBoolean(x.typ) {
check.errorf(s.Cond.Pos(), "non-boolean condition in for statement")
}
@ -731,7 +735,7 @@ func (check *checker) stmt(s ast.Stmt) {
// check expression to iterate over
decl := s.Tok == token.DEFINE
var x operand
check.expr(&x, s.X, -1)
check.expr(&x, s.X)
if x.mode == invalid {
// if we don't have a declaration, we can still check the loop's body
if !decl {
@ -792,7 +796,7 @@ func (check *checker) stmt(s ast.Stmt) {
if s.Key != nil {
x.typ = key
x.expr = s.Key
check.assign1to1(s.Key, nil, &x, decl, -1, false)
check.assign1to1(s.Key, nil, &x, decl, false)
} else {
check.invalidAST(s.Pos(), "range clause requires index iteration variable")
// ok to continue
@ -800,7 +804,7 @@ func (check *checker) stmt(s ast.Stmt) {
if s.Value != nil {
x.typ = val
x.expr = s.Value
check.assign1to1(s.Value, nil, &x, decl, -1, false)
check.assign1to1(s.Value, nil, &x, decl, false)
}
check.stmt(s.Body)

View File

@ -231,8 +231,43 @@ const (
type A [iota /* ERROR "cannot use iota" */ ]int
// constant expressions with operands accross different
// constant declarations must use the right iota values
const (
_c0 = iota
_c1
_c2
_x = _c2 + _d1 + _e0 // 3
)
const (
_d0 = iota
_d1
)
const (
_e0 = iota
)
var _ = assert(_x == 3)
// special cases
const (
_n0 = nil /* ERROR "invalid constant type" */
_n1 = [ /* ERROR "not constant" */ ]int{}
)
// iotas must not be usable in expressions outside constant declarations
type _ [iota /* ERROR "iota outside constant decl" */ ]byte
var _ = iota /* ERROR "iota outside constant decl" */
func _() {
_ = iota /* ERROR "iota outside constant decl" */
const _ = iota
_ = iota /* ERROR "iota outside constant decl" */
}
func _() {
iota := 123
const x = iota /* ERROR "is not constant" */
var y = iota
}

View File

@ -16,10 +16,9 @@ import (
// ident typechecks identifier e and initializes x with the value or type of e.
// If an error occurred, x.mode is set to invalid.
// iota >= 0 indicates that the expression is part of a constant declaration.
// For the meaning of def and cycleOk, see check.typ, below.
//
func (check *checker) ident(x *operand, e *ast.Ident, iota int, def *Named, cycleOk bool) {
func (check *checker) ident(x *operand, e *ast.Ident, def *Named, cycleOk bool) {
x.mode = invalid
x.expr = e
@ -56,11 +55,11 @@ func (check *checker) ident(x *operand, e *ast.Ident, iota int, def *Named, cycl
return
}
if obj == universeIota {
if iota < 0 {
check.invalidAST(e.Pos(), "cannot use iota outside constant declaration")
if check.iota == nil {
check.errorf(e.Pos(), "cannot use iota outside constant declaration")
return
}
x.val = exact.MakeInt64(int64(iota))
x.val = check.iota
} else {
x.val = obj.val // may be nil if we don't know the constant value
}
@ -93,14 +92,13 @@ func (check *checker) ident(x *operand, e *ast.Ident, iota int, def *Named, cycl
// typ typechecks the type expression e and initializes x with the type of e.
// If an error occurred, x.mode is set to invalid.
// iota >= 0 indicates that the expression is part of a constant declaration.
// If def != nil, e is the type specification for the named type def, declared
// in a type declaration, and def.underlying will be set to the type of e before
// any components of e are typechecked.
// If cycleOk is set, e (or elements of e) may refer to a named type that is not
// yet completely set up.
//
func (check *checker) typ(e ast.Expr, iota int, def *Named, cycleOk bool) (res Type) {
func (check *checker) typ(e ast.Expr, def *Named, cycleOk bool) (res Type) {
if trace {
check.trace(e.Pos(), "%s", e)
defer check.untrace("=> %s", res)
@ -117,7 +115,7 @@ func (check *checker) typ(e ast.Expr, iota int, def *Named, cycleOk bool) (res T
switch e := e.(type) {
case *ast.Ident:
var x operand
check.ident(&x, e, iota, def, cycleOk)
check.ident(&x, e, def, cycleOk)
switch x.mode {
case typexpr:
@ -132,7 +130,7 @@ func (check *checker) typ(e ast.Expr, iota int, def *Named, cycleOk bool) (res T
case *ast.SelectorExpr:
var x operand
check.selector(&x, e, iota)
check.selector(&x, e)
switch x.mode {
case typexpr:
@ -146,12 +144,12 @@ func (check *checker) typ(e ast.Expr, iota int, def *Named, cycleOk bool) (res T
}
case *ast.ParenExpr:
return check.typ(e.X, iota, def, cycleOk)
return check.typ(e.X, def, cycleOk)
case *ast.ArrayType:
if e.Len != nil {
var x operand
check.expr(&x, e.Len, iota)
check.expr(&x, e.Len)
if x.mode != constant {
if x.mode != invalid {
check.errorf(x.pos(), "array length %s must be constant", &x)
@ -174,7 +172,7 @@ func (check *checker) typ(e ast.Expr, iota int, def *Named, cycleOk bool) (res T
}
typ.len = n
typ.elt = check.typ(e.Elt, iota, nil, cycleOk)
typ.elt = check.typ(e.Elt, nil, cycleOk)
return typ
} else {
@ -183,7 +181,7 @@ func (check *checker) typ(e ast.Expr, iota int, def *Named, cycleOk bool) (res T
def.underlying = typ
}
typ.elt = check.typ(e.Elt, iota, nil, true)
typ.elt = check.typ(e.Elt, nil, true)
return typ
}
@ -202,7 +200,7 @@ func (check *checker) typ(e ast.Expr, iota int, def *Named, cycleOk bool) (res T
def.underlying = typ
}
typ.base = check.typ(e.X, iota, nil, true)
typ.base = check.typ(e.X, nil, true)
return typ
case *ast.FuncType:
@ -238,8 +236,8 @@ func (check *checker) typ(e ast.Expr, iota int, def *Named, cycleOk bool) (res T
def.underlying = typ
}
typ.key = check.typ(e.Key, iota, nil, true)
typ.elt = check.typ(e.Value, iota, nil, true)
typ.key = check.typ(e.Key, nil, true)
typ.elt = check.typ(e.Value, nil, true)
return typ
case *ast.ChanType:
@ -249,7 +247,7 @@ func (check *checker) typ(e ast.Expr, iota int, def *Named, cycleOk bool) (res T
}
typ.dir = e.Dir
typ.elt = check.typ(e.Value, iota, nil, true)
typ.elt = check.typ(e.Value, nil, true)
return typ
default:
@ -265,7 +263,7 @@ func (check *checker) typ(e ast.Expr, iota int, def *Named, cycleOk bool) (res T
//
func (check *checker) typOrNil(e ast.Expr) Type {
var x operand
check.rawExpr(&x, e, nil, -1)
check.rawExpr(&x, e, nil)
switch x.mode {
case invalid:
// ignore - error reported before
@ -302,7 +300,7 @@ func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicO
}
// the parser ensures that f.Tag is nil and we don't
// care if a constructed AST contains a non-nil tag
typ := check.typ(ftype, -1, nil, true)
typ := check.typ(ftype, nil, true)
if len(field.Names) > 0 {
// named parameter
for _, name := range field.Names {
@ -337,7 +335,7 @@ func (check *checker) collectMethods(list *ast.FieldList, cycleOk bool) (methods
}
scope := NewScope(nil)
for _, f := range list.List {
typ := check.typ(f.Type, -1, nil, cycleOk)
typ := check.typ(f.Type, nil, cycleOk)
// the parser ensures that f.Tag is nil and we don't
// care if a constructed AST contains a non-nil tag
if len(f.Names) > 0 {
@ -413,7 +411,7 @@ func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields [
}
for _, f := range list.List {
typ = check.typ(f.Type, -1, nil, cycleOk)
typ = check.typ(f.Type, nil, cycleOk)
tag = check.tag(f.Tag)
if len(f.Names) > 0 {
// named fields