[dev.typeparams] cmd/compile: fixing import of comm clauses/closures in generic functions

Improvements:
 - Fix export/import of the default case of a select statement (was not
   dealing with nil Comm case)
 - Set properly the name of closure functions in imported generic
   functions

Added new test exporting/importing a reasonably large channel package,
chansimp.go.

Change-Id: If2ee12bd749e5df415f48ec4b629a2fa68a79dcb
Reviewed-on: https://go-review.googlesource.com/c/go/+/321734
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
This commit is contained in:
Dan Scales 2021-05-10 16:23:35 -07:00
parent 626e89c261
commit 8d2b4cb6cc
5 changed files with 447 additions and 2 deletions

View file

@ -1523,7 +1523,12 @@ func (w *exportWriter) commList(cases []*ir.CommClause) {
w.uint64(uint64(len(cases)))
for _, cas := range cases {
w.pos(cas.Pos())
w.node(cas.Comm)
defaultCase := cas.Comm == nil
w.bool(defaultCase)
if !defaultCase {
// Only call w.node for non-default cause (cas.Comm is non-nil)
w.node(cas.Comm)
}
w.stmtList(cas.Body)
}
}

View file

@ -1121,7 +1121,13 @@ func (r *importReader) caseList(switchExpr ir.Node) []*ir.CaseClause {
func (r *importReader) commList() []*ir.CommClause {
cases := make([]*ir.CommClause, r.uint64())
for i := range cases {
cases[i] = ir.NewCommStmt(r.pos(), r.node(), r.stmtList())
pos := r.pos()
defaultCase := r.bool()
var comm ir.Node
if !defaultCase {
comm = r.node()
}
cases[i] = ir.NewCommStmt(pos, comm, r.stmtList())
}
return cases
}
@ -1257,6 +1263,12 @@ func (r *importReader) node() ir.Node {
if go117ExportTypes {
clo.SetType(typ)
}
if r.curfn.Type().HasTParam() {
// Generic functions aren't inlined, so give the closure a
// function name now, which is then available for use
// (after appending the type args) for each stenciling.
fn.Nname.SetSym(ClosureName(r.curfn))
}
return clo

View file

@ -0,0 +1,232 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package a
import (
"context"
"runtime"
)
// Equal reports whether two slices are equal: the same length and all
// elements equal. All floating point NaNs are considered equal.
func SliceEqual[Elem comparable](s1, s2 []Elem) bool {
if len(s1) != len(s2) {
return false
}
for i, v1 := range s1 {
v2 := s2[i]
if v1 != v2 {
isNaN := func(f Elem) bool { return f != f }
if !isNaN(v1) || !isNaN(v2) {
return false
}
}
}
return true
}
// ReadAll reads from c until the channel is closed or the context is
// canceled, returning all the values read.
func ReadAll[Elem any](ctx context.Context, c <-chan Elem) []Elem {
var r []Elem
for {
select {
case <-ctx.Done():
return r
case v, ok := <-c:
if !ok {
return r
}
r = append(r, v)
}
}
}
// Merge merges two channels into a single channel.
// This will leave a goroutine running until either both channels are closed
// or the context is canceled, at which point the returned channel is closed.
func Merge[Elem any](ctx context.Context, c1, c2 <-chan Elem) <-chan Elem {
r := make(chan Elem)
go func(ctx context.Context, c1, c2 <-chan Elem, r chan<- Elem) {
defer close(r)
for c1 != nil || c2 != nil {
select {
case <-ctx.Done():
return
case v1, ok := <-c1:
if ok {
r <- v1
} else {
c1 = nil
}
case v2, ok := <-c2:
if ok {
r <- v2
} else {
c2 = nil
}
}
}
}(ctx, c1, c2, r)
return r
}
// Filter calls f on each value read from c. If f returns true the value
// is sent on the returned channel. This will leave a goroutine running
// until c is closed or the context is canceled, at which point the
// returned channel is closed.
func Filter[Elem any](ctx context.Context, c <-chan Elem, f func(Elem) bool) <-chan Elem {
r := make(chan Elem)
go func(ctx context.Context, c <-chan Elem, f func(Elem) bool, r chan<- Elem) {
defer close(r)
for {
select {
case <-ctx.Done():
return
case v, ok := <-c:
if !ok {
return
}
if f(v) {
r <- v
}
}
}
}(ctx, c, f, r)
return r
}
// Sink returns a channel that discards all values sent to it.
// This will leave a goroutine running until the context is canceled
// or the returned channel is closed.
func Sink[Elem any](ctx context.Context) chan<- Elem {
r := make(chan Elem)
go func(ctx context.Context, r <-chan Elem) {
for {
select {
case <-ctx.Done():
return
case _, ok := <-r:
if !ok {
return
}
}
}
}(ctx, r)
return r
}
// An Exclusive is a value that may only be used by a single goroutine
// at a time. This is implemented using channels rather than a mutex.
type Exclusive[Val any] struct {
c chan Val
}
// MakeExclusive makes an initialized exclusive value.
func MakeExclusive[Val any](initial Val) *Exclusive[Val] {
r := &Exclusive[Val]{
c: make(chan Val, 1),
}
r.c <- initial
return r
}
// Acquire acquires the exclusive value for private use.
// It must be released using the Release method.
func (e *Exclusive[Val]) Acquire() Val {
return <-e.c
}
// TryAcquire attempts to acquire the value. The ok result reports whether
// the value was acquired. If the value is acquired, it must be released
// using the Release method.
func (e *Exclusive[Val]) TryAcquire() (v Val, ok bool) {
select {
case r := <-e.c:
return r, true
default:
return v, false
}
}
// Release updates and releases the value.
// This method panics if the value has not been acquired.
func (e *Exclusive[Val]) Release(v Val) {
select {
case e.c <- v:
default:
panic("Exclusive Release without Acquire")
}
}
// 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[Elem any]() (*Sender[Elem], *Receiver[Elem]) {
c := make(chan Elem)
d := make(chan struct{})
s := &Sender[Elem]{
values: c,
done: d,
}
r := &Receiver[Elem] {
values: c,
done: d,
}
runtime.SetFinalizer(r, (*Receiver[Elem]).finalize)
return s, r
}
// A Sender is used to send values to a Receiver.
type Sender[Elem any] struct {
values chan<- Elem
done <-chan struct{}
}
// Send sends a value to the receiver. It reports whether the value was sent.
// The value will not be sent if the context is closed or the receiver
// is freed.
func (s *Sender[Elem]) Send(ctx context.Context, v Elem) bool {
select {
case <-ctx.Done():
return false
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[Elem]) Close() {
close(s.values)
}
// A Receiver receives values from a Sender.
type Receiver[Elem any] struct {
values <-chan Elem
done chan<- struct{}
}
// Next returns the next value from the channel. The bool result indicates
// whether the value is valid.
func (r *Receiver[Elem]) Next(ctx context.Context) (v Elem, ok bool) {
select {
case <-ctx.Done():
case v, ok = <-r.values:
}
return v, ok
}
// finalize is a finalizer for the receiver.
func (r *Receiver[Elem]) finalize() {
close(r.done)
}

View file

@ -0,0 +1,189 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"a"
"context"
"fmt"
"runtime"
"sort"
"sync"
"time"
)
func TestReadAll() {
c := make(chan int)
go func() {
c <- 4
c <- 2
c <- 5
close(c)
}()
got := a.ReadAll(context.Background(), c)
want := []int{4, 2, 5}
if !a.SliceEqual(got, want) {
panic(fmt.Sprintf("ReadAll returned %v, want %v", got, want))
}
}
func TestMerge() {
c1 := make(chan int)
c2 := make(chan int)
go func() {
c1 <- 1
c1 <- 3
c1 <- 5
close(c1)
}()
go func() {
c2 <- 2
c2 <- 4
c2 <- 6
close(c2)
}()
ctx := context.Background()
got := a.ReadAll(ctx, a.Merge(ctx, c1, c2))
sort.Ints(got)
want := []int{1, 2, 3, 4, 5, 6}
if !a.SliceEqual(got, want) {
panic(fmt.Sprintf("Merge returned %v, want %v", got, want))
}
}
func TestFilter() {
c := make(chan int)
go func() {
c <- 1
c <- 2
c <- 3
close(c)
}()
even := func(i int) bool { return i%2 == 0 }
ctx := context.Background()
got := a.ReadAll(ctx, a.Filter(ctx, c, even))
want := []int{2}
if !a.SliceEqual(got, want) {
panic(fmt.Sprintf("Filter returned %v, want %v", got, want))
}
}
func TestSink() {
c := a.Sink[int](context.Background())
after := time.NewTimer(time.Minute)
defer after.Stop()
send := func(v int) {
select {
case c <- v:
case <-after.C:
panic("timed out sending to Sink")
}
}
send(1)
send(2)
send(3)
close(c)
}
func TestExclusive() {
val := 0
ex := a.MakeExclusive(&val)
var wg sync.WaitGroup
f := func() {
defer wg.Done()
for i := 0; i < 10; i++ {
p := ex.Acquire()
(*p)++
ex.Release(p)
}
}
wg.Add(2)
go f()
go f()
wg.Wait()
if val != 20 {
panic(fmt.Sprintf("after Acquire/Release loop got %d, want 20", val))
}
}
func TestExclusiveTry() {
s := ""
ex := a.MakeExclusive(&s)
p, ok := ex.TryAcquire()
if !ok {
panic("TryAcquire failed")
}
*p = "a"
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
_, ok := ex.TryAcquire()
if ok {
panic(fmt.Sprintf("TryAcquire succeeded unexpectedly"))
}
}()
wg.Wait()
ex.Release(p)
p, ok = ex.TryAcquire()
if !ok {
panic(fmt.Sprintf("TryAcquire failed"))
}
}
func TestRanger() {
s, r := a.Ranger[int]()
ctx := context.Background()
go func() {
// Receive one value then exit.
v, ok := r.Next(ctx)
if !ok {
panic(fmt.Sprintf("did not receive any values"))
} else if v != 1 {
panic(fmt.Sprintf("received %d, want 1", v))
}
}()
c1 := make(chan bool)
c2 := make(chan bool)
go func() {
defer close(c2)
if !s.Send(ctx, 1) {
panic(fmt.Sprintf("Send failed unexpectedly"))
}
close(c1)
if s.Send(ctx, 2) {
panic(fmt.Sprintf("Send succeeded unexpectedly"))
}
}()
<-c1
// Force a garbage collection to try to get the finalizers to run.
runtime.GC()
select {
case <-c2:
case <-time.After(time.Minute):
panic("Ranger Send should have failed, but timed out")
}
}
func main() {
TestReadAll()
TestMerge()
TestFilter()
TestSink()
TestExclusive()
TestExclusiveTry()
TestRanger()
}

View file

@ -0,0 +1,7 @@
// rundir -G=3
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ignored