The Go language was clarified so that a send/receive on a nil channel blocks forever, which makes nil channels not very useful but consistent with select. This patch implements that. Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu. Committed to mainline.
Ian
Index: libgo/runtime/go-reflect-chan.c =================================================================== --- libgo/runtime/go-reflect-chan.c (revision 178910) +++ libgo/runtime/go-reflect-chan.c (working copy) @@ -45,18 +45,13 @@ chansend (struct __go_channel_type *ct, void *pv; __go_assert (ct->__common.__code == GO_CHAN); - __go_assert (__go_type_descriptors_equal (ct->__element_type, - channel->element_type)); - if (channel == NULL) - __go_panic_msg ("send to nil channel"); - - if (__go_is_pointer_type (channel->element_type)) + if (__go_is_pointer_type (ct->__element_type)) pv = &val_i; else pv = (void *) val_i; - element_size = channel->element_type->__size; + element_size = ct->__element_type->__size; if (element_size <= sizeof (uint64_t)) { union @@ -112,12 +107,10 @@ chanrecv (struct __go_channel_type *ct, struct chanrecv_ret ret; __go_assert (ct->__common.__code == GO_CHAN); - __go_assert (__go_type_descriptors_equal (ct->__element_type, - channel->element_type)); - element_size = channel->element_type->__size; + element_size = ct->__element_type->__size; - if (__go_is_pointer_type (channel->element_type)) + if (__go_is_pointer_type (ct->__element_type)) pv = &ret.val; else { Index: libgo/runtime/channel.h =================================================================== --- libgo/runtime/channel.h (revision 178784) +++ libgo/runtime/channel.h (working copy) @@ -147,3 +147,6 @@ extern void __go_builtin_close (struct _ extern int __go_chan_len (struct __go_channel *); extern int __go_chan_cap (struct __go_channel *); + +extern uintptr_t __go_select (uintptr_t, _Bool, struct __go_channel **, + _Bool *); Index: libgo/runtime/go-send-big.c =================================================================== --- libgo/runtime/go-send-big.c (revision 178784) +++ libgo/runtime/go-send-big.c (working copy) @@ -17,7 +17,10 @@ __go_send_big (struct __go_channel* chan size_t offset; if (channel == NULL) - __go_panic_msg ("send to nil channel"); + { + // Block forever. + __go_select (0, 0, NULL, NULL); + } element_size = channel->element_type->__size; alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t); Index: libgo/runtime/go-send-nb-small.c =================================================================== --- libgo/runtime/go-send-nb-small.c (revision 178784) +++ libgo/runtime/go-send-nb-small.c (working copy) @@ -93,6 +93,9 @@ __go_send_nonblocking_acquire (struct __ _Bool __go_send_nonblocking_small (struct __go_channel *channel, uint64_t val) { + if (channel == NULL) + return 0; + __go_assert (channel->element_type->__size <= sizeof (uint64_t)); if (!__go_send_nonblocking_acquire (channel)) Index: libgo/runtime/chan.goc =================================================================== --- libgo/runtime/chan.goc (revision 178784) +++ libgo/runtime/chan.goc (working copy) @@ -6,6 +6,8 @@ package runtime #include "config.h" #include "channel.h" +#define nil NULL + typedef _Bool bool; typedef unsigned char byte; typedef struct __go_channel chan; @@ -13,7 +15,7 @@ typedef struct __go_channel chan; /* Do a channel receive with closed status. */ func chanrecv2(c *chan, val *byte) (received bool) { - uintptr_t element_size = c->element_type->__size; + uintptr_t element_size = c == nil ? 0 : c->element_type->__size; if (element_size > 8) { return __go_receive_big(c, val, 0); } else { Index: libgo/runtime/go-send-nb-big.c =================================================================== --- libgo/runtime/go-send-nb-big.c (revision 178784) +++ libgo/runtime/go-send-nb-big.c (working copy) @@ -15,6 +15,9 @@ __go_send_nonblocking_big (struct __go_c size_t alloc_size; size_t offset; + if (channel == NULL) + return 0; + element_size = channel->element_type->__size; alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t); Index: libgo/runtime/go-send-small.c =================================================================== --- libgo/runtime/go-send-small.c (revision 178784) +++ libgo/runtime/go-send-small.c (working copy) @@ -145,7 +145,10 @@ void __go_send_small (struct __go_channel *channel, uint64_t val, _Bool for_select) { if (channel == NULL) - __go_panic_msg ("send to nil channel"); + { + // Block forever. + __go_select (0, 0, NULL, NULL); + } __go_assert (channel->element_type->__size <= sizeof (uint64_t)); Index: libgo/runtime/go-rec-big.c =================================================================== --- libgo/runtime/go-rec-big.c (revision 178784) +++ libgo/runtime/go-rec-big.c (working copy) @@ -20,7 +20,10 @@ __go_receive_big (struct __go_channel *c size_t offset; if (channel == NULL) - __go_panic_msg ("receive from nil channel"); + { + /* Block forever. */ + __go_select (0, 0, NULL, NULL); + } element_size = channel->element_type->__size; alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t); Index: libgo/runtime/go-rec-nb-small.c =================================================================== --- libgo/runtime/go-rec-nb-small.c (revision 178784) +++ libgo/runtime/go-rec-nb-small.c (working copy) @@ -97,6 +97,14 @@ __go_receive_nonblocking_small (struct _ uintptr_t element_size; struct __go_receive_nonblocking_small ret; + if (channel == NULL) + { + ret.__val = 0; + ret.__success = 0; + ret.__closed = 0; + return ret; + } + element_size = channel->element_type->__size; __go_assert (element_size <= sizeof (uint64_t)); Index: libgo/runtime/go-rec-nb-big.c =================================================================== --- libgo/runtime/go-rec-nb-big.c (revision 178784) +++ libgo/runtime/go-rec-nb-big.c (working copy) @@ -18,6 +18,13 @@ __go_receive_nonblocking_big (struct __g size_t alloc_size; size_t offset; + if (channel == NULL) + { + if (closed != NULL) + *closed = 0; + return 0; + } + element_size = channel->element_type->__size; alloc_size = (element_size + sizeof (uint64_t) - 1) / sizeof (uint64_t); Index: libgo/runtime/go-rec-small.c =================================================================== --- libgo/runtime/go-rec-small.c (revision 178784) +++ libgo/runtime/go-rec-small.c (working copy) @@ -270,7 +270,10 @@ __go_receive_small_closed (struct __go_c uint64_t ret; if (channel == NULL) - __go_panic_msg ("receive from nil channel"); + { + /* Block forever. */ + __go_select (0, 0, NULL, NULL); + } element_size = channel->element_type->__size; __go_assert (element_size <= sizeof (uint64_t)); Index: gcc/testsuite/go.test/test/chan/select3.go =================================================================== --- gcc/testsuite/go.test/test/chan/select3.go (revision 178784) +++ gcc/testsuite/go.test/test/chan/select3.go (working copy) @@ -58,15 +58,15 @@ func main() { closedch := make(chan int) close(closedch) - // sending/receiving from a nil channel outside a select panics - testPanic(always, func() { + // sending/receiving from a nil channel blocks + testBlock(always, func() { nilch <- 7 }) - testPanic(always, func() { + testBlock(always, func() { <-nilch }) - // sending/receiving from a nil channel inside a select never panics + // sending/receiving from a nil channel inside a select is never selected testPanic(never, func() { select { case nilch <- 7: