diff --git a/releasenotes.md b/releasenotes.md index d0809ceba..cd84bd2ba 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -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. diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 3f514afa4..03cfafa3c 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -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); diff --git a/test/test_suite/methods/method_resolution_order.c3 b/test/test_suite/methods/method_resolution_order.c3 new file mode 100644 index 000000000..6d5088d72 --- /dev/null +++ b/test/test_suite/methods/method_resolution_order.c3 @@ -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; +} \ No newline at end of file