go/*: fix parsing of T(P){...} comp. literals and add .go2 tests

Additionally, simplify the syntax for contracts specified in a
type parameter list: It is now not possible to provide explicit
type parameters to a contract in a type parameter list - they
are always implicit. For instance

	func f(type P1, P2 C(P1, P2)) ...

must be written as

	func f(type P1, P2 C) ...

If a different order or different types are desired for C,
a new "intermediate" contract must be declared, as in

	contract C'(A, B) {
		C(B, int)  // here we allow type parameters
	}

	func f(type P1, P2 C')

This simplification will remove confusion if we decide to
allow individual contracts in type parameter lists, such as

	func f(type P C, P1, P2 C') ...

In this case, C accepts one type parameter (and applies to P),
and C' accepts two type parameters and applies to P1 and P2.

The simplification avoids questions such as whether this code
should be permitted:

	func f(type P C(P2), P1, P2 C'(P, P1)) ...

(i.e., can pass P2 to C, or P1 to C', etc.)
This commit is contained in:
Robert Griesemer 2019-05-15 13:14:10 -07:00
parent 6069527287
commit e3b54c8023
8 changed files with 249 additions and 41 deletions

View File

@ -1102,7 +1102,7 @@ func (p *parser) parseTypeParams(scope *ast.Scope) *ast.TypeParamList {
}
if p.tok == token.IDENT {
// contract
contract = p.parseType(true)
contract = p.parseTypeName(nil)
}
if lbrack.IsValid() {
@ -1785,23 +1785,6 @@ func isTypeName(x ast.Expr) bool {
return true
}
// isLiteralType reports whether x is a legal composite literal type.
func isLiteralType(x ast.Expr) bool {
switch t := x.(type) {
case *ast.BadExpr:
case *ast.Ident:
case *ast.SelectorExpr:
_, isIdent := t.X.(*ast.Ident)
return isIdent
case *ast.ArrayType:
case *ast.StructType:
case *ast.MapType:
default:
return false // all other nodes are not legal composite literal types
}
return true
}
// If x is of the form *T, deref returns T, otherwise it returns x.
func deref(x ast.Expr) ast.Expr {
if p, isPtr := x.(*ast.StarExpr); isPtr {
@ -1837,13 +1820,12 @@ func (p *parser) checkExprOrType(x ast.Expr) ast.Expr {
}
// If lhs is set and the result is an identifier, it is not resolved.
func (p *parser) parsePrimaryExpr(lhs bool) ast.Expr {
func (p *parser) parsePrimaryExpr(lhs bool) (x ast.Expr) {
if p.trace {
defer un(trace(p, "PrimaryExpr"))
}
x := p.parseOperand(lhs)
L:
x = p.parseOperand(lhs)
for {
switch p.tok {
case token.PERIOD:
@ -1874,23 +1856,31 @@ L:
}
x = p.parseCallOrConversion(p.checkExprOrType(x))
case token.LBRACE:
// TODO(gri) currently this doesn't accept instantiated types
// Use code from cmd/compile/internal/syntax/parser.go.
if isLiteralType(x) && (p.exprLev >= 0 || !isTypeName(x)) {
if lhs {
p.resolve(x)
// operand may have returned a parenthesized complit
// type; accept it but complain if we have a complit
t := unparen(x)
// determine if '{' belongs to a composite literal or a block statement
switch t.(type) {
case *ast.BadExpr, *ast.Ident, *ast.SelectorExpr, *ast.CallExpr: // *ast.CallExpr for instantiated types
if p.exprLev < 0 {
return
}
x = p.parseLiteralValue(x)
} else {
break L
// x is (possibly a) composite literal type
case *ast.ArrayType, *ast.StructType, *ast.MapType:
// x is a composite literal type
default:
return
}
if t != x {
p.error(t.Pos(), "cannot parenthesize type in composite literal")
// already progressed, no need to advance
}
x = p.parseLiteralValue(x)
default:
break L
return
}
lhs = false // no need to try to resolve again
}
return x
}
// If lhs is set and the result is an identifier, it is not resolved.
@ -2215,7 +2205,7 @@ func (p *parser) parseIfHeader() (init ast.Stmt, cond ast.Expr) {
}
// p.tok != token.LBRACE
outer := p.exprLev
prevLev := p.exprLev
p.exprLev = -1
if p.tok != token.SEMICOLON {
@ -2263,7 +2253,7 @@ func (p *parser) parseIfHeader() (init ast.Stmt, cond ast.Expr) {
cond = &ast.BadExpr{From: p.pos, To: p.pos}
}
p.exprLev = outer
p.exprLev = prevLev
return
}

View File

@ -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 }`,
@ -97,7 +97,7 @@ func TestValid(t *testing.T) {
// TestSingle is useful to track down a problem with a single short test program.
func TestSingle(t *testing.T) {
const src = `package p; func _((T(a, b,)))`
const src = `package p; var _ = T(P){}`
checkErrors(t, src, src)
}

62
src/go/parser/testdata/chans.go2 vendored Normal file
View File

@ -0,0 +1,62 @@
package chans
import "runtime"
// Ranger returns a Sender and a Receiver. The Receiver provides a
// Next method to retrieve values. The Sender provides a Send method
// to send values and a Close method to stop sending values. The Next
// method indicates when the Sender has been closed, and the Send
// method indicates when the Receiver has been freed.
//
// This is a convenient way to exit a goroutine sending values when
// the receiver stops reading them.
func Ranger(type T)() (*Sender(T), *Receiver(T)) {
c := make(chan T)
d := make(chan bool)
s := &Sender(T){values: c, done: d}
r := &Receiver(T){values: c, done: d}
runtime.SetFinalizer(r, r.finalize)
return s, r
}
// A sender is used to send values to a Receiver.
type Sender(type T) struct {
values chan<- T
done <-chan bool
}
// Send sends a value to the receiver. It returns whether any more
// values may be sent; if it returns false the value was not sent.
func (s *Sender(T)) Send(v T) bool {
select {
case s.values <- v:
return true
case <-s.done:
return false
}
}
// Close tells the receiver that no more values will arrive.
// After Close is called, the Sender may no longer be used.
func (s *Sender(T)) Close() {
close(s.values)
}
// A Receiver receives values from a Sender.
type Receiver(type T) struct {
values <-chan T
done chan<- bool
}
// Next returns the next value from the channel. The bool result
// indicates whether the value is valid, or whether the Sender has
// been closed and no more values will be received.
func (r *Receiver(T)) Next() (T, bool) {
v, ok := <-r.values
return v, ok
}
// finalize is a finalizer for the receiver.
func (r *Receiver(T)) finalize() {
close(r.done)
}

View File

@ -18,8 +18,7 @@ type node(type K, V) struct {
// New returns a new map.
func New(type K, V)(compare func(K, K) int) *Map(K, V) {
// TODO
// return &Map(K, V){compare: compare}
return &Map(K, V){compare: compare}
}
// find looks up key in the map, and returns either a pointer
@ -49,8 +48,7 @@ func (m *Map(K, V)) Insert(key K, val V) bool {
(*pn).val = val
return false
}
// TODO
// *pn = &node(K, V){key: key, val: val}
*pn = &node(K, V){key: key, val: val}
return true
}

69
src/go/parser/testdata/metrics.go2 vendored Normal file
View File

@ -0,0 +1,69 @@
package metrics
import "sync"
type Metric1(type T comparable) struct {
mu sync.Mutex
m map[T]int
}
func (m *Metric1(T)) Add(v T) {
m.mu.Lock()
defer m.mu.Unlock()
if m.m == nil {
m.m = make(map[T]int)
}
m[v]++
}
contract cmp2(T1, T2) {
comparable(T1)
comparable(T2)
}
type key2(type T1, T2 cmp2) struct {
f1 T1
f2 T2
}
type Metric2(type T1, T2 cmp2) struct {
mu sync.Mutex
m map[key2(T1, T2)]int
}
func (m *Metric2(T1, T2)) Add(v1 T1, v2 T2) {
m.mu.Lock()
defer m.mu.Unlock()
if m.m == nil {
m.m = make(map[key2(T1, T2)]int)
}
m[key(T1, T2){v1, v2}]++
}
contract cmp3(T1, T2, T3) {
comparable(T1)
comparable(T2)
comparable(T3)
}
type key3(type T1, T2, T3 cmp3) struct {
f1 T1
f2 T2
f3 T3
}
type Metric3(type T1, T2, T3 cmp3) struct {
mu sync.Mutex
m map[key3(T1, T2, T3)]int
}
func (m *Metric3(T1, T2, T3)) Add(v1 T1, v2 T2, v3 T3) {
m.mu.Lock()
defer m.mu.Unlock()
if m.m == nil {
m.m = make(map[key3]int)
}
m[key(T1, T2, T3){v1, v2, v3}]++
}
// Repeat for the maximum number of permitted arguments.

31
src/go/parser/testdata/set.go2 vendored Normal file
View File

@ -0,0 +1,31 @@
// Package set implements sets of any type.
package set
type Set(type Elem comparable) map[Elem]struct{}
func Make(type Elem comparable)() Set(Elem) {
return make(Set(Elem))
}
func (s Set(Elem)) Add(v Elem) {
s[v] = struct{}{}
}
func (s Set(Elem)) Delete(v Elem) {
delete(s, v)
}
func (s Set(Elem)) Contains(v Elem) bool {
_, ok := s[v]
return ok
}
func (s Set(Elem)) Len() int {
return len(s)
}
func (s Set(Elem)) Iterate(f func(Elem)) {
for v := range s {
f(v)
}
}

31
src/go/parser/testdata/slices.go2 vendored Normal file
View File

@ -0,0 +1,31 @@
// Package slices implements various slice algorithms.
package slices
// Map turns a []T1 to a []T2 using a mapping function.
func Map(type T1, T2)(s []T1, f func(T1) T2) []T2 {
r := make([]T2, len(s))
for i, v := range s {
r[i] = f(v)
}
return r
}
// Reduce reduces a []T1 to a single value using a reduction function.
func Reduce(type T1, T2)(s []T1, initializer T2, f func(T2, T1) T2) T2 {
r := initializer
for _, v := range s {
r = f(r, v)
}
return r
}
// Filter filters values from a slice using a filter function.
func Filter(type T)(s []T, f func(T) bool) []T {
var r []T
for _, v := range s {
if f(v) {
r = append(r, v)
}
}
return r
}

27
src/go/parser/testdata/sort.go2 vendored Normal file
View File

@ -0,0 +1,27 @@
package sort
type orderedSlice(type Elem comparable) []Elem
func (s orderedSlice(Elem)) Len() int { return len(s) }
func (s orderedSlice(Elem)) Less(i, j int) bool { return s[i] < s[j] }
func (s orderedSlice(Elem)) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// OrderedSlice sorts the slice s in ascending order.
// The elements of s must be ordered using the < operator.
func OrderedSlice(type Elem comparable)(s []Elem) {
sort.Sort(orderedSlice(Elem)(s))
}
type sliceFn(type Elem) struct {
s []Elem
f func(Elem, Elem) bool
}
func (s sliceFn(Elem)) Len() int { return len(s.s) }
func (s sliceFn(Elem)) Less(i, j int) bool { return s.f(s.s[i], s.s[j]) }
func (s sliceFn(Elem)) Swap(i, j int) { s.s[i], s.s[j] = s.s[j], s.s[i] }
// SliceFn sorts the slice s according to the function f.
func SliceFn(type Elem)(s []Elem, f func(Elem, Elem) bool) {
Sort(sliceFn(Elem){s, f})
}