go/parser: accept pointer designation for type parameters in contracts

Change-Id: Ib119b7cd22de7117db82b987a3cd4573cbb6f6c8
This commit is contained in:
Robert Griesemer 2020-03-31 22:00:51 -07:00
parent 29365a1738
commit 49c8636998
3 changed files with 25 additions and 5 deletions

View File

@ -921,12 +921,16 @@ type (
type Constraint struct {
// Invariant: Param == nil || len(MNames) == len(Types)
// MNames entries will be nil if we have a list of types
Param *Ident // constrained type parameter; or nil (for embedded contracts)
MNames []*Ident // list of method names; or nil (for embedded contracts or type constraints)
Types []Expr // embedded contract (single *CallExpr), list of types, or list of method signatures (*FuncType)
Star token.Pos // position of "*"; or token.NoPos if not present
Param *Ident // constrained type parameter; or nil (for embedded contracts)
MNames []*Ident // list of method names; or nil (for embedded contracts or type constraints)
Types []Expr // embedded contract (single *CallExpr), list of types, or list of method signatures (*FuncType)
}
func (c *Constraint) Pos() token.Pos {
if c.Star.IsValid() {
return c.Star
}
if c.Param != nil {
return c.Param.Pos()
}

View File

@ -2582,7 +2582,7 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token
return spec
}
// Constraint = TypeParam TypeOrMethod { "," TypeOrMethod } | ContractName "(" [ TypeList [ "," ] ] ")" .
// Constraint = [ "*" ] TypeParam TypeOrMethod { "," TypeOrMethod } | ContractName "(" [ TypeList [ "," ] ] ")" .
// TypeParam = Ident .
// TypeOrMethod = Type | MethodName Signature .
// ContractName = TypeName.
@ -2598,9 +2598,18 @@ func (p *parser) parseConstraint() *ast.Constraint {
return &ast.Constraint{Types: []ast.Expr{p.parseType(true)}}
}
var star token.Pos
if p.tok == token.MUL {
star = p.pos
p.next()
}
tname := p.parseTypeName(nil)
if p.tok == token.LPAREN {
// ContractName "(" [ TypeList [ "," ] ] ")"
if star.IsValid() {
p.error(star, "pointer type requires a method")
}
return &ast.Constraint{Types: []ast.Expr{p.parseTypeInstance(tname)}}
}
@ -2627,6 +2636,10 @@ func (p *parser) parseConstraint() *ast.Constraint {
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}
} else if star.IsValid() {
// type with (invalid) starred type parameter
p.error(star, "pointer type requires a method")
star = token.NoPos // suppress further errors
}
mnames = append(mnames, mname)
types = append(types, typ)
@ -2638,7 +2651,7 @@ func (p *parser) parseConstraint() *ast.Constraint {
}
// param != nil
return &ast.Constraint{Param: param, MNames: mnames, Types: types}
return &ast.Constraint{Star: star, Param: param, MNames: mnames, Types: types}
}
// ContractSpec = ident "(" [ IdentList [ "," ] ] ")" "{" { Constraint ";" } "}" .

View File

@ -98,6 +98,7 @@ var valids = []string{
`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; contract C(T){ *T m() }`,
`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`,
@ -179,6 +180,8 @@ var invalids = []string{
// contracts
`package p; contract C(T, T /* ERROR "T redeclared" */ ) {}`,
`package p; contract C(T) { imported /* ERROR "expected type parameter name" */ .T int }`,
`package p; contract C(T) { * /* ERROR "requires a method" */ C(T) }`,
`package p; contract C(T) { * /* ERROR "requires a method" */ T int }`,
`package p; func _() { contract /* ERROR "cannot be inside function" */ C(T) { T m(); type int, float32 } }`,
// issue 8656