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;
+ if(func->alloc != N) {
+ func->alloc->type = clos->left->left->type;
+ func->alloc->orig->type = func->alloc->type;
+ clos->left->right = func->alloc;
+ func->alloc = N;
}
walkexpr(&clos, init);
// typecheck will insert a PTRLIT node under CONVNOP,
// tag it with escape analysis result.
clos->left->esc = n->esc;
+ // non-escaping temp to use, if any.
+ // orderexpr did not compute the type; fill it in now.
+ if(n->alloc != N) {
+ n->alloc->type = clos->left->left->type;
+ n->alloc->orig->type = n->alloc->type;
+ clos->left->right = n->alloc;
+ n->alloc = N;
+ }
walkexpr(&clos, init);
return clos;
}
if(haspointers(t->type)) {
if(escassignfromtag(e, t->note, n->escretval, src) == EscNone) {
- switch(src->op) {
+ a = src;
+ while(a->op == OCONVNOP)
+ a = a->left;
+ switch(a->op) {
+ case OCALLPART:
case OCLOSURE:
case ODDDARG:
+ case OARRAYLIT:
+ case OPTRLIT:
+ case OSTRUCTLIT:
// 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
// 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;
+ a->noescape = 1;
break;
}
}
// Arrange that receive expressions only appear in direct assignments
// x = <-c or as standalone statements <-c, never in larger expressions.
-// TODO(rsc): Temporaries are not cleaned in for, if, select, and swtch
-// statements. The cleaning needs to be introduced aggressively, so
-// that for example a temporary introduced during evaluation of an
-// if condition is killed in both the 'if' and 'else' bodies, not delayed
-// until after the entire if statement has completed.
+// TODO(rsc): The temporary introduction during multiple assignments
+// should be moved into this file, so that the temporaries can be cleaned
+// and so that conversions implicit in the OAS2FUNC and OAS2RECV
+// nodes can be made explicit and then have their temporaries cleaned.
// TODO(rsc): Goto and multilevel break/continue can jump over
// inserted VARKILL annotations. Work out a way to handle these.
}
}
-// Cleantempnopop emits VARKILL instructions for each temporary
+// Cleantempnopop emits to *out VARKILL instructions for each temporary
// above the mark on the temporary stack, but it does not pop them
// from the stack.
static void
-cleantempnopop(NodeList *mark, Order *order)
+cleantempnopop(NodeList *mark, Order *order, NodeList **out)
{
NodeList *l;
Node *kill;
for(l=order->temp; l != mark; l=l->next) {
kill = nod(OVARKILL, l->n, N);
typecheck(&kill, Etop);
- order->out = list(order->out, kill);
+ *out = list(*out, kill);
}
}
static void
cleantemp(NodeList *top, Order *order)
{
- cleantempnopop(top, order);
+ cleantempnopop(top, order, &order->out);
poptemp(top, order);
}
orderblock(NodeList **l)
{
Order order;
+ NodeList *mark;
memset(&order, 0, sizeof order);
+ mark = marktemp(&order);
orderstmtlist(*l, &order);
+ cleantemp(mark, &order);
*l = order.out;
}
// Orderexprinplace orders the side effects in *np and
// leaves them as the init list of the final *np.
static void
-orderexprinplace(Node **np, Order *TODO)
+orderexprinplace(Node **np, Order *outer)
{
Node *n;
+ NodeList **lp;
Order order;
- // TODO(rsc): Decide how much of the passed-in order to use.
- // For example, should the temporaries created during the
- // ordering of expr be added onto the caller's order temp list
- // for freeing? Probably.
- USED(TODO);
-
n = *np;
memset(&order, 0, sizeof order);
orderexpr(&n, &order);
addinit(&n, order.out);
- *np = n;
-}
-
-// Orderexprtolist orders the side effects in *np and
-// appends them to *out.
-static void
-orderexprtolist(Node **np, NodeList **out)
-{
- Node *n;
- Order order;
- n = *np;
- memset(&order, 0, sizeof order);
- orderexpr(&n, &order);
- *out = concat(*out, order.out);
+ // insert new temporaries from order
+ // at head of outer list.
+ lp = &order.temp;
+ while(*lp != nil)
+ lp = &(*lp)->next;
+ *lp = outer->temp;
+ outer->temp = order.temp;
+
*np = n;
}
{
Node *n;
Order order;
-
+ NodeList *mark;
+
n = *np;
memset(&order, 0, sizeof order);
+ mark = marktemp(&order);
orderstmt(n, &order);
+ cleantemp(mark, &order);
*np = liststmt(order.out);
}
default:
fatal("orderstmt %O", n->op);
+ case OVARKILL:
+ order->out = list(order->out, n);
+ break;
+
case OAS:
case OAS2:
case OAS2DOTTYPE:
case OCLOSE:
case OCOPY:
- case OPANIC:
case OPRINT:
case OPRINTN:
case ORECOVER:
break;
case OFOR:
- // TODO(rsc): Clean temporaries.
+ // Clean temporaries from condition evaluation at
+ // beginning of loop body and after for statement.
+ t = marktemp(order);
orderexprinplace(&n->ntest, order);
- orderstmtinplace(&n->nincr);
+ l = nil;
+ cleantempnopop(t, order, &l);
+ n->nbody = concat(l, n->nbody);
orderblock(&n->nbody);
+ orderstmtinplace(&n->nincr);
order->out = list(order->out, n);
+ cleantemp(t, order);
break;
case OIF:
- // TODO(rsc): Clean temporaries.
+ // Clean temporaries from condition at
+ // beginning of both branches.
+ t = marktemp(order);
orderexprinplace(&n->ntest, order);
+ l = nil;
+ cleantempnopop(t, order, &l);
+ n->nbody = concat(l, n->nbody);
+ l = nil;
+ cleantempnopop(t, order, &l);
+ n->nelse = concat(l, n->nelse);
+ poptemp(t, order);
orderblock(&n->nbody);
orderblock(&n->nelse);
order->out = list(order->out, n);
break;
+ case OPANIC:
+ // Special: argument will be converted to interface using convT2E
+ // so make sure it is an addressable temporary.
+ t = marktemp(order);
+ orderexpr(&n->left, order);
+ if(!isinter(n->left->type))
+ orderaddrtemp(&n->left, order);
+ order->out = list(order->out, n);
+ cleantemp(t, order);
+ break;
+
case ORANGE:
// n->right is the expression being ranged over.
// order it, and then make a copy if we need one.
// 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);
+ // n->alloc is the temp for the iterator.
+ n->alloc = ordertemp(types[TUINT8], order, 1);
break;
}
for(l=n->list; l; l=l->next)
break;
case OSELECT:
- // TODO(rsc): Clean temporaries.
+ // Special: clean case temporaries in each block entry.
+ // Select must enter one of its blocks, so there is no
+ // need for a cleaning at the end.
+ t = marktemp(order);
for(l=n->list; l; l=l->next) {
if(l->n->op != OXCASE)
fatal("order select case %O", l->n->op);
r = l->n->left;
+ setlineno(l->n);
+ // Append any new body prologue to ninit.
+ // The next loop will insert ninit into nbody.
+ if(l->n->ninit != nil)
+ fatal("order select ninit");
if(r != nil) {
switch(r->op) {
case OSELRECV:
case OSELRECV2:
- orderexprinplace(&r->left, order);
- orderexprinplace(&r->ntest, order);
- orderexprtolist(&r->right->left, &l->n->ninit);
+ // case x = <-c
+ // case x, ok = <-c
+ // r->left is x, r->ntest is ok, r->right is ORECV, r->right->left is c.
+ // r->left == N means 'case <-c'.
+ // c is always evaluated; x and ok are only evaluated when assigned.
+ orderexpr(&r->right->left, order);
+
+ // Introduce temporary for receive and move actual copy into case body.
+ // avoids problems with target being addressed, as usual.
+ // NOTE: If we wanted to be clever, we could arrange for just one
+ // temporary per distinct type, sharing the temp among all receives
+ // with that temp. Similarly one ok bool could be shared among all
+ // the x,ok receives. Not worth doing until there's a clear need.
+ if(r->left != N && isblank(r->left))
+ r->left = N;
+ if(r->left != N) {
+ // use channel element type for temporary to avoid conversions,
+ // such as in case interfacevalue = <-intchan.
+ // the conversion happens in the OAS instead.
+ tmp1 = r->left;
+ r->left = ordertemp(r->right->left->type->type, order, haspointers(r->right->left->type->type));
+ tmp2 = nod(OAS, tmp1, r->left);
+ typecheck(&tmp2, Etop);
+ l->n->ninit = list(l->n->ninit, tmp2);
+ }
+ if(r->ntest != N && isblank(r->ntest))
+ r->ntest = N;
+ if(r->ntest != N) {
+ tmp1 = r->ntest;
+ r->ntest = ordertemp(tmp1->type, order, 0);
+ tmp2 = nod(OAS, tmp1, r->ntest);
+ typecheck(&tmp2, Etop);
+ l->n->ninit = list(l->n->ninit, tmp2);
+ }
+ orderblock(&l->n->ninit);
break;
+
case OSEND:
- orderexprtolist(&r->left, &l->n->ninit);
- orderexprtolist(&r->right, &l->n->ninit);
+ // case c <- x
+ // r->left is c, r->right is x, both are always evaluated.
+ orderexpr(&r->left, order);
+ if(!istemp(r->left))
+ r->left = ordercopyexpr(r->left, r->left->type, order, 0);
+ orderexpr(&r->right, order);
+ if(!istemp(r->right))
+ r->right = ordercopyexpr(r->right, r->right->type, order, 0);
break;
}
}
orderblock(&l->n->nbody);
}
+ // Now that we have accumulated all the temporaries, clean them.
+ // Also insert any ninit queued during the previous loop.
+ // (The temporary cleaning must follow that ninit work.)
+ for(l=n->list; l; l=l->next) {
+ cleantempnopop(t, order, &l->n->ninit);
+ l->n->nbody = concat(l->n->ninit, l->n->nbody);
+ l->n->ninit = nil;
+ }
order->out = list(order->out, n);
+ poptemp(t, order);
break;
case OSEND:
break;
case OSWITCH:
- // TODO(rsc): Clean temporaries.
+ // TODO(rsc): Clean temporaries more aggressively.
+ // Note that because walkswitch will rewrite some of the
+ // switch into a binary search, this is not as easy as it looks.
+ // (If we ran that code here we could invoke orderstmt on
+ // the if-else chain instead.)
+ // For now just clean all the temporaries at the end.
+ // In practice that's fine.
+ t = marktemp(order);
orderexpr(&n->ntest, order);
for(l=n->list; l; l=l->next) {
if(l->n->op != OXCASE)
orderblock(&l->n->nbody);
}
order->out = list(order->out, n);
+ cleantemp(t, order);
break;
}
orderexpr(Node **np, Order *order)
{
Node *n;
+ NodeList *mark, *l;
Type *t;
int lno;
t = typ(TARRAY);
t->bound = count(n->list);
t->type = types[TSTRING];
- n->left = ordertemp(t, order, 0);
+ n->alloc = ordertemp(t, order, 0);
}
break;
}
break;
+ case OCONVIFACE:
+ // concrete type (not interface) argument must be addressable
+ // temporary to pass to runtime.
+ orderexpr(&n->left, order);
+ if(!isinter(n->left->type))
+ orderaddrtemp(&n->left, order);
+ break;
+
case OANDAND:
case OOROR:
+ mark = marktemp(order);
orderexpr(&n->left, order);
+ // Clean temporaries from first branch at beginning of second.
+ // Leave them on the stack so that they can be killed in the outer
+ // context in case the short circuit is taken.
+ l = nil;
+ cleantempnopop(mark, order, &l);
+ n->right->ninit = concat(l, n->right->ninit);
orderexprinplace(&n->right, order);
break;
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);
- }
+ if(n->noescape && n->cvars != nil)
+ n->alloc = ordertemp(types[TUINT8], order, 0); // walk will fill in correct type
break;
-
+
+ case OARRAYLIT:
+ case OCALLPART:
+ orderexpr(&n->left, order);
+ orderexpr(&n->right, order);
+ orderexprlist(n->list, order);
+ orderexprlist(n->rlist, order);
+ if(n->noescape)
+ n->alloc = ordertemp(types[TUINT8], order, 0); // walk will fill in correct type
+ 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);
+ n->alloc = ordertemp(n->type, order, 0);
}
break;
// we only use a once, so no copy needed.
ha = a;
th = hiter(t);
- hit = n->left;
+ hit = n->alloc;
+ hit->type = th;
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
n->op = OSELRECV2;
n->left = n->list->n;
n->ntest = n->list->next->n;
+ n->list = nil;
n->right = n->rlist->n;
n->rlist = nil;
break;
walkselect(Node *sel)
{
int lno, i;
- Node *n, *r, *a, *tmp, *var, *cas, *dflt, *ch;
+ Node *n, *r, *a, *var, *cas, *dflt, *ch;
NodeList *l, *init;
if(sel->list == nil && sel->xoffset != 0)
// optimization: one-case select: single op.
// TODO(rsc): Reenable optimization once order.c can handle it.
// golang.org/issue/7672.
- if(0 && i == 1) {
+ if(i == 1) {
cas = sel->list->n;
setlineno(cas);
l = cas->ninit;
fatal("select %O", n->op);
case OSEND:
- ch = cheapexpr(n->left, &l);
- n->left = ch;
+ // ok already
+ ch = n->left;
break;
case OSELRECV:
- r = n->right;
- ch = cheapexpr(r->left, &l);
- r->left = ch;
-
+ ch = n->right->left;
+ Selrecv1:
if(n->left == N)
- n = r;
- else {
- n = nod(OAS, n->left, r);
- typecheck(&n, Etop);
- }
+ n = n->right;
+ else
+ n->op = OAS;
break;
case OSELRECV2:
- r = n->right;
- ch = cheapexpr(r->left, &l);
- r->left = ch;
-
- a = nod(OAS2, N, N);
- a->list = n->list;
- a->rlist = list1(n->right);
- n = a;
+ ch = n->right->left;
+ if(n->ntest == N)
+ goto Selrecv1;
+ if(n->left == N) {
+ typecheck(&nblank, Erv | Easgn);
+ n->left = nblank;
+ }
+ n->op = OAS2;
+ n->list = list(list1(n->left), n->ntest);
+ n->rlist = list1(n->right);
+ n->right = N;
+ n->left = N;
+ n->ntest = N;
+ n->typecheck = 0;
typecheck(&n, Etop);
break;
}
goto out;
}
- // introduce temporary variables for OSELRECV where needed.
+ // convert case value arguments to addresses.
// this rewrite is used by both the general code and the next optimization.
for(l=sel->list; l; l=l->next) {
cas = l->n;
if(n == N)
continue;
switch(n->op) {
+ case OSEND:
+ n->right = nod(OADDR, n->right, N);
+ typecheck(&n->right, Erv);
+ break;
case OSELRECV:
case OSELRECV2:
- ch = n->right->left;
-
- // If we can use the address of the target without
- // violating addressability or order of operations, do so.
- // Otherwise introduce a temporary.
- // Also introduce a temporary for := variables that escape,
- // so that we can delay the heap allocation until the case
- // is selected.
+ if(n->op == OSELRECV2 && n->ntest == N)
+ n->op = OSELRECV;
if(n->op == OSELRECV2) {
- if(n->ntest == N || isblank(n->ntest))
- n->ntest = nodnil();
- else if(n->ntest->op == ONAME &&
- (!n->colas || (n->ntest->class&PHEAP) == 0) &&
- convertop(types[TBOOL], n->ntest->type, nil) == OCONVNOP) {
- n->ntest = nod(OADDR, n->ntest, N);
- n->ntest->etype = 1; // pointer does not escape
- typecheck(&n->ntest, Erv);
- } else {
- tmp = temp(types[TBOOL]);
- a = nod(OADDR, tmp, N);
- a->etype = 1; // pointer does not escape
- typecheck(&a, Erv);
- r = nod(OAS, n->ntest, tmp);
- typecheck(&r, Etop);
- cas->nbody = concat(list1(r), cas->nbody);
- n->ntest = a;
- }
+ n->ntest = nod(OADDR, n->ntest, N);
+ typecheck(&n->ntest, Erv);
}
-
- if(n->left == N || isblank(n->left))
+ if(n->left == N)
n->left = nodnil();
- else if(n->left->op == ONAME &&
- (!n->colas || (n->left->class&PHEAP) == 0) &&
- convertop(ch->type->type, n->left->type, nil) == OCONVNOP) {
- if(n->colas && haspointers(ch->type->type)) {
- r = nod(OAS, n->left, N);
- typecheck(&r, Etop);
- sel->ninit = concat(sel->ninit, list1(r));
- }
+ else {
n->left = nod(OADDR, n->left, N);
- n->left->etype = 1; // pointer does not escape
typecheck(&n->left, Erv);
- if(!eqtype(ch->type->type, n->left->type->type)) {
- n->left = nod(OCONVNOP, n->left, N);
- n->left->type = ptrto(ch->type->type);
- n->left->typecheck = 1;
- }
- } else {
- tmp = temp(ch->type->type);
- a = nod(OADDR, tmp, N);
- if(haspointers(ch->type->type)) {
- // clear tmp for garbage collector, because the recv
- // must execute with tmp appearing to be live.
- r = nod(OAS, tmp, N);
- typecheck(&r, Etop);
- sel->ninit = concat(sel->ninit, list1(r));
- }
- a->etype = 1; // pointer does not escape
- typecheck(&a, Erv);
- r = nod(OAS, n->left, tmp);
- typecheck(&r, Etop);
- cas->nbody = concat(list1(r), cas->nbody);
- n->left = a;
- }
-
- cas->nbody = concat(n->ninit, cas->nbody);
- n->ninit = nil;
+ }
break;
}
}
fatal("select %O", n->op);
case OSEND:
- // if c != nil && selectnbsend(c, v) { body } else { default body }
- ch = cheapexpr(n->left, &r->ninit);
- a = n->right;
- a = assignconv(a, ch->type->type, "select chan send");
- walkexpr(&a, &r->ninit);
- if(islvalue(a)) {
- a = nod(OADDR, a, N);
- } else {
- var = temp(a->type);
- tmp = nod(OAS, var, a);
- typecheck(&tmp, Etop);
- r->ninit = list(r->ninit, tmp);
- a = nod(OADDR, var, N);
- }
+ // if selectnbsend(c, v) { body } else { default body }
+ ch = n->left;
r->ntest = mkcall1(chanfn("selectnbsend", 2, ch->type),
- types[TBOOL], &r->ninit, typename(ch->type), ch, a);
+ types[TBOOL], &r->ninit, typename(ch->type), ch, n->right);
break;
case OSELRECV:
// if c != nil && selectnbrecv(&v, c) { body } else { default body }
r = nod(OIF, N, N);
r->ninit = cas->ninit;
- ch = cheapexpr(n->right->left, &r->ninit);
+ ch = n->right->left;
r->ntest = mkcall1(chanfn("selectnbrecv", 2, ch->type),
types[TBOOL], &r->ninit, typename(ch->type), n->left, ch);
break;
// if c != nil && selectnbrecv2(&v, c) { body } else { default body }
r = nod(OIF, N, N);
r->ninit = cas->ninit;
- ch = cheapexpr(n->right->left, &r->ninit);
+ ch = n->right->left;
r->ntest = mkcall1(chanfn("selectnbrecv2", 2, ch->type),
types[TBOOL], &r->ninit, typename(ch->type), n->left, n->ntest, ch);
break;
case OSEND:
// selectsend(sel *byte, hchan *chan any, elem *any) (selected bool);
- n->left = localexpr(safeexpr(n->left, &r->ninit), n->left->type, &r->ninit);
- n->right = localexpr(n->right, n->left->type->type, &r->ninit);
- n->right = nod(OADDR, n->right, N);
- n->right->etype = 1; // pointer does not escape
- typecheck(&n->right, Erv);
- // cast to appropriate type if necessary.
- if(!eqtype(n->right->type->type, n->left->type->type) &&
- assignop(n->right->type->type, n->left->type->type, nil) == OCONVNOP) {
- n->right = nod(OCONVNOP, n->right, N);
- n->right->type = ptrto(n->left->type->type);
- n->right->typecheck = 1;
- }
r->ntest = mkcall1(chanfn("selectsend", 2, n->left->type), types[TBOOL],
&r->ninit, var, n->left, n->right);
break;
vauto = temp(ptrto(t));
// set auto to point at new temp or heap (3 assign)
- if(n->left != N) {
+ if(n->alloc != N) {
// temp allocated during order.c for dddarg
+ n->alloc->type = t;
if(vstat == N) {
- a = nod(OAS, n->left, N);
+ a = nod(OAS, n->alloc, N);
typecheck(&a, Etop);
*init = list(*init, a); // zero new temp
}
- a = nod(OADDR, n->left, N);
+ a = nod(OADDR, n->alloc, N);
} else if(n->esc == EscNone) {
a = temp(t);
if(vstat == N) {
Node*
localexpr(Node *n, Type *t, NodeList **init)
{
- if(n->op == ONAME && !n->addrtaken &&
+ if(n->op == ONAME && (!n->addrtaken || strncmp(n->sym->name, "autotmp_", 8) == 0) &&
(n->class == PAUTO || n->class == PPARAM || n->class == PPARAMOUT) &&
convertop(n->type, t, nil) == OCONVNOP)
return n;
ll = list(ll, n->left);
} else {
// regular types are passed by reference to avoid C vararg calls
- if(islvalue(n->left)) {
+ // orderexpr arranged for n->left to be a temporary for all
+ // the conversions it could see. comparison of an interface
+ // with a non-interface, especially in a switch on interface value
+ // with non-interface cases, is not visible to orderstmt, so we
+ // have to fall back on allocating a temp here.
+ if(islvalue(n->left))
ll = list(ll, nod(OADDR, n->left, N));
- } else {
- var = temp(n->left->type);
- n1 = nod(OAS, var, n->left);
- typecheck(&n1, Etop);
- *init = list(*init, n1);
- ll = list(ll, nod(OADDR, var, N));
- }
+ else
+ ll = list(ll, nod(OADDR, copyexpr(n->left, n->left->type, init), N));
}
argtype(fn, n->left->type);
argtype(fn, n->type);
} else {
n = nod(OCOMPLIT, N, typenod(tslice));
if(ddd != nil)
- n->left = ddd->left; // temporary to use
+ n->alloc = ddd->alloc; // temporary to use
n->list = lr0;
n->esc = esc;
typecheck(&n, Erv);
t->type = types[TSTRING];
t->bound = -1;
slice = nod(OCOMPLIT, N, typenod(t));
- slice->left = n->left;
+ slice->alloc = n->alloc;
slice->list = args;
slice->esc = EscNone;
args = list1(slice);
print(p) // ERROR "live at call to printpointer: autotmp_[0-9]+ autotmp_[0-9]+$"
}
}
+
+// conversion to interface should not leave temporary behind
+
+func f31(b1, b2, b3 bool) {
+ if b1 {
+ g31("a") // ERROR "live at call to convT2E: autotmp_[0-9]+$" "live at call to g31: autotmp_[0-9]+$"
+ }
+ if b2 {
+ h31("b") // ERROR "live at call to new: autotmp_[0-9]+$" "live at call to convT2E: autotmp_[0-9]+ autotmp_[0-9]+$" "live at call to h31: autotmp_[0-9]+$"
+ }
+ if b3 {
+ panic("asdf") // ERROR "live at call to convT2E: autotmp_[0-9]+$" "live at call to panic: autotmp_[0-9]+$"
+ }
+ print(b3)
+}
+
+func g31(interface{})
+func h31(...interface{})
+
+// non-escaping partial functions passed to function call should die on return
+
+type T32 int
+
+func (t *T32) Inc() { // ERROR "live at entry"
+ *t++
+}
+
+var t32 T32
+
+func f32(b bool) {
+ if b {
+ call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$"
+ }
+ call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$"
+ call32(t32.Inc) // ERROR "live at call to call32: autotmp_[0-9]+$"
+}
+
+//go:noescape
+func call32(func())
+
+// temporaries introduced during if conditions and && || expressions
+// should die once the condition has been acted upon.
+
+var m33 map[interface{}]int
+
+func f33() {
+ if m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
+ println()
+ return
+ } else {
+ println()
+ }
+ println()
+}
+
+func f34() {
+ if m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
+ println()
+ return
+ }
+ println()
+}
+
+func f35() {
+ if m33[nil] == 0 && m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
+ println()
+ return
+ }
+ println()
+}
+
+func f36() {
+ if m33[nil] == 0 || m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
+ println()
+ return
+ }
+ println()
+}
+
+func f37() {
+ if (m33[nil] == 0 || m33[nil] == 0) && m33[nil] == 0 { // ERROR "live at call to mapaccess1: autotmp_[0-9]+$"
+ println()
+ return
+ }
+ println()
+}
+
+// select temps should disappear in the case bodies
+
+var c38 chan string
+
+func fc38() chan string
+func fi38(int) *string
+func fb38() *bool
+
+func f38(b bool) {
+ // we don't care what temps are printed on the lines with output.
+ // we care that the println lines have no live variables
+ // and therefore no output.
+ if b {
+ select { // ERROR "live at call"
+ case <-fc38(): // ERROR "live at call"
+ println()
+ case fc38() <- *fi38(1): // ERROR "live at call"
+ println()
+ case *fi38(2) = <-fc38(): // ERROR "live at call"
+ println()
+ case *fi38(3), *fb38() = <-fc38(): // ERROR "live at call"
+ println()
+ }
+ println()
+ }
+ println()
+}