gc: fix some spurious leaks
Probably will spark some discussion. ☺
R=lvd
CC=golang-dev
https://golang.org/cl/4948041
diff --git a/src/cmd/gc/esc.c b/src/cmd/gc/esc.c
index 916a089..d83a7f7 100644
--- a/src/cmd/gc/esc.c
+++ b/src/cmd/gc/esc.c
@@ -148,7 +148,7 @@
lno = setlineno(n);
- if(n->op == OFOR)
+ if(n->op == OFOR || n->op == ORANGE)
loopdepth++;
esclist(n->ninit);
@@ -160,7 +160,7 @@
esc(n->left);
esc(n->right);
- if(n->op == OFOR)
+ if(n->op == OFOR || n->op == ORANGE)
loopdepth--;
if(debug['m'] > 1)
@@ -169,13 +169,7 @@
switch(n->op) {
case ODCL:
- case ODCLFIELD:
- // a declaration ties the node to the current
- // function, but we already have that edge in
- // curfn->dcl and will follow it explicitly in
- // escflood to avoid storing redundant information
- // What does have to happen here is note if the name
- // is declared inside a looping scope.
+ // Record loop depth at declaration.
if(n->left)
n->left->escloopdepth = loopdepth;
break;
@@ -184,28 +178,10 @@
loopdepth++;
break;
- case ORANGE: // for <list> = range <right> { <nbody> }
- switch(n->type->etype) {
- case TARRAY: // i, v = range sliceorarray
- if(n->list->next)
- escassign(n->list->next->n, n->right);
- break;
- case TMAP: // k [, v] = range map
- escassign(n->list->n, n->right);
- if(n->list->next)
- escassign(n->list->next->n, n->right);
- break;
- case TCHAN: // v = range chan
- escassign(n->list->n, n->right);
- break;
- }
- loopdepth++;
- esclist(n->nbody);
- loopdepth--;
- break;
-
- case OSELRECV: // v := <-ch left: v right->op = ORECV
- escassign(n->left, n->right);
+ case ORANGE:
+ // Everything but fixed array is a dereference.
+ if(isfixedarray(n->type))
+ escassign(n->list->next->n, n->right);
break;
case OSWITCH:
@@ -216,13 +192,6 @@
escassign(ll->n->nname, n->ntest->right);
esclist(ll->n->nbody);
}
- } else {
- escassign(N, n->ntest);
- for(ll=n->list; ll; ll=ll->next) { // cases
- for(lr=ll->n->list; lr; lr=lr->next)
- escassign(N, lr->n);
- esclist(ll->n->nbody);
- }
}
break;
@@ -245,7 +214,7 @@
break;
case OSEND: // ch <- x
- escassign(&theSink, n->right); // TODO: treat as *ch = x ?
+ escassign(&theSink, n->right);
break;
case ODEFER:
@@ -271,16 +240,10 @@
escassign(&theSink, n->left);
break;
- case OCOPY:
- // left leaks to right, but the return value is harmless
- // TODO: treat as *dst = *src, rather than as dst = src
- escassign(n->left, n->right);
- break;
-
case OAPPEND:
- // See TODO for OCOPY
- for(ll=n->list->next; ll; ll=ll->next)
- escassign(n->list->n, ll->n);
+ if(!n->isddd)
+ for(ll=n->list->next; ll; ll=ll->next)
+ escassign(&theSink, ll->n); // lose track of assign to dereference
break;
case OCALLMETH:
@@ -328,32 +291,47 @@
print("%L:[%d] %#S escassign: %hN = %hN\n", lineno, loopdepth,
(curfn && curfn->nname) ? curfn->nname->sym : S, dst, src);
- // the lhs of an assignment needs recursive analysis too
- // these are the only interesting cases
- // todo:check channel case
setlineno(dst);
-
+
+ // Analyze lhs of assignment.
+ // Replace dst with theSink if we can't track it.
switch(dst->op) {
- case OINDEX:
- case OSLICE:
- // slice: "dst[x] = src" is like *(underlying array)[x] = src
- // TODO maybe this never occurs b/c of OSLICEARR and it's inserted OADDR
- if(!isfixedarray(dst->left->type))
- goto doref;
- // fallthrough; treat "dst[x] = src" as "dst = src"
+ default:
+ dump("dst", dst);
+ fatal("escassign: unexpected dst");
+
+ case OARRAYLIT:
+ case OCLOSURE:
+ case OCONV:
+ case OCONVIFACE:
+ case OCONVNOP:
+ case OMAPLIT:
+ case OSTRUCTLIT:
+ break;
+
+ case ONAME:
+ if(dst->class == PEXTERN)
+ dst = &theSink;
+ break;
case ODOT: // treat "dst.x = src" as "dst = src"
escassign(dst->left, src);
return;
- case OINDEXMAP:
- escassign(&theSink, dst->right); // map key is put in map
- // fallthrough
+ case OINDEX:
+ if(isfixedarray(dst->left->type)) {
+ escassign(dst->left, src);
+ return;
+ }
+ dst = &theSink; // lose track of dereference
+ break;
case OIND:
case ODOTPTR:
- case OSLICEARR: // ->left is the OADDR of the array
- doref:
- // assignment to dereferences: for now we lose track
- escassign(&theSink, src);
- return;
+ dst = &theSink; // lose track of dereference
+ break;
+ case OINDEXMAP:
+ // lose track of key and value
+ escassign(&theSink, dst->right);
+ dst = &theSink;
+ break;
}
if(src->typecheck == 0 && src->op != OKEY) {
@@ -380,10 +358,23 @@
case ODOT:
case ODOTTYPE:
case ODOTTYPE2:
+ case OSLICE:
+ case OSLICEARR:
// Conversions, field access, slice all preserve the input value.
escassign(dst, src->left);
break;
+ case OAPPEND:
+ // Append returns first argument.
+ escassign(dst, src->list->n);
+ break;
+
+ case OINDEX:
+ // Index of array preserves input value.
+ if(isfixedarray(src->left->type))
+ escassign(dst, src->left);
+ break;
+
case OARRAYLIT:
case OSTRUCTLIT:
case OMAPLIT:
@@ -410,20 +401,6 @@
escflows(dst, src);
escfunc(src);
break;
-
- // end of the leaf cases. no calls to escflows() in the cases below.
- case OAPPEND:
- escassign(dst, src->list->n);
- break;
-
- case OSLICEARR: // like an implicit OIND to the underlying buffer, but typecheck has inserted an OADDR
- case OSLICESTR:
- case OSLICE:
- case OINDEX:
- case OINDEXMAP:
- // the big thing flows, the keys just need checking
- escassign(dst, src->left);
- break;
}
pdepth--;
@@ -525,10 +502,6 @@
if(debug['m']>2)
print("%L::flows:: %hN <- %hN\n", lineno, dst, src);
- // Assignments to global variables get lumped into theSink.
- if(dst->op == ONAME && dst->class == PEXTERN)
- dst = &theSink;
-
if(dst->escflowsrc == nil) {
dsts = list(dsts, dst);
dstcount++;
diff --git a/test/escape2.go b/test/escape2.go
index abbb574..f9d377a 100644
--- a/test/escape2.go
+++ b/test/escape2.go
@@ -613,3 +613,89 @@
return [2]*int{ x, nil }
}
+// does not leak c
+func foo93(c chan *int) *int {
+ for v := range c {
+ return v
+ }
+ return nil
+}
+
+// does not leak m
+func foo94(m map[*int]*int, b bool) *int {
+ for k, v := range m {
+ if b {
+ return k
+ }
+ return v
+ }
+ return nil
+}
+
+// does leak x
+func foo95(m map[*int]*int, x *int) { // ERROR "leaking param: NAME-x"
+ m[x] = x
+}
+
+// does not leak m
+func foo96(m []*int) *int {
+ return m[0]
+}
+
+// does leak m
+func foo97(m [1]*int) *int { // ERROR "leaking param: NAME-m"
+ return m[0]
+}
+
+// does not leak m
+func foo98(m map[int]*int) *int {
+ return m[0]
+}
+
+// does leak m
+func foo99(m *[1]*int) []*int { // ERROR "leaking param: NAME-m"
+ return m[:]
+}
+
+// does not leak m
+func foo100(m []*int) *int {
+ for _, v := range m {
+ return v
+ }
+ return nil
+}
+
+// does leak m
+func foo101(m [1]*int) *int { // ERROR "leaking param: NAME-m"
+ for _, v := range m {
+ return v
+ }
+ return nil
+}
+
+// does leak x
+func foo102(m []*int, x *int) { // ERROR "leaking param: NAME-x"
+ m[0] = x
+}
+
+// does not leak x
+func foo103(m [1]*int, x *int) {
+ m[0] = x
+}
+
+var y []*int
+
+// does not leak x
+func foo104(x []*int) {
+ copy(y, x)
+}
+
+// does not leak x
+func foo105(x []*int) {
+ _ = append(y, x...)
+}
+
+// does leak x
+func foo106(x *int) { // ERROR "leaking param: NAME-x"
+ _ = append(y, x)
+}