aboutsummaryrefslogtreecommitdiff
path: root/src/check_expr.cpp
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2023-06-23 14:33:01 +0100
committerGitHub <noreply@github.com>2023-06-23 14:33:01 +0100
commit26a5614572afd39fc35fc32b47d5f7e5e9771e56 (patch)
treeed94ed8589d3d0e32a5a8e494319bfcf292d27cd /src/check_expr.cpp
parentf36e19e86fe88198fc1d17426afea577920efbf8 (diff)
parent19ea0906332e6185cd0eefe873179b9058ccd725 (diff)
Merge branch 'master' into skytrias-vendor-additions
Diffstat (limited to 'src/check_expr.cpp')
-rw-r--r--src/check_expr.cpp2953
1 files changed, 1630 insertions, 1323 deletions
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 9846199f8..83e44b39f 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -14,10 +14,11 @@ enum CallArgumentError {
CallArgumentError_ParameterMissing,
CallArgumentError_DuplicateParameter,
CallArgumentError_NoneConstantParameter,
+ CallArgumentError_OutOfOrderParameters,
CallArgumentError_MAX,
};
-char const *CallArgumentError_strings[CallArgumentError_MAX] = {
+gb_global char const *CallArgumentError_strings[CallArgumentError_MAX] = {
"None",
"NoneProcedureType",
"WrongTypes",
@@ -33,12 +34,13 @@ char const *CallArgumentError_strings[CallArgumentError_MAX] = {
"ParameterMissing",
"DuplicateParameter",
"NoneConstantParameter",
+ "OutOfOrderParameters",
};
-enum CallArgumentErrorMode {
- CallArgumentMode_NoErrors,
- CallArgumentMode_ShowErrors,
+enum struct CallArgumentErrorMode {
+ NoErrors,
+ ShowErrors,
};
struct CallArgumentData {
@@ -57,7 +59,7 @@ struct ValidIndexAndScore {
i64 score;
};
-int valid_index_and_score_cmp(void const *a, void const *b) {
+gb_internal int valid_index_and_score_cmp(void const *a, void const *b) {
i64 si = (cast(ValidIndexAndScore const *)a)->score;
i64 sj = (cast(ValidIndexAndScore const *)b)->score;
return sj < si ? -1 : sj > si;
@@ -65,62 +67,57 @@ int valid_index_and_score_cmp(void const *a, void const *b) {
-#define CALL_ARGUMENT_CHECKER(name) CallArgumentError name(CheckerContext *c, Ast *call, Type *proc_type, Entity *entity, Array<Operand> operands, CallArgumentErrorMode show_error_mode, CallArgumentData *data)
-typedef CALL_ARGUMENT_CHECKER(CallArgumentCheckerType);
+gb_internal void check_expr (CheckerContext *c, Operand *operand, Ast *expression);
+gb_internal void check_multi_expr (CheckerContext *c, Operand *operand, Ast *expression);
+gb_internal void check_multi_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression);
+gb_internal void check_multi_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *type_hint);
+gb_internal void check_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint);
+gb_internal ExprKind check_expr_base (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint);
+gb_internal void check_expr_with_type_hint (CheckerContext *c, Operand *o, Ast *e, Type *t);
+gb_internal Type * check_type (CheckerContext *c, Ast *expression);
+gb_internal Type * check_type_expr (CheckerContext *c, Ast *expression, Type *named_type);
+gb_internal Type * make_optional_ok_type (Type *value, bool typed=true);
+gb_internal Entity * check_selector (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint);
+gb_internal Entity * check_ident (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name);
+gb_internal Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure);
+gb_internal void check_not_tuple (CheckerContext *c, Operand *operand);
+gb_internal void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type);
+gb_internal gbString expr_to_string (Ast *expression);
+gb_internal gbString expr_to_string (Ast *expression, gbAllocator allocator);
+gb_internal void update_untyped_expr_type (CheckerContext *c, Ast *e, Type *type, bool final);
+gb_internal bool check_is_terminating (Ast *node, String const &label);
+gb_internal bool check_has_break (Ast *stmt, String const &label, bool implicit);
+gb_internal void check_stmt (CheckerContext *c, Ast *node, u32 flags);
+gb_internal void check_stmt_list (CheckerContext *c, Slice<Ast *> const &stmts, u32 flags);
+gb_internal void check_init_constant (CheckerContext *c, Entity *e, Operand *operand);
+gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Type *type, ExactValue *out_value);
+gb_internal bool check_procedure_type (CheckerContext *c, Type *type, Ast *proc_type_node, Array<Operand> const *operands = nullptr);
+gb_internal void check_struct_type (CheckerContext *c, Type *struct_type, Ast *node, Array<Operand> *poly_operands,
+ Type *named_type = nullptr, Type *original_type_for_poly = nullptr);
+gb_internal void check_union_type (CheckerContext *c, Type *union_type, Ast *node, Array<Operand> *poly_operands,
+ Type *named_type = nullptr, Type *original_type_for_poly = nullptr);
+gb_internal Type * check_init_variable (CheckerContext *c, Entity *e, Operand *operand, String context_name);
-void check_expr (CheckerContext *c, Operand *operand, Ast *expression);
-void check_multi_expr (CheckerContext *c, Operand *operand, Ast *expression);
-void check_multi_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression);
-void check_multi_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *type_hint);
-void check_expr_or_type (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint);
-ExprKind check_expr_base (CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint);
-void check_expr_with_type_hint (CheckerContext *c, Operand *o, Ast *e, Type *t);
-Type * check_type (CheckerContext *c, Ast *expression);
-Type * check_type_expr (CheckerContext *c, Ast *expression, Type *named_type);
-Type * make_optional_ok_type (Type *value, bool typed=true);
-Entity * check_selector (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint);
-Entity * check_ident (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name);
-Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure);
-void check_not_tuple (CheckerContext *c, Operand *operand);
-void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type);
-gbString expr_to_string (Ast *expression);
-void check_proc_body (CheckerContext *c, Token token, DeclInfo *decl, Type *type, Ast *body);
-void update_untyped_expr_type (CheckerContext *c, Ast *e, Type *type, bool final);
-bool check_is_terminating (Ast *node, String const &label);
-bool check_has_break (Ast *stmt, String const &label, bool implicit);
-void check_stmt (CheckerContext *c, Ast *node, u32 flags);
-void check_stmt_list (CheckerContext *c, Slice<Ast *> const &stmts, u32 flags);
-void check_init_constant (CheckerContext *c, Entity *e, Operand *operand);
-bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Type *type, ExactValue *out_value);
-bool check_procedure_type (CheckerContext *c, Type *type, Ast *proc_type_node, Array<Operand> *operands = nullptr);
-void check_struct_type (CheckerContext *c, Type *struct_type, Ast *node, Array<Operand> *poly_operands,
- Type *named_type = nullptr, Type *original_type_for_poly = nullptr);
-void check_union_type (CheckerContext *c, Type *union_type, Ast *node, Array<Operand> *poly_operands,
- Type *named_type = nullptr, Type *original_type_for_poly = nullptr);
+gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type);
+gb_internal void add_map_key_type_dependencies(CheckerContext *ctx, Type *key);
-CallArgumentData check_call_arguments (CheckerContext *c, Operand *operand, Type *proc_type, Ast *call);
-Type * check_init_variable (CheckerContext *c, Entity *e, Operand *operand, String context_name);
+gb_internal Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
+gb_internal Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
+gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint);
-void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type);
-void add_map_key_type_dependencies(CheckerContext *ctx, Type *key);
+gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_);
-Type *make_soa_struct_slice(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
-Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_typ_expr, Ast *elem_expr, Type *elem);
+gb_internal void check_or_else_right_type(CheckerContext *c, Ast *expr, String const &name, Type *right_type);
+gb_internal void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_);
+gb_internal void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint);
+gb_internal void check_or_return_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_);
-bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint);
-
-void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_);
-
-void check_or_else_right_type(CheckerContext *c, Ast *expr, String const &name, Type *right_type);
-void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_);
-void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint);
-void check_or_return_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_);
-
-bool is_diverging_expr(Ast *expr);
+gb_internal bool is_diverging_expr(Ast *expr);
+gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_);
enum LoadDirectiveResult {
LoadDirective_Success = 0,
@@ -128,7 +125,7 @@ enum LoadDirectiveResult {
LoadDirective_NotFound = 2,
};
-bool is_load_directive_call(Ast *call) {
+gb_internal bool is_load_directive_call(Ast *call) {
call = unparen_expr(call);
if (call->kind != Ast_CallExpr) {
return false;
@@ -141,21 +138,21 @@ bool is_load_directive_call(Ast *call) {
String name = bd->name.string;
return name == "load";
}
-LoadDirectiveResult check_load_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint, bool err_on_not_found);
+gb_internal LoadDirectiveResult check_load_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint, bool err_on_not_found);
-void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") {
+gb_internal void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") {
auto results = did_you_mean_results(d);
if (results.count != 0) {
error_line("\tSuggestion: Did you mean?\n");
- for_array(i, results) {
- String const &target = results[i].target;
+ for (auto const &result : results) {
+ String const &target = result.target;
error_line("\t\t%s%.*s\n", prefix, LIT(target));
// error_line("\t\t%.*s %td\n", LIT(target), results[i].distance);
}
}
}
-void populate_check_did_you_mean_objc_entity(StringSet *set, Entity *e, bool is_type) {
+gb_internal void populate_check_did_you_mean_objc_entity(StringSet *set, Entity *e, bool is_type) {
if (e->kind != Entity_TypeName) {
return;
}
@@ -167,19 +164,16 @@ void populate_check_did_you_mean_objc_entity(StringSet *set, Entity *e, bool is_
GB_ASSERT(t->kind == Type_Struct);
if (is_type) {
- for_array(i, objc_metadata->type_entries) {
- String name = objc_metadata->type_entries[i].name;
- string_set_add(set, name);
+ for (auto const &entry : objc_metadata->type_entries) {
+ string_set_add(set, entry.name);
}
} else {
- for_array(i, objc_metadata->value_entries) {
- String name = objc_metadata->value_entries[i].name;
- string_set_add(set, name);
+ for (auto const &entry : objc_metadata->value_entries) {
+ string_set_add(set, entry.name);
}
}
- for_array(i, t->Struct.fields) {
- Entity *f = t->Struct.fields[i];
+ for (Entity *f : t->Struct.fields) {
if (f->flags & EntityFlag_Using && f->type != nullptr) {
if (f->type->kind == Type_Named && f->type->Named.type_name) {
populate_check_did_you_mean_objc_entity(set, f->type->Named.type_name, is_type);
@@ -189,69 +183,67 @@ void populate_check_did_you_mean_objc_entity(StringSet *set, Entity *e, bool is_
}
-void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") {
+gb_internal void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") {
ERROR_BLOCK();
GB_ASSERT(e->kind == Entity_TypeName);
GB_ASSERT(e->TypeName.objc_metadata != nullptr);
auto *objc_metadata = e->TypeName.objc_metadata;
- mutex_lock(objc_metadata->mutex);
- defer (mutex_unlock(objc_metadata->mutex));
+ MUTEX_GUARD(objc_metadata->mutex);
StringSet set = {};
- string_set_init(&set, heap_allocator());
defer (string_set_destroy(&set));
populate_check_did_you_mean_objc_entity(&set, e, is_type);
DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), set.entries.count, name);
defer (did_you_mean_destroy(&d));
- for_array(i, set.entries) {
- did_you_mean_append(&d, set.entries[i].value);
+ for (String const &target : set) {
+ did_you_mean_append(&d, target);
}
check_did_you_mean_print(&d, prefix);
}
-void check_did_you_mean_type(String const &name, Array<Entity *> const &fields, char const *prefix = "") {
+gb_internal void check_did_you_mean_type(String const &name, Array<Entity *> const &fields, char const *prefix = "") {
ERROR_BLOCK();
DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
defer (did_you_mean_destroy(&d));
- for_array(i, fields) {
- did_you_mean_append(&d, fields[i]->token.string);
+ for (Entity *e : fields) {
+ did_you_mean_append(&d, e->token.string);
}
check_did_you_mean_print(&d, prefix);
}
-void check_did_you_mean_type(String const &name, Slice<Entity *> const &fields, char const *prefix = "") {
+gb_internal void check_did_you_mean_type(String const &name, Slice<Entity *> const &fields, char const *prefix = "") {
ERROR_BLOCK();
DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
defer (did_you_mean_destroy(&d));
- for_array(i, fields) {
- did_you_mean_append(&d, fields[i]->token.string);
+ for (Entity *e : fields) {
+ did_you_mean_append(&d, e->token.string);
}
check_did_you_mean_print(&d, prefix);
}
-void check_did_you_mean_scope(String const &name, Scope *scope, char const *prefix = "") {
+gb_internal void check_did_you_mean_scope(String const &name, Scope *scope, char const *prefix = "") {
ERROR_BLOCK();
- DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), scope->elements.entries.count, name);
+ DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), scope->elements.count, name);
defer (did_you_mean_destroy(&d));
- mutex_lock(&scope->mutex);
- for_array(i, scope->elements.entries) {
- Entity *e = scope->elements.entries[i].value;
+ rw_mutex_shared_lock(&scope->mutex);
+ for (auto const &entry : scope->elements) {
+ Entity *e = entry.value;
did_you_mean_append(&d, e->token.string);
}
- mutex_unlock(&scope->mutex);
+ rw_mutex_shared_unlock(&scope->mutex);
check_did_you_mean_print(&d, prefix);
}
-Entity *entity_from_expr(Ast *expr) {
+gb_internal Entity *entity_from_expr(Ast *expr) {
expr = unparen_expr(expr);
switch (expr->kind) {
case Ast_Ident:
@@ -262,7 +254,7 @@ Entity *entity_from_expr(Ast *expr) {
return nullptr;
}
-void error_operand_not_expression(Operand *o) {
+gb_internal void error_operand_not_expression(Operand *o) {
if (o->mode == Addressing_Type) {
gbString err = expr_to_string(o->expr);
error(o->expr, "'%s' is not an expression but a type", err);
@@ -271,7 +263,7 @@ void error_operand_not_expression(Operand *o) {
}
}
-void error_operand_no_value(Operand *o) {
+gb_internal void error_operand_no_value(Operand *o) {
if (o->mode == Addressing_NoValue) {
gbString err = expr_to_string(o->expr);
Ast *x = unparen_expr(o->expr);
@@ -285,16 +277,16 @@ void error_operand_no_value(Operand *o) {
}
}
-void add_map_get_dependencies(CheckerContext *c) {
- if (build_context.use_static_map_calls) {
+gb_internal void add_map_get_dependencies(CheckerContext *c) {
+ if (build_context.dynamic_map_calls) {
+ add_package_dependency(c, "runtime", "__dynamic_map_get");
+ } else {
add_package_dependency(c, "runtime", "map_desired_position");
add_package_dependency(c, "runtime", "map_probe_distance");
- } else {
- add_package_dependency(c, "runtime", "__dynamic_map_get");
}
}
-void add_map_set_dependencies(CheckerContext *c) {
+gb_internal void add_map_set_dependencies(CheckerContext *c) {
init_core_source_code_location(c->checker);
if (t_map_set_proc == nullptr) {
@@ -302,28 +294,28 @@ void add_map_set_dependencies(CheckerContext *c) {
t_map_set_proc = alloc_type_proc_from_types(map_set_args, gb_count_of(map_set_args), t_rawptr, false, ProcCC_Odin);
}
- if (build_context.use_static_map_calls) {
+ if (build_context.dynamic_map_calls) {
+ add_package_dependency(c, "runtime", "__dynamic_map_set");
+ } else {
add_package_dependency(c, "runtime", "__dynamic_map_check_grow");
add_package_dependency(c, "runtime", "map_insert_hash_dynamic");
- } else {
- add_package_dependency(c, "runtime", "__dynamic_map_set");
}
}
-void add_map_reserve_dependencies(CheckerContext *c) {
+gb_internal void add_map_reserve_dependencies(CheckerContext *c) {
init_core_source_code_location(c->checker);
add_package_dependency(c, "runtime", "__dynamic_map_reserve");
}
-void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes, isize reserve_size) {
+gb_internal void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes, isize reserve_size) {
Scope *s = c->scope;
check_collect_entities(c, nodes);
- for_array(i, s->elements.entries) {
- Entity *e = s->elements.entries[i].value;
+ for (auto const &entry : s->elements) {
+ Entity *e = entry.value;
switch (e->kind) {
case Entity_Constant:
case Entity_TypeName:
@@ -339,8 +331,8 @@ void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes, isize reser
}
}
-bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_entity, Type *type,
- Array<Operand> *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) {
+gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_entity, Type *type,
+ Array<Operand> const *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) {
///////////////////////////////////////////////////////////////////////////////
// //
// TODO CLEANUP(bill): This procedure is very messy and hacky. Clean this!!! //
@@ -372,9 +364,6 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_
GB_ASSERT(dst == nullptr);
}
- mutex_lock(&info->gen_procs_mutex);
- defer (mutex_unlock(&info->gen_procs_mutex));
-
if (!src->Proc.is_polymorphic || src->Proc.is_poly_specialized) {
return false;
}
@@ -419,8 +408,6 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_
CheckerContext nctx = *old_c;
- nctx.procs_to_check_queue = old_c->procs_to_check_queue;
-
Scope *scope = create_scope(info, base_entity->scope);
scope->flags |= ScopeFlag_Proc;
nctx.scope = scope;
@@ -441,21 +428,39 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_
return false;
}
- auto *found_gen_procs = map_get(&info->gen_procs, base_entity->identifier.load());
- if (found_gen_procs) {
- auto procs = *found_gen_procs;
- for_array(i, procs) {
- Entity *other = procs[i];
+ GenProcsData *gen_procs = nullptr;
+
+ GB_ASSERT(base_entity->identifier.load()->kind == Ast_Ident);
+ GB_ASSERT(base_entity->kind == Entity_Procedure);
+
+ mutex_lock(&base_entity->Procedure.gen_procs_mutex); // @entity-mutex
+ gen_procs = base_entity->Procedure.gen_procs;
+ if (gen_procs) {
+ rw_mutex_shared_lock(&gen_procs->mutex); // @local-mutex
+
+ mutex_unlock(&base_entity->Procedure.gen_procs_mutex); // @entity-mutex
+
+ for (Entity *other : gen_procs->procs) {
Type *pt = base_type(other->type);
if (are_types_identical(pt, final_proc_type)) {
+ rw_mutex_shared_unlock(&gen_procs->mutex); // @local-mutex
+
if (poly_proc_data) {
poly_proc_data->gen_entity = other;
}
return true;
}
}
+
+ rw_mutex_shared_unlock(&gen_procs->mutex); // @local-mutex
+ } else {
+ gen_procs = gb_alloc_item(permanent_allocator(), GenProcsData);
+ gen_procs->procs.allocator = heap_allocator();
+ base_entity->Procedure.gen_procs = gen_procs;
+ mutex_unlock(&base_entity->Procedure.gen_procs_mutex); // @entity-mutex
}
+
{
// LEAK TODO(bill): This is technically a memory leak as it has to generate the type twice
bool prev_no_polymorphic_errors = nctx.no_polymorphic_errors;
@@ -468,24 +473,39 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_
// LEAK TODO(bill): Cloning this AST may be leaky
Ast *cloned_proc_type_node = clone_ast(pt->node);
success = check_procedure_type(&nctx, final_proc_type, cloned_proc_type_node, &operands);
-
if (!success) {
return false;
}
- if (found_gen_procs) {
- auto procs = *found_gen_procs;
- for_array(i, procs) {
- Entity *other = procs[i];
- Type *pt = base_type(other->type);
- if (are_types_identical(pt, final_proc_type)) {
- if (poly_proc_data) {
- poly_proc_data->gen_entity = other;
- }
- return true;
+ rw_mutex_shared_lock(&gen_procs->mutex); // @local-mutex
+ for (Entity *other : gen_procs->procs) {
+ Type *pt = base_type(other->type);
+ if (are_types_identical(pt, final_proc_type)) {
+ rw_mutex_shared_unlock(&gen_procs->mutex); // @local-mutex
+
+ if (poly_proc_data) {
+ poly_proc_data->gen_entity = other;
}
+
+ DeclInfo *decl = other->decl_info;
+ if (decl->proc_checked_state != ProcCheckedState_Checked) {
+ ProcInfo *proc_info = gb_alloc_item(permanent_allocator(), ProcInfo);
+ proc_info->file = other->file;
+ proc_info->token = other->token;
+ proc_info->decl = decl;
+ proc_info->type = other->type;
+ proc_info->body = decl->proc_lit->ProcLit.body;
+ proc_info->tags = other->Procedure.tags;;
+ proc_info->generated_from_polymorphic = true;
+ proc_info->poly_def_node = poly_def_node;
+
+ check_procedure_later(nctx.checker, proc_info);
+ }
+
+ return true;
}
}
+ rw_mutex_shared_unlock(&gen_procs->mutex); // @local-mutex
}
@@ -522,7 +542,8 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_
d->gen_proc_type = final_proc_type;
d->type_expr = pl->type;
d->proc_lit = proc_lit;
- d->proc_checked = false;
+ d->proc_checked_state = ProcCheckedState_Unchecked;
+ d->defer_use_checked = false;
Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags);
entity->identifier = ident;
@@ -532,7 +553,8 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_
entity->scope = scope->parent;
entity->file = base_entity->file;
entity->pkg = base_entity->pkg;
- entity->flags &= ~EntityFlag_ProcBodyChecked;
+ entity->flags = 0;
+ d->entity = entity;
AstFile *file = nullptr;
{
@@ -543,6 +565,10 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_
}
}
+ rw_mutex_lock(&gen_procs->mutex); // @local-mutex
+ array_add(&gen_procs->procs, entity);
+ rw_mutex_unlock(&gen_procs->mutex); // @local-mutex
+
ProcInfo *proc_info = gb_alloc_item(permanent_allocator(), ProcInfo);
proc_info->file = file;
proc_info->token = token;
@@ -553,13 +579,6 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_
proc_info->generated_from_polymorphic = true;
proc_info->poly_def_node = poly_def_node;
- if (found_gen_procs) {
- array_add(found_gen_procs, entity);
- } else {
- auto array = array_make<Entity *>(heap_allocator());
- array_add(&array, entity);
- map_set(&info->gen_procs, base_entity->identifier.load(), array);
- }
if (poly_proc_data) {
poly_proc_data->gen_entity = entity;
@@ -568,29 +587,29 @@ bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_
}
// NOTE(bill): Check the newly generated procedure body
- check_procedure_later(&nctx, proc_info);
+ check_procedure_later(nctx.checker, proc_info);
return true;
}
-bool check_polymorphic_procedure_assignment(CheckerContext *c, Operand *operand, Type *type, Ast *poly_def_node, PolyProcData *poly_proc_data) {
+gb_internal bool check_polymorphic_procedure_assignment(CheckerContext *c, Operand *operand, Type *type, Ast *poly_def_node, PolyProcData *poly_proc_data) {
if (operand->expr == nullptr) return false;
Entity *base_entity = entity_of_node(operand->expr);
if (base_entity == nullptr) return false;
return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_def_node, poly_proc_data);
}
-bool find_or_generate_polymorphic_procedure_from_parameters(CheckerContext *c, Entity *base_entity, Array<Operand> *operands, Ast *poly_def_node, PolyProcData *poly_proc_data) {
+gb_internal bool find_or_generate_polymorphic_procedure_from_parameters(CheckerContext *c, Entity *base_entity, Array<Operand> const *operands, Ast *poly_def_node, PolyProcData *poly_proc_data) {
return find_or_generate_polymorphic_procedure(c, base_entity, nullptr, operands, poly_def_node, poly_proc_data);
}
-bool check_type_specialization_to(CheckerContext *c, Type *specialization, Type *type, bool compound, bool modify_type);
-bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, bool compound, bool modify_type);
-bool check_cast_internal(CheckerContext *c, Operand *x, Type *type);
+gb_internal bool check_type_specialization_to(CheckerContext *c, Type *specialization, Type *type, bool compound, bool modify_type);
+gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, bool compound, bool modify_type);
+gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type);
#define MAXIMUM_TYPE_DISTANCE 10
-i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) {
+gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) {
if (c == nullptr) {
GB_ASSERT(operand->mode == Addressing_Value);
GB_ASSERT(is_type_typed(operand->type));
@@ -624,11 +643,8 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
Type *src = base_type(s);
Type *dst = base_type(type);
- if (is_type_untyped_undef(src)) {
- if (type_has_undef(dst)) {
- return 1;
- }
- return -1;
+ if (is_type_untyped_uninit(src)) {
+ return 1;
}
if (is_type_untyped_nil(src)) {
@@ -658,6 +674,11 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
return 1;
}
break;
+ case Basic_UntypedString:
+ if (is_type_string(dst)) {
+ return 1;
+ }
+ break;
case Basic_UntypedFloat:
if (is_type_float(dst)) {
return 1;
@@ -682,23 +703,49 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
}
return -1;
}
- if (src->kind == Type_Basic && src->Basic.kind == Basic_UntypedRune) {
- if (is_type_integer(dst) || is_type_rune(dst)) {
- if (is_type_typed(type)) {
- return 2;
+ if (src->kind == Type_Basic) {
+ i64 score = -1;
+ switch (src->Basic.kind) {
+ case Basic_UntypedRune:
+ if (is_type_integer(dst) || is_type_rune(dst)) {
+ score = 1;
+ }
+ break;
+ case Basic_UntypedInteger:
+ if (is_type_integer(dst) || is_type_rune(dst)) {
+ score = 1;
+ }
+ break;
+ case Basic_UntypedString:
+ if (is_type_string(dst)) {
+ score = 1;
+ }
+ break;
+ case Basic_UntypedFloat:
+ if (is_type_float(dst)) {
+ score = 1;
+ }
+ break;
+ case Basic_UntypedComplex:
+ if (is_type_complex(dst)) {
+ score = 1;
+ }
+ if (is_type_quaternion(dst)) {
+ score = 2;
}
- return 1;
+ break;
+ case Basic_UntypedQuaternion:
+ if (is_type_quaternion(dst)) {
+ score = 1;
+ }
+ break;
}
- return -1;
- }
- if (src->kind == Type_Basic && src->Basic.kind == Basic_UntypedBool) {
- if (is_type_boolean(dst)) {
- if (is_type_typed(type)) {
- return 2;
+ if (score > 0) {
+ if (is_type_typed(dst)) {
+ score += 1;
}
- return 1;
}
- return -1;
+ return score;
}
}
}
@@ -757,8 +804,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
}
if (is_type_union(dst)) {
- for_array(i, dst->Union.variants) {
- Type *vt = dst->Union.variants[i];
+ for (Type *vt : dst->Union.variants) {
if (are_types_identical(vt, s)) {
return 1;
}
@@ -774,8 +820,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
} else if (is_type_untyped(src)) {
i64 prev_lowest_score = -1;
i64 lowest_score = -1;
- for_array(i, dst->Union.variants) {
- Type *vt = dst->Union.variants[i];
+ for (Type *vt : dst->Union.variants) {
i64 score = check_distance_between_types(c, operand, vt);
if (score >= 0) {
if (lowest_score < 0) {
@@ -819,7 +864,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
PolyProcData poly_proc_data = {};
if (check_polymorphic_procedure_assignment(c, operand, type, operand->expr, &poly_proc_data)) {
Entity *e = poly_proc_data.gen_entity;
- add_type_and_value(c->info, operand->expr, Addressing_Value, e->type, {});
+ add_type_and_value(c, operand->expr, Addressing_Value, e->type, {});
add_entity_use(c, operand->expr, e);
return 4;
}
@@ -889,7 +934,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
}
-i64 assign_score_function(i64 distance, bool is_variadic=false) {
+gb_internal i64 assign_score_function(i64 distance, bool is_variadic=false) {
// 3*x^2 + 1 > x^2 + x + 1 (for positive x)
i64 const c = 3*MAXIMUM_TYPE_DISTANCE*MAXIMUM_TYPE_DISTANCE + 1;
@@ -902,7 +947,7 @@ i64 assign_score_function(i64 distance, bool is_variadic=false) {
}
-bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false) {
+gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false) {
i64 score = 0;
i64 distance = check_distance_between_types(c, operand, type);
bool ok = distance >= 0;
@@ -914,19 +959,19 @@ bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type
}
-bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) {
+gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) {
i64 score = 0;
return check_is_assignable_to_with_score(c, operand, type, &score);
}
-bool internal_check_is_assignable_to(Type *src, Type *dst) {
+gb_internal bool internal_check_is_assignable_to(Type *src, Type *dst) {
Operand x = {};
x.type = src;
x.mode = Addressing_Value;
return check_is_assignable_to(nullptr, &x, dst);
}
-AstPackage *get_package_of_type(Type *type) {
+gb_internal AstPackage *get_package_of_type(Type *type) {
for (;;) {
if (type == nullptr) {
return nullptr;
@@ -964,7 +1009,7 @@ AstPackage *get_package_of_type(Type *type) {
// NOTE(bill): 'content_name' is for debugging and error messages
-void check_assignment(CheckerContext *c, Operand *operand, Type *type, String context_name) {
+gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *type, String context_name) {
check_not_tuple(c, operand);
if (operand->mode == Addressing_Invalid) {
return;
@@ -973,13 +1018,13 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co
if (is_type_untyped(operand->type)) {
Type *target_type = type;
if (type == nullptr || is_type_any(type)) {
- if (type == nullptr && is_type_untyped_nil(operand->type)) {
- error(operand->expr, "Use of untyped nil in %.*s", LIT(context_name));
+ if (type == nullptr && is_type_untyped_uninit(operand->type)) {
+ error(operand->expr, "Use of --- in %.*s", LIT(context_name));
operand->mode = Addressing_Invalid;
return;
}
- if (type == nullptr && is_type_untyped_undef(operand->type)) {
- error(operand->expr, "Use of --- in %.*s", LIT(context_name));
+ if (type == nullptr && is_type_untyped_nil(operand->type)) {
+ error(operand->expr, "Use of untyped nil in %.*s", LIT(context_name));
operand->mode = Addressing_Invalid;
return;
}
@@ -1007,8 +1052,8 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co
if (type != nullptr && is_type_proc(type)) {
Array<Entity *> procs = proc_group_entities(c, *operand);
// NOTE(bill): These should be done
- for_array(i, procs) {
- Type *t = base_type(procs[i]->type);
+ for (Entity *e : procs) {
+ Type *t = base_type(e->type);
if (t == t_invalid) {
continue;
}
@@ -1016,7 +1061,6 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co
x.mode = Addressing_Value;
x.type = t;
if (check_is_assignable_to(c, &x, type)) {
- Entity *e = procs[i];
add_entity_use(c, operand->expr, e);
good = true;
break;
@@ -1048,8 +1092,8 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co
if (check_is_assignable_to(c, operand, type)) {
if (operand->mode == Addressing_Type && is_type_typeid(type)) {
- add_type_info_type(c, operand->type);
- add_type_and_value(c->info, operand->expr, Addressing_Value, type, exact_value_typeid(operand->type));
+ add_type_info_type(c, operand->type);
+ add_type_and_value(c, operand->expr, Addressing_Value, type, exact_value_typeid(operand->type));
}
} else {
gbString expr_str = expr_to_string(operand->expr);
@@ -1112,7 +1156,7 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co
}
}
-bool polymorphic_assign_index(Type **gt_, i64 *dst_count, i64 source_count) {
+gb_internal bool polymorphic_assign_index(Type **gt_, i64 *dst_count, i64 source_count) {
Type *gt = *gt_;
GB_ASSERT(gt->kind == Type_Generic);
@@ -1141,7 +1185,7 @@ bool polymorphic_assign_index(Type **gt_, i64 *dst_count, i64 source_count) {
return false;
}
-bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, bool compound, bool modify_type) {
+gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, bool compound, bool modify_type) {
Operand o = {Addressing_Value};
o.type = source;
switch (poly->kind) {
@@ -1441,12 +1485,12 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source,
return false;
}
-bool check_cycle(CheckerContext *c, Entity *curr, bool report) {
+gb_internal bool check_cycle(CheckerContext *c, Entity *curr, bool report) {
if (curr->state != EntityState_InProgress) {
return false;
}
for_array(i, *c->type_path) {
- Entity *prev = (*c->type_path)[i];
+ Entity *prev = c->type_path->data[i];
if (prev == curr) {
if (report) {
error(curr->token, "Illegal declaration cycle of `%.*s`", LIT(curr->token.string));
@@ -1463,7 +1507,7 @@ bool check_cycle(CheckerContext *c, Entity *curr, bool report) {
return false;
}
-Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name) {
+gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name) {
GB_ASSERT(n->kind == Ast_Ident);
o->mode = Addressing_Invalid;
o->expr = n;
@@ -1511,8 +1555,8 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ
if (type_hint != nullptr && is_type_proc(type_hint)) {
// NOTE(bill): These should be done
- for_array(i, procs) {
- Type *t = base_type(procs[i]->type);
+ for (Entity *proc : procs) {
+ Type *t = base_type(proc->type);
if (t == t_invalid) {
continue;
}
@@ -1520,7 +1564,7 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ
x.mode = Addressing_Value;
x.type = t;
if (check_is_assignable_to(c, &x, type_hint)) {
- e = procs[i];
+ e = proc;
add_entity_use(c, n, e);
skip = true;
break;
@@ -1612,7 +1656,9 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ
}
return e;
case Entity_LibraryName:
- error(n, "Use of library '%.*s' not in foreign block", LIT(name));
+ if (!allow_import_name) {
+ error(n, "Use of library '%.*s' not in foreign block", LIT(name));
+ }
return e;
case Entity_Label:
@@ -1632,7 +1678,7 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ
}
-bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
+gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
if (o->type == nullptr) {
gbString str = expr_to_string(o->expr);
error(o->expr, "Expression has no value '%s'", str);
@@ -1660,9 +1706,15 @@ bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
case Token_Not:
if (!is_type_boolean(type)) {
+ ERROR_BLOCK();
str = expr_to_string(o->expr);
- error(op, "Operator '%.*s' is only allowed on boolean expression", LIT(op.string));
+ error(op, "Operator '%.*s' is only allowed on boolean expressions", LIT(op.string));
gb_string_free(str);
+ if (is_type_integer(type)) {
+ error_line("\tSuggestion: Did you mean to use the bitwise not operator '~'?\n");
+ }
+ } else {
+ o->type = t_untyped_bool;
}
break;
@@ -1674,7 +1726,7 @@ bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
return true;
}
-bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
+gb_internal bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
Type *main_type = o->type;
// TODO(bill): Handle errors correctly
@@ -1785,7 +1837,7 @@ bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
}
-bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Type *type, ExactValue *out_value) {
+gb_internal bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Type *type, ExactValue *out_value) {
if (in_value.kind == ExactValue_Invalid) {
// NOTE(bill): There's already been an error
return true;
@@ -2000,7 +2052,48 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ
}
-void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
+gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, Type *type) {
+ if (is_type_integer(type) && o->value.kind == ExactValue_Integer) {
+ gbString b = type_to_string(type);
+
+ i64 sz = type_size_of(type);
+ BigInt *bi = &o->value.value_integer;
+ if (is_type_unsigned(type)) {
+ if (big_int_is_neg(bi)) {
+ error_line("\tA negative value cannot be represented by the unsigned integer type '%s'\n", b);
+ } else {
+ BigInt one = big_int_make_u64(1);
+ BigInt max_size = big_int_make_u64(1);
+ BigInt bits = big_int_make_i64(8*sz);
+ big_int_shl_eq(&max_size, &bits);
+ big_int_sub_eq(&max_size, &one);
+ String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
+ error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+ }
+ } else {
+ BigInt zero = big_int_make_u64(0);
+ BigInt one = big_int_make_u64(1);
+ BigInt max_size = big_int_make_u64(1);
+ BigInt bits = big_int_make_i64(8*sz - 1);
+ big_int_shl_eq(&max_size, &bits);
+ if (big_int_is_neg(bi)) {
+ big_int_neg(&max_size, &max_size);
+ String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
+ error_line("\tThe minimum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+ } else {
+ big_int_sub_eq(&max_size, &one);
+ String max_size_str = big_int_to_string(temporary_allocator(), &max_size);
+ error_line("\tThe maximum value that can be represented by '%s' is '%.*s'\n", b, LIT(max_size_str));
+ }
+ }
+
+ gb_string_free(b);
+
+ return true;
+ }
+ return false;
+}
+gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
gbString a = expr_to_string(o->expr);
gbString b = type_to_string(type);
defer(
@@ -2031,10 +2124,12 @@ void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type
error_line("\t whereas slices in general are assumed to be mutable.\n");
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
error_line("\tSuggestion: the expression may be casted to %s\n", b);
+ } else if (check_integer_exceed_suggestion(c, o, type)) {
+ return;
}
}
-void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
+gb_internal void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
gbString a = expr_to_string(o->expr);
gbString b = type_to_string(type);
defer(
@@ -2070,13 +2165,13 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
}
} else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) {
error_line("\tSuggestion: a string may be transmuted to %s\n", b);
- } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
- error_line("\tSuggestion: the expression may be casted to %s\n", b);
+ } else if (check_integer_exceed_suggestion(c, o, type)) {
+ return;
}
}
-bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
+gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
GB_ASSERT(o->mode == Addressing_Constant);
ExactValue out_value = o->value;
if (is_type_constant_type(type) && check_representable_as_constant(ctx, o->value, type, &out_value)) {
@@ -2097,16 +2192,22 @@ bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
o->mode = Addressing_Invalid;
);
+ ERROR_BLOCK();
+
+
if (is_type_numeric(o->type) && is_type_numeric(type)) {
if (!is_type_integer(o->type) && is_type_integer(type)) {
error(o->expr, "'%s' truncated to '%s', got %s", a, b, s);
} else {
- ERROR_BLOCK();
- error(o->expr, "Cannot convert numeric value '%s' to '%s' from '%s', got %s", a, b, c, s);
+ if (are_types_identical(o->type, type)) {
+ error(o->expr, "Numeric value '%s' from '%s' cannot be represented by '%s'", s, a, b);
+ } else {
+ error(o->expr, "Cannot convert numeric value '%s' from '%s' to '%s' from '%s'", s, a, b, c);
+ }
+
check_assignment_error_suggestion(ctx, o, type);
}
} else {
- ERROR_BLOCK();
error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s);
check_assignment_error_suggestion(ctx, o, type);
}
@@ -2114,7 +2215,7 @@ bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *type) {
}
}
-bool check_is_not_addressable(CheckerContext *c, Operand *o) {
+gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) {
if (o->mode == Addressing_OptionalOk) {
Ast *expr = unselector_expr(o->expr);
if (expr->kind != Ast_TypeAssertion) {
@@ -2145,7 +2246,7 @@ bool check_is_not_addressable(CheckerContext *c, Operand *o) {
return o->mode != Addressing_Variable && o->mode != Addressing_SoaVariable;
}
-void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
+gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
switch (op.kind) {
case Token_And: { // Pointer address
if (check_is_not_addressable(c, o)) {
@@ -2272,7 +2373,7 @@ void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
o->mode = Addressing_Value;
}
-void add_comparison_procedures_for_fields(CheckerContext *c, Type *t) {
+gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t) {
if (t == nullptr) {
return;
}
@@ -2325,7 +2426,7 @@ void add_comparison_procedures_for_fields(CheckerContext *c, Type *t) {
}
-void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
+gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Operand *y, TokenKind op) {
if (x->mode == Addressing_Type && y->mode == Addressing_Type) {
bool comp = are_types_identical(x->type, y->type);
switch (op) {
@@ -2341,7 +2442,7 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
if (x->mode == Addressing_Type && is_type_typeid(y->type)) {
add_type_info_type(c, x->type);
add_type_info_type(c, y->type);
- add_type_and_value(c->info, x->expr, Addressing_Value, y->type, exact_value_typeid(x->type));
+ add_type_and_value(c, x->expr, Addressing_Value, y->type, exact_value_typeid(x->type));
x->mode = Addressing_Value;
x->type = t_untyped_bool;
@@ -2349,14 +2450,14 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
} else if (is_type_typeid(x->type) && y->mode == Addressing_Type) {
add_type_info_type(c, x->type);
add_type_info_type(c, y->type);
- add_type_and_value(c->info, y->expr, Addressing_Value, x->type, exact_value_typeid(y->type));
+ add_type_and_value(c, y->expr, Addressing_Value, x->type, exact_value_typeid(y->type));
x->mode = Addressing_Value;
x->type = t_untyped_bool;
return;
}
-
+ TEMPORARY_ALLOCATOR_GUARD();
gbString err_str = nullptr;
if (check_is_assignable_to(c, x, y->type) ||
@@ -2383,13 +2484,11 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
}
if (!defined) {
- if (x->type == err_type && is_operand_nil(*x)) {
- err_type = y->type;
- }
- gbString type_string = type_to_string(err_type);
- defer (gb_string_free(type_string));
+ gbString xs = type_to_string(x->type, temporary_allocator());
+ gbString ys = type_to_string(y->type, temporary_allocator());
err_str = gb_string_make(temporary_allocator(),
- gb_bprintf("operator '%.*s' not defined for type '%s'", LIT(token_strings[op]), type_string));
+ gb_bprintf("operator '%.*s' not defined between the types '%s' and '%s'", LIT(token_strings[op]), xs, ys)
+ );
} else {
Type *comparison_type = x->type;
if (x->type == err_type && is_operand_nil(*x)) {
@@ -2401,22 +2500,20 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
} else {
gbString xt, yt;
if (x->mode == Addressing_ProcGroup) {
- xt = gb_string_make(heap_allocator(), "procedure group");
+ xt = gb_string_make(temporary_allocator(), "procedure group");
} else {
xt = type_to_string(x->type);
}
if (y->mode == Addressing_ProcGroup) {
- yt = gb_string_make(heap_allocator(), "procedure group");
+ yt = gb_string_make(temporary_allocator(), "procedure group");
} else {
yt = type_to_string(y->type);
}
err_str = gb_string_make(temporary_allocator(), gb_bprintf("mismatched types '%s' and '%s'", xt, yt));
- gb_string_free(yt);
- gb_string_free(xt);
}
if (err_str != nullptr) {
- error(x->expr, "Cannot compare expression, %s", err_str);
+ error(node, "Cannot compare expression, %s", err_str);
x->type = t_untyped_bool;
} else {
if (x->mode == Addressing_Constant &&
@@ -2518,7 +2615,7 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) {
}
-void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *type_hint) {
+gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *type_hint) {
GB_ASSERT(node->kind == Ast_BinaryExpr);
ast_node(be, BinaryExpr, node);
@@ -2581,10 +2678,12 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
x->type = t_untyped_integer;
}
+ x->expr = node;
x->value = exact_value_shift(be->op.kind, x_val, y_val);
+
if (is_type_typed(x->type)) {
- check_is_expressible(c, x, base_type(x->type));
+ check_is_expressible(c, x, x->type);
}
return;
}
@@ -2637,7 +2736,7 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
-bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
+gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
if (check_is_assignable_to(c, operand, y)) {
return true;
}
@@ -2853,7 +2952,7 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) {
return false;
}
-bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) {
+gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) {
bool is_const_expr = x->mode == Addressing_Constant;
Type *bt = base_type(type);
@@ -2888,7 +2987,7 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) {
}
-void check_cast(CheckerContext *c, Operand *x, Type *type) {
+gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
if (!is_operand_value(*x)) {
error(x->expr, "Only values can be casted");
x->mode = Addressing_Invalid;
@@ -2899,17 +2998,38 @@ void check_cast(CheckerContext *c, Operand *x, Type *type) {
bool can_convert = check_cast_internal(c, x, type);
if (!can_convert) {
- gbString expr_str = expr_to_string(x->expr);
- gbString to_type = type_to_string(type);
- gbString from_type = type_to_string(x->type);
+ TEMPORARY_ALLOCATOR_GUARD();
+ gbString expr_str = expr_to_string(x->expr, temporary_allocator());
+ gbString to_type = type_to_string(type, temporary_allocator());
+ gbString from_type = type_to_string(x->type, temporary_allocator());
+
+ x->mode = Addressing_Invalid;
+
+ begin_error_block();
error(x->expr, "Cannot cast '%s' as '%s' from '%s'", expr_str, to_type, from_type);
- gb_string_free(from_type);
- gb_string_free(to_type);
- gb_string_free(expr_str);
+ if (is_const_expr) {
+ gbString val_str = exact_value_to_string(x->value);
+ if (is_type_float(x->type) && is_type_integer(type)) {
+ error_line("\t%s cannot be represented without truncation/rounding as the type '%s'\n", val_str, to_type);
+
+ // NOTE(bill): keep the mode and modify the type to minimize errors further on
+ x->mode = Addressing_Constant;
+ x->type = type;
+ } else {
+ error_line("\t'%s' cannot be represented as the type '%s'\n", val_str, to_type);
+ if (is_type_numeric(type)) {
+ // NOTE(bill): keep the mode and modify the type to minimize errors further on
+ x->mode = Addressing_Constant;
+ x->type = type;
+ }
+ }
+ gb_string_free(val_str);
+ }
check_cast_error_suggestion(c, x, type);
- x->mode = Addressing_Invalid;
+ end_error_block();
+
return;
}
@@ -2932,7 +3052,7 @@ void check_cast(CheckerContext *c, Operand *x, Type *type) {
x->type = type;
}
-bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
+gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
if (!is_operand_value(*o)) {
error(o->expr, "'transmute' can only be applied to values");
o->mode = Addressing_Invalid;
@@ -3045,7 +3165,7 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
return true;
}
-bool check_binary_array_expr(CheckerContext *c, Token op, Operand *x, Operand *y) {
+gb_internal bool check_binary_array_expr(CheckerContext *c, Token op, Operand *x, Operand *y) {
if (is_type_array(x->type) && !is_type_array(y->type)) {
if (check_is_assignable_to(c, y, x->type)) {
if (check_binary_op(c, x, op)) {
@@ -3056,19 +3176,19 @@ bool check_binary_array_expr(CheckerContext *c, Token op, Operand *x, Operand *y
return false;
}
-bool is_ise_expr(Ast *node) {
+gb_internal bool is_ise_expr(Ast *node) {
node = unparen_expr(node);
return node->kind == Ast_ImplicitSelectorExpr;
}
-bool can_use_other_type_as_type_hint(bool use_lhs_as_type_hint, Type *other_type) {
+gb_internal bool can_use_other_type_as_type_hint(bool use_lhs_as_type_hint, Type *other_type) {
if (use_lhs_as_type_hint) { // RHS in this case
return other_type != nullptr && other_type != t_invalid && is_type_typed(other_type);
}
return false;
}
-Type *check_matrix_type_hint(Type *matrix, Type *type_hint) {
+gb_internal Type *check_matrix_type_hint(Type *matrix, Type *type_hint) {
Type *xt = base_type(matrix);
if (type_hint != nullptr) {
Type *th = base_type(type_hint);
@@ -3088,7 +3208,7 @@ Type *check_matrix_type_hint(Type *matrix, Type *type_hint) {
}
-void check_binary_matrix(CheckerContext *c, Token const &op, Operand *x, Operand *y, Type *type_hint, bool use_lhs_as_type_hint) {
+gb_internal void check_binary_matrix(CheckerContext *c, Token const &op, Operand *x, Operand *y, Type *type_hint, bool use_lhs_as_type_hint) {
if (!check_binary_op(c, x, op)) {
x->mode = Addressing_Invalid;
return;
@@ -3199,7 +3319,7 @@ matrix_error:
}
-void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint, bool use_lhs_as_type_hint=false) {
+gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint, bool use_lhs_as_type_hint=false) {
GB_ASSERT(node->kind == Ast_BinaryExpr);
Operand y_ = {}, *y = &y_;
@@ -3406,7 +3526,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
if (token_is_comparison(op.kind)) {
- check_comparison(c, x, y, op.kind);
+ check_comparison(c, node, x, y, op.kind);
return;
}
@@ -3565,7 +3685,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
x->mode = Addressing_Value;
}
-Operand make_operand_from_node(Ast *node) {
+gb_internal Operand make_operand_from_node(Ast *node) {
GB_ASSERT(node != nullptr);
Operand x = {};
x.expr = node;
@@ -3576,13 +3696,13 @@ Operand make_operand_from_node(Ast *node) {
}
-void update_untyped_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
+gb_internal void update_untyped_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
GB_ASSERT(e != nullptr);
ExprInfo *old = check_get_expr_info(c, 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->info, e, e->tav.mode, type ? type : e->tav.type, e->tav.value);
+ add_type_and_value(c, e, e->tav.mode, type ? type : e->tav.type, e->tav.value);
if (e->kind == Ast_TernaryIfExpr) {
update_untyped_expr_type(c, e->TernaryIfExpr.x, type, final);
update_untyped_expr_type(c, e->TernaryIfExpr.y, type, final);
@@ -3688,10 +3808,10 @@ void update_untyped_expr_type(CheckerContext *c, Ast *e, Type *type, bool final)
return;
}
- add_type_and_value(c->info, e, old->mode, type, old->value);
+ add_type_and_value(c, e, old->mode, type, old->value);
}
-void update_untyped_expr_value(CheckerContext *c, Ast *e, ExactValue value) {
+gb_internal void update_untyped_expr_value(CheckerContext *c, Ast *e, ExactValue value) {
GB_ASSERT(e != nullptr);
ExprInfo *found = check_get_expr_info(c, e);
if (found) {
@@ -3699,7 +3819,7 @@ void update_untyped_expr_value(CheckerContext *c, Ast *e, ExactValue value) {
}
}
-void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type) {
+gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_type) {
gbString expr_str = expr_to_string(operand->expr);
gbString type_str = type_to_string(target_type);
gbString from_type_str = type_to_string(operand->type);
@@ -3730,7 +3850,7 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ
operand->mode = Addressing_Invalid;
}
-ExactValue convert_exact_value_for_type(ExactValue v, Type *type) {
+gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) {
Type *t = core_type(type);
if (is_type_boolean(t)) {
// v = exact_value_to_boolean(v);
@@ -3748,7 +3868,7 @@ ExactValue convert_exact_value_for_type(ExactValue v, Type *type) {
return v;
}
-void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
+gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
GB_ASSERT_NOT_NULL(target_type);
if (operand->mode == Addressing_Invalid ||
operand->mode == Addressing_Type ||
@@ -3874,7 +3994,9 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
case Type_Union:
- if (!is_operand_nil(*operand) && !is_operand_undef(*operand)) {
+ if (!is_operand_nil(*operand) && !is_operand_uninit(*operand)) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
isize count = t->Union.variants.count;
ValidIndexAndScore *valids = gb_alloc_array(temporary_allocator(), ValidIndexAndScore, count);
isize valid_count = 0;
@@ -3939,8 +4061,8 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
error_line("\n\n");
return;
- } else if (is_type_untyped_undef(operand->type) && type_has_undef(target_type)) {
- target_type = t_untyped_undef;
+ } else if (is_type_untyped_uninit(operand->type)) {
+ target_type = t_untyped_uninit;
} else if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) {
begin_error_block();
defer (end_error_block());
@@ -3973,8 +4095,8 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
default:
- if (is_type_untyped_undef(operand->type) && type_has_undef(target_type)) {
- target_type = t_untyped_undef;
+ if (is_type_untyped_uninit(operand->type)) {
+ target_type = t_untyped_uninit;
} else if (is_type_untyped_nil(operand->type) && type_has_nil(target_type)) {
target_type = t_untyped_nil;
} else {
@@ -3986,8 +4108,8 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
}
if (is_type_any(target_type) && is_type_untyped(operand->type)) {
- if (is_type_untyped_nil(operand->type) && is_type_untyped_undef(operand->type)) {
-
+ if (is_type_untyped_nil(operand->type) && is_type_untyped_uninit(operand->type)) {
+
} else {
target_type = default_type(operand->type);
}
@@ -3997,7 +4119,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
operand->type = target_type;
}
-bool check_index_value(CheckerContext *c, Type *main_type, bool open_range, Ast *index_value, i64 max_count, i64 *value, Type *type_hint=nullptr) {
+gb_internal bool check_index_value(CheckerContext *c, Type *main_type, bool open_range, Ast *index_value, i64 max_count, i64 *value, Type *type_hint=nullptr) {
Operand operand = {Addressing_Invalid};
check_expr_with_type_hint(c, &operand, index_value, type_hint);
if (operand.mode == Addressing_Invalid) {
@@ -4039,10 +4161,10 @@ bool check_index_value(CheckerContext *c, Type *main_type, bool open_range, Ast
(c->state_flags & StateFlag_no_bounds_check) == 0) {
BigInt i = exact_value_to_integer(operand.value).value_integer;
if (i.sign && !is_type_enum(index_type) && !is_type_multi_pointer(main_type)) {
+ TEMPORARY_ALLOCATOR_GUARD();
String idx_str = big_int_to_string(temporary_allocator(), &i);
- gbString expr_str = expr_to_string(operand.expr);
+ gbString expr_str = expr_to_string(operand.expr, temporary_allocator());
error(operand.expr, "Index '%s' cannot be a negative value, got %.*s", expr_str, LIT(idx_str));
- gb_string_free(expr_str);
if (value) *value = 0;
return false;
}
@@ -4102,10 +4224,10 @@ bool check_index_value(CheckerContext *c, Type *main_type, bool open_range, Ast
}
if (out_of_bounds) {
+ TEMPORARY_ALLOCATOR_GUARD();
String idx_str = big_int_to_string(temporary_allocator(), &i);
- gbString expr_str = expr_to_string(operand.expr);
+ gbString expr_str = expr_to_string(operand.expr, temporary_allocator());
error(operand.expr, "Index '%s' is out of bounds range 0..<%lld, got %.*s", expr_str, max_count, LIT(idx_str));
- gb_string_free(expr_str);
return false;
}
@@ -4123,7 +4245,7 @@ bool check_index_value(CheckerContext *c, Type *main_type, bool open_range, Ast
return true;
}
-ExactValue get_constant_field_single(CheckerContext *c, ExactValue value, i32 index, bool *success_, bool *finish_) {
+gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue value, i32 index, bool *success_, bool *finish_) {
if (value.kind == ExactValue_String) {
GB_ASSERT(0 <= index && index < value.value_string.len);
u8 val = value.value_string[index];
@@ -4150,8 +4272,7 @@ ExactValue get_constant_field_single(CheckerContext *c, ExactValue value, i32 in
if (cl->elems[0]->kind == Ast_FieldValue) {
if (is_type_struct(node->tav.type)) {
bool found = false;
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
+ for (Ast *elem : cl->elems) {
if (elem->kind != Ast_FieldValue) {
continue;
}
@@ -4170,8 +4291,7 @@ ExactValue get_constant_field_single(CheckerContext *c, ExactValue value, i32 in
value = {};
}
} else if (is_type_array(node->tav.type) || is_type_enumerated_array(node->tav.type)) {
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
+ for (Ast *elem : cl->elems) {
if (elem->kind != Ast_FieldValue) {
continue;
}
@@ -4271,7 +4391,7 @@ ExactValue get_constant_field_single(CheckerContext *c, ExactValue value, i32 in
-ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selection sel, bool *success_) {
+gb_internal ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selection sel, bool *success_) {
if (operand->mode != Addressing_Constant) {
if (success_) *success_ = false;
return empty_exact_value;
@@ -4351,7 +4471,7 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti
return empty_exact_value;
}
-Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize new_count) {
+gb_internal Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize new_count) {
Type *array_type = base_type(type_deref(original_type));
GB_ASSERT(array_type->kind == Type_Array || array_type->kind == Type_SimdVector);
if (array_type->kind == Type_SimdVector) {
@@ -4378,7 +4498,7 @@ Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize n
}
-bool is_entity_declared_for_selector(Entity *entity, Scope *import_scope, bool *allow_builtin) {
+gb_internal bool is_entity_declared_for_selector(Entity *entity, Scope *import_scope, bool *allow_builtin) {
bool is_declared = entity != nullptr;
if (is_declared) {
if (entity->kind == Entity_Builtin) {
@@ -4393,7 +4513,7 @@ bool is_entity_declared_for_selector(Entity *entity, Scope *import_scope, bool *
}
// NOTE(bill, 2022-02-03): see `check_const_decl` for why it exists reasoning
-Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node, bool ident_only) {
+gb_internal Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node, bool ident_only) {
if (node->kind == Ast_Ident) {
String name = node->Ident.token.string;
return scope_lookup(c->scope, name);
@@ -4473,7 +4593,7 @@ Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node, bool i
}
-Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *type_hint) {
+gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *type_hint) {
ast_node(se, SelectorExpr, node);
bool check_op_expr = true;
@@ -4523,7 +4643,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
entity = scope_lookup_current(import_scope, entity_name);
bool allow_builtin = false;
if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) {
- error(op_expr, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name));
+ error(node, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name));
operand->mode = Addressing_Invalid;
operand->expr = node;
@@ -4536,14 +4656,14 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
operand->mode = Addressing_ProcGroup;
operand->proc_group = entity;
- add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value);
+ add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
return entity;
}
GB_ASSERT_MSG(entity->type != nullptr, "%.*s (%.*s)", LIT(entity->token.string), LIT(entity_strings[entity->kind]));
if (!is_entity_exported(entity, allow_builtin)) {
gbString sel_str = expr_to_string(selector);
- error(op_expr, "'%s' is not exported by '%.*s'", sel_str, LIT(import_name));
+ error(node, "'%s' is not exported by '%.*s'", sel_str, LIT(import_name));
gb_string_free(sel_str);
// NOTE(bill): make the state valid still, even if it's "invalid"
// operand->mode = Addressing_Invalid;
@@ -4554,8 +4674,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
if (entity->kind == Entity_ProcGroup) {
Array<Entity *> procs = entity->ProcGroup.entities;
bool skip = false;
- for_array(i, procs) {
- Entity *p = procs[i];
+ for (Entity *p : procs) {
Type *t = base_type(p->type);
if (t == t_invalid) {
continue;
@@ -4705,7 +4824,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
}
Entity *swizzle_entity = alloc_entity_variable(nullptr, make_token_ident(field_name), operand->type, EntityState_Resolved);
- add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value);
+ add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
return swizzle_entity;
}
end_of_array_selector_swizzle:;
@@ -4715,20 +4834,29 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
gbString op_str = expr_to_string(op_expr);
gbString type_str = type_to_string_shorthand(operand->type);
gbString sel_str = expr_to_string(selector);
- error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
- if (operand->type != nullptr && selector->kind == Ast_Ident) {
- String const &name = selector->Ident.token.string;
- Type *bt = base_type(operand->type);
- if (operand->type->kind == Type_Named &&
- operand->type->Named.type_name &&
- operand->type->Named.type_name->kind == Entity_TypeName &&
- operand->type->Named.type_name->TypeName.objc_metadata) {
- check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type);
- } else if (bt->kind == Type_Struct) {
- check_did_you_mean_type(name, bt->Struct.fields);
- } else if (bt->kind == Type_Enum) {
- check_did_you_mean_type(name, bt->Enum.fields);
+ if (operand->mode == Addressing_Type) {
+ if (is_type_polymorphic(operand->type, true)) {
+ error(op_expr, "Type '%s' has no field nor polymorphic parameter '%s'", op_str, sel_str);
+ } else {
+ error(op_expr, "Type '%s' has no field '%s'", op_str, sel_str);
+ }
+ } else {
+ error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
+
+ if (operand->type != nullptr && selector->kind == Ast_Ident) {
+ String const &name = selector->Ident.token.string;
+ Type *bt = base_type(operand->type);
+ if (operand->type->kind == Type_Named &&
+ operand->type->Named.type_name &&
+ operand->type->Named.type_name->kind == Entity_TypeName &&
+ operand->type->Named.type_name->TypeName.objc_metadata) {
+ check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type);
+ } else if (bt->kind == Type_Struct) {
+ check_did_you_mean_type(name, bt->Struct.fields);
+ } else if (bt->kind == Type_Enum) {
+ check_did_you_mean_type(name, bt->Enum.fields);
+ }
}
}
@@ -4749,7 +4877,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
operand->value = field_value;
operand->type = entity->type;
add_entity_use(c, selector, entity);
- add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value);
+ add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
return entity;
}
@@ -4774,7 +4902,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
operand->value = field_value;
operand->type = entity->type;
add_entity_use(c, selector, entity);
- add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value);
+ add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
return entity;
}
@@ -4862,12 +4990,12 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
break;
}
- add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value);
+ add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
return entity;
}
-bool is_type_normal_pointer(Type *ptr, Type **elem) {
+gb_internal bool is_type_normal_pointer(Type *ptr, Type **elem) {
ptr = base_type(ptr);
if (is_type_pointer(ptr)) {
if (is_type_rawptr(ptr)) {
@@ -4879,7 +5007,21 @@ bool is_type_normal_pointer(Type *ptr, Type **elem) {
return false;
}
-bool check_identifier_exists(Scope *s, Ast *node, bool nested = false, Scope **out_scope = nullptr) {
+gb_internal bool is_type_valid_atomic_type(Type *elem) {
+ elem = core_type(elem);
+ if (is_type_internally_pointer_like(elem)) {
+ return true;
+ }
+ if (elem->kind == Type_BitSet) {
+ elem = bit_set_to_int(elem);
+ }
+ if (elem->kind != Type_Basic) {
+ return false;
+ }
+ return (elem->Basic.flags & (BasicFlag_Boolean|BasicFlag_OrderedNumeric)) != 0;
+}
+
+gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = false, Scope **out_scope = nullptr) {
switch (node->kind) {
case_ast_node(i, Ident, node);
String name = i->token.string;
@@ -4909,33 +5051,47 @@ bool check_identifier_exists(Scope *s, Ast *node, bool nested = false, Scope **o
return false;
}
-isize add_dependencies_from_unpacking(CheckerContext *c, Entity **lhs, isize lhs_count, isize tuple_index, isize tuple_count) {
+gb_internal isize add_dependencies_from_unpacking(CheckerContext *c, Entity **lhs, isize lhs_count, isize tuple_index, isize tuple_count) {
if (lhs != nullptr && c->decl != nullptr) {
- mutex_lock(&c->info->deps_mutex);
-
for (isize j = 0; (tuple_index + j) < lhs_count && j < tuple_count; j++) {
Entity *e = lhs[tuple_index + j];
if (e != nullptr) {
DeclInfo *decl = decl_info_of_entity(e);
if (decl != nullptr) {
- for_array(k, decl->deps.entries) {
- Entity *dep = decl->deps.entries[k].ptr;
+ rw_mutex_shared_lock(&decl->deps_mutex);
+ rw_mutex_lock(&c->decl->deps_mutex);
+ for (Entity *dep : decl->deps) {
ptr_set_add(&c->decl->deps, dep);
}
+ rw_mutex_unlock(&c->decl->deps_mutex);
+ rw_mutex_shared_unlock(&decl->deps_mutex);
}
}
}
-
- mutex_unlock(&c->info->deps_mutex);
}
return tuple_count;
}
+gb_internal bool check_no_copy_assignment(Operand const &o, String const &context) {
+ if (o.type && is_type_no_copy(o.type)) {
+ Ast *expr = unparen_expr(o.expr);
+ if (expr && o.mode != Addressing_Constant) {
+ if (expr->kind == Ast_CallExpr) {
+ // Okay
+ } else {
+ error(o.expr, "Invalid use of #no_copy value in %.*s", LIT(context));
+ return true;
+ }
+ }
+ }
+ return false;
+}
-bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs, Array<Operand> *operands, Slice<Ast *> const &rhs) {
+
+gb_internal bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs, Array<Operand> *operands, Slice<Ast *> const &rhs) {
bool optional_ok = false;
isize tuple_index = 0;
- for_array(i, rhs) {
+ for (Ast *rhs_expr : rhs) {
CheckerContext c_ = *ctx;
CheckerContext *c = &c_;
@@ -4947,7 +5103,7 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
type_hint = lhs[tuple_index].type;
}
- check_expr_base(c, &o, rhs[i], type_hint);
+ check_expr_base(c, &o, rhs_expr, type_hint);
if (o.mode == Addressing_NoValue) {
error_operand_no_value(&o);
o.mode = Addressing_Invalid;
@@ -4999,9 +5155,10 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
}
} else {
TypeTuple *tuple = &o.type->Tuple;
- for_array(j, tuple->variables) {
- o.type = tuple->variables[j]->type;
+ for (Entity *e : tuple->variables) {
+ o.type = e->type;
array_add(operands, o);
+ check_no_copy_assignment(o, str_lit("assignment"));
}
tuple_index += tuple->variables.count;
@@ -5012,11 +5169,28 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
}
+typedef u32 UnpackFlags;
+enum UnpackFlag : u32 {
+ UnpackFlag_None = 0,
+ UnpackFlag_AllowOk = 1<<0,
+ UnpackFlag_IsVariadic = 1<<1,
+ UnpackFlag_AllowUndef = 1<<2,
+};
+
+
+gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs_arguments, UnpackFlags flags) {
+ bool allow_ok = (flags & UnpackFlag_AllowOk) != 0;
+ bool is_variadic = (flags & UnpackFlag_IsVariadic) != 0;
+ bool allow_undef = (flags & UnpackFlag_AllowUndef) != 0;
-bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs, bool allow_ok, bool is_variadic) {
bool optional_ok = false;
isize tuple_index = 0;
- for_array(i, rhs) {
+ for (Ast *rhs : rhs_arguments) {
+ if (rhs->kind == Ast_FieldValue) {
+ error(rhs, "Invalid use of 'field = value'");
+ rhs = rhs->FieldValue.value;
+ }
+
CheckerContext c_ = *ctx;
CheckerContext *c = &c_;
@@ -5024,12 +5198,11 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
Type *type_hint = nullptr;
+
if (lhs != nullptr && tuple_index < lhs_count) {
// NOTE(bill): override DeclInfo for dependency
Entity *e = lhs[tuple_index];
if (e != nullptr) {
- // DeclInfo *decl = decl_info_of_entity(e);
- // if (decl) c->decl = decl;
type_hint = e->type;
if (e->flags & EntityFlag_Ellipsis) {
GB_ASSERT(is_type_slice(e->type));
@@ -5041,8 +5214,6 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
// NOTE(bill): override DeclInfo for dependency
Entity *e = lhs[lhs_count-1];
if (e != nullptr) {
- // DeclInfo *decl = decl_info_of_entity(e);
- // if (decl) c->decl = decl;
type_hint = e->type;
if (e->flags & EntityFlag_Ellipsis) {
GB_ASSERT(is_type_slice(e->type));
@@ -5052,14 +5223,23 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
}
}
- check_expr_base(c, &o, rhs[i], type_hint);
+ Ast *rhs_expr = unparen_expr(rhs);
+ if (allow_undef && rhs_expr != nullptr && rhs_expr->kind == Ast_Uninit) {
+ // NOTE(bill): Just handle this very specific logic here
+ o.type = t_untyped_uninit;
+ o.mode = Addressing_Value;
+ o.expr = rhs;
+ add_type_and_value(c, rhs, o.mode, o.type, o.value);
+ } else {
+ check_expr_base(c, &o, rhs, type_hint);
+ }
if (o.mode == Addressing_NoValue) {
error_operand_no_value(&o);
o.mode = Addressing_Invalid;
}
if (o.type == nullptr || o.type->kind != Type_Tuple) {
- if (allow_ok && lhs_count == 2 && rhs.count == 1 &&
+ if (allow_ok && lhs_count == 2 && rhs_arguments.count == 1 &&
(o.mode == Addressing_MapIndex || o.mode == Addressing_OptionalOk || o.mode == Addressing_OptionalOkPtr)) {
Ast *expr = unparen_expr(o.expr);
@@ -5092,8 +5272,8 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
}
} else {
TypeTuple *tuple = &o.type->Tuple;
- for_array(j, tuple->variables) {
- o.type = tuple->variables[j]->type;
+ for (Entity *e : tuple->variables) {
+ o.type = e->type;
array_add(operands, o);
}
@@ -5105,17 +5285,7 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
return optional_ok;
}
-
-bool is_expr_constant_zero(Ast *expr) {
- GB_ASSERT(expr != nullptr);
- auto v = exact_value_to_integer(expr->tav.value);
- if (v.kind == ExactValue_Integer) {
- return big_int_cmp_zero(&v.value_integer) == 0;
- }
- return false;
-}
-
-isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_) {
+gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_) {
GB_ASSERT(pt != nullptr);
GB_ASSERT(pt->kind == Type_Proc);
isize param_count = 0;
@@ -5169,7 +5339,36 @@ isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_
}
-CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
+gb_internal isize lookup_procedure_parameter(TypeProc *pt, String const &parameter_name) {
+ isize param_count = pt->param_count;
+ for (isize i = 0; i < param_count; i++) {
+ Entity *e = pt->params->Tuple.variables[i];
+ String name = e->token.string;
+ if (is_blank_ident(name)) {
+ continue;
+ }
+ if (name == parameter_name) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+gb_internal isize lookup_procedure_parameter(Type *type, String const &parameter_name) {
+ type = base_type(type);
+ GB_ASSERT(type->kind == Type_Proc);
+ return lookup_procedure_parameter(&type->Proc, parameter_name);
+}
+
+gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, Ast *call,
+ Entity *entity, Type *proc_type,
+ Array<Operand> positional_operands, Array<Operand> const &named_operands,
+ CallArgumentErrorMode show_error_mode,
+ CallArgumentData *data) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
+ CallArgumentError err = CallArgumentError_None;
+
ast_node(ce, CallExpr, call);
GB_ASSERT(is_type_proc(proc_type));
proc_type = base_type(proc_type);
@@ -5180,16 +5379,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
bool variadic = pt->variadic;
bool vari_expand = (ce->ellipsis.pos.line != 0);
i64 score = 0;
- bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
-
+ bool show_error = show_error_mode == CallArgumentErrorMode::ShowErrors;
- TypeTuple *param_tuple = nullptr;
- if (pt->params != nullptr) {
- param_tuple = &pt->params->Tuple;
- }
-
-
- CallArgumentError err = CallArgumentError_None;
Type *final_proc_type = proc_type;
Entity *gen_entity = nullptr;
@@ -5207,306 +5398,135 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
LIT(ce->proc->Ident.token.string));
}
err = CallArgumentError_NonVariadicExpand;
- } else if (operands.count == 0 && param_count_excluding_defaults == 0) {
- err = CallArgumentError_None;
+ }
- if (variadic) {
- GB_ASSERT(param_tuple != nullptr && param_tuple->variables.count > 0);
- Type *t = param_tuple->variables[0]->type;
- if (is_type_polymorphic(t)) {
- error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input");
- err = CallArgumentError_AmbiguousPolymorphicVariadic;
+ GB_ASSERT(ce->split_args);
+ auto visited = slice_make<bool>(temporary_allocator(), pt->param_count);
+ auto ordered_operands = array_make<Operand>(temporary_allocator(), pt->param_count);
+ defer ({
+ for (Operand const &o : ordered_operands) {
+ if (o.expr != nullptr) {
+ call->viral_state_flags |= o.expr->viral_state_flags;
}
}
- } else {
- i32 error_code = 0;
- if (operands.count < param_count_excluding_defaults) {
- error_code = -1;
- } else if (!variadic && operands.count > param_count) {
- error_code = +1;
- }
- if (error_code != 0) {
- err = CallArgumentError_TooManyArguments;
- char const *err_fmt = "Too many arguments for '%s', expected %td arguments, got %td";
- if (error_code < 0) {
- err = CallArgumentError_TooFewArguments;
- err_fmt = "Too few arguments for '%s', expected %td arguments, got %td";
- }
-
- if (show_error) {
- gbString proc_str = expr_to_string(ce->proc);
- defer (gb_string_free(proc_str));
- error(call, err_fmt, proc_str, param_count_excluding_defaults, operands.count);
-
- #if 0
- error_line("\t");
- for_array(i, operands) {
- if (i > 0) {
- error_line(", ");
- }
- gbString s = expr_to_string(operands[i].expr);
- error_line("%s", s);
- gb_string_free(s);
- }
- error_line("\n");
- #endif
- }
- } else {
- // NOTE(bill): Generate the procedure type for this generic instance
- if (pt->is_polymorphic && !pt->is_poly_specialized) {
- PolyProcData poly_proc_data = {};
- if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &operands, call, &poly_proc_data)) {
- gen_entity = poly_proc_data.gen_entity;
- GB_ASSERT(is_type_proc(gen_entity->type));
- final_proc_type = gen_entity->type;
- } else {
- err = CallArgumentError_WrongTypes;
- }
- }
+ });
- GB_ASSERT(is_type_proc(final_proc_type));
- TypeProc *pt = &final_proc_type->Proc;
+ isize positional_operand_count = positional_operands.count;
+ if (variadic) {
+ positional_operand_count = gb_min(positional_operands.count, pt->variadic_index);
+ } else if (positional_operand_count > pt->param_count) {
+ err = CallArgumentError_TooManyArguments;
+ char const *err_fmt = "Too many arguments for '%s', expected %td arguments, got %td";
+ if (show_error) {
+ gbString proc_str = expr_to_string(ce->proc);
+ defer (gb_string_free(proc_str));
+ error(call, err_fmt, proc_str, param_count_excluding_defaults, positional_operands.count);
+ }
+ return err;
+ }
+ positional_operand_count = gb_min(positional_operand_count, pt->param_count);
- GB_ASSERT(pt->params != nullptr);
- auto sig_params = pt->params->Tuple.variables;
- isize operand_index = 0;
- isize max_operand_count = gb_min(param_count, operands.count);
- for (; operand_index < max_operand_count; operand_index++) {
- Entity *e = sig_params[operand_index];
- Type *t = e->type;
- Operand o = operands[operand_index];
- if (o.expr != nullptr) {
- call->viral_state_flags |= o.expr->viral_state_flags;
- }
+ for (isize i = 0; i < positional_operand_count; i++) {
+ ordered_operands[i] = positional_operands[i];
+ visited[i] = true;
+ }
- if (e->kind == Entity_TypeName) {
- // GB_ASSERT(!variadic);
- if (o.mode == Addressing_Invalid) {
- continue;
- } else if (o.mode != Addressing_Type) {
- if (show_error) {
- error(o.expr, "Expected a type for the argument '%.*s'", LIT(e->token.string));
- }
- err = CallArgumentError_WrongTypes;
- }
+ auto variadic_operands = slice(slice_from_array(positional_operands), positional_operand_count, positional_operands.count);
- if (are_types_identical(e->type, o.type)) {
- score += assign_score_function(1);
- } else {
- score += assign_score_function(MAXIMUM_TYPE_DISTANCE);
- }
+ if (named_operands.count != 0) {
+ GB_ASSERT(ce->split_args->named.count == named_operands.count);
+ for_array(i, ce->split_args->named) {
+ Ast *arg = ce->split_args->named[i];
+ Operand operand = named_operands[i];
- continue;
+ ast_node(fv, FieldValue, arg);
+ if (fv->field->kind != Ast_Ident) {
+ if (show_error) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(arg, "Invalid parameter name '%s' in procedure call", expr_str);
+ gb_string_free(expr_str);
}
-
- bool param_is_variadic = pt->variadic && pt->variadic_index == operand_index;
-
- i64 s = 0;
- if (!check_is_assignable_to_with_score(c, &o, t, &s, param_is_variadic)) {
- bool ok = false;
- if (e->flags & EntityFlag_AutoCast) {
- ok = check_is_castable_to(c, &o, t);
- } else if (e->flags & EntityFlag_AnyInt) {
- if (is_type_integer(t)) {
- ok = check_is_castable_to(c, &o, t);
- }
- }
- if (ok) {
- s = assign_score_function(MAXIMUM_TYPE_DISTANCE);
- } else {
- if (show_error) {
- check_assignment(c, &o, t, str_lit("argument"));
- }
- // TODO(bill, 2021-05-05): Is this incorrect logic to only fail if there is ambiguity for definite?
- if (o.mode == Addressing_Invalid) {
- err = CallArgumentError_WrongTypes;
- }
- }
- } else if (show_error) {
- check_assignment(c, &o, t, str_lit("argument"));
+ err = CallArgumentError_InvalidFieldValue;
+ continue;
+ }
+ String name = fv->field->Ident.token.string;
+ isize param_index = lookup_procedure_parameter(pt, name);
+ if (param_index < 0) {
+ if (show_error) {
+ error(arg, "No parameter named '%.*s' for this procedure type", LIT(name));
}
- score += s;
-
- if (e->flags & EntityFlag_ConstInput) {
- if (o.mode != Addressing_Constant) {
- if (show_error) {
- error(o.expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string));
- }
- err = CallArgumentError_NoneConstantParameter;
- }
+ err = CallArgumentError_ParameterNotFound;
+ continue;
+ }
+ if (visited[param_index]) {
+ if (show_error) {
+ error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name));
}
+ err = CallArgumentError_DuplicateParameter;
+ continue;
+ }
- if (o.mode == Addressing_Type && is_type_typeid(e->type)) {
- add_type_info_type(c, o.type);
- add_type_and_value(c->info, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type));
- } else if (show_error && is_type_untyped(o.type)) {
- update_untyped_expr_type(c, o.expr, t, true);
- }
+ visited[param_index] = true;
+ ordered_operands[param_index] = operand;
+ }
+ }
+ isize dummy_argument_count = 0;
+ bool actually_variadic = false;
+
+ if (variadic) {
+ if (visited[pt->variadic_index] &&
+ positional_operand_count < positional_operands.count) {
+ if (show_error) {
+ String name = pt->params->Tuple.variables[pt->variadic_index]->token.string;
+ error(call, "Variadic parameters already handled with a named argument '%.*s' in procedure call", LIT(name));
}
+ err = CallArgumentError_DuplicateParameter;
+ } else if (!visited[pt->variadic_index]) {
+ visited[pt->variadic_index] = true;
- if (variadic) {
- bool variadic_expand = false;
- Type *slice = sig_params[param_count]->type;
- GB_ASSERT(is_type_slice(slice));
- Type *elem = base_type(slice)->Slice.elem;
- Type *t = elem;
+ Operand *variadic_operand = &ordered_operands[pt->variadic_index];
- if (is_type_polymorphic(t)) {
- error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input");
- err = CallArgumentError_AmbiguousPolymorphicVariadic;
- }
+ if (vari_expand) {
+ GB_ASSERT(variadic_operands.count != 0);
+ *variadic_operand = variadic_operands[0];
+ variadic_operand->type = default_type(variadic_operand->type);
+ actually_variadic = true;
+ } else {
+ AstFile *f = call->file();
- for (; operand_index < operands.count; operand_index++) {
- Operand o = operands[operand_index];
- if (vari_expand) {
- variadic_expand = true;
- t = slice;
- if (operand_index != param_count) {
- if (show_error) {
- error(o.expr, "'..' in a variadic procedure can only have one variadic argument at the end");
- }
- if (data) {
- data->score = score;
- data->result_type = final_proc_type->Proc.results;
- data->gen_entity = gen_entity;
- }
- return CallArgumentError_MultipleVariadicExpand;
- }
- }
- i64 s = 0;
- if (!check_is_assignable_to_with_score(c, &o, t, &s, true)) {
- if (show_error) {
- check_assignment(c, &o, t, str_lit("argument"));
- }
- err = CallArgumentError_WrongTypes;
- } else if (show_error) {
- check_assignment(c, &o, t, str_lit("argument"));
- }
- score += s;
- if (is_type_any(elem)) {
- add_type_info_type(c, o.type);
- }
- if (o.mode == Addressing_Type && is_type_typeid(t)) {
- add_type_info_type(c, o.type);
- add_type_and_value(c->info, o.expr, Addressing_Value, t, exact_value_typeid(o.type));
- } else if (show_error && is_type_untyped(o.type)) {
- update_untyped_expr_type(c, o.expr, t, true);
+ // HACK(bill): this is an awful hack
+ Operand o = {};
+ o.mode = Addressing_Value;
+ o.expr = ast_ident(f, make_token_ident("nil"));
+ o.expr->Ident.token.pos = ast_token(call).pos;
+ if (variadic_operands.count != 0) {
+ actually_variadic = true;
+ o.expr->Ident.token.pos = ast_token(variadic_operands[0].expr).pos;
+
+ Entity *vt = pt->params->Tuple.variables[pt->variadic_index];
+ if (is_type_polymorphic(vt->type)) {
+ o.type = alloc_type_slice(default_type(variadic_operands[0].type));
+ } else {
+ o.type = vt->type;
}
+ } else {
+ dummy_argument_count += 1;
+ o.type = t_untyped_nil;
}
+ *variadic_operand = o;
}
}
- }
-
- if (data) {
- data->score = score;
- data->result_type = final_proc_type->Proc.results;
- data->gen_entity = gen_entity;
- add_type_and_value(c->info, ce->proc, Addressing_Value, final_proc_type, {});
- }
- return err;
-}
-
-bool is_call_expr_field_value(AstCallExpr *ce) {
- GB_ASSERT(ce != nullptr);
-
- if (ce->args.count == 0) {
- return false;
}
- return ce->args[0]->kind == Ast_FieldValue;
-}
-isize lookup_procedure_parameter(TypeProc *pt, String parameter_name) {
- isize param_count = pt->param_count;
- for (isize i = 0; i < param_count; i++) {
- Entity *e = pt->params->Tuple.variables[i];
- String name = e->token.string;
- if (is_blank_ident(name)) {
- continue;
- }
- if (name == parameter_name) {
- return i;
+ for (Operand const &o : ordered_operands) {
+ if (o.mode != Addressing_Invalid) {
+ check_no_copy_assignment(o, str_lit("procedure call expression"));
}
}
- return -1;
-}
-isize lookup_procedure_result(TypeProc *pt, String result_name) {
- isize result_count = pt->result_count;
- for (isize i = 0; i < result_count; i++) {
- Entity *e = pt->results->Tuple.variables[i];
- String name = e->token.string;
- if (is_blank_ident(name)) {
- continue;
- }
- if (name == result_name) {
- return i;
- }
- }
- return -1;
-}
-CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
- ast_node(ce, CallExpr, call);
- GB_ASSERT(is_type_proc(proc_type));
- proc_type = base_type(proc_type);
- TypeProc *pt = &proc_type->Proc;
-
- i64 score = 0;
- bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
- CallArgumentError err = CallArgumentError_None;
-
- isize param_count = pt->param_count;
- bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count);
- auto ordered_operands = array_make<Operand>(temporary_allocator(), param_count);
- defer ({
- for_array(i, ordered_operands) {
- Operand const &o = ordered_operands[i];
- if (o.expr != nullptr) {
- call->viral_state_flags |= o.expr->viral_state_flags;
- }
- }
- });
-
- for_array(i, ce->args) {
- Ast *arg = ce->args[i];
- ast_node(fv, FieldValue, arg);
- if (fv->field->kind != Ast_Ident) {
- if (show_error) {
- gbString expr_str = expr_to_string(fv->field);
- error(arg, "Invalid parameter name '%s' in procedure call", expr_str);
- gb_string_free(expr_str);
- }
- err = CallArgumentError_InvalidFieldValue;
- continue;
- }
- String name = fv->field->Ident.token.string;
- isize index = lookup_procedure_parameter(pt, name);
- if (index < 0) {
- if (show_error) {
- error(arg, "No parameter named '%.*s' for this procedure type", LIT(name));
- }
- err = CallArgumentError_ParameterNotFound;
- continue;
- }
- if (visited[index]) {
- if (show_error) {
- error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name));
- }
- err = CallArgumentError_DuplicateParameter;
- continue;
- }
-
- visited[index] = true;
- ordered_operands[index] = operands[i];
- }
-
- // NOTE(bill): Check for default values and missing parameters
- isize param_count_to_check = param_count;
- if (pt->variadic) {
- param_count_to_check--;
- }
- for (isize i = 0; i < param_count_to_check; i++) {
+ for (isize i = 0; i < pt->param_count; i++) {
if (!visited[i]) {
Entity *e = pt->params->Tuple.variables[i];
if (is_blank_ident(e->token)) {
@@ -5514,6 +5534,11 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
}
if (e->kind == Entity_Variable) {
if (e->Variable.param_value.kind != ParameterValue_Invalid) {
+ ordered_operands[i].mode = Addressing_Value;
+ ordered_operands[i].type = e->type;
+ ordered_operands[i].expr = e->Variable.param_value.original_ast_expr;
+
+ dummy_argument_count += 1;
score += assign_score_function(1);
continue;
}
@@ -5536,101 +5561,175 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
}
}
- Entity *gen_entity = nullptr;
- if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) {
- PolyProcData poly_proc_data = {};
- if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, call, &poly_proc_data)) {
- gen_entity = poly_proc_data.gen_entity;
- Type *gept = base_type(gen_entity->type);
- GB_ASSERT(is_type_proc(gept));
- proc_type = gept;
- pt = &gept->Proc;
- } else {
- err = CallArgumentError_WrongTypes;
+ auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error) -> i64 {
+ i64 s = 0;
+ if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic)) {
+ bool ok = false;
+ if (e && e->flags & EntityFlag_AnyInt) {
+ if (is_type_integer(param_type)) {
+ ok = check_is_castable_to(c, o, param_type);
+ }
+ }
+ if (ok) {
+ s = assign_score_function(MAXIMUM_TYPE_DISTANCE);
+ } else {
+ if (show_error) {
+ check_assignment(c, o, param_type, str_lit("procedure argument"));
+ }
+ err = CallArgumentError_WrongTypes;
+ }
+
+ } else if (show_error) {
+ check_assignment(c, o, param_type, str_lit("procedure argument"));
}
- }
+ if (e && e->flags & EntityFlag_ConstInput) {
+ if (o->mode != Addressing_Constant) {
+ if (show_error) {
+ error(o->expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string));
+ }
+ err = CallArgumentError_NoneConstantParameter;
+ }
+ }
- for (isize i = 0; i < param_count; i++) {
- Entity *e = pt->params->Tuple.variables[i];
- Operand *o = &ordered_operands[i];
- bool param_is_variadic = pt->variadic && pt->variadic_index == i;
+ if (!err && is_type_any(param_type)) {
+ add_type_info_type(c, o->type);
+ }
+ if (o->mode == Addressing_Type && is_type_typeid(param_type)) {
+ add_type_info_type(c, o->type);
+ add_type_and_value(c, o->expr, Addressing_Value, param_type, exact_value_typeid(o->type));
+ } else if (show_error && is_type_untyped(o->type)) {
+ update_untyped_expr_type(c, o->expr, param_type, true);
+ }
- if (o->mode == Addressing_Invalid) {
- if (param_is_variadic) {
- Type *slice = e->type;
- GB_ASSERT(is_type_slice(slice));
- Type *elem = base_type(slice)->Slice.elem;
- if (is_type_polymorphic(elem)) {
+ return s;
+ };
+
+
+ if (ordered_operands.count == 0 && param_count_excluding_defaults == 0) {
+ err = CallArgumentError_None;
+
+ if (variadic) {
+ GB_ASSERT(pt->params != nullptr && pt->params->Tuple.variables.count > 0);
+ Type *t = pt->params->Tuple.variables[0]->type;
+ if (is_type_polymorphic(t)) {
+ if (show_error) {
error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input");
- err = CallArgumentError_AmbiguousPolymorphicVariadic;
- return err;
}
+ err = CallArgumentError_AmbiguousPolymorphicVariadic;
}
- continue;
}
+ } else {
+ if (pt->is_polymorphic && !pt->is_poly_specialized && err == CallArgumentError_None) {
+ PolyProcData poly_proc_data = {};
+ if (find_or_generate_polymorphic_procedure_from_parameters(c, entity, &ordered_operands, call, &poly_proc_data)) {
+ gen_entity = poly_proc_data.gen_entity;
+ Type *gept = base_type(gen_entity->type);
+ GB_ASSERT(is_type_proc(gept));
+ final_proc_type = gen_entity->type;
+ pt = &gept->Proc;
- if (e->kind == Entity_TypeName) {
- GB_ASSERT(pt->is_polymorphic);
- if (o->mode != Addressing_Type) {
- if (show_error) {
- error(o->expr, "Expected a type for the argument '%.*s'", LIT(e->token.string));
- }
+ } else {
err = CallArgumentError_WrongTypes;
}
- if (are_types_identical(e->type, o->type)) {
- score += assign_score_function(1);
- } else {
- score += assign_score_function(MAXIMUM_TYPE_DISTANCE);
+ }
+
+ for (isize i = 0; i < pt->param_count; i++) {
+ Operand *o = &ordered_operands[i];
+ if (o->mode == Addressing_Invalid) {
+ continue;
}
- } else {
- i64 s = 0;
- if (!check_is_assignable_to_with_score(c, o, e->type, &s, param_is_variadic)) {
- bool ok = false;
- if (e->flags & EntityFlag_AutoCast) {
- ok = check_is_castable_to(c, o, e->type);
- }
- if (ok) {
- s = assign_score_function(MAXIMUM_TYPE_DISTANCE);
- } else {
+
+ Entity *e = pt->params->Tuple.variables[i];
+ bool param_is_variadic = pt->variadic && pt->variadic_index == i;
+
+ if (e->kind == Entity_TypeName) {
+ GB_ASSERT(pt->is_polymorphic);
+ if (o->mode != Addressing_Type) {
if (show_error) {
- check_assignment(c, o, e->type, str_lit("procedure argument"));
+ error(o->expr, "Expected a type for the argument '%.*s'", LIT(e->token.string));
}
err = CallArgumentError_WrongTypes;
}
-
- if (e->flags & EntityFlag_ConstInput) {
- if (o->mode != Addressing_Constant) {
- if (show_error) {
- error(o->expr, "Expected a constant value for the argument '%.*s'", LIT(e->token.string));
- }
- err = CallArgumentError_NoneConstantParameter;
- }
+ if (are_types_identical(e->type, o->type)) {
+ score += assign_score_function(1);
+ } else {
+ score += assign_score_function(MAXIMUM_TYPE_DISTANCE);
}
- } else if (show_error) {
- check_assignment(c, o, e->type, str_lit("procedure argument"));
+ continue;
}
- score += s;
+
+ if (param_is_variadic) {
+ continue;
+ }
+
+ score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error);
+ }
+ }
+
+ if (variadic) {
+ Type *slice = pt->params->Tuple.variables[pt->variadic_index]->type;
+ GB_ASSERT(is_type_slice(slice));
+ Type *elem = base_type(slice)->Slice.elem;
+ Type *t = elem;
+
+ if (is_type_polymorphic(t)) {
+ error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input %s", type_to_string(final_proc_type));
+ err = CallArgumentError_AmbiguousPolymorphicVariadic;
}
- if (o->mode == Addressing_Type && is_type_typeid(e->type)) {
- add_type_info_type(c, o->type);
- add_type_and_value(c->info, o->expr, Addressing_Value, e->type, exact_value_typeid(o->type));
+ for_array(operand_index, variadic_operands) {
+ Operand *o = &variadic_operands[operand_index];
+ if (vari_expand) {
+ t = slice;
+ if (operand_index > 0) {
+ if (show_error) {
+ error(o->expr, "'..' in a variadic procedure can only have one variadic argument at the end");
+ }
+ if (data) {
+ data->score = score;
+ data->result_type = final_proc_type->Proc.results;
+ data->gen_entity = gen_entity;
+ }
+ return CallArgumentError_MultipleVariadicExpand;
+ }
+ }
+ score += eval_param_and_score(c, o, t, err, true, nullptr, show_error);
}
}
if (data) {
data->score = score;
- data->result_type = pt->results;
+ data->result_type = final_proc_type->Proc.results;
data->gen_entity = gen_entity;
- add_type_and_value(c->info, ce->proc, Addressing_Value, proc_type, {});
+
+
+ Ast *proc_lit = nullptr;
+ if (ce->proc->tav.value.kind == ExactValue_Procedure) {
+ Ast *vp = unparen_expr(ce->proc->tav.value.value_procedure);
+ if (vp && vp->kind == Ast_ProcLit) {
+ proc_lit = vp;
+ }
+ }
+ if (proc_lit == nullptr) {
+ add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {});
+ }
}
return err;
}
-Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize *lhs_count_, bool *is_variadic) {
+gb_internal bool is_call_expr_field_value(AstCallExpr *ce) {
+ GB_ASSERT(ce != nullptr);
+
+ if (ce->args.count == 0) {
+ return false;
+ }
+ return ce->args[0]->kind == Ast_FieldValue;
+}
+
+gb_internal Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize *lhs_count_, bool *is_variadic) {
Entity **lhs = nullptr;
isize lhs_count = -1;
@@ -5669,10 +5768,9 @@ Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize
}
-bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, Slice<Ast *> *clauses, bool print_err) {
+gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, Slice<Ast *> *clauses, bool print_err) {
if (clauses != nullptr) {
- for_array(i, *clauses) {
- Ast *clause = (*clauses)[i];
+ for (Ast *clause : *clauses) {
Operand o = {};
check_expr(ctx, &o, clause);
if (o.mode != Addressing_Constant) {
@@ -5693,8 +5791,8 @@ bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, S
if (scope != nullptr) {
isize print_count = 0;
- for_array(j, scope->elements.entries) {
- Entity *e = scope->elements.entries[j].value;
+ for (auto const &entry : scope->elements) {
+ Entity *e = entry.value;
switch (e->kind) {
case Entity_TypeName: {
if (print_count == 0) error_line("\n\tWith the following definitions:\n");
@@ -5735,537 +5833,659 @@ bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Scope *scope, S
return true;
}
+gb_internal bool check_named_arguments(CheckerContext *c, Type *type, Slice<Ast *> const &named_args, Array<Operand> *named_operands, bool show_error) {
+ bool success = true;
-CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call, Slice<Ast *> const &args) {
- ast_node(ce, CallExpr, call);
-
- CallArgumentCheckerType *call_checker = check_call_arguments_internal;
- Array<Operand> operands = {};
- defer (array_free(&operands));
-
- Type *result_type = t_invalid;
-
- if (is_call_expr_field_value(ce)) {
- call_checker = check_named_call_arguments;
-
- operands = array_make<Operand>(heap_allocator(), args.count);
-
- // NOTE(bill): This is give type hints for the named parameters
- // in order to improve the type inference system
-
- StringMap<Type *> type_hint_map = {}; // Key: String
- string_map_init(&type_hint_map, heap_allocator(), 2*args.count);
- defer (string_map_destroy(&type_hint_map));
-
- Type *ptype = nullptr;
- bool single_case = true;
-
- if (operand->mode == Addressing_ProcGroup) {
- single_case = false;
- Array<Entity *> procs = proc_group_entities(c, *operand);
- if (procs.count == 1) {
- ptype = procs[0]->type;
- single_case = true;
- }
- } else {
- ptype = proc_type;
+ type = base_type(type);
+ if (named_args.count > 0) {
+ TypeProc *pt = nullptr;
+ if (is_type_proc(type)) {
+ pt = &type->Proc;
}
- if (single_case) {
- Type *bptype = base_type(ptype);
- if (is_type_proc(bptype)) {
- TypeProc *pt = &bptype->Proc;
- TypeTuple *param_tuple = nullptr;
- if (pt->params != nullptr) {
- param_tuple = &pt->params->Tuple;
+ for_array(i, named_args) {
+ Ast *arg = named_args[i];
+ if (arg->kind != Ast_FieldValue) {
+ if (show_error) {
+ error(arg, "Expected a 'field = value'");
}
- if (param_tuple != nullptr) {
- for_array(i, param_tuple->variables) {
- Entity *e = param_tuple->variables[i];
- if (is_blank_ident(e->token)) {
- continue;
- }
- string_map_set(&type_hint_map, e->token.string, e->type);
- }
+ return false;
+ }
+ ast_node(fv, FieldValue, arg);
+ if (fv->field->kind != Ast_Ident) {
+ if (show_error) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(arg, "Invalid parameter name '%s' in procedure call", expr_str);
+ gb_string_free(expr_str);
}
+ success = false;
+ continue;
}
- } else {
- Array<Entity *> procs = proc_group_entities(c, *operand);
- for_array(j, procs) {
- Type *proc_type = base_type(procs[j]->type);
- if (is_type_proc(proc_type)) {
- TypeProc *pt = &proc_type->Proc;
- TypeTuple *param_tuple = nullptr;
- if (pt->params != nullptr) {
- param_tuple = &pt->params->Tuple;
- }
- if (param_tuple == nullptr) {
- continue;
- }
- for_array(i, param_tuple->variables) {
- Entity *e = param_tuple->variables[i];
- if (is_blank_ident(e->token)) {
- continue;
- }
- StringHashKey key = string_hash_string(e->token.string);
- Type **found = string_map_get(&type_hint_map, key);
- if (found) {
- Type *t = *found;
- if (t == nullptr) {
- // NOTE(bill): Ambiguous named parameter across all types
- continue;
- }
- if (are_types_identical(t, e->type)) {
- // NOTE(bill): No need to set again
- } else {
- // NOTE(bill): Ambiguous named parameter across all types so set it to a nullptr
- string_map_set(&type_hint_map, key, cast(Type *)nullptr);
- }
- } else {
- string_map_set(&type_hint_map, key, e->type);
- }
+ String key = fv->field->Ident.token.string;
+ Ast *value = fv->value;
+
+ Type *type_hint = nullptr;
+ if (pt) {
+ isize param_index = lookup_procedure_parameter(pt, key);
+ if (param_index < 0) {
+ if (show_error) {
+ error(value, "No parameter named '%.*s' for this procedure type", LIT(key));
}
+ success = false;
+ continue;
+ }
+
+ Entity *e = pt->params->Tuple.variables[param_index];
+ if (!is_type_polymorphic(e->type)) {
+ type_hint = e->type;
}
+
+ }
+ Operand o = {};
+ check_expr_with_type_hint(c, &o, value, type_hint);
+ if (o.mode == Addressing_Invalid) {
+ success = false;
}
+ array_add(named_operands, o);
+ }
+
+ }
+ return success;
+}
+
+gb_internal bool check_call_arguments_single(CheckerContext *c, Ast *call, Operand *operand,
+ Entity *e, Type *proc_type,
+ Array<Operand> const &positional_operands, Array<Operand> const &named_operands,
+ CallArgumentErrorMode show_error_mode,
+ CallArgumentData *data) {
+
+ bool return_on_failure = show_error_mode == CallArgumentErrorMode::NoErrors;
+
+ Ast *ident = operand->expr;
+ while (ident->kind == Ast_SelectorExpr) {
+ Ast *s = ident->SelectorExpr.selector;
+ ident = s;
+ }
+ if (e == nullptr) {
+ e = entity_of_node(ident);
+ if (e != nullptr) {
+ proc_type = e->type;
}
+ }
+ GB_ASSERT(proc_type != nullptr);
+ proc_type = base_type(proc_type);
+ GB_ASSERT(proc_type->kind == Type_Proc);
- for_array(i, args) {
- Ast *arg = args[i];
- ast_node(fv, FieldValue, arg);
- Ast *field = fv->field;
+ CallArgumentError err = check_call_arguments_internal(c, call, e, proc_type, positional_operands, named_operands, show_error_mode, data);
+ if (return_on_failure && err != CallArgumentError_None) {
+ return false;
+ }
- Type *type_hint = nullptr;
+ Entity *entity_to_use = data->gen_entity != nullptr ? data->gen_entity : e;
+ if (!return_on_failure && entity_to_use != nullptr) {
+ add_entity_use(c, ident, entity_to_use);
+ update_untyped_expr_type(c, operand->expr, entity_to_use->type, true);
+ add_type_and_value(c, operand->expr, operand->mode, entity_to_use->type, operand->value);
+ }
- if (field != nullptr && field->kind == Ast_Ident) {
- String key = field->Ident.token.string;
- Type **found = string_map_get(&type_hint_map, key);
- if (found) {
- type_hint = *found;
- }
+ if (data->gen_entity != nullptr) {
+ Entity *e = data->gen_entity;
+ DeclInfo *decl = data->gen_entity->decl_info;
+ CheckerContext ctx = *c;
+ ctx.scope = decl->scope;
+ ctx.decl = decl;
+ ctx.proc_name = e->token.string;
+ ctx.curr_proc_decl = decl;
+ ctx.curr_proc_sig = e->type;
+
+ GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
+ bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, !return_on_failure);
+ if (return_on_failure) {
+ if (!ok) {
+ return false;
+ }
+
+ } else {
+ decl->where_clauses_evaluated = true;
+ if (ok && (data->gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+ check_procedure_later(c->checker, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags);
+ }
+ if (is_type_proc(data->gen_entity->type)) {
+ Type *t = base_type(entity_to_use->type);
+ data->result_type = t->Proc.results;
}
- check_expr_or_type(c, &operands[i], fv->value, type_hint);
- }
- } else {
- operands = array_make<Operand>(heap_allocator(), 0, 2*args.count);
- Entity **lhs = nullptr;
- isize lhs_count = -1;
- bool is_variadic = false;
- if (proc_type != nullptr && is_type_proc(proc_type)) {
- lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic);
- }
- if (operand->mode != Addressing_ProcGroup) {
- check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, is_variadic);
}
}
- if (operand->mode == Addressing_ProcGroup) {
- check_entity_decl(c, operand->proc_group, nullptr, nullptr);
+ return true;
+}
- auto procs = proc_group_entities_cloned(c, *operand);
- if (procs.count > 1) {
- isize max_arg_count = args.count;
- for_array(i, args) {
- // NOTE(bill): The only thing that may have multiple values
- // will be a call expression (assuming `or_return` and `()` will be stripped)
- Ast *arg = strip_or_return_expr(args[i]);
+gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, Operand *operand, Ast *call) {
+ ast_node(ce, CallExpr, call);
+ GB_ASSERT(ce->split_args != nullptr);
+
+ Slice<Ast *> const &positional_args = ce->split_args->positional;
+ Slice<Ast *> const &named_args = ce->split_args->named;
+
+ CallArgumentData data = {};
+ data.result_type = t_invalid;
+
+ GB_ASSERT(operand->mode == Addressing_ProcGroup);
+ auto procs = proc_group_entities_cloned(c, *operand);
+
+ if (procs.count > 1) {
+ isize max_arg_count = positional_args.count + named_args.count;
+ for (Ast *arg : positional_args) {
+ // NOTE(bill): The only thing that may have multiple values
+ // will be a call expression (assuming `or_return` and `()` will be stripped)
+ arg = strip_or_return_expr(arg);
+ if (arg && arg->kind == Ast_CallExpr) {
+ max_arg_count = ISIZE_MAX;
+ break;
+ }
+ }
+ if (max_arg_count != ISIZE_MAX) for (Ast *arg : named_args) {
+ // NOTE(bill): The only thing that may have multiple values
+ // will be a call expression (assuming `or_return` and `()` will be stripped)
+ if (arg->kind == Ast_FieldValue) {
+ arg = strip_or_return_expr(arg->FieldValue.value);
if (arg && arg->kind == Ast_CallExpr) {
max_arg_count = ISIZE_MAX;
break;
}
}
+ }
- for (isize proc_index = 0; proc_index < procs.count; /**/) {
- Entity *proc = procs[proc_index];
- Type *pt = base_type(proc->type);
- if (!(pt != nullptr && is_type_proc(pt))) {
- proc_index++;
- continue;
- }
-
- isize param_count = 0;
- isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, &param_count);
-
- if (param_count_excluding_defaults > max_arg_count) {
- array_unordered_remove(&procs, proc_index);
- } else {
- proc_index++;
+ // ignore named arguments first
+ for (Ast *arg : named_args) {
+ if (arg->kind != Ast_FieldValue) {
+ continue;
+ }
+ ast_node(fv, FieldValue, arg);
+ if (fv->field->kind != Ast_Ident) {
+ continue;
+ }
+ String key = fv->field->Ident.token.string;
+ for (isize proc_index = procs.count-1; proc_index >= 0; proc_index--) {
+ Type *t = procs[proc_index]->type;
+ if (is_type_proc(t)) {
+ isize param_index = lookup_procedure_parameter(t, key);
+ if (param_index < 0) {
+ array_unordered_remove(&procs, proc_index);
+ }
}
}
}
- if (procs.count == 1) {
- Ast *ident = operand->expr;
- while (ident->kind == Ast_SelectorExpr) {
- Ast *s = ident->SelectorExpr.selector;
- ident = s;
- }
+ if (procs.count == 0) {
+ // if any of the named arguments are wrong, the `procs` will be empty
+ // just start from scratch
+ array_free(&procs);
+ procs = proc_group_entities_cloned(c, *operand);
+ }
- Entity *e = procs[0];
+ // filter by positional argument length
+ for (isize proc_index = 0; proc_index < procs.count; /**/) {
+ Entity *proc = procs[proc_index];
+ Type *pt = base_type(proc->type);
+ if (!(pt != nullptr && is_type_proc(pt))) {
+ proc_index++;
+ continue;
+ }
- Entity **lhs = nullptr;
- isize lhs_count = -1;
- bool is_variadic = false;
- lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic);
- check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, is_variadic);
+ isize param_count = 0;
+ isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, &param_count);
- CallArgumentData data = {};
- CallArgumentError err = call_checker(c, call, e->type, e, operands, CallArgumentMode_ShowErrors, &data);
- if (err != CallArgumentError_None) {
- // handle error
- }
- Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
- add_entity_use(c, ident, entity_to_use);
- if (entity_to_use != nullptr) {
- update_untyped_expr_type(c, operand->expr, entity_to_use->type, true);
+ if (param_count_excluding_defaults > max_arg_count) {
+ array_unordered_remove(&procs, proc_index);
+ continue;
}
- return data;
+ proc_index++;
}
+ }
+
+ Entity **lhs = nullptr;
+ isize lhs_count = -1;
+ bool is_variadic = false;
+ auto positional_operands = array_make<Operand>(heap_allocator(), 0, 0);
+ auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
+ defer (array_free(&positional_operands));
+ defer (array_free(&named_operands));
- Entity **lhs = nullptr;
- isize lhs_count = -1;
+ if (procs.count == 1) {
+ Entity *e = procs[0];
- {
- // NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups
- // where the same positional parameter has the same type value (and ellipsis)
- bool proc_arg_count_all_equal = true;
- isize proc_arg_count = -1;
- for_array(i, procs) {
- Entity *p = procs[i];
- Type *pt = base_type(p->type);
- if (pt != nullptr && is_type_proc(pt)) {
- if (proc_arg_count < 0) {
- proc_arg_count = pt->Proc.param_count;
- } else {
- if (proc_arg_count != pt->Proc.param_count) {
- proc_arg_count_all_equal = false;
- break;
- }
+ lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic);
+ check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
+
+ if (check_named_arguments(c, e->type, named_args, &named_operands, true)) {
+ check_call_arguments_single(c, call, operand,
+ e, e->type,
+ positional_operands, named_operands,
+ CallArgumentErrorMode::ShowErrors,
+ &data);
+ }
+ return data;
+ }
+
+ {
+ // NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups
+ // where the same positional parameter has the same type value (and ellipsis)
+ bool proc_arg_count_all_equal = true;
+ isize proc_arg_count = -1;
+ for (Entity *p : procs) {
+ Type *pt = base_type(p->type);
+ if (pt != nullptr && is_type_proc(pt)) {
+ if (proc_arg_count < 0) {
+ proc_arg_count = pt->Proc.param_count;
+ } else {
+ if (proc_arg_count != pt->Proc.param_count) {
+ proc_arg_count_all_equal = false;
+ break;
}
}
}
+ }
+ if (proc_arg_count >= 0 && proc_arg_count_all_equal) {
+ lhs_count = proc_arg_count;
+ if (lhs_count > 0) {
+ lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count);
+ for (isize param_index = 0; param_index < lhs_count; param_index++) {
+ Entity *e = nullptr;
+ for (Entity *p : procs) {
+ Type *pt = base_type(p->type);
+ if (!(pt != nullptr && is_type_proc(pt))) {
+ continue;
+ }
-
- if (proc_arg_count >= 0 && proc_arg_count_all_equal) {
- lhs_count = proc_arg_count;
- if (lhs_count > 0) {
- lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count);
- for (isize param_index = 0; param_index < lhs_count; param_index++) {
- Entity *e = nullptr;
- for_array(j, procs) {
- Entity *p = procs[j];
- Type *pt = base_type(p->type);
- if (pt != nullptr && is_type_proc(pt)) {
- if (e == nullptr) {
- e = pt->Proc.params->Tuple.variables[param_index];
- } else {
- Entity *f = pt->Proc.params->Tuple.variables[param_index];
- if (e == f) {
- continue;
- }
- if (are_types_identical(e->type, f->type)) {
- bool ee = (e->flags & EntityFlag_Ellipsis) != 0;
- bool fe = (f->flags & EntityFlag_Ellipsis) != 0;
- if (ee == fe) {
- continue;
- }
- }
- // NOTE(bill): Entities are not close enough to be used
- e = nullptr;
- break;
+ if (e == nullptr) {
+ e = pt->Proc.params->Tuple.variables[param_index];
+ } else {
+ Entity *f = pt->Proc.params->Tuple.variables[param_index];
+ if (e == f) {
+ continue;
+ }
+ if (are_types_identical(e->type, f->type)) {
+ bool ee = (e->flags & EntityFlag_Ellipsis) != 0;
+ bool fe = (f->flags & EntityFlag_Ellipsis) != 0;
+ if (ee == fe) {
+ continue;
}
}
+ // NOTE(bill): Entities are not close enough to be used
+ e = nullptr;
+ break;
}
- lhs[param_index] = e;
}
+ lhs[param_index] = e;
}
}
}
+ }
+ check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
- check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, false);
-
- if (lhs != nullptr) {
- gb_free(heap_allocator(), lhs);
+ for_array(i, named_args) {
+ Ast *arg = named_args[i];
+ if (arg->kind != Ast_FieldValue) {
+ error(arg, "Expected a 'field = value'");
+ return data;
}
+ ast_node(fv, FieldValue, arg);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(arg, "Invalid parameter name '%s' in procedure call", expr_str);
+ gb_string_free(expr_str);
+ return data;
+ }
+ String key = fv->field->Ident.token.string;
+ Ast *value = fv->value;
- auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count);
- defer (array_free(&valids));
+ Type *type_hint = nullptr;
- auto proc_entities = array_make<Entity *>(heap_allocator(), 0, procs.count*2 + 1);
- defer (array_free(&proc_entities));
- for_array(i, procs) {
- array_add(&proc_entities, procs[i]);
+ for (isize lhs_idx = 0; lhs_idx < lhs_count; lhs_idx++) {
+ Entity *e = lhs[lhs_idx];
+ if (e != nullptr && e->token.string == key &&
+ !is_type_polymorphic(e->type)) {
+ type_hint = e->type;
+ break;
+ }
}
+ Operand o = {};
+ check_expr_with_type_hint(c, &o, value, type_hint);
+ array_add(&named_operands, o);
+ }
+ gb_free(heap_allocator(), lhs);
- gbString expr_name = expr_to_string(operand->expr);
- defer (gb_string_free(expr_name));
+ auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count);
+ defer (array_free(&valids));
- for_array(i, procs) {
- Entity *p = procs[i];
- Type *pt = base_type(p->type);
- if (pt != nullptr && is_type_proc(pt)) {
- CallArgumentError err = CallArgumentError_None;
- CallArgumentData data = {};
- CheckerContext ctx = *c;
+ auto proc_entities = array_make<Entity *>(heap_allocator(), 0, procs.count*2 + 1);
+ defer (array_free(&proc_entities));
+ for (Entity *proc : procs) {
+ array_add(&proc_entities, proc);
+ }
- ctx.no_polymorphic_errors = true;
- ctx.allow_polymorphic_types = is_type_polymorphic(pt);
- ctx.hide_polymorphic_errors = true;
- err = call_checker(&ctx, call, pt, p, operands, CallArgumentMode_NoErrors, &data);
- if (err != CallArgumentError_None) {
- continue;
- }
- isize index = i;
+ gbString expr_name = expr_to_string(operand->expr);
+ defer (gb_string_free(expr_name));
- if (data.gen_entity != nullptr) {
- Entity *e = data.gen_entity;
- DeclInfo *decl = data.gen_entity->decl_info;
- ctx.scope = decl->scope;
- ctx.decl = decl;
- ctx.proc_name = e->token.string;
- ctx.curr_proc_decl = decl;
- ctx.curr_proc_sig = e->type;
+ for_array(i, procs) {
+ Entity *p = procs[i];
+ Type *pt = base_type(p->type);
+ if (pt != nullptr && is_type_proc(pt)) {
+ CallArgumentData data = {};
+ CheckerContext ctx = *c;
- GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- if (!evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) {
- continue;
- }
+ ctx.no_polymorphic_errors = true;
+ ctx.allow_polymorphic_types = is_type_polymorphic(pt);
+ ctx.hide_polymorphic_errors = true;
- array_add(&proc_entities, data.gen_entity);
- index = proc_entities.count-1;
- }
+ bool is_a_candidate = check_call_arguments_single(&ctx, call, operand,
+ p, pt,
+ positional_operands, named_operands,
+ CallArgumentErrorMode::NoErrors,
+ &data);
+ if (!is_a_candidate) {
+ continue;
+ }
+ isize index = i;
- ValidIndexAndScore item = {};
- item.index = index;
- item.score = data.score;
- array_add(&valids, item);
+ if (data.gen_entity != nullptr) {
+ array_add(&proc_entities, data.gen_entity);
+ index = proc_entities.count-1;
}
+
+ ValidIndexAndScore item = {};
+ item.index = index;
+ item.score = data.score;
+ array_add(&valids, item);
}
+ }
- if (valids.count > 1) {
- gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp);
- i64 best_score = valids[0].score;
- Entity *best_entity = proc_entities[valids[0].index];
- GB_ASSERT(best_entity != nullptr);
- for (isize i = 1; i < valids.count; i++) {
- if (best_score > valids[i].score) {
- valids.count = i;
- break;
- }
- if (best_entity == proc_entities[valids[i].index]) {
- valids.count = i;
- break;
- }
+ if (valids.count > 1) {
+ gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp);
+ i64 best_score = valids[0].score;
+ Entity *best_entity = proc_entities[valids[0].index];
+ GB_ASSERT(best_entity != nullptr);
+ for (isize i = 1; i < valids.count; i++) {
+ if (best_score > valids[i].score) {
+ valids.count = i;
+ break;
+ }
+ if (best_entity == proc_entities[valids[i].index]) {
+ valids.count = i;
+ break;
}
}
+ }
+ auto print_argument_types = [&]() {
+ error_line("\tGiven argument types: (");
+ isize i = 0;
+ for (Operand const &o : positional_operands) {
+ if (i++ > 0) error_line(", ");
+ gbString type = type_to_string(o.type);
+ defer (gb_string_free(type));
+ error_line("%s", type);
+ }
+ for (Operand const &o : named_operands) {
+ if (i++ > 0) error_line(", ");
- if (valids.count == 0) {
- begin_error_block();
- defer (end_error_block());
+ gbString type = type_to_string(o.type);
+ defer (gb_string_free(type));
- error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name);
- if (operands.count == 0) {
- error_line("\tNo given arguments\n");
- } else {
- error_line("\tGiven argument types: (");
- for_array(i, operands) {
- Operand o = operands[i];
- if (i > 0) error_line(", ");
- gbString type = type_to_string(o.type);
- defer (gb_string_free(type));
- error_line("%s", type);
- }
- error_line(")\n");
- }
+ if (i < ce->split_args->named.count) {
+ Ast *named_field = ce->split_args->named[i];
+ ast_node(fv, FieldValue, named_field);
- if (procs.count > 0) {
- error_line("Did you mean to use one of the following:\n");
- }
- for_array(i, procs) {
- Entity *proc = procs[i];
- TokenPos pos = proc->token.pos;
- Type *t = base_type(proc->type);
- if (t == t_invalid) continue;
- GB_ASSERT(t->kind == Type_Proc);
- gbString pt;
- defer (gb_string_free(pt));
- if (t->Proc.node != nullptr) {
- pt = expr_to_string(t->Proc.node);
- } else {
- pt = type_to_string(t);
- }
- String prefix = {};
- String prefix_sep = {};
- if (proc->pkg) {
- prefix = proc->pkg->name;
- prefix_sep = str_lit(".");
- }
- String name = proc->token.string;
+ gbString field = expr_to_string(fv->field);
+ defer (gb_string_free(field));
- char const *sep = "::";
- if (proc->kind == Entity_Variable) {
- sep = ":=";
- }
- error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos));
- }
- if (procs.count > 0) {
- error_line("\n");
+ error_line("%s = %s", field, type);
+ } else {
+ error_line("%s", type);
}
+ }
+ error_line(")\n");
+ };
- result_type = t_invalid;
- } else if (valids.count > 1) {
- begin_error_block();
- defer (end_error_block());
+ if (valids.count == 0) {
+ begin_error_block();
+ defer (end_error_block());
- error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name);
- error_line("\tGiven argument types: (");
- for_array(i, operands) {
- Operand o = operands[i];
- if (i > 0) error_line(", ");
- gbString type = type_to_string(o.type);
- defer (gb_string_free(type));
- error_line("%s", type);
- }
- error_line(")\n");
-
- for (isize i = 0; i < valids.count; i++) {
- Entity *proc = proc_entities[valids[i].index];
- GB_ASSERT(proc != nullptr);
- TokenPos pos = proc->token.pos;
- Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc);
- gbString pt = nullptr;
- defer (gb_string_free(pt));
- if (t->Proc.node != nullptr) {
- pt = expr_to_string(t->Proc.node);
- } else {
- pt = type_to_string(t);
- }
- String name = proc->token.string;
- char const *sep = "::";
- if (proc->kind == Entity_Variable) {
- sep = ":=";
- }
- error_line("\t%.*s %s %s ", LIT(name), sep, pt);
- if (proc->decl_info->proc_lit != nullptr) {
- GB_ASSERT(proc->decl_info->proc_lit->kind == Ast_ProcLit);
- auto *pl = &proc->decl_info->proc_lit->ProcLit;
- if (pl->where_token.kind != Token_Invalid) {
- error_line("\n\t\twhere ");
- for_array(j, pl->where_clauses) {
- Ast *clause = pl->where_clauses[j];
- if (j != 0) {
- error_line("\t\t ");
- }
- gbString str = expr_to_string(clause);
- error_line("%s", str);
- gb_string_free(str);
+ error(operand->expr, "No procedures or ambiguous call for procedure group '%s' that match with the given arguments", expr_name);
+ if (positional_operands.count == 0 && named_operands.count == 0) {
+ error_line("\tNo given arguments\n");
+ } else {
+ print_argument_types();
+ }
- if (j != pl->where_clauses.count-1) {
- error_line(",");
- }
- }
- error_line("\n\t");
- }
- }
- error_line("at %s\n", token_pos_to_string(pos));
+ if (procs.count > 0) {
+ error_line("Did you mean to use one of the following:\n");
+ }
+ for (Entity *proc : procs) {
+ TokenPos pos = proc->token.pos;
+ Type *t = base_type(proc->type);
+ if (t == t_invalid) continue;
+ GB_ASSERT(t->kind == Type_Proc);
+ gbString pt;
+ defer (gb_string_free(pt));
+ if (t->Proc.node != nullptr) {
+ pt = expr_to_string(t->Proc.node);
+ } else {
+ pt = type_to_string(t);
}
- result_type = t_invalid;
- } else {
- GB_ASSERT(valids.count == 1);
- Ast *ident = operand->expr;
- while (ident->kind == Ast_SelectorExpr) {
- Ast *s = ident->SelectorExpr.selector;
- ident = s;
+ String prefix = {};
+ String prefix_sep = {};
+ if (proc->pkg) {
+ prefix = proc->pkg->name;
+ prefix_sep = str_lit(".");
}
+ String name = proc->token.string;
- Entity *e = proc_entities[valids[0].index];
- GB_ASSERT(e != nullptr);
-
- proc_type = e->type;
- CallArgumentData data = {};
- CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
- gb_unused(err);
- Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
- add_entity_use(c, ident, entity_to_use);
- if (entity_to_use != nullptr) {
- update_untyped_expr_type(c, operand->expr, entity_to_use->type, true);
+ char const *sep = "::";
+ if (proc->kind == Entity_Variable) {
+ sep = ":=";
}
+ error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos));
+ }
+ if (procs.count > 0) {
+ error_line("\n");
+ }
- if (data.gen_entity != nullptr) {
- Entity *e = data.gen_entity;
- DeclInfo *decl = data.gen_entity->decl_info;
- CheckerContext ctx = *c;
- ctx.scope = decl->scope;
- ctx.decl = decl;
- ctx.proc_name = e->token.string;
- ctx.curr_proc_decl = decl;
- ctx.curr_proc_sig = e->type;
+ data.result_type = t_invalid;
+ } else if (valids.count > 1) {
+ begin_error_block();
+ defer (end_error_block());
+
+ error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name);
+ print_argument_types();
- GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
- decl->where_clauses_evaluated = true;
+ for (isize i = 0; i < valids.count; i++) {
+ Entity *proc = proc_entities[valids[i].index];
+ GB_ASSERT(proc != nullptr);
+ TokenPos pos = proc->token.pos;
+ Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc);
+ gbString pt = nullptr;
+ defer (gb_string_free(pt));
+ if (t->Proc.node != nullptr) {
+ pt = expr_to_string(t->Proc.node);
+ } else {
+ pt = type_to_string(t);
+ }
+ String name = proc->token.string;
+ char const *sep = "::";
+ if (proc->kind == Entity_Variable) {
+ sep = ":=";
+ }
+ error_line("\t%.*s %s %s ", LIT(name), sep, pt);
+ if (proc->decl_info->proc_lit != nullptr) {
+ GB_ASSERT(proc->decl_info->proc_lit->kind == Ast_ProcLit);
+ auto *pl = &proc->decl_info->proc_lit->ProcLit;
+ if (pl->where_token.kind != Token_Invalid) {
+ error_line("\n\t\twhere ");
+ for_array(j, pl->where_clauses) {
+ Ast *clause = pl->where_clauses[j];
+ if (j != 0) {
+ error_line("\t\t ");
+ }
+ gbString str = expr_to_string(clause);
+ error_line("%s", str);
+ gb_string_free(str);
- if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) {
- check_procedure_later(c, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags);
+ if (j != pl->where_clauses.count-1) {
+ error_line(",");
+ }
+ }
+ error_line("\n\t");
}
}
- return data;
+ error_line("at %s\n", token_pos_to_string(pos));
}
+ data.result_type = t_invalid;
} else {
+ GB_ASSERT(valids.count == 1);
Ast *ident = operand->expr;
while (ident->kind == Ast_SelectorExpr) {
Ast *s = ident->SelectorExpr.selector;
ident = s;
}
- Entity *e = entity_of_node(ident);
+ Entity *e = proc_entities[valids[0].index];
+ GB_ASSERT(e != nullptr);
+ Array<Operand> named_operands = {};
- CallArgumentData data = {};
- CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
- gb_unused(err);
- Entity *entity_to_use = data.gen_entity != nullptr ? data.gen_entity : e;
- add_entity_use(c, ident, entity_to_use);
- if (entity_to_use != nullptr) {
- update_untyped_expr_type(c, operand->expr, entity_to_use->type, true);
+ check_call_arguments_single(c, call, operand,
+ e, e->type,
+ positional_operands, named_operands,
+ CallArgumentErrorMode::ShowErrors,
+ &data);
+ return data;
+ }
+
+ return data;
+}
+
+
+gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Ast *call) {
+ Type *proc_type = nullptr;
+
+ CallArgumentData data = {};
+ data.result_type = t_invalid;
+
+ proc_type = base_type(operand->type);
+
+ TypeProc *pt = nullptr;
+ if (proc_type) {
+ pt = &proc_type->Proc;
+ }
+
+ TEMPORARY_ALLOCATOR_GUARD();
+ ast_node(ce, CallExpr, call);
+
+ bool any_failure = false;
+
+ // Split positional and named args into separate arrays/slices
+ Slice<Ast *> positional_args = {};
+ Slice<Ast *> named_args = {};
+
+ if (ce->split_args == nullptr) {
+ positional_args = ce->args;
+ for (isize i = 0; i < ce->args.count; i++) {
+ Ast *arg = ce->args.data[i];
+ if (arg->kind == Ast_FieldValue) {
+ positional_args.count = i;
+ break;
+ }
}
- if (data.gen_entity != nullptr) {
- Entity *e = data.gen_entity;
- DeclInfo *decl = data.gen_entity->decl_info;
- CheckerContext ctx = *c;
- ctx.scope = decl->scope;
- ctx.decl = decl;
- ctx.proc_name = e->token.string;
- ctx.curr_proc_decl = decl;
- ctx.curr_proc_sig = e->type;
+ named_args = slice(ce->args, positional_args.count, ce->args.count);
- GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
- decl->where_clauses_evaluated = true;
+ auto split_args = gb_alloc_item(permanent_allocator(), AstSplitArgs);
+ split_args->positional = positional_args;
+ split_args->named = named_args;
+ ce->split_args = split_args;
+ } else {
+ positional_args = ce->split_args->positional;
+ named_args = ce->split_args->named;
+ }
+
+ if (operand->mode == Addressing_ProcGroup) {
+ return check_call_arguments_proc_group(c, operand, call);
+ }
+
+ auto positional_operands = array_make<Operand>(heap_allocator(), 0, positional_args.count);
+ auto named_operands = array_make<Operand>(heap_allocator(), 0, 0);
- if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) {
- check_procedure_later(c, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags);
+ defer (array_free(&positional_operands));
+ defer (array_free(&named_operands));
+
+ if (positional_args.count > 0) {
+ isize lhs_count = -1;
+ bool is_variadic = false;
+ Entity **lhs = nullptr;
+ if (pt != nullptr) {
+ lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic);
+ }
+ check_unpack_arguments(c, lhs, lhs_count, &positional_operands, positional_args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
+ }
+
+ if (named_args.count > 0) {
+ for_array(i, named_args) {
+ Ast *arg = named_args[i];
+ if (arg->kind != Ast_FieldValue) {
+ error(arg, "Expected a 'field = value'");
+ return data;
+ }
+ ast_node(fv, FieldValue, arg);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(arg, "Invalid parameter name '%s' in procedure call", expr_str);
+ any_failure = true;
+ gb_string_free(expr_str);
+ continue;
}
+ String key = fv->field->Ident.token.string;
+ Ast *value = fv->value;
+
+ isize param_index = lookup_procedure_parameter(pt, key);
+ Type *type_hint = nullptr;
+ if (param_index >= 0) {
+ Entity *e = pt->params->Tuple.variables[param_index];
+ type_hint = e->type;
+ }
+
+ Operand o = {};
+ check_expr_with_type_hint(c, &o, value, type_hint);
+ if (o.mode == Addressing_Invalid) {
+ any_failure = true;
+ }
+ array_add(&named_operands, o);
}
- return data;
+
}
+ if (!any_failure) {
+ check_call_arguments_single(c, call, operand,
+ nullptr, proc_type,
+ positional_operands, named_operands,
+ CallArgumentErrorMode::ShowErrors,
+ &data);
+ } else if (pt) {
+ data.result_type = pt->results;
+ }
- CallArgumentData data = {};
- data.result_type = t_invalid;
return data;
}
-
-isize lookup_polymorphic_record_parameter(Type *t, String parameter_name) {
+gb_internal isize lookup_polymorphic_record_parameter(Type *t, String parameter_name) {
if (!is_type_polymorphic_record(t)) {
return -1;
}
@@ -6288,7 +6508,7 @@ isize lookup_polymorphic_record_parameter(Type *t, String parameter_name) {
}
-CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *operand, Ast *call) {
+gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *operand, Ast *call) {
ast_node(ce, CallExpr, call);
Type *original_type = operand->type;
@@ -6349,7 +6569,7 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
lhs_count = params->variables.count;
}
- check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, false, false);
+ check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, UnpackFlag_None);
}
}
@@ -6375,6 +6595,8 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
ordered_operands = array_make<Operand>(permanent_allocator(), param_count);
array_copy(&ordered_operands, operands, 0);
} else {
+ TEMPORARY_ALLOCATOR_GUARD();
+
bool *visited = gb_alloc_array(temporary_allocator(), bool, param_count);
// LEAK(bill)
@@ -6607,8 +6829,48 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
+// returns true on success
+gb_internal bool check_call_parameter_mixture(Slice<Ast *> const &args, char const *context, bool allow_mixed=false) {
+ bool success = true;
+ if (args.count > 0) {
+ if (allow_mixed) {
+ bool was_named = false;
+ for (Ast *arg : args) {
+ if (was_named && arg->kind != Ast_FieldValue) {
+ error(arg, "Non-named parameter is not allowed to follow named parameter i.e. 'field = value' in a %s", context);
+ success = false;
+ break;
+ }
+ was_named = was_named || arg->kind == Ast_FieldValue;
+ }
+ } else {
+ bool first_is_field_value = (args[0]->kind == Ast_FieldValue);
+ for (Ast *arg : args) {
+ bool mix = false;
+ if (first_is_field_value) {
+ mix = arg->kind != Ast_FieldValue;
+ } else {
+ mix = arg->kind == Ast_FieldValue;
+ }
+ if (mix) {
+ error(arg, "Mixture of 'field = value' and value elements in a %s is not allowed", context);
+ success = false;
+ }
+ }
+ }
+
+ }
+ return success;
+}
+
+#define CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN(context_, ...) if (!check_call_parameter_mixture(args, context_, ##__VA_ARGS__)) { \
+ operand->mode = Addressing_Invalid; \
+ operand->expr = call; \
+ return Expr_Stmt; \
+}
+
-ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, Type *type_hint) {
+gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, Type *type_hint) {
if (proc != nullptr &&
proc->kind == Ast_BasicDirective) {
ast_node(bd, BasicDirective, proc);
@@ -6627,7 +6889,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->info, proc, operand->mode, operand->type, operand->value);
+ add_type_and_value(c, proc, operand->mode, operand->type, operand->value);
} else {
error(proc, "Unknown directive: #%.*s", LIT(name));
operand->expr = proc;
@@ -6646,33 +6908,9 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
}
}
- if (args.count > 0) {
- bool fail = false;
- bool first_is_field_value = (args[0]->kind == Ast_FieldValue);
- for_array(i, args) {
- Ast *arg = args[i];
- bool mix = false;
- if (first_is_field_value) {
- mix = arg->kind != Ast_FieldValue;
- } else {
- mix = arg->kind == Ast_FieldValue;
- }
- if (mix) {
- error(arg, "Mixture of 'field = value' and value elements in a procedure call is not allowed");
- fail = true;
- }
- }
-
- if (fail) {
- operand->mode = Addressing_Invalid;
- operand->expr = call;
- return Expr_Stmt;
- }
- }
-
if (operand->mode == Addressing_Invalid) {
- for_array(i, args) {
- Ast *arg = args[i];
+ CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call");
+ for (Ast *arg : args) {
if (arg->kind == Ast_FieldValue) {
arg = arg->FieldValue.value;
}
@@ -6686,6 +6924,8 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
if (operand->mode == Addressing_Type) {
Type *t = operand->type;
if (is_type_polymorphic_record(t)) {
+ CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("polymorphic type construction");
+
if (!is_type_named(t)) {
gbString s = expr_to_string(operand->expr);
error(call, "Illegal use of an unnamed polymorphic record, %s", s);
@@ -6705,20 +6945,29 @@ 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->info, call, Addressing_Type, ot, empty_exact_value);
+ add_type_and_value(c, call, Addressing_Type, ot, empty_exact_value);
} else {
operand->mode = Addressing_Invalid;
operand->type = t_invalid;
}
} else {
- gbString str = type_to_string(t);
- defer (gb_string_free(str));
+ CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("type conversion");
operand->mode = Addressing_Invalid;
isize arg_count = args.count;
switch (arg_count) {
- case 0: error(call, "Missing argument in conversion to '%s'", str); break;
- default: error(call, "Too many arguments in conversion to '%s'", str); break;
+ case 0:
+ {
+ gbString str = type_to_string(t);
+ error(call, "Missing argument in conversion to '%s'", str);
+ gb_string_free(str);
+ } break;
+ default:
+ {
+ gbString str = type_to_string(t);
+ error(call, "Too many arguments in conversion to '%s'", str);
+ gb_string_free(str);
+ } break;
case 1: {
Ast *arg = args[0];
if (arg->kind == Ast_FieldValue) {
@@ -6749,7 +6998,13 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
}
if (operand->mode == Addressing_Builtin) {
+ CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("builtin call");
+
i32 id = operand->builtin_id;
+ Entity *e = entity_of_node(operand->expr);
+ if (e != nullptr && e->token.string == "expand_to_tuple") {
+ warning(operand->expr, "'expand_to_tuple' has been replaced with 'expand_values'");
+ }
if (!check_builtin_procedure(c, operand, call, id, type_hint)) {
operand->mode = Addressing_Invalid;
operand->type = t_invalid;
@@ -6758,6 +7013,8 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
return builtin_procs[id].kind;
}
+ CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN(operand->mode == Addressing_ProcGroup ? "procedure group call": "procedure call", true);
+
Entity *initial_entity = entity_of_node(operand->expr);
if (initial_entity != nullptr && initial_entity->kind == Entity_Procedure) {
@@ -6769,8 +7026,8 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
}
}
- Type *proc_type = base_type(operand->type);
if (operand->mode != Addressing_ProcGroup) {
+ Type *proc_type = base_type(operand->type);
bool valid_type = (proc_type != nullptr) && is_type_proc(proc_type);
bool valid_mode = is_operand_value(*operand);
if (!valid_type || !valid_mode) {
@@ -6788,7 +7045,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
}
}
- CallArgumentData data = check_call_arguments(c, operand, proc_type, call, args);
+ CallArgumentData data = check_call_arguments(c, operand, call);
Type *result_type = data.result_type;
gb_zero_item(operand);
operand->expr = call;
@@ -6799,7 +7056,10 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
return Expr_Stmt;
}
- Type *pt = base_type(proc_type);
+ Type *pt = base_type(operand->type);
+ if (pt == nullptr) {
+ pt = t_invalid;
+ }
if (pt == t_invalid) {
if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) {
pt = type_of_expr(operand->expr->CallExpr.proc);
@@ -6844,7 +7104,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
if (decl->proc_lit) {
ast_node(pl, ProcLit, decl->proc_lit);
if (pl->inlining == ProcInlining_no_inline) {
- error(call, "'inline' cannot be applied to a procedure that has be marked as 'no_inline'");
+ error(call, "'#force_inline' cannot be applied to a procedure that has be marked as '#force_no_inline'");
}
}
}
@@ -6857,9 +7117,6 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
operand->expr = call;
{
- if (proc_type == t_invalid) {
- // gb_printf_err("%s\n", expr_to_string(operand->expr));
- }
Type *type = nullptr;
if (operand->expr != nullptr && operand->expr->kind == Ast_CallExpr) {
type = type_of_expr(operand->expr->CallExpr.proc);
@@ -6877,13 +7134,11 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
}
}
- // add_type_and_value(c->info, operand->expr, operand->mode, operand->type, operand->value);
-
return Expr_Expr;
}
-void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) {
+gb_internal void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) {
check_expr_base(c, o, e, t);
check_not_tuple(c, o);
char const *err_str = nullptr;
@@ -6908,7 +7163,7 @@ void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) {
}
}
-bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 *max_count, Type *original_type) {
+gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 *max_count, Type *original_type) {
switch (t->kind) {
case Type_Basic:
if (t->Basic.kind == Basic_string) {
@@ -7021,12 +7276,12 @@ bool check_set_index_data(Operand *o, Type *t, bool indirection, i64 *max_count,
return false;
}
-bool ternary_compare_types(Type *x, Type *y) {
- if (is_type_untyped_undef(x) && type_has_undef(y)) {
+gb_internal bool ternary_compare_types(Type *x, Type *y) {
+ if (is_type_untyped_uninit(x)) {
return true;
} else if (is_type_untyped_nil(x) && type_has_nil(y)) {
return true;
- } else if (is_type_untyped_undef(y) && type_has_undef(x)) {
+ } else if (is_type_untyped_uninit(y)) {
return true;
} else if (is_type_untyped_nil(y) && type_has_nil(x)) {
return true;
@@ -7035,7 +7290,7 @@ bool ternary_compare_types(Type *x, Type *y) {
}
-bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValue *inline_for_depth_, Type *type_hint=nullptr) {
+gb_internal bool check_range(CheckerContext *c, Ast *node, bool is_for_loop, Operand *x, Operand *y, ExactValue *inline_for_depth_, Type *type_hint=nullptr) {
if (!is_ast_range(node)) {
return false;
}
@@ -7084,9 +7339,17 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu
}
Type *type = x->type;
- if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) {
- error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
- return false;
+
+ if (is_for_loop) {
+ if (!is_type_integer(type) && !is_type_float(type) && !is_type_enum(type)) {
+ error(ie->op, "Only numerical types are allowed within interval expressions");
+ return false;
+ }
+ } else {
+ if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) {
+ error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
+ return false;
+ }
}
if (x->mode == Addressing_Constant &&
@@ -7121,13 +7384,13 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu
return false;
}
- 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);
+ add_type_and_value(c, ie->left, x->mode, x->type, x->value);
+ add_type_and_value(c, ie->right, y->mode, y->type, y->value);
return true;
}
-bool check_is_operand_compound_lit_constant(CheckerContext *c, Operand *o) {
+gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Operand *o) {
if (is_operand_nil(*o)) {
return true;
}
@@ -7138,7 +7401,7 @@ bool check_is_operand_compound_lit_constant(CheckerContext *c, Operand *o) {
return true;
}
if (expr->kind == Ast_ProcLit) {
- add_type_and_value(c->info, expr, Addressing_Constant, type_of_expr(expr), exact_value_procedure(expr));
+ add_type_and_value(c, expr, Addressing_Constant, type_of_expr(expr), exact_value_procedure(expr));
return true;
}
}
@@ -7146,7 +7409,7 @@ bool check_is_operand_compound_lit_constant(CheckerContext *c, Operand *o) {
}
-bool attempt_implicit_selector_expr(CheckerContext *c, Operand *o, AstImplicitSelectorExpr *ise, Type *th) {
+gb_internal bool attempt_implicit_selector_expr(CheckerContext *c, Operand *o, AstImplicitSelectorExpr *ise, Type *th) {
if (is_type_enum(th)) {
Type *enum_type = base_type(th);
GB_ASSERT(enum_type->kind == Type_Enum);
@@ -7165,12 +7428,12 @@ bool attempt_implicit_selector_expr(CheckerContext *c, Operand *o, AstImplicitSe
return true;
}
if (is_type_union(th)) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
Type *union_type = base_type(th);
auto operands = array_make<Operand>(temporary_allocator(), 0, union_type->Union.variants.count);
- for_array(i, union_type->Union.variants) {
- Type *vt = union_type->Union.variants[i];
-
+ for (Type *vt : union_type->Union.variants) {
Operand x = {};
if (attempt_implicit_selector_expr(c, &x, ise, vt)) {
array_add(&operands, x);
@@ -7185,7 +7448,7 @@ bool attempt_implicit_selector_expr(CheckerContext *c, Operand *o, AstImplicitSe
return false;
}
-ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ast_node(ise, ImplicitSelectorExpr, node);
o->type = t_invalid;
@@ -7229,7 +7492,7 @@ ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o, Ast *node,
}
-void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_) {
+gb_internal void check_promote_optional_ok(CheckerContext *c, Operand *x, Type **val_type_, Type **ok_type_) {
switch (x->mode) {
case Addressing_MapIndex:
case Addressing_OptionalOk:
@@ -7247,7 +7510,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->info, x->expr, x->mode, tuple, x->value);
+ add_type_and_value(c, x->expr, x->mode, tuple, x->value);
if (pt->Proc.result_count >= 2) {
if (ok_type_) *ok_type_ = tuple->Tuple.variables[1]->type;
@@ -7260,13 +7523,13 @@ 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->info, x->expr, x->mode, tuple, x->value);
+ add_type_and_value(c, x->expr, x->mode, tuple, x->value);
x->type = tuple;
GB_ASSERT(is_type_tuple(type_of_expr(x->expr)));
}
-void check_matrix_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+gb_internal void check_matrix_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ast_node(ie, MatrixIndexExpr, node);
check_expr(c, o, ie->expr);
@@ -7325,8 +7588,10 @@ void check_matrix_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *typ
i64 column_index = 0;
bool row_ok = check_index_value(c, t, false, ie->row_index, row_count, &row_index, nullptr);
bool column_ok = check_index_value(c, t, false, ie->column_index, column_count, &column_index, nullptr);
-
-
+ if (is_const && (ie->row_index->tav.mode != Addressing_Constant || ie->column_index->tav.mode != Addressing_Constant)) {
+ error(o->expr, "Cannot index constant matrix with non-constant indices '%s'", expr_to_string(node));
+ }
+
gb_unused(row_ok);
gb_unused(column_ok);
}
@@ -7339,7 +7604,7 @@ struct TypeAndToken {
typedef PtrMap<uintptr, TypeAndToken> SeenMap;
-void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Operand operand, bool use_expr = true) {
+gb_internal void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Operand operand, bool use_expr = true) {
if (operand.mode != Addressing_Constant) {
return;
}
@@ -7350,6 +7615,8 @@ void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Operand operan
uintptr key = hash_exact_value(operand.value);
TypeAndToken *found = map_get(seen, key);
if (found != nullptr) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
isize count = multi_map_count(seen, key);
TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
@@ -7381,7 +7648,7 @@ void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Operand operan
}
-void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, TokenKind upper_op, Operand const &x, Operand const &lhs, Operand const &rhs) {
+gb_internal void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, TokenKind upper_op, Operand const &x, Operand const &lhs, Operand const &rhs) {
if (is_type_enum(x.type)) {
// TODO(bill): Fix this logic so it's fast!!!
@@ -7400,8 +7667,7 @@ void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, TokenKind upper_op, Ope
}
bool found = false;
- for_array(j, bt->Enum.fields) {
- Entity *f = bt->Enum.fields[j];
+ for (Entity *f : bt->Enum.fields) {
GB_ASSERT(f->kind == Entity_Constant);
i64 fv = exact_value_to_i64(f->Constant.value);
@@ -7422,11 +7688,11 @@ void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, TokenKind upper_op, Ope
}
}
}
-void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, Operand const &x) {
+gb_internal void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, Operand const &x) {
add_constant_switch_case(ctx, seen, x);
}
-ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ast_node(bd, BasicDirective, node);
ExprKind kind = Expr_Expr;
@@ -7480,7 +7746,7 @@ ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, Ast *node, Ty
return kind;
}
-ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+gb_internal ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = Expr_Expr;
Operand cond = {Addressing_Invalid};
ast_node(te, TernaryIfExpr, node);
@@ -7521,16 +7787,27 @@ ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *t
return kind;
}
- convert_to_typed(c, &x, y.type);
+ bool use_type_hint = type_hint != nullptr && (is_operand_nil(x) || is_operand_nil(y));
+
+ convert_to_typed(c, &x, use_type_hint ? type_hint : y.type);
if (x.mode == Addressing_Invalid) {
return kind;
}
- convert_to_typed(c, &y, x.type);
+ convert_to_typed(c, &y, use_type_hint ? type_hint : x.type);
if (y.mode == Addressing_Invalid) {
x.mode = Addressing_Invalid;
return kind;
}
+ // NOTE(bill, 2023-01-30): Allow for expression like this:
+ // x: union{f32} = f32(123) if cond else nil
+ if (type_hint && !is_type_any(type_hint) && !ternary_compare_types(x.type, y.type)) {
+ if (check_is_assignable_to(c, &x, type_hint) && check_is_assignable_to(c, &y, type_hint)) {
+ check_cast(c, &x, type_hint);
+ check_cast(c, &y, type_hint);
+ }
+ }
+
if (!ternary_compare_types(x.type, y.type)) {
gbString its = type_to_string(x.type);
gbString ets = type_to_string(y.type);
@@ -7541,13 +7818,13 @@ ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *t
}
o->type = x.type;
- if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) {
+ if (is_type_untyped_nil(o->type) || is_type_untyped_uninit(o->type)) {
o->type = y.type;
}
o->mode = Addressing_Value;
o->expr = node;
- if (type_hint != nullptr && is_type_untyped(o->type)) {
+ if (type_hint != nullptr && is_type_untyped(o->type) && !is_type_any(type_hint)) {
if (check_cast_internal(c, &x, type_hint) &&
check_cast_internal(c, &y, type_hint)) {
convert_to_typed(c, o, type_hint);
@@ -7558,7 +7835,7 @@ ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *t
return kind;
}
-ExprKind check_ternary_when_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+gb_internal ExprKind check_ternary_when_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = Expr_Expr;
Operand cond = {};
ast_node(te, TernaryWhenExpr, node);
@@ -7585,7 +7862,7 @@ ExprKind check_ternary_when_expr(CheckerContext *c, Operand *o, Ast *node, Type
return kind;
}
-ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+gb_internal ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ast_node(oe, OrElseExpr, node);
String name = oe->token.string;
@@ -7680,7 +7957,7 @@ ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type
Type *left_type = nullptr;
Type *right_type = nullptr;
check_or_else_split_types(c, &x, name, &left_type, &right_type);
- add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value);
+ add_type_and_value(c, arg, x.mode, x.type, x.value);
if (left_type != nullptr) {
if (!y_is_diverging) {
@@ -7699,7 +7976,7 @@ ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type
return Expr_Expr;
}
-ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ast_node(re, OrReturnExpr, node);
String name = re->token.string;
@@ -7715,7 +7992,7 @@ ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *node, Type *ty
Type *left_type = nullptr;
Type *right_type = nullptr;
check_or_return_split_types(c, &x, name, &left_type, &right_type);
- add_type_and_value(&c->checker->info, re->expr, x.mode, x.type, x.value);
+ add_type_and_value(c, re->expr, x.mode, x.type, x.value);
if (right_type == nullptr) {
check_or_else_expr_no_value_error(c, name, x, type_hint);
@@ -7741,8 +8018,11 @@ ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *node, Type *ty
rhs.type = right_type;
rhs.mode = Addressing_Value;
- // TODO(bill): better error message
- if (!check_is_assignable_to(c, &rhs, end_type)) {
+ if (is_type_boolean(right_type) && is_type_boolean(end_type)) {
+ // NOTE(bill): allow implicit conversion between boolean types
+ // within 'or_return' to improve the experience using third-party code
+ } else if (!check_is_assignable_to(c, &rhs, end_type)) {
+ // TODO(bill): better error message
gbString a = type_to_string(right_type);
gbString b = type_to_string(end_type);
gbString ret_type = type_to_string(result_type);
@@ -7778,7 +8058,125 @@ ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *node, Type *ty
return Expr_Expr;
}
-ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+
+gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<Ast *> const &elems, Operand *o, Type *type, bool &is_constant) {
+ Type *bt = base_type(type);
+
+ StringSet fields_visited = {};
+ defer (string_set_destroy(&fields_visited));
+
+ StringMap<String> fields_visited_through_raw_union = {};
+ defer (string_map_destroy(&fields_visited_through_raw_union));
+
+ for (Ast *elem : elems) {
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(elem, "Invalid field name '%s' in structure literal", expr_str);
+ gb_string_free(expr_str);
+ continue;
+ }
+ String name = fv->field->Ident.token.string;
+
+ Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
+ bool is_unknown = sel.entity == nullptr;
+ if (is_unknown) {
+ error(fv->field, "Unknown field '%.*s' in structure literal", LIT(name));
+ continue;
+ }
+
+ Entity *field = bt->Struct.fields[sel.index[0]];
+ add_entity_use(c, fv->field, field);
+ if (string_set_update(&fields_visited, name)) {
+ if (sel.index.count > 1) {
+ if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) {
+ error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
+ } else {
+ error(fv->field, "Duplicate or reused field '%.*s' in structure literal", LIT(sel.entity->token.string));
+ }
+ } else {
+ error(fv->field, "Duplicate field '%.*s' in structure literal", LIT(field->token.string));
+ }
+ continue;
+ } else if (String *found = string_map_get(&fields_visited_through_raw_union, sel.entity->token.string)) {
+ error(fv->field, "Field '%.*s' is already initialized due to a previously assigned struct #raw_union field '%.*s'", LIT(sel.entity->token.string), LIT(*found));
+ continue;
+ }
+ if (sel.indirect) {
+ error(fv->field, "Cannot assign to the %d-nested anonymous indirect field '%.*s' in a structure literal", cast(int)sel.index.count-1, LIT(name));
+ continue;
+ }
+
+ if (sel.index.count > 1) {
+ if (is_constant) {
+ Type *ft = type;
+ for (i32 index : sel.index) {
+ Type *bt = base_type(ft);
+ switch (bt->kind) {
+ case Type_Struct:
+ if (bt->Struct.is_raw_union) {
+ is_constant = false;
+ break;
+ }
+ ft = bt->Struct.fields[index]->type;
+ break;
+ case Type_Array:
+ ft = bt->Array.elem;
+ break;
+ default:
+ GB_PANIC("invalid type: %s", type_to_string(ft));
+ break;
+ }
+ }
+ if (is_constant &&
+ (is_type_any(ft) || is_type_union(ft) || is_type_raw_union(ft) || is_type_typeid(ft))) {
+ is_constant = false;
+ }
+ }
+
+ Type *nested_ft = bt;
+ for (i32 index : sel.index) {
+ Type *bt = base_type(nested_ft);
+ switch (bt->kind) {
+ case Type_Struct:
+ if (bt->Struct.is_raw_union) {
+ for (Entity *re : bt->Struct.fields) {
+ string_map_set(&fields_visited_through_raw_union, re->token.string, sel.entity->token.string);
+ }
+ }
+ nested_ft = bt->Struct.fields[index]->type;
+ break;
+ case Type_Array:
+ nested_ft = bt->Array.elem;
+ break;
+ default:
+ GB_PANIC("invalid type %s", type_to_string(nested_ft));
+ break;
+ }
+ }
+ field = sel.entity;
+ }
+
+
+ Operand o = {};
+ check_expr_or_type(c, &o, fv->value, field->type);
+
+ if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
+ is_constant = false;
+ }
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &o);
+ }
+
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
+ }
+}
+
+gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = Expr_Expr;
ast_node(cl, CompoundLit, node);
@@ -7865,45 +8263,13 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
gb_string_free(type_str);
} else {
- Ast *elem = cl->elems[0];
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in structure literal", expr_str);
- gb_string_free(expr_str);
- break;
- }
-
- String name = fv->field->Ident.token.string;
-
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- bool is_unknown = sel.entity == nullptr;
- if (is_unknown) {
- error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
- break;
- }
-
- if (sel.index.count > 1) {
- error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
- break;
- }
-
- Entity *field = t->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
-
- Operand o = {};
- check_expr_or_type(c, &o, fv->value, field->type);
-
-
- check_assignment(c, &o, field->type, str_lit("structure literal"));
+ check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
}
-
}
}
break;
}
-
isize field_count = t->Struct.fields.count;
isize min_field_count = t->Struct.fields.count;
for (isize i = min_field_count-1; i >= 0; i--) {
@@ -7917,57 +8283,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
}
if (cl->elems[0]->kind == Ast_FieldValue) {
- bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count);
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in structure literal", expr_str);
- gb_string_free(expr_str);
- continue;
- }
- String name = fv->field->Ident.token.string;
-
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- bool is_unknown = sel.entity == nullptr;
- if (is_unknown) {
- error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
- continue;
- }
-
- if (sel.index.count > 1) {
- error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
- continue;
- }
-
- Entity *field = t->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
-
- if (fields_visited[sel.index[0]]) {
- error(elem, "Duplicate field '%.*s' in structure literal", LIT(name));
- continue;
- }
-
- fields_visited[sel.index[0]] = true;
-
- Operand o = {};
- check_expr_or_type(c, &o, fv->value, field->type);
-
- if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
- is_constant = false;
- }
- if (is_constant) {
- is_constant = check_is_operand_compound_lit_constant(c, &o);
- }
-
- check_assignment(c, &o, field->type, str_lit("structure literal"));
- }
+ check_compound_literal_field_values(c, cl->elems, o, type, is_constant);
} else {
bool seen_field_value = false;
@@ -8072,8 +8388,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
RangeCache rc = range_cache_make(heap_allocator());
defer (range_cache_destroy(&rc));
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
+ for (Ast *elem : cl->elems) {
if (elem->kind != Ast_FieldValue) {
error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
continue;
@@ -8085,7 +8400,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
Operand x = {};
Operand y = {};
- bool ok = check_range(c, fv->field, &x, &y, nullptr);
+ bool ok = check_range(c, fv->field, false, &x, &y, nullptr);
if (!ok) {
continue;
}
@@ -8141,7 +8456,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
error(elem, "Expected a constant integer as an array field");
continue;
}
- // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value);
+ // add_type_and_value(c, op_index.expr, op_index.mode, op_index.type, op_index.value);
i64 index = exact_value_to_i64(op_index.value);
@@ -8254,8 +8569,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
{
Type *bt = base_type(index_type);
GB_ASSERT(bt->kind == Type_Enum);
- for_array(i, bt->Enum.fields) {
- Entity *f = bt->Enum.fields[i];
+ for (Entity *f : bt->Enum.fields) {
if (f->kind != Entity_Constant) {
continue;
}
@@ -8284,15 +8598,13 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
bool is_partial = cl->tag && (cl->tag->BasicDirective.name.string == "partial");
SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue
- map_init(&seen, heap_allocator());
defer (map_destroy(&seen));
if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
RangeCache rc = range_cache_make(heap_allocator());
defer (range_cache_destroy(&rc));
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
+ for (Ast *elem : cl->elems) {
if (elem->kind != Ast_FieldValue) {
error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
continue;
@@ -8304,7 +8616,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
Operand x = {};
Operand y = {};
- bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type);
+ bool ok = check_range(c, fv->field, false, &x, &y, nullptr, index_type);
if (!ok) {
continue;
}
@@ -8450,14 +8762,15 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
// NOTE(bill): Check for missing cases when `#partial literal` is not present
if (cl->elems.count > 0 && !was_error && !is_partial) {
+ TEMPORARY_ALLOCATOR_GUARD();
+
Type *et = base_type(index_type);
GB_ASSERT(et->kind == Type_Enum);
auto fields = et->Enum.fields;
auto unhandled = array_make<Entity *>(temporary_allocator(), 0, fields.count);
- for_array(i, fields) {
- Entity *f = fields[i];
+ for (Entity *f : fields) {
if (f->kind != Entity_Constant) {
continue;
}
@@ -8583,8 +8896,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
bool key_is_typeid = is_type_typeid(t->Map.key);
bool value_is_typeid = is_type_typeid(t->Map.value);
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
+ for (Ast *elem : cl->elems) {
if (elem->kind != Ast_FieldValue) {
error(elem, "Only 'field = value' elements are allowed in a map literal");
continue;
@@ -8633,8 +8945,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed");
is_constant = false;
} else {
- for_array(index, cl->elems) {
- Ast *elem = cl->elems[index];
+ for (Ast *elem : cl->elems) {
if (elem->kind == Ast_FieldValue) {
error(elem, "'field = value' in a bit_set a literal is not allowed");
continue;
@@ -8686,8 +8997,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
BigInt one = {};
big_int_from_u64(&one, 1);
- for_array(i, cl->elems) {
- Ast *e = cl->elems[i];
+ for (Ast *e : cl->elems) {
GB_ASSERT(e->kind != Ast_FieldValue);
TypeAndValue tav = e->tav;
@@ -8742,7 +9052,7 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
return kind;
}
-ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+gb_internal ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = Expr_Expr;
ast_node(ta, TypeAssertion, node);
check_expr(c, o, ta->expr);
@@ -8786,8 +9096,7 @@ ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *node, Type *ty
if (bsrc->Union.variants.count != 1 && type_hint != nullptr) {
bool allowed = false;
- for_array(i, bsrc->Union.variants) {
- Type *vt = bsrc->Union.variants[i];
+ for (Type *vt : bsrc->Union.variants) {
if (are_types_identical(vt, type_hint)) {
allowed = true;
add_type_info_type(c, vt);
@@ -8820,8 +9129,7 @@ ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *node, Type *ty
if (is_type_union(src)) {
bool ok = false;
- for_array(i, bsrc->Union.variants) {
- Type *vt = bsrc->Union.variants[i];
+ for (Type *vt : bsrc->Union.variants) {
if (are_types_identical(vt, dst)) {
ok = true;
break;
@@ -8871,7 +9179,7 @@ ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *node, Type *ty
return kind;
}
-ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ast_node(se, SelectorCallExpr, node);
// IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables
// COM APIs is a great example of where this kind of thing is extremely useful
@@ -8981,8 +9289,7 @@ ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type
if (ce->args.count > 0) {
bool fail = false;
bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
- for_array(i, ce->args) {
- Ast *arg = ce->args[i];
+ for (Ast *arg : ce->args) {
bool mix = false;
if (first_is_field_value) {
mix = arg->kind != Ast_FieldValue;
@@ -9019,7 +9326,7 @@ ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type
}
-ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = Expr_Expr;
ast_node(ie, IndexExpr, node);
check_expr(c, o, ie->expr);
@@ -9143,7 +9450,7 @@ ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_h
return kind;
}
-ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = Expr_Stmt;
ast_node(se, SliceExpr, node);
check_expr(c, o, se->expr);
@@ -9331,7 +9638,7 @@ ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_h
return kind;
}
-ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
u32 prev_state_flags = c->state_flags;
defer (c->state_flags = prev_state_flags);
if (node->state_flags != 0) {
@@ -9404,9 +9711,10 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
check_ident(c, o, node, nullptr, type_hint, false);
case_end;
- case_ast_node(u, Undef, node);
+ case_ast_node(u, Uninit, node);
o->mode = Addressing_Value;
- o->type = t_untyped_undef;
+ o->type = t_untyped_uninit;
+ error(node, "Use of --- outside of variable declaration");
case_end;
@@ -9474,7 +9782,10 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
}
pl->decl = decl;
- check_procedure_later(&ctx, ctx.file, empty_token, decl, type, pl->body, pl->tags);
+ check_procedure_later(ctx.checker, ctx.file, empty_token, decl, type, pl->body, pl->tags);
+ mutex_lock(&ctx.checker->nested_proc_lits_mutex);
+ array_add(&ctx.checker->nested_proc_lits, decl);
+ mutex_unlock(&ctx.checker->nested_proc_lits_mutex);
}
check_close_scope(&ctx);
@@ -9758,7 +10069,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
-ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+gb_internal ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = check_expr_base_internal(c, o, node, type_hint);
if (o->type != nullptr && core_type(o->type) == nullptr) {
o->type = t_invalid;
@@ -9775,12 +10086,12 @@ ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hi
}
check_rtti_type_disallowed(node, o->type, "An expression is using a type, %s, which has been disallowed");
- add_type_and_value(c->info, node, o->mode, o->type, o->value);
+ add_type_and_value(c, node, o->mode, o->type, o->value);
return kind;
}
-void check_multi_expr_or_type(CheckerContext *c, Operand *o, Ast *e) {
+gb_internal void check_multi_expr_or_type(CheckerContext *c, Operand *o, Ast *e) {
check_expr_base(c, o, e, nullptr);
switch (o->mode) {
default:
@@ -9792,7 +10103,7 @@ void check_multi_expr_or_type(CheckerContext *c, Operand *o, Ast *e) {
o->mode = Addressing_Invalid;
}
-void check_multi_expr(CheckerContext *c, Operand *o, Ast *e) {
+gb_internal void check_multi_expr(CheckerContext *c, Operand *o, Ast *e) {
check_expr_base(c, o, e, nullptr);
switch (o->mode) {
default:
@@ -9807,7 +10118,7 @@ void check_multi_expr(CheckerContext *c, Operand *o, Ast *e) {
o->mode = Addressing_Invalid;
}
-void check_multi_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *type_hint) {
+gb_internal void check_multi_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *type_hint) {
check_expr_base(c, o, e, type_hint);
switch (o->mode) {
default:
@@ -9822,7 +10133,7 @@ void check_multi_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type
o->mode = Addressing_Invalid;
}
-void check_not_tuple(CheckerContext *c, Operand *o) {
+gb_internal void check_not_tuple(CheckerContext *c, Operand *o) {
if (o->mode == Addressing_Value) {
// NOTE(bill): Tuples are not first class thus never named
if (o->type->kind == Type_Tuple) {
@@ -9835,13 +10146,13 @@ void check_not_tuple(CheckerContext *c, Operand *o) {
}
}
-void check_expr(CheckerContext *c, Operand *o, Ast *e) {
+gb_internal void check_expr(CheckerContext *c, Operand *o, Ast *e) {
check_multi_expr(c, o, e);
check_not_tuple(c, o);
}
-void check_expr_or_type(CheckerContext *c, Operand *o, Ast *e, Type *type_hint) {
+gb_internal void check_expr_or_type(CheckerContext *c, Operand *o, Ast *e, Type *type_hint) {
check_expr_base(c, o, e, type_hint);
check_not_tuple(c, o);
error_operand_no_value(o);
@@ -9849,7 +10160,7 @@ void check_expr_or_type(CheckerContext *c, Operand *o, Ast *e, Type *type_hint)
-bool is_exact_value_zero(ExactValue const &v) {
+gb_internal bool is_exact_value_zero(ExactValue const &v) {
switch (v.kind) {
case ExactValue_Invalid:
return true;
@@ -9884,12 +10195,9 @@ bool is_exact_value_zero(ExactValue const &v) {
if (cl->elems.count == 0) {
return true;
} else {
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
+ for (Ast *elem : cl->elems) {
if (elem->tav.mode != Addressing_Constant) {
- // if (elem->tav.value.kind != ExactValue_Invalid) {
return false;
- // }
}
if (!is_exact_value_zero(elem->tav.value)) {
return false;
@@ -9913,9 +10221,9 @@ bool is_exact_value_zero(ExactValue const &v) {
-gbString write_expr_to_string(gbString str, Ast *node, bool shorthand);
+gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthand);
-gbString write_struct_fields_to_string(gbString str, Slice<Ast *> const &params) {
+gb_internal gbString write_struct_fields_to_string(gbString str, Slice<Ast *> const &params) {
for_array(i, params) {
if (i > 0) {
str = gb_string_appendc(str, ", ");
@@ -9925,7 +10233,7 @@ gbString write_struct_fields_to_string(gbString str, Slice<Ast *> const &params)
return str;
}
-gbString string_append_string(gbString str, String string) {
+gb_internal gbString string_append_string(gbString str, String string) {
if (string.len > 0) {
return gb_string_append_length(str, &string[0], string.len);
}
@@ -9933,13 +10241,13 @@ gbString string_append_string(gbString str, String string) {
}
-gbString string_append_token(gbString str, Token token) {
+gb_internal gbString string_append_token(gbString str, Token token) {
str = string_append_string(str, token.string);
return str;
}
-gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
+gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
if (node == nullptr)
return str;
@@ -9969,7 +10277,7 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
str = string_append_string(str, bd->name.string);
case_end;
- case_ast_node(ud, Undef, node);
+ case_ast_node(ud, Uninit, node);
str = gb_string_appendc(str, "---");
case_end;
@@ -10250,9 +10558,6 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
if (f->flags&FieldFlag_c_vararg) {
str = gb_string_appendc(str, "#c_vararg ");
}
- if (f->flags&FieldFlag_auto_cast) {
- str = gb_string_appendc(str, "auto_cast ");
- }
if (f->flags&FieldFlag_any_int) {
str = gb_string_appendc(str, "#any_int ");
}
@@ -10369,8 +10674,7 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
bool parens_needed = false;
if (pt->results && pt->results->kind == Ast_FieldList) {
- for_array(i, pt->results->FieldList.list) {
- Ast *field = pt->results->FieldList.list[i];
+ for (Ast *field : pt->results->FieldList.list) {
ast_node(f, Field, field);
if (f->names.count != 0) {
parens_needed = true;
@@ -10504,9 +10808,12 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
return str;
}
-gbString expr_to_string(Ast *expression) {
+gb_internal gbString expr_to_string(Ast *expression) {
return write_expr_to_string(gb_string_make(heap_allocator(), ""), expression, false);
}
-gbString expr_to_string_shorthand(Ast *expression) {
+gb_internal gbString expr_to_string(Ast *expression, gbAllocator allocator) {
+ return write_expr_to_string(gb_string_make(allocator, ""), expression, false);
+}
+gb_internal gbString expr_to_string_shorthand(Ast *expression) {
return write_expr_to_string(gb_string_make(heap_allocator(), ""), expression, true);
}