mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
[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:
parent
822aeacd9e
commit
81cd99858d
10 changed files with 837 additions and 427 deletions
|
|
@ -26,7 +26,9 @@ func (check *Checker) assignment(x *operand, T Type, context string) {
|
|||
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) {
|
||||
|
|
@ -66,6 +68,11 @@ func (check *Checker) assignment(x *operand, T Type, context string) {
|
|||
}
|
||||
// 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."
|
||||
|
|
@ -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 {
|
||||
if x.mode == invalid || x.typ == Typ[Invalid] {
|
||||
check.useLHS(lhs)
|
||||
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
|
||||
// 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
|
||||
}
|
||||
check.useGetter(get, r)
|
||||
if returnPos.IsValid() {
|
||||
check.errorf(atPos(returnPos), _WrongResultCount, "wrong number of return values (want %d, got %d)", l, r)
|
||||
// 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 initialize %d variables with %d values", l, r)
|
||||
}
|
||||
if returnPos.IsValid() {
|
||||
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", len(lhs), len(rhs))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -248,50 +258,46 @@ func (check *Checker) initVars(lhs []*Var, rhs []ast.Expr, returnPos token.Pos)
|
|||
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
|
||||
// don't report an error if we already reported one
|
||||
for _, x := range rhs {
|
||||
if x.mode == invalid {
|
||||
return
|
||||
}
|
||||
if l != r {
|
||||
check.useGetter(get, r)
|
||||
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
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
// 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
|
||||
|
|
@ -41,15 +41,14 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ 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)
|
||||
|
|
@ -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."
|
||||
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)
|
||||
|
|
@ -121,14 +120,17 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
// 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
|
||||
var xlist []*operand
|
||||
// convert []operand to []*operand
|
||||
for i := range alist {
|
||||
xlist = append(xlist, &alist[i])
|
||||
}
|
||||
arg(x, i)
|
||||
}, nargs)
|
||||
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
|
||||
|
|
@ -143,7 +145,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
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_ {
|
||||
|
|
@ -176,6 +178,25 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
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] {
|
||||
|
|
@ -196,7 +217,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
|
||||
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
|
||||
|
|
@ -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
|
||||
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
|
||||
}
|
||||
|
|
@ -283,20 +318,6 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
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))
|
||||
}
|
||||
|
|
@ -306,7 +327,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
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
|
||||
}
|
||||
|
||||
|
|
@ -316,7 +337,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
return
|
||||
}
|
||||
var src Type
|
||||
switch t := y.typ.Underlying().(type) {
|
||||
switch t := optype(y.typ).(type) {
|
||||
case *Basic:
|
||||
if isString(y.typ) {
|
||||
src = universeByte
|
||||
|
|
@ -343,7 +364,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
|
||||
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
|
||||
|
|
@ -389,7 +410,21 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
}
|
||||
|
||||
// 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
|
||||
|
|
@ -409,20 +444,6 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
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))
|
||||
}
|
||||
|
|
@ -434,25 +455,47 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
// 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) {
|
||||
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:
|
||||
min = 2
|
||||
m = 2
|
||||
case *Map, *Chan:
|
||||
min = 1
|
||||
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:] {
|
||||
|
|
@ -475,7 +518,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
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
|
||||
}
|
||||
|
|
@ -545,6 +588,10 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
|
||||
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
|
||||
|
|
@ -602,6 +649,10 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
|
||||
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
|
||||
|
|
@ -657,6 +708,40 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
|
|||
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 {
|
||||
|
|
@ -678,7 +763,7 @@ 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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// 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.
|
||||
|
|
@ -13,45 +14,73 @@ import (
|
|||
"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
|
||||
|
|
@ -62,20 +91,111 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
|
|||
// 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 {
|
||||
x.mode = invalid
|
||||
// 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() {
|
||||
|
|
@ -92,180 +212,253 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind {
|
|||
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) {
|
||||
// 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
|
||||
for i := 0; i < n; i++ {
|
||||
get(&x, i)
|
||||
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)
|
||||
func (check *Checker) exprList(elist []ast.Expr, allowCommaOk bool) (xlist []*operand, commaOk bool) {
|
||||
switch len(elist) {
|
||||
case 0:
|
||||
// nothing to do
|
||||
|
||||
// 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
|
||||
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}
|
||||
}
|
||||
// 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
|
||||
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
|
||||
x2 := &operand{mode: value, expr: e, typ: Typ[UntypedBool]}
|
||||
if x.mode == commaerr {
|
||||
x2.typ = universeError
|
||||
}
|
||||
xlist = append(xlist, x2)
|
||||
commaOk = true
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
return func(x *operand, i int) {
|
||||
x.mode = value
|
||||
x.expr = x0.expr
|
||||
x.typ = a[i]
|
||||
}, 2, true
|
||||
}
|
||||
x0.mode = value
|
||||
}
|
||||
|
||||
// single value
|
||||
return func(x *operand, i int) {
|
||||
if i != 0 {
|
||||
unreachable()
|
||||
}
|
||||
*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
|
||||
case invalid:
|
||||
return
|
||||
}
|
||||
if len(call.Args) == 1 && n > 1 {
|
||||
}
|
||||
|
||||
// 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(atPos(call.Ellipsis), _InvalidDotDotDotOperand, "cannot use ... with %d-valued %s", n, call.Args[0])
|
||||
check.useGetter(arg, n)
|
||||
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))
|
||||
}
|
||||
|
||||
// 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
|
||||
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
|
||||
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 {
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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
|
||||
}
|
||||
// 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)
|
||||
// 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{
|
||||
|
|
@ -368,7 +561,7 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
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
|
||||
|
|
@ -381,6 +574,8 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
goto Error
|
||||
}
|
||||
|
||||
check.instantiatedOperand(x)
|
||||
|
||||
obj, index, indirect = check.lookupFieldOrMethod(x.typ, x.mode == variable, check.pkg, sel)
|
||||
if obj == nil {
|
||||
switch {
|
||||
|
|
@ -390,8 +585,20 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
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) {
|
||||
|
|
@ -400,11 +607,11 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
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
|
||||
}
|
||||
|
|
@ -412,6 +619,43 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
// 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 {
|
||||
|
|
@ -434,7 +678,8 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
}
|
||||
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,
|
||||
}
|
||||
|
|
@ -458,7 +703,14 @@ func (check *Checker) selector(x *operand, e *ast.SelectorExpr) {
|
|||
// 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
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ func (check *Checker) conversion(x *operand, T Type) {
|
|||
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):
|
||||
|
|
@ -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"
|
||||
V := x.typ
|
||||
Vu := V.Underlying()
|
||||
Tu := T.Underlying()
|
||||
Vu := under(V)
|
||||
Tu := under(T)
|
||||
if check.identicalIgnoreTags(Vu, Tu) {
|
||||
return true
|
||||
}
|
||||
|
|
@ -97,14 +97,14 @@ func (x *operand) convertibleTo(check *Checker, T Type) bool {
|
|||
// 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
|
||||
}
|
||||
|
||||
|
|
@ -137,27 +137,27 @@ func (x *operand) convertibleTo(check *Checker, T Type) bool {
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -753,52 +753,12 @@ const (
|
|||
_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.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -155,9 +155,9 @@ func TestEvalPos(t *testing.T) {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// 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.
|
||||
|
|
@ -99,8 +100,8 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr, op token.Token) {
|
|||
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
|
||||
|
|
@ -122,7 +123,7 @@ func (check *Checker) unary(x *operand, e *ast.UnaryExpr, op token.Token) {
|
|||
}
|
||||
|
||||
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)
|
||||
|
|
@ -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
|
||||
// update the recorded type.
|
||||
if !final && isUntyped(typ) {
|
||||
old.typ = typ.Underlying().(*Basic)
|
||||
old.typ = asBasic(typ)
|
||||
check.untyped[x] = old
|
||||
return
|
||||
}
|
||||
|
|
@ -512,6 +513,7 @@ func (check *Checker) convertUntyped(x *operand, target Type) {
|
|||
}
|
||||
|
||||
func (check *Checker) canConvertUntyped(x *operand, target Type) error {
|
||||
target = expand(target)
|
||||
if x.mode == invalid || isTyped(x.typ) || target == Typ[Invalid] {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -744,7 +746,7 @@ func (check *Checker) shift(x, y *operand, e *ast.BinaryExpr, op token.Token) {
|
|||
if e != nil {
|
||||
x.expr = e // for better error message
|
||||
}
|
||||
check.representable(x, x.typ.Underlying().(*Basic))
|
||||
check.representable(x, asBasic(x.typ))
|
||||
}
|
||||
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_ {
|
||||
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
|
||||
|
|
@ -1028,7 +1030,7 @@ const (
|
|||
//
|
||||
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--
|
||||
|
|
@ -1133,7 +1135,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
// 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
|
||||
}
|
||||
|
|
@ -1144,7 +1146,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
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
|
||||
|
|
@ -1152,7 +1154,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
goto Error
|
||||
}
|
||||
|
||||
switch utyp := base.Underlying().(type) {
|
||||
switch utyp := optype(base).(type) {
|
||||
case *Struct:
|
||||
if len(e.Elts) == 0 {
|
||||
break
|
||||
|
|
@ -1280,7 +1282,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
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
|
||||
|
|
@ -1332,15 +1334,31 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
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
|
||||
|
|
@ -1363,7 +1381,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
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
|
||||
|
|
@ -1384,6 +1402,82 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
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 {
|
||||
|
|
@ -1396,6 +1490,13 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
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
|
||||
|
||||
|
|
@ -1408,7 +1509,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
|
||||
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 {
|
||||
|
|
@ -1436,7 +1537,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
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}
|
||||
|
|
@ -1445,6 +1546,10 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
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 {
|
||||
|
|
@ -1505,11 +1610,12 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
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
|
||||
|
|
@ -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")
|
||||
goto Error
|
||||
}
|
||||
T := check.typ(e.Type)
|
||||
T := check.varType(e.Type)
|
||||
if T == Typ[Invalid] {
|
||||
goto Error
|
||||
}
|
||||
|
|
@ -1526,7 +1632,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
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)
|
||||
|
|
@ -1536,7 +1642,7 @@ func (check *Checker) exprInternal(x *operand, e ast.Expr, hint Type) exprKind {
|
|||
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 {
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
if x.mode == value {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
6
src/go/types/testdata/builtins.src
vendored
6
src/go/types/testdata/builtins.src
vendored
|
|
@ -25,11 +25,11 @@ func append1() {
|
|||
_ = 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
|
||||
|
|
|
|||
22
src/go/types/testdata/expr3.src
vendored
22
src/go/types/testdata/expr3.src
vendored
|
|
@ -492,26 +492,26 @@ func _calls() {
|
|||
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()
|
||||
|
|
@ -519,7 +519,7 @@ func _calls() {
|
|||
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" */ ()...)
|
||||
|
||||
|
|
@ -528,7 +528,7 @@ func _calls() {
|
|||
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" */ ()...)
|
||||
|
||||
|
|
@ -536,7 +536,7 @@ func _calls() {
|
|||
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" */ ()...)
|
||||
|
||||
|
|
@ -545,7 +545,7 @@ func _calls() {
|
|||
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" */ ()...)
|
||||
|
||||
|
|
|
|||
2
src/go/types/testdata/stmt0.src
vendored
2
src/go/types/testdata/stmt0.src
vendored
|
|
@ -86,7 +86,7 @@ func assignments1() {
|
|||
|
||||
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" */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue