Prevent methods from using names of properties or fields. #1638

This commit is contained in:
Christoffer Lerno
2024-11-22 16:40:33 +01:00
parent ca0dc49f64
commit 652456646f
7 changed files with 138 additions and 21 deletions

View File

@@ -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.

View File

@@ -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);

View File

@@ -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)

View File

@@ -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 @@ CHECK_DEEPER:
// 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)
{

View File

@@ -242,4 +242,13 @@ static inline StorageType sema_resolve_storage_type(SemaContext *context, Type *
default:
return STORAGE_NORMAL;
}
}
}
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;
}

View File

@@ -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;

View File

@@ -0,0 +1,7 @@
module testc3;
fn bool any.type(self) { // #error: is not a valid method name
return true;
}
fn void main() {}