From c7f09f287993db4b60d16852ca97c50821bba1f2 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 31 Aug 2025 15:16:52 +0200 Subject: [PATCH] Disambiguate types when they have the same name and need cast between each other. --- releasenotes.md | 1 + src/compiler/compiler_internal.h | 3 + src/compiler/sema_casts.c | 14 +-- src/compiler/types.c | 94 +++++++++++++++++++ .../cast/implicit_cast_same_name.c3 | 15 +++ 5 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 test/test_suite/cast/implicit_cast_same_name.c3 diff --git a/releasenotes.md b/releasenotes.md index a5c22aaa4..af4ab6201 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -88,6 +88,7 @@ - Deprecated `@select` in favor of `???`. - Enum inference, like `Foo x = $eval("A")`, now works correctly for `$eval`. - Fix regression where files were added more than once. #2442 +- Disambiguate types when they have the same name and need cast between each other. ### Stdlib changes - Add `==` to `Pair`, `Triple` and TzDateTime. Add print to `Pair` and `Triple`. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 68c96eed4..27690bf87 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -2584,6 +2584,9 @@ FunctionPrototype *type_get_resolved_prototype(Type *type); bool type_is_inner_type(Type *type); const char *type_to_error_string(Type *type); const char *type_quoted_error_string(Type *type); +const char *type_quoted_error_string_with_path(Type *type); +const char *type_error_string_maybe_with_path(Type *type, Type *other_type); +const char *type_quoted_error_string_maybe_with_path(Type *type, Type *other_type); INLINE bool type_may_negate(Type *type); INLINE bool type_is_builtin(TypeKind kind); INLINE bool type_convert_will_trunc(Type *destination, Type *source); diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 1b76966c8..063fadba6 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -727,21 +727,23 @@ static bool report_cast_error(CastContext *cc, bool may_cast_explicit) } if (may_cast_explicit) { + Type *typeto = type_no_optional(to); + Type *from = type_no_optional(expr->type); if (expr->type->canonical->type_kind == TYPE_DISTINCT && type_no_optional(to)->canonical->type_kind == TYPE_DISTINCT) { RETURN_CAST_ERROR(expr, "Implicitly casting %s to %s is not permitted. It's possible to do an explicit cast by placing '(%s)' before the expression. However, explicit casts between distinct types are usually not intended and are not safe.", - type_quoted_error_string(type_no_optional(expr->type)), - type_quoted_error_string(to), - type_to_error_string(type_no_optional(to))); + type_quoted_error_string_maybe_with_path(from, typeto), + type_quoted_error_string_maybe_with_path(to, from), + type_error_string_maybe_with_path(typeto, from)); } RETURN_CAST_ERROR(expr, "Implicitly casting %s to %s is not permitted, but you may do an explicit cast by placing '(%s)' before the expression.", - type_quoted_error_string(type_no_optional(expr->type)), - type_quoted_error_string(to), - type_to_error_string(type_no_optional(to))); + type_quoted_error_string_maybe_with_path(from, typeto), + type_quoted_error_string_maybe_with_path(to, from), + type_error_string_maybe_with_path(typeto, from)); } if (to->type_kind == TYPE_INTERFACE) { diff --git a/src/compiler/types.c b/src/compiler/types.c index fad03941d..5cfedae8b 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -6,6 +6,7 @@ static Type *flatten_raw_function_type(Type *type); +static const char *type_to_error_string_with_path(Type *type); static struct { @@ -99,6 +100,18 @@ Type *type_int_unsigned_by_bitsize(BitSize bit_size) } } +const char *type_quoted_error_string_maybe_with_path(Type *type, Type *other_type) +{ + if (!str_eq(type_no_optional(type)->name, type_no_optional(other_type)->name)) return type_quoted_error_string(type); + return type_quoted_error_string_with_path(type); +} + +const char *type_error_string_maybe_with_path(Type *type, Type *other_type) +{ + if (!str_eq(type_no_optional(type)->name, type_no_optional(other_type)->name)) return type_to_error_string(type); + return type_to_error_string_with_path(type); +} + const char *type_quoted_error_string(Type *type) { if (type->canonical != type) @@ -108,6 +121,15 @@ const char *type_quoted_error_string(Type *type) return str_printf("'%s'", type_to_error_string(type)); } +const char *type_quoted_error_string_with_path(Type *type) +{ + if (type->canonical != type) + { + return str_printf("'%s' (%s)", type_to_error_string_with_path(type), type_to_error_string_with_path(type->canonical)); + } + return str_printf("'%s'", type_to_error_string_with_path(type)); +} + void type_append_name_to_scratch(Type *type) { type = type->canonical; @@ -234,6 +256,7 @@ static void type_add_parent_to_scratch(Decl *decl) return; } } + const char *type_to_error_string(Type *type) { switch (type->type_kind) @@ -301,6 +324,77 @@ const char *type_to_error_string(Type *type) UNREACHABLE } +static const char *type_to_error_string_with_path(Type *type) +{ + switch (type->type_kind) + { + case TYPE_POISONED: + return "poisoned"; + case TYPE_VOID: + case TYPE_BOOL: + case ALL_INTS: + case ALL_FLOATS: + case TYPE_ANYFAULT: + case TYPE_UNTYPED_LIST: + case TYPE_ANY: + case TYPE_MEMBER: + case TYPE_WILDCARD: + return type->name; + case TYPE_ENUM: + case TYPE_CONST_ENUM: + case TYPE_TYPEDEF: + case TYPE_STRUCT: + case TYPE_UNION: + case TYPE_DISTINCT: + case TYPE_BITSTRUCT: + case TYPE_INTERFACE: + { + Decl *decl = type->decl; + const char *suffix = decl->unit->module->generic_suffix; + scratch_buffer_clear(); + scratch_buffer_append(decl->unit->module->name->module); + scratch_buffer_append("::"); + if (suffix || type_is_inner_type(type)) + { + type_add_parent_to_scratch(decl); + } + scratch_buffer_append(decl->name); + if (suffix) scratch_buffer_append(suffix); + return scratch_buffer_copy(); + } + case TYPE_FUNC_PTR: + type = type->pointer; + FALLTHROUGH; + case TYPE_FUNC_RAW: + if (!type->function.prototype) return type->name; + scratch_buffer_clear(); + scratch_buffer_append("fn "); + type_append_func_to_scratch(type->function.prototype); + return scratch_buffer_copy(); + case TYPE_INFERRED_VECTOR: + return str_printf("%s[<*>]", type_to_error_string_with_path(type->array.base)); + case TYPE_VECTOR: + return str_printf("%s[<%llu>]", type_to_error_string_with_path(type->array.base), (unsigned long long)type->array.len); + case TYPE_TYPEINFO: + return "typeinfo"; + case TYPE_TYPEID: + return "typeid"; + case TYPE_POINTER: + return str_printf("%s*", type_to_error_string_with_path(type->pointer)); + case TYPE_OPTIONAL: + if (!type->optional) return "void?"; + return str_printf("%s?", type_to_error_string_with_path(type->optional)); + case TYPE_ARRAY: + return str_printf("%s[%llu]", type_to_error_string_with_path(type->array.base), (unsigned long long)type->array.len); + case TYPE_INFERRED_ARRAY: + case TYPE_FLEXIBLE_ARRAY: + return str_printf("%s[*]", type_to_error_string_with_path(type->array.base)); + case TYPE_SLICE: + return str_printf("%s[]", type_to_error_string_with_path(type->array.base)); + } + UNREACHABLE +} + bool type_is_matching_int(CanonicalType *type1, CanonicalType *type2) { diff --git a/test/test_suite/cast/implicit_cast_same_name.c3 b/test/test_suite/cast/implicit_cast_same_name.c3 new file mode 100644 index 000000000..ba61c1aab --- /dev/null +++ b/test/test_suite/cast/implicit_cast_same_name.c3 @@ -0,0 +1,15 @@ +module foo::a; +struct Lexer { int a; } +fn void foo(Lexer *lex) {} + +module foo::b; +struct Lexer { int a; } +fn void foo(Lexer *lex) {} + +module foo; + +fn void bar() +{ + a::Lexer l; + b::foo(&l); // #error: Implicitly casting 'foo::a::Lexer*' to 'foo::b::Lexer*' is not permitted, but you may do an explicit cast by placing '(foo::b::Lexer*)' before the expression +}