From db04e7cb2e7fd88e1d1e05012dce40c042186c2c Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sat, 2 May 2020 16:00:47 -0700 Subject: [PATCH] go/go2go, cmd/go2go, test: rewrite example code to not use contracts Don't emit interfaces that are type bounds in the generated .go file. Change-Id: I9b0fd2f6041e9464147ad6d82d349fe894ea762d Reviewed-on: https://team-review.git.corp.google.com/c/golang/go2-dev/+/735474 Reviewed-by: Robert Griesemer --- .../go2path/src/contracts/contracts.go2 | 26 ++++++------ .../testdata/go2path/src/graph/graph.go2 | 42 +++++++++++-------- .../go2go/testdata/go2path/src/maps/maps.go2 | 26 ++++++------ .../testdata/go2path/src/metrics/metrics.go2 | 19 ++------- .../go2path/src/orderedmap/orderedmap.go2 | 2 +- src/go/go2go/rewrite.go | 20 ++++++++- src/go/types/api.go | 2 +- test/gen/err001.go2 | 18 ++++---- test/gen/err002.dir/a.go2 | 4 +- test/gen/g006.go2 | 4 +- 10 files changed, 88 insertions(+), 75 deletions(-) diff --git a/src/cmd/go2go/testdata/go2path/src/contracts/contracts.go2 b/src/cmd/go2go/testdata/go2path/src/contracts/contracts.go2 index e84e74ec99..0d2fc8ebed 100644 --- a/src/cmd/go2go/testdata/go2path/src/contracts/contracts.go2 +++ b/src/cmd/go2go/testdata/go2path/src/contracts/contracts.go2 @@ -2,30 +2,30 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package contracts defines some useful contracts. +// Package contracts defines some useful type bounds. package contracts -// The Ordered contract permits any ordered type: any type that supports +// Ordered permits any ordered type: any type that supports // the operations <, <=, >=, >, as well as == and !=. -contract Ordered(T) { - T int, int8, int16, int32, int64, +type Ordered interface { + type int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, string } -// The Integer contract permits any integer type. -contract Integer(T) { - T int, int8, int16, int32, int64, +// Integer permits any integer type. +type Integer interface { + type int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr } -// The Signed contract permits any signed integer type. -contract Signed(T) { - T int, int8, int16, int32, int64 +// Signed permits any signed integer type. +type Signed interface { + type int, int8, int16, int32, int64 } -// The Unsigned contract permits any unsigned integer type. -contract Unsigned(T) { - T uint, uint8, uint16, uint32, uint64, uintptr +// Unsigned permits any unsigned integer type. +type Unsigned interface { + type uint, uint8, uint16, uint32, uint64, uintptr } diff --git a/src/cmd/go2go/testdata/go2path/src/graph/graph.go2 b/src/cmd/go2go/testdata/go2path/src/graph/graph.go2 index bf1379222e..54b2aea85b 100644 --- a/src/cmd/go2go/testdata/go2path/src/graph/graph.go2 +++ b/src/cmd/go2go/testdata/go2path/src/graph/graph.go2 @@ -10,27 +10,31 @@ import "errors" // A Graph is a collection of nodes. A node may have an arbitrary number // of edges. An edge connects two nodes. Both nodes and edges must be // comparable. This is an undirected simple graph. -type Graph(type Node, Edge G) struct { +type Graph(type Node NodeC(Edge), Edge EdgeC(Node)) struct { nodes []Node } -// G is the contract that the Graph Node and Edge types must implement. -contract G(Node, Edge) { - Node Edges() []Edge - comparable(Node) - Edge Nodes() (a, b Node) - comparable(Edge) +// NodeC is the contraints on a node in a graph, given the Edge type. +type NodeC(type Edge) interface { + comparable + Edges() []Edge +} + +// Edgec is the constraints on an edge in a graph, given the Node type. +type EdgeC(type Node) interface { + comparable + Nodes() (a, b Node) } // New creates a new Graph from a collection of Nodes. -func New(type Node, Edge G)(nodes []Node) *Graph(Node, Edge) { +func New(type Node NodeC(Edge), Edge EdgeC(Node))(nodes []Node) *Graph(Node, Edge) { return &Graph(Node, Edge){nodes: nodes} } // nodePath holds the path to a node during ShortestPath. // This should ideally be a type defined inside ShortestPath, // but the translator tool doesn't support that. -type nodePath(type Node, Edge G) struct { +type nodePath(type Node NodeC(Edge), Edge EdgeC(Node)) struct { node Node path []Edge } @@ -67,28 +71,32 @@ func (g *Graph(Node, Edge)) ShortestPath(from, to Node) ([]Edge, error) { return nil, errors.New("no path") } -// GraphP is a version of Grgaph that uses pointers. This is for testing. +// GraphP is a version of Graph that uses pointers. This is for testing. // I'm not sure which approach will be better in practice, or whether // this indicates a problem with the draft design. -type GraphP(type Node, Edge GP) struct { +type GraphP(type Node NodeCP(Edge), Edge EdgeCP(Node)) struct { nodes []*Node } -// GP is the contract that the GraphP Node and Edge types must implement. -contract GP(Node, Edge) { - Node Edges() []*Edge - Edge Nodes() (a, b *Node) +// NodeCP is the contraint on a Node in a GraphP. +type NodeCP(type Edge) interface { + Edges() []*Edge +} + +// EdgeCP is the contraint on an Edge in a GraphP. +type EdgeCP(type Node) interface { + Nodes() (a, b *Node) } // NewP creates a new GraphP from a collection of Nodes. -func NewP(type Node, Edge GP)(nodes []*Node) *GraphP(Node, Edge) { +func NewP(type Node NodeCP(Edge), Edge EdgeCP(Node))(nodes []*Node) *GraphP(Node, Edge) { return &GraphP(Node, Edge){nodes: nodes} } // nodePathP holds the path to a node during ShortestPath. // This should ideally be a type defined inside ShortestPath, // but the translator tool doesn't support that. -type nodePathP(type Node, Edge GP) struct { +type nodePathP(type Node NodeCP(Edge), Edge EdgeCP(Node)) struct { node *Node path []*Edge } diff --git a/src/cmd/go2go/testdata/go2path/src/maps/maps.go2 b/src/cmd/go2go/testdata/go2path/src/maps/maps.go2 index d64974ef3e..1d05f42c02 100644 --- a/src/cmd/go2go/testdata/go2path/src/maps/maps.go2 +++ b/src/cmd/go2go/testdata/go2path/src/maps/maps.go2 @@ -5,9 +5,12 @@ // Package maps implements simple functions to manipulate maps in various ways. package maps +// any is a convenient type bound. +type any interface{} + // Keys returns the keys of the map m. // The keys will be an indeterminate order. -func Keys(type K, V comparable(K))(m map[K]V) []K { +func Keys(type K comparable, V any)(m map[K]V) []K { r := make([]K, 0, len(m)) for k := range m { r = append(r, k) @@ -17,7 +20,7 @@ func Keys(type K, V comparable(K))(m map[K]V) []K { // Values returns the values of the map m. // The values will be in an indeterminate order. -func Values(type K, V comparable(K))(m map[K]V) []V { +func Values(type K comparable, V any)(m map[K]V) []V { r := make([]V, 0, len(m)) for _, v := range m { r = append(r, v) @@ -25,14 +28,9 @@ func Values(type K, V comparable(K))(m map[K]V) []V { return r } -contract twocomparable(K, V) { - comparable(K) - comparable(V) -} - // Equal reports whether two maps contain the same key/value pairs. // Values are compared using ==. -func Equal(type K, V twocomparable)(m1, m2 map[K]V) bool { +func Equal(type K, V comparable)(m1, m2 map[K]V) bool { if len(m1) != len(m2) { return false } @@ -45,7 +43,7 @@ func Equal(type K, V twocomparable)(m1, m2 map[K]V) bool { } // Copy returns a copy of m. -func Copy(type K, V comparable(K))(m map[K]V) map[K]V { +func Copy(type K comparable, V any)(m map[K]V) map[K]V { r := make(map[K]V, len(m)) for k, v := range m { r[k] = v @@ -55,7 +53,7 @@ func Copy(type K, V comparable(K))(m map[K]V) map[K]V { // Add adds all key/value pairs in m2 to m1. Keys in m2 that are already // present in m1 will be overwritten with the value in m2. -func Add(type K, V comparable(K))(m1, m2 map[K]V) { +func Add(type K comparable, V any)(m1, m2 map[K]V) { for k, v := range m2 { m1[k] = v } @@ -63,7 +61,7 @@ func Add(type K, V comparable(K))(m1, m2 map[K]V) { // Sub removes all keys in m2 from m1. Keys in m2 that are not present // in m1 are ignored. The values in m2 are ignored. -func Sub(type K, V comparable(K))(m1, m2 map[K]V) { +func Sub(type K comparable, V any)(m1, m2 map[K]V) { for k := range m2 { delete(m1, k) } @@ -71,7 +69,7 @@ func Sub(type K, V comparable(K))(m1, m2 map[K]V) { // Intersect removes all keys from m1 that are not present in m2. // Keys in m2 that are not in m1 are ignored. The values in m2 are ignored. -func Intersect(type K, V comparable(K))(m1, m2 map[K]V) { +func Intersect(type K comparable, V any)(m1, m2 map[K]V) { for k := range m1 { if _, ok := m2[k]; !ok { delete(m1, k) @@ -80,7 +78,7 @@ func Intersect(type K, V comparable(K))(m1, m2 map[K]V) { } // Filter deletes any key/value pairs from m for which f returns false. -func Filter(type K, V comparable(K))(m map[K]V, f func(K, V) bool) { +func Filter(type K comparable, V any)(m map[K]V, f func(K, V) bool) { for k, v := range m { if !f(k, v) { delete(m, k) @@ -89,7 +87,7 @@ func Filter(type K, V comparable(K))(m map[K]V, f func(K, V) bool) { } // TransformValues applies f to each value in m. The keys remain unchanged. -func TransformValues(type K, V comparable(K))(m map[K]V, f func(V) V) { +func TransformValues(type K comparable, V any)(m map[K]V, f func(V) V) { for k, v := range m { m[k] = f(v) } diff --git a/src/cmd/go2go/testdata/go2path/src/metrics/metrics.go2 b/src/cmd/go2go/testdata/go2path/src/metrics/metrics.go2 index 1cb4477bf4..f4f1f52973 100644 --- a/src/cmd/go2go/testdata/go2path/src/metrics/metrics.go2 +++ b/src/cmd/go2go/testdata/go2path/src/metrics/metrics.go2 @@ -38,18 +38,13 @@ func (m *Metric1(T)) Metrics() []T { return maps.Keys(m.m) } -contract cmp2(T1, T2) { - comparable(T1) - comparable(T2) -} - -type key2(type T1, T2 cmp2) struct { +type key2(type T1, T2 comparable) struct { f1 T1 f2 T2 } // Metric2 tracks metrics of pairs of values. -type Metric2(type T1, T2 cmp2) struct { +type Metric2(type T1, T2 comparable) struct { mu sync.Mutex m map[key2(T1, T2)]int } @@ -78,20 +73,14 @@ func (m *Metric2(T1, T2)) Metrics() (r1 []T1, r2 []T2) { return r1, r2 } -contract cmp3(T1, T2, T3) { - comparable(T1) - comparable(T2) - comparable(T3) -} - -type key3(type T1, T2, T3 cmp3) struct { +type key3(type T1, T2, T3 comparable) struct { f1 T1 f2 T2 f3 T3 } // Metric3 tracks metrics of triplets of values. -type Metric3(type T1, T2, T3 cmp3) struct { +type Metric3(type T1, T2, T3 comparable) struct { mu sync.Mutex m map[key3(T1, T2, T3)]int } diff --git a/src/cmd/go2go/testdata/go2path/src/orderedmap/orderedmap.go2 b/src/cmd/go2go/testdata/go2path/src/orderedmap/orderedmap.go2 index 684250f41d..49d2b4ac1b 100644 --- a/src/cmd/go2go/testdata/go2path/src/orderedmap/orderedmap.go2 +++ b/src/cmd/go2go/testdata/go2path/src/orderedmap/orderedmap.go2 @@ -37,7 +37,7 @@ func New(type K, V)(compare func(K, K) int) *Map(K, V) { // NewOrdered returns a new map whose key is an ordered type. // This is like New, but does not require providing a compare function. // The map compare function uses the obvious key ordering. -func NewOrdered(type K, V contracts.Ordered(K))() *Map(K, V) { +func NewOrdered(type K contracts.Ordered, V interface{})() *Map(K, V) { return New(K, V)(func(k1, k2 K) int { switch { case k1 < k2: diff --git a/src/go/go2go/rewrite.go b/src/go/go2go/rewrite.go index 79a5de1fd8..66b7829867 100644 --- a/src/go/go2go/rewrite.go +++ b/src/go/go2go/rewrite.go @@ -52,6 +52,24 @@ func isParameterizedTypeDecl(s ast.Spec) bool { return ts.TParams != nil } +// isTypeBound reports whether s is an interface type that must be a +// type bound. +func isTypeBound(s ast.Spec) bool { + ts := s.(*ast.TypeSpec) + it, ok := ts.Type.(*ast.InterfaceType) + if !ok { + return false + } + for _, m := range it.Methods.List { + for _, n := range m.Names { + if n.Name == "type" { + return true + } + } + } + return false +} + // A translator is used to translate a file from Go with contracts to Go 1. type translator struct { fset *token.FileSet @@ -306,7 +324,7 @@ func (t *translator) translate(file *ast.File) { case token.TYPE: newSpecs := make([]ast.Spec, 0, len(decl.Specs)) for j := range decl.Specs { - if !isParameterizedTypeDecl(decl.Specs[j]) { + if !isParameterizedTypeDecl(decl.Specs[j]) && !isTypeBound(decl.Specs[j]) { t.translateTypeSpec(&decl.Specs[j]) newSpecs = append(newSpecs, decl.Specs[j]) } diff --git a/src/go/types/api.go b/src/go/types/api.go index 0abb739991..d763fd81a2 100644 --- a/src/go/types/api.go +++ b/src/go/types/api.go @@ -35,7 +35,7 @@ import ( ) // If AcceptContracts is set, contracts are accepted. -const AcceptContracts = true +const AcceptContracts = false // An Error describes a type-checking error; it implements the error interface. // A "soft" error is an error that still permits a valid interpretation of a diff --git a/test/gen/err001.go2 b/test/gen/err001.go2 index bdb6735ce1..9ab1dce939 100644 --- a/test/gen/err001.go2 +++ b/test/gen/err001.go2 @@ -9,8 +9,8 @@ package p func F1(type T comparable)() {} func F2() { F1([]int)() } // ERROR "\[\]int does not satisfy comparable$" -contract C(T) { - T M() +type C interface { + M() } func F3(type T C)() {} @@ -18,12 +18,12 @@ func F4() { F3(int)() } // ERROR "int does not satisfy C.*method M" func F5(type T)() { F3(T)() } // ERROR "T does not satisfy C.*method M" -contract signed(T) { - T int, int8, int16, int32, int64 +type signed interface { + type int, int8, int16, int32, int64 } -contract integer(T) { - T int, int8, int16, int32, int64, +type integer interface { + type int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr } @@ -38,9 +38,9 @@ type MyUint uint func F10(a MyInt) bool { return F6(a) } func F11(a MyUint) bool { return F6(a) } // ERROR "MyUint does not satisfy signed.*uint not found in" -contract C2(T) { - C(T) - signed(T) +type C2 interface { + C + signed } func F20(type T C2)(a T) bool { diff --git a/test/gen/err002.dir/a.go2 b/test/gen/err002.dir/a.go2 index 64f40f26bc..de3ef43ddf 100644 --- a/test/gen/err002.dir/a.go2 +++ b/test/gen/err002.dir/a.go2 @@ -4,6 +4,6 @@ package a -contract C(T) { - T M() +type C interface { + M() } diff --git a/test/gen/g006.go2 b/test/gen/g006.go2 index be19f81fe1..0df250348a 100644 --- a/test/gen/g006.go2 +++ b/test/gen/g006.go2 @@ -13,8 +13,8 @@ import ( "sort" ) -contract Ordered(T) { - T int, int8, int16, int32, int64, +type Ordered interface { + type int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64, string