+// REVIEW INCOMPLETE
// Copyright 2012 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.
type opPredicates map[token.Token]func(Type) bool
-var unaryOpPredicates = opPredicates{
- token.ADD: isNumeric,
- token.SUB: isNumeric,
- token.XOR: isInteger,
- token.NOT: isBoolean,
+var unaryOpPredicates opPredicates
+
+func init() {
+ // Setting unaryOpPredicates in init avoids declaration cycles.
+ unaryOpPredicates = opPredicates{
+ token.ADD: isNumeric,
+ token.SUB: isNumeric,
+ token.XOR: isInteger,
+ token.NOT: isBoolean,
+ }
}
func (check *Checker) op(m opPredicates, x *operand, op token.Token) bool {
// Typed constants must be representable in
// their type after each constant operation.
- if typ, ok := x.typ.Underlying().(*Basic); ok && isTyped(typ) {
+ if typ := asBasic(x.typ); typ != nil && isTyped(typ) {
check.representable(x, typ)
return
}
return
case token.ARROW:
- typ, ok := x.typ.Underlying().(*Chan)
- if !ok {
+ typ := asChan(x.typ)
+ if typ == nil {
check.invalidOp(x, _InvalidReceive, "cannot receive from non-channel %s", x)
x.mode = invalid
return
// nothing to do (and don't cause an error below in the overflow check)
return
}
+ typ := asBasic(x.typ)
var prec uint
- if isUnsigned(x.typ) {
- prec = uint(check.conf.sizeof(x.typ) * 8)
+ if isUnsigned(typ) {
+ prec = uint(check.conf.sizeof(typ) * 8)
}
x.val = constant.UnaryOp(e.Op, x.val, prec)
x.expr = e
// representable checks that a constant operand is representable in the given
// basic type.
func (check *Checker) representable(x *operand, typ *Basic) {
- if err := check.isRepresentable(x, typ); err != nil {
+ if v, code := check.representation(x, typ); code != 0 {
+ check.invalidConversion(code, x, typ)
x.mode = invalid
- check.err(err)
+ } else if v != nil {
+ x.val = v
}
}
-func (check *Checker) isRepresentable(x *operand, typ *Basic) error {
+func (check *Checker) representation(x *operand, typ *Basic) (constant.Value, errorCode) {
assert(x.mode == constant_)
- if !representableConst(x.val, check, typ, &x.val) {
- var msg string
- var code errorCode
+ v := x.val
+ if !representableConst(x.val, check, typ, &v) {
if isNumeric(x.typ) && isNumeric(typ) {
// numeric conversion : error msg
//
// float -> float : overflows
//
if !isInteger(x.typ) && isInteger(typ) {
- msg = "%s truncated to %s"
- code = _TruncatedFloat
+ return nil, _TruncatedFloat
} else {
- msg = "%s overflows %s"
- code = _NumericOverflow
+ return nil, _NumericOverflow
}
- } else {
- msg = "cannot convert %s to %s"
- code = _InvalidConstVal
}
- return check.newErrorf(x, code, false, msg, x, typ)
+ return nil, _InvalidConstVal
}
- return nil
+ return v, 0
+}
+
+func (check *Checker) invalidConversion(code errorCode, x *operand, target Type) {
+ msg := "cannot convert %s to %s"
+ switch code {
+ case _TruncatedFloat:
+ msg = "%s truncated to %s"
+ case _NumericOverflow:
+ msg = "%s overflows %s"
+ }
+ check.errorf(x, code, msg, x, target)
}
// updateExprType updates the type of x to typ and invokes itself
// If the new type is not final and still untyped, just
// update the recorded type.
if !final && isUntyped(typ) {
- old.typ = typ.Underlying().(*Basic)
+ old.typ = asBasic(typ)
check.untyped[x] = old
return
}
// convertUntyped attempts to set the type of an untyped value to the target type.
func (check *Checker) convertUntyped(x *operand, target Type) {
- if err := check.canConvertUntyped(x, target); err != nil {
+ newType, val, code := check.implicitTypeAndValue(x, target)
+ if code != 0 {
+ check.invalidConversion(code, x, target.Underlying())
x.mode = invalid
- check.err(err)
+ return
+ }
+ if val != nil {
+ x.val = val
+ check.updateExprVal(x.expr, val)
+ }
+ if newType != x.typ {
+ x.typ = newType
+ check.updateExprType(x.expr, newType, false)
}
}
-func (check *Checker) canConvertUntyped(x *operand, target Type) error {
+// implicitTypeAndValue returns the implicit type of x when used in a context
+// where the target type is expected. If no such implicit conversion is
+// possible, it returns a nil Type.
+func (check *Checker) implicitTypeAndValue(x *operand, target Type) (Type, constant.Value, errorCode) {
+ target = expand(target)
if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] {
- return nil
+ return x.typ, nil, 0
}
if isUntyped(target) {
tkind := target.(*Basic).kind
if isNumeric(x.typ) && isNumeric(target) {
if xkind < tkind {
- x.typ = target
- check.updateExprType(x.expr, target, false)
+ return target, nil, 0
}
} else if xkind != tkind {
- return check.newErrorf(x, _InvalidUntypedConversion, false, "cannot convert %s to %s", x, target)
+ return nil, nil, _InvalidUntypedConversion
}
- return nil
+ return x.typ, nil, 0
}
- if t, ok := target.Underlying().(*Basic); ok && x.mode == constant_ {
- if err := check.isRepresentable(x, t); err != nil {
- return err
- }
- // Expression value may have been rounded - update if needed.
- check.updateExprVal(x.expr, x.val)
- } else {
- newTarget := check.implicitType(x, target)
- if newTarget == nil {
- return check.newErrorf(x, _InvalidUntypedConversion, false, "cannot convert %s to %s", x, target)
- }
- target = newTarget
- }
- x.typ = target
- // Even though implicitType can return UntypedNil, this value is final: the
- // predeclared identifier nil has no type.
- check.updateExprType(x.expr, target, true)
- return nil
-}
-
-// implicitType returns the implicit type of x when used in a context where the
-// target type is expected. If no such implicit conversion is possible, it
-// returns nil.
-func (check *Checker) implicitType(x *operand, target Type) Type {
- assert(isUntyped(x.typ))
- switch t := target.Underlying().(type) {
+ switch t := optype(target).(type) {
case *Basic:
- assert(x.mode != constant_)
+ if x.mode == constant_ {
+ v, code := check.representation(x, t)
+ if code != 0 {
+ return nil, nil, code
+ }
+ return target, v, code
+ }
// Non-constant untyped values may appear as the
// result of comparisons (untyped bool), intermediate
// (delayed-checked) rhs operands of shifts, and as
switch x.typ.(*Basic).kind {
case UntypedBool:
if !isBoolean(target) {
- return nil
+ return nil, nil, _InvalidUntypedConversion
}
case UntypedInt, UntypedRune, UntypedFloat, UntypedComplex:
if !isNumeric(target) {
- return nil
+ return nil, nil, _InvalidUntypedConversion
}
case UntypedString:
// Non-constant untyped string values are not permitted by the spec and
// should not occur during normal typechecking passes, but this path is
// reachable via the AssignableTo API.
if !isString(target) {
- return nil
+ return nil, nil, _InvalidUntypedConversion
}
case UntypedNil:
// Unsafe.Pointer is a basic type that includes nil.
if !hasNil(target) {
- return nil
+ return nil, nil, _InvalidUntypedConversion
}
// Preserve the type of nil as UntypedNil: see #13061.
- return Typ[UntypedNil]
+ return Typ[UntypedNil], nil, 0
default:
- return nil
+ return nil, nil, _InvalidUntypedConversion
+ }
+ case *Sum:
+ ok := t.is(func(t Type) bool {
+ target, _, _ := check.implicitTypeAndValue(x, t)
+ return target != nil
+ })
+ if !ok {
+ return nil, nil, _InvalidUntypedConversion
+ }
+ // keep nil untyped (was bug #39755)
+ if x.isNil() {
+ return Typ[UntypedNil], nil, 0
}
case *Interface:
// Values must have concrete dynamic types. If the value is nil,
// need the dynamic type for argument checking of say, print
// functions)
if x.isNil() {
- return Typ[UntypedNil]
+ return Typ[UntypedNil], nil, 0
}
// cannot assign untyped values to non-empty interfaces
- check.completeInterface(t)
+ check.completeInterface(token.NoPos, t)
if !t.Empty() {
- return nil
+ return nil, nil, _InvalidUntypedConversion
}
- return Default(x.typ)
+ return Default(x.typ), nil, 0
case *Pointer, *Signature, *Slice, *Map, *Chan:
if !x.isNil() {
- return nil
+ return nil, nil, _InvalidUntypedConversion
}
// Keep nil untyped - see comment for interfaces, above.
- return Typ[UntypedNil]
+ return Typ[UntypedNil], nil, 0
default:
- return nil
+ return nil, nil, _InvalidUntypedConversion
}
- return target
+ return target, nil, 0
}
func (check *Checker) comparison(x, y *operand, op token.Token) {
x.mode = value
}
-var binaryOpPredicates = opPredicates{
- token.ADD: func(typ Type) bool { return isNumeric(typ) || isString(typ) },
- token.SUB: isNumeric,
- token.MUL: isNumeric,
- token.QUO: isNumeric,
- token.REM: isInteger,
+var binaryOpPredicates opPredicates
+
+func init() {
+ // Setting binaryOpPredicates in init avoids declaration cycles.
+ binaryOpPredicates = opPredicates{
+ token.ADD: isNumericOrString,
+ token.SUB: isNumeric,
+ token.MUL: isNumeric,
+ token.QUO: isNumeric,
+ token.REM: isInteger,
- token.AND: isInteger,
- token.OR: isInteger,
- token.XOR: isInteger,
- token.AND_NOT: isInteger,
+ token.AND: isInteger,
+ token.OR: isInteger,
+ token.XOR: isInteger,
+ token.AND_NOT: isInteger,
- token.LAND: isBoolean,
- token.LOR: isBoolean,
+ token.LAND: isBoolean,
+ token.LOR: isBoolean,
+ }
}
// If e != nil, it must be the binary expression; it may be nil for non-constant expressions
// x.typ is unchanged
return
}
+ typ := asBasic(x.typ)
// force integer division of integer operands
- if op == token.QUO && isInteger(x.typ) {
+ if op == token.QUO && isInteger(typ) {
op = token.QUO_ASSIGN
}
x.val = constant.BinaryOp(x.val, op, y.val)
//
func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind {
if trace {
- check.trace(e.Pos(), "%s", e)
+ check.trace(e.Pos(), "expr %s", e)
check.indent++
defer func() {
check.indent--
// 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, elem: check.typ(atyp.Elt)}
+ typ = &Array{len: -1, elem: check.varType(atyp.Elt)}
base = typ
break
}
case hint != nil:
// no composite literal type present - use hint (element type of enclosing type)
typ = hint
- base, _ = deref(typ.Underlying()) // *T implies &T{}
+ base, _ = deref(under(typ)) // *T implies &T{}
default:
// TODO(gri) provide better error messages depending on context
goto Error
}
- switch utyp := base.Underlying().(type) {
+ switch utyp := optype(base).(type) {
case *Struct:
if len(e.Elts) == 0 {
break
duplicate := false
// if the key is of interface type, the type is also significant when checking for duplicates
xkey := keyVal(x.val)
- if _, ok := utyp.key.Underlying().(*Interface); ok {
+ if asInterface(utyp.key) != nil {
for _, vtyp := range visited[xkey] {
if check.identical(vtyp, x.typ) {
duplicate = true
check.selector(x, e)
case *ast.IndexExpr:
- check.expr(x, e.X)
+ check.exprOrType(x, e.X)
if x.mode == invalid {
check.use(e.Index)
goto Error
}
+ if x.mode == typexpr {
+ // type instantiation
+ x.mode = invalid
+ x.typ = check.varType(e)
+ if x.typ != Typ[Invalid] {
+ x.mode = typexpr
+ }
+ return expression
+ }
+
+ if x.mode == value {
+ if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 {
+ return check.call(x, nil, e)
+ }
+ }
+
valid := false
length := int64(-1) // valid if >= 0
- switch typ := x.typ.Underlying().(type) {
+ switch typ := optype(x.typ).(type) {
case *Basic:
if isString(typ) {
valid = true
x.typ = typ.elem
case *Pointer:
- if typ, _ := typ.base.Underlying().(*Array); typ != nil {
+ if typ := asArray(typ.base); typ != nil {
valid = true
length = typ.len
x.mode = variable
x.typ = typ.elem
x.expr = e
return expression
+
+ case *Sum:
+ // A sum type can be indexed if all of the sum's types
+ // support indexing and have the same index and element
+ // type. Special rules apply for maps in the sum type.
+ var tkey, telem Type // key is for map types only
+ nmaps := 0 // number of map types in sum type
+ if typ.is(func(t Type) bool {
+ var e Type
+ switch t := under(t).(type) {
+ case *Basic:
+ if isString(t) {
+ e = universeByte
+ }
+ case *Array:
+ e = t.elem
+ case *Pointer:
+ if t := asArray(t.base); t != nil {
+ e = t.elem
+ }
+ case *Slice:
+ e = t.elem
+ case *Map:
+ // If there are multiple maps in the sum type,
+ // they must have identical key types.
+ // TODO(gri) We may be able to relax this rule
+ // but it becomes complicated very quickly.
+ if tkey != nil && !Identical(t.key, tkey) {
+ return false
+ }
+ tkey = t.key
+ e = t.elem
+ nmaps++
+ case *TypeParam:
+ check.errorf(x, 0, "type of %s contains a type parameter - cannot index (implementation restriction)", x)
+ case *instance:
+ panic("unimplemented")
+ }
+ if e == nil || telem != nil && !Identical(e, telem) {
+ return false
+ }
+ telem = e
+ return true
+ }) {
+ // If there are maps, the index expression must be assignable
+ // to the map key type (as for simple map index expressions).
+ if nmaps > 0 {
+ var key operand
+ check.expr(&key, e.Index)
+ check.assignment(&key, tkey, "map index")
+ // ok to continue even if indexing failed - map element type is known
+
+ // If there are only maps, we are done.
+ if nmaps == len(typ.types) {
+ x.mode = mapindex
+ x.typ = telem
+ x.expr = e
+ return expression
+ }
+
+ // Otherwise we have mix of maps and other types. For
+ // now we require that the map key be an integer type.
+ // TODO(gri) This is probably not good enough.
+ valid = isInteger(tkey)
+ // avoid 2nd indexing error if indexing failed above
+ if !valid && key.mode == invalid {
+ goto Error
+ }
+ x.mode = value // map index expressions are not addressable
+ } else {
+ // no maps
+ valid = true
+ x.mode = variable
+ }
+ x.typ = telem
+ }
}
if !valid {
goto Error
}
+ // In pathological (invalid) cases (e.g.: type T1 [][[]T1{}[0][0]]T0)
+ // the element type may be accessed before it's set. Make sure we have
+ // a valid type.
+ if x.typ == nil {
+ x.typ = Typ[Invalid]
+ }
+
check.index(e.Index, length)
// ok to continue
valid := false
length := int64(-1) // valid if >= 0
- switch typ := x.typ.Underlying().(type) {
+ switch typ := optype(x.typ).(type) {
case *Basic:
if isString(typ) {
if e.Slice3 {
x.typ = &Slice{elem: typ.elem}
case *Pointer:
- if typ, _ := typ.base.Underlying().(*Array); typ != nil {
+ if typ := asArray(typ.base); typ != nil {
valid = true
length = typ.len
x.typ = &Slice{elem: typ.elem}
case *Slice:
valid = true
// x.typ doesn't change
+
+ case *Sum, *TypeParam:
+ check.errorf(x, 0, "generic slice expressions not yet implemented")
+ goto Error
}
if !valid {
if x.mode == invalid {
goto Error
}
- xtyp, _ := x.typ.Underlying().(*Interface)
+ xtyp, _ := under(x.typ).(*Interface)
if xtyp == nil {
check.invalidOp(x, _InvalidAssert, "%s is not an interface", x)
goto Error
}
+ check.ordinaryType(x, xtyp)
// x.(type) expressions are handled explicitly in type switches
if e.Type == nil {
// Don't use invalidAST because this can occur in the AST produced by
check.error(e, _BadTypeKeyword, "use of .(type) outside type switch")
goto Error
}
- T := check.typ(e.Type)
+ T := check.varType(e.Type)
if T == Typ[Invalid] {
goto Error
}
x.typ = T
case *ast.CallExpr:
- return check.call(x, e)
+ return check.call(x, e, e)
case *ast.StarExpr:
check.exprOrType(x, e.X)
case typexpr:
x.typ = &Pointer{base: x.typ}
default:
- if typ, ok := x.typ.Underlying().(*Pointer); ok {
+ if typ := asPointer(x.typ); typ != nil {
x.mode = variable
x.typ = typ.base
} else {
check.errorf(at, _ImpossibleAssert, "%s cannot have dynamic type %s (%s)", x, T, msg)
}
-func (check *Checker) singleValue(x *operand) {
- if x.mode == value {
- // tuple types are never named - no need for underlying type below
- if t, ok := x.typ.(*Tuple); ok {
- assert(t.Len() != 1)
- check.errorf(x, _TooManyValues, "%d-valued %s where single value is expected", t.Len(), x)
- x.mode = invalid
- }
- }
-}
-
// expr typechecks expression e and initializes x with the expression value.
// The result must be a single value.
// If an error occurred, x.mode is set to invalid.
//
func (check *Checker) expr(x *operand, e ast.Expr) {
- check.multiExpr(x, e)
+ check.rawExpr(x, e, nil)
+ check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
check.singleValue(x)
}
-// multiExpr is like expr but the result may be a multi-value.
+// multiExpr is like expr but the result may also be a multi-value.
func (check *Checker) multiExpr(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
- var msg string
- var code errorCode
- switch x.mode {
- default:
- return
- case novalue:
- msg = "%s used as value"
- code = _TooManyValues
- case builtin:
- msg = "%s must be called"
- code = _UncalledBuiltin
- case typexpr:
- msg = "%s is not an expression"
- code = _NotAnExpr
- }
- check.errorf(x, code, msg, x)
- x.mode = invalid
+ check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
+}
+
+// multiExprOrType is like multiExpr but the result may also be a type.
+func (check *Checker) multiExprOrType(x *operand, e ast.Expr) {
+ check.rawExpr(x, e, nil)
+ check.exclude(x, 1<<novalue|1<<builtin)
}
// exprWithHint typechecks expression e and initializes x with the expression value;
func (check *Checker) exprWithHint(x *operand, e ast.Expr, hint Type) {
assert(hint != nil)
check.rawExpr(x, e, hint)
+ check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
check.singleValue(x)
- var msg string
- var code errorCode
- switch x.mode {
- default:
- return
- case novalue:
- msg = "%s used as value"
- code = _TooManyValues
- case builtin:
- msg = "%s must be called"
- code = _UncalledBuiltin
- case typexpr:
- msg = "%s is not an expression"
- code = _NotAnExpr
- }
- check.errorf(x, code, msg, x)
- x.mode = invalid
}
// exprOrType typechecks expression or type e and initializes x with the expression value or type.
//
func (check *Checker) exprOrType(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
+ check.exclude(x, 1<<novalue)
check.singleValue(x)
- if x.mode == novalue {
- check.errorf(x, _NotAnExpr, "%s used as value or type", x)
+}
+
+// exclude reports an error if x.mode is in modeset and sets x.mode to invalid.
+// The modeset may contain any of 1<<novalue, 1<<builtin, 1<<typexpr.
+func (check *Checker) exclude(x *operand, modeset uint) {
+ if modeset&(1<<x.mode) != 0 {
+ var msg string
+ var code errorCode
+ switch x.mode {
+ case novalue:
+ if modeset&(1<<typexpr) != 0 {
+ msg = "%s used as value"
+ } else {
+ msg = "%s used as value or type"
+ }
+ code = _TooManyValues
+ case builtin:
+ msg = "%s must be called"
+ code = _UncalledBuiltin
+ case typexpr:
+ msg = "%s is not an expression"
+ code = _NotAnExpr
+ default:
+ unreachable()
+ }
+ check.errorf(x, code, msg, x)
x.mode = invalid
}
}
+
+// singleValue reports an error if x describes a tuple and sets x.mode to invalid.
+func (check *Checker) singleValue(x *operand) {
+ if x.mode == value {
+ // tuple types are never named - no need for underlying type below
+ if t, ok := x.typ.(*Tuple); ok {
+ assert(t.Len() != 1)
+ check.errorf(x, _TooManyValues, "%d-valued %s where single value is expected", t.Len(), x)
+ x.mode = invalid
+ }
+ }
+}