diff --git a/lib/std/core/builtin.c3 b/lib/std/core/builtin.c3 index ebaf3bb49..c148210ea 100644 --- a/lib/std/core/builtin.c3 +++ b/lib/std/core/builtin.c3 @@ -376,21 +376,39 @@ macro String @str_upper(String $str) @builtin => $$str_upper($str); macro String @str_lower(String $str) @builtin => $$str_lower($str); macro uint @str_hash(String $str) @builtin => $$str_hash($str); -macro uint int.hash(int i) => i; -macro uint uint.hash(uint i) => i; -macro uint short.hash(short s) => s; -macro uint ushort.hash(ushort s) => s; -macro uint char.hash(char c) => c; -macro uint ichar.hash(ichar c) => c; -macro uint long.hash(long i) => (uint)((i >> 32) ^ i); -macro uint ulong.hash(ulong i) => (uint)((i >> 32) ^ i); -macro uint int128.hash(int128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i); -macro uint uint128.hash(uint128 i) => (uint)((i >> 96) ^ (i >> 64) ^ (i >> 32) ^ i); -macro uint bool.hash(bool b) => (uint)b; -macro uint typeid.hash(typeid t) => ((ulong)(uptr)t).hash(); +macro @generic_hash_core(h, value) +{ + h ^= (uint)value; // insert lowest 32 bits + h *= 0x96f59e5b; // diffuse them up + h ^= h >> 16; // diffuse them down + return h; +} + +macro @generic_hash(value) +{ + uint h = @generic_hash_core((uint)0x3efd4391, value); + $for (var $cnt = 4; $cnt < $sizeof(value); $cnt += 4) + value >>= 32; // reduce value + h = @generic_hash_core(h, value); + $endfor + return h; +} + +macro uint int.hash(int i) => @generic_hash(i); +macro uint uint.hash(uint i) => @generic_hash(i); +macro uint short.hash(short s) => @generic_hash(s); +macro uint ushort.hash(ushort s) => @generic_hash(s); +macro uint char.hash(char c) => @generic_hash(c); +macro uint ichar.hash(ichar c) => @generic_hash(c); +macro uint long.hash(long i) => @generic_hash(i); +macro uint ulong.hash(ulong i) => @generic_hash(i); +macro uint int128.hash(int128 i) => @generic_hash(i); +macro uint uint128.hash(uint128 i) => @generic_hash(i); +macro uint bool.hash(bool b) => @generic_hash(b); +macro uint typeid.hash(typeid t) => @generic_hash(((ulong)(uptr)t)); macro uint String.hash(String c) => (uint)fnv32a::encode(c); macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c); -macro uint void*.hash(void* ptr) => ((ulong)(uptr)ptr).hash(); +macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr)); distinct EmptySlot = void*; const EmptySlot EMPTY_MACRO_SLOT @builtin = null; diff --git a/releasenotes.md b/releasenotes.md index 76b90e959..afd7a7f43 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -44,6 +44,7 @@ - Macros with default arguments to `&`, `#` and type parameters didn't work as expected. #1754. - `net::poll()` with negative timeout behaved incorrectly. - Return type inference bugs with macros #1757 +- `$defined` in a global scope should accept testing normal macros. ### Stdlib changes - Increase BitWriter.write_bits limit up to 32 bits. @@ -52,6 +53,7 @@ - Add "tokenizer" to String. - Add "skip_empty" to split methods. Add split_to_buffer method. - Add `@enum_from_value`. +- Updated hash function. ## 0.6.5 Change list diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 1d949aacf..97943d142 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1669,6 +1669,7 @@ typedef struct CallEnvKind kind : 8; bool ensures : 1; bool pure : 1; + bool in_no_eval : 1; SourceSpan in_if_resolution; Decl **opt_returns; union diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index aefc97b1a..a4d4cfa31 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -3962,7 +3962,7 @@ bool sema_analyse_var_decl(SemaContext *context, Decl *decl, bool local) } else { - if (context->call_env.kind == CALL_ENV_GLOBAL_INIT) + if (context->call_env.kind == CALL_ENV_GLOBAL_INIT && !context->call_env.in_no_eval) { if (context->current_macro) { diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 48c3e5a1b..99b222abe 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -8795,24 +8795,29 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr { Expr *main_expr = list[i]; SemaContext *active_context = context; + bool in_no_eval = active_context->call_env.in_no_eval; + active_context->call_env.in_no_eval = true; RETRY: switch (main_expr->expr_kind) { case EXPR_OTHER_CONTEXT: + active_context->call_env.in_no_eval = in_no_eval; active_context = expr->expr_other_context.context; + in_no_eval = active_context->call_env.in_no_eval; + active_context->call_env.in_no_eval = true; main_expr = expr->expr_other_context.inner; goto RETRY; case EXPR_ACCESS: if (!sema_expr_analyse_access(active_context, main_expr, &failed, CHECK_VALUE)) { - if (!failed) return false; + if (!failed) goto FAIL; success = false; } break; case EXPR_IDENTIFIER: { Decl *decl = sema_find_path_symbol(active_context, main_expr->identifier_expr.ident, main_expr->identifier_expr.path); - if (!decl_ok(decl)) return false; + if (!decl_ok(decl)) goto FAIL; success = decl != NULL; break; } @@ -8826,14 +8831,14 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr main_expr->resolve_status = RESOLVE_RUNNING; if (!sema_expr_analyse_unary(active_context, main_expr, &failed, CHECK_VALUE)) { - if (!failed) return false; + if (!failed) goto FAIL; success = false; } break; case EXPR_TYPEINFO: { Type *type = sema_expr_check_type_exists(active_context, main_expr->type_expr); - if (!type_ok(type)) return false; + if (!type_ok(type)) goto FAIL; success = type != NULL; break; } @@ -8843,7 +8848,7 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr case EXPR_HASH_IDENT: { Decl *decl = sema_resolve_symbol(active_context, main_expr->hash_ident_expr.identifier, NULL, main_expr->span); - if (!decl) return false; + if (!decl) goto FAIL; main_expr = copy_expr_single(decl->var.init_expr); goto RETRY; } @@ -8851,7 +8856,7 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr { if (!sema_expr_analyse_subscript(active_context, main_expr, CHECK_VALUE, true)) { - return false; + goto FAIL; } if (!expr_ok(main_expr)) { @@ -8862,14 +8867,14 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr case EXPR_CAST: if (!sema_expr_analyse_cast(active_context, main_expr, &failed)) { - if (!failed) return false; + if (!failed) goto FAIL; success = false; } break; case EXPR_CT_IDENT: { Decl *decl = sema_resolve_symbol(active_context, main_expr->ct_ident_expr.identifier, NULL, main_expr->span); - if (!decl) return false; + if (!decl) goto FAIL; break; } case EXPR_CALL: @@ -8877,23 +8882,23 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr bool no_match; if (!sema_expr_analyse_call(active_context, main_expr, &no_match)) { - if (!no_match) return false; + if (!no_match) goto FAIL; success = false; } break; } case EXPR_FORCE_UNWRAP: - if (!sema_analyse_expr(active_context, main_expr->inner_expr)) return false; + if (!sema_analyse_expr(active_context, main_expr->inner_expr)) goto FAIL; success = IS_OPTIONAL(main_expr->inner_expr); break; case EXPR_RETHROW: - if (!sema_analyse_expr(active_context, main_expr->rethrow_expr.inner)) return false; + if (!sema_analyse_expr(active_context, main_expr->rethrow_expr.inner)) goto FAIL; success = IS_OPTIONAL(main_expr->rethrow_expr.inner); break; case EXPR_OPTIONAL: if (!sema_expr_analyse_optional(active_context, main_expr, &failed)) { - if (!failed) return false; + if (!failed) goto FAIL; success = false; } break; @@ -8966,10 +8971,15 @@ static inline bool sema_expr_analyse_ct_defined(SemaContext *context, Expr *expr case EXPR_PTR_ACCESS: case EXPR_RVALUE: case EXPR_MAKE_ANY: - if (!sema_analyse_expr(active_context, main_expr)) return false; + if (!sema_analyse_expr(active_context, main_expr)) goto FAIL; break; } - if (!success) break; + active_context->call_env.in_no_eval = in_no_eval; + if (success) continue; + break; +FAIL: + active_context->call_env.in_no_eval = in_no_eval; + return false; } expr_rewrite_const_bool(expr, type_bool, success); return true; diff --git a/src/compiler/sema_types.c b/src/compiler/sema_types.c index f1feb362d..42666414e 100644 --- a/src/compiler/sema_types.c +++ b/src/compiler/sema_types.c @@ -312,7 +312,11 @@ INLINE bool sema_resolve_evaltype(SemaContext *context, TypeInfo *type_info, Res INLINE bool sema_resolve_typeof(SemaContext *context, TypeInfo *type_info) { Expr *expr = type_info->unresolved_type_expr; - if (!sema_analyse_expr_value(context, expr)) return false; + bool in_no_eval = context->call_env.in_no_eval; + context->call_env.in_no_eval = true; + bool success = sema_analyse_expr_value(context, expr); + context->call_env.in_no_eval = in_no_eval; + if (!success) return false; Type *expr_type = expr->type; if (expr_type->type_kind == TYPE_FUNC_RAW) expr_type = type_get_func_ptr(expr_type); switch (sema_resolve_storage_type(context, expr_type)) diff --git a/test/test_suite/compile_time/macro_compile_time_pseudo_evaluation.c3t b/test/test_suite/compile_time/macro_compile_time_pseudo_evaluation.c3t new file mode 100644 index 000000000..718507204 --- /dev/null +++ b/test/test_suite/compile_time/macro_compile_time_pseudo_evaluation.c3t @@ -0,0 +1,35 @@ +// #target: macos-x64 +module test; +import std; +macro foo(int a) +{ + int h = a; + return a; +} + +$typeof(baz(2)) y; +const ABC = $sizeof(baz(2)); + +macro baz(int x) => foo(x); + +fn void main() +{ + int x = ABC; + int a = baz(3); +} + +/* #expect: test.ll + + +@test.y = local_unnamed_addr global i32 0, align 4 +@test.ABC = local_unnamed_addr constant i64 4, align 8 + +entry: + %x = alloca i32, align 4 + %a = alloca i32, align 4 + %h = alloca i32, align 4 + store i32 4, ptr %x, align 4 + store i32 3, ptr %h, align 4 + store i32 3, ptr %a, align 4 + ret void +}