mirror of
https://github.com/golang/go
synced 2024-11-26 02:17:58 -07: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
@ -276,11 +276,11 @@ orderstmt(Node *n, NodeList **out)
|
|||||||
case OSELRECV2:
|
case OSELRECV2:
|
||||||
orderexprinplace(&r->left);
|
orderexprinplace(&r->left);
|
||||||
orderexprinplace(&r->ntest);
|
orderexprinplace(&r->ntest);
|
||||||
orderexpr(&r->right->left, out);
|
orderexpr(&r->right->left, &l->n->ninit);
|
||||||
break;
|
break;
|
||||||
case OSEND:
|
case OSEND:
|
||||||
orderexpr(&r->left, out);
|
orderexpr(&r->left, &l->n->ninit);
|
||||||
orderexpr(&r->right, out);
|
orderexpr(&r->right, &l->n->ninit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,15 +297,15 @@ walkselect(Node *sel)
|
|||||||
setlineno(cas);
|
setlineno(cas);
|
||||||
n = cas->left;
|
n = cas->left;
|
||||||
r = nod(OIF, N, N);
|
r = nod(OIF, N, N);
|
||||||
r->nbody = cas->ninit;
|
r->ninit = cas->ninit;
|
||||||
cas->ninit = nil;
|
cas->ninit = nil;
|
||||||
if(n != nil) {
|
if(n != nil) {
|
||||||
r->nbody = concat(r->nbody, n->ninit);
|
r->ninit = concat(r->ninit, n->ninit);
|
||||||
n->ninit = nil;
|
n->ninit = nil;
|
||||||
}
|
}
|
||||||
if(n == nil) {
|
if(n == nil) {
|
||||||
// selectdefault(sel *byte);
|
// selectdefault(sel *byte);
|
||||||
r->ntest = mkcall("selectdefault", types[TBOOL], &init, var);
|
r->ntest = mkcall("selectdefault", types[TBOOL], &r->ninit, var);
|
||||||
} else {
|
} else {
|
||||||
switch(n->op) {
|
switch(n->op) {
|
||||||
default:
|
default:
|
||||||
@ -313,25 +313,25 @@ walkselect(Node *sel)
|
|||||||
|
|
||||||
case OSEND:
|
case OSEND:
|
||||||
// selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
|
// 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 = localexpr(n->right, n->left->type->type, &r->ninit);
|
||||||
n->right = nod(OADDR, n->right, N);
|
n->right = nod(OADDR, n->right, N);
|
||||||
n->right->etype = 1; // pointer does not escape
|
n->right->etype = 1; // pointer does not escape
|
||||||
typecheck(&n->right, Erv);
|
typecheck(&n->right, Erv);
|
||||||
r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL],
|
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;
|
break;
|
||||||
|
|
||||||
case OSELRECV:
|
case OSELRECV:
|
||||||
// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
|
// selectrecv(sel *byte, hchan *chan any, elem *any) (selected bool);
|
||||||
r->ntest = mkcall1(chanfn("selectrecv", 2, n->right->left->type), types[TBOOL],
|
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;
|
break;
|
||||||
|
|
||||||
case OSELRECV2:
|
case OSELRECV2:
|
||||||
// selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
|
// selectrecv2(sel *byte, hchan *chan any, elem *any, received *bool) (selected bool);
|
||||||
r->ntest = mkcall1(chanfn("selectrecv2", 2, n->right->left->type), types[TBOOL],
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2040,11 +2040,13 @@ cheapexpr(Node *n, NodeList **init)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* return n in a local variable of type t if it is not already.
|
* 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*
|
Node*
|
||||||
localexpr(Node *n, Type *t, NodeList **init)
|
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) &&
|
(n->class == PAUTO || n->class == PPARAM || n->class == PPARAMOUT) &&
|
||||||
convertop(n->type, t, nil) == OCONVNOP)
|
convertop(n->type, t, nil) == OCONVNOP)
|
||||||
return n;
|
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 New Issue
Block a user