Implicit casting from struct to interface failure for inheriting interfaces #2151. Fix second bug in #2148

This commit is contained in:
Christoffer Lerno
2025-05-24 17:10:11 +02:00
parent fe98225f0a
commit 8d563eba7a
7 changed files with 116 additions and 32 deletions

View File

@@ -40,6 +40,7 @@
- Missing error on default values for body with default arguments #2148.
- `--path` does not interact correctly with relative path arguments #2149.
- Add missing `@noreturn` to `os::exit`.
- Implicit casting from struct to interface failure for inheriting interfaces #2151.
### Stdlib changes
- Added `String.quick_ztr` and `String.is_zstr`

View File

@@ -1143,14 +1143,23 @@ static bool rule_vecarr_to_infer(CastContext *cc, bool is_explicit, bool is_sile
return cast_is_allowed(cc, is_explicit, is_silent);
}
static inline BoolErr type_implements_interface_ignore_substruct(CastContext *cc, Decl *decl, Type *interface)
{
FOREACH(TypeInfo *, interface_type, decl->interfaces)
{
if (!sema_resolve_type_info(cc->context, interface_type, RESOLVE_TYPE_DEFAULT)) return BOOL_ERR;
if (interface_type->type == interface) return BOOL_TRUE;
BoolErr res = type_implements_interface_ignore_substruct(cc, interface_type->type->decl, interface);
if (res != BOOL_FALSE) return res;
}
return BOOL_FALSE;
}
static inline bool type_implements_interface(CastContext *cc, Decl *decl, Type *interface)
{
RETRY:;
FOREACH(TypeInfo *, interface_type, decl->interfaces)
{
if (!sema_resolve_type_info(cc->context, interface_type, RESOLVE_TYPE_DEFAULT)) return false;
if (interface_type->type == interface) return true;
}
BoolErr result = type_implements_interface_ignore_substruct(cc, decl, interface);
if (result != BOOL_FALSE) return result == BOOL_TRUE;
if (!decl->is_substruct) return false;
Type *inner;
if (decl->decl_kind == DECL_DISTINCT)

View File

@@ -2381,6 +2381,10 @@ bool sema_expr_analyse_macro_call(SemaContext *context, Expr *call_expr, Expr *s
default:
UNREACHABLE
}
if (body_arg->var.init_expr)
{
RETURN_SEMA_ERROR(body_arg->var.init_expr, "Macro body parameters should never have default values.");
}
TypeInfo *expected_type_info = vartype(body_param);
TypeInfo *type_info = vartype(body_arg);
if (type_info && !sema_resolve_type_info(context, type_info, RESOLVE_TYPE_DEFAULT)) return false;

View File

@@ -680,40 +680,55 @@ void sema_analysis_pass_lambda(Module *module)
DEBUG_LOG("Pass finished with %d error(s).", compiler.context.errors_found);
}
static bool sema_check_interface(SemaContext *context, Decl *decl, TypeInfo *interface_type, TypeInfo *original_type)
{
Decl *interface = interface_type->type->decl;
FOREACH(Decl *, method, interface->interface_methods)
{
Decl *matching_method = sema_decl_stack_resolve_symbol(method->name);
if (!matching_method)
{
if (method->func_decl.attr_optional) continue;
if (interface_type != original_type)
{
RETURN_SEMA_ERROR(original_type,
"'%s' was not fully implemented, the required method '%s' of interface '%s' needs to be implemented, did you forget it?",
original_type->type->decl->name, method->name, interface->name);
}
RETURN_SEMA_ERROR(original_type,
"'%s' was not fully implemented, the required method '%s' needs to be implemented, did you forget it?",
interface->name, method->name);
}
if (matching_method->decl_kind != DECL_FUNC)
{
if (method->func_decl.attr_optional) continue;
RETURN_SEMA_ERROR(matching_method, "'%s' was not fully implemented, it requires '%s' to be a function marked '@dynamic'.",
interface->name, method->name);
}
if (!matching_method->func_decl.attr_dynamic)
{
SEMA_ERROR(matching_method, "'%s(...)' must be marked '@dynamic' as it matches the method '%s' in interface '%s'.",
method->name, method->name, interface->name);
SEMA_NOTE(method, "Here is the interface method to implement.");
return false;
}
}
FOREACH(TypeInfo *, parent_interface, interface->interfaces)
{
if (!sema_check_interface(context, decl, parent_interface, original_type)) return false;
}
return true;
}
static inline bool sema_check_interfaces(SemaContext *context, Decl *decl)
{
Decl **store = sema_decl_stack_store();
FOREACH(Decl *, method, decl->methods) sema_decl_stack_push(method);
FOREACH(TypeInfo *, interface_type, decl->interfaces)
{
Decl *interface = interface_type->type->decl;
FOREACH(Decl *, method, interface->interface_methods)
if (!sema_check_interface(context, decl, interface_type, interface_type))
{
Decl *matching_method = sema_decl_stack_resolve_symbol(method->name);
if (!matching_method)
{
if (method->func_decl.attr_optional) continue;
SEMA_ERROR(interface_type, "'%s' was not fully implemented, required method '%s' needs to be implemented, did you forget it?",
interface->name, method->name);
sema_decl_stack_restore(store);
return false;
}
if (matching_method->decl_kind != DECL_FUNC)
{
if (method->func_decl.attr_optional) continue;
SEMA_ERROR(matching_method, "'%s' was not fully implemented, it requires '%s' to be a function marked '@dynamic'.",
interface->name, method->name);
sema_decl_stack_restore(store);
return false;
}
if (!matching_method->func_decl.attr_dynamic)
{
SEMA_ERROR(matching_method, "'%s(...)' must be marked '@dynamic' as it matches the method '%s' in interface '%s'.",
method->name, method->name, interface->name);
SEMA_NOTE(method, "Here is the interface method to implement.");
sema_decl_stack_restore(store);
return false;
}
sema_decl_stack_restore(store);
return false;
}
}
sema_decl_stack_restore(store);

View File

@@ -0,0 +1,13 @@
import std;
interface IFoo { fn void a(); }
interface IBar { fn void b(); }
interface IFooBar: IFoo, IBar {}
struct Foo (IFooBar) { int _a; } // #error: was not fully implemented
struct Foo2 (IFoo, IBar) { int _a; }
fn void Foo2.a(&s) @dynamic {}
fn void Foo2.b(&s) @dynamic {}

View File

@@ -0,0 +1,29 @@
import std;
interface IFoo { fn void a(); }
interface IBar { fn void b(); }
interface IFooBar: IFoo, IBar {}
struct Foo (IFooBar) { int _a; }
fn void Foo.a(&s) @dynamic {}
fn void Foo.b(&s) @dynamic {}
fn void foo(IFoo o) => o.a();
fn void bar(IBar o) => o.b();
fn void foobar(IFooBar o)
{
o.a();
o.b();
}
fn void main()
{
Foo f;
foo(&f);
bar(&f);
foobar(&f);
}

View File

@@ -0,0 +1,13 @@
fn int main()
{
@foo(;int x = 2) // #error: Macro body parameters should
{
};
return 0;
}
macro @foo(; @body(int x))
{
}