From 652456646fd2a1c1c9c87e8c300fefb0ae8b1f5d Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 22 Nov 2024 16:40:33 +0100 Subject: [PATCH] Prevent methods from using names of properties or fields. #1638 --- releasenotes.md | 1 + src/compiler/compiler_internal.h | 8 +- src/compiler/sema_decls.c | 99 +++++++++++++++++++ src/compiler/sema_expr.c | 16 +-- src/compiler/sema_internal.h | 11 ++- src/compiler/sema_name_resolution.c | 17 ++-- .../methods/method_name_collision.c3 | 7 ++ 7 files changed, 138 insertions(+), 21 deletions(-) create mode 100644 test/test_suite/methods/method_name_collision.c3 diff --git a/releasenotes.md b/releasenotes.md index d08efcbc5..6feffa5d8 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -21,6 +21,7 @@ - Indexing an Optional slice would crash in codegen #1636. - SimpleHeapAllocator bug when splitting blocks allowed memory overrun. - Not possible to alias or take reference for extension methods on non-user defined types. #1637 +- Prevent methods from using names of properties or fields. #1638 ### Stdlib changes - Add `io::MultiReader`, `io::MultiWriter`, and `io::TeeReader` structs. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 90c48bb34..4f90aa63e 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2255,9 +2255,15 @@ bool parse_stdin(void); Path *path_create_from_string(const char *string, uint32_t len, SourceSpan span); +typedef enum FindMember +{ + METHODS_AND_FIELDS, + FIELDS_ONLY +} FindMember; + void sema_analysis_run(void); Decl **sema_decl_stack_store(void); -Decl *sema_decl_stack_find_decl_member(SemaContext *context, Decl *decl_owner, const char *symbol); +Decl *sema_decl_stack_find_decl_member(SemaContext *context, Decl *decl_owner, const char *symbol, FindMember find); Decl *sema_decl_stack_resolve_symbol(const char *symbol); void sema_decl_stack_restore(Decl **state); void sema_decl_stack_push(Decl *decl); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 485edb548..494808e8d 100755 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -2212,6 +2212,103 @@ static inline bool sema_analyse_method(SemaContext *context, Decl *decl) // Resolve declaration of parent as needed. if (!sema_resolve_type_decl(context, par_type)) return false; + const char *kw = decl->name; + const char *errname = NULL; + switch (par_type->canonical->type_kind) + { + case TYPE_ENUM: + if (kw == kw_ordinal || kw == kw_nameof) + { + errname = "an enum"; + goto NOT_VALID_NAME; + } + FALLTHROUGH; + case TYPE_STRUCT: + case TYPE_UNION: + { + Decl *d = sema_decl_stack_find_decl_member(context, par_type->canonical->decl, kw, FIELDS_ONLY); + if (!decl_ok(d)) return false; + if (d) RETURN_SEMA_ERROR(decl, "%s already has a field with the same name.", type_quoted_error_string(par_type)); + break; + } + case TYPE_FAULTTYPE: + if (kw == kw_ordinal || kw == kw_nameof) + { + errname = "a fault"; + goto NOT_VALID_NAME; + } + break; + case TYPE_ANYFAULT: + if (kw == kw_type || kw == kw_nameof) + { + errname = "'anyfault'"; + goto NOT_VALID_NAME; + } + break; + case TYPE_ANY: + case TYPE_INTERFACE: + if (kw == kw_ptr || kw == kw_type) + { + errname = "an interface or 'any'"; + goto NOT_VALID_NAME; + } + break; + case TYPE_SLICE: + if (kw == kw_ptr || kw == kw_len) + { + errname = "a slice"; + goto NOT_VALID_NAME; + } + break; + case TYPE_VECTOR: + case TYPE_INFERRED_VECTOR: + { + unsigned len = strlen(decl->name); + if (len <= 4) + { + for (unsigned i = 0; i < len; i++) + { + if (!swizzle[(int) decl->name[i]]) goto NEXT; + } + RETURN_SEMA_ERROR(decl, "\"%s\" is not a valid method name for a vector, since it matches a swizzle combination.", kw); + } + } + NEXT:; + FALLTHROUGH; + case TYPE_ARRAY: + case TYPE_INFERRED_ARRAY: + case TYPE_FLEXIBLE_ARRAY: + if (kw == kw_len) + { + errname = "a vector or array"; + goto NOT_VALID_NAME; + } + break; + case TYPE_POISONED: + case TYPE_VOID: + case ALL_FLOATS: + case ALL_INTS: + case TYPE_BOOL: + case TYPE_FUNC_PTR: + case TYPE_POINTER: + case TYPE_FUNC_RAW: + case TYPE_UNTYPED_LIST: + case TYPE_BITSTRUCT: + case TYPE_DISTINCT: + break; + case TYPE_TYPEID: + if (type_property_by_name(kw) != TYPE_PROPERTY_NONE) + { + RETURN_SEMA_ERROR(decl, "\"%s\" is not a valid method name for a typeid as this is the name of a type property.", decl->name); + } + break; + case TYPE_TYPEDEF: + case TYPE_OPTIONAL: + case TYPE_WILDCARD: + case TYPE_TYPEINFO: + case TYPE_MEMBER: + UNREACHABLE + } Decl **params = decl->func_decl.signature.params; bool is_dynamic = decl->func_decl.attr_dynamic; @@ -2257,6 +2354,8 @@ static inline bool sema_analyse_method(SemaContext *context, Decl *decl) } return true; +NOT_VALID_NAME: + RETURN_SEMA_ERROR(decl, "\"%s\" is not a valid method name for %s, as this would shadow the built-in property '.%s'.", kw, errname, kw); } static const char *attribute_domain_to_string(AttributeDomain domain) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 6eaab8e73..8be112e42 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -3739,7 +3739,7 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp UNREACHABLE } - Decl *member = sema_decl_stack_find_decl_member(context, decl, name); + Decl *member = sema_decl_stack_find_decl_member(context, decl, name, METHODS_AND_FIELDS); if (!decl_ok(member)) return false; if (!member) { @@ -3859,7 +3859,7 @@ static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *e } Decl *underlying_type_decl = underlying_type->decl; - Decl *member = sema_decl_stack_find_decl_member(context, underlying_type_decl, name); + Decl *member = sema_decl_stack_find_decl_member(context, underlying_type_decl, name, METHODS_AND_FIELDS); if (!decl_ok(member)) return false; if (!member || !(decl_is_struct_type(member) || member->decl_kind == DECL_VAR || member->decl_kind == DECL_BITSTRUCT)) { @@ -5012,7 +5012,7 @@ static inline bool sema_expr_analyse_access(SemaContext *context, Expr *expr, bo // 10. Dump all members and methods into a decl stack. Decl *decl = type->decl; - Decl *member = sema_decl_stack_find_decl_member(context, decl, kw); + Decl *member = sema_decl_stack_find_decl_member(context, decl, kw, METHODS_AND_FIELDS); if (!decl_ok(member)) return false; if (member && decl_is_enum_kind(decl) && member->decl_kind == DECL_VAR && sema_cast_const(parent)) { @@ -7982,7 +7982,7 @@ static inline bool sema_expr_analyse_decl_element(SemaContext *context, Designat } return false; } - Decl *member = sema_decl_stack_find_decl_member(context, actual_type->decl, kw); + Decl *member = sema_decl_stack_find_decl_member(context, actual_type->decl, kw, METHODS_AND_FIELDS); if (!decl_ok(member)) return false; if (!member) { @@ -9134,14 +9134,6 @@ static inline BuiltinFunction builtin_by_name(const char *name) return BUILTIN_NONE; } -static inline TypeProperty type_property_by_name(const char *name) -{ - for (unsigned i = 0; i < NUMBER_OF_TYPE_PROPERTIES; i++) - { - if (type_property_list[i] == name) return (TypeProperty)i; - } - return TYPE_PROPERTY_NONE; -} static inline bool sema_expr_analyse_retval(SemaContext *context, Expr *expr) { diff --git a/src/compiler/sema_internal.h b/src/compiler/sema_internal.h index cdbc331f7..d34e1f657 100644 --- a/src/compiler/sema_internal.h +++ b/src/compiler/sema_internal.h @@ -242,4 +242,13 @@ static inline StorageType sema_resolve_storage_type(SemaContext *context, Type * default: return STORAGE_NORMAL; } -} \ No newline at end of file +} + +static inline TypeProperty type_property_by_name(const char *name) +{ + for (unsigned i = 0; i < NUMBER_OF_TYPE_PROPERTIES; i++) + { + if (type_property_list[i] == name) return (TypeProperty)i; + } + return TYPE_PROPERTY_NONE; +} diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 2583dcc48..ff278ff2b 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -80,11 +80,14 @@ static bool add_interface_to_decl_stack(SemaContext *context, Decl *decl) return true; } -static bool add_members_to_decl_stack(SemaContext *context, Decl *decl) +static bool add_members_to_decl_stack(SemaContext *context, Decl *decl, FindMember find) { - FOREACH(Decl *, func, decl->methods) + if (find != FIELDS_ONLY) { - sema_decl_stack_push(func); + FOREACH(Decl *, func, decl->methods) + { + sema_decl_stack_push(func); + } } while (decl->decl_kind == DECL_DISTINCT) { @@ -96,7 +99,7 @@ static bool add_members_to_decl_stack(SemaContext *context, Decl *decl) { FOREACH(Decl *, member, decl->enums.parameters) sema_decl_stack_push(member); } - if (decl->decl_kind == DECL_INTERFACE) + if (decl->decl_kind == DECL_INTERFACE && find != FIELDS_ONLY) { if (!add_interface_to_decl_stack(context, decl)) return false; } @@ -106,7 +109,7 @@ static bool add_members_to_decl_stack(SemaContext *context, Decl *decl) { if (member->name == NULL) { - if (!add_members_to_decl_stack(context, member)) return false; + if (!add_members_to_decl_stack(context, member, find)) return false; continue; } sema_decl_stack_push(member); @@ -115,10 +118,10 @@ static bool add_members_to_decl_stack(SemaContext *context, Decl *decl) return true; } -Decl *sema_decl_stack_find_decl_member(SemaContext *context, Decl *decl_owner, const char *symbol) +Decl *sema_decl_stack_find_decl_member(SemaContext *context, Decl *decl_owner, const char *symbol, FindMember find) { Decl **state = sema_decl_stack_store(); - if (!add_members_to_decl_stack(context, decl_owner)) return poisoned_decl; + if (!add_members_to_decl_stack(context, decl_owner, find)) return poisoned_decl; Decl *member = sema_decl_stack_resolve_symbol(symbol); sema_decl_stack_restore(state); return member; diff --git a/test/test_suite/methods/method_name_collision.c3 b/test/test_suite/methods/method_name_collision.c3 new file mode 100644 index 000000000..b12d33230 --- /dev/null +++ b/test/test_suite/methods/method_name_collision.c3 @@ -0,0 +1,7 @@ +module testc3; + +fn bool any.type(self) { // #error: is not a valid method name +return true; +} + +fn void main() {}