mirror of https://github.com/golang/go.git
go/parser, go/types: permit parentheses around embedded contracts
For symmetry with embedding in structs and interfaces. Fixed an incorrect error message print in the process. Change-Id: I295685438a22971edc610e6c51cfefd286eaffba
This commit is contained in:
parent
5cc90acd4b
commit
86c2daf9e9
|
|
@ -721,7 +721,8 @@ func (p *parser) parseFieldDecl(scope *ast.Scope) *ast.Field {
|
|||
typ = p.parseType(true)
|
||||
}
|
||||
} else {
|
||||
// embedded type
|
||||
// embedded, possibly parameterized type
|
||||
// (using the enclosing parentheses to distinguish it from a named field declaration)
|
||||
typ = p.parseType(true)
|
||||
}
|
||||
|
||||
|
|
@ -1138,54 +1139,6 @@ func (p *parser) parseChanType(typeContext bool) *ast.ChanType {
|
|||
return &ast.ChanType{Begin: pos, Arrow: arrow, Dir: dir, Value: value}
|
||||
}
|
||||
|
||||
// Constraint = TypeParam TypeOrMethod { "," TypeOrMethod } | ContractTypeName "(" [ TypeList [ "," ] ] ")" .
|
||||
// TypeParam = Ident .
|
||||
// TypeOrMethod = Type | MethodName Signature .
|
||||
// ContractTypeName = TypeName.
|
||||
func (p *parser) parseConstraint() *ast.Constraint {
|
||||
if p.trace {
|
||||
defer un(trace(p, "Constraint"))
|
||||
}
|
||||
|
||||
tname := p.parseTypeName(nil)
|
||||
if p.tok == token.LPAREN {
|
||||
// ContractTypeName "(" [ TypeList [ "," ] ] ")"
|
||||
return &ast.Constraint{Types: []ast.Expr{p.parseTypeInstance(tname)}}
|
||||
}
|
||||
|
||||
param, isIdent := tname.(*ast.Ident)
|
||||
if !isIdent {
|
||||
p.errorExpected(tname.Pos(), "type parameter name")
|
||||
param = &ast.Ident{NamePos: tname.Pos(), Name: "_"}
|
||||
}
|
||||
|
||||
// list of type constraints or methods
|
||||
var mnames []*ast.Ident
|
||||
var types []ast.Expr
|
||||
for {
|
||||
var mname *ast.Ident
|
||||
typ := p.parseType(false)
|
||||
if ident, isIdent := typ.(*ast.Ident); isIdent && p.tok == token.LPAREN {
|
||||
// method
|
||||
mname = ident
|
||||
scope := ast.NewScope(nil) // method scope
|
||||
tparams, params := p.parseParameters(scope, methodTypeParamsOk|variadicOk, "method")
|
||||
results := p.parseResult(scope, true)
|
||||
typ = &ast.FuncType{Func: token.NoPos, TParams: tparams, Params: params, Results: results}
|
||||
}
|
||||
mnames = append(mnames, mname)
|
||||
types = append(types, typ)
|
||||
|
||||
if p.tok != token.COMMA {
|
||||
break
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
|
||||
// param != nil
|
||||
return &ast.Constraint{Param: param, MNames: mnames, Types: types}
|
||||
}
|
||||
|
||||
func (p *parser) parseTypeInstance(typ ast.Expr) *ast.CallExpr {
|
||||
if p.trace {
|
||||
defer un(trace(p, "TypeInstantiation"))
|
||||
|
|
@ -2625,6 +2578,61 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token
|
|||
return spec
|
||||
}
|
||||
|
||||
// Constraint = TypeParam TypeOrMethod { "," TypeOrMethod } | ContractName "(" [ TypeList [ "," ] ] ")" .
|
||||
// TypeParam = Ident .
|
||||
// TypeOrMethod = Type | MethodName Signature .
|
||||
// ContractName = TypeName.
|
||||
func (p *parser) parseConstraint() *ast.Constraint {
|
||||
if p.trace {
|
||||
defer un(trace(p, "Constraint"))
|
||||
}
|
||||
|
||||
if p.tok == token.LPAREN {
|
||||
// embedded, possibly parameterized contract
|
||||
// (It's never a type but it looks like a possibly instantiated type, so
|
||||
// let's parse it as such and have the type-checker complain if need be.)
|
||||
return &ast.Constraint{Types: []ast.Expr{p.parseType(true)}}
|
||||
}
|
||||
|
||||
tname := p.parseTypeName(nil)
|
||||
if p.tok == token.LPAREN {
|
||||
// ContractName "(" [ TypeList [ "," ] ] ")"
|
||||
return &ast.Constraint{Types: []ast.Expr{p.parseTypeInstance(tname)}}
|
||||
}
|
||||
|
||||
param, isIdent := tname.(*ast.Ident)
|
||||
if !isIdent {
|
||||
p.errorExpected(tname.Pos(), "type parameter name")
|
||||
param = &ast.Ident{NamePos: tname.Pos(), Name: "_"}
|
||||
}
|
||||
|
||||
// list of type constraints or methods
|
||||
var mnames []*ast.Ident
|
||||
var types []ast.Expr
|
||||
for {
|
||||
var mname *ast.Ident
|
||||
typ := p.parseType(false)
|
||||
if ident, isIdent := typ.(*ast.Ident); isIdent && p.tok == token.LPAREN {
|
||||
// method
|
||||
mname = ident
|
||||
scope := ast.NewScope(nil) // method scope
|
||||
tparams, params := p.parseParameters(scope, methodTypeParamsOk|variadicOk, "method")
|
||||
results := p.parseResult(scope, true)
|
||||
typ = &ast.FuncType{Func: token.NoPos, TParams: tparams, Params: params, Results: results}
|
||||
}
|
||||
mnames = append(mnames, mname)
|
||||
types = append(types, typ)
|
||||
|
||||
if p.tok != token.COMMA {
|
||||
break
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
|
||||
// param != nil
|
||||
return &ast.Constraint{Param: param, MNames: mnames, Types: types}
|
||||
}
|
||||
|
||||
// ContractSpec = ident "(" [ IdentList [ "," ] ] ")" "{" { Constraint ";" } "}" .
|
||||
func (p *parser) parseContractSpec(doc *ast.CommentGroup, pos token.Pos, keyword token.Token, _ int) ast.Spec {
|
||||
if p.trace {
|
||||
|
|
@ -2643,7 +2651,7 @@ func (p *parser) parseContractSpec(doc *ast.CommentGroup, pos token.Pos, keyword
|
|||
}
|
||||
p.next()
|
||||
}
|
||||
p.declare(nil, nil, scope, ast.Typ, tparams...) // TODO(gri) should really be something other that ast.Typ
|
||||
p.declare(nil, nil, scope, ast.Typ, tparams...) // this should be something other that ast.Typ but we don't care (never used)
|
||||
p.expect(token.RPAREN)
|
||||
|
||||
var constraints []*ast.Constraint
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ var valids = []string{
|
|||
`package p; contract C(T){ T int, m(int), float64, n() float64, o(x string) rune }`,
|
||||
`package p; contract C(T){ T int; T imported.T; T chan<-int; T m(x int) float64; C0(); imported.C1(int, T,) }`,
|
||||
`package p; contract C(T){ T int, imported.T, chan<-int; T m(x int) float64; C0(); imported.C1(int, T,) }`,
|
||||
`package p; contract C(T){ (C(T)); (((imported.T))) }`,
|
||||
`package p; func _(type T1, T2 interface{})(x T1) T2`,
|
||||
`package p; func _(type T1 interface{ m() }, T2, T3 interface{})(x T1, y T3) T2`,
|
||||
|
||||
|
|
|
|||
|
|
@ -111,10 +111,9 @@ func (check *Checker) contractDecl(obj *Contract, cdecl *ast.ContractSpec) {
|
|||
check.invalidAST(cdecl.Pos(), "contract contains incorrect (possibly embedded contract) entry")
|
||||
continue
|
||||
}
|
||||
// TODO(gri) we can probably get away w/o checking this (even if the AST is broken)
|
||||
econtr, _ := c.Types[0].(*ast.CallExpr)
|
||||
econtr, _ := unparen(c.Types[0]).(*ast.CallExpr)
|
||||
if econtr == nil {
|
||||
check.invalidAST(c.Types[0].Pos(), "invalid embedded contract %s", econtr)
|
||||
check.errorf(c.Types[0].Pos(), "%s is not a contract", c.Types[0])
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,3 +70,14 @@ type NodeFace(type Edge) interface {
|
|||
type EdgeFace(type Node) interface {
|
||||
Nodes() (from, to Node)
|
||||
}
|
||||
|
||||
// Contracts may embed other contracts.
|
||||
// Parentheses are permitted (for symmetry with embedding in structs and interfaces)
|
||||
// but never required.
|
||||
contract _(T) {
|
||||
Stringer(T)
|
||||
(Sequence(T))
|
||||
|
||||
Graph /* ERROR not a contract */ (T, T)
|
||||
( /* ERROR not a contract */ []int)
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue