Switchable array bounds checking

This commit is contained in:
Ginger Bill
2016-09-12 14:41:36 +01:00
parent 687e78d5dd
commit 9ff4a8b5ab
20 changed files with 243 additions and 1840 deletions
+2 -2
View File
@@ -52,8 +52,8 @@ pushd %build_dir%
cl %compiler_settings% "..\src\main.cpp" ^
/link %linker_settings% -OUT:%exe_name% ^
&& odin run ..\examples/demo.odin
rem odin run ..\examples/demo.odin
&& odin run ..\code/demo.odin
rem odin run ..\code/demo.odin
:do_not_compile_exe
+4
View File
@@ -0,0 +1,4 @@
#load "runtime.odin"
#load "win32.odin"
#load "file.odin"
#load "print.odin"
+9
View File
@@ -0,0 +1,9 @@
#load "basic.odin"
main :: proc() {
str := "Hellope"
println(str, true, 6.28)
println([4]int{1, 2, 3, 4})
}
+1 -1
View File
@@ -29,7 +29,7 @@ to_c_string :: proc(s: string) -> []u8 {
}
Window :: type struct {
Window :: struct {
width, height: int
wc: WNDCLASSEXA
dc: HDC
+30 -20
View File
@@ -15,7 +15,7 @@ print_string_to_buffer :: proc(buf: ^[]byte, s: string) {
slice := buf as ^Raw_Bytes
if slice.len < slice.cap {
n := min(slice.cap-slice.len, len(s))
offset := ((slice.data as int) + slice.len) as ^byte
offset := ptr_offset(slice.data, slice.len)
memory_copy(offset, ^s[0], n)
slice.len += n
}
@@ -199,7 +199,7 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any) {
print_string_to_buffer(buf, " = ")
v: any
v.type_info = f.type_info
v.data = ptr_offset(arg.data as ^u8, f.offset)
v.data = ptr_offset(arg.data as ^byte, f.offset)
print_any_to_buffer(buf, v)
}
print_string_to_buffer(buf, "}")
@@ -210,29 +210,29 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any) {
case Integer:
if info.signed {
u: uint = 0;
i: int = 0;
if arg.data != null {
match info.size {
case 1: u = (arg.data as ^u8)^ as uint
case 2: u = (arg.data as ^u16)^ as uint
case 4: u = (arg.data as ^u32)^ as uint
case 8: u = (arg.data as ^u64)^ as uint
case 16: u = (arg.data as ^u128)^ as uint
case 1: i = (arg.data as ^i8)^ as int
case 2: i = (arg.data as ^i16)^ as int
case 4: i = (arg.data as ^i32)^ as int
case 8: i = (arg.data as ^i64)^ as int
case 16: i = (arg.data as ^i128)^ as int
}
}
print_uint_to_buffer(buf, u)
print_int_to_buffer(buf, i)
} else {
v: int = 0;
i: uint = 0;
if arg.data != null {
match info.size {
case 1: v = (arg.data as ^i8)^ as int
case 2: v = (arg.data as ^i16)^ as int
case 4: v = (arg.data as ^i32)^ as int
case 8: v = (arg.data as ^i64)^ as int
case 16: v = (arg.data as ^i128)^ as int
case 1: i = (arg.data as ^u8)^ as uint
case 2: i = (arg.data as ^u16)^ as uint
case 4: i = (arg.data as ^u32)^ as uint
case 8: i = (arg.data as ^u64)^ as uint
case 16: i = (arg.data as ^u128)^ as uint
}
}
print_int_to_buffer(buf, v)
print_uint_to_buffer(buf, i)
}
case Float:
@@ -359,6 +359,19 @@ type_info_is_string :: proc(info: ^Type_Info) -> bool {
print_to_buffer :: proc(buf: ^[]byte, args: ..any) {
prev_string := false
for i := 0; i < len(args); i++ {
arg := args[i]
is_string := arg.data != null && type_info_is_string(arg.type_info)
if i > 0 && !is_string && !prev_string {
// Add space between two non-string arguments
print_space_to_buffer(buf)
}
print_any_to_buffer(buf, arg)
prev_string = is_string
}
}
println_to_buffer :: proc(buf: ^[]byte, args: ..any) {
for i := 0; i < len(args); i++ {
arg := args[i]
if i > 0 {
@@ -366,13 +379,10 @@ print_to_buffer :: proc(buf: ^[]byte, args: ..any) {
}
print_any_to_buffer(buf, arg)
}
}
println_to_buffer :: proc(buf: ^[]byte, args: ..any) {
print_to_buffer(buf, ..args)
print_nl_to_buffer(buf)
}
print :: proc(args: ..any) {
data: [4096]byte
buf := data[:0]
+8 -1
View File
@@ -1,4 +1,5 @@
#load "win32.odin"
#load "print.odin"
// IMPORTANT NOTE(bill): Do not change the order of any of this data
// The compiler relies upon this _exact_ order
@@ -9,7 +10,7 @@ Type_Info :: union {
offset: int // offsets are not used in tuples
}
Record :: struct #ordered {
fields: []Member // IMPORTANT: This will need to be allocated on the heap
fields: []Member
}
@@ -288,3 +289,9 @@ __assert :: proc(msg: string) {
file_write(file_get_standard(File_Standard.ERROR), msg as []byte)
__debug_trap()
}
__abc_error :: proc(file: string, line, column: int, index, len: int) {
print(file, "(", line, ":", line, ") Index out of bounds: index: ", index, ", len: ", len, "\n")
__debug_trap()
}
-18
View File
@@ -1,18 +0,0 @@
#load "basic.odin"
main :: proc() {
Vector4 :: struct {
x: i8
y: i32
z: i16
w: i16
}
v := Vector4{1, 4, 9, 16}
t := type_info(v)
println(123, "Hello", true, 6.28)
println([4]int{1, 2, 3, 4})
println(v)
}
-1711
View File
File diff suppressed because it is too large Load Diff
-30
View File
@@ -1,30 +0,0 @@
type Bitmap: struct {
width, height: i32,
comp: i32,
data: []u8,
}
make_bitmap :: proc(filename: string) -> Bitmap {
stbi_load :: proc(filename: ^u8, x, y, comp: ^i32, req_comp: i32) -> ^u8 #foreign
c_buf: [1024]u8
bytes := filename as []byte
str_len := copy(c_buf[:], bytes)
b: Bitmap
pixels := stbi_load(^c_buf[0], ^b.width, ^b.height, ^b.comp, 4)
len := (b.width*b.height*b.comp) as int
b.data = slice_ptr(pixels, len)
return b
}
destroy_bitmap :: proc(b: ^Bitmap) {
stbi_image_free :: proc(retval_from_stbi_load: rawptr) #foreign
stbi_image_free(^b.data[0])
b.data = b.data[:0]
b.width = 0
b.height = 0
b.comp = 0
}
+10 -11
View File
@@ -1926,19 +1926,18 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *valu
}
if (operand.mode == Addressing_Constant) {
i64 i = exact_value_to_integer(operand.value).value_integer;
if (i < 0) {
gbString expr_str = expr_to_string(operand.expr);
error(&c->error_collector, ast_node_token(operand.expr),
"Index `%s` cannot be a negative value", expr_str);
gb_string_free(expr_str);
if (value) *value = 0;
return false;
}
if (max_count >= 0) { // NOTE(bill): Do array bound checking
i64 i = exact_value_to_integer(operand.value).value_integer;
if (i < 0) {
gbString expr_str = expr_to_string(operand.expr);
error(&c->error_collector, ast_node_token(operand.expr),
"Index `%s` cannot be a negative value", expr_str);
gb_string_free(expr_str);
if (value) *value = 0;
return false;
}
if (value) *value = i;
if (i >= max_count) {
gbString expr_str = expr_to_string(operand.expr);
error(&c->error_collector, ast_node_token(operand.expr),
-1
View File
@@ -452,7 +452,6 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d, b32 check_body_later) {
b32 is_foreign = (pd->tags & ProcTag_foreign) != 0;
b32 is_inline = (pd->tags & ProcTag_inline) != 0;
b32 is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
b32 is_pure = (pd->tags & ProcTag_pure) != 0;
+2 -2
View File
@@ -23,7 +23,7 @@ b32 ssa_gen_init(ssaGen *s, Checker *c) {
return false;
}
ssa_module_init(&s->module, c);
ssa_init_module(&s->module, c);
// TODO(bill): generate appropriate output name
isize pos = string_extension_position(c->parser->init_fullpath);
@@ -35,7 +35,7 @@ b32 ssa_gen_init(ssaGen *s, Checker *c) {
}
void ssa_gen_destroy(ssaGen *s) {
ssa_module_destroy(&s->module);
ssa_destroy_module(&s->module);
gb_file_close(&s->output_file);
}
+132 -31
View File
@@ -10,6 +10,8 @@ struct ssaModule {
gbArena arena;
gbAllocator allocator;
u32 stmt_state_flags;
String layout;
Map<ssaValue *> values; // Key: Entity *
@@ -298,7 +300,11 @@ ssaAddr ssa_make_addr_vector(ssaValue *addr, ssaValue *index, AstNode *expr) {
ssaValue *ssa_make_value_global(gbAllocator a, Entity *e, ssaValue *value);
void ssa_module_init(ssaModule *m, Checker *c) {
void ssa_module_add_value(ssaModule *m, Entity *e, ssaValue *v) {
map_set(&m->values, hash_pointer(e), v);
}
void ssa_init_module(ssaModule *m, Checker *c) {
// TODO(bill): Determine a decent size for the arena
isize token_count = c->parser->total_token_count;
isize arena_size = 4 * token_count * gb_size_of(ssaValue);
@@ -310,6 +316,10 @@ void ssa_module_init(ssaModule *m, Checker *c) {
map_init(&m->values, gb_heap_allocator());
map_init(&m->members, gb_heap_allocator());
// Default states
m->stmt_state_flags = 0;
m->stmt_state_flags |= StmtStateFlag_abc;
{
// Add type info data
{
@@ -322,7 +332,7 @@ void ssa_module_init(ssaModule *m, Checker *c) {
Entity *e = make_entity_variable(m->allocator, NULL, token, make_type_array(m->allocator, t_type_info, count));
ssaValue *g = ssa_make_value_global(m->allocator, e, NULL);
g->Global.is_private = true;
map_set(&m->values, hash_pointer(e), g);
ssa_module_add_value(m, e, g);
map_set(&m->members, hash_string(name), g);
}
@@ -356,22 +366,18 @@ void ssa_module_init(ssaModule *m, Checker *c) {
Entity *e = make_entity_variable(m->allocator, NULL, token,
make_type_array(m->allocator, t_type_info_member, count));
ssaValue *g = ssa_make_value_global(m->allocator, e, NULL);
map_set(&m->values, hash_pointer(e), g);
ssa_module_add_value(m, e, g);
map_set(&m->members, hash_string(name), g);
}
}
}
void ssa_module_destroy(ssaModule *m) {
void ssa_destroy_module(ssaModule *m) {
map_destroy(&m->values);
map_destroy(&m->members);
gb_arena_free(&m->arena);
}
void ssa_module_add_value(ssaModule *m, Entity *e, ssaValue *v) {
map_set(&m->values, hash_pointer(e), v);
}
Type *ssa_type(ssaValue *value);
Type *ssa_type(ssaInstr *instr) {
@@ -1342,7 +1348,7 @@ ssaValue *ssa_add_global_string_array(ssaModule *m, ExactValue value) {
g->Global.is_private = true;
// g->Global.is_constant = true;
map_set(&m->values, hash_pointer(entity), g);
ssa_module_add_value(m, entity, g);
map_set(&m->members, hash_string(name), g);
return g;
@@ -1362,6 +1368,16 @@ ssaValue *ssa_emit_string(ssaProcedure *proc, ssaValue *elem, ssaValue *len) {
return ssa_emit_load(proc, str);
}
ssaValue *ssa_emit_global_string(ssaProcedure *proc, ExactValue value) {
GB_ASSERT(value.kind == ExactValue_String);
ssaValue *global_array = ssa_add_global_string_array(proc->module, value);
ssaValue *elem = ssa_array_elem(proc, global_array);
ssaValue *len = ssa_array_len(proc, ssa_emit_load(proc, global_array));
return ssa_emit_string(proc, elem, len);
}
String lookup_polymorphic_field(CheckerInfo *info, Type *dst, Type *src) {
Type *prev_src = src;
// Type *prev_dst = dst;
@@ -1700,6 +1716,49 @@ ssaValue *ssa_emit_logical_binary_expr(ssaProcedure *proc, AstNode *expr) {
}
void ssa_array_bounds_check(ssaProcedure *proc, Token token, ssaValue *index, ssaValue *len) {
if ((proc->module->stmt_state_flags & StmtStateFlag_no_abc) != 0) {
return;
}
index = ssa_emit_conv(proc, index, t_int);
len = ssa_emit_conv(proc, len, t_int);
ssa_emit_comment(proc, make_string("ArrayBoundsCheck"));
Token lt = {Token_Lt};
Token ge = {Token_GtEq};
Token cmp_or = {Token_Or}; // NOTE(bill): Just `or` it as both sides have been evaluated anyway
ssaValue *lower = ssa_emit_comp(proc, lt, index, v_zero);
ssaValue *upper = ssa_emit_comp(proc, ge, index, len);
ssaValue *cond = ssa_emit_arith(proc, cmp_or, lower, upper, t_bool);
ssaBlock *then = ssa_add_block(proc, NULL, make_string("abc.then"));
ssaBlock *done = ssa__make_block(proc, NULL, make_string("abc.done")); // NOTE(bill): Append later
ssa_emit_if(proc, cond, then, done);
proc->curr_block = then;
gbAllocator a = proc->module->allocator;
ssaValue *file = ssa_emit_global_string(proc, make_exact_value_string(token.pos.file));
ssaValue *line = ssa_make_value_constant(a, t_int, make_exact_value_integer(token.pos.line));
ssaValue *column = ssa_make_value_constant(a, t_int, make_exact_value_integer(token.pos.column));
ssaValue **args = gb_alloc_array(a, ssaValue *, 5);
args[0] = file;
args[1] = line;
args[2] = column;
args[3] = index;
args[4] = len;
ssa_emit_global_call(proc, "__abc_error", args, 5);
ssa_emit_jump(proc, done);
gb_array_append(proc->blocks, done);
proc->curr_block = done;
}
ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue *tv) {
switch (expr->kind) {
@@ -2419,14 +2478,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
}
ssaValue *ssa_emit_global_string(ssaProcedure *proc, ExactValue value) {
GB_ASSERT(value.kind == ExactValue_String);
ssaValue *global_array = ssa_add_global_string_array(proc->module, value);
ssaValue *elem = ssa_array_elem(proc, global_array);
ssaValue *len = ssa_array_len(proc, ssa_emit_load(proc, global_array));
return ssa_emit_string(proc, elem, len);
}
ssaValue *ssa_build_expr(ssaProcedure *proc, AstNode *expr) {
expr = unparen_expr(expr);
@@ -2562,47 +2613,61 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
case_ast_node(ie, IndexExpr, expr);
ssa_emit_comment(proc, make_string("IndexExpr"));
ssaValue *v = NULL;
Type *t = get_base_type(type_of_expr(proc->module->info, ie->expr));
ssaValue *elem = NULL;
gbAllocator a = proc->module->allocator;
switch (t->kind) {
case Type_Vector: {
// HACK(bill): Fix how lvalues for vectors work
ssaValue *vector = ssa_build_addr(proc, ie->expr).addr;
ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
ssaValue *len = ssa_make_value_constant(a, t_int, make_exact_value_integer(t->Vector.count));
ssa_array_bounds_check(proc, ast_node_token(expr), index, len);
return ssa_make_addr_vector(vector, index, expr);
} break;
case Type_Array: {
ssaValue *array = ssa_build_addr(proc, ie->expr).addr;
Type *et = make_type_pointer(proc->module->allocator, t->Array.elem);
ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
ssaValue *elem = ssa_emit_struct_gep(proc, array, index, et);
ssaValue *len = ssa_make_value_constant(a, t_int, make_exact_value_integer(t->Vector.count));
ssa_array_bounds_check(proc, ast_node_token(expr), index, len);
return ssa_make_addr(elem, expr);
} break;
case Type_Slice: {
ssaValue *slice = ssa_build_expr(proc, ie->expr);
elem = ssa_slice_elem(proc, slice);
ssaValue *elem = ssa_slice_elem(proc, slice);
ssaValue *len = ssa_slice_len(proc, slice);
ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
ssa_array_bounds_check(proc, ast_node_token(expr), index, len);
ssaValue *v = ssa_emit_ptr_offset(proc, elem, index);
return ssa_make_addr(v, expr);
} break;
case Type_Basic: { // Basic_string
TypeAndValue *tv = map_get(&proc->module->info->types, hash_pointer(ie->expr));
ssaValue *elem = NULL;
ssaValue *len = NULL;
if (tv->mode == Addressing_Constant) {
ssaValue *array = ssa_add_global_string_array(proc->module, tv->value);
elem = ssa_array_elem(proc, array);
len = ssa_make_value_constant(a, t_int, make_exact_value_integer(tv->value.value_string.len));
} else {
elem = ssa_string_elem(proc, ssa_build_expr(proc, ie->expr));
ssaValue *str = ssa_build_expr(proc, ie->expr);
elem = ssa_string_elem(proc, str);
len = ssa_string_len(proc, str);
}
} break;
case Type_Pointer: {
ssaValue *array = ssa_build_expr(proc, ie->expr);
elem = ssa_array_elem(proc, array);
ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
ssa_array_bounds_check(proc, ast_node_token(expr), index, len);
ssaValue *v = ssa_emit_ptr_offset(proc, elem, index);
return ssa_make_addr(v, expr);
} break;
}
ssaValue *index = ssa_emit_conv(proc, ssa_build_expr(proc, ie->index), t_int);
v = ssa_emit_ptr_offset(proc, elem, index);
return ssa_make_addr(v, expr);
case_end;
case_ast_node(se, SliceExpr, expr);
@@ -2736,7 +2801,7 @@ void ssa_build_cond(ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssa
void ssa_gen_global_type_name(ssaModule *m, Entity *e, String name) {
ssaValue *t = ssa_make_value_type_name(m->allocator, name, e->type);
map_set(&m->values, hash_pointer(e), t);
ssa_module_add_value(m, e, t);
map_set(&m->members, hash_string(name), t);
Type *bt = get_base_type(e->type);
@@ -2795,6 +2860,24 @@ void ssa_build_stmt_list(ssaProcedure *proc, AstNodeArray stmts) {
}
void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
u32 prev_stmt_state_flags = proc->module->stmt_state_flags;
defer (proc->module->stmt_state_flags = prev_stmt_state_flags);
if (node->stmt_state_flags != 0) {
u32 in = node->stmt_state_flags;
u32 out = proc->module->stmt_state_flags;
defer (proc->module->stmt_state_flags = out);
if (in & StmtStateFlag_abc) {
out |= StmtStateFlag_abc;
out &= ~StmtStateFlag_no_abc;
} else if (in & StmtStateFlag_no_abc) {
out |= StmtStateFlag_no_abc;
out &= ~StmtStateFlag_abc;
}
}
switch (node->kind) {
case_ast_node(bs, EmptyStmt, node);
case_end;
@@ -3436,6 +3519,24 @@ void ssa_build_proc(ssaValue *value, ssaProcedure *parent) {
proc->parent = parent;
if (proc->body != NULL) {
u32 prev_stmt_state_flags = proc->module->stmt_state_flags;
defer (proc->module->stmt_state_flags = prev_stmt_state_flags);
if (proc->tags != 0) {
u32 in = proc->tags;
u32 out = proc->module->stmt_state_flags;
defer (proc->module->stmt_state_flags = out);
if (in & ProcTag_abc) {
out |= ProcTag_abc;
out &= ~ProcTag_no_abc;
} else if (in & ProcTag_no_abc) {
out |= ProcTag_no_abc;
out &= ~ProcTag_abc;
}
}
ssa_begin_procedure_body(proc);
ssa_insert_code_before_proc(proc, parent);
ssa_build_stmt(proc, proc->body);
+45 -12
View File
@@ -59,10 +59,11 @@ enum DeclKind {
};
enum ProcTag {
ProcTag_foreign = GB_BIT(0),
ProcTag_inline = GB_BIT(1),
ProcTag_no_inline = GB_BIT(2),
ProcTag_pure = GB_BIT(3),
ProcTag_abc = GB_BIT(0),
ProcTag_no_abc = GB_BIT(1),
ProcTag_foreign = GB_BIT(2),
ProcTag_inline = GB_BIT(3),
ProcTag_no_inline = GB_BIT(4),
};
enum VarDeclTag {
@@ -76,6 +77,12 @@ enum TypeFlag : u32 {
TypeFlag_atomic = GB_BIT(1),
};
enum StmtStateFlag : u32 {
StmtStateFlag_abc = GB_BIT(0),
StmtStateFlag_no_abc = GB_BIT(1),
};
enum CallExprKind {
CallExpr_Prefix, // call(...)
CallExpr_Postfix, // a'call
@@ -288,6 +295,7 @@ struct AstNode {
AstNodeKind kind;
// AstNode *prev, *next; // NOTE(bill): allow for Linked list
u32 type_flags;
u32 stmt_state_flags;
union {
#define AST_NODE_KIND(_kind_name_, name, ...) __VA_ARGS__ _kind_name_;
AST_NODE_KINDS
@@ -1169,15 +1177,25 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name) {
check_proc_add_tag(f, tag_expr, tags, ProcTag_inline, tag_name);
} else if (are_strings_equal(tag_name, make_string("no_inline"))) {
check_proc_add_tag(f, tag_expr, tags, ProcTag_no_inline, tag_name);
} else if (are_strings_equal(tag_name, make_string("pure"))) {
check_proc_add_tag(f, tag_expr, tags, ProcTag_pure, tag_name);
} else if (are_strings_equal(tag_name, make_string("abc"))) {
check_proc_add_tag(f, tag_expr, tags, ProcTag_abc, tag_name);
} else if (are_strings_equal(tag_name, make_string("no_abc"))) {
check_proc_add_tag(f, tag_expr, tags, ProcTag_no_abc, tag_name);
} else {
ast_file_err(f, ast_node_token(tag_expr), "Unknown procedure tag");
}
}
if ((*tags & ProcTag_inline) && (*tags & ProcTag_no_inline)) {
ast_file_err(f, f->cursor[0], "You cannot apply both `inline` and `no_inline` to a procedure");
ast_file_err(f, f->cursor[0], "You cannot apply both #inline and #no_inline to a procedure");
}
if ((*tags & ProcTag_abc) && (*tags & ProcTag_no_abc)) {
ast_file_err(f, f->cursor[0], "You cannot apply both #abc and #no_abc to a procedure");
}
if (((*tags & ProcTag_abc) || (*tags & ProcTag_no_abc)) && (*tags & ProcTag_foreign)) {
ast_file_err(f, f->cursor[0], "You cannot apply both #abc or #no_abc to a procedure without a body");
}
}
@@ -2493,22 +2511,23 @@ AstNode *parse_stmt(AstFile *f) {
case Token_Hash: {
s = parse_tag_stmt(f, NULL);
String tag = s->TagStmt.name.string;
if (are_strings_equal(s->TagStmt.name.string, make_string("load"))) {
if (are_strings_equal(tag, make_string("load"))) {
Token file_path = expect_token(f, Token_String);
if (f->curr_proc == NULL) {
return make_load_decl(f, s->TagStmt.token, file_path);
}
ast_file_err(f, token, "You cannot `load` within a procedure. This must be done at the file scope.");
ast_file_err(f, token, "You cannot use #load within a procedure. This must be done at the file scope.");
return make_bad_decl(f, token, file_path);
} else if (are_strings_equal(s->TagStmt.name.string, make_string("foreign_system_library"))) {
} else if (are_strings_equal(tag, make_string("foreign_system_library"))) {
Token file_path = expect_token(f, Token_String);
if (f->curr_proc == NULL) {
return make_foreign_system_library(f, s->TagStmt.token, file_path);
}
ast_file_err(f, token, "You cannot using `foreign_system_library` within a procedure. This must be done at the file scope.");
ast_file_err(f, token, "You cannot use #foreign_system_library within a procedure. This must be done at the file scope.");
return make_bad_decl(f, token, file_path);
} else if (are_strings_equal(s->TagStmt.name.string, make_string("thread_local"))) {
} else if (are_strings_equal(tag, make_string("thread_local"))) {
AstNode *var_decl = parse_simple_stmt(f);
if (var_decl->kind != AstNode_VarDecl ||
var_decl->VarDecl.kind != Declaration_Mutable) {
@@ -2521,6 +2540,20 @@ AstNode *parse_stmt(AstFile *f) {
}
var_decl->VarDecl.tags |= VarDeclTag_thread_local;
return var_decl;
} else if (are_strings_equal(tag, make_string("abc"))) {
s = parse_stmt(f);
s->stmt_state_flags |= StmtStateFlag_abc;
if ((s->stmt_state_flags & StmtStateFlag_no_abc) != 0) {
ast_file_err(f, token, "#abc and #no_abc cannot be applied together");
}
return s;
} else if (are_strings_equal(tag, make_string("no_abc"))) {
s = parse_stmt(f);
s->stmt_state_flags |= StmtStateFlag_no_abc;
if ((s->stmt_state_flags & StmtStateFlag_abc) != 0) {
ast_file_err(f, token, "#abc and #no_abc cannot be applied together");
}
return s;
}