From 9b78061c8fb83202973d509b9af24f0a12786958 Mon Sep 17 00:00:00 2001 From: Dudejoe870 Date: Tue, 28 May 2024 22:25:16 -0500 Subject: Initial hash directive implementation --- src/check_builtin.cpp | 167 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 118 insertions(+), 49 deletions(-) (limited to 'src/check_builtin.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index d85e94db3..aa3be0bbd 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1403,6 +1403,65 @@ gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c return result; } +gb_internal bool check_hash_kind(CheckerContext *c, Ast *call, String const &hash_kind, u8 const *data, isize data_size, u64 *hash_value) { + ast_node(ce, CallExpr, call); + ast_node(bd, BasicDirective, ce->proc); + String name = bd->name.string; + GB_ASSERT(name == "load_hash" || name == "hash"); + + String supported_hashes[] = { + str_lit("adler32"), + str_lit("crc32"), + str_lit("crc64"), + str_lit("fnv32"), + str_lit("fnv64"), + str_lit("fnv32a"), + str_lit("fnv64a"), + str_lit("murmur32"), + str_lit("murmur64"), + }; + + bool hash_found = false; + for (isize i = 0; i < gb_count_of(supported_hashes); i++) { + if (supported_hashes[i] == hash_kind) { + hash_found = true; + break; + } + } + if (!hash_found) { + ERROR_BLOCK(); + error(ce->proc, "Invalid hash kind passed to `#%.*s`, got: %.*s", LIT(name), LIT(hash_kind)); + error_line("\tAvailable hash kinds:\n"); + for (isize i = 0; i < gb_count_of(supported_hashes); i++) { + error_line("\t%.*s\n", LIT(supported_hashes[i])); + } + return false; + } + + if (hash_kind == "adler32") { + *hash_value = gb_adler32(data, data_size); + } else if (hash_kind == "crc32") { + *hash_value = gb_crc32(data, data_size); + } else if (hash_kind == "crc64") { + *hash_value = gb_crc64(data, data_size); + } else if (hash_kind == "fnv32") { + *hash_value = gb_fnv32(data, data_size); + } else if (hash_kind == "fnv64") { + *hash_value = gb_fnv64(data, data_size); + } else if (hash_kind == "fnv32a") { + *hash_value = fnv32a(data, data_size); + } else if (hash_kind == "fnv64a") { + *hash_value = fnv64a(data, data_size); + } else if (hash_kind == "murmur32") { + *hash_value = gb_murmur32(data, data_size); + } else if (hash_kind == "murmur64") { + *hash_value = gb_murmur64(data, data_size); + } else { + compiler_error("unhandled hash kind: %.*s", LIT(hash_kind)); + } + return true; +} + gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) { @@ -1480,35 +1539,6 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o String original_string = o.value.value_string; String hash_kind = o_hash.value.value_string; - String supported_hashes[] = { - str_lit("adler32"), - str_lit("crc32"), - str_lit("crc64"), - str_lit("fnv32"), - str_lit("fnv64"), - str_lit("fnv32a"), - str_lit("fnv64a"), - str_lit("murmur32"), - str_lit("murmur64"), - }; - - bool hash_found = false; - for (isize i = 0; i < gb_count_of(supported_hashes); i++) { - if (supported_hashes[i] == hash_kind) { - hash_found = true; - break; - } - } - if (!hash_found) { - ERROR_BLOCK(); - error(ce->proc, "Invalid hash kind passed to `#load_hash`, got: %.*s", LIT(hash_kind)); - error_line("\tAvailable hash kinds:\n"); - for (isize i = 0; i < gb_count_of(supported_hashes); i++) { - error_line("\t%.*s\n", LIT(supported_hashes[i])); - } - return false; - } - LoadFileCache *cache = nullptr; if (cache_load_file_directive(c, call, original_string, true, &cache)) { MUTEX_GUARD(&c->info->load_file_mutex); @@ -1520,26 +1550,9 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o } else { u8 *data = cache->data.text; isize file_size = cache->data.len; - if (hash_kind == "adler32") { - hash_value = gb_adler32(data, file_size); - } else if (hash_kind == "crc32") { - hash_value = gb_crc32(data, file_size); - } else if (hash_kind == "crc64") { - hash_value = gb_crc64(data, file_size); - } else if (hash_kind == "fnv32") { - hash_value = gb_fnv32(data, file_size); - } else if (hash_kind == "fnv64") { - hash_value = gb_fnv64(data, file_size); - } else if (hash_kind == "fnv32a") { - hash_value = fnv32a(data, file_size); - } else if (hash_kind == "fnv64a") { - hash_value = fnv64a(data, file_size); - } else if (hash_kind == "murmur32") { - hash_value = gb_murmur32(data, file_size); - } else if (hash_kind == "murmur64") { - hash_value = gb_murmur64(data, file_size); - } else { - compiler_error("unhandled hash kind: %.*s", LIT(hash_kind)); + + if (!check_hash_kind(c, call, hash_kind, data, file_size, &hash_value)) { + return false; } string_map_set(&cache->hashes, hash_kind, hash_value); } @@ -1550,6 +1563,62 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o return true; } return false; + } else if (name == "hash") { + if (ce->args.count != 2) { + if (ce->args.count == 0) { + error(ce->close, "'#hash' expects 2 argument, got 0"); + } else { + error(ce->args[0], "'#hash' expects 2 argument, got %td", ce->args.count); + } + return false; + } + + Ast *arg0 = ce->args[0]; + Ast *arg1 = ce->args[1]; + Operand o = {}; + check_expr(c, &o, arg0); + if (o.mode != Addressing_Constant) { + error(arg0, "'#hash' expected a constant string argument"); + return false; + } + + if (!is_type_string(o.type)) { + gbString str = type_to_string(o.type); + error(arg0, "'#hash' expected a constant string, got %s", str); + gb_string_free(str); + return false; + } + + Operand o_hash = {}; + check_expr(c, &o_hash, arg1); + if (o_hash.mode != Addressing_Constant) { + error(arg1, "'#hash' expected a constant string argument"); + return false; + } + + if (!is_type_string(o_hash.type)) { + gbString str = type_to_string(o.type); + error(arg1, "'#hash' expected a constant string, got %s", str); + gb_string_free(str); + return false; + } + gbAllocator a = heap_allocator(); + + GB_ASSERT(o.value.kind == ExactValue_String); + GB_ASSERT(o_hash.value.kind == ExactValue_String); + + String original_string = o.value.value_string; + String hash_kind = o_hash.value.value_string; + + // TODO: Cache hash values based off of string constant and hash kind? + u64 hash_value = 0; + if (check_hash_kind(c, call, hash_kind, original_string.text, original_string.len, &hash_value)) { + operand->type = t_untyped_integer; + operand->mode = Addressing_Constant; + operand->value = exact_value_u64(hash_value); + return true; + } + return false; } else if (name == "assert") { if (ce->args.count != 1 && ce->args.count != 2) { error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count); -- cgit v1.2.3 From 4f5b2bd12736b32c0a35496e8ca8f03d305c54b4 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 4 Jun 2024 19:01:50 +0200 Subject: fix crash when you have 2 `#load_directory` calls with the same path --- src/check_builtin.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/check_builtin.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index aa3be0bbd..15c63905e 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1345,6 +1345,8 @@ gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c map_set(&c->info->load_directory_map, call, new_cache); } else { cache->file_error = file_error; + + map_set(&c->info->load_directory_map, call, cache); } }); -- cgit v1.2.3 From b47a15733df5d0cb858a2be0d4eb334df3d50536 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Tue, 4 Jun 2024 19:06:13 +0200 Subject: implement `#exists(path)` --- src/check_builtin.cpp | 94 ++++++++++++++++++++++++++++++++++++++------------- src/check_expr.cpp | 1 + src/checker.hpp | 9 +++++ 3 files changed, 80 insertions(+), 24 deletions(-) (limited to 'src/check_builtin.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 15c63905e..7e3bcb7ee 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1079,7 +1079,7 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan return false; } -gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String const &original_string, bool err_on_not_found, LoadFileCache **cache_) { +gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String const &original_string, bool err_on_not_found, LoadFileCache **cache_, LoadFileTier tier) { ast_node(ce, CallExpr, call); ast_node(bd, BasicDirective, ce->proc); String builtin_name = bd->name.string; @@ -1105,12 +1105,16 @@ gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String gbFileError file_error = gbFileError_None; String data = {}; + bool exists = false; + LoadFileTier cache_tier = LoadFileTier_Invalid; LoadFileCache **cache_ptr = string_map_get(&c->info->load_file_cache, path); LoadFileCache *cache = cache_ptr ? *cache_ptr : nullptr; if (cache) { file_error = cache->file_error; data = cache->data; + exists = cache->exists; + cache_tier = cache->tier; } defer ({ if (cache == nullptr) { @@ -1118,60 +1122,78 @@ gb_internal bool cache_load_file_directive(CheckerContext *c, Ast *call, String new_cache->path = path; new_cache->data = data; new_cache->file_error = file_error; + new_cache->exists = exists; + new_cache->tier = cache_tier; string_map_init(&new_cache->hashes, 32); string_map_set(&c->info->load_file_cache, path, new_cache); if (cache_) *cache_ = new_cache; } else { cache->data = data; cache->file_error = file_error; + cache->exists = exists; + cache->tier = cache_tier; if (cache_) *cache_ = cache; } }); - TEMPORARY_ALLOCATOR_GUARD(); - char *c_str = alloc_cstring(temporary_allocator(), path); + if (tier > cache_tier) { + cache_tier = tier; - gbFile f = {}; - if (cache == nullptr) { + TEMPORARY_ALLOCATOR_GUARD(); + char *c_str = alloc_cstring(temporary_allocator(), path); + + gbFile f = {}; file_error = gb_file_open(&f, c_str); + defer (gb_file_close(&f)); + + if (file_error == gbFileError_None) { + exists = true; + + switch(tier) { + case LoadFileTier_Exists: + // Nothing to do. + break; + case LoadFileTier_Contents: { + isize file_size = cast(isize)gb_file_size(&f); + if (file_size > 0) { + u8 *ptr = cast(u8 *)gb_alloc(permanent_allocator(), file_size+1); + gb_file_read_at(&f, ptr, file_size, 0); + ptr[file_size] = '\0'; + data.text = ptr; + data.len = file_size; + } + break; + } + default: + GB_PANIC("Unhandled LoadFileTier"); + }; + } } - defer (gb_file_close(&f)); switch (file_error) { default: case gbFileError_Invalid: if (err_on_not_found) { - error(ce->proc, "Failed to `#%.*s` file: %s; invalid file or cannot be found", LIT(builtin_name), c_str); + error(ce->proc, "Failed to `#%.*s` file: %.*s; invalid file or cannot be found", LIT(builtin_name), LIT(path)); } call->state_flags |= StateFlag_DirectiveWasFalse; return false; case gbFileError_NotExists: if (err_on_not_found) { - error(ce->proc, "Failed to `#%.*s` file: %s; file cannot be found", LIT(builtin_name), c_str); + error(ce->proc, "Failed to `#%.*s` file: %.*s; file cannot be found", LIT(builtin_name), LIT(path)); } call->state_flags |= StateFlag_DirectiveWasFalse; return false; case gbFileError_Permission: if (err_on_not_found) { - error(ce->proc, "Failed to `#%.*s` file: %s; file permissions problem", LIT(builtin_name), c_str); + error(ce->proc, "Failed to `#%.*s` file: %.*s; file permissions problem", LIT(builtin_name), LIT(path)); } call->state_flags |= StateFlag_DirectiveWasFalse; return false; case gbFileError_None: // Okay break; - } - - if (cache == nullptr) { - isize file_size = cast(isize)gb_file_size(&f); - if (file_size > 0) { - u8 *ptr = cast(u8 *)gb_alloc(permanent_allocator(), file_size+1); - gb_file_read_at(&f, ptr, file_size, 0); - ptr[file_size] = '\0'; - data.text = ptr; - data.len = file_size; - } - } + }; return true; } @@ -1263,7 +1285,7 @@ gb_internal LoadDirectiveResult check_load_directive(CheckerContext *c, Operand operand->mode = Addressing_Constant; LoadFileCache *cache = nullptr; - if (cache_load_file_directive(c, call, o.value.value_string, err_on_not_found, &cache)) { + if (cache_load_file_directive(c, call, o.value.value_string, err_on_not_found, &cache, LoadFileTier_Contents)) { operand->value = exact_value_string(cache->data); return LoadDirective_Success; } @@ -1391,7 +1413,7 @@ gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c for (FileInfo fi : list) { LoadFileCache *cache = nullptr; - if (cache_load_file_directive(c, call, fi.fullpath, err_on_not_found, &cache)) { + if (cache_load_file_directive(c, call, fi.fullpath, err_on_not_found, &cache, LoadFileTier_Contents)) { array_add(&file_caches, cache); } else { result = LoadDirective_Error; @@ -1490,6 +1512,30 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o operand->type = t_source_code_location; operand->mode = Addressing_Value; + } else if (name == "exists") { + if (ce->args.count != 1) { + error(ce->close, "'#exists' expects 1 argument, got %td", ce->args.count); + return false; + } + + Operand o = {}; + check_expr(c, &o, ce->args[0]); + if (o.mode != Addressing_Constant || !is_type_string(o.type)) { + error(ce->args[0], "'#exists' expected a constant string argument"); + return false; + } + + operand->type = t_untyped_bool; + operand->mode = Addressing_Constant; + + String original_string = o.value.value_string; + LoadFileCache *cache = nullptr; + if (cache_load_file_directive(c, call, original_string, /* err_on_not_found=*/ false, &cache, LoadFileTier_Exists)) { + operand->value = exact_value_bool(cache->exists); + } else { + operand->value = exact_value_bool(false); + } + } else if (name == "load") { return check_load_directive(c, operand, call, type_hint, true) == LoadDirective_Success; } else if (name == "load_directory") { @@ -1542,7 +1588,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o String hash_kind = o_hash.value.value_string; LoadFileCache *cache = nullptr; - if (cache_load_file_directive(c, call, original_string, true, &cache)) { + if (cache_load_file_directive(c, call, original_string, true, &cache, LoadFileTier_Contents)) { MUTEX_GUARD(&c->info->load_file_mutex); // TODO(bill): make these procedures fast :P u64 hash_value = 0; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 2e008fe93..3bf2af24a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7408,6 +7408,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c String name = bd->name.string; if ( name == "location" || + name == "exists" || name == "assert" || name == "panic" || name == "defined" || diff --git a/src/checker.hpp b/src/checker.hpp index 539b72b2d..e793540e3 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -336,7 +336,16 @@ struct ObjcMsgData { ObjcMsgKind kind; Type *proc_type; }; + +enum LoadFileTier { + LoadFileTier_Invalid, + LoadFileTier_Exists, + LoadFileTier_Contents, +}; + struct LoadFileCache { + LoadFileTier tier; + bool exists; String path; gbFileError file_error; String data; -- cgit v1.2.3 From 7044a7d77650e922a66f3bfe99711f3ed370e1ba Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Jun 2024 23:55:48 +0100 Subject: Try to fix a possible race condition with polymorphic record parameters --- src/check_builtin.cpp | 31 ++++++++----------------------- src/check_expr.cpp | 11 +++-------- src/check_type.cpp | 30 +++++++++++++----------------- src/checker.cpp | 4 ++++ src/types.cpp | 18 ++++++++++++++++++ 5 files changed, 46 insertions(+), 48 deletions(-) (limited to 'src/check_builtin.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 7e3bcb7ee..eef925d94 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -5912,15 +5912,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (operand->mode != Addressing_Type) { error(operand->expr, "Expected a record type for '%.*s'", LIT(builtin_name)); } else { - Type *bt = base_type(operand->type); - if (bt->kind == Type_Struct) { - if (bt->Struct.polymorphic_params != nullptr) { - operand->value = exact_value_i64(bt->Struct.polymorphic_params->Tuple.variables.count); - } - } else if (bt->kind == Type_Union) { - if (bt->Union.polymorphic_params != nullptr) { - operand->value = exact_value_i64(bt->Union.polymorphic_params->Tuple.variables.count); - } + TypeTuple *tuple = get_record_polymorphic_params(operand->type); + if (tuple) { + operand->value = exact_value_i64(tuple->variables.count); } else { error(operand->expr, "Expected a record type for '%.*s'", LIT(builtin_name)); } @@ -5952,20 +5946,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As Entity *param = nullptr; i64 count = 0; - Type *bt = base_type(operand->type); - if (bt->kind == Type_Struct) { - if (bt->Struct.polymorphic_params != nullptr) { - count = bt->Struct.polymorphic_params->Tuple.variables.count; - if (index < count) { - param = bt->Struct.polymorphic_params->Tuple.variables[cast(isize)index]; - } - } - } else if (bt->kind == Type_Union) { - if (bt->Union.polymorphic_params != nullptr) { - count = bt->Union.polymorphic_params->Tuple.variables.count; - if (index < count) { - param = bt->Union.polymorphic_params->Tuple.variables[cast(isize)index]; - } + TypeTuple *tuple = get_record_polymorphic_params(operand->type); + if (tuple) { + count = tuple->variables.count; + if (index < count) { + param = tuple->variables[cast(isize)index]; } } else { error(operand->expr, "Expected a specialized polymorphic record type for '%.*s'", LIT(builtin_name)); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d2d01deda..ad546858c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7331,14 +7331,9 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O gbString s = gb_string_make_reserve(heap_allocator(), e->token.string.len+3); s = gb_string_append_fmt(s, "%.*s(", LIT(e->token.string)); - Type *params = nullptr; - switch (bt->kind) { - case Type_Struct: params = bt->Struct.polymorphic_params; break; - case Type_Union: params = bt->Union.polymorphic_params; break; - } - - if (params != nullptr) for_array(i, params->Tuple.variables) { - Entity *v = params->Tuple.variables[i]; + TypeTuple *tuple = get_record_polymorphic_params(e->type); + if (tuple != nullptr) for_array(i, tuple->variables) { + Entity *v = tuple->variables[i]; String name = v->token.string; if (i > 0) { s = gb_string_append_fmt(s, ", "); diff --git a/src/check_type.cpp b/src/check_type.cpp index 7ed657bee..e0dea19cb 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -564,19 +564,7 @@ gb_internal bool check_record_poly_operand_specialization(CheckerContext *ctx, T gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array const &ordered_operands) { for (Entity *e : found_gen_types->types) { Type *t = base_type(e->type); - TypeTuple *tuple = nullptr; - switch (t->kind) { - case Type_Struct: - if (t->Struct.polymorphic_params) { - tuple = &t->Struct.polymorphic_params->Tuple; - } - break; - case Type_Union: - if (t->Union.polymorphic_params) { - tuple = &t->Union.polymorphic_params->Tuple; - } - break; - } + TypeTuple *tuple = get_record_polymorphic_params(t); GB_ASSERT_MSG(tuple != nullptr, "%s :: %s", type_to_string(e->type), type_to_string(t)); GB_ASSERT(param_count == tuple->variables.count); @@ -663,6 +651,8 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast * &struct_type->Struct.is_polymorphic, node, poly_operands ); + wait_signal_set(&struct_type->Struct.polymorphic_wait_signal); + struct_type->Struct.is_poly_specialized = check_record_poly_operand_specialization(ctx, struct_type, poly_operands, &struct_type->Struct.is_polymorphic); if (original_type_for_poly) { GB_ASSERT(named_type != nullptr); @@ -712,6 +702,8 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no &union_type->Union.is_polymorphic, node, poly_operands ); + wait_signal_set(&union_type->Union.polymorphic_wait_signal); + union_type->Union.is_poly_specialized = check_record_poly_operand_specialization(ctx, union_type, poly_operands, &union_type->Union.is_polymorphic); if (original_type_for_poly) { GB_ASSERT(named_type != nullptr); @@ -1453,12 +1445,14 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special return true; } + wait_for_record_polymorphic_params(s); + wait_for_record_polymorphic_params(t); if (t->Struct.polymorphic_parent == s->Struct.polymorphic_parent && s->Struct.polymorphic_params != nullptr && t->Struct.polymorphic_params != nullptr) { - TypeTuple *s_tuple = &s->Struct.polymorphic_params->Tuple; - TypeTuple *t_tuple = &t->Struct.polymorphic_params->Tuple; + TypeTuple *s_tuple = get_record_polymorphic_params(s); + TypeTuple *t_tuple = get_record_polymorphic_params(t); GB_ASSERT(t_tuple->variables.count == s_tuple->variables.count); for_array(i, s_tuple->variables) { Entity *s_e = s_tuple->variables[i]; @@ -1506,12 +1500,14 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special return true; } + wait_for_record_polymorphic_params(s); + wait_for_record_polymorphic_params(t); if (t->Union.polymorphic_parent == s->Union.polymorphic_parent && s->Union.polymorphic_params != nullptr && t->Union.polymorphic_params != nullptr) { - TypeTuple *s_tuple = &s->Union.polymorphic_params->Tuple; - TypeTuple *t_tuple = &t->Union.polymorphic_params->Tuple; + TypeTuple *s_tuple = get_record_polymorphic_params(s); + TypeTuple *t_tuple = get_record_polymorphic_params(t); GB_ASSERT(t_tuple->variables.count == s_tuple->variables.count); for_array(i, s_tuple->variables) { Entity *s_e = s_tuple->variables[i]; diff --git a/src/checker.cpp b/src/checker.cpp index 8a58bb425..e90509c1f 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2031,6 +2031,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { } else { add_type_info_type_internal(c, t_type_info_ptr); } + wait_for_record_polymorphic_params(bt); add_type_info_type_internal(c, bt->Union.polymorphic_params); for_array(i, bt->Union.variants) { add_type_info_type_internal(c, bt->Union.variants[i]); @@ -2063,6 +2064,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { } } } + wait_for_record_polymorphic_params(bt); add_type_info_type_internal(c, bt->Struct.polymorphic_params); for_array(i, bt->Struct.fields) { Entity *f = bt->Struct.fields[i]; @@ -2292,6 +2294,7 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { } else { add_min_dep_type_info(c, t_type_info_ptr); } + wait_for_record_polymorphic_params(bt); add_min_dep_type_info(c, bt->Union.polymorphic_params); for_array(i, bt->Union.variants) { add_min_dep_type_info(c, bt->Union.variants[i]); @@ -2321,6 +2324,7 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { } } } + wait_for_record_polymorphic_params(bt); add_min_dep_type_info(c, bt->Struct.polymorphic_params); for_array(i, bt->Struct.fields) { Entity *f = bt->Struct.fields[i]; diff --git a/src/types.cpp b/src/types.cpp index 45aa26894..4ceba5244 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -140,6 +140,7 @@ struct TypeStruct { i64 custom_field_align; Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; + Wait_Signal polymorphic_wait_signal; Type * soa_elem; i32 soa_count; @@ -167,6 +168,7 @@ struct TypeUnion { i64 custom_align; Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; + Wait_Signal polymorphic_wait_signal; i16 tag_size; bool is_polymorphic; @@ -1093,6 +1095,7 @@ gb_internal Type *alloc_type_struct() { gb_internal Type *alloc_type_struct_complete() { Type *t = alloc_type(Type_Struct); wait_signal_set(&t->Struct.fields_wait_signal); + wait_signal_set(&t->Struct.polymorphic_wait_signal); return t; } @@ -2136,15 +2139,30 @@ gb_internal bool is_type_polymorphic_record_unspecialized(Type *t) { return false; } +gb_internal void wait_for_record_polymorphic_params(Type *t) { + t = base_type(t); + switch (t->kind) { + case Type_Struct: + wait_signal_until_available(&t->Struct.polymorphic_wait_signal); + break; + case Type_Union: + wait_signal_until_available(&t->Union.polymorphic_wait_signal); + break; + } +} + + gb_internal TypeTuple *get_record_polymorphic_params(Type *t) { t = base_type(t); switch (t->kind) { case Type_Struct: + wait_signal_until_available(&t->Struct.polymorphic_wait_signal); if (t->Struct.polymorphic_params) { return &t->Struct.polymorphic_params->Tuple; } break; case Type_Union: + wait_signal_until_available(&t->Union.polymorphic_wait_signal); if (t->Union.polymorphic_params) { return &t->Union.polymorphic_params->Tuple; } -- cgit v1.2.3 From 49f147cc86d06ee60dd6936404b18f952196d9a8 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 8 Jun 2024 17:05:00 -0400 Subject: Prevent panic when `swizzle` called with < 2 indices The requirement for at least 2 indices has been sourced from `lb_addr_swizzle` in `llvm_backend_general.cpp`, where there is an assert to ensure the swizzle_count is `1 < n <= 4`. --- src/check_builtin.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/check_builtin.cpp') diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index eef925d94..98c695a2c 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -2419,6 +2419,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As if (arg_count > max_count) { error(call, "Too many 'swizzle' indices, %td > %td", arg_count, max_count); return false; + } else if (arg_count < 2) { + error(call, "Not enough 'swizzle' indices, %td < 2", arg_count); + return false; } if (type->kind == Type_Array) { -- cgit v1.2.3 From fa3cae2bb04db76f52f1b2288a9c858f20332b8a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 10 Jun 2024 15:02:34 +0100 Subject: Add `intrinsics.procedure_of` ```odin foo :: proc(x: $T) { fmt.println(x) } bar :: intrinsics.procedure_of(foo(int(123))) // parameters are never ran at compile time, similar to `size_of` bar(333) // prints 333 ``` --- base/intrinsics/intrinsics.odin | 4 ++++ src/check_builtin.cpp | 46 +++++++++++++++++++++++++++++++++++++++++ src/check_decl.cpp | 17 ++++++++++----- src/check_expr.cpp | 1 + src/check_stmt.cpp | 10 ++++++++- src/checker.cpp | 4 ++++ src/checker.hpp | 6 ++++++ src/checker_builtin_procs.hpp | 4 ++++ src/parser.hpp | 1 + 9 files changed, 87 insertions(+), 6 deletions(-) (limited to 'src/check_builtin.cpp') diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 8873f3bbc..4f6fa2713 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -295,6 +295,10 @@ simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T --- // if all listed features are supported. has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) --- + +// Returns the value of the procedure where `x` must be a call expression +procedure_of :: proc(x: $T) -> T where type_is_proc(T) --- + // WASM targets only wasm_memory_grow :: proc(index, delta: uintptr) -> int --- wasm_memory_size :: proc(index: uintptr) -> int --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 98c695a2c..3aee804df 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1843,6 +1843,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_objc_register_class: case BuiltinProc_atomic_type_is_lock_free: case BuiltinProc_has_target_feature: + case BuiltinProc_procedure_of: // NOTE(bill): The first arg may be a Type, this will be checked case by case break; @@ -6157,6 +6158,51 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As break; } + case BuiltinProc_procedure_of: + { + Ast *call_expr = unparen_expr(ce->args[0]); + Operand op = {}; + check_expr_base(c, &op, ce->args[0], nullptr); + if (op.mode != Addressing_Value && !(call_expr && call_expr->kind == Ast_CallExpr)) { + error(ce->args[0], "Expected a call expression for '%.*s'", LIT(builtin_name)); + return false; + } + + Ast *proc = call_expr->CallExpr.proc; + Entity *e = entity_of_node(proc); + + if (e == nullptr) { + error(ce->args[0], "Invalid procedure value, expected a regular/specialized procedure"); + return false; + } + + TypeAndValue tav = proc->tav; + + + operand->type = e->type; + operand->mode = Addressing_Value; + operand->value = tav.value; + operand->builtin_id = BuiltinProc_Invalid; + operand->proc_group = nullptr; + + if (tav.mode == Addressing_Builtin) { + operand->mode = tav.mode; + operand->builtin_id = cast(BuiltinProcId)e->Builtin.id; + break; + } + + if (!is_type_proc(e->type)) { + gbString s = type_to_string(e->type); + error(ce->args[0], "Expected a procedure value, got '%s'", s); + gb_string_free(s); + return false; + } + + + ce->entity_procedure_of = e; + break; + } + case BuiltinProc_constant_utf16_cstring: { String value = {}; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 43947836b..13b14149a 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -88,11 +88,14 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o e->type = t_invalid; return nullptr; } else if (is_type_polymorphic(t)) { - gbString str = type_to_string(t); - defer (gb_string_free(str)); - error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); - e->type = t_invalid; - return nullptr; + Entity *e = entity_of_node(operand->expr); + if (e->state.load() != EntityState_Resolved) { + gbString str = type_to_string(t); + defer (gb_string_free(str)); + error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); + e->type = t_invalid; + return nullptr; + } } else if (is_type_empty_union(t)) { gbString str = type_to_string(t); defer (gb_string_free(str)); @@ -479,6 +482,9 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr entity = check_selector(ctx, &operand, init, e->type); } else { check_expr_or_type(ctx, &operand, init, e->type); + if (init->kind == Ast_CallExpr) { + entity = init->CallExpr.entity_procedure_of; + } } switch (operand.mode) { @@ -526,6 +532,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr return; } + if (entity != nullptr) { if (e->type != nullptr) { Operand x = {}; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 641f70566..01cba881e 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -578,6 +578,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E d->defer_use_checked = false; Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags); + entity->state.store(EntityState_Resolved); entity->identifier = ident; add_entity_and_decl_info(&nctx, ident, entity, d); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index c37c58cd6..f2e3b0242 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2224,8 +2224,16 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) { } if (do_require) { gbString expr_str = expr_to_string(ce->proc); + defer (gb_string_free(expr_str)); + if (builtin_id) { + String real_name = builtin_procs[builtin_id].name; + if (real_name != make_string(cast(u8 const *)expr_str, gb_string_length(expr_str))) { + error(node, "'%s' ('%.*s.%.*s') requires that its results must be handled", expr_str, + LIT(builtin_proc_pkg_name[builtin_procs[builtin_id].pkg]), LIT(real_name)); + return; + } + } error(node, "'%s' requires that its results must be handled", expr_str); - gb_string_free(expr_str); } return; } else if (expr && expr->kind == Ast_SelectorCallExpr) { diff --git a/src/checker.cpp b/src/checker.cpp index 8f0cc1cd1..852fb89bb 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1479,6 +1479,10 @@ gb_internal Entity *entity_of_node(Ast *expr) { case_ast_node(cc, CaseClause, expr); return cc->implicit_entity; case_end; + + case_ast_node(ce, CallExpr, expr); + return ce->entity_procedure_of; + case_end; } return nullptr; } diff --git a/src/checker.hpp b/src/checker.hpp index 2ac4c8e7a..492a64fb6 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -51,6 +51,12 @@ enum StmtFlag { enum BuiltinProcPkg { BuiltinProcPkg_builtin, BuiltinProcPkg_intrinsics, + BuiltinProcPkg_COUNT +}; + +String builtin_proc_pkg_name[BuiltinProcPkg_COUNT] = { + str_lit("builtin"), + str_lit("intrinsics"), }; struct BuiltinProc { diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 5f98bb7b3..35acad42f 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -299,6 +299,8 @@ BuiltinProc__type_simple_boolean_end, BuiltinProc__type_end, + BuiltinProc_procedure_of, + BuiltinProc___entry_point, BuiltinProc_objc_send, @@ -614,6 +616,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("procedure_of"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, diff --git a/src/parser.hpp b/src/parser.hpp index 0e411d9ac..02f2af28d 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -458,6 +458,7 @@ AST_KIND(_ExprBegin, "", bool) \ bool optional_ok_one; \ bool was_selector; \ AstSplitArgs *split_args; \ + Entity *entity_procedure_of; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(EnumFieldValue, "enum field value", struct { \ -- cgit v1.2.3