Eigenstate: myrddin-dev mailing list

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] 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,

---
 6/isel.c          |  2 +-
 mi/flatten.c      | 49 ++++++++++++++++++++++++++++++-------------------
 parse/dump.c      |  1 -
 parse/gram.y      | 15 ++++++---------
 parse/infer.c     | 35 ++++++++++++++++++++---------------
 parse/ops.def     |  2 +-
 parse/parse.h     |  7 ++++---
 parse/stab.c      |  4 ----
 test/pkgtrait.myr |  3 +--
 9 files changed, 63 insertions(+), 55 deletions(-)

diff --git a/6/isel.c b/6/isel.c
index d7ec0c03..ff8a6825 100644
--- a/6/isel.c
+++ b/6/isel.c
@@ -931,7 +931,7 @@ selexpr(Isel *s, Node *n)
 		/* 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:
diff --git a/mi/flatten.c b/mi/flatten.c
index 1b95d0f9..467c8285 100644
--- a/mi/flatten.c
+++ b/mi/flatten.c
@@ -114,10 +114,10 @@ islbl(Node *n)
 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 @@ traitfn(Srcloc loc, Trait *tr, char *fn, Type *ty)
 }
 
 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);
 	}
@@ -418,18 +415,23 @@ assign(Flattenctx *s, Node *lhs, Node *rhs)
 
 /* 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;
 		}
@@ -461,6 +463,12 @@ rval(Flattenctx *s, Node *n)
 	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;
@@ -655,7 +663,10 @@ flattenblk(Flattenctx *s, Node *n)
 		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;
 }
 
diff --git a/parse/dump.c b/parse/dump.c
index 847c0edc..94379846 100644
--- a/parse/dump.c
+++ b/parse/dump.c
@@ -186,7 +186,6 @@ outnode(Node *n, FILE *fd, int depth)
 		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);
diff --git a/parse/gram.y b/parse/gram.y
index 5c73295c..adc63c4d 100644
--- a/parse/gram.y
+++ b/parse/gram.y
@@ -147,7 +147,7 @@ static void setupinit(Node *n);
 %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 @@ pkgtydef: attrs tydef {
 	}
 	;
 
-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 @@ typedeclcore
 	: 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 @@ shiftexpr
 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 @@ params	: fnparam {
 	| /* 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); }
 	;
diff --git a/parse/infer.c b/parse/infer.c
index 548c1f2e..a627c007 100644
--- a/parse/infer.c
+++ b/parse/infer.c
@@ -261,7 +261,7 @@ adddispspecialization(Node *n, Stab *stab)
 	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 @@ tryconstrain(Type *base, Trait *tr, int update)
 				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 @@ inferexpr(Node **np, Type *ret, int *sawret)
 	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 @@ infernode(Node **np, Type *ret, int *sawret)
 		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)
@@ -2608,8 +2613,6 @@ typesub(Node *n, int noerr)
 		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:
@@ -2656,6 +2659,8 @@ typesub(Node *n, int noerr)
 			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)
@@ -2721,7 +2726,15 @@ specialize(void)
 	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;
@@ -2743,14 +2756,6 @@ specialize(void)
 			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");
 		}
diff --git a/parse/ops.def b/parse/ops.def
index dd9447ab..d81ace54 100644
--- 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(Ougt,	        1,	OTmisc, NULL)
 O(Ouge,	        1,	OTmisc, NULL)
 O(Oult,	        1,	OTmisc, NULL)
 O(Oule,	        1,	OTmisc, NULL)
-
diff --git a/parse/parse.h b/parse/parse.h
index 06b0b119..070434dc 100644
--- a/parse/parse.h
+++ b/parse/parse.h
@@ -108,10 +108,12 @@ struct Stab {
 	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 @@ struct Node {
 			char isnoret;
 			char isexportinit;
 			char isinit;
-			char isauto;
 		} decl;
 
 		struct {
diff --git a/parse/stab.c b/parse/stab.c
index 2d190d90..7e1b8c01 100644
--- a/parse/stab.c
+++ b/parse/stab.c
@@ -417,10 +417,6 @@ putdcl(Stab *st, Node *s)
 
 	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))
diff --git a/test/pkgtrait.myr b/test/pkgtrait.myr
index 40eb5944..38826342 100644
--- a/test/pkgtrait.myr
+++ b/test/pkgtrait.myr
@@ -8,7 +8,6 @@ impl disposable regex.regex# =
 ;;
 
 const main = {
-	var auto r : regex.regex#
-	r = std.try(regex.compile(".*"))
+	auto std.try(regex.compile(".*"))
 	std.exit(42)
 }
-- 
2.15.1


Follow-Ups:
Re: [PATCH] New auto operator."S. Gilles" <sgilles@xxxxxxxxxxxx>
Re: [PATCH] New auto operator.Ori Bernstein <ori@xxxxxxxxxxxxxx>
Re: [PATCH] New auto operator.Ori Bernstein <ori@xxxxxxxxxxxxxx>