show entire file path when printing line numbers in errors
-I dir1 -I dir2
add dir1 and dir2 to the list of paths to check for imported packages
- -N
- disable optimization
-S
write assembly language text to standard output
-u
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//
-// The base version before this file existed, active with debug['s']
-// == 0, assumes any node that has a reference to it created at some
-// point, may flow to the global scope except
-// - if its address is dereferenced immediately with only CONVNOPs in
-// between the * and the &
-// - if it is for a closure variable and the closure executed at the
-// place it's defined
-//
-// Flag -s disables the old codepaths and switches on the code here:
+// Escape analysis.
//
// First escfunc, esc and escassign recurse over the ast of each
// function to dig out flow(dst,src) edges between any
// pointer-containing nodes and store them in dst->escflowsrc. For
// variables assigned to a variable in an outer scope or used as a
// return value, they store a flow(theSink, src) edge to a fake node
-// 'the Sink'. For variables referenced in closures, an edge
+// 'the Sink'. For variables referenced in closures, an edge
// flow(closure, &var) is recorded and the flow of a closure itself to
// an outer scope is tracked the same way as other variables.
//
// variables of it can reach an & node as escaping and all function
// parameters it can reach as leaking.
//
-// Watch the variables moved to the heap and parameters tagged as
-// unsafe with -m, more detailed analysis output with -mm
+// If a value's address is taken but the address does not escape,
+// then the value can stay on the stack. If the value new(T) does
+// not escape, then new(T) can be rewritten into a stack allocation.
+// The same is true of slice literals.
//
+// If escape analysis is disabled (-s), this code is not used.
+// Instead, the compiler assumes that any value whose address
+// is taken without being immediately dereferenced
+// needs to be moved to the heap, and new(T) and slice
+// literals are always real allocations.
#include <u.h>
#include <libc.h>
static NodeList* dsts; // all dst nodes
static int loopdepth; // for detecting nested loop scopes
static int pdepth; // for debug printing in recursions.
-static int floodgen; // loop prevention in flood/walk
static Strlit* safetag; // gets slapped on safe parameters' field types for export
static int dstcount, edgecount; // diagnostic
+static NodeList* noesc; // list of possible non-escaping nodes, for printing
void
escapes(void)
for(l=xtop; l; l=l->next)
if(l->n->op == ODCLFUNC)
esctag(l->n);
+
+ if(debug['m']) {
+ for(l=noesc; l; l=l->next)
+ if(l->n->esc == EscNone)
+ warnl(l->n->lineno, "%S %#N does not escape",
+ (l->n->curfn && l->n->curfn->nname) ? l->n->curfn->nname->sym : S,
+ l->n);
+ }
}
+
static void
escfunc(Node *func)
{
ll->n->escloopdepth = loopdepth;
break;
case PPARAM:
+ if(ll->n->type && !haspointers(ll->n->type))
+ break;
ll->n->esc = EscNone; // prime for escflood later
+ noesc = list(noesc, ll->n);
ll->n->escloopdepth = loopdepth;
break;
}
esc(Node *n)
{
int lno;
- NodeList *ll, *lr, *l;
+ NodeList *ll, *lr;
if(n == N)
return;
if(n->op == OFOR || n->op == ORANGE)
loopdepth++;
- esclist(n->ninit);
- esclist(n->list);
- esclist(n->rlist);
+ esc(n->left);
+ esc(n->right);
esc(n->ntest);
esc(n->nincr);
+ esclist(n->ninit);
esclist(n->nbody);
- esc(n->left);
- esc(n->right);
+ esclist(n->nelse);
+ esclist(n->list);
+ esclist(n->rlist);
if(n->op == OFOR || n->op == ORANGE)
loopdepth--;
case ORANGE:
// Everything but fixed array is a dereference.
- if(isfixedarray(n->type))
+ if(isfixedarray(n->type) && n->list->next)
escassign(n->list->next->n, n->right);
break;
// ntest->right is the argument of the .(type),
// ll->n->nname is the variable per case
escassign(ll->n->nname, n->ntest->right);
- esclist(ll->n->nbody);
}
}
break;
case OCALLINTER:
esccall(n);
break;
-
+
case OCONV:
case OCONVNOP:
case OCONVIFACE:
escassign(n, n->left);
break;
-
+
case OARRAYLIT:
+ if(isslice(n->type)) {
+ n->esc = EscNone; // until proven otherwise
+ noesc = list(noesc, n);
+ n->escloopdepth = loopdepth;
+ // Values make it to memory, lose track.
+ for(ll=n->list; ll; ll=ll->next)
+ escassign(&theSink, ll->n->right);
+ } else {
+ // Link values to array.
+ for(ll=n->list; ll; ll=ll->next)
+ escassign(n, ll->n->right);
+ }
+ break;
+
case OSTRUCTLIT:
- for(l=n->list; l; l=l->next)
- escassign(n, l->n->right);
+ // Link values to struct.
+ for(ll=n->list; ll; ll=ll->next)
+ escassign(n, ll->n->right);
break;
+
case OMAPLIT:
- for(l=n->list; l; l=l->next) {
- escassign(n, l->n->left);
- escassign(n, l->n->right);
+ n->esc = EscNone; // until proven otherwise
+ noesc = list(noesc, n);
+ n->escloopdepth = loopdepth;
+ // Keys and values make it to memory, lose track.
+ for(ll=n->list; ll; ll=ll->next) {
+ escassign(&theSink, ll->n->left);
+ escassign(&theSink, ll->n->right);
}
break;
+
+ case OADDR:
+ case OCLOSURE:
+ case OMAKECHAN:
+ case OMAKEMAP:
+ case OMAKESLICE:
+ case ONEW:
+ n->escloopdepth = loopdepth;
+ n->esc = EscNone; // until proven otherwise
+ noesc = list(noesc, n);
+ break;
}
lineno = lno;
escassign(Node *dst, Node *src)
{
int lno;
- NodeList *ll;
if(isblank(dst) || dst == N || src == N || src->op == ONONAME || src->op == OXXX)
return;
break;
}
- if(src->typecheck == 0 && src->op != OKEY) {
- dump("escassign missing typecheck", src);
- fatal("escassign");
- }
-
lno = setlineno(src);
pdepth++;
case ODOTPTR: // dst = (*x).f
case ONAME:
case OPARAM:
+ case ODDDARG:
+ case OARRAYLIT:
+ case OMAPLIT:
+ case OSTRUCTLIT:
// loopdepth was set in the defining statement or function header
escflows(dst, src);
break;
escassign(dst, src->left);
break;
- case OARRAYLIT:
- case OSTRUCTLIT:
- case OMAPLIT:
- src->escloopdepth = loopdepth;
- escflows(dst, src);
- for(ll=src->list; ll; ll=ll->next) {
- escassign(src, ll->n->left);
- escassign(src, ll->n->right);
- }
- break;
-
case OMAKECHAN:
case OMAKEMAP:
case OMAKESLICE:
case ONEW:
- src->curfn = curfn; // should have been done in parse, but patch it up here.
- src->escloopdepth = loopdepth;
escflows(dst, src);
break;
case OCLOSURE:
- src->curfn = curfn; // should have been done in parse, but patch it up here.
- src->escloopdepth = loopdepth;
escflows(dst, src);
escfunc(src);
break;
+
+ case OADD:
+ case OSUB:
+ case OOR:
+ case OXOR:
+ case OMUL:
+ case ODIV:
+ case OMOD:
+ case OLSH:
+ case ORSH:
+ case OAND:
+ case OANDNOT:
+ case OPLUS:
+ case OMINUS:
+ case OCOM:
+ // Might be pointer arithmetic, in which case
+ // the operands flow into the result.
+ // TODO(rsc): Decide what the story is here. This is unsettling.
+ escassign(dst, src->left);
+ escassign(dst, src->right);
+ break;
+
}
pdepth--;
esccall(Node *n)
{
NodeList *ll, *lr;
- Node *a, *fn;
+ Node *a, *fn, *src;
Type *t, *fntype;
fn = N;
}
}
- if(fn && fn->ntype) {
+ if(fn && fn->op == ONAME && fn->class == PFUNC && fn->ntype) {
// Local function. Incorporate into flow graph.
// Receiver.
if(n->op != OCALLFUNC)
escassign(fn->ntype->left->left, n->left->left);
- for(ll=n->list, lr=fn->ntype->list; ll && lr; ll=ll->next) {
- if (lr->n->left)
- escassign(lr->n->left, ll->n);
- else
- escassign(&theSink, ll->n);
- if(lr->n->left && !lr->n->left->isddd)
- lr=lr->next;
+ for(lr=fn->ntype->list; ll && lr; ll=ll->next, lr=lr->next) {
+ src = ll->n;
+ if(lr->n->isddd && !n->isddd) {
+ // Introduce ODDDARG node to represent ... allocation.
+ src = nod(ODDDARG, N, N);
+ src->escloopdepth = loopdepth;
+ src->lineno = n->lineno;
+ src->esc = EscNone; // until we find otherwise
+ noesc = list(noesc, src);
+ n->right = src;
+ }
+ if(lr->n->left != N)
+ escassign(lr->n->left, src);
+ if(src != ll->n)
+ break;
}
+ // "..." arguments are untracked
+ for(; ll; ll=ll->next)
+ escassign(&theSink, ll->n);
return;
}
escassign(&theSink, n->left->left);
}
for(t=getinargx(fntype)->type; ll; ll=ll->next) {
+ src = ll->n;
+ if(t->isddd && !n->isddd) {
+ // Introduce ODDDARG node to represent ... allocation.
+ src = nod(ODDDARG, N, N);
+ src->escloopdepth = loopdepth;
+ src->lineno = n->lineno;
+ src->esc = EscNone; // until we find otherwise
+ noesc = list(noesc, src);
+ n->right = src;
+ }
if(!t->note || strcmp(t->note->s, safetag->s) != 0)
- escassign(&theSink, ll->n);
- if(t->down)
- t = t->down;
+ escassign(&theSink, src);
+ if(src != ll->n)
+ break;
+ t = t->down;
}
+ // "..." arguments are untracked
+ for(; ll; ll=ll->next)
+ escassign(&theSink, ll->n);
}
// Store the link src->dst in dst, throwing out some quick wins.
}
if(debug['m']>1)
- print("\nescflood:%d: dst %hN scope:%#S[%d]\n", floodgen, dst,
+ print("\nescflood:%d: dst %hN scope:%#S[%d]\n", walkgen, dst,
(dst->curfn && dst->curfn->nname) ? dst->curfn->nname->sym : S,
dst->escloopdepth);
for(l = dst->escflowsrc; l; l=l->next) {
- floodgen++;
+ walkgen++;
escwalk(0, dst, l->n);
}
}
NodeList *ll;
int leaks;
- if(src->escfloodgen == floodgen)
+ if(src->walkgen == walkgen)
return;
- src->escfloodgen = floodgen;
+ src->walkgen = walkgen;
if(debug['m']>1)
print("escwalk: level:%d depth:%d %.*s %hN scope:%#S[%d]\n",
if(src->class == PPARAM && leaks && src->esc == EscNone) {
src->esc = EscScope;
if(debug['m'])
- print("%L:leaking param: %hN\n", src->lineno, src);
+ warnl(src->lineno, "leaking param: %hN", src);
}
break;
case OADDR:
- if(leaks)
+ if(leaks) {
+ src->esc = EscHeap;
addrescapes(src->left);
+ if(debug['m'])
+ warnl(src->lineno, "%#N escapes to heap", src);
+ }
escwalk(level-1, dst, src->left);
break;
+ case OARRAYLIT:
+ if(isfixedarray(src->type))
+ break;
+ // fall through
+ case ODDDARG:
+ case OMAKECHAN:
+ case OMAKEMAP:
+ case OMAKESLICE:
+ case OMAPLIT:
+ case ONEW:
+ case OCLOSURE:
+ if(leaks) {
+ src->esc = EscHeap;
+ if(debug['m'])
+ warnl(src->lineno, "%#N escapes to heap", src);
+ }
+ break;
+
case OINDEX:
if(isfixedarray(src->type))
break;
+ // fall through
case OSLICE:
case ODOTPTR:
case OINDEXMAP:
case EscHeap: // touched by escflood, moved to heap
case EscScope: // touched by escflood, value leaves scope
break;
- default:
- fatal("messed up escape tagging: %N::%N", curfn, ll->n);
}
}
if(n->class == PAUTO && n->esc == EscNever)
break;
- if(!debug['s'] && n->esc != EscUnknown)
+ if(debug['s'] && n->esc != EscUnknown)
fatal("without escape analysis, only PAUTO's should have esc: %N", n);
switch(n->class) {
fatal("addrescapes before param assignment");
n->stackparam->xoffset = n->xoffset;
// fallthrough
- case PAUTO:
+ case PAUTO:
n->class |= PHEAP;
n->addable = 0;
n->ullman = 2;
n->heapaddr->class = PHEAP-1; // defer tempname to allocparams
n->heapaddr->ullman = 1;
n->curfn->dcl = list(n->curfn->dcl, n->heapaddr);
-
- if(debug['s'])
+ if(!debug['s'])
n->esc = EscHeap;
-
if(debug['m'])
print("%L: moved to heap: %hN\n", n->lineno, n);
struct Node
{
+ // Tree structure.
+ // Generic recursive walks should follow these fields.
+ Node* left;
+ Node* right;
+ Node* ntest;
+ Node* nincr;
+ NodeList* ninit;
+ NodeList* nbody;
+ NodeList* nelse;
+ NodeList* list;
+ NodeList* rlist;
+
uchar op;
uchar ullman; // sethi/ullman number
uchar addable; // type of addressability - 0 is not addressable
uchar implicit; // don't show in printout
// most nodes
- Node* left;
- Node* right;
Type* type;
Type* realtype; // as determined by typecheck
- NodeList* list;
- NodeList* rlist;
Node* orig; // original form, for printing, and tracking copies of ONAMEs
- // for-body
- NodeList* ninit;
- Node* ntest;
- Node* nincr;
- NodeList* nbody;
-
- // if-body
- NodeList* nelse;
-
- // cases
- Node* ncase;
-
// func
Node* nname;
Node* shortname;
// Escape analysis.
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
- int escfloodgen; // increased for every flood to detect loops
Sym* sym; // various
int32 vargen; // unique name for OTYPE/ONAME
int32 stkdelta; // offset added by stack frame compaction phase.
int32 ostk;
int32 iota;
+ uint32 walkgen;
};
#define N ((Node*)0)
-EXTERN int32 walkgen;
+
+/*
+ * Every node has a walkgen field.
+ * If you want to do a traversal of a node graph that
+ * might contain duplicates and want to avoid
+ * visiting the same nodes twice, increment walkgen
+ * before starting. Then before processing a node, do
+ *
+ * if(n->walkgen == walkgen)
+ * return;
+ * n->walkgen = walkgen;
+ *
+ * Such a walk cannot call another such walk recursively,
+ * because of the use of the global walkgen.
+ */
+EXTERN uint32 walkgen;
struct NodeList
{
void ullmancalc(Node *n);
void umagic(Magic *m);
void warn(char *fmt, ...);
+void warnl(int line, char *fmt, ...);
void yyerror(char *fmt, ...);
void yyerrorl(int line, char *fmt, ...);
print("flags:\n");
// -A is allow use of "any" type, for bootstrapping
print(" -I DIR search for packages in DIR\n");
+ print(" -L show full path in file:line prints\n");
+ print(" -N disable optimizer\n");
+ print(" -S print the assembly language\n");
+ print(" -V print the compiler version\n");
print(" -d print declarations\n");
print(" -e no limit on number of errors printed\n");
print(" -f print stack frame structure\n");
print(" -h panic on an error\n");
+ print(" -m print about moves to heap\n");
print(" -o file specify output file\n");
- print(" -S print the assembly language\n");
- print(" -V print the compiler version\n");
+ print(" -s disable escape analysis\n");
print(" -u disable package unsafe\n");
print(" -w print the parse tree after typing\n");
print(" -x print lex tokens\n");
errorexit();
// Phase 3b: escape analysis.
- if(debug['s'])
+ if(!debug['s'])
escapes();
// Phase 4: Compile function bodies.
dowidth(t);
if(ctxt != 0) {
-
// put everything into static array
vstat = staticname(t, ctxt);
arraylit(ctxt, 1, n, vstat, init);
vauto = nod(OXXX, N, N);
tempname(vauto, ptrto(t));
- // set auto to point at new heap (3 assign)
- a = nod(ONEW, N, N);
- a->list = list1(typenod(t));
+ // set auto to point at new temp or heap (3 assign)
+ if(n->esc == EscNone) {
+ a = nod(OXXX, N, N);
+ tempname(a, t);
+ a = nod(OADDR, a, N);
+ } else {
+ a = nod(ONEW, N, N);
+ a->list = list1(typenod(t));
+ }
a = nod(OAS, vauto, a);
typecheck(&a, Etop);
walkexpr(&a, init);
hcrash();
}
+void
+warnl(int line, char *fmt, ...)
+{
+ va_list arg;
+
+ va_start(arg, fmt);
+ adderr(line, fmt, arg);
+ va_end(arg);
+}
+
void
fatal(char *fmt, ...)
{
n->lineno = parserline();
n->xoffset = BADWIDTH;
n->orig = n;
+ n->curfn = curfn;
return n;
}
goto error;
// top&Eindir means this is &x in *&x. (or the arg to built-in print)
// n->etype means code generator flagged it as non-escaping.
- if(!(top & Eindir) && !n->etype && !debug['s'])
+ if(debug['s'] && !(top & Eindir) && !n->etype)
addrescapes(n->left);
n->type = ptrto(t);
goto ret;
if(!eqtype(rcvr, tt)) {
if(rcvr->etype == tptr && eqtype(rcvr->type, tt)) {
checklvalue(n->left, "call pointer method on");
- if(!debug['s'])
+ if(debug['s'])
addrescapes(n->left);
n->left = nod(OADDR, n->left, N);
n->left->implicit = 1;
static Node* ascompatee1(int, Node*, Node*, NodeList**);
static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**);
static NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**);
-static NodeList* ascompatte(int, int, Type**, NodeList*, int, NodeList**);
+static NodeList* ascompatte(int, Node*, int, Type**, NodeList*, int, NodeList**);
static Node* convas(Node*, NodeList**);
static void heapmoves(void);
static NodeList* paramstoheap(Type **argin, int out);
n->list = reorder3(ll);
break;
}
- ll = ascompatte(n->op, 0, getoutarg(curfn->type), n->list, 1, &n->ninit);
+ ll = ascompatte(n->op, nil, 0, getoutarg(curfn->type), n->list, 1, &n->ninit);
n->list = ll;
break;
goto ret;
walkexpr(&n->left, init);
walkexprlist(n->list, init);
- ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+ ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
n->list = reorder1(ll);
goto ret;
walkexpr(&n->left, init);
walkexprlist(n->list, init);
- ll = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+ ll = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
n->list = reorder1(ll);
goto ret;
goto ret;
walkexpr(&n->left, init);
walkexprlist(n->list, init);
- ll = ascompatte(n->op, 0, getthis(t), list1(n->left->left), 0, init);
- lr = ascompatte(n->op, n->isddd, getinarg(t), n->list, 0, init);
+ ll = ascompatte(n->op, n, 0, getthis(t), list1(n->left->left), 0, init);
+ lr = ascompatte(n->op, n, n->isddd, getinarg(t), n->list, 0, init);
ll = concat(ll, lr);
n->left->left = N;
ullmancalc(n->left);
goto ret;
case ONEW:
- n = callnew(n->type->type);
+ if(n->esc == EscNone && n->type->type->width < (1<<16)) {
+ r = nod(OXXX, N, N);
+ tempname(r, n->type->type);
+ *init = list(*init, nod(OAS, r, N)); // zero temp
+ r = nod(OADDR, r, N);
+ typecheck(&r, Erv);
+ n = r;
+ } else {
+ n = callnew(n->type->type);
+ }
goto ret;
case OCMPSTR:
* package all the arguments that match a ... T parameter into a []T.
*/
static NodeList*
-mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init)
+mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, int esc)
{
Node *a, *n;
Type *tslice;
-
+
tslice = typ(TARRAY);
tslice->type = l->type->type;
tslice->bound = -1;
- n = nod(OCOMPLIT, N, typenod(tslice));
- n->list = lr0;
- typecheck(&n, Erv);
- if(n->type == T)
- fatal("mkdotargslice: typecheck failed");
- walkexpr(&n, init);
+ if(count(lr0) == 0) {
+ n = nodnil();
+ n->type = tslice;
+ } else {
+ n = nod(OCOMPLIT, N, typenod(tslice));
+ n->list = lr0;
+ n->esc = esc;
+ typecheck(&n, Erv);
+ if(n->type == T)
+ fatal("mkdotargslice: typecheck failed");
+ walkexpr(&n, init);
+ }
a = nod(OAS, nodarg(l, fp), n);
nn = list(nn, convas(a, init));
* func(expr-list)
*/
static NodeList*
-ascompatte(int op, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
+ascompatte(int op, Node *call, int isddd, Type **nl, NodeList *lr, int fp, NodeList **init)
{
+ int esc;
Type *l, *ll;
Node *r, *a;
NodeList *nn, *lr0, *alist;
// normal case -- make a slice of all
// remaining arguments and pass it to
// the ddd parameter.
- nn = mkdotargslice(lr, nn, l, fp, init);
+ esc = EscUnknown;
+ if(call->right)
+ esc = call->right->esc;
+ nn = mkdotargslice(lr, nn, l, fp, init, esc);
goto ret;
}
return Value{Internal: *(*interface{})(unsafe.Pointer(&eface))}
}
+var dummy struct {
+ b bool
+ x interface{}
+}
+
+// Dummy annotation marking that the value x escapes,
+// for use in cases where the reflect code is so clever that
+// the compiler cannot follow.
+func escapes(x interface{}) {
+ if dummy.b {
+ dummy.x = x
+ }
+}
+
// valueFromAddr returns a Value using the given type and address.
func valueFromAddr(flag uint32, typ Type, addr unsafe.Pointer) Value {
+ // TODO(rsc): Eliminate this terrible hack.
+ // The escape analysis knows that addr is a pointer
+ // but it doesn't see addr get passed to anything
+ // that keeps it. packValue keeps it, but packValue
+ // takes a uintptr (iword(addr)), and integers (non-pointers)
+ // are assumed not to matter. The escapes function works
+ // because return values always escape (for now).
+ escapes(addr)
+
if flag&flagAddr != 0 {
// Addressable, so the internal value is
// an interface containing a pointer to the real value.
if i == nil {
return Value{}
}
+
+ // TODO(rsc): Eliminate this terrible hack.
+ // In the call to packValue, eface.typ doesn't escape,
+ // and eface.word is an integer. So it looks like
+ // i (= eface) doesn't escape. But really it does,
+ // because eface.word is actually a pointer.
+ escapes(i)
+
// For an interface value with the noAddr bit set,
// the representation is identical to an empty interface.
eface := *(*emptyInterface)(unsafe.Pointer(&i))
-// errchk -0 $G -sm $D/$F.go
+// errchk -0 $G -m $D/$F.go
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
var gxx *int
-func foo1(x int) { // ERROR "moved to heap: NAME-x"
- gxx = &x
+func foo1(x int) { // ERROR "moved to heap: NAME-x"
+ gxx = &x // ERROR "&x escapes to heap"
}
-func foo2(yy *int) { // ERROR "leaking param: NAME-yy"
+func foo2(yy *int) { // ERROR "leaking param: NAME-yy"
gxx = yy
}
-func foo3(x int) *int { // ERROR "moved to heap: NAME-x"
- return &x
+func foo3(x int) *int { // ERROR "moved to heap: NAME-x"
+ return &x // ERROR "&x escapes to heap"
}
type T *T
-func foo3b(t T) { // ERROR "leaking param: NAME-t"
+
+func foo3b(t T) { // ERROR "leaking param: NAME-t"
*t = t
}
// xx isn't going anywhere, so use of yy is ok
-func foo4(xx, yy *int) {
+func foo4(xx, yy *int) { // ERROR "xx does not escape" "yy does not escape"
xx = yy
}
// xx isn't going anywhere, so taking address of yy is ok
-func foo5(xx **int, yy *int) {
- xx = &yy
+func foo5(xx **int, yy *int) { // ERROR "xx does not escape" "yy does not escape"
+ xx = &yy // ERROR "&yy does not escape"
}
-func foo6(xx **int, yy *int) { // ERROR "leaking param: NAME-yy"
+func foo6(xx **int, yy *int) { // ERROR "xx does not escape" "leaking param: NAME-yy"
*xx = yy
}
-func foo7(xx **int, yy *int) {
+func foo7(xx **int, yy *int) { // ERROR "xx does not escape" "yy does not escape"
**xx = *yy
}
-func foo8(xx, yy *int) int {
+func foo8(xx, yy *int) int { // ERROR "xx does not escape" "yy does not escape"
xx = yy
return *xx
}
-func foo9(xx, yy *int) *int { // ERROR "leaking param: NAME-xx" "leaking param: NAME-yy"
+func foo9(xx, yy *int) *int { // ERROR "leaking param: NAME-xx" "leaking param: NAME-yy"
xx = yy
return xx
}
-func foo10(xx, yy *int) {
+func foo10(xx, yy *int) { // ERROR "xx does not escape" "yy does not escape"
*xx = *yy
}
func foo11() int {
x, y := 0, 42
- xx := &x
- yy := &y
+ xx := &x // ERROR "&x does not escape"
+ yy := &y // ERROR "&y does not escape"
*xx = *yy
return x
}
-
var xxx **int
-func foo12(yyy **int) { // ERROR "leaking param: NAME-yyy"
+func foo12(yyy **int) { // ERROR "leaking param: NAME-yyy"
xxx = yyy
}
-func foo13(yyy **int) {
+func foo13(yyy **int) { // ERROR "yyy does not escape"
*xxx = *yyy
}
-func foo14(yyy **int) {
+func foo14(yyy **int) { // ERROR "yyy does not escape"
**xxx = **yyy
}
-func foo15(yy *int) { // ERROR "moved to heap: NAME-yy"
- xxx = &yy
+func foo15(yy *int) { // ERROR "moved to heap: NAME-yy"
+ xxx = &yy // ERROR "&yy escapes to heap"
}
-func foo16(yy *int) { // ERROR "leaking param: NAME-yy"
+func foo16(yy *int) { // ERROR "leaking param: NAME-yy"
*xxx = yy
}
-func foo17(yy *int) {
+func foo17(yy *int) { // ERROR "yy does not escape"
**xxx = *yy
}
-func foo18(y int) { // ERROR "moved to heap: "NAME-y"
- *xxx = &y
+func foo18(y int) { // ERROR "moved to heap: "NAME-y"
+ *xxx = &y // ERROR "&y escapes to heap"
}
func foo19(y int) {
}
type Bar struct {
- i int
+ i int
ii *int
}
func NewBar() *Bar {
- return &Bar{ 42, nil }
+ return &Bar{42, nil} // ERROR "&struct literal escapes to heap"
}
-func NewBarp(x *int) *Bar { // ERROR "leaking param: NAME-x"
- return &Bar{ 42, x }
+func NewBarp(x *int) *Bar { // ERROR "leaking param: NAME-x"
+ return &Bar{42, x} // ERROR "&struct literal escapes to heap"
}
-func NewBarp2(x *int) *Bar {
- return &Bar{ *x, nil }
+func NewBarp2(x *int) *Bar { // ERROR "x does not escape"
+ return &Bar{*x, nil} // ERROR "&struct literal escapes to heap"
}
-func (b *Bar) NoLeak() int {
+func (b *Bar) NoLeak() int { // ERROR "b does not escape"
return *(b.ii)
}
-func (b *Bar) AlsoNoLeak() *int {
+func (b *Bar) AlsoNoLeak() *int { // ERROR "b does not escape"
return b.ii
}
type Bar2 struct {
- i [12]int
+ i [12]int
ii []int
}
func NewBar2() *Bar2 {
- return &Bar2{ [12]int{ 42 }, nil }
+ return &Bar2{[12]int{42}, nil} // ERROR "&struct literal escapes to heap"
}
-func (b *Bar2) NoLeak() int {
+func (b *Bar2) NoLeak() int { // ERROR "b does not escape"
return b.i[0]
}
-func (b *Bar2) Leak() []int { // ERROR "leaking param: NAME-b"
- return b.i[:]
+func (b *Bar2) Leak() []int { // ERROR "leaking param: NAME-b"
+ return b.i[:] // ERROR "&b.i escapes to heap"
}
-func (b *Bar2) AlsoNoLeak() []int {
+func (b *Bar2) AlsoNoLeak() []int { // ERROR "b does not escape"
return b.ii[0:1]
}
-func (b *Bar2) LeakSelf() { // ERROR "leaking param: NAME-b"
- b.ii = b.i[0:4]
+func (b *Bar2) LeakSelf() { // ERROR "leaking param: NAME-b"
+ b.ii = b.i[0:4] // ERROR "&b.i escapes to heap"
}
-func (b *Bar2) LeakSelf2() { // ERROR "leaking param: NAME-b"
+func (b *Bar2) LeakSelf2() { // ERROR "leaking param: NAME-b"
var buf []int
- buf = b.i[0:]
+ buf = b.i[0:] // ERROR "&b.i escapes to heap"
b.ii = buf
}
func foo21() func() int {
- x := 42 // ERROR "moved to heap: NAME-x"
- return func() int {
- return x
+ x := 42 // ERROR "moved to heap: NAME-x"
+ return func() int { // ERROR "func literal escapes to heap"
+ return x // ERROR "&x escapes to heap"
}
}
func foo22() int {
x := 42
- return func() int {
+ return func() int { // ERROR "func literal does not escape"
return x
}()
}
-func foo23(x int) func() int { // ERROR "moved to heap: NAME-x"
- return func() int {
- return x
+func foo23(x int) func() int { // ERROR "moved to heap: NAME-x"
+ return func() int { // ERROR "func literal escapes to heap"
+ return x // ERROR "&x escapes to heap"
}
}
-func foo23a(x int) (func() int) { // ERROR "moved to heap: NAME-x"
- f := func() int {
- return x
+func foo23a(x int) func() int { // ERROR "moved to heap: NAME-x"
+ f := func() int { // ERROR "func literal escapes to heap"
+ return x // ERROR "&x escapes to heap"
}
return f
}
-func foo23b(x int) *(func() int) { // ERROR "moved to heap: NAME-x"
- f := func() int { return x } // ERROR "moved to heap: NAME-f"
- return &f
+func foo23b(x int) *(func() int) { // ERROR "moved to heap: NAME-x"
+ f := func() int { return x } // ERROR "moved to heap: NAME-f" "func literal escapes to heap" "&x escapes to heap"
+ return &f // ERROR "&f escapes to heap"
}
func foo24(x int) int {
- return func() int {
+ return func() int { // ERROR "func literal does not escape"
return x
}()
}
-
var x *int
-func fooleak(xx *int) int { // ERROR "leaking param: NAME-xx"
+func fooleak(xx *int) int { // ERROR "leaking param: NAME-xx"
x = xx
return *x
}
-func foonoleak(xx *int) int {
+func foonoleak(xx *int) int { // ERROR "xx does not escape"
return *x + *xx
}
-func foo31(x int) int { // ERROR "moved to heap: NAME-x"
- return fooleak(&x)
+func foo31(x int) int { // ERROR "moved to heap: NAME-x"
+ return fooleak(&x) // ERROR "&x escapes to heap"
}
func foo32(x int) int {
- return foonoleak(&x)
+ return foonoleak(&x) // ERROR "&x does not escape"
}
type Foo struct {
xx *int
- x int
+ x int
}
var F Foo
var pf *Foo
-func (f *Foo) fooleak() { // ERROR "leaking param: NAME-f"
+func (f *Foo) fooleak() { // ERROR "leaking param: NAME-f"
pf = f
}
-func (f *Foo) foonoleak() {
+func (f *Foo) foonoleak() { // ERROR "f does not escape"
F.x = f.x
}
-func (f *Foo) Leak() { // ERROR "leaking param: NAME-f"
+func (f *Foo) Leak() { // ERROR "leaking param: NAME-f"
f.fooleak()
}
-func (f *Foo) NoLeak() {
+func (f *Foo) NoLeak() { // ERROR "f does not escape"
f.foonoleak()
}
-
-func foo41(x int) { // ERROR "moved to heap: NAME-x"
- F.xx = &x
+func foo41(x int) { // ERROR "moved to heap: NAME-x"
+ F.xx = &x // ERROR "&x escapes to heap"
}
-func (f *Foo) foo42(x int) { // ERROR "moved to heap: NAME-x"
- f.xx = &x
+func (f *Foo) foo42(x int) { // ERROR "f does not escape" "moved to heap: NAME-x"
+ f.xx = &x // ERROR "&x escapes to heap"
}
-func foo43(f *Foo, x int) { // ERROR "moved to heap: NAME-x"
- f.xx = &x
+func foo43(f *Foo, x int) { // ERROR "f does not escape" "moved to heap: NAME-x"
+ f.xx = &x // ERROR "&x escapes to heap"
}
-func foo44(yy *int) { // ERROR "leaking param: NAME-yy"
+func foo44(yy *int) { // ERROR "leaking param: NAME-yy"
F.xx = yy
}
-func (f *Foo) foo45() {
- F.x = f.x
+func (f *Foo) foo45() { // ERROR "f does not escape"
+ F.x = f.x
}
-func (f *Foo) foo46() {
- F.xx = f.xx
+func (f *Foo) foo46() { // ERROR "f does not escape"
+ F.xx = f.xx
}
-func (f *Foo) foo47() { // ERROR "leaking param: NAME-f"
- f.xx = &f.x
+func (f *Foo) foo47() { // ERROR "leaking param: NAME-f"
+ f.xx = &f.x // ERROR "&f.x escapes to heap"
}
-
var ptrSlice []*int
-func foo50(i *int) { // ERROR "leaking param: NAME-i"
+func foo50(i *int) { // ERROR "leaking param: NAME-i"
ptrSlice[0] = i
}
-
var ptrMap map[*int]*int
-func foo51(i *int) { // ERROR "leaking param: NAME-i"
+func foo51(i *int) { // ERROR "leaking param: NAME-i"
ptrMap[i] = i
}
-
func indaddr1(x int) *int { // ERROR "moved to heap: NAME-x"
- return &x
+ return &x // ERROR "&x escapes to heap"
}
-func indaddr2(x *int) *int { // ERROR "leaking param: NAME-x"
- return *&x
+func indaddr2(x *int) *int { // ERROR "leaking param: NAME-x"
+ return *&x // ERROR "&x does not escape"
}
-func indaddr3(x *int32) *int { // ERROR "leaking param: NAME-x"
- return *(**int)(unsafe.Pointer(&x))
+func indaddr3(x *int32) *int { // ERROR "leaking param: NAME-x"
+ return *(**int)(unsafe.Pointer(&x)) // ERROR "&x does not escape"
}
// From package math:
func Float32bits(f float32) uint32 {
- return *(*uint32)(unsafe.Pointer(&f))
+ return *(*uint32)(unsafe.Pointer(&f)) // ERROR "&f does not escape"
}
func Float32frombits(b uint32) float32 {
- return *(*float32)(unsafe.Pointer(&b))
+ return *(*float32)(unsafe.Pointer(&b)) // ERROR "&b does not escape"
}
func Float64bits(f float64) uint64 {
- return *(*uint64)(unsafe.Pointer(&f))
+ return *(*uint64)(unsafe.Pointer(&f)) // ERROR "&f does not escape"
}
func Float64frombits(b uint64) float64 {
- return *(*float64)(unsafe.Pointer(&b))
+ return *(*float64)(unsafe.Pointer(&b)) // ERROR "&b does not escape"
}
// contrast with
-func float64bitsptr(f float64) *uint64 { // ERROR "moved to heap: NAME-f"
- return (*uint64)(unsafe.Pointer(&f))
+func float64bitsptr(f float64) *uint64 { // ERROR "moved to heap: NAME-f"
+ return (*uint64)(unsafe.Pointer(&f)) // ERROR "&f escapes to heap"
}
-func float64ptrbitsptr(f *float64) *uint64 { // ERROR "leaking param: NAME-f"
+func float64ptrbitsptr(f *float64) *uint64 { // ERROR "leaking param: NAME-f"
return (*uint64)(unsafe.Pointer(f))
}
-func typesw(i interface{}) *int { // ERROR "leaking param: NAME-i"
+func typesw(i interface{}) *int { // ERROR "leaking param: NAME-i"
switch val := i.(type) {
case *int:
return val
case *int8:
- v := int(*val) // ERROR "moved to heap: NAME-v"
- return &v
+ v := int(*val) // ERROR "moved to heap: NAME-v"
+ return &v // ERROR "&v escapes to heap"
}
return nil
}
-func exprsw(i *int) *int { // ERROR "leaking param: NAME-i"
+func exprsw(i *int) *int { // ERROR "leaking param: NAME-i"
switch j := i; *j + 110 {
case 12:
return j
}
// assigning to an array element is like assigning to the array
-func foo60(i *int) *int { // ERROR "leaking param: NAME-i"
+func foo60(i *int) *int { // ERROR "leaking param: NAME-i"
var a [12]*int
a[0] = i
return a[1]
}
-func foo60a(i *int) *int {
+func foo60a(i *int) *int { // ERROR "i does not escape"
var a [12]*int
a[0] = i
return nil
}
// assigning to a struct field is like assigning to the struct
-func foo61(i *int) *int { // ERROR "leaking param: NAME-i"
+func foo61(i *int) *int { // ERROR "leaking param: NAME-i"
type S struct {
- a,b *int
+ a, b *int
}
var s S
s.a = i
return s.b
}
-func foo61a(i *int) *int {
+func foo61a(i *int) *int { // ERROR "i does not escape"
type S struct {
- a,b *int
+ a, b *int
}
var s S
s.a = i
// assigning to a struct field is like assigning to the struct but
// here this subtlety is lost, since s.a counts as an assignment to a
// track-losing dereference.
-func foo62(i *int) *int { // ERROR "leaking param: NAME-i"
+func foo62(i *int) *int { // ERROR "leaking param: NAME-i"
type S struct {
- a,b *int
+ a, b *int
}
- s := new(S)
+ s := new(S) // ERROR "new[(]S[)] does not escape"
s.a = i
- return nil // s.b
+ return nil // s.b
}
+type M interface {
+ M()
+}
-type M interface { M() }
-
-func foo63(m M) {
+func foo63(m M) { // ERROR "m does not escape"
}
-func foo64(m M) { // ERROR "leaking param: NAME-m"
+func foo64(m M) { // ERROR "leaking param: NAME-m"
m.M()
}
type MV int
+
func (MV) M() {}
func foo65() {
var mv MV
- foo63(&mv)
+ foo63(&mv) // ERROR "&mv does not escape"
}
func foo66() {
- var mv MV // ERROR "moved to heap: NAME-mv"
- foo64(&mv)
+ var mv MV // ERROR "moved to heap: NAME-mv"
+ foo64(&mv) // ERROR "&mv escapes to heap"
}
func foo67() {
func foo68() {
var mv MV
- foo64(mv) // escapes but it's an int so irrelevant
+ foo64(mv) // escapes but it's an int so irrelevant
}
-func foo69(m M) { // ERROR "leaking param: NAME-m"
+func foo69(m M) { // ERROR "leaking param: NAME-m"
foo64(m)
}
-func foo70(mv1 *MV, m M) { // ERROR "leaking param: NAME-mv1" "leaking param: NAME-m"
+func foo70(mv1 *MV, m M) { // ERROR "leaking param: NAME-mv1" "leaking param: NAME-m"
m = mv1
foo64(m)
}
-func foo71(x *int) []*int { // ERROR "leaking param: NAME-x"
+func foo71(x *int) []*int { // ERROR "leaking param: NAME-x"
var y []*int
y = append(y, x)
return y
}
-func foo71a(x int) []*int { // ERROR "moved to heap: NAME-x"
+func foo71a(x int) []*int { // ERROR "moved to heap: NAME-x"
var y []*int
- y = append(y, &x)
+ y = append(y, &x) // ERROR "&x escapes to heap"
return y
}
func foo72() {
var x int
var y [1]*int
- y[0] = &x
+ y[0] = &x // ERROR "&x does not escape"
}
func foo72aa() [10]*int {
- var x int // ERROR "moved to heap: NAME-x"
+ var x int // ERROR "moved to heap: NAME-x"
var y [10]*int
- y[0] = &x
+ y[0] = &x // ERROR "&x escapes to heap"
return y
}
func foo72a() {
var y [10]*int
for i := 0; i < 10; i++ {
- x := i // not moved to heap b/c y goes nowhere
- y[i] = &x
+ // escapes its scope
+ x := i // ERROR "moved to heap: NAME-x"
+ y[i] = &x // ERROR "&x escapes to heap"
}
return
}
func foo72b() [10]*int {
var y [10]*int
for i := 0; i < 10; i++ {
- x := i // ERROR "moved to heap: NAME-x"
- y[i] = &x
+ x := i // ERROR "moved to heap: NAME-x"
+ y[i] = &x // ERROR "&x escapes to heap"
}
return y
}
-
// issue 2145
func foo73() {
- s := []int{3,2,1}
+ s := []int{3, 2, 1} // ERROR "slice literal does not escape"
for _, v := range s {
- vv := v // ERROR "moved to heap: NAME-vv"
- defer func() { // "func literal escapes its scope" "&vv escapes its scope"
- println(vv)
+ vv := v // ERROR "moved to heap: NAME-vv"
+ // actually just escapes its scope
+ defer func() { // ERROR "func literal escapes to heap"
+ println(vv) // ERROR "&vv escapes to heap"
}()
}
}
func foo74() {
- s := []int{3,2,1}
+ s := []int{3, 2, 1} // ERROR "slice literal does not escape"
for _, v := range s {
- vv := v // ERROR "moved to heap: NAME-vv"
- fn := func() { // "func literal escapes its scope" "&vv escapes its scope"
- println(vv)
+ vv := v // ERROR "moved to heap: NAME-vv"
+ // actually just escapes its scope
+ fn := func() { // ERROR "func literal escapes to heap"
+ println(vv) // ERROR "&vv escapes to heap"
}
defer fn()
}
}
-func myprint(y *int, x ...interface{}) *int { // ERROR "leaking param: NAME-y"
+func myprint(y *int, x ...interface{}) *int { // ERROR "x does not escape" "leaking param: NAME-y"
return y
}
-func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "leaking param: NAME-x"
- return &x[0]
+func myprint1(y *int, x ...interface{}) *interface{} { // ERROR "y does not escape" "leaking param: NAME-x"
+ return &x[0] // ERROR "&x.0. escapes to heap"
}
func foo75(z *int) { // ERROR "leaking param: NAME-z"
- myprint(z, 1, 2, 3)
+ myprint(z, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
}
-func foo75a(z *int) {
- myprint1(z, 1, 2, 3) // "[.][.][.] argument escapes to heap"
+func foo75a(z *int) { // ERROR "z does not escape"
+ myprint1(z, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
}
-func foo76(z *int) {
- myprint(nil, z)
+func foo76(z *int) { // ERROR "leaking param: NAME-z"
+ myprint(nil, z) // ERROR "[.][.][.] argument does not escape"
}
-func foo76a(z *int) { // ERROR "leaking param: NAME-z"
- myprint1(nil, z) // "[.][.][.] argument escapes to heap"
+func foo76a(z *int) { // ERROR "leaking param: NAME-z"
+ myprint1(nil, z) // ERROR "[.][.][.] argument escapes to heap"
}
func foo76b() {
- myprint(nil, 1, 2, 3)
+ myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
}
func foo76c() {
- myprint1(nil, 1, 2, 3) // "[.][.][.] argument escapes to heap"
+ myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
}
func foo76d() {
- defer myprint(nil, 1, 2, 3)
+ defer myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument does not escape"
}
func foo76e() {
- defer myprint1(nil, 1, 2, 3) // "[.][.][.] argument escapes to heap"
+ defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
}
func foo76f() {
for {
- defer myprint(nil, 1, 2, 3) // "[.][.][.] argument escapes its scope"
+ // TODO: This one really only escapes its scope, but we don't distinguish yet.
+ defer myprint(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
}
}
func foo76g() {
for {
- defer myprint1(nil, 1, 2, 3) // "[.][.][.] argument escapes to heap"
+ defer myprint1(nil, 1, 2, 3) // ERROR "[.][.][.] argument escapes to heap"
}
}
-func foo77(z []interface{}) {
- myprint(nil, z...) // z does not escape
+func foo77(z []interface{}) { // ERROR "z does not escape"
+ myprint(nil, z...) // z does not escape
}
-func foo77a(z []interface{}) { // ERROR "leaking param: NAME-z"
+func foo77a(z []interface{}) { // ERROR "leaking param: NAME-z"
myprint1(nil, z...)
}
-func foo78(z int) *int { // ERROR "moved to heap: NAME-z"
- return &z // "&z escapes"
+func foo78(z int) *int { // ERROR "moved to heap: NAME-z"
+ return &z // ERROR "&z escapes to heap"
}
-func foo78a(z int) *int { // ERROR "moved to heap: NAME-z"
- y := &z
- x := &y
- return *x // really return y
+func foo78a(z int) *int { // ERROR "moved to heap: NAME-z"
+ y := &z // ERROR "&z escapes to heap"
+ x := &y // ERROR "&y does not escape"
+ return *x // really return y
}
func foo79() *int {
- return new(int) // "moved to heap: new[(]int[)]"
+ return new(int) // ERROR "new[(]int[)] escapes to heap"
}
func foo80() *int {
var z *int
for {
- z = new(int) // "new[(]int[)] escapes its scope"
+ // Really just escapes its scope but we don't distinguish
+ z = new(int) // ERROR "new[(]int[)] escapes to heap"
}
_ = z
return nil
func foo81() *int {
for {
- z := new(int)
+ z := new(int) // ERROR "new[(]int[)] does not escape"
_ = z
}
return nil
}
type LimitedFooer struct {
- Fooer
- N int64
+ Fooer
+ N int64
}
-func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: NAME-r"
- return &LimitedFooer{r, n}
+func LimitFooer(r Fooer, n int64) Fooer { // ERROR "leaking param: NAME-r"
+ return &LimitedFooer{r, n} // ERROR "&struct literal escapes to heap"
}
-func foo90(x *int) map[*int]*int { // ERROR "leaking param: NAME-x"
- return map[*int]*int{ nil: x }
+func foo90(x *int) map[*int]*int { // ERROR "leaking param: NAME-x"
+ return map[*int]*int{nil: x} // ERROR "map literal escapes to heap"
}
-func foo91(x *int) map[*int]*int { // ERROR "leaking param: NAME-x"
- return map[*int]*int{ x:nil }
+func foo91(x *int) map[*int]*int { // ERROR "leaking param: NAME-x"
+ return map[*int]*int{x: nil} // ERROR "map literal escapes to heap"
}
-func foo92(x *int) [2]*int { // ERROR "leaking param: NAME-x"
- return [2]*int{ x, nil }
+func foo92(x *int) [2]*int { // ERROR "leaking param: NAME-x"
+ return [2]*int{x, nil}
}
// does not leak c
-func foo93(c chan *int) *int {
+func foo93(c chan *int) *int { // ERROR "c does not escape"
for v := range c {
return v
}
}
// does not leak m
-func foo94(m map[*int]*int, b bool) *int {
+func foo94(m map[*int]*int, b bool) *int { // ERROR "m does not escape"
for k, v := range m {
if b {
return k
}
// does leak x
-func foo95(m map[*int]*int, x *int) { // ERROR "leaking param: NAME-x"
+func foo95(m map[*int]*int, x *int) { // ERROR "m does not escape" "leaking param: NAME-x"
m[x] = x
}
// does not leak m
-func foo96(m []*int) *int {
+func foo96(m []*int) *int { // ERROR "m does not escape"
return m[0]
}
// does leak m
-func foo97(m [1]*int) *int { // ERROR "leaking param: NAME-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 {
+func foo98(m map[int]*int) *int { // ERROR "m does not escape"
return m[0]
}
// does leak m
-func foo99(m *[1]*int) []*int { // ERROR "leaking param: NAME-m"
+func foo99(m *[1]*int) []*int { // ERROR "leaking param: NAME-m"
return m[:]
}
// does not leak m
-func foo100(m []*int) *int {
+func foo100(m []*int) *int { // ERROR "m does not escape"
for _, v := range m {
return v
}
}
// does leak m
-func foo101(m [1]*int) *int { // ERROR "leaking param: NAME-m"
+func foo101(m [1]*int) *int { // ERROR "leaking param: NAME-m"
for _, v := range m {
return v
}
return nil
}
+// does not leak m
+func foo101a(m [1]*int) *int { // ERROR "m does not escape"
+ for i := range m { // ERROR "moved to heap: NAME-i"
+ return &i // ERROR "&i escapes to heap"
+ }
+ return nil
+}
+
// does leak x
-func foo102(m []*int, x *int) { // ERROR "leaking param: NAME-x"
+func foo102(m []*int, x *int) { // ERROR "m does not escape" "leaking param: NAME-x"
m[0] = x
}
// does not leak x
-func foo103(m [1]*int, x *int) {
+func foo103(m [1]*int, x *int) { // ERROR "m does not escape" "x does not escape"
m[0] = x
}
var y []*int
// does not leak x
-func foo104(x []*int) {
+func foo104(x []*int) { // ERROR "x does not escape"
copy(y, x)
}
// does not leak x
-func foo105(x []*int) {
+func foo105(x []*int) { // ERROR "x does not escape"
_ = append(y, x...)
}
// does leak x
-func foo106(x *int) { // ERROR "leaking param: NAME-x"
+func foo106(x *int) { // ERROR "leaking param: NAME-x"
_ = append(y, x)
}
+
+func foo107(x *int) map[*int]*int { // ERROR "leaking param: NAME-x"
+ return map[*int]*int{x: nil} // ERROR "map literal escapes to heap"
+}
+
+func foo108(x *int) map[*int]*int { // ERROR "leaking param: NAME-x"
+ return map[*int]*int{nil: x} // ERROR "map literal escapes to heap"
+}
+
+func foo109(x *int) *int { // ERROR "leaking param: NAME-x"
+ m := map[*int]*int{x: nil} // ERROR "map literal does not escape"
+ for k, _ := range m {
+ return k
+ }
+ return nil
+}
+
+func foo110(x *int) *int { // ERROR "leaking param: NAME-x"
+ m := map[*int]*int{nil: x} // ERROR "map literal does not escape"
+ return m[nil]
+}
+
+func foo111(x *int) *int { // ERROR "leaking param: NAME-x"
+ m := []*int{x} // ERROR "slice literal does not escape"
+ return m[0]
+}
+
+func foo112(x *int) *int { // ERROR "leaking param: NAME-x"
+ m := [1]*int{x}
+ return m[0]
+}
+
+func foo113(x *int) *int { // ERROR "leaking param: NAME-x"
+ m := Bar{ii: x}
+ return m.ii
+}
+
+func foo114(x *int) *int { // ERROR "leaking param: NAME-x"
+ m := &Bar{ii: x} // ERROR "&struct literal does not escape"
+ return m.ii
+}
+
+func foo115(x *int) *int { // ERROR "leaking param: NAME-x"
+ return (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(x)) + 1))
+}
+
+func foo116(b bool) *int {
+ if b {
+ x := 1 // ERROR "moved to heap: NAME-x"
+ return &x // ERROR "&x escapes to heap"
+ } else {
+ y := 1 // ERROR "moved to heap: NAME-y"
+ return &y // ERROR "&y escapes to heap"
+ }
+ return nil
+}
+
+func foo117(unknown func(interface{})) { // ERROR "unknown does not escape"
+ x := 1 // ERROR "moved to heap: NAME-x"
+ unknown(&x) // ERROR "&x escapes to heap"
+}
+
+func foo118(unknown func(*int)) { // ERROR "unknown does not escape"
+ x := 1 // ERROR "moved to heap: NAME-x"
+ unknown(&x) // ERROR "&x escapes to heap"
+}