From 109e15b5a0635969d32c9f2853809446ad99cab0 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 25 Jan 2026 04:41:06 +0100 Subject: [PATCH] - Empty enums would return the values as zero sized arrays #2838 --- lib/std/collections/enummap.c3 | 1 + lib/std/hash/ripemd.c3 | 12 ++++++----- releasenotes.md | 1 + src/compiler/sema_decls.c | 21 +++++++++++-------- src/compiler/sema_expr.c | 4 ++-- .../enumerations/empty_enum_reflection.c3 | 6 ++++++ test/test_suite/enumerations/enum_values.c3t | 16 ++++++++------ test/unit/stdlib/hash/ripemd.c3 | 8 +++---- 8 files changed, 43 insertions(+), 26 deletions(-) create mode 100644 test/test_suite/enumerations/empty_enum_reflection.c3 diff --git a/lib/std/collections/enummap.c3 b/lib/std/collections/enummap.c3 index 3577b34a3..be26f34a6 100644 --- a/lib/std/collections/enummap.c3 +++ b/lib/std/collections/enummap.c3 @@ -1,5 +1,6 @@ <* @require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enummap" + @require Enum.values.len > 0 : "Only non-empty enums may be used with enummap" *> module std::collections::enummap ; import std::io; diff --git a/lib/std/hash/ripemd.c3 b/lib/std/hash/ripemd.c3 index b62d2ef5e..960f81ac9 100644 --- a/lib/std/hash/ripemd.c3 +++ b/lib/std/hash/ripemd.c3 @@ -1,16 +1,13 @@ // Copyright (c) 2025 Zack Puhl . All rights reserved. // Use of this source code is governed by the MIT license // a copy of which can be found in the LICENSE_STDLIB file. -<* - @require @in(DIGEST_BITS, ...PERMISSIBLE_SIZES_BITS) : "Invalid DIGEST_BITS; must be one of {128, 160, 256, 320}." -*> -module std::hash::ripemd ; +module std::hash::ripemd; +const usz[4] PERMISSIBLE_SIZES_BITS = { 128, 160, 256, 320 }; <* Unchanging block size. *> const BLOCK_SIZE = 64; -const usz[4] PERMISSIBLE_SIZES_BITS = { 128, 160, 256, 320 }; <* RIPE-MD initial values. *> const uint[10] H = { @@ -24,6 +21,11 @@ const uint[9] K = { 0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9 }; +<* + @require @in(DIGEST_BITS, ...PERMISSIBLE_SIZES_BITS) : "Invalid DIGEST_BITS; must be one of {128, 160, 256, 320}." +*> +module std::hash::ripemd ; + <* Ultimate digest's size in bytes. *> const DIGEST_BYTES = DIGEST_BITS / 8; diff --git a/releasenotes.md b/releasenotes.md index ec7de38a0..4e9d4fffa 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -124,6 +124,7 @@ - Constant deref of subscript had inserted checks #2818 - Raw vaargs with optional return not lowered correctly #2819 - Early exit in macro call crashes codegen #2820 +- Empty enums would return the values as zero sized arrays #2838 ### Stdlib changes - Add `ThreadPool` join function to wait for all threads to finish in the pool without destroying the threads. diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 5b007ace9..adb5793b3 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -5293,14 +5293,6 @@ FOUND:; vec_add(generic->generic_decl.instances, instance); AnalysisStage stage = module->stage; ASSERT(stage > ANALYSIS_IMPORTS); - // Add all the normal top level declarations - FOREACH(Decl *, decl, copied) unit_register_global_decl(decl->unit, decl); - // Add all the conditional declarations - FOREACH(Decl *, decl, copied_cond) - { - unit_register_optional_global_decl(decl->unit, decl); - if (decl->decl_kind != DECL_ERASED) vec_add(copied, decl); - } if (compiler.context.errors_found) return poisoned_decl; // Check contracts @@ -5316,11 +5308,22 @@ FOUND:; SourceSpan param_span = extend_span_with_token(params[0]->span, VECLAST(params)->span); if (!sema_analyse_generic_module_contracts(context, module, instance, contracts, param_span, invocation_span)) { - return poisoned_decl; + decl_poison(instance); + decl_poison(alias); + return alias; } } } + // Add all the normal top level declarations + FOREACH(Decl *, decl, copied) unit_register_global_decl(decl->unit, decl); + // Add all the conditional declarations + FOREACH(Decl *, decl, copied_cond) + { + unit_register_optional_global_decl(decl->unit, decl); + if (decl->decl_kind != DECL_ERASED) vec_add(copied, decl); + } + if (stage < ANALYSIS_METHODS_REGISTER) goto EXIT; FOREACH(Decl *, decl, copied) { diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 66483158b..0a28db3c9 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -4962,7 +4962,7 @@ static inline bool sema_expr_replace_with_enum_array(SemaContext *context, Expr initializer->initializer_list = element_values; enum_array_expr->expr_kind = EXPR_COMPOUND_LITERAL; enum_array_expr->expr_compound_literal.initializer = initializer; - enum_array_expr->expr_compound_literal.type_info = type_info_new_base(type_get_array(kind, elements), span); + enum_array_expr->expr_compound_literal.type_info = type_info_new_base(type_get_slice(kind), span); enum_array_expr->resolve_status = RESOLVE_NOT_DONE; return sema_analyse_expr_rvalue(context, enum_array_expr); } @@ -4985,7 +4985,7 @@ static inline bool sema_expr_replace_with_const_enum_array(SemaContext *context, initializer->initializer_list = element_values; enum_array_expr->expr_kind = EXPR_COMPOUND_LITERAL; enum_array_expr->expr_compound_literal.initializer = initializer; - enum_array_expr->expr_compound_literal.type_info = type_info_new_base(type_get_array(kind, elements), span); + enum_array_expr->expr_compound_literal.type_info = type_info_new_base(type_get_slice(kind), span); enum_array_expr->resolve_status = RESOLVE_NOT_DONE; return sema_analyse_expr_rvalue(context, enum_array_expr); } diff --git a/test/test_suite/enumerations/empty_enum_reflection.c3 b/test/test_suite/enumerations/empty_enum_reflection.c3 new file mode 100644 index 000000000..f3ef45b4c --- /dev/null +++ b/test/test_suite/enumerations/empty_enum_reflection.c3 @@ -0,0 +1,6 @@ +import std; +enum Foo +{ +} +EnumMap{Foo, int} map; // #error: Parameter(s) would violate constraint: @require "Enum.values.len > 0" violated +fn int main() => 1; \ No newline at end of file diff --git a/test/test_suite/enumerations/enum_values.c3t b/test/test_suite/enumerations/enum_values.c3t index fb6b6172f..db8b16a3d 100644 --- a/test/test_suite/enumerations/enum_values.c3t +++ b/test/test_suite/enumerations/enum_values.c3t @@ -9,7 +9,7 @@ enum Foo } Foo zfok = Foo.values[0]; -Foo[] zw = &&Foo.values; +Foo[] zw = Foo.values; fn void test(int x) { @@ -19,17 +19,21 @@ fn void test(int x) /* #expect: test.ll @test.zfok = local_unnamed_addr global i32 0, align 4 -@.taddr = private unnamed_addr global [2 x i32] [i32 0, i32 1], align 4 -@test.zw = local_unnamed_addr global %"int[]" { ptr @.taddr, i64 2 }, align 8 +@.__const_slice = private unnamed_addr global [2 x i32] [i32 0, i32 1], align 4 +@test.zw = local_unnamed_addr global %"int[]" { ptr @.__const_slice, i64 2 }, align 8 +@.__const = private unnamed_addr constant [2 x i32] [i32 0, i32 1], align 4 define void @test.test(i32 %0) #0 { entry: %zonk = alloca i32, align 4 %literal = alloca [2 x i32], align 4 call void @llvm.memcpy.p0.p0.i32(ptr align 4 %literal, ptr align 4 @.__const, i32 8, i1 false) + %1 = insertvalue %"int[]" undef, ptr %literal, 0 + %2 = insertvalue %"int[]" %1, i64 2, 1 + %3 = extractvalue %"int[]" %2, 0 %sext = sext i32 %0 to i64 - %ptroffset = getelementptr inbounds [4 x i8], ptr %literal, i64 %sext - %1 = load i32, ptr %ptroffset, align 4 - store i32 %1, ptr %zonk, align 4 + %ptroffset = getelementptr inbounds [4 x i8], ptr %3, i64 %sext + %4 = load i32, ptr %ptroffset, align 4 + store i32 %4, ptr %zonk, align 4 ret void } diff --git a/test/unit/stdlib/hash/ripemd.c3 b/test/unit/stdlib/hash/ripemd.c3 index 6ea894e84..7780d2597 100644 --- a/test/unit/stdlib/hash/ripemd.c3 +++ b/test/unit/stdlib/hash/ripemd.c3 @@ -48,7 +48,7 @@ fn void basic_streamed() x'e7660e67549435c62141e51c9ab1dcc3b1ee9f65c0b3e561ae8f58c5dba3d21997781cd1cc6fbc34' }; char[] phrase = "The quick brown fox jumps over the lazy dog"; - $foreach $i, $bits : ripemd::PERMISSIBLE_SIZES_BITS{128}: + $foreach $i, $bits : ripemd::PERMISSIBLE_SIZES_BITS: { // le scope RipeMd{$bits} r @noinit; r.init(); @@ -83,7 +83,7 @@ fn void large_input_streamed() }; char[] one_mb = mem::talloc_array(char, 1024*1024); one_mb[..] = 0xA5; - $foreach $i, $bits : ripemd::PERMISSIBLE_SIZES_BITS{128}: + $foreach $i, $bits : ripemd::PERMISSIBLE_SIZES_BITS: { RipeMd{$bits} r @noinit; r.init(); @@ -106,7 +106,7 @@ fn void large_input_streamed_per_byte() }; char[] one_mb = mem::talloc_array(char, 1024*1024); one_mb[..] = 0xA5; - $foreach $i, $bits : ripemd::PERMISSIBLE_SIZES_BITS{128}: + $foreach $i, $bits : ripemd::PERMISSIBLE_SIZES_BITS: { RipeMd{$bits} r @noinit; r.init(); @@ -132,7 +132,7 @@ fn void walk_lengths_streamed() { foreach (arr : CALCULATED_HASHES) { - $foreach $i, $bits : ripemd::PERMISSIBLE_SIZES_BITS{128}: + $foreach $i, $bits : ripemd::PERMISSIBLE_SIZES_BITS: { RipeMd{$bits} r @noinit; r.init();