mirror of https://github.com/golang/go.git
cmd/gc: escape analysis to track flow of in to out parameters.
includes step 0: synthesize outparams, from 6600044
includes step 1,2: give outparams loopdepth 0 and verify unchanged results
generate esc:$mask tags, but still tie to sink if a param has mask != 0
from 6610054
adds final steps:
- have esccall generate n->escretval, a list of nodes the function results flow to
- use these in esccall and ORETURN/OAS2FUNC/and f(g())
- only tie parameters to sink if tag is absent, otherwise according to mask, tie them to escretval
R=rsc, bradfitz
CC=dave, gobot, golang-dev, iant, rsc
https://golang.org/cl/6741044
This commit is contained in:
parent
c8fe9c7606
commit
507fcf37d2
121
src/cmd/gc/esc.c
121
src/cmd/gc/esc.c
|
|
@ -209,9 +209,10 @@ struct EscState {
|
||||||
int pdepth; // for debug printing in recursions.
|
int pdepth; // for debug printing in recursions.
|
||||||
int dstcount, edgecount; // diagnostic
|
int dstcount, edgecount; // diagnostic
|
||||||
NodeList* noesc; // list of possible non-escaping nodes, for printing
|
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*
|
static Strlit*
|
||||||
mktag(int mask)
|
mktag(int mask)
|
||||||
|
|
@ -260,8 +261,6 @@ analyze(NodeList *all, int recursive)
|
||||||
NodeList *l;
|
NodeList *l;
|
||||||
EscState es, *e;
|
EscState es, *e;
|
||||||
|
|
||||||
USED(recursive);
|
|
||||||
|
|
||||||
memset(&es, 0, sizeof es);
|
memset(&es, 0, sizeof es);
|
||||||
e = &es;
|
e = &es;
|
||||||
e->theSink.op = ONAME;
|
e->theSink.op = ONAME;
|
||||||
|
|
@ -269,6 +268,7 @@ analyze(NodeList *all, int recursive)
|
||||||
e->theSink.class = PEXTERN;
|
e->theSink.class = PEXTERN;
|
||||||
e->theSink.sym = lookup(".sink");
|
e->theSink.sym = lookup(".sink");
|
||||||
e->theSink.escloopdepth = -1;
|
e->theSink.escloopdepth = -1;
|
||||||
|
e->recursive = recursive;
|
||||||
|
|
||||||
for(l=all; l; l=l->next)
|
for(l=all; l; l=l->next)
|
||||||
if(l->n->op == ODCLFUNC)
|
if(l->n->op == ODCLFUNC)
|
||||||
|
|
@ -308,6 +308,8 @@ escfunc(EscState *e, Node *func)
|
||||||
NodeList *ll;
|
NodeList *ll;
|
||||||
int saveld;
|
int saveld;
|
||||||
|
|
||||||
|
// print("escfunc %N %s\n", func->nname, e->recursive?"(recursive)":"");
|
||||||
|
|
||||||
if(func->esc != 1)
|
if(func->esc != 1)
|
||||||
fatal("repeat escfunc %N", func->nname);
|
fatal("repeat escfunc %N", func->nname);
|
||||||
func->esc = EscFuncStarted;
|
func->esc = EscFuncStarted;
|
||||||
|
|
@ -335,6 +337,12 @@ escfunc(EscState *e, Node *func)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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);
|
escloopdepthlist(e, curfn->nbody);
|
||||||
esclist(e, curfn->nbody);
|
esclist(e, curfn->nbody);
|
||||||
curfn = savefn;
|
curfn = savefn;
|
||||||
|
|
@ -450,7 +458,7 @@ esc(EscState *e, Node *n)
|
||||||
}
|
}
|
||||||
// See case OLABEL in escloopdepth above
|
// See case OLABEL in escloopdepth above
|
||||||
// else if(n->left->sym->label == nil)
|
// 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;
|
n->left->sym->label = nil;
|
||||||
break;
|
break;
|
||||||
|
|
@ -506,13 +514,30 @@ esc(EscState *e, Node *n)
|
||||||
escassign(e, &e->theSink, ll->n);
|
escassign(e, &e->theSink, ll->n);
|
||||||
break;
|
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:
|
case ORETURN:
|
||||||
|
ll=n->list;
|
||||||
if(count(n->list) == 1 && curfn->type->outtuple > 1) {
|
if(count(n->list) == 1 && curfn->type->outtuple > 1) {
|
||||||
// OAS2FUNC in disguise
|
// 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) {
|
for(lr = curfn->dcl; lr && ll; lr=lr->next) {
|
||||||
if (lr->n->op != ONAME || lr->n->class != PPARAMOUT)
|
if (lr->n->op != ONAME || lr->n->class != PPARAMOUT)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -534,12 +559,6 @@ esc(EscState *e, Node *n)
|
||||||
escassign(e, &e->theSink, ll->n); // lose track of assign to dereference
|
escassign(e, &e->theSink, ll->n); // lose track of assign to dereference
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OCALLMETH:
|
|
||||||
case OCALLFUNC:
|
|
||||||
case OCALLINTER:
|
|
||||||
esccall(e, n);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case OCONV:
|
case OCONV:
|
||||||
case OCONVNOP:
|
case OCONVNOP:
|
||||||
case OCONVIFACE:
|
case OCONVIFACE:
|
||||||
|
|
@ -693,6 +712,14 @@ escassign(EscState *e, Node *dst, Node *src)
|
||||||
escflows(e, dst, src);
|
escflows(e, dst, src);
|
||||||
break;
|
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:
|
case ODOT:
|
||||||
// A non-pointer escaping from a struct does not concern us.
|
// A non-pointer escaping from a struct does not concern us.
|
||||||
if(src->type && !haspointers(src->type))
|
if(src->type && !haspointers(src->type))
|
||||||
|
|
@ -748,6 +775,26 @@ escassign(EscState *e, Node *dst, Node *src)
|
||||||
lineno = lno;
|
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
|
// This is a bit messier than fortunate, pulled out of esc's big
|
||||||
// switch for clarity. We either have the paramnodes, which may be
|
// switch for clarity. We either have the paramnodes, which may be
|
||||||
// connected to other things throug flows or we have the parameter type
|
// connected to other things throug flows or we have the parameter type
|
||||||
|
|
@ -760,6 +807,8 @@ esccall(EscState *e, Node *n)
|
||||||
NodeList *ll, *lr;
|
NodeList *ll, *lr;
|
||||||
Node *a, *fn, *src;
|
Node *a, *fn, *src;
|
||||||
Type *t, *fntype;
|
Type *t, *fntype;
|
||||||
|
char buf[40];
|
||||||
|
int i;
|
||||||
|
|
||||||
fn = N;
|
fn = N;
|
||||||
switch(n->op) {
|
switch(n->op) {
|
||||||
|
|
@ -787,19 +836,20 @@ esccall(EscState *e, Node *n)
|
||||||
ll = n->list;
|
ll = n->list;
|
||||||
if(n->list != nil && n->list->next == nil) {
|
if(n->list != nil && n->list->next == nil) {
|
||||||
a = n->list->n;
|
a = n->list->n;
|
||||||
if(a->type->etype == TSTRUCT && a->type->funarg) {
|
if(a->type->etype == TSTRUCT && a->type->funarg) // f(g()).
|
||||||
// f(g()).
|
ll = a->escretval;
|
||||||
// Since f's arguments are g's results and
|
|
||||||
// all function results escape, we're done.
|
|
||||||
ll = nil;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(fn && fn->op == ONAME && fn->class == PFUNC && fn->defn && fn->defn->nbody && fn->ntype && fn->defn->esc < EscFuncTagged) {
|
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.
|
// function in same mutually recursive group. Incorporate into flow graph.
|
||||||
if(fn->defn->esc == EscFuncUnknown)
|
// print("esc local fn: %N\n", fn->ntype);
|
||||||
|
if(fn->defn->esc == EscFuncUnknown || n->escretval != nil)
|
||||||
fatal("graph inconsistency");
|
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.
|
// Receiver.
|
||||||
if(n->op != OCALLFUNC)
|
if(n->op != OCALLFUNC)
|
||||||
escassign(e, fn->ntype->left->left, n->left->left);
|
escassign(e, fn->ntype->left->left, n->left->left);
|
||||||
|
|
@ -823,15 +873,35 @@ esccall(EscState *e, Node *n)
|
||||||
// "..." arguments are untracked
|
// "..." arguments are untracked
|
||||||
for(; ll; ll=ll->next)
|
for(; ll; ll=ll->next)
|
||||||
escassign(e, &e->theSink, ll->n);
|
escassign(e, &e->theSink, ll->n);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Imported or completely analyzed function. Use the escape tags.
|
// Imported or completely analyzed function. Use the escape tags.
|
||||||
if(n->op != OCALLFUNC) {
|
if(n->escretval != nil)
|
||||||
t = getthisx(fntype)->type;
|
fatal("esc already decorated call %+N\n", n);
|
||||||
if(parsetag(t->note) != EscNone)
|
|
||||||
escassign(e, &e->theSink, n->left->left);
|
// 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) {
|
for(t=getinargx(fntype)->type; ll; ll=ll->next) {
|
||||||
src = ll->n;
|
src = ll->n;
|
||||||
if(t->isddd && !n->isddd) {
|
if(t->isddd && !n->isddd) {
|
||||||
|
|
@ -843,8 +913,7 @@ esccall(EscState *e, Node *n)
|
||||||
e->noesc = list(e->noesc, src);
|
e->noesc = list(e->noesc, src);
|
||||||
n->right = src;
|
n->right = src;
|
||||||
}
|
}
|
||||||
if(parsetag(t->note) != EscNone)
|
escassignfromtag(e, t->note, n->escretval, src);
|
||||||
escassign(e, &e->theSink, src);
|
|
||||||
if(src != ll->n)
|
if(src != ll->n)
|
||||||
break;
|
break;
|
||||||
t = t->down;
|
t = t->down;
|
||||||
|
|
|
||||||
|
|
@ -306,7 +306,8 @@ struct Node
|
||||||
|
|
||||||
// Escape analysis.
|
// Escape analysis.
|
||||||
NodeList* escflowsrc; // flow(this, src)
|
NodeList* escflowsrc; // flow(this, src)
|
||||||
int escloopdepth; // -1: global, 0: not set, function top level:1, increased inside function for every loop or label to mark scopes
|
NodeList* escretval; // on OCALLxxx, list of dummy return values
|
||||||
|
int escloopdepth; // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
|
||||||
|
|
||||||
Sym* sym; // various
|
Sym* sym; // various
|
||||||
int32 vargen; // unique name for OTYPE/ONAME
|
int32 vargen; // unique name for OTYPE/ONAME
|
||||||
|
|
|
||||||
|
|
@ -561,12 +561,21 @@ func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "y does not esca
|
||||||
return &x[0] // ERROR "&x.0. escapes to heap"
|
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"
|
myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
|
||||||
}
|
}
|
||||||
|
|
||||||
func foo75a(z *int) { // ERROR "z 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 foo76(z *int) { // ERROR "leaking param: z"
|
||||||
|
|
@ -574,7 +583,7 @@ func foo76(z *int) { // ERROR "leaking param: z"
|
||||||
}
|
}
|
||||||
|
|
||||||
func foo76a(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 foo76b() {
|
||||||
|
|
@ -582,7 +591,7 @@ func foo76b() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func foo76c() {
|
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 foo76d() {
|
||||||
|
|
@ -590,7 +599,7 @@ func foo76d() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func foo76e() {
|
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() {
|
func foo76f() {
|
||||||
|
|
@ -610,10 +619,15 @@ func foo77(z []interface{}) { // ERROR "z does not escape"
|
||||||
myprint(nil, z...) // z does not escape
|
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...)
|
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"
|
func foo78(z int) *int { // ERROR "moved to heap: z"
|
||||||
return &z // ERROR "&z escapes to heap"
|
return &z // ERROR "&z escapes to heap"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue