blob: 6a9f3eab51b396178934b22125e650ed612d79ac [file] [log] [blame]
// 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 initialization and assignment checks.
package types
import (
"go/ast"
"go/token"
"code.google.com/p/go.tools/go/exact"
)
// assignment reports whether x can be assigned to a variable of type 'T',
// if necessary by attempting to convert untyped values to the appropriate
// type. If x.mode == invalid upon return, then assignment has already
// issued an error message and the caller doesn't have to report another.
// Use T == nil to indicate assignment to an untyped blank identifier.
//
// TODO(gri) Should find a better way to handle in-band errors.
// TODO(gri) The T == nil mechanism is not yet used - should simplify callers eventually.
//
func (check *checker) assignment(x *operand, T Type) bool {
switch x.mode {
case invalid:
return true // error reported before
case constant, variable, value, valueok:
// ok
default:
unreachable()
}
// x must be a single value
// (tuple types are never named - no need for underlying type)
if t, _ := x.typ.(*Tuple); t != nil {
assert(t.Len() > 1)
check.errorf(x.pos(), "%d-valued expression %s used as single value", t.Len(), x)
x.mode = invalid
return false
}
// spec: "If an untyped constant is assigned to a variable of interface
// type or the blank identifier, the constant is first converted to type
// bool, rune, int, float64, complex128 or string respectively, depending
// on whether the value is a boolean, rune, integer, floating-point, complex,
// or string constant."
if x.mode == constant && isUntyped(x.typ) && (T == nil || isInterface(T)) {
check.convertUntyped(x, defaultType(x.typ))
if x.mode == invalid {
return false
}
}
// spec: "If a left-hand side is the blank identifier, any typed or
// non-constant value except for the predeclared identifier nil may
// be assigned to it."
if T == nil && (x.mode != constant || isTyped(x.typ)) && !x.isNil() {
return true
}
// If we still have an untyped x, try to convert it to T.
if isUntyped(x.typ) {
check.convertUntyped(x, T)
if x.mode == invalid {
return false
}
}
return x.isAssignableTo(check.conf, T)
}
func (check *checker) initConst(lhs *Const, x *operand) {
lhs.val = exact.MakeUnknown()
if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
if lhs.typ == nil {
lhs.typ = Typ[Invalid]
}
return // nothing else to check
}
// If the lhs doesn't have a type yet, use the type of x.
if lhs.typ == nil {
lhs.typ = x.typ
}
if !check.assignment(x, lhs.typ) {
if x.mode != invalid {
check.errorf(x.pos(), "cannot define constant %s (type %s) as %s", lhs.Name(), lhs.typ, x)
}
return
}
// rhs must be a constant
if x.mode != constant {
check.errorf(x.pos(), "%s is not constant", x)
return
}
// rhs type must be a valid constant type
if !isConstType(x.typ) {
check.errorf(x.pos(), "%s has invalid constant type", x)
return
}
lhs.val = x.val
}
func (check *checker) initVar(lhs *Var, x *operand) Type {
if x.mode == invalid || x.typ == Typ[Invalid] || lhs.typ == Typ[Invalid] {
if lhs.typ == nil {
lhs.typ = Typ[Invalid]
}
return nil // nothing else to check
}
// If the lhs doesn't have a type yet, use the type of x.
if lhs.typ == nil {
typ := x.typ
if isUntyped(typ) {
// convert untyped types to default types
if typ == Typ[UntypedNil] {
check.errorf(x.pos(), "use of untyped nil")
lhs.typ = Typ[Invalid]
return nil // nothing else to check
}
typ = defaultType(typ)
}
lhs.typ = typ
}
if !check.assignment(x, lhs.typ) {
if x.mode != invalid {
check.errorf(x.pos(), "cannot initialize variable %s (type %s) with %s", lhs.Name(), lhs.typ, x)
}
return nil
}
return lhs.typ
}
func (check *checker) assignVar(lhs ast.Expr, x *operand) Type {
if x.mode == invalid || x.typ == Typ[Invalid] {
return nil
}
// Determine if the lhs is a (possibly parenthesized) identifier.
ident, _ := unparen(lhs).(*ast.Ident)
// Don't evaluate lhs if it is the blank identifier.
if ident != nil && ident.Name == "_" {
check.recordObject(ident, nil)
// If the lhs is untyped, determine the default type.
// The spec is unclear about this, but gc appears to
// do this.
// TODO(gri) This is still not correct (_ = 1<<1e3)
typ := x.typ
if isUntyped(typ) {
// convert untyped types to default types
if typ == Typ[UntypedNil] {
check.errorf(x.pos(), "use of untyped nil")
return nil // nothing else to check
}
typ = defaultType(typ)
}
check.updateExprType(x.expr, typ, true) // rhs has its final type
return typ
}
// If the lhs is an identifier denoting a variable v, this assignment
// is not a 'use' of v. Remember current value of v.used and restore
// after evaluating the lhs via check.expr.
var v *Var
var v_used bool
if ident != nil {
if obj := check.topScope.LookupParent(ident.Name); obj != nil {
v, _ = obj.(*Var)
if v != nil {
v_used = v.used
}
}
}
var z operand
check.expr(&z, lhs)
if v != nil {
v.used = v_used // restore v.used
}
if z.mode == invalid || z.typ == Typ[Invalid] {
return nil
}
if z.mode == constant || z.mode == value {
check.errorf(z.pos(), "cannot assign to non-variable %s", &z)
return nil
}
// TODO(gri) z.mode can also be valueok which in some cases is ok (maps)
// but in others isn't (channels). Complete the checks here.
if !check.assignment(x, z.typ) {
if x.mode != invalid {
check.errorf(x.pos(), "cannot assign %s to %s", x, &z)
}
return nil
}
return z.typ
}
// If returnPos is valid, initVars is called to type-check the assignment of
// return expressions, and returnPos is the position of the return statement.
func (check *checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) {
l := len(lhs)
r := len(rhs)
assert(l > 0)
// If the lhs and rhs have corresponding expressions,
// treat each matching pair as an individual pair.
if l == r {
var x operand
for i, e := range rhs {
check.expr(&x, e)
check.initVar(lhs[i], &x)
}
return
}
// Otherwise, the rhs must be a single expression (possibly
// a function call returning multiple values, or a comma-ok
// expression).
if r == 1 {
// l > 1
// Start with rhs so we have expression types
// for declarations with implicit types.
var x operand
rhs := rhs[0]
check.expr(&x, rhs)
if x.mode == invalid {
invalidateVars(lhs)
return
}
if t, ok := x.typ.(*Tuple); ok {
// function result
r = t.Len()
if l == r {
for i, lhs := range lhs {
x.mode = value
x.expr = rhs
x.typ = t.At(i).typ
check.initVar(lhs, &x)
}
return
}
}
if !returnPos.IsValid() && x.mode == valueok && l == 2 {
// comma-ok expression (not permitted with return statements)
x.mode = value
t1 := check.initVar(lhs[0], &x)
x.mode = value
x.expr = rhs
x.typ = Typ[UntypedBool]
t2 := check.initVar(lhs[1], &x)
if t1 != nil && t2 != nil {
check.recordCommaOkTypes(rhs, t1, t2)
}
return
}
}
invalidateVars(lhs)
// lhs variables may be function result parameters (return statement);
// use rhs position for properly located error messages
if returnPos.IsValid() {
check.errorf(returnPos, "wrong number of return values (want %d, got %d)", l, r)
return
}
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
}
func (check *checker) assignVars(lhs, rhs []ast.Expr) {
l := len(lhs)
r := len(rhs)
assert(l > 0)
// If the lhs and rhs have corresponding expressions,
// treat each matching pair as an individual pair.
if l == r {
var x operand
for i, e := range rhs {
check.expr(&x, e)
check.assignVar(lhs[i], &x)
}
return
}
// Otherwise, the rhs must be a single expression (possibly
// a function call returning multiple values, or a comma-ok
// expression).
if r == 1 {
// l > 1
var x operand
rhs := rhs[0]
check.expr(&x, rhs)
if x.mode == invalid {
return
}
if t, ok := x.typ.(*Tuple); ok {
// function result
r = t.Len()
if l == r {
for i, lhs := range lhs {
x.mode = value
x.expr = rhs
x.typ = t.At(i).typ
check.assignVar(lhs, &x)
}
return
}
}
if x.mode == valueok && l == 2 {
// comma-ok expression
x.mode = value
t1 := check.assignVar(lhs[0], &x)
x.mode = value
x.expr = rhs
x.typ = Typ[UntypedBool]
t2 := check.assignVar(lhs[1], &x)
if t1 != nil && t2 != nil {
check.recordCommaOkTypes(rhs, t1, t2)
}
return
}
}
check.errorf(rhs[0].Pos(), "assignment count mismatch (%d vs %d)", l, r)
}
func (check *checker) shortVarDecl(lhs, rhs []ast.Expr) {
scope := check.topScope
// collect lhs variables
vars := make([]*Var, len(lhs))
for i, lhs := range lhs {
var obj *Var
if ident, _ := lhs.(*ast.Ident); ident != nil {
// Use the correct obj if the ident is redeclared. The
// variable's scope starts after the declaration; so we
// must use Scope.Lookup here and call Scope.Insert later.
if alt := scope.Lookup(ident.Name); alt != nil {
// redeclared object must be a variable
if alt, _ := alt.(*Var); alt != nil {
obj = alt
} else {
check.errorf(lhs.Pos(), "cannot assign to %s", lhs)
}
} else {
// declare new variable
obj = NewVar(ident.Pos(), check.pkg, ident.Name, nil)
}
if obj != nil {
check.recordObject(ident, obj)
}
} else {
check.errorf(lhs.Pos(), "cannot declare %s", lhs)
}
if obj == nil {
obj = NewVar(lhs.Pos(), check.pkg, "_", nil) // dummy variable
}
vars[i] = obj
}
check.initVars(vars, rhs, token.NoPos)
// declare variables
n := scope.Len()
for _, obj := range vars {
scope.Insert(obj)
}
if n == scope.Len() {
check.errorf(lhs[0].Pos(), "no new variables on left side of :=")
}
}
func invalidateVars(list []*Var) {
for _, obj := range list {
if obj.typ == nil {
obj.typ = Typ[Invalid]
}
}
}