Commit eaa87200 authored by Dmitry Vyukov's avatar Dmitry Vyukov

cmd/gc: fix capturing by value for range statements

Kindly detected by race builders by failing TestRaceRange.
ORANGE typecheck does not increment decldepth around body.

Change-Id: I0df5f310cb3370a904c94d9647a9cf0f15729075
Reviewed-on: https://go-review.googlesource.com/3507Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent 8e2423a6
...@@ -18,14 +18,25 @@ typecheckrange(Node *n) ...@@ -18,14 +18,25 @@ typecheckrange(Node *n)
Node *v1, *v2; Node *v1, *v2;
NodeList *ll; NodeList *ll;
// Typechecking order is important here:
// 0. first typecheck range expression (slice/map/chan),
// it is evaluated only once and so logically it is not part of the loop.
// 1. typcheck produced values,
// this part can declare new vars and so it must be typechecked before body,
// because body can contain a closure that captures the vars.
// 2. decldepth++ to denote loop body.
// 3. typecheck body.
// 4. decldepth--.
typecheck(&n->right, Erv);
if((t = n->right->type) == T)
goto out;
// delicate little dance. see typecheckas2 // delicate little dance. see typecheckas2
for(ll=n->list; ll; ll=ll->next) for(ll=n->list; ll; ll=ll->next)
if(ll->n->defn != n) if(ll->n->defn != n)
typecheck(&ll->n, Erv | Easgn); typecheck(&ll->n, Erv | Easgn);
typecheck(&n->right, Erv);
if((t = n->right->type) == T)
goto out;
if(isptr[t->etype] && isfixedarray(t->type)) if(isptr[t->etype] && isfixedarray(t->type))
t = t->type; t = t->type;
n->type = t; n->type = t;
...@@ -106,7 +117,9 @@ out: ...@@ -106,7 +117,9 @@ out:
if(ll->n->typecheck == 0) if(ll->n->typecheck == 0)
typecheck(&ll->n, Erv | Easgn); typecheck(&ll->n, Erv | Easgn);
decldepth++;
typechecklist(n->nbody, Etop); typechecklist(n->nbody, Etop);
decldepth--;
} }
void void
......
...@@ -2828,7 +2828,8 @@ checkassign(Node *stmt, Node *n) ...@@ -2828,7 +2828,8 @@ checkassign(Node *stmt, Node *n)
{ {
Node *r, *l; Node *r, *l;
if(n->defn != stmt) { // Variables declared in ORANGE are assigned on every iteration.
if(n->defn != stmt || stmt->op == ORANGE) {
r = outervalue(n); r = outervalue(n);
for(l = n; l != r; l = l->left) { for(l = n; l != r; l = l->left) {
l->assigned = 1; l->assigned = 1;
......
...@@ -10,56 +10,109 @@ ...@@ -10,56 +10,109 @@
package main package main
func main() { func main() {
type X struct { {
v int type X struct {
} v int
var x X }
func() { var x X
x.v++ func() {
}() x.v++
if x.v != 1 { }()
panic("x.v != 1") if x.v != 1 {
} panic("x.v != 1")
}
type Y struct { type Y struct {
X X
} }
var y Y var y Y
func() { func() {
y.v = 1 y.v = 1
}() }()
if y.v != 1 { if y.v != 1 {
panic("y.v != 1") panic("y.v != 1")
}
} }
type Z struct { {
a [3]byte type Z struct {
} a [3]byte
var z Z }
func() { var z Z
i := 0 func() {
for z.a[1] = 1; i < 10; i++ { i := 0
for z.a[1] = 1; i < 10; i++ {
}
}()
if z.a[1] != 1 {
panic("z.a[1] != 1")
} }
}()
if z.a[1] != 1 {
panic("z.a[1] != 1")
} }
w := 0 {
tmp := 0 w := 0
f := func() { tmp := 0
if w != 1 { f := func() {
panic("w != 1") if w != 1 {
panic("w != 1")
}
} }
}
func() {
tmp = w // force capture of w, but do not write to it yet
_ = tmp
func() { func() {
tmp = w // force capture of w, but do not write to it yet
_ = tmp
func() { func() {
w++ // write in a nested closure func() {
w++ // write in a nested closure
}()
}() }()
}() }()
}() f()
f() }
{
var g func() int
for i := range [2]int{} {
if i == 0 {
g = func() int {
return i // test that we capture by ref here, i is mutated on every interation
}
}
}
if g() != 1 {
panic("g() != 1")
}
}
{
var g func() int
q := 0
for range [2]int{} {
q++
g = func() int {
return q // test that we capture by ref here
// q++ must on a different decldepth than q declaration
}
}
if g() != 2 {
panic("g() != 2")
}
}
{
var g func() int
var a [2]int
q := 0
for a[func() int {
q++
return 0
}()] = range [2]int{} {
g = func() int {
return q // test that we capture by ref here
// q++ must on a different decldepth than q declaration
}
}
if g() != 2 {
panic("g() != 2")
}
}
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment