diff --git a/analyser/module_analyser_stmt.c2 b/analyser/module_analyser_stmt.c2 index be4b2f453..70f63b816 100644 --- a/analyser/module_analyser_stmt.c2 +++ b/analyser/module_analyser_stmt.c2 @@ -437,6 +437,11 @@ fn void Analyser.analyseAssertStmt(Analyser* ma, Stmt* s) { Expr* inner = a.getInner(); ma.checker.check(builtins[BuiltinKind.Bool], qt, a.getInner2(), inner.getLoc()); + + if (a.getCall()) { + qt = ma.analyseExpr(a.getCall2(), true, RHS); + if (qt.isInvalid()) return; + } } fn void Analyser.analyseReturnStmt(Analyser* ma, Stmt* s) { diff --git a/ast/assert_stmt.c2 b/ast/assert_stmt.c2 index 9125ab6b7..6f84bf6b7 100644 --- a/ast/assert_stmt.c2 +++ b/ast/assert_stmt.c2 @@ -22,15 +22,18 @@ import string_buffer; public type AssertStmt struct @(opaque) { Stmt base; Expr* inner; + Expr* call; } public fn AssertStmt* AssertStmt.create(ast_context.Context* c, - SrcLoc loc, - Expr* inner) + SrcLoc loc, + Expr* inner, + Expr* call) { AssertStmt* s = c.alloc(sizeof(AssertStmt)); s.base.init(StmtKind.Assert, loc); s.inner = inner; + s.call = call; #if AstStatistics Stats.addStmt(StmtKind.Assert, sizeof(AssertStmt)); #endif @@ -38,16 +41,22 @@ public fn AssertStmt* AssertStmt.create(ast_context.Context* c, } fn Stmt* AssertStmt.instantiate(AssertStmt* s, Instantiator* inst) { - return (Stmt*)AssertStmt.create(inst.c, s.base.loc, s.inner.instantiate(inst)); + return (Stmt*)AssertStmt.create(inst.c, s.base.loc, s.inner.instantiate(inst), + s.call ? s.call.instantiate(inst) : nil); } public fn Expr* AssertStmt.getInner(const AssertStmt* s) { return s.inner; } public fn Expr** AssertStmt.getInner2(AssertStmt* s) { return &s.inner; } +public fn Expr* AssertStmt.getCall(const AssertStmt* s) { return s.call; } + +public fn Expr** AssertStmt.getCall2(AssertStmt* s) { return &s.call; } + fn void AssertStmt.print(const AssertStmt* s, string_buffer.Buf* out, u32 indent) { s.base.printKind(out, indent); out.newline(); s.inner.print(out, indent + 1); + if (s.call) s.call.print(out, indent + 1); } diff --git a/ast/utils.c2 b/ast/utils.c2 index 3d8b45a08..91275b536 100644 --- a/ast/utils.c2 +++ b/ast/utils.c2 @@ -41,7 +41,7 @@ static_assert(80, sizeof(FunctionDecl)); static_assert(8, sizeof(Stmt)); static_assert(12, sizeof(GotoStmt)); static_assert(24, sizeof(LabelStmt)); -static_assert(16, sizeof(AssertStmt)); +static_assert(24, sizeof(AssertStmt)); static_assert(8, sizeof(DeclStmt)); static_assert(16, sizeof(SwitchStmt)); static_assert(24, sizeof(AsmStmt)); diff --git a/common/ast_builder.c2 b/common/ast_builder.c2 index be5d97bbb..c814b8a4a 100644 --- a/common/ast_builder.c2 +++ b/common/ast_builder.c2 @@ -883,8 +883,8 @@ public fn SwitchCase* Builder.actOnCase(Builder* b, conds, num_conds, stmts, num_stmts); } -public fn Stmt* Builder.actOnAssertStmt(Builder* b, SrcLoc loc, Expr* inner) { - return (Stmt*)AssertStmt.create(b.context, loc, inner); +public fn Stmt* Builder.actOnAssertStmt(Builder* b, SrcLoc loc, Expr* inner, Expr* call) { + return (Stmt*)AssertStmt.create(b.context, loc, inner, call); } public fn Stmt* Builder.actOnBreakStmt(Builder* b, SrcLoc loc) { diff --git a/compiler/compiler.c2 b/compiler/compiler.c2 index 773916220..e019795a7 100644 --- a/compiler/compiler.c2 +++ b/compiler/compiler.c2 @@ -270,7 +270,8 @@ fn void Compiler.build(Compiler* c, c.astPool, c.builder, &c.kwinfo, - target.getFeatures()); + target.getFeatures(), + c.target.hasAsserts()); ast.initialize(c.context, c.astPool, c.targetInfo.intWidth / 8, color.useColor()); diff --git a/generator/ast_visitor.c2 b/generator/ast_visitor.c2 index 3f20a181e..26ebc1deb 100644 --- a/generator/ast_visitor.c2 +++ b/generator/ast_visitor.c2 @@ -218,6 +218,8 @@ fn void Visitor.handleStmt(Visitor* v, Stmt* s) { case Assert: AssertStmt* a = (AssertStmt*)s; v.handleExpr(a.getInner()); + Expr* call = a.getCall(); + if (call) v.handleExpr(call); break; } } diff --git a/generator/c/c_generator.c2 b/generator/c/c_generator.c2 index 56d5fd869..0dd8005d0 100644 --- a/generator/c/c_generator.c2 +++ b/generator/c/c_generator.c2 @@ -1431,17 +1431,6 @@ const char[] C_defines = #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) ```; -const char[] C2_assert = - ```c - int dprintf(int fd, const char *format, ...); - void abort(void); - static int c2_assert(const char* filename, int line, const char* funcname, const char* condstr) { - dprintf(2, "%s:%d: function %s: Assertion failed: %s\n", filename, line, funcname, condstr); - abort(); - return 0; - } - ```; - const char[] C2_strswitch = ```c static int c2_strswitch(const char* s1, const char* s2) { @@ -1508,10 +1497,6 @@ fn void Generator.emit_external_header(Generator* gen, bool enable_asserts, cons #endif out.add("#define to_container(type, member, ptr) ((type*)((char*)(ptr) - offsetof(type, member)))\n\n"); - if (enable_asserts) { - out.add(C2_assert); - } - // for switch(str): // String format: strings are prefixed by a length byte and concatenated as a single character array. // This minimizes memory use, cache misses and avoids extra symbols. diff --git a/generator/c/c_generator_stmt.c2 b/generator/c/c_generator_stmt.c2 index f23f01fb7..ed541dba8 100644 --- a/generator/c/c_generator_stmt.c2 +++ b/generator/c/c_generator_stmt.c2 @@ -16,7 +16,6 @@ module c_generator; import ast local; -import source_mgr; import string_buffer; fn void Generator.emitVarDecl(Generator* gen, VarDecl* vd, string_buffer.Buf* out, bool emit_init, bool first) { @@ -240,19 +239,12 @@ fn void Generator.emitStmt(Generator* gen, Stmt* s, u32 indent, bool newline) { if (!gen.enable_asserts) out.print(";//assert"); AssertStmt* a = (AssertStmt*)s; out.add1('('); - Expr* inner = a.getInner(); - gen.emitExpr(out, inner); + gen.emitExpr(out, a.getInner()); out.add1(')'); - if (gen.enable_asserts) { - source_mgr.Location loc = gen.sm.locate(s.getLoc()); - const char* funcname = gen.cur_function.asDecl().getFullName(); - out.print(" || c2_assert(\"%s\", %d, \"%s\", \"", loc.filename, loc.line, funcname); - // encode expression as a string - string_buffer.Buf* str = string_buffer.create(128, false, 0); - inner.printLiteral(str); - out.encodeBytes(str.data(), str.size(), '"'); - str.free(); - out.add("\")"); + Expr* call = a.getCall(); + if (call) { + out.add(" || "); + gen.emitExpr(out, call); } out.add(";\n"); break; diff --git a/libs/libc/c2_assert.c2i b/libs/libc/c2_assert.c2i new file mode 100644 index 000000000..b98de7df4 --- /dev/null +++ b/libs/libc/c2_assert.c2i @@ -0,0 +1,14 @@ +module c2_assert; + +import stdio local; +import stdlib local; + +public fn i32 c2_assert_fail(const char* filename @(auto_file), + u32 line @(auto_line), + const char* funcname @(auto_func), + const char* condstr) +{ + dprintf(2, "%s:%d: function %s: Assertion failed: %s\n", filename, line, funcname, condstr); + abort(); + return 0; +} diff --git a/libs/libc/manifest.yaml b/libs/libc/manifest.yaml index 00369c6cd..3fe2adbd8 100644 --- a/libs/libc/manifest.yaml +++ b/libs/libc/manifest.yaml @@ -40,4 +40,4 @@ modules: - sys_utsname - uio - unistd - + - c2_assert diff --git a/parser/c2_parser.c2 b/parser/c2_parser.c2 index b173d3fe8..8d7ef939c 100644 --- a/parser/c2_parser.c2 +++ b/parser/c2_parser.c2 @@ -61,9 +61,12 @@ public type Parser struct @(opaque) { const string_list.List* features; const keywords.Info* kwinfo; bool is_interface; + bool has_asserts; u32 va_list_idx; u32 varargs_idx; u32 stdarg_idx; + u32 c2_assert_idx; + u32 c2_assert_fail_idx; stmt_list.List* stmt_lists; u32 stmt_list_count; @@ -77,7 +80,8 @@ public fn Parser* create(SourceMgr* sm, string_pool.Pool* pool, ast_builder.Builder* builder, const keywords.Info* kwinfo, - const string_list.List* features) + const string_list.List* features, + bool has_asserts) { Parser* p = calloc(1, sizeof(Parser)); p.sm = sm; @@ -86,9 +90,12 @@ public fn Parser* create(SourceMgr* sm, p.builder = builder; p.features = features; p.kwinfo = kwinfo; + p.has_asserts = has_asserts; p.va_list_idx = pool.addStr("va_list", true); p.varargs_idx = pool.addStr("varargs", true); p.stdarg_idx = pool.addStr("stdarg", true); + p.c2_assert_idx = p.pool.addStr("c2_assert", true); + p.c2_assert_fail_idx = p.pool.addStr("c2_assert_fail", true); // create a stack of stmt_lists to re-use (resize stack if needed) p.stmt_lists = malloc(NumStmtLists * sizeof(stmt_list.List)); diff --git a/parser/c2_parser_stmt.c2 b/parser/c2_parser_stmt.c2 index 5816d907a..5f9512836 100644 --- a/parser/c2_parser_stmt.c2 +++ b/parser/c2_parser_stmt.c2 @@ -21,6 +21,7 @@ import constants local; import token local; import src_loc local; import string_list; +import string_buffer; fn Stmt* Parser.parseStmt(Parser* p) { // TODO use Jump Table (combined one for multiple purposes?) @@ -387,10 +388,36 @@ fn Stmt* Parser.parseAssertStmt(Parser* p) { SrcLoc loc = p.tok.loc; p.consumeToken(); p.expectAndConsume(Kind.LParen); + SrcLoc eloc = p.tok.loc; Expr* inner = p.parseExpr(); + u32 elen = p.prev_loc - eloc; p.expectAndConsume(Kind.RParen); + u32 loc_end = p.prev_loc; p.expectAndConsume(Kind.Semicolon); - return p.builder.actOnAssertStmt(loc, inner); + + Expr* call = nil; + if (p.has_asserts) { + // add c2_assert.fail func designator + Ref[2] ref; + ref[0].loc = loc; + ref[0].name_idx = p.c2_assert_idx; + ref[1].loc = loc; + ref[1].name_idx = p.c2_assert_fail_idx; + p.addImplicitImport(p.c2_assert_idx, false); + Expr* func = p.builder.actOnMemberExpr(nil, ref, 2); + + // encode expression as a string + string_buffer.Buf* buf = p.tokenizer.buf; + buf.clear(); + inner.printLiteral(buf); + u32 msg_len = buf.size(); + u32 msg_idx = p.pool.add(buf.data(), msg_len, true); + Expr* arg = p.builder.actOnStringLiteral(eloc, elen, msg_idx, msg_len); + + // create call expression `assert.fail("expression") + call = p.builder.actOnCallExpr(loc, loc_end, func, &arg, 1); + } + return p.builder.actOnAssertStmt(loc, inner, call); } fn Stmt* Parser.parseBreakStmt(Parser* p) { diff --git a/test/c_generator/stmts/assert.c2t b/test/c_generator/stmts/assert.c2t index b5c284aef..f244bc536 100644 --- a/test/c_generator/stmts/assert.c2t +++ b/test/c_generator/stmts/assert.c2t @@ -19,8 +19,8 @@ int32_t main(void); int32_t main(void) { - (test_a) || c2_assert("file1.c2", 7, "test.main", "a"); - (test_p) || c2_assert("file1.c2", 8, "test.main", "p"); + (test_a) || c2_assert_fail("file1.c2", 7, "test.main", "a"); + (test_p) || c2_assert_fail("file1.c2", 8, "test.main", "p"); return 0; }