The predeclared function "closed" has been removed from the Go language, as it is no longer necessary now that v, ok = <-c returns whether the channel is closed. This patch implements that in gccgo. This patch also fixes the handling of case v, ok := <-c in a select statement. Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu. Committed to mainline.
Ian
diff -r 8de7f64be423 go/expressions.cc --- a/go/expressions.cc Thu Mar 24 16:37:44 2011 -0700 +++ b/go/expressions.cc Thu Mar 24 21:58:25 2011 -0700 @@ -6530,7 +6530,6 @@ BUILTIN_APPEND, BUILTIN_CAP, BUILTIN_CLOSE, - BUILTIN_CLOSED, BUILTIN_COMPLEX, BUILTIN_COPY, BUILTIN_IMAG, @@ -6588,8 +6587,6 @@ this->code_ = BUILTIN_CAP; else if (name == "close") this->code_ = BUILTIN_CLOSE; - else if (name == "closed") - this->code_ = BUILTIN_CLOSED; else if (name == "complex") this->code_ = BUILTIN_COMPLEX; else if (name == "copy") @@ -7185,9 +7182,6 @@ case BUILTIN_PRINTLN: return Type::make_void_type(); - case BUILTIN_CLOSED: - return Type::lookup_bool_type(); - case BUILTIN_RECOVER: return Type::make_interface_type(NULL, BUILTINS_LOCATION); @@ -7451,7 +7445,6 @@ break; case BUILTIN_CLOSE: - case BUILTIN_CLOSED: if (this->check_one_arg()) { if (this->one_arg()->type()->channel_type() == NULL) @@ -7936,7 +7929,6 @@ } case BUILTIN_CLOSE: - case BUILTIN_CLOSED: { const Expression_list* args = this->args(); gcc_assert(args != NULL && args->size() == 1); @@ -7944,28 +7936,14 @@ tree arg_tree = arg->get_tree(context); if (arg_tree == error_mark_node) return error_mark_node; - if (this->code_ == BUILTIN_CLOSE) - { - static tree close_fndecl; - return Gogo::call_builtin(&close_fndecl, - location, - "__go_builtin_close", - 1, - void_type_node, - TREE_TYPE(arg_tree), - arg_tree); - } - else - { - static tree closed_fndecl; - return Gogo::call_builtin(&closed_fndecl, - location, - "__go_builtin_closed", - 1, - boolean_type_node, - TREE_TYPE(arg_tree), - arg_tree); - } + static tree close_fndecl; + return Gogo::call_builtin(&close_fndecl, + location, + "__go_builtin_close", + 1, + void_type_node, + TREE_TYPE(arg_tree), + arg_tree); } case BUILTIN_SIZEOF: diff -r 8de7f64be423 go/gogo.cc --- a/go/gogo.cc Thu Mar 24 16:37:44 2011 -0700 +++ b/go/gogo.cc Thu Mar 24 21:58:25 2011 -0700 @@ -173,15 +173,6 @@ close_type->set_is_builtin(); this->globals_->add_function_declaration("close", NULL, close_type, loc); - Typed_identifier_list* closed_result = new Typed_identifier_list(); - closed_result->push_back(Typed_identifier("", Type::lookup_bool_type(), - loc)); - Function_type* closed_type = Type::make_function_type(NULL, NULL, - closed_result, loc); - closed_type->set_is_varargs(); - closed_type->set_is_builtin(); - this->globals_->add_function_declaration("closed", NULL, closed_type, loc); - Typed_identifier_list* copy_result = new Typed_identifier_list(); copy_result->push_back(Typed_identifier("", int_type, loc)); Function_type* copy_type = Type::make_function_type(NULL, NULL, @@ -3506,12 +3497,15 @@ true); this->init_ = NULL; } + else if (this->type_from_chan_element_) + { + Expression* init = this->init_; + init->determine_type_no_context(); + this->type_ = this->type_from_chan_element(init, true); + this->init_ = NULL; + } else { - // type_from_chan_element_ should have been cleared during - // lowering. - gcc_assert(!this->type_from_chan_element_); - Type_context context(this->type_, false); this->init_->determine_type(&context); if (this->type_ == NULL) diff -r 8de7f64be423 go/parse.cc --- a/go/parse.cc Thu Mar 24 16:37:44 2011 -0700 +++ b/go/parse.cc Thu Mar 24 21:58:25 2011 -0700 @@ -1717,6 +1717,7 @@ Statement* s = Statement::make_tuple_receive_assignment(val_var, received_var, receive->channel(), + false, location); if (!this->gogo_->in_global_scope()) @@ -3629,6 +3630,7 @@ Expression* channel = receive->channel(); Statement* s = Statement::make_tuple_receive_assignment(val, success, channel, + false, location); this->gogo_->add_statement(s); } diff -r 8de7f64be423 go/statements.cc --- a/go/statements.cc Thu Mar 24 16:37:44 2011 -0700 +++ b/go/statements.cc Thu Mar 24 21:58:25 2011 -0700 @@ -1148,10 +1148,10 @@ { public: Tuple_receive_assignment_statement(Expression* val, Expression* closed, - Expression* channel, + Expression* channel, bool for_select, source_location location) : Statement(STATEMENT_TUPLE_RECEIVE_ASSIGNMENT, location), - val_(val), closed_(closed), channel_(channel) + val_(val), closed_(closed), channel_(channel), for_select_(for_select) { } protected: @@ -1176,6 +1176,8 @@ Expression* closed_; // The channel on which we receive the value. Expression* channel_; + // Whether this is for a select statement. + bool for_select_; }; // Traversal. @@ -1228,6 +1230,7 @@ b->add_statement(closed_temp); // func chanrecv2(c chan T, val *T) bool + // func chanrecv3(c chan T, val *T) bool (if for_select) source_location bloc = BUILTINS_LOCATION; Typed_identifier_list* param_types = new Typed_identifier_list(); param_types->push_back(Typed_identifier("c", channel_type, bloc)); @@ -1239,12 +1242,22 @@ Function_type* fntype = Type::make_function_type(NULL, param_types, ret_types, bloc); - Named_object* chanrecv2 = - Named_object::make_function_declaration("chanrecv2", NULL, fntype, bloc); - chanrecv2->func_declaration_value()->set_asm_name("runtime.chanrecv2"); - - // closed_temp = chanrecv2(channel, &val_temp) - Expression* func = Expression::make_func_reference(chanrecv2, NULL, loc); + Named_object* chanrecv; + if (!this->for_select_) + { + chanrecv = Named_object::make_function_declaration("chanrecv2", NULL, + fntype, bloc); + chanrecv->func_declaration_value()->set_asm_name("runtime.chanrecv2"); + } + else + { + chanrecv = Named_object::make_function_declaration("chanrecv3", NULL, + fntype, bloc); + chanrecv->func_declaration_value()->set_asm_name("runtime.chanrecv3"); + } + + // closed_temp = chanrecv[23](channel, &val_temp) + Expression* func = Expression::make_func_reference(chanrecv, NULL, loc); Expression_list* params = new Expression_list(); params->push_back(this->channel_); Expression* ref = Expression::make_temporary_reference(val_temp, loc); @@ -1272,10 +1285,11 @@ Statement* Statement::make_tuple_receive_assignment(Expression* val, Expression* closed, Expression* channel, + bool for_select, source_location location) { return new Tuple_receive_assignment_statement(val, closed, channel, - location); + for_select, location); } // An assignment to a pair of values from a type guard. This is a @@ -4151,7 +4165,7 @@ this->val_ = Expression::make_sink(loc); Statement* s = Statement::make_tuple_receive_assignment(this->val_, this->closed_, - ref, loc); + ref, true, loc); init->add_statement(s); } else if (this->closedvar_ != NULL) @@ -4165,8 +4179,14 @@ Expression* closed = Expression::make_var_reference(this->closedvar_, loc); Statement* s = Statement::make_tuple_receive_assignment(val, closed, ref, - loc); - init->add_statement(s); + true, loc); + // We have to put S in STATEMENTS_, because that is where the + // variables are declared. + gcc_assert(this->statements_ != NULL); + this->statements_->add_statement_at_front(s); + // We have to lower STATEMENTS_ again, to lower the tuple + // receive assignment we just added. + gogo->lower_block(function, this->statements_); } else { @@ -5281,7 +5301,7 @@ // Lower a for range over a channel. void -For_range_statement::lower_range_channel(Gogo* gogo, +For_range_statement::lower_range_channel(Gogo*, Block*, Block* body_block, Named_object* range_object, @@ -5299,12 +5319,11 @@ // The loop we generate: // for { - // index_temp = <-range - // if closed(range) { + // index_temp, ok_temp = <-range + // if !ok_temp { // break // } // index = index_temp - // value = value_temp // original body // } @@ -5315,26 +5334,30 @@ *ppost = NULL; // Set *PITER_INIT to - // index_temp = <-range - // if closed(range) { + // index_temp, ok_temp = <-range + // if !ok_temp { // break // } Block* iter_init = new Block(body_block, loc); - Expression* ref = this->make_range_ref(range_object, range_temp, loc); - Expression* cond = this->call_builtin(gogo, "closed", ref, loc); - - ref = this->make_range_ref(range_object, range_temp, loc); - Expression* recv = Expression::make_receive(ref, loc); - ref = Expression::make_temporary_reference(index_temp, loc); - Statement* s = Statement::make_assignment(ref, recv, loc); + Temporary_statement* ok_temp = + Statement::make_temporary(Type::lookup_bool_type(), NULL, loc); + iter_init->add_statement(ok_temp); + + Expression* cref = this->make_range_ref(range_object, range_temp, loc); + Expression* iref = Expression::make_temporary_reference(index_temp, loc); + Expression* oref = Expression::make_temporary_reference(ok_temp, loc); + Statement* s = Statement::make_tuple_receive_assignment(iref, oref, cref, + false, loc); iter_init->add_statement(s); Block* then_block = new Block(iter_init, loc); s = Statement::make_break_statement(this->break_label(), loc); then_block->add_statement(s); + oref = Expression::make_temporary_reference(ok_temp, loc); + Expression* cond = Expression::make_unary(OPERATOR_NOT, oref, loc); s = Statement::make_if_statement(cond, then_block, NULL, loc); iter_init->add_statement(s); diff -r 8de7f64be423 go/statements.h --- a/go/statements.h Thu Mar 24 16:37:44 2011 -0700 +++ b/go/statements.h Thu Mar 24 21:58:25 2011 -0700 @@ -160,10 +160,12 @@ Expression* should_set, source_location); // Make an assignment from a nonblocking receive to a pair of - // variables. + // variables. FOR_SELECT is true is this is being created for a + // case x, ok := <-c in a select statement. static Statement* make_tuple_receive_assignment(Expression* val, Expression* closed, - Expression* channel, source_location); + Expression* channel, bool for_select, + source_location); // Make an assignment from a type guard to a pair of variables. static Statement* diff -r 8de7f64be423 libgo/Makefile.am --- a/libgo/Makefile.am Thu Mar 24 16:37:44 2011 -0700 +++ b/libgo/Makefile.am Thu Mar 24 21:58:25 2011 -0700 @@ -358,7 +358,6 @@ runtime/go-chan-len.c \ runtime/go-check-interface.c \ runtime/go-close.c \ - runtime/go-closed.c \ runtime/go-construct-map.c \ runtime/go-convert-interface.c \ runtime/go-copy.c \ diff -r 8de7f64be423 libgo/runtime/chan.goc --- a/libgo/runtime/chan.goc Thu Mar 24 16:37:44 2011 -0700 +++ b/libgo/runtime/chan.goc Thu Mar 24 21:58:25 2011 -0700 @@ -10,7 +10,7 @@ typedef unsigned char byte; typedef struct __go_channel chan; -/* Do a nonblocking channel receive. */ +/* Do a channel receive with closed status. */ func chanrecv2(c *chan, val *byte) (received bool) { if (c->element_size > 8) { @@ -31,3 +31,25 @@ return received; } } + +/* Do a channel receive with closed status for a select statement. */ + +func chanrecv3(c *chan, val *byte) (received bool) { + if (c->element_size > 8) { + return __go_receive_big(c, val, 1); + } else { + union { + char b[8]; + uint64_t v; + } u; + + u.v = __go_receive_small_closed(c, 1, &received); +#ifndef WORDS_BIGENDIAN + __builtin_memcpy(val, u.b, c->element_size); +#else + __builtin_memcpy(val, u.b + 8 - c->element_size, + c->element_size); +#endif + return received; + } +} diff -r 8de7f64be423 libgo/runtime/channel.h --- a/libgo/runtime/channel.h Thu Mar 24 16:37:44 2011 -0700 +++ b/libgo/runtime/channel.h Thu Mar 24 21:58:25 2011 -0700 @@ -50,9 +50,6 @@ _Bool selected_for_receive; /* True if this channel has been closed. */ _Bool is_closed; - /* True if at least one null value has been read from a closed - channel. */ - _Bool saw_close; /* The list of select statements waiting to send on a synchronous channel. */ struct __go_channel_select *select_send_queue; diff -r 8de7f64be423 libgo/runtime/go-closed.c --- a/libgo/runtime/go-closed.c Thu Mar 24 16:37:44 2011 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* go-closed.c -- the builtin closed function. - - Copyright 2009 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. */ - -#include "go-assert.h" -#include "channel.h" - -/* Return whether a channel is closed. We only return true after at - least one nil value has been read from the channel. */ - -_Bool -__go_builtin_closed (struct __go_channel *channel) -{ - int i; - _Bool ret; - - i = pthread_mutex_lock (&channel->lock); - __go_assert (i == 0); - - while (channel->selected_for_receive) - { - i = pthread_cond_wait (&channel->cond, &channel->lock); - __go_assert (i == 0); - } - - ret = channel->saw_close; - - i = pthread_mutex_unlock (&channel->lock); - __go_assert (i == 0); - - return ret; -} diff -r 8de7f64be423 libgo/runtime/go-new-channel.c --- a/libgo/runtime/go-new-channel.c Thu Mar 24 16:37:44 2011 -0700 +++ b/libgo/runtime/go-new-channel.c Thu Mar 24 21:58:25 2011 -0700 @@ -44,7 +44,6 @@ ret->selected_for_send = 0; ret->selected_for_receive = 0; ret->is_closed = 0; - ret->saw_close = 0; ret->select_send_queue = NULL; ret->select_receive_queue = NULL; ret->select_mutex = NULL; diff -r 8de7f64be423 libgo/runtime/go-rec-nb-small.c --- a/libgo/runtime/go-rec-nb-small.c Thu Mar 24 16:37:44 2011 -0700 +++ b/libgo/runtime/go-rec-nb-small.c Thu Mar 24 21:58:25 2011 -0700 @@ -32,7 +32,6 @@ ? channel->next_store == 0 : channel->next_fetch == channel->next_store)) { - channel->saw_close = 1; __go_unlock_and_notify_selects (channel); return RECEIVE_NONBLOCKING_ACQUIRE_CLOSED; } diff -r 8de7f64be423 libgo/runtime/go-rec-small.c --- a/libgo/runtime/go-rec-small.c Thu Mar 24 16:37:44 2011 -0700 +++ b/libgo/runtime/go-rec-small.c Thu Mar 24 21:58:25 2011 -0700 @@ -124,7 +124,6 @@ ? channel->next_store == 0 : channel->next_fetch == channel->next_store)) { - channel->saw_close = 1; channel->selected_for_receive = 0; __go_unlock_and_notify_selects (channel); return 0; Index: gcc/testsuite/go.test/test/closedchan.go =================================================================== --- gcc/testsuite/go.test/test/closedchan.go (revision 171371) +++ gcc/testsuite/go.test/test/closedchan.go (working copy) @@ -4,7 +4,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Test close(c), closed(c). +// Test close(c), receive of closed channel. // // TODO(rsc): Doesn't check behavior of close(c) when there // are blocked senders/receivers. @@ -14,10 +14,11 @@ package main type Chan interface { Send(int) Nbsend(int) bool - Recv() int + Recv() (int) Nbrecv() (int, bool) + Recv2() (int, bool) + Nbrecv2() (int, bool, bool) Close() - Closed() bool Impl() string } @@ -52,12 +53,23 @@ func (c XChan) Nbrecv() (int, bool) { panic("nbrecv") } -func (c XChan) Close() { - close(c) +func (c XChan) Recv2() (int, bool) { + x, ok := <-c + return x, ok +} + +func (c XChan) Nbrecv2() (int, bool, bool) { + select { + case x, ok := <-c: + return x, ok, true + default: + return 0, false, false + } + panic("nbrecv2") } -func (c XChan) Closed() bool { - return closed(c) +func (c XChan) Close() { + close(c) } func (c XChan) Impl() string { @@ -101,12 +113,26 @@ func (c SChan) Nbrecv() (int, bool) { panic("nbrecv") } -func (c SChan) Close() { - close(c) +func (c SChan) Recv2() (int, bool) { + select { + case x, ok := <-c: + return x, ok + } + panic("recv") } -func (c SChan) Closed() bool { - return closed(c) +func (c SChan) Nbrecv2() (int, bool, bool) { + select { + default: + return 0, false, false + case x, ok := <-c: + return x, ok, true + } + panic("nbrecv") +} + +func (c SChan) Close() { + close(c) } func (c SChan) Impl() string { @@ -156,12 +182,28 @@ func (c SSChan) Nbrecv() (int, bool) { panic("nbrecv") } -func (c SSChan) Close() { - close(c) +func (c SSChan) Recv2() (int, bool) { + select { + case <-dummy: + case x, ok := <-c: + return x, ok + } + panic("recv") } -func (c SSChan) Closed() bool { - return closed(c) +func (c SSChan) Nbrecv2() (int, bool, bool) { + select { + case <-dummy: + default: + return 0, false, false + case x, ok := <-c: + return x, ok, true + } + panic("nbrecv") +} + +func (c SSChan) Close() { + close(c) } func (c SSChan) Impl() string { @@ -179,29 +221,23 @@ func shouldPanic(f func()) { } func test1(c Chan) { - // not closed until the close signal (a zero value) has been received. - if c.Closed() { - println("test1: Closed before Recv zero:", c.Impl()) - } - for i := 0; i < 3; i++ { // recv a close signal (a zero value) if x := c.Recv(); x != 0 { - println("test1: recv on closed got non-zero:", x, c.Impl()) + println("test1: recv on closed:", x, c.Impl()) } - - // should now be closed. - if !c.Closed() { - println("test1: not closed after recv zero", c.Impl()) + if x, ok := c.Recv2(); x != 0 || ok { + println("test1: recv2 on closed:", x, ok, c.Impl()) } - // should work with ,ok: received a value without blocking, so ok == true. - x, ok := c.Nbrecv() - if !ok { - println("test1: recv on closed got not ok", c.Impl()) + // should work with select: received a value without blocking, so selected == true. + x, selected := c.Nbrecv() + if x != 0 || !selected { + println("test1: recv on closed nb:", x, selected, c.Impl()) } - if x != 0 { - println("test1: recv ,ok on closed got non-zero:", x, c.Impl()) + x, ok, selected := c.Nbrecv2() + if x != 0 || ok || !selected { + println("test1: recv2 on closed nb:", x, ok, selected, c.Impl()) } } @@ -221,11 +257,6 @@ func test1(c Chan) { } func testasync1(c Chan) { - // not closed until the close signal (a zero value) has been received. - if c.Closed() { - println("testasync1: Closed before Recv zero:", c.Impl()) - } - // should be able to get the last value via Recv if x := c.Recv(); x != 1 { println("testasync1: Recv did not get 1:", x, c.Impl()) @@ -235,19 +266,31 @@ func testasync1(c Chan) { } func testasync2(c Chan) { - // not closed until the close signal (a zero value) has been received. - if c.Closed() { - println("testasync2: Closed before Recv zero:", c.Impl()) + // should be able to get the last value via Recv2 + if x, ok := c.Recv2(); x != 1 || !ok { + println("testasync1: Recv did not get 1, true:", x, ok, c.Impl()) } + test1(c) +} + +func testasync3(c Chan) { // should be able to get the last value via Nbrecv - if x, ok := c.Nbrecv(); !ok || x != 1 { - println("testasync2: Nbrecv did not get 1, true:", x, ok, c.Impl()) + if x, selected := c.Nbrecv(); x != 1 || !selected { + println("testasync2: Nbrecv did not get 1, true:", x, selected, c.Impl()) } test1(c) } +func testasync4(c Chan) { + // should be able to get the last value via Nbrecv2 + if x, ok, selected := c.Nbrecv2(); x != 1 || !ok || !selected { + println("testasync2: Nbrecv did not get 1, true, true:", x, ok, selected, c.Impl()) + } + test1(c) +} + func closedsync() chan int { c := make(chan int) close(c) @@ -261,15 +304,27 @@ func closedasync() chan int { return c } +var mks = []func(chan int) Chan { + func(c chan int) Chan { return XChan(c) }, + func(c chan int) Chan { return SChan(c) }, + func(c chan int) Chan { return SSChan(c) }, +} + +var testcloseds = []func(Chan) { + testasync1, + testasync2, + testasync3, + testasync4, +} + func main() { - test1(XChan(closedsync())) - test1(SChan(closedsync())) - test1(SSChan(closedsync())) - - testasync1(XChan(closedasync())) - testasync1(SChan(closedasync())) - testasync1(SSChan(closedasync())) - testasync2(XChan(closedasync())) - testasync2(SChan(closedasync())) - testasync2(SSChan(closedasync())) + for _, mk := range mks { + test1(mk(closedsync())) + } + + for _, testclosed := range testcloseds { + for _, mk := range mks { + testclosed(mk(closedasync())) + } + } } Index: gcc/testsuite/go.test/test/named1.go =================================================================== --- gcc/testsuite/go.test/test/named1.go (revision 171371) +++ gcc/testsuite/go.test/test/named1.go (working copy) @@ -43,10 +43,6 @@ func main() { _, b = m[2] // ERROR "cannot .* bool.*type Bool" m[2] = 1, b // ERROR "cannot use.*type Bool.*as type bool" - ////TODO(rsc): uncomment when this syntax is valid for receive+check closed - //// _, b = <-c // ERROR "cannot .* bool.*type Bool" - //// _ = b - var inter interface{} _, b = inter.(Map) // ERROR "cannot .* bool.*type Bool" _ = b @@ -57,8 +53,9 @@ func main() { _, b = minter.(Map) // ERROR "cannot .* bool.*type Bool" _ = b - asBool(closed(c)) // ERROR "cannot use.*type bool.*as type Bool" - b = closed(c) // ERROR "cannot use.*type bool.*type Bool" + _, bb := <-c + asBool(bb) // ERROR "cannot use.*type bool.*as type Bool" + _, b = <-c // ERROR "cannot .* bool.*type Bool" _ = b asString(String(slice)) // ERROR "cannot .*type Slice.*type String" Index: gcc/testsuite/go.test/test/chan/select3.go =================================================================== --- gcc/testsuite/go.test/test/chan/select3.go (revision 171364) +++ gcc/testsuite/go.test/test/chan/select3.go (working copy) @@ -88,12 +88,16 @@ func main() { ch <- 7 }) - // receiving (a small number of times) from a closed channel never blocks + // receiving from a closed channel never blocks testBlock(never, func() { for i := 0; i < 10; i++ { if <-closedch != 0 { panic("expected zero value when reading from closed channel") } + if x, ok := <-closedch; x != 0 || ok { + println("closedch:", x, ok) + panic("expected 0, false from closed channel") + } } }) @@ -191,9 +195,30 @@ func main() { case <-closedch: } }) + testBlock(never, func() { + select { + case x := <-closedch: + _ = x + } + }) + testBlock(never, func() { + select { + case x, ok := <-closedch: + _, _ = x, ok + } + }) testPanic(always, func() { select { case closedch <- 7: } }) + + // select should not get confused if it sees itself + testBlock(always, func() { + c := make(chan int) + select { + case c <- 1: + case <-c: + } + }) } Index: gcc/testsuite/go.test/test/chan/perm.go =================================================================== --- gcc/testsuite/go.test/test/chan/perm.go (revision 171371) +++ gcc/testsuite/go.test/test/chan/perm.go (working copy) @@ -22,21 +22,18 @@ func main() { c <- 0 // ok <-c // ok - //TODO(rsc): uncomment when this syntax is valid for receive+check closed - // x, ok := <-c // ok - // _, _ = x, ok + x, ok := <-c // ok + _, _ = x, ok cr <- 0 // ERROR "send" <-cr // ok - //TODO(rsc): uncomment when this syntax is valid for receive+check closed - // x, ok = <-cr // ok - // _, _ = x, ok + x, ok = <-cr // ok + _, _ = x, ok cs <- 0 // ok <-cs // ERROR "receive" - ////TODO(rsc): uncomment when this syntax is valid for receive+check closed - //// x, ok = <-cs // ERROR "receive" - //// _, _ = x, ok + x, ok = <-cs // ERROR "receive" + _, _ = x, ok select { case c <- 0: // ok Index: gcc/testsuite/go.test/test/chan/doubleselect.go =================================================================== --- gcc/testsuite/go.test/test/chan/doubleselect.go (revision 171359) +++ gcc/testsuite/go.test/test/chan/doubleselect.go (working copy) @@ -21,6 +21,8 @@ var iterations *int = flag.Int("n", 1000 func sender(n int, c1, c2, c3, c4 chan<- int) { defer close(c1) defer close(c2) + defer close(c3) + defer close(c4) for i := 0; i < n; i++ { select { @@ -35,26 +37,18 @@ func sender(n int, c1, c2, c3, c4 chan<- // mux receives the values from sender and forwards them onto another channel. // It would be simplier to just have sender's four cases all be the same // channel, but this doesn't actually trigger the bug. -func mux(out chan<- int, in <-chan int) { - for { - v := <-in - if closed(in) { - close(out) - break - } +func mux(out chan<- int, in <-chan int, done chan<- bool) { + for v := range in { out <- v } + done <- true } // recver gets a steam of values from the four mux's and checks for duplicates. func recver(in <-chan int) { seen := make(map[int]bool) - for { - v := <-in - if closed(in) { - break - } + for v := range in { if _, ok := seen[v]; ok { println("got duplicate value: ", v) panic("fail") @@ -70,15 +64,23 @@ func main() { c2 := make(chan int) c3 := make(chan int) c4 := make(chan int) + done := make(chan bool) cmux := make(chan int) go sender(*iterations, c1, c2, c3, c4) - go mux(cmux, c1) - go mux(cmux, c2) - go mux(cmux, c3) - go mux(cmux, c4) + go mux(cmux, c1, done) + go mux(cmux, c2, done) + go mux(cmux, c3, done) + go mux(cmux, c4, done) + go func() { + <-done + <-done + <-done + <-done + close(cmux) + }() // We keep the recver because it might catch more bugs in the future. // However, the result of the bug linked to at the top is that we'll - // end up panicing with: "throw: bad g->status in ready". + // end up panicking with: "throw: bad g->status in ready". recver(cmux) print("PASS\n") }