Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion analyser/module_analyser.c2
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,12 @@ public type Analyser struct @(opaque) {
// variables below differs per run
Module* mod;
bool usedPublic; // set to false during function body analysis or during non-public analysis
bool has_asserts;
bool check_only;

u32 c2_assert_idx;
u32 c2_assert_fail_idx;

// collect type-functions
u32 prefix_cache_name;
u32 prefix_cache_idx;
Expand Down Expand Up @@ -90,7 +94,7 @@ public fn Analyser* create(diagnostics.Diags* diags,
ast_builder.Builder* builder,
module_list.List* allmodules,
const warning_flags.Flags* warnings,
bool check_only)
bool has_asserts, bool check_only)
{
Analyser* ma = stdlib.calloc(1, sizeof(Analyser));
ma.diags = diags;
Expand All @@ -100,10 +104,31 @@ public fn Analyser* create(diagnostics.Diags* diags,
ma.builder = builder;
ma.allmodules = allmodules;
ma.warnings = warnings;
ma.has_asserts = has_asserts;
ma.check_only = check_only;
/* zero initialization is OK for ma.labels and ma.init_checkers */
ma.min_idx = astPool.addStr("min", true);
ma.max_idx = astPool.addStr("max", true);
ma.c2_assert_idx = astPool.addStr("c2_assert", true);
ma.c2_assert_fail_idx = astPool.addStr("c2_assert_fail", true);
return ma;
}

fn Analyser* Analyser.clone(Analyser* org) {
Analyser* ma = stdlib.calloc(1, sizeof(Analyser));
ma.diags = org.diags;
ma.checker.init(org.diags, org.builder);
ma.context = org.context;
ma.astPool = org.astPool;
ma.builder = org.builder;
ma.allmodules = org.allmodules;
ma.warnings = org.warnings;
ma.has_asserts = org.has_asserts;
ma.check_only = org.check_only;
ma.min_idx = org.min_idx;
ma.max_idx = org.max_idx;
ma.c2_assert_idx = org.c2_assert_idx;
ma.c2_assert_fail_idx = org.c2_assert_fail_idx;
return ma;
}

Expand Down
2 changes: 1 addition & 1 deletion analyser/module_analyser_call.c2
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ fn FunctionDecl* Analyser.instantiateTemplateFunction(Analyser* ma, CallExpr* ca

// Note: we need a separate scope for the body
Module* template_mod = fd.asDecl().getModule();
Analyser* analyser = create(ma.diags, ma.context, ma.astPool, ma.builder, ma.allmodules, ma.warnings, ma.check_only);
Analyser* analyser = ma.clone();
analyser.setMod(template_mod);
scope.Scope* tmpScope = scope.create(ma.allmodules,
ma.diags,
Expand Down
2 changes: 1 addition & 1 deletion analyser/module_analyser_function.c2
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ fn void Analyser.analyseFunction(Analyser* ma, FunctionDecl* fd) {

// Analyse a function body using a new analyser/scope
fn bool Analyser.analyseFunctionBody2(Analyser* ma, FunctionDecl* fd, Module* mod) {
Analyser* analyser = create(ma.diags, ma.context, ma.astPool, ma.builder, ma.allmodules, ma.warnings, false);
Analyser* analyser = ma.clone();
analyser.setMod(mod);
scope.Scope* tmpScope = scope.create(ma.allmodules,
ma.diags,
Expand Down
25 changes: 25 additions & 0 deletions analyser/module_analyser_stmt.c2
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ module module_analyser;
import ast local;
import label_vector local;
import scope;
import src_loc local;
import string_buffer;
import value_type local;

type Flow enum u8 {
Expand Down Expand Up @@ -436,6 +438,29 @@ fn void Analyser.analyseAssertStmt(Analyser* ma, Stmt* s) {

Expr* inner = a.getInner();
ma.checker.check(getBuiltinQT(Bool), qt, a.getInner2(), inner.getLoc());

if (ma.has_asserts) {
SrcLoc loc = s.getLoc();
// generate call to c2_assert.c2_assert_fail if asserts are enabled
// add c2_assert.fail func designator
Expr* func = ma.builder.actOnMemberExpr(nil, ma.c2_assert_idx, 0, loc, ma.c2_assert_fail_idx);
// encode expression as a string
char[128] tmp;
string_buffer.Buf buf.init(tmp, elemsof(tmp), true, false, 0);
inner.printLiteral(&buf);
u32 msg_len = buf.size();
u32 msg_idx = ma.astPool.add(buf.data(), msg_len, true);
buf.deinit();
SrcLoc eloc = inner.getStartLoc();
SrcLoc eloc_end = inner.getEndLoc();
Expr* arg = ma.builder.actOnStringLiteral(eloc, eloc_end - eloc, msg_idx, msg_len);

// create call expression `c2_assert.c2_assert_fail("expression")
Expr** callp = a.getCall2();
*callp = ma.builder.actOnCallExpr(loc, eloc_end + 1, func, &arg, 1);
qt = ma.analyseExpr(a.getCall2(), true, RHS);
if (qt.isInvalid()) return;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In which case will a.getCall() be nil?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the call expression is only constructed by the parser if asserts are enabled. The expression is always parsed and stored for the analyser.

}
}

fn void Analyser.analyseReturnStmt(Analyser* ma, Stmt* s) {
Expand Down
13 changes: 11 additions & 2 deletions ast/assert_stmt.c2
Original file line number Diff line number Diff line change
Expand Up @@ -22,32 +22,41 @@ 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)
Expr* inner,
Expr* call)
{
AssertStmt* s = c.alloc(sizeof(AssertStmt));
s.base.init(Assert, loc);
s.inner = inner;
s.call = call;
#if AstStatistics
Stats.addStmt(Assert, sizeof(AssertStmt));
#endif
return s;
}

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);
}

2 changes: 1 addition & 1 deletion ast/expr.c2
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ public fn SrcLoc Expr.getStartLoc(const Expr* e) {
return e.base.loc;
}

fn SrcLoc Expr.getEndLoc(const Expr* e) {
public fn SrcLoc Expr.getEndLoc(const Expr* e) {
switch (e.getKind()) {
case IntegerLiteral:
return ((IntegerLiteral*)e).getEndLoc();
Expand Down
2 changes: 1 addition & 1 deletion ast/utils.c2
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
2 changes: 1 addition & 1 deletion ast_utils/string_buffer.c2
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public fn Buf* create(u32 capacity, bool use_colors, u32 indent_step) {
return buf.init(nil, capacity, true, use_colors, indent_step);
}

fn void Buf.deinit(Buf* buf) {
public fn void Buf.deinit(Buf* buf) {
if (buf.own) free(buf.data_);
buf.data_ = nil;
}
Expand Down
4 changes: 2 additions & 2 deletions common/ast_builder.c2
Original file line number Diff line number Diff line change
Expand Up @@ -887,8 +887,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 = nil) {
return (Stmt*)AssertStmt.create(b.context, loc, inner, call);
}

public fn Stmt* Builder.actOnBreakStmt(Builder* b, SrcLoc loc) {
Expand Down
8 changes: 7 additions & 1 deletion compiler/compiler.c2
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ fn void Compiler.build(Compiler* c,
c.target.setNoBuild();
}

if (c.opts.check_only) {
c.target.disableAsserts();
}

diags.setWarningAsError(target.getWarnings().are_errors);
c.diags.clear();

Expand Down Expand Up @@ -286,7 +290,8 @@ fn void Compiler.build(Compiler* c,
c.astPool,
c.builder,
&c.kwinfo,
target.getFeatures());
c.target.getFeatures(),
c.target.hasAsserts());

ast.initialize(c.context, c.astPool, c.targetInfo.intWidth / 8, color.useColor());

Expand All @@ -296,6 +301,7 @@ fn void Compiler.build(Compiler* c,
c.builder,
&c.allmodules,
c.target.getWarnings(),
c.target.hasAsserts(),
c.opts.check_only);

if (opts.show_libs) {
Expand Down
2 changes: 2 additions & 0 deletions generator/ast_visitor.c2
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
19 changes: 2 additions & 17 deletions generator/c/c_generator.c2
Original file line number Diff line number Diff line change
Expand Up @@ -1327,7 +1327,7 @@ public fn void generate(string_pool.Pool* astPool,
}

// generate C2-internal and external lib stuff in external.h, the main component in build.c
gen.emit_external_header(enable_asserts, target);
gen.emit_external_header(target);

if (fast_build) {
gen.write(gen.cgen_dir, "_external.h", gen.out);
Expand Down Expand Up @@ -1463,17 +1463,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) {
Expand All @@ -1498,7 +1487,7 @@ const char[] C2_strswitch =
}
```;

fn void Generator.emit_external_header(Generator* gen, bool enable_asserts, const char* target) {
fn void Generator.emit_external_header(Generator* gen, const char* target) {
string_buffer.Buf* out = gen.out;

out.add(Include_guard1);
Expand Down Expand Up @@ -1540,10 +1529,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.
Expand Down
18 changes: 5 additions & 13 deletions generator/c/c_generator_stmt.c2
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -241,19 +240,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;
Expand Down
14 changes: 14 additions & 0 deletions libs/libc/c2_assert.c2i
Original file line number Diff line number Diff line change
@@ -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;
}
2 changes: 1 addition & 1 deletion libs/libc/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ modules:
- sys_utsname
- uio
- unistd

- c2_assert
7 changes: 6 additions & 1 deletion parser/c2_parser.c2
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,12 @@ public type Parser struct @(opaque) {
const keywords.Info* kwinfo;
bool is_interface;
bool is_generated;
bool has_asserts;
u32 va_list_idx;
u32 varargs_idx;
u32 stdarg_idx;
u32 main_idx;
u32 c2_assert_idx;

AttrRegistry attr_registry;

Expand All @@ -82,7 +84,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;
Expand All @@ -91,10 +94,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.main_idx = pool.addStr("main", true);
p.c2_assert_idx = p.pool.addStr("c2_assert", true);
p.attr_registry.init(pool);

// create a stack of stmt_lists to re-use (resize stack if needed)
Expand Down
1 change: 1 addition & 0 deletions parser/c2_parser_stmt.c2
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ fn Stmt* Parser.parseAssertStmt(Parser* p) {
Expr* inner = p.parseExpr();
p.expectAndConsume(RParen);
p.expectAndConsume(Semicolon);
if (p.has_asserts) p.addImplicitImport(p.c2_assert_idx, false);
return p.builder.actOnAssertStmt(loc, inner);
}

Expand Down
4 changes: 2 additions & 2 deletions test/c_generator/stmts/assert.c2t
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ int main(void);

int 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;
}

Loading