Method resolution and $define now works together well unless definitions are out of order for real.

This commit is contained in:
Christoffer Lerno
2026-02-04 12:23:37 +01:00
parent 6bc606a9b1
commit 1c8cb7fa11
6 changed files with 44 additions and 20 deletions

View File

@@ -3,6 +3,7 @@
## 0.7.10 Change list
### Changes / improvements
- Method resolution and `$define` now works together well unless definitions are out of order for real.
### Stdlib changes
- Summarize sort macros as generic function wrappers to reduce the amount of generated code. #2831

View File

@@ -720,6 +720,7 @@ typedef struct Decl_
bool attr_structlike : 1;
bool is_template : 1;
bool is_templated : 1;
bool is_method_checked : 1;
union
{
void *backend_ref;
@@ -2016,6 +2017,7 @@ typedef struct
HTable features;
Module std_module;
MethodTable method_extensions;
Type **types_with_failed_methods;
Decl **method_extension_list;
DeclTable symbols;
PathTable path_symbols;

View File

@@ -13,7 +13,7 @@ static inline bool sema_check_param_uniqueness_and_type(SemaContext *context, De
static inline bool sema_analyse_method(SemaContext *context, Decl *decl);
static inline bool sema_is_valid_method_param(SemaContext *context, Decl *param, Type *parent_type, bool is_dynamic);
static inline bool sema_analyse_macro_method(SemaContext *context, Decl *decl);
static inline bool unit_add_base_extension_method(SemaContext *context, Decl *method);
static inline bool unit_add_base_extension_method(SemaContext *context, Type *type, Decl *method);
static inline bool type_add_method(SemaContext *context, Type *parent_type, Decl *method);
static bool sema_analyse_operator_common(SemaContext *context, Decl *method, TypeInfo **rtype_ptr, Decl ***params_ptr, uint32_t parameters);
static inline bool sema_analyse_operator_element_at(SemaContext *context, Decl *method);
@@ -2295,7 +2295,7 @@ INLINE SourceSpan method_find_overload_span(Decl *method)
return method->attrs_resolved->overload;
}
static inline bool unit_add_base_extension_method(SemaContext *context, Decl *method)
static inline bool unit_add_base_extension_method(SemaContext *context, Type *type, Decl *method)
{
Decl *other = declptrzero(methodtable_set(&compiler.context.method_extensions, method));
if (other)
@@ -2304,6 +2304,13 @@ static inline bool unit_add_base_extension_method(SemaContext *context, Decl *me
SEMA_NOTE(other, "The previous definition was here.");
return false;
}
FOREACH(Type *, t, compiler.context.types_with_failed_methods)
{
if (t == type)
{
RETURN_SEMA_ERROR(method, "A method was added to %s which already was checked for method availability, declarations must be reordered.", type_quoted_error_string(type));
}
}
vec_add(compiler.context.method_extension_list, method);
DEBUG_LOG("Builtin type method '%s' analysed.", method->name);
return true;
@@ -2636,7 +2643,7 @@ static inline bool type_add_method(SemaContext *context, Type *parent_type, Decl
return true;
}
// Is it a base extension?
if (!type_is_user_defined(parent_type)) return unit_add_base_extension_method(context, method);
if (!type_is_user_defined(parent_type)) return unit_add_base_extension_method(context, parent_type, method);
// Resolve it as a user-defined type extension.
Decl *parent = parent_type->decl;
@@ -2653,7 +2660,10 @@ static inline bool type_add_method(SemaContext *context, Type *parent_type, Decl
SEMA_NOTE(other, "The previous definition was here.");
return decl_poison(method);
}
if (parent->is_method_checked)
{
RETURN_SEMA_ERROR(method, "A method was added to %s which already was checked for method availability, declarations must be reordered.", type_quoted_error_string(parent_type));
}
DEBUG_LOG("Method-like '%s.%s' analysed.", parent->name, method->name);
Methods *table = parent->method_table;

View File

@@ -5059,7 +5059,11 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp
if (!decl_ok(member)) return false;
if (!member)
{
if (missing_ref) goto MISSING_REF;
if (missing_ref)
{
vec_add(compiler.context.types_with_failed_methods, parent_type);
goto MISSING_REF;
}
RETURN_SEMA_ERROR(expr, "'%s' does not have a property or method '%s'.", type_to_error_string(parent_type), name);
}
expr_resolve_ident(expr, member);
@@ -5106,8 +5110,7 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp
{
if (decl->unit->module->stage < ANALYSIS_POST_REGISTER)
{
bool err = SEMA_WARN_STRICT(expr, "There might be a method '%s' for %s, but methods for the type have not yet been completely registered, so a warning is issued.", name, type_quoted_error_string(parent_type));
if (err) return false;
decl->is_method_checked = true;
}
goto MISSING_REF;
}
@@ -5205,6 +5208,8 @@ static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *e
sema_create_const_membersof(expr, decl->type->canonical, parent->const_expr.member.align, parent->const_expr.member.offset);
return true;
case TYPE_PROPERTY_METHODSOF:
decl->is_method_checked = true;
FALLTHROUGH;
case TYPE_PROPERTY_KINDOF:
case TYPE_PROPERTY_SIZEOF:
return sema_expr_rewrite_to_type_property(context, expr, decl->type->canonical, type_property, decl->type->canonical);
@@ -5260,6 +5265,7 @@ static inline bool sema_expr_analyse_member_access(SemaContext *context, Expr *e
return true;
MISSING_REF:
*missing_ref = true;
decl->is_method_checked = true;
return false;
}
@@ -6551,7 +6557,11 @@ CHECK_DEEPER:
Decl *method = sema_resolve_type_method(context, type, kw);
if (!method)
{
if (missing_ref) goto MISSING_REF;
if (missing_ref)
{
vec_add(compiler.context.types_with_failed_methods, type);
goto MISSING_REF;
}
RETURN_SEMA_ERROR(expr, "There is no member or method '%s' on %s", kw, type_quoted_error_string(parent->type));
}
@@ -6608,6 +6618,13 @@ CHECK_DEEPER:
// 11. If we didn't find a match...
if (!member)
{
Type *parent_type = type_no_optional(parent->type)->canonical;
ASSERT(type_is_user_defined(parent_type));
// Tag as maybe wrong.
if (!private && missing_ref && parent_type->decl->unit->module->stage < ANALYSIS_POST_REGISTER)
{
parent_type->decl->is_method_checked = true;
}
// 11a. We have a potential embedded struct check:
Expr *substruct = sema_enter_inline_member(current_parent, type);
if (substruct)
@@ -6625,13 +6642,7 @@ CHECK_DEEPER:
if (missing_ref) goto MISSING_REF;
RETURN_SEMA_ERROR(expr, "The method '%s' has private visibility.", kw);
}
Type *parent_type = type_no_optional(parent->type)->canonical;
ASSERT(type_is_user_defined(parent_type));
if (missing_ref && parent_type->decl->unit->module->stage < ANALYSIS_POST_REGISTER)
{
bool err = SEMA_WARN_STRICT(expr, "There might be a method '%s' for %s, but methods have not yet been completely registered, so analysis fails.", kw, type_quoted_error_string(parent->type));
if (err) return false;
}
if (parent_type->type_kind == TYPE_INTERFACE)
{
if (missing_ref) goto MISSING_REF;
@@ -6666,7 +6677,6 @@ CHECK_DEEPER:
}
}
// 13. Copy properties.
expr->access_resolved_expr = (ExprResolvedAccess) { .parent = current_parent, .ref = member };
if (expr->expr_kind == EXPR_ACCESS_UNRESOLVED) expr->expr_kind = EXPR_ACCESS_RESOLVED;
expr->type = type_add_optional(member->type, optional);

View File

@@ -18,7 +18,6 @@
#define UINT20_MAX 1048575U
#define SEMA_WARN(_node, ...) (sema_warn_at(context, (_node)->span, __VA_ARGS__))
#define SEMA_WARN_STRICT(_node, ...) (sema_warn_very_strict(context, (_node)->span, __VA_ARGS__))
#define SEMA_ERROR(_node, ...) sema_error_at(context, (_node)->span, __VA_ARGS__)
#define RETURN_SEMA_ERROR(_node, ...) do { sema_error_at(context, (_node)->span, __VA_ARGS__); return false; } while (0)
#define RETURN_VAL_SEMA_ERROR(val__, _node, ...) do { sema_error_at(context, (_node)->span, __VA_ARGS__); return (val__); } while (0)

View File

@@ -1,11 +1,11 @@
module test2;
import test3;
struct Bar @if($defined(Foo.b)) // warning: There might be a method 'b' for 'Foo', but methods for the type have not yet been
struct Bar @if($defined(Foo.b))
{
int a;
}
struct Bar2 @if($defined((Foo){}.b)) // warning: There might be a method 'b' for 'Foo', but methods have not yet been completely
struct Bar2 @if($defined((Foo){}.b))
{
int a;
}
@@ -15,6 +15,8 @@ struct Foo
{
int a;
}
fn void Foo.b(&self) {} // #error: A method was added to 'Foo' which already was checked for method availability
module test;
import test2;
@@ -22,6 +24,6 @@ import test3;
fn int main()
{
Bar b; // #error: 'Bar' could not be found, did you spell it right
Bar b;
return 0;
}