blob: 7b9bedf6595a1c80f20723dc0e49d34a792a82f1 [file] [log] [blame]
// 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.
// This file implements typechecking of expressions.
package types
import (
"go/ast"
"go/token"
"strconv"
)
// TODO(gri) Cleanups
// - 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
// - use "" or "_" consistently for anonymous identifiers? (e.g. reeceivers that have no name)
// - consider storing error messages in invalid operands for better error messages/debugging output
// TODO(gri) API issues
// - clients need access to builtins type information
// - API tests are missing (e.g., identifiers should be handled as expressions in callbacks)
/*
Basic algorithm:
Expressions are checked recursively, top down. Expression checker functions
are generally of the form:
func f(x *operand, e *ast.Expr, ...)
where e is the expression to be checked, and x is the result of the check.
The check performed by f may fail in which case x.mode == invalid, and
related error messages will have been issued by f.
If a hint argument is present, it is the composite literal element type
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
may change on the way up in the expression tree). Usually these are constants,
but the results of comparisons or non-constant shifts of untyped constants
may also be untyped, but not constant.
Untyped expressions may eventually become fully typed (i.e., not untyped),
typically when the value is assigned to a variable, or is used otherwise.
The updateExprType method is used to record this final type and update
the recorded types: the type-checked expression tree is again traversed down,
and the new type is propagated as needed. Untyped constant expression values
that become fully typed must now be representable by the full type (constant
sub-expression trees are left alone except for their roots). This mechanism
ensures that a client sees the actual (run-time) type an untyped value would
have. It also permits type-checking of lhs shift operands "as if the shift
were not present": when updateExprType visits an untyped lhs shift operand
and assigns it it's final type, that type must be an integer type, and a
constant lhs must be representable as an integer.
When an expression gets its final type, either on the way out from rawExpr,
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(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 := check.lookup(name).(*Var)
par.Type = typ
last = par
copy := *par
params = append(params, &copy)
}
} else {
// anonymous parameter
par := &Var{Type: typ}
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.Type = &Slice{Elt: last.Type}
}
return
}
func (check *checker) collectMethods(list *ast.FieldList) (methods []*Method) {
if list == nil {
return
}
for _, f := range list.List {
typ := check.typ(f.Type, len(f.Names) > 0) // cycles are not ok for embedded interfaces
// 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 {
methods = append(methods, &Method{QualifiedName{check.pkg, name.Name}, sig})
}
} else {
// embedded interface
utyp := underlying(typ)
if ityp, ok := utyp.(*Interface); ok {
methods = append(methods, ityp.Methods...)
} else if utyp != Typ[Invalid] {
// if utyp is invalid, don't complain (the root cause was reported before)
check.errorf(f.Type.Pos(), "%s is not an interface type", typ)
}
}
}
// Check for double declarations.
// The parser inserts methods into an interface-local scope, so local
// double declarations are reported by the parser already. We need to
// check again for conflicts due to embedded interfaces. This will lead
// to a 2nd error message if the double declaration was reported before
// by the parser.
// TODO(gri) clean this up a bit
seen := make(map[string]bool)
for _, m := range methods {
if seen[m.Name] {
check.errorf(list.Pos(), "multiple methods named %s", m.Name)
return // keep multiple entries, lookup will only return the first entry
}
seen[m.Name] = true
}
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) {
if list == nil {
return
}
var typ Type // current field typ
var tag string // current field tag
add := func(name string, isAnonymous bool) {
fields = append(fields, &Field{QualifiedName{check.pkg, name}, typ, tag, isAnonymous})
}
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(name.Name, false)
}
} else {
// anonymous field
switch t := deref(typ).(type) {
case *Basic:
add(t.Name, true)
case *NamedType:
add(t.Obj.GetName(), true)
default:
if typ != Typ[Invalid] {
check.invalidAST(f.Type.Pos(), "anonymous field type %s must be named", typ)
}
}
}
}
return
}
type opPredicates map[token.Token]func(Type) bool
var 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 {
if pred := m[op]; pred != nil {
if !pred(x.typ) {
check.invalidOp(x.pos(), "operator %s not defined for %s", op, x)
return false
}
} else {
check.invalidAST(x.pos(), "unknown operator %s", op)
return false
}
return true
}
func (check *checker) unary(x *operand, op token.Token) {
switch op {
case token.AND:
// spec: "As an exception to the addressability
// requirement x may also be a composite literal."
if _, ok := unparen(x.expr).(*ast.CompositeLit); ok {
x.mode = variable
}
if x.mode != variable {
check.invalidOp(x.pos(), "cannot take address of %s", x)
goto Error
}
x.typ = &Pointer{Base: x.typ}
return
case token.ARROW:
typ, ok := underlying(x.typ).(*Chan)
if !ok {
check.invalidOp(x.pos(), "cannot receive from non-channel %s", x)
goto Error
}
if typ.Dir&ast.RECV == 0 {
check.invalidOp(x.pos(), "cannot receive from send-only channel %s", x)
goto Error
}
x.mode = valueok
x.typ = typ.Elt
return
}
if !check.op(unaryOpPredicates, x, op) {
goto Error
}
if x.mode == constant {
typ := underlying(x.typ).(*Basic)
x.val = unaryOpConst(x.val, check.ctxt, op, typ)
// Typed constants must be representable in
// their type after each constant operation.
check.isRepresentable(x, typ)
return
}
x.mode = value
// x.typ remains unchanged
return
Error:
x.mode = invalid
}
func isShift(op token.Token) bool {
return op == token.SHL || op == token.SHR
}
func isComparison(op token.Token) bool {
// Note: tokens are not ordered well to make this much easier
switch op {
case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GTR, token.GEQ:
return true
}
return false
}
// isRepresentable checks that a constant operand is representable in the given type.
func (check *checker) isRepresentable(x *operand, typ *Basic) {
if x.mode != constant || isUntyped(typ) {
return
}
if !isRepresentableConst(x.val, check.ctxt, typ.Kind) {
var msg string
if isNumeric(x.typ) && isNumeric(typ) {
msg = "%s overflows (or cannot be accurately represented as) %s"
} else {
msg = "cannot convert %s to %s"
}
check.errorf(x.pos(), msg, x, typ)
x.mode = invalid
}
}
// updateExprType updates the type of x to typ and invokes itself
// recursively for the operands of x, depending on expression kind.
// If typ is still an untyped and not the final type, updateExprType
// only updates the recorded untyped type for x and possibly its
// operands. Otherwise (i.e., typ is not an untyped type anymore,
// or it is the final type for x), Context.Expr is invoked, if present.
// Also, if x is a constant, it must be representable as a value of typ,
// and if x is the (formerly untyped) lhs operand of a non-constant
// shift, it must be an integer value.
//
func (check *checker) updateExprType(x ast.Expr, typ Type, final bool) {
old, found := check.untyped[x]
if !found {
return // nothing to do
}
// update operands of x if necessary
switch x := x.(type) {
case *ast.BadExpr,
*ast.FuncLit,
*ast.CompositeLit,
*ast.IndexExpr,
*ast.SliceExpr,
*ast.TypeAssertExpr,
*ast.StarExpr,
*ast.KeyValueExpr,
*ast.ArrayType,
*ast.StructType,
*ast.FuncType,
*ast.InterfaceType,
*ast.MapType,
*ast.ChanType:
// These expression are never untyped - nothing to do.
// The respective sub-expressions got their final types
// upon assignment or use.
if debug {
check.dump("%s: found old type(%s): %s (new: %s)", x.Pos(), x, old.typ, typ)
unreachable()
}
return
case *ast.CallExpr:
// Resulting in an untyped constant (e.g., built-in complex).
// The respective calls take care of calling updateExprType
// for the arguments if necessary.
case *ast.Ident, *ast.BasicLit, *ast.SelectorExpr:
// An identifier denoting a constant, a constant literal,
// or a qualified identifier (imported untyped constant).
// No operands to take care of.
case *ast.ParenExpr:
check.updateExprType(x.X, typ, final)
case *ast.UnaryExpr:
// If x is a constant, the operands were constants.
// They don't need to be updated since they never
// get "materialized" into a typed value; and they
// will be processed at the end of the type check.
if old.isConst {
break
}
check.updateExprType(x.X, typ, final)
case *ast.BinaryExpr:
if old.isConst {
break // see comment for unary expressions
}
if isComparison(x.Op) {
// The result type is independent of operand types
// and the operand types must have final types.
} else if isShift(x.Op) {
// The result type depends only on lhs operand.
// The rhs type was updated when checking the shift.
check.updateExprType(x.X, typ, final)
} else {
// The operand types match the result type.
check.updateExprType(x.X, typ, final)
check.updateExprType(x.Y, typ, final)
}
default:
unreachable()
}
// If the new type is not final and still untyped, just
// update the recorded type.
if !final && isUntyped(typ) {
old.typ = underlying(typ).(*Basic)
check.untyped[x] = old
return
}
// Otherwise we have the final (typed or untyped type).
// Remove it from the map.
delete(check.untyped, x)
// If x is the lhs of a shift, its final type must be integer.
// We already know from the shift check that it is representable
// as an integer if it is a constant.
if old.isLhs && !isInteger(typ) {
check.invalidOp(x.Pos(), "shifted operand %s (type %s) must be integer", x, typ)
return
}
// Everything's fine, notify client of final type for x.
if f := check.ctxt.Expr; f != nil {
var val interface{}
if old.isConst {
val = old.val
}
f(x, typ, val)
}
}
// convertUntyped attempts to set the type of an untyped value to the target type.
func (check *checker) convertUntyped(x *operand, target Type) {
if x.mode == invalid || !isUntyped(x.typ) {
return
}
// TODO(gri) Sloppy code - clean up. This function is central
// to assignment and expression checking.
if isUntyped(target) {
// both x and target are untyped
xkind := x.typ.(*Basic).Kind
tkind := target.(*Basic).Kind
if isNumeric(x.typ) && isNumeric(target) {
if xkind < tkind {
x.typ = target
check.updateExprType(x.expr, target, false)
}
} else if xkind != tkind {
goto Error
}
return
}
// typed target
switch t := underlying(target).(type) {
case nil:
// We may reach here due to previous type errors.
// Be conservative and don't crash.
x.mode = invalid
return
case *Basic:
check.isRepresentable(x, t)
if x.mode == invalid {
return // error already reported
}
case *Interface:
if !x.isNil() && len(t.Methods) > 0 /* empty interfaces are ok */ {
goto Error
}
// Update operand types to the default type rather then
// the target (interface) type: values must have concrete
// dynamic types. If the value is nil, keep it untyped
// (this is important for tools such as go vet which need
// the dynamic type for argument checking of say, print
// functions)
if x.isNil() {
target = Typ[UntypedNil]
} else {
// cannot assign untyped values to non-empty interfaces
if len(t.Methods) > 0 {
goto Error
}
target = defaultType(x.typ)
}
case *Pointer, *Signature, *Slice, *Map, *Chan:
if !x.isNil() {
goto Error
}
// keep nil untyped - see comment for interfaces, above
target = Typ[UntypedNil]
default:
if debug {
check.dump("convertUntyped(x = %v, target = %v)", x, target)
}
unreachable()
}
x.typ = target
check.updateExprType(x.expr, target, true) // UntypedNils are final
return
Error:
check.errorf(x.pos(), "cannot convert %s to %s", x, target)
x.mode = invalid
}
func (check *checker) comparison(x, y *operand, op token.Token) {
// TODO(gri) deal with interface vs non-interface comparison
valid := false
if x.isAssignable(check.ctxt, y.typ) || y.isAssignable(check.ctxt, x.typ) {
switch op {
case token.EQL, token.NEQ:
valid = isComparable(x.typ) ||
x.isNil() && hasNil(y.typ) ||
y.isNil() && hasNil(x.typ)
case token.LSS, token.LEQ, token.GTR, token.GEQ:
valid = isOrdered(x.typ)
default:
unreachable()
}
}
if !valid {
check.invalidOp(x.pos(), "cannot compare %s %s %s", x, op, y)
x.mode = invalid
return
}
if x.mode == constant && y.mode == constant {
x.val = compareConst(x.val, y.val, op)
// The operands are never materialized; no need to update
// their types.
} else {
x.mode = value
// The operands have now their final types, which at run-
// time will be materialized. Update the expression trees.
// If the current types are untyped, the materialized type
// is the respective default type.
check.updateExprType(x.expr, defaultType(x.typ), true)
check.updateExprType(y.expr, defaultType(y.typ), true)
}
// spec: "Comparison operators compare two operands and yield
// an untyped boolean value."
x.typ = Typ[UntypedBool]
}
func (check *checker) shift(x, y *operand, op token.Token) {
untypedx := isUntyped(x.typ)
// The lhs must be of integer type or be representable
// as an integer; otherwise the shift has no chance.
if !isInteger(x.typ) && (!untypedx || !isRepresentableConst(x.val, nil, UntypedInt)) {
check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
x.mode = invalid
return
}
// spec: "The right operand in a shift expression must have unsigned
// integer type or be an untyped constant that can be converted to
// unsigned integer type."
switch {
case isInteger(y.typ) && isUnsigned(y.typ):
// nothing to do
case isUntyped(y.typ):
check.convertUntyped(y, Typ[UntypedInt])
if y.mode == invalid {
x.mode = invalid
return
}
default:
check.invalidOp(y.pos(), "shift count %s must be unsigned integer", y)
x.mode = invalid
return
}
if x.mode == constant {
if y.mode == constant {
if untypedx {
x.typ = Typ[UntypedInt]
}
if x.val != nil && y.val != nil {
// rhs must be within reasonable bounds
const stupidShift = 1024
s, ok := y.val.(int64)
if !ok || s < 0 || s >= stupidShift {
check.invalidOp(y.pos(), "%s: stupid shift", y)
x.mode = invalid
return
}
// everything's ok
x.val = shiftConst(x.val, uint(s), op)
} else {
x.val = nil
}
return
}
// non-constant shift with constant lhs
if untypedx {
// spec: "If the left operand of a non-constant shift expression is
// an untyped constant, the type of the constant is what it would be
// if the shift expression were replaced by its left operand alone;
// the type is int if it cannot be determined from the context (for
// instance, if the shift expression is an operand in a comparison
// against an untyped constant)".
// Delay operand checking until we know the final type:
// The lhs expression must be in the untyped map, mark
// the entry as lhs shift operand.
if info, ok := check.untyped[x.expr]; ok {
info.isLhs = true
check.untyped[x.expr] = info
} else {
unreachable()
}
// keep x's type
x.mode = value
return
}
}
// non-constant shift - lhs must be an integer
if !isInteger(x.typ) {
check.invalidOp(x.pos(), "shifted operand %s must be integer", x)
x.mode = invalid
return
}
// non-constant shift
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,
token.AND: isInteger,
token.OR: isInteger,
token.XOR: isInteger,
token.AND_NOT: isInteger,
token.LAND: isBoolean,
token.LOR: isBoolean,
}
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)
if x.mode == invalid {
return
}
if y.mode == invalid {
x.mode = invalid
x.expr = y.expr
return
}
if isShift(op) {
check.shift(x, &y, op)
return
}
check.convertUntyped(x, y.typ)
if x.mode == invalid {
return
}
check.convertUntyped(&y, x.typ)
if y.mode == invalid {
x.mode = invalid
return
}
if isComparison(op) {
check.comparison(x, &y, op)
return
}
if !IsIdentical(x.typ, y.typ) {
// only report an error if we have valid types
// (otherwise we had an error reported elsewhere already)
if x.typ != Typ[Invalid] && y.typ != Typ[Invalid] {
check.invalidOp(x.pos(), "mismatched types %s and %s", x.typ, y.typ)
}
x.mode = invalid
return
}
if !check.op(binaryOpPredicates, x, op) {
x.mode = invalid
return
}
if (op == token.QUO || op == token.REM) && y.mode == constant && isZeroConst(y.val) {
check.invalidOp(y.pos(), "division by zero")
x.mode = invalid
return
}
if x.mode == constant && y.mode == constant {
typ := underlying(x.typ).(*Basic)
x.val = binaryOpConst(x.val, y.val, op, typ)
// Typed constants must be representable in
// their type after each constant operation.
check.isRepresentable(x, typ)
return
}
x.mode = value
// x.typ is unchanged
}
// 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) {
var x operand
check.expr(&x, arg, nil, iota)
// an untyped constant must be representable as Int
check.convertUntyped(&x, Typ[Int])
if x.mode == invalid {
return
}
// the index/size must be of integer type
if !isInteger(x.typ) {
check.invalidArg(x.pos(), "%s must be integer", &x)
return
}
// a constant index/size i must be 0 <= i < length
if x.mode == constant && x.val != nil {
i = x.val.(int64)
if i < 0 {
check.invalidArg(x.pos(), "%s must not be negative", &x)
return
}
if length >= 0 && i >= length {
check.errorf(x.pos(), "index %s is out of bounds (>= %d)", &x, length)
return
}
// 0 <= i [ && i < length ]
return i, true
}
return -1, true
}
// compositeLitKey resolves unresolved composite literal keys.
// For details, see comment in go/parser/parser.go, method parseElement.
func (check *checker) compositeLitKey(key ast.Expr) {
if ident, ok := key.(*ast.Ident); ok && ident.Obj == nil {
if obj := check.pkg.Scope.Lookup(ident.Name); obj != nil {
check.register(ident, obj)
} else if obj := Universe.Lookup(ident.Name); obj != nil {
check.register(ident, obj)
} else {
check.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
}
}
}
// indexElts checks the elements (elts) of an array or slice composite literal
// against the literal's element type (typ), and the element indices against
// 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 {
visited := make(map[int64]bool, len(elts))
var index, max int64
for _, e := range elts {
// determine and check index
validIndex := false
eval := e
if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
check.compositeLitKey(kv.Key)
if i, ok := check.index(kv.Key, length, iota); ok {
if i >= 0 {
index = i
}
validIndex = true
}
eval = kv.Value
} else if length >= 0 && index >= length {
check.errorf(e.Pos(), "index %d is out of bounds (>= %d)", index, length)
} else {
validIndex = true
}
// if we have a valid index, check for duplicate entries
if validIndex {
if visited[index] {
check.errorf(e.Pos(), "duplicate index %d in array or slice literal", index)
}
visited[index] = true
}
index++
if index > max {
max = index
}
// check element against composite literal element type
var x operand
check.expr(&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)
}
}
return max
}
// argument typechecks passing an argument arg (if arg != nil) or
// x (if arg == nil) to the i'th parameter of the given signature.
// If passSlice is set, the argument is followed by ... in the call.
//
func (check *checker) argument(sig *Signature, i int, arg ast.Expr, x *operand, passSlice bool) {
// determine parameter
var par *Var
n := len(sig.Params)
if i < n {
par = sig.Params[i]
} else if sig.IsVariadic {
par = sig.Params[n-1]
} else {
check.errorf(arg.Pos(), "too many arguments")
return
}
// determine argument
var z operand
z.mode = variable
z.expr = nil // TODO(gri) can we do better here? (for good error messages)
z.typ = par.Type
if arg != nil {
check.expr(x, arg, z.typ, -1)
}
if x.mode == invalid {
return // ignore this argument
}
// check last argument of the form x...
if passSlice {
if i+1 != n {
check.errorf(x.pos(), "can only use ... with matching parameter")
return // ignore this argument
}
// spec: "If the final argument is assignable to a slice type []T,
// it may be passed unchanged as the value for a ...T parameter if
// the argument is followed by ..."
z.typ = &Slice{Elt: z.typ} // change final parameter type to []T
}
if !check.assignment(x, z.typ) && x.mode != invalid {
check.errorf(x.pos(), "cannot pass argument %s to %s", x, &z)
}
}
var emptyResult Result
func (check *checker) callExpr(x *operand) {
// convert x into a user-friendly set of values
var typ Type
var val interface{}
switch x.mode {
case invalid:
return // nothing to do
case novalue:
typ = &emptyResult
case constant:
typ = x.typ
val = x.val
default:
typ = x.typ
}
// if the operand is untyped, delay notification
// until it becomes typed or until the end of
// type checking
if isUntyped(typ) {
check.untyped[x.expr] = exprInfo{x.mode == constant, false, typ.(*Basic), val}
return
}
// TODO(gri) ensure that literals always report
// their dynamic (never interface) type.
// This is not the case yet.
if check.ctxt.Expr != nil {
check.ctxt.Expr(x.expr, typ, val)
}
}
// 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.
// 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) {
if trace {
c := ""
if cycleOk {
c = " ⨁"
}
check.trace(e.Pos(), "%s (%s, %d%s)", e, typeString(hint), iota, c)
defer check.untrace("=> %s", x)
}
// record final type of x if untyped, notify clients of type otherwise
defer check.callExpr(x)
switch e := e.(type) {
case *ast.BadExpr:
goto Error // error was reported before
case *ast.Ident:
if e.Name == "_" {
check.invalidOp(e.Pos(), "cannot use _ as value or type")
goto Error
}
obj := check.lookup(e)
if obj == nil {
goto Error // error was reported before
}
check.object(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.Type == 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 = 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 && underlying(obj.Type) == nil {
check.errorf(obj.spec.Pos(), "illegal cycle in declaration of %s", obj.Name)
x.expr = e
x.typ = Typ[Invalid]
return // don't goto Error - need x.mode == typexpr
}
case *Var:
x.mode = variable
case *Func:
x.mode = value
default:
unreachable()
}
x.typ = obj.GetType()
case *ast.Ellipsis:
// ellipses are handled explicitly where they are legal
// (array composite literals and parameter lists)
check.errorf(e.Pos(), "invalid use of '...'")
goto Error
case *ast.BasicLit:
x.setConst(e.Kind, e.Value)
if x.mode == invalid {
check.invalidAST(e.Pos(), "invalid literal %v", e.Value)
goto Error
}
case *ast.FuncLit:
if sig, ok := check.typ(e.Type, false).(*Signature); ok {
x.mode = value
x.typ = sig
check.later(nil, sig, e.Body)
} else {
check.invalidAST(e.Pos(), "invalid function literal %s", e)
goto Error
}
case *ast.CompositeLit:
typ := hint
openArray := false
if e.Type != nil {
// [...]T array types may only appear with composite literals.
// Check for them here so we don't have to handle ... in general.
typ = nil
if atyp, _ := e.Type.(*ast.ArrayType); atyp != nil && atyp.Len != nil {
if ellip, _ := atyp.Len.(*ast.Ellipsis); ellip != nil && ellip.Elt == nil {
// 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)}
openArray = true
}
}
if typ == nil {
typ = check.typ(e.Type, false)
}
}
if typ == nil {
check.errorf(e.Pos(), "missing type in composite literal")
goto Error
}
switch utyp := underlying(deref(typ)).(type) {
case *Struct:
if len(e.Elts) == 0 {
break
}
fields := utyp.Fields
if _, ok := e.Elts[0].(*ast.KeyValueExpr); ok {
// all elements must have keys
visited := make([]bool, len(fields))
for _, e := range e.Elts {
kv, _ := e.(*ast.KeyValueExpr)
if kv == nil {
check.errorf(e.Pos(), "mixture of field:value and value elements in struct literal")
continue
}
key, _ := kv.Key.(*ast.Ident)
if key == nil {
check.errorf(kv.Pos(), "invalid field name %s in struct literal", kv.Key)
continue
}
i := utyp.fieldIndex(QualifiedName{check.pkg, key.Name})
if i < 0 {
check.errorf(kv.Pos(), "unknown field %s in struct literal", key.Name)
continue
}
// 0 <= i < len(fields)
if visited[i] {
check.errorf(kv.Pos(), "duplicate field name %s in struct literal", key.Name)
continue
}
visited[i] = true
check.expr(x, kv.Value, nil, iota)
etyp := fields[i].Type
if !check.assignment(x, etyp) {
if x.mode != invalid {
check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp)
}
continue
}
}
} else {
// no element must have a key
for i, e := range e.Elts {
if kv, _ := e.(*ast.KeyValueExpr); kv != nil {
check.errorf(kv.Pos(), "mixture of field:value and value elements in struct literal")
continue
}
check.expr(x, e, nil, iota)
if i >= len(fields) {
check.errorf(x.pos(), "too many values in struct literal")
break // cannot continue
}
// i < len(fields)
etyp := fields[i].Type
if !check.assignment(x, etyp) {
if x.mode != invalid {
check.errorf(x.pos(), "cannot use %s as %s value in struct literal", x, etyp)
}
continue
}
}
if len(e.Elts) < len(fields) {
check.errorf(e.Rbrace, "too few values in struct literal")
// ok to continue
}
}
case *Array:
n := check.indexedElts(e.Elts, utyp.Elt, utyp.Len, iota)
// 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)
case *Map:
visited := make(map[interface{}]bool, len(e.Elts))
for _, e := range e.Elts {
kv, _ := e.(*ast.KeyValueExpr)
if kv == nil {
check.errorf(e.Pos(), "missing key in map literal")
continue
}
check.compositeLitKey(kv.Key)
check.expr(x, kv.Key, nil, 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)
}
continue
}
if x.mode == constant && x.val != nil {
if visited[x.val] {
check.errorf(x.pos(), "duplicate key %s in map literal", x.val)
continue
}
visited[x.val] = true
}
check.expr(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)
}
continue
}
}
default:
check.errorf(e.Pos(), "%s is not a valid composite literal type", typ)
goto Error
}
x.mode = value
x.typ = typ
case *ast.ParenExpr:
check.rawExpr(x, e.X, nil, iota, cycleOk)
case *ast.SelectorExpr:
sel := e.Sel.Name
// If the identifier refers to a package, handle everything here
// so we don't need a "package" mode for operands: package names
// can only appear in qualified identifiers which are mapped to
// selector expressions.
if ident, ok := e.X.(*ast.Ident); ok {
if pkg, ok := check.lookup(ident).(*Package); ok {
exp := pkg.Scope.Lookup(sel)
// gcimported package scopes contain non-exported
// objects such as types used in partially exported
// objects - do not accept them
if exp == nil || !ast.IsExported(exp.GetName()) {
check.errorf(e.Pos(), "cannot refer to unexported %s", e)
goto Error
}
check.register(e.Sel, exp)
// Simplified version of the code for *ast.Idents:
// - imported packages use types.Scope and types.Objects
// - imported objects are always fully initialized
switch exp := exp.(type) {
case *Const:
assert(exp.Val != nil)
x.mode = constant
x.typ = exp.Type
x.val = exp.Val
case *TypeName:
x.mode = typexpr
x.typ = exp.Type
case *Var:
x.mode = variable
x.typ = exp.Type
case *Func:
x.mode = value
x.typ = exp.Type
default:
unreachable()
}
x.expr = e
return
}
}
check.exprOrType(x, e.X, iota, false)
if x.mode == invalid {
goto Error
}
res := lookupField(x.typ, QualifiedName{check.pkg, sel})
if res.mode == invalid {
check.invalidOp(e.Pos(), "%s has no single field or method %s", x, sel)
goto Error
}
if x.mode == typexpr {
// method expression
sig, ok := res.typ.(*Signature)
if !ok {
check.invalidOp(e.Pos(), "%s has no method %s", x, sel)
goto Error
}
// the receiver type becomes the type of the first function
// argument of the method expression's function type
// TODO(gri) at the moment, method sets don't correctly track
// pointer vs non-pointer receivers => typechecker is too lenient
x.mode = value
x.typ = &Signature{
Params: append([]*Var{{Type: x.typ}}, sig.Params...),
Results: sig.Results,
IsVariadic: sig.IsVariadic,
}
} else {
// regular selector
x.mode = res.mode
x.typ = res.typ
}
case *ast.IndexExpr:
check.expr(x, e.X, nil, iota)
if x.mode == invalid {
goto Error
}
valid := false
length := int64(-1) // valid if >= 0
switch typ := underlying(x.typ).(type) {
case *Basic:
if isString(typ) {
valid = true
if x.mode == constant && x.val != nil {
length = int64(len(x.val.(string)))
}
// an indexed string always yields a byte value
// (not a constant) even if the string and the
// index are constant
x.mode = value
x.typ = Typ[Byte]
}
case *Array:
valid = true
length = typ.Len
if x.mode != variable {
x.mode = value
}
x.typ = typ.Elt
case *Pointer:
if typ, _ := underlying(typ.Base).(*Array); typ != nil {
valid = true
length = typ.Len
x.mode = variable
x.typ = typ.Elt
}
case *Slice:
valid = true
x.mode = variable
x.typ = typ.Elt
case *Map:
var key operand
check.expr(&key, e.Index, nil, 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)
}
goto Error
}
x.mode = valueok
x.typ = typ.Elt
x.expr = e
return
}
if !valid {
check.invalidOp(x.pos(), "cannot index %s", x)
goto Error
}
if e.Index == nil {
check.invalidAST(e.Pos(), "missing index expression for %s", x)
return
}
check.index(e.Index, length, iota)
// ok to continue
case *ast.SliceExpr:
check.expr(x, e.X, nil, iota)
if x.mode == invalid {
goto Error
}
valid := false
length := int64(-1) // valid if >= 0
switch typ := underlying(x.typ).(type) {
case *Basic:
if isString(typ) {
valid = true
if x.mode == constant && x.val != nil {
length = int64(len(x.val.(string))) + 1 // +1 for slice
}
// a sliced string always yields a string value
// of the same type as the original string (not
// a constant) even if the string and the indices
// are constant
x.mode = value
// x.typ doesn't change, but if it is an untyped
// string it becomes string (see also issue 4913).
if typ.Kind == UntypedString {
x.typ = Typ[String]
}
}
case *Array:
valid = true
length = typ.Len + 1 // +1 for slice
if x.mode != variable {
check.invalidOp(x.pos(), "cannot slice %s (value not addressable)", x)
goto Error
}
x.typ = &Slice{Elt: typ.Elt}
case *Pointer:
if typ, _ := underlying(typ.Base).(*Array); typ != nil {
valid = true
length = typ.Len + 1 // +1 for slice
x.mode = variable
x.typ = &Slice{Elt: typ.Elt}
}
case *Slice:
valid = true
x.mode = variable
// x.typ doesn't change
}
if !valid {
check.invalidOp(x.pos(), "cannot slice %s", x)
goto Error
}
lo := int64(0)
if e.Low != nil {
if i, ok := check.index(e.Low, length, iota); ok && i >= 0 {
lo = i
}
}
hi := int64(-1)
if e.High != nil {
if i, ok := check.index(e.High, length, iota); ok && i >= 0 {
hi = i
}
} else if length >= 0 {
hi = length
}
if lo >= 0 && hi >= 0 && lo > hi {
check.errorf(e.Low.Pos(), "inverted slice range: %d > %d", lo, hi)
// ok to continue
}
case *ast.TypeAssertExpr:
check.expr(x, e.X, nil, iota)
if x.mode == invalid {
goto Error
}
var T *Interface
if T, _ = underlying(x.typ).(*Interface); T == nil {
check.invalidOp(x.pos(), "%s is not an interface", x)
goto Error
}
// x.(type) expressions are handled explicitly in type switches
if e.Type == nil {
check.errorf(e.Pos(), "use of .(type) outside type switch")
goto Error
}
typ := check.typ(e.Type, false)
if typ == Typ[Invalid] {
goto Error
}
if method, wrongType := missingMethod(typ, T); method != nil {
var msg string
if wrongType {
msg = "%s cannot have dynamic type %s (wrong type for method %s)"
} else {
msg = "%s cannot have dynamic type %s (missing method %s)"
}
check.errorf(e.Type.Pos(), msg, x, typ, method.Name)
// ok to continue
}
x.mode = valueok
x.expr = e
x.typ = typ
case *ast.CallExpr:
check.exprOrType(x, e.Fun, iota, false)
if x.mode == invalid {
goto Error
} else if x.mode == typexpr {
check.conversion(x, e, x.typ, iota)
} else if sig, ok := underlying(x.typ).(*Signature); ok {
// check parameters
// If we have a trailing ... at the end of the parameter
// list, the last argument must match the parameter type
// []T of a variadic function parameter x ...T.
passSlice := false
if e.Ellipsis.IsValid() {
if sig.IsVariadic {
passSlice = true
} else {
check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun)
// ok to continue
}
}
// If we have a single argument that is a function call
// we need to handle it separately. Determine if this
// is the case without checking the argument.
var call *ast.CallExpr
if len(e.Args) == 1 {
call, _ = unparen(e.Args[0]).(*ast.CallExpr)
}
n := 0 // parameter count
if call != nil {
// We have a single argument that is a function call.
check.expr(x, call, nil, -1)
if x.mode == invalid {
goto Error // TODO(gri): we can do better
}
if t, _ := x.typ.(*Result); t != nil {
// multiple result values
n = len(t.Values)
for i, obj := range t.Values {
x.mode = value
x.expr = nil // TODO(gri) can we do better here? (for good error messages)
x.typ = obj.Type
check.argument(sig, i, nil, x, passSlice && i+1 == n)
}
} else {
// single result value
n = 1
check.argument(sig, 0, nil, x, passSlice)
}
} else {
// We don't have a single argument or it is not a function call.
n = len(e.Args)
for i, arg := range e.Args {
check.argument(sig, i, arg, x, passSlice && i+1 == n)
}
}
// determine if we have enough arguments
if sig.IsVariadic {
// a variadic function accepts an "empty"
// last argument: count one extra
n++
}
if n < len(sig.Params) {
check.errorf(e.Fun.Pos(), "too few arguments in call to %s", e.Fun)
// ok to continue
}
// determine result
switch len(sig.Results) {
case 0:
x.mode = novalue
case 1:
x.mode = value
x.typ = sig.Results[0].Type
default:
x.mode = value
x.typ = &Result{Values: sig.Results}
}
} else if bin, ok := x.typ.(*builtin); ok {
check.builtin(x, e, bin, iota)
} else {
check.invalidOp(x.pos(), "cannot call non-function %s", x)
goto Error
}
case *ast.StarExpr:
check.exprOrType(x, e.X, iota, true)
switch x.mode {
case invalid:
goto Error
case typexpr:
x.typ = &Pointer{Base: x.typ}
default:
if typ, ok := underlying(x.typ).(*Pointer); ok {
x.mode = variable
x.typ = typ.Base
} else {
check.invalidOp(x.pos(), "cannot indirect %s", x)
goto Error
}
}
case *ast.UnaryExpr:
check.expr(x, e.X, nil, iota)
if x.mode == invalid {
goto Error
}
check.unary(x, e.Op)
if x.mode == invalid {
goto Error
}
case *ast.BinaryExpr:
check.binary(x, e.X, e.Y, e.Op, iota)
if x.mode == invalid {
goto Error
}
case *ast.KeyValueExpr:
// key:value expressions are handled in composite literals
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
}
n, ok := x.val.(int64)
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)}
}
x.mode = typexpr
case *ast.StructType:
x.mode = typexpr
x.typ = &Struct{Fields: check.collectFields(e.Fields, cycleOk)}
case *ast.FuncType:
params, isVariadic := check.collectParams(e.Params, true)
results, _ := check.collectParams(e.Results, false)
x.mode = typexpr
x.typ = &Signature{Recv: nil, Params: params, Results: results, IsVariadic: isVariadic}
case *ast.InterfaceType:
x.mode = typexpr
x.typ = &Interface{Methods: check.collectMethods(e.Methods)}
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)}
default:
if debug {
check.dump("expr = %v (%T)", e, e)
}
unreachable()
}
// everything went well
x.expr = e
return
Error:
x.mode = invalid
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)
switch x.mode {
case novalue:
check.errorf(x.pos(), "%s used as value", x)
x.mode = invalid
case typexpr:
check.errorf(x.pos(), "%s is not an expression", x)
x.mode = invalid
}
}
func (check *checker) rawTyp(e ast.Expr, cycleOk, nilOk bool) Type {
var x operand
check.rawExpr(&x, e, nil, -1, cycleOk)
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 nilOk && x.isNil() {
return nil
}
fallthrough
default:
check.errorf(x.pos(), "%s is not a type", &x)
}
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.
//
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)
}