mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-13 09:22:22 -07:00
Merge remote-tracking branch 'offical/master'
This commit is contained in:
@@ -137,8 +137,8 @@ IP4_Address :: distinct [4]u8
|
||||
IP6_Address :: distinct [8]u16be
|
||||
Address :: union {IP4_Address, IP6_Address}
|
||||
|
||||
IP4_Loopback := IP4_Address{127, 0, 0, 1}
|
||||
IP6_Loopback := IP6_Address{0, 0, 0, 0, 0, 0, 0, 1}
|
||||
IP4_Loopback :: IP4_Address{127, 0, 0, 1}
|
||||
IP6_Loopback :: IP6_Address{0, 0, 0, 0, 0, 0, 0, 1}
|
||||
|
||||
IP4_Any := IP4_Address{}
|
||||
IP6_Any := IP6_Address{}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
@(require_results, enable_target_feature="lzcnt")
|
||||
_lzcnt_u32 :: #force_inline proc "c" (x: u32) -> u32 {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//+build amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
|
||||
cmpxchg16b :: #force_inline proc "c" (dst: ^u128, old, new: u128, $success, $failure: intrinsics.Atomic_Memory_Order) -> (val: u128) {
|
||||
return intrinsics.atomic_compare_exchange_strong_explicit(dst, old, new, success, failure)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
import "core:simd"
|
||||
|
||||
// _MM_SHUFFLE(z, y, x, w) -> (z<<6 | y<<4 | x<<2 | w)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
import "core:simd"
|
||||
|
||||
@(enable_target_feature="sse2")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
import "core:simd"
|
||||
|
||||
@(require_results, enable_target_feature="sse3")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
import "core:simd"
|
||||
_ :: simd
|
||||
|
||||
|
||||
@@ -36,6 +36,10 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
sync.wait(&t.cond, &t.mutex)
|
||||
}
|
||||
|
||||
if .Joined in t.flags {
|
||||
return nil
|
||||
}
|
||||
|
||||
when ODIN_OS != .Darwin {
|
||||
// Enable thread's cancelability.
|
||||
if can_set_thread_cancel_state {
|
||||
@@ -143,6 +147,11 @@ _join :: proc(t: ^Thread) {
|
||||
if res, ok := CAS(&t.flags, unjoined, joined); res == joined && !ok {
|
||||
return
|
||||
}
|
||||
// Prevent non-started threads from blocking main thread with initial wait
|
||||
// condition.
|
||||
if .Started not_in unjoined {
|
||||
_start(t)
|
||||
}
|
||||
unix.pthread_join(t.unix_thread, nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,10 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
__windows_thread_entry_proc :: proc "system" (t_: rawptr) -> win32.DWORD {
|
||||
t := (^Thread)(t_)
|
||||
|
||||
if .Joined in t.flags {
|
||||
return 0
|
||||
}
|
||||
|
||||
t.id = sync.current_thread_id()
|
||||
|
||||
{
|
||||
@@ -93,11 +97,16 @@ _join :: proc(t: ^Thread) {
|
||||
return
|
||||
}
|
||||
|
||||
t.flags += {.Joined}
|
||||
|
||||
if .Started not_in t.flags {
|
||||
t.flags += {.Started}
|
||||
win32.ResumeThread(t.win32_thread)
|
||||
}
|
||||
|
||||
win32.WaitForSingleObject(t.win32_thread, win32.INFINITE)
|
||||
win32.CloseHandle(t.win32_thread)
|
||||
t.win32_thread = win32.INVALID_HANDLE
|
||||
|
||||
t.flags += {.Joined}
|
||||
}
|
||||
|
||||
_join_multiple :: proc(threads: ..^Thread) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import "core:thread"
|
||||
import "core:time"
|
||||
import "core:reflect"
|
||||
import "base:runtime"
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
import "core:math/big"
|
||||
|
||||
/*
|
||||
|
||||
@@ -730,10 +730,11 @@ enum VetFlags : u64 {
|
||||
VetFlag_Semicolon = 1u<<4,
|
||||
VetFlag_UnusedVariables = 1u<<5,
|
||||
VetFlag_UnusedImports = 1u<<6,
|
||||
VetFlag_Deprecated = 1u<<7,
|
||||
|
||||
VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports,
|
||||
|
||||
VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt,
|
||||
VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt|VetFlag_Deprecated,
|
||||
|
||||
VetFlag_Using = VetFlag_UsingStmt|VetFlag_UsingParam,
|
||||
};
|
||||
@@ -755,6 +756,8 @@ u64 get_vet_flag_from_name(String const &name) {
|
||||
return VetFlag_Style;
|
||||
} else if (name == "semicolon") {
|
||||
return VetFlag_Semicolon;
|
||||
} else if (name == "deprecated") {
|
||||
return VetFlag_Deprecated;
|
||||
}
|
||||
return VetFlag_NONE;
|
||||
}
|
||||
|
||||
@@ -1441,6 +1441,13 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
|
||||
// return check_is_assignable_to(c, &o, poly); // && is_type_subtype_of_and_allow_polymorphic(o.type, poly);
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_BitField:
|
||||
if (source->kind == Type_BitField) {
|
||||
return is_polymorphic_type_assignable(c, poly->BitField.backing_type, source->BitField.backing_type, true, modify_type);
|
||||
}
|
||||
return false;
|
||||
|
||||
case Type_Tuple:
|
||||
GB_PANIC("This should never happen");
|
||||
return false;
|
||||
@@ -1787,6 +1794,13 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
if (o->mode == Addressing_Type) {
|
||||
gbString str = type_to_string(o->type);
|
||||
error(o->expr, "Expected an expression for operator '%.*s', got type '%s'", LIT(op.string), str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
Type *type = base_type(core_array_type(o->type));
|
||||
gbString str = nullptr;
|
||||
switch (op.kind) {
|
||||
@@ -10251,6 +10265,17 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
|
||||
case Type_Struct:
|
||||
if (is_type_soa_struct(t)) {
|
||||
valid = true;
|
||||
if (t->Struct.soa_kind == StructSoa_Fixed) {
|
||||
max_count = t->Struct.soa_count;
|
||||
if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) {
|
||||
gbString str = expr_to_string(node);
|
||||
error(node, "Cannot slice #soa array '%s', value is not addressable", str);
|
||||
gb_string_free(str);
|
||||
o->mode = Addressing_Invalid;
|
||||
o->expr = node;
|
||||
return kind;
|
||||
}
|
||||
}
|
||||
o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
|
||||
}
|
||||
break;
|
||||
|
||||
+50
-4
@@ -169,9 +169,16 @@ gb_internal bool check_has_break_list(Slice<Ast *> const &stmts, String const &l
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool check_has_break_expr(Ast * expr, String const &label) {
|
||||
if (expr && expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal bool check_has_break_expr_list(Slice<Ast *> const &exprs, String const &label) {
|
||||
for (Ast *expr : exprs) {
|
||||
if (expr && expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) {
|
||||
if (check_has_break_expr(expr, label)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -196,6 +203,13 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
|
||||
return check_has_break_list(stmt->BlockStmt.stmts, label, implicit);
|
||||
|
||||
case Ast_IfStmt:
|
||||
if (stmt->IfStmt.init && check_has_break(stmt->IfStmt.init, label, implicit)) {
|
||||
return true;
|
||||
}
|
||||
if (stmt->IfStmt.cond && check_has_break_expr(stmt->IfStmt.cond, label)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (check_has_break(stmt->IfStmt.body, label, implicit) ||
|
||||
(stmt->IfStmt.else_stmt != nullptr && check_has_break(stmt->IfStmt.else_stmt, label, implicit))) {
|
||||
return true;
|
||||
@@ -206,6 +220,9 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
|
||||
return check_has_break_list(stmt->CaseClause.stmts, label, implicit);
|
||||
|
||||
case Ast_SwitchStmt:
|
||||
if (stmt->SwitchStmt.init && check_has_break_expr(stmt->SwitchStmt.init, label)) {
|
||||
return true;
|
||||
}
|
||||
if (label != "" && check_has_break(stmt->SwitchStmt.body, label, false)) {
|
||||
return true;
|
||||
}
|
||||
@@ -218,6 +235,16 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
|
||||
break;
|
||||
|
||||
case Ast_ForStmt:
|
||||
if (stmt->ForStmt.init && check_has_break(stmt->ForStmt.init, label, implicit)) {
|
||||
return true;
|
||||
}
|
||||
if (stmt->ForStmt.cond && check_has_break_expr(stmt->ForStmt.cond, label)) {
|
||||
return true;
|
||||
}
|
||||
if (stmt->ForStmt.post && check_has_break(stmt->ForStmt.post, label, implicit)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (label != "" && check_has_break(stmt->ForStmt.body, label, false)) {
|
||||
return true;
|
||||
}
|
||||
@@ -253,7 +280,16 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
String label_string(Ast *node) {
|
||||
GB_ASSERT(node != nullptr);
|
||||
if (node->kind == Ast_Ident) {
|
||||
return node->Ident.token.string;
|
||||
} else if (node->kind == Ast_Label) {
|
||||
return label_string(node->Label.name);
|
||||
}
|
||||
GB_ASSERT("INVALID LABEL");
|
||||
return {};
|
||||
}
|
||||
|
||||
// NOTE(bill): The last expression has to be a 'return' statement
|
||||
// TODO(bill): This is a mild hack and should be probably handled properly
|
||||
@@ -264,7 +300,12 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(bs, BlockStmt, node);
|
||||
return check_is_terminating_list(bs->stmts, label);
|
||||
if (check_is_terminating_list(bs->stmts, label)) {
|
||||
if (bs->label != nullptr) {
|
||||
return check_is_terminating_list(bs->stmts, label_string(bs->label));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(es, ExprStmt, node);
|
||||
@@ -321,6 +362,9 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) {
|
||||
|
||||
case_ast_node(fs, ForStmt, node);
|
||||
if (fs->cond == nullptr && !check_has_break(fs->body, label, true)) {
|
||||
if (fs->label) {
|
||||
return !check_has_break(fs->body, label_string(fs->label), false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case_end;
|
||||
@@ -734,7 +778,7 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us,
|
||||
for (auto const &entry : scope->elements) {
|
||||
String name = entry.key;
|
||||
Entity *decl = entry.value;
|
||||
if (!is_entity_exported(decl)) continue;
|
||||
if (!is_entity_exported(decl, true)) continue;
|
||||
|
||||
Entity *found = scope_insert_with_name(ctx->scope, name, decl);
|
||||
if (found != nullptr) {
|
||||
@@ -759,6 +803,8 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us,
|
||||
bool is_ptr = is_type_pointer(e->type);
|
||||
Type *t = base_type(type_deref(e->type));
|
||||
if (t->kind == Type_Struct) {
|
||||
wait_signal_until_available(&t->Struct.fields_wait_signal);
|
||||
|
||||
Scope *found = t->Struct.scope;
|
||||
GB_ASSERT(found != nullptr);
|
||||
for (auto const &entry : found->elements) {
|
||||
|
||||
+8
-5
@@ -19,10 +19,12 @@ gb_internal void populate_using_array_index(CheckerContext *ctx, Ast *node, AstF
|
||||
}
|
||||
} else {
|
||||
Token tok = make_token_ident(name);
|
||||
if (field->names.count > 0) {
|
||||
tok.pos = ast_token(field->names[0]).pos;
|
||||
} else {
|
||||
tok.pos = ast_token(field->type).pos;
|
||||
if (field) {
|
||||
if (field->names.count > 0) {
|
||||
tok.pos = ast_token(field->names[0]).pos;
|
||||
} else {
|
||||
tok.pos = ast_token(field->type).pos;
|
||||
}
|
||||
}
|
||||
Entity *f = alloc_entity_array_elem(nullptr, tok, t->Array.elem, idx);
|
||||
add_entity(ctx, ctx->scope, nullptr, f);
|
||||
@@ -191,9 +193,10 @@ gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entit
|
||||
|
||||
if (is_using && p->names.count > 0) {
|
||||
Type *first_type = fields_array[fields_array.count-1]->type;
|
||||
bool soa_ptr = is_type_soa_pointer(first_type);
|
||||
Type *t = base_type(type_deref(first_type));
|
||||
|
||||
if (!does_field_type_allow_using(t) &&
|
||||
if ((soa_ptr || !does_field_type_allow_using(t)) &&
|
||||
p->names.count >= 1 &&
|
||||
p->names[0]->kind == Ast_Ident) {
|
||||
Token name_token = p->names[0]->Ident.token;
|
||||
|
||||
@@ -672,7 +672,20 @@ gb_internal int error_value_cmp(void const *a, void const *b) {
|
||||
return token_pos_cmp(x->pos, y->pos);
|
||||
}
|
||||
|
||||
gb_internal bool errors_already_printed = false;
|
||||
|
||||
gb_internal void print_all_errors(void) {
|
||||
if (errors_already_printed) {
|
||||
if (global_error_collector.warning_count.load() == global_error_collector.error_values.count) {
|
||||
for (ErrorValue &ev : global_error_collector.error_values) {
|
||||
array_free(&ev.msg);
|
||||
}
|
||||
array_clear(&global_error_collector.error_values);
|
||||
errors_already_printed = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto const &escape_char = [](gbString res, u8 c) -> gbString {
|
||||
switch (c) {
|
||||
case '\n': res = gb_string_append_length(res, "\\n", 2); break;
|
||||
@@ -827,4 +840,6 @@ gb_internal void print_all_errors(void) {
|
||||
}
|
||||
gbFile *f = gb_file_get_standard(gbFileStandard_Error);
|
||||
gb_file_write(f, res, gb_string_length(res));
|
||||
|
||||
errors_already_printed = true;
|
||||
}
|
||||
@@ -2514,7 +2514,7 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
|
||||
case Token_Lt: runtime_procedure = "cstring_lt"; break;
|
||||
case Token_Gt: runtime_procedure = "cstring_gt"; break;
|
||||
case Token_LtEq: runtime_procedure = "cstring_le"; break;
|
||||
case Token_GtEq: runtime_procedure = "cstring_gt"; break;
|
||||
case Token_GtEq: runtime_procedure = "cstring_ge"; break;
|
||||
}
|
||||
GB_ASSERT(runtime_procedure != nullptr);
|
||||
|
||||
@@ -2537,7 +2537,7 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
|
||||
case Token_Lt: runtime_procedure = "string_lt"; break;
|
||||
case Token_Gt: runtime_procedure = "string_gt"; break;
|
||||
case Token_LtEq: runtime_procedure = "string_le"; break;
|
||||
case Token_GtEq: runtime_procedure = "string_gt"; break;
|
||||
case Token_GtEq: runtime_procedure = "string_ge"; break;
|
||||
}
|
||||
GB_ASSERT(runtime_procedure != nullptr);
|
||||
|
||||
@@ -4141,7 +4141,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
|
||||
if (se->high == nullptr) {
|
||||
lbValue offset = base;
|
||||
LLVMValueRef indices[1] = {low.value};
|
||||
offset.value = LLVMBuildGEP2(p->builder, lb_type(p->module, offset.type->MultiPointer.elem), offset.value, indices, 1, "");
|
||||
offset.value = LLVMBuildGEP2(p->builder, lb_type(p->module, base_type(offset.type)->MultiPointer.elem), offset.value, indices, 1, "");
|
||||
lb_addr_store(p, res, offset);
|
||||
} else {
|
||||
low = lb_emit_conv(p, low, t_int);
|
||||
@@ -4150,7 +4150,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
|
||||
lb_emit_multi_pointer_slice_bounds_check(p, se->open, low, high);
|
||||
|
||||
LLVMValueRef indices[1] = {low.value};
|
||||
LLVMValueRef ptr = LLVMBuildGEP2(p->builder, lb_type(p->module, base.type->MultiPointer.elem), base.value, indices, 1, "");
|
||||
LLVMValueRef ptr = LLVMBuildGEP2(p->builder, lb_type(p->module, base_type(base.type)->MultiPointer.elem), base.value, indices, 1, "");
|
||||
LLVMValueRef len = LLVMBuildSub(p->builder, high.value, low.value, "");
|
||||
|
||||
LLVMValueRef gep0 = lb_emit_struct_ep(p, res.addr, 0).value;
|
||||
@@ -5194,6 +5194,54 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
|
||||
lbValue ptr = lb_address_from_load_or_generate_local(p, lb_build_expr(p, expr));
|
||||
return lb_addr(ptr);
|
||||
case_end;
|
||||
|
||||
|
||||
case_ast_node(be, OrBranchExpr, expr);
|
||||
lbBlock *block = nullptr;
|
||||
|
||||
if (be->label != nullptr) {
|
||||
lbBranchBlocks bb = lb_lookup_branch_blocks(p, be->label);
|
||||
switch (be->token.kind) {
|
||||
case Token_or_break: block = bb.break_; break;
|
||||
case Token_or_continue: block = bb.continue_; break;
|
||||
}
|
||||
} else {
|
||||
for (lbTargetList *t = p->target_list; t != nullptr && block == nullptr; t = t->prev) {
|
||||
if (t->is_block) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (be->token.kind) {
|
||||
case Token_or_break: block = t->break_; break;
|
||||
case Token_or_continue: block = t->continue_; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GB_ASSERT(block != nullptr);
|
||||
TypeAndValue tv = expr->tav;
|
||||
|
||||
lbValue lhs = {};
|
||||
lbValue rhs = {};
|
||||
lb_emit_try_lhs_rhs(p, be->expr, tv, &lhs, &rhs);
|
||||
Type *type = default_type(tv.type);
|
||||
if (lhs.value) {
|
||||
lhs = lb_emit_conv(p, lhs, type);
|
||||
} else if (type != nullptr && type != t_invalid) {
|
||||
lhs = lb_const_nil(p->module, type);
|
||||
}
|
||||
|
||||
lbBlock *then = lb_create_block(p, "or_branch.then");
|
||||
lbBlock *else_ = lb_create_block(p, "or_branch.else");
|
||||
|
||||
lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_);
|
||||
lb_start_block(p, else_);
|
||||
lb_emit_defer_stmts(p, lbDeferExit_Branch, block);
|
||||
lb_emit_jump(p, block);
|
||||
lb_start_block(p, then);
|
||||
|
||||
return lb_addr(lb_address_from_load_or_generate_local(p, lhs));
|
||||
case_end;
|
||||
}
|
||||
|
||||
TokenPos token_pos = ast_token(expr).pos;
|
||||
|
||||
@@ -1180,7 +1180,7 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
|
||||
Type *t = addr.bitfield.type;
|
||||
|
||||
if (do_mask) {
|
||||
GB_ASSERT(addr.bitfield.bit_size < 8*type_size_of(ct));
|
||||
GB_ASSERT(addr.bitfield.bit_size <= 8*type_size_of(ct));
|
||||
|
||||
lbValue mask = lb_const_int(p->module, t, (1ull<<cast(u64)addr.bitfield.bit_size)-1);
|
||||
r = lb_emit_arith(p, Token_And, r, mask, t);
|
||||
|
||||
+21
-20
@@ -2157,6 +2157,16 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
|
||||
lb_open_scope(p, is->scope); // Scope #1
|
||||
defer (lb_close_scope(p, lbDeferExit_Default, nullptr));
|
||||
|
||||
lbBlock *then = lb_create_block(p, "if.then");
|
||||
lbBlock *done = lb_create_block(p, "if.done");
|
||||
lbBlock *else_ = done;
|
||||
if (is->else_stmt != nullptr) {
|
||||
else_ = lb_create_block(p, "if.else");
|
||||
}
|
||||
if (is->label != nullptr) {
|
||||
lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
|
||||
tl->is_block = true;
|
||||
}
|
||||
if (is->init != nullptr) {
|
||||
lbBlock *init = lb_create_block(p, "if.init");
|
||||
lb_emit_jump(p, init);
|
||||
@@ -2164,22 +2174,12 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
|
||||
|
||||
lb_build_stmt(p, is->init);
|
||||
}
|
||||
lbBlock *then = lb_create_block(p, "if.then");
|
||||
lbBlock *done = lb_create_block(p, "if.done");
|
||||
lbBlock *else_ = done;
|
||||
if (is->else_stmt != nullptr) {
|
||||
else_ = lb_create_block(p, "if.else");
|
||||
}
|
||||
|
||||
lbValue cond = lb_build_cond(p, is->cond, then, else_);
|
||||
// Note `cond.value` only set for non-and/or conditions and const negs so that the `LLVMIsConstant()`
|
||||
// and `LLVMConstIntGetZExtValue()` calls below will be valid and `LLVMInstructionEraseFromParent()`
|
||||
// will target the correct (& only) branch statement
|
||||
|
||||
if (is->label != nullptr) {
|
||||
lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
|
||||
tl->is_block = true;
|
||||
}
|
||||
|
||||
if (cond.value && LLVMIsConstant(cond.value)) {
|
||||
// NOTE(bill): Do a compile time short circuit for when the condition is constantly known.
|
||||
@@ -2244,15 +2244,6 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
|
||||
if (p->debug_info != nullptr) {
|
||||
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, node));
|
||||
}
|
||||
|
||||
if (fs->init != nullptr) {
|
||||
#if 1
|
||||
lbBlock *init = lb_create_block(p, "for.init");
|
||||
lb_emit_jump(p, init);
|
||||
lb_start_block(p, init);
|
||||
#endif
|
||||
lb_build_stmt(p, fs->init);
|
||||
}
|
||||
lbBlock *body = lb_create_block(p, "for.body");
|
||||
lbBlock *done = lb_create_block(p, "for.done"); // NOTE(bill): Append later
|
||||
lbBlock *loop = body;
|
||||
@@ -2264,6 +2255,17 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
|
||||
post = lb_create_block(p, "for.post");
|
||||
}
|
||||
|
||||
lb_push_target_list(p, fs->label, done, post, nullptr);
|
||||
|
||||
if (fs->init != nullptr) {
|
||||
#if 1
|
||||
lbBlock *init = lb_create_block(p, "for.init");
|
||||
lb_emit_jump(p, init);
|
||||
lb_start_block(p, init);
|
||||
#endif
|
||||
lb_build_stmt(p, fs->init);
|
||||
}
|
||||
|
||||
lb_emit_jump(p, loop);
|
||||
lb_start_block(p, loop);
|
||||
|
||||
@@ -2276,7 +2278,6 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
|
||||
lb_start_block(p, body);
|
||||
}
|
||||
|
||||
lb_push_target_list(p, fs->label, done, post, nullptr);
|
||||
|
||||
lb_build_stmt(p, fs->body);
|
||||
|
||||
|
||||
+28
-3
@@ -11,6 +11,9 @@ gb_internal bool ast_file_vet_style(AstFile *f) {
|
||||
return (ast_file_vet_flags(f) & VetFlag_Style) != 0;
|
||||
}
|
||||
|
||||
gb_internal bool ast_file_vet_deprecated(AstFile *f) {
|
||||
return (ast_file_vet_flags(f) & VetFlag_Deprecated) != 0;
|
||||
}
|
||||
|
||||
gb_internal bool file_allow_newline(AstFile *f) {
|
||||
bool is_strict = build_context.strict_style || ast_file_vet_style(f);
|
||||
@@ -1569,7 +1572,7 @@ gb_internal Token expect_operator(AstFile *f) {
|
||||
LIT(p));
|
||||
}
|
||||
if (prev.kind == Token_Ellipsis) {
|
||||
syntax_warning(prev, "'..' for ranges has now been deprecated, prefer '..='");
|
||||
syntax_error(prev, "'..' for ranges are not allowed, did you mean '..<' or '..='?");
|
||||
f->tokens[f->curr_token_index].flags |= TokenFlag_Replace;
|
||||
}
|
||||
|
||||
@@ -5508,11 +5511,15 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
|
||||
}
|
||||
}
|
||||
if (files_with_ext == 0 || files_to_reserve == 1) {
|
||||
ERROR_BLOCK();
|
||||
if (files_with_ext != 0) {
|
||||
syntax_error(pos, "Directory contains no .odin files for the specified platform: %.*s", LIT(rel_path));
|
||||
} else {
|
||||
syntax_error(pos, "Empty directory that contains no .odin files: %.*s", LIT(rel_path));
|
||||
}
|
||||
if (build_context.command_kind == Command_test) {
|
||||
error_line("\tSuggestion: Make an .odin file that imports packages to test and use the `-all-packages` flag.");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -5690,8 +5697,26 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
|
||||
|
||||
if (collection_name.len > 0) {
|
||||
// NOTE(bill): `base:runtime` == `core:runtime`
|
||||
if (collection_name == "core" && string_starts_with(file_str, str_lit("runtime"))) {
|
||||
collection_name = str_lit("base");
|
||||
if (collection_name == "core") {
|
||||
bool replace_with_base = false;
|
||||
if (string_starts_with(file_str, str_lit("runtime"))) {
|
||||
replace_with_base = true;
|
||||
} else if (string_starts_with(file_str, str_lit("intrinsics"))) {
|
||||
replace_with_base = true;
|
||||
} if (string_starts_with(file_str, str_lit("builtin"))) {
|
||||
replace_with_base = true;
|
||||
}
|
||||
|
||||
if (replace_with_base) {
|
||||
collection_name = str_lit("base");
|
||||
}
|
||||
if (replace_with_base) {
|
||||
if (ast_file_vet_deprecated(node->file())) {
|
||||
syntax_error(node, "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str));
|
||||
} else {
|
||||
syntax_warning(ast_token(node), "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (collection_name == "system") {
|
||||
|
||||
+1
-4
@@ -400,16 +400,13 @@ gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(dir_stat.st_mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
i64 size = dir_stat.st_size;
|
||||
|
||||
FileInfo info = {};
|
||||
info.name = copy_string(a, name);
|
||||
info.fullpath = path_to_full_path(a, filepath);
|
||||
info.size = size;
|
||||
info.is_dir = S_ISDIR(dir_stat.st_mode);
|
||||
array_add(fi, info);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
ODIN=../../odin
|
||||
|
||||
all: rtti_test map_test pow_test 128_test asan_test
|
||||
all: rtti_test map_test pow_test 128_test asan_test string_compare_test
|
||||
|
||||
rtti_test:
|
||||
$(ODIN) run test_rtti.odin -file -vet -strict-style -o:minimal
|
||||
@@ -16,3 +16,6 @@ pow_test:
|
||||
|
||||
asan_test:
|
||||
$(ODIN) run test_asan.odin -file -sanitize:address -debug
|
||||
|
||||
string_compare_test:
|
||||
$(ODIN) run test_string_compare.odin -file -vet -strict-style -o:minimal
|
||||
@@ -1,8 +1,10 @@
|
||||
@echo off
|
||||
set PATH_TO_ODIN==..\..\odin
|
||||
rem %PATH_TO_ODIN% run test_rtti.odin -file -vet -strict-style -o:minimal || exit /b
|
||||
%PATH_TO_ODIN% run test_map.odin -file -vet -strict-style -o:minimal || exit /b
|
||||
%PATH_TO_ODIN% run test_map.odin -file -vet -strict-style -o:minimal || exit /b
|
||||
rem -define:SEED=42
|
||||
%PATH_TO_ODIN% run test_pow.odin -file -vet -strict-style -o:minimal || exit /b
|
||||
%PATH_TO_ODIN% run test_pow.odin -file -vet -strict-style -o:minimal || exit /b
|
||||
|
||||
%PATH_TO_ODIN% run test_128.odin -file -vet -strict-style -o:minimal || exit /b
|
||||
%PATH_TO_ODIN% run test_128.odin -file -vet -strict-style -o:minimal || exit /b
|
||||
|
||||
%PATH_TO_ODIN% run test_string_compare.odin -file -vet -strict-style -o:minimal || exit /b
|
||||
@@ -0,0 +1,93 @@
|
||||
package test_internal_string_compare
|
||||
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:testing"
|
||||
|
||||
Op :: enum { Eq, Lt, Gt }
|
||||
|
||||
Test :: struct {
|
||||
a: cstring,
|
||||
b: cstring,
|
||||
res: [Op]bool,
|
||||
}
|
||||
|
||||
CASES := []Test{
|
||||
{"hellope", "hellope", {.Eq=true, .Lt=false, .Gt=false}},
|
||||
{"Hellope", "hellope", {.Eq=false, .Lt=true, .Gt=false}}, // H < h
|
||||
{"Hell", "Hellope", {.Eq=false, .Lt=true, .Gt=false}},
|
||||
{"Hellope!", "Hellope", {.Eq=false, .Lt=false, .Gt=true }},
|
||||
{"Hellopf", "Hellope", {.Eq=false, .Lt=false, .Gt=true }},
|
||||
}
|
||||
|
||||
@test
|
||||
string_compare :: proc(t: ^testing.T) {
|
||||
for v in CASES {
|
||||
s_a := string(v.a)
|
||||
s_b := string(v.b)
|
||||
|
||||
for res, op in v.res {
|
||||
switch op {
|
||||
case .Eq:
|
||||
expect(t, (v.a == v.b) == res, fmt.tprintf("Expected cstring(\"%v\") == cstring(\"%v\") to be %v", v.a, v.b, res))
|
||||
expect(t, (s_a == s_b) == res, fmt.tprintf("Expected string(\"%v\") == string(\"%v\") to be %v", v.a, v.b, res))
|
||||
|
||||
// If a == b then a != b
|
||||
expect(t, (v.a != v.b) == !res, fmt.tprintf("Expected cstring(\"%v\") != cstring(\"%v\") to be %v", v.a, v.b, !res))
|
||||
expect(t, (s_a != s_b) == !res, fmt.tprintf("Expected string(\"%v\") != string(\"%v\") to be %v", v.a, v.b, !res))
|
||||
|
||||
case .Lt:
|
||||
expect(t, (v.a < v.b) == res, fmt.tprintf("Expected cstring(\"%v\") < cstring(\"%v\") to be %v", v.a, v.b, res))
|
||||
expect(t, (s_a < s_b) == res, fmt.tprintf("Expected string(\"%v\") < string(\"%v\") to be %v", v.a, v.b, res))
|
||||
|
||||
// .Lt | .Eq == .LtEq
|
||||
lteq := v.res[.Eq] | res
|
||||
expect(t, (v.a <= v.b) == lteq, fmt.tprintf("Expected cstring(\"%v\") <= cstring(\"%v\") to be %v", v.a, v.b, lteq))
|
||||
expect(t, (s_a <= s_b) == lteq, fmt.tprintf("Expected string(\"%v\") <= string(\"%v\") to be %v", v.a, v.b, lteq))
|
||||
|
||||
case .Gt:
|
||||
expect(t, (v.a > v.b) == res, fmt.tprintf("Expected cstring(\"%v\") > cstring(\"%v\") to be %v", v.a, v.b, res))
|
||||
expect(t, (s_a > s_b) == res, fmt.tprintf("Expected string(\"%v\") > string(\"%v\") to be %v", v.a, v.b, res))
|
||||
|
||||
// .Gt | .Eq == .GtEq
|
||||
gteq := v.res[.Eq] | res
|
||||
expect(t, (v.a >= v.b) == gteq, fmt.tprintf("Expected cstring(\"%v\") >= cstring(\"%v\") to be %v", v.a, v.b, gteq))
|
||||
expect(t, (s_a >= s_b) == gteq, fmt.tprintf("Expected string(\"%v\") >= string(\"%v\") to be %v", v.a, v.b, gteq))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------- -------- -------- -------- -------- -------- -------- -------- -------- --------
|
||||
|
||||
main :: proc() {
|
||||
t := testing.T{}
|
||||
|
||||
string_compare(&t)
|
||||
|
||||
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
|
||||
if TEST_fail > 0 {
|
||||
os.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_count := 0
|
||||
TEST_fail := 0
|
||||
|
||||
when ODIN_TEST {
|
||||
expect :: testing.expect
|
||||
log :: testing.log
|
||||
} else {
|
||||
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
|
||||
TEST_count += 1
|
||||
if !condition {
|
||||
TEST_fail += 1
|
||||
fmt.printf("[%v] %v\n", loc, message)
|
||||
return
|
||||
}
|
||||
}
|
||||
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
|
||||
fmt.printf("[%v] ", loc)
|
||||
fmt.printf("log: %v\n", v)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user