aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorColin Davidson <colrdavidson@gmail.com>2024-09-24 02:32:06 -0700
committerColin Davidson <colrdavidson@gmail.com>2024-09-24 02:32:06 -0700
commitf3ab14b8ccb45d0fef8a96937635bdf0943ce7d6 (patch)
tree1309d7c797117463996a84522ef3d1c9713a286c /src
parent99938c7d4fb26d43a07dd4b8f4f00ab87e67e73f (diff)
parentf7d74ff3a8596efef67d151ffb758ed085e94be0 (diff)
Merge branch 'master' into macharena
Diffstat (limited to 'src')
-rw-r--r--src/bug_report.cpp10
-rw-r--r--src/build_settings.cpp19
-rw-r--r--src/check_builtin.cpp113
-rw-r--r--src/check_decl.cpp24
-rw-r--r--src/check_expr.cpp185
-rw-r--r--src/check_stmt.cpp27
-rw-r--r--src/check_type.cpp32
-rw-r--r--src/checker.cpp139
-rw-r--r--src/checker_builtin_procs.hpp7
-rw-r--r--src/entity.cpp1
-rw-r--r--src/gb/gb.h29
-rw-r--r--src/linker.cpp18
-rw-r--r--src/llvm_abi.cpp9
-rw-r--r--src/llvm_backend.cpp7
-rw-r--r--src/llvm_backend.hpp2
-rw-r--r--src/llvm_backend_const.cpp4
-rw-r--r--src/llvm_backend_debug.cpp147
-rw-r--r--src/llvm_backend_expr.cpp77
-rw-r--r--src/llvm_backend_proc.cpp62
-rw-r--r--src/llvm_backend_stmt.cpp7
-rw-r--r--src/llvm_backend_type.cpp2
-rw-r--r--src/llvm_backend_utility.cpp2
-rw-r--r--src/main.cpp80
-rw-r--r--src/microsoft_craziness.h10
-rw-r--r--src/parser.cpp272
-rw-r--r--src/parser.hpp6
-rw-r--r--src/threading.cpp3
-rw-r--r--src/tokenizer.cpp15
-rw-r--r--src/types.cpp36
29 files changed, 1051 insertions, 294 deletions
diff --git a/src/bug_report.cpp b/src/bug_report.cpp
index 916d4016a..fa7e156ce 100644
--- a/src/bug_report.cpp
+++ b/src/bug_report.cpp
@@ -155,7 +155,7 @@ gb_internal void report_windows_product_type(DWORD ProductType) {
#endif
gb_internal void odin_cpuid(int leaf, int result[]) {
- #if defined(GB_CPU_ARM)
+ #if defined(GB_CPU_ARM) || defined(GB_CPU_RISCV)
return;
#elif defined(GB_CPU_X86)
@@ -225,6 +225,12 @@ gb_internal void report_cpu_info() {
gb_printf("ARM\n");
#endif
}
+ #elif defined(GB_CPU_RISCV)
+ #if defined(GB_ARCH_64_BIT)
+ gb_printf("RISCV64\n");
+ #else
+ gb_printf("RISCV32\n");
+ #endif
#else
gb_printf("Unknown\n");
#endif
@@ -913,6 +919,8 @@ gb_internal void report_os_info() {
{"23F79", {23, 5, 0}, "macOS", {"Sonoma", {14, 5, 0}}},
{"23G80", {23, 6, 0}, "macOS", {"Sonoma", {14, 6, 0}}},
{"23G93", {23, 6, 0}, "macOS", {"Sonoma", {14, 6, 1}}},
+ {"23H124", {23, 6, 0}, "macOS", {"Sonoma", {14, 7, 0}}},
+ {"24A335", {24, 0, 0}, "macOS", {"Sequoia", {15, 0, 0}}},
};
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 3d56f4202..7aff8e650 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -285,6 +285,7 @@ enum VetFlags : u64 {
VetFlag_Deprecated = 1u<<7,
VetFlag_Cast = 1u<<8,
VetFlag_Tabs = 1u<<9,
+ VetFlag_UnusedProcedures = 1u<<10,
VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports,
@@ -316,6 +317,8 @@ u64 get_vet_flag_from_name(String const &name) {
return VetFlag_Cast;
} else if (name == "tabs") {
return VetFlag_Tabs;
+ } else if (name == "unused-procedures") {
+ return VetFlag_UnusedProcedures;
}
return VetFlag_NONE;
}
@@ -383,6 +386,7 @@ struct BuildContext {
u64 vet_flags;
u32 sanitizer_flags;
+ StringSet vet_packages;
bool has_resource;
String link_flags;
@@ -411,6 +415,7 @@ struct BuildContext {
bool no_dynamic_literals;
bool no_output_files;
bool no_crt;
+ bool no_rpath;
bool no_entry_point;
bool no_thread_local;
bool use_lld;
@@ -430,6 +435,7 @@ struct BuildContext {
bool json_errors;
bool has_ansi_terminal_colours;
+ bool fast_isel;
bool ignore_lazy;
bool ignore_llvm_build;
bool ignore_panic;
@@ -1460,8 +1466,6 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
bc->thread_count = gb_max(bc->affinity.thread_count, 1);
}
- string_set_init(&bc->custom_attributes);
-
bc->ODIN_VENDOR = str_lit("odin");
bc->ODIN_VERSION = ODIN_VERSION;
bc->ODIN_ROOT = odin_root_dir();
@@ -1525,6 +1529,8 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
metrics = &target_haiku_amd64;
#elif defined(GB_CPU_ARM)
metrics = &target_linux_arm64;
+ #elif defined(GB_CPU_RISCV)
+ metrics = &target_linux_riscv64;
#else
metrics = &target_linux_amd64;
#endif
@@ -1647,7 +1653,7 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
// Disallow on wasm
bc->use_separate_modules = false;
- } if(bc->metrics.arch == TargetArch_riscv64) {
+ } if(bc->metrics.arch == TargetArch_riscv64 && bc->cross_compiling) {
bc->link_flags = str_lit("-target riscv64 ");
} else {
// NOTE: for targets other than darwin, we don't specify a `-target` link flag.
@@ -2046,10 +2052,11 @@ gb_internal bool init_build_paths(String init_filename) {
gbFile output_file_test;
const char* output_file_name = (const char*)output_file.text;
gbFileError output_test_err = gb_file_open_mode(&output_file_test, gbFileMode_Append | gbFileMode_Rw, output_file_name);
- gb_file_close(&output_file_test);
- gb_file_remove(output_file_name);
- if (output_test_err != 0) {
+ if (output_test_err == 0) {
+ gb_file_close(&output_file_test);
+ gb_file_remove(output_file_name);
+ } else {
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));
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index 1c4b88101..8c051cca2 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -1632,6 +1632,22 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
operand->type = t_source_code_location;
operand->mode = Addressing_Value;
+ } else if (name == "caller_expression") {
+ if (ce->args.count > 1) {
+ error(ce->args[0], "'#caller_expression' expects either 0 or 1 arguments, got %td", ce->args.count);
+ }
+ if (ce->args.count > 0) {
+ Ast *arg = ce->args[0];
+ Operand o = {};
+ Entity *e = check_ident(c, &o, arg, nullptr, nullptr, true);
+ if (e == nullptr || (e->flags & EntityFlag_Param) == 0) {
+ error(ce->args[0], "'#caller_expression' expected a valid earlier parameter name");
+ }
+ arg->Ident.entity = e;
+ }
+
+ operand->type = t_string;
+ operand->mode = Addressing_Value;
} else if (name == "exists") {
if (ce->args.count != 1) {
error(ce->close, "'#exists' expects 1 argument, got %td", ce->args.count);
@@ -1792,7 +1808,10 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count);
return false;
}
- if (!is_type_boolean(operand->type) || operand->mode != Addressing_Constant) {
+
+ // operand->type can be nil if the condition is a procedure, for example: #assert(assert())
+ // So let's check it before we use it, so we get the same error as if we wrote `#exists(assert())
+ if (operand->type == nullptr || !is_type_boolean(operand->type) || operand->mode != Addressing_Constant) {
gbString str = expr_to_string(ce->args[0]);
error(call, "'%s' is not a constant boolean", str);
gb_string_free(str);
@@ -2048,6 +2067,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return ok;
}
+ if (BuiltinProc__atomic_begin < id && id < BuiltinProc__atomic_end) {
+ if (build_context.metrics.arch == TargetArch_riscv64) {
+ if (!check_target_feature_is_enabled(str_lit("a"), nullptr)) {
+ error(call, "missing required target feature \"a\" for atomics, enable it by setting a different -microarch or explicitly adding it through -target-features");
+ }
+ }
+ }
+
switch (id) {
default:
GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name));
@@ -4942,16 +4969,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
check_assignment(c, &x, elem, builtin_name);
Type *t = type_deref(operand->type);
- switch (id) {
- case BuiltinProc_atomic_add:
- case BuiltinProc_atomic_sub:
- if (!is_type_numeric(t)) {
+ if (id != BuiltinProc_atomic_exchange) {
+ if (!is_type_integer_like(t)) {
gbString str = type_to_string(t);
- error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str);
+ error(operand->expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
} else if (is_type_different_to_arch_endianness(t)) {
gbString str = type_to_string(t);
- error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
+ error(operand->expr, "Expected an integer type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
}
}
@@ -4987,19 +5012,16 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
Type *t = type_deref(operand->type);
- switch (id) {
- case BuiltinProc_atomic_add_explicit:
- case BuiltinProc_atomic_sub_explicit:
- if (!is_type_numeric(t)) {
+ if (id != BuiltinProc_atomic_exchange_explicit) {
+ if (!is_type_integer_like(t)) {
gbString str = type_to_string(t);
- error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str);
+ error(operand->expr, "Expected an integer type for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
} else if (is_type_different_to_arch_endianness(t)) {
gbString str = type_to_string(t);
- error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
+ error(operand->expr, "Expected an integer type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
gb_string_free(str);
}
- break;
}
operand->type = elem;
@@ -5192,6 +5214,16 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return false;
}
+ if (sz >= 64) {
+ if (is_type_unsigned(x.type)) {
+ add_package_dependency(c, "runtime", "umodti3", true);
+ add_package_dependency(c, "runtime", "udivti3", true);
+ } else {
+ add_package_dependency(c, "runtime", "modti3", true);
+ add_package_dependency(c, "runtime", "divti3", true);
+ }
+ }
+
operand->type = x.type;
operand->mode = Addressing_Value;
}
@@ -5665,6 +5697,59 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break;
}
break;
+
+ case BuiltinProc_type_has_shared_fields:
+ {
+ Type *u = check_type(c, ce->args[0]);
+ Type *ut = base_type(u);
+ if (ut == nullptr || ut == t_invalid) {
+ error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ if (ut->kind != Type_Struct || ut->Struct.soa_kind != StructSoa_None) {
+ gbString t = type_to_string(ut);
+ error(ce->args[0], "Expected a struct type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ Type *v = check_type(c, ce->args[1]);
+ Type *vt = base_type(v);
+ if (vt == nullptr || vt == t_invalid) {
+ error(ce->args[1], "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ if (vt->kind != Type_Struct || vt->Struct.soa_kind != StructSoa_None) {
+ gbString t = type_to_string(vt);
+ error(ce->args[1], "Expected a struct type for '%.*s', got %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ bool is_shared = true;
+
+ for (Entity *v_field : vt->Struct.fields) {
+ bool found = false;
+ for (Entity *u_field : ut->Struct.fields) {
+ if (v_field->token.string == u_field->token.string &&
+ are_types_identical(v_field->type, u_field->type)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ is_shared = false;
+ break;
+ }
+ }
+
+ operand->mode = Addressing_Constant;
+ operand->value = exact_value_bool(is_shared);
+ operand->type = t_untyped_bool;
+ break;
+ }
+
case BuiltinProc_type_field_type:
{
Operand op = {};
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index c60084ec3..3b532a727 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -768,6 +768,19 @@ gb_internal bool are_signatures_similar_enough(Type *a_, Type *b_) {
if (a->result_count != b->result_count) {
return false;
}
+
+ if (a->c_vararg != b->c_vararg) {
+ return false;
+ }
+
+ if (a->variadic != b->variadic) {
+ return false;
+ }
+
+ if (a->variadic && a->variadic_index != b->variadic_index) {
+ return false;
+ }
+
for (isize i = 0; i < a->param_count; i++) {
Type *x = core_type(a->params->Tuple.variables[i]->type);
Type *y = core_type(b->params->Tuple.variables[i]->type);
@@ -779,6 +792,17 @@ gb_internal bool are_signatures_similar_enough(Type *a_, Type *b_) {
y = core_type(y->BitSet.underlying);
}
+ // Allow a `#c_vararg args: ..any` with `#c_vararg args: ..foo`.
+ if (a->variadic && i == a->variadic_index) {
+ GB_ASSERT(x->kind == Type_Slice);
+ GB_ASSERT(y->kind == Type_Slice);
+ Type *x_elem = core_type(x->Slice.elem);
+ Type *y_elem = core_type(y->Slice.elem);
+ if (is_type_any(x_elem) || is_type_any(y_elem)) {
+ continue;
+ }
+ }
+
if (!signature_parameter_similar_enough(x, y)) {
return false;
}
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 9cdba2710..fc1aa62e6 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -1180,11 +1180,15 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
LIT(article),
LIT(context_name));
} else {
+ ERROR_BLOCK();
error(operand->expr,
"Cannot assign '%s', a type, to %.*s%.*s",
op_type_str,
LIT(article),
LIT(context_name));
+ if (type && are_types_identical(type, t_any)) {
+ error_line("\tSuggestion: 'typeid_of(%s)'", expr_str);
+ }
}
break;
default:
@@ -2571,6 +2575,84 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) {
return o->mode != Addressing_Variable && o->mode != Addressing_SoaVariable;
}
+gb_internal ExactValue exact_bit_set_all_set_mask(Type *type) {
+ type = base_type(type);
+ GB_ASSERT(type->kind == Type_BitSet);
+
+ i64 lower = type->BitSet.lower;
+ i64 upper = type->BitSet.upper;
+ Type *elem = type->BitSet.elem;
+ Type *underlying = type->BitSet.underlying;
+ bool is_backed = underlying != nullptr;
+ gb_unused(is_backed);
+
+ BigInt b_lower = {};
+ BigInt b_upper = {};
+ big_int_from_i64(&b_lower, lower);
+ big_int_from_i64(&b_upper, upper);
+
+
+ BigInt one = {};
+ big_int_from_u64(&one, 1);
+
+ BigInt mask = {};
+
+ if (elem == nullptr) {
+ big_int_from_i64(&mask, -1);
+ } else if (is_type_enum(elem)) {
+ Type *e = base_type(elem);
+ GB_ASSERT(e->kind == Type_Enum);
+ gb_unused(e);
+
+ if ((big_int_cmp(&e->Enum.min_value->value_integer, &b_lower) == 0 || is_backed) &&
+ big_int_cmp(&e->Enum.max_value->value_integer, &b_upper) == 0) {
+
+ i64 lower_base = is_backed ? gb_min(0, lower) : lower;
+ BigInt b_lower_base = {};
+ big_int_from_i64(&b_lower_base, lower_base);
+
+ for (Entity *f : e->Enum.fields) {
+ if (f->kind != Entity_Constant) {
+ continue;
+ }
+ if (f->Constant.value.kind != ExactValue_Integer) {
+ continue;
+ }
+
+ BigInt shift_amount = f->Constant.value.value_integer;
+ big_int_sub_eq(&shift_amount, &b_lower_base);
+
+
+ BigInt value = {};
+ big_int_shl(&value, &one, &shift_amount);
+
+ big_int_or(&mask, &mask, &value);
+ }
+
+ } else {
+ // TODO(bill): enum range based");
+ big_int_from_i64(&mask, -1);
+ }
+ } else {
+ i64 lower_base = lower;
+ for (i64 x = lower; x <= upper; x++) {
+ BigInt shift_amount = {};
+ big_int_from_i64(&shift_amount, x - lower_base);
+
+ BigInt value = {};
+ big_int_shl(&value, &one, &shift_amount);
+
+ big_int_or(&mask, &mask, &value);
+ }
+ }
+
+
+ ExactValue res = {};
+ res.kind = ExactValue_Integer;
+ res.value_integer = mask;
+ return res;
+}
+
gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *node) {
switch (op.kind) {
case Token_And: { // Pointer address
@@ -2710,6 +2792,10 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
}
o->value = exact_unary_operator_value(op.kind, o->value, precision, is_unsigned);
+ if (op.kind == Token_Xor && is_type_bit_set(type)) {
+ ExactValue mask = exact_bit_set_all_set_mask(type);
+ o->value = exact_binary_operator_value(Token_And, o->value, mask);
+ }
if (is_type_typed(type)) {
if (node != nullptr) {
@@ -3526,10 +3612,11 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
if (are_types_identical(src_bt, dst_bt)) {
return true;
}
- if (is_type_integer(src_t) && is_type_integer(dst_t)) {
+ if ((is_type_integer(src_t) && is_type_integer(dst_t)) ||
+ is_type_integer(src_t) && is_type_bit_set(dst_t)) {
if (types_have_same_internal_endian(src_t, dst_t)) {
ExactValue src_v = exact_value_to_integer(o->value);
- GB_ASSERT(src_v.kind == ExactValue_Integer);
+ GB_ASSERT(src_v.kind == ExactValue_Integer || src_v.kind == ExactValue_Invalid);
BigInt v = src_v.value_integer;
BigInt smax = {};
@@ -3782,10 +3869,10 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
// NOTE(bill): Allow comparisons between types
if (is_ise_expr(be->left)) {
// Evalute the right before the left for an '.X' expression
- check_expr_or_type(c, y, be->right, type_hint);
+ check_expr_or_type(c, y, be->right, nullptr /* ignore type hint */);
check_expr_or_type(c, x, be->left, y->type);
} else {
- check_expr_or_type(c, x, be->left, type_hint);
+ check_expr_or_type(c, x, be->left, nullptr /* ignore type hint */);
check_expr_or_type(c, y, be->right, x->type);
}
bool xt = x->mode == Addressing_Type;
@@ -4516,7 +4603,7 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
(operand->value.kind == ExactValue_Integer ||
operand->value.kind == ExactValue_Float)) {
operand->mode = Addressing_Value;
- target_type = t_untyped_nil;
+ // target_type = t_untyped_nil;
operand->value = empty_exact_value;
update_untyped_expr_value(c, operand->expr, operand->value);
break;
@@ -4658,7 +4745,8 @@ gb_internal bool check_index_value(CheckerContext *c, Type *main_type, bool open
check_expr_with_type_hint(c, &operand, index_value, type_hint);
if (operand.mode == Addressing_Invalid) {
if (value) *value = 0;
- return false;
+ // NOTE(bill): return true here to propagate the errors better
+ return true;
}
Type *index_type = t_int;
@@ -4879,7 +4967,7 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
TypeAndValue tav = fv->value->tav;
if (success_) *success_ = true;
if (finish_) *finish_ = false;
- return tav.value;;
+ return tav.value;
}
}
@@ -4954,7 +5042,6 @@ gb_internal ExactValue get_constant_field(CheckerContext *c, Operand const *oper
return value;
}
}
-
if (success_) *success_ = true;
return value;
} else if (value.kind == ExactValue_Quaternion) {
@@ -5652,7 +5739,7 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals
gb_internal bool check_no_copy_assignment(Operand const &o, String const &context) {
if (o.type && is_type_no_copy(o.type)) {
Ast *expr = unparen_expr(o.expr);
- if (expr && o.mode != Addressing_Constant) {
+ if (expr && o.mode != Addressing_Constant && o.mode != Addressing_Type) {
if (expr->kind == Ast_CallExpr) {
// Okay
} else {
@@ -6117,22 +6204,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
Entity *vt = pt->params->Tuple.variables[pt->variadic_index];
o.type = vt->type;
-
- // NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameters with the backing array
- if (c->decl) {
- bool found = false;
- for (auto &vr : c->decl->variadic_reuses) {
- if (are_types_identical(vt->type, vr.slice_type)) {
- vr.max_count = gb_max(vr.max_count, variadic_operands.count);
- found = true;
- break;
- }
- }
- if (!found) {
- array_add(&c->decl->variadic_reuses, VariadicReuseData{vt->type, variadic_operands.count});
- }
- }
-
} else {
dummy_argument_count += 1;
o.type = t_untyped_nil;
@@ -6326,6 +6397,23 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
}
score += eval_param_and_score(c, o, t, err, true, var_entity, show_error);
}
+
+ if (!vari_expand && variadic_operands.count != 0) {
+ // NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameters with the backing array
+ if (c->decl) {
+ bool found = false;
+ for (auto &vr : c->decl->variadic_reuses) {
+ if (are_types_identical(slice, vr.slice_type)) {
+ vr.max_count = gb_max(vr.max_count, variadic_operands.count);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ array_add(&c->decl->variadic_reuses, VariadicReuseData{slice, variadic_operands.count});
+ }
+ }
+ }
}
if (data) {
@@ -7625,7 +7713,7 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
gbString s = gb_string_make_reserve(heap_allocator(), e->token.string.len+3);
s = gb_string_append_fmt(s, "%.*s(", LIT(e->token.string));
- TypeTuple *tuple = get_record_polymorphic_params(e->type);
+ TypeTuple *tuple = get_record_polymorphic_params(bt);
if (tuple != nullptr) for_array(i, tuple->variables) {
Entity *v = tuple->variables[i];
String name = v->token.string;
@@ -7640,8 +7728,10 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
s = write_type_to_string(s, v->type, false);
}
} else if (v->kind == Entity_Constant) {
- s = gb_string_append_fmt(s, "=");
- s = write_exact_value_to_string(s, v->Constant.value);
+ if (v->Constant.value.kind != ExactValue_Invalid) {
+ s = gb_string_append_fmt(s, "=");
+ s = write_exact_value_to_string(s, v->Constant.value);
+ }
}
}
s = gb_string_append_fmt(s, ")");
@@ -7717,7 +7807,8 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
name == "load" ||
name == "load_directory" ||
name == "load_hash" ||
- name == "hash"
+ name == "hash" ||
+ name == "caller_expression"
) {
operand->mode = Addressing_Builtin;
operand->builtin_id = BuiltinProc_DIRECTIVE;
@@ -7997,7 +8088,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc);
String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature;
if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) {
+ ERROR_BLOCK();
error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid));
+
+ error_line("\tSuggested Example: @(enable_target_feature=\"%.*s\")\n", LIT(invalid));
}
}
}
@@ -8632,6 +8726,10 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
error(node, "#caller_location may only be used as a default argument parameter");
o->type = t_source_code_location;
o->mode = Addressing_Value;
+ } else if (name == "caller_expression") {
+ error(node, "#caller_expression may only be used as a default argument parameter");
+ o->type = t_string;
+ o->mode = Addressing_Value;
} else {
if (name == "location") {
init_core_source_code_location(c->checker);
@@ -9033,6 +9131,10 @@ gb_internal ExprKind check_or_branch_expr(CheckerContext *c, Operand *o, Ast *no
}
if (label != nullptr) {
+ if (c->in_defer) {
+ error(label, "A labelled '%.*s' cannot be used within a 'defer'", LIT(name));
+ return Expr_Expr;
+ }
if (label->kind != Ast_Ident) {
error(label, "A branch statement's label name must be an identifier");
return Expr_Expr;
@@ -10055,6 +10157,22 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
is_constant = o->mode == Addressing_Constant;
}
+ if (elem->kind == Ast_BinaryExpr) {
+ switch (elem->BinaryExpr.op.kind) {
+ case Token_Or:
+ {
+ gbString x = expr_to_string(elem->BinaryExpr.left);
+ gbString y = expr_to_string(elem->BinaryExpr.right);
+ gbString e = expr_to_string(elem);
+ error(elem, "Was the following intended? '%s, %s'; if not, surround the expression with parentheses '(%s)'", x, y, e);
+ gb_string_free(e);
+ gb_string_free(y);
+ gb_string_free(x);
+ }
+ break;
+ }
+ }
+
check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
if (o->mode == Addressing_Constant) {
i64 lower = t->BitSet.lower;
@@ -10063,7 +10181,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
if (lower <= v && v <= upper) {
// okay
} else {
- error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper);
+ gbString s = expr_to_string(o->expr);
+ error(elem, "Bit field value out of bounds, %s (%lld) not in the range %lld .. %lld", s, v, lower, upper);
+ gb_string_free(s);
continue;
}
}
@@ -10544,7 +10664,8 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node,
o->expr = node;
return kind;
} else if (ok && !is_type_matrix(t)) {
- ExactValue value = type_and_value_of_expr(ie->expr).value;
+ TypeAndValue tav = type_and_value_of_expr(ie->expr);
+ ExactValue value = tav.value;
o->mode = Addressing_Constant;
bool success = false;
bool finish = false;
@@ -10821,7 +10942,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
case Token_context:
{
if (c->proc_name.len == 0 && c->curr_proc_sig == nullptr) {
- error(node, "'context' is only allowed within procedures %p", c->curr_proc_decl);
+ error(node, "'context' is only allowed within procedures");
return kind;
}
if (unparen_expr(c->assignment_lhs_hint) == node) {
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 12156df01..74a9e8825 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -199,6 +199,9 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
}
break;
+ case Ast_DeferStmt:
+ return check_has_break(stmt->DeferStmt.stmt, label, implicit);
+
case Ast_BlockStmt:
return check_has_break_list(stmt->BlockStmt.stmts, label, implicit);
@@ -1638,6 +1641,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
Ast *expr = unparen_expr(rs->expr);
+ Operand rhs_operand = {};
+
bool is_range = false;
bool is_possibly_addressable = true;
isize max_val_count = 2;
@@ -1695,7 +1700,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
}
}
}
- bool is_ptr = type_deref(operand.type);
+ bool is_ptr = is_type_pointer(operand.type);
Type *t = base_type(type_deref(operand.type));
switch (t->kind) {
@@ -1735,6 +1740,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
break;
case Type_EnumeratedArray:
+ is_possibly_addressable = operand.mode == Addressing_Variable || is_ptr;
array_add(&vals, t->EnumeratedArray.elem);
array_add(&vals, t->EnumeratedArray.index);
break;
@@ -1746,16 +1752,19 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
break;
case Type_DynamicArray:
+ is_possibly_addressable = true;
array_add(&vals, t->DynamicArray.elem);
array_add(&vals, t_int);
break;
case Type_Slice:
+ is_possibly_addressable = true;
array_add(&vals, t->Slice.elem);
array_add(&vals, t_int);
break;
case Type_Map:
+ is_possibly_addressable = true;
is_map = true;
array_add(&vals, t->Map.key);
array_add(&vals, t->Map.value);
@@ -1777,6 +1786,8 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
case Type_Tuple:
{
+ is_possibly_addressable = false;
+
isize count = t->Tuple.variables.count;
if (count < 1) {
ERROR_BLOCK();
@@ -1806,8 +1817,6 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
array_add(&vals, e->type);
}
- is_possibly_addressable = false;
-
bool do_break = false;
for (isize i = rs->vals.count-1; i >= 0; i--) {
if (rs->vals[i] != nullptr && count < i+2) {
@@ -1827,6 +1836,11 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
case Type_Struct:
if (t->Struct.soa_kind != StructSoa_None) {
+ if (t->Struct.soa_kind == StructSoa_Fixed) {
+ is_possibly_addressable = operand.mode == Addressing_Variable || is_ptr;
+ } else {
+ is_possibly_addressable = true;
+ }
is_soa = true;
array_add(&vals, t->Struct.soa_elem);
array_add(&vals, t_int);
@@ -1903,7 +1917,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
if (is_possibly_addressable && i == addressable_index) {
entity->flags &= ~EntityFlag_Value;
} else {
- char const *idx_name = is_map ? "key" : is_bit_set ? "element" : "index";
+ char const *idx_name = is_map ? "key" : (is_bit_set || i == 0) ? "element" : "index";
error(token, "The %s variable '%.*s' cannot be made addressable", idx_name, LIT(str));
}
}
@@ -2706,6 +2720,7 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags)
error(bs->label, "A branch statement's label name must be an identifier");
return;
}
+
Ast *ident = bs->label;
String name = ident->Ident.token.string;
Operand o = {};
@@ -2737,6 +2752,10 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags)
break;
}
+
+ if (ctx->in_defer) {
+ error(bs->label, "A labelled '%.*s' cannot be used within a 'defer'", LIT(token.string));
+ }
}
case_end;
diff --git a/src/check_type.cpp b/src/check_type.cpp
index 3767f7666..f0e0acb9b 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -1605,6 +1605,25 @@ gb_internal bool is_expr_from_a_parameter(CheckerContext *ctx, Ast *expr) {
return false;
}
+gb_internal bool is_caller_expression(Ast *expr) {
+ if (expr->kind == Ast_BasicDirective && expr->BasicDirective.name.string == "caller_expression") {
+ return true;
+ }
+
+ Ast *call = unparen_expr(expr);
+ if (call->kind != Ast_CallExpr) {
+ return false;
+ }
+
+ ast_node(ce, CallExpr, call);
+ if (ce->proc->kind != Ast_BasicDirective) {
+ return false;
+ }
+
+ ast_node(bd, BasicDirective, ce->proc);
+ String name = bd->name.string;
+ return name == "caller_expression";
+}
gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type **out_type_, Ast *expr, bool allow_caller_location) {
ParameterValue param_value = {};
@@ -1626,7 +1645,19 @@ gb_internal ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_
if (in_type) {
check_assignment(ctx, &o, in_type, str_lit("parameter value"));
}
+ } else if (is_caller_expression(expr)) {
+ if (expr->kind != Ast_BasicDirective) {
+ check_builtin_procedure_directive(ctx, &o, expr, t_string);
+ }
+
+ param_value.kind = ParameterValue_Expression;
+ o.type = t_string;
+ o.mode = Addressing_Value;
+ o.expr = expr;
+ if (in_type) {
+ check_assignment(ctx, &o, in_type, str_lit("parameter value"));
+ }
} else {
if (in_type) {
check_expr_with_type_hint(ctx, &o, expr, in_type);
@@ -1858,6 +1889,7 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
case ParameterValue_Nil:
break;
case ParameterValue_Location:
+ case ParameterValue_Expression:
case ParameterValue_Value:
gbString str = type_to_string(type);
error(params[i], "A default value for a parameter must not be a polymorphic constant type, got %s", str);
diff --git a/src/checker.cpp b/src/checker.cpp
index b24a7afdb..af1e0e675 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -533,18 +533,13 @@ gb_internal u64 check_vet_flags(CheckerContext *c) {
c->curr_proc_decl->proc_lit) {
file = c->curr_proc_decl->proc_lit->file();
}
- if (file && file->vet_flags_set) {
- return file->vet_flags;
- }
- return build_context.vet_flags;
+
+ return ast_file_vet_flags(file);
}
gb_internal u64 check_vet_flags(Ast *node) {
AstFile *file = node->file();
- if (file && file->vet_flags_set) {
- return file->vet_flags;
- }
- return build_context.vet_flags;
+ return ast_file_vet_flags(file);
}
enum VettedEntityKind {
@@ -681,20 +676,45 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) {
return false;
}
-gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
- bool vet_unused = (vet_flags & VetFlag_Unused) != 0;
- bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0;
-
+gb_internal void check_scope_usage_internal(Checker *c, Scope *scope, u64 vet_flags, bool per_entity) {
+ u64 original_vet_flags = vet_flags;
Array<VettedEntity> vetted_entities = {};
array_init(&vetted_entities, heap_allocator());
+ defer (array_free(&vetted_entities));
rw_mutex_shared_lock(&scope->mutex);
for (auto const &entry : scope->elements) {
Entity *e = entry.value;
if (e == nullptr) continue;
+
+ vet_flags = original_vet_flags;
+ if (per_entity) {
+ vet_flags = ast_file_vet_flags(e->file);
+ }
+
+ bool vet_unused = (vet_flags & VetFlag_Unused) != 0;
+ bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0;
+ bool vet_unused_procedures = (vet_flags & VetFlag_UnusedProcedures) != 0;
+
VettedEntity ve_unused = {};
VettedEntity ve_shadowed = {};
- bool is_unused = vet_unused && check_vet_unused(c, e, &ve_unused);
+ bool is_unused = false;
+ if (vet_unused && check_vet_unused(c, e, &ve_unused)) {
+ is_unused = true;
+ } else if (vet_unused_procedures &&
+ e->kind == Entity_Procedure) {
+ if (e->flags&EntityFlag_Used) {
+ is_unused = false;
+ } else if (e->flags & EntityFlag_Require) {
+ is_unused = false;
+ } else if (e->pkg && e->pkg->kind == Package_Init && e->token.string == "main") {
+ is_unused = false;
+ } else {
+ is_unused = true;
+ ve_unused.kind = VettedEntity_Unused;
+ ve_unused.entity = e;
+ }
+ }
bool is_shadowed = vet_shadowing && check_vet_shadowing(c, e, &ve_shadowed);
if (is_unused && is_shadowed) {
VettedEntity ve_both = ve_shadowed;
@@ -717,13 +737,18 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
}
rw_mutex_shared_unlock(&scope->mutex);
- gb_sort(vetted_entities.data, vetted_entities.count, gb_size_of(VettedEntity), vetted_entity_variable_pos_cmp);
+ array_sort(vetted_entities, vetted_entity_variable_pos_cmp);
for (auto const &ve : vetted_entities) {
Entity *e = ve.entity;
Entity *other = ve.other;
String name = e->token.string;
+ vet_flags = original_vet_flags;
+ if (per_entity) {
+ vet_flags = ast_file_vet_flags(e->file);
+ }
+
if (ve.kind == VettedEntity_Shadowed_And_Unused) {
error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line);
} else if (vet_flags) {
@@ -732,6 +757,9 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
if (e->kind == Entity_Variable && (vet_flags & VetFlag_UnusedVariables) != 0) {
error(e->token, "'%.*s' declared but not used", LIT(name));
}
+ if (e->kind == Entity_Procedure && (vet_flags & VetFlag_UnusedProcedures) != 0) {
+ error(e->token, "'%.*s' declared but not used", LIT(name));
+ }
if ((e->kind == Entity_ImportName || e->kind == Entity_LibraryName) && (vet_flags & VetFlag_UnusedImports) != 0) {
error(e->token, "'%.*s' declared but not used", LIT(name));
}
@@ -749,7 +777,11 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
}
}
- array_free(&vetted_entities);
+}
+
+
+gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
+ check_scope_usage_internal(c, scope, vet_flags, false);
for (Scope *child = scope->head_child; child != nullptr; child = child->next) {
if (child->flags & (ScopeFlag_Proc|ScopeFlag_Type|ScopeFlag_File)) {
@@ -1786,7 +1818,9 @@ gb_internal void add_entity_use(CheckerContext *c, Ast *identifier, Entity *enti
entity->flags |= EntityFlag_Used;
if (entity_has_deferred_procedure(entity)) {
Entity *deferred = entity->Procedure.deferred_procedure.entity;
- add_entity_use(c, nullptr, deferred);
+ if (deferred != entity) {
+ add_entity_use(c, nullptr, deferred);
+ }
}
if (identifier == nullptr || identifier->kind != Ast_Ident) {
return;
@@ -3174,8 +3208,8 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
return true;
} else if (name == "link_prefix") {
if (ev.kind == ExactValue_String) {
- String link_prefix = ev.value_string;
- if (!is_foreign_name_valid(link_prefix)) {
+ String link_prefix = string_trim_whitespace(ev.value_string);
+ if (link_prefix.len != 0 && !is_foreign_name_valid(link_prefix)) {
error(elem, "Invalid link prefix: '%.*s'", LIT(link_prefix));
} else {
c->foreign_context.link_prefix = link_prefix;
@@ -3186,8 +3220,8 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
return true;
} else if (name == "link_suffix") {
if (ev.kind == ExactValue_String) {
- String link_suffix = ev.value_string;
- if (!is_foreign_name_valid(link_suffix)) {
+ String link_suffix = string_trim_whitespace(ev.value_string);
+ if (link_suffix.len != 0 && !is_foreign_name_valid(link_suffix)) {
error(elem, "Invalid link suffix: '%.*s'", LIT(link_suffix));
} else {
c->foreign_context.link_suffix = link_suffix;
@@ -3489,7 +3523,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
if (ev.kind == ExactValue_String) {
ac->link_prefix = ev.value_string;
- if (!is_foreign_name_valid(ac->link_prefix)) {
+ if (ac->link_prefix.len != 0 && !is_foreign_name_valid(ac->link_prefix)) {
error(elem, "Invalid link prefix: %.*s", LIT(ac->link_prefix));
}
} else {
@@ -3501,7 +3535,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
if (ev.kind == ExactValue_String) {
ac->link_suffix = ev.value_string;
- if (!is_foreign_name_valid(ac->link_suffix)) {
+ if (ac->link_suffix.len != 0 && !is_foreign_name_valid(ac->link_suffix)) {
error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix));
}
} else {
@@ -3774,7 +3808,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
ac->link_prefix = ev.value_string;
- if (!is_foreign_name_valid(ac->link_prefix)) {
+ if (ac->link_prefix.len != 0 && !is_foreign_name_valid(ac->link_prefix)) {
error(elem, "Invalid link prefix: %.*s", LIT(ac->link_prefix));
}
} else {
@@ -3785,7 +3819,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
ac->link_suffix = ev.value_string;
- if (!is_foreign_name_valid(ac->link_suffix)) {
+ if (ac->link_suffix.len != 0 && !is_foreign_name_valid(ac->link_suffix)) {
error(elem, "Invalid link suffix: %.*s", LIT(ac->link_suffix));
}
} else {
@@ -4532,7 +4566,9 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n
case_end;
case_ast_node(fb, ForeignBlockDecl, decl);
- check_add_foreign_block_decl(c, decl);
+ if (curr_file != nullptr) {
+ array_add(&curr_file->delayed_decls_queues[AstDelayQueue_ForeignBlock], decl);
+ }
case_end;
default:
@@ -4548,6 +4584,14 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n
// NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something
// declared after this stmt in source
if (curr_file == nullptr) {
+ // For 'foreign' block statements that are not in file scope.
+ for_array(decl_index, nodes) {
+ Ast *decl = nodes[decl_index];
+ if (decl->kind == Ast_ForeignBlockDecl) {
+ check_add_foreign_block_decl(c, decl);
+ }
+ }
+
for_array(decl_index, nodes) {
Ast *decl = nodes[decl_index];
if (decl->kind == Ast_WhenStmt) {
@@ -4934,12 +4978,18 @@ gb_internal void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
}
- if (import_name.len == 0) {
+ if (is_blank_ident(import_name) && !is_blank_ident(id->import_name.string)) {
String invalid_name = id->fullpath;
invalid_name = get_invalid_import_name(invalid_name);
- error(id->token, "Import name %.*s, is not a valid identifier. Perhaps you want to reference the package by a different name like this: import <new_name> \"%.*s\" ", LIT(invalid_name), LIT(invalid_name));
- error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string));
+ ERROR_BLOCK();
+
+ if (id->import_name.string.len > 0) {
+ error(token, "Import name '%.*s' cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string));
+ } else {
+ error(id->token, "Import name '%.*s' is not a valid identifier", LIT(invalid_name));
+ error_line("\tSuggestion: Rename the directory or explicitly set an import name like this 'import <new_name> %.*s'", LIT(id->relpath.string));
+ }
} else {
GB_ASSERT(id->import_name.pos.line != 0);
id->import_name.string = import_name;
@@ -5218,9 +5268,9 @@ gb_internal bool collect_file_decl(CheckerContext *ctx, Ast *decl) {
case_end;
case_ast_node(fb, ForeignBlockDecl, decl);
- if (check_add_foreign_block_decl(ctx, decl)) {
- return true;
- }
+ GB_ASSERT(ctx->collect_delayed_decls);
+ decl->state_flags |= StateFlag_BeenHandled;
+ array_add(&curr_file->delayed_decls_queues[AstDelayQueue_ForeignBlock], decl);
case_end;
case_ast_node(ws, WhenStmt, decl);
@@ -5503,8 +5553,6 @@ gb_internal void check_import_entities(Checker *c) {
for_array(i, pkg->files) {
AstFile *f = pkg->files[i];
reset_checker_context(&ctx, f, &untyped);
- ctx.collect_delayed_decls = false;
-
correct_type_aliases_in_scope(&ctx, pkg->scope);
}
@@ -5512,6 +5560,17 @@ gb_internal void check_import_entities(Checker *c) {
AstFile *f = pkg->files[i];
reset_checker_context(&ctx, f, &untyped);
+ ctx.collect_delayed_decls = true;
+ for (Ast *decl : f->delayed_decls_queues[AstDelayQueue_ForeignBlock]) {
+ check_add_foreign_block_decl(&ctx, decl);
+ }
+ array_clear(&f->delayed_decls_queues[AstDelayQueue_ForeignBlock]);
+ }
+
+ for_array(i, pkg->files) {
+ AstFile *f = pkg->files[i];
+ reset_checker_context(&ctx, f, &untyped);
+
for (Ast *expr : f->delayed_decls_queues[AstDelayQueue_Expr]) {
Operand o = {};
check_expr(&ctx, &o, expr);
@@ -6095,6 +6154,11 @@ gb_internal void check_deferred_procedures(Checker *c) {
case DeferredProcedure_in_out_by_ptr: attribute = "deferred_in_out_by_ptr"; break;
}
+ if (src == dst) {
+ error(src->token, "'%.*s' cannot be used as its own %s", LIT(dst->token.string), attribute);
+ continue;
+ }
+
if (is_type_polymorphic(src->type) || is_type_polymorphic(dst->type)) {
error(src->token, "'%s' cannot be used with a polymorphic procedure", attribute);
continue;
@@ -6465,12 +6529,13 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("check scope usage");
for (auto const &entry : c->info.files) {
AstFile *f = entry.value;
- u64 vet_flags = build_context.vet_flags;
- if (f->vet_flags_set) {
- vet_flags = f->vet_flags;
- }
+ u64 vet_flags = ast_file_vet_flags(f);
check_scope_usage(c, f->scope, vet_flags);
}
+ for (auto const &entry : c->info.packages) {
+ AstPackage *pkg = entry.value;
+ check_scope_usage_internal(c, pkg->scope, 0, true);
+ }
TIME_SECTION("add basic type information");
// Add "Basic" type information
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index ef07938c7..2dfd570e4 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -99,6 +99,7 @@ enum BuiltinProcId {
BuiltinProc_prefetch_write_instruction,
BuiltinProc_prefetch_write_data,
+BuiltinProc__atomic_begin,
BuiltinProc_atomic_type_is_lock_free,
BuiltinProc_atomic_thread_fence,
BuiltinProc_atomic_signal_fence,
@@ -124,6 +125,7 @@ enum BuiltinProcId {
BuiltinProc_atomic_compare_exchange_strong_explicit,
BuiltinProc_atomic_compare_exchange_weak,
BuiltinProc_atomic_compare_exchange_weak_explicit,
+BuiltinProc__atomic_end,
BuiltinProc_fixed_point_mul,
BuiltinProc_fixed_point_div,
@@ -313,6 +315,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_map_info,
BuiltinProc_type_map_cell_info,
+ BuiltinProc_type_has_shared_fields,
+
BuiltinProc__type_end,
BuiltinProc_procedure_of,
@@ -436,6 +440,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("prefetch_write_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_write_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_type_is_lock_free"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_thread_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("atomic_signal_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
@@ -461,6 +466,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("atomic_compare_exchange_strong_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_compare_exchange_weak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
{STR_LIT("atomic_compare_exchange_weak_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+ {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_mul"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_div"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -647,6 +653,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_map_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_map_cell_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_has_shared_fields"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
diff --git a/src/entity.cpp b/src/entity.cpp
index db6ffdd52..0c4a20df4 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -104,6 +104,7 @@ enum ParameterValueKind {
ParameterValue_Constant,
ParameterValue_Nil,
ParameterValue_Location,
+ ParameterValue_Expression,
ParameterValue_Value,
};
diff --git a/src/gb/gb.h b/src/gb/gb.h
index 38dabc9bd..1fef4b4f5 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -39,7 +39,7 @@ extern "C" {
#endif
#endif
-#if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || defined(__ppc64__) || defined(__aarch64__)
+#if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || defined(__ppc64__) || defined(__aarch64__) || (defined(__riscv) && __riscv_xlen == 64)
#ifndef GB_ARCH_64_BIT
#define GB_ARCH_64_BIT 1
#endif
@@ -144,6 +144,13 @@ extern "C" {
#define GB_CACHE_LINE_SIZE 64
#endif
+#elif defined(__riscv)
+ #ifndef GB_CPU_RISCV
+ #define GB_CPU_RISCV 1
+ #endif
+ #ifndef GB_CACHE_LINE_SIZE
+ #define GB_CACHE_LINE_SIZE 64
+ #endif
#else
#error Unknown CPU Type
#endif
@@ -2562,7 +2569,7 @@ gb_inline void *gb_memcopy(void *dest, void const *source, isize n) {
void *dest_copy = dest;
__asm__ __volatile__("rep movsb" : "+D"(dest_copy), "+S"(source), "+c"(n) : : "memory");
-#elif defined(GB_CPU_ARM)
+#elif defined(GB_CPU_ARM) || defined(GB_CPU_RISCV)
u8 *s = cast(u8 *)source;
u8 *d = cast(u8 *)dest;
for (isize i = 0; i < n; i++) {
@@ -3188,11 +3195,11 @@ void gb_affinity_init(gbAffinity *a) {
a->core_count = 1;
a->threads_per_core = 1;
- if (sysctlbyname("hw.logicalcpu", &count, &count_size, NULL, 0) == 0) {
+ if (sysctlbyname("kern.smp.cpus", &count, &count_size, NULL, 0) == 0) {
if (count > 0) {
a->thread_count = count;
// Get # of physical cores
- if (sysctlbyname("hw.physicalcpu", &count, &count_size, NULL, 0) == 0) {
+ if (sysctlbyname("kern.smp.cores", &count, &count_size, NULL, 0) == 0) {
if (count > 0) {
a->core_count = count;
a->threads_per_core = a->thread_count / count;
@@ -3203,6 +3210,14 @@ void gb_affinity_init(gbAffinity *a) {
}
}
}
+ } else if (sysctlbyname("hw.ncpu", &count, &count_size, NULL, 0) == 0) {
+ // SMP disabled or unavailable.
+ if (count > 0) {
+ a->is_accurate = true;
+ a->thread_count = count;
+ a->core_count = count;
+ a->threads_per_core = 1;
+ }
}
}
@@ -6267,6 +6282,12 @@ gb_no_inline isize gb_snprintf_va(char *text, isize max_len, char const *fmt, va
asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
return virtual_timer_value;
}
+#elif defined(__riscv)
+ gb_inline u64 gb_rdtsc(void) {
+ u64 result = 0;
+ __asm__ volatile("rdcycle %0" : "=r"(result));
+ return result;
+ }
#else
#warning "gb_rdtsc not supported"
gb_inline u64 gb_rdtsc(void) { return 0; }
diff --git a/src/linker.cpp b/src/linker.cpp
index faca28932..500fead69 100644
--- a/src/linker.cpp
+++ b/src/linker.cpp
@@ -548,14 +548,8 @@ gb_internal i32 linker_stage(LinkerData *gen) {
// available at runtime wherever the executable is run, so we make require those to be
// local to the executable (unless the system collection is used, in which case we search
// the system library paths for the library file).
- if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
- // static libs and object files, absolute full path relative to the file in which the lib was imported from
+ if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".so")) || string_contains_string(lib, str_lit(".so."))) {
lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
- } else if (string_ends_with(lib, str_lit(".so")) || string_contains_string(lib, str_lit(".so."))) {
- // dynamic lib, relative path to executable
- // NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
- // at runtime to the executable
- lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
} else {
// dynamic or static system lib, just link regularly searching system library paths
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
@@ -643,6 +637,16 @@ gb_internal i32 linker_stage(LinkerData *gen) {
}
}
+ if (!build_context.no_rpath) {
+ // Set the rpath to the $ORIGIN/@loader_path (the path of the executable),
+ // so that dynamic libraries are looked for at that path.
+ if (build_context.metrics.os == TargetOs_darwin) {
+ link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,@loader_path ");
+ } else {
+ link_settings = gb_string_appendc(link_settings, "-Wl,-rpath,\\$ORIGIN ");
+ }
+ }
+
if (!build_context.no_crt) {
platform_lib_str = gb_string_appendc(platform_lib_str, "-lm ");
if (build_context.metrics.os == TargetOs_darwin) {
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp
index aa5c4dc60..42086b09d 100644
--- a/src/llvm_abi.cpp
+++ b/src/llvm_abi.cpp
@@ -1257,11 +1257,12 @@ namespace lbAbiWasm {
}
gb_internal lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
- if (!is_return && type == LLVMIntTypeInContext(c, 128)) {
- LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2);
+ if (type == LLVMIntTypeInContext(c, 128)) {
+ // LLVMTypeRef cast_type = LLVMVectorType(LLVMInt64TypeInContext(c), 2);
+ LLVMTypeRef cast_type = nullptr;
return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
}
-
+
if (!is_return && lb_sizeof(type) > 8) {
return lb_arg_type_indirect(type, nullptr);
}
@@ -1282,7 +1283,7 @@ namespace lbAbiWasm {
case LLVMPointerTypeKind:
return true;
case LLVMIntegerTypeKind:
- return lb_sizeof(type) <= 8;
+ return lb_sizeof(type) <= 16;
}
return false;
}
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index f852636a6..01ded321e 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -3081,6 +3081,13 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
lbModule *m = entry.value;
m->target_machine = target_machine;
LLVMSetModuleDataLayout(m->mod, LLVMCreateTargetDataLayout(target_machine));
+
+ #if LLVM_VERSION_MAJOR >= 18
+ if (build_context.fast_isel) {
+ LLVMSetTargetMachineFastISel(m->target_machine, true);
+ }
+ #endif
+
array_add(&target_machines, target_machine);
}
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index 29d2ccfe6..68f95cb03 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -528,7 +528,7 @@ gb_internal lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValu
gb_internal lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos);
gb_internal lbValue lb_const_source_code_location_const(lbModule *m, String const &procedure, TokenPos const &pos);
-gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos);
+gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TypeProc *procedure_type, Ast *call_expression);
gb_internal lbValue lb_equal_proc_for_type(lbModule *m, Type *type);
gb_internal lbValue lb_hasher_proc_for_type(lbModule *m, Type *type);
diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp
index 6a6b119aa..754bbfca2 100644
--- a/src/llvm_backend_const.cpp
+++ b/src/llvm_backend_const.cpp
@@ -154,7 +154,7 @@ gb_internal LLVMValueRef llvm_const_named_struct(lbModule *m, Type *t, LLVMValue
GB_ASSERT(value_count_ == bt->Struct.fields.count);
auto field_remapping = lb_get_struct_remapping(m, t);
- unsigned values_with_padding_count = LLVMCountStructElementTypes(struct_type);
+ unsigned values_with_padding_count = elem_count;
LLVMValueRef *values_with_padding = gb_alloc_array(permanent_allocator(), LLVMValueRef, values_with_padding_count);
for (unsigned i = 0; i < value_count; i++) {
@@ -722,7 +722,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
}
case ExactValue_Integer:
- if (is_type_pointer(type) || is_type_multi_pointer(type)) {
+ if (is_type_pointer(type) || is_type_multi_pointer(type) || is_type_proc(type)) {
LLVMTypeRef t = lb_type(m, original_type);
LLVMValueRef i = lb_big_int_to_llvm(m, t_uintptr, &value.value_integer);
res.value = LLVMConstIntToPtr(i, t);
diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp
index c896f889d..5cc79dcc8 100644
--- a/src/llvm_backend_debug.cpp
+++ b/src/llvm_backend_debug.cpp
@@ -55,6 +55,16 @@ gb_internal void lb_debug_file_line(lbModule *m, Ast *node, LLVMMetadataRef *fil
}
}
+gb_internal LLVMMetadataRef lb_debug_procedure_parameters(lbModule *m, Type *type) {
+ if (is_type_proc(type)) {
+ return lb_debug_type(m, t_rawptr);
+ }
+ if (type->kind == Type_Tuple && type->Tuple.variables.count == 1) {
+ return lb_debug_procedure_parameters(m, type->Tuple.variables[0]->type);
+ }
+ return lb_debug_type(m, type);
+}
+
gb_internal LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type) {
i64 size = type_size_of(type); // Check size
gb_unused(size);
@@ -72,13 +82,36 @@ gb_internal LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type)
parameter_count += 1;
}
}
- LLVMMetadataRef *parameters = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, parameter_count);
- unsigned param_index = 0;
- if (type->Proc.result_count == 0) {
- parameters[param_index++] = nullptr;
- } else {
- parameters[param_index++] = lb_debug_type(m, type->Proc.results);
+ auto parameters = array_make<LLVMMetadataRef>(permanent_allocator(), 0, type->Proc.param_count+type->Proc.result_count+2);
+
+ array_add(&parameters, cast(LLVMMetadataRef)nullptr);
+
+ bool return_is_tuple = false;
+ if (type->Proc.result_count != 0) {
+ Type *single_ret = reduce_tuple_to_single_type(type->Proc.results);
+ if (is_type_proc(single_ret)) {
+ single_ret = t_rawptr;
+ }
+ if (is_type_tuple(single_ret) && is_calling_convention_odin(type->Proc.calling_convention)) {
+ LLVMTypeRef actual = lb_type_internal_for_procedures_raw(m, type);
+ actual = LLVMGetReturnType(actual);
+ if (actual == nullptr) {
+ // results were passed as a single pointer
+ parameters[0] = lb_debug_procedure_parameters(m, single_ret);
+ } else {
+ LLVMTypeRef possible = lb_type(m, type->Proc.results);
+ if (possible == actual) {
+ // results were returned directly
+ parameters[0] = lb_debug_procedure_parameters(m, single_ret);
+ } else {
+ // resulsts were returned separately
+ return_is_tuple = true;
+ }
+ }
+ } else {
+ parameters[0] = lb_debug_procedure_parameters(m, single_ret);
+ }
}
LLVMMetadataRef file = nullptr;
@@ -88,8 +121,22 @@ gb_internal LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type)
if (e->kind != Entity_Variable) {
continue;
}
- parameters[param_index] = lb_debug_type(m, e->type);
- param_index += 1;
+ array_add(&parameters, lb_debug_procedure_parameters(m, e->type));
+ }
+
+
+ if (return_is_tuple) {
+ Type *results = type->Proc.results;
+ GB_ASSERT(results != nullptr && results->kind == Type_Tuple);
+ isize count = results->Tuple.variables.count;
+ parameters[0] = lb_debug_procedure_parameters(m, results->Tuple.variables[count-1]->type);
+ for (isize i = 0; i < count-1; i++) {
+ array_add(&parameters, lb_debug_procedure_parameters(m, results->Tuple.variables[i]->type));
+ }
+ }
+
+ if (type->Proc.calling_convention == ProcCC_Odin) {
+ array_add(&parameters, lb_debug_type(m, t_context_ptr));
}
LLVMDIFlags flags = LLVMDIFlagZero;
@@ -97,7 +144,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal_proc(lbModule *m, Type *type)
flags = LLVMDIFlagNoReturn;
}
- return LLVMDIBuilderCreateSubroutineType(m->debug_builder, file, parameters, parameter_count, flags);
+ return LLVMDIBuilderCreateSubroutineType(m->debug_builder, file, parameters.data, cast(unsigned)parameters.count, flags);
}
gb_internal LLVMMetadataRef lb_debug_struct_field(lbModule *m, String const &name, Type *type, u64 offset_in_bits) {
@@ -505,6 +552,48 @@ gb_internal LLVMMetadataRef lb_debug_bitset(lbModule *m, Type *type, String name
return final_decl;
}
+gb_internal LLVMMetadataRef lb_debug_bitfield(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
+ Type *bt = base_type(type);
+ GB_ASSERT(bt->kind == Type_BitField);
+
+ lb_debug_file_line(m, bt->BitField.node, &file, &line);
+
+ u64 size_in_bits = 8*type_size_of(bt);
+ u32 align_in_bits = 8*cast(u32)type_align_of(bt);
+
+ unsigned element_count = cast(unsigned)bt->BitField.fields.count;
+ LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
+
+ u64 offset_in_bits = 0;
+ for (unsigned i = 0; i < element_count; i++) {
+ Entity *f = bt->BitField.fields[i];
+ u8 bit_size = bt->BitField.bit_sizes[i];
+ GB_ASSERT(f->kind == Entity_Variable);
+ String name = f->token.string;
+ elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, line,
+ bit_size, offset_in_bits, 0,
+ LLVMDIFlagZero, lb_debug_type(m, f->type)
+ );
+
+ offset_in_bits += bit_size;
+ }
+
+ LLVMMetadataRef final_decl = LLVMDIBuilderCreateStructType(
+ m->debug_builder, scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ file, line,
+ size_in_bits, align_in_bits,
+ LLVMDIFlagZero,
+ nullptr,
+ elements, element_count,
+ 0,
+ nullptr,
+ "", 0
+ );
+ lb_set_llvm_metadata(m, type, final_decl);
+ return final_decl;
+}
+
gb_internal LLVMMetadataRef lb_debug_enum(lbModule *m, Type *type, String name, LLVMMetadataRef scope, LLVMMetadataRef file, unsigned line) {
Type *bt = base_type(type);
GB_ASSERT(bt->kind == Type_Enum);
@@ -769,6 +858,7 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
case Type_Union: return lb_debug_union( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_BitSet: return lb_debug_bitset( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_Enum: return lb_debug_enum( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
+ case Type_BitField: return lb_debug_bitfield( m, type, make_string_c(type_to_string(type, temporary_allocator())), nullptr, nullptr, 0);
case Type_Tuple:
if (type->Tuple.variables.count == 1) {
@@ -854,42 +944,6 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
lb_debug_type(m, type->Matrix.elem),
subscripts, gb_count_of(subscripts));
}
-
- case Type_BitField: {
- LLVMMetadataRef parent_scope = nullptr;
- LLVMMetadataRef scope = nullptr;
- LLVMMetadataRef file = nullptr;
- unsigned line = 0;
- u64 size_in_bits = 8*cast(u64)type_size_of(type);
- u32 align_in_bits = 8*cast(u32)type_align_of(type);
- LLVMDIFlags flags = LLVMDIFlagZero;
-
- unsigned element_count = cast(unsigned)type->BitField.fields.count;
- LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count);
-
- u64 offset_in_bits = 0;
- for (unsigned i = 0; i < element_count; i++) {
- Entity *f = type->BitField.fields[i];
- u8 bit_size = type->BitField.bit_sizes[i];
- GB_ASSERT(f->kind == Entity_Variable);
- String name = f->token.string;
- unsigned field_line = 0;
- LLVMDIFlags field_flags = LLVMDIFlagZero;
- elements[i] = LLVMDIBuilderCreateBitFieldMemberType(m->debug_builder, scope, cast(char const *)name.text, name.len, file, field_line,
- bit_size, offset_in_bits, offset_in_bits,
- field_flags, lb_debug_type(m, f->type)
- );
-
- offset_in_bits += bit_size;
- }
-
-
- return LLVMDIBuilderCreateStructType(m->debug_builder, parent_scope, "", 0, file, line,
- size_in_bits, align_in_bits, flags,
- nullptr, elements, element_count, 0, nullptr,
- "", 0
- );
- }
}
GB_PANIC("Invalid type %s", type_to_string(type));
@@ -969,12 +1023,13 @@ gb_internal LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
return lb_debug_struct(m, type, bt, name, scope, file, line);
}
- case Type_Struct: return lb_debug_struct(m, type, base_type(type), name, scope, file, line);
+ case Type_Struct: return lb_debug_struct(m, type, bt, name, scope, file, line);
case Type_Slice: return lb_debug_slice(m, type, name, scope, file, line);
case Type_DynamicArray: return lb_debug_dynamic_array(m, type, name, scope, file, line);
case Type_Union: return lb_debug_union(m, type, name, scope, file, line);
case Type_BitSet: return lb_debug_bitset(m, type, name, scope, file, line);
case Type_Enum: return lb_debug_enum(m, type, name, scope, file, line);
+ case Type_BitField: return lb_debug_bitfield(m, type, name, scope, file, line);
}
}
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp
index c2707612a..58467db2e 100644
--- a/src/llvm_backend_expr.cpp
+++ b/src/llvm_backend_expr.cpp
@@ -136,6 +136,11 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x,
switch (op) {
case Token_Xor:
opv = LLVMBuildNot(p->builder, v, "");
+ if (is_type_bit_set(elem_type)) {
+ ExactValue ev_mask = exact_bit_set_all_set_mask(elem_type);
+ lbValue mask = lb_const_value(p->module, elem_type, ev_mask);
+ opv = LLVMBuildAnd(p->builder, opv, mask.value, "");
+ }
break;
case Token_Sub:
if (is_type_float(elem_type)) {
@@ -176,8 +181,14 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x,
if (op == Token_Xor) {
lbValue cmp = {};
- cmp.value = LLVMBuildNot(p->builder, x.value, "");
cmp.type = x.type;
+ if (is_type_bit_set(x.type)) {
+ ExactValue ev_mask = exact_bit_set_all_set_mask(x.type);
+ lbValue mask = lb_const_value(p->module, x.type, ev_mask);
+ cmp.value = LLVMBuildXor(p->builder, x.value, mask.value, "");
+ } else {
+ cmp.value = LLVMBuildNot(p->builder, x.value, "");
+ }
return lb_emit_conv(p, cmp, type);
}
@@ -694,31 +705,37 @@ gb_internal lbValue lb_emit_matrix_flatten(lbProcedure *p, lbValue m, Type *type
lbAddr res = lb_add_local_generated(p, type, true);
- i64 row_count = mt->Matrix.row_count;
- i64 column_count = mt->Matrix.column_count;
- TEMPORARY_ALLOCATOR_GUARD();
-
- auto srcs = array_make<lbValue>(temporary_allocator(), 0, row_count*column_count);
- auto dsts = array_make<lbValue>(temporary_allocator(), 0, row_count*column_count);
-
- for (i64 j = 0; j < column_count; j++) {
- for (i64 i = 0; i < row_count; i++) {
- lbValue src = lb_emit_matrix_ev(p, m, i, j);
- array_add(&srcs, src);
- }
- }
-
- for (i64 j = 0; j < column_count; j++) {
- for (i64 i = 0; i < row_count; i++) {
- lbValue dst = lb_emit_array_epi(p, res.addr, i + j*row_count);
- array_add(&dsts, dst);
- }
- }
-
- GB_ASSERT(srcs.count == dsts.count);
- for_array(i, srcs) {
- lb_emit_store(p, dsts[i], srcs[i]);
- }
+ GB_ASSERT(type_size_of(type) == type_size_of(m.type));
+
+ lbValue m_ptr = lb_address_from_load_or_generate_local(p, m);
+ lbValue n = lb_const_int(p->module, t_int, type_size_of(type));
+ lb_mem_copy_non_overlapping(p, res.addr, m_ptr, n);
+
+ // i64 row_count = mt->Matrix.row_count;
+ // i64 column_count = mt->Matrix.column_count;
+ // TEMPORARY_ALLOCATOR_GUARD();
+
+ // auto srcs = array_make<lbValue>(temporary_allocator(), 0, row_count*column_count);
+ // auto dsts = array_make<lbValue>(temporary_allocator(), 0, row_count*column_count);
+
+ // for (i64 j = 0; j < column_count; j++) {
+ // for (i64 i = 0; i < row_count; i++) {
+ // lbValue src = lb_emit_matrix_ev(p, m, i, j);
+ // array_add(&srcs, src);
+ // }
+ // }
+
+ // for (i64 j = 0; j < column_count; j++) {
+ // for (i64 i = 0; i < row_count; i++) {
+ // lbValue dst = lb_emit_array_epi(p, res.addr, i + j*row_count);
+ // array_add(&dsts, dst);
+ // }
+ // }
+
+ // GB_ASSERT(srcs.count == dsts.count);
+ // for_array(i, srcs) {
+ // lb_emit_store(p, dsts[i], srcs[i]);
+ // }
return lb_addr_load(p, res);
}
@@ -3434,8 +3451,14 @@ gb_internal lbValue lb_build_expr_internal(lbProcedure *p, Ast *expr) {
switch (expr->kind) {
case_ast_node(bl, BasicLit, expr);
+ if (type != nullptr && type->Named.name == "Error") {
+ Entity *e = type->Named.type_name;
+ if (e->pkg && e->pkg->name == "os") {
+ return lb_const_nil(p->module, type);
+ }
+ }
TokenPos pos = bl->token.pos;
- GB_PANIC("Non-constant basic literal %s - %.*s", token_pos_to_string(pos), LIT(token_strings[bl->token.kind]));
+ GB_PANIC("Non-constant basic literal %s - %.*s (%s)", token_pos_to_string(pos), LIT(token_strings[bl->token.kind]), type_to_string(type));
case_end;
case_ast_node(bd, BasicDirective, expr);
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index e850d3364..d84599eb0 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -699,7 +699,9 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
}
if (e->Variable.param_value.kind != ParameterValue_Invalid) {
- lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, e->token.pos);
+ GB_ASSERT(e->Variable.param_value.kind != ParameterValue_Location);
+ GB_ASSERT(e->Variable.param_value.kind != ParameterValue_Expression);
+ lbValue c = lb_handle_param_value(p, e->type, e->Variable.param_value, nullptr, nullptr);
lb_addr_store(p, res, c);
}
@@ -3420,7 +3422,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
}
-gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos) {
+gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const &param_value, TypeProc *procedure_type, Ast* call_expression) {
switch (param_value.kind) {
case ParameterValue_Constant:
if (is_type_constant_type(parameter_type)) {
@@ -3446,8 +3448,60 @@ gb_internal lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type,
if (p->entity != nullptr) {
proc_name = p->entity->token.string;
}
+
+ ast_node(ce, CallExpr, call_expression);
+ TokenPos pos = ast_token(ce->proc).pos;
+
return lb_emit_source_code_location_as_global(p, proc_name, pos);
}
+ case ParameterValue_Expression:
+ {
+ Ast *orig = param_value.original_ast_expr;
+ if (orig->kind == Ast_BasicDirective) {
+ gbString expr = expr_to_string(call_expression, temporary_allocator());
+ return lb_const_string(p->module, make_string_c(expr));
+ }
+
+ isize param_idx = -1;
+ String param_str = {0};
+ {
+ Ast *call = unparen_expr(orig);
+ GB_ASSERT(call->kind == Ast_CallExpr);
+ ast_node(ce, CallExpr, call);
+ GB_ASSERT(ce->proc->kind == Ast_BasicDirective);
+ GB_ASSERT(ce->args.count == 1);
+ Ast *target = ce->args[0];
+ GB_ASSERT(target->kind == Ast_Ident);
+ String target_str = target->Ident.token.string;
+
+ param_idx = lookup_procedure_parameter(procedure_type, target_str);
+ param_str = target_str;
+ }
+ GB_ASSERT(param_idx >= 0);
+
+
+ Ast *target_expr = nullptr;
+ ast_node(ce, CallExpr, call_expression);
+
+ if (ce->split_args->positional.count > param_idx) {
+ target_expr = ce->split_args->positional[param_idx];
+ }
+
+ for_array(i, ce->split_args->named) {
+ Ast *arg = ce->split_args->named[i];
+ ast_node(fv, FieldValue, arg);
+ GB_ASSERT(fv->field->kind == Ast_Ident);
+ String name = fv->field->Ident.token.string;
+ if (name == param_str) {
+ target_expr = fv->value;
+ break;
+ }
+ }
+
+ gbString expr = expr_to_string(target_expr, temporary_allocator());
+ return lb_const_string(p->module, make_string_c(expr));
+ }
+
case ParameterValue_Value:
return lb_build_expr(p, param_value.ast_value);
}
@@ -3739,8 +3793,6 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
}
}
- TokenPos pos = ast_token(ce->proc).pos;
-
if (pt->params != nullptr) {
isize min_count = pt->params->Tuple.variables.count;
@@ -3764,7 +3816,7 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
args[arg_index] = lb_const_nil(p->module, e->type);
break;
case Entity_Variable:
- args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pos);
+ args[arg_index] = lb_handle_param_value(p, e->type, e->Variable.param_value, pt, expr);
break;
case Entity_Constant:
diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp
index f2fe4f7dc..df3d4bc03 100644
--- a/src/llvm_backend_stmt.cpp
+++ b/src/llvm_backend_stmt.cpp
@@ -1404,6 +1404,10 @@ gb_internal bool lb_switch_stmt_can_be_trivial_jump_table(AstSwitchStmt *ss, boo
}
+ if (is_typeid) {
+ return false;
+ }
+
return true;
}
@@ -2195,8 +2199,7 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
// and `LLVMConstIntGetZExtValue()` calls below will be valid and `LLVMInstructionEraseFromParent()`
// will target the correct (& only) branch statement
-
- if (cond.value && LLVMIsConstant(cond.value)) {
+ if (cond.value && LLVMIsAConstantInt(cond.value)) {
// NOTE(bill): Do a compile time short circuit for when the condition is constantly known.
// This done manually rather than relying on the SSA passes because sometimes the SSA passes
// miss some even if they are constantly known, especially with few optimization passes.
diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp
index 638170bfc..9d4505bb0 100644
--- a/src/llvm_backend_type.cpp
+++ b/src/llvm_backend_type.cpp
@@ -826,7 +826,7 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
if (t->Struct.soa_kind != StructSoa_None) {
- Type *kind_type = get_struct_field_type(tag_type, 10);
+ Type *kind_type = get_struct_field_type(tag_type, 7);
lbValue soa_kind = lb_const_value(m, kind_type, exact_value_i64(t->Struct.soa_kind));
LLVMValueRef soa_type = get_type_info_ptr(m, t->Struct.soa_elem);
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index 68c1e9d1e..f63c42ab9 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -263,7 +263,7 @@ gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) {
if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
res.value = LLVMBuildBitCast(p->builder, value.value, lb_type(p->module, t), "");
return res;
- } else if (is_type_array_like(src) && is_type_simd_vector(dst)) {
+ } else if (is_type_array_like(src) && (is_type_simd_vector(dst) || is_type_integer_128bit(dst))) {
unsigned align = cast(unsigned)gb_max(type_align_of(src), type_align_of(dst));
lbValue ptr = lb_address_from_load_or_generate_local(p, value);
if (lb_try_update_alignment(ptr, align)) {
diff --git a/src/main.cpp b/src/main.cpp
index 5131bdc21..a969e32a9 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -325,6 +325,7 @@ enum BuildFlagKind {
BuildFlag_NoTypeAssert,
BuildFlag_NoDynamicLiterals,
BuildFlag_NoCRT,
+ BuildFlag_NoRPath,
BuildFlag_NoEntryPoint,
BuildFlag_UseLLD,
BuildFlag_UseSeparateModules,
@@ -339,12 +340,14 @@ enum BuildFlagKind {
BuildFlag_VetUnused,
BuildFlag_VetUnusedImports,
BuildFlag_VetUnusedVariables,
+ BuildFlag_VetUnusedProcedures,
BuildFlag_VetUsingStmt,
BuildFlag_VetUsingParam,
BuildFlag_VetStyle,
BuildFlag_VetSemicolon,
BuildFlag_VetCast,
BuildFlag_VetTabs,
+ BuildFlag_VetPackages,
BuildFlag_CustomAttribute,
BuildFlag_IgnoreUnknownAttributes,
@@ -389,6 +392,7 @@ enum BuildFlagKind {
BuildFlag_PrintLinkerFlags,
// internal use only
+ BuildFlag_InternalFastISel,
BuildFlag_InternalIgnoreLazy,
BuildFlag_InternalIgnoreLLVMBuild,
BuildFlag_InternalIgnorePanic,
@@ -507,7 +511,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
auto build_flags = array_make<BuildFlag>(heap_allocator(), 0, BuildFlag_COUNT);
add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_SingleFile, str_lit("file"), BuildFlagParam_None, Command__does_build | Command__does_check);
- add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build | Command_test);
+ add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build | Command_test | Command_doc);
add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check);
@@ -532,6 +536,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_NoThreadLocal, str_lit("no-thread-local"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_NoRPath, str_lit("no-rpath"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_UseSeparateModules, str_lit("use-separate-modules"), BuildFlagParam_None, Command__does_build);
@@ -544,6 +549,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUnused, str_lit("vet-unused"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUnusedVariables, str_lit("vet-unused-variables"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_VetUnusedProcedures, str_lit("vet-unused-procedures"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUnusedImports, str_lit("vet-unused-imports"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetShadowing, str_lit("vet-shadowing"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUsingStmt, str_lit("vet-using-stmt"), BuildFlagParam_None, Command__does_check);
@@ -552,6 +558,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_VetSemicolon, str_lit("vet-semicolon"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetCast, str_lit("vet-cast"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetTabs, str_lit("vet-tabs"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_VetPackages, str_lit("vet-packages"), BuildFlagParam_String, Command__does_check);
add_flag(&build_flags, BuildFlag_CustomAttribute, str_lit("custom-attribute"), BuildFlagParam_String, Command__does_check, true);
add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check);
@@ -594,6 +601,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_PrintLinkerFlags, str_lit("print-linker-flags"), BuildFlagParam_None, Command_build);
+ add_flag(&build_flags, BuildFlag_InternalFastISel, str_lit("internal-fast-isel"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_InternalIgnoreLLVMBuild, str_lit("internal-ignore-llvm-build"),BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_InternalIgnorePanic, str_lit("internal-ignore-panic"), BuildFlagParam_None, Command_all);
@@ -1181,6 +1189,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_NoCRT:
build_context.no_crt = true;
break;
+ case BuildFlag_NoRPath:
+ build_context.no_rpath = true;
+ break;
case BuildFlag_NoEntryPoint:
build_context.no_entry_point = true;
break;
@@ -1213,6 +1224,36 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break;
case BuildFlag_VetCast: build_context.vet_flags |= VetFlag_Cast; break;
case BuildFlag_VetTabs: build_context.vet_flags |= VetFlag_Tabs; break;
+ case BuildFlag_VetUnusedProcedures:
+ build_context.vet_flags |= VetFlag_UnusedProcedures;
+ if (!set_flags[BuildFlag_VetPackages]) {
+ gb_printf_err("-%.*s must be used with -vet-packages\n", LIT(name));
+ bad_flags = true;
+ }
+ break;
+
+ case BuildFlag_VetPackages:
+ {
+ GB_ASSERT(value.kind == ExactValue_String);
+ String val = value.value_string;
+ String_Iterator it = {val, 0};
+ for (;;) {
+ String pkg = string_split_iterator(&it, ',');
+ if (pkg.len == 0) {
+ break;
+ }
+
+ pkg = string_trim_whitespace(pkg);
+ if (!string_is_valid_identifier(pkg)) {
+ gb_printf_err("-%.*s '%.*s' must be a valid identifier\n", LIT(name), LIT(pkg));
+ bad_flags = true;
+ continue;
+ }
+
+ string_set_add(&build_context.vet_packages, pkg);
+ }
+ }
+ break;
case BuildFlag_CustomAttribute:
{
@@ -1227,7 +1268,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
attr = string_trim_whitespace(attr);
if (!string_is_valid_identifier(attr)) {
- gb_printf_err("-custom-attribute '%.*s' must be a valid identifier\n", LIT(attr));
+ gb_printf_err("-%.*s '%.*s' must be a valid identifier\n", LIT(name), LIT(attr));
bad_flags = true;
continue;
}
@@ -1408,6 +1449,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
build_context.print_linker_flags = true;
break;
+ case BuildFlag_InternalFastISel:
+ build_context.fast_isel = true;
+ break;
case BuildFlag_InternalIgnoreLazy:
build_context.ignore_lazy = true;
break;
@@ -2154,6 +2198,12 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-doc-format");
print_usage_line(2, "Generates documentation as the .odin-doc format (useful for external tooling).");
print_usage_line(0, "");
+
+ print_usage_line(1, "-out:<filepath>");
+ print_usage_line(2, "Sets the base name of the resultig .odin-doc file.");
+ print_usage_line(2, "The extension can be optionally included; the resulting file will always have an extension of '.odin-doc'.");
+ print_usage_line(2, "Example: -out:foo");
+ print_usage_line(0, "");
}
if (run_or_build) {
@@ -2268,6 +2318,7 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(2, "Sets the build mode.");
print_usage_line(2, "Available options:");
print_usage_line(3, "-build-mode:exe Builds as an executable.");
+ print_usage_line(3, "-build-mode:test Builds as an executable that executes tests.");
print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library.");
print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library.");
print_usage_line(3, "-build-mode:lib Builds as a statically linked library.");
@@ -2309,6 +2360,10 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(2, "Disables automatic linking with the C Run Time.");
print_usage_line(0, "");
+ print_usage_line(1, "-no-rpath");
+ print_usage_line(2, "Disables automatic addition of an rpath linked to the executable directory.");
+ print_usage_line(0, "");
+
print_usage_line(1, "-no-thread-local");
print_usage_line(2, "Ignores @thread_local attribute, effectively treating the program as if it is single-threaded.");
print_usage_line(0, "");
@@ -2343,7 +2398,7 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(0, "");
print_usage_line(1, "-vet-unused");
- print_usage_line(2, "Checks for unused declarations.");
+ print_usage_line(2, "Checks for unused declarations (variables and imports).");
print_usage_line(0, "");
print_usage_line(1, "-vet-unused-variables");
@@ -2385,6 +2440,16 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-vet-tabs");
print_usage_line(2, "Errs when the use of tabs has not been used for indentation.");
print_usage_line(0, "");
+
+ print_usage_line(1, "-vet-packages:<comma-separated-strings>");
+ print_usage_line(2, "Sets which packages by name will be vetted.");
+ print_usage_line(2, "Files with specific +vet tags will not be ignored if they are not in the packages set.");
+ print_usage_line(0, "");
+
+ print_usage_line(1, "-vet-unused-procedures");
+ print_usage_line(2, "Checks for unused procedures.");
+ print_usage_line(2, "Must be used with -vet-packages or specified on a per file with +vet tags.");
+ print_usage_line(0, "");
}
if (check) {
@@ -3129,6 +3194,9 @@ int main(int arg_count, char const **arg_ptr) {
build_context.command = command;
+ string_set_init(&build_context.custom_attributes);
+ string_set_init(&build_context.vet_packages);
+
if (!parse_build_flags(args)) {
return 1;
}
@@ -3252,6 +3320,12 @@ int main(int arg_count, char const **arg_ptr) {
gb_printf_err("missing required target feature: \"%.*s\", enable it by setting a different -microarch or explicitly adding it through -target-features\n", LIT(disabled));
gb_exit(1);
}
+
+ // NOTE(laytan): some weird errors on LLVM 14 that LLVM 17 fixes.
+ if (LLVM_VERSION_MAJOR < 17) {
+ gb_printf_err("Invalid LLVM version %s, RISC-V targets require at least LLVM 17\n", LLVM_VERSION_STRING);
+ gb_exit(1);
+ }
}
if (build_context.show_debug_messages) {
diff --git a/src/microsoft_craziness.h b/src/microsoft_craziness.h
index 899583143..b0fd22a23 100644
--- a/src/microsoft_craziness.h
+++ b/src/microsoft_craziness.h
@@ -684,8 +684,14 @@ gb_internal void find_visual_studio_paths_from_env_vars(Find_Result *result) {
? str_lit("lib\\x64\\")
: str_lit("lib\\x86\\");
- result->vs_exe_path = mc_concat(vctid, exe);
- result->vs_library_path = mc_concat(vctid, lib);
+ if (string_ends_with(vctid, str_lit("\\"))) {
+ result->vs_exe_path = mc_concat(vctid, exe);
+ result->vs_library_path = mc_concat(vctid, lib);
+ } else {
+ result->vs_exe_path = mc_concat(vctid, str_lit("\\"), exe);
+ result->vs_library_path = mc_concat(vctid, str_lit("\\"), lib);
+ }
+
vs_found = true;
}
diff --git a/src/parser.cpp b/src/parser.cpp
index 5796012d9..520a23c5a 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -1,10 +1,28 @@
#include "parser_pos.cpp"
+gb_internal bool in_vet_packages(AstFile *file) {
+ if (file == nullptr) {
+ return true;
+ }
+ if (file->pkg == nullptr) {
+ return true;
+ }
+ if (build_context.vet_packages.entries.count == 0) {
+ return true;
+ }
+ return string_set_exists(&build_context.vet_packages, file->pkg->name);
+}
+
gb_internal u64 ast_file_vet_flags(AstFile *f) {
if (f != nullptr && f->vet_flags_set) {
return f->vet_flags;
}
- return build_context.vet_flags;
+
+ bool found = in_vet_packages(f);
+ if (found) {
+ return build_context.vet_flags;
+ }
+ return 0;
}
gb_internal bool ast_file_vet_style(AstFile *f) {
@@ -1921,6 +1939,9 @@ gb_internal Array<Ast *> parse_enum_field_list(AstFile *f) {
f->curr_token.kind != Token_EOF) {
CommentGroup *docs = f->lead_comment;
CommentGroup *comment = nullptr;
+
+ parse_enforce_tabs(f);
+
Ast *name = parse_value(f);
Ast *value = nullptr;
if (f->curr_token.kind == Token_Eq) {
@@ -2259,6 +2280,7 @@ gb_internal Array<Ast *> parse_union_variant_list(AstFile *f) {
auto variants = array_make<Ast *>(ast_allocator(f));
while (f->curr_token.kind != Token_CloseBrace &&
f->curr_token.kind != Token_EOF) {
+ parse_enforce_tabs(f);
Ast *type = parse_type(f);
if (type->kind != Ast_BadExpr) {
array_add(&variants, type);
@@ -4274,6 +4296,7 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl
while (f->curr_token.kind != follow &&
f->curr_token.kind != Token_Colon &&
f->curr_token.kind != Token_EOF) {
+ if (!is_signature) parse_enforce_tabs(f);
u32 flags = parse_field_prefixes(f);
Ast *param = parse_var_type(f, allow_ellipsis, allow_typeid_token);
if (param->kind == Ast_Ellipsis) {
@@ -4363,6 +4386,8 @@ gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_fl
f->curr_token.kind != Token_EOF &&
f->curr_token.kind != Token_Semicolon) {
CommentGroup *docs = f->lead_comment;
+
+ if (!is_signature) parse_enforce_tabs(f);
u32 set_flags = parse_field_prefixes(f);
Token tag = {};
Array<Ast *> names = parse_ident_list(f, allow_poly_names);
@@ -4532,6 +4557,10 @@ gb_internal Ast *parse_if_stmt(AstFile *f) {
return ast_bad_stmt(f, f->curr_token, f->curr_token);
}
+ Ast *top_if_stmt = nullptr;
+
+ Ast *prev_if_stmt = nullptr;
+if_else_chain:;
Token token = expect_token(f, Token_if);
Ast *init = nullptr;
Ast *cond = nullptr;
@@ -4573,12 +4602,24 @@ gb_internal Ast *parse_if_stmt(AstFile *f) {
ignore_strict_style = true;
}
skip_possible_newline_for_literal(f, ignore_strict_style);
+
+ Ast *curr_if_stmt = ast_if_stmt(f, token, init, cond, body, nullptr);
+ if (top_if_stmt == nullptr) {
+ top_if_stmt = curr_if_stmt;
+ }
+ if (prev_if_stmt != nullptr) {
+ prev_if_stmt->IfStmt.else_stmt = curr_if_stmt;
+ }
+
if (f->curr_token.kind == Token_else) {
Token else_token = expect_token(f, Token_else);
switch (f->curr_token.kind) {
case Token_if:
- else_stmt = parse_if_stmt(f);
- break;
+ // NOTE(bill): Instead of relying on recursive descent for an if-else chain
+ // we can just inline the tail-recursion manually with a simple loop like
+ // construct using a `goto`
+ prev_if_stmt = curr_if_stmt;
+ goto if_else_chain;
case Token_OpenBrace:
else_stmt = parse_block_stmt(f, false);
break;
@@ -4593,7 +4634,9 @@ gb_internal Ast *parse_if_stmt(AstFile *f) {
}
}
- return ast_if_stmt(f, token, init, cond, body, else_stmt);
+ curr_if_stmt->IfStmt.else_stmt = else_stmt;
+
+ return top_if_stmt;
}
gb_internal Ast *parse_when_stmt(AstFile *f) {
@@ -4924,7 +4967,7 @@ gb_internal Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) {
}
if (f->in_when_statement) {
- syntax_error(import_name, "Cannot use 'import' within a 'when' statement. Prefer using the file suffixes (e.g. foo_windows.odin) or '//+build' tags");
+ syntax_error(import_name, "Cannot use 'import' within a 'when' statement. Prefer using the file suffixes (e.g. foo_windows.odin) or '#+build' tags");
}
if (kind != ImportDecl_Standard) {
@@ -5312,6 +5355,12 @@ gb_internal Ast *parse_stmt(AstFile *f) {
s = ast_empty_stmt(f, token);
expect_semicolon(f);
return s;
+
+ case Token_FileTag:
+ // This is always an error because all valid file tags will have been processed in `parse_file` already.
+ // Any remaining file tags must be past the package line and thus invalid.
+ syntax_error(token, "Lines starting with #+ (file tags) are only allowed before the package line.");
+ return ast_bad_stmt(f, token, f->curr_token);
}
// Error correction statements
@@ -5347,16 +5396,12 @@ gb_internal Ast *parse_stmt(AstFile *f) {
}
-
-gb_internal u64 check_vet_flags(AstFile *file) {
- if (file && file->vet_flags_set) {
- return file->vet_flags;
+gb_internal void parse_enforce_tabs(AstFile *f) {
+ // Checks to see if tabs have been used for indentation
+ if ((ast_file_vet_flags(f) & VetFlag_Tabs) == 0) {
+ return;
}
- return build_context.vet_flags;
-}
-
-gb_internal void parse_enforce_tabs(AstFile *f) {
Token prev = f->prev_token;
Token curr = f->curr_token;
if (prev.pos.line < curr.pos.line) {
@@ -5373,6 +5418,10 @@ gb_internal void parse_enforce_tabs(AstFile *f) {
isize len = end-it;
for (isize i = 0; i < len; i++) {
+ if (it[i] == '/') {
+ // ignore comments
+ break;
+ }
if (it[i] == ' ') {
syntax_error(curr, "With '-vet-tabs', tabs must be used for indentation");
break;
@@ -5387,11 +5436,7 @@ gb_internal Array<Ast *> parse_stmt_list(AstFile *f) {
while (f->curr_token.kind != Token_case &&
f->curr_token.kind != Token_CloseBrace &&
f->curr_token.kind != Token_EOF) {
-
- // Checks to see if tabs have been used for indentation
- if (check_vet_flags(f) & VetFlag_Tabs) {
- parse_enforce_tabs(f);
- }
+ parse_enforce_tabs(f);
Ast *stmt = parse_stmt(f);
if (stmt && stmt->kind != Ast_EmptyStmt) {
@@ -5902,20 +5947,6 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
do_error(node, "Unknown library collection: '%.*s'", LIT(collection_name));
return false;
}
- } else {
-#if !defined(GB_SYSTEM_WINDOWS)
- // @NOTE(vassvik): foreign imports of shared libraries that are not in the system collection on
- // linux/mac have to be local to the executable for consistency with shared libraries.
- // Unix does not have a concept of "import library" for shared/dynamic libraries,
- // so we need to pass the relative path to the linker, and add the current
- // working directory of the exe to the library search paths.
- // Static libraries can be linked directly with the full pathname
- //
- if (node->kind == Ast_ForeignImportDecl && (string_ends_with(file_str, str_lit(".so")) || string_contains_string(file_str, str_lit(".so.")))) {
- *path = file_str;
- return true;
- }
-#endif
}
if (is_package_name_reserved(file_str)) {
@@ -6061,7 +6092,7 @@ gb_internal String build_tag_get_token(String s, String *out) {
}
gb_internal bool parse_build_tag(Token token_for_pos, String s) {
- String const prefix = str_lit("+build");
+ String const prefix = str_lit("build");
GB_ASSERT(string_starts_with(s, prefix));
s = string_trim_whitespace(substring(s, prefix.len, s.len));
@@ -6074,6 +6105,10 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
while (s.len > 0) {
bool this_kind_correct = true;
+ bool this_kind_os_seen = false;
+ bool this_kind_arch_seen = false;
+ int num_tokens = 0;
+
do {
String p = string_trim_whitespace(build_tag_get_token(s, &s));
if (p.len == 0) break;
@@ -6099,7 +6134,18 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
TargetOsKind os = get_target_os_from_string(p);
TargetArchKind arch = get_target_arch_from_string(p);
+ num_tokens += 1;
+
+ // Catches 'windows linux', which is an impossible combination.
+ // Also catches usage of more than two things within a comma separated group.
+ if (num_tokens > 2 || (this_kind_os_seen && os != TargetOs_Invalid) || (this_kind_arch_seen && arch != TargetArch_Invalid)) {
+ syntax_error(token_for_pos, "Invalid build tag: Missing ',' before '%.*s'. Format: '#+build linux, windows amd64, darwin'", LIT(p));
+ break;
+ }
+
if (os != TargetOs_Invalid) {
+ this_kind_os_seen = true;
+
GB_ASSERT(arch == TargetArch_Invalid);
if (is_notted) {
this_kind_correct = this_kind_correct && (os != build_context.metrics.os);
@@ -6107,6 +6153,8 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
this_kind_correct = this_kind_correct && (os == build_context.metrics.os);
}
} else if (arch != TargetArch_Invalid) {
+ this_kind_arch_seen = true;
+
if (is_notted) {
this_kind_correct = this_kind_correct && (arch != build_context.metrics.arch);
} else {
@@ -6146,7 +6194,7 @@ gb_internal String vet_tag_get_token(String s, String *out) {
gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
- String const prefix = str_lit("+vet");
+ String const prefix = str_lit("vet");
GB_ASSERT(string_starts_with(s, prefix));
s = string_trim_whitespace(substring(s, prefix.len, s.len));
@@ -6251,7 +6299,7 @@ gb_internal isize calc_decl_count(Ast *decl) {
}
gb_internal bool parse_build_project_directory_tag(Token token_for_pos, String s) {
- String const prefix = str_lit("+build-project-name");
+ String const prefix = str_lit("build-project-name");
GB_ASSERT(string_starts_with(s, prefix));
s = string_trim_whitespace(substring(s, prefix.len, s.len));
if (s.len == 0) {
@@ -6295,6 +6343,48 @@ gb_internal bool parse_build_project_directory_tag(Token token_for_pos, String s
return any_correct;
}
+gb_internal bool parse_file_tag(const String &lc, const Token &tok, AstFile *f) {
+ if (string_starts_with(lc, str_lit("build-project-name"))) {
+ if (!parse_build_project_directory_tag(tok, lc)) {
+ return false;
+ }
+ } else if (string_starts_with(lc, str_lit("build"))) {
+ if (!parse_build_tag(tok, lc)) {
+ return false;
+ }
+ } else if (string_starts_with(lc, str_lit("vet"))) {
+ f->vet_flags = parse_vet_tag(tok, lc);
+ f->vet_flags_set = true;
+ } else if (string_starts_with(lc, str_lit("ignore"))) {
+ return false;
+ } else if (string_starts_with(lc, str_lit("private"))) {
+ f->flags |= AstFile_IsPrivatePkg;
+ String command = string_trim_starts_with(lc, str_lit("private "));
+ command = string_trim_whitespace(command);
+ if (lc == "private") {
+ f->flags |= AstFile_IsPrivatePkg;
+ } else if (command == "package") {
+ f->flags |= AstFile_IsPrivatePkg;
+ } else if (command == "file") {
+ f->flags |= AstFile_IsPrivateFile;
+ }
+ } else if (lc == "lazy") {
+ if (build_context.ignore_lazy) {
+ // Ignore
+ } else if (f->pkg->kind == Package_Init && build_context.command_kind == Command_doc) {
+ // Ignore
+ } else {
+ f->flags |= AstFile_IsLazy;
+ }
+ } else if (lc == "no-instrumentation") {
+ f->flags |= AstFile_NoInstrumentation;
+ } else {
+ error(tok, "Unknown tag '%.*s'", LIT(lc));
+ }
+
+ return true;
+}
+
gb_internal bool parse_file(Parser *p, AstFile *f) {
if (f->tokens.count == 0) {
return true;
@@ -6313,9 +6403,34 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
CommentGroup *docs = f->lead_comment;
+ Array<Token> tags = array_make<Token>(temporary_allocator());
+ bool first_invalid_token_set = false;
+ Token first_invalid_token = {};
+
+ while (f->curr_token.kind != Token_package && f->curr_token.kind != Token_EOF) {
+ if (f->curr_token.kind == Token_Comment) {
+ consume_comment_groups(f, f->prev_token);
+ } else if (f->curr_token.kind == Token_FileTag) {
+ array_add(&tags, f->curr_token);
+ advance_token(f);
+ } else {
+ if (!first_invalid_token_set) {
+ first_invalid_token_set = true;
+ first_invalid_token = f->curr_token;
+ }
+
+ advance_token(f);
+ }
+ }
+
if (f->curr_token.kind != Token_package) {
ERROR_BLOCK();
- syntax_error(f->curr_token, "Expected a package declaration at the beginning of the file");
+
+ // The while loop above scanned until it found the package token. If we never
+ // found one, then make this error appear on the first invalid token line.
+ Token t = first_invalid_token_set ? first_invalid_token : f->curr_token;
+ syntax_error(t, "Expected a package declaration at the beginning of the file");
+
// IMPORTANT NOTE(bill): this is technically a race condition with the suggestion, but it's ony a suggession
// so in practice is should be "fine"
if (f->pkg && f->pkg->name != "") {
@@ -6324,18 +6439,16 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
return false;
}
+ // There was an OK package declaration. But there some invalid token was hit before the package declaration.
+ if (first_invalid_token_set) {
+ syntax_error(first_invalid_token, "Expected only comments or lines starting with '#+' before the package declaration");
+ return false;
+ }
+
f->package_token = expect_token(f, Token_package);
if (f->package_token.kind != Token_package) {
return false;
}
- if (docs != nullptr) {
- TokenPos end = token_pos_end(docs->list[docs->list.count-1]);
- if (end.line == f->package_token.pos.line || end.line+1 == f->package_token.pos.line) {
- // Okay
- } else {
- docs = nullptr;
- }
- }
Token package_name = expect_token_after(f, Token_Ident, "package");
if (package_name.kind == Token_Ident) {
@@ -6349,53 +6462,40 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
}
f->package_name = package_name.string;
- if (!f->pkg->is_single_file && docs != nullptr && docs->list.count > 0) {
- for (Token const &tok : docs->list) {
- GB_ASSERT(tok.kind == Token_Comment);
- String str = tok.string;
- if (string_starts_with(str, str_lit("//"))) {
+ // TODO: Shouldn't single file only matter for build tags? no-instrumentation for example
+ // should be respected even when in single file mode.
+ if (!f->pkg->is_single_file) {
+ if (docs != nullptr && docs->list.count > 0) {
+ for (Token const &tok : docs->list) {
+ GB_ASSERT(tok.kind == Token_Comment);
+ String str = tok.string;
+
+ if (!string_starts_with(str, str_lit("//"))) {
+ continue;
+ }
+
String lc = string_trim_whitespace(substring(str, 2, str.len));
- if (lc.len > 0 && lc[0] == '+') {
- if (string_starts_with(lc, str_lit("+build-project-name"))) {
- if (!parse_build_project_directory_tag(tok, lc)) {
- return false;
- }
- } else if (string_starts_with(lc, str_lit("+build"))) {
- if (!parse_build_tag(tok, lc)) {
- return false;
- }
- } else if (string_starts_with(lc, str_lit("+vet"))) {
- f->vet_flags = parse_vet_tag(tok, lc);
- f->vet_flags_set = true;
- } else if (string_starts_with(lc, str_lit("+ignore"))) {
+ if (string_starts_with(lc, str_lit("+"))) {
+ syntax_warning(tok, "'//+' is deprecated: Use '#+' instead");
+ String lt = substring(lc, 1, lc.len);
+ if (parse_file_tag(lt, tok, f) == false) {
return false;
- } else if (string_starts_with(lc, str_lit("+private"))) {
- f->flags |= AstFile_IsPrivatePkg;
- String command = string_trim_starts_with(lc, str_lit("+private "));
- command = string_trim_whitespace(command);
- if (lc == "+private") {
- f->flags |= AstFile_IsPrivatePkg;
- } else if (command == "package") {
- f->flags |= AstFile_IsPrivatePkg;
- } else if (command == "file") {
- f->flags |= AstFile_IsPrivateFile;
- }
- } else if (lc == "+lazy") {
- if (build_context.ignore_lazy) {
- // Ignore
- } else if (f->pkg->kind == Package_Init && build_context.command_kind == Command_doc) {
- // Ignore
- } else {
- f->flags |= AstFile_IsLazy;
- }
- } else if (lc == "+no-instrumentation") {
- f->flags |= AstFile_NoInstrumentation;
- } else {
- warning(tok, "Ignoring unknown tag '%.*s'", LIT(lc));
}
}
}
}
+
+ for (Token const &tok : tags) {
+ GB_ASSERT(tok.kind == Token_FileTag);
+ String str = tok.string;
+
+ if (string_starts_with(str, str_lit("#+"))) {
+ String lt = string_trim_whitespace(substring(str, 2, str.len));
+ if (parse_file_tag(lt, tok, f) == false) {
+ return false;
+ }
+ }
+ }
}
Ast *pd = ast_package_decl(f, f->package_token, package_name, docs, f->line_comment);
@@ -6416,7 +6516,7 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
}
f->total_file_decl_count += calc_decl_count(stmt);
- if (stmt->kind == Ast_WhenStmt || stmt->kind == Ast_ExprStmt || stmt->kind == Ast_ImportDecl) {
+ if (stmt->kind == Ast_WhenStmt || stmt->kind == Ast_ExprStmt || stmt->kind == Ast_ImportDecl || stmt->kind == Ast_ForeignBlockDecl) {
f->delayed_decl_count += 1;
}
}
diff --git a/src/parser.hpp b/src/parser.hpp
index 565a8e621..d5117a31e 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -82,6 +82,7 @@ enum AstFileFlag : u32 {
enum AstDelayQueueKind {
AstDelayQueue_Import,
AstDelayQueue_Expr,
+ AstDelayQueue_ForeignBlock,
AstDelayQueue_COUNT,
};
@@ -885,4 +886,7 @@ gb_internal gb_inline gbAllocator ast_allocator(AstFile *f) {
gb_internal Ast *alloc_ast_node(AstFile *f, AstKind kind);
gb_internal gbString expr_to_string(Ast *expression);
-gb_internal bool allow_field_separator(AstFile *f); \ No newline at end of file
+gb_internal bool allow_field_separator(AstFile *f);
+
+
+gb_internal void parse_enforce_tabs(AstFile *f); \ No newline at end of file
diff --git a/src/threading.cpp b/src/threading.cpp
index e30a20a06..af8fd803c 100644
--- a/src/threading.cpp
+++ b/src/threading.cpp
@@ -529,6 +529,9 @@ gb_internal gb_inline void yield_thread(void) {
_mm_pause();
#elif defined(GB_CPU_ARM)
__asm__ volatile ("yield" : : : "memory");
+#elif defined(GB_CPU_RISCV)
+ // I guess?
+ __asm__ volatile ("nop" : : : "memory");
#else
#error Unknown architecture
#endif
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index 4425bee29..53f6135d0 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -2,6 +2,7 @@
TOKEN_KIND(Token_Invalid, "Invalid"), \
TOKEN_KIND(Token_EOF, "EOF"), \
TOKEN_KIND(Token_Comment, "Comment"), \
+ TOKEN_KIND(Token_FileTag, "FileTag"), \
\
TOKEN_KIND(Token__LiteralBegin, ""), \
TOKEN_KIND(Token_Ident, "identifier"), \
@@ -939,6 +940,20 @@ gb_internal void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
if (t->curr_rune == '!') {
token->kind = Token_Comment;
tokenizer_skip_line(t);
+ } else if (t->curr_rune == '+') {
+ token->kind = Token_FileTag;
+
+ // Skip until end of line or until we hit what is probably a comment.
+ // The parsing of tags happens in `parse_file`.
+ while (t->curr_rune != GB_RUNE_EOF) {
+ if (t->curr_rune == '\n') {
+ break;
+ }
+ if (t->curr_rune == '/') {
+ break;
+ }
+ advance_to_next_rune(t);
+ }
}
break;
case '/':
diff --git a/src/types.cpp b/src/types.cpp
index 21dd6ad39..a9a7d6dda 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -1474,6 +1474,7 @@ gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) {
Type *elem = t->Matrix.elem;
i64 row_count = gb_max(t->Matrix.row_count, 1);
+ i64 column_count = gb_max(t->Matrix.column_count, 1);
bool pop = type_path_push(tp, elem);
if (tp->failure) {
@@ -1491,7 +1492,7 @@ gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) {
// could be maximally aligned but as a compromise, having no padding will be
// beneficial to third libraries that assume no padding
- i64 total_expected_size = row_count*t->Matrix.column_count*elem_size;
+ i64 total_expected_size = row_count*column_count*elem_size;
// i64 min_alignment = prev_pow2(elem_align * row_count);
i64 min_alignment = prev_pow2(total_expected_size);
while (total_expected_size != 0 && (total_expected_size % min_alignment) != 0) {
@@ -1523,12 +1524,15 @@ gb_internal i64 matrix_type_stride_in_bytes(Type *t, struct TypePath *tp) {
i64 stride_in_bytes = 0;
// NOTE(bill, 2021-10-25): The alignment strategy here is to have zero padding
- // It would be better for performance to pad each column so that each column
+ // It would be better for performance to pad each column/row so that each column/row
// could be maximally aligned but as a compromise, having no padding will be
// beneficial to third libraries that assume no padding
- i64 row_count = t->Matrix.row_count;
- stride_in_bytes = elem_size*row_count;
-
+
+ if (t->Matrix.is_row_major) {
+ stride_in_bytes = elem_size*t->Matrix.column_count;
+ } else {
+ stride_in_bytes = elem_size*t->Matrix.row_count;
+ }
t->Matrix.stride_in_bytes = stride_in_bytes;
return stride_in_bytes;
}
@@ -2810,8 +2814,14 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
case Type_Union:
if (x->Union.variants.count == y->Union.variants.count &&
- x->Union.custom_align == y->Union.custom_align &&
x->Union.kind == y->Union.kind) {
+
+ if (x->Union.custom_align != y->Union.custom_align) {
+ if (type_align_of(x) != type_align_of(y)) {
+ return false;
+ }
+ }
+
// NOTE(bill): zeroth variant is nullptr
for_array(i, x->Union.variants) {
if (!are_types_identical(x->Union.variants[i], y->Union.variants[i])) {
@@ -2827,10 +2837,16 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
x->Struct.is_no_copy == y->Struct.is_no_copy &&
x->Struct.fields.count == y->Struct.fields.count &&
x->Struct.is_packed == y->Struct.is_packed &&
- x->Struct.custom_align == y->Struct.custom_align &&
x->Struct.soa_kind == y->Struct.soa_kind &&
x->Struct.soa_count == y->Struct.soa_count &&
are_types_identical(x->Struct.soa_elem, y->Struct.soa_elem)) {
+
+ if (x->Struct.custom_align != y->Struct.custom_align) {
+ if (type_align_of(x) != type_align_of(y)) {
+ return false;
+ }
+ }
+
for_array(i, x->Struct.fields) {
Entity *xf = x->Struct.fields[i];
Entity *yf = y->Struct.fields[i];
@@ -4205,7 +4221,11 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
case Type_Matrix: {
i64 stride_in_bytes = matrix_type_stride_in_bytes(t, path);
- return stride_in_bytes * t->Matrix.column_count;
+ if (t->Matrix.is_row_major) {
+ return stride_in_bytes * t->Matrix.row_count;
+ } else {
+ return stride_in_bytes * t->Matrix.column_count;
+ }
}
case Type_BitField: