diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6ebe76fb39..6a2e346113 100644 --- a/src/codegen/llvm.zig +++ b/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); diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 8e4b262565..2f6fa78f0c 100644 --- a/test/behavior/union.zig +++ b/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); +}