diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go index 81c64589d0..e817c47eb7 100644 --- a/src/go/ast/ast.go +++ b/src/go/ast/ast.go @@ -257,6 +257,16 @@ func (f *FieldList) NumFields() int { return n } +// A TypeParamList represents a list of type parameters, enclosed by parentheses. +type TypeParamList struct { + Lparen token.Pos // position of "(" + Names []*Ident // type parameter names; or nil + Rparen token.Pos // position of ")" +} + +func (t *TypeParamList) Pos() token.Pos { return t.Lparen } +func (t *TypeParamList) End() token.Pos { return t.Rparen } + // An expression is represented by a tree consisting of one // or more of the following concrete expression nodes. // @@ -962,11 +972,12 @@ type ( // A FuncDecl node represents a function declaration. FuncDecl struct { - Doc *CommentGroup // associated documentation; or nil - Recv *FieldList // receiver (methods); or nil (functions) - Name *Ident // function/method name - Type *FuncType // function signature: parameters, results, and position of "func" keyword - Body *BlockStmt // function body; or nil for external (non-Go) function + Doc *CommentGroup // associated documentation; or nil + Recv *FieldList // receiver (methods); or nil (functions) + Name *Ident // function/method name + TPar *TypeParamList // type parameters; or nil + Type *FuncType // function signature: parameters, results, and position of "func" keyword + Body *BlockStmt // function body; or nil for external (non-Go) function } ) diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index 31a73985bf..19cd931d16 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -880,19 +880,32 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [ return } -func (p *parser) parseParameters(scope *ast.Scope, ellipsisOk bool) *ast.FieldList { +func (p *parser) parseParameters(scope *ast.Scope, typeParamsOk, ellipsisOk bool) (tparams *ast.TypeParamList, params *ast.FieldList) { if p.trace { defer un(trace(p, "Parameters")) } - var params []*ast.Field lparen := p.expect(token.LPAREN) + if typeParamsOk && p.tok == token.TYPE { + p.next() + var names []*ast.Ident + if p.tok != token.RPAREN { + names = p.parseIdentList() + } + rparen := p.expect(token.RPAREN) + tparams = &ast.TypeParamList{Lparen: lparen, Names: names, Rparen: rparen} + p.declare(tparams, nil, scope, ast.Typ, names...) + lparen = p.expect(token.LPAREN) + } + + var fields []*ast.Field if p.tok != token.RPAREN { - params = p.parseParameterList(scope, ellipsisOk) + fields = p.parseParameterList(scope, ellipsisOk) } rparen := p.expect(token.RPAREN) + params = &ast.FieldList{Opening: lparen, List: fields, Closing: rparen} - return &ast.FieldList{Opening: lparen, List: params, Closing: rparen} + return } func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList { @@ -901,7 +914,8 @@ func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList { } if p.tok == token.LPAREN { - return p.parseParameters(scope, false) + _, results := p.parseParameters(scope, false, false) + return results } typ := p.tryType() @@ -914,17 +928,6 @@ func (p *parser) parseResult(scope *ast.Scope) *ast.FieldList { return nil } -func (p *parser) parseSignature(scope *ast.Scope) (params, results *ast.FieldList) { - if p.trace { - defer un(trace(p, "Signature")) - } - - params = p.parseParameters(scope, true) - results = p.parseResult(scope) - - return -} - func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) { if p.trace { defer un(trace(p, "FuncType")) @@ -932,7 +935,8 @@ func (p *parser) parseFuncType() (*ast.FuncType, *ast.Scope) { pos := p.expect(token.FUNC) scope := ast.NewScope(p.topScope) // function scope - params, results := p.parseSignature(scope) + _, params := p.parseParameters(scope, false, true) + results := p.parseResult(scope) return &ast.FuncType{Func: pos, Params: params, Results: results}, scope } @@ -950,7 +954,8 @@ func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field { // method idents = []*ast.Ident{ident} scope := ast.NewScope(nil) // method scope - params, results := p.parseSignature(scope) + _, params := p.parseParameters(scope, false, true) + results := p.parseResult(scope) typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results} } else { // embedded interface @@ -2440,12 +2445,13 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl { var recv *ast.FieldList if p.tok == token.LPAREN { - recv = p.parseParameters(scope, false) + _, recv = p.parseParameters(scope, false, false) } ident := p.parseIdent() - params, results := p.parseSignature(scope) + tparams, params := p.parseParameters(scope, recv == nil, true) + results := p.parseResult(scope) var body *ast.BlockStmt if p.tok == token.LBRACE { @@ -2467,6 +2473,7 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl { Doc: doc, Recv: recv, Name: ident, + TPar: tparams, Type: &ast.FuncType{ Func: pos, Params: params, diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go index 49bb681e09..fd5ed6352d 100644 --- a/src/go/parser/short_test.go +++ b/src/go/parser/short_test.go @@ -48,6 +48,9 @@ var valids = []string{ `package p; var _ = map[*P]int{&P{}:0, {}:1}`, `package p; type T = int`, `package p; type (T = p.T; _ = struct{}; x = *T)`, + `package p; func _(type)()`, + `package p; func _(type)()()`, + `package p; func _(type A, B)(a A) B`, } func TestValid(t *testing.T) { diff --git a/src/go/printer/nodes.go b/src/go/printer/nodes.go index 0360c4606e..21e2533f48 100644 --- a/src/go/printer/nodes.go +++ b/src/go/printer/nodes.go @@ -1773,6 +1773,15 @@ func (p *printer) funcDecl(d *ast.FuncDecl) { p.print(blank) } p.expr(d.Name) + // TODO(gri) decide if we should print empty type parameter lists "(type)" at all + if tparams := d.TPar; tparams != nil { + p.print(tparams.Lparen, token.LPAREN, token.TYPE) + if len(tparams.Names) > 0 { + p.print(blank) + p.identList(tparams.Names, true) + } + p.print(tparams.Rparen, token.RPAREN) + } p.signature(d.Type.Params, d.Type.Results) p.funcBody(p.distanceFrom(d.Pos(), startCol), vtab, d.Body) } diff --git a/src/go/printer/testdata/declarations.golden b/src/go/printer/testdata/declarations.golden index fe0f7838de..058baa8e50 100644 --- a/src/go/printer/testdata/declarations.golden +++ b/src/go/printer/testdata/declarations.golden @@ -992,6 +992,11 @@ func _(struct { y int }) // no extra comma between } and ) +// type parameters +func _(type)() +func _(type A, B)(a A, b B) int {} +func _(type T)(x, y T) T + // alias declarations type c0 struct{} diff --git a/src/go/printer/testdata/declarations.input b/src/go/printer/testdata/declarations.input index a858051ef0..8787f4910b 100644 --- a/src/go/printer/testdata/declarations.input +++ b/src/go/printer/testdata/declarations.input @@ -1005,6 +1005,11 @@ func _(struct { y int }) // no extra comma between } and ) +// type parameters +func _(type)() +func _(type A, B)(a A, b B) int {} +func _(type T)(x, y T) T + // alias declarations type c0 struct{} diff --git a/src/go/types/assignments.go b/src/go/types/assignments.go index 34a9d7843d..079a7c6676 100644 --- a/src/go/types/assignments.go +++ b/src/go/types/assignments.go @@ -50,6 +50,11 @@ func (check *Checker) assignment(x *operand, T Type, context string) { } // x.typ is typed + // A generic (non-instantiated) value cannot be assigned to a variable. + if isGeneric(x.typ) { + check.errorf(x.pos(), "cannot use generic %s 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." @@ -131,6 +136,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 } @@ -204,25 +210,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, orig_rhs []ast.Expr, returnPos token.Pos) { + rhs, commaOk := check.exprList(orig_rhs, 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(returnPos, "wrong number of return values (want %d, got %d)", l, r) + check.errorf(returnPos, "wrong number of return values (want %d, got %d)", len(lhs), len(rhs)) return } - check.errorf(rhs[0].Pos(), "cannot initialize %d variables with %d values", l, r) + check.errorf(rhs[0].pos(), "cannot initialize %d variables with %d values", len(lhs), len(rhs)) return } @@ -231,50 +239,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(orig_rhs[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, orig_rhs []ast.Expr) { + rhs, commaOk := check.exprList(orig_rhs, 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].Pos(), "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].pos(), "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(orig_rhs[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 a445ebf1c6..3086acbbe9 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -29,8 +29,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 @@ -39,15 +39,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] } + nargs = len(xlist) // evaluate first argument, if present if nargs > 0 { arg(x, 0) @@ -117,14 +116,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 diff --git a/src/go/types/call.go b/src/go/types/call.go index be4cfdf9eb..0e40df580b 100644 --- a/src/go/types/call.go +++ b/src/go/types/call.go @@ -19,7 +19,6 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind { switch x.mode { case invalid: check.use(e.Args...) - x.mode = invalid x.expr = e return statement @@ -66,13 +65,38 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind { 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 := check.exprOrTypeList(e.Args) + + // instantiate function if needed + if n := len(args); len(sig.tparams) > 0 && n > 0 && args[0].mode == typexpr { + // if the first argument is a type, assume we have explicit type arguments + if len(sig.tparams) != n { + check.errorf(args[n-1].pos(), "got %d type arguments but want %d", n, len(sig.tparams)) + x.mode = invalid + x.expr = e + return expression + } + // collect types + targs := make([]Type, n) + for i, a := range args { + if a.mode != typexpr { + // error was reported earlier + x.mode = invalid + x.expr = e + return expression + } + targs[i] = a.typ + } + // result is type-instantiated function value + x.mode = value + x.expr = e + x.typ = subst(sig, targs) + return expression } + sig = check.arguments(e, sig, args) + // determine result switch sig.results.Len() { case 0: @@ -88,229 +112,200 @@ func (check *Checker) call(x *operand, e *ast.CallExpr) exprKind { x.mode = value x.typ = sig.results } - x.expr = e 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 && isParametrized(x.typ) { + x.mode = invalid + } + return statement } } -// use type-checks each argument. -// Useful to make sure expressions are evaluated -// (and variables are "used") in the presence of other errors. -// The arguments may be nil. -func (check *Checker) use(arg ...ast.Expr) { - var x operand - for _, e := range arg { - // The nil check below is necessary since certain AST fields - // may legally be nil (e.g., the ast.SliceExpr.High field). - if e != nil { - check.rawExpr(&x, e, nil) - } - } -} +// 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) { + switch len(elist) { + case 0: + // nothing to do -// useLHS is like use, but doesn't "use" top-level identifiers. -// It should be called instead of use if the arguments are -// expressions on the lhs of an assignment. -// The arguments must not be nil. -func (check *Checker) useLHS(arg ...ast.Expr) { - var x operand - for _, e := range arg { - // If the lhs is an identifier denoting a variable v, this assignment - // is not a 'use' of v. Remember current value of v.used and restore - // after evaluating the lhs via check.rawExpr. - var v *Var - var v_used bool - if ident, _ := unparen(e).(*ast.Ident); ident != nil { - // never type-check the blank name on the lhs - if ident.Name == "_" { - continue + 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} } - if _, obj := check.scope.LookupParent(ident.Name, token.NoPos); obj != nil { - // It's ok to mark non-local variables, but ignore variables - // from other packages to avoid potential race conditions with - // dot-imported variables. - if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg { - v = w - v_used = v.used - } + break + } + + // 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.rawExpr(&x, e, nil) - if v != nil { - v.used = v_used // restore v.used + if 0 < ntypes && ntypes < len(xlist) { + check.errorf(xlist[0].pos(), "mix of value and type expressions") } } + + return } -// 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) - } -} +func (check *Checker) exprList(elist []ast.Expr, allowCommaOk bool) (xlist []*operand, commaOk bool) { + switch len(elist) { + case 0: + // nothing to do -// 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) + 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 + } -// 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 - } - - 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 + xlist = append(xlist, &operand{mode: value, expr: e, typ: Typ[UntypedBool]}) + 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 + } } - if x0.mode == mapindex || x0.mode == commaok || x0.mode == commaerr { - // comma-ok value - if allowCommaOk { - a := [2]Type{x0.typ, Typ[UntypedBool]} - if x0.mode == commaerr { - a[1] = universeError - } - return func(x *operand, i int) { - x.mode = value - x.expr = x0.expr - x.typ = a[i] - }, 2, true - } - x0.mode = value - } - - // single value - return func(x *operand, i int) { - if i != 0 { - unreachable() - } - *x = x0 - }, 1, false + return } -// arguments checks argument passing for the call with the given signature. -// The arg function provides the operand for the i'th argument. -func (check *Checker) arguments(x *operand, call *ast.CallExpr, sig *Signature, arg getter, n int) { - if call.Ellipsis.IsValid() { - // last argument is of the form x... - if !sig.variadic { - check.errorf(call.Ellipsis, "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.pos(), "%s used as value", a) return - } - if len(call.Args) == 1 && n > 1 { - // f()... is not permitted if f() is multi-valued - check.errorf(call.Ellipsis, "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 + params := sig.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(call.Ellipsis, "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)) + } + params = NewTuple(vars...) + 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(call.Ellipsis, "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(call.Rparen, "too few arguments in call to %s", call.Fun) - // ok to continue - } -} - -// argument checks passing of argument x to the i'th parameter of the given signature. -// If ellipsis is valid, the argument is followed by ... at that position in the call. -func (check *Checker) argument(sig *Signature, i int, x *operand, ellipsis token.Pos, context string) { - check.singleValue(x) - if x.mode == invalid { - return - } - - n := sig.params.Len() - - // determine parameter type - var typ Type switch { - case i < n: - typ = sig.params.vars[i].typ - case sig.variadic: - typ = sig.params.vars[n-1].typ - if debug { - if _, ok := typ.(*Slice); !ok { - check.dump("%v: expected unnamed slice type, got %s", sig.params.vars[n-1].Pos(), typ) - } - } - default: - check.errorf(x.pos(), "too many arguments") + case nargs < npars: + check.errorf(call.Rparen, "not enough arguments in call to %s", call.Fun) + return + case nargs > npars: + check.errorf(args[npars].pos(), "too many arguments in call to %s", call.Fun) // report at first extra argument return } - if ellipsis.IsValid() { - // argument is of the form x... and x is single-valued - if i != n-1 { - check.errorf(ellipsis, "can only use ... with matching parameter") + // infer type arguments and instantiate signature if necessary + if len(sig.tparams) > 0 { + targs := check.infer(call.Rparen, sig.tparams, params, args) + if targs == nil { return } - if _, ok := x.typ.Underlying().(*Slice); !ok && x.typ != Typ[UntypedNil] { // see issue #18268 - check.errorf(x.pos(), "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 + rsig = subst(sig, targs).(*Signature) + params = subst(params, targs).(*Tuple) + // TODO(gri) Optimization: We don't need to check arguments + // from which we inferred parameter types. } - check.assignment(x, typ, context) + // check arguments + for i, a := range args { + check.assignment(a, params.vars[i].typ, "argument") + } + + return } var cgoPrefixes = [...]string{ @@ -563,3 +558,52 @@ Error: x.mode = invalid x.expr = e } + +// use type-checks each argument. +// Useful to make sure expressions are evaluated +// (and variables are "used") in the presence of other errors. +// The arguments may be nil. +func (check *Checker) use(arg ...ast.Expr) { + var x operand + for _, e := range arg { + // The nil check below is necessary since certain AST fields + // may legally be nil (e.g., the ast.SliceExpr.High field). + if e != nil { + check.rawExpr(&x, e, nil) + } + } +} + +// useLHS is like use, but doesn't "use" top-level identifiers. +// It should be called instead of use if the arguments are +// expressions on the lhs of an assignment. +// The arguments must not be nil. +func (check *Checker) useLHS(arg ...ast.Expr) { + var x operand + for _, e := range arg { + // If the lhs is an identifier denoting a variable v, this assignment + // is not a 'use' of v. Remember current value of v.used and restore + // after evaluating the lhs via check.rawExpr. + var v *Var + var v_used bool + if ident, _ := unparen(e).(*ast.Ident); ident != nil { + // never type-check the blank name on the lhs + if ident.Name == "_" { + continue + } + if _, obj := check.scope.LookupParent(ident.Name, token.NoPos); obj != nil { + // It's ok to mark non-local variables, but ignore variables + // from other packages to avoid potential race conditions with + // dot-imported variables. + if w, _ := obj.(*Var); w != nil && w.pkg == check.pkg { + v = w + v_used = v.used + } + } + } + check.rawExpr(&x, e, nil) + if v != nil { + v.used = v_used // restore v.used + } + } +} diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index 89122d75ff..a4a89559e1 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -98,6 +98,7 @@ var tests = [][]string{ {"testdata/issue23203b.src"}, {"testdata/issue28251.src"}, {"testdata/issue6977.src"}, + {"testdata/typeparams.src"}, } var fset = token.NewFileSet() diff --git a/src/go/types/decl.go b/src/go/types/decl.go index 5c0e611c51..7bbda9c55d 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -649,7 +649,7 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) { sig := new(Signature) obj.typ = sig // guard against cycles fdecl := decl.fdecl - check.funcType(sig, fdecl.Recv, fdecl.Type) + check.funcType(sig, fdecl.Recv, fdecl.TPar, fdecl.Type) if sig.recv == nil && obj.name == "init" && (sig.params.Len() > 0 || sig.results.Len() > 0) { check.errorf(fdecl.Pos(), "func init must have no arguments and no return values") // ok to continue diff --git a/src/go/types/expr.go b/src/go/types/expr.go index d1e892a9b7..710516513b 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -1579,6 +1579,73 @@ func (check *Checker) typeAssertion(pos token.Pos, x *operand, xtyp *Interface, check.errorf(pos, "%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< 0 +} + // Comparable reports whether values of type T are comparable. func Comparable(T Type) bool { switch t := T.Underlying().(type) { @@ -113,13 +119,13 @@ func hasNil(typ Type) bool { // identical reports whether x and y are identical types. // Receivers of Signature types are ignored. func (check *Checker) identical(x, y Type) bool { - return check.identical0(x, y, true, nil) + return check.identical0(x, y, true, nil, nil) } // identicalIgnoreTags reports whether x and y are identical types if tags are ignored. // Receivers of Signature types are ignored. func (check *Checker) identicalIgnoreTags(x, y Type) bool { - return check.identical0(x, y, false, nil) + return check.identical0(x, y, false, nil, nil) } // An ifacePair is a node in a stack of interface type pairs compared for identity. @@ -132,11 +138,18 @@ func (p *ifacePair) identical(q *ifacePair) bool { return p.x == q.x && p.y == q.y || p.x == q.y && p.y == q.x } -func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { +func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair, tparams []Type) bool { if x == y { return true } + // make sure type parameter is in x if we have one + // TODO(gri) this may not be needed: we should only be inferring in one direction, + // from (given) argument types, to possibly free parameter types + if _, ok := x.(*TypeParam); !ok { + x, y = y, x + } + switch x := x.(type) { case *Basic: // Basic types are singletons except for the rune and byte @@ -152,13 +165,13 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { if y, ok := y.(*Array); ok { // If one or both array lengths are unknown (< 0) due to some error, // assume they are the same to avoid spurious follow-on errors. - return (x.len < 0 || y.len < 0 || x.len == y.len) && check.identical0(x.elem, y.elem, cmpTags, p) + return (x.len < 0 || y.len < 0 || x.len == y.len) && check.identical0(x.elem, y.elem, cmpTags, p, tparams) } case *Slice: // Two slice types are identical if they have identical element types. if y, ok := y.(*Slice); ok { - return check.identical0(x.elem, y.elem, cmpTags, p) + return check.identical0(x.elem, y.elem, cmpTags, p, tparams) } case *Struct: @@ -173,7 +186,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { if f.embedded != g.embedded || cmpTags && x.Tag(i) != y.Tag(i) || !f.sameId(g.pkg, g.name) || - !check.identical0(f.typ, g.typ, cmpTags, p) { + !check.identical0(f.typ, g.typ, cmpTags, p, tparams) { return false } } @@ -184,7 +197,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { case *Pointer: // Two pointer types are identical if they have identical base types. if y, ok := y.(*Pointer); ok { - return check.identical0(x.base, y.base, cmpTags, p) + return check.identical0(x.base, y.base, cmpTags, p, tparams) } case *Tuple: @@ -195,7 +208,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { if x != nil { for i, v := range x.vars { w := y.vars[i] - if !check.identical0(v.typ, w.typ, cmpTags, p) { + if !check.identical0(v.typ, w.typ, cmpTags, p, tparams) { return false } } @@ -211,8 +224,8 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { // names are not required to match. if y, ok := y.(*Signature); ok { return x.variadic == y.variadic && - check.identical0(x.params, y.params, cmpTags, p) && - check.identical0(x.results, y.results, cmpTags, p) + check.identical0(x.params, y.params, cmpTags, p, tparams) && + check.identical0(x.results, y.results, cmpTags, p, tparams) } case *Interface: @@ -266,7 +279,7 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { } for i, f := range a { g := b[i] - if f.Id() != g.Id() || !check.identical0(f.typ, g.typ, cmpTags, q) { + if f.Id() != g.Id() || !check.identical0(f.typ, g.typ, cmpTags, q, tparams) { return false } } @@ -277,14 +290,14 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { case *Map: // Two map types are identical if they have identical key and value types. if y, ok := y.(*Map); ok { - return check.identical0(x.key, y.key, cmpTags, p) && check.identical0(x.elem, y.elem, cmpTags, p) + return check.identical0(x.key, y.key, cmpTags, p, tparams) && check.identical0(x.elem, y.elem, cmpTags, p, tparams) } case *Chan: // Two channel types are identical if they have identical value types // and the same direction. if y, ok := y.(*Chan); ok { - return x.dir == y.dir && check.identical0(x.elem, y.elem, cmpTags, p) + return x.dir == y.dir && check.identical0(x.elem, y.elem, cmpTags, p, tparams) } case *Named: @@ -294,6 +307,22 @@ func (check *Checker) identical0(x, y Type, cmpTags bool, p *ifacePair) bool { return x.obj == y.obj } + case *TypeParam: + if tparams == nil { + unreachable() + } + if y, ok := y.(*TypeParam); ok { + // TODO(gri) do we need to look at type names here? + // - consider type-checking a generic function calling another generic function + // - what about self-recursive calls? + return x.index == y.index + } + if x := tparams[x.index]; x != nil { + return check.identical0(x, y, cmpTags, p, tparams) + } + tparams[x.index] = y // infer type from y + return true + case nil: default: diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index c1593bbee9..1eb7a62c4f 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -49,6 +49,11 @@ func (check *Checker) funcBody(decl *declInfo, name string, sig *Signature, body check.error(body.Rbrace, "missing return") } + // TODO(gri) Should we make it an error to declare generic functions + // where the type parameters are not used? + // 12/19/2018: Probably not - it can make sense to have an API with + // all functions uniformly sharing the same type parameters. + // spec: "Implementation restriction: A compiler may make it illegal to // declare a variable inside a function body if the variable is never used." check.usage(sig.scope) diff --git a/src/go/types/subst.go b/src/go/types/subst.go new file mode 100644 index 0000000000..c584e100f6 --- /dev/null +++ b/src/go/types/subst.go @@ -0,0 +1,144 @@ +// Copyright 2018 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. + +// This file implements instantiation of generic types +// through substitution of type parameters by actual +// types. + +package types + +func subst(typ Type, targs []Type) Type { + if len(targs) == 0 { + return typ + } + s := subster{targs, make(map[Type]Type)} + return s.typ(typ) +} + +type subster struct { + targs []Type + cache map[Type]Type +} + +func (s *subster) typ(typ Type) (res Type) { + // TODO(gri) this is not correct in the presence of cycles + if t, hit := s.cache[typ]; hit { + return t + } + defer func() { + s.cache[typ] = res + }() + + switch t := typ.(type) { + case nil, *Basic: // TODO(gri) should nil be handled here? + // nothing to do + + case *Array: + elem := s.typ(t.elem) + if elem != t.elem { + return &Array{t.len, elem} + } + + case *Slice: + elem := s.typ(t.elem) + if elem != t.elem { + return &Slice{elem} + } + + case *Struct: + if fields, copied := s.varList(t.fields); copied { + return &Struct{fields, t.tags} + } + + case *Pointer: + base := s.typ(t.base) + if base != t.base { + return &Pointer{base} + } + + case *Tuple: + return s.tuple(t) + + case *Signature: + recv := s.var_(t.recv) // not strictly needed (receivers cannot be parametrized) + params := s.tuple(t.params) + results := s.tuple(t.results) + if recv != t.recv || params != t.params || results != t.results { + copy := *t + copy.tparams = nil // TODO(gri) is this correct? + copy.recv = recv + copy.params = params + copy.results = results + return © + } + + case *Interface: + panic("subst not implemented for interfaces") + + case *Map: + key := s.typ(t.key) + elem := s.typ(t.elem) + if key != t.key || elem != t.elem { + return &Map{key, elem} + } + + case *Chan: + elem := s.typ(t.elem) + if elem != t.elem { + return &Chan{t.dir, elem} + } + + case *Named: + // TODO(gri) is this correct? + // nothing to do + + case *TypeParam: + if targ := s.targs[t.index]; targ != nil { + return targ + } + + default: + panic("unimplemented") + } + + return typ +} + +func (s *subster) var_(v *Var) *Var { + if v != nil { + if typ := s.typ(v.typ); typ != v.typ { + copy := *v + copy.typ = typ + return © + } + } + return v +} + +func (s *subster) tuple(t *Tuple) *Tuple { + if t != nil { + if vars, copied := s.varList(t.vars); copied { + return &Tuple{vars} + } + } + return t +} + +func (s *subster) varList(in []*Var) (out []*Var, copied bool) { + out = in + for i, v := range in { + if w := s.var_(v); w != v { + if !copied { + // first variable that got substituted => allocate new out slice + // and copy all variables + new := make([]*Var, len(in)) + copy(new, out) + out = new + copied = true + } + out[i] = w + } + } + return +} diff --git a/src/go/types/testdata/builtins.src b/src/go/types/testdata/builtins.src index ecdba51553..2de9cdb947 100644 --- a/src/go/types/testdata/builtins.src +++ b/src/go/types/testdata/builtins.src @@ -25,11 +25,11 @@ func append1() { _ = append(s, b) _ = append(s, x /* ERROR cannot use x */ ) _ = append(s, s /* ERROR cannot use s */ ) - _ = append(s... /* ERROR can only use ... with matching parameter */ ) - _ = append(s, b, s... /* ERROR can only use ... with matching parameter */ ) + _ = append(s... ) /* ERROR not enough arguments */ + _ = append(s, b, s /* ERROR too many arguments */ ... ) _ = append(s, 1, 2, 3) _ = append(s, 1, 2, 3, x /* ERROR cannot use x */ , 5, 6, 6) - _ = append(s, 1, 2, s... /* ERROR can only use ... with matching parameter */ ) + _ = append(s, 1, 2 /* ERROR too many arguments */ , s... ) _ = append([]interface{}(nil), 1, 2, "foo", x, 3.1425, false) type S []byte diff --git a/src/go/types/testdata/expr3.src b/src/go/types/testdata/expr3.src index 63af9fc867..e42af0ba01 100644 --- a/src/go/types/testdata/expr3.src +++ b/src/go/types/testdata/expr3.src @@ -491,26 +491,26 @@ func _calls() { f1(0) f1(x) f1(10.0) - f1() /* ERROR "too few arguments" */ + f1() /* ERROR "not enough arguments" */ f1(x, y /* ERROR "too many arguments" */ ) f1(s /* ERROR "cannot use .* in argument" */ ) f1(x ... /* ERROR "cannot use ..." */ ) f1(g0 /* ERROR "used as value" */ ()) f1(g1()) - f1(g2 /* ERROR "cannot use g2" */ /* ERROR "too many arguments" */ ()) + f1(g2 /* ERROR "too many arguments" */ ()) - f2() /* ERROR "too few arguments" */ - f2(3.14) /* ERROR "too few arguments" */ + f2() /* ERROR "not enough arguments" */ + f2(3.14) /* ERROR "not enough arguments" */ f2(3.14, "foo") f2(x /* ERROR "cannot use .* in argument" */ , "foo") f2(g0 /* ERROR "used as value" */ ()) - f2(g1 /* ERROR "cannot use .* in argument" */ ()) /* ERROR "too few arguments" */ + f2(g1()) /* ERROR "not enough arguments" */ f2(g2()) - fs() /* ERROR "too few arguments" */ + fs() /* ERROR "not enough arguments" */ fs(g0 /* ERROR "used as value" */ ()) fs(g1 /* ERROR "cannot use .* in argument" */ ()) - fs(g2 /* ERROR "cannot use .* in argument" */ /* ERROR "too many arguments" */ ()) + fs(g2 /* ERROR "too many arguments" */ ()) fs(gs()) fv() @@ -518,7 +518,7 @@ func _calls() { fv(s /* ERROR "cannot use .* in argument" */ ) fv(s...) fv(x /* ERROR "cannot use" */ ...) - fv(1, s... /* ERROR "can only use ... with matching parameter" */ ) + fv(1, s /* ERROR "too many arguments" */ ... ) fv(gs /* ERROR "cannot use .* in argument" */ ()) fv(gs /* ERROR "cannot use .* in argument" */ ()...) @@ -527,7 +527,7 @@ func _calls() { t.fm(1, 2.0, x) t.fm(s /* ERROR "cannot use .* in argument" */ ) t.fm(g1()) - t.fm(1, s... /* ERROR "can only use ... with matching parameter" */ ) + t.fm(1, s /* ERROR "too many arguments" */ ... ) t.fm(gs /* ERROR "cannot use .* in argument" */ ()) t.fm(gs /* ERROR "cannot use .* in argument" */ ()...) @@ -535,7 +535,7 @@ func _calls() { T.fm(t, 1, 2.0, x) T.fm(t, s /* ERROR "cannot use .* in argument" */ ) T.fm(t, g1()) - T.fm(t, 1, s... /* ERROR "can only use ... with matching parameter" */ ) + T.fm(t, 1, s /* ERROR "too many arguments" */ ... ) T.fm(t, gs /* ERROR "cannot use .* in argument" */ ()) T.fm(t, gs /* ERROR "cannot use .* in argument" */ ()...) @@ -544,7 +544,7 @@ func _calls() { i.fm(1, 2.0, x) i.fm(s /* ERROR "cannot use .* in argument" */ ) i.fm(g1()) - i.fm(1, s... /* ERROR "can only use ... with matching parameter" */ ) + i.fm(1, s /* ERROR "too many arguments" */ ... ) i.fm(gs /* ERROR "cannot use .* in argument" */ ()) i.fm(gs /* ERROR "cannot use .* in argument" */ ()...) diff --git a/src/go/types/testdata/stmt0.src b/src/go/types/testdata/stmt0.src index 446997ac09..ed936aab91 100644 --- a/src/go/types/testdata/stmt0.src +++ b/src/go/types/testdata/stmt0.src @@ -86,7 +86,7 @@ func assignments1() { g := func(int, bool){} var m map[int]int - g(m[0]) /* ERROR "too few arguments" */ + g(m[0]) /* ERROR "not enough arguments" */ // assignments to _ _ = nil /* ERROR "use of untyped nil" */ diff --git a/src/go/types/testdata/typeparams.src b/src/go/types/testdata/typeparams.src new file mode 100644 index 0000000000..51226b6302 --- /dev/null +++ b/src/go/types/testdata/typeparams.src @@ -0,0 +1,113 @@ +// Copyright 2018 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. + +package p + +func identity(type T)(x T) T { return x } + +func _(type)(x int) int +func _(type T)(T T) T { return T } +func _(type T)(T T) T { var x T /* ERROR T.*is not a type */ ; return x } +func _(type T, T /* ERROR redeclared */ )() + +func reverse(type T)(list []T) []T { + rlist := make([]T, len(list)) + i := len(list) + for _, x := range list { + i-- + rlist[i] = x + } + return rlist +} + +var _ = reverse /* ERROR cannot use generic reverse */ +var _ = reverse(int, float32 /* ERROR got 2 type arguments */ ) ([]int{1, 2, 3}) +var _ = reverse(int)([ /* ERROR cannot use */ ]float32{1, 2, 3}) +var f = reverse(chan int) +var _ = f(0 /* ERROR cannot convert 0 .* to \[\]chan int */ ) + +func swap(type A, B)(a A, b B) (B, A) { return b, a } + +var _ = swap /* ERROR single value is expected */ (int, float32)(1, 2) +var f32, i = swap(int, float32)(swap(float32, int)(1, 2)) +var _ float32 = f32 +var _ int = i + +func swapswap(type A, B)(a A, b B) (A, B) { + return swap(B, A)(b, a) +} + +// type F(type A, B) func(A, B) (B, A) + +func min(type T)(x, y T) T { + //if x < y { + // return x + //} + return y +} + +func new(type T)() *T { + var x T + return &x +} + +var _ = new /* ERROR cannot use generic new */ +var _ *int = new(int)() + +func _(type T)(map[T /* ERROR invalid map key type */]int) // w/o contract we don't know if T is comparable + +func f1(type T1)(struct{T1}) int +var _ = f1(int)(struct{T1}{}) +type T1 = int + +func f2(type t1)(struct{t1; x float32}) int +var _ = f2(t1)(struct{t1; x float32}{}) +type t1 = int + + +func f3(type A, B, C)(A, struct{x B}, func(A, struct{x B}, *C)) int + +var _ = f3(int, rune, bool)(1, struct{x rune}{}, nil) + +// type inference checks + +var _ = new() /* ERROR cannot infer T */ + +func f4(type A, B, C)(A, B) C + +var _ = f4(1, 2) /* ERROR cannot infer C */ +var _ = f4(int, float32, complex128)(1, 2) + +func f5(type A, B, C)(A, []*B, struct{f []C}) int + +var _ = f5(int, float32, complex128)(0, nil, struct{f []complex128}{}) +var _ = f5(0, nil /* ERROR untyped nil */, struct{f []complex128}{}) // <<<< TODO(gri) need better error message +var _ = f5(0, []*float32{new(float32)()}, struct{f []complex128}{}) + +func f6(type A)(A, []A) int + +var _ = f6(0, nil /* ERROR does not match */ ) // <<<< TODO(gri) should this be accepted? + +// type inference with variadic functions + +func f7(type T)(...T) T + +var _ int = f7() /* ERROR cannot infer T */ +var _ int = f7(1) +var _ int = f7(1, 2) +var _ int = f7([]int{}...) +var _ int = f7 /* ERROR cannot use */ ([]float64{}...) +var _ float64 = f7([]float64{}...) +var _ = f7(1, 2.3 /* ERROR does not match */ ) +var _ = f7(1.2, 3 /* ERROR does not match */ ) + +func f8(type A, B)(A, B, ...B) int + +var _ = f8(1) /* ERROR not enough arguments */ +var _ = f8(1, 2.3) +var _ = f8(1, 2.3, 3.4, 4.5) +var _ = f8(1, 2.3, 3.4, 4 /* ERROR does not match */ ) +var _ = f8(int, float64)(1, 2.3, 3.4, 4) + +var _ = f8(int, float64)(0, 0, nil...) // test case for #18268 diff --git a/src/go/types/type.go b/src/go/types/type.go index 087cda429d..4dac379bbc 100644 --- a/src/go/types/type.go +++ b/src/go/types/type.go @@ -199,11 +199,12 @@ type Signature struct { // and store it in the Func Object) because when type-checking a function // literal we call the general type checker which returns a general Type. // We then unpack the *Signature and use the scope for the literal body. - scope *Scope // function scope, present for package-local signatures - recv *Var // nil if not a method - params *Tuple // (incoming) parameters from left to right; or nil - results *Tuple // (outgoing) results from left to right; or nil - variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only) + scope *Scope // function scope, present for package-local signatures + recv *Var // nil if not a method + tparams []*TypeName // type parameters from left to right; or nil + params *Tuple // (incoming) parameters from left to right; or nil + results *Tuple // (outgoing) results from left to right; or nil + variadic bool // true if the last parameter's type is of the form ...T (or string, for append built-in only) } // NewSignature returns a new function type for the given receiver, parameters, @@ -220,7 +221,7 @@ func NewSignature(recv *Var, params, results *Tuple, variadic bool) *Signature { panic("types.NewSignature: variadic parameter must be of unnamed slice type") } } - return &Signature{nil, recv, params, results, variadic} + return &Signature{nil, recv, nil, params, results, variadic} } // Recv returns the receiver of signature s (if a method), or nil if a @@ -496,6 +497,21 @@ func (t *Named) AddMethod(m *Func) { } } +// A TypeParam represents a type parameter type. +type TypeParam struct { + obj *TypeName + index int +} + +// NewTypeParam returns a new TypeParam. +func NewTypeParam(obj *TypeName, index int) *TypeParam { + typ := &TypeParam{obj, index} + if obj.typ == nil { + obj.typ = typ + } + return typ +} + // Implementations for Type methods. func (b *Basic) Underlying() Type { return b } @@ -509,6 +525,7 @@ func (t *Interface) Underlying() Type { return t } func (m *Map) Underlying() Type { return m } func (c *Chan) Underlying() Type { return c } func (t *Named) Underlying() Type { return t.underlying } +func (c *TypeParam) Underlying() Type { return c } func (b *Basic) String() string { return TypeString(b, nil) } func (a *Array) String() string { return TypeString(a, nil) } @@ -521,3 +538,4 @@ func (t *Interface) String() string { return TypeString(t, nil) } func (m *Map) String() string { return TypeString(m, nil) } func (c *Chan) String() string { return TypeString(c, nil) } func (t *Named) String() string { return TypeString(t, nil) } +func (c *TypeParam) String() string { return TypeString(c, nil) } diff --git a/src/go/types/typestring.go b/src/go/types/typestring.go index 31c572f83b..19f84127ba 100644 --- a/src/go/types/typestring.go +++ b/src/go/types/typestring.go @@ -121,11 +121,19 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { if i > 0 { buf.WriteString("; ") } - if !f.embedded { - buf.WriteString(f.name) + buf.WriteString(f.name) + if f.embedded { + // emphasize that the embedded field's name + // doesn't match the field's type name + if f.name != embeddedFieldName(f.typ) { + buf.WriteString(" /* = ") + writeType(buf, f.typ, qf, visited) + buf.WriteString(" */") + } + } else { buf.WriteByte(' ') + writeType(buf, f.typ, qf, visited) } - writeType(buf, f.typ, qf, visited) if tag := t.Tag(i); tag != "" { fmt.Fprintf(buf, " %q", tag) } @@ -239,6 +247,15 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { } buf.WriteString(s) + case *TypeParam: + var s string + if t.obj != nil { + s = t.obj.name + } else { + s = fmt.Sprintf("TypeParam[%d]", t.index) + } + buf.WriteString(s) + default: // For externally defined implementations of Type. buf.WriteString(t.String()) @@ -287,6 +304,17 @@ func WriteSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier) { } func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []Type) { + if len(sig.tparams) > 0 { + buf.WriteString("(type ") + for i, tname := range sig.tparams { + if i > 0 { + buf.WriteString(", ") + } + buf.WriteString(tname.name) + } + buf.WriteByte(')') + } + writeTuple(buf, sig.params, sig.variadic, qf, visited) n := sig.results.Len() @@ -305,3 +333,20 @@ func writeSignature(buf *bytes.Buffer, sig *Signature, qf Qualifier, visited []T // multiple or named result(s) writeTuple(buf, sig.results, false, qf, visited) } + +// embeddedFieldName returns an embedded field's name given its type. +// The result is "" if the type doesn't have an embedded field name. +func embeddedFieldName(typ Type) string { + switch t := typ.(type) { + case *Basic: + return t.name + case *Named: + return t.obj.name + case *Pointer: + // *T is ok, but **T is not + if _, ok := t.base.(*Pointer); !ok { + return embeddedFieldName(t.base) + } + } + return "" // not a (pointer to) a defined type +} diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index d5837c4f0e..0fcd6da862 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -143,12 +143,19 @@ func (check *Checker) definedType(e ast.Expr, def *Named) (T Type) { } // funcType type-checks a function or method type. -func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast.FuncType) { +func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, tpar *ast.TypeParamList, ftyp *ast.FuncType) { + // type parameters are in a scope enclosing the function scope + // TODO(gri) should we always have this extra scope? + if tpar != nil && len(tpar.Names) > 0 { + check.scope = NewScope(check.scope, token.NoPos, token.NoPos, "function type parameters") // TODO(gri) replace with check.openScope call + defer check.closeScope() + } scope := NewScope(check.scope, token.NoPos, token.NoPos, "function") scope.isFunc = true check.recordScope(ftyp, scope) recvList, _ := check.collectParams(scope, recvPar, false) + tparams := check.collectTypeParams(check.scope, tpar) params, variadic := check.collectParams(scope, ftyp.Params, true) results, _ := check.collectParams(scope, ftyp.Results, false) @@ -199,9 +206,14 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast } } sig.recv = recv + // A method cannot have type parameters - this should be checked by the parser. + if len(tparams) > 0 { + check.invalidAST(tpar.Pos(), "method cannot have type parameters") + } } sig.scope = scope + sig.tparams = tparams sig.params = NewTuple(params...) sig.results = NewTuple(results...) sig.variadic = variadic @@ -282,7 +294,7 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { case *ast.FuncType: typ := new(Signature) def.setUnderlying(typ) - check.funcType(typ, nil, e) + check.funcType(typ, nil, nil, e) return typ case *ast.InterfaceType: @@ -394,6 +406,21 @@ func (check *Checker) arrayLength(e ast.Expr) int64 { return -1 } +func (check *Checker) collectTypeParams(scope *Scope, list *ast.TypeParamList) (tparams []*TypeName) { + if list == nil { + return + } + + for i, name := range list.Names { + tpar := NewTypeName(name.Pos(), check.pkg, name.Name, nil) + NewTypeParam(tpar, i) // assigns type to tpar as a side-effect + check.declare(scope, name, tpar, scope.pos) + tparams = append(tparams, tpar) + } + + return +} + func (check *Checker) collectParams(scope *Scope, list *ast.FieldList, variadicOk bool) (params []*Var, variadic bool) { if list == nil { return