mirror of https://github.com/golang/go.git
go/ast, go/parser, go/printer: treat contract decl as declaration, not type (cleanup)
Progress snapshot. This change makes a contract declaration a proper declaration (as it is envisioned in the design draft). Specifically, contracts cannot be used in type position anymore, and a contract declaration is not treated as a type declaration with the type being a contract. The change has not been pushed through go/types yet; this change will temporarily brake go/types. Change-Id: Ia4034caebab07dac449a02cdd362d6ce5d61c4a3
This commit is contained in:
parent
bf01ee1864
commit
3a40a4f856
|
|
@ -192,7 +192,7 @@ func isDirective(c string) bool {
|
|||
type Field struct {
|
||||
Doc *CommentGroup // associated documentation; or nil
|
||||
Names []*Ident // field/method/(type) parameter names; or nil
|
||||
Type Expr // field/method/parameter type or contract; or nil
|
||||
Type Expr // field/method/parameter type or contract name; or nil
|
||||
Tag *BasicLit // field tag; or nil
|
||||
Comment *CommentGroup // line comments; or nil
|
||||
}
|
||||
|
|
@ -440,7 +440,7 @@ type (
|
|||
}
|
||||
|
||||
// An InterfaceType node represents an interface type.
|
||||
// The Types list is an experimental extension for interfaces that serve as type bounds (like contracts).
|
||||
// The Types list is an experimental extension for interfaces that serve as type bounds (as in contracts).
|
||||
InterfaceType struct {
|
||||
Interface token.Pos // position of "interface" keyword
|
||||
Methods *FieldList // list of methods
|
||||
|
|
@ -462,23 +462,8 @@ type (
|
|||
Dir ChanDir // channel direction
|
||||
Value Expr // value type
|
||||
}
|
||||
|
||||
// A ContractType node represents a contract.
|
||||
ContractType struct {
|
||||
Contract token.Pos // position of "contract" pseudo keyword
|
||||
TParams []*Ident // list of (incoming) type parameters; or nil
|
||||
Lbrace token.Pos // position of "{"
|
||||
Constraints []*Constraint // list of constraints
|
||||
Rbrace token.Pos // position of "}"
|
||||
}
|
||||
)
|
||||
|
||||
type Constraint struct {
|
||||
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)
|
||||
}
|
||||
|
||||
// Pos and End implementations for expression/type nodes.
|
||||
|
||||
func (x *BadExpr) Pos() token.Pos { return x.From }
|
||||
|
|
@ -513,7 +498,6 @@ func (x *FuncType) Pos() token.Pos {
|
|||
func (x *InterfaceType) Pos() token.Pos { return x.Interface }
|
||||
func (x *MapType) Pos() token.Pos { return x.Map }
|
||||
func (x *ChanType) Pos() token.Pos { return x.Begin }
|
||||
func (x *ContractType) Pos() token.Pos { return x.Contract }
|
||||
|
||||
func (x *BadExpr) End() token.Pos { return x.To }
|
||||
func (x *Ident) End() token.Pos { return token.Pos(int(x.NamePos) + len(x.Name)) }
|
||||
|
|
@ -547,7 +531,6 @@ func (x *FuncType) End() token.Pos {
|
|||
func (x *InterfaceType) End() token.Pos { return x.Methods.End() }
|
||||
func (x *MapType) End() token.Pos { return x.Value.End() }
|
||||
func (x *ChanType) End() token.Pos { return x.Value.End() }
|
||||
func (x *ContractType) End() token.Pos { return x.Rbrace }
|
||||
|
||||
// exprNode() ensures that only expression/type nodes can be
|
||||
// assigned to an Expr.
|
||||
|
|
@ -575,7 +558,6 @@ func (*FuncType) exprNode() {}
|
|||
func (*InterfaceType) exprNode() {}
|
||||
func (*MapType) exprNode() {}
|
||||
func (*ChanType) exprNode() {}
|
||||
func (*ContractType) exprNode() {}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Convenience functions for Idents
|
||||
|
|
@ -921,8 +903,25 @@ type (
|
|||
Type Expr // *Ident, *ParenExpr, *SelectorExpr, *StarExpr, or any of the *XxxTypes
|
||||
Comment *CommentGroup // line comments; or nil
|
||||
}
|
||||
|
||||
// A ContractSpec node represents a contract declaration.
|
||||
ContractSpec struct {
|
||||
Doc *CommentGroup // associated documentation; or nil
|
||||
Name *Ident // contract name
|
||||
TParams []*Ident // list of (incoming) type parameters; or nil
|
||||
Lbrace token.Pos // position of "{"
|
||||
Constraints []*Constraint // list of constraints
|
||||
Rbrace token.Pos // position of "}"
|
||||
Comment *CommentGroup // line comments; or nil
|
||||
}
|
||||
)
|
||||
|
||||
type Constraint struct {
|
||||
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)
|
||||
}
|
||||
|
||||
// Pos and End implementations for spec nodes.
|
||||
|
||||
func (s *ImportSpec) Pos() token.Pos {
|
||||
|
|
@ -931,8 +930,9 @@ func (s *ImportSpec) Pos() token.Pos {
|
|||
}
|
||||
return s.Path.Pos()
|
||||
}
|
||||
func (s *ValueSpec) Pos() token.Pos { return s.Names[0].Pos() }
|
||||
func (s *TypeSpec) Pos() token.Pos { return s.Name.Pos() }
|
||||
func (s *ValueSpec) Pos() token.Pos { return s.Names[0].Pos() }
|
||||
func (s *TypeSpec) Pos() token.Pos { return s.Name.Pos() }
|
||||
func (s *ContractSpec) Pos() token.Pos { return s.Name.Pos() }
|
||||
|
||||
func (s *ImportSpec) End() token.Pos {
|
||||
if s.EndPos != 0 {
|
||||
|
|
@ -950,14 +950,16 @@ func (s *ValueSpec) End() token.Pos {
|
|||
}
|
||||
return s.Names[len(s.Names)-1].End()
|
||||
}
|
||||
func (s *TypeSpec) End() token.Pos { return s.Type.End() }
|
||||
func (s *TypeSpec) End() token.Pos { return s.Type.End() }
|
||||
func (s *ContractSpec) End() token.Pos { return s.Rbrace }
|
||||
|
||||
// specNode() ensures that only spec nodes can be
|
||||
// assigned to a Spec.
|
||||
//
|
||||
func (*ImportSpec) specNode() {}
|
||||
func (*ValueSpec) specNode() {}
|
||||
func (*TypeSpec) specNode() {}
|
||||
func (*ImportSpec) specNode() {}
|
||||
func (*ValueSpec) specNode() {}
|
||||
func (*TypeSpec) specNode() {}
|
||||
func (*ContractSpec) specNode() {}
|
||||
|
||||
// A declaration is represented by one of the following declaration nodes.
|
||||
//
|
||||
|
|
@ -984,7 +986,7 @@ type (
|
|||
GenDecl struct {
|
||||
Doc *CommentGroup // associated documentation; or nil
|
||||
TokPos token.Pos // position of Tok
|
||||
Tok token.Token // IMPORT, CONST, TYPE, VAR
|
||||
Tok token.Token // IMPORT, CONST, TYPE, VAR, or IDENT (for "contract" pseudo keyword)
|
||||
Lparen token.Pos // position of '(', if any
|
||||
Specs []Spec
|
||||
Rparen token.Pos // position of ')', if any
|
||||
|
|
|
|||
|
|
@ -1298,37 +1298,6 @@ func (p *parser) parseChanType(typeContext bool) *ast.ChanType {
|
|||
return &ast.ChanType{Begin: pos, Arrow: arrow, Dir: dir, Value: value}
|
||||
}
|
||||
|
||||
// ContractType = "(" [ IdentList [ "," ] ] ")" "{" { Constraint ";" } "}" .
|
||||
// (The "contract" keyword is already consumed.)
|
||||
func (p *parser) parseContractType(pos token.Pos) *ast.ContractType {
|
||||
if p.trace {
|
||||
defer un(trace(p, "ContractType"))
|
||||
}
|
||||
|
||||
var params []*ast.Ident
|
||||
p.expect(token.LPAREN)
|
||||
scope := ast.NewScope(nil) // contract scope
|
||||
for p.tok != token.RPAREN && p.tok != token.EOF {
|
||||
params = append(params, p.parseIdent())
|
||||
if !p.atComma("contract parameter list", token.RPAREN) {
|
||||
break
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
p.declare(nil, nil, scope, ast.Typ, params...)
|
||||
p.expect(token.RPAREN)
|
||||
|
||||
var constraints []*ast.Constraint
|
||||
lbrace := p.expect(token.LBRACE)
|
||||
for p.tok != token.RBRACE && p.tok != token.EOF {
|
||||
constraints = append(constraints, p.parseConstraint())
|
||||
p.expectSemi()
|
||||
}
|
||||
rbrace := p.expect(token.RBRACE)
|
||||
|
||||
return &ast.ContractType{Contract: pos, TParams: params, Lbrace: lbrace, Constraints: constraints, Rbrace: rbrace}
|
||||
}
|
||||
|
||||
// Constraint = TypeParam TypeOrMethod { "," TypeOrMethod } | ContractTypeName "(" [ TypeList [ "," ] ] ")" .
|
||||
// TypeParam = Ident .
|
||||
// TypeOrMethod = Type | MethodName Signature .
|
||||
|
|
@ -1416,13 +1385,6 @@ func (p *parser) parseTypeInstance(typ ast.Expr) *ast.CallExpr {
|
|||
func (p *parser) tryIdentOrType(typeContext bool) ast.Expr {
|
||||
switch p.tok {
|
||||
case token.IDENT:
|
||||
// TODO(gri) we need to be smarter about this to avoid problems with existing code
|
||||
if p.lit == "contract" {
|
||||
pos := p.pos
|
||||
p.next()
|
||||
typ := p.parseContractType(pos)
|
||||
return typ
|
||||
}
|
||||
typ := p.parseTypeName(nil)
|
||||
if typeContext && (useBrackets && p.tok == token.LBRACK || p.tok == token.LPAREN) {
|
||||
typ = p.parseTypeInstance(typ)
|
||||
|
|
@ -2846,6 +2808,7 @@ func (p *parser) parseTypeSpec(doc *ast.CommentGroup, _ token.Pos, _ token.Token
|
|||
return spec
|
||||
}
|
||||
|
||||
// ContractType = ident "(" [ IdentList [ "," ] ] ")" "{" { Constraint ";" } "}" .
|
||||
func (p *parser) parseContractSpec(doc *ast.CommentGroup, pos token.Pos, _ token.Token, _ int) ast.Spec {
|
||||
if p.trace {
|
||||
defer un(trace(p, "ContractSpec"))
|
||||
|
|
@ -2854,10 +2817,31 @@ func (p *parser) parseContractSpec(doc *ast.CommentGroup, pos token.Pos, _ token
|
|||
// For now we represent a contract specification like a type representation.
|
||||
// They cannot have "outer" type parameters, though.
|
||||
ident := p.parseIdent()
|
||||
typ := p.parseContractType(pos)
|
||||
spec := &ast.TypeSpec{Doc: doc, Name: ident, Type: typ}
|
||||
p.declare(spec, nil, p.topScope, ast.Typ, ident)
|
||||
p.expectSemi() // call before accessing p.linecomment
|
||||
|
||||
var tparams []*ast.Ident
|
||||
p.expect(token.LPAREN)
|
||||
scope := ast.NewScope(nil) // contract scope
|
||||
for p.tok != token.RPAREN && p.tok != token.EOF {
|
||||
tparams = append(tparams, p.parseIdent())
|
||||
if !p.atComma("contract parameter list", token.RPAREN) {
|
||||
break
|
||||
}
|
||||
p.next()
|
||||
}
|
||||
p.declare(nil, nil, scope, ast.Typ, tparams...) // TODO(gri) should really be something other that ast.Typ
|
||||
p.expect(token.RPAREN)
|
||||
|
||||
var constraints []*ast.Constraint
|
||||
lbrace := p.expect(token.LBRACE)
|
||||
for p.tok != token.RBRACE && p.tok != token.EOF {
|
||||
constraints = append(constraints, p.parseConstraint())
|
||||
p.expectSemi()
|
||||
}
|
||||
rbrace := p.expect(token.RBRACE)
|
||||
|
||||
spec := &ast.ContractSpec{Doc: doc, Name: ident, TParams: tparams, Lbrace: lbrace, Constraints: constraints, Rbrace: rbrace}
|
||||
p.declare(spec, nil, p.topScope, ast.Typ, ident) // TODO(gri) should really be something other that ast.Typ
|
||||
p.expectSemi() // call before accessing p.linecomment
|
||||
spec.Comment = p.lineComment
|
||||
|
||||
return spec
|
||||
|
|
|
|||
|
|
@ -90,11 +90,8 @@ var valids = []string{
|
|||
}`,
|
||||
`package p; contract C(T){ T m(int), n() float64, o(x string) rune }`,
|
||||
`package p; contract C(T){ T int, m(int), float64, n() float64, o(x string) rune }`,
|
||||
`package p; type C contract(){}`,
|
||||
`package p; type C contract(T, S, R,){}`,
|
||||
`package p; type C contract(T){ T (m(x, int)); }`,
|
||||
`package p; type C contract(T){ T int; T imported.T; T chan<-int; T m(x int) float64; C0(); imported.C1(int, T,) }`,
|
||||
`package p; type C contract(T){ T int, imported.T, chan<-int; T m(x int) float64; C0(); imported.C1(int, T,) }`,
|
||||
`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; func _(type T1, T2 interface{})(x T1) T2`,
|
||||
`package p; func _(type T1 interface{ m() }, T2, T3 interface{})(x T1, y T3) T2`,
|
||||
|
||||
|
|
@ -162,8 +159,8 @@ var invalids = []string{
|
|||
`package p; func f() { defer func() {} /* ERROR HERE "function must be invoked" */ }`,
|
||||
`package p; func f() { go func() { func() { f(x func /* ERROR "missing ','" */ (){}) } } }`,
|
||||
//`package p; func f(x func(), u v func /* ERROR "missing ','" */ ()){}`,
|
||||
`package p; type C contract(T, T /* ERROR "T redeclared" */ ) {}`,
|
||||
`package p; type C contract(T) { imported /* ERROR "expected type parameter name" */ .T int }`,
|
||||
`package p; contract C(T, T /* ERROR "T redeclared" */ ) {}`,
|
||||
`package p; contract C(T) { imported /* ERROR "expected type parameter name" */ .T int }`,
|
||||
|
||||
// issue 8656
|
||||
`package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`,
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ contract Complex(T) {
|
|||
// ordered numeric types.
|
||||
type OrderedAbs(type T OrderedNumeric) T
|
||||
|
||||
func (a OrderedAbs(T)) Abs() T {
|
||||
func (a OrderedAbs(T)) Abs() OrderedAbs(T) {
|
||||
if a < 0 {
|
||||
return -a
|
||||
}
|
||||
|
|
|
|||
|
|
@ -971,23 +971,6 @@ func (p *printer) expr1(expr ast.Expr, prec1, depth int) {
|
|||
p.print(blank)
|
||||
p.expr(x.Value)
|
||||
|
||||
case *ast.ContractType:
|
||||
if p.mode&noContractKeyword == 0 {
|
||||
p.print(&ast.Ident{NamePos: x.Contract, Name: "contract"})
|
||||
}
|
||||
p.print(token.LPAREN)
|
||||
for i, par := range x.TParams {
|
||||
if i > 0 {
|
||||
p.print(token.COMMA, blank)
|
||||
}
|
||||
p.print(par)
|
||||
}
|
||||
p.print(token.RPAREN, x.Lbrace, token.LBRACE)
|
||||
for _, c := range x.Constraints {
|
||||
p.constraint(c)
|
||||
}
|
||||
p.print(x.Rbrace, token.RBRACE)
|
||||
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
|
@ -1633,6 +1616,23 @@ func (p *printer) spec(spec ast.Spec, n int, doIndent bool) {
|
|||
p.expr(s.Type)
|
||||
p.setComment(s.Comment)
|
||||
|
||||
case *ast.ContractSpec:
|
||||
p.setComment(s.Doc)
|
||||
p.expr(s.Name)
|
||||
p.print(token.LPAREN)
|
||||
for i, par := range s.TParams {
|
||||
if i > 0 {
|
||||
p.print(token.COMMA, blank)
|
||||
}
|
||||
p.print(par)
|
||||
}
|
||||
p.print(token.RPAREN, s.Lbrace, token.LBRACE)
|
||||
for _, c := range s.Constraints {
|
||||
p.constraint(c)
|
||||
}
|
||||
p.print(s.Rbrace, token.RBRACE)
|
||||
p.setComment(s.Comment)
|
||||
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
|
@ -1643,8 +1643,7 @@ func (p *printer) genDecl(d *ast.GenDecl) {
|
|||
// Contract declarations rely on the pseudo-keyword (identifier) "contract";
|
||||
// in the AST the respective token is ast.IDENT. Catch and correct this here.
|
||||
if d.Tok == token.IDENT {
|
||||
p.print(&ast.Ident{NamePos: d.Pos(), Name: "contract"}, noContractKeyword)
|
||||
defer p.print(noContractKeyword)
|
||||
p.print(&ast.Ident{NamePos: d.Pos(), Name: "contract"})
|
||||
} else {
|
||||
p.print(d.Pos(), d.Tok)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,9 +38,8 @@ const (
|
|||
type pmode int
|
||||
|
||||
const (
|
||||
noExtraBlank pmode = 1 << iota // disables extra blank after /*-style comment
|
||||
noExtraLinebreak // disables extra line break after /*-style comment
|
||||
noContractKeyword // disables printing of "contract" pseudo-keyword when printing a contract type
|
||||
noExtraBlank pmode = 1 << iota // disables extra blank after /*-style comment
|
||||
noExtraLinebreak // disables extra line break after /*-style comment
|
||||
)
|
||||
|
||||
type commentInfo struct {
|
||||
|
|
|
|||
Loading…
Reference in New Issue