diff options
Diffstat (limited to 'src/pkg/go/types/expr.go')
-rw-r--r-- | src/pkg/go/types/expr.go | 1520 |
1 files changed, 0 insertions, 1520 deletions
diff --git a/src/pkg/go/types/expr.go b/src/pkg/go/types/expr.go deleted file mode 100644 index 86d782d48..000000000 --- a/src/pkg/go/types/expr.go +++ /dev/null @@ -1,1520 +0,0 @@ -// 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) - -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, ©) - } - } 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 %s" - } else { - msg = "cannot convert %s to %s" - } - check.errorf(x.pos(), msg, x, typ) - x.mode = invalid - } -} - -// updateExprType updates the type of all untyped nodes in the -// expression tree of x to typ. If shiftOp is set, x is the lhs -// of a shift expression. In that case, and if x is in the set -// of shift operands with delayed type checking, and typ is not -// an untyped type, updateExprType will check if typ is an -// integer type. -// If Context.Expr != nil, it is called for all nodes that are -// now assigned their final (not untyped) type. -func (check *checker) updateExprType(x ast.Expr, typ Type, shiftOp bool) { - switch x := x.(type) { - case *ast.BadExpr, - *ast.FuncLit, - *ast.CompositeLit, - *ast.SelectorExpr, - *ast.IndexExpr, - *ast.SliceExpr, - *ast.TypeAssertExpr, - *ast.CallExpr, - *ast.StarExpr, - *ast.KeyValueExpr, - *ast.ArrayType, - *ast.StructType, - *ast.FuncType, - *ast.InterfaceType, - *ast.MapType, - *ast.ChanType: - // these expression are never untyped - nothing to do - return - - case *ast.Ident, *ast.BasicLit: - // update type - - case *ast.ParenExpr: - check.updateExprType(x.X, typ, false) - - case *ast.UnaryExpr: - check.updateExprType(x.X, typ, false) - - case *ast.BinaryExpr: - if isComparison(x.Op) { - // result type is independent of operand types - } else if isShift(x.Op) { - // result type depends only on lhs operand - check.updateExprType(x.X, typ, true) - } else { - // operand types match result type - check.updateExprType(x.X, typ, false) - check.updateExprType(x.Y, typ, false) - } - - case *ast.Ellipsis: - unreachable() - default: - unreachable() - } - - // TODO(gri) t should always exist, shouldn't it? - if t := check.untyped[x]; t != nil { - if isUntyped(typ) { - check.untyped[x] = typ.(*Basic) - } else { - // notify clients of final type for x - if f := check.ctxt.Expr; f != nil { - f(x, typ, check.constants[x]) - } - delete(check.untyped, x) - delete(check.constants, x) - // check delayed shift - // Note: Using shiftOp is an optimization: it prevents - // map lookups when we know x is not a shiftOp in the - // first place. - if shiftOp && check.shiftOps[x] { - if !isInteger(typ) { - check.invalidOp(x.Pos(), "shifted operand %s (type %s) must be integer", x, typ) - } - delete(check.shiftOps, x) - } - } - } -} - -// 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, false) - 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) - } else { - x.mode = value - } - - x.typ = Typ[UntypedBool] -} - -func (check *checker) shift(x, y *operand, op token.Token) { - // 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 y.mode == constant && isUntyped(y.typ): - check.convertUntyped(x, Typ[UntypedInt]) - 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 { - // constant shift - lhs must be (representable as) an integer - if isUntyped(x.typ) { - if !isRepresentableConst(x.val, check.ctxt, UntypedInt) { - check.invalidOp(x.pos(), "shifted operand %s must be integer", x) - x.mode = invalid - return - } - x.typ = Typ[UntypedInt] - } - assert(x.isInteger(check.ctxt)) - - // 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) - return - } - - // non-constant shift with constant lhs - if isUntyped(x.typ) { - // 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 type - check.shiftOps[x.expr] = true - 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) { - 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 expression for validity. If length >= 0, it is the upper -// bound for the index. The result is a valid index >= 0, or a negative value. -// -func (check *checker) index(index ast.Expr, length int64, iota int) int64 { - var x operand - - check.expr(&x, index, nil, iota) - if !x.isInteger(check.ctxt) { - check.errorf(x.pos(), "index %s must be integer", &x) - return -1 - } - if x.mode != constant { - return -1 // we cannot check more - } - // The spec doesn't require int64 indices, but perhaps it should. - i, ok := x.val.(int64) - if !ok { - check.errorf(x.pos(), "stupid index %s", &x) - return -1 - } - if i < 0 { - check.errorf(x.pos(), "index %s must not be negative", &x) - return -1 - } - if length >= 0 && i >= length { - check.errorf(x.pos(), "index %s is out of bounds (>= %d)", &x, length) - return -1 - } - - return i -} - -// 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 := check.index(kv.Key, length, iota); 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) { - 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] = typ.(*Basic) - if val != nil { - check.constants[x.expr] = 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) - } - - 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.Val == nil { - goto Error // cycle detected - } - 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 - } - 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 { - 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 { - 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 { - 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 { - lo = check.index(e.Low, length, iota) - } - - hi := int64(-1) - if e.High != nil { - hi = check.index(e.High, length, iota) - } 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) -} |