return // already checked
}
// The obj.Val field for constants is initialized to its respective
- // iota value by the parser.
- // The object's fields can be in one of the following states:
- // Type != nil => the constant value is Val
- // Type == nil => the constant is not typechecked yet, and Val can be:
- // Val is int => Val is the value of iota for this declaration
- // Val == nil => the object's expression is being evaluated
- if obj.Val == nil {
- check.errorf(obj.GetPos(), "illegal cycle in initialization of %s", obj.Name)
+ // iota value (type int) by the parser.
+ // If the object's type is Typ[Invalid], the object value is ignored.
+ // If the object's type is valid, the object value must be a legal
+ // constant value; it may be nil to indicate that we don't know the
+ // value of the constant (e.g., in: "const x = float32("foo")" we
+ // know that x is a constant and has type float32, but we don't
+ // have a value due to the error in the conversion).
+ if obj.visited {
+ check.errorf(obj.GetPos(), "illegal cycle in initialization of constant %s", obj.Name)
obj.Type = Typ[Invalid]
return
}
+ obj.visited = true
spec := obj.spec
iota := obj.Val.(int)
- obj.Val = nil // mark obj as "visited" for cycle detection
+ obj.Val = nil // set to a valid (but unknown) constant value
// determine spec for type and initialization expressions
init := spec
if len(init.Values) == 0 {
return // already checked
}
if obj.visited {
- check.errorf(obj.GetPos(), "illegal cycle in initialization of %s", obj.Name)
+ check.errorf(obj.GetPos(), "illegal cycle in initialization of variable %s", obj.Name)
obj.Type = Typ[Invalid]
return
}
// Representation of constant values.
//
+// invalid -> nil (i.e., we don't know the constant value; this can only happen in erroneous programs)
// bool -> bool (true, false)
// numeric -> int64, *big.Int, *big.Rat, Complex (ordered by increasing data structure "size")
// string -> string
func toImagConst(x interface{}) interface{} {
var im *big.Rat
switch x := x.(type) {
+ case nil:
+ im = rat0
case int64:
im = big.NewRat(x, 1)
case *big.Int:
//
func isNegConst(x interface{}) bool {
switch x := x.(type) {
+ case nil:
+ return false
case int64:
return x < 0
case *big.Int:
// of precision.
//
func isRepresentableConst(x interface{}, ctxt *Context, as BasicKind) bool {
+ if x == nil {
+ return true // avoid spurious errors
+ }
+
switch x := x.(type) {
case bool:
return as == Bool || as == UntypedBool
// unaryOpConst returns the result of the constant evaluation op x where x is of the given type.
func unaryOpConst(x interface{}, ctxt *Context, op token.Token, typ *Basic) interface{} {
+ if x == nil {
+ return nil
+ }
+
switch op {
case token.ADD:
return x // nothing to do
// division. Division by zero leads to a run-time panic.
//
func binaryOpConst(x, y interface{}, op token.Token, typ *Basic) interface{} {
+ if x == nil || y == nil {
+ return nil
+ }
+
x, y = matchConst(x, y)
switch x := x.(type) {
//
func shiftConst(x interface{}, s uint, op token.Token) interface{} {
switch x := x.(type) {
+ case nil:
+ return nil
+
case int64:
switch op {
case token.SHL:
// or NilType).
//
func compareConst(x, y interface{}, op token.Token) (z bool) {
+ if x == nil || y == nil {
+ return false
+ }
+
x, y = matchConst(x, y)
// x == y => x == y
}
// assign1to1 typechecks a single assignment of the form lhs = rhs (if rhs != nil),
-// or lhs = x (if rhs == nil). If decl is set, the lhs operand must be an identifier.
-// If its type is not set, it is deduced from the type or value of x. If lhs has a
-// type it is used as a hint when evaluating rhs, if present.
+// or lhs = x (if rhs == nil). If decl is set, the lhs operand must be an identifier;
+// 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) {
- ident, _ := lhs.(*ast.Ident)
+ // Start with rhs so we have an expression type
+ // for declarations with implicit type.
if x == nil {
- assert(rhs != nil)
x = new(operand)
- }
-
- if ident != nil && ident.Name == "_" {
- // anything can be assigned to a blank identifier - check rhs only, if present
- if rhs != nil {
- check.expr(x, rhs, nil, iota)
+ check.expr(x, rhs, nil, iota)
+ // don't exit for declarations - we need the lhs obj first
+ if x.mode == invalid && !decl {
+ return
}
- return
}
+ // x.mode == valid || decl
+
+ // lhs may be an identifier
+ ident, _ := lhs.(*ast.Ident)
+ // regular assignment; we know x is valid
if !decl {
- // regular assignment - start with lhs to obtain a type hint
- // TODO(gri) clean this up - we don't need type hints anymore
+ // anything can be assigned to the blank identifier
+ if ident != nil && ident.Name == "_" {
+ return
+ }
+
var z operand
check.expr(&z, lhs, nil, -1)
if z.mode == invalid {
- z.typ = nil // so we can proceed with rhs
- }
-
- if rhs != nil {
- check.expr(x, rhs, z.typ, -1)
- if x.mode == invalid {
- return
- }
- }
-
- if x.mode == invalid || z.mode == invalid {
return
}
- if !check.assignment(x, z.typ) {
+ // TODO(gri) verify that all other z.mode values
+ // that may appear here are legal
+ if z.mode == constant || !check.assignment(x, z.typ) {
if x.mode != invalid {
check.errorf(x.pos(), "cannot assign %s to %s", x, &z)
}
- return
- }
- if z.mode == constant {
- check.errorf(x.pos(), "cannot assign %s to %s", x, &z)
}
return
}
- // declaration - lhs must be an identifier
+ // declaration with initialization; lhs must be an identifier
if ident == nil {
check.errorf(lhs.Pos(), "cannot declare %s", lhs)
return
}
- // lhs may or may not be typed yet
- obj := check.lookup(ident)
+ // Determine typ of lhs: If the object doesn't have a type
+ // yet, determine it from the type of x; if x is invalid,
+ // set the object type to Typ[Invalid].
var typ Type
- if t := obj.GetType(); t != nil {
- typ = t
- }
+ obj := check.lookup(ident)
+ switch obj := obj.(type) {
+ default:
+ unreachable()
- if rhs != nil {
- check.expr(x, rhs, typ, iota)
- // continue even if x.mode == invalid
- }
+ case nil:
+ // TODO(gri) is this really unreachable?
+ unreachable()
- if typ == nil {
- // determine lhs type from rhs expression;
- // for variables, convert untyped types to
- // default types
- typ = Typ[Invalid]
- if x.mode != invalid {
- typ = x.typ
- if _, ok := obj.(*Var); ok && isUntyped(typ) {
- if x.isNil() {
- check.errorf(x.pos(), "use of untyped nil")
- x.mode = invalid
- } else {
+ case *Const:
+ typ = obj.Type // may already be Typ[Invalid]
+ if typ == nil {
+ typ = Typ[Invalid]
+ if x.mode != invalid {
+ typ = x.typ
+ }
+ obj.Type = typ
+ }
+
+ case *Var:
+ typ = obj.Type // may already be Typ[Invalid]
+ if typ == nil {
+ typ = Typ[Invalid]
+ if x.mode != invalid {
+ typ = x.typ
+ if isUntyped(typ) {
+ // convert untyped types to default types
+ if typ == Typ[UntypedNil] {
+ check.errorf(x.pos(), "use of untyped nil")
+ obj.Type = Typ[Invalid]
+ return
+ }
typ = defaultType(typ)
}
}
- }
- switch obj := obj.(type) {
- case *Const:
- obj.Type = typ
- case *Var:
obj.Type = typ
- default:
- unreachable()
}
}
- if x.mode != invalid {
- if !check.assignment(x, typ) {
- if x.mode != invalid {
- switch obj.(type) {
- case *Const:
- check.errorf(x.pos(), "cannot assign %s to variable of type %s", x, typ)
- case *Var:
- check.errorf(x.pos(), "cannot initialize constant of type %s with %s", typ, x)
- default:
- unreachable()
- }
- x.mode = invalid
+ // nothing else to check if we don't have a valid lhs or rhs
+ if typ == Typ[Invalid] || x.mode == invalid {
+ return
+ }
+
+ if !check.assignment(x, typ) {
+ if x.mode != invalid {
+ if x.typ != Typ[Invalid] && typ != Typ[Invalid] {
+ check.errorf(x.pos(), "cannot initialize %s (type %s) with %s", ident.Name, typ, x)
}
}
+ return
}
// for constants, set their value
- if obj, ok := obj.(*Const); ok {
- assert(obj.Val == nil)
- if x.mode != invalid {
- if x.mode == constant {
- if isConstType(x.typ) {
- obj.Val = x.val
- } else {
- check.errorf(x.pos(), "%s has invalid constant type", x)
- }
- } else {
- check.errorf(x.pos(), "%s is not constant", x)
- }
- }
- if obj.Val == nil {
- // set the constant to its type's zero value to reduce spurious errors
- switch typ := underlying(obj.Type); {
- case typ == Typ[Invalid]:
- // ignore
- case isBoolean(typ):
- obj.Val = false
- case isNumeric(typ):
- obj.Val = int64(0)
- case isString(typ):
- obj.Val = ""
- case hasNil(typ):
- obj.Val = nilConst
- default:
- // in all other cases just prevent use of the constant
- // TODO(gri) re-evaluate this code
- obj.Val = nilConst
+ if obj, _ := obj.(*Const); obj != nil {
+ obj.Val = nil // failure case: we don't know the constant value
+ if x.mode == constant {
+ if isConstType(x.typ) {
+ obj.Val = x.val
+ } else if x.typ != Typ[Invalid] {
+ check.errorf(x.pos(), "%s has invalid constant type", x)
}
+ } else if x.mode != invalid {
+ check.errorf(x.pos(), "%s is not constant", x)
}
}
}
check.expr(&x, tag, nil, -1)
check.multipleDefaults(s.Body.List)
+ // TODO(gri) check also correct use of fallthrough
seen := make(map[interface{}]token.Pos)
for _, s := range s.Body.List {
clause, _ := s.(*ast.CaseClause)