diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index 56a1e6b09c..6eeb9df96f 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -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 diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go index abca95fc4d..8fa7d4a28a 100644 --- a/src/go/parser/short_test.go +++ b/src/go/parser/short_test.go @@ -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`, diff --git a/src/go/types/contracts.go b/src/go/types/contracts.go index 727c0de781..817368efb7 100644 --- a/src/go/types/contracts.go +++ b/src/go/types/contracts.go @@ -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 } diff --git a/src/go/types/examples/contracts.go2 b/src/go/types/examples/contracts.go2 index 0ea35aa61b..7387e9d77c 100644 --- a/src/go/types/examples/contracts.go2 +++ b/src/go/types/examples/contracts.go2 @@ -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) +}