|
@@ -155,6 +155,154 @@ gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize l
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
|
|
|
+ // NOTE(bill): The original_entity's scope may not be same scope that it was inserted into
|
|
|
+ // e.g. file entity inserted into its package scope
|
|
|
+ String original_name = original_entity->token.string;
|
|
|
+ Scope *found_scope = nullptr;
|
|
|
+ Entity *found_entity = nullptr;
|
|
|
+ scope_lookup_parent(original_entity->scope, original_name, &found_scope, &found_entity);
|
|
|
+ if (found_scope == nullptr) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ rw_mutex_lock(&found_scope->mutex);
|
|
|
+ defer (rw_mutex_unlock(&found_scope->mutex));
|
|
|
+
|
|
|
+ // IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the
|
|
|
+ // original entity was still used check checked, but the checking was only
|
|
|
+ // relying on "constant" data such as the Entity.type and Entity.Constant.value
|
|
|
+ //
|
|
|
+ // Therefore two things can be done: the type can be assigned to state that it
|
|
|
+ // has been "evaluated" and the variant data can be copied across
|
|
|
+
|
|
|
+ string_map_set(&found_scope->elements, original_name, new_entity);
|
|
|
+
|
|
|
+ original_entity->flags |= EntityFlag_Overridden;
|
|
|
+ original_entity->type = new_entity->type;
|
|
|
+ original_entity->aliased_of = new_entity;
|
|
|
+
|
|
|
+ Ast *empty_ident = nullptr;
|
|
|
+ original_entity->identifier.compare_exchange_strong(empty_ident, new_entity->identifier);
|
|
|
+
|
|
|
+ if (original_entity->identifier.load() != nullptr &&
|
|
|
+ original_entity->identifier.load()->kind == Ast_Ident) {
|
|
|
+ original_entity->identifier.load()->Ident.entity = new_entity;
|
|
|
+ }
|
|
|
+
|
|
|
+ // IMPORTANT NOTE(bill, 2021-04-10): copy only the variants
|
|
|
+ // This is most likely NEVER required, but it does not at all hurt to keep
|
|
|
+ isize offset = cast(u8 *)&original_entity->Dummy.start - cast(u8 *)original_entity;
|
|
|
+ isize size = gb_size_of(*original_entity) - offset;
|
|
|
+ gb_memmove(cast(u8 *)original_entity, cast(u8 *)new_entity, size);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d);
|
|
|
+
|
|
|
+gb_internal bool check_try_override_const_decl(CheckerContext *ctx, Entity *e, Entity *entity, Ast *init, Type *named_type) {
|
|
|
+ if (entity == nullptr) {
|
|
|
+ retry_proc_lit:;
|
|
|
+ init = unparen_expr(init);
|
|
|
+ if (init == nullptr) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (init->kind == Ast_TernaryWhenExpr) {
|
|
|
+ ast_node(we, TernaryWhenExpr, init);
|
|
|
+ if (we->cond == nullptr) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (we->cond->tav.value.kind != ExactValue_Bool) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ init = we->cond->tav.value.value_bool ? we->x : we->y;
|
|
|
+ goto retry_proc_lit;
|
|
|
+ } if (init->kind == Ast_ProcLit) {
|
|
|
+ // NOTE(bill, 2024-07-04): Override as a procedure entity because this could be within a `when` statement
|
|
|
+ e->kind = Entity_Procedure;
|
|
|
+ e->type = nullptr;
|
|
|
+ DeclInfo *d = decl_info_of_entity(e);
|
|
|
+ d->proc_lit = init;
|
|
|
+ check_proc_decl(ctx, e, d);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ switch (entity->kind) {
|
|
|
+ case Entity_TypeName:
|
|
|
+ // @TypeAliasingProblem
|
|
|
+ // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases
|
|
|
+ // being "confused" as constants
|
|
|
+ //
|
|
|
+ // A :: B
|
|
|
+ // C :: proc "c" (^A)
|
|
|
+ // B :: struct {x: C}
|
|
|
+ //
|
|
|
+ // A gets evaluated first, and then checks B.
|
|
|
+ // B then checks C.
|
|
|
+ // C then tries to check A which is unresolved but thought to be a constant.
|
|
|
+ // Therefore within C's check, A errs as "not a type".
|
|
|
+ //
|
|
|
+ // This is because a const declaration may or may not be a type and this cannot
|
|
|
+ // be determined from a syntactical standpoint.
|
|
|
+ // This check allows the compiler to override the entity to be checked as a type.
|
|
|
+ //
|
|
|
+ // There is no problem if B is prefixed with the `#type` helper enforcing at
|
|
|
+ // both a syntax and semantic level that B must be a type.
|
|
|
+ //
|
|
|
+ // A :: #type B
|
|
|
+ //
|
|
|
+ // This approach is not fool proof and can fail in case such as:
|
|
|
+ //
|
|
|
+ // X :: type_of(x)
|
|
|
+ // X :: Foo(int).Type
|
|
|
+ //
|
|
|
+ // Since even these kind of declarations may cause weird checking cycles.
|
|
|
+ // For the time being, these are going to be treated as an unfortunate error
|
|
|
+ // until there is a proper delaying system to try declaration again if they
|
|
|
+ // have failed.
|
|
|
+
|
|
|
+ e->kind = Entity_TypeName;
|
|
|
+ check_type_decl(ctx, e, init, named_type);
|
|
|
+ return true;
|
|
|
+ case Entity_Builtin:
|
|
|
+ if (e->type != nullptr) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ e->kind = Entity_Builtin;
|
|
|
+ e->Builtin.id = entity->Builtin.id;
|
|
|
+ e->type = t_invalid;
|
|
|
+ return true;
|
|
|
+ case Entity_ProcGroup:
|
|
|
+ // NOTE(bill, 2020-06-10): It is better to just clone the contents than overriding the entity in the scope
|
|
|
+ // Thank goodness I made entities a tagged union to allow for this implace patching
|
|
|
+ e->kind = Entity_ProcGroup;
|
|
|
+ e->ProcGroup.entities = array_clone(heap_allocator(), entity->ProcGroup.entities);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (e->type != nullptr && entity->type != nullptr) {
|
|
|
+ Operand x = {};
|
|
|
+ x.type = entity->type;
|
|
|
+ x.mode = Addressing_Variable;
|
|
|
+ if (!check_is_assignable_to(ctx, &x, e->type)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // NOTE(bill): Override aliased entity
|
|
|
+ switch (entity->kind) {
|
|
|
+ case Entity_ProcGroup:
|
|
|
+ case Entity_Procedure:
|
|
|
+ case Entity_LibraryName:
|
|
|
+ case Entity_ImportName:
|
|
|
+ override_entity_in_scope(e, entity);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
gb_internal void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
|
|
|
if (operand->mode == Addressing_Invalid ||
|
|
|
operand->type == t_invalid ||
|
|
@@ -165,6 +313,13 @@ gb_internal void check_init_constant(CheckerContext *ctx, Entity *e, Operand *op
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ if (operand->mode != Addressing_Constant) {
|
|
|
+ Entity *entity = entity_of_node(operand->expr);
|
|
|
+ if (check_try_override_const_decl(ctx, e, entity, operand->expr, nullptr)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (operand->mode != Addressing_Constant) {
|
|
|
gbString str = expr_to_string(operand->expr);
|
|
|
error(operand->expr, "'%s' is not a compile-time known constant", str);
|
|
@@ -373,49 +528,6 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
|
|
|
}
|
|
|
|
|
|
|
|
|
-gb_internal void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
|
|
|
- // NOTE(bill): The original_entity's scope may not be same scope that it was inserted into
|
|
|
- // e.g. file entity inserted into its package scope
|
|
|
- String original_name = original_entity->token.string;
|
|
|
- Scope *found_scope = nullptr;
|
|
|
- Entity *found_entity = nullptr;
|
|
|
- scope_lookup_parent(original_entity->scope, original_name, &found_scope, &found_entity);
|
|
|
- if (found_scope == nullptr) {
|
|
|
- return;
|
|
|
- }
|
|
|
- rw_mutex_lock(&found_scope->mutex);
|
|
|
- defer (rw_mutex_unlock(&found_scope->mutex));
|
|
|
-
|
|
|
- // IMPORTANT NOTE(bill, 2021-04-10): Overriding behaviour was flawed in that the
|
|
|
- // original entity was still used check checked, but the checking was only
|
|
|
- // relying on "constant" data such as the Entity.type and Entity.Constant.value
|
|
|
- //
|
|
|
- // Therefore two things can be done: the type can be assigned to state that it
|
|
|
- // has been "evaluated" and the variant data can be copied across
|
|
|
-
|
|
|
- string_map_set(&found_scope->elements, original_name, new_entity);
|
|
|
-
|
|
|
- original_entity->flags |= EntityFlag_Overridden;
|
|
|
- original_entity->type = new_entity->type;
|
|
|
- original_entity->aliased_of = new_entity;
|
|
|
-
|
|
|
- Ast *empty_ident = nullptr;
|
|
|
- original_entity->identifier.compare_exchange_strong(empty_ident, new_entity->identifier);
|
|
|
-
|
|
|
- if (original_entity->identifier.load() != nullptr &&
|
|
|
- original_entity->identifier.load()->kind == Ast_Ident) {
|
|
|
- original_entity->identifier.load()->Ident.entity = new_entity;
|
|
|
- }
|
|
|
-
|
|
|
- // IMPORTANT NOTE(bill, 2021-04-10): copy only the variants
|
|
|
- // This is most likely NEVER required, but it does not at all hurt to keep
|
|
|
- isize offset = cast(u8 *)&original_entity->Dummy.start - cast(u8 *)original_entity;
|
|
|
- isize size = gb_size_of(*original_entity) - offset;
|
|
|
- gb_memmove(cast(u8 *)original_entity, cast(u8 *)new_entity, size);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, Type *named_type) {
|
|
|
GB_ASSERT(e->type == nullptr);
|
|
|
GB_ASSERT(e->kind == Entity_Constant);
|
|
@@ -441,41 +553,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr
|
|
|
|
|
|
if (init != nullptr) {
|
|
|
Entity *entity = check_entity_from_ident_or_selector(ctx, init, false);
|
|
|
- if (entity != nullptr && entity->kind == Entity_TypeName) {
|
|
|
- // @TypeAliasingProblem
|
|
|
- // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases
|
|
|
- // being "confused" as constants
|
|
|
- //
|
|
|
- // A :: B
|
|
|
- // C :: proc "c" (^A)
|
|
|
- // B :: struct {x: C}
|
|
|
- //
|
|
|
- // A gets evaluated first, and then checks B.
|
|
|
- // B then checks C.
|
|
|
- // C then tries to check A which is unresolved but thought to be a constant.
|
|
|
- // Therefore within C's check, A errs as "not a type".
|
|
|
- //
|
|
|
- // This is because a const declaration may or may not be a type and this cannot
|
|
|
- // be determined from a syntactical standpoint.
|
|
|
- // This check allows the compiler to override the entity to be checked as a type.
|
|
|
- //
|
|
|
- // There is no problem if B is prefixed with the `#type` helper enforcing at
|
|
|
- // both a syntax and semantic level that B must be a type.
|
|
|
- //
|
|
|
- // A :: #type B
|
|
|
- //
|
|
|
- // This approach is not fool proof and can fail in case such as:
|
|
|
- //
|
|
|
- // X :: type_of(x)
|
|
|
- // X :: Foo(int).Type
|
|
|
- //
|
|
|
- // Since even these kind of declarations may cause weird checking cycles.
|
|
|
- // For the time being, these are going to be treated as an unfortunate error
|
|
|
- // until there is a proper delaying system to try declaration again if they
|
|
|
- // have failed.
|
|
|
-
|
|
|
- e->kind = Entity_TypeName;
|
|
|
- check_type_decl(ctx, e, init, named_type);
|
|
|
+ if (check_try_override_const_decl(ctx, e, entity, init, named_type)) {
|
|
|
return;
|
|
|
}
|
|
|
entity = nullptr;
|