case constant_, variable, mapindex, value, commaok, commaerr:
// ok
default:
- unreachable()
+ // we may get here because of other problems (issue #39634, crash 12)
+ check.errorf(x, 0, "cannot assign %s to %s in %s", x, T, context)
+ return
}
if isUntyped(x.typ) {
}
// x.typ is typed
+ // A generic (non-instantiated) function value cannot be assigned to a variable.
+ if sig := asSignature(x.typ); sig != nil && len(sig.tparams) > 0 {
+ check.errorf(x, 0, "cannot use generic function %s without instantiation in %s", x, context)
+ }
+
// spec: "If a left-hand side is the blank identifier, any typed or
// non-constant value except for the predeclared identifier nil may
// be assigned to it."
func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
if x.mode == invalid || x.typ == Typ[Invalid] {
+ check.useLHS(lhs)
return nil
}
// If returnPos is valid, initVars is called to type-check the assignment of
// return expressions, and returnPos is the position of the return statement.
-func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) {
- l := len(lhs)
- get, r, commaOk := unpack(func(x *operand, i int) { check.multiExpr(x, rhs[i]) }, len(rhs), l == 2 && !returnPos.IsValid())
- if get == nil || l != r {
- // invalidate lhs and use rhs
+func (check *Checker) initVars(lhs []*Var, origRHS []ast.Expr, returnPos token.Pos) {
+ rhs, commaOk := check.exprList(origRHS, len(lhs) == 2 && !returnPos.IsValid())
+
+ if len(lhs) != len(rhs) {
+ // invalidate lhs
for _, obj := range lhs {
if obj.typ == nil {
obj.typ = Typ[Invalid]
}
}
- if get == nil {
- return // error reported by unpack
+ // don't report an error if we already reported one
+ for _, x := range rhs {
+ if x.mode == invalid {
+ return
+ }
}
- check.useGetter(get, r)
if returnPos.IsValid() {
- check.errorf(atPos(returnPos), _WrongResultCount, "wrong number of return values (want %d, got %d)", l, r)
+ check.errorf(atPos(returnPos), _WrongResultCount, "wrong number of return values (want %d, got %d)", len(lhs), len(rhs))
return
}
- check.errorf(rhs[0], _WrongAssignCount, "cannot initialize %d variables with %d values", l, r)
+ check.errorf(rhs[0], _WrongAssignCount, "cannot initialize %d variables with %d values", len(lhs), len(rhs))
return
}
context = "return statement"
}
- var x operand
if commaOk {
var a [2]Type
for i := range a {
- get(&x, i)
- a[i] = check.initVar(lhs[i], &x, context)
+ a[i] = check.initVar(lhs[i], rhs[i], context)
}
- check.recordCommaOkTypes(rhs[0], a)
+ check.recordCommaOkTypes(origRHS[0], a)
return
}
for i, lhs := range lhs {
- get(&x, i)
- check.initVar(lhs, &x, context)
+ check.initVar(lhs, rhs[i], context)
}
}
-func (check *Checker) assignVars(lhs, rhs []ast.Expr) {
- l := len(lhs)
- get, r, commaOk := unpack(func(x *operand, i int) { check.multiExpr(x, rhs[i]) }, len(rhs), l == 2)
- if get == nil {
+func (check *Checker) assignVars(lhs, origRHS []ast.Expr) {
+ rhs, commaOk := check.exprList(origRHS, len(lhs) == 2)
+
+ if len(lhs) != len(rhs) {
check.useLHS(lhs...)
- return // error reported by unpack
- }
- if l != r {
- check.useGetter(get, r)
- check.errorf(rhs[0], _WrongAssignCount, "cannot assign %d values to %d variables", r, l)
+ // don't report an error if we already reported one
+ for _, x := range rhs {
+ if x.mode == invalid {
+ return
+ }
+ }
+ check.errorf(rhs[0], _WrongAssignCount, "cannot assign %d values to %d variables", len(rhs), len(lhs))
return
}
- var x operand
if commaOk {
var a [2]Type
for i := range a {
- get(&x, i)
- a[i] = check.assignVar(lhs[i], &x)
+ a[i] = check.assignVar(lhs[i], rhs[i])
}
- check.recordCommaOkTypes(rhs[0], a)
+ check.recordCommaOkTypes(origRHS[0], a)
return
}
for i, lhs := range lhs {
- get(&x, i)
- check.assignVar(lhs, &x)
+ check.assignVar(lhs, rhs[i])
}
}
// For len(x) and cap(x) we need to know if x contains any function calls or
// receive operations. Save/restore current setting and set hasCallOrRecv to
// false for the evaluation of x so that we can check it afterwards.
- // Note: We must do this _before_ calling unpack because unpack evaluates the
- // first argument before we even call arg(x, 0)!
+ // Note: We must do this _before_ calling exprList because exprList evaluates
+ // all arguments.
if id == _Len || id == _Cap {
defer func(b bool) {
check.hasCallOrRecv = b
}
// determine actual arguments
- var arg getter
+ var arg func(*operand, int) // TODO(gri) remove use of arg getter in favor of using xlist directly
nargs := len(call.Args)
switch id {
default:
// make argument getter
- arg, nargs, _ = unpack(func(x *operand, i int) { check.multiExpr(x, call.Args[i]) }, nargs, false)
- if arg == nil {
- return
- }
+ xlist, _ := check.exprList(call.Args, false)
+ arg = func(x *operand, i int) { *x = *xlist[i]; x.typ = expand(x.typ) }
+ nargs = len(xlist)
// evaluate first argument, if present
if nargs > 0 {
arg(x, 0)
// of S and the respective parameter passing rules apply."
S := x.typ
var T Type
- if s, _ := S.Underlying().(*Slice); s != nil {
+ if s := asSlice(S); s != nil {
T = s.elem
} else {
check.invalidArg(x, _InvalidAppend, "%s is not a slice", x)
// check general case by creating custom signature
sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature
sig.variadic = true
- check.arguments(x, call, sig, func(x *operand, i int) {
- // only evaluate arguments that have not been evaluated before
- if i < len(alist) {
- *x = alist[i]
- return
- }
- arg(x, i)
- }, nargs)
+ var xlist []*operand
+ // convert []operand to []*operand
+ for i := range alist {
+ xlist = append(xlist, &alist[i])
+ }
+ for i := len(alist); i < nargs; i++ {
+ var x operand
+ arg(&x, i)
+ xlist = append(xlist, &x)
+ }
+ check.arguments(call, sig, xlist) // discard result (we know the result type)
// ok to continue even if check.arguments reported errors
x.mode = value
mode := invalid
var typ Type
var val constant.Value
- switch typ = implicitArrayDeref(x.typ.Underlying()); t := typ.(type) {
+ switch typ = implicitArrayDeref(optype(x.typ)); t := typ.(type) {
case *Basic:
if isString(t) && id == _Len {
if x.mode == constant_ {
if id == _Len {
mode = value
}
+
+ case *Sum:
+ if t.is(func(t Type) bool {
+ switch t := under(t).(type) {
+ case *Basic:
+ if isString(t) && id == _Len {
+ return true
+ }
+ case *Array, *Slice, *Chan:
+ return true
+ case *Map:
+ if id == _Len {
+ return true
+ }
+ }
+ return false
+ }) {
+ mode = value
+ }
}
if mode == invalid && typ != Typ[Invalid] {
case _Close:
// close(c)
- c, _ := x.typ.Underlying().(*Chan)
+ c := asChan(x.typ)
if c == nil {
check.invalidArg(x, _InvalidClose, "%s is not a channel", x)
return
}
// the argument types must be of floating-point type
- if !isFloat(x.typ) {
+ f := func(x Type) Type {
+ if t := asBasic(x); t != nil {
+ switch t.kind {
+ case Float32:
+ return Typ[Complex64]
+ case Float64:
+ return Typ[Complex128]
+ case UntypedFloat:
+ return Typ[UntypedComplex]
+ }
+ }
+ return nil
+ }
+ resTyp := check.applyTypeFunc(f, x.typ)
+ if resTyp == nil {
check.invalidArg(x, _InvalidComplex, "arguments have type %s, expected floating-point", x.typ)
return
}
x.mode = value
}
- // determine result type
- var res BasicKind
- switch x.typ.Underlying().(*Basic).kind {
- case Float32:
- res = Complex64
- case Float64:
- res = Complex128
- case UntypedFloat:
- res = UntypedComplex
- default:
- unreachable()
- }
- resTyp := Typ[res]
-
if check.Types != nil && x.mode != constant_ {
check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ, x.typ))
}
case _Copy:
// copy(x, y []T) int
var dst Type
- if t, _ := x.typ.Underlying().(*Slice); t != nil {
+ if t := asSlice(x.typ); t != nil {
dst = t.elem
}
return
}
var src Type
- switch t := y.typ.Underlying().(type) {
+ switch t := optype(y.typ).(type) {
case *Basic:
if isString(y.typ) {
src = universeByte
case _Delete:
// delete(m, k)
- m, _ := x.typ.Underlying().(*Map)
+ m := asMap(x.typ)
if m == nil {
check.invalidArg(x, _InvalidDelete, "%s is not a map", x)
return
}
// the argument must be of complex type
- if !isComplex(x.typ) {
+ f := func(x Type) Type {
+ if t := asBasic(x); t != nil {
+ switch t.kind {
+ case Complex64:
+ return Typ[Float32]
+ case Complex128:
+ return Typ[Float64]
+ case UntypedComplex:
+ return Typ[UntypedFloat]
+ }
+ }
+ return nil
+ }
+ resTyp := check.applyTypeFunc(f, x.typ)
+ if resTyp == nil {
code := _InvalidImag
if id == _Real {
code = _InvalidReal
x.mode = value
}
- // determine result type
- var res BasicKind
- switch x.typ.Underlying().(*Basic).kind {
- case Complex64:
- res = Float32
- case Complex128:
- res = Float64
- case UntypedComplex:
- res = UntypedFloat
- default:
- unreachable()
- }
- resTyp := Typ[res]
-
if check.Types != nil && x.mode != constant_ {
check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ))
}
// make(T, n, m)
// (no argument evaluated yet)
arg0 := call.Args[0]
- T := check.typ(arg0)
+ T := check.varType(arg0)
if T == Typ[Invalid] {
return
}
- var min int // minimum number of arguments
- switch T.Underlying().(type) {
- case *Slice:
- min = 2
- case *Map, *Chan:
- min = 1
- default:
+ min, max := -1, 10
+ var valid func(t Type) bool
+ valid = func(t Type) bool {
+ var m int
+ switch t := optype(t).(type) {
+ case *Slice:
+ m = 2
+ case *Map, *Chan:
+ m = 1
+ case *Sum:
+ return t.is(valid)
+ default:
+ return false
+ }
+ if m > min {
+ min = m
+ }
+ if m+1 < max {
+ max = m + 1
+ }
+ return true
+ }
+
+ if !valid(T) {
check.invalidArg(arg0, _InvalidMake, "cannot make %s; type must be slice, map, or channel", arg0)
return
}
- if nargs < min || min+1 < nargs {
- check.errorf(call, _WrongArgCount, "%v expects %d or %d arguments; found %d", call, min, min+1, nargs)
+ if nargs < min || max < nargs {
+ if min == max {
+ check.errorf(call, _WrongArgCount, "%v expects %d arguments; found %d", call, min, nargs)
+ } else {
+ check.errorf(call, _WrongArgCount, "%v expects %d or %d arguments; found %d", call, min, max, nargs)
+ }
return
}
+
types := []Type{T}
var sizes []int64 // constant integer arguments, if any
for _, arg := range call.Args[1:] {
case _New:
// new(T)
// (no argument evaluated yet)
- T := check.typ(call.Args[0])
+ T := check.varType(call.Args[0])
if T == Typ[Invalid] {
return
}
case _Alignof:
// unsafe.Alignof(x T) uintptr
+ if asTypeParam(x.typ) != nil {
+ check.invalidOp(call, 0, "unsafe.Alignof undefined for %s", x)
+ return
+ }
check.assignment(x, nil, "argument to unsafe.Alignof")
if x.mode == invalid {
return
case _Sizeof:
// unsafe.Sizeof(x T) uintptr
+ if asTypeParam(x.typ) != nil {
+ check.invalidOp(call, 0, "unsafe.Sizeof undefined for %s", x)
+ return
+ }
check.assignment(x, nil, "argument to unsafe.Sizeof")
if x.mode == invalid {
return
return true
}
+// applyTypeFunc applies f to x. If x is a type parameter,
+// the result is a type parameter constrained by an new
+// interface bound. The type bounds for that interface
+// are computed by applying f to each of the type bounds
+// of x. If any of these applications of f return nil,
+// applyTypeFunc returns nil.
+// If x is not a type parameter, the result is f(x).
+func (check *Checker) applyTypeFunc(f func(Type) Type, x Type) Type {
+ if tp := asTypeParam(x); tp != nil {
+ // Test if t satisfies the requirements for the argument
+ // type and collect possible result types at the same time.
+ var rtypes []Type
+ if !tp.Bound().is(func(x Type) bool {
+ if r := f(x); r != nil {
+ rtypes = append(rtypes, r)
+ return true
+ }
+ return false
+ }) {
+ return nil
+ }
+
+ // construct a suitable new type parameter
+ tpar := NewTypeName(token.NoPos, nil /* = Universe pkg */, "<type parameter>", nil)
+ ptyp := check.NewTypeParam(tpar, 0, &emptyInterface) // assigns type to tpar as a side-effect
+ tsum := NewSum(rtypes)
+ ptyp.bound = &Interface{types: tsum, allMethods: markComplete, allTypes: tsum}
+
+ return ptyp
+ }
+
+ return f(x)
+}
+
// makeSig makes a signature for the given argument and result types.
// Default types are used for untyped arguments, and res may be nil.
func makeSig(res Type, args ...Type) *Signature {
//
func implicitArrayDeref(typ Type) Type {
if p, ok := typ.(*Pointer); ok {
- if a, ok := p.base.Underlying().(*Array); ok {
+ if a := asArray(p.base); a != nil {
return a
}
}
+// REVIEW INCOMPLETE
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
"unicode"
)
-func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
- check.exprOrType(x, e.Fun)
+// TODO(rFindley) this has diverged a bit from types2. Bring it up to date.
+// If call == nil, the "call" was an index expression, and orig is of type *ast.IndexExpr.
+func (check *Checker) call(x *operand, call *ast.CallExpr, orig ast.Expr) exprKind {
+ assert(orig != nil)
+ if call != nil {
+ assert(call == orig)
+ check.exprOrType(x, call.Fun)
+ } else {
+ // We must have an index expression.
+ // x has already been set up (evaluation of orig.X).
+ // Set up fake call so we can use its fields below.
+ expr := orig.(*ast.IndexExpr)
+ call = &ast.CallExpr{Fun: expr.X, Lparen: expr.Lbrack, Args: []ast.Expr{expr.Index}, Rparen: expr.Rbrack, Brackets: true}
+ }
switch x.mode {
case invalid:
- check.use(e.Args...)
- x.mode = invalid
- x.expr = e
+ check.use(call.Args...)
+ x.expr = orig
return statement
case typexpr:
- // conversion
+ // conversion or type instantiation
T := x.typ
x.mode = invalid
- switch n := len(e.Args); n {
+ if isGeneric(T) {
+ // type instantiation
+ x.typ = check.typ(call)
+ if x.typ != Typ[Invalid] {
+ x.mode = typexpr
+ }
+ return expression
+ }
+
+ // conversion
+ switch n := len(call.Args); n {
case 0:
- check.errorf(inNode(e, e.Rparen), _WrongArgCount, "missing argument in conversion to %s", T)
+ check.errorf(inNode(call, call.Rparen), _WrongArgCount, "missing argument in conversion to %s", T)
case 1:
- check.expr(x, e.Args[0])
+ check.expr(x, call.Args[0])
if x.mode != invalid {
- if e.Ellipsis.IsValid() {
- check.errorf(e.Args[0], _BadDotDotDotSyntax, "invalid use of ... in conversion to %s", T)
+ if call.Ellipsis.IsValid() {
+ check.errorf(call.Args[0], _BadDotDotDotSyntax, "invalid use of ... in conversion to %s", T)
break
}
+ if t := asInterface(T); t != nil {
+ check.completeInterface(token.NoPos, t)
+ if t.IsConstraint() {
+ check.errorf(call, 0, "cannot use interface %s in conversion (contains type list or is comparable)", T)
+ break
+ }
+ }
check.conversion(x, T)
}
default:
- check.use(e.Args...)
- check.errorf(e.Args[n-1], _WrongArgCount, "too many arguments in conversion to %s", T)
+ check.use(call.Args...)
+ check.errorf(call.Args[n-1], _WrongArgCount, "too many arguments in conversion to %s", T)
}
- x.expr = e
+ x.expr = orig
return conversion
case builtin:
id := x.id
- if !check.builtin(x, e, id) {
+ if !check.builtin(x, call, id) {
x.mode = invalid
}
- x.expr = e
+ x.expr = orig
// a non-constant result implies a function call
if x.mode != invalid && x.mode != constant_ {
check.hasCallOrRecv = true
// function/method call
cgocall := x.mode == cgofunc
- sig, _ := x.typ.Underlying().(*Signature)
+ sig := asSignature(x.typ)
if sig == nil {
check.invalidOp(x, _InvalidCall, "cannot call non-function %s", x)
x.mode = invalid
- x.expr = e
+ x.expr = orig
return statement
}
- arg, n, _ := unpack(func(x *operand, i int) { check.multiExpr(x, e.Args[i]) }, len(e.Args), false)
- if arg != nil {
- check.arguments(x, e, sig, arg, n)
- } else {
+ // evaluate arguments
+ args, ok := check.exprOrTypeList(call.Args)
+ if ok && call.Brackets && len(args) > 0 && args[0].mode != typexpr {
+ check.errorf(args[0], _NotAType, "%s is not a type", args[0])
+ ok = false
+ }
+ if !ok {
x.mode = invalid
+ x.expr = orig
+ return expression
+ }
+
+ // instantiate function if needed
+ if n := len(args); n > 0 && len(sig.tparams) > 0 && args[0].mode == typexpr {
+ // If the first argument is a type, assume we have explicit type arguments.
+
+ // check number of type arguments
+ // TODO(rFindley)
+ // if !check.conf.InferFromConstraints && n != len(sig.tparams) || n > len(sig.tparams) {
+ if n != len(sig.tparams) || n > len(sig.tparams) {
+ check.errorf(args[n-1], 0, "got %d type arguments but want %d", n, len(sig.tparams))
+ x.mode = invalid
+ x.expr = orig
+ return expression
+ }
+
+ // collect types
+ targs := make([]Type, n)
+ // TODO(rFindley) positioner?
+ poslist := make([]token.Pos, n)
+ for i, a := range args {
+ if a.mode != typexpr {
+ // error was reported earlier
+ x.mode = invalid
+ x.expr = orig
+ return expression
+ }
+ targs[i] = a.typ
+ poslist[i] = a.Pos()
+ }
+
+ // if we don't have enough type arguments, use constraint type inference
+ var inferred bool
+ if n < len(sig.tparams) {
+ var failed int
+ targs, failed = check.inferB(sig.tparams, targs)
+ if targs == nil {
+ // error was already reported
+ x.mode = invalid
+ x.expr = orig
+ return expression
+ }
+ if failed >= 0 {
+ // at least one type argument couldn't be inferred
+ assert(targs[failed] == nil)
+ tpar := sig.tparams[failed]
+ ppos := check.fset.Position(tpar.pos).String()
+ check.errorf(inNode(call, call.Rparen), 0, "cannot infer %s (%s) (%s)", tpar.name, ppos, targs)
+ x.mode = invalid
+ x.expr = orig
+ return expression
+ }
+ // all type arguments were inferred sucessfully
+ if debug {
+ for _, targ := range targs {
+ assert(targ != nil)
+ }
+ }
+ n = len(targs)
+ inferred = true
+ }
+ assert(n == len(sig.tparams))
+
+ // instantiate function signature
+ for i, typ := range targs {
+ // some positions may be missing if types are inferred
+ var pos token.Pos
+ if i < len(poslist) {
+ pos = poslist[i]
+ }
+ check.ordinaryType(atPos(pos), typ)
+ }
+ res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature)
+ assert(res.tparams == nil) // signature is not generic anymore
+ if inferred {
+ check.recordInferred(orig, targs, res)
+ }
+ x.typ = res
+ x.mode = value
+ x.expr = orig
+ return expression
}
+ // If we reach here, orig must have been a regular call, not an index expression.
+ assert(!call.Brackets)
+
+ sig = check.arguments(call, sig, args)
+
// determine result
switch sig.results.Len() {
case 0:
x.mode = value
x.typ = sig.results
}
-
- x.expr = e
+ x.expr = call
check.hasCallOrRecv = true
+ // if type inference failed, a parametrized result must be invalidated
+ // (operands cannot have a parametrized type)
+ if x.mode == value && len(sig.tparams) > 0 && isParameterized(sig.tparams, x.typ) {
+ x.mode = invalid
+ }
+
return statement
}
}
-// useGetter is like use, but takes a getter instead of a list of expressions.
-// It should be called instead of use if a getter is present to avoid repeated
-// evaluation of the first argument (since the getter was likely obtained via
-// unpack, which may have evaluated the first argument already).
-func (check *Checker) useGetter(get getter, n int) {
- var x operand
- for i := 0; i < n; i++ {
- get(&x, i)
+// exprOrTypeList returns a list of operands and reports an error if the
+// list contains a mix of values and types (ignoring invalid operands).
+func (check *Checker) exprOrTypeList(elist []ast.Expr) (xlist []*operand, ok bool) {
+ ok = true
+
+ switch len(elist) {
+ case 0:
+ // nothing to do
+
+ case 1:
+ // single (possibly comma-ok) value or type, or function returning multiple values
+ e := elist[0]
+ var x operand
+ check.multiExprOrType(&x, e)
+ if t, ok := x.typ.(*Tuple); ok && x.mode != invalid && x.mode != typexpr {
+ // multiple values
+ xlist = make([]*operand, t.Len())
+ for i, v := range t.vars {
+ xlist[i] = &operand{mode: value, expr: e, typ: v.typ}
+ }
+ break
+ }
+
+ check.instantiatedOperand(&x)
+
+ // exactly one (possibly invalid or comma-ok) value or type
+ xlist = []*operand{&x}
+
+ default:
+ // multiple (possibly invalid) values or types
+ xlist = make([]*operand, len(elist))
+ ntypes := 0
+ for i, e := range elist {
+ var x operand
+ check.exprOrType(&x, e)
+ xlist[i] = &x
+ switch x.mode {
+ case invalid:
+ ntypes = len(xlist) // make 'if' condition fail below (no additional error in this case)
+ case typexpr:
+ ntypes++
+ check.instantiatedOperand(&x)
+ }
+ }
+ if 0 < ntypes && ntypes < len(xlist) {
+ check.errorf(xlist[0], 0, "mix of value and type expressions")
+ ok = false
+ }
}
+
+ return
}
-// A getter sets x as the i'th operand, where 0 <= i < n and n is the total
-// number of operands (context-specific, and maintained elsewhere). A getter
-// type-checks the i'th operand; the details of the actual check are getter-
-// specific.
-type getter func(x *operand, i int)
-
-// unpack takes a getter get and a number of operands n. If n == 1, unpack
-// calls the incoming getter for the first operand. If that operand is
-// invalid, unpack returns (nil, 0, false). Otherwise, if that operand is a
-// function call, or a comma-ok expression and allowCommaOk is set, the result
-// is a new getter and operand count providing access to the function results,
-// or comma-ok values, respectively. The third result value reports if it
-// is indeed the comma-ok case. In all other cases, the incoming getter and
-// operand count are returned unchanged, and the third result value is false.
-//
-// In other words, if there's exactly one operand that - after type-checking
-// by calling get - stands for multiple operands, the resulting getter provides
-// access to those operands instead.
-//
-// If the returned getter is called at most once for a given operand index i
-// (including i == 0), that operand is guaranteed to cause only one call of
-// the incoming getter with that i.
-//
-func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) {
- if n != 1 {
- // zero or multiple values
- return get, n, false
- }
- // possibly result of an n-valued function call or comma,ok value
- var x0 operand
- get(&x0, 0)
- if x0.mode == invalid {
- return nil, 0, false
- }
+func (check *Checker) exprList(elist []ast.Expr, allowCommaOk bool) (xlist []*operand, commaOk bool) {
+ switch len(elist) {
+ case 0:
+ // nothing to do
+
+ case 1:
+ // single (possibly comma-ok) value, or function returning multiple values
+ e := elist[0]
+ var x operand
+ check.multiExpr(&x, e)
+ if t, ok := x.typ.(*Tuple); ok && x.mode != invalid {
+ // multiple values
+ xlist = make([]*operand, t.Len())
+ for i, v := range t.vars {
+ xlist[i] = &operand{mode: value, expr: e, typ: v.typ}
+ }
+ break
+ }
- if t, ok := x0.typ.(*Tuple); ok {
- // result of an n-valued function call
- return func(x *operand, i int) {
+ // exactly one (possibly invalid or comma-ok) value
+ xlist = []*operand{&x}
+ if allowCommaOk && (x.mode == mapindex || x.mode == commaok || x.mode == commaerr) {
x.mode = value
- x.expr = x0.expr
- x.typ = t.At(i).typ
- }, t.Len(), false
- }
-
- if x0.mode == mapindex || x0.mode == commaok || x0.mode == commaerr {
- // comma-ok value
- if allowCommaOk {
- a := [2]Type{x0.typ, Typ[UntypedBool]}
- if x0.mode == commaerr {
- a[1] = universeError
+ x2 := &operand{mode: value, expr: e, typ: Typ[UntypedBool]}
+ if x.mode == commaerr {
+ x2.typ = universeError
}
- return func(x *operand, i int) {
- x.mode = value
- x.expr = x0.expr
- x.typ = a[i]
- }, 2, true
+ xlist = append(xlist, x2)
+ commaOk = true
}
- x0.mode = value
- }
- // single value
- return func(x *operand, i int) {
- if i != 0 {
- unreachable()
+ default:
+ // multiple (possibly invalid) values
+ xlist = make([]*operand, len(elist))
+ for i, e := range elist {
+ var x operand
+ check.expr(&x, e)
+ xlist[i] = &x
}
- *x = x0
- }, 1, false
+ }
+
+ return
}
-// arguments checks argument passing for the call with the given signature.
-// The arg function provides the operand for the i'th argument.
-func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, arg getter, n int) {
- if call.Ellipsis.IsValid() {
- // last argument is of the form x...
- if !sig.variadic {
- check.errorf(atPos(call.Ellipsis), _NonVariadicDotDotDot, "cannot use ... in call to non-variadic %s", call.Fun)
- check.useGetter(arg, n)
+func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, args []*operand) (rsig *Signature) {
+ rsig = sig
+
+ // TODO(gri) try to eliminate this extra verification loop
+ for _, a := range args {
+ switch a.mode {
+ case typexpr:
+ check.errorf(a, 0, "%s used as value", a)
return
- }
- if len(call.Args) == 1 && n > 1 {
- // f()... is not permitted if f() is multi-valued
- check.errorf(atPos(call.Ellipsis), _InvalidDotDotDotOperand, "cannot use ... with %d-valued %s", n, call.Args[0])
- check.useGetter(arg, n)
+ case invalid:
return
}
}
- // evaluate arguments
- context := check.sprintf("argument to %s", call.Fun)
- for i := 0; i < n; i++ {
- arg(x, i)
- if x.mode != invalid {
- var ellipsis token.Pos
- if i == n-1 && call.Ellipsis.IsValid() {
- ellipsis = call.Ellipsis
+ // Function call argument/parameter count requirements
+ //
+ // | standard call | dotdotdot call |
+ // --------------+------------------+----------------+
+ // standard func | nargs == npars | invalid |
+ // --------------+------------------+----------------+
+ // variadic func | nargs >= npars-1 | nargs == npars |
+ // --------------+------------------+----------------+
+
+ nargs := len(args)
+ npars := sig.params.Len()
+ ddd := call.Ellipsis.IsValid()
+
+ // set up parameters
+ sigParams := sig.params // adjusted for variadic functions (may be nil for empty parameter lists!)
+ adjusted := false // indicates if sigParams is different from t.params
+ if sig.variadic {
+ if ddd {
+ // variadic_func(a, b, c...)
+ if len(call.Args) == 1 && nargs > 1 {
+ // f()... is not permitted if f() is multi-valued
+ check.errorf(inNode(call, call.Ellipsis), _InvalidDotDotDot, "cannot use ... with %d-valued %s", nargs, call.Args[0])
+ return
}
- check.argument(sig, i, x, ellipsis, context)
+ } else {
+ // variadic_func(a, b, c)
+ if nargs >= npars-1 {
+ // Create custom parameters for arguments: keep
+ // the first npars-1 parameters and add one for
+ // each argument mapping to the ... parameter.
+ vars := make([]*Var, npars-1) // npars > 0 for variadic functions
+ copy(vars, sig.params.vars)
+ last := sig.params.vars[npars-1]
+ typ := last.typ.(*Slice).elem
+ for len(vars) < nargs {
+ vars = append(vars, NewParam(last.pos, last.pkg, last.name, typ))
+ }
+ sigParams = NewTuple(vars...) // possibly nil!
+ adjusted = true
+ npars = nargs
+ } else {
+ // nargs < npars-1
+ npars-- // for correct error message below
+ }
+ }
+ } else {
+ if ddd {
+ // standard_func(a, b, c...)
+ check.errorf(inNode(call, call.Ellipsis), _NonVariadicDotDotDot, "cannot use ... in call to non-variadic %s", call.Fun)
+ return
}
+ // standard_func(a, b, c)
}
// check argument count
- if sig.variadic {
- // a variadic function accepts an "empty"
- // last argument: count one extra
- n++
- }
- if n < sig.params.Len() {
- check.errorf(inNode(call, call.Rparen), _WrongArgCount, "too few arguments in call to %s", call.Fun)
- // ok to continue
- }
-}
-
-// argument checks passing of argument x to the i'th parameter of the given signature.
-// If ellipsis is valid, the argument is followed by ... at that position in the call.
-func (check *Checker) argument(sig *Signature, i int, x *operand, ellipsis token.Pos, context string) {
- check.singleValue(x)
- if x.mode == invalid {
+ switch {
+ case nargs < npars:
+ check.errorf(inNode(call, call.Rparen), _WrongArgCount, "not enough arguments in call to %s", call.Fun)
+ return
+ case nargs > npars:
+ check.errorf(args[npars], _WrongArgCount, "too many arguments in call to %s", call.Fun) // report at first extra argument
return
}
- n := sig.params.Len()
-
- // determine parameter type
- var typ Type
- switch {
- case i < n:
- typ = sig.params.vars[i].typ
- case sig.variadic:
- typ = sig.params.vars[n-1].typ
+ // infer type arguments and instantiate signature if necessary
+ if len(sig.tparams) > 0 {
+ // TODO(gri) provide position information for targs so we can feed
+ // it to the instantiate call for better error reporting
+ targs, failed := check.infer(sig.tparams, sigParams, args)
+ if targs == nil {
+ return // error already reported
+ }
+ if failed >= 0 {
+ // Some type arguments couldn't be inferred. Use
+ // bounds type inference to try to make progress.
+ // TODO(rFindley)
+ /*
+ if check.conf.InferFromConstraints {
+ targs, failed = check.inferB(sig.tparams, targs)
+ if targs == nil {
+ return // error already reported
+ }
+ }
+ */
+ if failed >= 0 {
+ // at least one type argument couldn't be inferred
+ assert(targs[failed] == nil)
+ tpar := sig.tparams[failed]
+ ppos := check.fset.Position(tpar.pos).String()
+ check.errorf(inNode(call, call.Rparen), 0, "cannot infer %s (%s) (%s)", tpar.name, ppos, targs)
+ return
+ }
+ }
+ // all type arguments were inferred sucessfully
if debug {
- if _, ok := typ.(*Slice); !ok {
- check.dump("%v: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), typ)
+ for _, targ := range targs {
+ assert(targ != nil)
}
}
- default:
- check.errorf(x, _WrongArgCount, "too many arguments")
- return
- }
- if ellipsis.IsValid() {
- if i != n-1 {
- check.errorf(atPos(ellipsis), _MisplacedDotDotDot, "can only use ... with matching parameter")
- return
- }
- // argument is of the form x... and x is single-valued
- if _, ok := x.typ.Underlying().(*Slice); !ok && x.typ != Typ[UntypedNil] { // see issue #18268
- check.errorf(x, _InvalidDotDotDotOperand, "cannot use %s as parameter of type %s", x, typ)
- return
+ // compute result signature
+ rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature)
+ assert(rsig.tparams == nil) // signature is not generic anymore
+ check.recordInferred(call, targs, rsig)
+
+ // Optimization: Only if the parameter list was adjusted do we
+ // need to compute it from the adjusted list; otherwise we can
+ // simply use the result signature's parameter list.
+ if adjusted {
+ sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.tparams, targs)).(*Tuple)
+ } else {
+ sigParams = rsig.params
}
- } else if sig.variadic && i >= n-1 {
- // use the variadic parameter slice's element type
- typ = typ.(*Slice).elem
}
- check.assignment(x, typ, context)
+ // check arguments
+ // TODO(gri) Possible optimization (may be tricky): We could avoid
+ // checking arguments from which we inferred type arguments.
+ for i, a := range args {
+ check.assignment(a, sigParams.vars[i].typ, check.sprintf("argument to %s", call.Fun))
+ }
+
+ return
}
var cgoPrefixes = [...]string{
x.typ = exp.typ
x.id = exp.id
default:
- check.dump("unexpected object %v", exp)
+ check.dump("%v: unexpected object %v", e.Sel.Pos(), exp)
unreachable()
}
x.expr = e
goto Error
}
+ check.instantiatedOperand(x)
+
obj, index, indirect = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
if obj == nil {
switch {
case indirect:
check.errorf(e.Sel, _InvalidMethodExpr, "cannot call pointer method %s on %s", sel, x.typ)
default:
- // Check if capitalization of sel matters and provide better error
- // message in that case.
+ var why string
+ if tpar := asTypeParam(x.typ); tpar != nil {
+ // Type parameter bounds don't specify fields, so don't mention "field".
+ switch obj := tpar.Bound().obj.(type) {
+ case nil:
+ why = check.sprintf("type bound for %s has no method %s", x.typ, sel)
+ case *TypeName:
+ why = check.sprintf("interface %s has no method %s", obj.name, sel)
+ }
+ } else {
+ why = check.sprintf("type %s has no field or method %s", x.typ, sel)
+ }
+
+ // Check if capitalization of sel matters and provide better error message in that case.
if len(sel) > 0 {
var changeCase string
if r := rune(sel[0]); unicode.IsUpper(r) {
changeCase = string(unicode.ToUpper(r)) + sel[1:]
}
if obj, _, _ = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil {
- check.errorf(e.Sel, _MissingFieldOrMethod, "%s.%s undefined (type %s has no field or method %s, but does have %s)", x.expr, sel, x.typ, sel, changeCase)
- break
+ why += ", but does have " + changeCase
}
}
- check.errorf(e.Sel, _MissingFieldOrMethod, "%s.%s undefined (type %s has no field or method %s)", x.expr, sel, x.typ, sel)
+
+ check.errorf(e.Sel, _MissingFieldOrMethod, "%s.%s undefined (%s)", x.expr, sel, why)
}
goto Error
}
// methods may not have a fully set up signature yet
if m, _ := obj.(*Func); m != nil {
check.objDecl(m, nil)
+ // If m has a parameterized receiver type, infer the type parameter
+ // values from the actual receiver provided and then substitute the
+ // type parameters in the signature accordingly.
+ // TODO(gri) factor this code out
+ sig := m.typ.(*Signature)
+ if len(sig.rparams) > 0 {
+ // The method may have a pointer receiver, but the actually provided receiver
+ // may be a (hopefully addressable) non-pointer value, or vice versa. Here we
+ // only care about inferring receiver type parameters; to make the inference
+ // work, match up pointer-ness of receiver and argument.
+ arg := x
+ if ptrRecv := isPointer(sig.recv.typ); ptrRecv != isPointer(arg.typ) {
+ copy := *arg
+ if ptrRecv {
+ copy.typ = NewPointer(arg.typ)
+ } else {
+ copy.typ = arg.typ.(*Pointer).base
+ }
+ arg = ©
+ }
+ targs, failed := check.infer(sig.rparams, NewTuple(sig.recv), []*operand{arg})
+ if failed >= 0 {
+ // We may reach here if there were other errors (see issue #40056).
+ // check.infer will report a follow-up error.
+ // TODO(gri) avoid the follow-up error or provide better explanation.
+ goto Error
+ }
+ // Don't modify m. Instead - for now - make a copy of m and use that instead.
+ // (If we modify m, some tests will fail; possibly because the m is in use.)
+ // TODO(gri) investigate and provide a correct explanation here
+ copy := *m
+ copy.typ = check.subst(e.Pos(), m.typ, makeSubstMap(sig.rparams, targs))
+ obj = ©
+ }
+ // TODO(gri) we also need to do substitution for parameterized interface methods
+ // (this breaks code in testdata/linalg.go2 at the moment)
+ // 12/20/2019: Is this TODO still correct?
}
if x.mode == typexpr {
}
x.mode = value
x.typ = &Signature{
- params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "", x.typ)}, params...)...),
+ tparams: sig.tparams,
+ params: NewTuple(append([]*Var{NewVar(token.NoPos, check.pkg, "_", x.typ)}, params...)...),
results: sig.results,
variadic: sig.variadic,
}
// addressability, should we report the type &(x.typ) instead?
check.recordSelection(e, MethodVal, x.typ, obj, index, indirect)
- if debug {
+ // TODO(gri) The verification pass below is disabled for now because
+ // method sets don't match method lookup in some cases.
+ // For instance, if we made a copy above when creating a
+ // custom method for a parameterized received type, the
+ // method set method doesn't match (no copy there). There
+ /// may be other situations.
+ disabled := true
+ if !disabled && debug {
// Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
// TODO(gri) This only works because we call LookupFieldOrMethod
// _before_ calling NewMethodSet: LookupFieldOrMethod completes
switch {
case constArg && isConstType(T):
// constant conversion
- switch t := T.Underlying().(*Basic); {
+ switch t := asBasic(T); {
case representableConst(x.val, check, t, &x.val):
ok = true
case isInteger(x.typ) && isString(t):
// "x's type and T have identical underlying types if tags are ignored"
V := x.typ
- Vu := V.Underlying()
- Tu := T.Underlying()
+ Vu := under(V)
+ Tu := under(T)
if check.identicalIgnoreTags(Vu, Tu) {
return true
}
// have identical underlying types if tags are ignored"
if V, ok := V.(*Pointer); ok {
if T, ok := T.(*Pointer); ok {
- if check.identicalIgnoreTags(V.base.Underlying(), T.base.Underlying()) {
+ if check.identicalIgnoreTags(under(V.base), under(T.base)) {
return true
}
}
}
// "x's type and T are both integer or floating point types"
- if (isInteger(V) || isFloat(V)) && (isInteger(T) || isFloat(T)) {
+ if isIntegerOrFloat(V) && isIntegerOrFloat(T) {
return true
}
}
func isUintptr(typ Type) bool {
- t, ok := typ.Underlying().(*Basic)
- return ok && t.kind == Uintptr
+ t := asBasic(typ)
+ return t != nil && t.kind == Uintptr
}
func isUnsafePointer(typ Type) bool {
- // TODO(gri): Is this (typ.Underlying() instead of just typ) correct?
+ // TODO(gri): Is this asBasic() instead of typ.(*Basic) correct?
+ // (The former calls under(), while the latter doesn't.)
// The spec does not say so, but gc claims it is. See also
// issue 6326.
- t, ok := typ.Underlying().(*Basic)
- return ok && t.kind == UnsafePointer
+ t := asBasic(typ)
+ return t != nil && t.kind == UnsafePointer
}
func isPointer(typ Type) bool {
- _, ok := typ.Underlying().(*Pointer)
- return ok
+ return asPointer(typ) != nil
}
func isBytesOrRunes(typ Type) bool {
- if s, ok := typ.(*Slice); ok {
- t, ok := s.elem.Underlying().(*Basic)
- return ok && (t.kind == Byte || t.kind == Rune)
+ if s := asSlice(typ); s != nil {
+ t := asBasic(s.elem)
+ return t != nil && (t.kind == Byte || t.kind == Rune)
}
return false
}
_NonVariadicDotDotDot
// _MisplacedDotDotDot occurs when a "..." is used somewhere other than the
- // final argument to a function call.
+ // final argument in a function declaration.
//
// Example:
- // func printArgs(args ...int) {
- // for _, a := range args {
- // println(a)
- // }
- // }
- //
- // func f() {
- // a := []int{1,2,3}
- // printArgs(0, a...)
- // }
+ // func f(...int, int)
_MisplacedDotDotDot
- // _InvalidDotDotDotOperand occurs when a "..." operator is applied to a
- // single-valued operand.
- //
- // Example:
- // func printArgs(args ...int) {
- // for _, a := range args {
- // println(a)
- // }
- // }
- //
- // func f() {
- // a := 1
- // printArgs(a...)
- // }
- //
- // Example:
- // func args() (int, int) {
- // return 1, 2
- // }
- //
- // func printArgs(args ...int) {
- // for _, a := range args {
- // println(a)
- // }
- // }
- //
- // func g() {
- // printArgs(args()...)
- // }
- _InvalidDotDotDotOperand
-
// _InvalidDotDotDot occurs when a "..." is used in a non-variadic built-in
// function.
//
import "io"
type R = io.Reader
func _() {
- /* interface{R}.Read => , func(interface{io.Reader}, p []byte) (n int, err error) */
+ /* interface{R}.Read => , func(_ interface{io.Reader}, p []byte) (n int, err error) */
_ = func() {
- /* interface{io.Writer}.Write => , func(interface{io.Writer}, p []byte) (n int, err error) */
+ /* interface{io.Writer}.Write => , func(_ interface{io.Writer}, p []byte) (n int, err error) */
type io interface {} // must not shadow io in line above
}
type R interface {} // must not shadow R in first line of this function body
+// 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.
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
}
if x.mode == constant_ {
- typ := x.typ.Underlying().(*Basic)
+ typ := asBasic(x.typ)
var prec uint
if isUnsigned(typ) {
prec = uint(check.conf.sizeof(typ) * 8)
// 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
}
}
func (check *Checker) canConvertUntyped(x *operand, target Type) error {
+ target = expand(target)
if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] {
return nil
}
if e != nil {
x.expr = e // for better error message
}
- check.representable(x, x.typ.Underlying().(*Basic))
+ check.representable(x, asBasic(x.typ))
}
return
}
if x.mode == constant_ && y.mode == constant_ {
xval := x.val
yval := y.val
- typ := x.typ.Underlying().(*Basic)
+ typ := asBasic(x.typ)
// force integer division of integer operands
if op == token.QUO && isInteger(typ) {
op = token.QUO_ASSIGN
//
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
+ }
+ }
+}
_ = append(s, b)
_ = append(s, x /* ERROR cannot use x */ )
_ = append(s, s /* ERROR cannot use s */ )
- _ = append(s... /* ERROR can only use ... with matching parameter */ )
- _ = append(s, b, s... /* ERROR can only use ... with matching parameter */ )
+ _ = append(s...) /* ERROR not enough arguments */
+ _ = append(s, b, s /* ERROR too many arguments */ ...)
_ = append(s, 1, 2, 3)
_ = append(s, 1, 2, 3, x /* ERROR cannot use x */ , 5, 6, 6)
- _ = append(s, 1, 2, s... /* ERROR can only use ... with matching parameter */ )
+ _ = append(s, 1, 2 /* ERROR too many arguments */, s...)
_ = append([]interface{}(nil), 1, 2, "foo", x, 3.1425, false)
type S []byte
f1(0)
f1(x)
f1(10.0)
- f1() /* ERROR "too few arguments" */
+ f1() /* ERROR "not enough arguments" */
f1(x, y /* ERROR "too many arguments" */ )
f1(s /* ERROR "cannot use .* in argument" */ )
f1(x ... /* ERROR "cannot use ..." */ )
f1(g0 /* ERROR "used as value" */ ())
f1(g1())
- f1(g2 /* ERROR "cannot use g2" */ /* ERROR "too many arguments" */ ())
+ f1(g2 /* ERROR "too many arguments" */ ())
- f2() /* ERROR "too few arguments" */
- f2(3.14) /* ERROR "too few arguments" */
+ f2() /* ERROR "not enough arguments" */
+ f2(3.14) /* ERROR "not enough arguments" */
f2(3.14, "foo")
f2(x /* ERROR "cannot use .* in argument" */ , "foo")
f2(g0 /* ERROR "used as value" */ ())
- f2(g1 /* ERROR "cannot use .* in argument" */ ()) /* ERROR "too few arguments" */
+ f2(g1()) /* ERROR "not enough arguments" */
f2(g2())
- fs() /* ERROR "too few arguments" */
+ fs() /* ERROR "not enough arguments" */
fs(g0 /* ERROR "used as value" */ ())
fs(g1 /* ERROR "cannot use .* in argument" */ ())
- fs(g2 /* ERROR "cannot use .* in argument" */ /* ERROR "too many arguments" */ ())
+ fs(g2 /* ERROR "too many arguments" */ ())
fs(gs())
fv()
fv(s /* ERROR "cannot use .* in argument" */ )
fv(s...)
fv(x /* ERROR "cannot use" */ ...)
- fv(1, s... /* ERROR "can only use ... with matching parameter" */ )
+ fv(1, s /* ERROR "too many arguments" */ ...)
fv(gs /* ERROR "cannot use .* in argument" */ ())
fv(gs /* ERROR "cannot use .* in argument" */ ()...)
t.fm(1, 2.0, x)
t.fm(s /* ERROR "cannot use .* in argument" */ )
t.fm(g1())
- t.fm(1, s... /* ERROR "can only use ... with matching parameter" */ )
+ t.fm(1, s /* ERROR "too many arguments" */ ...)
t.fm(gs /* ERROR "cannot use .* in argument" */ ())
t.fm(gs /* ERROR "cannot use .* in argument" */ ()...)
T.fm(t, 1, 2.0, x)
T.fm(t, s /* ERROR "cannot use .* in argument" */ )
T.fm(t, g1())
- T.fm(t, 1, s... /* ERROR "can only use ... with matching parameter" */ )
+ T.fm(t, 1, s /* ERROR "too many arguments" */ ...)
T.fm(t, gs /* ERROR "cannot use .* in argument" */ ())
T.fm(t, gs /* ERROR "cannot use .* in argument" */ ()...)
i.fm(1, 2.0, x)
i.fm(s /* ERROR "cannot use .* in argument" */ )
i.fm(g1())
- i.fm(1, s... /* ERROR "can only use ... with matching parameter" */ )
+ i.fm(1, s /* ERROR "too many arguments" */ ...)
i.fm(gs /* ERROR "cannot use .* in argument" */ ())
i.fm(gs /* ERROR "cannot use .* in argument" */ ()...)
g := func(int, bool){}
var m map[int]int
- g(m[0]) /* ERROR "too few arguments" */
+ g(m[0]) /* ERROR "not enough arguments" */
// assignments to _
_ = nil /* ERROR "use of untyped nil" */