diff options
Diffstat (limited to 'bootstrap')
| -rw-r--r-- | bootstrap/all.h | 13 | ||||
| -rw-r--r-- | bootstrap/cgen.c | 40 | ||||
| -rw-r--r-- | bootstrap/dump.c | 4 | ||||
| -rw-r--r-- | bootstrap/parse.c | 33 |
4 files changed, 69 insertions, 21 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); |