go/*: move TParams field into ast.FuncType and adjust dependencies

This step further consolidates all parameter types (except for the
receiver) in an ast.FuncType which now matches more closely the
representation of a types.Signature. As a result, fewer parameters
need to passed around because we can just use an *ast.FuncType or
a *types.Signature instead.

As an immediate (and implicit) consequence, parameterized interface
methods now type-check. (But we cannot yet "implement" them with a
matching concrete type.)

Change-Id: I2ea24694ade9838488625ffec48d5e98070d1006
This commit is contained in:
Robert Griesemer 2020-01-06 14:02:11 -08:00
parent e3a0a7429a
commit 32c4c5a5f8
7 changed files with 52 additions and 42 deletions

View File

@ -435,6 +435,7 @@ type (
// A FuncType node represents a function type.
FuncType struct {
Func token.Pos // position of "func" keyword (token.NoPos if there is no "func")
TParams *FieldList // type parameters; or nil
Params *FieldList // (incoming) parameters; non-nil
Results *FieldList // (outgoing) results; or nil
}
@ -994,12 +995,11 @@ 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
TParams *FieldList // 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
Doc *CommentGroup // associated documentation; or nil
Recv *FieldList // receiver (methods); or nil (functions)
Name *Ident // function/method name
Type *FuncType // function signature: type and value parameters, results, and position of "func" keyword
Body *BlockStmt // function body; or nil for external (non-Go) function
}
)

View File

@ -1134,9 +1134,9 @@ func (p *parser) parseMethodSpec(scope *ast.Scope) *ast.Field {
// method
idents = []*ast.Ident{ident}
scope := ast.NewScope(nil) // method scope
_, params := p.parseParameters(scope, methodTypeParamsOk|variadicOk, "method")
tparams, params := p.parseParameters(scope, methodTypeParamsOk|variadicOk, "method")
results := p.parseResult(scope, true)
typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results}
typ = &ast.FuncType{Func: token.NoPos, TParams: tparams, Params: params, Results: results}
} else {
// embedded interface
typ = x
@ -1256,9 +1256,9 @@ func (p *parser) parseConstraint() *ast.Constraint {
// method
mname = ident
scope := ast.NewScope(nil) // method scope
_, params := p.parseParameters(scope, methodTypeParamsOk|variadicOk, "method")
tparams, params := p.parseParameters(scope, methodTypeParamsOk|variadicOk, "method")
results := p.parseResult(scope, true)
typ = &ast.FuncType{Func: token.NoPos, Params: params, Results: results}
typ = &ast.FuncType{Func: token.NoPos, TParams: tparams, Params: params, Results: results}
}
mnames = append(mnames, mname)
types = append(types, typ)
@ -2807,12 +2807,12 @@ func (p *parser) parseFuncDecl() *ast.FuncDecl {
}
decl := &ast.FuncDecl{
Doc: doc,
Recv: recv,
Name: ident,
TParams: tparams,
Doc: doc,
Recv: recv,
Name: ident,
Type: &ast.FuncType{
Func: pos,
TParams: tparams,
Params: params,
Results: results,
},

View File

@ -384,22 +384,26 @@ func (p *printer) parameters(isTypeParam bool, fields *ast.FieldList) {
p.print(fields.Closing, token.RPAREN)
}
func (p *printer) signature(params, result *ast.FieldList) {
if params != nil {
p.parameters(false, params)
func (p *printer) signature(sig *ast.FuncType) {
if sig.TParams != nil {
p.parameters(true, sig.TParams)
}
if sig.Params != nil {
p.parameters(false, sig.Params)
} else {
p.print(token.LPAREN, token.RPAREN)
}
n := result.NumFields()
res := sig.Results
n := res.NumFields()
if n > 0 {
// result != nil
// res != nil
p.print(blank)
if n == 1 && result.List[0].Names == nil {
// single anonymous result; no ()'s
p.expr(stripParensAlways(result.List[0].Type))
if n == 1 && res.List[0].Names == nil {
// single anonymous res; no ()'s
p.expr(stripParensAlways(res.List[0].Type))
return
}
p.parameters(false, result)
p.parameters(false, res)
}
}
@ -472,7 +476,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
// method
p.expr(f.Names[0])
p.signature(ftyp.Params, ftyp.Results)
p.signature(ftyp)
} else {
// embedded interface
p.expr(f.Type)
@ -549,7 +553,7 @@ func (p *printer) fieldList(fields *ast.FieldList, isStruct, isIncomplete bool)
if ftyp, isFtyp := f.Type.(*ast.FuncType); isFtyp {
// method
p.expr(f.Names[0])
p.signature(ftyp.Params, ftyp.Results)
p.signature(ftyp)
} else {
// embedded interface
p.expr(f.Type)
@ -802,7 +806,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
p.print(x.Type.Pos(), token.FUNC)
// See the comment in funcDecl about how the header size is computed.
startCol := p.out.Column - len("func")
p.signature(x.Type.Params, x.Type.Results)
p.signature(x.Type)
p.funcBody(p.distanceFrom(x.Type.Pos(), startCol), blank, x.Body)
case *ast.ParenExpr:
@ -947,7 +951,7 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
case *ast.FuncType:
p.print(token.FUNC)
p.signature(x.Params, x.Results)
p.signature(x)
case *ast.InterfaceType:
p.print(token.INTERFACE)
@ -1075,7 +1079,7 @@ func (p *printer) constraint(x *ast.Constraint) {
for i, m := range x.MNames {
p.print(m)
t := x.Types[i].(*ast.FuncType)
p.signature(t.Params, t.Results)
p.signature(t)
//p.print(token.COMMA)
}
}
@ -1821,10 +1825,7 @@ func (p *printer) funcDecl(d *ast.FuncDecl) {
p.print(blank)
}
p.expr(d.Name)
if d.TParams != nil {
p.parameters(true, d.TParams)
}
p.signature(d.Type.Params, d.Type.Results)
p.signature(d.Type)
p.funcBody(p.distanceFrom(d.Pos(), startCol), vtab, d.Body)
}

View File

@ -770,7 +770,7 @@ func (check *Checker) funcDecl(obj *Func, decl *declInfo) {
saved := obj.color_
obj.color_ = black
fdecl := decl.fdecl
check.funcType(sig, fdecl.Recv, fdecl.TParams, fdecl.Type)
check.funcType(sig, fdecl.Recv, fdecl.Type)
obj.color_ = saved
// function body must be type-checked after global declarations

View File

@ -415,8 +415,8 @@ func (check *Checker) collectObjects() {
// treat as function
}
if name == "init" {
if d.TParams != nil {
check.softErrorf(d.TParams.Pos(), "func init must have no type parameters")
if d.Type.TParams != nil {
check.softErrorf(d.Type.TParams.Pos(), "func init must have no type parameters")
}
if t := d.Type; t.Params.NumFields() != 0 || t.Results != nil {
check.softErrorf(d.Pos(), "func init must have no arguments and no return values")
@ -435,8 +435,8 @@ func (check *Checker) collectObjects() {
} else {
// method
// d.Recv != nil && len(d.Recv.List) > 0
if !methodTypeParamsOk && d.TParams != nil {
check.invalidAST(d.TParams.Pos(), "method must have no type parameters")
if !methodTypeParamsOk && d.Type.TParams != nil {
check.invalidAST(d.Type.TParams.Pos(), "method must have no type parameters")
}
ptr, recv, _ := check.unpackRecv(d.Recv.List[0].Type, false)
// (Methods with invalid receiver cannot be associated to a type, and

View File

@ -4,4 +4,13 @@
package p
type S(type T) struct{}; func (S) _()
type I interface{
m(type T)(T)
}
type S struct{}
func (S) m(type T)(T)
// TODO(gri) this should work
var _ I = S /* ERROR wrong type for method m */ {}

View File

@ -164,7 +164,7 @@ func (check *Checker) genericType(e ast.Expr, reportErr bool) Type {
}
// funcType type-checks a function or method type.
func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftparams *ast.FieldList, ftyp *ast.FuncType) {
func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftyp *ast.FuncType) {
check.openScope(ftyp, "function")
check.scope.isFunc = true
check.recordScope(ftyp, check.scope)
@ -214,8 +214,8 @@ func (check *Checker) funcType(sig *Signature, recvPar *ast.FieldList, ftparams
}
}
if ftparams != nil {
sig.tparams = check.collectTypeParams(ftparams)
if ftyp.TParams != nil {
sig.tparams = check.collectTypeParams(ftyp.TParams)
}
// Value (non-type) parameters' scope starts in the function body. Use a temporary scope for their
@ -391,7 +391,7 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) (T Type) {
case *ast.FuncType:
typ := new(Signature)
def.setUnderlying(typ)
check.funcType(typ, nil, nil, e)
check.funcType(typ, nil, e)
return typ
case *ast.InterfaceType: