aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlemon <lsof@mailbox.org>2022-08-20 06:31:06 +0200
committerlemon <lsof@mailbox.org>2022-08-20 06:31:06 +0200
commit19acdd751963e88b6e75693f947b1b1fd2912f11 (patch)
treee276d518a6eee672d3b2f32381fb41b544a9d6c7
parent58d8abf355c1543ec6afd99189848634f51f6273 (diff)
defer
-rw-r--r--bootstrap/all.h13
-rw-r--r--bootstrap/cgen.c40
-rw-r--r--bootstrap/dump.c4
-rw-r--r--bootstrap/parse.c33
-rw-r--r--examples/defer.cff16
-rw-r--r--misc/cff.vim2
6 files changed, 86 insertions, 22 deletions
diff --git a/bootstrap/all.h b/bootstrap/all.h
index 1bfe919..64ab9b1 100644
--- a/bootstrap/all.h
+++ b/bootstrap/all.h
@@ -38,6 +38,7 @@ struct span {
_(const) \
_(continue) \
_(def) \
+ _(defer) \
_(defmacro) \
_(do) \
_(else) \
@@ -125,6 +126,7 @@ struct parser {
struct env *primenv;
struct env *tlenv;
struct env *curenv;
+ struct blockstmt *curblock;
struct fn *curfn;
struct expan {
struct expan *prev;
@@ -343,6 +345,7 @@ enum exprtype {
struct blockstmt {
struct env env;
slice_t(struct stmt) stmts;
+ struct defer *defers;
};
struct expr {
@@ -475,7 +478,10 @@ struct stmt {
struct blockstmt *f;
bool byptr;
} euswitch;
- struct expr *retex;
+ struct {
+ struct expr *ex;
+ int deferage;
+ } ret;
struct {
int id;
} brkcon;
@@ -488,6 +494,11 @@ struct stmt {
} cswitch;
};
};
+struct defer {
+ struct defer *next;
+ int age;
+ struct expr ex;
+} ;
struct comfile {
slice_t(struct decl *) decls;
diff --git a/bootstrap/cgen.c b/bootstrap/cgen.c
index e047f08..94ae420 100644
--- a/bootstrap/cgen.c
+++ b/bootstrap/cgen.c
@@ -318,7 +318,7 @@ static void genfn(bool externp, const char *cname, struct fn *fn);
static void genstatic(bool externp, const char *cname, struct var *var);
static void
-genstmt(struct stmt *stmt) {
+genstmt(struct blockstmt *block, struct stmt *stmt) {
const char *p = fileid2path(stmt->span.fileid);
if (stmt->t != Sblock)
pri("#line %d %S\n", stmt->span.line, p, (u64)strlen(p));
@@ -367,7 +367,7 @@ genstmt(struct stmt *stmt) {
genblock(stmt->ifelse.t);
if (stmt->ifelse.f) {
pri("else\n");
- genstmt(stmt->ifelse.f);
+ genstmt(block, stmt->ifelse.f);
}
break;
case Swhile:
@@ -384,7 +384,7 @@ genstmt(struct stmt *stmt) {
case Sfor:
pri("{\n");
for (int i = 0; i < stmt->loop.ini.n; ++i)
- genstmt(&stmt->loop.ini.d[i]);
+ genstmt(block, &stmt->loop.ini.d[i]);
pri("for (; ");
pri("%e;", &stmt->loop.test);
if (stmt->loop.next)
@@ -446,10 +446,22 @@ genstmt(struct stmt *stmt) {
pri("}\n"); pri("}\n");
break;
case Sreturn:
- pri("return");
- if (stmt->retex)
- pri(" %e", stmt->retex);
- pri(";\n");
+ pri("do { ");
+ if (stmt->ret.ex) {
+ if (stmt->ret.ex->ty->t != TYvoid)
+ pri("__auto_type __ret = ");
+ pri("%e;", stmt->ret.ex);
+ }
+ for (struct defer* defer = block->defers; defer; defer = defer->next) {
+ if (defer->age < stmt->ret.deferage) {
+ const char *p = fileid2path(defer->ex.span.fileid);
+ pri("\n#line %d %S\n", defer->ex.span.line, p, (u64)strlen(p));
+ genexpr(&defer->ex);
+ pri(";\n");
+ }
+ }
+ pri("return%s;", (stmt->ret.ex && stmt->ret.ex->ty->t != TYvoid) ? " __ret" : "");
+ pri("} while (0);\n");
break;
case Sbreak:
pri("goto _brk%d;\n", stmt->brkcon.id);
@@ -475,7 +487,7 @@ static void
genblock(struct blockstmt block) {
pri("{\n");
for (int i = 0; i < block.stmts.n; ++i)
- genstmt(&block.stmts.d[i]);
+ genstmt(&block, &block.stmts.d[i]);
pri("}\n");
}
@@ -644,8 +656,8 @@ liftnested(struct stmt *stmt) {
liftnested(blocktostmt(*stmt->iswitch.f));
break;
case Sreturn:
- if (stmt->retex)
- liftnestedex(stmt->retex);
+ if (stmt->ret.ex)
+ liftnestedex(stmt->ret.ex);
break;
case Seuswitch:
liftnestedex(&stmt->euswitch.test);
@@ -687,7 +699,15 @@ genfn(bool externp, const char *cname, struct fn *fn) {
if (!fn->body) {
pri(";\n");
} else {
+ pri("{");
genblock(fn->body->block);
+ for (struct defer* defer = fn->body->block.defers; defer; defer = defer->next) {
+ const char *p = fileid2path(defer->ex.span.fileid);
+ pri("\n#line %d %S\n", defer->ex.span.line, p, (u64)strlen(p));
+ genexpr(&defer->ex);
+ pri(";\n");
+ }
+ pri("}");
}
}
diff --git a/bootstrap/dump.c b/bootstrap/dump.c
index 0cf5670..c6b20ff 100644
--- a/bootstrap/dump.c
+++ b/bootstrap/dump.c
@@ -386,8 +386,8 @@ dumpstmt(const struct stmt *stmt, int ws) {
epri("%w}\n", ws);
break;
case Sreturn:
- if (stmt->retex)
- epri("%wreturn %e;\n", ws, stmt->retex);
+ if (stmt->ret.ex)
+ epri("%wreturn %e;\n", ws, stmt->ret.ex);
else
epri("%wreturn;\n", ws);
break;
diff --git a/bootstrap/parse.c b/bootstrap/parse.c
index 31469df..4b56c2b 100644
--- a/bootstrap/parse.c
+++ b/bootstrap/parse.c
@@ -2443,6 +2443,7 @@ parsestmt(stmt_yielder_t yield, void *yarg, struct parser *P) {
struct stmt st = {0};
struct tok tok;
const char *label = NULL;
+ static int deferage;
if (lexmatch(P, &tok, '{')) {
st.span = tok.span;
@@ -2520,15 +2521,16 @@ parsestmt(stmt_yielder_t yield, void *yarg, struct parser *P) {
if (!P->curfn)
fatal(P, tok.span, "return disallowed here");
st.t = Sreturn;
+ st.ret.deferage = deferage;
st.span = tok.span;
if (!lexmatch(P, &tok, ';')) {
P->targty = P->curfn->retty;
- st.retex = exprdup(parseexpr(P));
+ st.ret.ex = exprdup(parseexpr(P));
lexexpect(P, ';');
- if (!typematchestarg(P->curfn->retty, st.retex->ty))
- fatal(P, st.retex->span,
+ if (!typematchestarg(P->curfn->retty, st.ret.ex->ty))
+ fatal(P, st.ret.ex->span,
"incompatible type in return statement (%t, expected %t)",
- st.retex->ty, P->curfn->retty);
+ st.ret.ex->ty, P->curfn->retty);
} else if (P->curfn->retty != ty_void) {
fatal(P, tok.span,
"return statement in non-void function must return a value");
@@ -2559,6 +2561,18 @@ parsestmt(stmt_yielder_t yield, void *yarg, struct parser *P) {
st.brkcon.id = decl->loopid;
}
lexexpect(P, ';');
+ } else if (lexmatch(P, &tok, TKkw_defer)) {
+ struct expr ex = parseexpr(P);
+ struct blockstmt *block = P->curblock;
+ struct defer *defer = malloc(sizeof *defer);
+ lexexpect(P, ';');
+ assert(block != NULL && "defer outside block");
+ defer->age = deferage++;
+ defer->ex = ex;
+ defer->next = block->defers;
+ block->defers = defer;
+ st.t = Sblock;
+ st.block = (struct blockstmt){0};
} else if (isdecltokt((tok = lexpeek(P)).t)) {
st.t = Sdecl;
st.span = tok.span;
@@ -2606,10 +2620,13 @@ parseblock0(struct parser *P) {
block.env.parent = P->curenv;
pushenv(P, &block.env);
- while ((tokt = lexpeek(P).t) != '}' && tokt != TKkw_case && tokt != ')') {
- if (lexmatch(P, NULL, ';'))
- continue;
- parsestmt(parseblockstyield, &stmts, P);
+ block.defers = P->curblock ? P->curblock->defers : NULL;
+ WITH_TMPCHANGE(struct blockstmt *, P->curblock, &block) {
+ while ((tokt = lexpeek(P).t) != '}' && tokt != TKkw_case && tokt != ')') {
+ if (lexmatch(P, NULL, ';'))
+ continue;
+ parsestmt(parseblockstyield, &stmts, P);
+ }
}
vec_slice_cpy(&block.stmts, &stmts);
diff --git a/examples/defer.cff b/examples/defer.cff
new file mode 100644
index 0000000..f91b8dc
--- /dev/null
+++ b/examples/defer.cff
@@ -0,0 +1,16 @@
+import "libc.hff";
+
+extern fn main() void {
+ let p = malloc(1);
+ defer free(p);
+ if p == #null {
+ if #f {
+ return;
+ }
+ return;
+ }
+ defer printf("bye\n");
+ if 0 > 0 {
+ return;
+ }
+}
diff --git a/misc/cff.vim b/misc/cff.vim
index 1c6ca50..0d64b5b 100644
--- a/misc/cff.vim
+++ b/misc/cff.vim
@@ -6,7 +6,7 @@ if exists("b:current_syntax")
endif
-syn keyword Statement return break continue case
+syn keyword Statement return break continue case defer
syn keyword Repeat for while do if else switch
syn keyword StorageClass typedef fn static def let extern const
syn keyword Include import