mirror of
https://github.com/golang/go
synced 2024-11-05 18:36:08 +00:00
cmd/gc: fix eval order in select
Ordinary variable load was assumed to be not worth saving, but not if one of the function calls later might change its value. Fixes #4313. R=ken2 CC=golang-dev https://golang.org/cl/6997047
This commit is contained in:
parent
b3bb4bd292
commit
1b3244e0db
4 changed files with 41 additions and 11 deletions
|
@ -276,11 +276,11 @@ orderstmt(Node *n, NodeList **out)
|
|||
case OSELRECV2:
|
||||
orderexprinplace(&r->left);
|
||||
orderexprinplace(&r->ntest);
|
||||
orderexpr(&r->right->left, out);
|
||||
orderexpr(&r->right->left, &l->n->ninit);
|
||||
break;
|
||||
case OSEND:
|
||||
orderexpr(&r->left, out);
|
||||
orderexpr(&r->right, out);
|
||||
orderexpr(&r->left, &l->n->ninit);
|
||||
orderexpr(&r->right, &l->n->ninit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -297,15 +297,15 @@ walkselect(Node *sel)
|
|||
setlineno(cas);
|
||||
n = cas->left;
|
||||
r = nod(OIF, N, N);
|
||||
r->nbody = cas->ninit;
|
||||
r->ninit = cas->ninit;
|
||||
cas->ninit = nil;
|
||||
if(n != nil) {
|
||||
r->nbody = concat(r->nbody, n->ninit);
|
||||
r->ninit = concat(r->ninit, n->ninit);
|
||||
n->ninit = nil;
|
||||
}
|
||||
if(n == nil) {
|
||||
// selectdefault(sel *byte);
|
||||
r->ntest = mkcall("selectdefault", types[TBOOL], &init, var);
|
||||
r->ntest = mkcall("selectdefault", types[TBOOL], &r->ninit, var);
|
||||
} else {
|
||||
switch(n->op) {
|
||||
default:
|
||||
|
@ -313,25 +313,25 @@ walkselect(Node *sel)
|
|||
|
||||
case OSEND:
|
||||
// selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
|
||||
n->left = safeexpr(n->left, &r->ninit);
|
||||
n->left = localexpr(safeexpr(n->left, &r->ninit), n->left->type, &r->ninit);
|
||||
n->right = localexpr(n->right, n->left->type->type, &r->ninit);
|
||||
n->right = nod(OADDR, n->right, N);
|
||||
n->right->etype = 1; // pointer does not escape
|
||||
typecheck(&n->right, Erv);
|
||||
r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL],
|
||||
&init, var, n->left, n->right);
|
||||
&r->ninit, var, n->left, n->right);
|
||||
break;
|
||||
|
||||
case OSELRECV:
|
||||
// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
|
||||
r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->left->type), types[TBOOL],
|
||||
&init, var, n->right->left, n->left);
|
||||
&r->ninit, var, n->right->left, n->left);
|
||||
break;
|
||||
|
||||
case OSELRECV2:
|
||||
// selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
|
||||
r->ntest = mkcall1(chanfn("selectrecv2", 2, n->right->left->type), types[TBOOL],
|
||||
&init, var, n->right->left, n->left, n->ntest);
|
||||
&r->ninit, var, n->right->left, n->left, n->ntest);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2040,11 +2040,13 @@ cheapexpr(Node *n, NodeList **init)
|
|||
|
||||
/*
|
||||
* return n in a local variable of type t if it is not already.
|
||||
* the value is guaranteed not to change except by direct
|
||||
* assignment to it.
|
||||
*/
|
||||
Node*
|
||||
localexpr(Node *n, Type *t, NodeList **init)
|
||||
{
|
||||
if(n->op == ONAME &&
|
||||
if(n->op == ONAME && !n->addrtaken &&
|
||||
(n->class == PAUTO || n->class == PPARAM || n->class == PPARAMOUT) &&
|
||||
convertop(n->type, t, nil) == OCONVNOP)
|
||||
return n;
|
||||
|
|
28
test/fixedbugs/issue4313.go
Normal file
28
test/fixedbugs/issue4313.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
// run
|
||||
|
||||
// Copyright 2012 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.
|
||||
|
||||
// Order of operations in select.
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
c := make(chan int, 1)
|
||||
x := 0
|
||||
select {
|
||||
case c <- x: // should see x = 0, not x = 42 (after makec)
|
||||
case <-makec(&x): // should be evaluated only after c and x on previous line
|
||||
}
|
||||
y := <-c
|
||||
if y != 0 {
|
||||
panic(y)
|
||||
}
|
||||
}
|
||||
|
||||
func makec(px *int) chan bool {
|
||||
if false { for {} }
|
||||
*px = 42
|
||||
return make(chan bool, 0)
|
||||
}
|
Loading…
Reference in a new issue