aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2026-01-30 10:49:55 +0000
committerGitHub <noreply@github.com>2026-01-30 10:49:55 +0000
commit19b545e7cb0b09a7c8b3424ca8276b9e37f8de80 (patch)
tree974b844bf082c526f6a12396c4a80adfc73b9a60 /src
parent8b745c3909a3482aebe27998d8b870286e448e35 (diff)
parent5a21213fa5e2c74d5021adb2a87f0cc441a38eab (diff)
Merge branch 'master' into bill/feature-using-stmt
Diffstat (limited to 'src')
-rw-r--r--src/build_settings.cpp19
-rw-r--r--src/check_builtin.cpp26
-rw-r--r--src/check_decl.cpp1
-rw-r--r--src/check_expr.cpp152
-rw-r--r--src/check_stmt.cpp4
-rw-r--r--src/check_type.cpp29
-rw-r--r--src/checker.cpp48
-rw-r--r--src/checker.hpp1
-rw-r--r--src/entity.cpp1
-rw-r--r--src/exact_value.cpp13
-rw-r--r--src/linker.cpp18
-rw-r--r--src/llvm_backend.cpp23
-rw-r--r--src/llvm_backend.hpp10
-rw-r--r--src/llvm_backend_const.cpp23
-rw-r--r--src/llvm_backend_expr.cpp88
-rw-r--r--src/llvm_backend_general.cpp2
-rw-r--r--src/llvm_backend_opt.cpp10
-rw-r--r--src/llvm_backend_proc.cpp65
-rw-r--r--src/llvm_backend_stmt.cpp76
-rw-r--r--src/llvm_backend_utility.cpp15
-rw-r--r--src/main.cpp32
-rw-r--r--src/parser.cpp131
-rw-r--r--src/parser.hpp20
-rw-r--r--src/string.cpp107
-rw-r--r--src/types.cpp74
25 files changed, 731 insertions, 257 deletions
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 12631c403..6b8fefa59 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -360,14 +360,13 @@ enum OptInFeatureFlags : u64 {
OptInFeatureFlag_IntegerDivisionByZero_Self = 1u<<4,
OptInFeatureFlag_IntegerDivisionByZero_AllBits = 1u<<5,
- OptInFeatureFlag_UsingStmt = 1u<<6,
-
-
OptInFeatureFlag_IntegerDivisionByZero_ALL = OptInFeatureFlag_IntegerDivisionByZero_Trap|
OptInFeatureFlag_IntegerDivisionByZero_Zero|
OptInFeatureFlag_IntegerDivisionByZero_Self|
OptInFeatureFlag_IntegerDivisionByZero_AllBits,
+ OptInFeatureFlag_ForceTypeAssert = 1u<<6,
+ OptInFeatureFlag_UsingStmt = 1u<<7,
};
u64 get_feature_flag_from_name(String const &name) {
@@ -388,6 +387,9 @@ u64 get_feature_flag_from_name(String const &name) {
}
if (name == "using-stmt") {
return OptInFeatureFlag_UsingStmt;
+ }
+ if (name == "force-type-assert") {
+ return OptInFeatureFlag_ForceTypeAssert;
}
@@ -587,6 +589,7 @@ struct BuildContext {
RelocMode reloc_mode;
bool disable_red_zone;
+ bool disable_unwind;
isize max_error_count;
@@ -844,6 +847,15 @@ gb_global TargetMetrics target_freestanding_amd64_win64 = {
TargetABI_Win64,
};
+gb_global TargetMetrics target_freestanding_amd64_mingw = {
+ TargetOs_freestanding,
+ TargetArch_amd64,
+ 8, 8, AMD64_MAX_ALIGNMENT, 32,
+ str_lit("x86_64-pc-windows-gnu"),
+ TargetABI_Win64,
+};
+
+
gb_global TargetMetrics target_freestanding_arm64 = {
TargetOs_freestanding,
TargetArch_arm64,
@@ -906,6 +918,7 @@ gb_global NamedTargetMetrics named_targets[] = {
{ str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv },
{ str_lit("freestanding_amd64_win64"), &target_freestanding_amd64_win64 },
+ { str_lit("freestanding_amd64_mingw"), &target_freestanding_amd64_mingw },
{ str_lit("freestanding_arm64"), &target_freestanding_arm64 },
{ str_lit("freestanding_arm32"), &target_freestanding_arm32 },
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index 1b3e6912c..e732d8ec3 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -2695,6 +2695,16 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_size_of: {
// size_of :: proc(Type or expr) -> untyped int
+ if (ce->args[0]->kind == Ast_UnaryExpr) {
+ ast_node(arg, UnaryExpr, ce->args[0]);
+ if (arg->op.kind == Token_And) {
+ ERROR_BLOCK();
+
+ warning(ce->args[0], "'size_of(&x)' returns the size of a pointer, not the size of x");
+ error_line("\tSuggestion: Use 'size_of(rawptr)' if you want the size of the pointer");
+ }
+ }
+
Operand o = {};
check_expr_or_type(c, &o, ce->args[0]);
if (o.mode == Addressing_Invalid) {
@@ -5373,6 +5383,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return false;
}
}
+ if (!are_types_identical(x.type, y.type)) {
+ gbString xts = type_to_string(x.type);
+ gbString yts = type_to_string(y.type);
+ error(x.expr, "Mismatched types for '%.*s', got %s vs %s", LIT(builtin_name), xts, yts);
+ gb_string_free(yts);
+ gb_string_free(xts);
+ return false;
+ }
operand->mode = Addressing_Value;
operand->type = make_optional_ok_type(default_type(x.type));
@@ -5416,6 +5434,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return false;
}
}
+ if (!are_types_identical(x.type, y.type)) {
+ gbString xts = type_to_string(x.type);
+ gbString yts = type_to_string(y.type);
+ error(x.expr, "Mismatched types for '%.*s', got %s vs %s", LIT(builtin_name), xts, yts);
+ gb_string_free(yts);
+ gb_string_free(xts);
+ return false;
+ }
operand->mode = Addressing_Value;
operand->type = default_type(x.type);
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 27babd255..8019d00c3 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -1473,6 +1473,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
e->Procedure.no_sanitize_address = ac.no_sanitize_address;
e->Procedure.no_sanitize_memory = ac.no_sanitize_memory;
+ e->Procedure.no_sanitize_thread = ac.no_sanitize_thread;
e->deprecated_message = ac.deprecated_message;
e->warning_message = ac.warning_message;
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 2fe6c0251..1c09ad908 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -1133,7 +1133,11 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
x.mode = Addressing_Value;
x.type = t;
if (check_is_assignable_to(c, &x, type)) {
- add_entity_use(c, operand->expr, e);
+ if (operand->expr->kind == Ast_SelectorExpr) {
+ add_entity_use(c, operand->expr->SelectorExpr.selector, e);
+ } else {
+ add_entity_use(c, operand->expr, e);
+ }
good = true;
break;
}
@@ -3526,28 +3530,17 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type)
Type *elem = core_array_type(bt);
if (core_type(bt)->kind == Type_Basic) {
- if (check_representable_as_constant(c, x->value, type, &x->value)) {
- return true;
- }
- goto check_castable;
- } else if (!are_types_identical(elem, bt) &&
- elem->kind == Type_Basic &&
- check_representable_as_constant(c, x->value, elem, &x->value)) {
- if (check_representable_as_constant(c, x->value, type, &x->value)) {
- return true;
- }
- goto check_castable;
+ return check_representable_as_constant(c, x->value, type, &x->value) ||
+ (is_type_pointer(type) && check_is_castable_to(c, x, type));
+ } else if (!are_types_identical(elem, bt) && elem->kind == Type_Basic && x->type->kind == Type_Basic) {
+ return check_representable_as_constant(c, x->value, elem, &x->value) ||
+ (is_type_pointer(elem) && check_is_castable_to(c, x, elem));
} else if (check_is_castable_to(c, x, type)) {
x->value = {};
x->mode = Addressing_Value;
return true;
}
-
- return false;
- }
-
-check_castable:
- if (check_is_castable_to(c, x, type)) {
+ } else if (check_is_castable_to(c, x, type)) {
if (x->mode != Addressing_Constant) {
x->mode = Addressing_Value;
} else if (is_type_slice(type) && is_type_string(x->type)) {
@@ -4132,15 +4125,19 @@ gb_internal void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Typ
i64 upper = yt->BitSet.upper;
if (lower <= key && key <= upper) {
- i64 bit = 1ll<<key;
- i64 bits = big_int_to_i64(&v.value_integer);
+ BigInt idx = big_int_make_i64(key - lower);
+ BigInt bit = big_int_make_i64(1);
+ big_int_shl_eq(&bit, &idx);
+
+ BigInt mask = {};
+ big_int_and(&mask, &bit, &v.value_integer);
x->mode = Addressing_Constant;
x->type = t_untyped_bool;
if (op.kind == Token_in) {
- x->value = exact_value_bool((bit & bits) != 0);
+ x->value = exact_value_bool(!big_int_is_zero(&mask));
} else {
- x->value = exact_value_bool((bit & bits) == 0);
+ x->value = exact_value_bool(big_int_is_zero(&mask));
}
x->expr = node;
return;
@@ -4707,12 +4704,14 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
switch (t->kind) {
case Type_Basic:
if (operand->mode == Addressing_Constant) {
- check_is_expressible(c, operand, t);
+ check_is_expressible(c, operand, target_type);
if (operand->mode == Addressing_Invalid) {
return;
}
update_untyped_expr_value(c, operand->expr, operand->value);
- } else {
+ }
+
+ {
switch (operand->type->Basic.kind) {
case Basic_UntypedBool:
if (!is_type_boolean(target_type)) {
@@ -5195,6 +5194,11 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
}
} else {
TypeAndValue index_tav = fv->field->tav;
+ if (index_tav.mode != Addressing_Constant) {
+ if (success_) *success_ = false;
+ if (finish_) *finish_ = true;
+ return empty_exact_value;
+ }
GB_ASSERT(index_tav.mode == Addressing_Constant);
ExactValue index_value = index_tav.value;
if (is_type_enumerated_array(node->tav.type)) {
@@ -8172,13 +8176,7 @@ gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast *
Type *return_type = proc.result_count == 0 ? nullptr : proc.results->Tuple.variables[0]->type;
bool is_return_instancetype = return_type != nullptr && return_type == t_objc_instancetype;
- if (params.count == 0 || !is_type_objc_ptr_to_object(params[0]->type)) {
- if (!proc_entity->Procedure.is_objc_class_method) {
- // Not a class method, invalid call
- error(call, "Invalid Objective-C call: The Objective-C method is not a class method but this first parameter is not an Objective-C object pointer.");
- return;
- }
-
+ if (proc_entity->Procedure.is_objc_class_method) {
if (is_return_instancetype) {
if (ce->proc->kind == Ast_SelectorExpr) {
ast_node(se, SelectorExpr, ce->proc);
@@ -8194,7 +8192,8 @@ gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast *
self_type = t_objc_Class;
params_start = 0;
- } else if (ce->args.count > 0) {
+ } else {
+ GB_ASSERT(ce->args.count > 0);
GB_ASSERT(is_type_objc_ptr_to_object(params[0]->type));
if (ce->args[0]->tav.objc_super_target) {
@@ -8226,7 +8225,7 @@ gb_internal void check_objc_call_expr(CheckerContext *c, Operand *operand, Ast *
add_objc_proc_type(c, call, return_type, param_types);
}
-gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, Type *type_hint) {
+gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *proc, Slice<Ast *> const &args, ProcInlining inlining, ProcTailing tailing, Type *type_hint) {
if (proc != nullptr &&
proc->kind == Ast_BasicDirective) {
ast_node(bd, BasicDirective, proc);
@@ -8257,7 +8256,10 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
return Expr_Expr;
}
if (inlining != ProcInlining_none) {
- error(call, "Inlining operators are not allowed on built-in procedures");
+ error(call, "Inlining directives are not allowed on built-in procedures");
+ }
+ if (tailing != ProcTailing_none) {
+ error(call, "Tailing directives are not allowed on built-in procedures");
}
} else {
if (proc != nullptr) {
@@ -8399,6 +8401,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
bool is_call_inlined = false;
+ bool is_call_tailed = true;
switch (inlining) {
case ProcInlining_inline:
@@ -8433,6 +8436,23 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
}
}
+ switch (tailing) {
+ case ProcTailing_none:
+ break;
+ case ProcTailing_must_tail:
+ is_call_tailed = true;
+ if (c->curr_proc_sig == nullptr || !are_types_identical(c->curr_proc_sig, pt)) {
+ ERROR_BLOCK();
+ gbString a = type_to_string(pt);
+ gbString b = type_to_string(c->curr_proc_sig);
+ error(call, "Use of '#must_tail' of a procedure must have the same type as the procedure it was called within");
+ error_line("\tCall type: %s, parent type: %s", a, b);
+ gb_string_free(b);
+ gb_string_free(a);
+ }
+ break;
+ }
+
{
String invalid;
if (pt->kind == Type_Proc && pt->Proc.require_target_feature.len != 0) {
@@ -8759,6 +8779,14 @@ gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Opera
if (is_operand_nil(*o)) {
return true;
}
+ if (is_type_any(field_type)) {
+ return false;
+ }
+ if (field_type != nullptr && is_type_typeid(field_type) && o->mode == Addressing_Type) {
+ add_type_info_type(c, o->type);
+ return true;
+ }
+
Ast *expr = unparen_expr(o->expr);
if (expr != nullptr) {
Entity *e = strip_entity_wrapping(entity_from_expr(expr));
@@ -8769,13 +8797,12 @@ gb_internal bool check_is_operand_compound_lit_constant(CheckerContext *c, Opera
add_type_and_value(c, expr, Addressing_Constant, type_of_expr(expr), exact_value_procedure(expr));
return true;
}
- }
- if (field_type != nullptr && is_type_typeid(field_type) && o->mode == Addressing_Type) {
- add_type_info_type(c, o->type);
- return true;
- }
- if (is_type_any(field_type)) {
- return false;
+
+ if (e != nullptr && e->kind == Entity_Variable && e->Variable.is_rodata) {
+ // DeclInfo *d = e->decl_info;
+ // TODO(bill): rodata inlining for non-indirect values
+ // e.g. **NOT** []T{...}
+ }
}
return o->mode == Addressing_Constant;
}
@@ -11044,8 +11071,20 @@ gb_internal ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *no
end:;
if ((c->state_flags & StateFlag_no_type_assert) == 0) {
- add_package_dependency(c, "runtime", "type_assertion_check");
- add_package_dependency(c, "runtime", "type_assertion_check2");
+ bool has_context = true;
+ if (c->proc_name.len == 0 && c->curr_proc_sig == nullptr) {
+ has_context = false;
+ } else if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) {
+ has_context = false;
+ }
+
+ if (has_context) {
+ add_package_dependency(c, "runtime", "type_assertion_check_with_context");
+ add_package_dependency(c, "runtime", "type_assertion_check2_with_context");
+ } else {
+ add_package_dependency(c, "runtime", "type_assertion_check_contextless");
+ add_package_dependency(c, "runtime", "type_assertion_check2_contextless");
+ }
}
return kind;
}
@@ -11570,6 +11609,15 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
return kind;
case_end;
+ case_ast_node(ht, HelperType, node);
+ Type *type = check_type(c, ht->type);
+ if (type != nullptr && type != t_invalid) {
+ o->mode = Addressing_Type;
+ o->type = type;
+ }
+ return kind;
+ case_end;
+
case_ast_node(i, Implicit, node);
switch (i->kind) {
case Token_context:
@@ -11832,7 +11880,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
case_end;
case_ast_node(ce, CallExpr, node);
- return check_call_expr(c, o, node, ce->proc, ce->args, ce->inlining, type_hint);
+ return check_call_expr(c, o, node, ce->proc, ce->args, ce->inlining, ce->tailing, type_hint);
case_end;
case_ast_node(de, DerefExpr, node);
@@ -12103,12 +12151,12 @@ gb_internal bool is_exact_value_zero(ExactValue const &v) {
-gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y, bool *do_break_) {
+gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y) {
ast_node(x_cl, CompoundLit, x.value_compound);
ast_node(y_cl, CompoundLit, y.value_compound);
if (x_cl->elems.count != y_cl->elems.count) {
- if (do_break_) *do_break_ = true;
+ return false;
}
bool test = op == Token_CmpEq;
@@ -12573,6 +12621,12 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
case_end;
case_ast_node(ce, CallExpr, node);
+ switch (ce->tailing) {
+ case ProcTailing_must_tail:
+ str = gb_string_appendc(str, "#must_tail ");
+ break;
+ }
+
switch (ce->inlining) {
case ProcInlining_inline:
str = gb_string_appendc(str, "#force_inline ");
@@ -12640,8 +12694,10 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
str = write_expr_to_string(str, st->polymorphic_params, shorthand);
str = gb_string_appendc(str, ") ");
}
- if (st->is_packed) str = gb_string_appendc(str, "#packed ");
- if (st->is_raw_union) str = gb_string_appendc(str, "#raw_union ");
+ if (st->is_packed) str = gb_string_appendc(str, "#packed ");
+ if (st->is_raw_union) str = gb_string_appendc(str, "#raw_union ");
+ if (st->is_all_or_none) str = gb_string_appendc(str, "#all_or_none ");
+ if (st->is_simple) str = gb_string_appendc(str, "#simple ");
if (st->align) {
str = gb_string_appendc(str, "#align ");
str = write_expr_to_string(str, st->align, shorthand);
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 26860db4f..1f433df36 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -1027,7 +1027,9 @@ gb_internal void check_unroll_range_stmt(CheckerContext *ctx, Ast *node, u32 mod
error(operand.expr, "Cannot iterate over '%s' of type '%s' in an '#unroll for' statement", s, t);
gb_string_free(t);
gb_string_free(s);
- } else if (operand.mode != Addressing_Constant && unroll_count <= 0) {
+ } else if (operand.mode != Addressing_Constant && (
+ unroll_count <= 0 &&
+ compare_exact_values(Token_CmpEq, inline_for_depth, exact_value_i64(0)))) {
error(operand.expr, "An '#unroll for' expression must be known at compile time");
}
}
diff --git a/src/check_type.cpp b/src/check_type.cpp
index 2452cc6d0..b1d0045e9 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -679,6 +679,21 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *
gb_unused(where_clause_ok);
}
check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context);
+
+ if (st->is_simple) {
+ bool success = true;
+ for (Entity *f : struct_type->Struct.fields) {
+ if (!is_type_nearly_simple_compare(f->type)) {
+ gbString s = type_to_string(f->type);
+ error(f->token, "'struct #simple' requires all fields to be at least 'nearly simple compare', got %s", s);
+ gb_string_free(s);
+ }
+ }
+ if (success) {
+ struct_type->Struct.is_simple = true;
+ }
+ }
+
wait_signal_set(&struct_type->Struct.fields_wait_signal);
}
@@ -3753,6 +3768,20 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
set_base_type(named_type, *type);
return true;
case_end;
+
+ default: {
+ Operand o = {};
+ check_expr_base(ctx, &o, e, nullptr);
+
+ if (o.mode == Addressing_Constant &&
+ o.value.kind == ExactValue_Typeid) {
+ Type *t = o.value.value_typeid;
+ if (t != nullptr && t != t_invalid) {
+ *type = t;
+ return true;
+ }
+ }
+ }
}
*type = t_invalid;
diff --git a/src/checker.cpp b/src/checker.cpp
index 4d5482933..453f3e241 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -1530,17 +1530,16 @@ gb_internal void destroy_checker_info(CheckerInfo *i) {
map_destroy(&i->load_directory_map);
}
-gb_internal CheckerContext make_checker_context(Checker *c) {
- CheckerContext ctx = {};
- ctx.checker = c;
- ctx.info = &c->info;
- ctx.scope = builtin_pkg->scope;
- ctx.pkg = builtin_pkg;
+gb_internal void init_checker_context(CheckerContext *ctx, Checker *c) {
+ ctx->checker = c;
+ ctx->info = &c->info;
+ ctx->scope = builtin_pkg->scope;
+ ctx->pkg = builtin_pkg;
- ctx.type_path = new_checker_type_path(heap_allocator());
- ctx.type_level = 0;
- return ctx;
+ ctx->type_path = new_checker_type_path(heap_allocator());
+ ctx->type_level = 0;
}
+
gb_internal void destroy_checker_context(CheckerContext *ctx) {
destroy_checker_type_path(ctx->type_path, heap_allocator());
}
@@ -1605,7 +1604,7 @@ gb_internal void init_checker(Checker *c) {
mpsc_init(&c->global_untyped_queue, a); // , 1<<20);
mpsc_init(&c->soa_types_to_complete, a); // , 1<<20);
- c->builtin_ctx = make_checker_context(c);
+ init_checker_context(&c->builtin_ctx, c);
}
gb_internal void destroy_checker(Checker *c) {
@@ -3581,7 +3580,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
return true;
} else if (name == "test") {
if (value != nullptr) {
- error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
+ error(value, "Expected no value for '%.*s'", LIT(name));
}
ac->test = true;
return true;
@@ -3629,13 +3628,13 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
return true;
} else if (name == "init") {
if (value != nullptr) {
- error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
+ error(value, "Expected no value for '%.*s'", LIT(name));
}
ac->init = true;
return true;
} else if (name == "fini") {
if (value != nullptr) {
- error(value, "'%.*s' expects no parameter, or a string literal containing \"file\" or \"package\"", LIT(name));
+ error(value, "Expected no value for '%.*s'", LIT(name));
}
ac->fini = true;
return true;
@@ -3991,6 +3990,12 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
}
ac->no_sanitize_memory = true;
return true;
+ } else if (name == "no_sanitize_thread") {
+ if (value != nullptr) {
+ error(value, "'%.*s' expects no parameter", LIT(name));
+ }
+ ac->no_sanitize_thread = true;
+ return true;
}
return false;
}
@@ -4027,6 +4032,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
} else if (ev.kind == ExactValue_String) {
String model = ev.value_string;
if (model == "default" ||
+ model == "globaldynamic" ||
model == "localdynamic" ||
model == "initialexec" ||
model == "localexec") {
@@ -4035,6 +4041,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
ERROR_BLOCK();
error(elem, "Invalid thread local model '%.*s'. Valid models:", LIT(model));
error_line("\tdefault\n");
+ error_line("\tglobaldynamic\n");
error_line("\tlocaldynamic\n");
error_line("\tinitialexec\n");
error_line("\tlocalexec\n");
@@ -4962,7 +4969,7 @@ gb_internal void check_collect_entities(CheckerContext *c, Slice<Ast *> const &n
gb_internal CheckerContext *create_checker_context(Checker *c) {
CheckerContext *ctx = gb_alloc_item(permanent_allocator(), CheckerContext);
- *ctx = make_checker_context(c);
+ init_checker_context(ctx, c);
return ctx;
}
@@ -5407,7 +5414,8 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
}
gb_internal void check_foreign_import_fullpaths(Checker *c) {
- CheckerContext ctx = make_checker_context(c);
+ CheckerContext ctx = {};
+ init_checker_context(&ctx, c);
UntypedExprInfoMap untyped = {};
defer (map_destroy(&untyped));
@@ -5790,7 +5798,7 @@ gb_internal void check_collect_entities_all(Checker *c) {
for (isize i = 0; i < thread_count; i++) {
auto *wd = &collect_entity_worker_data[i];
wd->c = c;
- wd->ctx = make_checker_context(c);
+ init_checker_context(&wd->ctx, c);
map_init(&wd->untyped);
}
@@ -5831,7 +5839,7 @@ gb_internal void check_export_entities(Checker *c) {
for (isize i = 0; i < thread_count; i++) {
auto *wd = &collect_entity_worker_data[i];
map_clear(&wd->untyped);
- wd->ctx = make_checker_context(c);
+ init_checker_context(&wd->ctx, c);
}
for (auto const &entry : c->info.packages) {
@@ -5898,7 +5906,8 @@ gb_internal void check_import_entities(Checker *c) {
}
TIME_SECTION("check_import_entities - collect file decls");
- CheckerContext ctx = make_checker_context(c);
+ CheckerContext ctx = {};
+ init_checker_context(&ctx, c);
UntypedExprInfoMap untyped = {};
defer (map_destroy(&untyped));
@@ -6247,7 +6256,8 @@ gb_internal bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *u
}
}
- CheckerContext ctx = make_checker_context(c);
+ CheckerContext ctx = {};
+ init_checker_context(&ctx, c);
defer (destroy_checker_context(&ctx));
reset_checker_context(&ctx, pi->file, untyped);
ctx.decl = pi->decl;
diff --git a/src/checker.hpp b/src/checker.hpp
index bda7b2746..f9c279a51 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -141,6 +141,7 @@ struct AttributeContext {
bool instrumentation_exit : 1;
bool no_sanitize_address : 1;
bool no_sanitize_memory : 1;
+ bool no_sanitize_thread : 1;
bool rodata : 1;
bool ignore_duplicates : 1;
u32 optimization_mode; // ProcedureOptimizationMode
diff --git a/src/entity.cpp b/src/entity.cpp
index 2b21fdcac..55aca8069 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -268,6 +268,7 @@ struct Entity {
bool is_anonymous : 1;
bool no_sanitize_address : 1;
bool no_sanitize_memory : 1;
+ bool no_sanitize_thread : 1;
bool is_objc_impl_or_import : 1;
bool is_objc_class_method : 1;
} Procedure;
diff --git a/src/exact_value.cpp b/src/exact_value.cpp
index f266b8b24..0f425e043 100644
--- a/src/exact_value.cpp
+++ b/src/exact_value.cpp
@@ -947,7 +947,7 @@ gb_internal gb_inline i32 cmp_f64(f64 a, f64 b) {
return (a > b) - (a < b);
}
-gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y, bool *do_break_);
+gb_internal bool compare_exact_values_compound_lit(TokenKind op, ExactValue x, ExactValue y);
gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
match_exact_values(&x, &y);
@@ -1060,18 +1060,13 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y)
case ExactValue_Compound:
if (op != Token_CmpEq && op != Token_NotEq) {
- break;
+ return false;
}
if (x.kind != y.kind) {
- break;
+ return false;
}
- bool do_break = false;
- bool res = compare_exact_values_compound_lit(op, x, y, &do_break);
- if (do_break) {
- break;
- }
- return res;
+ return compare_exact_values_compound_lit(op, x, y);
}
GB_PANIC("Invalid comparison: %d", x.kind);
diff --git a/src/linker.cpp b/src/linker.cpp
index c2a3ee928..bc6268049 100644
--- a/src/linker.cpp
+++ b/src/linker.cpp
@@ -174,7 +174,7 @@ try_cross_linking:;
switch (build_context.linker_choice) {
case Linker_Default: break;
case Linker_lld: section_name = str_lit("lld-link"); break;
- #if defined(GB_SYSTEM_LINUX)
+ #if defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_NETBSD)
case Linker_mold: section_name = str_lit("mold-link"); break;
#endif
#if defined(GB_SYSTEM_WINDOWS)
@@ -737,7 +737,21 @@ try_cross_linking:;
}
if (build_context.build_mode == BuildMode_StaticLibrary) {
- compiler_error("TODO(bill): -build-mode:static on non-windows targets");
+ TIME_SECTION("Static Library Creation");
+
+ gbString ar_command = gb_string_make(heap_allocator(), "");
+ defer (gb_string_free(ar_command));
+
+ ar_command = gb_string_appendc(ar_command, "ar rcs ");
+ ar_command = gb_string_append_fmt(ar_command, "\"%.*s\" ", LIT(output_filename));
+ ar_command = gb_string_appendc(ar_command, object_files);
+
+ result = system_exec_command_line_app("ar", ar_command);
+ if (result) {
+ return result;
+ }
+
+ return result;
}
// NOTE(dweiler): We use clang as a frontend for the linker as there are
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index 1cde65640..c15f326f8 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -239,9 +239,9 @@ gb_internal lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx
gb_internal String lb_internal_gen_name_from_type(char const *prefix, Type *type) {
gbString str = gb_string_make(permanent_allocator(), prefix);
- u64 hash = type_hash_canonical_type(type);
- str = gb_string_appendc(str, "-");
- str = gb_string_append_fmt(str, "%llu", cast(unsigned long long)hash);
+ str = gb_string_appendc(str, "$$");
+ gbString ct = temp_canonical_string(type);
+ str = gb_string_append_length(str, ct, gb_string_length(ct));
String proc_name = make_string(cast(u8 const *)str, gb_string_length(str));
return proc_name;
}
@@ -2045,6 +2045,11 @@ gb_internal bool lb_init_global_var(lbModule *m, lbProcedure *p, Entity *e, Ast
lb_emit_store(p, data, lb_emit_conv(p, gp, t_rawptr));
lb_emit_store(p, ti, lb_typeid(p->module, var_type));
} else {
+ i64 sz = type_size_of(e->type);
+ if (sz >= 4 * 1024) {
+ warning(init_expr, "[Possible Code Generation Issue] Non-constant initialization is large (%lld bytes), and might cause problems with LLVM", cast(long long)sz);
+ }
+
LLVMTypeRef vt = llvm_addr_type(p->module, var.var);
lbValue src0 = lb_emit_conv(p, var.init, t);
LLVMValueRef src = OdinLLVMBuildTransmute(p, src0.value, vt);
@@ -2111,7 +2116,7 @@ gb_internal void lb_create_startup_runtime_generate_body(lbModule *m, lbProcedur
for (Entity *e : info->init_procedures) {
lbValue value = lb_find_procedure_value_from_entity(m, e);
- lb_emit_call(p, value, {}, ProcInlining_none);
+ lb_emit_call(p, value, {}, ProcInlining_none, ProcTailing_none);
}
@@ -2157,7 +2162,7 @@ gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // C
for (Entity *e : info->fini_procedures) {
lbValue value = lb_find_procedure_value_from_entity(main_module, e);
- lb_emit_call(p, value, {}, ProcInlining_none);
+ lb_emit_call(p, value, {}, ProcInlining_none, ProcTailing_none);
}
lb_end_procedure_body(p);
@@ -2850,7 +2855,7 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
}
lbValue startup_runtime_value = {startup_runtime->value, startup_runtime->type};
- lb_emit_call(p, startup_runtime_value, {}, ProcInlining_none);
+ lb_emit_call(p, startup_runtime_value, {}, ProcInlining_none, ProcTailing_none);
if (build_context.command_kind == Command_test) {
Type *t_Internal_Test = find_type_in_pkg(m->info, str_lit("testing"), str_lit("Internal_Test"));
@@ -2917,16 +2922,16 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
auto exit_args = array_make<lbValue>(temporary_allocator(), 1);
exit_args[0] = lb_emit_select(p, result, lb_const_int(m, t_int, 0), lb_const_int(m, t_int, 1));
- lb_emit_call(p, exit_runner, exit_args, ProcInlining_none);
+ lb_emit_call(p, exit_runner, exit_args, ProcInlining_none, ProcTailing_none);
} else {
if (m->info->entry_point != nullptr) {
lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point);
- lb_emit_call(p, entry_point, {}, ProcInlining_no_inline);
+ lb_emit_call(p, entry_point, {}, ProcInlining_no_inline, ProcTailing_none);
}
if (call_cleanup) {
lbValue cleanup_runtime_value = {cleanup_runtime->value, cleanup_runtime->type};
- lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none);
+ lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none, ProcTailing_none);
}
if (is_dll_main) {
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index da5d91f2e..3491c0d39 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -345,6 +345,7 @@ struct lbProcedure {
Ast * body;
u64 tags;
ProcInlining inlining;
+ ProcTailing tailing;
bool is_foreign;
bool is_export;
bool is_entry_point;
@@ -484,7 +485,7 @@ gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlo
gb_internal void lb_emit_defer_stmts(lbProcedure *p, lbDeferExitKind kind, lbBlock *block, Ast *node);
gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t);
gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue right);
-gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining = ProcInlining_none);
+gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining = ProcInlining_none, ProcTailing tailing = ProcTailing_none);
gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t);
gb_internal lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x);
@@ -618,7 +619,7 @@ gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef p
gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef ptr, usize len, unsigned alignment, bool is_volatile);
gb_internal gb_inline i64 lb_max_zero_init_size(void) {
- return cast(i64)(4*build_context.int_size);
+ return cast(i64)(8);
}
gb_internal LLVMTypeRef OdinLLVMGetArrayElementType(LLVMTypeRef type);
@@ -670,6 +671,7 @@ enum lbCallingConventionKind : unsigned {
lbCallingConvention_PreserveAll = 15,
lbCallingConvention_Swift = 16,
lbCallingConvention_CXX_FAST_TLS = 17,
+ lbCallingConvention_PreserveNone = 21,
lbCallingConvention_FirstTargetCC = 64,
lbCallingConvention_X86_StdCall = 64,
lbCallingConvention_X86_FastCall = 65,
@@ -723,6 +725,10 @@ lbCallingConventionKind const lb_calling_convention_map[ProcCC_MAX] = {
lbCallingConvention_Win64, // ProcCC_Win64,
lbCallingConvention_X86_64_SysV, // ProcCC_SysV,
+ lbCallingConvention_PreserveNone, // ProcCC_PreserveNone,
+ lbCallingConvention_PreserveMost, // ProcCC_PreserveMost,
+ lbCallingConvention_PreserveAll, // ProcCC_PreserveAll,
+
};
enum : LLVMDWARFTypeEncoding {
diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp
index 22e124792..8ce2137ab 100644
--- a/src/llvm_backend_const.cpp
+++ b/src/llvm_backend_const.cpp
@@ -498,6 +498,13 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi
if (big_int_is_zero(a)) {
return LLVMConstNull(lb_type(m, original_type));
}
+
+ BigInt val = {};
+ big_int_init(&val, a);
+
+ if (big_int_is_neg(&val)) {
+ mp_incr(&val);
+ }
size_t sz = cast(size_t)type_size_of(original_type);
u64 rop64[4] = {}; // 2 u64 is the maximum we will ever need, so doubling it will be fine :P
@@ -509,7 +516,7 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi
size_t nails = 0;
mp_endian endian = MP_LITTLE_ENDIAN;
- max_count = mp_pack_count(a, nails, size);
+ max_count = mp_pack_count(&val, nails, size);
if (sz < max_count) {
debug_print_big_int(a);
gb_printf_err("%s -> %tu\n", type_to_string(original_type), sz);
@@ -520,7 +527,7 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi
mp_err err = mp_pack(rop, sz, &written,
MP_LSB_FIRST,
size, endian, nails,
- a);
+ &val);
GB_ASSERT(err == MP_OKAY);
if (!is_type_endian_little(original_type)) {
@@ -531,12 +538,18 @@ gb_internal LLVMValueRef lb_big_int_to_llvm(lbModule *m, Type *original_type, Bi
}
}
+ if (big_int_is_neg(a)) {
+ // sizeof instead of sz for sign extend to work properly
+ for (size_t i = 0; i < sizeof rop64; i++) {
+ rop[i] = ~rop[i];
+ }
+ }
+
+ big_int_dealloc(&val);
+
GB_ASSERT(!is_type_array(original_type));
LLVMValueRef value = LLVMConstIntOfArbitraryPrecision(lb_type(m, original_type), cast(unsigned)((sz+7)/8), cast(u64 *)rop);
- if (big_int_is_neg(a)) {
- value = LLVMConstNeg(value);
- }
return value;
}
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp
index dba61df44..aba196af8 100644
--- a/src/llvm_backend_expr.cpp
+++ b/src/llvm_backend_expr.cpp
@@ -282,11 +282,16 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x,
return res;
}
-
-gb_internal IntegerDivisionByZeroKind lb_check_for_integer_division_by_zero_behaviour(lbProcedure *p) {
+gb_internal u64 lb_get_file_feature_flags(lbProcedure *p, Ast *expr = nullptr) {
AstFile *file = nullptr;
- if (p->body && p->body->file()) {
+ if (expr != nullptr) {
+ file = expr->file();
+ }
+
+ if (file != nullptr) {
+ // it is now set
+ } else if (p->body && p->body->file()) {
file = p->body->file();
} else if (p->type_expr && p->type_expr->file()) {
file = p->type_expr->file();
@@ -295,20 +300,29 @@ gb_internal IntegerDivisionByZeroKind lb_check_for_integer_division_by_zero_beha
}
if (file != nullptr && file->feature_flags_set) {
- u64 flags = file->feature_flags;
- if (flags & OptInFeatureFlag_IntegerDivisionByZero_Trap) {
- return IntegerDivisionByZero_Trap;
- }
- if (flags & OptInFeatureFlag_IntegerDivisionByZero_Zero) {
- return IntegerDivisionByZero_Zero;
- }
- if (flags & OptInFeatureFlag_IntegerDivisionByZero_Self) {
- return IntegerDivisionByZero_Self;
- }
- if (flags & OptInFeatureFlag_IntegerDivisionByZero_AllBits) {
- return IntegerDivisionByZero_AllBits;
- }
+ return file->feature_flags;
+ }
+ return 0;
+}
+
+
+
+gb_internal IntegerDivisionByZeroKind lb_check_for_integer_division_by_zero_behaviour(lbProcedure *p) {
+ u64 flags = lb_get_file_feature_flags(p);
+
+ if (flags & OptInFeatureFlag_IntegerDivisionByZero_Trap) {
+ return IntegerDivisionByZero_Trap;
+ }
+ if (flags & OptInFeatureFlag_IntegerDivisionByZero_Zero) {
+ return IntegerDivisionByZero_Zero;
+ }
+ if (flags & OptInFeatureFlag_IntegerDivisionByZero_Self) {
+ return IntegerDivisionByZero_Self;
+ }
+ if (flags & OptInFeatureFlag_IntegerDivisionByZero_AllBits) {
+ return IntegerDivisionByZero_AllBits;
}
+
return build_context.integer_division_by_zero_behaviour;
}
@@ -3802,6 +3816,17 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
Type *type = type_of_expr(ue_expr);
GB_ASSERT(!is_type_tuple(type));
+ bool do_type_check = true;
+ if (build_context.no_type_assert) {
+ u64 feature_flags = lb_get_file_feature_flags(p, ue_expr);
+ if ((feature_flags & OptInFeatureFlag_ForceTypeAssert) == 0) {
+ do_type_check = false;
+ }
+
+ } else if ((p->state_flags & StateFlag_no_type_assert) != 0) {
+ do_type_check = false;
+ }
+
lbValue e = lb_build_expr(p, ta->expr);
Type *t = type_deref(e.type);
if (is_type_union(t)) {
@@ -3813,7 +3838,7 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
Type *dst_type = type;
- if (!build_context.no_type_assert && (p->state_flags & StateFlag_no_type_assert) == 0) {
+ if (do_type_check) {
lbValue src_tag = {};
lbValue dst_tag = {};
if (is_type_union_maybe_pointer(src_type)) {
@@ -3840,7 +3865,12 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
args[4] = lb_typeid(p->module, src_type);
args[5] = lb_typeid(p->module, dst_type);
}
- lb_emit_runtime_call(p, "type_assertion_check", args);
+
+ char const *name = "type_assertion_check_contextless";
+ if (p->context_stack.count > 0) {
+ name = "type_assertion_check_with_context";
+ }
+ lb_emit_runtime_call(p, name, args);
}
lbValue data_ptr = v;
@@ -3851,21 +3881,33 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
v = lb_emit_load(p, v);
}
lbValue data_ptr = lb_emit_struct_ev(p, v, 0);
- if (!build_context.no_type_assert && (p->state_flags & StateFlag_no_type_assert) == 0) {
+ if (do_type_check) {
GB_ASSERT(!build_context.no_rtti);
lbValue any_id = lb_emit_struct_ev(p, v, 1);
+ isize arg_count = 6;
+ if (build_context.no_rtti) {
+ arg_count = 4;
+ }
+
lbValue id = lb_typeid(p->module, type);
lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
- auto args = array_make<lbValue>(permanent_allocator(), 6);
+ auto args = array_make<lbValue>(permanent_allocator(), arg_count);
args[0] = ok;
lb_set_file_line_col(p, array_slice(args, 1, args.count), pos);
- args[4] = any_id;
- args[5] = id;
- lb_emit_runtime_call(p, "type_assertion_check", args);
+ if (!build_context.no_rtti) {
+ args[4] = any_id;
+ args[5] = id;
+ }
+
+ char const *name = "type_assertion_check_contextless";
+ if (p->context_stack.count > 0) {
+ name = "type_assertion_check_with_context";
+ }
+ lb_emit_runtime_call(p, name, args);
}
return lb_emit_conv(p, data_ptr, tv.type);
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index 4ebb40d96..c0e57143d 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -2628,6 +2628,8 @@ gb_internal bool lb_apply_thread_local_model(LLVMValueRef value, String model) {
LLVMThreadLocalMode mode = LLVMGeneralDynamicTLSModel;
if (model == "default") {
mode = LLVMGeneralDynamicTLSModel;
+ } else if (model == "globaldynamic") {
+ mode = LLVMGeneralDynamicTLSModel;
} else if (model == "localdynamic") {
mode = LLVMLocalDynamicTLSModel;
} else if (model == "initialexec") {
diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp
index 4971b1f10..4131f32bf 100644
--- a/src/llvm_backend_opt.cpp
+++ b/src/llvm_backend_opt.cpp
@@ -309,7 +309,15 @@ gb_internal void lb_run_remove_dead_instruction_pass(lbProcedure *p) {
// NOTE(bill): Explicit instructions are set here because some instructions could have side effects
switch (LLVMGetInstructionOpcode(curr_instr)) {
- // case LLVMAlloca:
+ case LLVMAlloca:
+ if (map_get(&p->tuple_fix_map, curr_instr) != nullptr) {
+ // NOTE(bill, 2025-12-27): Remove temporary tuple fix alloca instructions
+ // if they are never used
+ removal_count += 1;
+ LLVMInstructionEraseFromParent(curr_instr);
+ was_dead_instructions = true;
+ }
+ break;
case LLVMLoad:
if (LLVMGetVolatile(curr_instr)) {
break;
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index 27167aefd..837d7ce48 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -12,9 +12,9 @@ gb_internal void lb_mem_copy_overlapping(lbProcedure *p, lbValue dst, lbValue sr
len = lb_emit_conv(p, len, t_int);
char const *name = "llvm.memmove";
- if (LLVMIsConstant(len.value)) {
+ if (!p->is_startup && LLVMIsConstant(len.value)) {
i64 const_len = cast(i64)LLVMConstIntGetSExtValue(len.value);
- if (const_len <= 4*build_context.int_size) {
+ if (const_len <= lb_max_zero_init_size()) {
name = "llvm.memmove.inline";
}
}
@@ -41,9 +41,9 @@ gb_internal void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValu
len = lb_emit_conv(p, len, t_int);
char const *name = "llvm.memcpy";
- if (LLVMIsConstant(len.value)) {
+ if (!p->is_startup && LLVMIsConstant(len.value)) {
i64 const_len = cast(i64)LLVMConstIntGetSExtValue(len.value);
- if (const_len <= 4*build_context.int_size) {
+ if (const_len <= lb_max_zero_init_size()) {
name = "llvm.memcpy.inline";
}
}
@@ -117,6 +117,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
p->type_expr = decl->type_expr;
p->body = pl->body;
p->inlining = pl->inlining;
+ p->tailing = pl->tailing;
p->is_foreign = entity->Procedure.is_foreign;
p->is_export = entity->Procedure.is_export;
p->is_entry_point = false;
@@ -152,6 +153,10 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
lb_ensure_abi_function_type(m, p);
lb_add_function_type_attributes(p->value, p->abi_function_type, p->abi_function_type->calling_convention);
+ if (build_context.disable_unwind) {
+ lb_add_attribute_to_proc(m, p->value, "nounwind");
+ }
+
if (pt->Proc.diverging) {
lb_add_attribute_to_proc(m, p->value, "noreturn");
}
@@ -346,7 +351,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
if (build_context.sanitizer_flags & SanitizerFlag_Memory && !entity->Procedure.no_sanitize_memory) {
lb_add_attribute_to_proc(m, p->value, "sanitize_memory");
}
- if (build_context.sanitizer_flags & SanitizerFlag_Thread) {
+ if (build_context.sanitizer_flags & SanitizerFlag_Thread && !entity->Procedure.no_sanitize_thread) {
lb_add_attribute_to_proc(m, p->value, "sanitize_thread");
}
}
@@ -387,6 +392,7 @@ gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name
p->body = nullptr;
p->tags = 0;
p->inlining = ProcInlining_none;
+ p->tailing = ProcTailing_none;
p->is_foreign = false;
p->is_export = false;
p->is_entry_point = false;
@@ -855,7 +861,7 @@ gb_internal Array<lbValue> lb_value_to_array(lbProcedure *p, gbAllocator const &
-gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, Array<lbValue> const &processed_args, Type *abi_rt, lbAddr context_ptr, ProcInlining inlining) {
+gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr, Array<lbValue> const &processed_args, Type *abi_rt, lbAddr context_ptr, ProcInlining inlining, ProcTailing tailing) {
GB_ASSERT(p->module->ctx == LLVMGetTypeContext(LLVMTypeOf(value.value)));
unsigned arg_count = cast(unsigned)processed_args.count;
@@ -972,6 +978,15 @@ gb_internal lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue
break;
}
+ switch (tailing) {
+ case ProcTailing_none:
+ break;
+ case ProcTailing_must_tail:
+ LLVMSetTailCall(ret, true);
+ LLVMSetTailCallKind(ret, LLVMTailCallKindMustTail);
+ break;
+ }
+
lbValue res = {};
res.value = ret;
res.type = abi_rt;
@@ -1045,7 +1060,7 @@ gb_internal lbValue lb_emit_conjugate(lbProcedure *p, lbValue val, Type *type) {
return lb_emit_load(p, res);
}
-gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining) {
+gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args, ProcInlining inlining, ProcTailing tailing) {
lbModule *m = p->module;
Type *pt = base_type(value.type);
@@ -1168,10 +1183,10 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
if (return_by_pointer) {
lbValue return_ptr = lb_add_local_generated(p, rt, true).addr;
- lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining);
+ lb_emit_call_internal(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining, tailing);
result = lb_emit_load(p, return_ptr);
} else if (rt != nullptr) {
- result = lb_emit_call_internal(p, value, {}, processed_args, rt, context_ptr, inlining);
+ result = lb_emit_call_internal(p, value, {}, processed_args, rt, context_ptr, inlining, tailing);
if (ft->ret.cast_type) {
result.value = OdinLLVMBuildTransmute(p, result.value, ft->ret.cast_type);
}
@@ -1184,7 +1199,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
result = lb_emit_conv(p, result, rt);
}
} else {
- lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining);
+ lb_emit_call_internal(p, value, {}, processed_args, nullptr, context_ptr, inlining, tailing);
}
if (original_rt != rt) {
@@ -1212,15 +1227,6 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> c
}
tuple_fix_values[ret_count-1] = result;
- #if 0
- for (isize j = 0; j < ret_count; j++) {
- tuple_geps[j] = lb_emit_struct_ep(p, result_ptr, cast(i32)j);
- }
- for (isize j = 0; j < ret_count; j++) {
- lb_emit_store(p, tuple_geps[j], tuple_fix_values[j]);
- }
- #endif
-
result = lb_emit_load(p, result_ptr);
lbTupleFix tf = {tuple_fix_values};
@@ -4411,6 +4417,25 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
return lb_handle_objc_auto_send(p, expr, slice(call_args, 0, call_args.count));
}
- return lb_emit_call(p, value, call_args, ce->inlining);
+
+ ProcInlining inlining = ce->inlining;
+ ProcTailing tailing = ce->tailing;
+
+ if (tailing == ProcTailing_none &&
+ proc_entity && proc_entity->kind == Entity_Procedure &&
+ proc_entity->decl_info &&
+ proc_entity->decl_info->proc_lit) {
+ ast_node(pl, ProcLit, proc_entity->decl_info->proc_lit);
+
+ if (pl->inlining != ProcInlining_none) {
+ inlining = pl->inlining;
+ }
+
+ if (pl->tailing != ProcTailing_none) {
+ tailing = pl->tailing;
+ }
+ }
+
+ return lb_emit_call(p, value, call_args, inlining, tailing);
}
diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp
index 3dbcea4fb..81755af2d 100644
--- a/src/llvm_backend_stmt.cpp
+++ b/src/llvm_backend_stmt.cpp
@@ -1434,12 +1434,13 @@ gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt *
if (unroll_count_ev.kind == ExactValue_Invalid) {
- GB_ASSERT(expr->tav.mode == Addressing_Constant);
Type *t = base_type(expr->tav.type);
switch (t->kind) {
case Type_Basic:
+ GB_ASSERT(expr->tav.mode == Addressing_Constant);
+
GB_ASSERT(is_type_string(t));
{
ExactValue value = expr->tav.value;
@@ -1720,7 +1721,45 @@ gb_internal void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *
Ast *clause = body->stmts[i];
ast_node(cc, CaseClause, clause);
- body_blocks[i] = lb_create_block(p, cc->list.count == 0 ? "switch.default.body" : "switch.case.body");
+ char const *block_name = cc->list.count == 0 ? "switch.default.body" : "switch.case.body";
+
+ if (is_trivial && cc->list.count >= 1) {
+ gbString bn = gb_string_make(heap_allocator(), "switch.case.");
+
+ Ast *first = cc->list[0];
+ if (first->tav.mode == Addressing_Type) {
+ bn = gb_string_appendc(bn, "type.");
+ } else if (is_type_rune(first->tav.type)) {
+ bn = gb_string_appendc(bn, "rune.");
+ } else {
+ bn = gb_string_appendc(bn, "value.");
+ }
+
+ for_array(i, cc->list) {
+ if (i > 0) {
+ bn = gb_string_appendc(bn, "..");
+ }
+
+ Ast *expr = cc->list[i];
+ if (expr->tav.mode == Addressing_Type) {
+ bn = write_type_to_string(bn, expr->tav.type, false);
+ } else {
+ ExactValue value = expr->tav.value;
+ if (is_type_rune(expr->tav.type) && value.kind == ExactValue_Integer) {
+ Rune r = cast(Rune)exact_value_to_i64(value);
+ u8 rune_temp[6] = {};
+ isize size = gb_utf8_encode_rune(rune_temp, r);
+ bn = gb_string_append_length(bn, rune_temp, size);
+ } else {
+ bn = write_exact_value_to_string(bn, value, 1024);
+ }
+ }
+ }
+
+ block_name = cast(char const *)bn;
+ }
+
+ body_blocks[i] = lb_create_block(p, block_name);
if (cc->list.count == 0) {
default_block = body_blocks[i];
}
@@ -1962,7 +2001,7 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
num_cases += cc->list.count;
if (cc->list.count == 0) {
GB_ASSERT(default_block == nullptr);
- default_block = lb_create_block(p, "typeswitch.default.body");
+ default_block = lb_create_block(p, "typeswitch.case.default");
else_block = default_block;
}
}
@@ -2042,7 +2081,16 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
continue;
}
- lbBlock *body = lb_create_block(p, "typeswitch.body");
+ char const *body_name = "typeswitch.case";
+
+ if (!are_types_identical(case_entity->type, parent_base_type)) {
+ gbString canonical_name = temp_canonical_string(case_entity->type);
+ gbString bn = gb_string_make(heap_allocator(), "typeswitch.case.");
+ bn = gb_string_append_length(bn, canonical_name, gb_string_length(canonical_name));
+ body_name = cast(char const *)bn;
+ }
+
+ lbBlock *body = lb_create_block(p, body_name);
if (p->debug_info != nullptr) {
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, clause));
}
@@ -2122,14 +2170,7 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
for_array(i, vd->names) {
lbValue value = {};
- if (vd->values.count > 0) {
- GB_ASSERT(vd->names.count == vd->values.count);
- Ast *ast_value = vd->values[i];
- GB_ASSERT(ast_value->tav.mode == Addressing_Constant ||
- ast_value->tav.mode == Addressing_Invalid);
- value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value, LB_CONST_CONTEXT_DEFAULT_NO_LOCAL);
- }
Ast *ident = vd->names[i];
GB_ASSERT(!is_blank_ident(ident));
@@ -2137,6 +2178,19 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
GB_ASSERT(e->flags & EntityFlag_Static);
String name = e->token.string;
+ if (vd->values.count > 0) {
+ GB_ASSERT(vd->names.count == vd->values.count);
+ Ast *ast_value = vd->values[i];
+ GB_ASSERT(ast_value->tav.mode == Addressing_Constant ||
+ ast_value->tav.mode == Addressing_Invalid);
+
+ auto cc = LB_CONST_CONTEXT_DEFAULT_NO_LOCAL;
+ if (e->Variable.is_rodata) {
+ cc.is_rodata = true;
+ }
+ value = lb_const_value(p->module, ast_value->tav.type, ast_value->tav.value, cc);
+ }
+
String mangled_name = {};
{
gbString str = gb_string_make_length(permanent_allocator(), p->name.text, p->name.len);
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index 9ddbd1f9c..929239486 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -89,7 +89,7 @@ gb_internal LLVMValueRef lb_mem_zero_ptr_internal(lbProcedure *p, LLVMValueRef p
bool is_inlinable = false;
i64 const_len = 0;
- if (LLVMIsConstant(len)) {
+ if (!p->is_startup && LLVMIsConstant(len)) {
const_len = cast(i64)LLVMConstIntGetSExtValue(len);
// TODO(bill): Determine when it is better to do the `*.inline` versions
if (const_len <= lb_max_zero_init_size()) {
@@ -803,7 +803,12 @@ gb_internal lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type
args[5] = lb_typeid(m, dst_type);
args[6] = lb_emit_conv(p, value_, t_rawptr);
}
- lb_emit_runtime_call(p, "type_assertion_check2", args);
+
+ char const *name = "type_assertion_check2_contextless";
+ if (p->context_stack.count > 0) {
+ name = "type_assertion_check2_with_context";
+ }
+ lb_emit_runtime_call(p, name, args);
}
return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0));
@@ -877,7 +882,11 @@ gb_internal lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *ty
args[5] = dst_typeid;
args[6] = lb_emit_struct_ev(p, value, 0);
}
- lb_emit_runtime_call(p, "type_assertion_check2", args);
+ char const *name = "type_assertion_check2_contextless";
+ if (p->context_stack.count > 0) {
+ name = "type_assertion_check2_with_context";
+ }
+ lb_emit_runtime_call(p, name, args);
}
return lb_addr(lb_emit_struct_ep(p, v.addr, 0));
diff --git a/src/main.cpp b/src/main.cpp
index 9a5df8aea..624869e65 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -324,8 +324,6 @@ enum BuildFlagKind {
BuildFlag_NoCRT,
BuildFlag_NoRPath,
BuildFlag_NoEntryPoint,
- BuildFlag_UseLLD,
- BuildFlag_UseRADLink,
BuildFlag_Linker,
BuildFlag_UseSeparateModules,
BuildFlag_UseSingleModule,
@@ -363,6 +361,8 @@ enum BuildFlagKind {
BuildFlag_RelocMode,
BuildFlag_DisableRedZone,
+ BuildFlag_DisableUnwind,
+
BuildFlag_DisallowDo,
BuildFlag_DefaultToNilAllocator,
BuildFlag_DefaultToPanicAllocator,
@@ -554,8 +554,6 @@ gb_internal bool parse_build_flags(Array<String> args) {
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_UseRADLink, str_lit("radlink"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_Linker, str_lit("linker"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_UseSeparateModules, str_lit("use-separate-modules"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_UseSingleModule, str_lit("use-single-module"), BuildFlagParam_None, Command__does_build);
@@ -592,6 +590,8 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_DisableRedZone, str_lit("disable-red-zone"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_DisableUnwind, str_lit("disable-unwind"), BuildFlagParam_None, Command__does_build);
+
add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_DefaultToPanicAllocator, str_lit("default-to-panic-allocator"),BuildFlagParam_None, Command__does_check);
@@ -1252,14 +1252,6 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_NoThreadLocal:
build_context.no_thread_local = true;
break;
- case BuildFlag_UseLLD:
- gb_printf_err("Warning: Use of -lld has been deprecated in favour of -linker:lld\n");
- build_context.linker_choice = Linker_lld;
- break;
- case BuildFlag_UseRADLink:
- gb_printf_err("Warning: Use of -lld has been deprecated in favour of -linker:radlink\n");
- build_context.linker_choice = Linker_radlink;
- break;
case BuildFlag_Linker:
{
GB_ASSERT(value.kind == ExactValue_String);
@@ -1424,6 +1416,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_DisableRedZone:
build_context.disable_red_zone = true;
break;
+ case BuildFlag_DisableUnwind:
+ build_context.disable_unwind = true;
+ break;
+
case BuildFlag_DisallowDo:
build_context.disallow_do = true;
break;
@@ -2971,6 +2967,10 @@ gb_internal int print_show_help(String const arg0, String command, String option
if (check) {
if (print_flag("-target:<string>")) {
print_usage_line(2, "Sets the target for the executable to be built in.");
+ print_usage_line(2, "Examples:");
+ print_usage_line(3, "-target:linux_amd64");
+ print_usage_line(3, "-target:windows_amd64");
+ print_usage_line(3, "-target:\"?\" for a list");
}
if (print_flag("-terse-errors")) {
@@ -4034,6 +4034,14 @@ int main(int arg_count, char const **arg_ptr) {
remove_temp_files(gen);
}
+ if (any_errors()) {
+ print_all_errors();
+ return 1;
+ }
+ if (any_warnings()) {
+ print_all_errors();
+ }
+
end_of_code_gen:;
if (build_context.export_dependencies_format != DependenciesExportUnspecified) {
diff --git a/src/parser.cpp b/src/parser.cpp
index e27e184d0..360537ab7 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -1230,7 +1230,7 @@ gb_internal Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) {
}
gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, isize field_count,
- Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_all_or_none,
+ Ast *polymorphic_params, bool is_packed, bool is_raw_union, bool is_all_or_none, bool is_simple,
Ast *align, Ast *min_field_align, Ast *max_field_align,
Token where_token, Array<Ast *> const &where_clauses) {
Ast *result = alloc_ast_node(f, Ast_StructType);
@@ -1241,6 +1241,7 @@ gb_internal Ast *ast_struct_type(AstFile *f, Token token, Slice<Ast *> fields, i
result->StructType.is_packed = is_packed;
result->StructType.is_raw_union = is_raw_union;
result->StructType.is_all_or_none = is_all_or_none;
+ result->StructType.is_simple = is_simple;
result->StructType.align = align;
result->StructType.min_field_align = min_field_align;
result->StructType.max_field_align = max_field_align;
@@ -2176,38 +2177,50 @@ gb_internal bool ast_on_same_line(Token const &x, Ast *yp) {
return x.pos.line == y.pos.line;
}
-gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) {
+gb_internal Ast *parse_inlining_or_tailing_operand(AstFile *f, Token token) {
Ast *expr = parse_unary_expr(f, false);
Ast *e = strip_or_return_expr(expr);
if (e == nullptr) {
return expr;
}
if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) {
- syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind]));
+ syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[e->kind]));
return ast_bad_expr(f, token, f->curr_token);
}
ProcInlining pi = ProcInlining_none;
+ ProcTailing pt = ProcTailing_none;
if (token.kind == Token_Ident) {
if (token.string == "force_inline") {
pi = ProcInlining_inline;
} else if (token.string == "force_no_inline") {
pi = ProcInlining_no_inline;
+ } else if (token.string == "must_tail") {
+ pt = ProcTailing_must_tail;
}
}
if (pi != ProcInlining_none) {
if (e->kind == Ast_ProcLit) {
- if (expr->ProcLit.inlining != ProcInlining_none &&
- expr->ProcLit.inlining != pi) {
+ if (e->ProcLit.inlining != ProcInlining_none &&
+ e->ProcLit.inlining != pi) {
syntax_error(expr, "Cannot apply both '#force_inline' and '#force_no_inline' to a procedure literal");
}
- expr->ProcLit.inlining = pi;
+ e->ProcLit.inlining = pi;
} else if (e->kind == Ast_CallExpr) {
- if (expr->CallExpr.inlining != ProcInlining_none &&
- expr->CallExpr.inlining != pi) {
+ if (e->CallExpr.inlining != ProcInlining_none &&
+ e->CallExpr.inlining != pi) {
syntax_error(expr, "Cannot apply both '#force_inline' and '#force_no_inline' to a procedure call");
}
- expr->CallExpr.inlining = pi;
+ e->CallExpr.inlining = pi;
+ }
+ }
+
+ if (pt != ProcTailing_none) {
+ if (e->kind == Ast_ProcLit) {
+ syntax_error(expr, "'#must_call' can only be applied to a procedure call, not the procedure literal");
+ e->ProcLit.tailing = pt;
+ } else if (e->kind == Ast_CallExpr) {
+ e->CallExpr.tailing = pt;
}
}
@@ -2507,8 +2520,9 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
syntax_error(tag, "#relative types have now been removed in favour of \"core:relative\"");
return ast_relative_type(f, tag, type);
} else if (name.string == "force_inline" ||
- name.string == "force_no_inline") {
- return parse_force_inlining_operand(f, name);
+ name.string == "force_no_inline" ||
+ name.string == "must_tail") {
+ return parse_inlining_or_tailing_operand(f, name);
}
return ast_basic_directive(f, token, name);
}
@@ -2775,6 +2789,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
bool is_packed = false;
bool is_all_or_none = false;
bool is_raw_union = false;
+ bool is_simple = false;
Ast *align = nullptr;
Ast *min_field_align = nullptr;
Ast *max_field_align = nullptr;
@@ -2803,7 +2818,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
}
is_packed = true;
} else if (tag.string == "all_or_none") {
- if (is_packed) {
+ if (is_all_or_none) {
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
}
is_all_or_none = true;
@@ -2856,11 +2871,16 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
error_line("\tSuggestion: #max_field_align(%s)", s);
gb_string_free(s);
}
- }else if (tag.string == "raw_union") {
+ } else if (tag.string == "raw_union") {
if (is_raw_union) {
syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
}
is_raw_union = true;
+ } else if (tag.string == "simple") {
+ if (is_simple) {
+ syntax_error(tag, "Duplicate struct tag '#%.*s'", LIT(tag.string));
+ }
+ is_simple = true;
} else {
syntax_error(tag, "Invalid struct tag '#%.*s'", LIT(tag.string));
}
@@ -2906,7 +2926,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
parser_check_polymorphic_record_parameters(f, polymorphic_params);
return ast_struct_type(f, token, decls, name_count,
- polymorphic_params, is_packed, is_raw_union, is_all_or_none,
+ polymorphic_params, is_packed, is_raw_union, is_all_or_none, is_simple,
align, min_field_align, max_field_align,
where_token, where_clauses);
} break;
@@ -4008,6 +4028,10 @@ gb_internal ProcCallingConvention string_to_calling_convention(String const &s)
if (s == "win64") return ProcCC_Win64;
if (s == "sysv") return ProcCC_SysV;
+ if (s == "preserve/none") return ProcCC_PreserveNone;
+ if (s == "preserve/most") return ProcCC_PreserveMost;
+ if (s == "preserve/all") return ProcCC_PreserveAll;
+
if (s == "system") {
if (build_context.metrics.os == TargetOs_windows) {
return ProcCC_StdCall;
@@ -5054,6 +5078,10 @@ gb_internal Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) {
syntax_error(import_name, "'using import' is not allowed, please use the import name explicitly");
}
+ if (file_path.string == "\".\"") {
+ syntax_error(import_name, "Cannot cyclicly import packages");
+ }
+
expect_semicolon(f);
return s;
}
@@ -5399,8 +5427,9 @@ gb_internal Ast *parse_stmt(AstFile *f) {
expect_semicolon(f);
return stmt;
} else if (name.string == "force_inline" ||
- name.string == "force_no_inline") {
- Ast *expr = parse_force_inlining_operand(f, name);
+ name.string == "force_no_inline" ||
+ name.string == "must_tail") {
+ Ast *expr = parse_inlining_or_tailing_operand(f, name);
Ast *stmt = ast_expr_stmt(f, expr);
expect_semicolon(f);
return stmt;
@@ -5818,6 +5847,11 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
return nullptr;
}
+ if (string_ends_with(path, str_lit(".odin"))) {
+ error(pos, "'import' declarations cannot import directories with a .odin extension/suffix");
+ return nullptr;
+ }
+
isize files_with_ext = 0;
isize files_to_reserve = 1; // always reserve 1
for (FileInfo fi : list) {
@@ -5996,6 +6030,12 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
has_windows_drive = true;
}
}
+
+ for (isize i = 0; i < original_string.len; i++) {
+ if (original_string.text[i] == '\\') {
+ original_string.text[i] = '/';
+ }
+ }
}
#endif
@@ -6205,9 +6245,28 @@ gb_internal String build_tag_get_token(String s, String *out) {
return s;
}
+// returns true on failure
+gb_internal bool build_require_space_after(String s, String prefix) {
+ GB_ASSERT(string_starts_with(s, prefix));
+
+ if (s.len == prefix.len) {
+ return false;
+ }
+ String stripped = string_trim_whitespace(substring(s, prefix.len, s.len));
+
+ if (s[prefix.len] != ' ' && stripped.len != 0) {
+ return true;
+ }
+ return false;
+}
+
gb_internal bool parse_build_tag(Token token_for_pos, String s) {
String const prefix = str_lit("build");
GB_ASSERT(string_starts_with(s, prefix));
+ if (build_require_space_after(s, prefix)) {
+ syntax_error(token_for_pos, "Expected a space after #+%.*s", LIT(prefix));
+ return true;
+ }
s = string_trim_whitespace(substring(s, prefix.len, s.len));
if (s.len == 0) {
@@ -6331,19 +6390,21 @@ gb_internal String vet_tag_get_token(String s, String *out, bool allow_colon) {
}
-gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
+gb_internal u64 parse_vet_tag(Token token_for_pos, String s, u64 base_vet_flags) {
String const prefix = str_lit("vet");
GB_ASSERT(string_starts_with(s, prefix));
+ if (build_require_space_after(s, prefix)) {
+ syntax_error(token_for_pos, "Expected a space after #+%.*s", LIT(prefix));
+ return true;
+ }
s = string_trim_whitespace(substring(s, prefix.len, s.len));
+ u64 vet_flags = base_vet_flags;
+
if (s.len == 0) {
- return VetFlag_All;
+ vet_flags |= VetFlag_All;
}
-
- u64 vet_flags = 0;
- u64 vet_not_flags = 0;
-
while (s.len > 0) {
String p = string_trim_whitespace(vet_tag_get_token(s, &s, /*allow_colon*/false));
if (p.len == 0) {
@@ -6356,16 +6417,16 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
p = substring(p, 1, p.len);
if (p.len == 0) {
syntax_error(token_for_pos, "Expected a vet flag name after '!'");
- return build_context.vet_flags;
+ return vet_flags;
}
}
u64 flag = get_vet_flag_from_name(p);
if (flag != VetFlag_NONE) {
if (is_notted) {
- vet_not_flags |= flag;
+ vet_flags = vet_flags &~ flag;
} else {
- vet_flags |= flag;
+ vet_flags |= flag;
}
} else {
ERROR_BLOCK();
@@ -6383,26 +6444,20 @@ gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
error_line("\tcast\n");
error_line("\ttabs\n");
error_line("\texplicit-allocators\n");
- return build_context.vet_flags;
+ return vet_flags;
}
}
- if (vet_flags == 0 && vet_not_flags == 0) {
- return build_context.vet_flags;
- }
- if (vet_flags == 0 && vet_not_flags != 0) {
- return build_context.vet_flags &~ vet_not_flags;
- }
- if (vet_flags != 0 && vet_not_flags == 0) {
- return vet_flags;
- }
- GB_ASSERT(vet_flags != 0 && vet_not_flags != 0);
- return vet_flags &~ vet_not_flags;
+ return vet_flags;
}
gb_internal u64 parse_feature_tag(Token token_for_pos, String s) {
String const prefix = str_lit("feature");
GB_ASSERT(string_starts_with(s, prefix));
+ if (build_require_space_after(s, prefix)) {
+ syntax_error(token_for_pos, "Expected a space after #+%.*s", LIT(prefix));
+ return true;
+ }
s = string_trim_whitespace(substring(s, prefix.len, s.len));
if (s.len == 0) {
@@ -6578,7 +6633,7 @@ gb_internal bool parse_file_tag(const String &lc, const Token &tok, AstFile *f)
return false;
}
} else if (string_starts_with(lc, str_lit("vet"))) {
- f->vet_flags = parse_vet_tag(tok, lc);
+ f->vet_flags = parse_vet_tag(tok, lc, ast_file_vet_flags(f));
f->vet_flags_set = true;
} else if (string_starts_with(lc, str_lit("test"))) {
if ((build_context.command_kind & Command_test) == 0) {
diff --git a/src/parser.hpp b/src/parser.hpp
index 71b61d95f..39f56ffae 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -263,12 +263,17 @@ struct ForeignFileWorkerData {
-enum ProcInlining {
- ProcInlining_none = 0,
- ProcInlining_inline = 1,
+enum ProcInlining : u8 {
+ ProcInlining_none = 0,
+ ProcInlining_inline = 1,
ProcInlining_no_inline = 2,
};
+enum ProcTailing : u8 {
+ ProcTailing_none = 0,
+ ProcTailing_must_tail = 1,
+};
+
enum ProcTag {
ProcTag_bounds_check = 1<<0,
ProcTag_no_bounds_check = 1<<1,
@@ -296,6 +301,9 @@ enum ProcCallingConvention : i32 {
ProcCC_Win64 = 9,
ProcCC_SysV = 10,
+ ProcCC_PreserveNone = 11,
+ ProcCC_PreserveMost = 12,
+ ProcCC_PreserveAll = 13,
ProcCC_MAX,
@@ -315,6 +323,9 @@ gb_global char const *proc_calling_convention_strings[ProcCC_MAX] = {
"inlineasm",
"win64",
"sysv",
+ "preserve/none",
+ "preserve/most",
+ "preserve/all",
};
gb_internal ProcCallingConvention default_calling_convention(void) {
@@ -441,6 +452,7 @@ struct AstSplitArgs {
Ast *body; \
u64 tags; \
ProcInlining inlining; \
+ ProcTailing tailing; \
Token where_token; \
Slice<Ast *> where_clauses; \
DeclInfo *decl; \
@@ -486,6 +498,7 @@ AST_KIND(_ExprBegin, "", bool) \
Token close; \
Token ellipsis; \
ProcInlining inlining; \
+ ProcTailing tailing; \
bool optional_ok_one; \
bool was_selector; \
AstSplitArgs *split_args; \
@@ -767,6 +780,7 @@ AST_KIND(_TypeBegin, "", bool) \
bool is_raw_union; \
bool is_no_copy; \
bool is_all_or_none; \
+ bool is_simple; \
}) \
AST_KIND(UnionType, "union type", struct { \
Scope *scope; \
diff --git a/src/string.cpp b/src/string.cpp
index 9c08114a7..c26457acf 100644
--- a/src/string.cpp
+++ b/src/string.cpp
@@ -635,26 +635,107 @@ gb_internal String normalize_path(gbAllocator a, String const &path, String cons
#elif defined(GB_SYSTEM_UNIX) || defined(GB_SYSTEM_OSX)
#include <wchar.h>
- gb_internal int convert_multibyte_to_widechar(char const *multibyte_input, usize input_length, wchar_t *output, usize output_size) {
- String string = copy_string(heap_allocator(), make_string(cast(u8 const*)multibyte_input, input_length)); /* Guarantee NULL terminator */
- u8* input = string.text;
+ gb_internal void utf16_encode_surrogate_pair(Rune r, u16 *r1, u16 *r2) {
+ static Rune const _surr_self = 0x10000;
+ static Rune const _surr1 = 0xd800;
+ static Rune const _surr2 = 0xdc00;
+ Rune r_ = r - _surr_self;
+ *r1 = _surr1 + ((r_ >> 10) & 0x3ff);
+ *r2 = _surr2 + (r_ & 0x3ff);
+ }
+
+ gb_internal isize utf16_decode(u16 const *s, isize n, Rune *r) {
+ static Rune const _surr1 = 0xd800;
+ static Rune const _surr2 = 0xdc00;
+ static Rune const _surr3 = 0xe000;
+ static Rune const _surr_self = 0x10000;
+ if (n < 1) {
+ *r = GB_RUNE_INVALID;
+ return 0;
+ }
+ u16 c = s[0];
+ if (c < 0xd800 || c > 0xdfff) {
+ *r = cast(Rune)c;
+ return 1;
+ }
+ if (c >= 0xdc00) {
+ *r = GB_RUNE_INVALID;
+ return 1;
+ }
+ if (n < 2) {
+ *r = GB_RUNE_INVALID;
+ return 1;
+ }
+ u16 c2 = s[1];
+ if (c2 < 0xdc00 || c2 > 0xdfff) {
+ *r = GB_RUNE_INVALID;
+ return 1;
+ }
+ *r = (((c-_surr1)<<10) | (c2 - _surr2)) + _surr_self;
+ return 2;
+ }
- mbstate_t ps = { 0 };
- size_t result = mbsrtowcs(output, cast(const char**)&input, output_size, &ps);
+ gb_internal int convert_multibyte_to_widechar(char const *multibyte_input, usize input_length, wchar_t *output, usize output_size) {
+ u16 *out = cast(u16 *)output;
+ String s = make_string(cast(u8 const *)multibyte_input, input_length);
+ isize i = 0;
+ isize output_len = 0;
+ while (i < s.len) {
+ Rune r = 0;
+ isize width = utf8_decode(s.text + i, s.len - i, &r);
+ if (r == GB_RUNE_INVALID) {
+ return -1;
+ }
+ i += width;
+ if (r < 0x10000) {
+ if (out) {
+ if (cast(usize)output_len+1 > output_size) {
+ return -1;
+ }
+ out[output_len] = cast(u16)r;
+ }
+ output_len += 1;
+ } else {
+ if (out) {
+ if (cast(usize)output_len+2 > output_size) {
+ return -1;
+ }
+ u16 r1, r2;
+ utf16_encode_surrogate_pair(r, &r1, &r2);
+ out[output_len+0] = r1;
+ out[output_len+1] = r2;
+ }
+ output_len += 2;
+ }
+ }
- gb_free(heap_allocator(), string.text);
- return (result == (size_t)-1) ? -1 : (int)result;
+ return cast(int)output_len;
}
gb_internal int convert_widechar_to_multibyte(wchar_t const *widechar_input, usize input_length, char* output, usize output_size) {
- String string = copy_string(heap_allocator(), make_string(cast(u8 const*)widechar_input, input_length)); /* Guarantee NULL terminator */
- u8* input = string.text;
+ u16 const *in = cast(u16 const *)widechar_input;
+ isize i = 0;
+ isize output_len = 0;
+ while (i < input_length) {
+ Rune r;
+ isize width = utf16_decode(in + i, input_length - i, &r);
+ if (r == GB_RUNE_INVALID) {
+ return -1;
+ }
+ i += width;
- mbstate_t ps = { 0 };
- size_t result = wcsrtombs(output, cast(const wchar_t**)&input, output_size, &ps);
+ u8 buf[4];
+ isize char_len = gb_utf8_encode_rune(buf, r);
- gb_free(heap_allocator(), string.text);
- return (result == (size_t)-1) ? -1 : (int)result;
+ if(output) {
+ if (cast(usize)output_len+cast(usize)char_len > output_size) {
+ return -1;
+ }
+ gb_memmove(output + output_len, buf, char_len);
+ }
+ output_len += char_len;
+ }
+ return cast(int)output_len;
}
#else
#error Implement system
diff --git a/src/types.cpp b/src/types.cpp
index 18e3b56ac..a7f2bfda2 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -163,6 +163,7 @@ struct TypeStruct {
bool is_packed : 1;
bool is_raw_union : 1;
bool is_all_or_none : 1;
+ bool is_simple : 1;
bool is_poly_specialized : 1;
std::atomic<bool> are_offsets_being_processed;
@@ -2701,12 +2702,16 @@ gb_internal bool is_type_simple_compare(Type *t) {
case Type_SoaPointer:
case Type_Proc:
case Type_BitSet:
+ case Type_BitField:
return true;
case Type_Matrix:
return is_type_simple_compare(t->Matrix.elem);
case Type_Struct:
+ if (t->Struct.is_simple) {
+ return true;
+ }
for_array(i, t->Struct.fields) {
Entity *f = t->Struct.fields[i];
if (!is_type_simple_compare(f->type)) {
@@ -2728,6 +2733,16 @@ gb_internal bool is_type_simple_compare(Type *t) {
case Type_SimdVector:
return is_type_simple_compare(t->SimdVector.elem);
+ case Type_Tuple:
+ if (t->Tuple.variables.count == 1) {
+ return is_type_simple_compare(t->Tuple.variables[0]->type);
+ }
+ break;
+
+ case Type_Slice:
+ case Type_DynamicArray:
+ case Type_Map:
+ return false;
}
return false;
@@ -2757,12 +2772,16 @@ gb_internal bool is_type_nearly_simple_compare(Type *t) {
case Type_SoaPointer:
case Type_Proc:
case Type_BitSet:
+ case Type_BitField:
return true;
case Type_Matrix:
return is_type_nearly_simple_compare(t->Matrix.elem);
case Type_Struct:
+ if (t->Struct.is_simple) {
+ return true;
+ }
for_array(i, t->Struct.fields) {
Entity *f = t->Struct.fields[i];
if (!is_type_nearly_simple_compare(f->type)) {
@@ -2784,6 +2803,17 @@ gb_internal bool is_type_nearly_simple_compare(Type *t) {
case Type_SimdVector:
return is_type_nearly_simple_compare(t->SimdVector.elem);
+ case Type_Tuple:
+ if (t->Tuple.variables.count == 1) {
+ return is_type_nearly_simple_compare(t->Tuple.variables[0]->type);
+ }
+ break;
+
+ case Type_Slice:
+ case Type_DynamicArray:
+ case Type_Map:
+ return false;
+
}
return false;
@@ -5099,9 +5129,11 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
str = gb_string_appendc(str, ")");
}
- if (type->Struct.is_packed) str = gb_string_appendc(str, " #packed");
- if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union");
+ if (type->Struct.is_packed) str = gb_string_appendc(str, " #packed");
+ if (type->Struct.is_raw_union) str = gb_string_appendc(str, " #raw_union");
if (type->Struct.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Struct.custom_align);
+ if (type->Struct.is_all_or_none) str = gb_string_appendc(str, " #all_or_none");
+ if (type->Struct.is_simple) str = gb_string_appendc(str, " #simple");
str = gb_string_appendc(str, " {");
@@ -5210,40 +5242,12 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
case Type_Proc:
str = gb_string_appendc(str, "proc");
- switch (type->Proc.calling_convention) {
- case ProcCC_Odin:
- if (default_calling_convention() != ProcCC_Odin) {
- str = gb_string_appendc(str, " \"odin\" ");
- }
- break;
- case ProcCC_Contextless:
- if (default_calling_convention() != ProcCC_Contextless) {
- str = gb_string_appendc(str, " \"contextless\" ");
- }
- break;
- case ProcCC_CDecl:
- str = gb_string_appendc(str, " \"c\" ");
- break;
- case ProcCC_StdCall:
- str = gb_string_appendc(str, " \"std\" ");
- break;
- case ProcCC_FastCall:
- str = gb_string_appendc(str, " \"fastcall\" ");
- break;
- break;
- case ProcCC_None:
- str = gb_string_appendc(str, " \"none\" ");
- break;
- case ProcCC_Naked:
- str = gb_string_appendc(str, " \"naked\" ");
- break;
- // case ProcCC_VectorCall:
- // str = gb_string_appendc(str, " \"vectorcall\" ");
- // break;
- // case ProcCC_ClrCall:
- // str = gb_string_appendc(str, " \"clrcall\" ");
- // break;
+ if (type->Proc.calling_convention != default_calling_convention()) {
+ str = gb_string_appendc(str, " \"");
+ str = gb_string_appendc(str, proc_calling_convention_strings[type->Proc.calling_convention]);
+ str = gb_string_appendc(str, "\" ");
}
+
str = gb_string_appendc(str, "(");
if (type->Proc.params) {
str = write_type_to_string(str, type->Proc.params, shorthand, allow_polymorphic);