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,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) { - 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) +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 - } - // 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 - } + 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 + } + + default: + // multiple (possibly invalid) values + xlist = make([]*operand, len(elist)) + for i, e := range elist { + var x operand + check.expr(&x, e) + xlist[i] = &x } - x0.mode = value } - // single value - return 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 - } - 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 + } + } else { + // variadic_func(a, b, c) + if nargs >= npars-1 { + // Create custom parameters for arguments: keep + // the first npars-1 parameters and add one for + // each argument mapping to the ... parameter. + vars := make([]*Var, npars-1) // npars > 0 for variadic functions + copy(vars, sig.params.vars) + last := sig.params.vars[npars-1] + typ := last.typ.(*Slice).elem + for len(vars) < nargs { + vars = append(vars, NewParam(last.pos, last.pkg, last.name, typ)) + } + sigParams = NewTuple(vars...) // possibly nil! + adjusted = true + npars = nargs + } else { + // nargs < npars-1 + npars-- // for correct error message below } - check.argument(sig, i, x, ellipsis, context) } + } else { + if ddd { + // standard_func(a, b, c...) + check.errorf(inNode(call, call.Ellipsis), _NonVariadicDotDotDot, "cannot use ... in call to non-variadic %s", call.Fun) + return + } + // standard_func(a, b, c) } // check argument count - 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 - if debug { - if _, ok := typ.(*Slice); !ok { - check.dump("%v: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), 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 } } - default: - check.errorf(x, _WrongArgCount, "too many arguments") - return + // all type arguments were inferred sucessfully + if debug { + for _, targ := range targs { + assert(targ != nil) + } + } + + // compute result signature + rsig = check.instantiate(call.Pos(), sig, targs, nil).(*Signature) + assert(rsig.tparams == nil) // signature is not generic anymore + check.recordInferred(call, targs, rsig) + + // Optimization: Only if the parameter list was adjusted do we + // need to compute it from the adjusted list; otherwise we can + // simply use the result signature's parameter list. + if adjusted { + sigParams = check.subst(call.Pos(), sigParams, makeSubstMap(sig.tparams, targs)).(*Tuple) + } else { + sigParams = rsig.params + } } - if ellipsis.IsValid() { - 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 - } - } else if sig.variadic && i >= n-1 { - // use the variadic parameter slice's element type - typ = typ.(*Slice).elem + // 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)) } - check.assignment(x, typ, context) + 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,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<