[dev.typeparams] go/types: import expr changes from dev.go2go

This change imports assignments.go, builtins.go, call.go,
conversions.go, and expr.go from the dev.go2go branch.

Changes from dev.go2go:
 - Update error positions and codes.
 - Fix some failing tests due to error message changes.
 - Fix a bug in exprInternal where normal IndexExpr checking wasn't
   proceeding in the case of a non-generic indexed func.
 - Fix the type of the second operand in commaerr expressions to be
   universeError. We should add tests in a later CL.

This code was mostly reviewed, but call.go and expr.go were marked
incomplete.  Additionally, these two files had notably diverged from
types2, requiring further understanding.

The dev.go2go branch significantly simplified the type checking of
arguments, resulting in the removal of the _InvalidDotDotDot operand
error code.

Change-Id: Iba2cef95e17bfaa6da6d4eb94c2e2ce1c691ac44
Reviewed-on: https://go-review.googlesource.com/c/go/+/282193
Run-TryBot: Robert Findley <rfindley@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
Trust: Robert Griesemer <gri@golang.org>
Trust: Robert Findley <rfindley@google.com>
This commit is contained in:
Rob Findley 2021-01-06 12:25:10 -05:00 committed by Robert Findley
parent 822aeacd9e
commit 81cd99858d
10 changed files with 837 additions and 427 deletions

View file

@ -26,7 +26,9 @@ func (check *Checker) assignment(x *operand, T Type, context string) {
case constant_, variable, mapindex, value, commaok, commaerr: case constant_, variable, mapindex, value, commaok, commaerr:
// ok // ok
default: 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) { if isUntyped(x.typ) {
@ -66,6 +68,11 @@ func (check *Checker) assignment(x *operand, T Type, context string) {
} }
// x.typ is typed // 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 // spec: "If a left-hand side is the blank identifier, any typed or
// non-constant value except for the predeclared identifier nil may // non-constant value except for the predeclared identifier nil may
// be assigned to it." // be assigned to it."
@ -148,6 +155,7 @@ func (check *Checker) initVar(lhs *Var, x *operand, context string) Type {
func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type { func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
if x.mode == invalid || x.typ == Typ[Invalid] { if x.mode == invalid || x.typ == Typ[Invalid] {
check.useLHS(lhs)
return nil return nil
} }
@ -221,25 +229,27 @@ func (check *Checker) assignVar(lhs ast.Expr, x *operand) Type {
// If returnPos is valid, initVars is called to type-check the assignment of // If returnPos is valid, initVars is called to type-check the assignment of
// return expressions, and returnPos is the position of the return statement. // return expressions, and returnPos is the position of the return statement.
func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos) { func (check *Checker) initVars(lhs []*Var, origRHS []ast.Expr, returnPos token.Pos) {
l := len(lhs) rhs, commaOk := check.exprList(origRHS, len(lhs) == 2 && !returnPos.IsValid())
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 { if len(lhs) != len(rhs) {
// invalidate lhs and use rhs // invalidate lhs
for _, obj := range lhs { for _, obj := range lhs {
if obj.typ == nil { if obj.typ == nil {
obj.typ = Typ[Invalid] obj.typ = Typ[Invalid]
} }
} }
if get == nil { // don't report an error if we already reported one
return // error reported by unpack for _, x := range rhs {
if x.mode == invalid {
return
}
} }
check.useGetter(get, r)
if returnPos.IsValid() { 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 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 return
} }
@ -248,50 +258,46 @@ func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos)
context = "return statement" context = "return statement"
} }
var x operand
if commaOk { if commaOk {
var a [2]Type var a [2]Type
for i := range a { for i := range a {
get(&x, i) a[i] = check.initVar(lhs[i], rhs[i], context)
a[i] = check.initVar(lhs[i], &x, context)
} }
check.recordCommaOkTypes(rhs[0], a) check.recordCommaOkTypes(origRHS[0], a)
return return
} }
for i, lhs := range lhs { for i, lhs := range lhs {
get(&x, i) check.initVar(lhs, rhs[i], context)
check.initVar(lhs, &x, context)
} }
} }
func (check *Checker) assignVars(lhs, rhs []ast.Expr) { func (check *Checker) assignVars(lhs, origRHS []ast.Expr) {
l := len(lhs) rhs, commaOk := check.exprList(origRHS, len(lhs) == 2)
get, r, commaOk := unpack(func(x *operand, i int) { check.multiExpr(x, rhs[i]) }, len(rhs), l == 2)
if get == nil { if len(lhs) != len(rhs) {
check.useLHS(lhs...) check.useLHS(lhs...)
return // error reported by unpack // don't report an error if we already reported one
} for _, x := range rhs {
if l != r { if x.mode == invalid {
check.useGetter(get, r) return
check.errorf(rhs[0], _WrongAssignCount, "cannot assign %d values to %d variables", r, l) }
}
check.errorf(rhs[0], _WrongAssignCount, "cannot assign %d values to %d variables", len(rhs), len(lhs))
return return
} }
var x operand
if commaOk { if commaOk {
var a [2]Type var a [2]Type
for i := range a { for i := range a {
get(&x, i) a[i] = check.assignVar(lhs[i], rhs[i])
a[i] = check.assignVar(lhs[i], &x)
} }
check.recordCommaOkTypes(rhs[0], a) check.recordCommaOkTypes(origRHS[0], a)
return return
} }
for i, lhs := range lhs { for i, lhs := range lhs {
get(&x, i) check.assignVar(lhs, rhs[i])
check.assignVar(lhs, &x)
} }
} }

View file

@ -31,8 +31,8 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
// For len(x) and cap(x) we need to know if x contains any function calls or // 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 // receive operations. Save/restore current setting and set hasCallOrRecv to
// false for the evaluation of x so that we can check it afterwards. // 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 // Note: We must do this _before_ calling exprList because exprList evaluates
// first argument before we even call arg(x, 0)! // all arguments.
if id == _Len || id == _Cap { if id == _Len || id == _Cap {
defer func(b bool) { defer func(b bool) {
check.hasCallOrRecv = b check.hasCallOrRecv = b
@ -41,15 +41,14 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
} }
// determine actual arguments // 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) nargs := len(call.Args)
switch id { switch id {
default: default:
// make argument getter // make argument getter
arg, nargs, _ = unpack(func(x *operand, i int) { check.multiExpr(x, call.Args[i]) }, nargs, false) xlist, _ := check.exprList(call.Args, false)
if arg == nil { arg = func(x *operand, i int) { *x = *xlist[i]; x.typ = expand(x.typ) }
return nargs = len(xlist)
}
// evaluate first argument, if present // evaluate first argument, if present
if nargs > 0 { if nargs > 0 {
arg(x, 0) arg(x, 0)
@ -84,7 +83,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
// of S and the respective parameter passing rules apply." // of S and the respective parameter passing rules apply."
S := x.typ S := x.typ
var T Type var T Type
if s, _ := S.Underlying().(*Slice); s != nil { if s := asSlice(S); s != nil {
T = s.elem T = s.elem
} else { } else {
check.invalidArg(x, _InvalidAppend, "%s is not a slice", x) check.invalidArg(x, _InvalidAppend, "%s is not a slice", x)
@ -121,14 +120,17 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
// check general case by creating custom signature // check general case by creating custom signature
sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature sig := makeSig(S, S, NewSlice(T)) // []T required for variadic signature
sig.variadic = true sig.variadic = true
check.arguments(x, call, sig, func(x *operand, i int) { var xlist []*operand
// only evaluate arguments that have not been evaluated before // convert []operand to []*operand
if i < len(alist) { for i := range alist {
*x = alist[i] xlist = append(xlist, &alist[i])
return }
} for i := len(alist); i < nargs; i++ {
arg(x, i) var x operand
}, nargs) 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 // ok to continue even if check.arguments reported errors
x.mode = value x.mode = value
@ -143,7 +145,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
mode := invalid mode := invalid
var typ Type var typ Type
var val constant.Value var val constant.Value
switch typ = implicitArrayDeref(x.typ.Underlying()); t := typ.(type) { switch typ = implicitArrayDeref(optype(x.typ)); t := typ.(type) {
case *Basic: case *Basic:
if isString(t) && id == _Len { if isString(t) && id == _Len {
if x.mode == constant_ { if x.mode == constant_ {
@ -176,6 +178,25 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
if id == _Len { if id == _Len {
mode = value 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] { if mode == invalid && typ != Typ[Invalid] {
@ -196,7 +217,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case _Close: case _Close:
// close(c) // close(c)
c, _ := x.typ.Underlying().(*Chan) c := asChan(x.typ)
if c == nil { if c == nil {
check.invalidArg(x, _InvalidClose, "%s is not a channel", x) check.invalidArg(x, _InvalidClose, "%s is not a channel", x)
return return
@ -271,7 +292,21 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
} }
// the argument types must be of floating-point type // 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) check.invalidArg(x, _InvalidComplex, "arguments have type %s, expected floating-point", x.typ)
return return
} }
@ -283,20 +318,6 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
x.mode = value 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_ { if check.Types != nil && x.mode != constant_ {
check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ, x.typ)) check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ, x.typ))
} }
@ -306,7 +327,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case _Copy: case _Copy:
// copy(x, y []T) int // copy(x, y []T) int
var dst Type var dst Type
if t, _ := x.typ.Underlying().(*Slice); t != nil { if t := asSlice(x.typ); t != nil {
dst = t.elem dst = t.elem
} }
@ -316,7 +337,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
return return
} }
var src Type var src Type
switch t := y.typ.Underlying().(type) { switch t := optype(y.typ).(type) {
case *Basic: case *Basic:
if isString(y.typ) { if isString(y.typ) {
src = universeByte src = universeByte
@ -343,7 +364,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case _Delete: case _Delete:
// delete(m, k) // delete(m, k)
m, _ := x.typ.Underlying().(*Map) m := asMap(x.typ)
if m == nil { if m == nil {
check.invalidArg(x, _InvalidDelete, "%s is not a map", x) check.invalidArg(x, _InvalidDelete, "%s is not a map", x)
return return
@ -389,7 +410,21 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
} }
// the argument must be of complex type // 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 code := _InvalidImag
if id == _Real { if id == _Real {
code = _InvalidReal code = _InvalidReal
@ -409,20 +444,6 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
x.mode = value 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_ { if check.Types != nil && x.mode != constant_ {
check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ)) check.recordBuiltinType(call.Fun, makeSig(resTyp, x.typ))
} }
@ -434,25 +455,47 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
// make(T, n, m) // make(T, n, m)
// (no argument evaluated yet) // (no argument evaluated yet)
arg0 := call.Args[0] arg0 := call.Args[0]
T := check.typ(arg0) T := check.varType(arg0)
if T == Typ[Invalid] { if T == Typ[Invalid] {
return return
} }
var min int // minimum number of arguments min, max := -1, 10
switch T.Underlying().(type) { var valid func(t Type) bool
case *Slice: valid = func(t Type) bool {
min = 2 var m int
case *Map, *Chan: switch t := optype(t).(type) {
min = 1 case *Slice:
default: 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) check.invalidArg(arg0, _InvalidMake, "cannot make %s; type must be slice, map, or channel", arg0)
return return
} }
if nargs < min || min+1 < nargs { if nargs < min || max < nargs {
check.errorf(call, _WrongArgCount, "%v expects %d or %d arguments; found %d", call, min, min+1, 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 return
} }
types := []Type{T} types := []Type{T}
var sizes []int64 // constant integer arguments, if any var sizes []int64 // constant integer arguments, if any
for _, arg := range call.Args[1:] { for _, arg := range call.Args[1:] {
@ -475,7 +518,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case _New: case _New:
// new(T) // new(T)
// (no argument evaluated yet) // (no argument evaluated yet)
T := check.typ(call.Args[0]) T := check.varType(call.Args[0])
if T == Typ[Invalid] { if T == Typ[Invalid] {
return return
} }
@ -545,6 +588,10 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case _Alignof: case _Alignof:
// unsafe.Alignof(x T) uintptr // 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") check.assignment(x, nil, "argument to unsafe.Alignof")
if x.mode == invalid { if x.mode == invalid {
return return
@ -602,6 +649,10 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case _Sizeof: case _Sizeof:
// unsafe.Sizeof(x T) uintptr // 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") check.assignment(x, nil, "argument to unsafe.Sizeof")
if x.mode == invalid { if x.mode == invalid {
return return
@ -657,6 +708,40 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
return true 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. // makeSig makes a signature for the given argument and result types.
// Default types are used for untyped arguments, and res may be nil. // Default types are used for untyped arguments, and res may be nil.
func makeSig(res Type, args ...Type) *Signature { func makeSig(res Type, args ...Type) *Signature {
@ -678,7 +763,7 @@ func makeSig(res Type, args ...Type) *Signature {
// //
func implicitArrayDeref(typ Type) Type { func implicitArrayDeref(typ Type) Type {
if p, ok := typ.(*Pointer); ok { if p, ok := typ.(*Pointer); ok {
if a, ok := p.base.Underlying().(*Array); ok { if a := asArray(p.base); a != nil {
return a return a
} }
} }

View file

@ -1,3 +1,4 @@
// REVIEW INCOMPLETE
// Copyright 2013 The Go Authors. All rights reserved. // Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -13,45 +14,73 @@ import (
"unicode" "unicode"
) )
func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind { // TODO(rFindley) this has diverged a bit from types2. Bring it up to date.
check.exprOrType(x, e.Fun) // 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 { switch x.mode {
case invalid: case invalid:
check.use(e.Args...) check.use(call.Args...)
x.mode = invalid x.expr = orig
x.expr = e
return statement return statement
case typexpr: case typexpr:
// conversion // conversion or type instantiation
T := x.typ T := x.typ
x.mode = invalid 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: 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: case 1:
check.expr(x, e.Args[0]) check.expr(x, call.Args[0])
if x.mode != invalid { if x.mode != invalid {
if e.Ellipsis.IsValid() { if call.Ellipsis.IsValid() {
check.errorf(e.Args[0], _BadDotDotDotSyntax, "invalid use of ... in conversion to %s", T) check.errorf(call.Args[0], _BadDotDotDotSyntax, "invalid use of ... in conversion to %s", T)
break 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) check.conversion(x, T)
} }
default: default:
check.use(e.Args...) check.use(call.Args...)
check.errorf(e.Args[n-1], _WrongArgCount, "too many arguments in conversion to %s", T) check.errorf(call.Args[n-1], _WrongArgCount, "too many arguments in conversion to %s", T)
} }
x.expr = e x.expr = orig
return conversion return conversion
case builtin: case builtin:
id := x.id id := x.id
if !check.builtin(x, e, id) { if !check.builtin(x, call, id) {
x.mode = invalid x.mode = invalid
} }
x.expr = e x.expr = orig
// a non-constant result implies a function call // a non-constant result implies a function call
if x.mode != invalid && x.mode != constant_ { if x.mode != invalid && x.mode != constant_ {
check.hasCallOrRecv = true check.hasCallOrRecv = true
@ -62,20 +91,111 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
// function/method call // function/method call
cgocall := x.mode == cgofunc cgocall := x.mode == cgofunc
sig, _ := x.typ.Underlying().(*Signature) sig := asSignature(x.typ)
if sig == nil { if sig == nil {
check.invalidOp(x, _InvalidCall, "cannot call non-function %s", x) check.invalidOp(x, _InvalidCall, "cannot call non-function %s", x)
x.mode = invalid x.mode = invalid
x.expr = e x.expr = orig
return statement return statement
} }
arg, n, _ := unpack(func(x *operand, i int) { check.multiExpr(x, e.Args[i]) }, len(e.Args), false) // evaluate arguments
if arg != nil { args, ok := check.exprOrTypeList(call.Args)
check.arguments(x, e, sig, arg, n) if ok && call.Brackets && len(args) > 0 && args[0].mode != typexpr {
} else { check.errorf(args[0], _NotAType, "%s is not a type", args[0])
x.mode = invalid 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 // determine result
switch sig.results.Len() { switch sig.results.Len() {
@ -92,180 +212,253 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
x.mode = value x.mode = value
x.typ = sig.results x.typ = sig.results
} }
x.expr = call
x.expr = e
check.hasCallOrRecv = true 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 return statement
} }
} }
// useGetter is like use, but takes a getter instead of a list of expressions. // exprOrTypeList returns a list of operands and reports an error if the
// It should be called instead of use if a getter is present to avoid repeated // list contains a mix of values and types (ignoring invalid operands).
// evaluation of the first argument (since the getter was likely obtained via func (check *Checker) exprOrTypeList(elist []ast.Expr) (xlist []*operand, ok bool) {
// unpack, which may have evaluated the first argument already). ok = true
func (check *Checker) useGetter(get getter, n int) {
var x operand switch len(elist) {
for i := 0; i < n; i++ { case 0:
get(&x, i) // 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 func (check *Checker) exprList(elist []ast.Expr, allowCommaOk bool) (xlist []*operand, commaOk bool) {
// number of operands (context-specific, and maintained elsewhere). A getter switch len(elist) {
// type-checks the i'th operand; the details of the actual check are getter- case 0:
// specific. // nothing to do
type getter func(x *operand, i int)
// unpack takes a getter get and a number of operands n. If n == 1, unpack case 1:
// calls the incoming getter for the first operand. If that operand is // single (possibly comma-ok) value, or function returning multiple values
// invalid, unpack returns (nil, 0, false). Otherwise, if that operand is a e := elist[0]
// function call, or a comma-ok expression and allowCommaOk is set, the result var x operand
// is a new getter and operand count providing access to the function results, check.multiExpr(&x, e)
// or comma-ok values, respectively. The third result value reports if it if t, ok := x.typ.(*Tuple); ok && x.mode != invalid {
// is indeed the comma-ok case. In all other cases, the incoming getter and // multiple values
// operand count are returned unchanged, and the third result value is false. xlist = make([]*operand, t.Len())
// for i, v := range t.vars {
// In other words, if there's exactly one operand that - after type-checking xlist[i] = &operand{mode: value, expr: e, typ: v.typ}
// by calling get - stands for multiple operands, the resulting getter provides }
// access to those operands instead. break
// }
// 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
}
if t, ok := x0.typ.(*Tuple); ok { // exactly one (possibly invalid or comma-ok) value
// result of an n-valued function call xlist = []*operand{&x}
return func(x *operand, i int) { if allowCommaOk && (x.mode == mapindex || x.mode == commaok || x.mode == commaerr) {
x.mode = value x.mode = value
x.expr = x0.expr x2 := &operand{mode: value, expr: e, typ: Typ[UntypedBool]}
x.typ = t.At(i).typ if x.mode == commaerr {
}, t.Len(), false x2.typ = universeError
}
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
} }
return func(x *operand, i int) { xlist = append(xlist, x2)
x.mode = value commaOk = true
x.expr = x0.expr }
x.typ = a[i]
}, 2, true 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
} }
x0.mode = value
} }
// single value return
return func(x *operand, i int) {
if i != 0 {
unreachable()
}
*x = x0
}, 1, false
} }
// arguments checks argument passing for the call with the given signature. func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, args []*operand) (rsig *Signature) {
// The arg function provides the operand for the i'th argument. rsig = sig
func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, arg getter, n int) {
if call.Ellipsis.IsValid() { // TODO(gri) try to eliminate this extra verification loop
// last argument is of the form x... for _, a := range args {
if !sig.variadic { switch a.mode {
check.errorf(atPos(call.Ellipsis), _NonVariadicDotDotDot, "cannot use ... in call to non-variadic %s", call.Fun) case typexpr:
check.useGetter(arg, n) check.errorf(a, 0, "%s used as value", a)
return return
} case invalid:
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)
return return
} }
} }
// evaluate arguments // Function call argument/parameter count requirements
context := check.sprintf("argument to %s", call.Fun) //
for i := 0; i < n; i++ { // | standard call | dotdotdot call |
arg(x, i) // --------------+------------------+----------------+
if x.mode != invalid { // standard func | nargs == npars | invalid |
var ellipsis token.Pos // --------------+------------------+----------------+
if i == n-1 && call.Ellipsis.IsValid() { // variadic func | nargs >= npars-1 | nargs == npars |
ellipsis = call.Ellipsis // --------------+------------------+----------------+
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
}
} 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
} }
check.argument(sig, i, x, ellipsis, context)
} }
} 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 // check argument count
if sig.variadic { switch {
// a variadic function accepts an "empty" case nargs < npars:
// last argument: count one extra check.errorf(inNode(call, call.Rparen), _WrongArgCount, "not enough arguments in call to %s", call.Fun)
n++ return
} case nargs > npars:
if n < sig.params.Len() { check.errorf(args[npars], _WrongArgCount, "too many arguments in call to %s", call.Fun) // report at first extra argument
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 {
return return
} }
n := sig.params.Len() // infer type arguments and instantiate signature if necessary
if len(sig.tparams) > 0 {
// determine parameter type // TODO(gri) provide position information for targs so we can feed
var typ Type // it to the instantiate call for better error reporting
switch { targs, failed := check.infer(sig.tparams, sigParams, args)
case i < n: if targs == nil {
typ = sig.params.vars[i].typ return // error already reported
case sig.variadic: }
typ = sig.params.vars[n-1].typ if failed >= 0 {
if debug { // Some type arguments couldn't be inferred. Use
if _, ok := typ.(*Slice); !ok { // bounds type inference to try to make progress.
check.dump("%v: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), typ) // 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
} }
} }
default: // all type arguments were inferred sucessfully
check.errorf(x, _WrongArgCount, "too many arguments") if debug {
return for _, targ := range targs {
assert(targ != nil)
}
}
// 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
}
} }
if ellipsis.IsValid() { // check arguments
if i != n-1 { // TODO(gri) Possible optimization (may be tricky): We could avoid
check.errorf(atPos(ellipsis), _MisplacedDotDotDot, "can only use ... with matching parameter") // checking arguments from which we inferred type arguments.
return for i, a := range args {
} check.assignment(a, sigParams.vars[i].typ, check.sprintf("argument to %s", call.Fun))
// 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
}
} else if sig.variadic && i >= n-1 {
// use the variadic parameter slice's element type
typ = typ.(*Slice).elem
} }
check.assignment(x, typ, context) return
} }
var cgoPrefixes = [...]string{ var cgoPrefixes = [...]string{
@ -368,7 +561,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
x.typ = exp.typ x.typ = exp.typ
x.id = exp.id x.id = exp.id
default: default:
check.dump("unexpected object %v", exp) check.dump("%v: unexpected object %v", e.Sel.Pos(), exp)
unreachable() unreachable()
} }
x.expr = e x.expr = e
@ -381,6 +574,8 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
goto Error goto Error
} }
check.instantiatedOperand(x)
obj, index, indirect = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel) obj, index, indirect = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
if obj == nil { if obj == nil {
switch { switch {
@ -390,8 +585,20 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
case indirect: case indirect:
check.errorf(e.Sel, _InvalidMethodExpr, "cannot call pointer method %s on %s", sel, x.typ) check.errorf(e.Sel, _InvalidMethodExpr, "cannot call pointer method %s on %s", sel, x.typ)
default: default:
// Check if capitalization of sel matters and provide better error var why string
// message in that case. 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 { if len(sel) > 0 {
var changeCase string var changeCase string
if r := rune(sel[0]); unicode.IsUpper(r) { if r := rune(sel[0]); unicode.IsUpper(r) {
@ -400,11 +607,11 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
changeCase = string(unicode.ToUpper(r)) + sel[1:] changeCase = string(unicode.ToUpper(r)) + sel[1:]
} }
if obj, _, _ = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, changeCase); obj != nil { 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) why += ", but does have " + changeCase
break
} }
} }
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 goto Error
} }
@ -412,6 +619,43 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
// methods may not have a fully set up signature yet // methods may not have a fully set up signature yet
if m, _ := obj.(*Func); m != nil { if m, _ := obj.(*Func); m != nil {
check.objDecl(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 = &copy
}
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 = &copy
}
// 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 { if x.mode == typexpr {
@ -434,7 +678,8 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
} }
x.mode = value x.mode = value
x.typ = &Signature{ 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, results: sig.results,
variadic: sig.variadic, variadic: sig.variadic,
} }
@ -458,7 +703,14 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
// addressability, should we report the type &(x.typ) instead? // addressability, should we report the type &(x.typ) instead?
check.recordSelection(e, MethodVal, x.typ, obj, index, indirect) 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. // Verify that LookupFieldOrMethod and MethodSet.Lookup agree.
// TODO(gri) This only works because we call LookupFieldOrMethod // TODO(gri) This only works because we call LookupFieldOrMethod
// _before_ calling NewMethodSet: LookupFieldOrMethod completes // _before_ calling NewMethodSet: LookupFieldOrMethod completes

View file

@ -20,7 +20,7 @@ func (check *Checker) conversion(x *operand, T Type) {
switch { switch {
case constArg && isConstType(T): case constArg && isConstType(T):
// constant conversion // constant conversion
switch t := T.Underlying().(*Basic); { switch t := asBasic(T); {
case representableConst(x.val, check, t, &x.val): case representableConst(x.val, check, t, &x.val):
ok = true ok = true
case isInteger(x.typ) && isString(t): case isInteger(x.typ) && isString(t):
@ -87,8 +87,8 @@ func (x *operand) convertibleTo(check *Checker, T Type) bool {
// "x's type and T have identical underlying types if tags are ignored" // "x's type and T have identical underlying types if tags are ignored"
V := x.typ V := x.typ
Vu := V.Underlying() Vu := under(V)
Tu := T.Underlying() Tu := under(T)
if check.identicalIgnoreTags(Vu, Tu) { if check.identicalIgnoreTags(Vu, Tu) {
return true return true
} }
@ -97,14 +97,14 @@ func (x *operand) convertibleTo(check *Checker, T Type) bool {
// have identical underlying types if tags are ignored" // have identical underlying types if tags are ignored"
if V, ok := V.(*Pointer); ok { if V, ok := V.(*Pointer); ok {
if T, ok := T.(*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 return true
} }
} }
} }
// "x's type and T are both integer or floating point types" // "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 return true
} }
@ -137,27 +137,27 @@ func (x *operand) convertibleTo(check *Checker, T Type) bool {
} }
func isUintptr(typ Type) bool { func isUintptr(typ Type) bool {
t, ok := typ.Underlying().(*Basic) t := asBasic(typ)
return ok && t.kind == Uintptr return t != nil && t.kind == Uintptr
} }
func isUnsafePointer(typ Type) bool { 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 // The spec does not say so, but gc claims it is. See also
// issue 6326. // issue 6326.
t, ok := typ.Underlying().(*Basic) t := asBasic(typ)
return ok && t.kind == UnsafePointer return t != nil && t.kind == UnsafePointer
} }
func isPointer(typ Type) bool { func isPointer(typ Type) bool {
_, ok := typ.Underlying().(*Pointer) return asPointer(typ) != nil
return ok
} }
func isBytesOrRunes(typ Type) bool { func isBytesOrRunes(typ Type) bool {
if s, ok := typ.(*Slice); ok { if s := asSlice(typ); s != nil {
t, ok := s.elem.Underlying().(*Basic) t := asBasic(s.elem)
return ok && (t.kind == Byte || t.kind == Rune) return t != nil && (t.kind == Byte || t.kind == Rune)
} }
return false return false
} }

View file

@ -753,52 +753,12 @@ const (
_NonVariadicDotDotDot _NonVariadicDotDotDot
// _MisplacedDotDotDot occurs when a "..." is used somewhere other than the // _MisplacedDotDotDot occurs when a "..." is used somewhere other than the
// final argument to a function call. // final argument in a function declaration.
// //
// Example: // Example:
// func printArgs(args ...int) { // func f(...int, int)
// for _, a := range args {
// println(a)
// }
// }
//
// func f() {
// a := []int{1,2,3}
// printArgs(0, a...)
// }
_MisplacedDotDotDot _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 // _InvalidDotDotDot occurs when a "..." is used in a non-variadic built-in
// function. // function.
// //

View file

@ -155,9 +155,9 @@ func TestEvalPos(t *testing.T) {
import "io" import "io"
type R = io.Reader type R = io.Reader
func _() { 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() { _ = 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 io interface {} // must not shadow io in line above
} }
type R interface {} // must not shadow R in first line of this function body type R interface {} // must not shadow R in first line of this function body

View file

@ -1,3 +1,4 @@
// REVIEW INCOMPLETE
// Copyright 2012 The Go Authors. All rights reserved. // Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
@ -99,8 +100,8 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr, op token.Token) {
return return
case token.ARROW: case token.ARROW:
typ, ok := x.typ.Underlying().(*Chan) typ := asChan(x.typ)
if !ok { if typ == nil {
check.invalidOp(x, _InvalidReceive, "cannot receive from non-channel %s", x) check.invalidOp(x, _InvalidReceive, "cannot receive from non-channel %s", x)
x.mode = invalid x.mode = invalid
return return
@ -122,7 +123,7 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr, op token.Token) {
} }
if x.mode == constant_ { if x.mode == constant_ {
typ := x.typ.Underlying().(*Basic) typ := asBasic(x.typ)
var prec uint var prec uint
if isUnsigned(typ) { if isUnsigned(typ) {
prec = uint(check.conf.sizeof(typ) * 8) prec = uint(check.conf.sizeof(typ) * 8)
@ -461,7 +462,7 @@ func (check *Checker) updateExprType(x ast.Expr, typ Type, final bool) {
// If the new type is not final and still untyped, just // If the new type is not final and still untyped, just
// update the recorded type. // update the recorded type.
if !final && isUntyped(typ) { if !final && isUntyped(typ) {
old.typ = typ.Underlying().(*Basic) old.typ = asBasic(typ)
check.untyped[x] = old check.untyped[x] = old
return return
} }
@ -512,6 +513,7 @@ func (check *Checker) convertUntyped(x *operand, target Type) {
} }
func (check *Checker) canConvertUntyped(x *operand, target Type) error { func (check *Checker) canConvertUntyped(x *operand, target Type) error {
target = expand(target)
if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] { if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] {
return nil return nil
} }
@ -744,7 +746,7 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
if e != nil { if e != nil {
x.expr = e // for better error message x.expr = e // for better error message
} }
check.representable(x, x.typ.Underlying().(*Basic)) check.representable(x, asBasic(x.typ))
} }
return return
} }
@ -889,7 +891,7 @@ func (check *Checker) binary(x *operand, e *ast.BinaryExpr, lhs, rhs ast.Expr, o
if x.mode == constant_ && y.mode == constant_ { if x.mode == constant_ && y.mode == constant_ {
xval := x.val xval := x.val
yval := y.val yval := y.val
typ := x.typ.Underlying().(*Basic) typ := asBasic(x.typ)
// force integer division of integer operands // force integer division of integer operands
if op == token.QUO && isInteger(typ) { if op == token.QUO && isInteger(typ) {
op = token.QUO_ASSIGN op = token.QUO_ASSIGN
@ -1028,7 +1030,7 @@ const (
// //
func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind { func (check *Checker) rawExpr(x *operand, e ast.Expr, hint Type) exprKind {
if trace { if trace {
check.trace(e.Pos(), "%s", e) check.trace(e.Pos(), "expr %s", e)
check.indent++ check.indent++
defer func() { defer func() {
check.indent-- check.indent--
@ -1133,7 +1135,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
// We have an "open" [...]T array type. // We have an "open" [...]T array type.
// Create a new ArrayType with unknown length (-1) // Create a new ArrayType with unknown length (-1)
// and finish setting it up after analyzing the literal. // 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 base = typ
break break
} }
@ -1144,7 +1146,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
case hint != nil: case hint != nil:
// no composite literal type present - use hint (element type of enclosing type) // no composite literal type present - use hint (element type of enclosing type)
typ = hint typ = hint
base, _ = deref(typ.Underlying()) // *T implies &T{} base, _ = deref(under(typ)) // *T implies &T{}
default: default:
// TODO(gri) provide better error messages depending on context // TODO(gri) provide better error messages depending on context
@ -1152,7 +1154,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
goto Error goto Error
} }
switch utyp := base.Underlying().(type) { switch utyp := optype(base).(type) {
case *Struct: case *Struct:
if len(e.Elts) == 0 { if len(e.Elts) == 0 {
break break
@ -1280,7 +1282,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
duplicate := false duplicate := false
// if the key is of interface type, the type is also significant when checking for duplicates // if the key is of interface type, the type is also significant when checking for duplicates
xkey := keyVal(x.val) xkey := keyVal(x.val)
if _, ok := utyp.key.Underlying().(*Interface); ok { if asInterface(utyp.key) != nil {
for _, vtyp := range visited[xkey] { for _, vtyp := range visited[xkey] {
if check.identical(vtyp, x.typ) { if check.identical(vtyp, x.typ) {
duplicate = true duplicate = true
@ -1332,15 +1334,31 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
check.selector(x, e) check.selector(x, e)
case *ast.IndexExpr: case *ast.IndexExpr:
check.expr(x, e.X) check.exprOrType(x, e.X)
if x.mode == invalid { if x.mode == invalid {
check.use(e.Index) check.use(e.Index)
goto Error 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 valid := false
length := int64(-1) // valid if >= 0 length := int64(-1) // valid if >= 0
switch typ := x.typ.Underlying().(type) { switch typ := optype(x.typ).(type) {
case *Basic: case *Basic:
if isString(typ) { if isString(typ) {
valid = true valid = true
@ -1363,7 +1381,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
x.typ = typ.elem x.typ = typ.elem
case *Pointer: case *Pointer:
if typ, _ := typ.base.Underlying().(*Array); typ != nil { if typ := asArray(typ.base); typ != nil {
valid = true valid = true
length = typ.len length = typ.len
x.mode = variable x.mode = variable
@ -1384,6 +1402,82 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
x.typ = typ.elem x.typ = typ.elem
x.expr = e x.expr = e
return expression 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 { if !valid {
@ -1396,6 +1490,13 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
goto Error 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) check.index(e.Index, length)
// ok to continue // ok to continue
@ -1408,7 +1509,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
valid := false valid := false
length := int64(-1) // valid if >= 0 length := int64(-1) // valid if >= 0
switch typ := x.typ.Underlying().(type) { switch typ := optype(x.typ).(type) {
case *Basic: case *Basic:
if isString(typ) { if isString(typ) {
if e.Slice3 { if e.Slice3 {
@ -1436,7 +1537,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
x.typ = &Slice{elem: typ.elem} x.typ = &Slice{elem: typ.elem}
case *Pointer: case *Pointer:
if typ, _ := typ.base.Underlying().(*Array); typ != nil { if typ := asArray(typ.base); typ != nil {
valid = true valid = true
length = typ.len length = typ.len
x.typ = &Slice{elem: typ.elem} x.typ = &Slice{elem: typ.elem}
@ -1445,6 +1546,10 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
case *Slice: case *Slice:
valid = true valid = true
// x.typ doesn't change // x.typ doesn't change
case *Sum, *TypeParam:
check.errorf(x, 0, "generic slice expressions not yet implemented")
goto Error
} }
if !valid { if !valid {
@ -1505,11 +1610,12 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
if x.mode == invalid { if x.mode == invalid {
goto Error goto Error
} }
xtyp, _ := x.typ.Underlying().(*Interface) xtyp, _ := under(x.typ).(*Interface)
if xtyp == nil { if xtyp == nil {
check.invalidOp(x, _InvalidAssert, "%s is not an interface", x) check.invalidOp(x, _InvalidAssert, "%s is not an interface", x)
goto Error goto Error
} }
check.ordinaryType(x, xtyp)
// x.(type) expressions are handled explicitly in type switches // x.(type) expressions are handled explicitly in type switches
if e.Type == nil { if e.Type == nil {
// Don't use invalidAST because this can occur in the AST produced by // Don't use invalidAST because this can occur in the AST produced by
@ -1517,7 +1623,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
check.error(e, _BadTypeKeyword, "use of .(type) outside type switch") check.error(e, _BadTypeKeyword, "use of .(type) outside type switch")
goto Error goto Error
} }
T := check.typ(e.Type) T := check.varType(e.Type)
if T == Typ[Invalid] { if T == Typ[Invalid] {
goto Error goto Error
} }
@ -1526,7 +1632,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
x.typ = T x.typ = T
case *ast.CallExpr: case *ast.CallExpr:
return check.call(x, e) return check.call(x, e, e)
case *ast.StarExpr: case *ast.StarExpr:
check.exprOrType(x, e.X) check.exprOrType(x, e.X)
@ -1536,7 +1642,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
case typexpr: case typexpr:
x.typ = &Pointer{base: x.typ} x.typ = &Pointer{base: x.typ}
default: default:
if typ, ok := x.typ.Underlying().(*Pointer); ok { if typ := asPointer(x.typ); typ != nil {
x.mode = variable x.mode = variable
x.typ = typ.base x.typ = typ.base
} else { } else {
@ -1637,6 +1743,77 @@ func (check *Checker) typeAssertion(at positioner, x *operand, xtyp *Interface,
check.errorf(at, _ImpossibleAssert, "%s cannot have dynamic type %s (%s)", x, T, msg) check.errorf(at, _ImpossibleAssert, "%s cannot have dynamic type %s (%s)", x, T, msg)
} }
// 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.rawExpr(x, e, nil)
check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
check.singleValue(x)
}
// 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)
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;
// hint is the type of a composite literal element.
// If an error occurred, x.mode is set to invalid.
//
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)
}
// exprOrType typechecks expression or type e and initializes x with the expression value or type.
// If an error occurred, x.mode is set to invalid.
//
func (check *Checker) exprOrType(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
check.exclude(x, 1<<novalue)
check.singleValue(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) { func (check *Checker) singleValue(x *operand) {
if x.mode == value { if x.mode == value {
// tuple types are never named - no need for underlying type below // tuple types are never named - no need for underlying type below
@ -1647,73 +1824,3 @@ func (check *Checker) singleValue(x *operand) {
} }
} }
} }
// 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.singleValue(x)
}
// multiExpr is like expr but the result may 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
}
// exprWithHint typechecks expression e and initializes x with the expression value;
// hint is the type of a composite literal element.
// If an error occurred, x.mode is set to invalid.
//
func (check *Checker) exprWithHint(x *operand, e ast.Expr, hint Type) {
assert(hint != nil)
check.rawExpr(x, e, hint)
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.
// If an error occurred, x.mode is set to invalid.
//
func (check *Checker) exprOrType(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
check.singleValue(x)
if x.mode == novalue {
check.errorf(x, _NotAnExpr, "%s used as value or type", x)
x.mode = invalid
}
}

View file

@ -25,11 +25,11 @@ func append1() {
_ = append(s, b) _ = append(s, b)
_ = append(s, x /* ERROR cannot use x */ ) _ = append(s, x /* ERROR cannot use x */ )
_ = append(s, s /* ERROR cannot use s */ ) _ = append(s, s /* ERROR cannot use s */ )
_ = append(s... /* ERROR can only use ... with matching parameter */ ) _ = append(s...) /* ERROR not enough arguments */
_ = append(s, b, s... /* ERROR can only use ... with matching parameter */ ) _ = append(s, b, s /* ERROR too many arguments */ ...)
_ = append(s, 1, 2, 3) _ = append(s, 1, 2, 3)
_ = append(s, 1, 2, 3, x /* ERROR cannot use x */ , 5, 6, 6) _ = 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) _ = append([]interface{}(nil), 1, 2, "foo", x, 3.1425, false)
type S []byte type S []byte

View file

@ -492,26 +492,26 @@ func _calls() {
f1(0) f1(0)
f1(x) f1(x)
f1(10.0) f1(10.0)
f1() /* ERROR "too few arguments" */ f1() /* ERROR "not enough arguments" */
f1(x, y /* ERROR "too many arguments" */ ) f1(x, y /* ERROR "too many arguments" */ )
f1(s /* ERROR "cannot use .* in argument" */ ) f1(s /* ERROR "cannot use .* in argument" */ )
f1(x ... /* ERROR "cannot use ..." */ ) f1(x ... /* ERROR "cannot use ..." */ )
f1(g0 /* ERROR "used as value" */ ()) f1(g0 /* ERROR "used as value" */ ())
f1(g1()) f1(g1())
f1(g2 /* ERROR "cannot use g2" */ /* ERROR "too many arguments" */ ()) f1(g2 /* ERROR "too many arguments" */ ())
f2() /* ERROR "too few arguments" */ f2() /* ERROR "not enough arguments" */
f2(3.14) /* ERROR "too few arguments" */ f2(3.14) /* ERROR "not enough arguments" */
f2(3.14, "foo") f2(3.14, "foo")
f2(x /* ERROR "cannot use .* in argument" */ , "foo") f2(x /* ERROR "cannot use .* in argument" */ , "foo")
f2(g0 /* ERROR "used as value" */ ()) 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()) f2(g2())
fs() /* ERROR "too few arguments" */ fs() /* ERROR "not enough arguments" */
fs(g0 /* ERROR "used as value" */ ()) fs(g0 /* ERROR "used as value" */ ())
fs(g1 /* ERROR "cannot use .* in argument" */ ()) 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()) fs(gs())
fv() fv()
@ -519,7 +519,7 @@ func _calls() {
fv(s /* ERROR "cannot use .* in argument" */ ) fv(s /* ERROR "cannot use .* in argument" */ )
fv(s...) fv(s...)
fv(x /* ERROR "cannot use" */ ...) 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" */ ())
fv(gs /* ERROR "cannot use .* in argument" */ ()...) fv(gs /* ERROR "cannot use .* in argument" */ ()...)
@ -528,7 +528,7 @@ func _calls() {
t.fm(1, 2.0, x) t.fm(1, 2.0, x)
t.fm(s /* ERROR "cannot use .* in argument" */ ) t.fm(s /* ERROR "cannot use .* in argument" */ )
t.fm(g1()) 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(gs /* ERROR "cannot use .* in argument" */ ()...) t.fm(gs /* ERROR "cannot use .* in argument" */ ()...)
@ -536,7 +536,7 @@ func _calls() {
T.fm(t, 1, 2.0, x) T.fm(t, 1, 2.0, x)
T.fm(t, s /* ERROR "cannot use .* in argument" */ ) T.fm(t, s /* ERROR "cannot use .* in argument" */ )
T.fm(t, g1()) 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" */ ())
T.fm(t, gs /* ERROR "cannot use .* in argument" */ ()...) T.fm(t, gs /* ERROR "cannot use .* in argument" */ ()...)
@ -545,7 +545,7 @@ func _calls() {
i.fm(1, 2.0, x) i.fm(1, 2.0, x)
i.fm(s /* ERROR "cannot use .* in argument" */ ) i.fm(s /* ERROR "cannot use .* in argument" */ )
i.fm(g1()) 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" */ ())
i.fm(gs /* ERROR "cannot use .* in argument" */ ()...) i.fm(gs /* ERROR "cannot use .* in argument" */ ()...)

View file

@ -86,7 +86,7 @@ func assignments1() {
g := func(int, bool){} g := func(int, bool){}
var m map[int]int var m map[int]int
g(m[0]) /* ERROR "too few arguments" */ g(m[0]) /* ERROR "not enough arguments" */
// assignments to _ // assignments to _
_ = nil /* ERROR "use of untyped nil" */ _ = nil /* ERROR "use of untyped nil" */