From df8904909b903ec90451fc2feca50de06b38f447 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Fri, 27 Jun 2025 15:02:12 +0200 Subject: [PATCH] Fix bugs relating to optional interface addr-of #2244. --- releasenotes.md | 1 + src/compiler/compiler_internal.h | 7 +- src/compiler/llvm_codegen_expr.c | 2 +- src/compiler/sema_expr.c | 5 +- test/test_suite/any/interface_optional.c3 | 33 ++++++ .../test_suite/any/interface_optional_try.c3t | 106 ++++++++++++++++++ 6 files changed, 146 insertions(+), 8 deletions(-) create mode 100644 test/test_suite/any/interface_optional.c3 create mode 100644 test/test_suite/any/interface_optional_try.c3t diff --git a/releasenotes.md b/releasenotes.md index 6c28b4d53..6c2475e26 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -66,6 +66,7 @@ - Assert casting bitstruct to short/char #2237. - @tag didn't work with members #2236. - Assert comparing untyped lists #2240. +- Fix bugs relating to optional interface addr-of #2244. ### Stdlib changes - Deprecate `String.is_zstr` and `String.quick_zstr` #2188. diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 64179db42..6a4648a67 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -282,11 +282,8 @@ struct Type_ TypeKind type_kind; CanonicalType *canonical; const char *name; - union - { - Type **type_cache; - Type *func_ptr; - }; + Type **type_cache; + Type *func_ptr; union { void *backend_type; diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 38b98a165..fee2794eb 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -2372,8 +2372,8 @@ static inline void llvm_emit_post_inc_dec(GenContext *c, BEValue *value, Expr *e static void llvm_emit_dynamic_method_addr(GenContext *c, BEValue *value, Expr *expr) { llvm_emit_expr(c, value, expr->access_resolved_expr.parent); - llvm_emit_type_from_any(c, value); llvm_value_rvalue(c, value); + llvm_emit_type_from_any(c, value); LLVMValueRef introspect = LLVMBuildIntToPtr(c->builder, value->value, c->ptr_type, ""); Decl *dyn_fn = expr->access_resolved_expr.ref; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 87a63838a..6557300e1 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -8108,10 +8108,11 @@ RESOLVED: RETURN_SEMA_ERROR(inner, error); } + Type *no_optional = type_no_optional(inner->type)->canonical; // 3. Get the pointer of the underlying type. - if (inner->type->type_kind == TYPE_FUNC_RAW) + if (no_optional->type_kind == TYPE_FUNC_RAW) { - expr->type = type_get_func_ptr(inner->type); + expr->type = type_add_optional(type_get_func_ptr(no_optional), IS_OPTIONAL((inner))); return true; } expr->type = type_get_ptr_recurse(inner->type); diff --git a/test/test_suite/any/interface_optional.c3 b/test/test_suite/any/interface_optional.c3 new file mode 100644 index 000000000..237bed841 --- /dev/null +++ b/test/test_suite/any/interface_optional.c3 @@ -0,0 +1,33 @@ +module repro; + +import std::io; + +interface VeryOptional +{ + fn void do_something() @optional; +} + +struct Foo (VeryOptional) +{ + String name; +} + +Foo foo = { "Foo" }; + +fn VeryOptional? get_very_optional() +{ + return &foo; +} + +fn void main() +{ + VeryOptional? v = get_very_optional(); + if (&v.do_something) // #error: expression may not be an optional + { + v.do_something(); + } + else + { + io::printfn("do_something is not implemented for Foo"); + } +} \ No newline at end of file diff --git a/test/test_suite/any/interface_optional_try.c3t b/test/test_suite/any/interface_optional_try.c3t new file mode 100644 index 000000000..ff2fbb702 --- /dev/null +++ b/test/test_suite/any/interface_optional_try.c3t @@ -0,0 +1,106 @@ +// #target: macos-x64 +module test; +import std::io; + +interface VeryOptional +{ + fn void do_something() @optional; +} + +struct Foo (VeryOptional) +{ + String name; +} + +Foo foo = { "Foo" }; + +fn VeryOptional? get_very_optional() +{ + return &foo; +} + +fn void main() +{ + VeryOptional? v = get_very_optional(); + if (try x = &v.do_something) + { + + } + else + { + io::printfn("do_something is not implemented for Foo"); + } +} + +/* #expect: test.ll + +define void @test.main() #0 { +entry: + %v = alloca %any, align 8 + %v.f = alloca i64, align 8 + %retparam = alloca %any, align 8 + %x = alloca ptr, align 8 + %.inlinecache = alloca ptr, align 8 + %.cachedtype = alloca ptr, align 8 + %retparam3 = alloca i64, align 8 + store ptr null, ptr %.cachedtype, align 8 + %0 = call i64 @test.get_very_optional(ptr %retparam) + %not_err = icmp eq i64 %0, 0 + %1 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %1, label %after_check, label %assign_optional + +assign_optional: ; preds = %entry + store i64 %0, ptr %v.f, align 8 + br label %after_assign + +after_check: ; preds = %entry + call void @llvm.memcpy.p0.p0.i32(ptr align 8 %v, ptr align 8 %retparam, i32 16, i1 false) + store i64 0, ptr %v.f, align 8 + br label %after_assign + +after_assign: ; preds = %after_check, %assign_optional + store ptr null, ptr %x, align 8 + %optval = load i64, ptr %v.f, align 8 + %not_err1 = icmp eq i64 %optval, 0 + %2 = call i1 @llvm.expect.i1(i1 %not_err1, i1 true) + br i1 %2, label %after_check2, label %catch_landing + +after_check2: ; preds = %after_assign + %3 = load %any, ptr %v, align 8 + %4 = extractvalue %any %3, 1 + %5 = inttoptr i64 %4 to ptr + %type = load ptr, ptr %.cachedtype, align 8 + %6 = icmp eq ptr %5, %type + br i1 %6, label %cache_hit, label %cache_miss + +cache_miss: ; preds = %after_check2 + %ptradd = getelementptr inbounds i8, ptr %5, i64 16 + %7 = load ptr, ptr %ptradd, align 8 + %8 = call ptr @.dyn_search(ptr %7, ptr @"$sel.do_something") + store ptr %8, ptr %.inlinecache, align 8 + store ptr %5, ptr %.cachedtype, align 8 + br label %9 + +cache_hit: ; preds = %after_check2 + %cache_hit_fn = load ptr, ptr %.inlinecache, align 8 + br label %9 + +9: ; preds = %cache_hit, %cache_miss + %fn_phi = phi ptr [ %cache_hit_fn, %cache_hit ], [ %8, %cache_miss ] + store ptr %fn_phi, ptr %x, align 8 + br label %phi_try_catch + +catch_landing: ; preds = %after_assign + br label %phi_try_catch + +phi_try_catch: ; preds = %catch_landing, %9 + %val = phi i1 [ true, %9 ], [ false, %catch_landing ] + br i1 %val, label %if.exit, label %if.else + +if.else: ; preds = %phi_try_catch + %10 = call i64 @std.io.printfn(ptr %retparam3, ptr @.str.1, i64 39, ptr null, i64 0) + br label %if.exit + +if.exit: ; preds = %if.else, %phi_try_catch + ret void +}