mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-05 18:24:06 +00:00
Runtime assert
This commit is contained in:
@@ -3,17 +3,16 @@
|
||||
#load "game.odin"
|
||||
|
||||
main :: proc() {
|
||||
|
||||
print_int(min(1, 2)); nl()
|
||||
print_int(max(1, 2)); nl()
|
||||
print_int(abs(-1337)); nl()
|
||||
|
||||
a, b, c := 1, 2, -1337
|
||||
|
||||
print_int(min(a, b)); nl()
|
||||
print_int(max(a, b)); nl()
|
||||
print_int(abs(c) as int); nl()
|
||||
|
||||
|
||||
nl()
|
||||
/*
|
||||
Vec3 :: type struct { x, y, z: f32 }
|
||||
|
||||
@@ -36,7 +36,7 @@ memory_copy :: proc(dst, src: rawptr, n: int) #inline {
|
||||
}
|
||||
|
||||
v128b :: type {4}u32
|
||||
static_assert(align_of(v128b) == 16)
|
||||
assert(align_of(v128b) == 16)
|
||||
|
||||
d, s: ^byte = dst, src
|
||||
|
||||
@@ -236,10 +236,6 @@ __string_eq :: proc(a, b: string) -> bool {
|
||||
return memory_compare(^a[0], ^b[0], len(a)) == 0
|
||||
}
|
||||
|
||||
__string_ne :: proc(a, b : string) -> bool #inline {
|
||||
return !__string_eq(a, b)
|
||||
}
|
||||
|
||||
__string_cmp :: proc(a, b : string) -> int {
|
||||
min_len := len(a)
|
||||
if len(b) < min_len {
|
||||
@@ -263,6 +259,7 @@ __string_cmp :: proc(a, b : string) -> int {
|
||||
return 0
|
||||
}
|
||||
|
||||
__string_ne :: proc(a, b : string) -> bool #inline { return !__string_eq(a, b) }
|
||||
__string_lt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) < 0 }
|
||||
__string_gt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > 0 }
|
||||
__string_le :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) <= 0 }
|
||||
@@ -291,9 +288,9 @@ Allocator :: type struct {
|
||||
|
||||
|
||||
Context :: type struct {
|
||||
thread_id: i32
|
||||
thread_id: int
|
||||
|
||||
user_index: i32
|
||||
user_index: int
|
||||
user_data: rawptr
|
||||
|
||||
allocator: Allocator
|
||||
@@ -350,11 +347,7 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment:
|
||||
return null
|
||||
}
|
||||
|
||||
if new_size < old_size {
|
||||
new_size = old_size
|
||||
}
|
||||
|
||||
if old_size == new_size {
|
||||
if new_size == old_size {
|
||||
return old_memory
|
||||
}
|
||||
|
||||
@@ -362,11 +355,8 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment:
|
||||
if new_memory == null {
|
||||
return null
|
||||
}
|
||||
min_size := old_size;
|
||||
if min_size > new_size {
|
||||
min_size = new_size;
|
||||
}
|
||||
memory_copy(new_memory, old_memory, min_size);
|
||||
|
||||
memory_copy(new_memory, old_memory, min(old_size, new_size));
|
||||
dealloc(old_memory)
|
||||
return new_memory
|
||||
}
|
||||
@@ -399,3 +389,10 @@ __default_allocator :: proc() -> Allocator {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
__assert :: proc(msg: string) {
|
||||
file_write(file_get_standard(File_Standard.ERROR), msg as []byte)
|
||||
debug_trap()
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ enum BuiltinProcId {
|
||||
BuiltinProc_align_of_val,
|
||||
BuiltinProc_offset_of,
|
||||
BuiltinProc_offset_of_val,
|
||||
BuiltinProc_static_assert,
|
||||
BuiltinProc_assert,
|
||||
|
||||
BuiltinProc_len,
|
||||
BuiltinProc_cap,
|
||||
@@ -170,7 +170,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
|
||||
{STR_LIT("align_of_val"), 1, false, Expr_Expr},
|
||||
{STR_LIT("offset_of"), 2, false, Expr_Expr},
|
||||
{STR_LIT("offset_of_val"), 1, false, Expr_Expr},
|
||||
{STR_LIT("static_assert"), 1, false, Expr_Stmt},
|
||||
{STR_LIT("assert"), 1, false, Expr_Stmt},
|
||||
|
||||
{STR_LIT("len"), 1, false, Expr_Expr},
|
||||
{STR_LIT("cap"), 1, false, Expr_Expr},
|
||||
|
||||
@@ -123,6 +123,8 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (type != NULL) {
|
||||
if (!check_is_assignable_to(c, operand, type, is_argument)) {
|
||||
gbString type_string = type_to_string(type);
|
||||
@@ -132,16 +134,23 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
|
||||
defer (gb_string_free(op_type_string));
|
||||
defer (gb_string_free(expr_str));
|
||||
|
||||
|
||||
// TODO(bill): is this a good enough error message?
|
||||
error(&c->error_collector, ast_node_token(operand->expr),
|
||||
"Cannot assign value `%s` of type `%s` to `%s` in %.*s",
|
||||
expr_str,
|
||||
op_type_string,
|
||||
type_string,
|
||||
LIT(context_name));
|
||||
|
||||
if (operand->mode == Addressing_Builtin) {
|
||||
// TODO(bill): is this a good enough error message?
|
||||
error(&c->error_collector, ast_node_token(operand->expr),
|
||||
"Cannot assign builtin procedure `%s` in %.*s",
|
||||
expr_str,
|
||||
LIT(context_name));
|
||||
} else {
|
||||
// TODO(bill): is this a good enough error message?
|
||||
error(&c->error_collector, ast_node_token(operand->expr),
|
||||
"Cannot assign value `%s` of type `%s` to `%s` in %.*s",
|
||||
expr_str,
|
||||
op_type_string,
|
||||
type_string,
|
||||
LIT(context_name));
|
||||
}
|
||||
operand->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1987,24 +1996,27 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
operand->type = t_int;
|
||||
} break;
|
||||
|
||||
case BuiltinProc_static_assert:
|
||||
// static_assert :: proc(cond: bool)
|
||||
case BuiltinProc_assert:
|
||||
// assert :: proc(cond: bool)
|
||||
|
||||
if (operand->mode != Addressing_Constant ||
|
||||
!is_type_boolean(operand->type)) {
|
||||
if (!is_type_boolean(operand->type)) {
|
||||
gbString str = expr_to_string(ce->arg_list);
|
||||
defer (gb_string_free(str));
|
||||
error(&c->error_collector, ast_node_token(call),
|
||||
"`%s` is not a constant boolean", str);
|
||||
"`%s` is not a boolean", str);
|
||||
return false;
|
||||
}
|
||||
if (!operand->value.value_bool) {
|
||||
if (operand->mode == Addressing_Constant &&
|
||||
!operand->value.value_bool) {
|
||||
gbString str = expr_to_string(ce->arg_list);
|
||||
defer (gb_string_free(str));
|
||||
error(&c->error_collector, ast_node_token(call),
|
||||
"Static assertion: `%s`", str);
|
||||
return true;
|
||||
}
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
operand->mode = Addressing_NoValue;
|
||||
}
|
||||
break;
|
||||
|
||||
// TODO(bill): Should these be procedures and are their names appropriate?
|
||||
|
||||
@@ -203,6 +203,21 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
|
||||
if (operand->mode == Addressing_Invalid ||
|
||||
operand->type == t_invalid ||
|
||||
e->type == t_invalid) {
|
||||
|
||||
if (operand->mode == Addressing_Builtin) {
|
||||
gbString expr_str = expr_to_string(operand->expr);
|
||||
defer (gb_string_free(expr_str));
|
||||
|
||||
// TODO(bill): is this a good enough error message?
|
||||
error(&c->error_collector, ast_node_token(operand->expr),
|
||||
"Cannot assign builtin procedure `%s` in %.*s",
|
||||
expr_str,
|
||||
LIT(context_name));
|
||||
|
||||
operand->mode = Addressing_Invalid;
|
||||
}
|
||||
|
||||
|
||||
if (e->type == NULL)
|
||||
e->type = t_invalid;
|
||||
return NULL;
|
||||
@@ -530,7 +545,7 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, Cyc
|
||||
|
||||
|
||||
|
||||
void check_var_decl(Checker *c, AstNode *node) {
|
||||
void check_var_decl_node(Checker *c, AstNode *node) {
|
||||
ast_node(vd, VarDecl, node);
|
||||
isize entity_count = vd->name_count;
|
||||
isize entity_index = 0;
|
||||
@@ -1150,7 +1165,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
if (vd->name_count > 1 && vd->type != NULL) {
|
||||
error(&c->error_collector, us->token, "`using` can only be applied to one variable of the same type");
|
||||
}
|
||||
check_var_decl(c, us->node);
|
||||
check_var_decl_node(c, us->node);
|
||||
|
||||
for (AstNode *item = vd->name_list; item != NULL; item = item->next) {
|
||||
ast_node(i, Ident, item);
|
||||
@@ -1191,7 +1206,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
|
||||
case_ast_node(vd, VarDecl, node);
|
||||
check_var_decl(c, node);
|
||||
check_var_decl_node(c, node);
|
||||
case_end;
|
||||
|
||||
case_ast_node(pd, ProcDecl, node);
|
||||
|
||||
@@ -761,6 +761,20 @@ ssaValue *ssa_add_param(ssaProcedure *proc, Entity *e) {
|
||||
}
|
||||
|
||||
|
||||
ssaValue *ssa_emit_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize arg_count) {
|
||||
Type *pt = get_base_type(ssa_type(value));
|
||||
GB_ASSERT(pt->kind == Type_Proc);
|
||||
Type *results = pt->Proc.results;
|
||||
return ssa_emit(p, ssa_make_instr_call(p, value, args, arg_count, results));
|
||||
}
|
||||
|
||||
ssaValue *ssa_emit_global_call(ssaProcedure *proc, char *name, ssaValue **args, isize arg_count) {
|
||||
ssaValue **found = map_get(&proc->module->members, hash_string(make_string(name)));
|
||||
GB_ASSERT_MSG(found != NULL, "%s", name);
|
||||
ssaValue *gp = *found;
|
||||
return ssa_emit_call(proc, gp, args, arg_count);
|
||||
}
|
||||
|
||||
|
||||
Type *ssa_type(ssaAddr lval) {
|
||||
if (lval.addr != NULL) {
|
||||
@@ -1558,8 +1572,6 @@ ssaValue *ssa_emit_down_cast(ssaProcedure *proc, ssaValue *value, Type *t) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv) {
|
||||
switch (expr->kind) {
|
||||
case_ast_node(bl, BasicLit, expr);
|
||||
@@ -1826,14 +1838,11 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
|
||||
i64 s = type_size_of(proc->module->sizes, allocator, type);
|
||||
i64 a = type_align_of(proc->module->sizes, allocator, type);
|
||||
// TODO(bill): Make procedure for: ssa_get_global_procedure()
|
||||
ssaValue *alloc_align_proc = *map_get(&proc->module->members, hash_string(make_string("alloc_align")));
|
||||
|
||||
ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2);
|
||||
args[0] = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(s));
|
||||
args[1] = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(a));
|
||||
|
||||
ssaValue *call = ssa_emit(proc, ssa_make_instr_call(proc, alloc_align_proc, args, 2, t_rawptr));
|
||||
ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2);
|
||||
ssaValue *v = ssa_emit_conv(proc, call, ptr_type);
|
||||
return v;
|
||||
} break;
|
||||
@@ -1848,7 +1857,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
|
||||
i64 s = type_size_of(proc->module->sizes, allocator, type);
|
||||
i64 a = type_align_of(proc->module->sizes, allocator, type);
|
||||
ssaValue *alloc_align_proc = *map_get(&proc->module->members, hash_string(make_string("alloc_align")));
|
||||
|
||||
ssaValue *elem_size = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(s));
|
||||
ssaValue *elem_align = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(a));
|
||||
@@ -1868,8 +1876,8 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
ssaValue **args = gb_alloc_array(allocator, ssaValue *, 2);
|
||||
args[0] = slice_size;
|
||||
args[1] = elem_align;
|
||||
ssaValue *call = ssa_emit_global_call(proc, "alloc_align", args, 2);
|
||||
|
||||
ssaValue *call = ssa_emit(proc, ssa_make_instr_call(proc, alloc_align_proc, args, 2, t_rawptr));
|
||||
ssaValue *ptr = ssa_emit_conv(proc, call, ptr_type, true);
|
||||
ssaValue *slice = ssa_add_local_generated(proc, slice_type);
|
||||
ssa_emit_store(proc, ssa_emit_struct_gep(proc, slice, v_zero32, ptr_type), ptr);
|
||||
@@ -1884,7 +1892,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
gbAllocator allocator = proc->module->allocator;
|
||||
|
||||
ssaValue *value = ssa_build_expr(proc, ce->arg_list);
|
||||
ssaValue *dealloc_proc = *map_get(&proc->module->members, hash_string(make_string("dealloc")));
|
||||
|
||||
if (is_type_slice(ssa_type(value))) {
|
||||
Type *etp = get_base_type(ssa_type(value));
|
||||
@@ -1894,8 +1901,50 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
|
||||
ssaValue **args = gb_alloc_array(allocator, ssaValue *, 1);
|
||||
args[0] = ssa_emit_conv(proc, value, t_rawptr, true);
|
||||
return ssa_emit_global_call(proc, "dealloc", args, 1);
|
||||
} break;
|
||||
|
||||
return ssa_emit(proc, ssa_make_instr_call(proc, dealloc_proc, args, 1, NULL));
|
||||
|
||||
case BuiltinProc_assert: {
|
||||
ssaValue *cond = ssa_build_expr(proc, ce->arg_list);
|
||||
GB_ASSERT(is_type_boolean(ssa_type(cond)));
|
||||
|
||||
Token eq = {Token_CmpEq};
|
||||
cond = ssa_emit_comp(proc, eq, cond, v_false);
|
||||
ssaBlock *err = ssa_add_block(proc, NULL, make_string("builtin.assert.err"));
|
||||
ssaBlock *done = ssa__make_block(proc, NULL, make_string("builtin.assert.done"));
|
||||
|
||||
ssa_emit_if(proc, cond, err, done);
|
||||
proc->curr_block = err;
|
||||
|
||||
Token token = ast_node_token(ce->arg_list);
|
||||
TokenPos pos = token.pos;
|
||||
gbString expr = expr_to_string(ce->arg_list);
|
||||
defer (gb_string_free(expr));
|
||||
|
||||
isize err_len = pos.file.len + 1 + 10 + 1 + 10 + 1;
|
||||
err_len += 20;
|
||||
err_len += gb_string_length(expr);
|
||||
err_len += 2;
|
||||
// HACK(bill): memory leaks
|
||||
u8 *err_str = gb_alloc_array(gb_heap_allocator(), u8, err_len);
|
||||
isize len = gb_snprintf(cast(char *)err_str, err_len,
|
||||
"%.*s(%td:%td) Runtime assertion: %s\n",
|
||||
LIT(pos.file), pos.line, pos.column, expr);
|
||||
|
||||
ssaValue *array = ssa_add_global_string_array(proc, make_exact_value_string(make_string(err_str, len-1)));
|
||||
ssaValue *elem = ssa_array_elem(proc, array);
|
||||
ssaValue *string = ssa_emit_load(proc, ssa_emit_string(proc, elem, ssa_array_len(proc, array)));
|
||||
|
||||
ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 1);
|
||||
args[0] = string;
|
||||
ssa_emit_global_call(proc, "__assert", args, 1);
|
||||
|
||||
ssa_emit_jump(proc, done);
|
||||
gb_array_append(proc->blocks, done);
|
||||
proc->curr_block = done;
|
||||
|
||||
return NULL;
|
||||
} break;
|
||||
|
||||
case BuiltinProc_len: {
|
||||
@@ -2132,8 +2181,7 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
||||
args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type, true);
|
||||
}
|
||||
|
||||
ssaValue *call = ssa_make_instr_call(proc, value, args, arg_count, type->results);
|
||||
return ssa_emit(proc, call);
|
||||
return ssa_emit_call(proc, value, args, arg_count);
|
||||
case_end;
|
||||
|
||||
case_ast_node(se, SliceExpr, expr);
|
||||
|
||||
Reference in New Issue
Block a user