~sircmpwn/harec

1f8d030130795ba8458c8f5234cbc153463d3b13 — Joe Finney a month ago 2623740
Allow abort/assert to accept non-constant strings.

This is useful for tests, allowing for statements like:
    assert(a == b, fmt::asprintf("expected {}, got {}, a, b));

The message for the above assertion (if a = 4, b = 5) will be:
    Abort: /path/to/file.ha:42:1: expected 4, got 5

This patch reorders the output for assert(false), without the
message string, to be consistent with assert(false, "foo").
Previously it was "Abort: Assertion failed: location", but now it
is "Abort: location: Assertion failed" to match "Abort: location:
foo".

This patch also changes the signature for rt::_abort, adding a
location parameter (matching abort_fixed).

Related to ~sircmpwn/hare#109.

Signed-off-by: Joe Finney <me@spxtr.net>
6 files changed, 29 insertions(+), 39 deletions(-)

M docs/runtime.txt
M include/expr.h
M rt/abort.ha
M src/check.c
M src/gen.c
M src/parse.c
M docs/runtime.txt => docs/runtime.txt +2 -2
@@ 1,9 1,9 @@
harec expects the runtime to provide some features under the "rt" namespace.

@noreturn @symbol("rt.abort") fn _abort(msg: str) void;
@noreturn @symbol("rt.abort") fn _abort(loc: str, msg: str) void;
	Print a diagnostic message and terminate the program.

@noreturn fn abort_fixed(reason: int) void;
@noreturn fn abort_fixed(loc: str, reason: int) void;
	Print a diagnostic message from a list of pre-determined abort reasons,
	and terminate the program. The list of reasons are:


M include/expr.h => include/expr.h +1 -1
@@ 347,7 347,7 @@ struct expression {
	const struct type *result;
	enum expr_type type;
	bool terminates;
	struct location loc; // For fixed aborts
	struct location loc; // For aborts
	union {
		struct expression_access access;
		struct expression_alloc alloc;

M rt/abort.ha => rt/abort.ha +5 -9
@@ 1,6 1,9 @@
export @noreturn @symbol("rt.abort") fn _abort(msg: str) void = {
export @noreturn @symbol("rt.abort") fn _abort(loc: str, msg: str) void = {
	const prefix = "Abort: ";
	const sep = ": ";
	write(2, constchar(prefix), len(prefix));
	write(2, constchar(loc), len(loc));
	write(2, constchar(sep), len(sep));
	write(2, constchar(msg), len(msg));
	write(2, constchar("\n"), 1);
	kill(getpid(), SIGABRT);


@@ 16,12 19,5 @@ const reasons: [_]str = [
];

export @noreturn fn abort_fixed(loc: str, i: int) void = {
	const prefix = "Abort: ";
	const sep = ": ";
	write(2, constchar(prefix), len(prefix));
	write(2, constchar(loc), len(loc));
	write(2, constchar(sep), len(sep));
	write(2, constchar(reasons[i]), len(reasons[i]));
	write(2, constchar("\n"), 1);
	kill(getpid(), SIGABRT);
	_abort(loc, reasons[i]);
};

M src/check.c => src/check.c +2 -24
@@ 601,33 601,11 @@ check_expr_assert(struct context *ctx,
				"Assertion message must be string");
			return;
		}

		assert(expr->assert.message->type == EXPR_CONSTANT);
		size_t n = snprintf(NULL, 0, "%s:%d:%d: ",
			sources[aexpr->loc.file],
			aexpr->loc.lineno, aexpr->loc.colno);
		size_t s_len = expr->assert.message->constant.string.len;
		char *s = xcalloc(1, n + s_len + 1);
		snprintf(s, n + 1, "%s:%d:%d: ", sources[aexpr->loc.file],
			aexpr->loc.lineno, aexpr->loc.colno);
		memcpy(s+n, expr->assert.message->constant.string.value, s_len);
		s[n + s_len] = '\0';

		expr->assert.message->constant.string.value = s;
		expr->assert.message->constant.string.len = n + s_len;
	} else {
		int n = snprintf(NULL, 0, "Assertion failed: %s:%d:%d",
			sources[aexpr->loc.file],
			aexpr->loc.lineno, aexpr->loc.colno);
		char *s = xcalloc(1, n + 1);
		snprintf(s, n, "Assertion failed: %s:%d:%d",
			sources[aexpr->loc.file],
			aexpr->loc.lineno, aexpr->loc.colno);

		expr->assert.message->type = EXPR_CONSTANT;
		expr->assert.message->result = &builtin_type_const_str;
		expr->assert.message->constant.string.value = s;
		expr->assert.message->constant.string.len = n - 1;
		expr->assert.message->constant.string.value = "Assertion failed";
		expr->assert.message->constant.string.len = 16;
	}

	if (expr->assert.is_static) {

M src/gen.c => src/gen.c +17 -1
@@ 808,7 808,23 @@ gen_expr_assert(struct gen_context *ctx, const struct expression *expr)
	}

	struct qbe_value qmsg = mkqval(ctx, &msg);
	pushi(ctx->current, NULL, Q_CALL, &rtfunc, &qmsg, NULL);

	int n = snprintf(NULL, 0, "%s:%d:%d",
			sources[expr->loc.file], expr->loc.lineno,
			expr->loc.colno);
	char *s = xcalloc(1, n + 1);
	snprintf(s, n, "%s:%d:%d",
			sources[expr->loc.file], expr->loc.lineno,
			expr->loc.colno);
	struct expression eloc = {0};
	eloc.type = EXPR_CONSTANT;
	eloc.result = &builtin_type_const_str;
	eloc.constant.string.value = s;
	eloc.constant.string.len = n - 1;
	struct gen_value loc_msg = gen_expr(ctx, &eloc);
	struct qbe_value loc_qmsg = mkqval(ctx, &loc_msg);

	pushi(ctx->current, NULL, Q_CALL, &rtfunc, &loc_qmsg, &qmsg, NULL);

	if (expr->assert.cond) {
		push(&ctx->current->body, &passedl);

M src/parse.c => src/parse.c +2 -2
@@ 1112,7 1112,7 @@ parse_assertion_expression(struct lexer *lexer, bool is_static)
		want(lexer, T_LPAREN, &tok);
		exp->assert.cond = parse_expression(lexer);
		if (lex(lexer, &tok) == T_COMMA) {
			exp->assert.message = parse_constant(lexer);
			exp->assert.message = parse_expression(lexer);
		} else {
			unlex(lexer, &tok);
		}


@@ 1122,7 1122,7 @@ parse_assertion_expression(struct lexer *lexer, bool is_static)
		want(lexer, T_LPAREN, &tok);
		if (lex(lexer, &tok) != T_RPAREN) {
			unlex(lexer, &tok);
			exp->assert.message = parse_constant(lexer);
			exp->assert.message = parse_expression(lexer);
			want(lexer, T_RPAREN, &tok);
		}
		break;