From 81cd99858d2ae99e7ac2785367ee5b97de9e6e25 Mon Sep 17 00:00:00 2001 From: Rob Findley Date: Wed, 6 Jan 2021 12:25:10 -0500 Subject: [PATCH] [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 TryBot-Result: Go Bot Reviewed-by: Robert Griesemer Trust: Robert Griesemer Trust: Robert Findley --- src/go/types/assignments.go | 70 ++-- src/go/types/builtins.go | 211 +++++++---- src/go/types/call.go | 586 +++++++++++++++++++++-------- src/go/types/conversions.go | 30 +- src/go/types/errorcodes.go | 44 +-- src/go/types/eval_test.go | 4 +- src/go/types/expr.go | 245 ++++++++---- src/go/types/testdata/builtins.src | 6 +- src/go/types/testdata/expr3.src | 22 +- src/go/types/testdata/stmt0.src | 2 +- 10 files changed, 815 insertions(+), 405 deletions(-) diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go index 616564b567..3374f81899 100644 --- a/src/go/types/assignments.go +++ b/src/go/types/assignments.go @@ -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 + // don't report an error if we already reported one + for _, x := range rhs { + if x.mode == invalid { + return + } } - check.useGetter(get, r) if returnPos.IsValid() { - check.errorf(atPos(returnPos), _WrongResultCount, "wrong number of return values (want %d, got %d)", l, r) + check.errorf(atPos(returnPos), _WrongResultCount, "wrong number of return values (want %d, got %d)", len(lhs), len(rhs)) return } - check.errorf(rhs[0], _WrongAssignCount, "cannot initialize %d variables with %d values", l, r) + check.errorf(rhs[0], _WrongAssignCount, "cannot initialize %d variables with %d values", len(lhs), len(rhs)) return } @@ -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 - } - if l != r { - check.useGetter(get, r) - check.errorf(rhs[0], _WrongAssignCount, "cannot assign %d values to %d variables", r, l) + // don't report an error if we already reported one + for _, x := range rhs { + if x.mode == invalid { + return + } + } + check.errorf(rhs[0], _WrongAssignCount, "cannot assign %d values to %d variables", len(rhs), len(lhs)) return } - var x operand if commaOk { var a [2]Type for i := range a { - get(&x, i) - a[i] = check.assignVar(lhs[i], &x) + a[i] = check.assignVar(lhs[i], rhs[i]) } - check.recordCommaOkTypes(rhs[0], a) + check.recordCommaOkTypes(origRHS[0], a) return } for i, lhs := range lhs { - get(&x, i) - check.assignVar(lhs, &x) + check.assignVar(lhs, rhs[i]) } } diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index fd35f78676..17916c6b0c 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -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 - } - arg(x, i) - }, nargs) + var xlist []*operand + // convert []operand to []*operand + for i := range alist { + xlist = append(xlist, &alist[i]) + } + for i := len(alist); i < nargs; i++ { + var x operand + arg(&x, i) + xlist = append(xlist, &x) + } + check.arguments(call, sig, xlist) // discard result (we know the result type) // ok to continue even if check.arguments reported errors x.mode = value @@ -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) { - case *Slice: - min = 2 - case *Map, *Chan: - min = 1 - default: + min, max := -1, 10 + var valid func(t Type) bool + valid = func(t Type) bool { + var m int + switch t := optype(t).(type) { + case *Slice: + m = 2 + case *Map, *Chan: + m = 1 + case *Sum: + return t.is(valid) + default: + return false + } + if m > min { + min = m + } + if m+1 < max { + max = m + 1 + } + return true + } + + if !valid(T) { check.invalidArg(arg0, _InvalidMake, "cannot make %s; type must be slice, map, or channel", arg0) return } - if nargs < min || min+1 < nargs { - check.errorf(call, _WrongArgCount, "%v expects %d or %d arguments; found %d", call, min, min+1, nargs) + if nargs < min || max < nargs { + if min == max { + check.errorf(call, _WrongArgCount, "%v expects %d arguments; found %d", call, min, nargs) + } else { + check.errorf(call, _WrongArgCount, "%v expects %d or %d arguments; found %d", call, min, max, nargs) + } return } + types := []Type{T} var sizes []int64 // constant integer arguments, if any for _, arg := range call.Args[1:] { @@ -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 */, "", 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 } } diff --git a/src/go/types/call.go b/src/go/types/call.go index 424ec902ff..e10e0a643d 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -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,21 +91,112 @@ 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 { + // evaluate arguments + args, ok := check.exprOrTypeList(call.Args) + if ok && call.Brackets && len(args) > 0 && args[0].mode != typexpr { + check.errorf(args[0], _NotAType, "%s is not a type", args[0]) + ok = false + } + if !ok { x.mode = invalid + x.expr = orig + return expression + } + + // instantiate function if needed + if n := len(args); n > 0 && len(sig.tparams) > 0 && args[0].mode == typexpr { + // If the first argument is a type, assume we have explicit type arguments. + + // check number of type arguments + // TODO(rFindley) + // if !check.conf.InferFromConstraints && n != len(sig.tparams) || n > len(sig.tparams) { + if n != len(sig.tparams) || n > len(sig.tparams) { + check.errorf(args[n-1], 0, "got %d type arguments but want %d", n, len(sig.tparams)) + x.mode = invalid + x.expr = orig + return expression + } + + // collect types + targs := make([]Type, n) + // TODO(rFindley) positioner? + poslist := make([]token.Pos, n) + for i, a := range args { + if a.mode != typexpr { + // error was reported earlier + x.mode = invalid + x.expr = orig + return expression + } + targs[i] = a.typ + poslist[i] = a.Pos() + } + + // if we don't have enough type arguments, use constraint type inference + var inferred bool + if n < len(sig.tparams) { + var failed int + targs, failed = check.inferB(sig.tparams, targs) + if targs == nil { + // error was already reported + x.mode = invalid + x.expr = orig + return expression + } + if failed >= 0 { + // at least one type argument couldn't be inferred + assert(targs[failed] == nil) + tpar := sig.tparams[failed] + ppos := check.fset.Position(tpar.pos).String() + check.errorf(inNode(call, call.Rparen), 0, "cannot infer %s (%s) (%s)", tpar.name, ppos, targs) + x.mode = invalid + x.expr = orig + return expression + } + // all type arguments were inferred sucessfully + if debug { + for _, targ := range targs { + assert(targ != nil) + } + } + n = len(targs) + inferred = true + } + assert(n == len(sig.tparams)) + + // instantiate function signature + for i, typ := range targs { + // some positions may be missing if types are inferred + var pos token.Pos + if i < len(poslist) { + pos = poslist[i] + } + check.ordinaryType(atPos(pos), typ) + } + res := check.instantiate(x.Pos(), sig, targs, poslist).(*Signature) + assert(res.tparams == nil) // signature is not generic anymore + if inferred { + check.recordInferred(orig, targs, res) + } + x.typ = res + x.mode = value + x.expr = orig + return expression } + // If we reach here, orig must have been a regular call, not an index expression. + assert(!call.Brackets) + + sig = check.arguments(call, sig, args) + // determine result switch sig.results.Len() { case 0: @@ -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) { - var x operand - for i := 0; i < n; i++ { - get(&x, i) +// exprOrTypeList returns a list of operands and reports an error if the +// list contains a mix of values and types (ignoring invalid operands). +func (check *Checker) exprOrTypeList(elist []ast.Expr) (xlist []*operand, ok bool) { + ok = true + + switch len(elist) { + case 0: + // nothing to do + + case 1: + // single (possibly comma-ok) value or type, or function returning multiple values + e := elist[0] + var x operand + check.multiExprOrType(&x, e) + if t, ok := x.typ.(*Tuple); ok && x.mode != invalid && x.mode != typexpr { + // multiple values + xlist = make([]*operand, t.Len()) + for i, v := range t.vars { + xlist[i] = &operand{mode: value, expr: e, typ: v.typ} + } + break + } + + check.instantiatedOperand(&x) + + // exactly one (possibly invalid or comma-ok) value or type + xlist = []*operand{&x} + + default: + // multiple (possibly invalid) values or types + xlist = make([]*operand, len(elist)) + ntypes := 0 + for i, e := range elist { + var x operand + check.exprOrType(&x, e) + xlist[i] = &x + switch x.mode { + case invalid: + ntypes = len(xlist) // make 'if' condition fail below (no additional error in this case) + case typexpr: + ntypes++ + check.instantiatedOperand(&x) + } + } + if 0 < ntypes && ntypes < len(xlist) { + check.errorf(xlist[0], 0, "mix of value and type expressions") + ok = false + } } + + return } -// A getter sets x as the i'th operand, where 0 <= i < n and n is the total -// number of operands (context-specific, and maintained elsewhere). A getter -// type-checks the i'th operand; the details of the actual check are getter- -// specific. -type getter func(x *operand, i int) - -// unpack takes a getter get and a number of operands n. If n == 1, unpack -// calls the incoming getter for the first operand. If that operand is -// invalid, unpack returns (nil, 0, false). Otherwise, if that operand is a -// function call, or a comma-ok expression and allowCommaOk is set, the result -// is a new getter and operand count providing access to the function results, -// or comma-ok values, respectively. The third result value reports if it -// is indeed the comma-ok case. In all other cases, the incoming getter and -// operand count are returned unchanged, and the third result value is false. -// -// In other words, if there's exactly one operand that - after type-checking -// by calling get - stands for multiple operands, the resulting getter provides -// access to those operands instead. -// -// If the returned getter is called at most once for a given operand index i -// (including i == 0), that operand is guaranteed to cause only one call of -// the incoming getter with that i. -// -func unpack(get getter, n int, allowCommaOk bool) (getter, int, bool) { - if n != 1 { - // zero or multiple values - return get, n, false - } - // possibly result of an n-valued function call or comma,ok value - var x0 operand - get(&x0, 0) - if x0.mode == invalid { - return nil, 0, false - } +func (check *Checker) exprList(elist []ast.Expr, allowCommaOk bool) (xlist []*operand, commaOk bool) { + switch len(elist) { + case 0: + // nothing to do + + case 1: + // single (possibly comma-ok) value, or function returning multiple values + e := elist[0] + var x operand + check.multiExpr(&x, e) + if t, ok := x.typ.(*Tuple); ok && x.mode != invalid { + // multiple values + xlist = make([]*operand, t.Len()) + for i, v := range t.vars { + xlist[i] = &operand{mode: value, expr: e, typ: v.typ} + } + break + } - if t, ok := x0.typ.(*Tuple); ok { - // result of an n-valued function call - return func(x *operand, i int) { + // exactly one (possibly invalid or comma-ok) value + xlist = []*operand{&x} + if allowCommaOk && (x.mode == mapindex || x.mode == commaok || x.mode == commaerr) { x.mode = value - x.expr = x0.expr - x.typ = t.At(i).typ - }, t.Len(), false - } - - if x0.mode == mapindex || x0.mode == commaok || x0.mode == commaerr { - // comma-ok value - if allowCommaOk { - a := [2]Type{x0.typ, Typ[UntypedBool]} - if x0.mode == commaerr { - a[1] = universeError + x2 := &operand{mode: value, expr: e, typ: Typ[UntypedBool]} + if x.mode == commaerr { + x2.typ = universeError } - return func(x *operand, i int) { - x.mode = value - x.expr = x0.expr - x.typ = a[i] - }, 2, true + xlist = append(xlist, x2) + commaOk = true } - x0.mode = value - } - // single value - return func(x *operand, i int) { - if i != 0 { - unreachable() + default: + // multiple (possibly invalid) values + xlist = make([]*operand, len(elist)) + for i, e := range elist { + var x operand + check.expr(&x, e) + xlist[i] = &x } - *x = x0 - }, 1, false + } + + return } -// arguments checks argument passing for the call with the given signature. -// The arg function provides the operand for the i'th argument. -func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, arg getter, n int) { - if call.Ellipsis.IsValid() { - // last argument is of the form x... - if !sig.variadic { - check.errorf(atPos(call.Ellipsis), _NonVariadicDotDotDot, "cannot use ... in call to non-variadic %s", call.Fun) - check.useGetter(arg, n) +func (check *Checker) arguments(call *ast.CallExpr, sig *Signature, args []*operand) (rsig *Signature) { + rsig = sig + + // TODO(gri) try to eliminate this extra verification loop + for _, a := range args { + switch a.mode { + case typexpr: + check.errorf(a, 0, "%s used as value", a) return - } - if len(call.Args) == 1 && n > 1 { - // f()... is not permitted if f() is multi-valued - check.errorf(atPos(call.Ellipsis), _InvalidDotDotDotOperand, "cannot use ... with %d-valued %s", n, call.Args[0]) - check.useGetter(arg, n) + case invalid: return } } - // evaluate arguments - context := check.sprintf("argument to %s", call.Fun) - for i := 0; i < n; i++ { - arg(x, i) - if x.mode != invalid { - var ellipsis token.Pos - if i == n-1 && call.Ellipsis.IsValid() { - ellipsis = call.Ellipsis + // Function call argument/parameter count requirements + // + // | standard call | dotdotdot call | + // --------------+------------------+----------------+ + // standard func | nargs == npars | invalid | + // --------------+------------------+----------------+ + // variadic func | nargs >= npars-1 | nargs == npars | + // --------------+------------------+----------------+ + + nargs := len(args) + npars := sig.params.Len() + ddd := call.Ellipsis.IsValid() + + // set up parameters + sigParams := sig.params // adjusted for variadic functions (may be nil for empty parameter lists!) + adjusted := false // indicates if sigParams is different from t.params + if sig.variadic { + if ddd { + // variadic_func(a, b, c...) + if len(call.Args) == 1 && nargs > 1 { + // f()... is not permitted if f() is multi-valued + check.errorf(inNode(call, call.Ellipsis), _InvalidDotDotDot, "cannot use ... with %d-valued %s", nargs, call.Args[0]) + return } - check.argument(sig, i, x, ellipsis, context) + } else { + // variadic_func(a, b, c) + if nargs >= npars-1 { + // Create custom parameters for arguments: keep + // the first npars-1 parameters and add one for + // each argument mapping to the ... parameter. + vars := make([]*Var, npars-1) // npars > 0 for variadic functions + copy(vars, sig.params.vars) + last := sig.params.vars[npars-1] + typ := last.typ.(*Slice).elem + for len(vars) < nargs { + vars = append(vars, NewParam(last.pos, last.pkg, last.name, typ)) + } + sigParams = NewTuple(vars...) // possibly nil! + adjusted = true + npars = nargs + } else { + // nargs < npars-1 + npars-- // for correct error message below + } + } + } else { + if ddd { + // standard_func(a, b, c...) + check.errorf(inNode(call, call.Ellipsis), _NonVariadicDotDotDot, "cannot use ... in call to non-variadic %s", call.Fun) + return } + // standard_func(a, b, c) } // check argument count - if sig.variadic { - // a variadic function accepts an "empty" - // last argument: count one extra - n++ - } - if n < sig.params.Len() { - check.errorf(inNode(call, call.Rparen), _WrongArgCount, "too few arguments in call to %s", call.Fun) - // ok to continue - } -} - -// argument checks passing of argument x to the i'th parameter of the given signature. -// If ellipsis is valid, the argument is followed by ... at that position in the call. -func (check *Checker) argument(sig *Signature, i int, x *operand, ellipsis token.Pos, context string) { - check.singleValue(x) - if x.mode == invalid { + switch { + case nargs < npars: + check.errorf(inNode(call, call.Rparen), _WrongArgCount, "not enough arguments in call to %s", call.Fun) + return + case nargs > npars: + check.errorf(args[npars], _WrongArgCount, "too many arguments in call to %s", call.Fun) // report at first extra argument return } - n := sig.params.Len() - - // determine parameter type - var typ Type - switch { - case i < n: - typ = sig.params.vars[i].typ - case sig.variadic: - typ = sig.params.vars[n-1].typ + // infer type arguments and instantiate signature if necessary + if len(sig.tparams) > 0 { + // TODO(gri) provide position information for targs so we can feed + // it to the instantiate call for better error reporting + targs, failed := check.infer(sig.tparams, sigParams, args) + if targs == nil { + return // error already reported + } + if failed >= 0 { + // Some type arguments couldn't be inferred. Use + // bounds type inference to try to make progress. + // TODO(rFindley) + /* + if check.conf.InferFromConstraints { + targs, failed = check.inferB(sig.tparams, targs) + if targs == nil { + return // error already reported + } + } + */ + if failed >= 0 { + // at least one type argument couldn't be inferred + assert(targs[failed] == nil) + tpar := sig.tparams[failed] + ppos := check.fset.Position(tpar.pos).String() + check.errorf(inNode(call, call.Rparen), 0, "cannot infer %s (%s) (%s)", tpar.name, ppos, targs) + return + } + } + // all type arguments were inferred sucessfully if debug { - if _, ok := typ.(*Slice); !ok { - check.dump("%v: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), typ) + for _, targ := range targs { + assert(targ != nil) } } - default: - check.errorf(x, _WrongArgCount, "too many arguments") - return - } - if ellipsis.IsValid() { - if i != n-1 { - check.errorf(atPos(ellipsis), _MisplacedDotDotDot, "can only use ... with matching parameter") - return - } - // argument is of the form x... and x is single-valued - if _, ok := x.typ.Underlying().(*Slice); !ok && x.typ != Typ[UntypedNil] { // see issue #18268 - check.errorf(x, _InvalidDotDotDotOperand, "cannot use %s as parameter of type %s", x, typ) - return + // compute result signature + rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature) + assert(rsig.tparams == nil) // signature is not generic anymore + check.recordInferred(call, targs, rsig) + + // Optimization: Only if the parameter list was adjusted do we + // need to compute it from the adjusted list; otherwise we can + // simply use the result signature's parameter list. + if adjusted { + sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.tparams, targs)).(*Tuple) + } else { + sigParams = rsig.params } - } else if sig.variadic && i >= n-1 { - // use the variadic parameter slice's element type - typ = typ.(*Slice).elem } - check.assignment(x, typ, context) + // check arguments + // TODO(gri) Possible optimization (may be tricky): We could avoid + // checking arguments from which we inferred type arguments. + for i, a := range args { + check.assignment(a, sigParams.vars[i].typ, check.sprintf("argument to %s", call.Fun)) + } + + return } var cgoPrefixes = [...]string{ @@ -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 diff --git a/src/go/types/conversions.go b/src/go/types/conversions.go index 1cab1cc70f..0756b575ae 100644 --- a/src/go/types/conversions.go +++ b/src/go/types/conversions.go @@ -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 } diff --git a/src/go/types/errorcodes.go b/src/go/types/errorcodes.go index c01a12c346..897b34d74f 100644 --- a/src/go/types/errorcodes.go +++ b/src/go/types/errorcodes.go @@ -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. // diff --git a/src/go/types/eval_test.go b/src/go/types/eval_test.go index d940bf0e80..33dfbefe19 100644 --- a/src/go/types/eval_test.go +++ b/src/go/types/eval_test.go @@ -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 diff --git a/src/go/types/expr.go b/src/go/types/expr.go index c57edf8f0d..dcccd87c89 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -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,46 +1743,26 @@ func (check *Checker) typeAssertion(at positioner, x *operand, xtyp *Interface, check.errorf(at, _ImpossibleAssert, "%s cannot have dynamic type %s (%s)", x, T, msg) } -func (check *Checker) singleValue(x *operand) { - if x.mode == value { - // tuple types are never named - no need for underlying type below - if t, ok := x.typ.(*Tuple); ok { - assert(t.Len() != 1) - check.errorf(x, _TooManyValues, "%d-valued %s where single value is expected", t.Len(), x) - x.mode = invalid - } - } -} - // expr typechecks expression e and initializes x with the expression value. // The result must be a single value. // If an error occurred, x.mode is set to invalid. // func (check *Checker) expr(x *operand, e ast.Expr) { - check.multiExpr(x, e) + check.rawExpr(x, e, nil) + check.exclude(x, 1<