mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 15:48:51 +00:00
Merge branch 'master' into tilde
This commit is contained in:
+2
-1
@@ -270,6 +270,7 @@ bin/
|
|||||||
|
|
||||||
# - Linux/MacOS
|
# - Linux/MacOS
|
||||||
odin
|
odin
|
||||||
|
!odin/
|
||||||
odin.dSYM
|
odin.dSYM
|
||||||
*.bin
|
*.bin
|
||||||
demo.bin
|
demo.bin
|
||||||
@@ -286,4 +287,4 @@ shared/
|
|||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
examples/bug/
|
examples/bug/
|
||||||
build.sh
|
build.sh
|
||||||
!core/debug/
|
!core/debug/
|
||||||
|
|||||||
@@ -242,7 +242,7 @@ arena_growing_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, min
|
|||||||
return arena_growing_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
|
return arena_growing_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
|
// Ability to bootstrap allocate a struct with an arena within the struct itself using the static variant strategy.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||||
bootstrap: Arena
|
bootstrap: Arena
|
||||||
@@ -258,7 +258,7 @@ arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintpt
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
|
// Ability to bootstrap allocate a struct with an arena within the struct itself using the static variant strategy.
|
||||||
@(require_results)
|
@(require_results)
|
||||||
arena_static_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
arena_static_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||||
return arena_static_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
|
return arena_static_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
|
||||||
@@ -271,7 +271,7 @@ arena_allocator :: proc(arena: ^Arena) -> mem.Allocator {
|
|||||||
return mem.Allocator{arena_allocator_proc, arena}
|
return mem.Allocator{arena_allocator_proc, arena}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The allocator procedured by an `Allocator` produced by `arena_allocator`
|
// The allocator procedure used by an `Allocator` produced by `arena_allocator`
|
||||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||||
size, alignment: int,
|
size, alignment: int,
|
||||||
old_memory: rawptr, old_size: int,
|
old_memory: rawptr, old_size: int,
|
||||||
@@ -328,7 +328,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// An `Arena_Temp` is a way to produce temporary watermarks to reset a arena to a previous state.
|
// An `Arena_Temp` is a way to produce temporary watermarks to reset an arena to a previous state.
|
||||||
// All uses of an `Arena_Temp` must be handled by ending them with `arena_temp_end` or ignoring them with `arena_temp_ignore`.
|
// All uses of an `Arena_Temp` must be handled by ending them with `arena_temp_end` or ignoring them with `arena_temp_ignore`.
|
||||||
Arena_Temp :: struct {
|
Arena_Temp :: struct {
|
||||||
arena: ^Arena,
|
arena: ^Arena,
|
||||||
|
|||||||
@@ -7,10 +7,21 @@ current_thread_id :: proc "contextless" () -> int {
|
|||||||
return _current_thread_id()
|
return _current_thread_id()
|
||||||
}
|
}
|
||||||
|
|
||||||
// A Mutex is a mutual exclusion lock
|
// A Mutex is a [[mutual exclusion lock; https://en.wikipedia.org/wiki/Mutual_exclusion]]
|
||||||
// The zero value for a Mutex is an unlocked mutex
|
// It can be used to prevent more than one thread from executing the same piece of code,
|
||||||
|
// and thus prevent access to same piece of memory by multiple threads, at the same time.
|
||||||
//
|
//
|
||||||
// A Mutex must not be copied after first use
|
// A Mutex's zero value represents an initial, *unlocked* state.
|
||||||
|
//
|
||||||
|
// If another thread tries to take the lock while another thread holds it, it will pause
|
||||||
|
// until the lock is released. Code or memory that is "surrounded" by a mutex lock is said
|
||||||
|
// to be "guarded by a mutex".
|
||||||
|
//
|
||||||
|
// A Mutex must not be copied after first use (e.g., after locking it the first time).
|
||||||
|
// This is because, in order to coordinate with other threads, all threads must watch
|
||||||
|
// the same memory address to know when the lock has been released. Trying to use a
|
||||||
|
// copy of the lock at a different memory address will result in broken and unsafe
|
||||||
|
// behavior. For this reason, Mutexes are marked as `#no_copy`.
|
||||||
Mutex :: struct #no_copy {
|
Mutex :: struct #no_copy {
|
||||||
impl: _Mutex,
|
impl: _Mutex,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,6 +159,11 @@ foreign kernel32 {
|
|||||||
WaitForSingleObject :: proc(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD ---
|
WaitForSingleObject :: proc(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD ---
|
||||||
Sleep :: proc(dwMilliseconds: DWORD) ---
|
Sleep :: proc(dwMilliseconds: DWORD) ---
|
||||||
GetProcessId :: proc(handle: HANDLE) -> DWORD ---
|
GetProcessId :: proc(handle: HANDLE) -> DWORD ---
|
||||||
|
CopyFileW :: proc(
|
||||||
|
lpExistingFileName: LPCWSTR,
|
||||||
|
lpNewFileName: LPCWSTR,
|
||||||
|
bFailIfExists: BOOL,
|
||||||
|
) -> BOOL ---
|
||||||
CopyFileExW :: proc(
|
CopyFileExW :: proc(
|
||||||
lpExistingFileName: LPCWSTR,
|
lpExistingFileName: LPCWSTR,
|
||||||
lpNewFileName: LPCWSTR,
|
lpNewFileName: LPCWSTR,
|
||||||
|
|||||||
@@ -206,4 +206,14 @@ foreign ws2_32 {
|
|||||||
optval: ^c_char,
|
optval: ^c_char,
|
||||||
optlen: ^c_int,
|
optlen: ^c_int,
|
||||||
) -> c_int ---
|
) -> c_int ---
|
||||||
|
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohl)
|
||||||
|
ntohl :: proc(netlong: c_ulong) -> c_ulong ---
|
||||||
|
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohs)
|
||||||
|
ntohs :: proc(netshort: c_ushort) -> c_ushort ---
|
||||||
|
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htonl)
|
||||||
|
@(deprecated="Use endian specific integers instead, https://odin-lang.org/docs/overview/#basic-types")
|
||||||
|
htonl :: proc(hostlong: c_ulong) -> c_ulong ---
|
||||||
|
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htons)
|
||||||
|
@(deprecated="Use endian specific integers instead, https://odin-lang.org/docs/overview/#basic-types")
|
||||||
|
htons :: proc(hostshort: c_ushort) -> c_ushort ---
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ import "core:fmt"
|
|||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:time"
|
import "core:time"
|
||||||
import "core:intrinsics"
|
import "core:intrinsics"
|
||||||
|
import "core:reflect"
|
||||||
|
|
||||||
|
_ :: reflect // alias reflect to nothing to force visibility for -vet
|
||||||
|
|
||||||
// IMPORTANT NOTE: Compiler requires this layout
|
// IMPORTANT NOTE: Compiler requires this layout
|
||||||
Test_Signature :: proc(^T)
|
Test_Signature :: proc(^T)
|
||||||
@@ -89,7 +92,7 @@ expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bo
|
|||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) {
|
expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) {
|
||||||
ok := value == expected
|
ok := value == expected || reflect.is_nil(value) && reflect.is_nil(expected)
|
||||||
if !ok {
|
if !ok {
|
||||||
errorf(t, "expected %v, got %v", expected, value, loc=loc)
|
errorf(t, "expected %v, got %v", expected, value, loc=loc)
|
||||||
}
|
}
|
||||||
@@ -100,4 +103,4 @@ expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location) -> boo
|
|||||||
|
|
||||||
set_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) {
|
set_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) {
|
||||||
_fail_timeout(t, duration, loc)
|
_fail_timeout(t, duration, loc)
|
||||||
}
|
}
|
||||||
|
|||||||
+67
-57
@@ -757,6 +757,66 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin
|
|||||||
return link_name;
|
return link_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext const &ac) {
|
||||||
|
if (!(ac.objc_name.len || ac.objc_is_class_method || ac.objc_type)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
|
||||||
|
error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
|
||||||
|
} else if (ac.objc_type == nullptr) {
|
||||||
|
error(e->token, "@(objc_name) requires that @(objc_type) to be set");
|
||||||
|
} else if (ac.objc_name.len == 0 && ac.objc_type) {
|
||||||
|
error(e->token, "@(objc_name) is required with @(objc_type)");
|
||||||
|
} else {
|
||||||
|
Type *t = ac.objc_type;
|
||||||
|
if (t->kind == Type_Named) {
|
||||||
|
Entity *tn = t->Named.type_name;
|
||||||
|
|
||||||
|
GB_ASSERT(tn->kind == Entity_TypeName);
|
||||||
|
|
||||||
|
if (tn->scope != e->scope) {
|
||||||
|
error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
|
||||||
|
} else {
|
||||||
|
mutex_lock(&global_type_name_objc_metadata_mutex);
|
||||||
|
defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
|
||||||
|
|
||||||
|
if (!tn->TypeName.objc_metadata) {
|
||||||
|
tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
|
||||||
|
}
|
||||||
|
auto *md = tn->TypeName.objc_metadata;
|
||||||
|
mutex_lock(md->mutex);
|
||||||
|
defer (mutex_unlock(md->mutex));
|
||||||
|
|
||||||
|
if (!ac.objc_is_class_method) {
|
||||||
|
bool ok = true;
|
||||||
|
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
|
||||||
|
if (entry.name == ac.objc_name) {
|
||||||
|
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ok) {
|
||||||
|
array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bool ok = true;
|
||||||
|
for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
|
||||||
|
if (entry.name == ac.objc_name) {
|
||||||
|
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ok) {
|
||||||
|
array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||||
GB_ASSERT(e->type == nullptr);
|
GB_ASSERT(e->type == nullptr);
|
||||||
if (d->proc_lit->kind != Ast_ProcLit) {
|
if (d->proc_lit->kind != Ast_ProcLit) {
|
||||||
@@ -840,62 +900,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
|||||||
}
|
}
|
||||||
e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
|
e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
|
||||||
|
|
||||||
if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) {
|
check_objc_methods(ctx, e, ac);
|
||||||
if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
|
|
||||||
error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
|
|
||||||
} else if (ac.objc_type == nullptr) {
|
|
||||||
error(e->token, "@(objc_name) requires that @(objc_type) to be set");
|
|
||||||
} else if (ac.objc_name.len == 0 && ac.objc_type) {
|
|
||||||
error(e->token, "@(objc_name) is required with @(objc_type)");
|
|
||||||
} else {
|
|
||||||
Type *t = ac.objc_type;
|
|
||||||
if (t->kind == Type_Named) {
|
|
||||||
Entity *tn = t->Named.type_name;
|
|
||||||
|
|
||||||
GB_ASSERT(tn->kind == Entity_TypeName);
|
|
||||||
|
|
||||||
if (tn->scope != e->scope) {
|
|
||||||
error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
|
|
||||||
} else {
|
|
||||||
mutex_lock(&global_type_name_objc_metadata_mutex);
|
|
||||||
defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
|
|
||||||
|
|
||||||
if (!tn->TypeName.objc_metadata) {
|
|
||||||
tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
|
|
||||||
}
|
|
||||||
auto *md = tn->TypeName.objc_metadata;
|
|
||||||
mutex_lock(md->mutex);
|
|
||||||
defer (mutex_unlock(md->mutex));
|
|
||||||
|
|
||||||
if (!ac.objc_is_class_method) {
|
|
||||||
bool ok = true;
|
|
||||||
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
|
|
||||||
if (entry.name == ac.objc_name) {
|
|
||||||
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
|
|
||||||
ok = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ok) {
|
|
||||||
array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bool ok = true;
|
|
||||||
for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
|
|
||||||
if (entry.name == ac.objc_name) {
|
|
||||||
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
|
|
||||||
ok = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ok) {
|
|
||||||
array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) {
|
if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) {
|
||||||
error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together");
|
error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together");
|
||||||
@@ -1241,7 +1246,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
|
|||||||
check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
|
check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
|
||||||
}
|
}
|
||||||
|
|
||||||
gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) {
|
gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) {
|
||||||
GB_ASSERT(pg_entity->kind == Entity_ProcGroup);
|
GB_ASSERT(pg_entity->kind == Entity_ProcGroup);
|
||||||
auto *pge = &pg_entity->ProcGroup;
|
auto *pge = &pg_entity->ProcGroup;
|
||||||
String proc_group_name = pg_entity->token.string;
|
String proc_group_name = pg_entity->token.string;
|
||||||
@@ -1366,6 +1371,11 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AttributeContext ac = {};
|
||||||
|
check_decl_attributes(ctx, d->attributes, proc_group_attribute, &ac);
|
||||||
|
check_objc_methods(ctx, pg_entity, ac);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_type) {
|
gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_type) {
|
||||||
|
|||||||
+121
-75
@@ -6136,7 +6136,6 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
|||||||
{
|
{
|
||||||
// NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups
|
// NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups
|
||||||
// where the same positional parameter has the same type value (and ellipsis)
|
// where the same positional parameter has the same type value (and ellipsis)
|
||||||
bool proc_arg_count_all_equal = true;
|
|
||||||
isize proc_arg_count = -1;
|
isize proc_arg_count = -1;
|
||||||
for (Entity *p : procs) {
|
for (Entity *p : procs) {
|
||||||
Type *pt = base_type(p->type);
|
Type *pt = base_type(p->type);
|
||||||
@@ -6144,15 +6143,12 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
|||||||
if (proc_arg_count < 0) {
|
if (proc_arg_count < 0) {
|
||||||
proc_arg_count = pt->Proc.param_count;
|
proc_arg_count = pt->Proc.param_count;
|
||||||
} else {
|
} else {
|
||||||
if (proc_arg_count != pt->Proc.param_count) {
|
proc_arg_count = gb_min(proc_arg_count, pt->Proc.param_count);
|
||||||
proc_arg_count_all_equal = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proc_arg_count >= 0 && proc_arg_count_all_equal) {
|
if (proc_arg_count >= 0) {
|
||||||
lhs_count = proc_arg_count;
|
lhs_count = proc_arg_count;
|
||||||
if (lhs_count > 0) {
|
if (lhs_count > 0) {
|
||||||
lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count);
|
lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count);
|
||||||
@@ -6258,14 +6254,18 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
|||||||
}
|
}
|
||||||
isize index = i;
|
isize index = i;
|
||||||
|
|
||||||
|
ValidIndexAndScore item = {};
|
||||||
|
item.score = data.score;
|
||||||
|
|
||||||
if (data.gen_entity != nullptr) {
|
if (data.gen_entity != nullptr) {
|
||||||
array_add(&proc_entities, data.gen_entity);
|
array_add(&proc_entities, data.gen_entity);
|
||||||
index = proc_entities.count-1;
|
index = proc_entities.count-1;
|
||||||
|
|
||||||
|
// prefer non-polymorphic procedures over polymorphic
|
||||||
|
item.score += assign_score_function(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidIndexAndScore item = {};
|
|
||||||
item.index = index;
|
item.index = index;
|
||||||
item.score = data.score;
|
|
||||||
array_add(&valids, item);
|
array_add(&valids, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6328,9 +6328,44 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
|||||||
print_argument_types();
|
print_argument_types();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (procs.count == 0) {
|
||||||
|
procs = proc_group_entities_cloned(c, *operand);
|
||||||
|
}
|
||||||
if (procs.count > 0) {
|
if (procs.count > 0) {
|
||||||
error_line("Did you mean to use one of the following:\n");
|
error_line("Did you mean to use one of the following:\n");
|
||||||
}
|
}
|
||||||
|
isize max_name_length = 0;
|
||||||
|
isize max_type_length = 0;
|
||||||
|
for (Entity *proc : procs) {
|
||||||
|
Type *t = base_type(proc->type);
|
||||||
|
if (t == t_invalid) continue;
|
||||||
|
String prefix = {};
|
||||||
|
String prefix_sep = {};
|
||||||
|
if (proc->pkg) {
|
||||||
|
prefix = proc->pkg->name;
|
||||||
|
prefix_sep = str_lit(".");
|
||||||
|
}
|
||||||
|
String name = proc->token.string;
|
||||||
|
max_name_length = gb_max(max_name_length, prefix.len + prefix_sep.len + name.len);
|
||||||
|
|
||||||
|
gbString pt;
|
||||||
|
if (t->Proc.node != nullptr) {
|
||||||
|
pt = expr_to_string(t->Proc.node);
|
||||||
|
} else {
|
||||||
|
pt = type_to_string(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
max_type_length = gb_max(max_type_length, gb_string_length(pt));
|
||||||
|
gb_string_free(pt);
|
||||||
|
}
|
||||||
|
|
||||||
|
isize max_spaces = gb_max(max_name_length, max_type_length);
|
||||||
|
char *spaces = gb_alloc_array(temporary_allocator(), char, max_spaces+1);
|
||||||
|
for (isize i = 0; i < max_spaces; i++) {
|
||||||
|
spaces[i] = ' ';
|
||||||
|
}
|
||||||
|
spaces[max_spaces] = 0;
|
||||||
|
|
||||||
for (Entity *proc : procs) {
|
for (Entity *proc : procs) {
|
||||||
TokenPos pos = proc->token.pos;
|
TokenPos pos = proc->token.pos;
|
||||||
Type *t = base_type(proc->type);
|
Type *t = base_type(proc->type);
|
||||||
@@ -6350,12 +6385,23 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
|||||||
prefix_sep = str_lit(".");
|
prefix_sep = str_lit(".");
|
||||||
}
|
}
|
||||||
String name = proc->token.string;
|
String name = proc->token.string;
|
||||||
|
isize len = prefix.len + prefix_sep.len + name.len;
|
||||||
|
|
||||||
|
int name_padding = cast(int)gb_max(max_name_length - len, 0);
|
||||||
|
int type_padding = cast(int)gb_max(max_type_length - gb_string_length(pt), 0);
|
||||||
|
|
||||||
char const *sep = "::";
|
char const *sep = "::";
|
||||||
if (proc->kind == Entity_Variable) {
|
if (proc->kind == Entity_Variable) {
|
||||||
sep = ":=";
|
sep = ":=";
|
||||||
}
|
}
|
||||||
error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos));
|
error_line("\t%.*s%.*s%.*s %.*s%s %s %.*sat %s\n",
|
||||||
|
LIT(prefix), LIT(prefix_sep), LIT(name),
|
||||||
|
name_padding, spaces,
|
||||||
|
sep,
|
||||||
|
pt,
|
||||||
|
type_padding, spaces,
|
||||||
|
token_pos_to_string(pos)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (procs.count > 0) {
|
if (procs.count > 0) {
|
||||||
error_line("\n");
|
error_line("\n");
|
||||||
@@ -6369,8 +6415,8 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
|||||||
error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name);
|
error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name);
|
||||||
print_argument_types();
|
print_argument_types();
|
||||||
|
|
||||||
for (isize i = 0; i < valids.count; i++) {
|
for (auto const &valid : valids) {
|
||||||
Entity *proc = proc_entities[valids[i].index];
|
Entity *proc = proc_entities[valid.index];
|
||||||
GB_ASSERT(proc != nullptr);
|
GB_ASSERT(proc != nullptr);
|
||||||
TokenPos pos = proc->token.pos;
|
TokenPos pos = proc->token.pos;
|
||||||
Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc);
|
Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc);
|
||||||
@@ -9315,13 +9361,13 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
|
|||||||
ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
|
ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
|
||||||
c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
|
c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
|
||||||
|
|
||||||
if (x.mode == Addressing_Invalid || x.type == t_invalid) {
|
if (x.mode == Addressing_Invalid || (x.type == t_invalid && x.mode != Addressing_ProcGroup)) {
|
||||||
o->mode = Addressing_Invalid;
|
o->mode = Addressing_Invalid;
|
||||||
o->type = t_invalid;
|
o->type = t_invalid;
|
||||||
o->expr = node;
|
o->expr = node;
|
||||||
return kind;
|
return kind;
|
||||||
}
|
}
|
||||||
if (!is_type_proc(x.type)) {
|
if (!is_type_proc(x.type) && x.mode != Addressing_ProcGroup) {
|
||||||
gbString type_str = type_to_string(x.type);
|
gbString type_str = type_to_string(x.type);
|
||||||
error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
|
error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
|
||||||
gb_string_free(type_str);
|
gb_string_free(type_str);
|
||||||
@@ -9344,76 +9390,76 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
|
|||||||
first_arg->state_flags |= StateFlag_SelectorCallExpr;
|
first_arg->state_flags |= StateFlag_SelectorCallExpr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Type *pt = base_type(x.type);
|
if (e->kind != Entity_ProcGroup) {
|
||||||
GB_ASSERT(pt->kind == Type_Proc);
|
Type *pt = base_type(x.type);
|
||||||
Type *first_type = nullptr;
|
GB_ASSERT_MSG(pt->kind == Type_Proc, "%.*s %.*s %s", LIT(e->token.string), LIT(entity_strings[e->kind]), type_to_string(x.type));
|
||||||
String first_arg_name = {};
|
Type *first_type = nullptr;
|
||||||
if (pt->Proc.param_count > 0) {
|
String first_arg_name = {};
|
||||||
Entity *f = pt->Proc.params->Tuple.variables[0];
|
if (pt->Proc.param_count > 0) {
|
||||||
first_type = f->type;
|
Entity *f = pt->Proc.params->Tuple.variables[0];
|
||||||
first_arg_name = f->token.string;
|
first_type = f->type;
|
||||||
}
|
first_arg_name = f->token.string;
|
||||||
if (first_arg_name.len == 0) {
|
}
|
||||||
first_arg_name = str_lit("_");
|
if (first_arg_name.len == 0) {
|
||||||
}
|
first_arg_name = str_lit("_");
|
||||||
|
}
|
||||||
|
|
||||||
if (first_type == nullptr) {
|
if (first_type == nullptr) {
|
||||||
error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
|
error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
|
||||||
o->mode = Addressing_Invalid;
|
o->mode = Addressing_Invalid;
|
||||||
o->type = t_invalid;
|
o->type = t_invalid;
|
||||||
o->expr = node;
|
o->expr = node;
|
||||||
return Expr_Stmt;
|
return Expr_Stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
Operand y = {};
|
Operand y = {};
|
||||||
y.mode = first_arg->tav.mode;
|
y.mode = first_arg->tav.mode;
|
||||||
y.type = first_arg->tav.type;
|
y.type = first_arg->tav.type;
|
||||||
y.value = first_arg->tav.value;
|
y.value = first_arg->tav.value;
|
||||||
|
|
||||||
if (check_is_assignable_to(c, &y, first_type)) {
|
if (check_is_assignable_to(c, &y, first_type)) {
|
||||||
// Do nothing, it's valid
|
// Do nothing, it's valid
|
||||||
} else {
|
} else {
|
||||||
Operand z = y;
|
Operand z = y;
|
||||||
z.type = type_deref(y.type);
|
z.type = type_deref(y.type);
|
||||||
if (check_is_assignable_to(c, &z, first_type)) {
|
if (check_is_assignable_to(c, &z, first_type)) {
|
||||||
// NOTE(bill): AST GENERATION HACK!
|
|
||||||
Token op = {Token_Pointer};
|
|
||||||
first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
|
|
||||||
} else if (y.mode == Addressing_Variable) {
|
|
||||||
Operand w = y;
|
|
||||||
w.type = alloc_type_pointer(y.type);
|
|
||||||
if (check_is_assignable_to(c, &w, first_type)) {
|
|
||||||
// NOTE(bill): AST GENERATION HACK!
|
// NOTE(bill): AST GENERATION HACK!
|
||||||
Token op = {Token_And};
|
Token op = {Token_Pointer};
|
||||||
first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
|
first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
|
||||||
|
} else if (y.mode == Addressing_Variable) {
|
||||||
|
Operand w = y;
|
||||||
|
w.type = alloc_type_pointer(y.type);
|
||||||
|
if (check_is_assignable_to(c, &w, first_type)) {
|
||||||
|
// NOTE(bill): AST GENERATION HACK!
|
||||||
|
Token op = {Token_And};
|
||||||
|
first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ce->args.count > 0) {
|
||||||
|
bool fail = false;
|
||||||
|
bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
|
||||||
|
for (Ast *arg : ce->args) {
|
||||||
|
bool mix = false;
|
||||||
|
if (first_is_field_value) {
|
||||||
|
mix = arg->kind != Ast_FieldValue;
|
||||||
|
} else {
|
||||||
|
mix = arg->kind == Ast_FieldValue;
|
||||||
|
}
|
||||||
|
if (mix) {
|
||||||
|
fail = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!fail && first_is_field_value) {
|
||||||
|
Token op = {Token_Eq};
|
||||||
|
AstFile *f = first_arg->file();
|
||||||
|
first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ce->args.count > 0) {
|
|
||||||
bool fail = false;
|
|
||||||
bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
|
|
||||||
for (Ast *arg : ce->args) {
|
|
||||||
bool mix = false;
|
|
||||||
if (first_is_field_value) {
|
|
||||||
mix = arg->kind != Ast_FieldValue;
|
|
||||||
} else {
|
|
||||||
mix = arg->kind == Ast_FieldValue;
|
|
||||||
}
|
|
||||||
if (mix) {
|
|
||||||
fail = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!fail && first_is_field_value) {
|
|
||||||
Token op = {Token_Eq};
|
|
||||||
AstFile *f = first_arg->file();
|
|
||||||
first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
|
auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
|
||||||
modified_args[0] = first_arg;
|
modified_args[0] = first_arg;
|
||||||
slice_copy(&modified_args, ce->args, 1);
|
slice_copy(&modified_args, ce->args, 1);
|
||||||
|
|||||||
@@ -2935,6 +2935,54 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gb_internal DECL_ATTRIBUTE_PROC(proc_group_attribute) {
|
||||||
|
if (name == ATTRIBUTE_USER_TAG_NAME) {
|
||||||
|
ExactValue ev = check_decl_attribute_value(c, value);
|
||||||
|
if (ev.kind != ExactValue_String) {
|
||||||
|
error(elem, "Expected a string value for '%.*s'", LIT(name));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (name == "objc_name") {
|
||||||
|
ExactValue ev = check_decl_attribute_value(c, value);
|
||||||
|
if (ev.kind == ExactValue_String) {
|
||||||
|
if (string_is_valid_identifier(ev.value_string)) {
|
||||||
|
ac->objc_name = ev.value_string;
|
||||||
|
} else {
|
||||||
|
error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error(elem, "Expected a string value for '%.*s'", LIT(name));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (name == "objc_is_class_method") {
|
||||||
|
ExactValue ev = check_decl_attribute_value(c, value);
|
||||||
|
if (ev.kind == ExactValue_Bool) {
|
||||||
|
ac->objc_is_class_method = ev.value_bool;
|
||||||
|
} else {
|
||||||
|
error(elem, "Expected a boolean value for '%.*s'", LIT(name));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (name == "objc_type") {
|
||||||
|
if (value == nullptr) {
|
||||||
|
error(elem, "Expected a type for '%.*s'", LIT(name));
|
||||||
|
} else {
|
||||||
|
Type *objc_type = check_type(c, value);
|
||||||
|
if (objc_type != nullptr) {
|
||||||
|
if (!has_type_got_objc_class_attribute(objc_type)) {
|
||||||
|
gbString t = type_to_string(objc_type);
|
||||||
|
error(value, "'%.*s' expected a named type with the attribute @(obj_class=<string>), got type %s", LIT(name), t);
|
||||||
|
gb_string_free(t);
|
||||||
|
} else {
|
||||||
|
ac->objc_type = objc_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
||||||
if (name == ATTRIBUTE_USER_TAG_NAME) {
|
if (name == ATTRIBUTE_USER_TAG_NAME) {
|
||||||
ExactValue ev = check_decl_attribute_value(c, value);
|
ExactValue ev = check_decl_attribute_value(c, value);
|
||||||
|
|||||||
+15
-12
@@ -1825,25 +1825,28 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
|
|||||||
TEMPORARY_ALLOCATOR_GUARD();
|
TEMPORARY_ALLOCATOR_GUARD();
|
||||||
auto args = array_make<lbValue>(temporary_allocator(), 1);
|
auto args = array_make<lbValue>(temporary_allocator(), 1);
|
||||||
args[0] = lb_addr_load(p, all_tests_slice);
|
args[0] = lb_addr_load(p, all_tests_slice);
|
||||||
lb_emit_call(p, runner, args);
|
lbValue result = lb_emit_call(p, runner, args);
|
||||||
|
|
||||||
|
lbValue exit_runner = lb_find_package_value(m, str_lit("os"), str_lit("exit"));
|
||||||
|
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);
|
||||||
} else {
|
} else {
|
||||||
if (m->info->entry_point != nullptr) {
|
if (m->info->entry_point != nullptr) {
|
||||||
lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point);
|
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);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
if (call_cleanup) {
|
||||||
|
lbValue cleanup_runtime_value = {cleanup_runtime->value, cleanup_runtime->type};
|
||||||
|
lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none);
|
||||||
|
}
|
||||||
|
|
||||||
if (call_cleanup) {
|
if (is_dll_main) {
|
||||||
lbValue cleanup_runtime_value = {cleanup_runtime->value, cleanup_runtime->type};
|
LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 1, false));
|
||||||
lb_emit_call(p, cleanup_runtime_value, {}, ProcInlining_none);
|
} else {
|
||||||
}
|
LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false));
|
||||||
|
}
|
||||||
|
|
||||||
if (is_dll_main) {
|
|
||||||
LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 1, false));
|
|
||||||
} else {
|
|
||||||
LLVMBuildRet(p->builder, LLVMConstInt(lb_type(m, t_i32), 0, false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lb_end_procedure_body(p);
|
lb_end_procedure_body(p);
|
||||||
|
|||||||
@@ -4171,7 +4171,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
|
|||||||
|
|
||||||
// HACK TODO(bill): THIS IS A MASSIVE HACK!!!!
|
// HACK TODO(bill): THIS IS A MASSIVE HACK!!!!
|
||||||
if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) {
|
if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) {
|
||||||
GB_ASSERT_MSG(union_variant_index(ft, fet) > 0, "%s", type_to_string(fet));
|
GB_ASSERT_MSG(union_variant_index(ft, fet) >= 0, "%s", type_to_string(fet));
|
||||||
|
|
||||||
lb_emit_store_union_variant(p, gep, field_expr, fet);
|
lb_emit_store_union_variant(p, gep, field_expr, fet);
|
||||||
} else {
|
} else {
|
||||||
@@ -4519,8 +4519,9 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
|
|||||||
Selection sel = lookup_field(type, selector, false);
|
Selection sel = lookup_field(type, selector, false);
|
||||||
GB_ASSERT(sel.entity != nullptr);
|
GB_ASSERT(sel.entity != nullptr);
|
||||||
if (sel.pseudo_field) {
|
if (sel.pseudo_field) {
|
||||||
GB_ASSERT(sel.entity->kind == Entity_Procedure);
|
GB_ASSERT(sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup);
|
||||||
Entity *e = entity_of_node(sel_node);
|
Entity *e = entity_of_node(sel_node);
|
||||||
|
GB_ASSERT(e->kind == Entity_Procedure);
|
||||||
return lb_addr(lb_find_value_from_entity(p->module, e));
|
return lb_addr(lb_find_value_from_entity(p->module, e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -3081,7 +3081,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
|
|||||||
mutex_lock(md->mutex);
|
mutex_lock(md->mutex);
|
||||||
defer (mutex_unlock(md->mutex));
|
defer (mutex_unlock(md->mutex));
|
||||||
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
|
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
|
||||||
GB_ASSERT(entry.entity->kind == Entity_Procedure);
|
GB_ASSERT(entry.entity->kind == Entity_Procedure || entry.entity->kind == Entity_ProcGroup);
|
||||||
if (entry.name == field_name) {
|
if (entry.name == field_name) {
|
||||||
sel.entity = entry.entity;
|
sel.entity = entry.entity;
|
||||||
sel.pseudo_field = true;
|
sel.pseudo_field = true;
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ set COMMON=-collection:tests=..\..
|
|||||||
..\..\..\odin build ..\test_issue_2113.odin %COMMON% -file -debug || exit /b
|
..\..\..\odin build ..\test_issue_2113.odin %COMMON% -file -debug || exit /b
|
||||||
..\..\..\odin test ..\test_issue_2466.odin %COMMON% -file || exit /b
|
..\..\..\odin test ..\test_issue_2466.odin %COMMON% -file || exit /b
|
||||||
..\..\..\odin test ..\test_issue_2615.odin %COMMON% -file || exit /b
|
..\..\..\odin test ..\test_issue_2615.odin %COMMON% -file || exit /b
|
||||||
|
..\..\..\odin test ..\test_issue_2637.odin %COMMON% -file || exit /b
|
||||||
|
|
||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ $ODIN test ../test_issue_2087.odin $COMMON -file
|
|||||||
$ODIN build ../test_issue_2113.odin $COMMON -file -debug
|
$ODIN build ../test_issue_2113.odin $COMMON -file -debug
|
||||||
$ODIN test ../test_issue_2466.odin $COMMON -file
|
$ODIN test ../test_issue_2466.odin $COMMON -file
|
||||||
$ODIN test ../test_issue_2615.odin $COMMON -file
|
$ODIN test ../test_issue_2615.odin $COMMON -file
|
||||||
|
$ODIN test ../test_issue_2637.odin $COMMON -file
|
||||||
if [[ $($ODIN build ../test_issue_2395.odin $COMMON -file 2>&1 >/dev/null | grep -c "$NO_NIL_ERR") -eq 2 ]] ; then
|
if [[ $($ODIN build ../test_issue_2395.odin $COMMON -file 2>&1 >/dev/null | grep -c "$NO_NIL_ERR") -eq 2 ]] ; then
|
||||||
echo "SUCCESSFUL 1/1"
|
echo "SUCCESSFUL 1/1"
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// Tests issue #2637 https://github.com/odin-lang/Odin/issues/2637
|
||||||
|
package test_issues
|
||||||
|
|
||||||
|
import "core:testing"
|
||||||
|
|
||||||
|
Foo :: Maybe(string)
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
test_expect_value_succeeds_with_nil :: proc(t: ^testing.T) {
|
||||||
|
x: Foo
|
||||||
|
testing.expect(t, x == nil) // Succeeds
|
||||||
|
testing.expect_value(t, x, nil) // Fails, "expected nil, got nil"
|
||||||
|
}
|
||||||
Vendored
+1
-1
@@ -50,7 +50,7 @@ Player :: struct {
|
|||||||
player_num: c.int,
|
player_num: c.int,
|
||||||
using u: struct #raw_union {
|
using u: struct #raw_union {
|
||||||
local: struct {},
|
local: struct {},
|
||||||
remove: struct {
|
remote: struct {
|
||||||
ip_address: [32]byte,
|
ip_address: [32]byte,
|
||||||
port: u16,
|
port: u16,
|
||||||
},
|
},
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1133,7 +1133,7 @@ foreign lib {
|
|||||||
|
|
||||||
SetGesturesEnabled :: proc(flags: Gestures) --- // Enable a set of gestures using flags
|
SetGesturesEnabled :: proc(flags: Gestures) --- // Enable a set of gestures using flags
|
||||||
IsGestureDetected :: proc(gesture: Gesture) -> bool --- // Check if a gesture have been detected
|
IsGestureDetected :: proc(gesture: Gesture) -> bool --- // Check if a gesture have been detected
|
||||||
GetGestureDetected :: proc() -> Gesture --- // Get latest detected gesture
|
GetGestureDetected :: proc() -> Gestures --- // Get latest detected gesture
|
||||||
GetGestureHoldDuration :: proc() -> f32 --- // Get gesture hold time in milliseconds
|
GetGestureHoldDuration :: proc() -> f32 --- // Get gesture hold time in milliseconds
|
||||||
GetGestureDragVector :: proc() -> Vector2 --- // Get gesture drag vector
|
GetGestureDragVector :: proc() -> Vector2 --- // Get gesture drag vector
|
||||||
GetGestureDragAngle :: proc() -> f32 --- // Get gesture drag angle
|
GetGestureDragAngle :: proc() -> f32 --- // Get gesture drag angle
|
||||||
|
|||||||
Reference in New Issue
Block a user