diff options
| author | gingerBill <bill@gingerbill.org> | 2023-06-06 22:42:04 +0100 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2023-06-06 22:42:04 +0100 |
| commit | 4a75a1e839252e7719e4126e0e98dd647850f91d (patch) | |
| tree | c842ea0f1924563be025ef49726d98d9f2763e94 /src | |
| parent | 60ec3594ab05faaa46c7cac50e87571ab3f5eaa6 (diff) | |
| parent | 6a2ff3a3711e3da6bc9f2be9d9a67361b3ff9bd5 (diff) | |
Merge branch 'master' into separate-int-word-sizes
Diffstat (limited to 'src')
| -rw-r--r-- | src/build_settings.cpp | 24 | ||||
| -rw-r--r-- | src/check_builtin.cpp | 83 | ||||
| -rw-r--r-- | src/check_decl.cpp | 24 | ||||
| -rw-r--r-- | src/check_expr.cpp | 77 | ||||
| -rw-r--r-- | src/check_stmt.cpp | 28 | ||||
| -rw-r--r-- | src/check_type.cpp | 6 | ||||
| -rw-r--r-- | src/checker.cpp | 35 | ||||
| -rw-r--r-- | src/checker_builtin_procs.hpp | 2 | ||||
| -rw-r--r-- | src/docs_writer.cpp | 80 | ||||
| -rw-r--r-- | src/llvm_abi.cpp | 37 | ||||
| -rw-r--r-- | src/llvm_backend.cpp | 41 | ||||
| -rw-r--r-- | src/llvm_backend.hpp | 2 | ||||
| -rw-r--r-- | src/llvm_backend_const.cpp | 15 | ||||
| -rw-r--r-- | src/llvm_backend_debug.cpp | 2 | ||||
| -rw-r--r-- | src/llvm_backend_expr.cpp | 94 | ||||
| -rw-r--r-- | src/llvm_backend_general.cpp | 4 | ||||
| -rw-r--r-- | src/llvm_backend_opt.cpp | 12 | ||||
| -rw-r--r-- | src/llvm_backend_proc.cpp | 8 | ||||
| -rw-r--r-- | src/llvm_backend_stmt.cpp | 252 | ||||
| -rw-r--r-- | src/parser.cpp | 47 | ||||
| -rw-r--r-- | src/parser.hpp | 12 | ||||
| -rw-r--r-- | src/parser_pos.cpp | 4 | ||||
| -rw-r--r-- | src/path.cpp | 63 | ||||
| -rw-r--r-- | src/tokenizer.cpp | 6 | ||||
| -rw-r--r-- | src/types.cpp | 31 |
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 { |