go.tools/go/types: fix more cycle errors, lots of refactoring

- moved ident and typ expr checking into typexpr.go
- as a result, fewer parameters are needed for expr checking
- forward-chain type decls of the form type ( A B; B C; C *A) etc.
  so that cycles are getting the right types in all cases
- fixed several corner case bugs, added more test cases

R=adonovan
CC=golang-dev
https://golang.org/cl/10773043
This commit is contained in:
Robert Griesemer 2013-07-02 16:39:30 -07:00
parent 6ae930a01c
commit b58f98e9c2
13 changed files with 612 additions and 396 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, nil, -1)
check.expr(&x, e, -1)
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], nil, -1)
check.expr(&x, rhs[0], -1)
if x.mode == invalid {
goto Error
}

View File

@ -50,7 +50,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, nil, iota)
check.expr(x, arg0, iota)
if x.mode == invalid {
goto Error
}
@ -65,7 +65,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, nil, iota)
check.expr(x, arg, iota)
if x.mode == invalid {
goto Error
}
@ -134,7 +134,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
}
var y operand
check.expr(&y, args[1], nil, iota)
check.expr(&y, args[1], iota)
if y.mode == invalid {
goto Error
}
@ -196,7 +196,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
case _Copy:
var y operand
check.expr(&y, args[1], nil, iota)
check.expr(&y, args[1], iota)
if y.mode == invalid {
goto Error
}
@ -233,7 +233,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], nil, iota)
check.expr(x, args[1], iota)
if x.mode == invalid {
goto Error
}
@ -271,7 +271,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
x.typ = Typ[k]
case _Make:
resultTyp := check.typ(arg0, false)
resultTyp := check.typ(arg0, iota, nil, false)
if resultTyp == Typ[Invalid] {
goto Error
}
@ -303,7 +303,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
x.typ = resultTyp
case _New:
resultTyp := check.typ(arg0, false)
resultTyp := check.typ(arg0, iota, nil, false)
if resultTyp == Typ[Invalid] {
goto Error
}
@ -315,7 +315,7 @@ func (check *checker) builtin(x *operand, call *ast.CallExpr, bin *Builtin, iota
case _Print, _Println:
for _, arg := range args {
check.expr(x, arg, nil, -1)
check.expr(x, arg, iota)
if x.mode == invalid {
goto Error
}
@ -337,7 +337,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, nil, -1)
check.expr(x, arg.X, iota)
if x.mode == invalid {
goto Error
}
@ -397,7 +397,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, true) // permit trace for types, e.g.: new(trace(T))
check.rawExpr(x1, arg, nil, iota) // 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

@ -12,13 +12,13 @@ import (
)
func (check *checker) call(x *operand, e *ast.CallExpr, iota int) {
check.exprOrType(x, e.Fun, iota, false)
check.exprOrType(x, e.Fun, iota)
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, nil, iota)
check.expr(x, arg, iota)
}
goto Error
@ -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, nil, -1)
check.expr(x, call, iota)
if x.mode == invalid {
goto Error // TODO(gri): we can do better
}
@ -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, z.typ, -1)
check.expr(x, arg, -1)
}
if x.mode == invalid {
return // ignore this argument
@ -230,7 +230,7 @@ func (check *checker) selector(x *operand, e *ast.SelectorExpr, iota int) {
}
}
check.exprOrType(x, e.X, iota, false)
check.exprOrType(x, e.X, iota)
if x.mode == invalid {
goto Error
}

View File

@ -25,7 +25,7 @@ func (check *checker) conversion(x *operand, conv *ast.CallExpr, typ Type, iota
}
// evaluate argument
check.expr(x, conv.Args[0], nil, iota)
check.expr(x, conv.Args[0], iota)
if x.mode == invalid {
goto Error
}

View File

@ -9,7 +9,6 @@ package types
import (
"go/ast"
"go/token"
"strconv"
"code.google.com/p/go.tools/go/exact"
)
@ -69,175 +68,6 @@ on the way down in updateExprType, or at the end of the type checker run,
if present the Context.Expr method is invoked to notify a go/types client.
*/
func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) {
if list == nil {
return
}
var last *Var
for i, field := range list.List {
ftype := field.Type
if t, _ := ftype.(*ast.Ellipsis); t != nil {
ftype = t.Elt
if variadicOk && i == len(list.List)-1 {
isVariadic = true
} else {
check.invalidAST(field.Pos(), "... not permitted")
// ok to continue
}
}
// 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, true)
if len(field.Names) > 0 {
// named parameter
for _, name := range field.Names {
par := NewVar(name.Pos(), check.pkg, name.Name, typ)
check.declare(scope, name, par)
last = par
copy := *par
params = append(params, &copy)
}
} else {
// anonymous parameter
par := NewVar(ftype.Pos(), check.pkg, "", typ)
check.callImplicitObj(field, par)
last = nil // not accessible inside function
params = append(params, par)
}
}
// For a variadic function, change the last parameter's object type
// from T to []T (this is the type used inside the function), but
// keep the params list unchanged (this is the externally visible type).
if isVariadic && last != nil {
last.typ = &Slice{elt: last.typ}
}
return
}
func (check *checker) collectMethods(list *ast.FieldList, cycleOk bool) (methods []*Func) {
if list == nil {
return nil
}
scope := NewScope(nil)
for _, f := range list.List {
typ := check.typ(f.Type, 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 {
// methods (the parser ensures that there's only one
// and we don't care if a constructed AST has more)
sig, ok := typ.(*Signature)
if !ok {
check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ)
continue
}
for _, name := range f.Names {
m := NewFunc(name.Pos(), check.pkg, name.Name, sig)
check.declare(scope, name, m)
methods = append(methods, m)
}
} else {
// embedded interface
switch t := typ.Underlying().(type) {
case nil:
// The underlying type is in the process of being defined
// but we need it in order to complete this type. For now
// complain with an "unimplemented" error. This requires
// a bit more work.
// TODO(gri) finish this.
check.errorf(f.Type.Pos(), "reference to incomplete type %s - unimplemented", f.Type)
case *Interface:
for _, m := range t.methods {
check.declare(scope, nil, m)
methods = append(methods, m)
}
default:
if t != Typ[Invalid] {
check.errorf(f.Type.Pos(), "%s is not an interface type", typ)
}
}
}
}
return
}
func (check *checker) tag(t *ast.BasicLit) string {
if t != nil {
if t.Kind == token.STRING {
if val, err := strconv.Unquote(t.Value); err == nil {
return val
}
}
check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value)
}
return ""
}
func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field, tags []string) {
if list == nil {
return
}
scope := NewScope(nil)
var typ Type // current field typ
var tag string // current field tag
add := func(field *ast.Field, ident *ast.Ident, name string, anonymous bool, pos token.Pos) {
if tag != "" && tags == nil {
tags = make([]string, len(fields))
}
if tags != nil {
tags = append(tags, tag)
}
fld := NewField(pos, check.pkg, name, typ, anonymous)
check.declare(scope, ident, fld)
fields = append(fields, fld)
}
for _, f := range list.List {
typ = check.typ(f.Type, cycleOk)
tag = check.tag(f.Tag)
if len(f.Names) > 0 {
// named fields
for _, name := range f.Names {
add(f, name, name.Name, false, name.Pos())
}
} else {
// anonymous field
pos := f.Type.Pos()
t, isPtr := deref(typ)
switch t := t.(type) {
case *Basic:
add(f, nil, t.name, true, pos)
case *Named:
// spec: "An embedded type must be specified as a type name
// T or as a pointer to a non-interface type name *T, and T
// itself may not be a pointer type."
switch t.underlying.(type) {
case *Pointer:
check.errorf(pos, "anonymous field type cannot be a pointer")
continue // ignore this field
case *Interface:
if isPtr {
check.errorf(pos, "anonymous field type cannot be a pointer to an interface")
continue // ignore this field
}
}
add(f, nil, t.obj.name, true, pos)
default:
if typ != Typ[Invalid] {
check.invalidAST(pos, "anonymous field type %s must be named", typ)
}
}
}
}
return
}
type opPredicates map[token.Token]func(Type) bool
var unaryOpPredicates = opPredicates{
@ -784,8 +614,8 @@ var binaryOpPredicates = opPredicates{
func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token, iota int) {
var y operand
check.expr(x, lhs, nil, iota)
check.expr(&y, rhs, nil, iota)
check.expr(x, lhs, iota)
check.expr(&y, rhs, iota)
if x.mode == invalid {
return
@ -859,7 +689,7 @@ func (check *checker) binary(x *operand, lhs, rhs ast.Expr, op token.Token, iota
// TODO(gri): Do we need iota?
func (check *checker) index(arg ast.Expr, length int64, iota int) (i int64, ok bool) {
var x operand
check.expr(&x, arg, nil, iota)
check.expr(&x, arg, iota)
// an untyped constant must be representable as Int
check.convertUntyped(&x, Typ[Int])
@ -931,7 +761,7 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota
// check element against composite literal element type
var x operand
check.expr(&x, eval, typ, iota)
check.exprWithHint(&x, eval, typ, iota)
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)
}
@ -939,7 +769,11 @@ func (check *checker) indexedElts(elts []ast.Expr, typ Type, length int64, iota
return max
}
func (check *checker) callExpr(x *operand) {
func (check *checker) callExpr(x *operand, ignore *bool) {
if *ignore {
return
}
// convert x into a user-friendly set of values
var typ Type
var val exact.Value
@ -977,86 +811,28 @@ func (check *checker) callExpr(x *operand) {
// 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.
// cycleOk indicates whether it is ok for a type expression to refer to itself.
//
func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycleOk bool) {
func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int) {
// make sure x has a valid state for deferred functions in case of bailout
// (was issue 5770)
x.mode = invalid
x.typ = Typ[Invalid]
if trace {
c := ""
if cycleOk {
c = " ⨁"
}
check.trace(e.Pos(), "%s%s", e, c)
check.trace(e.Pos(), "%s", e)
defer check.untrace("=> %s", x)
}
// record final type of x if untyped, notify clients of type otherwise
defer check.callExpr(x)
ignore := false // if set, don't do anything in the deferred call
defer check.callExpr(x, &ignore)
switch e := e.(type) {
case *ast.BadExpr:
goto Error // error was reported before
case *ast.Ident:
obj := check.topScope.LookupParent(e.Name)
check.callIdent(e, obj)
if obj == nil {
if e.Name == "_" {
check.errorf(e.Pos(), "cannot use _ as value or type")
} else {
check.errorf(e.Pos(), "undeclared name: %s", e.Name)
}
goto Error // error was reported before
}
typ := obj.Type()
if check.objMap == nil {
if typ == nil {
check.dump("%s: %s not declared?", e.Pos(), e)
}
assert(typ != nil)
} else if typ == nil {
check.declareObject(obj, cycleOk)
}
switch obj := obj.(type) {
case *Package:
check.errorf(e.Pos(), "use of package %s not in selector", obj.name)
goto Error
case *Const:
if obj.typ == Typ[Invalid] {
goto Error
}
x.mode = constant
if obj == universeIota {
if iota < 0 {
check.invalidAST(e.Pos(), "cannot use iota outside constant declaration")
goto Error
}
x.val = exact.MakeInt64(int64(iota))
} else {
x.val = obj.val // may be nil if we don't know the constant value
}
case *TypeName:
x.mode = typexpr
if !cycleOk && obj.typ.Underlying() == nil {
check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name)
x.expr = e
x.typ = Typ[Invalid]
return // don't goto Error - want x.mode == typexpr
}
case *Var:
x.mode = variable
case *Func:
x.mode = value
default:
unreachable()
}
x.typ = obj.Type()
check.ident(x, e, iota, nil, false)
case *ast.Ellipsis:
// ellipses are handled explicitly where they are legal
@ -1072,7 +848,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
}
case *ast.FuncLit:
if sig, ok := check.typ(e.Type, false).(*Signature); ok {
if sig, ok := check.typ(e.Type, iota, nil, false).(*Signature); ok {
x.mode = value
x.typ = sig
check.later(nil, sig, e.Body)
@ -1093,12 +869,12 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
// 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, cycleOk)}
typ = &Array{len: -1, elt: check.typ(atyp.Elt, iota, nil, false)}
openArray = true
}
}
if typ == nil {
typ = check.typ(e.Type, false)
typ = check.typ(e.Type, iota, nil, false)
}
}
if typ == nil {
@ -1139,7 +915,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
continue
}
visited[i] = true
check.expr(x, kv.Value, nil, iota)
check.expr(x, kv.Value, iota)
etyp := fld.typ
if !check.assignment(x, etyp) {
if x.mode != invalid {
@ -1155,7 +931,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
check.errorf(kv.Pos(), "mixture of field:value and value elements in struct literal")
continue
}
check.expr(x, e, nil, iota)
check.expr(x, e, iota)
if i >= len(fields) {
check.errorf(x.pos(), "too many values in struct literal")
break // cannot continue
@ -1193,7 +969,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
check.errorf(e.Pos(), "missing key in map literal")
continue
}
check.expr(x, kv.Key, nil, iota)
check.expr(x, kv.Key, iota)
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)
@ -1207,7 +983,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
}
visited[x.val] = true
}
check.expr(x, kv.Value, utyp.elt, iota)
check.exprWithHint(x, kv.Value, utyp.elt, iota)
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)
@ -1225,13 +1001,13 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
x.typ = typ
case *ast.ParenExpr:
check.rawExpr(x, e.X, nil, iota, cycleOk)
check.rawExpr(x, e.X, nil, iota)
case *ast.SelectorExpr:
check.selector(x, e, iota)
case *ast.IndexExpr:
check.expr(x, e.X, nil, iota)
check.expr(x, e.X, iota)
if x.mode == invalid {
goto Error
}
@ -1275,7 +1051,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
case *Map:
var key operand
check.expr(&key, e.Index, nil, iota)
check.expr(&key, e.Index, iota)
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)
@ -1302,7 +1078,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
// ok to continue
case *ast.SliceExpr:
check.expr(x, e.X, nil, iota)
check.expr(x, e.X, iota)
if x.mode == invalid {
goto Error
}
@ -1378,7 +1154,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
}
case *ast.TypeAssertExpr:
check.expr(x, e.X, nil, iota)
check.expr(x, e.X, iota)
if x.mode == invalid {
goto Error
}
@ -1392,7 +1168,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
check.invalidAST(e.Pos(), "use of .(type) outside type switch")
goto Error
}
typ := check.typ(e.Type, false)
typ := check.typ(e.Type, iota, nil, false)
if typ == Typ[Invalid] {
goto Error
}
@ -1419,7 +1195,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
check.call(x, e, iota)
case *ast.StarExpr:
check.exprOrType(x, e.X, iota, true)
check.exprOrType(x, e.X, iota)
switch x.mode {
case invalid:
goto Error
@ -1436,7 +1212,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
}
case *ast.UnaryExpr:
check.expr(x, e.X, nil, iota)
check.expr(x, e.X, iota)
if x.mode == invalid {
goto Error
}
@ -1456,58 +1232,13 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle
check.invalidAST(e.Pos(), "no key:value expected")
goto Error
case *ast.ArrayType:
if e.Len != nil {
check.expr(x, e.Len, nil, iota)
if x.mode == invalid {
goto Error
}
if x.mode != constant {
if x.mode != invalid {
check.errorf(x.pos(), "array length %s must be constant", x)
}
goto Error
}
if !x.isInteger() {
check.errorf(x.pos(), "array length %s must be integer", x)
goto Error
}
n, ok := exact.Int64Val(x.val)
if !ok || n < 0 {
check.errorf(x.pos(), "invalid array length %s", x)
goto Error
}
x.typ = &Array{len: n, elt: check.typ(e.Elt, cycleOk)}
} else {
x.typ = &Slice{elt: check.typ(e.Elt, true)}
}
case *ast.ArrayType, *ast.StructType, *ast.FuncType,
*ast.InterfaceType, *ast.MapType, *ast.ChanType:
x.mode = typexpr
case *ast.StructType:
x.mode = typexpr
x.typ = NewStruct(check.collectFields(e.Fields, cycleOk))
case *ast.FuncType:
scope := NewScope(check.topScope)
if retainASTLinks {
scope.node = e
}
params, isVariadic := check.collectParams(scope, e.Params, true)
results, _ := check.collectParams(scope, e.Results, false)
x.mode = typexpr
x.typ = &Signature{scope: scope, recv: nil, params: NewTuple(params...), results: NewTuple(results...), isVariadic: isVariadic}
case *ast.InterfaceType:
x.mode = typexpr
x.typ = NewInterface(check.collectMethods(e.Methods, cycleOk))
case *ast.MapType:
x.mode = typexpr
x.typ = &Map{key: check.typ(e.Key, true), elt: check.typ(e.Value, true)}
case *ast.ChanType:
x.mode = typexpr
x.typ = &Chan{dir: e.Dir, elt: check.typ(e.Value, true)}
x.typ = check.typ(e, iota, nil, false)
// check.typ is already notifying clients
// of e's type; don't do it a 2nd time
ignore = true
default:
if debug {
@ -1525,18 +1256,12 @@ Error:
x.expr = e
}
// exprOrType is like rawExpr but reports an error if e doesn't represents a value or type.
func (check *checker) exprOrType(x *operand, e ast.Expr, iota int, cycleOk bool) {
check.rawExpr(x, e, nil, iota, cycleOk)
if x.mode == novalue {
check.errorf(x.pos(), "%s used as value or type", x)
x.mode = invalid
}
}
// expr is like rawExpr but reports an error if e doesn't represents a value.
func (check *checker) expr(x *operand, e ast.Expr, hint Type, iota int) {
check.rawExpr(x, e, hint, iota, false)
// 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)
switch x.mode {
case novalue:
check.errorf(x.pos(), "%s used as value", x)
@ -1547,37 +1272,32 @@ func (check *checker) expr(x *operand, e ast.Expr, hint Type, iota int) {
}
}
func (check *checker) rawTyp(e ast.Expr, cycleOk, nilOk bool) Type {
var x operand
check.rawExpr(&x, e, nil, -1, cycleOk)
// 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) {
assert(hint != nil)
check.rawExpr(x, e, hint, iota)
switch x.mode {
case invalid:
// ignore - error reported before
case novalue:
check.errorf(x.pos(), "%s used as type", &x)
check.errorf(x.pos(), "%s used as value", x)
x.mode = invalid
case typexpr:
return x.typ
case constant:
if nilOk && x.isNil() {
return nil
}
fallthrough
default:
check.errorf(x.pos(), "%s is not a type", &x)
check.errorf(x.pos(), "%s is not an expression", x)
x.mode = invalid
}
return Typ[Invalid]
}
// typOrNil is like rawExpr but reports an error if e doesn't represents a type or the predeclared value nil.
// It returns e's type, nil, or Typ[Invalid] if an error occurred.
// 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) typOrNil(e ast.Expr, cycleOk bool) Type {
return check.rawTyp(e, cycleOk, true)
}
// typ is like rawExpr but reports an error if e doesn't represents a type.
// It returns e's type, or Typ[Invalid] if an error occurred.
//
func (check *checker) typ(e ast.Expr, cycleOk bool) Type {
return check.rawTyp(e, cycleOk, false)
func (check *checker) exprOrType(x *operand, e ast.Expr, iota int) {
check.rawExpr(x, e, nil, iota)
if x.mode == novalue {
check.errorf(x.pos(), "%s used as value or type", x)
x.mode = invalid
}
}

View File

@ -326,7 +326,7 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
check.topScope = pkg.scope
for _, obj := range objList {
if obj.Type() == nil {
check.declareObject(obj, false)
check.declareObject(obj, nil, false)
}
}
check.objMap = nil // done with global declarations
@ -335,7 +335,9 @@ func (check *checker) resolveFiles(files []*ast.File, importer Importer) {
// - done by the caller for now
}
func (check *checker) declareObject(obj Object, cycleOk bool) {
// declareObject declares obj in the top-most scope.
// See declareType for the details on def and cycleOk.
func (check *checker) declareObject(obj Object, def *Named, cycleOk bool) {
d := check.objMap[obj]
// adjust file scope for current object
@ -348,7 +350,7 @@ func (check *checker) declareObject(obj Object, cycleOk bool) {
case *Var:
check.declareVar(obj, d.typ, d.init)
case *TypeName:
check.declareType(obj, d.typ, cycleOk)
check.declareType(obj, d.typ, def, cycleOk)
case *Func:
check.declareFunc(obj)
default:
@ -367,11 +369,10 @@ func (check *checker) declareConst(obj *Const, typ, init ast.Expr) {
obj.visited = true
iota, ok := exact.Int64Val(obj.val) // set in phase 1
assert(ok)
// obj.val = exact.MakeUnknown() //do we need this? should we set val to nil?
// determine type, if any
if typ != nil {
obj.typ = check.typ(typ, false)
obj.typ = check.typ(typ, int(iota), nil, false)
}
var x operand
@ -380,7 +381,7 @@ func (check *checker) declareConst(obj *Const, typ, init ast.Expr) {
goto Error // error reported before
}
check.expr(&x, init, nil, int(iota))
check.expr(&x, init, int(iota))
if x.mode == invalid {
goto Error
}
@ -406,7 +407,7 @@ func (check *checker) declareVar(obj *Var, typ, init ast.Expr) {
// determine type, if any
if typ != nil {
obj.typ = check.typ(typ, false)
obj.typ = check.typ(typ, -1, nil, false)
}
if init == nil {
@ -423,7 +424,7 @@ func (check *checker) declareVar(obj *Var, typ, init ast.Expr) {
}
var x operand
check.expr(&x, init, nil, -1)
check.expr(&x, init, -1)
if x.mode == invalid {
goto Error
}
@ -473,10 +474,46 @@ Error:
return
}
func (check *checker) declareType(obj *TypeName, typ ast.Expr, cycleOk bool) {
func (check *checker) declareType(obj *TypeName, typ ast.Expr, def *Named, cycleOk bool) {
assert(obj.Type() == nil)
named := &Named{obj: obj}
obj.typ = named // mark object so recursion terminates in case of cycles
named.underlying = check.typ(typ, cycleOk).Underlying()
obj.typ = named // make sure recursive type declarations terminate
// If this type (named) defines the type of another (def) type declaration,
// set def's underlying type to this type so that we can resolve the true
// underlying of def later.
if def != nil {
def.underlying = named
}
// Typecheck typ - it may be a named type that is not yet complete.
// For instance, consider:
//
// type (
// A B
// B *C
// C A
// )
//
// When we declare obj = C, typ is the identifier A which is incomplete.
u := check.typ(typ, -1, named, cycleOk)
// Determine the unnamed underlying type.
// In the above example, the underlying type of A was (temporarily) set
// to B whose underlying type was set to *C. Such "forward chains" always
// end in an unnamed type (cycles are terminated with an invalid type).
for {
n, _ := u.(*Named)
if n == nil {
break
}
u = n.underlying
}
named.underlying = u
// the underlying type has been determined
named.complete = true
// typecheck associated method signatures
if scope := check.methods[obj]; scope != nil {
@ -512,7 +549,7 @@ func (check *checker) declareType(obj *TypeName, typ ast.Expr, cycleOk bool) {
oldScope := check.topScope
check.topScope = fileScope
sig := check.typ(m.decl.Type, cycleOk).(*Signature)
sig := check.typ(m.decl.Type, -1, nil, cycleOk).(*Signature)
params, _ := check.collectParams(sig.scope, m.decl.Recv, false)
check.topScope = oldScope // reset topScope
@ -534,7 +571,7 @@ func (check *checker) declareFunc(obj *Func) {
// 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, false).(*Signature)
sig := check.typ(fdecl.Type, -1, 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
@ -643,7 +680,7 @@ func (check *checker) declStmt(decl ast.Decl) {
case *ast.TypeSpec:
obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
check.declare(check.topScope, s.Name, obj)
check.declareType(obj, s.Type, false)
check.declareType(obj, s.Type, nil, false)
default:
check.invalidAST(s.Pos(), "const, type, or var declaration expected")

View File

@ -48,7 +48,7 @@ func (check *checker) assign1to1(lhs, rhs ast.Expr, x *operand, decl bool, iota
// for declarations with implicit type.
if x == nil {
x = new(operand)
check.expr(x, rhs, nil, iota)
check.expr(x, rhs, iota)
// 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, nil, -1)
check.expr(&z, lhs, -1)
if z.mode == invalid || z.typ == Typ[Invalid] {
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], nil, iota)
check.expr(&x, rhs[0], iota)
if x.mode == invalid {
goto Error
}
@ -335,7 +335,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, nil, -1)
check.expr(&x, e.Fun, -1)
if x.mode != invalid {
if b, ok := x.typ.(*Builtin); ok && !b.isStatement {
used = false
@ -351,15 +351,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, false)
check.rawExpr(&x, s.X, nil, -1)
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, nil, -1)
check.expr(&x, s.Value, nil, -1)
check.expr(&ch, s.Chan, -1)
check.expr(&x, s.Value, -1)
if ch.mode == invalid || x.mode == invalid {
return
}
@ -526,7 +526,7 @@ func (check *checker) stmt(s ast.Stmt) {
check.openScope(s)
check.optionalStmt(s.Init)
var x operand
check.expr(&x, s.Cond, nil, -1)
check.expr(&x, s.Cond, -1)
if x.mode != invalid && !isBoolean(x.typ) {
check.errorf(s.Cond.Pos(), "non-boolean condition in if statement")
}
@ -545,7 +545,7 @@ func (check *checker) stmt(s ast.Stmt) {
check.callIdent(ident, Universe.Lookup(nil, "true"))
tag = ident
}
check.expr(&x, tag, nil, -1)
check.expr(&x, tag, -1)
check.multipleDefaults(s.Body.List)
// TODO(gri) check also correct use of fallthrough
@ -559,7 +559,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, nil, -1)
check.expr(&y, expr, -1)
if y.mode == invalid {
continue // error reported before
}
@ -645,7 +645,7 @@ func (check *checker) stmt(s ast.Stmt) {
return
}
var x operand
check.expr(&x, expr.X, nil, -1)
check.expr(&x, expr.X, -1)
if x.mode == invalid {
return
}
@ -670,7 +670,7 @@ func (check *checker) stmt(s ast.Stmt) {
// Check each type in this type switch case.
var typ Type
for _, expr := range clause.List {
typ = check.typOrNil(expr, false)
typ = check.typOrNil(expr)
if typ != nil && typ != Typ[Invalid] {
if method, wrongType := MissingMethod(typ, T); method != nil {
var msg string
@ -717,7 +717,7 @@ func (check *checker) stmt(s ast.Stmt) {
check.optionalStmt(s.Init)
if s.Cond != nil {
var x operand
check.expr(&x, s.Cond, nil, -1)
check.expr(&x, s.Cond, -1)
if x.mode != invalid && !isBoolean(x.typ) {
check.errorf(s.Cond.Pos(), "non-boolean condition in for statement")
}
@ -731,7 +731,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, nil, -1)
check.expr(&x, s.X, -1)
if x.mode == invalid {
// if we don't have a declaration, we can still check the loop's body
if !decl {

View File

@ -224,10 +224,15 @@ const (
const (
_b0 = iota
_b1 = assert(iota + iota2 == 5)
_b2 = len([iota]int{}) // iota may appear in a type!
_b3 = assert(_b2 == 2)
_b4 = len(A /* ERROR "not a valid composite literal" */ {})
)
type A [iota /* ERROR "cannot use iota" */ ]int
// special cases
const (
_n0 = nil /* ERROR "invalid constant type" */
_n1 = [ /* ERROR "not constant" */ ]int{}
)
)

View File

@ -59,9 +59,8 @@ type (
I4 interface{ f(I4) }
// testcase for issue 5090
// TODO(gri) fix this
I5 interface{ f(I6) }
I6 interface{ I5 /* ERROR "unimplemented" */ }
I6 interface{ I5 }
// maps
M0 map[M0]M0

View File

@ -188,9 +188,9 @@ type (
// cycles in function/method declarations
// (test cases for issue 5217 and variants)
func f1(x f1 /* ERROR "not a type" */ ) {}
func f2(x *f2 /* ERROR "cannot indirect" */ ) {}
func f2(x *f2 /* ERROR "not a type" */ ) {}
func f3() (x f3 /* ERROR "not a type" */ ) { return }
func f4() (x *f4 /* ERROR "cannot indirect" */ ) { return }
func f4() (x *f4 /* ERROR "not a type" */ ) { return }
func (S0) m1(x S0 /* ERROR "field or method" */ .m1) {}
func (S0) m2(x *S0 /* ERROR "field or method" */ .m2) {}

View File

@ -326,7 +326,8 @@ func (c *Chan) Elem() Type { return c.elt }
// A Named represents a named type.
type Named struct {
obj *TypeName // corresponding declared object
underlying Type // nil if not fully declared yet; never a *Named
underlying Type // possibly a *Named if !complete; never a *Named if complete
complete bool // if set, the underlying type has been determined
methods []*Func // methods declared for this type (not the method set of this type)
}
@ -337,7 +338,7 @@ func NewNamed(obj *TypeName, underlying Type, methods []*Func) *Named {
if _, ok := underlying.(*Named); ok {
panic("types.NewNamed: underlying type must not be *Named")
}
typ := &Named{obj, underlying, methods}
typ := &Named{obj, underlying, true, methods}
if obj.typ == nil {
obj.typ = typ
}

454
go/types/typexpr.go Normal file
View File

@ -0,0 +1,454 @@
// 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.
// This file implements typechecking of identifiers and type expressions.
package types
import (
"go/ast"
"go/token"
"strconv"
"code.google.com/p/go.tools/go/exact"
)
// 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) {
x.mode = invalid
x.expr = e
obj := check.topScope.LookupParent(e.Name)
check.callIdent(e, obj)
if obj == nil {
if e.Name == "_" {
check.errorf(e.Pos(), "cannot use _ as value or type")
} else {
check.errorf(e.Pos(), "undeclared name: %s", e.Name)
}
return
}
typ := obj.Type()
if typ == nil {
// object not yet declared
if check.objMap == nil {
check.dump("%s: %s should have been declared (we are inside a function)", e.Pos(), e)
unreachable()
}
check.declareObject(obj, def, cycleOk)
typ = obj.Type()
}
assert(typ != nil)
switch obj := obj.(type) {
case *Package:
check.errorf(e.Pos(), "use of package %s not in selector", obj.name)
return
case *Const:
if typ == Typ[Invalid] {
return
}
if obj == universeIota {
if iota < 0 {
check.invalidAST(e.Pos(), "cannot use iota outside constant declaration")
return
}
x.val = exact.MakeInt64(int64(iota))
} else {
x.val = obj.val // may be nil if we don't know the constant value
}
x.mode = constant
case *TypeName:
x.mode = typexpr
named, _ := typ.(*Named)
if !cycleOk && named != nil && !named.complete {
check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name)
// maintain x.mode == typexpr despite error
typ = Typ[Invalid]
}
if def != nil {
def.underlying = typ
}
case *Var:
x.mode = variable
case *Func:
x.mode = value
default:
unreachable()
}
x.typ = typ
}
// 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) {
if trace {
check.trace(e.Pos(), "%s", e)
defer check.untrace("=> %s", res)
}
// notify clients of type
if f := check.ctxt.Expr; f != nil {
defer func() {
assert(e != nil && res != nil && !isUntyped(res))
f(e, res, nil)
}()
}
switch e := e.(type) {
case *ast.Ident:
var x operand
check.ident(&x, e, iota, def, cycleOk)
switch x.mode {
case typexpr:
return x.typ
case invalid:
// ignore - error reported before
case novalue:
check.errorf(x.pos(), "%s used as type", &x)
default:
check.errorf(x.pos(), "%s is not a type", &x)
}
case *ast.SelectorExpr:
var x operand
check.selector(&x, e, iota)
switch x.mode {
case typexpr:
return x.typ
case invalid:
// ignore - error reported before
case novalue:
check.errorf(x.pos(), "%s used as type", &x)
default:
check.errorf(x.pos(), "%s is not a type", &x)
}
case *ast.ParenExpr:
return check.typ(e.X, iota, def, cycleOk)
case *ast.ArrayType:
if e.Len != nil {
var x operand
check.expr(&x, e.Len, iota)
if x.mode != constant {
if x.mode != invalid {
check.errorf(x.pos(), "array length %s must be constant", &x)
}
break
}
if !x.isInteger() {
check.errorf(x.pos(), "array length %s must be integer", &x)
break
}
n, ok := exact.Int64Val(x.val)
if !ok || n < 0 {
check.errorf(x.pos(), "invalid array length %s", &x)
break
}
typ := new(Array)
if def != nil {
def.underlying = typ
}
typ.len = n
typ.elt = check.typ(e.Elt, iota, nil, cycleOk)
return typ
} else {
typ := new(Slice)
if def != nil {
def.underlying = typ
}
typ.elt = check.typ(e.Elt, iota, nil, true)
return typ
}
case *ast.StructType:
typ := new(Struct)
if def != nil {
def.underlying = typ
}
typ.fields, typ.tags = check.collectFields(e.Fields, cycleOk)
return typ
case *ast.StarExpr:
typ := new(Pointer)
if def != nil {
def.underlying = typ
}
typ.base = check.typ(e.X, iota, nil, true)
return typ
case *ast.FuncType:
typ := new(Signature)
if def != nil {
def.underlying = typ
}
scope := NewScope(check.topScope)
if retainASTLinks {
scope.node = e
}
typ.scope = scope
params, isVariadic := check.collectParams(scope, e.Params, true)
results, _ := check.collectParams(scope, e.Results, false)
typ.params = NewTuple(params...)
typ.results = NewTuple(results...)
typ.isVariadic = isVariadic
return typ
case *ast.InterfaceType:
typ := new(Interface)
if def != nil {
def.underlying = typ
}
typ.methods = check.collectMethods(e.Methods, cycleOk)
return typ
case *ast.MapType:
typ := new(Map)
if def != nil {
def.underlying = typ
}
typ.key = check.typ(e.Key, iota, nil, true)
typ.elt = check.typ(e.Value, iota, nil, true)
return typ
case *ast.ChanType:
typ := new(Chan)
if def != nil {
def.underlying = typ
}
typ.dir = e.Dir
typ.elt = check.typ(e.Value, iota, nil, true)
return typ
default:
check.errorf(e.Pos(), "%s is not a type", e)
}
return Typ[Invalid]
}
// typeOrNil typechecks the type expression (or nil value) e
// and returns the typ of e, or nil.
// If e is neither a type nor nil, typOrNil returns Typ[Invalid].
//
func (check *checker) typOrNil(e ast.Expr) Type {
var x operand
check.rawExpr(&x, e, nil, -1)
switch x.mode {
case invalid:
// ignore - error reported before
case novalue:
check.errorf(x.pos(), "%s used as type", &x)
case typexpr:
return x.typ
case constant:
if x.isNil() {
return nil
}
fallthrough
default:
check.errorf(x.pos(), "%s is not a type", &x)
}
return Typ[Invalid]
}
func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, isVariadic bool) {
if list == nil {
return
}
var last *Var
for i, field := range list.List {
ftype := field.Type
if t, _ := ftype.(*ast.Ellipsis); t != nil {
ftype = t.Elt
if variadicOk && i == len(list.List)-1 {
isVariadic = true
} else {
check.invalidAST(field.Pos(), "... not permitted")
// ok to continue
}
}
// 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)
if len(field.Names) > 0 {
// named parameter
for _, name := range field.Names {
par := NewVar(name.Pos(), check.pkg, name.Name, typ)
check.declare(scope, name, par)
last = par
copy := *par
params = append(params, &copy)
}
} else {
// anonymous parameter
par := NewVar(ftype.Pos(), check.pkg, "", typ)
check.callImplicitObj(field, par)
last = nil // not accessible inside function
params = append(params, par)
}
}
// For a variadic function, change the last parameter's object type
// from T to []T (this is the type used inside the function), but
// keep the params list unchanged (this is the externally visible type).
if isVariadic && last != nil {
last.typ = &Slice{elt: last.typ}
}
return
}
func (check *checker) collectMethods(list *ast.FieldList, cycleOk bool) (methods []*Func) {
if list == nil {
return nil
}
scope := NewScope(nil)
for _, f := range list.List {
typ := check.typ(f.Type, -1, 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 {
// methods (the parser ensures that there's only one
// and we don't care if a constructed AST has more)
sig, ok := typ.(*Signature)
if !ok {
check.invalidAST(f.Type.Pos(), "%s is not a method signature", typ)
continue
}
for _, name := range f.Names {
m := NewFunc(name.Pos(), check.pkg, name.Name, sig)
check.declare(scope, name, m)
methods = append(methods, m)
}
} else {
// embedded interface
switch t := typ.Underlying().(type) {
case nil:
// The underlying type is in the process of being defined
// but we need it in order to complete this type. For now
// complain with an "unimplemented" error. This requires
// a bit more work.
// TODO(gri) finish this.
check.errorf(f.Type.Pos(), "reference to incomplete type %s - unimplemented", f.Type)
case *Interface:
for _, m := range t.methods {
check.declare(scope, nil, m)
methods = append(methods, m)
}
default:
if t != Typ[Invalid] {
check.errorf(f.Type.Pos(), "%s is not an interface type", typ)
}
}
}
}
return
}
func (check *checker) tag(t *ast.BasicLit) string {
if t != nil {
if t.Kind == token.STRING {
if val, err := strconv.Unquote(t.Value); err == nil {
return val
}
}
check.invalidAST(t.Pos(), "incorrect tag syntax: %q", t.Value)
}
return ""
}
func (check *checker) collectFields(list *ast.FieldList, cycleOk bool) (fields []*Field, tags []string) {
if list == nil {
return
}
scope := NewScope(nil)
var typ Type // current field typ
var tag string // current field tag
add := func(field *ast.Field, ident *ast.Ident, name string, anonymous bool, pos token.Pos) {
if tag != "" && tags == nil {
tags = make([]string, len(fields))
}
if tags != nil {
tags = append(tags, tag)
}
fld := NewField(pos, check.pkg, name, typ, anonymous)
check.declare(scope, ident, fld)
fields = append(fields, fld)
}
for _, f := range list.List {
typ = check.typ(f.Type, -1, nil, cycleOk)
tag = check.tag(f.Tag)
if len(f.Names) > 0 {
// named fields
for _, name := range f.Names {
add(f, name, name.Name, false, name.Pos())
}
} else {
// anonymous field
pos := f.Type.Pos()
t, isPtr := deref(typ)
switch t := t.(type) {
case *Basic:
add(f, nil, t.name, true, pos)
case *Named:
// spec: "An embedded type must be specified as a type name
// T or as a pointer to a non-interface type name *T, and T
// itself may not be a pointer type."
switch t.Underlying().(type) {
case *Pointer:
check.errorf(pos, "anonymous field type cannot be a pointer")
continue // ignore this field
case *Interface:
if isPtr {
check.errorf(pos, "anonymous field type cannot be a pointer to an interface")
continue // ignore this field
}
}
add(f, nil, t.obj.name, true, pos)
default:
if typ != Typ[Invalid] {
check.invalidAST(pos, "anonymous field type %s must be named", typ)
}
}
}
}
return
}

View File

@ -103,7 +103,7 @@ func init() {
// Error has a nil package in its qualified name since it is in no package
sig := &Signature{results: NewTuple(NewVar(token.NoPos, nil, "", Typ[String]))}
methods := []*Func{NewFunc(token.NoPos, nil, "Error", sig)}
def(NewTypeName(token.NoPos, nil, "error", &Named{underlying: NewInterface(methods)}))
def(NewTypeName(token.NoPos, nil, "error", &Named{underlying: NewInterface(methods), complete: true}))
}
for _, c := range predeclaredConstants {