Skip to content

Commit dab58eb

Browse files
committed
assert: output more detailed messages
* output values of non-literal assert expression comparison operands * output values of non-literal static_assert operands
1 parent ae78fb1 commit dab58eb

File tree

6 files changed

+171
-20
lines changed

6 files changed

+171
-20
lines changed

analyser/module_analyser.c2

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import label_vector local;
2929
import name_vector local;
3030
import src_loc local;
3131
import scope;
32+
import string_buffer;
3233
import string_pool;
3334
import warning_flags;
3435
import struct_func_list as sf_list;
@@ -508,7 +509,24 @@ fn void Analyser.handleStaticAssert(void* arg, StaticAssert* d) {
508509
Value val2 = ctv_analyser.get_value(rhs);
509510

510511
if (!val1.is_equal(&val2)) {
511-
ma.errorRange(rhs.getStartLoc(), rhs.getRange(), "static_assert failed, expected %s, got %s", val1.str(), val2.str());
512+
char[256] tmp;
513+
string_buffer.Buf buf.init(tmp, elemsof(tmp), false, false, 0);
514+
lhs.printLiteral(&buf);
515+
buf.add(" == ");
516+
rhs.printLiteral(&buf);
517+
if (lhs.isLiteral()) {
518+
if (rhs.isLiteral()) {
519+
ma.errorRange(rhs.getStartLoc(), rhs.getRange(), "static_assert failed: %s", buf.data());
520+
} else {
521+
ma.errorRange(rhs.getStartLoc(), rhs.getRange(), "static_assert failed: %s, got %s", buf.data(), val2.str());
522+
}
523+
} else {
524+
if (rhs.isLiteral()) {
525+
ma.errorRange(lhs.getStartLoc(), lhs.getRange(), "static_assert failed: %s, got %s", buf.data(), val1.str());
526+
} else {
527+
ma.errorRange(lhs.getStartLoc(), lhs.getRange(), "static_assert failed: %s, got %s and %s", buf.data(), val1.str(), val2.str());
528+
}
529+
}
512530
}
513531
}
514532

ast/expr.c2

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,28 @@ public fn bool Expr.isStringLiteral(const Expr* e) {
197197
return e.getKind() == ExprKind.StringLiteral;
198198
}
199199

200+
public fn bool Expr.isLiteral(const Expr* e) {
201+
switch (e.getKind()) {
202+
case IntegerLiteral:
203+
case FloatLiteral:
204+
case BooleanLiteral:
205+
case CharLiteral:
206+
case StringLiteral:
207+
case Nil:
208+
return true;
209+
default:
210+
break;
211+
}
212+
return false;
213+
}
214+
215+
public fn bool Expr.isComparison(const Expr* e) {
216+
if (e.getKind() != BinaryOperator) return false;
217+
BinaryOperator* binop = (BinaryOperator*)e;
218+
BinaryOpcode opcode = binop.getOpcode();
219+
return opcode.isComparison();
220+
}
221+
200222
public fn bool Expr.isNil(const Expr* e) {
201223
return e.getKind() == ExprKind.Nil;
202224
}

generator/c/c_generator.c2

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,10 +1427,18 @@ const char[] C_defines =
14271427

14281428
const char[] C2_assert =
14291429
```c
1430+
#define va_list __builtin_va_list
1431+
#define va_start __builtin_va_start
1432+
#define va_end __builtin_va_end
14301433
int dprintf(int fd, const char *format, ...);
1434+
int vdprintf(int fd, const char *format, va_list args);
14311435
void abort(void);
1432-
static int c2_assert(const char* filename, int line, const char* funcname, const char* condstr) {
1433-
dprintf(2, "%s:%d: function %s: Assertion failed: %s\n", filename, line, funcname, condstr);
1436+
static int c2_assert(const char* filename, int line, const char* funcname, const char* fmt, ...) {
1437+
va_list args;
1438+
va_start(args, fmt);
1439+
dprintf(2, "%s:%d: function %s: assertion failed: ", filename, line, funcname);
1440+
vdprintf(2, fmt, args);
1441+
dprintf(2, "\n");
14341442
abort();
14351443
return 0;
14361444
}

generator/c/c_generator_stmt.c2

Lines changed: 96 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -238,23 +238,105 @@ fn void Generator.emitStmt(Generator* gen, Stmt* s, u32 indent, bool newline) {
238238
break;
239239
case Assert:
240240
if (!gen.enable_asserts) break;
241+
gen.emitAssertStmt(cast<AssertStmt*>(s), indent);
242+
break;
243+
}
244+
}
241245

242-
AssertStmt* a = cast<AssertStmt*>(s);
243-
source_mgr.Location loc = gen.sm.locate(s.getLoc());
244-
const char* funcname = gen.cur_function.asDecl().getFullName();
245-
246-
out.add1('(');
247-
Expr* inner = a.getInner();
248-
gen.emitExpr(out, inner);
249-
out.print(") || c2_assert(\"%s\", %d, \"%s\", \"", loc.filename, loc.line, funcname);
250-
// encode expression as a string
251-
string_buffer.Buf* str = string_buffer.create(128, false, 0);
252-
inner.printLiteral(str);
253-
out.encodeBytes(str.data(), str.size(), '"');
254-
str.free();
255-
out.add("\");\n");
246+
fn const char* get_type_format(Expr* e) {
247+
QualType qt = e.getType();
248+
QualType canon = qt.getCanonicalType();
249+
if (canon.isPointer() || canon.isFunction()) return "p";
250+
const Type* t = canon.getTypeOrNil();
251+
if (canon.isEnum()) {
252+
// output numeric value
253+
EnumType* et = (EnumType*)t;
254+
canon = et.getImplType();
255+
t = canon.getTypeOrNil();
256+
}
257+
const BuiltinType* bi = (BuiltinType*)t;
258+
switch (bi.getBaseKind()) {
259+
case Char:
260+
case Int8:
261+
case Int16:
262+
case Int32:
263+
return "d";
264+
case Int64:
265+
return "ld";
266+
case UInt8:
267+
case UInt16:
268+
case UInt32:
269+
return "u";
270+
case UInt64:
271+
return "lu";
272+
case Float32:
273+
case Float64:
274+
return "g";
275+
case Bool:
276+
return "d";
277+
case ISize: // not a base kind
278+
case USize: // not a base kind
279+
case Void:
256280
break;
257281
}
282+
return nil;
283+
}
284+
285+
// encode expression as a string
286+
fn void encode_expression(string_buffer.Buf* out, Expr* e) {
287+
char[128] tmp;
288+
string_buffer.Buf buf.init(tmp, elemsof(tmp), false, false, 0);
289+
e.printLiteral(&buf);
290+
const char* s = buf.data();
291+
for (const char* p = s;; p++) {
292+
if (!*p || *p == '%') {
293+
out.encodeBytes(s, (u32)(p - s), '"');
294+
if (!*p) break;
295+
if (*p == '%') out.add("%%");
296+
}
297+
}
298+
}
299+
300+
fn void Generator.emitAssertStmt(Generator* gen, AssertStmt* a, u32 indent) {
301+
string_buffer.Buf* out = gen.out;
302+
source_mgr.Location loc = gen.sm.locate(((Stmt*)a).getLoc());
303+
const char* funcname = gen.cur_function.asDecl().getFullName();
304+
Expr* inner = a.getInner();
305+
out.add1('(');
306+
gen.emitExpr(out, inner);
307+
out.print(") || c2_assert(\"%s\", %d, \"%s\", \"", loc.filename, loc.line, funcname);
308+
encode_expression(out, inner);
309+
if (inner.isComparison()) {
310+
BinaryOperator* b = (BinaryOperator*)inner;
311+
Expr* lhs = b.getLHS();
312+
Expr* rhs = b.getRHS();
313+
const char* fmt1 = lhs.isLiteral() ? nil : get_type_format(lhs);
314+
const char* fmt2 = rhs.isLiteral() ? nil : get_type_format(rhs);
315+
if (fmt1) {
316+
out.print(", ");
317+
encode_expression(out, lhs);
318+
out.print(": %%%s", fmt1);
319+
}
320+
if (fmt2) {
321+
out.print(", ");
322+
encode_expression(out, rhs);
323+
out.print(": %%%s", fmt2);
324+
}
325+
out.add1('"');
326+
if (fmt1) {
327+
out.add(", ");
328+
if (*fmt1 == 'p') out.add("(void*)");
329+
gen.emitExpr(out, lhs);
330+
}
331+
if (fmt2) {
332+
out.add(", ");
333+
if (*fmt2 == 'p') out.add("(void*)");
334+
gen.emitExpr(out, rhs);
335+
}
336+
} else {
337+
out.add1('"');
338+
}
339+
out.add(");\n");
258340
}
259341

260342
fn void emitAsmPart(string_buffer.Buf* out, bool multi_line, u32 indent) {

test/c_generator/stmts/assert.c2t

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,16 @@
66
module test;
77

88
i32 a = 10;
9+
i32 b = 20;
910
void* p = nil;
1011

1112
public fn i32 main() {
12-
assert(a);
1313
assert(p);
14+
assert(a);
15+
assert(a == 10);
16+
assert(a != 10);
17+
assert(a > 10);
18+
assert(a == b);
1419
return 0;
1520
}
1621

@@ -19,8 +24,12 @@ int32_t main(void);
1924

2025
int32_t main(void)
2126
{
22-
(test_a) || c2_assert("file1.c2", 7, "test.main", "a");
2327
(test_p) || c2_assert("file1.c2", 8, "test.main", "p");
28+
(test_a) || c2_assert("file1.c2", 9, "test.main", "a");
29+
(test_a == 10) || c2_assert("file1.c2", 10, "test.main", "a == 10, a: %d", test_a);
30+
(test_a != 10) || c2_assert("file1.c2", 11, "test.main", "a != 10, a: %d", test_a);
31+
(test_a > 10) || c2_assert("file1.c2", 12, "test.main", "a > 10, a: %d", test_a);
32+
(test_a == test_b) || c2_assert("file1.c2", 13, "test.main", "a == b, a: %d, b: %d", test_a, test_b);
2433
return 0;
2534
}
2635

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
// @warnings{no-unused}
22
module test;
33

4-
static_assert(3, 4); // @error{static_assert failed, expected 3, got 4}
4+
static_assert(3, 4); // @error{static_assert failed: 3 == 4}
5+
static_assert(0xff, 0b11111110); // @error{static_assert failed: 0xff == 0b11111110}
56

7+
type S struct {
8+
i32 x, y;
9+
}
10+
11+
type T struct {
12+
u32 x;
13+
}
14+
15+
static_assert(sizeof(S), 4); // @error{static_assert failed: sizeof(S) == 4, got 8}
16+
static_assert(4, sizeof(S)); // @error{static_assert failed: 4 == sizeof(S), got 8}
17+
static_assert(sizeof(T), sizeof(S)); // @error{static_assert failed: sizeof(T) == sizeof(S), got 4 and 8}

0 commit comments

Comments
 (0)