aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorgingerBill <bill@gingerbill.org>2023-06-06 22:42:04 +0100
committergingerBill <bill@gingerbill.org>2023-06-06 22:42:04 +0100
commit4a75a1e839252e7719e4126e0e98dd647850f91d (patch)
treec842ea0f1924563be025ef49726d98d9f2763e94 /src
parent60ec3594ab05faaa46c7cac50e87571ab3f5eaa6 (diff)
parent6a2ff3a3711e3da6bc9f2be9d9a67361b3ff9bd5 (diff)
Merge branch 'master' into separate-int-word-sizes
Diffstat (limited to 'src')
-rw-r--r--src/build_settings.cpp24
-rw-r--r--src/check_builtin.cpp83
-rw-r--r--src/check_decl.cpp24
-rw-r--r--src/check_expr.cpp77
-rw-r--r--src/check_stmt.cpp28
-rw-r--r--src/check_type.cpp6
-rw-r--r--src/checker.cpp35
-rw-r--r--src/checker_builtin_procs.hpp2
-rw-r--r--src/docs_writer.cpp80
-rw-r--r--src/llvm_abi.cpp37
-rw-r--r--src/llvm_backend.cpp41
-rw-r--r--src/llvm_backend.hpp2
-rw-r--r--src/llvm_backend_const.cpp15
-rw-r--r--src/llvm_backend_debug.cpp2
-rw-r--r--src/llvm_backend_expr.cpp94
-rw-r--r--src/llvm_backend_general.cpp4
-rw-r--r--src/llvm_backend_opt.cpp12
-rw-r--r--src/llvm_backend_proc.cpp8
-rw-r--r--src/llvm_backend_stmt.cpp252
-rw-r--r--src/parser.cpp47
-rw-r--r--src/parser.hpp12
-rw-r--r--src/parser_pos.cpp4
-rw-r--r--src/path.cpp63
-rw-r--r--src/tokenizer.cpp6
-rw-r--r--src/types.cpp31
25 files changed, 758 insertions, 231 deletions
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 39fc34cf1..4aa552255 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -1538,13 +1538,23 @@ gb_internal bool init_build_paths(String init_filename) {
} else if (is_arch_wasm()) {
output_extension = STR_LIT("wasm");
} else if (build_context.build_mode == BuildMode_Executable) {
- // By default use a .bin executable extension.
- output_extension = STR_LIT("bin");
+ // By default use no executable extension.
+ output_extension = make_string(nullptr, 0);
+ String const single_file_extension = str_lit(".odin");
if (build_context.metrics.os == TargetOs_windows) {
output_extension = STR_LIT("exe");
} else if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
- output_extension = make_string(nullptr, 0);
+ // Do nothing: we don't want the .bin extension
+ // when cross compiling
+ } else if (path_is_directory(last_path_element(bc->build_paths[BuildPath_Main_Package].basename))) {
+ // Add .bin extension to avoid collision
+ // with package directory name
+ output_extension = STR_LIT("bin");
+ } else if (string_ends_with(init_filename, single_file_extension) && path_is_directory(remove_extension_from_path(init_filename))) {
+ // Add bin extension if compiling single-file package
+ // with same output name as a directory
+ output_extension = STR_LIT("bin");
}
} else if (build_context.build_mode == BuildMode_DynamicLibrary) {
// By default use a .so shared library extension.
@@ -1656,6 +1666,14 @@ gb_internal bool init_build_paths(String init_filename) {
return false;
}
+ if (!write_directory(bc->build_paths[BuildPath_Output].basename)) {
+ String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
+ defer (gb_free(ha, output_file.text));
+ gb_printf_err("No write permissions for output path: %.*s\n", LIT(output_file));
+ return false;
+ }
+
+
if (bc->target_features_string.len != 0) {
enable_target_feature({}, bc->target_features_string);
}
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index 5f9715959..46ee6b7f9 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -4843,6 +4843,89 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
operand->mode = Addressing_Type;
break;
+ case BuiltinProc_type_merge:
+ {
+ operand->mode = Addressing_Type;
+ operand->type = t_invalid;
+
+ Operand x = {};
+ Operand y = {};
+ check_expr_or_type(c, &x, ce->args[0]);
+ check_expr_or_type(c, &y, ce->args[1]);
+ if (x.mode != Addressing_Type) {
+ error(x.expr, "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ if (y.mode != Addressing_Type) {
+ error(y.expr, "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+
+ if (is_type_polymorphic(x.type)) {
+ gbString t = type_to_string(x.type);
+ error(x.expr, "Expected a non-polymorphic type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+ if (is_type_polymorphic(y.type)) {
+ gbString t = type_to_string(y.type);
+ error(y.expr, "Expected a non-polymorphic type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+ if (!is_type_union(x.type)) {
+ gbString t = type_to_string(x.type);
+ error(x.expr, "Expected a union type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+ if (!is_type_union(y.type)) {
+ gbString t = type_to_string(y.type);
+ error(x.expr, "Expected a union type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+ Type *ux = base_type(x.type);
+ Type *uy = base_type(y.type);
+ GB_ASSERT(ux->kind == Type_Union);
+ GB_ASSERT(uy->kind == Type_Union);
+
+ i64 custom_align = gb_max(ux->Union.custom_align, uy->Union.custom_align);
+ if (ux->Union.kind != uy->Union.kind) {
+ error(x.expr, "Union kinds must match, got %s vs %s", union_type_kind_strings[ux->Union.kind], union_type_kind_strings[uy->Union.kind]);
+ }
+
+ Type *merged_union = alloc_type_union();
+
+ merged_union->Union.node = call;
+ merged_union->Union.scope = create_scope(c->info, c->scope);
+ merged_union->Union.kind = ux->Union.kind;
+ merged_union->Union.custom_align = custom_align;
+
+ auto variants = array_make<Type *>(permanent_allocator(), 0, ux->Union.variants.count+uy->Union.variants.count);
+ for (Type *t : ux->Union.variants) {
+ array_add(&variants, t);
+ }
+ for (Type *t : uy->Union.variants) {
+ bool ok = true;
+ for (Type *other_t : ux->Union.variants) {
+ if (are_types_identical(other_t, t)) {
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ array_add(&variants, t);
+ }
+
+ }
+ merged_union->Union.variants = slice_from_array(variants);
+
+ operand->mode = Addressing_Type;
+ operand->type = merged_union;
+ }
+ break;
+
case BuiltinProc_type_is_boolean:
case BuiltinProc_type_is_integer:
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index a984f87a3..b651e33e6 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -43,14 +43,20 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o
}
if (operand->mode == Addressing_Type) {
- if (e->type != nullptr && is_type_typeid(e->type)) {
+ if (e->type != nullptr && is_type_typeid(e->type) && !is_type_polymorphic(operand->type)) {
add_type_info_type(ctx, operand->type);
add_type_and_value(ctx, operand->expr, Addressing_Value, e->type, exact_value_typeid(operand->type));
return e->type;
} else {
+ ERROR_BLOCK();
+
gbString t = type_to_string(operand->type);
defer (gb_string_free(t));
- error(operand->expr, "Cannot assign a type '%s' to variable '%.*s'", t, LIT(e->token.string));
+ if (is_type_polymorphic(operand->type)) {
+ error(operand->expr, "Cannot assign a non-specialized polymorphic type '%s' to variable '%.*s'", t, LIT(e->token.string));
+ } else {
+ error(operand->expr, "Cannot assign a type '%s' to variable '%.*s'", t, LIT(e->token.string));
+ }
if (e->type == nullptr) {
error_line("\tThe type of the variable '%.*s' cannot be inferred as a type does not have a default type\n", LIT(e->token.string));
}
@@ -59,20 +65,17 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o
}
}
-
-
if (e->type == nullptr) {
// NOTE(bill): Use the type of the operand
Type *t = operand->type;
if (is_type_untyped(t)) {
- if (t == t_invalid || is_type_untyped_nil(t)) {
- error(e->token, "Invalid use of untyped nil in %.*s", LIT(context_name));
+ if (is_type_untyped_uninit(t)) {
+ error(e->token, "Invalid use of --- in %.*s", LIT(context_name));
e->type = t_invalid;
return nullptr;
- }
- if (t == t_invalid || is_type_untyped_undef(t)) {
- error(e->token, "Invalid use of --- in %.*s", LIT(context_name));
+ } else if (t == t_invalid || is_type_untyped_nil(t)) {
+ error(e->token, "Invalid use of untyped nil in %.*s", LIT(context_name));
e->type = t_invalid;
return nullptr;
}
@@ -119,7 +122,7 @@ gb_internal void check_init_variables(CheckerContext *ctx, Entity **lhs, isize l
// an extra allocation
TEMPORARY_ALLOCATOR_GUARD();
auto operands = array_make<Operand>(temporary_allocator(), 0, 2*lhs_count);
- check_unpack_arguments(ctx, lhs, lhs_count, &operands, inits, true, false);
+ check_unpack_arguments(ctx, lhs, lhs_count, &operands, inits, UnpackFlag_AllowOk|UnpackFlag_AllowUndef);
isize rhs_count = operands.count;
isize max = gb_min(lhs_count, rhs_count);
@@ -947,6 +950,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
if (ac.require_declaration) {
e->flags |= EntityFlag_Require;
+ pl->inlining = ProcInlining_no_inline;
}
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 0db12aba0..830b5315d 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -646,11 +646,8 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
Type *src = base_type(s);
Type *dst = base_type(type);
- if (is_type_untyped_undef(src)) {
- if (type_has_undef(dst)) {
- return 1;
- }
- return -1;
+ if (is_type_untyped_uninit(src)) {
+ return 1;
}
if (is_type_untyped_nil(src)) {
@@ -993,13 +990,13 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
if (is_type_untyped(operand->type)) {
Type *target_type = type;
if (type == nullptr || is_type_any(type)) {
- if (type == nullptr && is_type_untyped_nil(operand->type)) {
- error(operand->expr, "Use of untyped nil in %.*s", LIT(context_name));
+ if (type == nullptr && is_type_untyped_uninit(operand->type)) {
+ error(operand->expr, "Use of --- in %.*s", LIT(context_name));
operand->mode = Addressing_Invalid;
return;
}
- if (type == nullptr && is_type_untyped_undef(operand->type)) {
- error(operand->expr, "Use of --- in %.*s", LIT(context_name));
+ if (type == nullptr && is_type_untyped_nil(operand->type)) {
+ error(operand->expr, "Use of untyped nil in %.*s", LIT(context_name));
operand->mode = Addressing_Invalid;
return;
}
@@ -1067,7 +1064,7 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
if (check_is_assignable_to(c, operand, type)) {
if (operand->mode == Addressing_Type && is_type_typeid(type)) {
- add_type_info_type(c, operand->type);
+ add_type_info_type(c, operand->type);
add_type_and_value(c, operand->expr, Addressing_Value, type, exact_value_typeid(operand->type));
}
} else {
@@ -3969,7 +3966,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
case Type_Union:
- if (!is_operand_nil(*operand) && !is_operand_undef(*operand)) {
+ if (!is_operand_nil(*operand) && !is_operand_uninit(*operand)) {
TEMPORARY_ALLOCATOR_GUARD();
isize count = t->Union.variants.count;
@@ -4036,8 +4033,8 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
error_line("\n\n");
return;
- } else if (is_type_untyped_undef(operand->type) && type_has_undef(target_type)) {
- target_type = t_untyped_undef;
+ } else if (is_type_untyped_uninit(operand->type)) {
+ target_type = t_untyped_uninit;
} else if (!is_type_untyped_nil(operand->type) || !type_has_nil(target_type)) {
begin_error_block();
defer (end_error_block());
@@ -4070,8 +4067,8 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
default:
- if (is_type_untyped_undef(operand->type) && type_has_undef(target_type)) {
- target_type = t_untyped_undef;
+ if (is_type_untyped_uninit(operand->type)) {
+ target_type = t_untyped_uninit;
} else if (is_type_untyped_nil(operand->type) && type_has_nil(target_type)) {
target_type = t_untyped_nil;
} else {
@@ -4083,8 +4080,8 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
}
if (is_type_any(target_type) && is_type_untyped(operand->type)) {
- if (is_type_untyped_nil(operand->type) && is_type_untyped_undef(operand->type)) {
-
+ if (is_type_untyped_nil(operand->type) && is_type_untyped_uninit(operand->type)) {
+
} else {
target_type = default_type(operand->type);
}
@@ -5144,8 +5141,20 @@ gb_internal bool check_assignment_arguments(CheckerContext *ctx, Array<Operand>
}
+typedef u32 UnpackFlags;
+enum UnpackFlag : u32 {
+ UnpackFlag_None = 0,
+ UnpackFlag_AllowOk = 1<<0,
+ UnpackFlag_IsVariadic = 1<<1,
+ UnpackFlag_AllowUndef = 1<<2,
+};
+
+
+gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs, UnpackFlags flags) {
+ bool allow_ok = (flags & UnpackFlag_AllowOk) != 0;
+ bool is_variadic = (flags & UnpackFlag_IsVariadic) != 0;
+ bool allow_undef = (flags & UnpackFlag_AllowUndef) != 0;
-gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs, bool allow_ok, bool is_variadic) {
bool optional_ok = false;
isize tuple_index = 0;
for_array(i, rhs) {
@@ -5184,7 +5193,16 @@ gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize
}
}
- check_expr_base(c, &o, rhs[i], type_hint);
+ Ast *rhs_expr = unparen_expr(rhs[i]);
+ 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);
+ } else {
+ check_expr_base(c, &o, rhs[i], type_hint);
+ }
if (o.mode == Addressing_NoValue) {
error_operand_no_value(&o);
o.mode = Addressing_Invalid;
@@ -5968,7 +5986,7 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
lhs = populate_proc_parameter_list(c, proc_type, &lhs_count, &is_variadic);
}
if (operand->mode != Addressing_ProcGroup) {
- check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, is_variadic);
+ check_unpack_arguments(c, lhs, lhs_count, &operands, args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
}
}
@@ -6025,7 +6043,7 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
isize lhs_count = -1;
bool is_variadic = false;
lhs = populate_proc_parameter_list(c, e->type, &lhs_count, &is_variadic);
- check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, is_variadic);
+ check_unpack_arguments(c, lhs, lhs_count, &operands, args, is_variadic ? UnpackFlag_IsVariadic : UnpackFlag_None);
CallArgumentData data = {};
CallArgumentError err = call_checker(c, call, e->type, e, operands, CallArgumentMode_ShowErrors, &data);
@@ -6101,7 +6119,7 @@ gb_internal CallArgumentData check_call_arguments(CheckerContext *c, Operand *op
}
- check_unpack_arguments(c, lhs, lhs_count, &operands, args, false, false);
+ check_unpack_arguments(c, lhs, lhs_count, &operands, args, UnpackFlag_None);
if (lhs != nullptr) {
gb_free(heap_allocator(), lhs);
@@ -6462,7 +6480,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
lhs_count = params->variables.count;
}
- check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, false, false);
+ check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, UnpackFlag_None);
}
}
@@ -7146,11 +7164,11 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
}
gb_internal bool ternary_compare_types(Type *x, Type *y) {
- if (is_type_untyped_undef(x) && type_has_undef(y)) {
+ if (is_type_untyped_uninit(x)) {
return true;
} else if (is_type_untyped_nil(x) && type_has_nil(y)) {
return true;
- } else if (is_type_untyped_undef(y) && type_has_undef(x)) {
+ } else if (is_type_untyped_uninit(y)) {
return true;
} else if (is_type_untyped_nil(y) && type_has_nil(x)) {
return true;
@@ -7687,7 +7705,7 @@ gb_internal ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *n
}
o->type = x.type;
- if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) {
+ if (is_type_untyped_nil(o->type) || is_type_untyped_uninit(o->type)) {
o->type = y.type;
}
@@ -9580,9 +9598,10 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
check_ident(c, o, node, nullptr, type_hint, false);
case_end;
- case_ast_node(u, Undef, node);
+ case_ast_node(u, Uninit, node);
o->mode = Addressing_Value;
- o->type = t_untyped_undef;
+ o->type = t_untyped_uninit;
+ error(node, "Use of --- outside of variable declaration");
case_end;
@@ -10145,7 +10164,7 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
str = string_append_string(str, bd->name.string);
case_end;
- case_ast_node(ud, Undef, node);
+ case_ast_node(ud, Uninit, node);
str = gb_string_appendc(str, "---");
case_end;
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 388a64e00..bdfa24460 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -402,6 +402,12 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
Type *assignment_type = lhs->type;
+ if (rhs->mode == Addressing_Type && is_type_polymorphic(rhs->type)) {
+ gbString t = type_to_string(rhs->type);
+ error(rhs->expr, "Invalid use of a non-specialized polymorphic type '%s'", t);
+ gb_string_free(t);
+ }
+
switch (lhs->mode) {
case Addressing_Invalid:
return nullptr;
@@ -1455,6 +1461,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
bool is_map = false;
bool use_by_reference_for_value = false;
bool is_soa = false;
+ bool is_reverse = rs->reverse;
Ast *expr = unparen_expr(rs->expr);
@@ -1470,6 +1477,10 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
}
array_add(&vals, x.type);
array_add(&vals, t_int);
+
+ if (is_reverse) {
+ error(node, "#reverse for is not supported with ranges, prefer an explicit for loop with init, condition, and post arguments");
+ }
} else {
Operand operand = {Addressing_Invalid};
check_expr_base(ctx, &operand, expr, nullptr);
@@ -1482,6 +1493,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
gb_string_free(t);
goto skip_expr_range_stmt;
} else {
+ if (is_reverse) {
+ error(node, "#reverse for is not supported for enum types");
+ }
array_add(&vals, operand.type);
array_add(&vals, t_int);
add_type_info_type(ctx, operand.type);
@@ -1495,7 +1509,11 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
if (is_type_string(t) && t->Basic.kind != Basic_cstring) {
array_add(&vals, t_rune);
array_add(&vals, t_int);
- add_package_dependency(ctx, "runtime", "string_decode_rune");
+ if (is_reverse) {
+ add_package_dependency(ctx, "runtime", "string_decode_last_rune");
+ } else {
+ add_package_dependency(ctx, "runtime", "string_decode_rune");
+ }
}
break;
@@ -1528,6 +1546,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
is_map = true;
array_add(&vals, t->Map.key);
array_add(&vals, t->Map.value);
+ if (is_reverse) {
+ error(node, "#reverse for is not supported for map types, as maps are unordered");
+ }
break;
case Type_Tuple:
@@ -1564,6 +1585,9 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
break;
}
+ if (is_reverse) {
+ error(node, "#reverse for is not supported for multiple return valued parameters");
+ }
}
break;
@@ -2170,7 +2194,7 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
auto operands = array_make<Operand>(heap_allocator(), 0, 2*rs->results.count);
defer (array_free(&operands));
- check_unpack_arguments(ctx, result_entities, result_count, &operands, rs->results, true, false);
+ check_unpack_arguments(ctx, result_entities, result_count, &operands, rs->results, UnpackFlag_AllowOk);
if (result_count == 0 && rs->results.count > 0) {
error(rs->results[0], "No return values expected");
diff --git a/src/check_type.cpp b/src/check_type.cpp
index dfe774f6b..bbfc25a12 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -135,7 +135,7 @@ gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entit
type = t_invalid;
}
if (is_type_untyped(type)) {
- if (is_type_untyped_undef(type)) {
+ if (is_type_untyped_uninit(type)) {
error(params[i], "Cannot determine parameter type from ---");
} else {
error(params[i], "Cannot determine parameter type from a nil");
@@ -473,7 +473,7 @@ gb_internal Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *poly
type = t_invalid;
}
if (is_type_untyped(type)) {
- if (is_type_untyped_undef(type)) {
+ if (is_type_untyped_uninit(type)) {
error(params[i], "Cannot determine parameter type from ---");
} else {
error(params[i], "Cannot determine parameter type from a nil");
@@ -1528,7 +1528,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
type = t_invalid;
}
if (is_type_untyped(type)) {
- if (is_type_untyped_undef(type)) {
+ if (is_type_untyped_uninit(type)) {
error(param, "Cannot determine parameter type from ---");
} else {
error(param, "Cannot determine parameter type from a nil");
diff --git a/src/checker.cpp b/src/checker.cpp
index f820a1468..d09d735a5 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -27,8 +27,8 @@ gb_internal bool is_operand_value(Operand o) {
gb_internal bool is_operand_nil(Operand o) {
return o.mode == Addressing_Value && o.type == t_untyped_nil;
}
-gb_internal bool is_operand_undef(Operand o) {
- return o.mode == Addressing_Value && o.type == t_untyped_undef;
+gb_internal bool is_operand_uninit(Operand o) {
+ return o.mode == Addressing_Value && o.type == t_untyped_uninit;
}
gb_internal bool check_rtti_type_disallowed(Token const &token, Type *type, char const *format) {
@@ -3469,6 +3469,19 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at
StringSet set = {};
defer (string_set_destroy(&set));
+ bool is_runtime = false;
+ if (c->scope && c->scope->file && (c->scope->flags & ScopeFlag_File) &&
+ c->scope->file->pkg &&
+ c->scope->file->pkg->kind == Package_Runtime) {
+ is_runtime = true;
+ } else if (c->scope && c->scope->parent &&
+ (c->scope->flags & ScopeFlag_Proc) &&
+ (c->scope->parent->flags & ScopeFlag_File) &&
+ c->scope->parent->file->pkg &&
+ c->scope->parent->file->pkg->kind == Package_Runtime) {
+ is_runtime = true;
+ }
+
for_array(i, attributes) {
Ast *attr = attributes[i];
if (attr->kind != Ast_Attribute) continue;
@@ -3504,9 +3517,14 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at
continue;
}
+ if (name == "builtin" && is_runtime) {
+ continue;
+ }
+
if (!proc(c, elem, name, value, ac)) {
if (!build_context.ignore_unknown_attributes) {
error(elem, "Unknown attribute element name '%.*s'", LIT(name));
+ error_line("\tDid you forget to use build flag '-ignore-unknown-attributes'?\n");
}
}
}
@@ -3663,9 +3681,9 @@ gb_internal void check_builtin_attributes(CheckerContext *ctx, Entity *e, Array<
error(value, "'builtin' cannot have a field value");
}
// Remove the builtin tag
- attr->Attribute.elems[k] = attr->Attribute.elems[attr->Attribute.elems.count-1];
- attr->Attribute.elems.count -= 1;
- k--;
+ // attr->Attribute.elems[k] = attr->Attribute.elems[attr->Attribute.elems.count-1];
+ // attr->Attribute.elems.count -= 1;
+ // k--;
mutex_unlock(&ctx->info->builtin_mutex);
}
@@ -3874,6 +3892,13 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
cc = ProcCC_CDecl;
if (c->foreign_context.default_cc > 0) {
cc = c->foreign_context.default_cc;
+ } else if (is_arch_wasm()) {
+ begin_error_block();
+ error(init, "For wasm related targets, it is required that you either define the"
+ " @(default_calling_convention=<string>) on the foreign block or"
+ " explicitly assign it on the procedure signature");
+ error_line("\tSuggestion: when dealing with normal Odin code (e.g. js_wasm32), use \"contextless\"; when dealing with Emscripten like code, use \"c\"\n");
+ end_error_block();
}
}
e->Procedure.link_prefix = c->foreign_context.link_prefix;
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index 72f001c70..edd046087 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -203,6 +203,7 @@ BuiltinProc__type_begin,
BuiltinProc_type_elem_type,
BuiltinProc_type_convert_variants_to_pointers,
+ BuiltinProc_type_merge,
BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_boolean,
@@ -501,6 +502,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_core_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_elem_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_convert_variants_to_pointers"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_merge"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_boolean"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp
index 7488e955a..2dd2f338b 100644
--- a/src/docs_writer.cpp
+++ b/src/docs_writer.cpp
@@ -30,6 +30,7 @@ struct OdinDocWriter {
PtrMap<AstPackage *, OdinDocPkgIndex> pkg_cache;
PtrMap<Entity *, OdinDocEntityIndex> entity_cache;
PtrMap<Type *, OdinDocTypeIndex> type_cache;
+ PtrMap<Type *, Type *> stable_type_cache;
OdinDocWriterItemTracker<OdinDocFile> files;
OdinDocWriterItemTracker<OdinDocPkg> pkgs;
@@ -51,6 +52,7 @@ gb_internal void odin_doc_writer_item_tracker_init(OdinDocWriterItemTracker<T> *
gb_internal void odin_doc_writer_prepare(OdinDocWriter *w) {
+ debugf("odin_doc_writer_prepare\n");
w->state = OdinDocWriterState_Preparing;
string_map_init(&w->string_cache);
@@ -59,6 +61,7 @@ gb_internal void odin_doc_writer_prepare(OdinDocWriter *w) {
map_init(&w->pkg_cache);
map_init(&w->entity_cache);
map_init(&w->type_cache);
+ map_init(&w->stable_type_cache);
odin_doc_writer_item_tracker_init(&w->files, 1);
odin_doc_writer_item_tracker_init(&w->pkgs, 1);
@@ -70,6 +73,7 @@ gb_internal void odin_doc_writer_prepare(OdinDocWriter *w) {
gb_internal void odin_doc_writer_destroy(OdinDocWriter *w) {
+ debugf("odin_doc_writer_destroy\n");
gb_free(heap_allocator(), w->data);
string_map_destroy(&w->string_cache);
@@ -77,6 +81,7 @@ gb_internal void odin_doc_writer_destroy(OdinDocWriter *w) {
map_destroy(&w->pkg_cache);
map_destroy(&w->entity_cache);
map_destroy(&w->type_cache);
+ map_destroy(&w->stable_type_cache);
}
@@ -102,6 +107,7 @@ gb_internal isize odin_doc_writer_calc_total_size(OdinDocWriter *w) {
}
gb_internal void odin_doc_writer_start_writing(OdinDocWriter *w) {
+ debugf("odin_doc_writer_start_writing\n");
w->state = OdinDocWriterState_Writing;
string_map_clear(&w->string_cache);
@@ -138,6 +144,7 @@ gb_internal void odin_doc_writer_assign_tracker(OdinDocArray<T> *array, OdinDocW
gb_internal void odin_doc_writer_end_writing(OdinDocWriter *w) {
+ debugf("odin_doc_writer_end_writing\n");
OdinDocHeader *h = w->header;
gb_memmove(h->base.magic, OdinDocHeader_MagicString, gb_strlen(OdinDocHeader_MagicString));
@@ -471,22 +478,60 @@ gb_internal OdinDocArray<OdinDocEntityIndex> odin_doc_add_entity_as_slice(OdinDo
return odin_write_item_as_slice(w, index);
}
+
+
gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
if (type == nullptr) {
return 0;
}
+
+ // Type **mapped_type = map_get(&w->stable_type_cache, type); // may map to itself
+ // if (mapped_type && *mapped_type) {
+ // type = *mapped_type;
+ // }
+
OdinDocTypeIndex *found = map_get(&w->type_cache, type);
if (found) {
return *found;
}
for (auto const &entry : w->type_cache) {
// NOTE(bill): THIS IS SLOW
- Type *other = entry.key;
- if (are_types_identical_unique_tuples(type, other)) {
- OdinDocTypeIndex index = entry.value;
- map_set(&w->type_cache, type, index);
- return index;
+ Type *x = type;
+ Type *y = entry.key;
+
+ if (x == y) {
+ goto do_set;
+ }
+
+ if (!x | !y) {
+ continue;
+ }
+
+ if (x->kind == Type_Named) {
+ Entity *e = x->Named.type_name;
+ if (e->TypeName.is_type_alias) {
+ x = x->Named.base;
+ }
+ }
+ if (y->kind == Type_Named) {
+ Entity *e = y->Named.type_name;
+ if (e->TypeName.is_type_alias) {
+ y = y->Named.base;
+ }
}
+ if (x->kind != y->kind) {
+ continue;
+ }
+
+ if (!are_types_identical_internal(x, y, true)) {
+ continue;
+ }
+
+ do_set:
+ OdinDocTypeIndex index = entry.value;
+ map_set(&w->type_cache, type, index);
+ map_set(&w->stable_type_cache, type, entry.key);
+ return index;
}
@@ -495,6 +540,7 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
OdinDocTypeIndex type_index = 0;
type_index = odin_doc_write_item(w, &w->types, &doc_type, &dst);
map_set(&w->type_cache, type, type_index);
+ map_set(&w->stable_type_cache, type, type);
switch (type->kind) {
case Type_Basic:
@@ -856,13 +902,12 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e)
break;
}
- if (e->flags & EntityFlag_Param) {
- if (e->flags & EntityFlag_Using) { flags |= OdinDocEntityFlag_Param_Using; }
- if (e->flags & EntityFlag_ConstInput) { flags |= OdinDocEntityFlag_Param_Const; }
- if (e->flags & EntityFlag_Ellipsis) { flags |= OdinDocEntityFlag_Param_Ellipsis; }
- if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
- if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
- }
+ if (e->flags & EntityFlag_Using) { flags |= OdinDocEntityFlag_Param_Using; }
+ if (e->flags & EntityFlag_ConstInput) { flags |= OdinDocEntityFlag_Param_Const; }
+ if (e->flags & EntityFlag_Ellipsis) { flags |= OdinDocEntityFlag_Param_Ellipsis; }
+ if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
+ if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
+
if (e->scope && (e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) && !is_entity_exported(e)) {
flags |= OdinDocEntityFlag_Private;
}
@@ -910,6 +955,8 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e)
}
gb_internal void odin_doc_update_entities(OdinDocWriter *w) {
+ debugf("odin_doc_update_entities %s\n", w->state ? "preparing" : "writing");
+
{
// NOTE(bill): Double pass, just in case entities are created on odin_doc_type
auto entities = array_make<Entity *>(heap_allocator(), 0, w->entity_cache.count);
@@ -974,6 +1021,8 @@ gb_internal OdinDocArray<OdinDocScopeEntry> odin_doc_add_pkg_entries(OdinDocWrit
return {};
}
+ debugf("odin_doc_add_pkg_entries %s -> package %.*s\n", w->state ? "preparing" : "writing", LIT(pkg->name));
+
auto entries = array_make<OdinDocScopeEntry>(heap_allocator(), 0, w->entity_cache.count);
defer (array_free(&entries));
@@ -1017,6 +1066,8 @@ gb_internal OdinDocArray<OdinDocScopeEntry> odin_doc_add_pkg_entries(OdinDocWrit
gb_internal void odin_doc_write_docs(OdinDocWriter *w) {
+ debugf("odin_doc_write_docs %s", w->state ? "preparing" : "writing");
+
auto pkgs = array_make<AstPackage *>(heap_allocator(), 0, w->info->packages.count);
defer (array_free(&pkgs));
for (auto const &entry : w->info->packages) {
@@ -1032,6 +1083,7 @@ gb_internal void odin_doc_write_docs(OdinDocWriter *w) {
}
}
+ debugf("odin_doc_update_entities sort pkgs %s\n", w->state ? "preparing" : "writing");
gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name);
for_array(i, pkgs) {
@@ -1092,6 +1144,7 @@ gb_internal void odin_doc_write_docs(OdinDocWriter *w) {
gb_internal void odin_doc_write_to_file(OdinDocWriter *w, char const *filename) {
+ debugf("odin_doc_write_to_file %s\n", filename);
gbFile f = {};
gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, filename);
if (err != gbFileError_None) {
@@ -1102,6 +1155,7 @@ gb_internal void odin_doc_write_to_file(OdinDocWriter *w, char const *filename)
defer (gb_file_close(&f));
if (gb_file_write(&f, w->data, w->data_len)) {
err = gb_file_truncate(&f, w->data_len);
+ debugf("Wrote .odin-doc file to: %s\n", filename);
gb_printf("Wrote .odin-doc file to: %s\n", filename);
}
}
@@ -1112,6 +1166,8 @@ gb_internal void odin_doc_write(CheckerInfo *info, char const *filename) {
defer (odin_doc_writer_destroy(w));
w->info = info;
+ debugf("odin_doc_write %s\n", filename);
+
odin_doc_writer_prepare(w);
odin_doc_write_docs(w);
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp
index 6308d7bbf..79edbe83e 100644
--- a/src/llvm_abi.cpp
+++ b/src/llvm_abi.cpp
@@ -1179,7 +1179,11 @@ namespace lbAbiArm64 {
if (is_register(type)) {
args[i] = non_struct(c, type);
} else if (is_homogenous_aggregate(c, type, &homo_base_type, &homo_member_count)) {
- args[i] = lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
+ if (is_homogenous_aggregate_small_enough(homo_base_type, homo_member_count)) {
+ args[i] = lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
+ } else {
+ args[i] = lb_arg_type_indirect(type, nullptr);;
+ }
} else {
i64 size = lb_sizeof(type);
if (size <= 16) {
@@ -1213,7 +1217,7 @@ namespace lbAbiWasm {
The approach taken optimizes for passing things in multiple
registers/arguments if possible rather than by pointer.
*/
- gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
+ gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention);
gb_internal LB_ABI_COMPUTE_RETURN_TYPE(compute_return_type);
enum {MAX_DIRECT_STRUCT_SIZE = 32};
@@ -1221,7 +1225,7 @@ namespace lbAbiWasm {
gb_internal LB_ABI_INFO(abi_info) {
lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
ft->ctx = c;
- ft->args = compute_arg_types(c, arg_types, arg_count);
+ ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention);
ft->ret = compute_return_type(ft, c, return_type, return_is_defined, return_is_tuple);
ft->calling_convention = calling_convention;
return ft;
@@ -1258,13 +1262,26 @@ namespace lbAbiWasm {
return false;
}
- gb_internal bool type_can_be_direct(LLVMTypeRef type) {
+ gb_internal bool type_can_be_direct(LLVMTypeRef type, ProcCallingConvention calling_convention) {
LLVMTypeKind kind = LLVMGetTypeKind(type);
i64 sz = lb_sizeof(type);
if (sz == 0) {
return false;
}
- if (sz <= MAX_DIRECT_STRUCT_SIZE) {
+ if (calling_convention == ProcCC_CDecl) {
+ // WASM Basic C ABI:
+ // https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md#function-signatures
+ if (kind == LLVMArrayTypeKind) {
+ return false;
+ } else if (kind == LLVMStructTypeKind) {
+ unsigned count = LLVMCountStructElementTypes(type);
+ if (count == 1) {
+ return type_can_be_direct(LLVMStructGetTypeAtIndex(type, 0), calling_convention);
+ }
+ } else if (is_basic_register_type(type)) {
+ return true;
+ }
+ } else if (sz <= MAX_DIRECT_STRUCT_SIZE) {
if (kind == LLVMArrayTypeKind) {
if (is_basic_register_type(OdinLLVMGetArrayElementType(type))) {
return true;
@@ -1284,7 +1301,7 @@ namespace lbAbiWasm {
return false;
}
- gb_internal lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type) {
+ gb_internal lbArgType is_struct(LLVMContextRef c, LLVMTypeRef type, ProcCallingConvention calling_convention) {
LLVMTypeKind kind = LLVMGetTypeKind(type);
GB_ASSERT(kind == LLVMArrayTypeKind || kind == LLVMStructTypeKind);
@@ -1292,21 +1309,21 @@ namespace lbAbiWasm {
if (sz == 0) {
return lb_arg_type_ignore(type);
}
- if (type_can_be_direct(type)) {
+ if (type_can_be_direct(type, calling_convention)) {
return lb_arg_type_direct(type);
}
return lb_arg_type_indirect(type, nullptr);
}
- gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
+ gb_internal Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention) {
auto args = array_make<lbArgType>(lb_function_type_args_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
LLVMTypeRef t = arg_types[i];
LLVMTypeKind kind = LLVMGetTypeKind(t);
if (kind == LLVMStructTypeKind || kind == LLVMArrayTypeKind) {
- args[i] = is_struct(c, t);
+ args[i] = is_struct(c, t, calling_convention);
} else {
args[i] = non_struct(c, t, false);
}
@@ -1318,7 +1335,7 @@ namespace lbAbiWasm {
if (!return_is_defined) {
return lb_arg_type_direct(LLVMVoidTypeInContext(c));
} else if (lb_is_type_kind(return_type, LLVMStructTypeKind) || lb_is_type_kind(return_type, LLVMArrayTypeKind)) {
- if (type_can_be_direct(return_type)) {
+ if (type_can_be_direct(return_type, ft->calling_convention)) {
return lb_arg_type_direct(return_type);
}
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index 62fa52490..34a401c33 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -720,6 +720,7 @@ gb_internal lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) {
lbBlock *check_grow_block = lb_create_block(p, "check-grow");
lbBlock *grow_fail_block = lb_create_block(p, "grow-fail");
lbBlock *insert_block = lb_create_block(p, "insert");
+ lbBlock *rehash_block = lb_create_block(p, "rehash");
lb_emit_if(p, lb_emit_comp_against_nil(p, Token_NotEq, found_ptr), found_block, check_grow_block);
lb_start_block(p, found_block);
@@ -737,12 +738,19 @@ gb_internal lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) {
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
args[1] = map_info;
args[2] = lb_emit_load(p, location_ptr);
- lbValue grow_err = lb_emit_runtime_call(p, "__dynamic_map_check_grow", args);
+ lbValue grow_err_and_has_grown = lb_emit_runtime_call(p, "__dynamic_map_check_grow", args);
+ lbValue grow_err = lb_emit_struct_ev(p, grow_err_and_has_grown, 0);
+ lbValue has_grown = lb_emit_struct_ev(p, grow_err_and_has_grown, 1);
lb_emit_if(p, lb_emit_comp_against_nil(p, Token_NotEq, grow_err), grow_fail_block, insert_block);
lb_start_block(p, grow_fail_block);
LLVMBuildRet(p->builder, LLVMConstNull(lb_type(m, t_rawptr)));
+
+ lb_emit_if(p, has_grown, grow_fail_block, rehash_block);
+ lb_start_block(p, rehash_block);
+ lbValue key = lb_emit_load(p, key_ptr);
+ hash = lb_gen_map_key_hash(p, map_ptr, key, nullptr);
}
lb_start_block(p, insert_block);
@@ -916,7 +924,7 @@ gb_internal lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) {
return hashed_key;
}
-gb_internal lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_) {
+gb_internal lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue const &map_ptr, lbValue key, lbValue *key_ptr_) {
TEMPORARY_ALLOCATOR_GUARD();
lbValue key_ptr = lb_address_from_load_or_generate_local(p, key);
@@ -924,13 +932,22 @@ gb_internal lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_t
if (key_ptr_) *key_ptr_ = key_ptr;
+ Type* key_type = base_type(type_deref(map_ptr.type))->Map.key;
+
lbValue hashed_key = lb_const_hash(p->module, key, key_type);
if (hashed_key.value == nullptr) {
lbValue hasher = lb_hasher_proc_for_type(p->module, key_type);
+ lbValue seed = {};
+ {
+ auto args = array_make<lbValue>(temporary_allocator(), 1);
+ args[0] = lb_map_data_uintptr(p, lb_emit_load(p, map_ptr));
+ seed = lb_emit_runtime_call(p, "map_seed_from_map_data", args);
+ }
+
auto args = array_make<lbValue>(temporary_allocator(), 2);
args[0] = key_ptr;
- args[1] = lb_const_int(p->module, t_uintptr, 0);
+ args[1] = seed;
hashed_key = lb_emit_call(p, hasher, args);
}
@@ -945,7 +962,7 @@ gb_internal lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue cons
lbValue ptr = {};
lbValue key_ptr = {};
- lbValue hash = lb_gen_map_key_hash(p, key, map_type->Map.key, &key_ptr);
+ lbValue hash = lb_gen_map_key_hash(p, map_ptr, key, &key_ptr);
if (build_context.dynamic_map_calls) {
auto args = array_make<lbValue>(temporary_allocator(), 4);
@@ -976,7 +993,7 @@ gb_internal void lb_internal_dynamic_map_set(lbProcedure *p, lbValue const &map_
GB_ASSERT(map_type->kind == Type_Map);
lbValue key_ptr = {};
- lbValue hash = lb_gen_map_key_hash(p, map_key, map_type->Map.key, &key_ptr);
+ lbValue hash = lb_gen_map_key_hash(p, map_ptr, map_key, &key_ptr);
lbValue v = lb_emit_conv(p, map_value, map_type->Map.value);
lbValue value_ptr = lb_address_from_load_or_generate_local(p, v);
@@ -1129,12 +1146,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
lbValue init = lb_build_expr(p, init_expr);
if (init.value == nullptr) {
LLVMTypeRef global_type = llvm_addr_type(p->module, var.var);
- if (is_type_untyped_undef(init.type)) {
- // LLVMSetInitializer(var.var.value, LLVMGetUndef(global_type));
- LLVMSetInitializer(var.var.value, LLVMConstNull(global_type));
- var.is_initialized = true;
- continue;
- } else if (is_type_untyped_nil(init.type)) {
+ if (is_type_untyped_nil(init.type)) {
LLVMSetInitializer(var.var.value, LLVMConstNull(global_type));
var.is_initialized = true;
continue;
@@ -1365,7 +1377,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_emit_worker_proc) {
gb_internal void lb_llvm_function_pass_per_function_internal(lbModule *module, lbProcedure *p, lbFunctionPassManagerKind pass_manager_kind = lbFunctionPassManager_default) {
LLVMPassManagerRef pass_manager = module->function_pass_managers[pass_manager_kind];
- lb_run_function_pass_manager(pass_manager, p);
+ lb_run_function_pass_manager(pass_manager, p, pass_manager_kind);
}
gb_internal WORKER_TASK_PROC(lb_llvm_function_pass_per_module) {
@@ -1899,7 +1911,7 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
}
- lb_run_function_pass_manager(default_function_pass_manager, p);
+ lb_run_function_pass_manager(default_function_pass_manager, p, lbFunctionPassManager_default);
return p;
}
@@ -2363,8 +2375,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
}
}
}
- if (!var.is_initialized &&
- (is_type_untyped_nil(tav.type) || is_type_untyped_undef(tav.type))) {
+ if (!var.is_initialized && is_type_untyped_nil(tav.type)) {
var.is_initialized = true;
}
}
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index c626bb9a5..4c4d9703d 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -477,7 +477,7 @@ gb_internal String lb_get_const_string(lbModule *m, lbValue value);
gb_internal lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool zero_init=true);
gb_internal lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id);
-gb_internal lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_);
+gb_internal lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue const &map_ptr, lbValue key, lbValue *key_ptr_);
gb_internal lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type);
gb_internal lbValue lb_gen_map_info_ptr(lbModule *m, Type *map_type);
diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp
index 8db6e2a1f..8149b1eda 100644
--- a/src/llvm_backend_const.cpp
+++ b/src/llvm_backend_const.cpp
@@ -1,6 +1,6 @@
gb_internal bool lb_is_const(lbValue value) {
LLVMValueRef v = value.value;
- if (is_type_untyped_nil(value.type) || is_type_untyped_undef(value.type)) {
+ if (is_type_untyped_nil(value.type)) {
// TODO(bill): Is this correct behaviour?
return true;
}
@@ -107,7 +107,11 @@ gb_internal LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
case LLVMPointerTypeKind:
return LLVMConstPointerCast(val, dst);
case LLVMStructTypeKind:
- return LLVMConstBitCast(val, dst);
+ // GB_PANIC("%s -> %s", LLVMPrintValueToString(val), LLVMPrintTypeToString(dst));
+ // NOTE(bill): It's not possible to do a bit cast on a struct, why was this code even here in the first place?
+ // It seems mostly to exist to get around the "anonymous -> named" struct assignments
+ // return LLVMConstBitCast(val, dst);
+ return val;
default:
GB_PANIC("Unhandled const cast %s to %s", LLVMPrintTypeToString(src), LLVMPrintTypeToString(dst));
}
@@ -1036,9 +1040,10 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
cv_type = cvt->Struct.fields[index]->type;
- if (is_type_struct(cv_type)) {
- auto cv_field_remapping = lb_get_struct_remapping(m, cv_type);
- idx_list[j-1] = cast(unsigned)cv_field_remapping[index];
+ if (is_type_struct(cvt)) {
+ auto cv_field_remapping = lb_get_struct_remapping(m, cvt);
+ unsigned remapped_index = cast(unsigned)cv_field_remapping[index];
+ idx_list[j-1] = remapped_index;
} else {
idx_list[j-1] = cast(unsigned)index;
}
diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp
index c39039361..b9c6c606e 100644
--- a/src/llvm_backend_debug.cpp
+++ b/src/llvm_backend_debug.cpp
@@ -283,7 +283,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
case Basic_UntypedString: GB_PANIC("Basic_UntypedString"); break;
case Basic_UntypedRune: GB_PANIC("Basic_UntypedRune"); break;
case Basic_UntypedNil: GB_PANIC("Basic_UntypedNil"); break;
- case Basic_UntypedUndef: GB_PANIC("Basic_UntypedUndef"); break;
+ case Basic_UntypedUninit: GB_PANIC("Basic_UntypedUninit"); break;
default: GB_PANIC("Basic Unhandled"); break;
}
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp
index 412698368..b2adc254d 100644
--- a/src/llvm_backend_expr.cpp
+++ b/src/llvm_backend_expr.cpp
@@ -1,20 +1,18 @@
gb_internal lbValue lb_emit_arith_matrix(lbProcedure *p, TokenKind op, lbValue lhs, lbValue rhs, Type *type, bool component_wise);
-gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *type) {
+gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, Ast *left, Ast *right, Type *final_type) {
lbModule *m = p->module;
lbBlock *rhs = lb_create_block(p, "logical.cmp.rhs");
lbBlock *done = lb_create_block(p, "logical.cmp.done");
- type = default_type(type);
-
lbValue short_circuit = {};
if (op == Token_CmpAnd) {
lb_build_cond(p, left, rhs, done);
- short_circuit = lb_const_bool(m, type, false);
+ short_circuit = lb_const_bool(m, t_llvm_bool, false);
} else if (op == Token_CmpOr) {
lb_build_cond(p, left, done, rhs);
- short_circuit = lb_const_bool(m, type, true);
+ short_circuit = lb_const_bool(m, t_llvm_bool, true);
}
if (rhs->preds.count == 0) {
@@ -25,7 +23,7 @@ gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, As
if (done->preds.count == 0) {
lb_start_block(p, rhs);
if (lb_is_expr_untyped_const(right)) {
- return lb_expr_untyped_const_to_typed(m, right, type);
+ return lb_expr_untyped_const_to_typed(m, right, default_type(final_type));
}
return lb_build_expr(p, right);
}
@@ -43,10 +41,11 @@ gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, As
lb_start_block(p, rhs);
lbValue edge = {};
if (lb_is_expr_untyped_const(right)) {
- edge = lb_expr_untyped_const_to_typed(m, right, type);
+ edge = lb_expr_untyped_const_to_typed(m, right, t_llvm_bool);
} else {
- edge = lb_build_expr(p, right);
+ edge = lb_emit_conv(p, lb_build_expr(p, right), t_llvm_bool);
}
+ GB_ASSERT(edge.type == t_llvm_bool);
incoming_values[done->preds.count] = edge.value;
incoming_blocks[done->preds.count] = p->curr_block->block;
@@ -54,7 +53,7 @@ gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, As
lb_emit_jump(p, done);
lb_start_block(p, done);
- LLVMTypeRef dst_type = lb_type(m, type);
+ LLVMTypeRef dst_type = lb_type(m, t_llvm_bool);
LLVMValueRef phi = nullptr;
GB_ASSERT(incoming_values.count == incoming_blocks.count);
@@ -67,48 +66,36 @@ gb_internal lbValue lb_emit_logical_binary_expr(lbProcedure *p, TokenKind op, As
break;
}
}
+
+ lbValue res = {};
if (phi_type == nullptr) {
phi = LLVMBuildPhi(p->builder, dst_type, "");
LLVMAddIncoming(phi, incoming_values.data, incoming_blocks.data, cast(unsigned)incoming_values.count);
- lbValue res = {};
- res.type = type;
res.value = phi;
- return res;
- }
-
- for_array(i, incoming_values) {
- LLVMValueRef incoming_value = incoming_values[i];
- LLVMTypeRef incoming_type = LLVMTypeOf(incoming_value);
-
- if (phi_type != incoming_type) {
- GB_ASSERT_MSG(LLVMIsConstant(incoming_value), "%s vs %s", LLVMPrintTypeToString(phi_type), LLVMPrintTypeToString(incoming_type));
- bool ok = !!LLVMConstIntGetZExtValue(incoming_value);
- incoming_values[i] = LLVMConstInt(phi_type, ok, false);
+ res.type = t_llvm_bool;
+ } else {
+ for_array(i, incoming_values) {
+ LLVMValueRef incoming_value = incoming_values[i];
+ LLVMTypeRef incoming_type = LLVMTypeOf(incoming_value);
+
+ if (phi_type != incoming_type) {
+ GB_ASSERT_MSG(LLVMIsConstant(incoming_value), "%s vs %s", LLVMPrintTypeToString(phi_type), LLVMPrintTypeToString(incoming_type));
+ bool ok = !!LLVMConstIntGetZExtValue(incoming_value);
+ incoming_values[i] = LLVMConstInt(phi_type, ok, false);
+ }
+
}
-
+
+ // NOTE(bill): this now only uses i1 for the logic to prevent issues with corrupted booleans which are not of value 0 or 1 (e.g. 2)
+ // Doing this may produce slightly worse code as a result but it will be correct behaviour
+
+ phi = LLVMBuildPhi(p->builder, phi_type, "");
+ LLVMAddIncoming(phi, incoming_values.data, incoming_blocks.data, cast(unsigned)incoming_values.count);
+ res.value = phi;
+ res.type = t_llvm_bool;
}
-
- phi = LLVMBuildPhi(p->builder, phi_type, "");
- LLVMAddIncoming(phi, incoming_values.data, incoming_blocks.data, cast(unsigned)incoming_values.count);
-
- LLVMTypeRef i1 = LLVMInt1TypeInContext(m->ctx);
- if ((phi_type == i1) ^ (dst_type == i1)) {
- if (phi_type == i1) {
- phi = LLVMBuildZExt(p->builder, phi, dst_type, "");
- } else {
- phi = LLVMBuildTruncOrBitCast(p->builder, phi, dst_type, "");
- }
- } else if (lb_sizeof(phi_type) < lb_sizeof(dst_type)) {
- phi = LLVMBuildZExt(p->builder, phi, dst_type, "");
- } else {
- phi = LLVMBuildTruncOrBitCast(p->builder, phi, dst_type, "");
- }
-
- lbValue res = {};
- res.type = type;
- res.value = phi;
- return res;
+ return lb_emit_conv(p, res, default_type(final_type));
}
@@ -1499,12 +1486,12 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
GB_ASSERT(src != nullptr);
GB_ASSERT(dst != nullptr);
+ if (is_type_untyped_uninit(src)) {
+ return lb_const_undef(m, t);
+ }
if (is_type_untyped_nil(src)) {
return lb_const_nil(m, t);
}
- if (is_type_untyped_undef(src)) {
- return lb_const_undef(m, t);
- }
if (LLVMIsConstant(value.value)) {
if (is_type_any(dst)) {
@@ -1566,7 +1553,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
// bool <-> llvm bool
if (is_type_boolean(src) && dst == t_llvm_bool) {
lbValue res = {};
- res.value = LLVMBuildTrunc(p->builder, value.value, lb_type(m, dst), "");
+ res.value = LLVMBuildICmp(p->builder, LLVMIntNE, value.value, LLVMConstNull(lb_type(m, src)), "");
res.type = t;
return res;
}
@@ -2145,12 +2132,12 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
if (is_type_any(dst)) {
+ if (is_type_untyped_uninit(src)) {
+ return lb_const_undef(p->module, t);
+ }
if (is_type_untyped_nil(src)) {
return lb_const_nil(p->module, t);
}
- if (is_type_untyped_undef(src)) {
- return lb_const_undef(p->module, t);
- }
lbAddr result = lb_add_local_generated(p, t, true);
@@ -3149,11 +3136,11 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
return lb_addr_load(p, lb_build_addr(p, expr));
case_end;
- case_ast_node(u, Undef, expr)
+ case_ast_node(u, Uninit, expr)
lbValue res = {};
if (is_type_untyped(type)) {
res.value = nullptr;
- res.type = t_untyped_undef;
+ res.type = t_untyped_uninit;
} else {
res.value = LLVMGetUndef(lb_type(m, type));
res.type = type;
@@ -3783,6 +3770,7 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
multi_ptr = lb_emit_load(p, multi_ptr);
}
lbValue index = lb_build_expr(p, ie->index);
+ index = lb_emit_conv(p, index, t_int);
lbValue v = {};
LLVMValueRef indices[1] = {index.value};
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index 3d38416e0..5cb339eb7 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -677,7 +677,7 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
return;
}
GB_ASSERT(value.type != nullptr);
- if (is_type_untyped_undef(value.type)) {
+ if (is_type_untyped_uninit(value.type)) {
Type *t = lb_addr_type(addr);
value.type = t;
value.value = LLVMGetUndef(lb_type(p->module, t));
@@ -1830,7 +1830,7 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Basic_UntypedString: GB_PANIC("Basic_UntypedString"); break;
case Basic_UntypedRune: GB_PANIC("Basic_UntypedRune"); break;
case Basic_UntypedNil: GB_PANIC("Basic_UntypedNil"); break;
- case Basic_UntypedUndef: GB_PANIC("Basic_UntypedUndef"); break;
+ case Basic_UntypedUninit: GB_PANIC("Basic_UntypedUninit"); break;
}
break;
case Type_Named:
diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp
index 61f51b15b..141ee88c7 100644
--- a/src/llvm_backend_opt.cpp
+++ b/src/llvm_backend_opt.cpp
@@ -370,11 +370,21 @@ gb_internal void lb_run_remove_dead_instruction_pass(lbProcedure *p) {
}
-gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p) {
+gb_internal void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p, lbFunctionPassManagerKind pass_manager_kind) {
if (p == nullptr) {
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 651ebf35c..ddae64ef0 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -3218,10 +3218,10 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
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);
- } else if (is_type_untyped_undef(args[i].type)) {
- args[i] = lb_const_undef(m, e->type);
}
}
@@ -3409,10 +3409,10 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
Entity *e = param_tuple->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);
- } else if (is_type_untyped_undef(args[i].type)) {
- args[i] = lb_const_undef(m, e->type);
}
}
}
diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp
index 00e02092d..35fd2b7de 100644
--- a/src/llvm_backend_stmt.cpp
+++ b/src/llvm_backend_stmt.cpp
@@ -249,7 +249,8 @@ gb_internal void lb_build_when_stmt(lbProcedure *p, AstWhenStmt *ws) {
gb_internal void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_type, lbValue count_ptr,
- lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
+ lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_,
+ bool is_reverse) {
lbModule *m = p->module;
lbValue count = {};
@@ -266,25 +267,78 @@ gb_internal void lb_build_range_indexed(lbProcedure *p, lbValue expr, Type *val_
lbBlock *done = nullptr;
lbBlock *body = nullptr;
+ loop = lb_create_block(p, "for.index.loop");
+ body = lb_create_block(p, "for.index.body");
+ done = lb_create_block(p, "for.index.done");
lbAddr index = lb_add_local_generated(p, t_int, false);
- lb_addr_store(p, index, lb_const_int(m, t_int, cast(u64)-1));
- loop = lb_create_block(p, "for.index.loop");
- lb_emit_jump(p, loop);
- lb_start_block(p, loop);
+ if (!is_reverse) {
+ /*
+ for x, i in array {
+ ...
+ }
- lbValue incr = lb_emit_arith(p, Token_Add, lb_addr_load(p, index), lb_const_int(m, t_int, 1), t_int);
- lb_addr_store(p, index, incr);
+ i := -1
+ for {
+ i += 1
+ if !(i < len(array)) {
+ break
+ }
+ #no_bounds_check x := array[i]
+ ...
+ }
+ */
- body = lb_create_block(p, "for.index.body");
- done = lb_create_block(p, "for.index.done");
- if (count.value == nullptr) {
- GB_ASSERT(count_ptr.value != nullptr);
- count = lb_emit_load(p, count_ptr);
+ lb_addr_store(p, index, lb_const_int(m, t_int, cast(u64)-1));
+
+ lb_emit_jump(p, loop);
+ lb_start_block(p, loop);
+
+ lbValue incr = lb_emit_arith(p, Token_Add, lb_addr_load(p, index), lb_const_int(m, t_int, 1), t_int);
+ lb_addr_store(p, index, incr);
+
+ if (count.value == nullptr) {
+ GB_ASSERT(count_ptr.value != nullptr);
+ count = lb_emit_load(p, count_ptr);
+ }
+ lbValue cond = lb_emit_comp(p, Token_Lt, incr, count);
+ lb_emit_if(p, cond, body, done);
+ } else {
+ // NOTE(bill): REVERSED LOGIC
+ /*
+ #reverse for x, i in array {
+ ...
+ }
+
+ i := len(array)
+ for {
+ i -= 1
+ if i < 0 {
+ break
+ }
+ #no_bounds_check x := array[i]
+ ...
+ }
+ */
+
+ if (count.value == nullptr) {
+ GB_ASSERT(count_ptr.value != nullptr);
+ count = lb_emit_load(p, count_ptr);
+ }
+ count = lb_emit_conv(p, count, t_int);
+ lb_addr_store(p, index, count);
+
+ lb_emit_jump(p, loop);
+ lb_start_block(p, loop);
+
+ lbValue incr = lb_emit_arith(p, Token_Sub, lb_addr_load(p, index), lb_const_int(m, t_int, 1), t_int);
+ lb_addr_store(p, index, incr);
+
+ lbValue anti_cond = lb_emit_comp(p, Token_Lt, incr, lb_const_int(m, t_int, 0));
+ lb_emit_if(p, anti_cond, done, body);
}
- lbValue cond = lb_emit_comp(p, Token_Lt, incr, count);
- lb_emit_if(p, cond, body, done);
+
lb_start_block(p, body);
idx = lb_addr_load(p, index);
@@ -452,7 +506,8 @@ gb_internal void lb_build_range_map(lbProcedure *p, lbValue expr, Type *val_type
gb_internal void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_type,
- lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_) {
+ lbValue *val_, lbValue *idx_, lbBlock **loop_, lbBlock **done_,
+ bool is_reverse) {
lbModule *m = p->module;
lbValue count = lb_const_int(m, t_int, 0);
Type *expr_type = base_type(expr.type);
@@ -471,35 +526,88 @@ gb_internal void lb_build_range_string(lbProcedure *p, lbValue expr, Type *val_t
lbBlock *done = nullptr;
lbBlock *body = nullptr;
+ loop = lb_create_block(p, "for.string.loop");
+ body = lb_create_block(p, "for.string.body");
+ done = lb_create_block(p, "for.string.done");
lbAddr offset_ = lb_add_local_generated(p, t_int, false);
- lb_addr_store(p, offset_, lb_const_int(m, t_int, 0));
+ lbValue offset = {};
+ lbValue cond = {};
- loop = lb_create_block(p, "for.string.loop");
- lb_emit_jump(p, loop);
- lb_start_block(p, loop);
+ if (!is_reverse) {
+ /*
+ for c, offset in str {
+ ...
+ }
+
+ offset := 0
+ for offset < len(str) {
+ c, _w := string_decode_rune(str[offset:])
+ ...
+ offset += _w
+ }
+ */
+ lb_addr_store(p, offset_, lb_const_int(m, t_int, 0));
+ lb_emit_jump(p, loop);
+ lb_start_block(p, loop);
- body = lb_create_block(p, "for.string.body");
- done = lb_create_block(p, "for.string.done");
+ offset = lb_addr_load(p, offset_);
+ cond = lb_emit_comp(p, Token_Lt, offset, count);
+ } else {
+ // NOTE(bill): REVERSED LOGIC
+ /*
+ #reverse for c, offset in str {
+ ...
+ }
- lbValue offset = lb_addr_load(p, offset_);
- lbValue cond = lb_emit_comp(p, Token_Lt, offset, count);
+ offset := len(str)
+ for offset > 0 {
+ c, _w := string_decode_last_rune(str[:offset])
+ offset -= _w
+ ...
+ }
+ */
+ lb_addr_store(p, offset_, count);
+
+ lb_emit_jump(p, loop);
+ lb_start_block(p, loop);
+
+ offset = lb_addr_load(p, offset_);
+ cond = lb_emit_comp(p, Token_Gt, offset, lb_const_int(m, t_int, 0));
+ }
lb_emit_if(p, cond, body, done);
lb_start_block(p, body);
- lbValue str_elem = lb_emit_ptr_offset(p, lb_string_elem(p, expr), offset);
- lbValue str_len = lb_emit_arith(p, Token_Sub, count, offset, t_int);
- auto args = array_make<lbValue>(permanent_allocator(), 1);
- args[0] = lb_emit_string(p, str_elem, str_len);
- lbValue rune_and_len = lb_emit_runtime_call(p, "string_decode_rune", args);
- lbValue len = lb_emit_struct_ev(p, rune_and_len, 1);
- lb_addr_store(p, offset_, lb_emit_arith(p, Token_Add, offset, len, t_int));
+ lbValue rune_and_len = {};
+ if (!is_reverse) {
+ lbValue str_elem = lb_emit_ptr_offset(p, lb_string_elem(p, expr), offset);
+ lbValue str_len = lb_emit_arith(p, Token_Sub, count, offset, t_int);
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
+ args[0] = lb_emit_string(p, str_elem, str_len);
+
+ rune_and_len = lb_emit_runtime_call(p, "string_decode_rune", args);
+ lbValue len = lb_emit_struct_ev(p, rune_and_len, 1);
+ lb_addr_store(p, offset_, lb_emit_arith(p, Token_Add, offset, len, t_int));
+
+ idx = offset;
+ } else {
+ // NOTE(bill): REVERSED LOGIC
+ lbValue str_elem = lb_string_elem(p, expr);
+ lbValue str_len = offset;
+ auto args = array_make<lbValue>(permanent_allocator(), 1);
+ args[0] = lb_emit_string(p, str_elem, str_len);
+
+ rune_and_len = lb_emit_runtime_call(p, "string_decode_last_rune", args);
+ lbValue len = lb_emit_struct_ev(p, rune_and_len, 1);
+ lb_addr_store(p, offset_, lb_emit_arith(p, Token_Sub, offset, len, t_int));
+
+ idx = lb_addr_load(p, offset_);
+ }
- idx = offset;
if (val_type != nullptr) {
val = lb_emit_struct_ev(p, rune_and_len, 0);
}
@@ -702,6 +810,8 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
lbBlock *body = nullptr;
lbBlock *done = nullptr;
+ bool is_reverse = rs->reverse;
+
lb_open_scope(p, scope);
@@ -723,20 +833,70 @@ gb_internal void lb_build_range_stmt_struct_soa(lbProcedure *p, AstRangeStmt *rs
lbAddr index = lb_add_local_generated(p, t_int, false);
- lb_addr_store(p, index, lb_const_int(p->module, t_int, cast(u64)-1));
- loop = lb_create_block(p, "for.soa.loop");
- lb_emit_jump(p, loop);
- lb_start_block(p, loop);
+ if (!is_reverse) {
+ /*
+ for x, i in array {
+ ...
+ }
- lbValue incr = lb_emit_arith(p, Token_Add, lb_addr_load(p, index), lb_const_int(p->module, t_int, 1), t_int);
- lb_addr_store(p, index, incr);
+ i := -1
+ for {
+ i += 1
+ if !(i < len(array)) {
+ break
+ }
+ x := array[i] // but #soa-ified
+ ...
+ }
+ */
- body = lb_create_block(p, "for.soa.body");
- done = lb_create_block(p, "for.soa.done");
+ lb_addr_store(p, index, lb_const_int(p->module, t_int, cast(u64)-1));
- lbValue cond = lb_emit_comp(p, Token_Lt, incr, count);
- lb_emit_if(p, cond, body, done);
+ loop = lb_create_block(p, "for.soa.loop");
+ lb_emit_jump(p, loop);
+ lb_start_block(p, loop);
+
+ lbValue incr = lb_emit_arith(p, Token_Add, lb_addr_load(p, index), lb_const_int(p->module, t_int, 1), t_int);
+ lb_addr_store(p, index, incr);
+
+ body = lb_create_block(p, "for.soa.body");
+ done = lb_create_block(p, "for.soa.done");
+
+ lbValue cond = lb_emit_comp(p, Token_Lt, incr, count);
+ lb_emit_if(p, cond, body, done);
+ } else {
+ // NOTE(bill): REVERSED LOGIC
+ /*
+ #reverse for x, i in array {
+ ...
+ }
+
+ i := len(array)
+ for {
+ i -= 1
+ if i < 0 {
+ break
+ }
+ #no_bounds_check x := array[i] // but #soa-ified
+ ...
+ }
+ */
+ lb_addr_store(p, index, count);
+
+ loop = lb_create_block(p, "for.soa.loop");
+ lb_emit_jump(p, loop);
+ lb_start_block(p, loop);
+
+ lbValue incr = lb_emit_arith(p, Token_Sub, lb_addr_load(p, index), lb_const_int(p->module, t_int, 1), t_int);
+ lb_addr_store(p, index, incr);
+
+ body = lb_create_block(p, "for.soa.body");
+ done = lb_create_block(p, "for.soa.done");
+
+ lbValue cond = lb_emit_comp(p, Token_Lt, incr, lb_const_int(p->module, t_int, 0));
+ lb_emit_if(p, cond, done, body);
+ }
lb_start_block(p, body);
@@ -820,7 +980,7 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
}
lbAddr count_ptr = lb_add_local_generated(p, t_int, false);
lb_addr_store(p, count_ptr, lb_const_int(p->module, t_int, et->Array.count));
- lb_build_range_indexed(p, array, val0_type, count_ptr.addr, &val, &key, &loop, &done);
+ lb_build_range_indexed(p, array, val0_type, count_ptr.addr, &val, &key, &loop, &done, rs->reverse);
break;
}
case Type_EnumeratedArray: {
@@ -830,7 +990,7 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
}
lbAddr count_ptr = lb_add_local_generated(p, t_int, false);
lb_addr_store(p, count_ptr, lb_const_int(p->module, t_int, et->EnumeratedArray.count));
- lb_build_range_indexed(p, array, val0_type, count_ptr.addr, &val, &key, &loop, &done);
+ lb_build_range_indexed(p, array, val0_type, count_ptr.addr, &val, &key, &loop, &done, rs->reverse);
break;
}
case Type_DynamicArray: {
@@ -840,7 +1000,7 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
array = lb_emit_load(p, array);
}
count_ptr = lb_emit_struct_ep(p, array, 1);
- lb_build_range_indexed(p, array, val0_type, count_ptr, &val, &key, &loop, &done);
+ lb_build_range_indexed(p, array, val0_type, count_ptr, &val, &key, &loop, &done, rs->reverse);
break;
}
case Type_Slice: {
@@ -853,7 +1013,7 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
count_ptr = lb_add_local_generated(p, t_int, false).addr;
lb_emit_store(p, count_ptr, lb_slice_len(p, slice));
}
- lb_build_range_indexed(p, slice, val0_type, count_ptr, &val, &key, &loop, &done);
+ lb_build_range_indexed(p, slice, val0_type, count_ptr, &val, &key, &loop, &done, rs->reverse);
break;
}
case Type_Basic: {
@@ -868,7 +1028,7 @@ gb_internal void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *sc
}
Type *t = base_type(string.type);
GB_ASSERT(!is_type_cstring(t));
- lb_build_range_string(p, string, val0_type, &val, &key, &loop, &done);
+ lb_build_range_string(p, string, val0_type, &val, &key, &loop, &done, rs->reverse);
break;
}
case Type_Tuple:
diff --git a/src/parser.cpp b/src/parser.cpp
index 698ba99ab..c19e3f859 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -115,7 +115,7 @@ gb_internal Ast *clone_ast(Ast *node, AstFile *f) {
n->Ident.entity = nullptr;
break;
case Ast_Implicit: break;
- case Ast_Undef: break;
+ case Ast_Uninit: break;
case Ast_BasicLit: break;
case Ast_BasicDirective: break;
@@ -646,9 +646,9 @@ gb_internal Ast *ast_implicit(AstFile *f, Token token) {
result->Implicit = token;
return result;
}
-gb_internal Ast *ast_undef(AstFile *f, Token token) {
- Ast *result = alloc_ast_node(f, Ast_Undef);
- result->Undef = token;
+gb_internal Ast *ast_uninit(AstFile *f, Token token) {
+ Ast *result = alloc_ast_node(f, Ast_Uninit);
+ result->Uninit = token;
return result;
}
@@ -2092,8 +2092,8 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
case Token_Ident:
return parse_ident(f);
- case Token_Undef:
- return ast_undef(f, expect_token(f, Token_Undef));
+ case Token_Uninit:
+ return ast_uninit(f, expect_token(f, Token_Uninit));
case Token_context:
return ast_implicit(f, expect_token(f, Token_context));
@@ -2292,7 +2292,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
skip_possible_newline_for_literal(f);
- if (allow_token(f, Token_Undef)) {
+ if (allow_token(f, Token_Uninit)) {
if (where_token.kind != Token_Invalid) {
syntax_error(where_token, "'where' clauses are not allowed on procedure literals without a defined body (replaced with ---)");
}
@@ -3744,8 +3744,18 @@ gb_internal bool allow_field_separator(AstFile *f) {
if (allow_token(f, Token_Comma)) {
return true;
}
- if (ALLOW_NEWLINE && token.kind == Token_Semicolon) {
- if (!token_is_newline(token)) {
+ if (token.kind == Token_Semicolon) {
+ bool ok = false;
+ if (ALLOW_NEWLINE && token_is_newline(token)) {
+ TokenKind next = peek_token(f).kind;
+ switch (next) {
+ case Token_CloseBrace:
+ case Token_CloseParen:
+ ok = true;
+ break;
+ }
+ }
+ if (!ok) {
String p = token_to_string(token);
syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p));
}
@@ -4509,7 +4519,7 @@ gb_internal Ast *parse_foreign_decl(AstFile *f) {
return ast_bad_decl(f, token, f->curr_token);
}
-gb_internal Ast *parse_attribute(AstFile *f, Token token, TokenKind open_kind, TokenKind close_kind) {
+gb_internal Ast *parse_attribute(AstFile *f, Token token, TokenKind open_kind, TokenKind close_kind, CommentGroup *docs) {
Array<Ast *> elems = {};
Token open = {};
Token close = {};
@@ -4550,6 +4560,9 @@ gb_internal Ast *parse_attribute(AstFile *f, Token token, TokenKind open_kind, T
Ast *decl = parse_stmt(f);
if (decl->kind == Ast_ValueDecl) {
+ if (decl->ValueDecl.docs == nullptr && docs != nullptr) {
+ decl->ValueDecl.docs = docs;
+ }
array_add(&decl->ValueDecl.attributes, attribute);
} else if (decl->kind == Ast_ForeignBlockDecl) {
array_add(&decl->ForeignBlockDecl.attributes, attribute);
@@ -4698,8 +4711,9 @@ gb_internal Ast *parse_stmt(AstFile *f) {
} break;
case Token_At: {
+ CommentGroup *docs = f->lead_comment;
Token token = expect_token(f, Token_At);
- return parse_attribute(f, token, Token_OpenParen, Token_CloseParen);
+ return parse_attribute(f, token, Token_OpenParen, Token_CloseParen, docs);
}
case Token_Hash: {
@@ -4749,6 +4763,17 @@ gb_internal Ast *parse_stmt(AstFile *f) {
return stmt;
} else if (tag == "unroll") {
return parse_unrolled_for_loop(f, name);
+ } else if (tag == "reverse") {
+ Ast *for_stmt = parse_for_stmt(f);
+ if (for_stmt->kind == Ast_RangeStmt) {
+ if (for_stmt->RangeStmt.reverse) {
+ syntax_error(token, "#reverse already applied to a 'for in' statement");
+ }
+ for_stmt->RangeStmt.reverse = true;
+ } else {
+ syntax_error(token, "#reverse can only be applied to a 'for in' statement");
+ }
+ return for_stmt;
} else if (tag == "include") {
syntax_error(token, "#include is not a valid import declaration kind. Did you mean 'import'?");
s = ast_bad_stmt(f, token, f->curr_token);
diff --git a/src/parser.hpp b/src/parser.hpp
index 5302fd274..6ba4ef6d6 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -356,6 +356,15 @@ enum UnionTypeKind : u8 {
UnionType_Normal = 0,
UnionType_no_nil = 2,
UnionType_shared_nil = 3,
+
+ UnionType_COUNT
+};
+
+gb_global char const *union_type_kind_strings[UnionType_COUNT] = {
+ "(normal)",
+ "#maybe",
+ "#no_nil",
+ "#shared_nil",
};
#define AST_KINDS \
@@ -364,7 +373,7 @@ enum UnionTypeKind : u8 {
Entity *entity; \
}) \
AST_KIND(Implicit, "implicit", Token) \
- AST_KIND(Undef, "undef", Token) \
+ AST_KIND(Uninit, "uninitialized value", Token) \
AST_KIND(BasicLit, "basic literal", struct { \
Token token; \
}) \
@@ -520,6 +529,7 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
Token in_token; \
Ast *expr; \
Ast *body; \
+ bool reverse; \
}) \
AST_KIND(UnrollRangeStmt, "#unroll range statement", struct { \
Scope *scope; \
diff --git a/src/parser_pos.cpp b/src/parser_pos.cpp
index 1274f05a0..52d49e897 100644
--- a/src/parser_pos.cpp
+++ b/src/parser_pos.cpp
@@ -2,7 +2,7 @@ gb_internal Token ast_token(Ast *node) {
switch (node->kind) {
case Ast_Ident: return node->Ident.token;
case Ast_Implicit: return node->Implicit;
- case Ast_Undef: return node->Undef;
+ case Ast_Uninit: return node->Uninit;
case Ast_BasicLit: return node->BasicLit.token;
case Ast_BasicDirective: return node->BasicDirective.token;
case Ast_ProcGroup: return node->ProcGroup.token;
@@ -137,7 +137,7 @@ Token ast_end_token(Ast *node) {
return empty_token;
case Ast_Ident: return node->Ident.token;
case Ast_Implicit: return node->Implicit;
- case Ast_Undef: return node->Undef;
+ case Ast_Uninit: return node->Uninit;
case Ast_BasicLit: return node->BasicLit.token;
case Ast_BasicDirective: return node->BasicDirective.token;
case Ast_ProcGroup: return node->ProcGroup.close;
diff --git a/src/path.cpp b/src/path.cpp
index 49a2d4a4f..de80c9def 100644
--- a/src/path.cpp
+++ b/src/path.cpp
@@ -1,6 +1,10 @@
/*
Path handling utilities.
*/
+#if !defined(GB_SYSTEM_WINDOWS)
+#include <unistd.h>
+#endif
+
gb_internal String remove_extension_from_path(String const &s) {
if (s.len != 0 && s.text[s.len-1] == '.') {
return s;
@@ -25,6 +29,29 @@ gb_internal String remove_directory_from_path(String const &s) {
return substring(s, s.len-len, s.len);
}
+
+// NOTE(Mark Naughton): getcwd as String
+#if !defined(GB_SYSTEM_WINDOWS)
+gb_internal String get_current_directory(void) {
+ char cwd[256];
+ getcwd(cwd, 256);
+
+ return make_string_c(cwd);
+}
+
+#else
+gb_internal String get_current_directory(void) {
+ gbAllocator a = heap_allocator();
+
+ wchar_t cwd[256];
+ GetCurrentDirectoryW(256, cwd);
+
+ String16 wstr = make_string16_c(cwd);
+
+ return string16_to_string(a, wstr);
+}
+#endif
+
gb_internal bool path_is_directory(String path);
gb_internal String directory_from_path(String const &s) {
@@ -392,7 +419,43 @@ gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi)
return ReadDirectory_None;
}
+
+
#else
#error Implement read_directory
#endif
+#if !defined(GB_SYSTEM_WINDOWS)
+gb_internal bool write_directory(String path) {
+ char const *pathname = (char *) path.text;
+
+ if (access(pathname, W_OK) < 0) {
+ return false;
+ }
+
+ return true;
+}
+#else
+gb_internal bool write_directory(String path) {
+ String16 wstr = string_to_string16(heap_allocator(), path);
+ LPCWSTR wdirectory_name = wstr.text;
+
+ HANDLE directory = CreateFileW(wdirectory_name,
+ GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+
+ if (directory == INVALID_HANDLE_VALUE) {
+ DWORD error_code = GetLastError();
+ if (error_code == ERROR_ACCESS_DENIED) {
+ return false;
+ }
+ }
+
+ CloseHandle(directory);
+ return true;
+}
+#endif
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index 547a864fb..17a396b9f 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -54,7 +54,7 @@ TOKEN_KIND(Token__AssignOpEnd, ""), \
TOKEN_KIND(Token_Increment, "++"), \
TOKEN_KIND(Token_Decrement, "--"), \
TOKEN_KIND(Token_ArrowRight,"->"), \
- TOKEN_KIND(Token_Undef, "---"), \
+ TOKEN_KIND(Token_Uninit, "---"), \
\
TOKEN_KIND(Token__ComparisonBegin, ""), \
TOKEN_KIND(Token_CmpEq, "=="), \
@@ -917,7 +917,7 @@ gb_internal void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
token->kind = Token_Decrement;
if (t->curr_rune == '-') {
advance_to_next_rune(t);
- token->kind = Token_Undef;
+ token->kind = Token_Uninit;
}
break;
case '>':
@@ -1078,7 +1078,7 @@ semicolon_check:;
case Token_Imag:
case Token_Rune:
case Token_String:
- case Token_Undef:
+ case Token_Uninit:
/*fallthrough*/
case Token_Question:
case Token_Pointer:
diff --git a/src/types.cpp b/src/types.cpp
index ddfedf1e1..3cc077f84 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -83,7 +83,7 @@ enum BasicKind {
Basic_UntypedString,
Basic_UntypedRune,
Basic_UntypedNil,
- Basic_UntypedUndef,
+ Basic_UntypedUninit,
Basic_COUNT,
@@ -515,7 +515,7 @@ gb_global Type basic_types[] = {
{Type_Basic, {Basic_UntypedString, BasicFlag_String | BasicFlag_Untyped, 0, STR_LIT("untyped string")}},
{Type_Basic, {Basic_UntypedRune, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped rune")}},
{Type_Basic, {Basic_UntypedNil, BasicFlag_Untyped, 0, STR_LIT("untyped nil")}},
- {Type_Basic, {Basic_UntypedUndef, BasicFlag_Untyped, 0, STR_LIT("untyped undefined")}},
+ {Type_Basic, {Basic_UntypedUninit, BasicFlag_Untyped, 0, STR_LIT("untyped uninitialized")}},
};
// gb_global Type basic_type_aliases[] = {
@@ -589,7 +589,7 @@ gb_global Type *t_untyped_quaternion = &basic_types[Basic_UntypedQuaternion];
gb_global Type *t_untyped_string = &basic_types[Basic_UntypedString];
gb_global Type *t_untyped_rune = &basic_types[Basic_UntypedRune];
gb_global Type *t_untyped_nil = &basic_types[Basic_UntypedNil];
-gb_global Type *t_untyped_undef = &basic_types[Basic_UntypedUndef];
+gb_global Type *t_untyped_uninit = &basic_types[Basic_UntypedUninit];
@@ -1866,14 +1866,15 @@ gb_internal bool is_type_typeid(Type *t) {
}
gb_internal bool is_type_untyped_nil(Type *t) {
t = base_type(t);
- return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedNil);
+ // NOTE(bill): checking for `nil` or `---` at once is just to improve the error handling
+ return (t->kind == Type_Basic && (t->Basic.kind == Basic_UntypedNil || t->Basic.kind == Basic_UntypedUninit));
}
-gb_internal bool is_type_untyped_undef(Type *t) {
+gb_internal bool is_type_untyped_uninit(Type *t) {
t = base_type(t);
- return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedUndef);
+ // NOTE(bill): checking for `nil` or `---` at once is just to improve the error handling
+ return (t->kind == Type_Basic && t->Basic.kind == Basic_UntypedUninit);
}
-
gb_internal bool is_type_empty_union(Type *t) {
t = base_type(t);
return t->kind == Type_Union && t->Union.variants.count == 0;
@@ -2206,10 +2207,6 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) {
}
-gb_internal gb_inline bool type_has_undef(Type *t) {
- return true;
-}
-
gb_internal bool type_has_nil(Type *t) {
t = base_type(t);
switch (t->kind) {
@@ -2769,13 +2766,23 @@ gb_internal Type *default_type(Type *type) {
return type;
}
+gb_internal bool union_variant_index_types_equal(Type *v, Type *vt) {
+ if (are_types_identical(v, vt)) {
+ return true;
+ }
+ if (is_type_proc(v) && is_type_proc(vt)) {
+ return are_types_identical(base_type(v), base_type(vt));
+ }
+ return false;
+}
+
gb_internal i64 union_variant_index(Type *u, Type *v) {
u = base_type(u);
GB_ASSERT(u->kind == Type_Union);
for_array(i, u->Union.variants) {
Type *vt = u->Union.variants[i];
- if (are_types_identical(v, vt)) {
+ if (union_variant_index_types_equal(v, vt)) {
if (u->Union.kind == UnionType_no_nil) {
return cast(i64)(i+0);
} else {