From 4899ee14e22dc583b212cf8edf4819fa1d889f51 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 25 Jan 2026 21:24:27 +0100 Subject: [PATCH] Resolving &X.b when X is a const incorrectly checked for runtime constness #2842 Creating a generic instance fails if it is created after interface checking #2840 --- releasenotes.md | 1 + src/compiler/sema_builtins.c | 29 ++++++++++--------- src/compiler/sema_decls.c | 24 ++++++++++++++- src/compiler/sema_internal.h | 1 + .../any/generic_interface_stage.c3t | 8 +++++ .../builtins/catch_zero_unaligned.c3 | 4 +++ 6 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 test/test_suite/any/generic_interface_stage.c3t create mode 100644 test/test_suite/builtins/catch_zero_unaligned.c3 diff --git a/releasenotes.md b/releasenotes.md index 499079792..b1d8ac374 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -133,6 +133,7 @@ - Packed .c3l files without compressions weren't unpacked correctly. - Lowering of optional in && was incorrect #2843 - Resolving &X.b when X is a const incorrectly checked for runtime constness #2842 +- Alignment param on $$unaligned_* not checked for zero #2844 ### 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_builtins.c b/src/compiler/sema_builtins.c index ed9201f06..f590402d3 100644 --- a/src/compiler/sema_builtins.c +++ b/src/compiler/sema_builtins.c @@ -37,7 +37,7 @@ static bool sema_expr_analyse_syscall(SemaContext *context, Expr *expr); static inline bool sema_expr_analyse_swizzle(SemaContext *context, Expr *expr, bool swizzle_two); static inline int builtin_expected_args(BuiltinFunction func); static inline bool is_valid_atomicity(SemaContext *context, Expr *expr); -static bool sema_check_alignment_expression(SemaContext *context, Expr *align); +static bool sema_check_alignment_expression(SemaContext *context, Expr *align, bool may_be_zero); static bool sema_expr_is_valid_mask_for_value(SemaContext *context, Expr *expr, Expr *value) { @@ -78,15 +78,16 @@ static bool sema_check_builtin_args_const(SemaContext *context, Expr **args, siz return true; } -static bool sema_check_alignment_expression(SemaContext *context, Expr *align) +static bool sema_check_alignment_expression(SemaContext *context, Expr *align, bool may_be_zero) { if (!sema_analyse_expr_rhs(context, type_usz, align, false, NULL, false)) return false; if (!expr_is_const_int(align) || !int_fits(align->const_expr.ixx, TYPE_U64) || (!is_power_of_two(align->const_expr.ixx.i.low) && align->const_expr.ixx.i.low)) { - RETURN_SEMA_ERROR(align, "Expected a constant power-of-two alignment or zero."); + RETURN_SEMA_ERROR(align, may_be_zero ? "Expected a constant power-of-two alignment or zero." : "Expected a constant power-of-two alignment."); } + if (!may_be_zero && align->const_expr.ixx.i.low == 0) RETURN_SEMA_ERROR(align, "Alignment must not be zero."); return true; } @@ -258,7 +259,7 @@ static bool sema_expr_analyse_compare_exchange(SemaContext *context, Expr *expr) RETURN_SEMA_ERROR(args[6], "Failure ordering may not be RELEASE / ACQUIRE_RELEASE."); } Expr *align = args[7]; - if (!sema_check_alignment_expression(context, align)) return false; + if (!sema_check_alignment_expression(context, align, true)) return false; expr->type = type_add_optional(args[1]->type, optional); return true; } @@ -1049,7 +1050,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) type_get_vector(pointer_type->pointer, flat_pointer_vec->type_kind, len)), type_quoted_error_string(args[2]->type)); } - if (!sema_check_alignment_expression(context, args[3])) return false; + if (!sema_check_alignment_expression(context, args[3], true)) return false; if (!sema_expr_is_valid_mask_for_value(context, args[1], args[2])) return false; rtype = type_get_vector(pointer_type->pointer, flat_pointer_vec->type_kind, len); break; @@ -1072,7 +1073,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) type_get_vector_from_vector(pointer_type->pointer, flat_pointer_vec)), type_quoted_error_string(args[2]->type)); } - if (!sema_check_alignment_expression(context, args[3])) return false; + if (!sema_check_alignment_expression(context, args[3], true)) return false; if (!sema_expr_is_valid_mask_for_value(context, args[2], args[1])) return false; rtype = type_void; break; @@ -1125,7 +1126,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) { RETURN_SEMA_ERROR(args[2], "Expected the value to be of type %s.", type_quoted_error_string(pointer_type->pointer)); } - if (!sema_check_alignment_expression(context, args[3])) return false; + if (!sema_check_alignment_expression(context, args[3], true)) return false; if (!sema_expr_is_valid_mask_for_value(context, args[1], args[2])) return false; rtype = pointer_type->pointer; break; @@ -1140,7 +1141,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) { RETURN_SEMA_ERROR(args[2], "Expected the value to be of type %s.", type_quoted_error_string(pointer_type->pointer)); } - if (!sema_check_alignment_expression(context, args[3])) return false; + if (!sema_check_alignment_expression(context, args[3], true)) return false; if (!sema_expr_is_valid_mask_for_value(context, args[2], args[1])) return false; rtype = type_void; break; @@ -1217,7 +1218,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) if (!sema_check_builtin_args(context, args, (BuiltinArg[]) {BA_POINTER, BA_INTEGER, BA_BOOL}, 3)) return false; Type *original = type_flatten(args[0]->type); if (original == type_voidptr) RETURN_SEMA_ERROR(args[0], "Expected a typed pointer."); - if (!sema_check_alignment_expression(context, args[1])) return false; + if (!sema_check_alignment_expression(context, args[1], false)) return false; if (!sema_cast_const(args[2])) RETURN_SEMA_ERROR(args[2], "'is_volatile' must be a compile time constant."); rtype = original->pointer; break; @@ -1228,7 +1229,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) if (!sema_check_builtin_args(context, args, (BuiltinArg[]) {BA_POINTER}, 1)) return false; if (!sema_check_builtin_args(context, &args[2], (BuiltinArg[]) {BA_INTEGER, BA_BOOL}, 2)) return false; Type *original = type_flatten(args[0]->type); - if (!sema_check_alignment_expression(context, args[2])) return false; + if (!sema_check_alignment_expression(context, args[2], false)) return false; if (!sema_cast_const(args[3])) RETURN_SEMA_ERROR(args[3], "'is_volatile' must be a compile time constant."); if (original != type_voidptr) { @@ -1282,7 +1283,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) if (!sema_cast_const(args[3])) RETURN_SEMA_ERROR(args[3], "Ordering must be a compile time constant."); if (!is_valid_atomicity(context, args[3])) return false; if (args[3]->const_expr.ixx.i.low == ATOMIC_UNORDERED) RETURN_SEMA_ERROR(args[3], "'unordered' is not valid ordering."); - if (!sema_check_alignment_expression(context, args[4])) return false; + if (!sema_check_alignment_expression(context, args[4], true)) return false; rtype = args[1]->type; break; } @@ -1302,7 +1303,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) if (!sema_cast_const(args[3])) RETURN_SEMA_ERROR(args[3], "Ordering must be a compile time constant."); if (!is_valid_atomicity(context, args[3])) return false; if (args[3]->const_expr.ixx.i.low == ATOMIC_UNORDERED) RETURN_SEMA_ERROR(args[3], "'unordered' is not valid ordering."); - if (!sema_check_alignment_expression(context, args[4])) return false; + if (!sema_check_alignment_expression(context, args[4], true)) return false; rtype = args[1]->type; break; } @@ -1321,7 +1322,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) if (!sema_cast_const(args[3])) RETURN_SEMA_ERROR(args[3], "Ordering must be a compile time constant."); if (!is_valid_atomicity(context, args[3])) return false; if (args[3]->const_expr.ixx.i.low == ATOMIC_UNORDERED) RETURN_SEMA_ERROR(args[3], "'unordered' is not valid ordering."); - if (!sema_check_alignment_expression(context, args[4])) return false; + if (!sema_check_alignment_expression(context, args[4], true)) return false; rtype = args[1]->type; break; } @@ -1341,7 +1342,7 @@ bool sema_expr_analyse_builtin_call(SemaContext *context, Expr *expr) if (!sema_cast_const(args[3])) RETURN_SEMA_ERROR(args[3], "Ordering must be a compile time constant."); if (!is_valid_atomicity(context, args[3])) return false; if (args[3]->const_expr.ixx.i.low == ATOMIC_UNORDERED) RETURN_SEMA_ERROR(args[3], "'unordered' is not valid ordering."); - if (!sema_check_alignment_expression(context, args[4])) return false; + if (!sema_check_alignment_expression(context, args[4], true)) return false; rtype = args[1]->type; break; } diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 6d6c35f80..aac81fa66 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -5374,7 +5374,29 @@ FOUND:; break; } } - ASSERT(stage < ANALYSIS_INTERFACE); + if (stage < ANALYSIS_INTERFACE) goto EXIT; + if (compiler.context.errors_found) return poisoned_decl; + FOREACH(Decl *, decl, copied) + { + SemaContext context_gen; + switch (decl->decl_kind) + { + case DECL_TYPEDEF: + case DECL_STRUCT: + case DECL_UNION: + case DECL_ENUM: + case DECL_BITSTRUCT: + break; + default: + continue; + } + if (decl->interfaces) + { + sema_context_init(&context_gen, decl->unit); + sema_check_interfaces(&context_gen, decl); + sema_context_destroy(&context_gen); + } + } EXIT:; if (compiler.context.errors_found) return poisoned_decl; } diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index 31a6fa2c4..c650ed7cb 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -143,6 +143,7 @@ bool sema_analyse_attributes(SemaContext *context, Decl *decl, Attr **attrs, Att void unit_register_optional_global_decl(CompilationUnit *unit, Decl *decl); bool analyse_func_body(SemaContext *context, Decl *decl); +bool sema_check_interfaces(SemaContext *context, Decl *decl); INLINE bool sema_analyse_stmt_chain(SemaContext *context, Ast *statement) { diff --git a/test/test_suite/any/generic_interface_stage.c3t b/test/test_suite/any/generic_interface_stage.c3t new file mode 100644 index 000000000..3d1b35665 --- /dev/null +++ b/test/test_suite/any/generic_interface_stage.c3t @@ -0,0 +1,8 @@ +import std; +fn int foo() => 0; +alias FooFn = fn int(); +enum Bar : const FooFn +{ + FOO = fn () => (int)(iptr)ExclusiveRange{int}.typeid, + BAR = &foo, +} \ No newline at end of file diff --git a/test/test_suite/builtins/catch_zero_unaligned.c3 b/test/test_suite/builtins/catch_zero_unaligned.c3 new file mode 100644 index 000000000..a1587cc21 --- /dev/null +++ b/test/test_suite/builtins/catch_zero_unaligned.c3 @@ -0,0 +1,4 @@ +fn void unaligned_load_store(void* dst, void* src) @nostrip +{ + $$unaligned_store(dst, $$unaligned_load((char*)src, 2, false), 0, false); // #error: Alignment must not be zero +} \ No newline at end of file