ref: 6fb0d750b296506f31cfd0871fdac82ee878834b
parent: 24a566ff1751b246c8b6efd8adea3447975b9efa
author: Quentin Carbonneaux <quentin@c9x.me>
date: Thu Jan 18 06:46:13 EST 2018
New auto operator. Summary: -------- During the Myrcon in September Ori suggested an 'auto' operator that would evaluate what it applies to, store the result in a temporary t, and call __dispose__(t) when the current block exits. This patch implements this idea under the form of a unary operator. This, for instance, allows to have: impl disposable regex# = __dispose__ = {r; regex.free(r)} ;; regex.exec(auto std.try(regex.compile("f..bar")), "foobar") Like before, it is guaranteed that __dispose__ is called in reverse order of auto appearance. Backward compatibility: ----------------------- Nope. Auto variables are now gone. This should not be a problem, simply rewrite: var auto x = foo() into: var x = auto foo() Implementation: --------------- It largely reuses the code I had written for 'auto' variables but needs a little finer grain tracking because we don't always want to call __dispose__ for *all* auto expression results when leaving a block (some might not be evaluated yet). For example: auto 1 if b -> void ;; auto 2 Only __dispose__(1) must be called when '-> void' is executed. If the block falls through, __dispose__(2) and __dispose__(1) will be called in sequence. TODO: ----- - Err when goto jumps in/out of a block that has auto expressions. - Support auto in patterns. match ... | `std.Some (auto x): ... is essentially rewritten to: match ... | `std.Some (auto x): auto x ... - Test edge cases (e.g., auto in loop condition) Actually, test. Cheers,
--- a/6/isel.c
+++ b/6/isel.c
@@ -939,7 +939,7 @@
/* These operators should never show up in the reduced trees,
* since they should have been replaced with more primitive
* expressions by now */
- case Obad: case Opreinc: case Opostinc: case Opredec:
+ case Obad: case Oauto: case Opreinc: case Opostinc: case Opredec:
case Opostdec: case Olor: case Oland: case Oaddeq:
case Osubeq: case Omuleq: case Odiveq: case Omodeq: case Oboreq:
case Obandeq: case Obxoreq: case Obsleq: case Obsreq: case Omemb:
--- a/mi/flatten.c
+++ b/mi/flatten.c
@@ -114,10 +114,10 @@
static Node *
temp(Flattenctx *flatten, Node *e)
{
- Node *t, *dcl;
+ Node *t;
assert(e->type == Nexpr);
- t = gentemp(e->loc, e->expr.type, &dcl);
+ t = gentemp(e->loc, e->expr.type, NULL);
return t;
}
@@ -225,23 +225,20 @@
}
static void
-dispose(Flattenctx *s, Stab *st)
+dispose(Flattenctx *s, Stab *st, size_t n)
{
- Node *d, *call, *func, *val;
+ Node *e, *call, *func;
Trait *tr;
Type *ty;
size_t i;
tr = traittab[Tcdisp];
- /* dispose in reverse order of declaration */
- for (i = st->nautodcl; i-- > 0;) {
- d = st->autodcl[i];
- ty = decltype(d);
- val = mkexpr(Zloc, Ovar, d->decl.name, NULL);
- val->expr.type = ty;
- val->expr.did = d->decl.did;
+ /* dispose in reverse order of appearance */
+ for (i = st->nautotmp; i-- > n;) {
+ e = st->autotmp[i];
+ ty = exprtype(e);
func = traitfn(Zloc, tr, "__dispose__", ty);
- call = mkexpr(Zloc, Ocall, func, val, NULL);
+ call = mkexpr(Zloc, Ocall, func, e, NULL);
call->expr.type = mktype(Zloc, Tyvoid);
flatten(s, call);
}
@@ -433,18 +430,23 @@
/* returns 1 when the exit jump needs to be emitted */
static int
-exitscope(Flattenctx *s, Stab *stop, Srcloc loc, int x)
+exitscope(Flattenctx *s, Stab *stop, Srcloc loc, Exit x)
{
+ Node *exit;
Stab *st;
for (st = s->curst;; st = st->super) {
- if (st->exit[x]) {
- jmp(s, st->exit[x]);
+ exit = st->exit[x];
+ if (st->ndisposed[x] < st->nautotmp) {
+ st->exit[x] = genlbl(loc);
+ flatten(s, st->exit[x]);
+ dispose(s, st, st->ndisposed[x]);
+ st->ndisposed[x] = st->nautotmp;
+ }
+ if (exit) {
+ jmp(s, exit);
return 0;
}
- st->exit[x] = genlbl(loc);
- flatten(s, st->exit[x]);
- dispose(s, st);
if ((!stop && st->isfunc) || st == stop) {
return 1;
}
@@ -476,6 +478,12 @@
r = NULL;
args = n->expr.args;
switch (exprop(n)) {
+ case Oauto:
+ r = rval(s, n->expr.args[0]);
+ t = temp(s, r);
+ r = asn(t, r);
+ lappend(&s->curst->autotmp, &s->curst->nautotmp, t);
+ break;
case Osize:
r = n; /* don't touch subexprs; they're a pseudo decl */
break;
@@ -666,7 +674,10 @@
flatten(s, n->block.stmts[i]);
}
assert(s->curst == n->block.scope);
- dispose(s, s->curst);
+ if (st->isfunc)
+ exitscope(s, NULL, Zloc, Xret);
+ else
+ dispose(s, s->curst, 0);
s->curst = st;
}
--- a/parse/dump.c
+++ b/parse/dump.c
@@ -186,7 +186,6 @@
findentf(fd, depth + 1, "isimport=%d\n", n->decl.isimport);
findentf(fd, depth + 1, "isnoret=%d\n", n->decl.isnoret);
findentf(fd, depth + 1, "isexportinit=%d\n", n->decl.isexportinit);
- findentf(fd, depth + 1, "isauto=%d\n", n->decl.isauto);
findentf(fd, depth, ")\n");
outsym(n, fd, depth + 1);
outnode(n->decl.init, fd, depth + 1);
--- a/parse/gram.y
+++ b/parse/gram.y
@@ -147,7 +147,7 @@
%type<node> littok literal lorexpr landexpr borexpr strlit bandexpr
%type<node> cmpexpr addexpr mulexpr shiftexpr prefixexpr
%type<node> postfixexpr funclit seqlit tuplit name block stmt label
-%type<node> use fnparam declbody declcore typedeclcore autodecl structent
+%type<node> use fnparam declbody declcore typedeclcore structent
%type<node> arrayelt structelt tuphead ifstmt forstmt whilestmt
%type<node> matchstmt elifs optexprln loopcond optexpr match
@@ -419,8 +419,8 @@
}
;
-declbody: autodecl Tasn expr {$$ = $1; $1->decl.init = $3;}
- | autodecl
+declbody: declcore Tasn expr {$$ = $1; $1->decl.init = $3;}
+ | declcore
;
declcore: name {$$ = mkdecl($1->loc, $1, mktyvar($1->loc));}
@@ -431,10 +431,6 @@
: name Tcolon type {$$ = mkdecl($1->loc, $1, $3);}
;
-autodecl: Tauto declcore {$$ = $2; $$->decl.isauto = 1;}
- | declcore
- ;
-
name : Tident {$$ = mkname($1->loc, $1->id);}
| Tident Tdot Tident {
$$ = mknsname($3->loc, $1->id, $3->id);
@@ -764,7 +760,8 @@
shiftop : Tbsl | Tbsr;
prefixexpr
- : Tinc prefixexpr {$$ = mkexpr($1->loc, Opreinc, $2, NULL);}
+ : Tauto prefixexpr {$$ = mkexpr($1->loc, Oauto, $2, NULL);}
+ | Tinc prefixexpr {$$ = mkexpr($1->loc, Opreinc, $2, NULL);}
| Tdec prefixexpr {$$ = mkexpr($1->loc, Opredec, $2, NULL);}
| Tband prefixexpr {$$ = mkexpr($1->loc, Oaddr, $2, NULL);}
| Tlnot prefixexpr {$$ = mkexpr($1->loc, Olnot, $2, NULL);}
@@ -923,7 +920,7 @@
| /* empty */ {$$.nl = NULL; $$.nn = 0;}
;
-fnparam : autodecl {$$ = $1;}
+fnparam : declcore {$$ = $1;}
| Tgap { $$ = mkpseudodecl($1->loc, mktyvar($1->loc)); }
| Tgap Tcolon type { $$ = mkpseudodecl($1->loc, $3); }
;
--- a/parse/infer.c
+++ b/parse/infer.c
@@ -260,7 +260,7 @@
Type *ty;
tr = traittab[Tcdisp];
- ty = decltype(n);
+ ty = exprtype(n);
assert(tr->nproto == 1);
if (hthas(tr->proto[0]->decl.impls, ty))
return;
@@ -880,7 +880,7 @@
if (update)
bsput(ty->trneed, tr->uid);
return 1;
- }
+ }
if (bshas(tm->traits, tr->uid))
return 1;
if (tm->name && ty->type == Tyname) {
@@ -1635,6 +1635,13 @@
infernode(&n->expr.idx, NULL, NULL);
n = checkns(n, np);
switch (exprop(n)) {
+ case Oauto: /* @a -> @a */
+ infersub(n, ret, sawret, &isconst);
+ t = type(args[0]);
+ constrain(n, t, traittab[Tcdisp]);
+ n->expr.isconst = isconst;
+ settype(n, t);
+ break;
/* all operands are same type */
case Oadd: /* @a + @a -> @a */
case Osub: /* @a - @a -> @a */
@@ -2076,8 +2083,6 @@
inferdecl(n);
if (hasparams(type(n)) && !ingeneric)
fatal(n, "generic type in non-generic near %s", ctxstr(n));
- if (n->decl.isauto)
- constrain(n, type(n), traittab[Tcdisp]);
popenv(n->decl.env);
indentdepth--;
if (n->decl.isgeneric)
@@ -2626,8 +2631,6 @@
if (streq(declname(n), "__init__"))
if (!initcompatible(tybase(decltype(n))))
fatal(n, "__init__ must be (->void), got %s", tystr(decltype(n)));
- if (n->decl.isauto)
- adddispspecialization(n, curstab());
popenv(n->decl.env);
break;
case Nblock:
@@ -2674,6 +2677,8 @@
settype(n->expr.args[0], exprtype(n));
settype(n->expr.args[0]->expr.args[0], exprtype(n));
}
+ if (exprop(n) == Oauto)
+ adddispspecialization(n, curstab());
for (i = 0; i < n->expr.nargs; i++)
typesub(n->expr.args[i], noerr);
if (!noerr)
@@ -2739,7 +2744,15 @@
for (i = 0; i < nspecializations; i++) {
pushstab(specializationscope[i]);
n = specializations[i];
- if (n->type == Nexpr) {
+ if (n->type == Nexpr && exprop(n) == Oauto) {
+ tr = traittab[Tcdisp];
+ assert(tr->nproto == 1);
+ ty = exprtype(n);
+ dt = mktyfunc(n->loc, NULL, 0, mktype(n->loc, Tyvoid));
+ lappend(&dt->sub, &dt->nsub, ty);
+ d = specializedcl(tr->proto[0], ty, dt, &name);
+ htput(tr->proto[0]->decl.impls, ty, d);
+ } else if (n->type == Nexpr && exprop(n) == Ovar) {
d = specializedcl(genericdecls[i], n->expr.param, n->expr.type, &name);
n->expr.args[0] = name;
n->expr.did = d->decl.did;
@@ -2761,14 +2774,6 @@
it = itertype(n->iterstmt.seq, mktype(n->loc, Tyvoid));
d = specializedcl(tr->proto[1], ty, it, &name);
htput(tr->proto[1]->decl.impls, ty, d);
- } else if (n->type == Ndecl && n->decl.isauto) {
- tr = traittab[Tcdisp];
- assert(tr->nproto == 1);
- ty = decltype(n);
- dt = mktyfunc(n->loc, NULL, 0, mktype(n->loc, Tyvoid));
- lappend(&dt->sub, &dt->nsub, ty);
- d = specializedcl(tr->proto[0], ty, dt, &name);
- htput(tr->proto[0]->decl.impls, ty, d);
} else {
die("unknown node for specialization\n");
}
--- a/parse/ops.def
+++ b/parse/ops.def
@@ -1,5 +1,6 @@
/* operator name, is it pure, pretty name */
O(Obad, 1, OTmisc, "BAD")
+O(Oauto, 1, OTpre, "auto")
O(Oadd, 1, OTbin, "+")
O(Osub, 1, OTbin, "-")
O(Omul, 1, OTbin, "*")
@@ -104,4 +105,3 @@
O(Ouge, 1, OTmisc, NULL)
O(Oult, 1, OTmisc, NULL)
O(Oule, 1, OTmisc, NULL)
-
--- a/parse/parse.h
+++ b/parse/parse.h
@@ -108,10 +108,12 @@
Htab *lbl; /* labels */
Htab *impl; /* trait implementations: really a set of implemented traits. */
- Node **autodcl; /* declarations in dcl marked 'auto' */
- size_t nautodcl;
+ /* See mi/flatten.c for the following. */
+ Node **autotmp; /* temporaries for 'auto' expressions */
+ size_t nautotmp;
Node *exit[Nexits];
+ size_t ndisposed[Nexits];
};
struct Tyenv {
@@ -331,7 +333,6 @@
char isnoret;
char isexportinit;
char isinit;
- char isauto;
} decl;
struct {
--- a/parse/stab.c
+++ b/parse/stab.c
@@ -417,10 +417,6 @@
st = findstab(st, s->decl.name);
old = htget(st->dcl, s->decl.name);
- if (s->decl.isauto) {
- assert(!old);
- lappend(&st->autodcl, &st->nautodcl, s);
- }
if (!old)
forcedcl(st, s);
else if (!mergedecl(old, s))
--- a/test/pkgtrait.myr
+++ b/test/pkgtrait.myr
@@ -8,7 +8,6 @@
;;
const main = {
- var auto r : regex.regex#
- r = std.try(regex.compile(".*"))
+ auto std.try(regex.compile(".*"))
std.exit(42)
}