shithub: mc

Download patch

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)
 }