mirror of
https://github.com/odin-lang/Odin.git
synced 2026-02-17 08:34:08 +00:00
Merge branch 'master' into odin-global-constants-as-enums
This commit is contained in:
158
src/checker.cpp
158
src/checker.cpp
@@ -504,6 +504,7 @@ enum VettedEntityKind {
|
||||
|
||||
VettedEntity_Unused,
|
||||
VettedEntity_Shadowed,
|
||||
VettedEntity_Shadowed_And_Unused,
|
||||
};
|
||||
struct VettedEntity {
|
||||
VettedEntityKind kind;
|
||||
@@ -625,12 +626,18 @@ void check_scope_usage(Checker *c, Scope *scope) {
|
||||
MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
|
||||
Entity *e = scope->elements.entries[i].value;
|
||||
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);
|
||||
VettedEntity ve_unused = {};
|
||||
VettedEntity ve_shadowed = {};
|
||||
bool is_unused = vet_unused && check_vet_unused(c, e, &ve_unused);
|
||||
bool is_shadowed = vet_shadowing && check_vet_shadowing(c, e, &ve_shadowed);
|
||||
if (is_unused && is_shadowed) {
|
||||
VettedEntity ve_both = ve_shadowed;
|
||||
ve_both.kind = VettedEntity_Shadowed_And_Unused;
|
||||
array_add(&vetted_entities, ve_both);
|
||||
} else if (is_unused) {
|
||||
array_add(&vetted_entities, ve_unused);
|
||||
} else if (is_shadowed) {
|
||||
array_add(&vetted_entities, ve_shadowed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,16 +649,18 @@ void check_scope_usage(Checker *c, Scope *scope) {
|
||||
Entity *other = ve.other;
|
||||
String name = e->token.string;
|
||||
|
||||
if (build_context.vet) {
|
||||
if (ve.kind == VettedEntity_Shadowed_And_Unused) {
|
||||
error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line);
|
||||
} else if (build_context.vet) {
|
||||
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);
|
||||
error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %d", LIT(name), 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);
|
||||
error(e->token, "Declaration of '%.*s' shadows declaration at line %d", LIT(name), other->token.pos.line);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -688,12 +697,17 @@ void add_dependency(CheckerInfo *info, DeclInfo *d, Entity *e) {
|
||||
ptr_set_add(&d->deps, e);
|
||||
mutex_unlock(&info->deps_mutex);
|
||||
}
|
||||
void add_type_info_dependency(DeclInfo *d, Type *type) {
|
||||
void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *type, bool require_mutex) {
|
||||
if (d == nullptr) {
|
||||
return;
|
||||
}
|
||||
// NOTE(bill): no mutex is required here because the only procedure calling it is wrapped in a mutex already
|
||||
if (require_mutex) {
|
||||
mutex_lock(&info->deps_mutex);
|
||||
}
|
||||
ptr_set_add(&d->type_info_deps, type);
|
||||
if (require_mutex) {
|
||||
mutex_unlock(&info->deps_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
AstPackage *get_core_package(CheckerInfo *info, String name) {
|
||||
@@ -919,6 +933,16 @@ void init_universal(void) {
|
||||
add_global_string_constant("ODIN_ENDIAN_STRING", target_endian_names[target_endians[bc->metrics.arch]]);
|
||||
}
|
||||
|
||||
{
|
||||
GlobalEnumValue values[ErrorPosStyle_COUNT] = {
|
||||
{"Default", ErrorPosStyle_Default},
|
||||
{"Unix", ErrorPosStyle_Unix},
|
||||
};
|
||||
|
||||
auto fields = add_global_enum_type(str_lit("Odin_Error_Pos_Style_Type"), values, gb_count_of(values));
|
||||
add_global_enum_constant(fields, "ODIN_ERROR_POS_STYLE", build_context.ODIN_ERROR_POS_STYLE);
|
||||
}
|
||||
|
||||
|
||||
add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG);
|
||||
add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT);
|
||||
@@ -1620,7 +1644,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_type_info_dependency(c->decl, t);
|
||||
add_type_info_dependency(c->info, c->decl, t, false);
|
||||
|
||||
auto found = map_get(&c->info->type_info_map, t);
|
||||
if (found != nullptr) {
|
||||
@@ -1749,6 +1773,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
|
||||
} else {
|
||||
add_type_info_type_internal(c, t_type_info_ptr);
|
||||
}
|
||||
add_type_info_type_internal(c, bt->Union.polymorphic_params);
|
||||
for_array(i, bt->Union.variants) {
|
||||
add_type_info_type_internal(c, bt->Union.variants[i]);
|
||||
}
|
||||
@@ -1772,6 +1797,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
|
||||
}
|
||||
}
|
||||
}
|
||||
add_type_info_type_internal(c, bt->Struct.polymorphic_params);
|
||||
for_array(i, bt->Struct.fields) {
|
||||
Entity *f = bt->Struct.fields[i];
|
||||
add_type_info_type_internal(c, f->type);
|
||||
@@ -1965,6 +1991,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
|
||||
} else {
|
||||
add_min_dep_type_info(c, t_type_info_ptr);
|
||||
}
|
||||
add_min_dep_type_info(c, bt->Union.polymorphic_params);
|
||||
for_array(i, bt->Union.variants) {
|
||||
add_min_dep_type_info(c, bt->Union.variants[i]);
|
||||
}
|
||||
@@ -1988,6 +2015,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
|
||||
}
|
||||
}
|
||||
}
|
||||
add_min_dep_type_info(c, bt->Struct.polymorphic_params);
|
||||
for_array(i, bt->Struct.fields) {
|
||||
Entity *f = bt->Struct.fields[i];
|
||||
add_min_dep_type_info(c, f->type);
|
||||
@@ -3479,9 +3507,12 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
||||
|
||||
if (entity_visibility_kind == EntityVisiblity_Public &&
|
||||
(c->scope->flags&ScopeFlag_File) &&
|
||||
c->scope->file &&
|
||||
(c->scope->file->flags & AstFile_IsPrivate)) {
|
||||
entity_visibility_kind = EntityVisiblity_PrivateToPackage;
|
||||
c->scope->file) {
|
||||
if (c->scope->file->flags & AstFile_IsPrivateFile) {
|
||||
entity_visibility_kind = EntityVisiblity_PrivateToFile;
|
||||
} else if (c->scope->file->flags & AstFile_IsPrivatePkg) {
|
||||
entity_visibility_kind = EntityVisiblity_PrivateToPackage;
|
||||
}
|
||||
}
|
||||
|
||||
if (entity_visibility_kind != EntityVisiblity_Public && !(c->scope->flags&ScopeFlag_File)) {
|
||||
@@ -3572,9 +3603,6 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
||||
|
||||
if (is_ast_type(init)) {
|
||||
e = alloc_entity_type_name(d->scope, token, nullptr);
|
||||
// if (vd->type != nullptr) {
|
||||
// error(name, "A type declaration cannot have an type parameter");
|
||||
// }
|
||||
} else if (init->kind == Ast_ProcLit) {
|
||||
if (c->scope->flags&ScopeFlag_Type) {
|
||||
error(name, "Procedure declarations are not allowed within a struct");
|
||||
@@ -3677,6 +3705,59 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
|
||||
check_collect_entities(&c, block->stmts);
|
||||
}
|
||||
|
||||
bool correct_single_type_alias(CheckerContext *c, Entity *e) {
|
||||
if (e->kind == Entity_Constant) {
|
||||
DeclInfo *d = e->decl_info;
|
||||
if (d != nullptr && d->init_expr != nullptr) {
|
||||
Ast *init = d->init_expr;
|
||||
Entity *alias_of = check_entity_from_ident_or_selector(c, init, true);
|
||||
if (alias_of != nullptr && alias_of->kind == Entity_TypeName) {
|
||||
e->kind = Entity_TypeName;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool correct_type_alias_in_scope_backwards(CheckerContext *c, Scope *s) {
|
||||
isize n = s->elements.entries.count;
|
||||
bool correction = false;
|
||||
for (isize i = n-1; i >= 0; i--) {
|
||||
correction |= correct_single_type_alias(c, s->elements.entries[i].value);
|
||||
}
|
||||
return correction;
|
||||
}
|
||||
bool correct_type_alias_in_scope_forwards(CheckerContext *c, Scope *s) {
|
||||
isize n = s->elements.entries.count;
|
||||
bool correction = false;
|
||||
for (isize i = 0; i < n; i++) {
|
||||
correction |= correct_single_type_alias(c, s->elements.entries[i].value);
|
||||
}
|
||||
return correction;
|
||||
}
|
||||
|
||||
|
||||
void correct_type_aliases_in_scope(CheckerContext *c, Scope *s) {
|
||||
// NOTE(bill, 2022-02-04): This is used to solve the problem caused by type aliases
|
||||
// of type aliases being "confused" as constants
|
||||
//
|
||||
// A :: C
|
||||
// B :: A
|
||||
// C :: struct {b: ^B}
|
||||
//
|
||||
// See @TypeAliasingProblem for more information
|
||||
for (;;) {
|
||||
bool corrections = false;
|
||||
corrections |= correct_type_alias_in_scope_backwards(c, s);
|
||||
corrections |= correct_type_alias_in_scope_forwards(c, s);
|
||||
if (!corrections) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): If file_scopes == nullptr, this will act like a local scope
|
||||
void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes) {
|
||||
AstFile *curr_file = nullptr;
|
||||
@@ -3748,6 +3829,7 @@ void check_collect_entities(CheckerContext *c, Slice<Ast *> const &nodes) {
|
||||
}
|
||||
}
|
||||
|
||||
// correct_type_aliases(c);
|
||||
|
||||
// NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something
|
||||
// declared after this stmt in source
|
||||
@@ -4393,10 +4475,11 @@ bool collect_file_decls(CheckerContext *ctx, Slice<Ast *> const &decls) {
|
||||
|
||||
for_array(i, decls) {
|
||||
if (collect_file_decl(ctx, decls[i])) {
|
||||
correct_type_aliases_in_scope(ctx, ctx->scope);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
correct_type_aliases_in_scope(ctx, ctx->scope);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -4666,6 +4749,15 @@ void check_import_entities(Checker *c) {
|
||||
}
|
||||
add_untyped_expressions(ctx.info, &untyped);
|
||||
}
|
||||
|
||||
for_array(i, pkg->files) {
|
||||
AstFile *f = pkg->files[i];
|
||||
reset_checker_context(&ctx, f, &untyped);
|
||||
ctx.collect_delayed_decls = false;
|
||||
|
||||
correct_type_aliases_in_scope(&ctx, pkg->scope);
|
||||
}
|
||||
|
||||
for_array(i, pkg->files) {
|
||||
AstFile *f = pkg->files[i];
|
||||
reset_checker_context(&ctx, f, &untyped);
|
||||
@@ -4887,6 +4979,9 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
|
||||
bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0;
|
||||
bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0;
|
||||
|
||||
bool type_assert = (pi->tags & ProcTag_type_assert) != 0;
|
||||
bool no_type_assert = (pi->tags & ProcTag_no_type_assert) != 0;
|
||||
|
||||
if (bounds_check) {
|
||||
ctx.state_flags |= StateFlag_bounds_check;
|
||||
ctx.state_flags &= ~StateFlag_no_bounds_check;
|
||||
@@ -4894,6 +4989,15 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
|
||||
ctx.state_flags |= StateFlag_no_bounds_check;
|
||||
ctx.state_flags &= ~StateFlag_bounds_check;
|
||||
}
|
||||
|
||||
if (type_assert) {
|
||||
ctx.state_flags |= StateFlag_type_assert;
|
||||
ctx.state_flags &= ~StateFlag_no_type_assert;
|
||||
} else if (no_type_assert) {
|
||||
ctx.state_flags |= StateFlag_no_type_assert;
|
||||
ctx.state_flags &= ~StateFlag_type_assert;
|
||||
}
|
||||
|
||||
if (pi->body != nullptr && e != nullptr) {
|
||||
GB_ASSERT((e->flags & EntityFlag_ProcBodyChecked) == 0);
|
||||
}
|
||||
@@ -5307,12 +5411,18 @@ void check_unique_package_names(Checker *c) {
|
||||
string_map_set(&pkgs, key, pkg);
|
||||
continue;
|
||||
}
|
||||
auto *curr = pkg->files[0]->pkg_decl;
|
||||
auto *prev = (*found)->files[0]->pkg_decl;
|
||||
if (curr == prev) {
|
||||
// NOTE(bill): A false positive was found, ignore it
|
||||
continue;
|
||||
}
|
||||
|
||||
error(pkg->files[0]->pkg_decl, "Duplicate declaration of 'package %.*s'", LIT(name));
|
||||
error(curr, "Duplicate declaration of 'package %.*s'", LIT(name));
|
||||
error_line("\tA package name must be unique\n"
|
||||
"\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n"
|
||||
"\tA package name is required for link name prefixing to have a consistent ABI\n");
|
||||
error((*found)->files[0]->pkg_decl, "found at previous location");
|
||||
error(prev, "found at previous location");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5504,9 +5614,6 @@ void check_parsed_files(Checker *c) {
|
||||
TIME_SECTION("calculate global init order");
|
||||
calculate_global_init_order(c);
|
||||
|
||||
TIME_SECTION("generate minimum dependency set");
|
||||
generate_minimum_dependency_set(c, c->info.entry_point);
|
||||
|
||||
TIME_SECTION("check test procedures");
|
||||
check_test_procedures(c);
|
||||
|
||||
@@ -5517,6 +5624,9 @@ void check_parsed_files(Checker *c) {
|
||||
add_type_info_for_type_definitions(c);
|
||||
check_merge_queues_into_arrays(c);
|
||||
|
||||
TIME_SECTION("generate minimum dependency set");
|
||||
generate_minimum_dependency_set(c, c->info.entry_point);
|
||||
|
||||
TIME_SECTION("check entry point");
|
||||
if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) {
|
||||
Scope *s = c->info.init_scope;
|
||||
|
||||
Reference in New Issue
Block a user