commit d6819ca19bed2b349a3356efbaa5bac943ee020b
parent 1e82efdec742a39d80d179076236a677c32e7124
Author: Drew DeVault <sir@cmpwn.com>
Date: Thu, 2 Sep 2021 13:58:38 +0200
hare::unit: implement return expressions
This also:
- Implements them in cmd/hare
- Fixes termination semantics for compound
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
4 files changed, 77 insertions(+), 12 deletions(-)
diff --git a/cmd/harec/gen.ha b/cmd/harec/gen.ha
@@ -32,8 +32,9 @@ fn gen(store: *types::typestore, unit: *unit::unit) void = {
fn gen_func(ctx: *context, decl: *unit::decl) void = {
// TODO: const fndecl = &decl.decl as *unit::decl_func;
const fndecl = decl.decl as unit::decl_func;
- if (fndecl.body == null) {
- return; // Prototype
+ const body = match (fndecl.body) {
+ null => return, // Prototype
+ expr: *unit::expr => expr,
};
const fntype = fndecl.prototype.repr as types::func;
assert(fntype.flags == 0);
@@ -65,12 +66,13 @@ fn gen_func(ctx: *context, decl: *unit::decl) void = {
fmt::fprintln(ctx.out, mklabel(ctx, "body"))!;
- // TODO: We needn't write our own return if the user returned themselves
- const rval = gen_expr(ctx, fndecl.body: *unit::expr);
- if (has_rval) {
- emit(ctx, void, qinstr::RET, rval);
- } else {
- emit(ctx, void, qinstr::RET);
+ const rval = gen_expr(ctx, body);
+ if (!body.terminates) {
+ if (has_rval) {
+ emit(ctx, void, qinstr::RET, rval);
+ } else {
+ emit(ctx, void, qinstr::RET);
+ };
};
io::write(os::stdout, bufio::buffer(ctx.out))!;
@@ -81,6 +83,7 @@ fn gen_expr(ctx: *context, expr: *unit::expr) value = {
return match (expr.expr) {
unit::compound_expr => gen_expr_compound(ctx, expr),
unit::constant_expr => gen_expr_const(ctx, expr),
+ unit::return_expr => gen_expr_return(ctx, expr),
};
};
@@ -109,3 +112,14 @@ fn gen_expr_const(ctx: *context, expr: *unit::expr) value = {
_type = expr.result,
};
};
+
+fn gen_expr_return(ctx: *context, expr: *unit::expr) value = {
+ const rexpr = expr.expr as unit::return_expr;
+ match (rexpr) {
+ null => emit(ctx, void, qinstr::RET),
+ expr: *unit::expr => {
+ emit(ctx, void, qinstr::RET, gen_expr(ctx, expr));
+ },
+ };
+ return vvoid;
+};
diff --git a/hare/unit/context.ha b/hare/unit/context.ha
@@ -3,4 +3,5 @@ use hare::types;
type context = struct {
store: *types::typestore,
scope: *scope,
+ fntype: *types::func,
};
diff --git a/hare/unit/expr.ha b/hare/unit/expr.ha
@@ -8,7 +8,7 @@ export type expr = struct {
end: lex::location,
result: const *types::_type,
terminates: bool,
- expr: (compound_expr | constant_expr | void),
+ expr: (compound_expr | constant_expr | return_expr),
};
// A compound expression, i.e. { ... }
@@ -16,3 +16,6 @@ export type compound_expr = []*expr;
// The value of a constant expression.
export type constant_expr = ast::value; // TODO: composite types
+
+// A return expression, i.e. return <value>
+export type return_expr = nullable *expr;
diff --git a/hare/unit/process.ha b/hare/unit/process.ha
@@ -48,6 +48,7 @@ fn process_func(
assert(fntype.variadism == types::variadism::NONE); // TODO
assert(len(fntype.params) == 0); // TODO
+ ctx.fntype = &fntype;
const body: nullable *expr = match (afndecl.body) {
abody: ast::expr => process_expr(ctx, &abody)?,
void => null,
@@ -95,7 +96,7 @@ fn process_expr(
ast::size_expr => abort(), // TODO
ast::offset_expr => abort(), // TODO
ast::propagate_expr => abort(), // TODO
- ast::return_expr => abort(), // TODO
+ ast::return_expr => process_return(ctx, expr),
ast::slice_expr => abort(), // TODO
ast::switch_expr => abort(), // TODO
ast::unarithm_expr => abort(), // TODO
@@ -105,8 +106,9 @@ fn process_compound(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
const compound = aexpr.expr as ast::compound_expr;
const scope = scope_push(ctx, scope_class::COMPOUND);
- let exprs: compound_expr = [];
- for (let i = 0z; i < len(compound.exprs); i += 1) {
+ let exprs: compound_expr = alloc([], len(compound.exprs));
+ let i = 0z;
+ for (i < len(compound.exprs); i += 1) {
append(exprs, process_expr(ctx, compound.exprs[i])?);
};
@@ -116,6 +118,8 @@ fn process_compound(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
end = aexpr.end,
result = &types::builtin_void, // TODO: Pick result type
expr = exprs,
+ terminates = exprs[i - 1].terminates,
+ ...
});
};
@@ -128,6 +132,12 @@ fn process_compound(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
assert(expr.result.repr as types::builtin == types::builtin::VOID);
const compound = expr.expr as compound_expr;
assert(len(compound) == 3);
+
+ const aexpr = parse_expr("{ return; }");
+ defer ast::expr_free(aexpr);
+ const expr = process_compound(&ctx, aexpr)!;
+ assert(expr.terminates);
+
// TODO: test yields
};
@@ -216,3 +226,40 @@ fn process_constant(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
};
};
};
+
+fn process_return(ctx: *context, aexpr: *ast::expr) (*expr | error) = {
+ const ret = aexpr.expr as ast::return_expr;
+ const rval = match (ret) {
+ null => null,
+ aexpr: *ast::expr => process_expr(ctx, aexpr)?,
+ };
+ // TODO: assert(types::assignable(ctx.fntype.result, rval.type));
+ return alloc(expr {
+ start = aexpr.start,
+ end = aexpr.end,
+ terminates = true,
+ result = &types::builtin_void,
+ expr = rval: return_expr,
+ });
+};
+
+@test fn _return() void = {
+ const ctx = mktestctx();
+ defer freetestctx(&ctx);
+ const aexpr = parse_expr("return;");
+ defer ast::expr_free(aexpr);
+ const expr = process_return(&ctx, aexpr)!;
+ assert(expr.terminates);
+ assert(expr.result.repr as types::builtin == types::builtin::VOID);
+ const rval = expr.expr as return_expr;
+ assert(rval == null);
+
+ const aexpr = parse_expr("return 10;");
+ defer ast::expr_free(aexpr);
+ const expr = process_return(&ctx, aexpr)!;
+ assert(expr.terminates);
+ assert(expr.result.repr as types::builtin == types::builtin::VOID);
+ const rval = expr.expr as return_expr;
+ assert(rval != null);
+ assert((rval: *expr).expr is constant_expr);
+};