From f0526543144487fe063201745babcbbf64cf8496 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 3 Jul 2013 10:06:09 -0700 Subject: [PATCH] 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 --- go/types/assignments.go | 4 +- go/types/builtins.go | 31 ++++++------- go/types/call.go | 18 ++++---- go/types/check.go | 1 + go/types/conversions.go | 10 ++-- go/types/expr.go | 89 ++++++++++++++++-------------------- go/types/resolver.go | 38 +++++++++++---- go/types/stmt.go | 62 +++++++++++++------------ go/types/testdata/const0.src | 35 ++++++++++++++ go/types/typexpr.go | 40 ++++++++-------- 10 files changed, 187 insertions(+), 141 deletions(-) diff --git a/go/types/assignments.go b/go/types/assignments.go index e5db6d20..398a9b67 100644 --- a/go/types/assignments.go +++ b/go/types/assignments.go @@ -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 } diff --git a/go/types/builtins.go b/go/types/builtins.go index ba102627..e6233cac 100644 --- a/go/types/builtins.go +++ b/go/types/builtins.go @@ -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 } diff --git a/go/types/call.go b/go/types/call.go index e85cf7bc..e977d83b 100644 --- a/go/types/call.go +++ b/go/types/call.go @@ -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 } diff --git a/go/types/check.go b/go/types/check.go index 474b4d81..027c06c2 100644 --- a/go/types/check.go +++ b/go/types/check.go @@ -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 diff --git a/go/types/conversions.go b/go/types/conversions.go index 87fe9342..3675c143 100644 --- a/go/types/conversions.go +++ b/go/types/conversions.go @@ -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 } diff --git a/go/types/expr.go b/go/types/expr.go index 9ae85c12..6f34b71c 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -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 diff --git a/go/types/resolver.go b/go/types/resolver.go index 26ba0129..13bf95dc 100644 --- a/go/types/resolver.go +++ b/go/types/resolver.go @@ -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 diff --git a/go/types/stmt.go b/go/types/stmt.go index 2ff5c995..3e749af7 100644 --- a/go/types/stmt.go +++ b/go/types/stmt.go @@ -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) diff --git a/go/types/testdata/const0.src b/go/types/testdata/const0.src index 66224baf..71c6664c 100644 --- a/go/types/testdata/const0.src +++ b/go/types/testdata/const0.src @@ -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 +} diff --git a/go/types/typexpr.go b/go/types/typexpr.go index 891afb4a..f0dda03f 100644 --- a/go/types/typexpr.go +++ b/go/types/typexpr.go @@ -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