diff --git a/go/types/call.go b/go/types/call.go index 066b8383..67ddac3e 100644 --- a/go/types/call.go +++ b/go/types/call.go @@ -14,73 +14,70 @@ import ( func (check *checker) call(x *operand, e *ast.CallExpr) { check.exprOrType(x, e.Fun) if x.mode == invalid { - // We don't have a valid call or conversion but we have list of arguments. + // We don't have a valid call or conversion but we have a list of arguments. // Typecheck them independently for better partial type information in // the presence of type errors. for _, arg := range e.Args { check.expr(x, arg) } - goto Error + x.mode = invalid + x.expr = e + return + } - } else if x.mode == typexpr { + if x.mode == typexpr { check.conversion(x, e, x.typ) + return + } - } else if sig, ok := x.typ.Underlying().(*Signature); ok { - // check parameters + if sig, ok := x.typ.Underlying().(*Signature); ok { + // function/method call - // 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() { + // last argument is of the form x... if sig.isVariadic { passSlice = true } else { - check.errorf(e.Ellipsis, "cannot use ... in call to %s", e.Fun) + check.errorf(e.Ellipsis, "cannot use ... in call to non-variadic %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) - if x.mode == invalid { - goto Error // TODO(gri): we can do better - } - if t, ok := x.typ.(*Tuple); ok { - // multiple result values - n = t.Len() - for i := 0; i < n; i++ { - obj := t.At(i) - x.mode = value - x.expr = nil // TODO(gri) can we do better here? (for good error messages) - x.typ = obj.typ - check.argument(sig, i, nil, x, passSlice && i+1 == n) + // evaluate arguments + n := len(e.Args) // argument count + if n == 1 { + // single argument but possibly a multi-valued function call + arg := e.Args[0] + check.expr(x, arg) + if x.mode != invalid { + if t, ok := x.typ.(*Tuple); ok { + // argument is multi-valued function call + n = t.Len() + for i := 0; i < n; i++ { + x.mode = value + x.expr = arg + x.typ = t.At(i).typ + check.argument(sig, i, x, passSlice && i == n-1) + } + } else { + // single value + check.argument(sig, 0, x, passSlice) } } else { - // single result value - n = 1 - check.argument(sig, 0, nil, x, passSlice) + n = sig.params.Len() // avoid additional argument length errors below } - } else { - // We don't have a single argument or it is not a function call. - n = len(e.Args) + // zero or multiple arguments for i, arg := range e.Args { - check.argument(sig, i, arg, x, passSlice && i+1 == n) + check.expr(x, arg) + if x.mode != invalid { + check.argument(sig, i, x, passSlice && i == n-1) + } } } - // determine if we have enough arguments + // check argument count if sig.isVariadic { // a variadic function accepts an "empty" // last argument: count one extra @@ -97,82 +94,64 @@ func (check *checker) call(x *operand, e *ast.CallExpr) { x.mode = novalue case 1: x.mode = value - x.typ = sig.results.vars[0].typ + x.typ = sig.results.vars[0].typ // unpack tuple default: x.mode = value x.typ = sig.results } - - } else if bin, ok := x.typ.(*Builtin); ok { - check.builtin(x, e, bin) - - } else { - check.invalidOp(x.pos(), "cannot call non-function %s", x) - goto Error + x.expr = e + return } - // everything went well - x.expr = e - return + if bin, ok := x.typ.(*Builtin); ok { + check.builtin(x, e, bin) + return + } -Error: + check.invalidOp(x.pos(), "cannot call non-function %s", x) x.mode = invalid x.expr = e } -// argument typechecks passing an argument arg (if arg != nil) or -// x (if arg == nil) to the i'th parameter of the given signature. +// argument checks passing of argument x 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 +func (check *checker) argument(sig *Signature, i int, x *operand, passSlice bool) { n := sig.params.Len() - if i < n { - par = sig.params.vars[i] - } else if sig.isVariadic { - par = sig.params.vars[n-1] - } else { - var pos token.Pos - switch { - case arg != nil: - pos = arg.Pos() - case x != nil: - pos = x.pos() - default: - // TODO(gri) what position to use? + + // determine parameter type + var typ Type + switch { + case i < n: + typ = sig.params.vars[i].typ + case sig.isVariadic: + typ = sig.params.vars[n-1].typ + if debug { + if _, ok := typ.(*Slice); !ok { + check.dump("%s: expected slice type, got %s", sig.params.vars[n-1].Pos(), typ) + } } - check.errorf(pos, "too many arguments") + default: + check.errorf(x.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.typ - - if arg != nil { - check.expr(x, arg) - } - if x.mode == invalid { - return // ignore this argument - } - - // check last argument of the form x... if passSlice { - if i+1 != n { + // argument is of the form x... + if i != n-1 { check.errorf(x.pos(), "can only use ... with matching parameter") - return // ignore this argument + return } - // 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 _, ok := x.typ.(*Slice); !ok { + check.errorf(x.pos(), "cannot use %s as parameter of type %s", x, typ) + return + } + } else if sig.isVariadic && i >= n-1 { + // use the variadic parameter slice's element type + typ = typ.(*Slice).elt } - if !check.assignment(x, z.typ) && x.mode != invalid { - check.errorf(x.pos(), "cannot pass argument %s to %s", x, &z) + if !check.assignment(x, typ) && x.mode != invalid { + check.errorf(x.pos(), "cannot pass argument %s to parameter of type %s", x, typ) } } diff --git a/go/types/errors.go b/go/types/errors.go index d2befaa1..f8a4a7b5 100644 --- a/go/types/errors.go +++ b/go/types/errors.go @@ -188,10 +188,12 @@ func writeTuple(buf *bytes.Buffer, tup *Tuple, isVariadic bool) { buf.WriteString(v.name) buf.WriteByte(' ') } + typ := v.typ if isVariadic && i == len(tup.vars)-1 { buf.WriteString("...") + typ = typ.(*Slice).elt } - writeType(buf, v.typ) + writeType(buf, typ) } } buf.WriteByte(')') diff --git a/go/types/gcimporter.go b/go/types/gcimporter.go index 7cf524d8..7a06f6a8 100644 --- a/go/types/gcimporter.go +++ b/go/types/gcimporter.go @@ -535,6 +535,9 @@ func (p *gcParser) parseParameter() (par *Var, isVariadic bool) { isVariadic = true } typ := p.parseType() + if isVariadic { + typ = &Slice{elt: typ} + } // ignore argument tag (e.g. "noescape") if p.tok == scanner.String { p.next() diff --git a/go/types/testdata/expr3.src b/go/types/testdata/expr3.src index d3cbf62a..bec1ad16 100644 --- a/go/types/testdata/expr3.src +++ b/go/types/testdata/expr3.src @@ -298,6 +298,7 @@ func f2(u float32, s string) {} func fs(s []byte) {} func fv(x ...int) {} func fi(x ... interface{}) {} +func (T) fm(x ...int) func g0() {} func g1() int { return 0} @@ -335,17 +336,44 @@ func _calls() { fs /* ERROR "too few arguments" */ () fs(g0 /* ERROR "used as value" */ ()) fs(g1 /* ERROR "cannot pass" */ ()) - // fs(g2()) // TODO(gri) missing position in error message + fs(g2 /* ERROR "cannot pass" */ /* ERROR "too many arguments" */ ()) fs(gs()) fv() fv(1, 2.0, x) fv(s /* ERROR "cannot pass" */ ) fv(s...) + fv(x /* ERROR "cannot use" */ ...) fv(1, s /* ERROR "can only use ... with matching parameter" */ ...) fv(gs /* ERROR "cannot pass" */ ()) fv(gs /* ERROR "cannot pass" */ ()...) + var t T + t.fm() + t.fm(1, 2.0, x) + t.fm(s /* ERROR "cannot pass" */ ) + t.fm(g1()) + t.fm(1, s /* ERROR "can only use ... with matching parameter" */ ...) + t.fm(gs /* ERROR "cannot pass" */ ()) + t.fm(gs /* ERROR "cannot pass" */ ()...) + + T.fm(t, ) + T.fm(t, 1, 2.0, x) + T.fm(t, s /* ERROR "cannot pass" */ ) + T.fm(t, g1()) + T.fm(t, 1, s /* ERROR "can only use ... with matching parameter" */ ...) + T.fm(t, gs /* ERROR "cannot pass" */ ()) + T.fm(t, gs /* ERROR "cannot pass" */ ()...) + + var i interface{ fm(x ...int) } = t + i.fm() + i.fm(1, 2.0, x) + i.fm(s /* ERROR "cannot pass" */ ) + i.fm(g1()) + i.fm(1, s /* ERROR "can only use ... with matching parameter" */ ...) + i.fm(gs /* ERROR "cannot pass" */ ()) + i.fm(gs /* ERROR "cannot pass" */ ()...) + fi() fi(1, 2.0, x, 3.14, "foo") fi(g2()) diff --git a/go/types/types.go b/go/types/types.go index dfedae41..3528edad 100644 --- a/go/types/types.go +++ b/go/types/types.go @@ -197,8 +197,18 @@ type Signature struct { // NewSignature returns a new function type for the given receiver, parameters, // and results, either of which may be nil. If isVariadic is set, the function -// is variadic. +// is variadic, it must have at least one parameter, and the last parameter +// must be of unnamed slice type. func NewSignature(recv *Var, params, results *Tuple, isVariadic bool) *Signature { + if isVariadic { + n := params.Len() + if n == 0 { + panic("types.NewSignature: variadic function must have at least one parameter") + } + if _, ok := params.At(n - 1).typ.(*Slice); !ok { + panic("types.NewSignature: variadic parameter must be of unnamed slice type") + } + } return &Signature{nil, nil, recv, params, results, isVariadic} } diff --git a/go/types/typexpr.go b/go/types/typexpr.go index d6c501bf..8777692b 100644 --- a/go/types/typexpr.go +++ b/go/types/typexpr.go @@ -298,7 +298,7 @@ func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicO if list == nil { return } - var last *Var + for i, field := range list.List { ftype := field.Type if t, _ := ftype.(*ast.Ellipsis); t != nil { @@ -307,37 +307,33 @@ func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicO isVariadic = true } else { check.invalidAST(field.Pos(), "... not permitted") - // ok to continue + // ignore ... and continue } } + typ := check.typ(ftype, nil, true) // 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, nil, true) if len(field.Names) > 0 { // named parameter for _, name := range field.Names { par := NewVar(name.Pos(), check.pkg, name.Name, typ) check.declare(scope, name, par) - - last = par - copy := *par - params = append(params, ©) + params = append(params, par) } } else { // anonymous parameter par := NewVar(ftype.Pos(), check.pkg, "", typ) check.callImplicitObj(field, par) - - 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 { + + // For a variadic function, change the last parameter's type from T to []T. + if isVariadic && len(params) > 0 { + last := params[len(params)-1] last.typ = &Slice{elt: last.typ} } + return } @@ -345,6 +341,7 @@ func (check *checker) collectMethods(list *ast.FieldList, cycleOk bool) (methods if list == nil { return nil } + scope := NewScope(nil) for _, f := range list.List { typ := check.typ(f.Type, nil, cycleOk) @@ -385,6 +382,7 @@ func (check *checker) collectMethods(list *ast.FieldList, cycleOk bool) (methods } } } + return }