aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorftphikari <ftphikari@gmail.com>2023-07-25 15:32:18 +0300
committerGitHub <noreply@github.com>2023-07-25 15:32:18 +0300
commit699aec331d44da58bceddfb788bf349995473ad9 (patch)
tree3f5ce42c72c18fff1fc79f0229797be72f0e7638 /src
parentd2375a79f29d8377c813484bce3127ae9c205974 (diff)
parent5ac7fe453f5fbf0995c24f0c1c12ed439ae3aee9 (diff)
Merge branch 'odin-lang:master' into master
Diffstat (limited to 'src')
-rw-r--r--src/array.cpp4
-rw-r--r--src/build_settings.cpp6
-rw-r--r--src/check_builtin.cpp4
-rw-r--r--src/check_decl.cpp124
-rw-r--r--src/check_expr.cpp2051
-rw-r--r--src/check_stmt.cpp61
-rw-r--r--src/check_type.cpp11
-rw-r--r--src/checker.cpp67
-rw-r--r--src/checker.hpp3
-rw-r--r--src/entity.cpp6
-rw-r--r--src/error.cpp31
-rw-r--r--src/gb/gb.h39
-rw-r--r--src/llvm_backend.cpp96
-rw-r--r--src/llvm_backend.hpp8
-rw-r--r--src/llvm_backend_const.cpp6
-rw-r--r--src/llvm_backend_expr.cpp46
-rw-r--r--src/llvm_backend_general.cpp113
-rw-r--r--src/llvm_backend_opt.cpp10
-rw-r--r--src/llvm_backend_proc.cpp342
-rw-r--r--src/llvm_backend_stmt.cpp97
-rw-r--r--src/llvm_backend_type.cpp11
-rw-r--r--src/llvm_backend_utility.cpp8
-rw-r--r--src/main.cpp51
-rw-r--r--src/parser.cpp12
-rw-r--r--src/parser.hpp6
-rw-r--r--src/parser_pos.cpp12
-rw-r--r--src/types.cpp26
27 files changed, 1856 insertions, 1395 deletions
diff --git a/src/array.cpp b/src/array.cpp
index f1a1f93e2..d8e25d25d 100644
--- a/src/array.cpp
+++ b/src/array.cpp
@@ -80,7 +80,9 @@ gb_internal Slice<T> slice_make(gbAllocator const &allocator, isize count) {
GB_ASSERT(count >= 0);
Slice<T> s = {};
s.data = gb_alloc_array(allocator, T, count);
- GB_ASSERT(s.data != nullptr);
+ if (count > 0) {
+ GB_ASSERT(s.data != nullptr);
+ }
s.count = count;
return s;
}
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 92e0df38b..866631f9a 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -309,7 +309,7 @@ struct BuildContext {
bool copy_file_contents;
- bool disallow_rtti;
+ bool no_rtti;
bool dynamic_map_calls;
@@ -1227,8 +1227,8 @@ gb_internal void init_build_context(TargetMetrics *cross_target) {
if (bc->metrics.os == TargetOs_freestanding) {
bc->no_entry_point = true;
} else {
- if (bc->disallow_rtti) {
- gb_printf_err("-disallow-rtti is only allowed on freestanding targets\n");
+ if (bc->no_rtti) {
+ gb_printf_err("-no-rtti is only allowed on freestanding targets\n");
gb_exit(1);
}
}
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index 46ee6b7f9..269a0ec48 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -2063,7 +2063,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (c->scope->flags&ScopeFlag_Global) {
compiler_error("'type_info_of' Cannot be declared within the runtime package due to how the internals of the compiler works");
}
- if (build_context.disallow_rtti) {
+ if (build_context.no_rtti) {
error(call, "'%.*s' has been disallowed", LIT(builtin_name));
return false;
}
@@ -2106,7 +2106,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
if (c->scope->flags&ScopeFlag_Global) {
compiler_error("'typeid_of' Cannot be declared within the runtime package due to how the internals of the compiler works");
}
- if (build_context.disallow_rtti) {
+ if (build_context.no_rtti) {
error(call, "'%.*s' has been disallowed", LIT(builtin_name));
return false;
}
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index b651e33e6..2b2fb867c 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -757,6 +757,66 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin
return link_name;
}
+gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext const &ac) {
+ if (!(ac.objc_name.len || ac.objc_is_class_method || ac.objc_type)) {
+ return;
+ }
+ if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
+ error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
+ } else if (ac.objc_type == nullptr) {
+ error(e->token, "@(objc_name) requires that @(objc_type) to be set");
+ } else if (ac.objc_name.len == 0 && ac.objc_type) {
+ error(e->token, "@(objc_name) is required with @(objc_type)");
+ } else {
+ Type *t = ac.objc_type;
+ if (t->kind == Type_Named) {
+ Entity *tn = t->Named.type_name;
+
+ GB_ASSERT(tn->kind == Entity_TypeName);
+
+ if (tn->scope != e->scope) {
+ error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
+ } else {
+ mutex_lock(&global_type_name_objc_metadata_mutex);
+ defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
+
+ if (!tn->TypeName.objc_metadata) {
+ tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
+ }
+ auto *md = tn->TypeName.objc_metadata;
+ mutex_lock(md->mutex);
+ defer (mutex_unlock(md->mutex));
+
+ if (!ac.objc_is_class_method) {
+ bool ok = true;
+ for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
+ if (entry.name == ac.objc_name) {
+ error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+ }
+ } else {
+ bool ok = true;
+ for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
+ if (entry.name == ac.objc_name) {
+ error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+ }
+ }
+ }
+ }
+ }
+}
+
gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
GB_ASSERT(e->type == nullptr);
if (d->proc_lit->kind != Ast_ProcLit) {
@@ -840,62 +900,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
- if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) {
- if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
- error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
- } else if (ac.objc_type == nullptr) {
- error(e->token, "@(objc_name) requires that @(objc_type) to be set");
- } else if (ac.objc_name.len == 0 && ac.objc_type) {
- error(e->token, "@(objc_name) is required with @(objc_type)");
- } else {
- Type *t = ac.objc_type;
- if (t->kind == Type_Named) {
- Entity *tn = t->Named.type_name;
-
- GB_ASSERT(tn->kind == Entity_TypeName);
-
- if (tn->scope != e->scope) {
- error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
- } else {
- mutex_lock(&global_type_name_objc_metadata_mutex);
- defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
-
- if (!tn->TypeName.objc_metadata) {
- tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
- }
- auto *md = tn->TypeName.objc_metadata;
- mutex_lock(md->mutex);
- defer (mutex_unlock(md->mutex));
-
- if (!ac.objc_is_class_method) {
- bool ok = true;
- for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
- if (entry.name == ac.objc_name) {
- error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
- ok = false;
- break;
- }
- }
- if (ok) {
- array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
- }
- } else {
- bool ok = true;
- for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
- if (entry.name == ac.objc_name) {
- error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
- ok = false;
- break;
- }
- }
- if (ok) {
- array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
- }
- }
- }
- }
- }
- }
+ check_objc_methods(ctx, e, ac);
if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) {
error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together");
@@ -1241,7 +1246,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
}
-gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) {
+gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) {
GB_ASSERT(pg_entity->kind == Entity_ProcGroup);
auto *pge = &pg_entity->ProcGroup;
String proc_group_name = pg_entity->token.string;
@@ -1366,6 +1371,11 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity,
}
}
+ AttributeContext ac = {};
+ check_decl_attributes(ctx, d->attributes, proc_group_attribute, &ac);
+ check_objc_methods(ctx, pg_entity, ac);
+
+
}
gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_type) {
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 830b5315d..98154f33d 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -14,6 +14,7 @@ enum CallArgumentError {
CallArgumentError_ParameterMissing,
CallArgumentError_DuplicateParameter,
CallArgumentError_NoneConstantParameter,
+ CallArgumentError_OutOfOrderParameters,
CallArgumentError_MAX,
};
@@ -33,12 +34,13 @@ gb_global char const *CallArgumentError_strings[CallArgumentError_MAX] = {
"ParameterMissing",
"DuplicateParameter",
"NoneConstantParameter",
+ "OutOfOrderParameters",
};
-enum CallArgumentErrorMode {
- CallArgumentMode_NoErrors,
- CallArgumentMode_ShowErrors,
+enum struct CallArgumentErrorMode {
+ NoErrors,
+ ShowErrors,
};
struct CallArgumentData {
@@ -65,11 +67,6 @@ gb_internal 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);
@@ -94,14 +91,13 @@ gb_internal void check_stmt (CheckerContext *c, Ast *nod
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> *operands = nullptr);
+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 CallArgumentData check_call_arguments (CheckerContext *c, Operand *operand, Type *proc_type, Ast *call);
-gb_internal Type * check_init_variable (CheckerContext *c, Entity *e, Operand *operand, String context_name);
+gb_internal Type * check_init_variable (CheckerContext *c, Entity *e, Operand *operand, String context_name);
gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type);
@@ -121,6 +117,7 @@ gb_internal void check_or_return_split_types(CheckerContext *c, Operand *x, Stri
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,
@@ -335,7 +332,7 @@ gb_internal void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes,
}
gb_internal 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) {
+ 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!!! //
@@ -602,7 +599,7 @@ gb_internal bool check_polymorphic_procedure_assignment(CheckerContext *c, Opera
return find_or_generate_polymorphic_procedure(c, base_entity, type, nullptr, poly_def_node, poly_proc_data);
}
-gb_internal 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);
}
@@ -667,6 +664,11 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
if (check_representable_as_constant(c, operand->value, dst, nullptr)) {
if (is_type_typed(dst) && src->kind == Type_Basic) {
switch (src->Basic.kind) {
+ case Basic_UntypedBool:
+ if (is_type_boolean(dst)) {
+ return 1;
+ }
+ break;
case Basic_UntypedRune:
if (is_type_integer(dst) || is_type_rune(dst)) {
return 1;
@@ -677,6 +679,11 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
return 1;
}
break;
+ case Basic_UntypedString:
+ if (is_type_string(dst)) {
+ return 1;
+ }
+ break;
case Basic_UntypedFloat:
if (is_type_float(dst)) {
return 1;
@@ -701,23 +708,58 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
}
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) {
+ Type *d = base_array_type(dst);
+ i64 score = -1;
+ switch (src->Basic.kind) {
+ case Basic_UntypedBool:
+ if (is_type_boolean(d)) {
+ score = 1;
+ }
+ break;
+ case Basic_UntypedRune:
+ if (is_type_integer(d) || is_type_rune(d)) {
+ score = 1;
+ }
+ break;
+ case Basic_UntypedInteger:
+ if (is_type_integer(d) || is_type_rune(d)) {
+ score = 1;
+ }
+ break;
+ case Basic_UntypedString:
+ if (is_type_string(d)) {
+ score = 1;
+ }
+ break;
+ case Basic_UntypedFloat:
+ if (is_type_float(d)) {
+ score = 1;
+ }
+ break;
+ case Basic_UntypedComplex:
+ if (is_type_complex(d)) {
+ score = 1;
+ }
+ if (is_type_quaternion(d)) {
+ score = 2;
+ }
+ break;
+ case Basic_UntypedQuaternion:
+ if (is_type_quaternion(d)) {
+ score = 1;
}
- return 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(d)) {
+ score += 1;
+ }
+ if (d != dst) {
+ score += 6;
}
- return 1;
}
- return -1;
+ return score;
}
}
}
@@ -1677,7 +1719,7 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
break;
case Token_Not:
- if (!is_type_boolean(type)) {
+ if (!is_type_boolean(type) || is_type_array_like(o->type)) {
ERROR_BLOCK();
str = expr_to_string(o->expr);
error(op, "Operator '%.*s' is only allowed on boolean expressions", LIT(op.string));
@@ -2218,6 +2260,37 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) {
return o->mode != Addressing_Variable && o->mode != Addressing_SoaVariable;
}
+gb_internal void check_old_for_or_switch_value_usage(Ast *expr) {
+ if (!build_context.strict_style) {
+ return;
+ }
+
+ Entity *e = entity_of_node(expr);
+ if (e != nullptr && (e->flags & EntityFlag_OldForOrSwitchValue) != 0) {
+ GB_ASSERT(e->kind == Entity_Variable);
+
+ begin_error_block();
+ defer (end_error_block());
+
+ if ((e->flags & EntityFlag_ForValue) != 0) {
+ Type *parent_type = type_deref(e->Variable.for_loop_parent_type);
+
+ error(expr, "Assuming a for-in defined value is addressable as the iterable is passed by value has been disallowed with '-strict-style'.");
+
+ if (is_type_map(parent_type)) {
+ error_line("\tSuggestion: Prefer doing 'for key, &%.*s in ...'\n", LIT(e->token.string));
+ } else {
+ error_line("\tSuggestion: Prefer doing 'for &%.*s in ...'\n", LIT(e->token.string));
+ }
+ } else {
+ GB_ASSERT((e->flags & EntityFlag_SwitchValue) != 0);
+
+ error(expr, "Assuming a switch-in defined value is addressable as the iterable is passed by value has been disallowed with '-strict-style'.");
+ error_line("\tSuggestion: Prefer doing 'switch &%.*s in ...'\n", LIT(e->token.string));
+ }
+ }
+}
+
gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
switch (op.kind) {
case Token_And: { // Pointer address
@@ -2227,7 +2300,7 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
gbString str = expr_to_string(ue->expr);
defer (gb_string_free(str));
- Entity *e = entity_of_node(o->expr);
+ Entity *e = entity_of_node(ue->expr);
if (e != nullptr && (e->flags & EntityFlag_Param) != 0) {
error(op, "Cannot take the pointer address of '%s' which is a procedure parameter", str);
} else {
@@ -2245,7 +2318,15 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
defer (end_error_block());
error(op, "Cannot take the pointer address of '%s'", str);
if (e != nullptr && (e->flags & EntityFlag_ForValue) != 0) {
- error_line("\tSuggestion: Did you want to pass the iterable value to the for statement by pointer to get addressable semantics?\n");
+ Type *parent_type = type_deref(e->Variable.for_loop_parent_type);
+
+ if (parent_type != nullptr && is_type_string(parent_type)) {
+ error_line("\tSuggestion: Iterating over a string produces an intermediate 'rune' value which cannot be addressed.\n");
+ } else if (parent_type != nullptr && is_type_tuple(parent_type)) {
+ error_line("\tSuggestion: Iterating over a procedure does not produce values which are addressable.\n");
+ } else {
+ error_line("\tSuggestion: Did you want to pass the iterable value to the for statement by pointer to get addressable semantics?\n");
+ }
}
if (e != nullptr && (e->flags & EntityFlag_SwitchValue) != 0) {
error_line("\tSuggestion: Did you want to pass the value to the switch statement by pointer to get addressable semantics?\n");
@@ -2270,6 +2351,11 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
o->type = alloc_type_pointer(o->type);
}
} else {
+ if (build_context.strict_style && ast_node_expect(node, Ast_UnaryExpr)) {
+ ast_node(ue, UnaryExpr, node);
+ check_old_for_or_switch_value_usage(ue->expr);
+ }
+
o->type = alloc_type_pointer(o->type);
}
@@ -4688,7 +4774,10 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
if (entity == nullptr && selector->kind == Ast_Ident) {
String field_name = selector->Ident.token.string;
- if (is_type_dynamic_array(type_deref(operand->type))) {
+ Type *t = type_deref(operand->type);
+ if (t == nullptr) {
+ error(operand->expr, "Cannot use a selector expression on 0-value expression");
+ } else if (is_type_dynamic_array(t)) {
init_mem_allocator(c->checker);
}
sel = lookup_field(operand->type, field_name, operand->mode == Addressing_Type);
@@ -5150,14 +5239,19 @@ enum UnpackFlag : u32 {
};
-gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs, UnpackFlags flags) {
+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 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_;
@@ -5165,12 +5259,11 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
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));
@@ -5182,8 +5275,6 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
// 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));
@@ -5193,15 +5284,15 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
}
}
- Ast *rhs_expr = unparen_expr(rhs[i]);
+ 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[i];
- add_type_and_value(c, rhs[i], o.mode, o.type, o.value);
+ o.expr = rhs;
+ add_type_and_value(c, rhs, o.mode, o.type, o.value);
} else {
- check_expr_base(c, &o, rhs[i], type_hint);
+ check_expr_base(c, &o, rhs, type_hint);
}
if (o.mode == Addressing_NoValue) {
error_operand_no_value(&o);
@@ -5209,7 +5300,7 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
}
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);
@@ -5309,7 +5400,36 @@ gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *
}
-gb_internal 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);
@@ -5320,16 +5440,8 @@ gb_internal 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;
-
-
- TypeTuple *param_tuple = nullptr;
- if (pt->params != nullptr) {
- param_tuple = &pt->params->Tuple;
- }
-
+ bool show_error = show_error_mode == CallArgumentErrorMode::ShowErrors;
- CallArgumentError err = CallArgumentError_None;
Type *final_proc_type = proc_type;
Entity *gen_entity = nullptr;
@@ -5347,302 +5459,135 @@ gb_internal 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;
-
- 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;
- }
-
- 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;
- }
-
- if (are_types_identical(e->type, o.type)) {
- score += assign_score_function(1);
- } else {
- score += assign_score_function(MAXIMUM_TYPE_DISTANCE);
- }
+ });
- continue;
- }
+ 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);
- bool param_is_variadic = pt->variadic && pt->variadic_index == operand_index;
+ for (isize i = 0; i < positional_operand_count; i++) {
+ ordered_operands[i] = positional_operands[i];
+ visited[i] = true;
+ }
- i64 s = 0;
- if (!check_is_assignable_to_with_score(c, &o, t, &s, param_is_variadic)) {
- bool ok = false;
- 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"));
- }
- score += s;
+ auto variadic_operands = slice(slice_from_array(positional_operands), positional_operand_count, positional_operands.count);
- 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 (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];
- if (o.mode == Addressing_Type && is_type_typeid(e->type)) {
- add_type_info_type(c, o.type);
- add_type_and_value(c, 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);
+ 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;
}
-
- 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;
-
- if (is_type_polymorphic(t)) {
- error(call, "Ambiguous call to a polymorphic variadic procedure with no variadic input");
- err = CallArgumentError_AmbiguousPolymorphicVariadic;
- }
-
- 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, 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);
- }
+ 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));
}
+ err = CallArgumentError_ParameterNotFound;
+ continue;
}
- }
- }
-
- if (data) {
- data->score = score;
- data->result_type = final_proc_type->Proc.results;
- data->gen_entity = gen_entity;
-
-
- 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 (visited[param_index]) {
+ if (show_error) {
+ error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name));
+ }
+ err = CallArgumentError_DuplicateParameter;
+ continue;
}
- }
- if (proc_lit == nullptr) {
- add_type_and_value(c, ce->proc, Addressing_Value, final_proc_type, {});
- }
- }
-
- return err;
-}
-
-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 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;
+ visited[param_index] = true;
+ ordered_operands[param_index] = operand;
}
}
- return -1;
-}
-gb_internal 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;
+ isize dummy_argument_count = 0;
+ bool actually_variadic = false;
- i64 score = 0;
- bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
- CallArgumentError err = CallArgumentError_None;
+ 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;
- TEMPORARY_ALLOCATOR_GUARD();
+ Operand *variadic_operand = &ordered_operands[pt->variadic_index];
- 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 (Operand const &o : ordered_operands) {
- if (o.expr != nullptr) {
- call->viral_state_flags |= o.expr->viral_state_flags;
- }
- }
- });
+ 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_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));
+ // 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;
}
- 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 (Operand const &o : ordered_operands) {
+ if (o.mode != Addressing_Invalid) {
+ check_no_copy_assignment(o, str_lit("procedure call expression"));
+ }
}
- 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)) {
@@ -5650,6 +5595,11 @@ gb_internal 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;
}
@@ -5672,97 +5622,174 @@ gb_internal 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 (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 (o->mode == Addressing_Type && is_type_typeid(e->type)) {
- add_type_info_type(c, o->type);
- add_type_and_value(c, o->expr, Addressing_Value, e->type, exact_value_typeid(o->type));
+ 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;
+ }
+
+ 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, 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;
}
+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;
@@ -5867,535 +5894,704 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco
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;
-gb_internal 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, 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 (Entity *e : param_tuple->variables) {
- 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 (Entity *proc : procs) {
- Type *proc_type = base_type(proc->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 (Entity *e : param_tuple->variables) {
- 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, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
}
}
- for (Operand const &o : operands) {
- check_no_copy_assignment(o, str_lit("call expression"));
- }
+ return true;
+}
- if (operand->mode == Addressing_ProcGroup) {
- check_entity_decl(c, operand->proc_group, nullptr, nullptr);
- auto procs = proc_group_entities_cloned(c, *operand);
+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);
- if (procs.count > 1) {
- isize max_arg_count = args.count;
- for (Ast *arg : 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);
+ 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, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
+ 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;
- Entity **lhs = nullptr;
- isize lhs_count = -1;
+ 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));
- {
- // 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 (procs.count == 1) {
+ Entity *e = procs[0];
+
+ 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)
+ 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 {
+ proc_arg_count = gb_min(proc_arg_count, pt->Proc.param_count);
}
}
+ }
+ if (proc_arg_count >= 0) {
+ 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 (Entity *p : procs) {
- 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, UnpackFlag_None);
-
- 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;
}
-
- auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count);
- defer (array_free(&valids));
-
- 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);
+ 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;
+ Type *type_hint = nullptr;
- gbString expr_name = expr_to_string(operand->expr);
- defer (gb_string_free(expr_name));
+ 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);
+ }
- 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;
+ gb_free(heap_allocator(), lhs);
- ctx.no_polymorphic_errors = true;
- ctx.allow_polymorphic_types = is_type_polymorphic(pt);
- ctx.hide_polymorphic_errors = true;
+ auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count);
+ defer (array_free(&valids));
- err = call_checker(&ctx, call, pt, p, operands, CallArgumentMode_NoErrors, &data);
- if (err != CallArgumentError_None) {
- continue;
- }
- isize index = i;
+ 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);
+ }
- 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;
- GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- if (!evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) {
- continue;
- }
+ gbString expr_name = expr_to_string(operand->expr);
+ defer (gb_string_free(expr_name));
- array_add(&proc_entities, data.gen_entity);
- index = proc_entities.count-1;
- }
+ 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;
- ValidIndexAndScore item = {};
- item.index = index;
- item.score = data.score;
- array_add(&valids, item);
- }
- }
+ ctx.no_polymorphic_errors = true;
+ ctx.allow_polymorphic_types = is_type_polymorphic(pt);
+ ctx.hide_polymorphic_errors = true;
- 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;
- }
+ 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.score = data.score;
- if (valids.count == 0) {
- begin_error_block();
- defer (end_error_block());
+ if (data.gen_entity != nullptr) {
+ array_add(&proc_entities, data.gen_entity);
+ index = proc_entities.count-1;
- 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");
+ // prefer non-polymorphic procedures over polymorphic
+ item.score += assign_score_function(1);
}
- 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);
- }
- String prefix = {};
- String prefix_sep = {};
- if (proc->pkg) {
- prefix = proc->pkg->name;
- prefix_sep = str_lit(".");
- }
- String name = proc->token.string;
+ item.index = index;
+ array_add(&valids, item);
+ }
+ }
- 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 (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 (procs.count > 0) {
- error_line("\n");
+ 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(", ");
+
+ gbString type = type_to_string(o.type);
+ defer (gb_string_free(type));
+
+ if (i < ce->split_args->named.count) {
+ Ast *named_field = ce->split_args->named[i];
+ ast_node(fv, FieldValue, named_field);
- result_type = t_invalid;
- } else if (valids.count > 1) {
- begin_error_block();
- defer (end_error_block());
+ gbString field = expr_to_string(fv->field);
+ defer (gb_string_free(field));
- 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 = %s", field, type);
+ } else {
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_line(")\n");
+ };
- if (j != pl->where_clauses.count-1) {
- error_line(",");
- }
- }
- error_line("\n\t");
- }
- }
- error_line("at %s\n", token_pos_to_string(pos));
- }
- result_type = t_invalid;
+ if (valids.count == 0) {
+ begin_error_block();
+ defer (end_error_block());
+
+ 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 {
- GB_ASSERT(valids.count == 1);
- Ast *ident = operand->expr;
- while (ident->kind == Ast_SelectorExpr) {
- Ast *s = ident->SelectorExpr.selector;
- ident = s;
+ print_argument_types();
+ }
+
+ if (procs.count == 0) {
+ procs = proc_group_entities_cloned(c, *operand);
+ }
+ if (procs.count > 0) {
+ error_line("Did you mean to use one of the following:\n");
+ }
+ isize max_name_length = 0;
+ isize max_type_length = 0;
+ for (Entity *proc : procs) {
+ Type *t = base_type(proc->type);
+ if (t == t_invalid) continue;
+ String prefix = {};
+ String prefix_sep = {};
+ if (proc->pkg) {
+ prefix = proc->pkg->name;
+ prefix_sep = str_lit(".");
+ }
+ String name = proc->token.string;
+ max_name_length = gb_max(max_name_length, prefix.len + prefix_sep.len + name.len);
+
+ gbString pt;
+ if (t->Proc.node != nullptr) {
+ pt = expr_to_string(t->Proc.node);
+ } else {
+ pt = type_to_string(t);
}
- Entity *e = proc_entities[valids[0].index];
- GB_ASSERT(e != nullptr);
+ max_type_length = gb_max(max_type_length, gb_string_length(pt));
+ gb_string_free(pt);
+ }
- 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);
- }
+ isize max_spaces = gb_max(max_name_length, max_type_length);
+ char *spaces = gb_alloc_array(temporary_allocator(), char, max_spaces+1);
+ for (isize i = 0; i < max_spaces; i++) {
+ spaces[i] = ' ';
+ }
+ spaces[max_spaces] = 0;
- 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;
+ 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);
+ }
+ String prefix = {};
+ String prefix_sep = {};
+ if (proc->pkg) {
+ prefix = proc->pkg->name;
+ prefix_sep = str_lit(".");
+ }
+ String name = proc->token.string;
+ isize len = prefix.len + prefix_sep.len + name.len;
+
+ int name_padding = cast(int)gb_max(max_name_length - len, 0);
+ int type_padding = cast(int)gb_max(max_type_length - gb_string_length(pt), 0);
+
+ char const *sep = "::";
+ if (proc->kind == Entity_Variable) {
+ sep = ":=";
+ }
+ error_line("\t%.*s%.*s%.*s %.*s%s %s %.*sat %s\n",
+ LIT(prefix), LIT(prefix_sep), LIT(name),
+ name_padding, spaces,
+ sep,
+ pt,
+ type_padding, spaces,
+ token_pos_to_string(pos)
+ );
+ }
+ if (procs.count > 0) {
+ error_line("\n");
+ }
- 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;
+ 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();
+
+ for (auto const &valid : valids) {
+ Entity *proc = proc_entities[valid.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->checker, 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 (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 (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);
+
+ 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;
}
-
gb_internal isize lookup_polymorphic_record_parameter(Type *t, String parameter_name) {
if (!is_type_polymorphic_record(t)) {
return -1;
@@ -6699,15 +6895,15 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
return err;
}
- String generated_name = make_string_c(expr_to_string(call));
-
CheckerContext ctx = *c;
// NOTE(bill): We need to make sure the lookup scope for the record is the same as where it was created
ctx.scope = polymorphic_record_parent_scope(original_type);
GB_ASSERT(ctx.scope != nullptr);
- Type *named_type = alloc_type_named(generated_name, nullptr, nullptr);
Type *bt = base_type(original_type);
+ String generated_name = make_string_c(expr_to_string(call));
+
+ Type *named_type = alloc_type_named(generated_name, nullptr, nullptr);
if (bt->kind == Type_Struct) {
Ast *node = clone_ast(bt->Struct.node);
Type *struct_type = alloc_type_struct();
@@ -6732,6 +6928,49 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
GB_PANIC("Unsupported parametric polymorphic record type");
}
+
+ bt = base_type(named_type);
+ if (bt->kind == Type_Struct || bt->kind == Type_Union) {
+ GB_ASSERT(original_type->kind == Type_Named);
+ Entity *e = original_type->Named.type_name;
+ GB_ASSERT(e->kind == Entity_TypeName);
+
+ gbString s = gb_string_make_reserve(heap_allocator(), e->token.string.len+3);
+ s = gb_string_append_fmt(s, "%.*s(", LIT(e->token.string));
+
+ Type *params = nullptr;
+ switch (bt->kind) {
+ case Type_Struct: params = bt->Struct.polymorphic_params; break;
+ case Type_Union: params = bt->Union.polymorphic_params; break;
+ }
+
+ if (params != nullptr) for_array(i, params->Tuple.variables) {
+ Entity *v = params->Tuple.variables[i];
+ String name = v->token.string;
+ if (i > 0) {
+ s = gb_string_append_fmt(s, ", ");
+ }
+ s = gb_string_append_fmt(s, "$%.*s", LIT(name));
+
+ if (v->kind == Entity_TypeName) {
+ if (v->type->kind != Type_Generic) {
+ s = gb_string_append_fmt(s, "=");
+ s = write_type_to_string(s, v->type, false);
+ }
+ } else if (v->kind == Entity_Constant) {
+ s = gb_string_append_fmt(s, "=");
+ s = write_exact_value_to_string(s, v->Constant.value);
+ }
+ }
+ s = gb_string_append_fmt(s, ")");
+
+ String new_name = make_string_c(s);
+ named_type->Named.name = new_name;
+ if (named_type->Named.type_name) {
+ named_type->Named.type_name->token.string = new_name;
+ }
+ }
+
operand->mode = Addressing_Type;
operand->type = named_type;
}
@@ -6740,6 +6979,46 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
+// 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; \
+}
+
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 &&
@@ -6779,30 +7058,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
}
- if (args.count > 0) {
- bool fail = false;
- 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 procedure call is not allowed");
- fail = true;
- }
- }
-
- if (fail) {
- operand->mode = Addressing_Invalid;
- operand->expr = call;
- return Expr_Stmt;
- }
- }
-
if (operand->mode == Addressing_Invalid) {
+ CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("procedure call");
for (Ast *arg : args) {
if (arg->kind == Ast_FieldValue) {
arg = arg->FieldValue.value;
@@ -6817,6 +7074,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
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);
@@ -6842,6 +7101,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
operand->type = t_invalid;
}
} else {
+ CHECK_CALL_PARAMETER_MIXTURE_OR_RETURN("type conversion");
+
operand->mode = Addressing_Invalid;
isize arg_count = args.count;
switch (arg_count) {
@@ -6887,6 +7148,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
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") {
@@ -6900,6 +7163,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
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) {
@@ -6911,8 +7176,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
}
- 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) {
@@ -6930,7 +7195,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
}
- 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;
@@ -6941,7 +7206,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
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);
@@ -6986,7 +7254,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
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'");
}
}
}
@@ -6999,9 +7267,6 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
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);
@@ -7019,8 +7284,6 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
}
- // add_type_and_value(c, operand->expr, operand->mode, operand->type, operand->value);
-
return Expr_Expr;
}
@@ -9098,13 +9361,13 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
- if (x.mode == Addressing_Invalid || x.type == t_invalid) {
+ if (x.mode == Addressing_Invalid || (x.type == t_invalid && x.mode != Addressing_ProcGroup)) {
o->mode = Addressing_Invalid;
o->type = t_invalid;
o->expr = node;
return kind;
}
- if (!is_type_proc(x.type)) {
+ if (!is_type_proc(x.type) && x.mode != Addressing_ProcGroup) {
gbString type_str = type_to_string(x.type);
error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
gb_string_free(type_str);
@@ -9127,76 +9390,76 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
first_arg->state_flags |= StateFlag_SelectorCallExpr;
}
- Type *pt = base_type(x.type);
- GB_ASSERT(pt->kind == Type_Proc);
- Type *first_type = nullptr;
- String first_arg_name = {};
- if (pt->Proc.param_count > 0) {
- Entity *f = pt->Proc.params->Tuple.variables[0];
- first_type = f->type;
- first_arg_name = f->token.string;
- }
- if (first_arg_name.len == 0) {
- first_arg_name = str_lit("_");
- }
+ if (e->kind != Entity_ProcGroup) {
+ Type *pt = base_type(x.type);
+ GB_ASSERT_MSG(pt->kind == Type_Proc, "%.*s %.*s %s", LIT(e->token.string), LIT(entity_strings[e->kind]), type_to_string(x.type));
+ Type *first_type = nullptr;
+ String first_arg_name = {};
+ if (pt->Proc.param_count > 0) {
+ Entity *f = pt->Proc.params->Tuple.variables[0];
+ first_type = f->type;
+ first_arg_name = f->token.string;
+ }
+ if (first_arg_name.len == 0) {
+ first_arg_name = str_lit("_");
+ }
- if (first_type == nullptr) {
- error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
- o->mode = Addressing_Invalid;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Stmt;
- }
+ if (first_type == nullptr) {
+ error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Stmt;
+ }
- Operand y = {};
- y.mode = first_arg->tav.mode;
- y.type = first_arg->tav.type;
- y.value = first_arg->tav.value;
+ Operand y = {};
+ y.mode = first_arg->tav.mode;
+ y.type = first_arg->tav.type;
+ y.value = first_arg->tav.value;
- if (check_is_assignable_to(c, &y, first_type)) {
- // Do nothing, it's valid
- } else {
- Operand z = y;
- z.type = type_deref(y.type);
- if (check_is_assignable_to(c, &z, first_type)) {
- // NOTE(bill): AST GENERATION HACK!
- Token op = {Token_Pointer};
- first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
- } else if (y.mode == Addressing_Variable) {
- Operand w = y;
- w.type = alloc_type_pointer(y.type);
- if (check_is_assignable_to(c, &w, first_type)) {
+ if (check_is_assignable_to(c, &y, first_type)) {
+ // Do nothing, it's valid
+ } else {
+ Operand z = y;
+ z.type = type_deref(y.type);
+ if (check_is_assignable_to(c, &z, first_type)) {
// NOTE(bill): AST GENERATION HACK!
- Token op = {Token_And};
- first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
+ Token op = {Token_Pointer};
+ first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
+ } else if (y.mode == Addressing_Variable) {
+ Operand w = y;
+ w.type = alloc_type_pointer(y.type);
+ if (check_is_assignable_to(c, &w, first_type)) {
+ // NOTE(bill): AST GENERATION HACK!
+ Token op = {Token_And};
+ first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
+ }
}
}
- }
- if (ce->args.count > 0) {
- bool fail = false;
- bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
- for (Ast *arg : ce->args) {
- bool mix = false;
- if (first_is_field_value) {
- mix = arg->kind != Ast_FieldValue;
- } else {
- mix = arg->kind == Ast_FieldValue;
+ if (ce->args.count > 0) {
+ bool fail = false;
+ bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
+ for (Ast *arg : ce->args) {
+ bool mix = false;
+ if (first_is_field_value) {
+ mix = arg->kind != Ast_FieldValue;
+ } else {
+ mix = arg->kind == Ast_FieldValue;
+ }
+ if (mix) {
+ fail = true;
+ break;
+ }
}
- if (mix) {
- fail = true;
- break;
+ if (!fail && first_is_field_value) {
+ Token op = {Token_Eq};
+ AstFile *f = first_arg->file();
+ first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
}
}
- if (!fail && first_is_field_value) {
- Token op = {Token_Eq};
- AstFile *f = first_arg->file();
- first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
- }
}
-
-
auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
modified_args[0] = first_arg;
slice_copy(&modified_args, ce->args, 1);
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 09af496ab..a15977b7d 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -417,6 +417,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
return nullptr;
case Addressing_Variable:
+ check_old_for_or_switch_value_usage(lhs->expr);
break;
case Addressing_MapIndex: {
@@ -1141,8 +1142,14 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
syntax_error(as_token, "Expected 1 expression after 'in'");
return;
}
+ bool is_addressed = false;
+
Ast *lhs = as->lhs[0];
Ast *rhs = as->rhs[0];
+ if (lhs->kind == Ast_UnaryExpr && lhs->UnaryExpr.op.kind == Token_And) {
+ is_addressed = true;
+ lhs = lhs->UnaryExpr.expr;
+ }
check_expr(ctx, &x, rhs);
check_assignment(ctx, &x, nullptr, str_lit("type switch expression"));
@@ -1281,12 +1288,15 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
}
}
- bool is_reference = false;
+ bool is_reference = is_addressed;
+ bool old_style = false;
- if (is_ptr &&
+ if (!is_reference &&
+ is_ptr &&
cc->list.count == 1 &&
case_type != nullptr) {
is_reference = true;
+ old_style = true;
}
if (cc->list.count > 1 || saw_nil) {
@@ -1305,9 +1315,12 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
{
Entity *tag_var = alloc_entity_variable(ctx->scope, lhs->Ident.token, case_type, EntityState_Resolved);
tag_var->flags |= EntityFlag_Used;
+ tag_var->flags |= EntityFlag_SwitchValue;
if (!is_reference) {
tag_var->flags |= EntityFlag_Value;
- tag_var->flags |= EntityFlag_SwitchValue;
+ }
+ if (old_style) {
+ tag_var->flags |= EntityFlag_OldForOrSwitchValue;
}
add_entity(ctx, ctx->scope, lhs, tag_var);
add_entity_use(ctx, lhs, tag_var);
@@ -1469,12 +1482,15 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
Ast *expr = unparen_expr(rs->expr);
+ bool is_possibly_addressable = true;
isize max_val_count = 2;
if (is_ast_range(expr)) {
ast_node(ie, BinaryExpr, expr);
Operand x = {};
Operand y = {};
+ is_possibly_addressable = false;
+
bool ok = check_range(ctx, expr, true, &x, &y, nullptr);
if (!ok) {
goto skip_expr_range_stmt;
@@ -1497,6 +1513,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
gb_string_free(t);
goto skip_expr_range_stmt;
} else {
+ is_possibly_addressable = false;
+
if (is_reverse) {
error(node, "#reverse for is not supported for enum types");
}
@@ -1510,7 +1528,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
Type *t = base_type(type_deref(operand.type));
switch (t->kind) {
case Type_Basic:
- if (is_type_string(t) && t->Basic.kind != Basic_cstring) {
+ if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
+ is_possibly_addressable = false;
array_add(&vals, t_rune);
array_add(&vals, t_int);
if (is_reverse) {
@@ -1529,6 +1548,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
case Type_Array:
if (is_ptr) use_by_reference_for_value = true;
+ if (!is_ptr) is_possibly_addressable = operand.mode == Addressing_Variable;
array_add(&vals, t->Array.elem);
array_add(&vals, t_int);
break;
@@ -1575,6 +1595,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
array_add(&vals, e->type);
}
+ is_possibly_addressable = false;
+
if (rs->vals.count > 1 && rs->vals[1] != nullptr && count < 3) {
gbString s = type_to_string(t);
error(operand.expr, "Expected a 3-valued expression on the rhs, got (%s)", s);
@@ -1644,8 +1666,13 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
}
Ast * name = lhs[i];
Type *type = rhs[i];
-
Entity *entity = nullptr;
+
+ bool is_addressed = false;
+ if (name->kind == Ast_UnaryExpr && name->UnaryExpr.op.kind == Token_And) {
+ is_addressed = true;
+ name = name->UnaryExpr.expr;
+ }
if (name->kind == Ast_Ident) {
Token token = name->Ident.token;
String str = token.string;
@@ -1659,7 +1686,17 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
entity->flags |= EntityFlag_ForValue;
entity->flags |= EntityFlag_Value;
entity->identifier = name;
- if (i == addressable_index && use_by_reference_for_value) {
+ entity->Variable.for_loop_parent_type = type_of_expr(expr);
+
+ if (is_addressed) {
+ if (is_possibly_addressable && i == addressable_index) {
+ entity->flags &= ~EntityFlag_Value;
+ } else {
+ char const *idx_name = is_map ? "key" : "index";
+ error(token, "The %s variable '%.*s' cannot be made addressable", idx_name, LIT(str));
+ }
+ } else if (i == addressable_index && use_by_reference_for_value) {
+ entity->flags |= EntityFlag_OldForOrSwitchValue;
entity->flags &= ~EntityFlag_Value;
}
if (is_soa) {
@@ -1678,7 +1715,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
entity = found;
}
} else {
- error(name, "A variable declaration must be an identifier");
+ gbString s = expr_to_string(lhs[i]);
+ error(name, "A variable declaration must be an identifier, got %s", s);
+ gb_string_free(s);
}
if (entity == nullptr) {
@@ -2207,7 +2246,13 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
} else if (operands.count != result_count) {
// Ignore error message as it has most likely already been reported
if (all_operands_valid(operands)) {
- error(node, "Expected %td return values, got %td", result_count, operands.count);
+ if (operands.count == 1) {
+ gbString t = type_to_string(operands[0].type);
+ error(node, "Expected %td return values, got %td (%s)", result_count, operands.count, t);
+ gb_string_free(t);
+ } else {
+ error(node, "Expected %td return values, got %td", result_count, operands.count);
+ }
}
} else {
for (isize i = 0; i < result_count; i++) {
diff --git a/src/check_type.cpp b/src/check_type.cpp
index bbfc25a12..a68f83ba9 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -729,6 +729,12 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no
union_type->Union.kind = ut->kind;
switch (ut->kind) {
case UnionType_no_nil:
+ if (union_type->Union.is_polymorphic && poly_operands == nullptr) {
+ GB_ASSERT(variants.count == 0);
+ if (ut->variants.count != 1) {
+ break;
+ }
+ }
if (variants.count < 2) {
error(ut->align, "A union with #no_nil must have at least 2 variants");
}
@@ -1410,7 +1416,7 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_
}
-gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array<Operand> *operands) {
+gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is_variadic_, isize *variadic_index_, bool *success_, isize *specialization_count_, Array<Operand> const *operands) {
if (_params == nullptr) {
return nullptr;
}
@@ -1658,7 +1664,6 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
ExactValue poly_const = {};
if (operands != nullptr && variables.count < operands->count) {
-
Operand op = (*operands)[variables.count];
if (op.expr == nullptr) {
// NOTE(bill): 2019-03-30
@@ -1961,7 +1966,7 @@ gb_internal Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_res
// NOTE(bill): 'operands' is for generating non generic procedure type
-gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, Array<Operand> *operands) {
+gb_internal bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, Array<Operand> const *operands) {
ast_node(pt, ProcType, proc_type_node);
if (ctx->polymorphic_scope == nullptr && ctx->allow_polymorphic_types) {
diff --git a/src/checker.cpp b/src/checker.cpp
index a8227fc2e..2a2cb5c42 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -32,7 +32,7 @@ gb_internal bool is_operand_uninit(Operand o) {
}
gb_internal bool check_rtti_type_disallowed(Token const &token, Type *type, char const *format) {
- if (build_context.disallow_rtti && type) {
+ if (build_context.no_rtti && type) {
if (is_type_any(type)) {
gbString t = type_to_string(type);
error(token, format, t);
@@ -285,17 +285,6 @@ gb_internal Scope *create_scope_from_package(CheckerContext *c, AstPackage *pkg)
}
gb_internal void destroy_scope(Scope *scope) {
- for (auto const &entry : scope->elements) {
- Entity *e = entry.value;
- if (e->kind == Entity_Variable) {
- if (!(e->flags & EntityFlag_Used)) {
-#if 0
- warning(e->token, "Unused variable '%.*s'", LIT(e->token.string));
-#endif
- }
- }
- }
-
for (Scope *child = scope->head_child; child != nullptr; child = child->next) {
destroy_scope(child);
}
@@ -1054,7 +1043,7 @@ gb_internal void init_universal(void) {
add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test);
add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point);
add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES);
- add_global_bool_constant("ODIN_DISALLOW_RTTI", bc->disallow_rtti);
+ add_global_bool_constant("ODIN_NO_RTTI", bc->no_rtti);
add_global_bool_constant("ODIN_VALGRIND_SUPPORT", bc->ODIN_VALGRIND_SUPPORT);
@@ -1742,7 +1731,7 @@ gb_internal void add_implicit_entity(CheckerContext *c, Ast *clause, Entity *e)
gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t);
gb_internal void add_type_info_type(CheckerContext *c, Type *t) {
- if (build_context.disallow_rtti) {
+ if (build_context.no_rtti) {
return;
}
if (t == nullptr) {
@@ -2343,7 +2332,7 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
str_lit("__multi3"),
);
- FORCE_ADD_RUNTIME_ENTITIES(!build_context.disallow_rtti,
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_rtti,
// Odin types
str_lit("Type_Info"),
@@ -2946,6 +2935,54 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
return false;
}
+gb_internal DECL_ATTRIBUTE_PROC(proc_group_attribute) {
+ if (name == ATTRIBUTE_USER_TAG_NAME) {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind != ExactValue_String) {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_name") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_String) {
+ if (string_is_valid_identifier(ev.value_string)) {
+ ac->objc_name = ev.value_string;
+ } else {
+ error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
+ }
+ } else {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_is_class_method") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_Bool) {
+ ac->objc_is_class_method = ev.value_bool;
+ } else {
+ error(elem, "Expected a boolean value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_type") {
+ if (value == nullptr) {
+ error(elem, "Expected a type for '%.*s'", LIT(name));
+ } else {
+ Type *objc_type = check_type(c, value);
+ if (objc_type != nullptr) {
+ if (!has_type_got_objc_class_attribute(objc_type)) {
+ gbString t = type_to_string(objc_type);
+ error(value, "'%.*s' expected a named type with the attribute @(obj_class=<string>), got type %s", LIT(name), t);
+ gb_string_free(t);
+ } else {
+ ac->objc_type = objc_type;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+
gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
if (name == ATTRIBUTE_USER_TAG_NAME) {
ExactValue ev = check_decl_attribute_value(c, value);
diff --git a/src/checker.hpp b/src/checker.hpp
index 1a95e2772..b06d0a8f9 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -199,6 +199,9 @@ struct DeclInfo {
BlockingMutex type_and_value_mutex;
Array<BlockLabel> labels;
+
+ // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time
+ struct lbModule *code_gen_module;
};
// ProcInfo stores the information needed for checking a procedure
diff --git a/src/entity.cpp b/src/entity.cpp
index d6f4edece..649dd900d 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -84,7 +84,9 @@ enum EntityFlag : u64 {
EntityFlag_CustomLinkage_LinkOnce = 1ull<<44,
EntityFlag_Require = 1ull<<50,
- EntityFlag_ByPtr = 1ull<<51, // enforce parameter is passed by pointer
+ EntityFlag_ByPtr = 1ull<<51, // enforce parameter is passed by pointer
+
+ EntityFlag_OldForOrSwitchValue = 1ull<<52,
EntityFlag_Overridden = 1ull<<63,
};
@@ -209,6 +211,8 @@ struct Entity {
ParameterValue param_value;
+ Type *for_loop_parent_type;
+
String thread_local_model;
Entity * foreign_library;
Ast * foreign_library_ident;
diff --git a/src/error.cpp b/src/error.cpp
index defc2593f..eb010eb36 100644
--- a/src/error.cpp
+++ b/src/error.cpp
@@ -265,7 +265,8 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) {
defer (gb_string_free(the_line));
if (the_line != nullptr) {
- String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line));
+ char const *line_text = the_line;
+ isize line_len = gb_string_length(the_line);
// TODO(bill): This assumes ASCII
@@ -285,21 +286,27 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) {
isize squiggle_extra = 0;
- if (line.len > MAX_LINE_LENGTH_PADDED) {
+ if (line_len > MAX_LINE_LENGTH_PADDED) {
i32 left = MAX_TAB_WIDTH;
- line.text += offset-left;
- line.len -= offset-left;
- offset = left+MAX_TAB_WIDTH/2;
- if (line.len > MAX_LINE_LENGTH_PADDED) {
- line.len = MAX_LINE_LENGTH_PADDED;
- if (error_length > line.len-left) {
- error_length = cast(i32)line.len - left;
+ if (offset > 0) {
+ line_text += offset-left;
+ line_len -= offset-left;
+ offset = left+MAX_TAB_WIDTH/2;
+ }
+ if (line_len > MAX_LINE_LENGTH_PADDED) {
+ line_len = MAX_LINE_LENGTH_PADDED;
+ if (error_length > line_len-left) {
+ error_length = cast(i32)line_len - left;
squiggle_extra = 1;
}
}
- error_out("... %.*s ...", LIT(line));
+ if (offset > 0) {
+ error_out("... %.*s ...", cast(i32)line_len, line_text);
+ } else {
+ error_out("%.*s ...", cast(i32)line_len, line_text);
+ }
} else {
- error_out("%.*s", LIT(line));
+ error_out("%.*s", cast(i32)line_len, line_text);
}
error_out("\n\t");
@@ -312,7 +319,7 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) {
error_out("^");
if (end.file_id == pos.file_id) {
if (end.line > pos.line) {
- for (i32 i = offset; i < line.len; i++) {
+ for (i32 i = offset; i < line_len; i++) {
error_out("~");
}
} else if (end.line == pos.line && end.column > pos.column) {
diff --git a/src/gb/gb.h b/src/gb/gb.h
index bc4c1f27d..3d4bff9b4 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -3299,12 +3299,39 @@ void const *gb_memrchr(void const *data, u8 c, isize n) {
-gb_inline void *gb_alloc_align (gbAllocator a, isize size, isize alignment) { return a.proc(a.data, gbAllocation_Alloc, size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); }
-gb_inline void *gb_alloc (gbAllocator a, isize size) { return gb_alloc_align(a, size, GB_DEFAULT_MEMORY_ALIGNMENT); }
-gb_inline void gb_free (gbAllocator a, void *ptr) { if (ptr != NULL) a.proc(a.data, gbAllocation_Free, 0, 0, ptr, 0, GB_DEFAULT_ALLOCATOR_FLAGS); }
-gb_inline void gb_free_all (gbAllocator a) { a.proc(a.data, gbAllocation_FreeAll, 0, 0, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS); }
-gb_inline void *gb_resize (gbAllocator a, void *ptr, isize old_size, isize new_size) { return gb_resize_align(a, ptr, old_size, new_size, GB_DEFAULT_MEMORY_ALIGNMENT); }
-gb_inline void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment) { return a.proc(a.data, gbAllocation_Resize, new_size, alignment, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS); }
+gb_inline void *gb_alloc_align (gbAllocator a, isize size, isize alignment) {
+ if (size == 0) {
+ return NULL;
+ }
+ return a.proc(a.data, gbAllocation_Alloc, size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS);
+}
+gb_inline void *gb_alloc(gbAllocator a, isize size) {
+ return gb_alloc_align(a, size, GB_DEFAULT_MEMORY_ALIGNMENT);
+}
+gb_inline void gb_free(gbAllocator a, void *ptr) {
+ if (ptr != NULL) {
+ a.proc(a.data, gbAllocation_Free, 0, 0, ptr, 0, GB_DEFAULT_ALLOCATOR_FLAGS);
+ }
+}
+gb_inline void gb_free_all(gbAllocator a) {
+ a.proc(a.data, gbAllocation_FreeAll, 0, 0, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS);
+}
+gb_inline void *gb_resize(gbAllocator a, void *ptr, isize old_size, isize new_size) {
+ return gb_resize_align(a, ptr, old_size, new_size, GB_DEFAULT_MEMORY_ALIGNMENT);
+}
+gb_inline void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment) {
+ if (new_size == 0) {
+ if (ptr != NULL) {
+ return a.proc(a.data, gbAllocation_Free, 0, 0, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS);
+ }
+ return NULL;
+ } else if (ptr == NULL) {
+ return a.proc(a.data, gbAllocation_Alloc, new_size, alignment, NULL, 0, GB_DEFAULT_ALLOCATOR_FLAGS);
+ } else if (old_size == new_size && ((uintptr)ptr % (uintptr)alignment) == 0) {
+ return ptr;
+ }
+ return a.proc(a.data, gbAllocation_Resize, new_size, alignment, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS);
+}
gb_inline void *gb_alloc_copy (gbAllocator a, void const *src, isize size) {
return gb_memcopy(gb_alloc(a, size), src, size);
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index 34a401c33..938c9b2ac 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -157,10 +157,10 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
return {compare_proc->value, compare_proc->type};
}
- static u32 proc_index = 0;
+ static std::atomic<u32> proc_index;
char buf[32] = {};
- isize n = gb_snprintf(buf, 32, "__$equal%u", ++proc_index);
+ isize n = gb_snprintf(buf, 32, "__$equal%u", 1+proc_index.fetch_add(1));
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
String proc_name = make_string_c(str);
@@ -218,7 +218,9 @@ gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 0, false));
} else if (type->kind == Type_Union) {
- if (is_type_union_maybe_pointer(type)) {
+ if (type_size_of(type) == 0) {
+ LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_bool), 1, false));
+ } else if (is_type_union_maybe_pointer(type)) {
Type *v = type->Union.variants[0];
Type *pv = alloc_type_pointer(v);
@@ -656,10 +658,10 @@ gb_internal lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) {
GB_ASSERT(*found != nullptr);
return {(*found)->value, (*found)->type};
}
- static u32 proc_index = 0;
+ static std::atomic<u32> proc_index;
char buf[32] = {};
- isize n = gb_snprintf(buf, 32, "__$map_set-%u", ++proc_index);
+ isize n = gb_snprintf(buf, 32, "__$map_set-%u", 1+proc_index.fetch_add(1));
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
String proc_name = make_string_c(str);
@@ -772,56 +774,6 @@ gb_internal lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) {
return {p->value, p->type};
}
-
-gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) {
- MUTEX_GUARD(&m->gen->anonymous_proc_lits_mutex);
-
- lbProcedure **found = map_get(&m->gen->anonymous_proc_lits, expr);
- if (found) {
- return lb_find_procedure_value_from_entity(m, (*found)->entity);
- }
-
- ast_node(pl, ProcLit, expr);
-
- // NOTE(bill): Generate a new name
- // parent$count
- isize name_len = prefix_name.len + 1 + 8 + 1;
- char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
- i32 name_id = cast(i32)m->gen->anonymous_proc_lits.count;
-
- name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), name_id);
- String name = make_string((u8 *)name_text, name_len-1);
-
- Type *type = type_of_expr(expr);
-
- Token token = {};
- token.pos = ast_token(expr).pos;
- token.kind = Token_Ident;
- token.string = name;
- Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags);
- e->file = expr->file();
- e->decl_info = pl->decl;
- e->code_gen_module = m;
- e->flags |= EntityFlag_ProcBodyChecked;
- lbProcedure *p = lb_create_procedure(m, e);
-
- lbValue value = {};
- value.value = p->value;
- value.type = p->type;
-
- array_add(&m->procedures_to_generate, p);
- if (parent != nullptr) {
- array_add(&parent->children, p);
- } else {
- string_map_set(&m->members, name, value);
- }
-
- map_set(&m->gen->anonymous_proc_lits, expr, p);
-
- return value;
-}
-
-
gb_internal lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type) {
lbAddr *found = map_get(&m->map_cell_info_map, type);
if (found) {
@@ -1048,7 +1000,7 @@ struct lbGlobalVariable {
};
gb_internal lbProcedure *lb_create_startup_type_info(lbModule *m) {
- if (build_context.disallow_rtti) {
+ if (build_context.no_rtti) {
return nullptr;
}
Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
@@ -1513,7 +1465,7 @@ gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc
lbModule *m = cast(lbModule *)data;
for (isize i = 0; i < m->missing_procedures_to_check.count; i++) {
lbProcedure *p = m->missing_procedures_to_check[i];
- debugf("Generate missing procedure: %.*s\n", LIT(p->name));
+ debugf("Generate missing procedure: %.*s module %p\n", LIT(p->name), m);
lb_generate_procedure(m, p);
}
return 0;
@@ -1577,7 +1529,6 @@ gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) {
thread_pool_wait();
}
-
gb_internal String lb_filepath_ll_for_module(lbModule *m) {
String path = concatenate3_strings(permanent_allocator(),
build_context.build_paths[BuildPath_Output].basename,
@@ -1874,25 +1825,28 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
TEMPORARY_ALLOCATOR_GUARD();
auto args = array_make<lbValue>(temporary_allocator(), 1);
args[0] = lb_addr_load(p, all_tests_slice);
- lb_emit_call(p, runner, args);
+ lbValue result = lb_emit_call(p, runner, args);
+
+ lbValue exit_runner = lb_find_package_value(m, str_lit("os"), str_lit("exit"));
+ auto exit_args = array_make<lbValue>(temporary_allocator(), 1);
+ exit_args[0] = lb_emit_select(p, result, lb_const_int(m, t_int, 0), lb_const_int(m, t_int, 1));
+ lb_emit_call(p, exit_runner, exit_args, ProcInlining_none);
} else {
if (m->info->entry_point != nullptr) {
lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point);
lb_emit_call(p, entry_point, {}, ProcInlining_no_inline);
}
- }
-
-
- if (call_cleanup) {
- lbValue cleanup_runtime_value = {cleanup_runtime->value, cleanup_runtime->type};
- lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none);
- }
+ if (call_cleanup) {
+ lbValue cleanup_runtime_value = {cleanup_runtime->value, cleanup_runtime->type};
+ lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none);
+ }
- if (is_dll_main) {
- LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 1, false));
- } else {
- LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false));
+ if (is_dll_main) {
+ LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 1, false));
+ } else {
+ LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false));
+ }
}
lb_end_procedure_body(p);
@@ -2170,7 +2124,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
TIME_SECTION("LLVM Global Variables");
- if (!build_context.disallow_rtti) {
+ if (!build_context.no_rtti) {
lbModule *m = default_module;
{ // Add type info data
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index 4c4d9703d..ce01485ff 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -164,7 +164,7 @@ struct lbModule {
PtrMap<Type *, lbProcedure *> map_get_procs;
PtrMap<Type *, lbProcedure *> map_set_procs;
- u32 nested_type_name_guid;
+ std::atomic<u32> nested_type_name_guid;
Array<lbProcedure *> procedures_to_generate;
Array<Entity *> global_procedures_and_types_to_create;
@@ -201,7 +201,7 @@ struct lbGenerator {
PtrMap<LLVMContextRef, lbModule *> modules_through_ctx;
lbModule default_module;
- BlockingMutex anonymous_proc_lits_mutex;
+ RecursiveMutex anonymous_proc_lits_mutex;
PtrMap<Ast *, lbProcedure *> anonymous_proc_lits;
BlockingMutex foreign_mutex;
@@ -346,6 +346,8 @@ struct lbProcedure {
};
+#define ABI_PKG_NAME_SEPARATOR "."
+
#if !ODIN_LLVM_MINIMUM_VERSION_14
#define LLVMConstGEP2(Ty__, ConstantVal__, ConstantIndices__, NumIndices__) LLVMConstGEP(ConstantVal__, ConstantIndices__, NumIndices__)
@@ -545,6 +547,8 @@ gb_internal gb_inline i64 lb_max_zero_init_size(void) {
gb_internal LLVMTypeRef OdinLLVMGetArrayElementType(LLVMTypeRef type);
gb_internal LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type);
+gb_internal String lb_filepath_ll_for_module(lbModule *m);
+
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
#define LB_CLEANUP_RUNTIME_PROC_NAME "__$cleanup_runtime"
#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp
index c9d2f5b26..2a121ff5d 100644
--- a/src/llvm_backend_const.cpp
+++ b/src/llvm_backend_const.cpp
@@ -473,6 +473,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
if (value.kind == ExactValue_Procedure) {
lbValue res = {};
Ast *expr = unparen_expr(value.value_procedure);
+ GB_ASSERT(expr != nullptr);
if (expr->kind == Ast_ProcLit) {
res = lb_generate_anonymous_proc_lit(m, str_lit("_proclit"), expr);
} else {
@@ -482,7 +483,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
GB_ASSERT(res.value != nullptr);
GB_ASSERT(LLVMGetValueKind(res.value) == LLVMFunctionValueKind);
- res.value = LLVMConstPointerCast(res.value, lb_type(m, res.type));
+ if (LLVMGetIntrinsicID(res.value) == 0) {
+ // NOTE(bill): do not cast intrinsics as they are not really procedures that can be casted
+ res.value = LLVMConstPointerCast(res.value, lb_type(m, res.type));
+ }
return res;
}
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp
index f95e351ce..5e6831fc2 100644
--- a/src/llvm_backend_expr.cpp
+++ b/src/llvm_backend_expr.cpp
@@ -1325,6 +1325,15 @@ handle_op:;
return {};
}
+gb_internal bool lb_is_empty_string_constant(Ast *expr) {
+ if (expr->tav.value.kind == ExactValue_String &&
+ is_type_string(expr->tav.type)) {
+ String s = expr->tav.value.value_string;
+ return s.len == 0;
+ }
+ return false;
+}
+
gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
ast_node(be, BinaryExpr, expr);
@@ -1373,15 +1382,33 @@ gb_internal lbValue lb_build_binary_expr(lbProcedure *p, Ast *expr) {
case Token_CmpEq:
case Token_NotEq:
if (is_type_untyped_nil(be->right->tav.type)) {
+ // `x == nil` or `x != nil`
lbValue left = lb_build_expr(p, be->left);
lbValue cmp = lb_emit_comp_against_nil(p, be->op.kind, left);
Type *type = default_type(tv.type);
return lb_emit_conv(p, cmp, type);
} else if (is_type_untyped_nil(be->left->tav.type)) {
+ // `nil == x` or `nil != x`
lbValue right = lb_build_expr(p, be->right);
lbValue cmp = lb_emit_comp_against_nil(p, be->op.kind, right);
Type *type = default_type(tv.type);
return lb_emit_conv(p, cmp, type);
+ } else if (lb_is_empty_string_constant(be->right)) {
+ // `x == ""` or `x != ""`
+ lbValue s = lb_build_expr(p, be->left);
+ s = lb_emit_conv(p, s, t_string);
+ lbValue len = lb_string_len(p, s);
+ lbValue cmp = lb_emit_comp(p, be->op.kind, len, lb_const_int(p->module, t_int, 0));
+ Type *type = default_type(tv.type);
+ return lb_emit_conv(p, cmp, type);
+ } else if (lb_is_empty_string_constant(be->left)) {
+ // `"" == x` or `"" != x`
+ lbValue s = lb_build_expr(p, be->right);
+ s = lb_emit_conv(p, s, t_string);
+ lbValue len = lb_string_len(p, s);
+ lbValue cmp = lb_emit_comp(p, be->op.kind, len, lb_const_int(p->module, t_int, 0));
+ Type *type = default_type(tv.type);
+ return lb_emit_conv(p, cmp, type);
}
/*fallthrough*/
case Token_Lt:
@@ -2246,7 +2273,6 @@ gb_internal lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValu
-
gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right) {
Type *a = core_type(left.type);
Type *b = core_type(right.type);
@@ -2254,7 +2280,10 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
GB_ASSERT(gb_is_between(op_kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1));
lbValue nil_check = {};
- if (is_type_untyped_nil(left.type)) {
+
+ if (is_type_array_like(left.type) || is_type_array_like(right.type)) {
+ // don't do `nil` check if it is array-like
+ } else if (is_type_untyped_nil(left.type)) {
nil_check = lb_emit_comp_against_nil(p, op_kind, right);
} else if (is_type_untyped_nil(right.type)) {
nil_check = lb_emit_comp_against_nil(p, op_kind, left);
@@ -2310,7 +2339,7 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
lbValue res = lb_emit_comp(p, op_kind, val, lb_const_nil(p->module, val.type));
return lb_emit_conv(p, res, t_bool);
}
- if (is_type_array(a) || is_type_enumerated_array(a)) {
+ if (is_type_array_like(a)) {
Type *tl = base_type(a);
lbValue lhs = lb_address_from_load_or_generate_local(p, left);
lbValue rhs = lb_address_from_load_or_generate_local(p, right);
@@ -2984,7 +3013,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
isize arg_count = 6;
- if (build_context.disallow_rtti) {
+ if (build_context.no_rtti) {
arg_count = 4;
}
@@ -2996,7 +3025,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
args[2] = lb_const_int(p->module, t_i32, pos.line);
args[3] = lb_const_int(p->module, t_i32, pos.column);
- if (!build_context.disallow_rtti) {
+ if (!build_context.no_rtti) {
args[4] = lb_typeid(p->module, src_type);
args[5] = lb_typeid(p->module, dst_type);
}
@@ -3012,7 +3041,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
}
lbValue data_ptr = lb_emit_struct_ev(p, v, 0);
if ((p->state_flags & StateFlag_no_type_assert) == 0) {
- GB_ASSERT(!build_context.disallow_rtti);
+ GB_ASSERT(!build_context.no_rtti);
lbValue any_id = lb_emit_struct_ev(p, v, 1);
@@ -4142,7 +4171,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
// HACK TODO(bill): THIS IS A MASSIVE HACK!!!!
if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) {
- GB_ASSERT_MSG(union_variant_index(ft, fet) > 0, "%s", type_to_string(fet));
+ GB_ASSERT_MSG(union_variant_index(ft, fet) >= 0, "%s", type_to_string(fet));
lb_emit_store_union_variant(p, gep, field_expr, fet);
} else {
@@ -4490,8 +4519,9 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
Selection sel = lookup_field(type, selector, false);
GB_ASSERT(sel.entity != nullptr);
if (sel.pseudo_field) {
- GB_ASSERT(sel.entity->kind == Entity_Procedure);
+ GB_ASSERT(sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup);
Entity *e = entity_of_node(sel_node);
+ GB_ASSERT(e->kind == Entity_Procedure);
return lb_addr(lb_find_value_from_entity(p->module, e));
}
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index e5f3e3081..ad8a1816a 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -334,10 +334,35 @@ gb_internal bool lb_is_instr_terminating(LLVMValueRef instr) {
return false;
}
+gb_internal lbModule *lb_module_of_expr(lbGenerator *gen, Ast *expr) {
+ GB_ASSERT(expr != nullptr);
+ lbModule **found = nullptr;
+ AstFile *file = expr->file();
+ if (file) {
+ found = map_get(&gen->modules, cast(void *)file);
+ if (found) {
+ return *found;
+ }
+
+ if (file->pkg) {
+ found = map_get(&gen->modules, cast(void *)file->pkg);
+ if (found) {
+ return *found;
+ }
+ }
+ }
+
+ return &gen->default_module;
+}
gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) {
GB_ASSERT(e != nullptr);
lbModule **found = nullptr;
+ if (e->kind == Entity_Procedure &&
+ e->decl_info &&
+ e->decl_info->code_gen_module) {
+ return e->decl_info->code_gen_module;
+ }
if (e->file) {
found = map_get(&gen->modules, cast(void *)e->file);
if (found) {
@@ -1298,6 +1323,7 @@ gb_internal lbValue lb_emit_union_tag_value(lbProcedure *p, lbValue u) {
gb_internal void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *variant_type) {
Type *t = type_deref(parent.type);
+ GB_ASSERT(is_type_union(t));
if (is_type_union_maybe_pointer(t) || type_size_of(t) == 0) {
// No tag needed!
@@ -1377,7 +1403,7 @@ gb_internal String lb_mangle_name(lbModule *m, Entity *e) {
char *new_name = gb_alloc_array(permanent_allocator(), char, max_len);
isize new_name_len = gb_snprintf(
new_name, max_len,
- "%.*s.%.*s", LIT(pkgn), LIT(name)
+ "%.*s" ABI_PKG_NAME_SEPARATOR "%.*s", LIT(pkgn), LIT(name)
);
if (require_suffix_id) {
char *str = new_name + new_name_len-1;
@@ -1426,8 +1452,8 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur
if (p != nullptr) {
isize name_len = p->name.len + 1 + ts_name.len + 1 + 10 + 1;
char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
- u32 guid = ++p->module->nested_type_name_guid;
- name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%u", LIT(p->name), LIT(ts_name), guid);
+ u32 guid = 1+p->module->nested_type_name_guid.fetch_add(1);
+ name_len = gb_snprintf(name_text, name_len, "%.*s" ABI_PKG_NAME_SEPARATOR "%.*s-%u", LIT(p->name), LIT(ts_name), guid);
String name = make_string(cast(u8 *)name_text, name_len-1);
e->TypeName.ir_mangled_name = name;
@@ -1436,9 +1462,8 @@ gb_internal String lb_set_nested_type_name_ir_mangled_name(Entity *e, lbProcedur
// NOTE(bill): a nested type be required before its parameter procedure exists. Just give it a temp name for now
isize name_len = 9 + 1 + ts_name.len + 1 + 10 + 1;
char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
- static u32 guid = 0;
- guid += 1;
- name_len = gb_snprintf(name_text, name_len, "_internal.%.*s-%u", LIT(ts_name), guid);
+ static std::atomic<u32> guid;
+ name_len = gb_snprintf(name_text, name_len, "_internal" ABI_PKG_NAME_SEPARATOR "%.*s-%u", LIT(ts_name), 1+guid.fetch_add(1));
String name = make_string(cast(u8 *)name_text, name_len-1);
e->TypeName.ir_mangled_name = name;
@@ -2662,9 +2687,12 @@ gb_internal lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *e
gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) {
+ lbGenerator *gen = m->gen;
+
GB_ASSERT(is_type_proc(e->type));
e = strip_entity_wrapping(e);
GB_ASSERT(e != nullptr);
+ GB_ASSERT(e->kind == Entity_Procedure);
lbValue *found = nullptr;
rw_mutex_shared_lock(&m->values_mutex);
@@ -2678,27 +2706,34 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
lbModule *other_module = m;
if (USE_SEPARATE_MODULES) {
- other_module = lb_module_of_entity(m->gen, e);
+ other_module = lb_module_of_entity(gen, e);
}
if (other_module == m) {
- debugf("Missing Procedure (lb_find_procedure_value_from_entity): %.*s\n", LIT(e->token.string));
+ debugf("Missing Procedure (lb_find_procedure_value_from_entity): %.*s module %p\n", LIT(e->token.string), m);
}
ignore_body = other_module != m;
lbProcedure *missing_proc = lb_create_procedure(m, e, ignore_body);
if (ignore_body) {
+ mutex_lock(&gen->anonymous_proc_lits_mutex);
+ defer (mutex_unlock(&gen->anonymous_proc_lits_mutex));
+
GB_ASSERT(other_module != nullptr);
rw_mutex_shared_lock(&other_module->values_mutex);
auto *found = map_get(&other_module->values, e);
rw_mutex_shared_unlock(&other_module->values_mutex);
if (found == nullptr) {
+ // THIS IS THE RACE CONDITION
lbProcedure *missing_proc_in_other_module = lb_create_procedure(other_module, e, false);
array_add(&other_module->missing_procedures_to_check, missing_proc_in_other_module);
}
} else {
array_add(&m->missing_procedures_to_check, missing_proc);
}
+
+ rw_mutex_shared_lock(&m->values_mutex);
found = map_get(&m->values, e);
+ rw_mutex_shared_unlock(&m->values_mutex);
if (found) {
return *found;
}
@@ -2708,6 +2743,63 @@ gb_internal lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e)
}
+
+gb_internal lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) {
+ lbGenerator *gen = m->gen;
+
+ mutex_lock(&gen->anonymous_proc_lits_mutex);
+ defer (mutex_unlock(&gen->anonymous_proc_lits_mutex));
+
+ TokenPos pos = ast_token(expr).pos;
+ lbProcedure **found = map_get(&gen->anonymous_proc_lits, expr);
+ if (found) {
+ return lb_find_procedure_value_from_entity(m, (*found)->entity);
+ }
+
+ ast_node(pl, ProcLit, expr);
+
+ // NOTE(bill): Generate a new name
+ // parent$count
+ isize name_len = prefix_name.len + 6 + 11;
+ char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
+ static std::atomic<i32> name_id;
+ name_len = gb_snprintf(name_text, name_len, "%.*s$anon-%d", LIT(prefix_name), 1+name_id.fetch_add(1));
+ String name = make_string((u8 *)name_text, name_len-1);
+
+ Type *type = type_of_expr(expr);
+
+ GB_ASSERT(pl->decl->entity == nullptr);
+ Token token = {};
+ token.pos = ast_token(expr).pos;
+ token.kind = Token_Ident;
+ token.string = name;
+ Entity *e = alloc_entity_procedure(nullptr, token, type, pl->tags);
+ e->file = expr->file();
+
+ // NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time
+ pl->decl->code_gen_module = m;
+ e->decl_info = pl->decl;
+ pl->decl->entity = e;
+ e->flags |= EntityFlag_ProcBodyChecked;
+
+ lbProcedure *p = lb_create_procedure(m, e);
+ GB_ASSERT(e->code_gen_module == m);
+
+ lbValue value = {};
+ value.value = p->value;
+ value.type = p->type;
+
+ map_set(&gen->anonymous_proc_lits, expr, p);
+ array_add(&m->procedures_to_generate, p);
+ if (parent != nullptr) {
+ array_add(&parent->children, p);
+ } else {
+ string_map_set(&m->members, name, value);
+ }
+ return value;
+}
+
+
gb_internal lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value, Entity **entity_) {
GB_ASSERT(type != nullptr);
type = default_type(type);
@@ -2915,8 +3007,9 @@ gb_internal lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero
LLVMPositionBuilderAtEnd(p->builder, p->decl_block->block);
char const *name = "";
- if (e != nullptr) {
- // name = alloc_cstring(permanent_allocator(), e->token.string);
+ if (e != nullptr && e->token.string.len > 0 && e->token.string != "_") {
+ // NOTE(bill): for debugging purposes only
+ name = alloc_cstring(permanent_allocator(), e->token.string);
}
LLVMTypeRef llvm_type = lb_type(p->module, type);
diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp
index 141ee88c7..54e667a0b 100644
--- a/src/llvm_backend_opt.cpp
+++ b/src/llvm_backend_opt.cpp
@@ -375,16 +375,6 @@ gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedur
return;
}
LLVMRunFunctionPassManager(fpm, p->value);
- switch (pass_manager_kind) {
- case lbFunctionPassManager_none:
- return;
- case lbFunctionPassManager_default:
- case lbFunctionPassManager_default_without_memcpy:
- if (build_context.optimization_level < 0) {
- return;
- }
- break;
- }
// NOTE(bill): LLVMAddDCEPass doesn't seem to be exported in the official DLL's for LLVM
// which means we cannot rely upon it
// This is also useful for read the .ll for debug purposes because a lot of instructions
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index 8b9f8b249..c27c55337 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -757,7 +757,7 @@ gb_internal void lb_build_nested_proc(lbProcedure *p, AstProcLit *pd, Entity *e)
char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
i32 guid = cast(i32)p->children.count;
- name_len = gb_snprintf(name_text, name_len, "%.*s.%.*s-%d", LIT(p->name), LIT(pd_name), guid);
+ name_len = gb_snprintf(name_text, name_len, "%.*s" ABI_PKG_NAME_SEPARATOR "%.*s-%d", LIT(p->name), LIT(pd_name), guid);
String name = make_string(cast(u8 *)name_text, name_len-1);
e->Procedure.link_name = name;
@@ -1160,7 +1160,13 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
}
- Entity **found = map_get(&p->module->procedure_values, value.value);
+ LLVMValueRef the_proc_value = value.value;
+
+ if (LLVMIsAConstantExpr(the_proc_value)) {
+ // NOTE(bill): it's a bit cast
+ the_proc_value = LLVMGetOperand(the_proc_value, 0);
+ }
+ Entity **found = map_get(&p->module->procedure_values, the_proc_value);
if (found != nullptr) {
Entity *e = *found;
if (e != nullptr && entity_has_deferred_procedure(e)) {
@@ -3145,6 +3151,18 @@ gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) {
}
return res;
}
+
+gb_internal void lb_add_values_to_array(lbProcedure *p, Array<lbValue> *args, lbValue value) {
+ if (is_type_tuple(value.type)) {
+ for_array(i, value.type->Tuple.variables) {
+ lbValue sub_value = lb_emit_struct_ev(p, value, cast(i32)i);
+ array_add(args, sub_value);
+ }
+ } else {
+ array_add(args, value);
+ }
+}
+
gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
lbModule *m = p->module;
@@ -3219,245 +3237,147 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
GB_ASSERT(proc_type_->kind == Type_Proc);
TypeProc *pt = &proc_type_->Proc;
- if (is_call_expr_field_value(ce)) {
- auto args = array_make<lbValue>(permanent_allocator(), pt->param_count);
-
- for_array(arg_index, ce->args) {
- Ast *arg = ce->args[arg_index];
- ast_node(fv, FieldValue, arg);
- GB_ASSERT(fv->field->kind == Ast_Ident);
- String name = fv->field->Ident.token.string;
- isize index = lookup_procedure_parameter(pt, name);
- GB_ASSERT(index >= 0);
- TypeAndValue tav = type_and_value_of_expr(fv->value);
- if (tav.mode == Addressing_Type) {
- args[index] = lb_const_nil(m, tav.type);
- } else {
- args[index] = lb_build_expr(p, fv->value);
- }
- }
- TypeTuple *params = &pt->params->Tuple;
- for (isize i = 0; i < args.count; i++) {
- Entity *e = params->variables[i];
- if (e->kind == Entity_TypeName) {
- args[i] = lb_const_nil(m, e->type);
- } else if (e->kind == Entity_Constant) {
- continue;
- } else {
- GB_ASSERT(e->kind == Entity_Variable);
- if (args[i].value == nullptr) {
- args[i] = lb_handle_param_value(p, e->type, e->Variable.param_value, ast_token(expr).pos);
- } else if (is_type_typeid(e->type) && !is_type_typeid(args[i].type)) {
- args[i] = lb_typeid(p->module, args[i].type);
- } else {
- args[i] = lb_emit_conv(p, args[i], e->type);
- }
- }
- }
+ GB_ASSERT(ce->split_args != nullptr);
- for (isize i = 0; i < args.count; i++) {
- Entity *e = params->variables[i];
- if (args[i].type == nullptr) {
- continue;
- } else if (is_type_untyped_uninit(args[i].type)) {
- args[i] = lb_const_undef(m, e->type);
- } else if (is_type_untyped_nil(args[i].type)) {
- args[i] = lb_const_nil(m, e->type);
- }
- }
-
- return lb_emit_call(p, value, args, ce->inlining);
- }
+ auto args = array_make<lbValue>(permanent_allocator(), 0, pt->param_count);
- isize arg_index = 0;
+ bool vari_expand = (ce->ellipsis.pos.line != 0);
+ bool is_c_vararg = pt->c_vararg;
- isize arg_count = 0;
- for_array(i, ce->args) {
- Ast *arg = ce->args[i];
- TypeAndValue tav = type_and_value_of_expr(arg);
- GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s %d", expr_to_string(arg), expr_to_string(expr), tav.mode);
- GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg));
- Type *at = tav.type;
- if (is_type_tuple(at)) {
- arg_count += at->Tuple.variables.count;
- } else {
- arg_count++;
+ for_array(i, ce->split_args->positional) {
+ Entity *e = pt->params->Tuple.variables[i];
+ if (e->kind == Entity_TypeName) {
+ array_add(&args, lb_const_nil(p->module, e->type));
+ continue;
+ } else if (e->kind == Entity_Constant) {
+ array_add(&args, lb_const_value(p->module, e->type, e->Constant.value));
+ continue;
}
- }
- isize param_count = 0;
- if (pt->params) {
- GB_ASSERT(pt->params->kind == Type_Tuple);
- param_count = pt->params->Tuple.variables.count;
- }
+ GB_ASSERT(e->kind == Entity_Variable);
- auto args = array_make<lbValue>(permanent_allocator(), cast(isize)gb_max(param_count, arg_count));
- isize variadic_index = pt->variadic_index;
- bool variadic = pt->variadic && variadic_index >= 0;
- bool vari_expand = ce->ellipsis.pos.line != 0;
- bool is_c_vararg = pt->c_vararg;
+ if (pt->variadic && pt->variadic_index == i) {
+ lbValue variadic_args = lb_const_nil(p->module, e->type);
+ auto variadic = slice(ce->split_args->positional, pt->variadic_index, ce->split_args->positional.count);
+ if (variadic.count != 0) {
+ // variadic call argument generation
+ Type *slice_type = e->type;
+ GB_ASSERT(slice_type->kind == Type_Slice);
- String proc_name = {};
- if (p->entity != nullptr) {
- proc_name = p->entity->token.string;
- }
- TokenPos pos = ast_token(ce->proc).pos;
+ if (is_c_vararg) {
+ GB_ASSERT(!vari_expand);
- TypeTuple *param_tuple = nullptr;
- if (pt->params) {
- GB_ASSERT(pt->params->kind == Type_Tuple);
- param_tuple = &pt->params->Tuple;
- }
+ Type *elem_type = slice_type->Slice.elem;
- for_array(i, ce->args) {
- Ast *arg = ce->args[i];
- TypeAndValue arg_tv = type_and_value_of_expr(arg);
- if (arg_tv.mode == Addressing_Type) {
- args[arg_index++] = lb_const_nil(m, arg_tv.type);
- } else {
- lbValue a = lb_build_expr(p, arg);
- Type *at = a.type;
- if (at->kind == Type_Tuple) {
- lbTupleFix *tf = map_get(&p->tuple_fix_map, a.value);
- if (tf) {
- for_array(j, tf->values) {
- args[arg_index++] = tf->values[j];
+ for (Ast *var_arg : variadic) {
+ lbValue arg = lb_build_expr(p, var_arg);
+ if (is_type_any(elem_type)) {
+ array_add(&args, lb_emit_conv(p, arg, default_type(arg.type)));
+ } else {
+ array_add(&args, lb_emit_conv(p, arg, elem_type));
+ }
}
+ break;
+ } else if (vari_expand) {
+ GB_ASSERT(variadic.count == 1);
+ variadic_args = lb_build_expr(p, variadic[0]);
+ variadic_args = lb_emit_conv(p, variadic_args, slice_type);
} else {
- for_array(j, at->Tuple.variables) {
- lbValue v = lb_emit_struct_ev(p, a, cast(i32)j);
- args[arg_index++] = v;
- }
- }
- } else {
- args[arg_index++] = a;
- }
- }
- }
-
-
- if (param_count > 0) {
- GB_ASSERT_MSG(pt->params != nullptr, "%s %td", expr_to_string(expr), pt->param_count);
- GB_ASSERT(param_count < 1000000);
+ Type *elem_type = slice_type->Slice.elem;
- if (arg_count < param_count) {
- isize end = cast(isize)param_count;
- if (variadic) {
- end = variadic_index;
- }
- while (arg_index < end) {
- Entity *e = param_tuple->variables[arg_index];
- GB_ASSERT(e->kind == Entity_Variable);
- args[arg_index++] = lb_handle_param_value(p, e->type, e->Variable.param_value, ast_token(expr).pos);
- }
- }
-
- if (is_c_vararg) {
- GB_ASSERT(variadic);
- GB_ASSERT(!vari_expand);
- isize i = 0;
- for (; i < variadic_index; i++) {
- Entity *e = param_tuple->variables[i];
- if (e->kind == Entity_Variable) {
- args[i] = lb_emit_conv(p, args[i], e->type);
- }
- }
- Type *variadic_type = param_tuple->variables[i]->type;
- GB_ASSERT(is_type_slice(variadic_type));
- variadic_type = base_type(variadic_type)->Slice.elem;
- if (!is_type_any(variadic_type)) {
- for (; i < arg_count; i++) {
- args[i] = lb_emit_conv(p, args[i], variadic_type);
- }
- } else {
- for (; i < arg_count; i++) {
- args[i] = lb_emit_conv(p, args[i], default_type(args[i].type));
- }
- }
- } else if (variadic) {
- isize i = 0;
- for (; i < variadic_index; i++) {
- Entity *e = param_tuple->variables[i];
- if (e->kind == Entity_Variable) {
- args[i] = lb_emit_conv(p, args[i], e->type);
- }
- }
- if (!vari_expand) {
- Type *variadic_type = param_tuple->variables[i]->type;
- GB_ASSERT(is_type_slice(variadic_type));
- variadic_type = base_type(variadic_type)->Slice.elem;
- for (; i < arg_count; i++) {
- args[i] = lb_emit_conv(p, args[i], variadic_type);
- }
- }
- } else {
- for (isize i = 0; i < param_count; i++) {
- Entity *e = param_tuple->variables[i];
- if (e->kind == Entity_Variable) {
- if (args[i].value == nullptr) {
- continue;
- }
- GB_ASSERT_MSG(args[i].value != nullptr, "%.*s", LIT(e->token.string));
- if (is_type_typeid(e->type) && !is_type_typeid(args[i].type)) {
- GB_ASSERT(LLVMIsNull(args[i].value));
- args[i] = lb_typeid(p->module, args[i].type);
- } else {
- args[i] = lb_emit_conv(p, args[i], e->type);
+ auto var_args = array_make<lbValue>(heap_allocator(), 0, variadic.count);
+ defer (array_free(&var_args));
+ for (Ast *var_arg : variadic) {
+ lbValue v = lb_build_expr(p, var_arg);
+ lb_add_values_to_array(p, &var_args, v);
}
- }
- }
- }
-
- if (variadic && !vari_expand && !is_c_vararg) {
- // variadic call argument generation
- Type *slice_type = param_tuple->variables[variadic_index]->type;
- Type *elem_type = base_type(slice_type)->Slice.elem;
- lbAddr slice = lb_add_local_generated(p, slice_type, true);
- isize slice_len = arg_count+1 - (variadic_index+1);
+ isize slice_len = var_args.count;
+ if (slice_len > 0) {
+ lbAddr slice = lb_add_local_generated(p, slice_type, true);
+ lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true);
+
+ for (isize i = 0; i < var_args.count; i++) {
+ lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i);
+ lbValue var_arg = var_args[i];
+ var_arg = lb_emit_conv(p, var_arg, elem_type);
+ lb_emit_store(p, addr, var_arg);
+ }
- if (slice_len > 0) {
- lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true);
+ lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0);
+ lbValue len = lb_const_int(p->module, t_int, slice_len);
+ lb_fill_slice(p, slice, base_elem, len);
- for (isize i = variadic_index, j = 0; i < arg_count; i++, j++) {
- lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)j);
- lb_emit_store(p, addr, args[i]);
+ variadic_args = lb_addr_load(p, slice);
+ }
}
-
- lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0);
- lbValue len = lb_const_int(m, t_int, slice_len);
- lb_fill_slice(p, slice, base_elem, len);
}
+ array_add(&args, variadic_args);
- arg_count = param_count;
- args[variadic_index] = lb_addr_load(p, slice);
+ break;
+ } else {
+ lbValue value = lb_build_expr(p, ce->split_args->positional[i]);
+ lb_add_values_to_array(p, &args, value);
}
}
- if (variadic && variadic_index+1 < param_count) {
- for (isize i = variadic_index+1; i < param_count; i++) {
- Entity *e = param_tuple->variables[i];
- args[i] = lb_handle_param_value(p, e->type, e->Variable.param_value, ast_token(expr).pos);
- }
+ if (!is_c_vararg) {
+ array_resize(&args, pt->param_count);
}
- isize final_count = param_count;
- if (is_c_vararg) {
- final_count = arg_count;
+ for (Ast *arg : ce->split_args->named) {
+ ast_node(fv, FieldValue, arg);
+ GB_ASSERT(fv->field->kind == Ast_Ident);
+ String name = fv->field->Ident.token.string;
+ gb_unused(name);
+ isize param_index = lookup_procedure_parameter(pt, name);
+ GB_ASSERT(param_index >= 0);
+
+ lbValue value = lb_build_expr(p, fv->value);
+ GB_ASSERT(!is_type_tuple(value.type));
+ args[param_index] = value;
}
- if (param_tuple != nullptr) {
- for (isize i = 0; i < gb_min(args.count, param_tuple->variables.count); i++) {
- Entity *e = param_tuple->variables[i];
- if (args[i].type == nullptr) {
+ TokenPos pos = ast_token(ce->proc).pos;
+
+
+ if (pt->params != nullptr) {
+ isize min_count = pt->params->Tuple.variables.count;
+ if (is_c_vararg) {
+ min_count -= 1;
+ }
+ GB_ASSERT(args.count >= min_count);
+ for_array(arg_index, pt->params->Tuple.variables) {
+ Entity *e = pt->params->Tuple.variables[arg_index];
+ if (pt->variadic && arg_index == pt->variadic_index) {
+ if (!is_c_vararg && args[arg_index].value == 0) {
+ args[arg_index] = lb_const_nil(p->module, e->type);
+ }
continue;
- } else if (is_type_untyped_uninit(args[i].type)) {
- args[i] = lb_const_undef(m, e->type);
- } else if (is_type_untyped_nil(args[i].type)) {
- args[i] = lb_const_nil(m, e->type);
+ }
+
+ lbValue arg = args[arg_index];
+ if (arg.value == nullptr) {
+ switch (e->kind) {
+ case Entity_TypeName:
+ args[arg_index] = lb_const_nil(p->module, e->type);
+ break;
+ case Entity_Variable:
+ args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pos);
+ break;
+
+ case Entity_Constant:
+ args[arg_index] = lb_const_value(p->module, e->type, e->Constant.value);
+ break;
+ default:
+ GB_PANIC("Unknown entity kind %.*s\n", LIT(entity_strings[e->kind]));
+ }
+ } else {
+ args[arg_index] = lb_emit_conv(p, arg, e->type);
}
}
}
+ isize final_count = is_c_vararg ? args.count : pt->param_count;
auto call_args = array_slice(args, 0, final_count);
return lb_emit_call(p, value, call_args, ce->inlining);
}
diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp
index 275d1f728..60420402a 100644
--- a/src/llvm_backend_stmt.cpp
+++ b/src/llvm_backend_stmt.cpp
@@ -619,6 +619,18 @@ gb_internal void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_t
}
+gb_internal Ast *lb_strip_and_prefix(Ast *ident) {
+ if (ident != nullptr) {
+ if (ident->kind == Ast_UnaryExpr && ident->UnaryExpr.op.kind == Token_And) {
+ ident = ident->UnaryExpr.expr;
+ }
+ GB_ASSERT(ident->kind == Ast_Ident);
+ }
+ return ident;
+}
+
+
+
gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
AstRangeStmt *rs, Scope *scope) {
bool ADD_EXTRA_WRAPPING_CHECK = true;
@@ -627,13 +639,15 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
lb_open_scope(p, scope);
+ Ast *val0 = rs->vals.count > 0 ? lb_strip_and_prefix(rs->vals[0]) : nullptr;
+ Ast *val1 = rs->vals.count > 1 ? lb_strip_and_prefix(rs->vals[1]) : nullptr;
Type *val0_type = nullptr;
Type *val1_type = nullptr;
- if (rs->vals.count > 0 && rs->vals[0] != nullptr && !is_blank_ident(rs->vals[0])) {
- val0_type = type_of_expr(rs->vals[0]);
+ if (val0 != nullptr && !is_blank_ident(val0)) {
+ val0_type = type_of_expr(val0);
}
- if (rs->vals.count > 1 && rs->vals[1] != nullptr && !is_blank_ident(rs->vals[1])) {
- val1_type = type_of_expr(rs->vals[1]);
+ if (val1 != nullptr && !is_blank_ident(val1)) {
+ val1_type = type_of_expr(val1);
}
TokenKind op = Token_Lt;
@@ -649,7 +663,7 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
lbAddr value;
if (val0_type != nullptr) {
- Entity *e = entity_of_node(rs->vals[0]);
+ Entity *e = entity_of_node(val0);
value = lb_add_local(p, val0_type, e, false);
} else {
value = lb_add_local_generated(p, lower.type, false);
@@ -658,7 +672,7 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
lbAddr index;
if (val1_type != nullptr) {
- Entity *e = entity_of_node(rs->vals[1]);
+ Entity *e = entity_of_node(val1);
index = lb_add_local(p, val1_type, e, false);
} else {
index = lb_add_local_generated(p, t_int, false);
@@ -680,8 +694,8 @@ gb_internal void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
lbValue val = lb_addr_load(p, value);
lbValue idx = lb_addr_load(p, index);
- if (val0_type) lb_store_range_stmt_val(p, rs->vals[0], val);
- if (val1_type) lb_store_range_stmt_val(p, rs->vals[1], idx);
+ if (val0_type) lb_store_range_stmt_val(p, val0, val);
+ if (val1_type) lb_store_range_stmt_val(p, val1, idx);
{
// NOTE: this check block will most likely be optimized out, and is here
@@ -815,12 +829,14 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
lb_open_scope(p, scope);
+ Ast *val0 = rs->vals.count > 0 ? lb_strip_and_prefix(rs->vals[0]) : nullptr;
+ Ast *val1 = rs->vals.count > 1 ? lb_strip_and_prefix(rs->vals[1]) : nullptr;
Type *val_types[2] = {};
- if (rs->vals.count > 0 && rs->vals[0] != nullptr && !is_blank_ident(rs->vals[0])) {
- val_types[0] = type_of_expr(rs->vals[0]);
+ if (val0 != nullptr && !is_blank_ident(val0)) {
+ val_types[0] = type_of_expr(val0);
}
- if (rs->vals.count > 1 && rs->vals[1] != nullptr && !is_blank_ident(rs->vals[1])) {
- val_types[1] = type_of_expr(rs->vals[1]);
+ if (val1 != nullptr && !is_blank_ident(val1)) {
+ val_types[1] = type_of_expr(val1);
}
@@ -901,14 +917,14 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
if (val_types[0]) {
- Entity *e = entity_of_node(rs->vals[0]);
+ Entity *e = entity_of_node(val0);
if (e != nullptr) {
lbAddr soa_val = lb_addr_soa_variable(array.addr, lb_addr_load(p, index), nullptr);
map_set(&p->module->soa_values, e, soa_val);
}
}
if (val_types[1]) {
- lb_store_range_stmt_val(p, rs->vals[1], lb_addr_load(p, index));
+ lb_store_range_stmt_val(p, val1, lb_addr_load(p, index));
}
@@ -942,13 +958,15 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
lb_open_scope(p, scope);
+ Ast *val0 = rs->vals.count > 0 ? lb_strip_and_prefix(rs->vals[0]) : nullptr;
+ Ast *val1 = rs->vals.count > 1 ? lb_strip_and_prefix(rs->vals[1]) : nullptr;
Type *val0_type = nullptr;
Type *val1_type = nullptr;
- if (rs->vals.count > 0 && rs->vals[0] != nullptr && !is_blank_ident(rs->vals[0])) {
- val0_type = type_of_expr(rs->vals[0]);
+ if (val0 != nullptr && !is_blank_ident(val0)) {
+ val0_type = type_of_expr(val0);
}
- if (rs->vals.count > 1 && rs->vals[1] != nullptr && !is_blank_ident(rs->vals[1])) {
- val1_type = type_of_expr(rs->vals[1]);
+ if (val1 != nullptr && !is_blank_ident(val1)) {
+ val1_type = type_of_expr(val1);
}
lbValue val = {};
@@ -1042,11 +1060,11 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
if (is_map) {
- if (val0_type) lb_store_range_stmt_val(p, rs->vals[0], key);
- if (val1_type) lb_store_range_stmt_val(p, rs->vals[1], val);
+ if (val0_type) lb_store_range_stmt_val(p, val0, key);
+ if (val1_type) lb_store_range_stmt_val(p, val1, val);
} else {
- if (val0_type) lb_store_range_stmt_val(p, rs->vals[0], val);
- if (val1_type) lb_store_range_stmt_val(p, rs->vals[1], key);
+ if (val0_type) lb_store_range_stmt_val(p, val0, val);
+ if (val1_type) lb_store_range_stmt_val(p, val1, key);
}
lb_push_target_list(p, rs->label, done, loop, nullptr);
@@ -1064,21 +1082,23 @@ gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt *
lb_open_scope(p, scope); // Open scope here
+ Ast *val0 = lb_strip_and_prefix(rs->val0);
+ Ast *val1 = lb_strip_and_prefix(rs->val1);
Type *val0_type = nullptr;
Type *val1_type = nullptr;
- if (rs->val0 != nullptr && !is_blank_ident(rs->val0)) {
- val0_type = type_of_expr(rs->val0);
+ if (val0 != nullptr && !is_blank_ident(val0)) {
+ val0_type = type_of_expr(val0);
}
- if (rs->val1 != nullptr && !is_blank_ident(rs->val1)) {
- val1_type = type_of_expr(rs->val1);
+ if (val1 != nullptr && !is_blank_ident(val1)) {
+ val1_type = type_of_expr(val1);
}
if (val0_type != nullptr) {
- Entity *e = entity_of_node(rs->val0);
+ Entity *e = entity_of_node(val0);
lb_add_local(p, e->type, e, true);
}
if (val1_type != nullptr) {
- Entity *e = entity_of_node(rs->val1);
+ Entity *e = entity_of_node(val1);
lb_add_local(p, e->type, e, true);
}
@@ -1092,8 +1112,8 @@ gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt *
lbAddr val0_addr = {};
lbAddr val1_addr = {};
- if (val0_type) val0_addr = lb_build_addr(p, rs->val0);
- if (val1_type) val1_addr = lb_build_addr(p, rs->val1);
+ if (val0_type) val0_addr = lb_build_addr(p, val0);
+ if (val1_type) val1_addr = lb_build_addr(p, val1);
TokenKind op = expr->BinaryExpr.op.kind;
Ast *start_expr = expr->BinaryExpr.left;
@@ -1135,8 +1155,8 @@ gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt *
lbAddr val0_addr = {};
lbAddr val1_addr = {};
- if (val0_type) val0_addr = lb_build_addr(p, rs->val0);
- if (val1_type) val1_addr = lb_build_addr(p, rs->val1);
+ if (val0_type) val0_addr = lb_build_addr(p, val0);
+ if (val1_type) val1_addr = lb_build_addr(p, val1);
for_array(i, bet->Enum.fields) {
Entity *field = bet->Enum.fields[i];
@@ -1149,8 +1169,8 @@ gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt *
} else {
lbAddr val0_addr = {};
lbAddr val1_addr = {};
- if (val0_type) val0_addr = lb_build_addr(p, rs->val0);
- if (val1_type) val1_addr = lb_build_addr(p, rs->val1);
+ if (val0_type) val0_addr = lb_build_addr(p, val0);
+ if (val1_type) val1_addr = lb_build_addr(p, val1);
GB_ASSERT(expr->tav.mode == Addressing_Constant);
@@ -1858,7 +1878,9 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return
} else if (return_count == 1) {
Entity *e = tuple->variables[0];
if (res_count == 0) {
+ rw_mutex_shared_lock(&p->module->values_mutex);
lbValue found = map_must_get(&p->module->values, e);
+ rw_mutex_shared_unlock(&p->module->values_mutex);
res = lb_emit_load(p, found);
} else {
res = lb_build_expr(p, return_results[0]);
@@ -1867,7 +1889,9 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return
if (p->type->Proc.has_named_results) {
// NOTE(bill): store the named values before returning
if (e->token.string != "") {
+ rw_mutex_shared_lock(&p->module->values_mutex);
lbValue found = map_must_get(&p->module->values, e);
+ rw_mutex_shared_unlock(&p->module->values_mutex);
lb_emit_store(p, found, lb_emit_conv(p, res, e->type));
}
}
@@ -1883,7 +1907,9 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return
} else {
for (isize res_index = 0; res_index < return_count; res_index++) {
Entity *e = tuple->variables[res_index];
+ rw_mutex_shared_lock(&p->module->values_mutex);
lbValue found = map_must_get(&p->module->values, e);
+ rw_mutex_shared_unlock(&p->module->values_mutex);
lbValue res = lb_emit_load(p, found);
array_add(&results, res);
}
@@ -1905,7 +1931,9 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice<Ast *> const &return
if (e->token.string == "") {
continue;
}
+ rw_mutex_shared_lock(&p->module->values_mutex);
named_results[i] = map_must_get(&p->module->values, e);
+ rw_mutex_shared_unlock(&p->module->values_mutex);
values[i] = lb_emit_conv(p, results[i], e->type);
}
@@ -2463,6 +2491,7 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) {
lb_add_entity(p->module, e, val);
lb_add_debug_local_variable(p, val.value, e->type, e->token);
lvals_preused[lval_index] = true;
+ lvals[lval_index] = *comp_lit_addr;
}
}
}
diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp
index 1e26fd6bd..4716733cc 100644
--- a/src/llvm_backend_type.cpp
+++ b/src/llvm_backend_type.cpp
@@ -15,7 +15,7 @@ gb_internal isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_
}
gb_internal lbValue lb_typeid(lbModule *m, Type *type) {
- GB_ASSERT(!build_context.disallow_rtti);
+ GB_ASSERT(!build_context.no_rtti);
type = default_type(type);
@@ -92,7 +92,7 @@ gb_internal lbValue lb_typeid(lbModule *m, Type *type) {
}
gb_internal lbValue lb_type_info(lbModule *m, Type *type) {
- GB_ASSERT(!build_context.disallow_rtti);
+ GB_ASSERT(!build_context.no_rtti);
type = default_type(type);
@@ -141,7 +141,7 @@ gb_internal lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count)
gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data
- if (build_context.disallow_rtti) {
+ if (build_context.no_rtti) {
return;
}
@@ -654,10 +654,9 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup
lbValue count = lb_const_int(m, t_int, variant_count);
vals[0] = llvm_const_slice(m, memory_types, count);
- i64 tag_size = union_tag_size(t);
- i64 tag_offset = align_formula(t->Union.variant_block_size, tag_size);
-
+ i64 tag_size = union_tag_size(t);
if (tag_size > 0) {
+ i64 tag_offset = align_formula(t->Union.variant_block_size, tag_size);
vals[1] = lb_const_int(m, t_uintptr, tag_offset).value;
vals[2] = lb_type_info(m, union_tag_type(t)).value;
} else {
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index 0c26382ed..2ecad1703 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -721,7 +721,7 @@ gb_internal lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type
Type *dst_type = tuple->Tuple.variables[0]->type;
isize arg_count = 7;
- if (build_context.disallow_rtti) {
+ if (build_context.no_rtti) {
arg_count = 4;
}
@@ -733,7 +733,7 @@ gb_internal lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type
args[2] = lb_const_int(m, t_i32, pos.line);
args[3] = lb_const_int(m, t_i32, pos.column);
- if (!build_context.disallow_rtti) {
+ if (!build_context.no_rtti) {
args[4] = lb_typeid(m, src_type);
args[5] = lb_typeid(m, dst_type);
args[6] = lb_emit_conv(p, value_, t_rawptr);
@@ -797,7 +797,7 @@ gb_internal lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *ty
lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
isize arg_count = 7;
- if (build_context.disallow_rtti) {
+ if (build_context.no_rtti) {
arg_count = 4;
}
auto args = array_make<lbValue>(permanent_allocator(), arg_count);
@@ -807,7 +807,7 @@ gb_internal lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *ty
args[2] = lb_const_int(m, t_i32, pos.line);
args[3] = lb_const_int(m, t_i32, pos.column);
- if (!build_context.disallow_rtti) {
+ if (!build_context.no_rtti) {
args[4] = any_typeid;
args[5] = dst_typeid;
args[6] = lb_emit_struct_ev(p, value, 0);
diff --git a/src/main.cpp b/src/main.cpp
index 162cd309e..db2702b19 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -655,7 +655,6 @@ enum BuildFlagKind {
BuildFlag_ShowDebugMessages,
BuildFlag_Vet,
BuildFlag_VetExtra,
- BuildFlag_UseLLVMApi,
BuildFlag_IgnoreUnknownAttributes,
BuildFlag_ExtraLinkerFlags,
BuildFlag_ExtraAssemblerFlags,
@@ -671,11 +670,10 @@ enum BuildFlagKind {
BuildFlag_DisallowDo,
BuildFlag_DefaultToNilAllocator,
- BuildFlag_InsertSemicolon,
BuildFlag_StrictStyle,
BuildFlag_StrictStyleInitOnly,
BuildFlag_ForeignErrorProcedures,
- BuildFlag_DisallowRTTI,
+ BuildFlag_NoRTTI,
BuildFlag_DynamicMapCalls,
BuildFlag_Compact,
@@ -834,7 +832,6 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build);
@@ -849,12 +846,12 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_StrictStyleInitOnly, str_lit("strict-style-init-only"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ForeignErrorProcedures, str_lit("foreign-error-procedures"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_DisallowRTTI, str_lit("disallow-rtti"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoRTTI, str_lit("no-rtti"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoRTTI, str_lit("disallow-rtti"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_DynamicMapCalls, str_lit("dynamic-map-calls"), BuildFlagParam_None, Command__does_check);
@@ -1372,11 +1369,6 @@ gb_internal bool parse_build_flags(Array<String> args) {
build_context.vet_extra = true;
break;
}
- case BuildFlag_UseLLVMApi: {
- gb_printf_err("-llvm-api flag is not required any more\n");
- bad_flags = true;
- break;
- }
case BuildFlag_IgnoreUnknownAttributes:
build_context.ignore_unknown_attributes = true;
break;
@@ -1448,8 +1440,12 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_DisallowDo:
build_context.disallow_do = true;
break;
- case BuildFlag_DisallowRTTI:
- build_context.disallow_rtti = true;
+ case BuildFlag_NoRTTI:
+ if (name == "disallow-rtti") {
+ gb_printf_err("'-disallow-rtti' has been replaced with '-no-rtti'\n");
+ bad_flags = true;
+ }
+ build_context.no_rtti = true;
break;
case BuildFlag_DynamicMapCalls:
build_context.dynamic_map_calls = true;
@@ -1460,11 +1456,6 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_ForeignErrorProcedures:
build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true;
break;
- case BuildFlag_InsertSemicolon: {
- gb_printf_err("-insert-semicolon flag is not required any more\n");
- bad_flags = true;
- break;
- }
case BuildFlag_StrictStyle: {
if (build_context.strict_style_init_only) {
gb_printf_err("-strict-style and -strict-style-init-only cannot be used together\n");
@@ -2558,6 +2549,22 @@ gb_internal int strip_semicolons(Parser *parser) {
gb_internal void init_terminal(void) {
build_context.has_ansi_terminal_colours = false;
+
+ gbAllocator a = heap_allocator();
+
+ char const *no_color = gb_get_env("NO_COLOR", a);
+ defer (gb_free(a, cast(void *)no_color));
+ if (no_color != nullptr) {
+ return;
+ }
+
+ char const *force_color = gb_get_env("FORCE_COLOR", a);
+ defer (gb_free(a, cast(void *)force_color));
+ if (force_color != nullptr) {
+ build_context.has_ansi_terminal_colours = true;
+ return;
+ }
+
#if defined(GB_SYSTEM_WINDOWS)
HANDLE hnd = GetStdHandle(STD_ERROR_HANDLE);
DWORD mode = 0;
@@ -2567,10 +2574,16 @@ gb_internal void init_terminal(void) {
build_context.has_ansi_terminal_colours = true;
}
}
+#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
+ char const *term_ = gb_get_env("TERM", a);
+ defer (gb_free(a, cast(void *)term_));
+ String term = make_string_c(term_);
+ if (!str_eq(term, str_lit("dumb")) && isatty(STDERR_FILENO)) {
+ build_context.has_ansi_terminal_colours = true;
+ }
#endif
if (!build_context.has_ansi_terminal_colours) {
- gbAllocator a = heap_allocator();
char const *odin_terminal_ = gb_get_env("ODIN_TERMINAL", a);
defer (gb_free(a, cast(void *)odin_terminal_));
String odin_terminal = make_string_c(odin_terminal_);
diff --git a/src/parser.cpp b/src/parser.cpp
index 883342b21..b756412ff 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -2752,9 +2752,9 @@ gb_internal Ast *parse_call_expr(AstFile *f, Ast *operand) {
open_paren = expect_token(f, Token_OpenParen);
+ bool seen_ellipsis = false;
while (f->curr_token.kind != Token_CloseParen &&
- f->curr_token.kind != Token_EOF &&
- ellipsis.pos.line == 0) {
+ f->curr_token.kind != Token_EOF) {
if (f->curr_token.kind == Token_Comma) {
syntax_error(f->curr_token, "Expected an expression not ,");
} else if (f->curr_token.kind == Token_Eq) {
@@ -2777,11 +2777,15 @@ gb_internal Ast *parse_call_expr(AstFile *f, Ast *operand) {
Ast *value = parse_value(f);
arg = ast_field_value(f, arg, value, eq);
-
-
+ } else if (seen_ellipsis) {
+ syntax_error(arg, "Positional arguments are not allowed after '..'");
}
array_add(&args, arg);
+ if (ellipsis.pos.line != 0) {
+ seen_ellipsis = true;
+ }
+
if (!allow_field_separator(f)) {
break;
}
diff --git a/src/parser.hpp b/src/parser.hpp
index 6ba4ef6d6..900fddbab 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -367,6 +367,11 @@ gb_global char const *union_type_kind_strings[UnionType_COUNT] = {
"#shared_nil",
};
+struct AstSplitArgs {
+ Slice<Ast *> positional;
+ Slice<Ast *> named;
+};
+
#define AST_KINDS \
AST_KIND(Ident, "identifier", struct { \
Token token; \
@@ -442,6 +447,7 @@ AST_KIND(_ExprBegin, "", bool) \
ProcInlining inlining; \
bool optional_ok_one; \
bool was_selector; \
+ AstSplitArgs *split_args; \
}) \
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
AST_KIND(EnumFieldValue, "enum field value", struct { \
diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp
index 52d49e897..3d2e8f27d 100644
--- a/src/parser_pos.cpp
+++ b/src/parser_pos.cpp
@@ -37,11 +37,15 @@ gb_internal Token ast_token(Ast *node) {
return ast_token(node->ImplicitSelectorExpr.selector);
}
return node->ImplicitSelectorExpr.token;
- case Ast_IndexExpr: return node->IndexExpr.open;
- case Ast_MatrixIndexExpr: return node->MatrixIndexExpr.open;
- case Ast_SliceExpr: return node->SliceExpr.open;
+ case Ast_IndexExpr: return ast_token(node->IndexExpr.expr);
+ case Ast_MatrixIndexExpr: return ast_token(node->MatrixIndexExpr.expr);
+ case Ast_SliceExpr: return ast_token(node->SliceExpr.expr);
case Ast_Ellipsis: return node->Ellipsis.token;
- case Ast_FieldValue: return node->FieldValue.eq;
+ case Ast_FieldValue:
+ if (node->FieldValue.field) {
+ return ast_token(node->FieldValue.field);
+ }
+ return node->FieldValue.eq;
case Ast_EnumFieldValue: return ast_token(node->EnumFieldValue.name);
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x);
diff --git a/src/types.cpp b/src/types.cpp
index 3cc077f84..847aea9f3 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -2108,8 +2108,12 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) {
return is_type_polymorphic(t->Matrix.elem, or_specialized);
case Type_Tuple:
- for_array(i, t->Tuple.variables) {
- if (is_type_polymorphic(t->Tuple.variables[i]->type, or_specialized)) {
+ for (Entity *e : t->Tuple.variables) {
+ if (e->kind == Entity_Constant) {
+ if (e->Constant.value.kind != ExactValue_Invalid) {
+ return or_specialized;
+ }
+ } else if (is_type_polymorphic(e->type, or_specialized)) {
return true;
}
}
@@ -2119,7 +2123,6 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) {
if (t->Proc.is_polymorphic) {
return true;
}
- #if 1
if (t->Proc.param_count > 0 &&
is_type_polymorphic(t->Proc.params, or_specialized)) {
return true;
@@ -2128,7 +2131,6 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) {
is_type_polymorphic(t->Proc.results, or_specialized)) {
return true;
}
- #endif
break;
case Type_Enum:
@@ -3079,7 +3081,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
mutex_lock(md->mutex);
defer (mutex_unlock(md->mutex));
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
- GB_ASSERT(entry.entity->kind == Entity_Procedure);
+ GB_ASSERT(entry.entity->kind == Entity_Procedure || entry.entity->kind == Entity_ProcGroup);
if (entry.name == field_name) {
sel.entity = entry.entity;
sel.pseudo_field = true;
@@ -3326,6 +3328,9 @@ gb_internal bool are_struct_fields_reordered(Type *type) {
type = base_type(type);
GB_ASSERT(type->kind == Type_Struct);
type_set_offsets(type);
+ if (type->Struct.fields.count == 0) {
+ return false;
+ }
GB_ASSERT(type->Struct.offsets != nullptr);
i64 prev_offset = 0;
@@ -3344,6 +3349,9 @@ gb_internal Slice<i32> struct_fields_index_by_increasing_offset(gbAllocator allo
type = base_type(type);
GB_ASSERT(type->kind == Type_Struct);
type_set_offsets(type);
+ if (type->Struct.fields.count == 0) {
+ return {};
+ }
GB_ASSERT(type->Struct.offsets != nullptr);
auto indices = slice_make<i32>(allocator, type->Struct.fields.count);
@@ -4273,6 +4281,10 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
if (var == nullptr) {
continue;
}
+ if (comma_index++ > 0) {
+ str = gb_string_appendc(str, ", ");
+ }
+
String name = var->token.string;
if (var->kind == Entity_Constant) {
str = gb_string_appendc(str, "$");
@@ -4289,10 +4301,6 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
continue;
}
- if (comma_index++ > 0) {
- str = gb_string_appendc(str, ", ");
- }
-
if (var->kind == Entity_Variable) {
if (var->flags&EntityFlag_CVarArg) {
str = gb_string_appendc(str, "#c_vararg ");