Commit 3ba7098a17
src/codegen/llvm.zig
@@ -4491,17 +4491,29 @@ pub const FuncGen = struct {
}
return null;
}
+
const abi_ret_ty = try lowerFnRetTy(self.dg, fn_info);
+ const ptr_abi_ty = abi_ret_ty.pointerType(0);
const operand = try self.resolveInst(un_op);
+ const target = self.dg.module.getTarget();
+ const alignment = ret_ty.abiAlignment(target);
+
+ if (isByRef(ret_ty)) {
+ // operand is a pointer however self.ret_ptr is null so that means
+ // we need to return a value.
+ const casted_ptr = self.builder.buildBitCast(operand, ptr_abi_ty, "");
+ const load_inst = self.builder.buildLoad(casted_ptr, "");
+ load_inst.setAlignment(alignment);
+ _ = self.builder.buildRet(load_inst);
+ return null;
+ }
+
const llvm_ret_ty = operand.typeOf();
if (abi_ret_ty == llvm_ret_ty) {
_ = self.builder.buildRet(operand);
return null;
}
- const target = self.dg.module.getTarget();
- const alignment = ret_ty.abiAlignment(target);
- const ptr_abi_ty = abi_ret_ty.pointerType(0);
const rp = self.buildAlloca(llvm_ret_ty);
rp.setAlignment(alignment);
const store_inst = self.builder.buildStore(operand, rp);
test/behavior/union.zig
@@ -1226,3 +1226,33 @@ test "extern union most-aligned field is smaller" {
var a: ?U = .{ .un = [_]u8{0} ** 110 };
try expect(a != null);
}
+
+test "return an extern union from C calling convention" {
+ if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+
+ const namespace = struct {
+ const S = extern struct {
+ x: c_int,
+ };
+ const U = extern union {
+ l: c_long,
+ d: f64,
+ s: S,
+ };
+
+ fn bar(arg_u: U) callconv(.C) U {
+ var u = arg_u;
+ return u;
+ }
+ };
+
+ var u: namespace.U = namespace.U{
+ .l = @as(c_long, 42),
+ };
+ u = namespace.bar(namespace.U{
+ .d = 4.0,
+ });
+ try expect(u.d == 4.0);
+}