Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 35 additions & 16 deletions src/check_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2859,6 +2859,26 @@ gb_internal void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
}
}

gb_internal void validate_map_key(CheckerContext *ctx, Ast *node, Type *key) {
if (!is_type_valid_for_keys(key)) {
if (is_type_boolean(key)) {
error(node, "A boolean cannot be used as a key for a map, use an array instead for this case");
} else {
gbString str = type_to_string(key);
error(node, "Invalid type of a key for a map, got '%s'", str);
gb_string_free(str);
}
return;
}
if (type_size_of(key) == 0) {
gbString str = type_to_string(key);
error(node, "Invalid type of a key for a map of size 0, got '%s'", str);
gb_string_free(str);
return;
}
add_map_key_type_dependencies(ctx, key);
}

gb_internal void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
GB_ASSERT(type->kind == Type_Map);
ast_node(mt, MapType, node);
Expand All @@ -2878,30 +2898,29 @@ gb_internal void check_map_type(CheckerContext *ctx, Type *type, Ast *node) {
Type *key = check_type(ctx, mt->key);
Type *value = check_type(ctx, mt->value);

if (!is_type_valid_for_keys(key)) {
if (is_type_boolean(key)) {
error(node, "A boolean cannot be used as a key for a map, use an array instead for this case");
} else {
gbString str = type_to_string(key);
error(node, "Invalid type of a key for a map, got '%s'", str);
gb_string_free(str);
}
}
if (type_size_of(key) == 0) {
gbString str = type_to_string(key);
error(node, "Invalid type of a key for a map of size 0, got '%s'", str);
gb_string_free(str);
}

type->Map.key = key;
type->Map.value = value;

add_map_key_type_dependencies(ctx, key);
// If the key type is still being resolved its struct fields may be empty causing false invalid key errors.
// Defer to check_deferred_map_key_types which runs after all global entities are resolved.
if (key->kind == Type_Named && key->Named.type_name && key->Named.type_name->state.load() == EntityState_InProgress) {
array_add(&ctx->checker->deferred_map_key_checks, node);
} else {
validate_map_key(ctx, node, key);
}

init_core_map_type(ctx->checker);
init_map_internal_types(type);
}

gb_internal void check_deferred_map_key_types(Checker *c) {
CheckerContext ctx = c->builtin_ctx;
ctx.decl = make_decl_info(nullptr, nullptr);
for (Ast *node : c->deferred_map_key_checks) {
validate_map_key(&ctx, node, node->tav.type->Map.key);
}
}

gb_internal void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) {
ast_node(mt, MatrixType, node);

Expand Down
4 changes: 4 additions & 0 deletions src/checker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1603,6 +1603,7 @@ gb_internal void init_checker(Checker *c) {
// NOTE(bill): 1 Mi elements should be enough on average
array_init(&c->procs_to_check, heap_allocator(), 0, 1<<20);
array_init(&c->nested_proc_lits, heap_allocator(), 0, 1<<20);
array_init(&c->deferred_map_key_checks, heap_allocator());

mpsc_init(&c->global_untyped_queue, a); // , 1<<20);
mpsc_init(&c->soa_types_to_complete, a); // , 1<<20);
Expand All @@ -1617,6 +1618,7 @@ gb_internal void destroy_checker(Checker *c) {

array_free(&c->nested_proc_lits);
array_free(&c->procs_to_check);
array_free(&c->deferred_map_key_checks);
mpsc_destroy(&c->global_untyped_queue);
mpsc_destroy(&c->soa_types_to_complete);
}
Expand Down Expand Up @@ -5051,6 +5053,8 @@ gb_internal void check_all_global_entities(Checker *c) {
}
}

check_deferred_map_key_types(c);

in_single_threaded_checker_stage.store(false, std::memory_order_relaxed);
}

Expand Down
1 change: 1 addition & 0 deletions src/checker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,7 @@ struct Checker {
BlockingMutex nested_proc_lits_mutex;
Array<DeclInfo *> nested_proc_lits;

Array<Ast *> deferred_map_key_checks;

MPSCQueue<UntypedExprInfo> global_untyped_queue;
MPSCQueue<Type *> soa_types_to_complete;
Expand Down
1 change: 1 addition & 0 deletions tests/issues/run.bat
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused
..\..\..\odin test ..\test_issue_6068.odin %COMMON% || exit /b
..\..\..\odin test ..\test_issue_6101.odin %COMMON% || exit /b
..\..\..\odin test ..\test_issue_6165.odin %COMMON% || exit /b
..\..\..\odin test ..\test_issue_6348.odin %COMMON% || exit /b

@echo off

Expand Down
1 change: 1 addition & 0 deletions tests/issues/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ $ODIN test ../test_issue_5699.odin $COMMON
$ODIN test ../test_issue_6068.odin $COMMON
$ODIN test ../test_issue_6101.odin $COMMON
$ODIN test ../test_issue_6165.odin $COMMON
$ODIN test ../test_issue_6348.odin $COMMON
set +x

popd
Expand Down
26 changes: 26 additions & 0 deletions tests/issues/test_issue_6348.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Tests issue #6348 https://github.com/odin-lang/Odin/issues/6348
package test_issues

import "core:testing"

// Key type declared before map type
Foo1 :: struct { f: proc(^Bar1) }
Bar1 :: struct { m: map[Foo1]int }

// Map type declared before key type
Bar2 :: struct { m: map[Foo2]int }
Foo2 :: struct { f: proc(^Bar2) }

// Named proc type alias
MyProc :: proc(^Bar3)
Foo3 :: struct { f: MyProc }
Bar3 :: struct { m: map[Foo3]int }

// Chain
Foo4 :: struct { f: proc(^Baz4) }
Baz4 :: struct { g: proc(^Bar4) }
Bar4 :: struct { m: map[Foo4]int }


@(test)
test_issue_6348 :: proc(t: ^testing.T) {}