Commit c97c7f9e3b

Andrew Kelley <andrew@ziglang.org>
2022-05-23 23:36:21
C backend: update to new error union semantics
1 parent 3a059eb
Changed files (1)
src
codegen
src/codegen/c.zig
@@ -749,6 +749,12 @@ pub const DeclGen = struct {
                 const error_type = ty.errorUnionSet();
                 const payload_type = ty.errorUnionPayload();
 
+                if (error_type.errorSetCardinality() == .zero) {
+                    // We use the payload directly as the type.
+                    const payload_val = val.castTag(.eu_payload).?.data;
+                    return dg.renderValue(writer, payload_type, payload_val, location);
+                }
+
                 if (!payload_type.hasRuntimeBits()) {
                     // We use the error type directly as the type.
                     const err_val = if (val.errorUnionIsPayload()) Value.initTag(.zero) else val;
@@ -894,10 +900,12 @@ pub const DeclGen = struct {
                 try w.writeAll("ZIG_COLD ");
             }
         }
-        const return_ty = dg.decl.ty.fnReturnType();
-        if (return_ty.hasRuntimeBits()) {
-            try dg.renderType(w, return_ty);
-        } else if (return_ty.zigTypeTag() == .NoReturn) {
+        const fn_info = dg.decl.ty.fnInfo();
+        if (fn_info.return_type.hasRuntimeBits()) {
+            try dg.renderType(w, fn_info.return_type);
+        } else if (fn_info.return_type.isError()) {
+            try dg.renderType(w, Type.anyerror);
+        } else if (fn_info.return_type.zigTypeTag() == .NoReturn) {
             try w.writeAll("zig_noreturn void");
         } else {
             try w.writeAll("void");
@@ -905,22 +913,19 @@ pub const DeclGen = struct {
         try w.writeAll(" ");
         try dg.renderDeclName(w, dg.decl_index);
         try w.writeAll("(");
-        const param_len = dg.decl.ty.fnParamLen();
 
-        var index: usize = 0;
         var params_written: usize = 0;
-        while (index < param_len) : (index += 1) {
-            const param_type = dg.decl.ty.fnParamType(index);
+        for (fn_info.param_types) |param_type, index| {
             if (!param_type.hasRuntimeBitsIgnoreComptime()) continue;
             if (params_written > 0) {
                 try w.writeAll(", ");
             }
             const name = CValue{ .arg = index };
-            try dg.renderTypeAndName(w, dg.decl.ty.fnParamType(index), name, .Mut, 0);
+            try dg.renderTypeAndName(w, param_type, name, .Mut, 0);
             params_written += 1;
         }
 
-        if (dg.decl.ty.fnIsVarArgs()) {
+        if (fn_info.is_var_args) {
             if (params_written != 0) try w.writeAll(", ");
             try w.writeAll("...");
         } else if (params_written == 0) {
@@ -1156,26 +1161,36 @@ pub const DeclGen = struct {
     }
 
     fn renderErrorUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
-        const child_type = t.errorUnionPayload();
-        const err_set_type = t.errorUnionSet();
+        const payload_ty = t.errorUnionPayload();
+        const error_ty = t.errorUnionSet();
 
         var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
         defer buffer.deinit();
         const bw = buffer.writer();
 
-        try bw.writeAll("typedef struct { ");
         const payload_name = CValue{ .bytes = "payload" };
-        try dg.renderTypeAndName(bw, child_type, payload_name, .Mut, 0);
-        try bw.writeAll("; uint16_t error; } ");
+        const target = dg.module.getTarget();
+        const payload_align = payload_ty.abiAlignment(target);
+        const error_align = Type.anyerror.abiAlignment(target);
+        if (error_align > payload_align) {
+            try bw.writeAll("typedef struct { ");
+            try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0);
+            try bw.writeAll("; uint16_t error; } ");
+        } else {
+            try bw.writeAll("typedef struct { uint16_t error; ");
+            try dg.renderTypeAndName(bw, payload_ty, payload_name, .Mut, 0);
+            try bw.writeAll("; } ");
+        }
+
         const name_index = buffer.items.len;
-        if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {
+        if (error_ty.castTag(.error_set_inferred)) |inf_err_set_payload| {
             const func = inf_err_set_payload.data.func;
             try bw.writeAll("zig_E_");
             try dg.renderDeclName(bw, func.owner_decl);
             try bw.writeAll(";\n");
         } else {
             try bw.print("zig_E_{s}_{s};\n", .{
-                typeToCIdentifier(err_set_type, dg.module), typeToCIdentifier(child_type, dg.module),
+                typeToCIdentifier(error_ty, dg.module), typeToCIdentifier(payload_ty, dg.module),
             });
         }
 
@@ -1359,12 +1374,19 @@ pub const DeclGen = struct {
                 return w.writeAll(name);
             },
             .ErrorSet => {
-                comptime assert(Type.initTag(.anyerror).abiSize(builtin.target) == 2);
+                comptime assert(Type.anyerror.abiSize(builtin.target) == 2);
                 return w.writeAll("uint16_t");
             },
             .ErrorUnion => {
-                if (t.errorUnionPayload().abiSize(target) == 0) {
-                    return dg.renderType(w, t.errorUnionSet());
+                const error_ty = t.errorUnionSet();
+                const payload_ty = t.errorUnionPayload();
+
+                if (error_ty.errorSetCardinality() == .zero) {
+                    return dg.renderType(w, payload_ty);
+                }
+
+                if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
+                    return dg.renderType(w, Type.anyerror);
                 }
 
                 const name = dg.getTypedefName(t) orelse
@@ -1901,8 +1923,8 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
             .array_elem_val     => try airArrayElemVal(f, inst),
 
             .unwrap_errunion_payload     => try airUnwrapErrUnionPay(f, inst, ""),
-            .unwrap_errunion_err         => try airUnwrapErrUnionErr(f, inst),
             .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst, "&"),
+            .unwrap_errunion_err         => try airUnwrapErrUnionErr(f, inst),
             .unwrap_errunion_err_ptr     => try airUnwrapErrUnionErr(f, inst),
             .wrap_errunion_payload       => try airWrapErrUnionPay(f, inst),
             .wrap_errunion_err           => try airWrapErrUnionErr(f, inst),
@@ -2120,11 +2142,14 @@ fn airLoad(f: *Function, inst: Air.Inst.Index) !CValue {
 fn airRet(f: *Function, inst: Air.Inst.Index) !CValue {
     const un_op = f.air.instructions.items(.data)[inst].un_op;
     const writer = f.object.writer();
-    if (f.air.typeOf(un_op).isFnOrHasRuntimeBitsIgnoreComptime()) {
+    const ret_ty = f.air.typeOf(un_op);
+    if (ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) {
         const operand = try f.resolveInst(un_op);
         try writer.writeAll("return ");
         try f.writeCValue(writer, operand);
         try writer.writeAll(";\n");
+    } else if (ret_ty.isError()) {
+        try writer.writeAll("return 0;");
     } else {
         try writer.writeAll("return;\n");
     }
@@ -2136,13 +2161,16 @@ fn airRetLoad(f: *Function, inst: Air.Inst.Index) !CValue {
     const writer = f.object.writer();
     const ptr_ty = f.air.typeOf(un_op);
     const ret_ty = ptr_ty.childType();
-    if (!ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) {
+    if (ret_ty.isFnOrHasRuntimeBitsIgnoreComptime()) {
+        const ptr = try f.resolveInst(un_op);
+        try writer.writeAll("return *");
+        try f.writeCValue(writer, ptr);
+        try writer.writeAll(";\n");
+    } else if (ret_ty.isError()) {
+        try writer.writeAll("return 0;\n");
+    } else {
         try writer.writeAll("return;\n");
     }
-    const ptr = try f.resolveInst(un_op);
-    try writer.writeAll("return *");
-    try f.writeCValue(writer, ptr);
-    try writer.writeAll(";\n");
     return CValue.none;
 }
 
@@ -2713,19 +2741,20 @@ fn airCall(
         .Pointer => callee_ty.childType(),
         else => unreachable,
     };
-    const ret_ty = fn_ty.fnReturnType();
-    const unused_result = f.liveness.isUnused(inst);
     const writer = f.object.writer();
 
-    var result_local: CValue = .none;
-    if (unused_result) {
-        if (ret_ty.hasRuntimeBits()) {
-            try writer.print("(void)", .{});
+    const result_local: CValue = r: {
+        if (f.liveness.isUnused(inst)) {
+            if (loweredFnRetTyHasBits(fn_ty)) {
+                try writer.print("(void)", .{});
+            }
+            break :r .none;
+        } else {
+            const local = try f.allocLocal(fn_ty.fnReturnType(), .Const);
+            try writer.writeAll(" = ");
+            break :r local;
         }
-    } else {
-        result_local = try f.allocLocal(ret_ty, .Const);
-        try writer.writeAll(" = ");
-    }
+    };
 
     callee: {
         known: {
@@ -3307,7 +3336,8 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue {
     return local;
 }
 
-// *(E!T) -> E NOT *E
+/// *(E!T) -> E
+/// Note that the result is never a pointer.
 fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
     if (f.liveness.isUnused(inst))
         return CValue.none;
@@ -3319,7 +3349,11 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
     const operand_ty = f.air.typeOf(ty_op.operand);
 
     if (operand_ty.zigTypeTag() == .Pointer) {
-        if (!operand_ty.childType().errorUnionPayload().hasRuntimeBits()) {
+        const err_union_ty = operand_ty.childType();
+        if (err_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
+            return CValue{ .bytes = "0" };
+        }
+        if (!err_union_ty.errorUnionPayload().hasRuntimeBits()) {
             return operand;
         }
         const local = try f.allocLocal(inst_ty, .Const);
@@ -3328,6 +3362,9 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
         try writer.writeAll(";\n");
         return local;
     }
+    if (operand_ty.errorUnionSet().errorSetCardinality() == .zero) {
+        return CValue{ .bytes = "0" };
+    }
     if (!operand_ty.errorUnionPayload().hasRuntimeBits()) {
         return operand;
     }
@@ -3343,7 +3380,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
     return local;
 }
 
-fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, maybe_addrof: []const u8) !CValue {
+fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, maybe_addrof: [*:0]const u8) !CValue {
     if (f.liveness.isUnused(inst))
         return CValue.none;
 
@@ -3351,17 +3388,19 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, maybe_addrof: []cons
     const writer = f.object.writer();
     const operand = try f.resolveInst(ty_op.operand);
     const operand_ty = f.air.typeOf(ty_op.operand);
+    const operand_is_ptr = operand_ty.zigTypeTag() == .Pointer;
+    const error_union_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty;
+
+    if (error_union_ty.errorUnionSet().errorSetCardinality() == .zero) {
+        return operand;
+    }
 
-    const error_union_ty = if (operand_ty.zigTypeTag() == .Pointer)
-        operand_ty.childType()
-    else
-        operand_ty;
     if (!error_union_ty.errorUnionPayload().hasRuntimeBits()) {
         return CValue.none;
     }
 
     const inst_ty = f.air.typeOfIndex(inst);
-    const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
+    const maybe_deref = if (operand_is_ptr) "->" else ".";
 
     const local = try f.allocLocal(inst_ty, .Const);
     try writer.print(" = {s}(", .{maybe_addrof});
@@ -3421,6 +3460,11 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue {
     const error_ty = error_union_ty.errorUnionSet();
     const payload_ty = error_union_ty.errorUnionPayload();
 
+    if (error_ty.errorSetCardinality() == .zero) {
+        // TODO: write undefined bytes through the pointer here
+        return operand;
+    }
+
     // First, set the non-error value.
     if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
         try f.writeCValueDeref(writer, operand);
@@ -3464,6 +3508,9 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
     const operand = try f.resolveInst(ty_op.operand);
 
     const inst_ty = f.air.typeOfIndex(inst);
+    if (inst_ty.errorUnionSet().errorSetCardinality() == .zero) {
+        return operand;
+    }
     const local = try f.allocLocal(inst_ty, .Const);
     try writer.writeAll(" = { .error = 0, .payload = ");
     try f.writeCValue(writer, operand);
@@ -3486,16 +3533,23 @@ fn airIsErr(
     const operand_ty = f.air.typeOf(un_op);
     const local = try f.allocLocal(Type.initTag(.bool), .Const);
     const payload_ty = operand_ty.errorUnionPayload();
+    const error_ty = operand_ty.errorUnionSet();
+
     try writer.writeAll(" = ");
-    if (is_ptr) {
-        try f.writeCValueDeref(writer, operand);
+
+    if (error_ty.errorSetCardinality() == .zero) {
+        try writer.print("0 {s} 0;\n", .{op_str});
     } else {
-        try f.writeCValue(writer, operand);
-    }
-    if (payload_ty.hasRuntimeBits()) {
-        try writer.writeAll(".error");
+        if (is_ptr) {
+            try f.writeCValueDeref(writer, operand);
+        } else {
+            try f.writeCValue(writer, operand);
+        }
+        if (payload_ty.hasRuntimeBits()) {
+            try writer.writeAll(".error");
+        }
+        try writer.print(" {s} 0;\n", .{op_str});
     }
-    try writer.print(" {s} 0;\n", .{op_str});
     return local;
 }
 
@@ -4129,3 +4183,14 @@ fn intMin(ty: Type, target: std.Target, buf: []u8) []const u8 {
         },
     }
 }
+
+fn loweredFnRetTyHasBits(fn_ty: Type) bool {
+    const ret_ty = fn_ty.fnReturnType();
+    if (ret_ty.hasRuntimeBitsIgnoreComptime()) {
+        return true;
+    }
+    if (ret_ty.isError()) {
+        return true;
+    }
+    return false;
+}