nodconst(&con, types[TINT32], 0);
p = gins(ACMP, &con, N);
p->reg = 0;
- patch(gbranch(ABNE, T, -1), retpc);
+ p = gbranch(ABEQ, T, +1);
+ cgen_ret(N);
+ patch(p, pc);
}
break;
}
if(proc == 2) {
nodreg(®, types[TINT64], D_AX);
gins(ATESTQ, ®, ®);
- patch(gbranch(AJNE, T, -1), retpc);
+ p = gbranch(AJEQ, T, +1);
+ cgen_ret(N);
+ patch(p, pc);
}
break;
}
{
Prog *p;
- genlist(n->list); // copy out args
- if(hasdefer || curfn->exit) {
- gjmp(retpc);
- return;
- }
+ if(n != N)
+ genlist(n->list); // copy out args
+ if(hasdefer)
+ ginscall(deferreturn, 0);
+ genlist(curfn->exit);
p = gins(ARET, N, N);
- if(n->op == ORETJMP) {
+ if(n != N && n->op == ORETJMP) {
p->to.type = D_EXTERN;
p->to.sym = linksym(n->left->sym);
}
if(proc == 2) {
nodreg(®, types[TINT64], D_AX);
gins(ATESTL, ®, ®);
- patch(gbranch(AJNE, T, -1), retpc);
+ p = gbranch(AJEQ, T, +1);
+ cgen_ret(N);
+ patch(p, pc);
}
break;
}
// typecheck will insert a PTRLIT node under CONVNOP,
// tag it with escape analysis result.
clos->left->esc = func->esc;
+ // non-escaping temp to use, if any.
+ // orderexpr did not compute the type; fill it in now.
+ if(func->left != N) {
+ func->left->type = clos->left->left->type;
+ func->left->orig->type = func->left->type;
+ clos->left->right = func->left;
+ func->left = N;
+ }
walkexpr(&clos, init);
return clos;
int wl, wr, lno, et;
Val v, rv;
Mpint b;
+ NodeList *l1, *l2;
// pick off just the opcodes that can be
// constant evaluated.
default:
return;
case OADD:
- case OADDSTR:
case OAND:
case OANDAND:
case OANDNOT:
if(!okforconst[n->type->etype] && n->type->etype != TNIL)
return;
break;
+
+ case OADDSTR:
+ // merge adjacent constants in the argument list.
+ for(l1=n->list; l1 != nil; l1= l1->next) {
+ if(isconst(l1->n, CTSTR) && l1->next != nil && isconst(l1->next->n, CTSTR)) {
+ l2 = l1;
+ len = 0;
+ while(l2 != nil && isconst(l2->n, CTSTR)) {
+ nr = l2->n;
+ len += nr->val.u.sval->len;
+ l2 = l2->next;
+ }
+ // merge from l1 up to but not including l2
+ str = mal(sizeof(*str) + len);
+ str->len = len;
+ len = 0;
+ l2 = l1;
+ while(l2 != nil && isconst(l2->n, CTSTR)) {
+ nr = l2->n;
+ memmove(str->s+len, nr->val.u.sval->s, nr->val.u.sval->len);
+ len += nr->val.u.sval->len;
+ l2 = l2->next;
+ }
+ nl = nod(OXXX, N, N);
+ *nl = *l1->n;
+ nl->orig = nl;
+ nl->val.ctype = CTSTR;
+ nl->val.u.sval = str;
+ l1->n = nl;
+ l1->next = l2;
+ }
+ }
+ // fix list end pointer.
+ for(l2=n->list; l2 != nil; l2=l2->next)
+ n->list->end = l2;
+ // collapse single-constant list to single constant.
+ if(count(n->list) == 1 && isconst(n->list->n, CTSTR)) {
+ n->op = OLITERAL;
+ n->val = n->list->n->val;
+ }
+ return;
}
nl = n->left;
if(cmpslit(nl, nr) > 0)
goto settrue;
goto setfalse;
- case TUP(OADDSTR, CTSTR):
- len = v.u.sval->len + rv.u.sval->len;
- str = mal(sizeof(*str) + len);
- str->len = len;
- memcpy(str->s, v.u.sval->s, v.u.sval->len);
- memcpy(str->s+v.u.sval->len, rv.u.sval->s, rv.u.sval->len);
- str->len = len;
- v.u.sval = str;
- break;
case TUP(OOROR, CTBOOL):
if(v.u.bval || rv.u.bval)
lineno = lno;
}
-static void
+static int
escassignfromtag(EscState *e, Strlit *note, NodeList *dsts, Node *src)
{
- int em;
+ int em, em0;
em = parsetag(note);
-
+
if(em == EscUnknown) {
escassign(e, &e->theSink, src);
- return;
+ return em;
}
-
+
+ if(em == EscNone)
+ return em;
+
+ em0 = em;
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);
+ return em0;
}
// This is a bit messier than fortunate, pulled out of esc's big
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) {
// function in same mutually recursive group. Incorporate into flow graph.
// print("esc local fn: %N\n", fn->ntype);
if(lr->n->isddd && !n->isddd) {
// Introduce ODDDARG node to represent ... allocation.
src = nod(ODDDARG, N, N);
+ src->type = typ(TARRAY);
+ src->type->type = lr->n->type->type;
+ src->type->bound = count(ll);
src->escloopdepth = e->loopdepth;
src->lineno = n->lineno;
src->esc = EscNone; // until we find otherwise
src = nod(ODDDARG, N, N);
src->escloopdepth = e->loopdepth;
src->lineno = n->lineno;
+ src->type = typ(TARRAY);
+ src->type->type = t->type->type;
+ src->type->bound = count(ll);
src->esc = EscNone; // until we find otherwise
e->noesc = list(e->noesc, src);
n->right = src;
}
- if(haspointers(t->type))
- escassignfromtag(e, t->note, n->escretval, src);
+ if(haspointers(t->type)) {
+ if(escassignfromtag(e, t->note, n->escretval, src) == EscNone) {
+ switch(src->op) {
+ case OCLOSURE:
+ case ODDDARG:
+ // The callee has already been analyzed, so its arguments have esc tags.
+ // The argument is marked as not escaping at all.
+ // Record that fact so that any temporary used for
+ // synthesizing this expression can be reclaimed when
+ // the function returns.
+ // This 'noescape' is even stronger than the usual esc == EscNone.
+ // src->esc == EscNone means that src does not escape the current function.
+ // src->noescape = 1 here means that src does not escape this statement
+ // in the current function.
+ src->noescape = 1;
+ break;
+ }
+ }
+ }
if(src != ll->n)
break;
t = t->down;
// Binary
case OADD:
- case OADDSTR:
case OAND:
case OANDAND:
case OANDNOT:
exprfmt(f, n->right, nprec+1);
return 0;
+ case OADDSTR:
+ for(l=n->list; l; l=l->next) {
+ if(l != n->list)
+ fmtprint(f, " + ");
+ exprfmt(f, l->n, nprec);
+ }
+ return 0;
+
case OCMPSTR:
case OCMPIFACE:
exprfmt(f, n->left, nprec);
EXTERN Prog* breakpc;
EXTERN Prog* pc;
EXTERN Prog* firstpc;
-EXTERN Prog* retpc;
EXTERN Node* nodfp;
EXTERN int disable_checknil;
// Ordercall orders the call expression n.
// n->op is OCALLMETH/OCALLFUNC/OCALLINTER.
static void
-ordercall(Node *n, Order *order)
+ordercall(Node *n, Order *order, int special)
{
orderexpr(&n->left, order);
ordercallargs(&n->list, order);
+ if(!special)
+ orderexpr(&n->right, order); // ODDDARG temp
}
// Ordermapassign appends n to order->out, introducing temporaries
// a map index expression.
t = marktemp(order);
orderexpr(&n->left, order);
- orderexpr(&n->right, order);
n->left = ordersafeexpr(n->left, order);
tmp1 = treecopy(n->left);
if(tmp1->op == OINDEXMAP)
tmp1 = ordercopyexpr(tmp1, n->left->type, order, 0);
n->right = nod(n->etype, tmp1, n->right);
typecheck(&n->right, Erv);
+ orderexpr(&n->right, order);
n->etype = 0;
n->op = OAS;
ordermapassign(n, order);
// Special: avoid copy of func call n->rlist->n.
t = marktemp(order);
orderexprlist(n->list, order);
- ordercall(n->rlist->n, order);
+ ordercall(n->rlist->n, order, 0);
ordermapassign(n, order);
cleantemp(t, order);
break;
case OCALLMETH:
// Special: handle call arguments.
t = marktemp(order);
- ordercall(n, order);
+ ordercall(n, order, 0);
order->out = list(order->out, n);
cleantemp(t, order);
break;
poptemp(t1, order);
break;
default:
- ordercall(n->left, order);
+ ordercall(n->left, order, 1);
break;
}
order->out = list(order->out, n);
break;
case ORANGE:
- // TODO(rsc): Clean temporaries.
+ // n->right is the expression being ranged over.
+ // order it, and then make a copy if we need one.
+ // We almost always do, to ensure that we don't
+ // see any value changes made during the loop.
+ // Usually the copy is cheap (e.g., array pointer, chan, slice, string are all tiny).
+ // The exception is ranging over an array value (not a slice, not a pointer to array),
+ // which must make a copy to avoid seeing updates made during
+ // the range body. Ranging over an array value is uncommon though.
+ t = marktemp(order);
orderexpr(&n->right, order);
+ switch(n->type->etype) {
+ default:
+ fatal("orderstmt range %T", n->type);
+ case TARRAY:
+ if(count(n->list) < 2 || isblank(n->list->next->n)) {
+ // for i := range x will only use x once, to compute len(x).
+ // No need to copy it.
+ break;
+ }
+ // fall through
+ case TCHAN:
+ case TSTRING:
+ // chan, string, slice, array ranges use value multiple times.
+ // make copy.
+ r = n->right;
+ if(r->type->etype == TSTRING && r->type != types[TSTRING]) {
+ r = nod(OCONV, r, N);
+ r->type = types[TSTRING];
+ typecheck(&r, Erv);
+ }
+ n->right = ordercopyexpr(r, r->type, order, 0);
+ break;
+ case TMAP:
+ // copy the map value in case it is a map literal.
+ // TODO(rsc): Make tmp = literal expressions reuse tmp.
+ // For maps tmp is just one word so it hardly matters.
+ r = n->right;
+ n->right = ordercopyexpr(r, r->type, order, 0);
+ // temp is the iterator instead of the map value.
+ n->left = ordertemp(hiter(n->right->type), order, 1);
+ break;
+ }
for(l=n->list; l; l=l->next)
orderexprinplace(&l->n, order);
orderblock(&n->nbody);
order->out = list(order->out, n);
+ cleantemp(t, order);
break;
case ORETURN:
orderexpr(Node **np, Order *order)
{
Node *n;
+ Type *t;
int lno;
n = *np;
orderexprlist(n->rlist, order);
break;
+ case OADDSTR:
+ // Addition of strings turns into a function call.
+ // Allocate a temporary to hold the strings.
+ // Fewer than 5 strings use direct runtime helpers.
+ orderexprlist(n->list, order);
+ if(count(n->list) > 5) {
+ t = typ(TARRAY);
+ t->bound = count(n->list);
+ t->type = types[TSTRING];
+ n->left = ordertemp(t, order, 0);
+ }
+ break;
+
case OINDEXMAP:
// key must be addressable
orderexpr(&n->left, order);
case OCALLINTER:
case OAPPEND:
case OCOMPLEX:
- ordercall(n, order);
+ ordercall(n, order, 0);
n = ordercopyexpr(n, n->type, order, 0);
break;
+ case OCLOSURE:
+ if(n->noescape && n->cvars != nil) {
+ t = typ(TARRAY);
+ t->type = types[TUNSAFEPTR];
+ t->bound = 1+count(n->cvars);
+ n->left = ordertemp(t, order, 0);
+ }
+ break;
+
+ case ODDDARG:
+ if(n->noescape) {
+ // The ddd argument does not live beyond the call it is created for.
+ // Allocate a temporary that will be cleaned up when this statement
+ // completes. We could be more aggressive and try to arrange for it
+ // to be cleaned up when the call completes.
+ n->left = ordertemp(n->type, order, 0);
+ }
+ break;
+
case ORECV:
n = ordercopyexpr(n, n->type, order, 1);
break;
{
Plist *pl;
Node nod1, *n;
- Prog *ptxt, *p, *p1;
+ Prog *ptxt, *p;
int32 lno;
Type *t;
Iter save;
}
genlist(curfn->enter);
-
- retpc = nil;
- if(hasdefer || curfn->exit) {
- p1 = gjmp(nil);
- retpc = gjmp(nil);
- patch(p1, pc);
- }
-
genlist(curfn->nbody);
gclean();
checklabels();
if(curfn->type->outtuple != 0)
ginscall(throwreturn, 0);
- if(retpc)
- patch(retpc, pc);
ginit();
+ // TODO: Determine when the final cgen_ret can be omitted. Perhaps always?
+ cgen_ret(nil);
if(hasdefer) {
- ginscall(deferreturn, 0);
// deferreturn pretends to have one uintptr argument.
// Reserve space for it so stack scanner is happy.
if(maxarg < widthptr)
maxarg = widthptr;
}
- if(curfn->exit)
- genlist(curfn->exit);
gclean();
if(nerrors != 0)
goto ret;
a = n->right;
lno = setlineno(a);
- if(t->etype == TSTRING && !eqtype(t, types[TSTRING])) {
- a = nod(OCONV, n->right, N);
- a->type = types[TSTRING];
- }
v1 = n->list->n;
v2 = N;
- if(n->list->next)
+ if(n->list->next && !isblank(n->list->next->n))
v2 = n->list->next->n;
// n->list has no meaning anymore, clear it
// to avoid erroneous processing by racewalk.
n->list = nil;
hv2 = N;
- if(v2 == N && t->etype == TARRAY) {
- // will have just one reference to argument.
- // no need to make a potentially expensive copy.
- ha = a;
- } else {
- ha = temp(a->type);
- init = list(init, nod(OAS, ha, a));
- }
-
switch(t->etype) {
default:
fatal("walkrange");
case TARRAY:
+ // orderstmt arranged for a copy of the array/slice variable if needed.
+ ha = a;
hv1 = temp(types[TINT]);
hn = temp(types[TINT]);
hp = nil;
break;
case TMAP:
- // allocate an iterator state structure on the stack
+ // orderstmt allocated the iterator for us.
+ // we only use a once, so no copy needed.
+ ha = a;
th = hiter(t);
- hit = temp(th);
- init = list(init, nod(OAS, hit, N));
+ hit = n->left;
+ n->left = N;
keyname = newname(th->type->sym); // depends on layout of iterator struct. See reflect.c:hiter
valname = newname(th->type->down->sym); // ditto
break;
case TCHAN:
+ // orderstmt arranged for a copy of the channel variable.
+ ha = a;
+ n->ntest = N;
+
hv1 = temp(t->type);
if(haspointers(t->type))
init = list(init, nod(OAS, hv1, N));
break;
case TSTRING:
+ // orderstmt arranged for a copy of the string variable.
+ ha = a;
+
ohv1 = temp(types[TINT]);
hv1 = temp(types[TINT]);
vauto = temp(ptrto(t));
// set auto to point at new temp or heap (3 assign)
- if(n->esc == EscNone) {
- a = nod(OAS, temp(t), N);
- typecheck(&a, Etop);
- *init = list(*init, a); // zero new temp
- a = nod(OADDR, a->left, N);
+ if(n->left != N) {
+ // temp allocated during order.c for dddarg
+ if(vstat == N) {
+ a = nod(OAS, n->left, N);
+ typecheck(&a, Etop);
+ *init = list(*init, a); // zero new temp
+ }
+ a = nod(OADDR, n->left, N);
+ } else if(n->esc == EscNone) {
+ a = temp(t);
+ if(vstat == N) {
+ a = nod(OAS, temp(t), N);
+ typecheck(&a, Etop);
+ *init = list(*init, a); // zero new temp
+ a = a->left;
+ }
+ a = nod(OADDR, a, N);
} else {
a = nod(ONEW, N, N);
a->list = list1(typenod(t));
if(!isptr[t->etype])
fatal("anylit: not ptr");
- r = nod(ONEW, N, N);
- r->typecheck = 1;
- r->type = t;
- r->esc = n->esc;
+ if(n->right != N) {
+ r = nod(OADDR, n->right, N);
+ typecheck(&r, Erv);
+ } else {
+ r = nod(ONEW, N, N);
+ r->typecheck = 1;
+ r->type = t;
+ r->esc = n->esc;
+ }
walkexpr(&r, init);
-
a = nod(OAS, var, r);
typecheck(&a, Etop);
if(iscmp[n->op]) {
n->etype = n->op;
n->op = OCMPSTR;
- } else if(n->op == OADD)
+ } else if(n->op == OADD) {
+ // create OADDSTR node with list of strings in x + y + z + (w + v) + ...
n->op = OADDSTR;
+ if(l->op == OADDSTR)
+ n->list = l->list;
+ else
+ n->list = list1(l);
+ if(r->op == OADDSTR)
+ n->list = concat(n->list, r->list);
+ else
+ n->list = list(n->list, r);
+ n->left = N;
+ n->right = N;
+ }
}
if(et == TINTER) {
if(l->op == OLITERAL && l->val.ctype == CTNIL) {
// s + "badgerbadgerbadger" == "badgerbadgerbadger"
if((n->etype == OEQ || n->etype == ONE) &&
isconst(n->right, CTSTR) &&
- n->left->op == OADDSTR && isconst(n->left->right, CTSTR) &&
- cmpslit(n->right, n->left->right) == 0) {
- r = nod(n->etype, nod(OLEN, n->left->left, N), nodintconst(0));
+ n->left->op == OADDSTR && count(n->left->list) == 2 &&
+ isconst(n->left->list->next->n, CTSTR) &&
+ cmpslit(n->right, n->left->list->next->n) == 0) {
+ r = nod(n->etype, nod(OLEN, n->left->list->n, N), nodintconst(0));
typecheck(&r, Erv);
walkexpr(&r, init);
r->type = n->type;
* 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, int esc)
+mkdotargslice(NodeList *lr0, NodeList *nn, Type *l, int fp, NodeList **init, Node *ddd)
{
Node *a, *n;
Type *tslice;
-
+ int esc;
+
+ esc = EscUnknown;
+ if(ddd != nil)
+ esc = ddd->esc;
+
tslice = typ(TARRAY);
tslice->type = l->type->type;
tslice->bound = -1;
n->type = tslice;
} else {
n = nod(OCOMPLIT, N, typenod(tslice));
+ if(ddd != nil)
+ n->left = ddd->left; // temporary to use
n->list = lr0;
n->esc = esc;
typecheck(&n, Erv);
static NodeList*
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.
- esc = EscUnknown;
- if(call->right)
- esc = call->right->esc;
- nn = mkdotargslice(lr, nn, l, fp, init, esc);
+ nn = mkdotargslice(lr, nn, l, fp, init, call->right);
goto ret;
}
addstr(Node *n, NodeList **init)
{
Node *r, *cat, *slice;
- NodeList *args;
- int count;
+ NodeList *args, *l;
+ int c;
Type *t;
- count = 0;
- for(r=n; r->op == OADDSTR; r=r->left)
- count++; // r->right
- count++; // r
- if(count < 2)
- yyerror("addstr count %d too small", count);
+ // orderexpr rewrote OADDSTR to have a list of strings.
+ c = count(n->list);
+ if(c < 2)
+ yyerror("addstr count %d too small", c);
// build list of string arguments
args = nil;
- for(r=n; r->op == OADDSTR; r=r->left)
- args = concat(list1(conv(r->right, types[TSTRING])), args);
- args = concat(list1(conv(r, types[TSTRING])), args);
+ for(l=n->list; l != nil; l=l->next)
+ args = list(args, conv(l->n, types[TSTRING]));
- if(count <= 5) {
+ if(c <= 5) {
// small numbers of strings use direct runtime helpers.
- snprint(namebuf, sizeof(namebuf), "concatstring%d", count);
+ // note: orderexpr knows this cutoff too.
+ snprint(namebuf, sizeof(namebuf), "concatstring%d", c);
} else {
// large numbers of strings are passed to the runtime as a slice.
strcpy(namebuf, "concatstrings");
t->type = types[TSTRING];
t->bound = -1;
slice = nod(OCOMPLIT, N, typenod(t));
+ slice->left = n->left;
slice->list = args;
slice->esc = EscNone;
args = list1(slice);
m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
m2[[2]string{"x", "y"}] = nil // ERROR "live at call to mapassign1: autotmp_[0-9]+ autotmp_[0-9]+$"
}
+
+// defer should not cause spurious ambiguously live variables
+
+func f25(b bool) {
+ defer g25()
+ if b {
+ return
+ }
+ var x string
+ _ = &x
+ x = g15() // ERROR "live at call to g15: x"
+ print(x) // ERROR "live at call to printstring: x"
+} // ERROR "live at call to deferreturn: x"
+
+func g25()
+
+// non-escaping ... slices passed to function call should die on return,
+// so that the temporaries do not stack and do not cause ambiguously
+// live variables.
+
+func f26(b bool) {
+ if b {
+ print26(1,2,3) // ERROR "live at call to print26: autotmp_[0-9]+$"
+ }
+ print26(4,5,6) // ERROR "live at call to print26: autotmp_[0-9]+$"
+ print26(7,8,9) // ERROR "live at call to print26: autotmp_[0-9]+$"
+ println()
+}
+
+//go:noescape
+func print26(...interface{})
+
+// non-escaping closures passed to function call should die on return
+
+func f27(b bool) {
+ x := 0
+ if b {
+ call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$"
+ }
+ call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$"
+ call27(func() {x++}) // ERROR "live at call to call27: autotmp_[0-9]+$"
+}
+
+//go:noescape
+func call27(func())
+
+// concatstring slice should die on return
+
+var s1, s2, s3, s4, s5, s6, s7, s8, s9, s10 string
+
+func f28(b bool) {
+ if b {
+ print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
+ }
+ print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
+ print(s1+s2+s3+s4+s5+s6+s7+s8+s9+s10) // ERROR "live at call to concatstrings: autotmp_[0-9]+$" "live at call to printstring: autotmp_[0-9]+$"
+}
+
+// map iterator should die on end of range loop
+
+func f29(b bool) {
+ if b {
+ for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$"
+ print(k) // ERROR "live at call to printstring: autotmp_[0-9]+$"
+ }
+ }
+ for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$"
+ print(k) // ERROR "live at call to printstring: autotmp_[0-9]+$"
+ }
+ for k := range m { // ERROR "live at call to mapiterinit: autotmp_[0-9]+$" "live at call to mapiternext: autotmp_[0-9]+$"
+ print(k) // ERROR "live at call to printstring: autotmp_[0-9]+$"
+ }
+}
+
+// copy of array of pointers should die at end of range loop
+
+var ptrarr [10]*int
+
+func f30(b bool) {
+ // two live temps during print(p):
+ // the copy of ptrarr and the internal iterator pointer.
+ if b {
+ for _, p := range ptrarr {
+ print(p) // ERROR "live at call to printpointer: autotmp_[0-9]+ autotmp_[0-9]+$"
+ }
+ }
+ for _, p := range ptrarr {
+ print(p) // ERROR "live at call to printpointer: autotmp_[0-9]+ autotmp_[0-9]+$"
+ }
+ for _, p := range ptrarr {
+ print(p) // ERROR "live at call to printpointer: autotmp_[0-9]+ autotmp_[0-9]+$"
+ }
+}