The Go frontend was miscompiling
        select {
        case v, ok := <- c:
                // v and ok had the wrong values here
        }
This patch fixes the problem.  Bootstrapped and ran Go testsuite on
x86_64-unknown-linux-gnu.  Committed to mainline.

Many things to Aram Hăvărneanu who very patiently tracked down the
miscompilation.

Ian

Index: gcc/go/gofrontend/statements.cc
===================================================================
--- gcc/go/gofrontend/statements.cc	(revision 180838)
+++ gcc/go/gofrontend/statements.cc	(working copy)
@@ -4559,10 +4559,26 @@ Select_clauses::Select_clause::lower(Gog
 							  loc);
       Statement* s = Statement::make_tuple_receive_assignment(val, closed, ref,
 							      true, loc);
+
       // We have to put S in STATEMENTS_, because that is where the
       // variables are declared.
+
       go_assert(this->statements_ != NULL);
-      this->statements_->add_statement_at_front(s);
+
+      // Skip the variable declaration statements themselves.
+      size_t skip = 1;
+      if (this->var_ != NULL)
+	skip = 2;
+
+      // Verify that we are only skipping variable declarations.
+      size_t i = 0;
+      for (Block::iterator p = this->statements_->begin();
+	   i < skip && p != this->statements_->end();
+	   ++p, ++i)
+	go_assert((*p)->variable_declaration_statement() != NULL);
+
+      this->statements_->insert_statement_before(skip, s);
+
       // We have to lower STATEMENTS_ again, to lower the tuple
       // receive assignment we just added.
       gogo->lower_block(function, this->statements_);
@@ -4655,7 +4671,8 @@ Select_clauses::Select_clause::dump_clau
         {
           ast_dump_context->dump_expression(this->channel_);
           ast_dump_context->ostream() << " <- " ;
-          ast_dump_context->dump_expression(this->val_);
+	  if (this->val_ != NULL)
+	    ast_dump_context->dump_expression(this->val_);
         }
       else
         {
@@ -4667,8 +4684,7 @@ Select_clauses::Select_clause::dump_clau
               ast_dump_context->ostream() << " , " ;
               ast_dump_context->dump_expression(this->closed_);
             }
-          if (this->closedvar_ != NULL ||
-              this->var_ != NULL)
+          if (this->closedvar_ != NULL || this->var_ != NULL)
             ast_dump_context->ostream() << " := " ;
 
           ast_dump_context->ostream() << " <- " ;
Index: gcc/testsuite/go.test/test/closedchan.go
===================================================================
--- gcc/testsuite/go.test/test/closedchan.go	(revision 180838)
+++ gcc/testsuite/go.test/test/closedchan.go	(working copy)
@@ -11,6 +11,10 @@
 
 package main
 
+import "os"
+
+var failed bool
+
 type Chan interface {
 	Send(int)
 	Nbsend(int) bool
@@ -225,19 +229,23 @@ func test1(c Chan) {
 		// recv a close signal (a zero value)
 		if x := c.Recv(); x != 0 {
 			println("test1: recv on closed:", x, c.Impl())
+			failed = true
 		}
 		if x, ok := c.Recv2(); x != 0 || ok {
 			println("test1: recv2 on closed:", x, ok, c.Impl())
+			failed = true
 		}
 
 		// 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())
+			failed = true
 		}
 		x, ok, selected := c.Nbrecv2()
 		if x != 0 || ok || !selected {
 			println("test1: recv2 on closed nb:", x, ok, selected, c.Impl())
+			failed = true
 		}
 	}
 
@@ -247,12 +255,14 @@ func test1(c Chan) {
 	// the value should have been discarded.
 	if x := c.Recv(); x != 0 {
 		println("test1: recv on closed got non-zero after send on closed:", x, c.Impl())
+		failed = true
 	}
 
 	// similarly Send.
 	shouldPanic(func() { c.Send(2) })
 	if x := c.Recv(); x != 0 {
 		println("test1: recv on closed got non-zero after send on closed:", x, c.Impl())
+		failed = true
 	}
 }
 
@@ -260,6 +270,7 @@ func testasync1(c Chan) {
 	// 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())
+		failed = true
 	}
 
 	test1(c)
@@ -269,6 +280,7 @@ func testasync2(c Chan) {
 	// 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())
+		failed = true
 	}
 
 	test1(c)
@@ -278,6 +290,7 @@ func testasync3(c Chan) {
 	// should be able to get the last value via Nbrecv
 	if x, selected := c.Nbrecv(); x != 1 || !selected {
 		println("testasync2: Nbrecv did not get 1, true:", x, selected, c.Impl())
+		failed = true
 	}
 
 	test1(c)
@@ -287,6 +300,7 @@ 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())
+		failed = true
 	}
 	test1(c)
 }
@@ -327,4 +341,19 @@ func main() {
 			testclosed(mk(closedasync()))
 		}
 	}
+	
+	var ch chan int	
+	shouldPanic(func() {
+		close(ch)
+	})
+	
+	ch = make(chan int)
+	close(ch)
+	shouldPanic(func() {
+		close(ch)
+	})
+
+	if failed {
+		os.Exit(1)
+	}
 }

Reply via email to