diff --git a/src/go/ast/ast.go b/src/go/ast/ast.go index b595573851..a931d1b48b 100644 --- a/src/go/ast/ast.go +++ b/src/go/ast/ast.go @@ -440,9 +440,11 @@ type ( } // An InterfaceType node represents an interface type. + // The Types list is an experimental extension for interfaces that serve as type bounds (like contracts). InterfaceType struct { Interface token.Pos // position of "interface" keyword Methods *FieldList // list of methods + Types []Expr // list of types (TODO(gri) for now they are all lumped together, loosing syntax info) Incomplete bool // true if (source) methods are missing in the Methods list } diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go index e87757f164..3578a2f67a 100644 --- a/src/go/parser/parser.go +++ b/src/go/parser/parser.go @@ -1231,9 +1231,20 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType { pos := p.expect(token.INTERFACE) lbrace := p.expect(token.LBRACE) scope := ast.NewScope(nil) // interface scope - var list []*ast.Field - for p.tok == token.IDENT { - list = append(list, p.parseMethodSpec(scope)) + var mlist []*ast.Field + var tlist []ast.Expr +L: + for { + switch p.tok { + case token.IDENT: + mlist = append(mlist, p.parseMethodSpec(scope)) + case token.TYPE: + p.next() + tlist = append(tlist, p.parseTypeList()...) + p.expectSemi() + default: + break L + } } rbrace := p.expect(token.RBRACE) @@ -1241,9 +1252,10 @@ func (p *parser) parseInterfaceType() *ast.InterfaceType { Interface: pos, Methods: &ast.FieldList{ Opening: lbrace, - List: list, + List: mlist, Closing: rbrace, }, + Types: tlist, } } diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go index 461434eaca..b39fae7d76 100644 --- a/src/go/parser/short_test.go +++ b/src/go/parser/short_test.go @@ -97,6 +97,10 @@ var valids = []string{ `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`, + + // interfaces as contracts (experimental) + `package p; type _ interface{type int}`, + `package p; type _ interface{type int, float32; type bool; m(); type string;}`, } func TestValid(t *testing.T) { diff --git a/src/go/types/contracts.go b/src/go/types/contracts.go index 2723fd8195..c5bc9d381c 100644 --- a/src/go/types/contracts.go +++ b/src/go/types/contracts.go @@ -79,24 +79,7 @@ func (check *Checker) contractType(contr *Contract, e *ast.ContractType) { switch nmethods { case 0: // type constraints - for _, texpr := range c.Types { - if texpr == nil { - check.invalidAST(pos, "missing type constraint") - continue - } - typ := check.typ(texpr) - // A type constraint may be a predeclared type or a - // composite type composed only of predeclared types. - // TODO(gri) should we keep this restriction? - var why string - if !check.typeConstraint(typ, &why) { - check.errorf(texpr.Pos(), "invalid type constraint %s (%s)", typ, why) - continue - } - // add type - iface.types = append(iface.types, typ) - - } + iface.types = check.collectTypeConstraints(pos, iface.types, c.Types) case 1: // method constraint @@ -161,6 +144,27 @@ func (check *Checker) contractType(contr *Contract, e *ast.ContractType) { contr.IFaces = ifaces } +func (check *Checker) collectTypeConstraints(pos token.Pos, list []Type, types []ast.Expr) []Type { + for _, texpr := range types { + if texpr == nil { + check.invalidAST(pos, "missing type constraint") + continue + } + typ := check.typ(texpr) + // A type constraint may be a predeclared type or a + // composite type composed only of predeclared types. + // TODO(gri) should we keep this restriction? + var why string + if !check.typeConstraint(typ, &why) { + check.errorf(texpr.Pos(), "invalid type constraint %s (%s)", typ, why) + continue + } + // add type + list = append(list, typ) + } + return list +} + // TODO(gri) does this simply check for the absence of defined types? // (if so, should choose a better name) func (check *Checker) typeConstraint(typ Type, why *string) bool { @@ -213,7 +217,8 @@ func (check *Checker) typeConstraint(typ Type, why *string) bool { *why = check.sprintf("%s is not a type", t) return false case *TypeParam: - // ok + // TODO(gri) should this be ok? need a good use case + // ok for now default: unreachable() } diff --git a/src/go/types/exprstring.go b/src/go/types/exprstring.go index e89950e19b..c6c44b6da6 100644 --- a/src/go/types/exprstring.go +++ b/src/go/types/exprstring.go @@ -98,12 +98,7 @@ func WriteExpr(buf *bytes.Buffer, x ast.Expr) { case *ast.CallExpr: WriteExpr(buf, x.Fun) buf.WriteByte('(') - for i, arg := range x.Args { - if i > 0 { - buf.WriteString(", ") - } - WriteExpr(buf, arg) - } + writeExprList(buf, x.Args) if x.Ellipsis.IsValid() { buf.WriteString("...") } @@ -144,6 +139,13 @@ func WriteExpr(buf *bytes.Buffer, x ast.Expr) { case *ast.InterfaceType: buf.WriteString("interface{") writeFieldList(buf, x.Methods, "; ", true) + if len(x.Types) > 0 { + if len(x.Methods.List) > 0 { + buf.WriteString("; ") + } + buf.WriteString("type ") + writeExprList(buf, x.Types) + } buf.WriteByte('}') case *ast.MapType: @@ -232,3 +234,12 @@ func writeIdentList(buf *bytes.Buffer, list []*ast.Ident) { buf.WriteString(x.Name) } } + +func writeExprList(buf *bytes.Buffer, list []ast.Expr) { + for i, x := range list { + if i > 0 { + buf.WriteString(", ") + } + WriteExpr(buf, x) + } +} diff --git a/src/go/types/testdata/contracts.go2 b/src/go/types/testdata/contracts.go2 index 28af96227e..4973a9ee85 100644 --- a/src/go/types/testdata/contracts.go2 +++ b/src/go/types/testdata/contracts.go2 @@ -161,3 +161,25 @@ func _(type F, C FloatComplex)(c C) { var fim F = F(im) c = C(complex(fre, fim)) } + +// -------------------------------------------------------------------------------------- +// Parameterized interfaces as contracts + +type Int interface { + type int8, int16, int32, int64, int + type uint8, uint16, uint32, uint64, uint +} + +type Adder(type T) interface { + Add(T) T +} + +func adderSum(type T Adder)(data []T) T { + var s T + for _, x := range data { + // TODO(gri) make this work + // s = s.Add(x) + _ = x + } + return s +} diff --git a/src/go/types/typestring.go b/src/go/types/typestring.go index 4ad4bf8ffe..4b27045282 100644 --- a/src/go/types/typestring.go +++ b/src/go/types/typestring.go @@ -201,11 +201,14 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { if !empty && len(t.types) > 0 { buf.WriteString("; ") } - for i, typ := range t.types { - if i > 0 { - buf.WriteString(", ") + if len(t.types) > 0 { + buf.WriteString("type ") + for i, typ := range t.types { + if i > 0 { + buf.WriteString(", ") + } + writeType(buf, typ, qf, visited) } - writeType(buf, typ, qf, visited) empty = false } if !empty && len(t.embeddeds) > 0 { diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index 7262bdc364..e22966fc47 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -623,6 +623,9 @@ func (check *Checker) interfaceType(ityp *Interface, iface *ast.InterfaceType, d } } + // type constraints + ityp.types = check.collectTypeConstraints(iface.Pos(), ityp.types, iface.Types) + if len(ityp.methods) == 0 && len(ityp.embeddeds) == 0 { // empty interface ityp.allMethods = markComplete