mirror of
https://github.com/odin-lang/Odin.git
synced 2026-05-02 11:04:40 +00:00
Add experimental support for a threaded semantic checker to -threaded-checker
This commit is contained in:
@@ -587,7 +587,9 @@ void big_int_add(BigInt *dst, BigInt const *x, BigInt const *y) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_ASSERT(overflow == 0);
|
if (overflow != 0) {
|
||||||
|
GB_ASSERT_MSG(overflow == 0, "%p %p %p", dst, x, y);
|
||||||
|
}
|
||||||
dst->len = i;
|
dst->len = i;
|
||||||
big_int_normalize(dst);
|
big_int_normalize(dst);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -208,6 +208,7 @@ struct BuildContext {
|
|||||||
bool linker_map_file;
|
bool linker_map_file;
|
||||||
|
|
||||||
bool use_separate_modules;
|
bool use_separate_modules;
|
||||||
|
bool threaded_checker;
|
||||||
|
|
||||||
u32 cmd_doc_flags;
|
u32 cmd_doc_flags;
|
||||||
Array<String> extra_packages;
|
Array<String> extra_packages;
|
||||||
|
|||||||
@@ -1686,7 +1686,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
|
|
||||||
Ast *dummy_node_struct = alloc_ast_node(nullptr, Ast_Invalid);
|
Ast *dummy_node_struct = alloc_ast_node(nullptr, Ast_Invalid);
|
||||||
Ast *dummy_node_soa = alloc_ast_node(nullptr, Ast_Invalid);
|
Ast *dummy_node_soa = alloc_ast_node(nullptr, Ast_Invalid);
|
||||||
Scope *s = create_scope(builtin_pkg->scope);
|
Scope *s = create_scope(c->info, builtin_pkg->scope);
|
||||||
|
|
||||||
auto fields = array_make<Entity *>(permanent_allocator(), 0, types.count);
|
auto fields = array_make<Entity *>(permanent_allocator(), 0, types.count);
|
||||||
for_array(i, types) {
|
for_array(i, types) {
|
||||||
@@ -1917,7 +1917,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
soa_struct->Struct.soa_elem = elem;
|
soa_struct->Struct.soa_elem = elem;
|
||||||
soa_struct->Struct.soa_count = count;
|
soa_struct->Struct.soa_count = count;
|
||||||
|
|
||||||
scope = create_scope(c->scope);
|
scope = create_scope(c->info, c->scope);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
|
|
||||||
String params_xyzw[4] = {
|
String params_xyzw[4] = {
|
||||||
@@ -1950,7 +1950,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
|||||||
soa_struct->Struct.soa_elem = elem;
|
soa_struct->Struct.soa_elem = elem;
|
||||||
soa_struct->Struct.soa_count = count;
|
soa_struct->Struct.soa_count = count;
|
||||||
|
|
||||||
scope = create_scope(old_struct->Struct.scope->parent);
|
scope = create_scope(c->info, old_struct->Struct.scope->parent);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
|
|
||||||
for_array(i, old_struct->Struct.fields) {
|
for_array(i, old_struct->Struct.fields) {
|
||||||
|
|||||||
@@ -1276,7 +1276,8 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
|
|||||||
Entity *uvar = using_entities[i].uvar;
|
Entity *uvar = using_entities[i].uvar;
|
||||||
Entity *prev = scope_insert(ctx->scope, uvar);
|
Entity *prev = scope_insert(ctx->scope, uvar);
|
||||||
if (prev != nullptr) {
|
if (prev != nullptr) {
|
||||||
error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string));
|
error(e->token, "Namespace collision while 'using' procedure argument '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string));
|
||||||
|
error_line("%.*s != %.*s\n", LIT(uvar->token.string), LIT(prev->token.string));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,7 +283,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
|
|||||||
CheckerInfo *info = c->info;
|
CheckerInfo *info = c->info;
|
||||||
CheckerContext nctx = *c;
|
CheckerContext nctx = *c;
|
||||||
|
|
||||||
Scope *scope = create_scope(base_entity->scope);
|
Scope *scope = create_scope(c->info, base_entity->scope);
|
||||||
scope->flags |= ScopeFlag_Proc;
|
scope->flags |= ScopeFlag_Proc;
|
||||||
nctx.scope = scope;
|
nctx.scope = scope;
|
||||||
nctx.allow_polymorphic_types = true;
|
nctx.allow_polymorphic_types = true;
|
||||||
@@ -307,11 +307,11 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
|
|||||||
}
|
}
|
||||||
|
|
||||||
gb_mutex_lock(&info->gen_procs_mutex);
|
gb_mutex_lock(&info->gen_procs_mutex);
|
||||||
|
defer (gb_mutex_unlock(&info->gen_procs_mutex));
|
||||||
auto *found_gen_procs = map_get(&info->gen_procs, hash_pointer(base_entity->identifier));
|
auto *found_gen_procs = map_get(&info->gen_procs, hash_pointer(base_entity->identifier));
|
||||||
gb_mutex_unlock(&info->gen_procs_mutex);
|
|
||||||
if (found_gen_procs) {
|
if (found_gen_procs) {
|
||||||
gb_mutex_lock(&info->gen_procs_mutex);
|
// gb_mutex_lock(&info->gen_procs_mutex);
|
||||||
defer (gb_mutex_unlock(&info->gen_procs_mutex));
|
// defer (gb_mutex_unlock(&info->gen_procs_mutex));
|
||||||
|
|
||||||
auto procs = *found_gen_procs;
|
auto procs = *found_gen_procs;
|
||||||
for_array(i, procs) {
|
for_array(i, procs) {
|
||||||
@@ -349,8 +349,8 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (found_gen_procs) {
|
if (found_gen_procs) {
|
||||||
gb_mutex_lock(&info->gen_procs_mutex);
|
// gb_mutex_lock(&info->gen_procs_mutex);
|
||||||
defer (gb_mutex_unlock(&info->gen_procs_mutex));
|
// defer (gb_mutex_unlock(&info->gen_procs_mutex));
|
||||||
|
|
||||||
auto procs = *found_gen_procs;
|
auto procs = *found_gen_procs;
|
||||||
for_array(i, procs) {
|
for_array(i, procs) {
|
||||||
@@ -421,7 +421,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
|
|||||||
proc_info->generated_from_polymorphic = true;
|
proc_info->generated_from_polymorphic = true;
|
||||||
proc_info->poly_def_node = poly_def_node;
|
proc_info->poly_def_node = poly_def_node;
|
||||||
|
|
||||||
gb_mutex_lock(&info->gen_procs_mutex);
|
// gb_mutex_lock(&info->gen_procs_mutex);
|
||||||
if (found_gen_procs) {
|
if (found_gen_procs) {
|
||||||
array_add(found_gen_procs, entity);
|
array_add(found_gen_procs, entity);
|
||||||
} else {
|
} else {
|
||||||
@@ -429,7 +429,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
|
|||||||
array_add(&array, entity);
|
array_add(&array, entity);
|
||||||
map_set(&info->gen_procs, hash_pointer(base_entity->identifier), array);
|
map_set(&info->gen_procs, hash_pointer(base_entity->identifier), array);
|
||||||
}
|
}
|
||||||
gb_mutex_unlock(&info->gen_procs_mutex);
|
// gb_mutex_unlock(&info->gen_procs_mutex);
|
||||||
|
|
||||||
GB_ASSERT(entity != nullptr);
|
GB_ASSERT(entity != nullptr);
|
||||||
|
|
||||||
@@ -1738,7 +1738,12 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
|
|||||||
|
|
||||||
void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
|
void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
|
||||||
GB_ASSERT(o->mode == Addressing_Constant);
|
GB_ASSERT(o->mode == Addressing_Constant);
|
||||||
if (!is_type_constant_type(type) || !check_representable_as_constant(ctx, o->value, type, &o->value)) {
|
ExactValue out_value = o->value;
|
||||||
|
if (is_type_constant_type(type) && check_representable_as_constant(ctx, o->value, type, &out_value)) {
|
||||||
|
o->value = out_value;
|
||||||
|
} else {
|
||||||
|
o->value = out_value;
|
||||||
|
|
||||||
gbString a = expr_to_string(o->expr);
|
gbString a = expr_to_string(o->expr);
|
||||||
gbString b = type_to_string(type);
|
gbString b = type_to_string(type);
|
||||||
gbString c = type_to_string(o->type);
|
gbString c = type_to_string(o->type);
|
||||||
@@ -1753,7 +1758,13 @@ void check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
|
|||||||
if (!is_type_integer(o->type) && is_type_integer(type)) {
|
if (!is_type_integer(o->type) && is_type_integer(type)) {
|
||||||
error(o->expr, "'%s' truncated to '%s'", a, b);
|
error(o->expr, "'%s' truncated to '%s'", a, b);
|
||||||
} else {
|
} else {
|
||||||
error(o->expr, "Cannot convert '%s' to '%s' from '%s", a, b, c);
|
#if 0
|
||||||
|
gb_printf_err("AddressingMode, %d\n", o->mode);
|
||||||
|
gb_printf_err("ExactValueKind, %d\n", o->value.kind);
|
||||||
|
bool ok = check_representable_as_constant(ctx, o->value, type, &out_value);
|
||||||
|
gb_printf_err("ok, %d\n", ok);
|
||||||
|
#endif
|
||||||
|
error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s", a, b, c);
|
||||||
check_assignment_error_suggestion(ctx, o, type);
|
check_assignment_error_suggestion(ctx, o, type);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -1797,10 +1808,6 @@ bool check_is_not_addressable(CheckerContext *c, Operand *o) {
|
|||||||
void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
|
void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
|
||||||
switch (op.kind) {
|
switch (op.kind) {
|
||||||
case Token_And: { // Pointer address
|
case Token_And: { // Pointer address
|
||||||
if (node->kind == Ast_TypeAssertion) {
|
|
||||||
gb_printf_err("%s\n", expr_to_string(node));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (check_is_not_addressable(c, o)) {
|
if (check_is_not_addressable(c, o)) {
|
||||||
if (ast_node_expect(node, Ast_UnaryExpr)) {
|
if (ast_node_expect(node, Ast_UnaryExpr)) {
|
||||||
ast_node(ue, UnaryExpr, node);
|
ast_node(ue, UnaryExpr, node);
|
||||||
@@ -2225,7 +2232,8 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
|
|||||||
|
|
||||||
TokenPos pos = ast_token(x->expr).pos;
|
TokenPos pos = ast_token(x->expr).pos;
|
||||||
if (x_is_untyped) {
|
if (x_is_untyped) {
|
||||||
ExprInfo *info = check_get_expr_info(&c->checker->info, x->expr);
|
gb_mutex_lock(&c->info->untyped_mutex);
|
||||||
|
ExprInfo *info = check_get_expr_info(c->info, x->expr);
|
||||||
if (info != nullptr) {
|
if (info != nullptr) {
|
||||||
info->is_lhs = true;
|
info->is_lhs = true;
|
||||||
}
|
}
|
||||||
@@ -2234,6 +2242,8 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
|
|||||||
x->type = type_hint;
|
x->type = type_hint;
|
||||||
}
|
}
|
||||||
// x->value = x_val;
|
// x->value = x_val;
|
||||||
|
|
||||||
|
gb_mutex_unlock(&c->info->untyped_mutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2519,6 +2529,25 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Type *dst_bt = base_type(t);
|
||||||
|
if (dst_bt == nullptr || dst_bt == t_invalid) {
|
||||||
|
GB_ASSERT(global_error_collector.count != 0);
|
||||||
|
|
||||||
|
o->mode = Addressing_Invalid;
|
||||||
|
o->expr = node;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type *src_bt = base_type(o->type);
|
||||||
|
if (src_bt == nullptr || src_bt == t_invalid) {
|
||||||
|
// NOTE(bill): this should be an error
|
||||||
|
GB_ASSERT(global_error_collector.count != 0);
|
||||||
|
o->mode = Addressing_Value;
|
||||||
|
o->expr = node;
|
||||||
|
o->type = t;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
i64 srcz = type_size_of(o->type);
|
i64 srcz = type_size_of(o->type);
|
||||||
i64 dstz = type_size_of(t);
|
i64 dstz = type_size_of(t);
|
||||||
if (srcz != dstz) {
|
if (srcz != dstz) {
|
||||||
@@ -2899,11 +2928,13 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
|
|||||||
|
|
||||||
void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
|
void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
|
||||||
GB_ASSERT(e != nullptr);
|
GB_ASSERT(e != nullptr);
|
||||||
ExprInfo *old = check_get_expr_info(&c->checker->info, e);
|
gb_mutex_lock(&c->info->untyped_mutex);
|
||||||
|
defer (gb_mutex_unlock(&c->info->untyped_mutex));
|
||||||
|
ExprInfo *old = check_get_expr_info(c->info, e);
|
||||||
if (old == nullptr) {
|
if (old == nullptr) {
|
||||||
if (type != nullptr && type != t_invalid) {
|
if (type != nullptr && type != t_invalid) {
|
||||||
if (e->tav.type == nullptr || e->tav.type == t_invalid) {
|
if (e->tav.type == nullptr || e->tav.type == t_invalid) {
|
||||||
add_type_and_value(&c->checker->info, e, e->tav.mode, type ? type : e->tav.type, e->tav.value);
|
add_type_and_value(c->info, e, e->tav.mode, type ? type : e->tav.type, e->tav.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -2966,7 +2997,7 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We need to remove it and then give it a new one
|
// We need to remove it and then give it a new one
|
||||||
check_remove_expr_info(&c->checker->info, e);
|
map_remove(&c->info->untyped, hash_node(e));
|
||||||
|
|
||||||
if (old->is_lhs && !is_type_integer(type)) {
|
if (old->is_lhs && !is_type_integer(type)) {
|
||||||
gbString expr_str = expr_to_string(e);
|
gbString expr_str = expr_to_string(e);
|
||||||
@@ -2977,11 +3008,14 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_type_and_value(&c->checker->info, e, old->mode, type, old->value);
|
add_type_and_value(c->info, e, old->mode, type, old->value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_expr_value(CheckerContext *c, Ast *e, ExactValue value) {
|
void update_expr_value(CheckerContext *c, Ast *e, ExactValue value) {
|
||||||
ExprInfo *found = check_get_expr_info(&c->checker->info, e);
|
ExprInfo *found = nullptr;
|
||||||
|
gb_mutex_lock(&c->info->untyped_mutex);
|
||||||
|
found = check_get_expr_info(c->info, e);
|
||||||
|
gb_mutex_unlock(&c->info->untyped_mutex);
|
||||||
if (found) {
|
if (found) {
|
||||||
found->value = value;
|
found->value = value;
|
||||||
}
|
}
|
||||||
@@ -3001,7 +3035,7 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
error(operand->expr, "Cannot convert '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text);
|
error(operand->expr, "Cannot convert untyped value '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text);
|
||||||
|
|
||||||
gb_string_free(from_type_str);
|
gb_string_free(from_type_str);
|
||||||
gb_string_free(type_str);
|
gb_string_free(type_str);
|
||||||
@@ -5682,7 +5716,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
|
|||||||
operand->builtin_id = BuiltinProc_DIRECTIVE;
|
operand->builtin_id = BuiltinProc_DIRECTIVE;
|
||||||
operand->expr = proc;
|
operand->expr = proc;
|
||||||
operand->type = t_invalid;
|
operand->type = t_invalid;
|
||||||
add_type_and_value(&c->checker->info, proc, operand->mode, operand->type, operand->value);
|
add_type_and_value(c->info, proc, operand->mode, operand->type, operand->value);
|
||||||
} else {
|
} else {
|
||||||
GB_PANIC("Unhandled #%.*s", LIT(name));
|
GB_PANIC("Unhandled #%.*s", LIT(name));
|
||||||
}
|
}
|
||||||
@@ -5757,7 +5791,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
|
|||||||
GB_ASSERT(ot->kind == Type_Named);
|
GB_ASSERT(ot->kind == Type_Named);
|
||||||
Entity *e = ot->Named.type_name;
|
Entity *e = ot->Named.type_name;
|
||||||
add_entity_use(c, ident, e);
|
add_entity_use(c, ident, e);
|
||||||
add_type_and_value(&c->checker->info, call, Addressing_Type, ot, empty_exact_value);
|
add_type_and_value(c->info, call, Addressing_Type, ot, empty_exact_value);
|
||||||
} else {
|
} else {
|
||||||
operand->mode = Addressing_Invalid;
|
operand->mode = Addressing_Invalid;
|
||||||
operand->type = t_invalid;
|
operand->type = t_invalid;
|
||||||
@@ -6153,8 +6187,8 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_type_and_value(&c->checker->info, ie->left, x->mode, x->type, x->value);
|
add_type_and_value(c->info, ie->left, x->mode, x->type, x->value);
|
||||||
add_type_and_value(&c->checker->info, ie->right, y->mode, y->type, y->value);
|
add_type_and_value(c->info, ie->right, y->mode, y->type, y->value);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -6284,7 +6318,7 @@ void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_,
|
|||||||
Type *pt = base_type(type_of_expr(expr->CallExpr.proc));
|
Type *pt = base_type(type_of_expr(expr->CallExpr.proc));
|
||||||
if (is_type_proc(pt)) {
|
if (is_type_proc(pt)) {
|
||||||
Type *tuple = pt->Proc.results;
|
Type *tuple = pt->Proc.results;
|
||||||
add_type_and_value(&c->checker->info, x->expr, x->mode, tuple, x->value);
|
add_type_and_value(c->info, x->expr, x->mode, tuple, x->value);
|
||||||
|
|
||||||
if (pt->Proc.result_count >= 2) {
|
if (pt->Proc.result_count >= 2) {
|
||||||
if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type;
|
if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type;
|
||||||
@@ -6297,7 +6331,7 @@ void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_,
|
|||||||
|
|
||||||
Type *tuple = make_optional_ok_type(x->type);
|
Type *tuple = make_optional_ok_type(x->type);
|
||||||
if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type;
|
if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type;
|
||||||
add_type_and_value(&c->checker->info, x->expr, x->mode, tuple, x->value);
|
add_type_and_value(c->info, x->expr, x->mode, tuple, x->value);
|
||||||
x->type = tuple;
|
x->type = tuple;
|
||||||
GB_ASSERT(is_type_tuple(type_of_expr(x->expr)));
|
GB_ASSERT(is_type_tuple(type_of_expr(x->expr)));
|
||||||
}
|
}
|
||||||
@@ -8163,7 +8197,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
|||||||
error(x.expr, "Expected a constant string for the inline asm constraints parameter");
|
error(x.expr, "Expected a constant string for the inline asm constraints parameter");
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope *scope = create_scope(c->scope);
|
Scope *scope = create_scope(c->info, c->scope);
|
||||||
scope->flags |= ScopeFlag_Proc;
|
scope->flags |= ScopeFlag_Proc;
|
||||||
|
|
||||||
Type *params = alloc_type_tuple();
|
Type *params = alloc_type_tuple();
|
||||||
@@ -8221,9 +8255,9 @@ ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hi
|
|||||||
gb_string_free(xs);
|
gb_string_free(xs);
|
||||||
}
|
}
|
||||||
if (o->type != nullptr && is_type_untyped(o->type)) {
|
if (o->type != nullptr && is_type_untyped(o->type)) {
|
||||||
add_untyped(&c->checker->info, node, false, o->mode, o->type, o->value);
|
add_untyped(c->info, node, false, o->mode, o->type, o->value);
|
||||||
}
|
}
|
||||||
add_type_and_value(&c->checker->info, node, o->mode, o->type, o->value);
|
add_type_and_value(c->info, node, o->mode, o->type, o->value);
|
||||||
return kind;
|
return kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -587,7 +587,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
|
|||||||
Entity *found = scope_insert(ctx->scope, f);
|
Entity *found = scope_insert(ctx->scope, f);
|
||||||
if (found != nullptr) {
|
if (found != nullptr) {
|
||||||
gbString expr_str = expr_to_string(expr);
|
gbString expr_str = expr_to_string(expr);
|
||||||
error(us->token, "Namespace collision while 'using' '%s' of: %.*s", expr_str, LIT(found->token.string));
|
error(us->token, "Namespace collision while 'using' enum '%s' of: %.*s", expr_str, LIT(found->token.string));
|
||||||
gb_string_free(expr_str);
|
gb_string_free(expr_str);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -611,7 +611,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
|
|||||||
if (found != nullptr) {
|
if (found != nullptr) {
|
||||||
gbString expr_str = expr_to_string(expr);
|
gbString expr_str = expr_to_string(expr);
|
||||||
error(us->token,
|
error(us->token,
|
||||||
"Namespace collision while 'using' '%s' of: %.*s\n"
|
"Namespace collision while 'using' import name '%s' of: %.*s\n"
|
||||||
"\tat %s\n"
|
"\tat %s\n"
|
||||||
"\tat %s",
|
"\tat %s",
|
||||||
expr_str, LIT(found->token.string),
|
expr_str, LIT(found->token.string),
|
||||||
@@ -1103,7 +1103,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
|
|||||||
if (y.mode != Addressing_Constant) {
|
if (y.mode != Addressing_Constant) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
update_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type));
|
||||||
add_constant_switch_case(ctx, &seen, y);
|
add_constant_switch_case(ctx, &seen, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1260,7 +1260,10 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
|
|||||||
param_value.kind = ParameterValue_Constant;
|
param_value.kind = ParameterValue_Constant;
|
||||||
param_value.value = o.value;
|
param_value.value = o.value;
|
||||||
} else {
|
} else {
|
||||||
error(o.expr, "Invalid constant parameter");
|
gbString s = expr_to_string(o.expr);
|
||||||
|
error(o.expr, "Invalid constant parameter, got '%s'", s);
|
||||||
|
// error(o.expr, "Invalid constant parameter, got '%s' %d %d", s, o.mode, o.value.kind);
|
||||||
|
gb_string_free(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2044,7 +2047,7 @@ void init_map_entry_type(Type *type) {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid);
|
Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid);
|
||||||
Scope *s = create_scope(builtin_pkg->scope);
|
Scope *s = create_scope(nullptr, builtin_pkg->scope);
|
||||||
|
|
||||||
auto fields = array_make<Entity *>(permanent_allocator(), 0, 4);
|
auto fields = array_make<Entity *>(permanent_allocator(), 0, 4);
|
||||||
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hash")), t_uintptr, false, cast(i32)fields.count, EntityState_Resolved));
|
array_add(&fields, alloc_entity_field(s, make_token_ident(str_lit("hash")), t_uintptr, false, cast(i32)fields.count, EntityState_Resolved));
|
||||||
@@ -2078,7 +2081,7 @@ void init_map_internal_types(Type *type) {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid);
|
Ast *dummy_node = alloc_ast_node(nullptr, Ast_Invalid);
|
||||||
Scope *s = create_scope(builtin_pkg->scope);
|
Scope *s = create_scope(nullptr, builtin_pkg->scope);
|
||||||
|
|
||||||
Type *hashes_type = alloc_type_slice(t_int);
|
Type *hashes_type = alloc_type_slice(t_int);
|
||||||
Type *entries_type = alloc_type_dynamic_array(type->Map.entry_type);
|
Type *entries_type = alloc_type_dynamic_array(type->Map.entry_type);
|
||||||
@@ -2211,7 +2214,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
|
|||||||
soa_struct->Struct.soa_count = 0;
|
soa_struct->Struct.soa_count = 0;
|
||||||
soa_struct->Struct.is_polymorphic = true;
|
soa_struct->Struct.is_polymorphic = true;
|
||||||
|
|
||||||
scope = create_scope(ctx->scope);
|
scope = create_scope(ctx->info, ctx->scope);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
} else if (is_type_array(elem)) {
|
} else if (is_type_array(elem)) {
|
||||||
Type *old_array = base_type(elem);
|
Type *old_array = base_type(elem);
|
||||||
@@ -2225,7 +2228,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
|
|||||||
soa_struct->Struct.soa_elem = elem;
|
soa_struct->Struct.soa_elem = elem;
|
||||||
soa_struct->Struct.soa_count = count;
|
soa_struct->Struct.soa_count = count;
|
||||||
|
|
||||||
scope = create_scope(ctx->scope);
|
scope = create_scope(ctx->info, ctx->scope);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
|
|
||||||
String params_xyzw[4] = {
|
String params_xyzw[4] = {
|
||||||
@@ -2267,7 +2270,7 @@ Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_expr, Ast *el
|
|||||||
soa_struct->Struct.soa_elem = elem;
|
soa_struct->Struct.soa_elem = elem;
|
||||||
soa_struct->Struct.soa_count = count;
|
soa_struct->Struct.soa_count = count;
|
||||||
|
|
||||||
scope = create_scope(old_struct->Struct.scope->parent);
|
scope = create_scope(ctx->info, old_struct->Struct.scope->parent);
|
||||||
soa_struct->Struct.scope = scope;
|
soa_struct->Struct.scope = scope;
|
||||||
|
|
||||||
for_array(i, old_struct->Struct.fields) {
|
for_array(i, old_struct->Struct.fields) {
|
||||||
|
|||||||
171
src/checker.cpp
171
src/checker.cpp
@@ -224,7 +224,7 @@ bool decl_info_has_init(DeclInfo *d) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
Scope *create_scope(Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
|
Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
|
||||||
Scope *s = gb_alloc_item(permanent_allocator(), Scope);
|
Scope *s = gb_alloc_item(permanent_allocator(), Scope);
|
||||||
s->parent = parent;
|
s->parent = parent;
|
||||||
string_map_init(&s->elements, heap_allocator(), init_elements_capacity);
|
string_map_init(&s->elements, heap_allocator(), init_elements_capacity);
|
||||||
@@ -234,7 +234,9 @@ Scope *create_scope(Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CA
|
|||||||
s->delayed_directives.allocator = heap_allocator();
|
s->delayed_directives.allocator = heap_allocator();
|
||||||
|
|
||||||
if (parent != nullptr && parent != builtin_pkg->scope) {
|
if (parent != nullptr && parent != builtin_pkg->scope) {
|
||||||
|
if (info) gb_mutex_lock(&info->scope_mutex);
|
||||||
DLIST_APPEND(parent->first_child, parent->last_child, s);
|
DLIST_APPEND(parent->first_child, parent->last_child, s);
|
||||||
|
if (info) gb_mutex_unlock(&info->scope_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parent != nullptr && parent->flags & ScopeFlag_ContextDefined) {
|
if (parent != nullptr && parent->flags & ScopeFlag_ContextDefined) {
|
||||||
@@ -244,12 +246,12 @@ Scope *create_scope(Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CA
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
Scope *create_scope_from_file(AstFile *f) {
|
Scope *create_scope_from_file(CheckerInfo *info, AstFile *f) {
|
||||||
GB_ASSERT(f != nullptr);
|
GB_ASSERT(f != nullptr);
|
||||||
GB_ASSERT(f->pkg != nullptr);
|
GB_ASSERT(f->pkg != nullptr);
|
||||||
GB_ASSERT(f->pkg->scope != nullptr);
|
GB_ASSERT(f->pkg->scope != nullptr);
|
||||||
|
|
||||||
Scope *s = create_scope(f->pkg->scope);
|
Scope *s = create_scope(info, f->pkg->scope);
|
||||||
|
|
||||||
array_reserve(&s->delayed_imports, f->imports.count);
|
array_reserve(&s->delayed_imports, f->imports.count);
|
||||||
array_reserve(&s->delayed_directives, f->directive_count);
|
array_reserve(&s->delayed_directives, f->directive_count);
|
||||||
@@ -269,7 +271,7 @@ Scope *create_scope_from_package(CheckerContext *c, AstPackage *pkg) {
|
|||||||
decl_count += pkg->files[i]->decls.count;
|
decl_count += pkg->files[i]->decls.count;
|
||||||
}
|
}
|
||||||
isize init_elements_capacity = 2*decl_count;
|
isize init_elements_capacity = 2*decl_count;
|
||||||
Scope *s = create_scope(builtin_pkg->scope, init_elements_capacity);
|
Scope *s = create_scope(c->info, builtin_pkg->scope, init_elements_capacity);
|
||||||
|
|
||||||
s->flags |= ScopeFlag_Pkg;
|
s->flags |= ScopeFlag_Pkg;
|
||||||
s->pkg = pkg;
|
s->pkg = pkg;
|
||||||
@@ -329,7 +331,7 @@ void check_open_scope(CheckerContext *c, Ast *node) {
|
|||||||
GB_ASSERT(node->kind == Ast_Invalid ||
|
GB_ASSERT(node->kind == Ast_Invalid ||
|
||||||
is_ast_stmt(node) ||
|
is_ast_stmt(node) ||
|
||||||
is_ast_type(node));
|
is_ast_type(node));
|
||||||
Scope *scope = create_scope(c->scope);
|
Scope *scope = create_scope(c->info, c->scope);
|
||||||
add_scope(c, node, scope);
|
add_scope(c, node, scope);
|
||||||
switch (node->kind) {
|
switch (node->kind) {
|
||||||
case Ast_ProcType:
|
case Ast_ProcType:
|
||||||
@@ -715,7 +717,7 @@ AstPackage *create_builtin_package(char const *name) {
|
|||||||
pkg->name = make_string_c(name);
|
pkg->name = make_string_c(name);
|
||||||
pkg->kind = Package_Normal;
|
pkg->kind = Package_Normal;
|
||||||
|
|
||||||
pkg->scope = create_scope(nullptr);
|
pkg->scope = create_scope(nullptr, nullptr);
|
||||||
pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin;
|
pkg->scope->flags |= ScopeFlag_Pkg | ScopeFlag_Global | ScopeFlag_Builtin;
|
||||||
pkg->scope->pkg = pkg;
|
pkg->scope->pkg = pkg;
|
||||||
return pkg;
|
return pkg;
|
||||||
@@ -860,6 +862,7 @@ void init_checker_info(CheckerInfo *i) {
|
|||||||
gb_mutex_init(&i->identifier_uses_mutex);
|
gb_mutex_init(&i->identifier_uses_mutex);
|
||||||
gb_mutex_init(&i->entity_mutex);
|
gb_mutex_init(&i->entity_mutex);
|
||||||
gb_mutex_init(&i->foreign_mutex);
|
gb_mutex_init(&i->foreign_mutex);
|
||||||
|
gb_mutex_init(&i->scope_mutex);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -887,6 +890,7 @@ void destroy_checker_info(CheckerInfo *i) {
|
|||||||
gb_mutex_destroy(&i->identifier_uses_mutex);
|
gb_mutex_destroy(&i->identifier_uses_mutex);
|
||||||
gb_mutex_destroy(&i->entity_mutex);
|
gb_mutex_destroy(&i->entity_mutex);
|
||||||
gb_mutex_destroy(&i->foreign_mutex);
|
gb_mutex_destroy(&i->foreign_mutex);
|
||||||
|
gb_mutex_destroy(&i->scope_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
CheckerContext make_checker_context(Checker *c) {
|
CheckerContext make_checker_context(Checker *c) {
|
||||||
@@ -902,6 +906,10 @@ CheckerContext make_checker_context(Checker *c) {
|
|||||||
ctx.poly_level = 0;
|
ctx.poly_level = 0;
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
void destroy_checker_context(CheckerContext *ctx) {
|
||||||
|
destroy_checker_type_path(ctx->type_path);
|
||||||
|
destroy_checker_poly_path(ctx->poly_path);
|
||||||
|
}
|
||||||
|
|
||||||
void add_curr_ast_file(CheckerContext *ctx, AstFile *file) {
|
void add_curr_ast_file(CheckerContext *ctx, AstFile *file) {
|
||||||
if (file != nullptr) {
|
if (file != nullptr) {
|
||||||
@@ -917,30 +925,13 @@ void reset_checker_context(CheckerContext *ctx, AstFile *file) {
|
|||||||
if (ctx == nullptr) {
|
if (ctx == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto checker = ctx->checker;
|
destroy_checker_context(ctx);
|
||||||
auto info = ctx->info;
|
*ctx = make_checker_context(ctx->checker);
|
||||||
auto type_path = ctx->type_path;
|
|
||||||
auto poly_path = ctx->poly_path;
|
|
||||||
array_clear(type_path);
|
|
||||||
array_clear(poly_path);
|
|
||||||
|
|
||||||
gb_zero_item(ctx);
|
|
||||||
ctx->checker = checker;
|
|
||||||
ctx->info = info;
|
|
||||||
ctx->type_path = type_path;
|
|
||||||
ctx->poly_path = poly_path;
|
|
||||||
ctx->scope = builtin_pkg->scope;
|
|
||||||
ctx->pkg = builtin_pkg;
|
|
||||||
|
|
||||||
add_curr_ast_file(ctx, file);
|
add_curr_ast_file(ctx, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void destroy_checker_context(CheckerContext *ctx) {
|
|
||||||
destroy_checker_type_path(ctx->type_path);
|
|
||||||
destroy_checker_poly_path(ctx->poly_path);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool init_checker(Checker *c, Parser *parser) {
|
bool init_checker(Checker *c, Parser *parser) {
|
||||||
c->parser = parser;
|
c->parser = parser;
|
||||||
@@ -1051,21 +1042,13 @@ Scope *scope_of_node(Ast *node) {
|
|||||||
return node->scope;
|
return node->scope;
|
||||||
}
|
}
|
||||||
ExprInfo *check_get_expr_info(CheckerInfo *i, Ast *expr) {
|
ExprInfo *check_get_expr_info(CheckerInfo *i, Ast *expr) {
|
||||||
gb_mutex_lock(&i->untyped_mutex);
|
|
||||||
ExprInfo *res = nullptr;
|
ExprInfo *res = nullptr;
|
||||||
ExprInfo **found = map_get(&i->untyped, hash_node(expr));
|
ExprInfo **found = map_get(&i->untyped, hash_node(expr));
|
||||||
if (found) {
|
if (found) {
|
||||||
res = *found;
|
res = *found;
|
||||||
}
|
}
|
||||||
gb_mutex_unlock(&i->untyped_mutex);
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
void check_remove_expr_info(CheckerInfo *i, Ast *expr) {
|
|
||||||
gb_mutex_lock(&i->untyped_mutex);
|
|
||||||
map_remove(&i->untyped, hash_node(expr));
|
|
||||||
gb_mutex_unlock(&i->untyped_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) {
|
isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) {
|
||||||
@@ -1527,6 +1510,7 @@ void check_procedure_later(Checker *c, ProcInfo *info) {
|
|||||||
GB_ASSERT(info->decl != nullptr);
|
GB_ASSERT(info->decl != nullptr);
|
||||||
|
|
||||||
mpmc_enqueue(&c->procs_to_check_queue, info);
|
mpmc_enqueue(&c->procs_to_check_queue, info);
|
||||||
|
gb_semaphore_post(&c->procs_to_check_semaphore, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, Ast *body, u64 tags) {
|
void check_procedure_later(Checker *c, AstFile *file, Token token, DeclInfo *decl, Type *type, Ast *body, u64 tags) {
|
||||||
@@ -4281,6 +4265,9 @@ void check_proc_info(Checker *c, ProcInfo *pi) {
|
|||||||
if (pi->type == nullptr) {
|
if (pi->type == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (pi->decl->proc_checked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
CheckerContext ctx = make_checker_context(c);
|
CheckerContext ctx = make_checker_context(c);
|
||||||
defer (destroy_checker_context(&ctx));
|
defer (destroy_checker_context(&ctx));
|
||||||
@@ -4316,11 +4303,16 @@ void check_proc_info(Checker *c, ProcInfo *pi) {
|
|||||||
ctx.state_flags |= StateFlag_no_bounds_check;
|
ctx.state_flags |= StateFlag_no_bounds_check;
|
||||||
ctx.state_flags &= ~StateFlag_bounds_check;
|
ctx.state_flags &= ~StateFlag_bounds_check;
|
||||||
}
|
}
|
||||||
|
if (pi->body != nullptr && pi->decl->entity != nullptr) {
|
||||||
|
GB_ASSERT((pi->decl->entity->flags & EntityFlag_ProcBodyChecked) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
check_proc_body(&ctx, pi->token, pi->decl, pi->type, pi->body);
|
check_proc_body(&ctx, pi->token, pi->decl, pi->type, pi->body);
|
||||||
if (pi->body != nullptr && pi->decl->entity != nullptr) {
|
if (pi->body != nullptr && pi->decl->entity != nullptr) {
|
||||||
pi->decl->entity->flags |= EntityFlag_ProcBodyChecked;
|
pi->decl->entity->flags |= EntityFlag_ProcBodyChecked;
|
||||||
}
|
}
|
||||||
|
pi->decl->proc_checked = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GB_STATIC_ASSERT(sizeof(isize) == sizeof(void *));
|
GB_STATIC_ASSERT(sizeof(isize) == sizeof(void *));
|
||||||
@@ -4400,23 +4392,114 @@ void check_test_names(Checker *c) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool proc_bodies_is_running;
|
||||||
|
|
||||||
|
GB_THREAD_PROC(thread_proc_body) {
|
||||||
|
Checker *c = cast(Checker *)thread->user_data;
|
||||||
|
auto *q = &c->procs_to_check_queue;
|
||||||
|
ProcInfo *pi = nullptr;
|
||||||
|
|
||||||
|
while (proc_bodies_is_running) {
|
||||||
|
gb_semaphore_wait(&c->procs_to_check_semaphore);
|
||||||
|
|
||||||
|
if (mpmc_dequeue(q, &pi)) {
|
||||||
|
if (pi->decl->parent && pi->decl->parent->entity) {
|
||||||
|
Entity *parent = pi->decl->parent->entity;
|
||||||
|
// NOTE(bill): Only check a nested procedure if its parent's body has been checked first
|
||||||
|
// This is prevent any possible race conditions in evaluation when multithreaded
|
||||||
|
// NOTE(bill): In single threaded mode, this should never happen
|
||||||
|
if (parent->kind == Entity_Procedure && (parent->flags & EntityFlag_ProcBodyChecked) == 0) {
|
||||||
|
mpmc_enqueue(q, pi);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check_proc_info(c, pi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_semaphore_release(&c->procs_to_check_semaphore);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void check_procedure_bodies(Checker *c) {
|
void check_procedure_bodies(Checker *c) {
|
||||||
auto *q = &c->procs_to_check_queue;
|
auto *q = &c->procs_to_check_queue;
|
||||||
ProcInfo *pi = nullptr;
|
ProcInfo *pi = nullptr;
|
||||||
|
|
||||||
while (mpmc_dequeue(q, &pi)) {
|
isize thread_count = gb_max(build_context.thread_count, 1);
|
||||||
if (pi->decl->parent && pi->decl->parent->entity) {
|
isize worker_count = thread_count-1; // NOTE(bill): The main thread will also be used for work
|
||||||
Entity *parent = pi->decl->parent->entity;
|
if (!build_context.threaded_checker) {
|
||||||
// NOTE(bill): Only check a nested procedure if its parent's body has been checked first
|
worker_count = 0;
|
||||||
// This is prevent any possible race conditions in evaluation when multithreaded
|
}
|
||||||
// NOTE(bill): In single threaded mode, this should never happen
|
|
||||||
if (parent->kind == Entity_Procedure && (parent->flags & EntityFlag_ProcBodyChecked) == 0) {
|
if (worker_count == 0) {
|
||||||
mpmc_enqueue(q, pi);
|
while (mpmc_dequeue(q, &pi)) {
|
||||||
continue;
|
if (pi->decl->parent && pi->decl->parent->entity) {
|
||||||
|
Entity *parent = pi->decl->parent->entity;
|
||||||
|
// NOTE(bill): Only check a nested procedure if its parent's body has been checked first
|
||||||
|
// This is prevent any possible race conditions in evaluation when multithreaded
|
||||||
|
// NOTE(bill): In single threaded mode, this should never happen
|
||||||
|
if (parent->kind == Entity_Procedure && (parent->flags & EntityFlag_ProcBodyChecked) == 0) {
|
||||||
|
mpmc_enqueue(q, pi);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
check_proc_info(c, pi);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
proc_bodies_is_running = true;
|
||||||
|
|
||||||
|
gbThread threads[64] = {};
|
||||||
|
for (isize i = 0; i < worker_count; i++) {
|
||||||
|
gb_thread_init(threads+i);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (isize i = 0; i < worker_count; i++) {
|
||||||
|
gb_thread_start(threads+i, thread_proc_body, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (q->count.load(std::memory_order_relaxed) > 0) {
|
||||||
|
if (mpmc_dequeue(q, &pi)) {
|
||||||
|
if (pi->decl->parent && pi->decl->parent->entity) {
|
||||||
|
Entity *parent = pi->decl->parent->entity;
|
||||||
|
// NOTE(bill): Only check a nested procedure if its parent's body has been checked first
|
||||||
|
// This is prevent any possible race conditions in evaluation when multithreaded
|
||||||
|
// NOTE(bill): In single threaded mode, this should never happen
|
||||||
|
if (parent->kind == Entity_Procedure && (parent->flags & EntityFlag_ProcBodyChecked) == 0) {
|
||||||
|
mpmc_enqueue(q, pi);
|
||||||
|
|
||||||
|
gb_yield();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check_proc_info(c, pi);
|
||||||
|
}
|
||||||
|
|
||||||
|
gb_yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
proc_bodies_is_running = false;
|
||||||
|
gb_semaphore_post(&c->procs_to_check_semaphore, cast(i32)worker_count);
|
||||||
|
|
||||||
|
gb_yield();
|
||||||
|
|
||||||
|
for (isize i = 0; i < worker_count; i++) {
|
||||||
|
gb_thread_destroy(threads+i);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (mpmc_dequeue(q, &pi)) {
|
||||||
|
if (pi->decl->parent && pi->decl->parent->entity) {
|
||||||
|
Entity *parent = pi->decl->parent->entity;
|
||||||
|
// NOTE(bill): Only check a nested procedure if its parent's body has been checked first
|
||||||
|
// This is prevent any possible race conditions in evaluation when multithreaded
|
||||||
|
// NOTE(bill): In single threaded mode, this should never happen
|
||||||
|
if (parent->kind == Entity_Procedure && (parent->flags & EntityFlag_ProcBodyChecked) == 0) {
|
||||||
|
mpmc_enqueue(q, pi);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check_proc_info(c, pi);
|
||||||
}
|
}
|
||||||
check_proc_info(c, pi);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4457,7 +4540,7 @@ void check_parsed_files(Checker *c) {
|
|||||||
AstFile *f = pkg->files[j];
|
AstFile *f = pkg->files[j];
|
||||||
string_map_set(&c->info.files, f->fullpath, f);
|
string_map_set(&c->info.files, f->fullpath, f);
|
||||||
|
|
||||||
create_scope_from_file(f);
|
create_scope_from_file(nullptr, f);
|
||||||
reset_checker_context(ctx, f);
|
reset_checker_context(ctx, f);
|
||||||
check_collect_entities(ctx, f->decls);
|
check_collect_entities(ctx, f->decls);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ struct DeclInfo {
|
|||||||
Type * gen_proc_type; // Precalculated
|
Type * gen_proc_type; // Precalculated
|
||||||
bool is_using;
|
bool is_using;
|
||||||
bool where_clauses_evaluated;
|
bool where_clauses_evaluated;
|
||||||
|
bool proc_checked;
|
||||||
|
|
||||||
CommentGroup *comment;
|
CommentGroup *comment;
|
||||||
CommentGroup *docs;
|
CommentGroup *docs;
|
||||||
@@ -289,6 +290,7 @@ struct CheckerInfo {
|
|||||||
gbMutex identifier_uses_mutex;
|
gbMutex identifier_uses_mutex;
|
||||||
gbMutex entity_mutex;
|
gbMutex entity_mutex;
|
||||||
gbMutex foreign_mutex;
|
gbMutex foreign_mutex;
|
||||||
|
gbMutex scope_mutex;
|
||||||
|
|
||||||
Map<ExprInfo *> untyped; // Key: Ast * | Expression -> ExprInfo *
|
Map<ExprInfo *> untyped; // Key: Ast * | Expression -> ExprInfo *
|
||||||
// NOTE(bill): This needs to be a map and not on the Ast
|
// NOTE(bill): This needs to be a map and not on the Ast
|
||||||
@@ -398,7 +400,6 @@ Entity *scope_insert (Scope *s, Entity *entity);
|
|||||||
|
|
||||||
|
|
||||||
ExprInfo *check_get_expr_info (CheckerInfo *i, Ast *expr);
|
ExprInfo *check_get_expr_info (CheckerInfo *i, Ast *expr);
|
||||||
void check_remove_expr_info (CheckerInfo *i, Ast *expr);
|
|
||||||
void add_untyped (CheckerInfo *i, Ast *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value);
|
void add_untyped (CheckerInfo *i, Ast *expression, bool lhs, AddressingMode mode, Type *basic_type, ExactValue value);
|
||||||
void add_type_and_value (CheckerInfo *i, Ast *expression, AddressingMode mode, Type *type, ExactValue value);
|
void add_type_and_value (CheckerInfo *i, Ast *expression, AddressingMode mode, Type *type, ExactValue value);
|
||||||
void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity);
|
void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity);
|
||||||
|
|||||||
@@ -27,18 +27,18 @@ Quaternion256 quaternion256_inverse(Quaternion256 x) {
|
|||||||
|
|
||||||
|
|
||||||
enum ExactValueKind {
|
enum ExactValueKind {
|
||||||
ExactValue_Invalid,
|
ExactValue_Invalid = 0,
|
||||||
|
|
||||||
ExactValue_Bool,
|
ExactValue_Bool = 1,
|
||||||
ExactValue_String,
|
ExactValue_String = 2,
|
||||||
ExactValue_Integer,
|
ExactValue_Integer = 3,
|
||||||
ExactValue_Float,
|
ExactValue_Float = 4,
|
||||||
ExactValue_Complex,
|
ExactValue_Complex = 5,
|
||||||
ExactValue_Quaternion,
|
ExactValue_Quaternion = 6,
|
||||||
ExactValue_Pointer,
|
ExactValue_Pointer = 7,
|
||||||
ExactValue_Compound, // TODO(bill): Is this good enough?
|
ExactValue_Compound = 8, // TODO(bill): Is this good enough?
|
||||||
ExactValue_Procedure, // TODO(bill): Is this good enough?
|
ExactValue_Procedure = 9, // TODO(bill): Is this good enough?
|
||||||
ExactValue_Typeid,
|
ExactValue_Typeid = 10,
|
||||||
|
|
||||||
ExactValue_Count,
|
ExactValue_Count,
|
||||||
};
|
};
|
||||||
|
|||||||
12
src/main.cpp
12
src/main.cpp
@@ -600,6 +600,7 @@ enum BuildFlagKind {
|
|||||||
BuildFlag_NoEntryPoint,
|
BuildFlag_NoEntryPoint,
|
||||||
BuildFlag_UseLLD,
|
BuildFlag_UseLLD,
|
||||||
BuildFlag_UseSeparateModules,
|
BuildFlag_UseSeparateModules,
|
||||||
|
BuildFlag_ThreadedChecker,
|
||||||
BuildFlag_Vet,
|
BuildFlag_Vet,
|
||||||
BuildFlag_VetExtra,
|
BuildFlag_VetExtra,
|
||||||
BuildFlag_UseLLVMApi,
|
BuildFlag_UseLLVMApi,
|
||||||
@@ -722,6 +723,7 @@ bool parse_build_flags(Array<String> args) {
|
|||||||
add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
|
add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
|
||||||
add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build);
|
add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build);
|
||||||
add_flag(&build_flags, BuildFlag_UseSeparateModules,str_lit("use-separate-modules"),BuildFlagParam_None, Command__does_build);
|
add_flag(&build_flags, BuildFlag_UseSeparateModules,str_lit("use-separate-modules"),BuildFlagParam_None, Command__does_build);
|
||||||
|
add_flag(&build_flags, BuildFlag_ThreadedChecker, str_lit("threaded-checker"), BuildFlagParam_None, Command__does_check);
|
||||||
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
|
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
|
||||||
add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check);
|
add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check);
|
||||||
add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build);
|
add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build);
|
||||||
@@ -1206,6 +1208,10 @@ bool parse_build_flags(Array<String> args) {
|
|||||||
build_context.use_separate_modules = true;
|
build_context.use_separate_modules = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case BuildFlag_ThreadedChecker:
|
||||||
|
build_context.threaded_checker = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case BuildFlag_Vet:
|
case BuildFlag_Vet:
|
||||||
build_context.vet = true;
|
build_context.vet = true;
|
||||||
break;
|
break;
|
||||||
@@ -1746,6 +1752,11 @@ void print_show_help(String const arg0, String const &command) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (check) {
|
if (check) {
|
||||||
|
print_usage_line(1, "-threaded-checker");
|
||||||
|
print_usage_line(1, "[EXPERIMENTAL]");
|
||||||
|
print_usage_line(2, "Multithread the semantic checker stage");
|
||||||
|
print_usage_line(0, "");
|
||||||
|
|
||||||
print_usage_line(1, "-vet");
|
print_usage_line(1, "-vet");
|
||||||
print_usage_line(2, "Do extra checks on the code");
|
print_usage_line(2, "Do extra checks on the code");
|
||||||
print_usage_line(2, "Extra checks include:");
|
print_usage_line(2, "Extra checks include:");
|
||||||
@@ -1960,6 +1971,7 @@ int main(int arg_count, char const **arg_ptr) {
|
|||||||
init_global_error_collector();
|
init_global_error_collector();
|
||||||
init_keyword_hash_table();
|
init_keyword_hash_table();
|
||||||
global_big_int_init();
|
global_big_int_init();
|
||||||
|
init_type_mutex();
|
||||||
|
|
||||||
if (!check_env()) {
|
if (!check_env()) {
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -661,6 +661,8 @@ gb_global Type *t_map_header = nullptr;
|
|||||||
gb_global Type *t_equal_proc = nullptr;
|
gb_global Type *t_equal_proc = nullptr;
|
||||||
gb_global Type *t_hasher_proc = nullptr;
|
gb_global Type *t_hasher_proc = nullptr;
|
||||||
|
|
||||||
|
gb_global gbMutex g_type_mutex;
|
||||||
|
|
||||||
|
|
||||||
i64 type_size_of (Type *t);
|
i64 type_size_of (Type *t);
|
||||||
i64 type_align_of (Type *t);
|
i64 type_align_of (Type *t);
|
||||||
@@ -674,6 +676,10 @@ bool is_type_pointer(Type *t);
|
|||||||
bool is_type_slice(Type *t);
|
bool is_type_slice(Type *t);
|
||||||
bool is_type_integer(Type *t);
|
bool is_type_integer(Type *t);
|
||||||
|
|
||||||
|
void init_type_mutex(void) {
|
||||||
|
gb_mutex_init(&g_type_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t) {
|
bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t) {
|
||||||
if (ptr_set_exists(s, t)) {
|
if (ptr_set_exists(s, t)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -2727,7 +2733,7 @@ void type_path_print_illegal_cycle(TypePath *tp, isize start_index) {
|
|||||||
GB_ASSERT(start_index < tp->path.count);
|
GB_ASSERT(start_index < tp->path.count);
|
||||||
Entity *e = tp->path[start_index];
|
Entity *e = tp->path[start_index];
|
||||||
GB_ASSERT(e != nullptr);
|
GB_ASSERT(e != nullptr);
|
||||||
error(e->token, "Illegal declaration cycle of `%.*s`", LIT(e->token.string));
|
error(e->token, "Illegal type declaration cycle of `%.*s`", LIT(e->token.string));
|
||||||
// NOTE(bill): Print cycle, if it's deep enough
|
// NOTE(bill): Print cycle, if it's deep enough
|
||||||
for (isize j = start_index; j < tp->path.count; j++) {
|
for (isize j = start_index; j < tp->path.count; j++) {
|
||||||
Entity *e = tp->path[j];
|
Entity *e = tp->path[j];
|
||||||
@@ -2844,6 +2850,8 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
|
|||||||
if (t->failure) {
|
if (t->failure) {
|
||||||
return FAILURE_ALIGNMENT;
|
return FAILURE_ALIGNMENT;
|
||||||
}
|
}
|
||||||
|
gb_mutex_lock(&g_type_mutex);
|
||||||
|
defer (gb_mutex_unlock(&g_type_mutex));
|
||||||
|
|
||||||
t = base_type(t);
|
t = base_type(t);
|
||||||
|
|
||||||
@@ -3038,6 +3046,9 @@ Array<i64> type_set_offsets_of(Array<Entity *> const &fields, bool is_packed, bo
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool type_set_offsets(Type *t) {
|
bool type_set_offsets(Type *t) {
|
||||||
|
gb_mutex_lock(&g_type_mutex);
|
||||||
|
defer (gb_mutex_unlock(&g_type_mutex));
|
||||||
|
|
||||||
t = base_type(t);
|
t = base_type(t);
|
||||||
if (t->kind == Type_Struct) {
|
if (t->kind == Type_Struct) {
|
||||||
if (!t->Struct.are_offsets_set) {
|
if (!t->Struct.are_offsets_set) {
|
||||||
@@ -3066,6 +3077,9 @@ i64 type_size_of_internal(Type *t, TypePath *path) {
|
|||||||
if (t->failure) {
|
if (t->failure) {
|
||||||
return FAILURE_SIZE;
|
return FAILURE_SIZE;
|
||||||
}
|
}
|
||||||
|
gb_mutex_lock(&g_type_mutex);
|
||||||
|
defer (gb_mutex_unlock(&g_type_mutex));
|
||||||
|
|
||||||
|
|
||||||
switch (t->kind) {
|
switch (t->kind) {
|
||||||
case Type_Named: {
|
case Type_Named: {
|
||||||
|
|||||||
Reference in New Issue
Block a user