- Testing for the presence of methods at the top level is prohibited previous to method registration.

This commit is contained in:
Christoffer Lerno
2025-12-24 15:32:21 +01:00
parent c205719563
commit 18b246c577
3 changed files with 52 additions and 7 deletions

View File

@@ -8,6 +8,7 @@
- Remove use of LLVMGetGlobalContext for single module compilation.
- Fixed bug where constants would get modified when slicing them. #2660
- Support for NetBSD.
- Testing for the presence of methods at the top level is prohibited previous to method registration.
### Fixes
- Regression with npot vector in struct triggering an assert #2219.

View File

@@ -178,7 +178,7 @@ static inline bool sema_create_const_min(Expr *expr, Type *type, Type *flat);
static inline bool sema_create_const_max(Expr *expr, Type *type, Type *flat);
static inline bool sema_create_const_params(Expr *expr, Type *type);
static inline void sema_create_const_membersof(Expr *expr, Type *type, AlignSize alignment, AlignSize offset);
static inline void sema_create_const_methodsof(Expr *expr, Type *type);
static inline bool sema_create_const_methodsof(SemaContext *context, Expr *expr, Type *type);
static inline bool expr_both_any_integer_or_integer_bool_vector(Expr *left, Expr *right);
static inline bool expr_both_const_foldable(Expr *left, Expr *right, BinaryOp op);
@@ -5055,7 +5055,14 @@ static inline bool sema_expr_analyse_type_access(SemaContext *context, Expr *exp
}
if (!member)
{
if (missing_ref) goto MISSING_REF;
if (missing_ref)
{
if (decl->unit->module->stage < ANALYSIS_POST_REGISTER)
{
RETURN_SEMA_ERROR(expr, "There might be a method '%s' for %s, but methods for the type have not yet been completely registered, so this yields an error.", name, type_quoted_error_string(parent_type));
}
goto MISSING_REF;
}
RETURN_SEMA_ERROR(expr, "No method or inner struct/union '%s.%s' found.", type_to_error_string(decl->type), name);
}
if (!member->unit)
@@ -5532,13 +5539,17 @@ static inline void append_extension_methods(Type *type, Decl **extensions, Expr
}
}
static inline void sema_create_const_methodsof(Expr *expr, Type *type)
static inline bool sema_create_const_methodsof(SemaContext *context, Expr *expr, Type *type)
{
Expr **method_exprs = NULL;
CONTINUE:
if (type_is_user_defined(type))
{
Decl *decl = type->decl;
if (!decl->unit || decl->unit->module->stage < ANALYSIS_POST_REGISTER)
{
RETURN_SEMA_ERROR(expr, "Methods are not fully determined for %s at this point.", decl->name);
}
// Interface, prefer interface methods.
if (decl->decl_kind == DECL_INTERFACE)
{
@@ -5555,6 +5566,7 @@ CONTINUE:
type = type_find_parent_type(type);
if (type) goto CONTINUE;
expr_rewrite_const_untyped_list(expr, method_exprs);
return true;
}
@@ -6033,8 +6045,7 @@ static bool sema_expr_rewrite_to_type_property(SemaContext *context, Expr *expr,
return true;
}
case TYPE_PROPERTY_METHODSOF:
sema_create_const_methodsof(expr, type);
return true;
return sema_create_const_methodsof(context, expr, type);
case TYPE_PROPERTY_PARAMSOF:
return sema_create_const_paramsof(expr, flat);
case TYPE_PROPERTY_PARAMS:
@@ -6563,10 +6574,16 @@ CHECK_DEEPER:
if (missing_ref) goto MISSING_REF;
RETURN_SEMA_ERROR(expr, "The method '%s' has private visibility.", kw);
}
if (parent->type->canonical->type_kind == TYPE_INTERFACE)
Type *parent_type = parent->type->canonical;
ASSERT(type_is_user_defined(parent_type));
if (missing_ref && parent_type->decl->unit->module->stage < ANALYSIS_POST_REGISTER)
{
RETURN_SEMA_ERROR(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 (parent_type->type_kind == TYPE_INTERFACE)
{
if (missing_ref) goto MISSING_REF;
RETURN_SEMA_ERROR(expr, "The '%s' interface has no method '%s', did you spell it correctly?", parent->type->canonical->name, kw);
RETURN_SEMA_ERROR(expr, "The '%s' interface has no method '%s', did you spell it correctly?", parent_type->name, kw);
}
if (missing_ref) goto MISSING_REF;
RETURN_SEMA_ERROR(expr, "There is no field or method '%s.%s'.", type_to_error_string(parent->type), kw);

View File

@@ -0,0 +1,27 @@
module test2;
import test3;
struct Bar @if($defined(Foo.b)) // #error: "There might be a method 'b' for 'Foo', but methods for the type have not yet been completely registered, so this yields an error.
{
int a;
}
struct Bar2 @if($defined((Foo){}.b)) // #error: There might be a method 'b' for 'Foo', but methods have not yet been completely registered, so analysis fails.
{
int a;
}
module test3;
struct Foo
{
int a;
}
module test;
import test2;
import test3;
fn int main()
{
Bar b;
return 0;
}