[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:
// 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])
}
}

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
// 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
}
}

View file

@ -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 = &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 {
@ -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

View file

@ -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
}

View file

@ -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.
//

View file

@ -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

View file

@ -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
}
}

View file

@ -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

View file

@ -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" */ ()...)

View file

@ -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" */