int pdepth; // for debug printing in recursions.
int dstcount, edgecount; // diagnostic
NodeList* noesc; // list of possible non-escaping nodes, for printing
+ int recursive; // recursive function or group of mutually recursive functions.
};
-static Strlit *tags[16] = { nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil };
+static Strlit *tags[16];
static Strlit*
mktag(int mask)
NodeList *l;
EscState es, *e;
- USED(recursive);
-
memset(&es, 0, sizeof es);
e = &es;
e->theSink.op = ONAME;
e->theSink.class = PEXTERN;
e->theSink.sym = lookup(".sink");
e->theSink.escloopdepth = -1;
+ e->recursive = recursive;
for(l=all; l; l=l->next)
if(l->n->op == ODCLFUNC)
NodeList *ll;
int saveld;
+// print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":"");
+
if(func->esc != 1)
fatal("repeat escfunc %N", func->nname);
func->esc = EscFuncStarted;
}
}
+ // in a mutually recursive group we lose track of the return values
+ if(e->recursive)
+ for(ll=curfn->dcl; ll; ll=ll->next)
+ if(ll->n->op == ONAME && ll->n->class == PPARAMOUT)
+ escflows(e, &e->theSink, ll->n);
+
escloopdepthlist(e, curfn->nbody);
esclist(e, curfn->nbody);
curfn = savefn;
}
// See case OLABEL in escloopdepth above
// else if(n->left->sym->label == nil)
- // fatal("escape anaylysis missed or messed up a label: %+N", n);
+ // fatal("escape analysis missed or messed up a label: %+N", n);
n->left->sym->label = nil;
break;
escassign(e, &e->theSink, ll->n);
break;
+ case OCALLMETH:
+ case OCALLFUNC:
+ case OCALLINTER:
+ esccall(e, n);
+ break;
+
+ case OAS2FUNC: // x,y = f()
+ // esccall already done on n->rlist->n. tie it's escretval to n->list
+ lr=n->rlist->n->escretval;
+ for(ll=n->list; lr && ll; lr=lr->next, ll=ll->next)
+ escassign(e, ll->n, lr->n);
+ if(lr || ll)
+ fatal("esc oas2func");
+ break;
+
case ORETURN:
+ ll=n->list;
if(count(n->list) == 1 && curfn->type->outtuple > 1) {
// OAS2FUNC in disguise
- break;
+ // esccall already done on n->list->n
+ // tie n->list->n->escretval to curfn->dcl PPARAMOUT's
+ ll = n->list->n->escretval;
}
- ll=n->list;
for(lr = curfn->dcl; lr && ll; lr=lr->next) {
if (lr->n->op != ONAME || lr->n->class != PPARAMOUT)
continue;
escassign(e, &e->theSink, ll->n); // lose track of assign to dereference
break;
- case OCALLMETH:
- case OCALLFUNC:
- case OCALLINTER:
- esccall(e, n);
- break;
-
case OCONV:
case OCONVNOP:
case OCONVIFACE:
escflows(e, dst, src);
break;
+ case OCALLMETH:
+ case OCALLFUNC:
+ case OCALLINTER:
+ if(count(src->escretval) != 1)
+ fatal("escassign from call %+N", src);
+ escflows(e, dst, src->escretval->n);
+ break;
+
case ODOT:
// A non-pointer escaping from a struct does not concern us.
if(src->type && !haspointers(src->type))
lineno = lno;
}
+static void
+escassignfromtag(EscState *e, Strlit *note, NodeList *dsts, Node *src)
+{
+ int em;
+
+ em = parsetag(note);
+
+ if(em == EscUnknown) {
+ escassign(e, &e->theSink, src);
+ return;
+ }
+
+ for(em >>= EscBits; em && dsts; em >>= 1, dsts=dsts->next)
+ if(em & 1)
+ escassign(e, dsts->n, src);
+
+ if (em != 0 && dsts == nil)
+ fatal("corrupt esc tag %Z or messed up escretval list\n", note);
+}
+
// This is a bit messier than fortunate, pulled out of esc's big
// switch for clarity. We either have the paramnodes, which may be
// connected to other things throug flows or we have the parameter type
NodeList *ll, *lr;
Node *a, *fn, *src;
Type *t, *fntype;
+ char buf[40];
+ int i;
fn = N;
switch(n->op) {
ll = n->list;
if(n->list != nil && n->list->next == nil) {
a = n->list->n;
- if(a->type->etype == TSTRUCT && a->type->funarg) {
- // f(g()).
- // Since f's arguments are g's results and
- // all function results escape, we're done.
- ll = nil;
- }
+ if(a->type->etype == TSTRUCT && a->type->funarg) // f(g()).
+ ll = a->escretval;
}
if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype && fn->defn->esc < EscFuncTagged) {
- // Local function in this round. Incorporate into flow graph.
- if(fn->defn->esc == EscFuncUnknown)
+ // function in same mutually recursive group. Incorporate into flow graph.
+// print("esc local fn: %N\n", fn->ntype);
+ if(fn->defn->esc == EscFuncUnknown || n->escretval != nil)
fatal("graph inconsistency");
+ // set up out list on this call node
+ for(lr=fn->ntype->rlist; lr; lr=lr->next)
+ n->escretval = list(n->escretval, lr->n->left); // type.rlist -> dclfield -> ONAME (PPARAMOUT)
+
// Receiver.
if(n->op != OCALLFUNC)
escassign(e, fn->ntype->left->left, n->left->left);
// "..." arguments are untracked
for(; ll; ll=ll->next)
escassign(e, &e->theSink, ll->n);
+
return;
}
// Imported or completely analyzed function. Use the escape tags.
- if(n->op != OCALLFUNC) {
- t = getthisx(fntype)->type;
- if(parsetag(t->note) != EscNone)
- escassign(e, &e->theSink, n->left->left);
+ if(n->escretval != nil)
+ fatal("esc already decorated call %+N\n", n);
+
+ // set up out list on this call node with dummy auto ONAMES in the current (calling) function.
+ i = 0;
+ for(t=getoutargx(fntype)->type; t; t=t->down) {
+ src = nod(ONAME, N, N);
+ snprint(buf, sizeof buf, ".dum%d", i++);
+ src->sym = lookup(buf);
+ src->type = t->type;
+ src->class = PAUTO;
+ src->curfn = curfn;
+ src->escloopdepth = e->loopdepth;
+ src->used = 1;
+ src->lineno = n->lineno;
+ n->escretval = list(n->escretval, src);
}
+
+// print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval);
+
+ // Receiver.
+ if(n->op != OCALLFUNC)
+ escassignfromtag(e, getthisx(fntype)->type->note, n->escretval, n->left->left);
+
for(t=getinargx(fntype)->type; ll; ll=ll->next) {
src = ll->n;
if(t->isddd && !n->isddd) {
e->noesc = list(e->noesc, src);
n->right = src;
}
- if(parsetag(t->note) != EscNone)
- escassign(e, &e->theSink, src);
+ escassignfromtag(e, t->note, n->escretval, src);
if(src != ll->n)
break;
t = t->down;
return &x[0] // ERROR "&x.0. escapes to heap"
}
-func foo75(z *int) { // ERROR "leaking param: z"
+func foo75(z *int) { // ERROR "z does not escape"
myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
}
func foo75a(z *int) { // ERROR "z does not escape"
- myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
+ myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
+}
+
+func foo75esc(z *int) { // ERROR "leaking param: z"
+ gxx = myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
+}
+
+func foo75aesc(z *int) { // ERROR "z does not escape"
+ var ppi **interface{} // assignments to pointer dereferences lose track
+ *ppi = myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
}
func foo76(z *int) { // ERROR "leaking param: z"
}
func foo76a(z *int) { // ERROR "leaking param: z"
- myprint1(nil, z) // ERROR "[.][.][.] argument escapes to heap"
+ myprint1(nil, z) // ERROR "[.][.][.] argument does not escape"
}
func foo76b() {
}
func foo76c() {
- myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
+ myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
}
func foo76d() {
}
func foo76e() {
- defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
+ defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
}
func foo76f() {
myprint(nil, z...) // z does not escape
}
-func foo77a(z []interface{}) { // ERROR "leaking param: z"
+func foo77a(z []interface{}) { // ERROR "z does not escape"
myprint1(nil, z...)
}
+func foo77b(z []interface{}) { // ERROR "leaking param: z"
+ var ppi **interface{}
+ *ppi = myprint1(nil, z...)
+}
+
func foo78(z int) *int { // ERROR "moved to heap: z"
return &z // ERROR "&z escapes to heap"
}
--- /dev/null
+// errorcheck -0 -m -l
+
+// 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.
+
+// Test, using compiler diagnostic flags, that the escape analysis is working.
+// Compiles but does not run. Inlining is disabled.
+
+package foo
+
+func noleak(p *int) int { // ERROR "p does not escape"
+ return *p
+}
+
+func leaktoret(p *int) *int { // ERROR "leaking param: p to result"
+ return p
+}
+
+func leaktoret2(p *int) (*int, *int) { // ERROR "leaking param: p to result .anon1" "leaking param: p to result .anon2"
+ return p, p
+}
+
+func leaktoret22(p, q *int) (*int, *int) { // ERROR "leaking param: p to result .anon2" "leaking param: q to result .anon3"
+ return p, q
+}
+
+func leaktoret22b(p, q *int) (*int, *int) { // ERROR "leaking param: p to result .anon3" "leaking param: q to result .anon2"
+ return leaktoret22(q, p)
+}
+
+func leaktoret22c(p, q *int) (*int, *int) { // ERROR "leaking param: p to result .anon3" "leaking param: q to result .anon2"
+ r, s := leaktoret22(q, p)
+ return r, s
+}
+
+func leaktoret22d(p, q *int) (r, s *int) { // ERROR "leaking param: p to result s" "leaking param: q to result r"
+ r, s = leaktoret22(q, p)
+ return
+}
+
+func leaktoret22e(p, q *int) (r, s *int) { // ERROR "leaking param: p to result s" "leaking param: q to result r"
+ r, s = leaktoret22(q, p)
+ return r, s
+}
+
+func leaktoret22f(p, q *int) (r, s *int) { // ERROR "leaking param: p to result s" "leaking param: q to result r"
+ rr, ss := leaktoret22(q, p)
+ return rr, ss
+}
+
+var gp *int
+
+func leaktosink(p *int) *int { // ERROR "leaking param: p"
+ gp = p
+ return p
+}
+
+func f1() {
+ var x int
+ p := noleak(&x) // ERROR "&x does not escape"
+ _ = p
+}
+
+func f2() {
+ var x int
+ p := leaktoret(&x) // ERROR "&x does not escape"
+ _ = p
+}
+
+func f3() {
+ var x int // ERROR "moved to heap: x"
+ p := leaktoret(&x) // ERROR "&x escapes to heap"
+ gp = p
+}
+
+func f4() {
+ var x int // ERROR "moved to heap: x"
+ p, q := leaktoret2(&x) // ERROR "&x escapes to heap"
+ gp = p
+ gp = q
+}
+
+func f5() {
+ var x int
+ leaktoret22(leaktoret2(&x)) // ERROR "&x does not escape"
+}
+
+func f6() {
+ var x int // ERROR "moved to heap: x"
+ px1, px2 := leaktoret22(leaktoret2(&x)) // ERROR "&x escapes to heap"
+ gp = px1
+ _ = px2
+}
+
+type T struct{ x int }
+
+func (t *T) Foo(u int) (*T, bool) { // ERROR "leaking param: t to result"
+ t.x += u
+ return t, true
+}
+
+func f7() *T {
+ r, _ := new(T).Foo(42) // ERROR "new.T. escapes to heap"
+ return r
+}
+
+func leakrecursive1(p, q *int) (*int, *int) { // ERROR "leaking param: p" "leaking param: q"
+ return leakrecursive2(q, p)
+}
+
+func leakrecursive2(p, q *int) (*int, *int) { // ERROR "leaking param: p" "leaking param: q"
+ if *p > *q {
+ return leakrecursive1(q, p)
+ }
+ // without this, leakrecursive? are safe for p and q, b/c in fact their graph does not have leaking edges.
+ return p, q
+}
+