mirror of https://github.com/golang/go.git
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:
parent
bd486c39ba
commit
fd16d941dc
|
|
@ -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
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{}
|
||||
|
|
|
|||
|
|
@ -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{}
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ var tests = [][]string{
|
|||
{"testdata/issue23203b.src"},
|
||||
{"testdata/issue28251.src"},
|
||||
{"testdata/issue6977.src"},
|
||||
{"testdata/typeparams.src"},
|
||||
}
|
||||
|
||||
var fset = token.NewFileSet()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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" */ ()...)
|
||||
|
||||
|
|
|
|||
|
|
@ -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" */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue