From 919b9e1afcdc65cea25f0c6b4ca7362f5279f0ff Mon Sep 17 00:00:00 2001 From: Akuli Date: Mon, 5 Jan 2026 23:14:12 +0200 Subject: [PATCH 1/4] Introduce classdata.definition --- compiler/typecheck/step1_global_symbols.jou | 1 + compiler/types.jou | 1 + 2 files changed, 2 insertions(+) diff --git a/compiler/typecheck/step1_global_symbols.jou b/compiler/typecheck/step1_global_symbols.jou index 8efeb3a5c..734b513a7 100644 --- a/compiler/typecheck/step1_global_symbols.jou +++ b/compiler/typecheck/step1_global_symbols.jou @@ -125,6 +125,7 @@ def typecheck_statement_with_symbol(stmt: AstStatement*) -> None: stmt.symbol().signature = typecheck_signature(jou_file, &stmt.method, self_class) case AstStatementKind.Class: t = create_empty_class(stmt.symbol().name) + t.classdata.definition = stmt if stmt.classdef.is_generic(): for nameptr = stmt.classdef.generic_typevar_names.ptr; nameptr < stmt.classdef.generic_typevar_names.end(); nameptr++: t.classdata.generic_params.append(create_typevar(*nameptr)) diff --git a/compiler/types.jou b/compiler/types.jou index b8364b0d4..80b07a069 100644 --- a/compiler/types.jou +++ b/compiler/types.jou @@ -21,6 +21,7 @@ class ClassField: class ClassData: generic_params: List[Type*] # this is the "int" part of List[int] fields: List[ClassField] + definition: AstStatement* @public def is_generic(self) -> bool: From 6e3ad3e4835c66f1490356febf92b6281c19b54b Mon Sep 17 00:00:00 2001 From: Akuli Date: Tue, 6 Jan 2026 00:03:31 +0200 Subject: [PATCH 2/4] Replace the horrible method not found error message with 3 separate errors --- .../step3_function_and_method_bodies.jou | 59 ++++++++++++++----- tests/404/import_private_method.jou | 2 +- tests/404/indirect_import_method.jou | 3 +- tests/404/method.jou | 2 +- 4 files changed, 47 insertions(+), 19 deletions(-) diff --git a/compiler/typecheck/step3_function_and_method_bodies.jou b/compiler/typecheck/step3_function_and_method_bodies.jou index b61e6f690..49f11de13 100644 --- a/compiler/typecheck/step3_function_and_method_bodies.jou +++ b/compiler/typecheck/step3_function_and_method_bodies.jou @@ -629,6 +629,49 @@ def nth(n: int) -> byte[100]: return result +def fail_with_method_not_found( + state: State*, + location: Location, + self_class: Type*, + method_name: byte*, +) -> noreturn: + assert self_class.kind == TypeKind.Class + class_ast = self_class.classdata.definition + assert class_ast != NULL + assert class_ast.kind == AstStatementKind.Class + + msg: byte[500] + + # Is the method in the class definition? + for stmt = class_ast.classdef.body.ptr; stmt < class_ast.classdef.body.end(); stmt++: + if ( + stmt.kind == AstStatementKind.MethodDef + and strcmp(stmt.method.ast_signature.name, method_name) == 0 + and strcmp(stmt.location.path, state.jou_file.path) != 0 + ): + # Method exists + if stmt.symbol().public: + # TODO: generate something that can be copy/pasted to become an import? + snprintf( + msg, sizeof(msg), + "to use the '%s' method of class %s, you need to import the file that defines it (%s)", + method_name, + self_class.name, + stmt.location.path, + ) + else: + snprintf( + msg, sizeof(msg), + "method '%s' of class %s is private (try adding @public)", + method_name, + self_class.name, + ) + fail(location, msg) + + snprintf(msg, sizeof(msg), "class %s has no method named '%s'", self_class.name, method_name) + fail(location, msg) + + # This is for code that looks like obj.member(). It could be a method call or a # function pointer call, because `obj` may have a field whose type is funcptr. # @@ -684,21 +727,7 @@ def typecheck_member_that_looks_like_method(state: State*, call: AstCall*) -> Ty field = self_class.find_class_field(member_name) if field == NULL: - # TODO: better error messages!!!!! - big_msg: byte* = NULL - asprintf( - &big_msg, - "method '%s' not found in class %s. The problem is ONE of the following: 1) you didn't import the file that defines class %.*s, 2) the '%s' method is private, 3) there really is no such method in class %.*s. I know that this is a bad compiler error message, and I will later split this into 3 different error messages.", - member_name, - self_class.name, - strcspn(self_class.name, "[") as int, # List[int] --> List - self_class.name, - member_name, - strcspn(self_class.name, "[") as int, # List[int] --> List - self_class.name, - ) - assert big_msg != NULL - fail(func_location, big_msg) + fail_with_method_not_found(state, func_location, self_class, member_name) # It is a function pointer call where the function is in a class field. # It looks like obj.field(). diff --git a/tests/404/import_private_method.jou b/tests/404/import_private_method.jou index cacf03eea..b59397bba 100644 --- a/tests/404/import_private_method.jou +++ b/tests/404/import_private_method.jou @@ -3,4 +3,4 @@ import "./imported/privates.jou" def foo() -> None: p = PublicClass{} # TODO: ideally error message would say that it exists but is private - p.private_method() # Error: method 'private_method' not found in class PublicClass. The problem is ONE of the following: 1) you didn't import the file that defines class PublicClass, 2) the 'private_method' method is private, 3) there really is no such method in class PublicClass. I know that this is a bad compiler error message, and I will later split this into 3 different error messages. + p.private_method() # Error: method 'private_method' of class PublicClass is private (try adding @public) diff --git a/tests/404/indirect_import_method.jou b/tests/404/indirect_import_method.jou index 277e51ac7..aba5d1db7 100644 --- a/tests/404/indirect_import_method.jou +++ b/tests/404/indirect_import_method.jou @@ -2,5 +2,4 @@ import "./imported/point_factory.jou" def blah() -> int: p = make_point() - # TODO: Improve this error! - return p.get_sum() # Error: method 'get_sum' not found in class Point. The problem is ONE of the following: 1) you didn't import the file that defines class Point, 2) the 'get_sum' method is private, 3) there really is no such method in class Point. I know that this is a bad compiler error message, and I will later split this into 3 different error messages. + return p.get_sum() # Error: to use the 'get_sum' method of class Point, you need to import the file that defines it (tests/404/imported/point.jou) diff --git a/tests/404/method.jou b/tests/404/method.jou index 8e850d5d0..62306f051 100644 --- a/tests/404/method.jou +++ b/tests/404/method.jou @@ -3,4 +3,4 @@ class Foo: def main() -> int: f = Foo{x=1} - f.bar() # Error: method 'bar' not found in class Foo. The problem is ONE of the following: 1) you didn't import the file that defines class Foo, 2) the 'bar' method is private, 3) there really is no such method in class Foo. I know that this is a bad compiler error message, and I will later split this into 3 different error messages. + f.bar() # Error: class Foo has no method named 'bar' From 59e3ffab24d93231333aa08b401039b3ee51dcee Mon Sep 17 00:00:00 2001 From: Akuli Date: Tue, 6 Jan 2026 00:27:43 +0200 Subject: [PATCH 3/4] tweak message --- compiler/typecheck/step3_function_and_method_bodies.jou | 2 +- tests/404/import_private_method.jou | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/typecheck/step3_function_and_method_bodies.jou b/compiler/typecheck/step3_function_and_method_bodies.jou index 49f11de13..b2265a828 100644 --- a/compiler/typecheck/step3_function_and_method_bodies.jou +++ b/compiler/typecheck/step3_function_and_method_bodies.jou @@ -662,7 +662,7 @@ def fail_with_method_not_found( else: snprintf( msg, sizeof(msg), - "method '%s' of class %s is private (try adding @public)", + "method '%s' of class %s is private (maybe add @public to it?)", method_name, self_class.name, ) diff --git a/tests/404/import_private_method.jou b/tests/404/import_private_method.jou index b59397bba..f37c82557 100644 --- a/tests/404/import_private_method.jou +++ b/tests/404/import_private_method.jou @@ -3,4 +3,4 @@ import "./imported/privates.jou" def foo() -> None: p = PublicClass{} # TODO: ideally error message would say that it exists but is private - p.private_method() # Error: method 'private_method' of class PublicClass is private (try adding @public) + p.private_method() # Error: method 'private_method' of class PublicClass is private (maybe add @public to it?) From bac8e5df794924c283b2480d61c1efcd3c4bb41b Mon Sep 17 00:00:00 2001 From: Akuli Date: Tue, 6 Jan 2026 00:29:06 +0200 Subject: [PATCH 4/4] Update compiler/typecheck/step3_function_and_method_bodies.jou --- compiler/typecheck/step3_function_and_method_bodies.jou | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/typecheck/step3_function_and_method_bodies.jou b/compiler/typecheck/step3_function_and_method_bodies.jou index b2265a828..9d63b25ac 100644 --- a/compiler/typecheck/step3_function_and_method_bodies.jou +++ b/compiler/typecheck/step3_function_and_method_bodies.jou @@ -651,7 +651,7 @@ def fail_with_method_not_found( ): # Method exists if stmt.symbol().public: - # TODO: generate something that can be copy/pasted to become an import? + # TODO: generate something that can be copy/pasted to write an import statement? snprintf( msg, sizeof(msg), "to use the '%s' method of class %s, you need to import the file that defines it (%s)",