aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/big_int.cpp4
-rw-r--r--src/build_settings.cpp1
-rw-r--r--src/check_builtin.cpp6
-rw-r--r--src/check_decl.cpp3
-rw-r--r--src/check_expr.cpp94
-rw-r--r--src/check_stmt.cpp6
-rw-r--r--src/check_type.cpp15
-rw-r--r--src/checker.cpp171
-rw-r--r--src/checker.hpp3
-rw-r--r--src/exact_value.cpp24
-rw-r--r--src/main.cpp12
-rw-r--r--src/types.cpp16
12 files changed, 253 insertions, 102 deletions
diff --git a/src/big_int.cpp b/src/big_int.cpp
index 9e57b3fc2..168e53fb2 100644
--- a/src/big_int.cpp
+++ b/src/big_int.cpp
@@ -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;
big_int_normalize(dst);
return;
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 3ddec8628..ecca85e4f 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -208,6 +208,7 @@ struct BuildContext {
bool linker_map_file;
bool use_separate_modules;
+ bool threaded_checker;
u32 cmd_doc_flags;
Array<String> extra_packages;
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index cbf49a2c7..2d25aaae4 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -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_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);
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_count = count;
- scope = create_scope(c->scope);
+ scope = create_scope(c->info, c->scope);
soa_struct->Struct.scope = scope;
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_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;
for_array(i, old_struct->Struct.fields) {
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index d7f04ca5c..8b66452f3 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -1276,7 +1276,8 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
Entity *uvar = using_entities[i].uvar;
Entity *prev = scope_insert(ctx->scope, uvar);
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;
}
}
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 3ff002eb1..455fed21a 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -283,7 +283,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
CheckerInfo *info = c->info;
CheckerContext nctx = *c;
- Scope *scope = create_scope(base_entity->scope);
+ Scope *scope = create_scope(c->info, base_entity->scope);
scope->flags |= ScopeFlag_Proc;
nctx.scope = scope;
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);
+ defer (gb_mutex_unlock(&info->gen_procs_mutex));
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) {
- gb_mutex_lock(&info->gen_procs_mutex);
- defer (gb_mutex_unlock(&info->gen_procs_mutex));
+ // gb_mutex_lock(&info->gen_procs_mutex);
+ // defer (gb_mutex_unlock(&info->gen_procs_mutex));
auto procs = *found_gen_procs;
for_array(i, procs) {
@@ -349,8 +349,8 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
}
if (found_gen_procs) {
- gb_mutex_lock(&info->gen_procs_mutex);
- defer (gb_mutex_unlock(&info->gen_procs_mutex));
+ // gb_mutex_lock(&info->gen_procs_mutex);
+ // defer (gb_mutex_unlock(&info->gen_procs_mutex));
auto procs = *found_gen_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->poly_def_node = poly_def_node;
- gb_mutex_lock(&info->gen_procs_mutex);
+ // gb_mutex_lock(&info->gen_procs_mutex);
if (found_gen_procs) {
array_add(found_gen_procs, entity);
} else {
@@ -429,7 +429,7 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *c, Entity *base_enti
array_add(&array, entity);
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);
@@ -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) {
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 b = type_to_string(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)) {
error(o->expr, "'%s' truncated to '%s'", a, b);
} 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);
}
} 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) {
switch (op.kind) {
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 (ast_node_expect(node, Ast_UnaryExpr)) {
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;
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) {
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->value = x_val;
+
+ gb_mutex_unlock(&c->info->untyped_mutex);
return;
}
}
@@ -2519,6 +2529,25 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
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 dstz = type_size_of(t);
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) {
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 (type != nullptr && 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;
@@ -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
- check_remove_expr_info(&c->checker->info, e);
+ map_remove(&c->info->untyped, hash_node(e));
if (old->is_lhs && !is_type_integer(type)) {
gbString expr_str = expr_to_string(e);
@@ -2977,11 +3008,14 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
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) {
- 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) {
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(type_str);
@@ -5682,7 +5716,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
operand->builtin_id = BuiltinProc_DIRECTIVE;
operand->expr = proc;
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 {
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);
Entity *e = ot->Named.type_name;
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 {
operand->mode = Addressing_Invalid;
operand->type = t_invalid;
@@ -6153,8 +6187,8 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu
return false;
}
- add_type_and_value(&c->checker->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->left, x->mode, x->type, x->value);
+ add_type_and_value(c->info, ie->right, y->mode, y->type, y->value);
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));
if (is_type_proc(pt)) {
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 (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);
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;
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");
}
- Scope *scope = create_scope(c->scope);
+ Scope *scope = create_scope(c->info, c->scope);
scope->flags |= ScopeFlag_Proc;
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);
}
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;
}
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 501fb7abf..1d7cf3cce 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -587,7 +587,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
Entity *found = scope_insert(ctx->scope, f);
if (found != nullptr) {
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);
return false;
}
@@ -611,7 +611,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
if (found != nullptr) {
gbString expr_str = expr_to_string(expr);
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",
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) {
continue;
}
-
+ update_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type));
add_constant_switch_case(ctx, &seen, y);
}
}
diff --git a/src/check_type.cpp b/src/check_type.cpp
index e7832272a..cc4ffebca 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -1260,7 +1260,10 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
param_value.kind = ParameterValue_Constant;
param_value.value = o.value;
} 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);
- Scope *s = create_scope(builtin_pkg->scope);
+ Scope *s = create_scope(nullptr, builtin_pkg->scope);
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));
@@ -2078,7 +2081,7 @@ void init_map_internal_types(Type *type) {
}
*/
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 *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.is_polymorphic = true;
- scope = create_scope(ctx->scope);
+ scope = create_scope(ctx->info, ctx->scope);
soa_struct->Struct.scope = scope;
} else if (is_type_array(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_count = count;
- scope = create_scope(ctx->scope);
+ scope = create_scope(ctx->info, ctx->scope);
soa_struct->Struct.scope = scope;
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_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;
for_array(i, old_struct->Struct.fields) {
diff --git a/src/checker.cpp b/src/checker.cpp
index e7a53ded9..529311baa 100644
--- a/src/checker.cpp
+++ b/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);
s->parent = parent;
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();
if (parent != nullptr && parent != builtin_pkg->scope) {
+ if (info) gb_mutex_lock(&info->scope_mutex);
DLIST_APPEND(parent->first_child, parent->last_child, s);
+ if (info) gb_mutex_unlock(&info->scope_mutex);
}
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;
}
-Scope *create_scope_from_file(AstFile *f) {
+Scope *create_scope_from_file(CheckerInfo *info, AstFile *f) {
GB_ASSERT(f != nullptr);
GB_ASSERT(f->pkg != 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_directives, f->directive_count);
@@ -269,7 +271,7 @@ Scope *create_scope_from_package(CheckerContext *c, AstPackage *pkg) {
decl_count += pkg->files[i]->decls.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->pkg = pkg;
@@ -329,7 +331,7 @@ void check_open_scope(CheckerContext *c, Ast *node) {
GB_ASSERT(node->kind == Ast_Invalid ||
is_ast_stmt(node) ||
is_ast_type(node));
- Scope *scope = create_scope(c->scope);
+ Scope *scope = create_scope(c->info, c->scope);
add_scope(c, node, scope);
switch (node->kind) {
case Ast_ProcType:
@@ -715,7 +717,7 @@ AstPackage *create_builtin_package(char const *name) {
pkg->name = make_string_c(name);
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->pkg = pkg;
return pkg;
@@ -860,6 +862,7 @@ void init_checker_info(CheckerInfo *i) {
gb_mutex_init(&i->identifier_uses_mutex);
gb_mutex_init(&i->entity_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->entity_mutex);
gb_mutex_destroy(&i->foreign_mutex);
+ gb_mutex_destroy(&i->scope_mutex);
}
CheckerContext make_checker_context(Checker *c) {
@@ -902,6 +906,10 @@ CheckerContext make_checker_context(Checker *c) {
ctx.poly_level = 0;
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) {
if (file != nullptr) {
@@ -917,30 +925,13 @@ void reset_checker_context(CheckerContext *ctx, AstFile *file) {
if (ctx == nullptr) {
return;
}
- auto checker = ctx->checker;
- auto info = ctx->info;
- 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;
-
+ destroy_checker_context(ctx);
+ *ctx = make_checker_context(ctx->checker);
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) {
c->parser = parser;
@@ -1051,21 +1042,13 @@ Scope *scope_of_node(Ast *node) {
return node->scope;
}
ExprInfo *check_get_expr_info(CheckerInfo *i, Ast *expr) {
- gb_mutex_lock(&i->untyped_mutex);
ExprInfo *res = nullptr;
ExprInfo **found = map_get(&i->untyped, hash_node(expr));
if (found) {
res = *found;
}
- gb_mutex_unlock(&i->untyped_mutex);
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) {
@@ -1527,6 +1510,7 @@ void check_procedure_later(Checker *c, ProcInfo *info) {
GB_ASSERT(info->decl != nullptr);
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) {
@@ -4281,6 +4265,9 @@ void check_proc_info(Checker *c, ProcInfo *pi) {
if (pi->type == nullptr) {
return;
}
+ if (pi->decl->proc_checked) {
+ return;
+ }
CheckerContext ctx = make_checker_context(c);
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_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);
if (pi->body != nullptr && pi->decl->entity != nullptr) {
pi->decl->entity->flags |= EntityFlag_ProcBodyChecked;
}
+ pi->decl->proc_checked = true;
+
}
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) {
auto *q = &c->procs_to_check_queue;
ProcInfo *pi = nullptr;
- 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;
+ isize thread_count = gb_max(build_context.thread_count, 1);
+ isize worker_count = thread_count-1; // NOTE(bill): The main thread will also be used for work
+ if (!build_context.threaded_checker) {
+ worker_count = 0;
+ }
+
+ if (worker_count == 0) {
+ 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);
+ }
+ } 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];
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);
check_collect_entities(ctx, f->decls);
}
diff --git a/src/checker.hpp b/src/checker.hpp
index 9395d1565..6a00ce0e1 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -144,6 +144,7 @@ struct DeclInfo {
Type * gen_proc_type; // Precalculated
bool is_using;
bool where_clauses_evaluated;
+ bool proc_checked;
CommentGroup *comment;
CommentGroup *docs;
@@ -289,6 +290,7 @@ struct CheckerInfo {
gbMutex identifier_uses_mutex;
gbMutex entity_mutex;
gbMutex foreign_mutex;
+ gbMutex scope_mutex;
Map<ExprInfo *> untyped; // Key: Ast * | Expression -> ExprInfo *
// 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);
-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_type_and_value (CheckerInfo *i, Ast *expression, AddressingMode mode, Type *type, ExactValue value);
void add_entity_use (CheckerContext *c, Ast *identifier, Entity *entity);
diff --git a/src/exact_value.cpp b/src/exact_value.cpp
index 12c14b4fa..9bcaf76de 100644
--- a/src/exact_value.cpp
+++ b/src/exact_value.cpp
@@ -27,18 +27,18 @@ Quaternion256 quaternion256_inverse(Quaternion256 x) {
enum ExactValueKind {
- ExactValue_Invalid,
-
- ExactValue_Bool,
- ExactValue_String,
- ExactValue_Integer,
- ExactValue_Float,
- ExactValue_Complex,
- ExactValue_Quaternion,
- ExactValue_Pointer,
- ExactValue_Compound, // TODO(bill): Is this good enough?
- ExactValue_Procedure, // TODO(bill): Is this good enough?
- ExactValue_Typeid,
+ ExactValue_Invalid = 0,
+
+ ExactValue_Bool = 1,
+ ExactValue_String = 2,
+ ExactValue_Integer = 3,
+ ExactValue_Float = 4,
+ ExactValue_Complex = 5,
+ ExactValue_Quaternion = 6,
+ ExactValue_Pointer = 7,
+ ExactValue_Compound = 8, // TODO(bill): Is this good enough?
+ ExactValue_Procedure = 9, // TODO(bill): Is this good enough?
+ ExactValue_Typeid = 10,
ExactValue_Count,
};
diff --git a/src/main.cpp b/src/main.cpp
index 5222a0321..345642cc6 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -600,6 +600,7 @@ enum BuildFlagKind {
BuildFlag_NoEntryPoint,
BuildFlag_UseLLD,
BuildFlag_UseSeparateModules,
+ BuildFlag_ThreadedChecker,
BuildFlag_Vet,
BuildFlag_VetExtra,
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_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_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_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);
@@ -1206,6 +1208,10 @@ bool parse_build_flags(Array<String> args) {
build_context.use_separate_modules = true;
break;
+ case BuildFlag_ThreadedChecker:
+ build_context.threaded_checker = true;
+ break;
+
case BuildFlag_Vet:
build_context.vet = true;
break;
@@ -1746,6 +1752,11 @@ void print_show_help(String const arg0, String const &command) {
}
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(2, "Do extra checks on the code");
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_keyword_hash_table();
global_big_int_init();
+ init_type_mutex();
if (!check_env()) {
return 1;
diff --git a/src/types.cpp b/src/types.cpp
index 5f6fdbb34..dac2632a9 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -661,6 +661,8 @@ gb_global Type *t_map_header = nullptr;
gb_global Type *t_equal_proc = nullptr;
gb_global Type *t_hasher_proc = nullptr;
+gb_global gbMutex g_type_mutex;
+
i64 type_size_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_integer(Type *t);
+void init_type_mutex(void) {
+ gb_mutex_init(&g_type_mutex);
+}
+
bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t) {
if (ptr_set_exists(s, t)) {
return true;
@@ -2727,7 +2733,7 @@ void type_path_print_illegal_cycle(TypePath *tp, isize start_index) {
GB_ASSERT(start_index < tp->path.count);
Entity *e = tp->path[start_index];
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
for (isize j = start_index; j < tp->path.count; j++) {
Entity *e = tp->path[j];
@@ -2844,6 +2850,8 @@ i64 type_align_of_internal(Type *t, TypePath *path) {
if (t->failure) {
return FAILURE_ALIGNMENT;
}
+ gb_mutex_lock(&g_type_mutex);
+ defer (gb_mutex_unlock(&g_type_mutex));
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) {
+ gb_mutex_lock(&g_type_mutex);
+ defer (gb_mutex_unlock(&g_type_mutex));
+
t = base_type(t);
if (t->kind == Type_Struct) {
if (!t->Struct.are_offsets_set) {
@@ -3066,6 +3077,9 @@ i64 type_size_of_internal(Type *t, TypePath *path) {
if (t->failure) {
return FAILURE_SIZE;
}
+ gb_mutex_lock(&g_type_mutex);
+ defer (gb_mutex_unlock(&g_type_mutex));
+
switch (t->kind) {
case Type_Named: {