Skip to content

Commit

Permalink
Prevent methods from using names of properties or fields. #1638
Browse files Browse the repository at this point in the history
  • Loading branch information
lerno committed Nov 22, 2024
1 parent ca0dc49 commit 6524566
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 21 deletions.
1 change: 1 addition & 0 deletions releasenotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 7 additions & 1 deletion src/compiler/compiler_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
99 changes: 99 additions & 0 deletions src/compiler/sema_decls.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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)
Expand Down
16 changes: 4 additions & 12 deletions src/compiler/sema_expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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))
{
Expand Down Expand Up @@ -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))
{
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand Down
11 changes: 10 additions & 1 deletion src/compiler/sema_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
17 changes: 10 additions & 7 deletions src/compiler/sema_name_resolution.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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;
}
Expand All @@ -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);
Expand All @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions test/test_suite/methods/method_name_collision.c3
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module testc3;

fn bool any.type(self) { // #error: is not a valid method name
return true;
}

fn void main() {}

0 comments on commit 6524566

Please sign in to comment.