diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go index d888785ed2..b595573851 100644 --- a/src/go/ast/ast.go +++ b/src/go/ast/ast.go @@ -474,7 +474,7 @@ type ( 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 types (*FuncType) + Types []Expr // embedded contract (single *CallExpr), list of types, or list of method signatures (*FuncType) } // Pos and End implementations for expression/type nodes. diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index e5bcf17cc0..ec15a7a3c4 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -1098,21 +1098,22 @@ func (p *parser) parseTypeParams(scope *ast.Scope) *ast.FieldList { p.expect(token.TYPE) } - f := new(ast.Field) - if p.tok != token.RBRACK && p.tok != token.RPAREN { - f.Names = p.parseIdentList() + fields := p.parseParameterList(scope, false) + // determine which form we have (list of type parameters with optional + // contract, or type parameters, all with interfaces as type bounds) + for _, f := range fields { + if len(f.Names) == 0 { + assert(f.Type != nil, "expected non-nil type") + f.Names = []*ast.Ident{f.Type.(*ast.Ident)} + f.Type = nil + } } - if p.tok == token.IDENT { - // contract - f.Type = p.parseTypeName(nil) - } - p.declare(f, nil, scope, ast.Typ, f.Names...) if lbrack.IsValid() { rbrack = p.expect(token.RBRACK) } - return &ast.FieldList{Opening: lbrack, List: []*ast.Field{f}, Closing: rbrack} + return &ast.FieldList{Opening: lbrack, List: fields, Closing: rbrack} } func (p *parser) parseParameters(scope *ast.Scope, typeParamsOk, ellipsisOk bool) (tparams, params *ast.FieldList) { diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go index e8861d6ab2..461434eaca 100644 --- a/src/go/parser/short_test.go +++ b/src/go/parser/short_test.go @@ -51,7 +51,7 @@ var valids = []string{ `package p; type T (*int)`, `package p; type T(type P) struct { P }`, `package p; type T(type P comparable) struct { P }`, - // `package p; type T(type P comparable(P)) struct { P }`, + `package p; type T(type P comparable(P)) struct { P }`, `package p; type T(type P1, P2) struct { P1; f []P2 }`, // `package p; type T[type] struct { P }`, // `package p; type T[type P] struct { P }`, @@ -68,7 +68,7 @@ var valids = []string{ `package p; func _((T(P1, P2, P3)))`, `package p; func _(type A, B)(a A) B`, `package p; func _(type A, B C)(a A) B`, - // `package p; func _(type A, B C(A, B))(a A) B`, + `package p; func _(type A, B C(A, B))(a A) B`, // `package p; type _ struct { T[P] }`, // `package p; type _ struct { T []E }`, // `package p; type _ struct { T [P]E }`, @@ -95,6 +95,8 @@ var valids = []string{ `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; func _(type T1, T2 interface{})(x T1) T2`, + `package p; func _(type T1 interface{ m() }, T2, T3 interface{})(x T1, y T3) T2`, } func TestValid(t *testing.T) { diff --git a/src/go/types/check_test.go b/src/go/types/check_test.go index baa3b30d00..ec28ae0e40 100644 --- a/src/go/types/check_test.go +++ b/src/go/types/check_test.go @@ -111,6 +111,11 @@ var tests = [][]string{ {"testdata/chans.go2"}, {"testdata/map.go2"}, {"testdata/map2.go2"}, + + // Go 2 prototype examples + // {"examples/contracts.go2"}, // TODO(gri) enable + {"examples/functions.go2"}, + {"examples/types.go2"}, } var fset = token.NewFileSet() diff --git a/src/go/types/contracts.go b/src/go/types/contracts.go index d26e1b3388..e2bee0e106 100644 --- a/src/go/types/contracts.go +++ b/src/go/types/contracts.go @@ -26,9 +26,8 @@ func (check *Checker) contractType(contr *Contract, e *ast.ContractType) { tparams[index] = tpar } - // each type parameter's constraints are represented by an interface + // each type parameter's constraints are represented by a (lazily allocated) interface ifaces := make(map[*TypeName]*Interface) - ifaceFor := func(tpar *TypeName) *Interface { iface := ifaces[tpar] if iface == nil { diff --git a/src/go/types/examples/contracts.go2 b/src/go/types/examples/contracts.go2 index cd199f0c4c..e71c1d08d3 100644 --- a/src/go/types/examples/contracts.go2 +++ b/src/go/types/examples/contracts.go2 @@ -47,3 +47,16 @@ func New (type Node, Edge G) (nodes []Node) *Graph(Node, Edge) { panic("unimplem func (g *Graph(Node, Edge)) ShortestPath(from, to Node) []Edge { panic("unimplemented") } +// Same Graph using interface bounds instead of a contract. + +/* +type AltGraph (type Node NodeFace(Edge), Edge EdgeFace(Node)) struct { } + +type NodeFace(type Edge) interface { + Edges() []Edge +} + +type EdgeFace(type Node) interface { + Nodes() (from, to Node) +} +*/ \ No newline at end of file diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 0d183daf06..15ee13aca4 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -273,6 +273,7 @@ func (check *Checker) typInternal(e ast.Expr, def *Named) Type { } case *ast.CallExpr: + // We may have a parameterized type or an "instantiated" contract. typ := new(Parameterized) def.setUnderlying(typ) if check.parameterizedType(typ, e) {