go/*: type-checking of function type parameters

This change implements parsing and type-checking
of parametrized functions (without contracts).
Type-checking includes checking of generic function
calls using explicit type parameters as well as
implicit type parameters inferred from the actual
arguments.

Change-Id: I03c9c6912aa1e2ac79d9c5125fd5ac72df4e808a
This commit is contained in:
Robert Griesemer 2018-12-12 17:30:01 -08:00
parent bd486c39ba
commit fd16d941dc
23 changed files with 965 additions and 362 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -98,6 +98,7 @@ var tests = [][]string{
{"testdata/issue23203b.src"},
{"testdata/issue28251.src"},
{"testdata/issue6977.src"},
{"testdata/typeparams.src"},
}
var fset = token.NewFileSet()

View File

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

View File

@ -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<<novalue|1<<builtin|1<<typexpr)
check.singleValue(x)
}
// multiExpr is like expr but the result may also be a multi-value.
func (check *Checker) multiExpr(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
}
// multiExprOrType is like multiExpr but the result may also be a type.
func (check *Checker) multiExprOrType(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
check.exclude(x, 1<<novalue|1<<builtin)
}
// exprWithHint typechecks expression e and initializes x with the expression value;
// hint is the type of a composite literal element.
// If an error occurred, x.mode is set to invalid.
//
func (check *Checker) exprWithHint(x *operand, e ast.Expr, hint Type) {
assert(hint != nil)
check.rawExpr(x, e, hint)
check.exclude(x, 1<<novalue|1<<builtin|1<<typexpr)
check.singleValue(x)
}
// exprOrType typechecks expression or type e and initializes x with the expression value or type.
// If an error occurred, x.mode is set to invalid.
//
func (check *Checker) exprOrType(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
check.exclude(x, 1<<novalue)
check.singleValue(x)
}
// exclude reports an error if x.mode is in modeset and sets x.mode to invalid.
// The modeset may contain any of 1<<novalue, 1<<builtin, 1<<typexpr.
func (check *Checker) exclude(x *operand, modeset uint) {
if modeset&(1<<x.mode) != 0 {
var msg string
switch x.mode {
case novalue:
if modeset&(1<<typexpr) != 0 {
msg = "%s used as value"
} else {
msg = "%s used as value or type"
}
case builtin:
msg = "%s must be called"
case typexpr:
msg = "%s is not an expression"
default:
unreachable()
}
check.errorf(x.pos(), msg, x)
x.mode = invalid
}
}
// singleValue reports an error if x describes a tuple and sets x.mode to invalid.
func (check *Checker) singleValue(x *operand) {
if x.mode == value {
// tuple types are never named - no need for underlying type below
@ -1589,65 +1656,3 @@ func (check *Checker) singleValue(x *operand) {
}
}
}
// expr typechecks expression e and initializes x with the expression value.
// The result must be a single value.
// If an error occurred, x.mode is set to invalid.
//
func (check *Checker) expr(x *operand, e ast.Expr) {
check.multiExpr(x, e)
check.singleValue(x)
}
// multiExpr is like expr but the result may be a multi-value.
func (check *Checker) multiExpr(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
var msg string
switch x.mode {
default:
return
case novalue:
msg = "%s used as value"
case builtin:
msg = "%s must be called"
case typexpr:
msg = "%s is not an expression"
}
check.errorf(x.pos(), msg, x)
x.mode = invalid
}
// exprWithHint typechecks expression e and initializes x with the expression value;
// hint is the type of a composite literal element.
// If an error occurred, x.mode is set to invalid.
//
func (check *Checker) exprWithHint(x *operand, e ast.Expr, hint Type) {
assert(hint != nil)
check.rawExpr(x, e, hint)
check.singleValue(x)
var msg string
switch x.mode {
default:
return
case novalue:
msg = "%s used as value"
case builtin:
msg = "%s must be called"
case typexpr:
msg = "%s is not an expression"
}
check.errorf(x.pos(), msg, x)
x.mode = invalid
}
// exprOrType typechecks expression or type e and initializes x with the expression value or type.
// If an error occurred, x.mode is set to invalid.
//
func (check *Checker) exprOrType(x *operand, e ast.Expr) {
check.rawExpr(x, e, nil)
check.singleValue(x)
if x.mode == novalue {
check.errorf(x.pos(), "%s used as value or type", x)
x.mode = invalid
}
}

126
src/go/types/infer.go Normal file
View File

@ -0,0 +1,126 @@
// 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 type parameter inference given
// a list of concrete arguments and a parameter list.
package types
import "go/token"
// infer returns the list of actual type arguments for the given list of type parameters tparams
// by inferring them from the actual arguments args for the parameters pars. If infer fails to
// determine all type arguments, an error is reported and the result is nil.
func (check *Checker) infer(pos token.Pos, tparams []*TypeName, params *Tuple, args []*operand) []Type {
assert(params.Len() == len(args))
targs := make([]Type, len(tparams))
// determine indices of type-parametrized parameters
var indices []int
for i := 0; i < params.Len(); i++ {
par := params.At(i).typ
if isParametrized(par) {
indices = append(indices, i)
}
}
// 1st pass: unify parameter and argument types for typed arguments
for _, i := range indices {
arg := args[i]
if arg.mode == invalid {
// TODO(gri) we might still be able to infer all targs by
// simply ignoring (continue) invalid args
return nil // error was reported earlier
}
if isUntyped(arg.typ) {
continue // handled in 2nd pass
}
par := params.At(i)
if !check.identical0(par.typ, arg.typ, true, nil, targs) {
check.errorf(arg.pos(), "type %s for %s does not match %s = %s", arg.typ, arg.expr, par.typ, subst(par.typ, targs))
return nil
}
}
// 2nd pass: unify parameter and default argument types for remaining parametrized parameter types with untyped arguments
for _, i := range indices {
arg := args[i]
if isTyped(arg.typ) {
continue // handled in 1st pass
}
par := params.At(i)
if !check.identical0(par.typ, Default(arg.typ), true, nil, targs) {
check.errorf(arg.pos(), "default type %s for %s does not match %s = %s", Default(arg.typ), arg.expr, par.typ, subst(par.typ, targs))
return nil
}
}
// check if all type parameters have been determined
// TODO(gri) consider moving this outside this function and then we won't need to pass in pos
for i, t := range targs {
if t == nil {
tpar := tparams[i]
check.errorf(pos, "cannot infer %s (%s)", tpar.name, tpar.pos)
return nil
}
}
return targs
}
// isParametrized reports whether typ contains any type parameters.
// TODO(gri) do we need to handle cycles here?
func isParametrized(typ Type) bool {
switch t := typ.(type) {
case nil, *Basic, *Named: // TODO(gri) should nil be handled here?
break
case *Array:
return isParametrized(t.elem)
case *Slice:
return isParametrized(t.elem)
case *Struct:
for _, fld := range t.fields {
if isParametrized(fld.typ) {
return true
}
}
case *Pointer:
return isParametrized(t.base)
case *Tuple:
n := t.Len()
for i := 0; i < n; i++ {
if isParametrized(t.At(i).typ) {
return true
}
}
case *Signature:
assert(t.tparams == nil) // TODO(gri) is this correct?
assert(t.recv == nil || !isParametrized(t.recv.typ)) // interface method receiver may not be nil
return isParametrized(t.params) || isParametrized(t.results)
case *Interface:
panic("unimplemented")
case *Map:
return isParametrized(t.key) || isParametrized(t.elem)
case *Chan:
return isParametrized(t.elem)
case *TypeParam:
return true
default:
unreachable()
}
return false
}

View File

@ -77,6 +77,12 @@ func IsInterface(typ Type) bool {
return ok
}
func isGeneric(typ Type) bool {
// TODO(gri) can a defined type ever be generic?
t, ok := typ.Underlying().(*Signature)
return ok && len(t.tparams) > 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:

View File

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

144
src/go/types/subst.go Normal file
View File

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

View File

@ -25,11 +25,11 @@ func append1() {
_ = append(s, b)
_ = append(s, x /* ERROR cannot use x */ )
_ = append(s, s /* ERROR cannot use s */ )
_ = append(s... /* ERROR can only use ... with matching parameter */ )
_ = append(s, b, s... /* ERROR can only use ... with matching parameter */ )
_ = append(s... ) /* ERROR not enough arguments */
_ = append(s, b, s /* ERROR too many arguments */ ... )
_ = append(s, 1, 2, 3)
_ = append(s, 1, 2, 3, x /* ERROR cannot use x */ , 5, 6, 6)
_ = append(s, 1, 2, s... /* ERROR can only use ... with matching parameter */ )
_ = append(s, 1, 2 /* ERROR too many arguments */ , s... )
_ = append([]interface{}(nil), 1, 2, "foo", x, 3.1425, false)
type S []byte

View File

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

View File

@ -86,7 +86,7 @@ func assignments1() {
g := func(int, bool){}
var m map[int]int
g(m[0]) /* ERROR "too few arguments" */
g(m[0]) /* ERROR "not enough arguments" */
// assignments to _
_ = nil /* ERROR "use of untyped nil" */

113
src/go/types/testdata/typeparams.src vendored Normal file
View File

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

View File

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

View File

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

View File

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