mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-05 11:11:37 -07:00
Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 784c48c9e3 | |||
| 008d8f25c8 | |||
| 7ffcf34dca | |||
| f3a4904f21 | |||
| 3aec78b1d4 | |||
| a3e6e8d304 | |||
| a747c03f29 | |||
| 2301ae157c | |||
| d3c7d6d485 | |||
| 9b063ad9a3 | |||
| c2f9bf489e | |||
| e496b95881 | |||
| 444f4f446a | |||
| 41ad896f3f | |||
| 0a4b88f9a6 | |||
| 4c2f03b1f2 | |||
| 52dcaeb1e9 |
@@ -72,6 +72,8 @@ main :: proc() {
|
||||
|
||||
## Requirements to build and run
|
||||
|
||||
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin).
|
||||
|
||||
- Windows
|
||||
* x86-64
|
||||
* MSVC 2010 installed (C++11 support)
|
||||
|
||||
+3
-4
@@ -843,7 +843,6 @@ fmt_bit_set :: proc(fi: ^Fmt_Info, v: any, name: string = "") {
|
||||
case runtime.Type_Info_Bit_Set:
|
||||
bits: u64;
|
||||
bit_size := u64(8*type_info.size);
|
||||
verb := 'b';
|
||||
|
||||
switch bit_size {
|
||||
case 0: bits = 0;
|
||||
@@ -886,7 +885,7 @@ fmt_bit_set :: proc(fi: ^Fmt_Info, v: any, name: string = "") {
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt_bit_field :: proc(fi: ^Fmt_Info, v: any, name: string = "") {
|
||||
fmt_bit_field :: proc(fi: ^Fmt_Info, v: any, bit_field_name: string = "") {
|
||||
type_info := type_info_of(v.id);
|
||||
switch info in type_info.variant {
|
||||
case runtime.Type_Info_Named:
|
||||
@@ -902,8 +901,8 @@ fmt_bit_field :: proc(fi: ^Fmt_Info, v: any, name: string = "") {
|
||||
case 8: data = cast(u64)(^u64)(v.data)^;
|
||||
}
|
||||
|
||||
if name != "" {
|
||||
write_string(fi.buf, name);
|
||||
if bit_field_name != "" {
|
||||
write_string(fi.buf, bit_field_name);
|
||||
write_byte(fi.buf, '{');
|
||||
} else {
|
||||
write_string(fi.buf, "bit_field{");
|
||||
|
||||
+2
-2
@@ -226,9 +226,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case Allocator_Mode.Free:
|
||||
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
|
||||
if old_memory == last_ptr {
|
||||
size := scratch.curr_offset - scratch.prev_offset;
|
||||
full_size := scratch.curr_offset - scratch.prev_offset;
|
||||
scratch.curr_offset = scratch.prev_offset;
|
||||
zero(last_ptr, size);
|
||||
zero(last_ptr, full_size);
|
||||
return nil;
|
||||
}
|
||||
// NOTE(bill): It's scratch memory, don't worry about freeing
|
||||
|
||||
+34
-5
@@ -67,12 +67,41 @@ copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawpt
|
||||
compare :: proc "contextless" (a, b: []byte) -> int {
|
||||
return compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
|
||||
}
|
||||
compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int {
|
||||
pa :: ptr_offset;
|
||||
for i in 0..n-1 do switch {
|
||||
case pa(a, i)^ < pa(b, i)^: return -1;
|
||||
case pa(a, i)^ > pa(b, i)^: return +1;
|
||||
compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check {
|
||||
ptr_idx :: inline proc(ptr: $P/^$T, n: int) -> T {
|
||||
return ptr_offset(ptr, n)^;
|
||||
}
|
||||
|
||||
x := slice_ptr(a, n);
|
||||
y := slice_ptr(b, n);
|
||||
|
||||
SU :: size_of(uintptr);
|
||||
fast := n/SU + 1;
|
||||
offset := (fast-1)*SU;
|
||||
curr_block := 0;
|
||||
if n < SU {
|
||||
fast = 0;
|
||||
}
|
||||
|
||||
la := slice_ptr((^uintptr)(a), fast);
|
||||
lb := slice_ptr((^uintptr)(b), fast);
|
||||
|
||||
for /**/; curr_block < fast; curr_block += 1 {
|
||||
if la[curr_block] ~ lb[curr_block] != 0 {
|
||||
for pos := curr_block*SU; pos < n; pos += 1 {
|
||||
if x[pos] ~ y[pos] != 0 {
|
||||
return (int(x[pos]) - int(y[pos])) < 0 ? -1 : +1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for /**/; offset < n; offset += 1 {
|
||||
if x[offset] ~ y[offset] != 0 {
|
||||
return (int(x[offset]) - int(y[offset])) < 0 ? -1 : +1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,13 +22,13 @@ print_u64 :: proc(fd: os.Handle, u: u64) {
|
||||
|
||||
print_i64 :: proc(fd: os.Handle, u: i64) {
|
||||
digits := "0123456789";
|
||||
b :: i64(10);
|
||||
|
||||
neg := u < 0;
|
||||
u = abs(u);
|
||||
|
||||
a: [129]byte;
|
||||
i := len(a);
|
||||
b := i64(10);
|
||||
for u >= b {
|
||||
i -= 1; a[i] = digits[u % b];
|
||||
u /= b;
|
||||
|
||||
+15
-1
@@ -408,7 +408,6 @@ parametric_polymorphism :: proc() {
|
||||
}
|
||||
assert(table.count <= len(table.slots));
|
||||
|
||||
hash := get_hash(key);
|
||||
index = int(hash % u32(len(table.slots)));
|
||||
|
||||
for table.slots[index].occupied {
|
||||
@@ -815,6 +814,21 @@ bit_set_type :: proc() {
|
||||
y |= {1, 4, 2};
|
||||
assert(2 in y);
|
||||
}
|
||||
{
|
||||
Letters :: bit_set['A'..'Z'];
|
||||
a := Letters{'A', 'B'};
|
||||
b := Letters{'A', 'B', 'C', 'D', 'F'};
|
||||
c := Letters{'A', 'B'};
|
||||
|
||||
|
||||
assert(a <= b); // 'a' is a subset of 'b'
|
||||
assert(b >= a); // 'b' is a superset of 'a'
|
||||
assert(a < b); // 'a' is a strict subset of 'b'
|
||||
assert(b > a); // 'b' is a strict superset of 'a'
|
||||
|
||||
assert(!(a < c)); // 'a' is a not strict subset of 'c'
|
||||
assert(!(c > a)); // 'c' is a not strict superset of 'a'
|
||||
}
|
||||
}
|
||||
|
||||
diverging_procedures :: proc() {
|
||||
|
||||
@@ -103,6 +103,7 @@ struct BuildContext {
|
||||
bool no_output_files;
|
||||
bool no_crt;
|
||||
bool use_lld;
|
||||
bool vet;
|
||||
|
||||
gbAffinity affinity;
|
||||
isize thread_count;
|
||||
|
||||
+59
-17
@@ -27,7 +27,7 @@ struct CallArgumentData {
|
||||
};
|
||||
|
||||
struct PolyProcData {
|
||||
Entity * gen_entity;
|
||||
Entity * gen_entity;
|
||||
ProcInfo proc_info;
|
||||
};
|
||||
|
||||
@@ -278,7 +278,6 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
auto *found_gen_procs = map_get(&nctx.info->gen_procs, hash_pointer(base_entity->identifier));
|
||||
if (found_gen_procs) {
|
||||
auto procs = *found_gen_procs;
|
||||
@@ -304,13 +303,14 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
|
||||
// NOTE(bill): Reset scope from the failed procedure type
|
||||
scope_reset(scope);
|
||||
|
||||
success = check_procedure_type(&nctx, final_proc_type, pt->node, &operands);
|
||||
// LEAK TODO(bill): Cloning this AST may be leaky
|
||||
Ast *cloned_proc_type_node = clone_ast(pt->node);
|
||||
success = check_procedure_type(&nctx, final_proc_type, cloned_proc_type_node, &operands);
|
||||
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (found_gen_procs) {
|
||||
auto procs = *found_gen_procs;
|
||||
for_array(i, procs) {
|
||||
@@ -1558,7 +1558,11 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
|
||||
case Token_Gt:
|
||||
case Token_LtEq:
|
||||
case Token_GtEq:
|
||||
defined = is_type_ordered(x->type) && is_type_ordered(y->type);
|
||||
if (are_types_identical(x->type, y->type) && is_type_bit_set(x->type)) {
|
||||
defined = true;
|
||||
} else {
|
||||
defined = is_type_ordered(x->type) && is_type_ordered(y->type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1596,7 +1600,42 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
|
||||
if (x->mode == Addressing_Constant &&
|
||||
y->mode == Addressing_Constant) {
|
||||
if (is_type_constant_type(x->type)) {
|
||||
x->value = exact_value_bool(compare_exact_values(op, x->value, y->value));
|
||||
if (is_type_bit_set(x->type)) {
|
||||
switch (op) {
|
||||
case Token_CmpEq:
|
||||
case Token_NotEq:
|
||||
x->value = exact_value_bool(compare_exact_values(op, x->value, y->value));
|
||||
break;
|
||||
case Token_Lt:
|
||||
case Token_LtEq:
|
||||
{
|
||||
ExactValue lhs = x->value;
|
||||
ExactValue rhs = y->value;
|
||||
ExactValue res = exact_binary_operator_value(Token_And, lhs, rhs);
|
||||
res = exact_value_bool(compare_exact_values(op, res, lhs));
|
||||
if (op == Token_Lt) {
|
||||
res = exact_binary_operator_value(Token_And, res, exact_value_bool(compare_exact_values(op, lhs, rhs)));
|
||||
}
|
||||
x->value = res;
|
||||
break;
|
||||
}
|
||||
case Token_Gt:
|
||||
case Token_GtEq:
|
||||
{
|
||||
ExactValue lhs = x->value;
|
||||
ExactValue rhs = y->value;
|
||||
ExactValue res = exact_binary_operator_value(Token_And, lhs, rhs);
|
||||
res = exact_value_bool(compare_exact_values(op, res, rhs));
|
||||
if (op == Token_Gt) {
|
||||
res = exact_binary_operator_value(Token_And, res, exact_value_bool(compare_exact_values(op, lhs, rhs)));
|
||||
}
|
||||
x->value = res;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
x->value = exact_value_bool(compare_exact_values(op, x->value, y->value));
|
||||
}
|
||||
} else {
|
||||
x->mode = Addressing_Value;
|
||||
}
|
||||
@@ -2084,8 +2123,8 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as
|
||||
add_package_dependency(c, "runtime", "__dynamic_map_get");
|
||||
} else if (is_type_bit_set(y->type)) {
|
||||
Type *yt = base_type(y->type);
|
||||
check_assignment(c, x, yt->BitSet.elem, str_lit("bit_set 'in'"));
|
||||
|
||||
check_assignment(c, x, yt->BitSet.elem, str_lit("bit_set 'in'"));
|
||||
if (x->mode == Addressing_Constant && y->mode == Addressing_Constant) {
|
||||
ExactValue k = exact_value_to_integer(x->value);
|
||||
ExactValue v = exact_value_to_integer(y->value);
|
||||
@@ -2109,7 +2148,6 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as
|
||||
x->mode = Addressing_Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
gbString t = type_to_string(y->type);
|
||||
error(x->expr, "expected either a map or bitset for 'in', got %s", t);
|
||||
@@ -2647,9 +2685,9 @@ bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64
|
||||
if (value) *value = v;
|
||||
bool out_of_bounds = false;
|
||||
if (open_range) {
|
||||
out_of_bounds = v > max_count;
|
||||
out_of_bounds = v > max_count+1;
|
||||
} else {
|
||||
out_of_bounds = v >= max_count;
|
||||
out_of_bounds = v > max_count;
|
||||
}
|
||||
if (v < 0) {
|
||||
out_of_bounds = true;
|
||||
@@ -4818,7 +4856,6 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
|
||||
ast_node(ce, CallExpr, call);
|
||||
|
||||
Type *original_type = operand->type;
|
||||
Type *struct_type = base_type(operand->type);
|
||||
GB_ASSERT(is_type_polymorphic_record(original_type));
|
||||
|
||||
bool show_error = true;
|
||||
@@ -4983,6 +5020,11 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
|
||||
|
||||
String generated_name = make_string_c(expr_to_string(call));
|
||||
|
||||
CheckerContext ctx = *c;
|
||||
// NOTE(bill): We need to make sure the lookup scope for the record is the same as where it was created
|
||||
ctx.scope = polymorphic_record_parent_scope(original_type);
|
||||
GB_ASSERT(ctx.scope != nullptr);
|
||||
|
||||
Type *named_type = alloc_type_named(generated_name, nullptr, nullptr);
|
||||
Type *bt = base_type(original_type);
|
||||
if (bt->kind == Type_Struct) {
|
||||
@@ -4992,9 +5034,9 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
|
||||
struct_type->Struct.polymorphic_parent = original_type;
|
||||
set_base_type(named_type, struct_type);
|
||||
|
||||
check_open_scope(c, node);
|
||||
check_struct_type(c, struct_type, node, &ordered_operands, named_type, original_type);
|
||||
check_close_scope(c);
|
||||
check_open_scope(&ctx, node);
|
||||
check_struct_type(&ctx, struct_type, node, &ordered_operands, named_type, original_type);
|
||||
check_close_scope(&ctx);
|
||||
} else if (bt->kind == Type_Union) {
|
||||
Ast *node = clone_ast(bt->Union.node);
|
||||
Type *union_type = alloc_type_union();
|
||||
@@ -5002,9 +5044,9 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
|
||||
union_type->Union.polymorphic_parent = original_type;
|
||||
set_base_type(named_type, union_type);
|
||||
|
||||
check_open_scope(c, node);
|
||||
check_union_type(c, union_type, node, &ordered_operands, named_type, original_type);
|
||||
check_close_scope(c);
|
||||
check_open_scope(&ctx, node);
|
||||
check_union_type(&ctx, union_type, node, &ordered_operands, named_type, original_type);
|
||||
check_close_scope(&ctx);
|
||||
} else {
|
||||
GB_PANIC("Unsupported parametric polymorphic record type");
|
||||
}
|
||||
|
||||
+1
-1
@@ -1008,7 +1008,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
|
||||
}
|
||||
|
||||
if (complete) {
|
||||
Type *ut = base_type(x.type);
|
||||
Type *ut = base_type(type_deref(x.type));
|
||||
GB_ASSERT(is_type_union(ut));
|
||||
auto variants = ut->Union.variants;
|
||||
|
||||
|
||||
+10
-1
@@ -333,7 +333,7 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<
|
||||
|
||||
scope_reserve(ctx->scope, min_field_count);
|
||||
|
||||
if (st->is_raw_union) {
|
||||
if (st->is_raw_union && min_field_count > 1) {
|
||||
struct_type->Struct.is_raw_union = true;
|
||||
context = str_lit("struct #raw_union");
|
||||
}
|
||||
@@ -1566,6 +1566,9 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
|
||||
param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
|
||||
|
||||
add_entity(ctx->checker, scope, name, param);
|
||||
if (is_using) {
|
||||
add_entity_use(ctx, name, param);
|
||||
}
|
||||
array_add(&variables, param);
|
||||
}
|
||||
}
|
||||
@@ -2521,6 +2524,12 @@ Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) {
|
||||
type->Named.base = t_invalid;
|
||||
}
|
||||
|
||||
if (is_type_polymorphic(type)) {
|
||||
type->flags |= TypeFlag_Polymorphic;
|
||||
} else if (is_type_polymorphic(type, true)) {
|
||||
type->flags |= TypeFlag_PolySpecialized;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (!ctx->allow_polymorphic_types && is_type_polymorphic(type)) {
|
||||
gbString str = type_to_string(type);
|
||||
|
||||
+140
-19
@@ -320,6 +320,8 @@ void check_open_scope(CheckerContext *c, Ast *node) {
|
||||
case Ast_StructType:
|
||||
case Ast_EnumType:
|
||||
case Ast_UnionType:
|
||||
case Ast_BitFieldType:
|
||||
case Ast_BitSetType:
|
||||
scope->flags |= ScopeFlag_Type;
|
||||
break;
|
||||
}
|
||||
@@ -413,41 +415,160 @@ GB_COMPARE_PROC(entity_variable_pos_cmp) {
|
||||
}
|
||||
|
||||
|
||||
enum VettedEntityKind {
|
||||
VettedEntity_Invalid,
|
||||
|
||||
VettedEntity_Unused,
|
||||
VettedEntity_Shadowed,
|
||||
};
|
||||
struct VettedEntity {
|
||||
VettedEntityKind kind;
|
||||
Entity *entity;
|
||||
Entity *other;
|
||||
};
|
||||
void init_vetted_entity(VettedEntity *ve, VettedEntityKind kind, Entity *entity, Entity *other=nullptr) {
|
||||
ve->kind = kind;
|
||||
ve->entity = entity;
|
||||
ve->other = other;
|
||||
}
|
||||
|
||||
|
||||
GB_COMPARE_PROC(vetted_entity_variable_pos_cmp) {
|
||||
Entity *x = (*cast(VettedEntity **)a)->entity;
|
||||
Entity *y = (*cast(VettedEntity **)b)->entity;
|
||||
|
||||
return token_pos_cmp(x->token.pos, y->token.pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool check_vet_shadowing(Checker *c, Entity *e, VettedEntity *ve) {
|
||||
if (e->kind != Entity_Variable) {
|
||||
return false;
|
||||
}
|
||||
String name = e->token.string;
|
||||
if (name == "_") {
|
||||
return false;
|
||||
}
|
||||
if (e->flags & EntityFlag_Param) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e->scope->flags & (ScopeFlag_Global|ScopeFlag_File|ScopeFlag_Proc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Scope *parent = e->scope->parent;
|
||||
if (parent->flags & (ScopeFlag_Global|ScopeFlag_File)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Entity *shadowed = scope_lookup(parent, name);
|
||||
if (shadowed == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (shadowed->kind != Entity_Variable) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (shadowed->scope->flags & (ScopeFlag_Global|ScopeFlag_File)) {
|
||||
// return false;
|
||||
}
|
||||
|
||||
// NOTE(bill): The entities must be in the same file
|
||||
if (e->token.pos.file != shadowed->token.pos.file) {
|
||||
return false;
|
||||
}
|
||||
// NOTE(bill): The shaded identifier must appear before this one to be an
|
||||
// instance of shadowing
|
||||
if (token_pos_cmp(shadowed->token.pos, e->token.pos) > 0) {
|
||||
return false;
|
||||
}
|
||||
// NOTE(bill): If the types differ, don't complain
|
||||
if (are_types_identical(e->type, shadowed->type)) {
|
||||
gb_zero_item(ve);
|
||||
ve->kind = VettedEntity_Shadowed;
|
||||
ve->entity = e;
|
||||
ve->other = shadowed;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) {
|
||||
if ((e->flags&EntityFlag_Used) == 0) {
|
||||
switch (e->kind) {
|
||||
case Entity_Variable:
|
||||
case Entity_ImportName:
|
||||
case Entity_LibraryName:
|
||||
gb_zero_item(ve);
|
||||
ve->kind = VettedEntity_Unused;
|
||||
ve->entity = e;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void check_scope_usage(Checker *c, Scope *scope) {
|
||||
// TODO(bill): Use this?
|
||||
#if 0
|
||||
Array<Entity *> unused = {};
|
||||
array_init(&unused, heap_allocator());
|
||||
defer (array_free(&unused));
|
||||
if (!build_context.vet) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool vet_unused = true;
|
||||
bool vet_shadowing = true;
|
||||
|
||||
Array<VettedEntity> vetted_entities = {};
|
||||
array_init(&vetted_entities, heap_allocator());
|
||||
|
||||
for_array(i, scope->elements.entries) {
|
||||
Entity *e = scope->elements.entries[i].value;
|
||||
if (e != nullptr && (e->flags&EntityFlag_Used) == 0) {
|
||||
switch (e->kind) {
|
||||
case Entity_Variable:
|
||||
case Entity_ImportName:
|
||||
case Entity_LibraryName:
|
||||
array_add(&unused, e);
|
||||
break;
|
||||
}
|
||||
if (e == nullptr) continue;
|
||||
VettedEntity ve = {};
|
||||
if (vet_unused && check_vet_unused(c, e, &ve)) {
|
||||
array_add(&vetted_entities, ve);
|
||||
}
|
||||
if (vet_shadowing && check_vet_shadowing(c, e, &ve)) {
|
||||
array_add(&vetted_entities, ve);
|
||||
}
|
||||
}
|
||||
|
||||
gb_sort_array(unused.data, unused.count, entity_variable_pos_cmp);
|
||||
gb_sort_array(vetted_entities.data, vetted_entities.count, vetted_entity_variable_pos_cmp);
|
||||
|
||||
for_array(i, unused) {
|
||||
Entity *e = unused[i];
|
||||
error(e->token, "'%.*s' declared but not used", LIT(e->token.string));
|
||||
for_array(i, vetted_entities) {
|
||||
auto ve = vetted_entities[i];
|
||||
Entity *e = ve.entity;
|
||||
Entity *other = ve.other;
|
||||
String name = e->token.string;
|
||||
|
||||
switch (ve.kind) {
|
||||
case VettedEntity_Unused:
|
||||
error(e->token, "'%.*s' declared but not used", LIT(name));
|
||||
break;
|
||||
case VettedEntity_Shadowed:
|
||||
if (e->flags&EntityFlag_Using) {
|
||||
error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line);
|
||||
} else {
|
||||
error(e->token, "Declaration of '%.*s' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
array_free(&vetted_entities);
|
||||
|
||||
for (Scope *child = scope->first_child;
|
||||
child != nullptr;
|
||||
child = child->next) {
|
||||
if (!child->is_proc && !child->is_struct && !child->is_file) {
|
||||
if (child->flags & (ScopeFlag_Proc|ScopeFlag_Type|ScopeFlag_File)) {
|
||||
// Ignore these
|
||||
} else {
|
||||
check_scope_usage(c, child);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -349,7 +349,6 @@ enum ScopeFlag {
|
||||
ScopeFlag_Proc = 1<<5,
|
||||
ScopeFlag_Type = 1<<6,
|
||||
|
||||
|
||||
ScopeFlag_HasBeenImported = 1<<10, // This is only applicable to file scopes
|
||||
};
|
||||
|
||||
|
||||
+4
-4
@@ -85,14 +85,14 @@ struct Entity {
|
||||
Token token;
|
||||
Scope * scope;
|
||||
Type * type;
|
||||
Ast * identifier; // Can be nullptr
|
||||
Ast * identifier; // Can be nullptr
|
||||
DeclInfo * decl_info;
|
||||
DeclInfo * parent_proc_decl; // nullptr if in file/global scope
|
||||
AstPackage *pkg;
|
||||
|
||||
// TODO(bill): Cleanup how `using` works for entities
|
||||
Entity * using_parent;
|
||||
Ast * using_expr;
|
||||
Ast * using_expr;
|
||||
|
||||
isize order_in_src;
|
||||
String deprecated_message;
|
||||
@@ -109,7 +109,7 @@ struct Entity {
|
||||
|
||||
String thread_local_model;
|
||||
Entity * foreign_library;
|
||||
Ast * foreign_library_ident;
|
||||
Ast * foreign_library_ident;
|
||||
String link_name;
|
||||
String link_prefix;
|
||||
bool is_foreign;
|
||||
@@ -117,9 +117,9 @@ struct Entity {
|
||||
bool is_immutable;
|
||||
} Variable;
|
||||
struct {
|
||||
bool is_type_alias;
|
||||
Type * type_parameter_specialization;
|
||||
String ir_mangled_name;
|
||||
bool is_type_alias;
|
||||
} TypeName;
|
||||
struct {
|
||||
u64 tags;
|
||||
|
||||
+9
-6
@@ -763,7 +763,7 @@ extern "C++" {
|
||||
#ifndef GB_ASSERT_MSG
|
||||
#define GB_ASSERT_MSG(cond, msg, ...) do { \
|
||||
if (!(cond)) { \
|
||||
gb_assert_handler(#cond, __FILE__, cast(i64)__LINE__, msg, ##__VA_ARGS__); \
|
||||
gb_assert_handler("Assertion Failure", #cond, __FILE__, cast(i64)__LINE__, msg, ##__VA_ARGS__); \
|
||||
GB_DEBUG_TRAP(); \
|
||||
} \
|
||||
} while (0)
|
||||
@@ -779,10 +779,13 @@ extern "C++" {
|
||||
|
||||
// NOTE(bill): Things that shouldn't happen with a message!
|
||||
#ifndef GB_PANIC
|
||||
#define GB_PANIC(msg, ...) GB_ASSERT_MSG(0, msg, ##__VA_ARGS__)
|
||||
#define GB_PANIC(msg, ...) do { \
|
||||
gb_assert_handler("Panic", NULL, __FILE__, cast(i64)__LINE__, msg, ##__VA_ARGS__); \
|
||||
GB_DEBUG_TRAP(); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
GB_DEF void gb_assert_handler(char const *condition, char const *file, i32 line, char const *msg, ...);
|
||||
GB_DEF void gb_assert_handler(char const *prefix, char const *condition, char const *file, i32 line, char const *msg, ...);
|
||||
|
||||
|
||||
|
||||
@@ -979,7 +982,7 @@ typedef struct gbThread {
|
||||
|
||||
gbSemaphore semaphore;
|
||||
isize stack_size;
|
||||
b32 is_running;
|
||||
b32 volatile is_running;
|
||||
} gbThread;
|
||||
|
||||
GB_DEF void gb_thread_init (gbThread *t);
|
||||
@@ -3613,8 +3616,8 @@ extern "C" {
|
||||
#pragma warning(disable:4127) // Conditional expression is constant
|
||||
#endif
|
||||
|
||||
void gb_assert_handler(char const *condition, char const *file, i32 line, char const *msg, ...) {
|
||||
gb_printf_err("%s(%d): Assert Failure: ", file, line);
|
||||
void gb_assert_handler(char const *prefix, char const *condition, char const *file, i32 line, char const *msg, ...) {
|
||||
gb_printf_err("%s(%d): %s: ", file, line, prefix);
|
||||
if (condition)
|
||||
gb_printf_err( "`%s` ", condition);
|
||||
if (msg) {
|
||||
|
||||
+83
-15
@@ -846,7 +846,7 @@ void ir_build_proc (irValue *value, irProcedure *parent);
|
||||
void ir_gen_global_type_name(irModule *m, Entity *e, String name);
|
||||
irValue *ir_get_type_info_ptr (irProcedure *proc, Type *type);
|
||||
void ir_value_set_debug_location(irProcedure *proc, irValue *v);
|
||||
void ir_push_debug_location (irModule *m, Ast *node, irDebugInfo *scope);
|
||||
void ir_push_debug_location (irModule *m, Ast *node, irDebugInfo *scope, Entity *e=nullptr);
|
||||
void ir_pop_debug_location (irModule *m);
|
||||
irDebugInfo *ir_add_debug_info_local(irProcedure *proc, Entity *e, i32 arg_id);
|
||||
irDebugInfo *ir_add_debug_info_file(irModule *module, AstFile *file);
|
||||
@@ -1513,7 +1513,8 @@ irValue *ir_add_local(irProcedure *proc, Entity *e, Ast *expr, bool zero_initial
|
||||
}
|
||||
|
||||
// if (proc->module->generate_debug_info && expr != nullptr && proc->entity != nullptr) {
|
||||
if (proc->module->generate_debug_info && proc->entity != nullptr) {
|
||||
// if (proc->module->generate_debug_info && proc->entity != nullptr) {
|
||||
if (proc->module->generate_debug_info) {
|
||||
// GB_ASSERT_NOT_NULL(proc->debug_scope);
|
||||
if (expr != nullptr) {
|
||||
ir_emit(proc, ir_instr_debug_declare(proc, expr, e, true, instr));
|
||||
@@ -1589,7 +1590,7 @@ irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type, i
|
||||
irValue *v = ir_value_param(proc, e, abi_type);
|
||||
irValueParam *p = &v->Param;
|
||||
|
||||
ir_push_debug_location(proc->module, e ? e->identifier : nullptr, proc->debug_scope);
|
||||
ir_push_debug_location(proc->module, e ? e->identifier : nullptr, proc->debug_scope, e);
|
||||
defer (ir_pop_debug_location(proc->module));
|
||||
|
||||
switch (p->kind) {
|
||||
@@ -2590,8 +2591,21 @@ irDebugInfo *ir_add_debug_info_proc(irProcedure *proc) {
|
||||
return di;
|
||||
}
|
||||
|
||||
irDebugInfo *ir_add_debug_info_location(irModule *m, Ast *node, irDebugInfo *scope) {
|
||||
irDebugInfo *ir_add_debug_info_location(irModule *m, Ast *node, irDebugInfo *scope, Entity *e) {
|
||||
if (node == nullptr || scope == nullptr) {
|
||||
if (e != nullptr && scope != nullptr) {
|
||||
// irDebugInfo **existing = map_get(&m->debug_info, hash_entity(e));
|
||||
// if (existing != nullptr) {
|
||||
// return *existing;
|
||||
// }
|
||||
|
||||
// // TODO HACK(bill): This is a little dirty but it is should do for the weird edge cases
|
||||
// irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_Location);
|
||||
// di->Location.pos = e->token.pos;
|
||||
// di->Location.scope = scope;
|
||||
// map_set(&m->debug_info, hash_entity(e), di);
|
||||
// return di;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
// TODO(lachsinc): Should we traverse the node/children until we find one with
|
||||
@@ -2607,8 +2621,8 @@ irDebugInfo *ir_add_debug_info_location(irModule *m, Ast *node, irDebugInfo *sco
|
||||
return di;
|
||||
}
|
||||
|
||||
void ir_push_debug_location(irModule *m, Ast *node, irDebugInfo *scope) {
|
||||
irDebugInfo *debug_location = ir_add_debug_info_location(m, node, scope);
|
||||
void ir_push_debug_location(irModule *m, Ast *node, irDebugInfo *scope, Entity *e) {
|
||||
irDebugInfo *debug_location = ir_add_debug_info_location(m, node, scope, e);
|
||||
array_add(&m->debug_location_stack, debug_location);
|
||||
}
|
||||
|
||||
@@ -2666,9 +2680,18 @@ void ir_value_set_debug_location(irProcedure *proc, irValue *v) {
|
||||
v->loc = *array_end_ptr(&m->debug_location_stack);
|
||||
|
||||
if (v->loc == nullptr && proc->entity != nullptr) {
|
||||
// NOTE(lachsinc): Entry point (main()) and runtime_startup are the only ones where null location is considered valid.
|
||||
GB_ASSERT_MSG(proc->is_entry_point || (string_compare(proc->name, str_lit(IR_STARTUP_RUNTIME_PROC_NAME)) == 0),
|
||||
"%.*s %p", LIT(proc->name), proc->entity);
|
||||
if (proc->is_entry_point || (string_compare(proc->name, str_lit(IR_STARTUP_RUNTIME_PROC_NAME)) == 0)) {
|
||||
// NOTE(lachsinc): Entry point (main()) and runtime_startup are the only ones where null location is considered valid.
|
||||
} else {
|
||||
if (v->kind == irValue_Instr) {
|
||||
auto *instr = &v->Instr;
|
||||
gb_printf_err("Instruction kind: %.*s\n", LIT(ir_instr_strings[instr->kind]));
|
||||
if (instr->kind == irInstr_DebugDeclare) {
|
||||
gb_printf_err("\t%.*s\n", LIT(instr->DebugDeclare.entity->token.string));
|
||||
}
|
||||
}
|
||||
GB_PANIC("Value wihout debug location: %.*s %p; %p", LIT(proc->name), proc->entity, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3823,6 +3846,37 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal
|
||||
return ir_emit_runtime_call(proc, runtime_proc, args);
|
||||
}
|
||||
|
||||
if (is_type_bit_set(a)) {
|
||||
switch (op_kind) {
|
||||
case Token_Lt:
|
||||
case Token_LtEq:
|
||||
case Token_Gt:
|
||||
case Token_GtEq:
|
||||
{
|
||||
Type *it = bit_set_to_int(a);
|
||||
irValue *lhs = ir_emit_bitcast(proc, left, it);
|
||||
irValue *rhs = ir_emit_bitcast(proc, right, it);
|
||||
irValue *res = ir_emit_arith(proc, Token_And, lhs, rhs, it);
|
||||
|
||||
if (op_kind == Token_Lt || op_kind == Token_LtEq) {
|
||||
// (lhs & rhs) == lhs
|
||||
res = ir_emit(proc, ir_instr_binary_op(proc, Token_CmpEq, res, lhs, t_llvm_bool));
|
||||
} else if (op_kind == Token_Gt || op_kind == Token_GtEq) {
|
||||
// (lhs & rhs) == rhs
|
||||
res = ir_emit(proc, ir_instr_binary_op(proc, Token_CmpEq, res, rhs, t_llvm_bool));
|
||||
}
|
||||
|
||||
// NOTE(bill): Strict subsets
|
||||
if (op_kind == Token_Lt || op_kind == Token_Gt) {
|
||||
// res &~ (lhs == rhs)
|
||||
irValue *eq = ir_emit(proc, ir_instr_binary_op(proc, Token_CmpEq, lhs, rhs, t_llvm_bool));
|
||||
res = ir_emit_arith(proc, Token_AndNot, res, eq, t_llvm_bool);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ir_emit(proc, ir_instr_binary_op(proc, op_kind, left, right, t_llvm_bool));
|
||||
}
|
||||
@@ -4276,7 +4330,6 @@ void ir_emit_store_union_variant(irProcedure *proc, irValue *parent, irValue *va
|
||||
gbAllocator a = ir_allocator();
|
||||
irValue *underlying = ir_emit_conv(proc, parent, alloc_type_pointer(variant_type));
|
||||
|
||||
irValue *v = variant;
|
||||
ir_emit_store(proc, underlying, variant);
|
||||
|
||||
Type *t = type_deref(ir_type(parent));
|
||||
@@ -4684,6 +4737,14 @@ irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t) {
|
||||
return ir_emit_ptr_to_uintptr(proc, value, t);
|
||||
}
|
||||
|
||||
if (is_type_integer(src) && (is_type_pointer(dst) || is_type_cstring(dst))) {
|
||||
Type *vt = core_type(ir_type(value));
|
||||
return ir_emit(proc, ir_instr_conv(proc, irConv_inttoptr, value, vt, t));
|
||||
}else if ((is_type_pointer(src) || is_type_cstring(src)) && is_type_integer(dst)) {
|
||||
Type *vt = core_type(ir_type(value));
|
||||
return ir_emit(proc, ir_instr_conv(proc, irConv_ptrtoint, value, vt, t));
|
||||
}
|
||||
|
||||
if (ir_is_type_aggregate(src) || ir_is_type_aggregate(dst)) {
|
||||
irValue *s = ir_address_from_load_or_generate_local(proc, value);
|
||||
irValue *d = ir_emit_bitcast(proc, s, alloc_type_pointer(t));
|
||||
@@ -6160,7 +6221,6 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
|
||||
|
||||
Type *key_type = rt->BitSet.elem;
|
||||
GB_ASSERT(are_types_identical(ir_type(left), key_type));
|
||||
|
||||
Type *it = bit_set_to_int(rt);
|
||||
left = ir_emit_conv(proc, left, it);
|
||||
|
||||
@@ -6965,9 +7025,18 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) {
|
||||
|
||||
GB_ASSERT(ir_type(field_expr)->kind != Type_Tuple);
|
||||
|
||||
irValue *fv = ir_emit_conv(proc, field_expr, ft);
|
||||
irValue *gep = ir_emit_struct_ep(proc, v, cast(i32)index);
|
||||
ir_emit_store(proc, gep, fv);
|
||||
Type *fet = ir_type(field_expr);
|
||||
// HACK TODO(bill): THIS IS A MASSIVE HACK!!!!
|
||||
if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) {
|
||||
GB_ASSERT_MSG(union_variant_index(ft, fet) > 0, "%s", type_to_string(fet));
|
||||
|
||||
irValue *gep = ir_emit_struct_ep(proc, v, cast(i32)index);
|
||||
ir_emit_store_union_variant(proc, gep, field_expr, fet);
|
||||
} else {
|
||||
irValue *fv = ir_emit_conv(proc, field_expr, ft);
|
||||
irValue *gep = ir_emit_struct_ep(proc, v, cast(i32)index);
|
||||
ir_emit_store(proc, gep, fv);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -8505,7 +8574,6 @@ void ir_begin_procedure_body(irProcedure *proc) {
|
||||
ir_push_debug_location(proc->module, proc->entity->identifier, proc->debug_scope);
|
||||
GB_ASSERT_NOT_NULL(proc->debug_scope);
|
||||
} else {
|
||||
// GB_ASSERT(proc->is_entry_point || (string_compare(proc->name, str_lit(IR_STARTUP_RUNTIME_PROC_NAME)) == 0));
|
||||
ir_push_debug_location(proc->module, nullptr, nullptr);
|
||||
}
|
||||
|
||||
|
||||
+38
-6
@@ -238,6 +238,32 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct = false
|
||||
void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hint);
|
||||
|
||||
|
||||
void ir_print_alignment_prefix_hack(irFileBuffer *f, i64 alignment) {
|
||||
// NOTE(bill): This is written like this as it may need to
|
||||
// changed for specific alignments
|
||||
switch (alignment) {
|
||||
case 1:
|
||||
ir_write_string(f, str_lit("[0 x i8]"));
|
||||
break;
|
||||
case 2:
|
||||
ir_write_string(f, str_lit("[0 x i16]"));
|
||||
break;
|
||||
case 4:
|
||||
ir_write_string(f, str_lit("[0 x i32]"));
|
||||
break;
|
||||
case 8:
|
||||
ir_write_string(f, str_lit("[0 x i64]"));
|
||||
break;
|
||||
case 16:
|
||||
ir_write_string(f, str_lit("[0 x <4 x i32>]"));
|
||||
break;
|
||||
default:
|
||||
GB_PANIC("Invalid alignment");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
|
||||
GB_ASSERT(is_type_proc(t));
|
||||
t = base_type(t);
|
||||
@@ -398,8 +424,9 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
|
||||
i64 align = type_align_of(t);
|
||||
i64 block_size = t->Union.variant_block_size;
|
||||
|
||||
ir_fprintf(f, "{[0 x <%lld x i8>], ", align);
|
||||
ir_fprintf(f, "[%lld x i8], ", block_size);
|
||||
ir_write_byte(f, '{');
|
||||
ir_print_alignment_prefix_hack(f, align);
|
||||
ir_fprintf(f, ", [%lld x i8], ", block_size);
|
||||
// ir_print_type(f, m, t_type_info_ptr);
|
||||
ir_print_type(f, m, union_tag_type(t));
|
||||
ir_write_byte(f, '}');
|
||||
@@ -412,7 +439,9 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
|
||||
// LLVM takes the first element's alignment as the entire alignment (like C)
|
||||
i64 size_of_union = type_size_of(t);
|
||||
i64 align_of_union = type_align_of(t);
|
||||
ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8]}", align_of_union, size_of_union);
|
||||
ir_write_byte(f, '{');
|
||||
ir_print_alignment_prefix_hack(f, align_of_union);
|
||||
ir_fprintf(f, ", [%lld x i8]}", align_of_union, size_of_union);
|
||||
return;
|
||||
} else {
|
||||
if (t->Struct.is_packed) {
|
||||
@@ -420,7 +449,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
|
||||
}
|
||||
ir_write_byte(f, '{');
|
||||
if (t->Struct.custom_align > 0) {
|
||||
ir_fprintf(f, "[0 x <%lld x i8>]", t->Struct.custom_align);
|
||||
ir_print_alignment_prefix_hack(f, t->Struct.custom_align);
|
||||
if (t->Struct.fields.count > 0) {
|
||||
ir_write_string(f, str_lit(", "));
|
||||
}
|
||||
@@ -499,7 +528,9 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
|
||||
case Type_BitField: {
|
||||
i64 align = type_align_of(t);
|
||||
i64 size = type_size_of(t);
|
||||
ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8]}", align, size);
|
||||
ir_write_byte(f, '{');
|
||||
ir_print_alignment_prefix_hack(f, align);
|
||||
ir_fprintf(f, ", [%lld x i8]}", size);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1975,6 +2006,7 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ir_print_type_name(irFileBuffer *f, irModule *m, irValue *v) {
|
||||
GB_ASSERT(v->kind == irValue_TypeName);
|
||||
Type *t = base_type(v->TypeName.type);
|
||||
@@ -1998,7 +2030,7 @@ void ir_print_type_name(irFileBuffer *f, irModule *m, irValue *v) {
|
||||
}
|
||||
ir_write_byte(f, '{');
|
||||
if (t->Struct.custom_align > 0) {
|
||||
ir_fprintf(f, "[0 x <%lld x i8>]", t->Struct.custom_align);
|
||||
ir_print_alignment_prefix_hack(f, t->Struct.custom_align);
|
||||
}
|
||||
ir_write_byte(f, '}');
|
||||
if (t->Struct.is_packed) {
|
||||
|
||||
@@ -214,6 +214,7 @@ enum BuildFlagKind {
|
||||
BuildFlag_NoBoundsCheck,
|
||||
BuildFlag_NoCRT,
|
||||
BuildFlag_UseLLD,
|
||||
BuildFlag_Vet,
|
||||
|
||||
BuildFlag_COUNT,
|
||||
};
|
||||
@@ -256,6 +257,7 @@ bool parse_build_flags(Array<String> args) {
|
||||
add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None);
|
||||
add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None);
|
||||
add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None);
|
||||
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None);
|
||||
|
||||
GB_ASSERT(args.count >= 3);
|
||||
Array<String> flag_args = array_slice(args, 3, args.count);
|
||||
@@ -563,6 +565,10 @@ bool parse_build_flags(Array<String> args) {
|
||||
case BuildFlag_UseLLD:
|
||||
build_context.use_lld = true;
|
||||
break;
|
||||
|
||||
case BuildFlag_Vet:
|
||||
build_context.vet = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -4567,7 +4567,10 @@ GB_THREAD_PROC(parse_worker_file_proc) {
|
||||
if (thread == nullptr) return 0;
|
||||
auto *p = cast(Parser *)thread->user_data;
|
||||
isize index = thread->user_index;
|
||||
ParseFileError err = process_imported_file(p, p->files_to_process[index]);
|
||||
gb_mutex_lock(&p->file_add_mutex);
|
||||
auto file_to_process = p->files_to_process[index];
|
||||
gb_mutex_unlock(&p->file_add_mutex);
|
||||
ParseFileError err = process_imported_file(p, file_to_process);
|
||||
return cast(isize)err;
|
||||
}
|
||||
|
||||
|
||||
+39
-5
@@ -215,6 +215,11 @@ String const type_strings[] = {
|
||||
TYPE_KINDS
|
||||
#undef TYPE_KIND
|
||||
|
||||
enum TypeFlag : u32 {
|
||||
TypeFlag_Polymorphic = 1<<1,
|
||||
TypeFlag_PolySpecialized = 1<<2,
|
||||
};
|
||||
|
||||
struct Type {
|
||||
TypeKind kind;
|
||||
union {
|
||||
@@ -226,6 +231,7 @@ struct Type {
|
||||
// NOTE(bill): These need to be at the end to not affect the unionized data
|
||||
i64 cached_size;
|
||||
i64 cached_align;
|
||||
u32 flags; // TypeFlag
|
||||
bool failure;
|
||||
};
|
||||
|
||||
@@ -1072,6 +1078,7 @@ bool is_type_indexable(Type *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool is_type_polymorphic_record(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t->kind == Type_Struct) {
|
||||
@@ -1082,6 +1089,18 @@ bool is_type_polymorphic_record(Type *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Scope *polymorphic_record_parent_scope(Type *t) {
|
||||
t = base_type(t);
|
||||
if (is_type_polymorphic_record(t)) {
|
||||
if (t->kind == Type_Struct) {
|
||||
return t->Struct.scope->parent;
|
||||
} else if (t->kind == Type_Union) {
|
||||
return t->Union.scope->parent;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool is_type_polymorphic_record_specialized(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t->kind == Type_Struct) {
|
||||
@@ -1180,11 +1199,11 @@ bool is_type_polymorphic(Type *t, bool or_specialized=false) {
|
||||
if (or_specialized && t->Union.is_poly_specialized) {
|
||||
return true;
|
||||
}
|
||||
for_array(i, t->Union.variants) {
|
||||
if (is_type_polymorphic(t->Union.variants[i], or_specialized)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// for_array(i, t->Union.variants) {
|
||||
// if (is_type_polymorphic(t->Union.variants[i], or_specialized)) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
break;
|
||||
case Type_Struct:
|
||||
if (t->Struct.is_polymorphic) {
|
||||
@@ -1613,11 +1632,26 @@ i64 union_tag_size(Type *u) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 1
|
||||
// TODO(bill): Is this an okay approach?
|
||||
i64 max_align = 1;
|
||||
for_array(i, u->Union.variants) {
|
||||
Type *variant_type = u->Union.variants[i];
|
||||
i64 align = type_align_of(variant_type);
|
||||
if (max_align < align) {
|
||||
max_align = align;
|
||||
}
|
||||
}
|
||||
|
||||
u->Union.tag_size = gb_min(max_align, build_context.max_align);
|
||||
return max_align;
|
||||
#else
|
||||
i64 bytes = next_pow2(cast(i64)(floor_log2(n)/8 + 1));
|
||||
i64 tag_size = gb_max(bytes, 1);
|
||||
|
||||
u->Union.tag_size = tag_size;
|
||||
return tag_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
Type *union_tag_type(Type *u) {
|
||||
|
||||
Reference in New Issue
Block a user