From 79db06ecd142065d62766500735dd85deed0fbf3 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 20 Feb 2025 15:51:21 +0100 Subject: [PATCH] Crash when trying to define a method macro that isn't `@construct` but has no arguments. --- releasenotes.md | 1 + src/compiler/sema_decls.c | 26 +++++++++---------- .../macro_methods/macro_methods_no_args.c3 | 17 ++++++++++++ 3 files changed, 31 insertions(+), 13 deletions(-) create mode 100644 test/test_suite/macro_methods/macro_methods_no_args.c3 diff --git a/releasenotes.md b/releasenotes.md index 227a40c49..6aeacd863 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -15,6 +15,7 @@ - Test runner with tracking allocator didn't properly handle teardown_fn - Correctly give an error if a character literal contains a line break. - Implicitly unwrapped optional value in defer incorrectly copied #1982. +- Crash when trying to define a method macro that isn't `@construct` but has no arguments. ### Stdlib changes diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 192043adc..a897fc288 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -11,7 +11,7 @@ static inline bool sema_analyse_func_macro(SemaContext *context, Decl *decl, AttributeDomain domain, bool *erase_decl); static inline bool sema_analyse_func(SemaContext *context, Decl *decl, bool *erase_decl); static inline bool sema_analyse_macro(SemaContext *context, Decl *decl, bool *erase_decl); -static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfo *method_parent, bool is_export, bool is_deprecated, SourceSpan span); +static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfo *method_parent, bool is_export, bool is_deprecated, Decl *decl); static inline bool sema_analyse_main_function(SemaContext *context, Decl *decl); static inline bool sema_check_param_uniqueness_and_type(SemaContext *context, Decl **decls, Decl *current, unsigned current_index, unsigned count); @@ -1093,7 +1093,7 @@ ERROR: return decl_poison(decl); } -static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfo *method_parent, bool is_export, bool is_deprecated, SourceSpan span) +static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, TypeInfo *method_parent, bool is_export, bool is_deprecated, Decl *decl) { Variadic variadic_type = sig->variadic; Decl **params = sig->params; @@ -1177,6 +1177,15 @@ static inline bool sema_analyse_signature(SemaContext *context, Signature *sig, param->var.is_self = true; } + // Ensure it has at least one parameter if method. + if (method_parent && !vec_size(params) && decl->operator != OVERLOAD_CONSTRUCT) + { + RETURN_SEMA_ERROR(decl, "A method must start with an argument of the type " + "it is a method of, e.g. 'fn void %s.%s(%s* self)', " + "unless it is a 'construct' method,", + type_to_error_string(method_parent->type), decl->name, type_to_error_string(method_parent->type)); + } + // Check parameters for (unsigned i = 0; i < param_count; i++) { @@ -1352,7 +1361,7 @@ bool sema_analyse_function_signature(SemaContext *context, Decl *func_decl, Type bool deprecated = func_decl->resolved_attributes && func_decl->attrs_resolved && func_decl->attrs_resolved->deprecated; - if (!sema_analyse_signature(context, signature, parent, func_decl->is_export, deprecated, func_decl->span)) return false; + if (!sema_analyse_signature(context, signature, parent, func_decl->is_export, deprecated, func_decl)) return false; Variadic variadic_type = signature->variadic; @@ -2358,15 +2367,6 @@ static inline bool sema_analyse_method(SemaContext *context, Decl *decl) bool is_constructor = decl->operator == OVERLOAD_CONSTRUCT; - // Ensure it has at least one parameter. - if (!vec_size(params) && !is_constructor) - { - RETURN_SEMA_ERROR(decl, "A method must start with an argument of the type " - "it is a method of, e.g. 'fn void %s.%s(%s* self)', " - "unless it is a 'construct' method,", - type_to_error_string(par_type), decl->name, type_to_error_string(par_type)); - } - // Ensure that the first parameter is valid. if (!is_constructor && !sema_is_valid_method_param(context, params[0], par_type, is_dynamic)) return false; @@ -3874,7 +3874,7 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl, bool *er if (!sema_analyse_signature(context, &decl->func_decl.signature, type_infoptrzero(decl->func_decl.type_parent), - false, deprecated, decl->span)) return false; + false, deprecated, decl)) return false; DeclId body_param = decl->func_decl.body_param; if (!decl->func_decl.signature.is_at_macro && body_param && !decl->func_decl.signature.is_safemacro) diff --git a/test/test_suite/macro_methods/macro_methods_no_args.c3 b/test/test_suite/macro_methods/macro_methods_no_args.c3 new file mode 100644 index 000000000..c7d235144 --- /dev/null +++ b/test/test_suite/macro_methods/macro_methods_no_args.c3 @@ -0,0 +1,17 @@ +import std::io; +// Issue #1990 +struct Foo +{ + uint field; +} + +macro Foo.bar() // #error: A method must start with an argument +{ + io.printn("UwU"); +} + +fn void main() +{ + Foo foo = {}; + foo.bar(); +} \ No newline at end of file