authorgravatar for 30519309+Scibuild@users.noreply.github.comScibuild <30519309+Scibuild@users.noreply.github.com> 2021-11-18 08:26:15+11:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-23 13:46:56-07:00
log2e15a404e287982b261c14240db44af9175f765a
tree386b910dd63f698bd41abb84957efe3c788d824b
parent8f1e417757f2172485cdea792580e2a2afb53ffa

C backend: errors and optionals

* bitcast treats all pointers as pointers * correctly unwrapping error unions with pointers * equality operators for primitive optional types

4 files changed, 79 insertions(+), 28 deletions(-)

src/codegen/c.zig+52-8
...@@ -1162,12 +1162,12 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1162,12 +1162,12 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
11621162
1163 .slice => try airSlice(f, inst),1163 .slice => try airSlice(f, inst),
11641164
1165 .cmp_eq => try airBinOp(f, inst, " == "),1165 .cmp_eq => try airEquality(f, inst, .cmp_eq),
1166 .cmp_gt => try airBinOp(f, inst, " > "),1166 .cmp_gt => try airBinOp(f, inst, " > "),
1167 .cmp_gte => try airBinOp(f, inst, " >= "),1167 .cmp_gte => try airBinOp(f, inst, " >= "),
1168 .cmp_lt => try airBinOp(f, inst, " < "),1168 .cmp_lt => try airBinOp(f, inst, " < "),
1169 .cmp_lte => try airBinOp(f, inst, " <= "),1169 .cmp_lte => try airBinOp(f, inst, " <= "),
1170 .cmp_neq => try airBinOp(f, inst, " != "),1170 .cmp_neq => try airEquality(f, inst, .cmp_neq),
11711171
1172 // bool_and and bool_or are non-short-circuit operations1172 // bool_and and bool_or are non-short-circuit operations
1173 .bool_and => try airBinOp(f, inst, " & "),1173 .bool_and => try airBinOp(f, inst, " & "),
...@@ -1257,9 +1257,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1257,9 +1257,9 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1257 .slice_elem_ptr => try airSliceElemPtr(f, inst),1257 .slice_elem_ptr => try airSliceElemPtr(f, inst),
1258 .array_elem_val => try airArrayElemVal(f, inst),1258 .array_elem_val => try airArrayElemVal(f, inst),
12591259
1260 .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst),1260 .unwrap_errunion_payload => try airUnwrapErrUnionPay(f, inst, ""),
1261 .unwrap_errunion_err => try airUnwrapErrUnionErr(f, inst),1261 .unwrap_errunion_err => try airUnwrapErrUnionErr(f, inst),
1262 .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst),1262 .unwrap_errunion_payload_ptr => try airUnwrapErrUnionPay(f, inst, "&"),
1263 .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(f, inst),1263 .unwrap_errunion_err_ptr => try airUnwrapErrUnionErr(f, inst),
1264 .wrap_errunion_payload => try airWrapErrUnionPay(f, inst),1264 .wrap_errunion_payload => try airWrapErrUnionPay(f, inst),
1265 .wrap_errunion_err => try airWrapErrUnionErr(f, inst),1265 .wrap_errunion_err => try airWrapErrUnionErr(f, inst),
...@@ -1908,6 +1908,51 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue...@@ -1908,6 +1908,51 @@ fn airBinOp(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue
1908 return local;1908 return local;
1909}1909}
19101910
1911fn airEquality(f: *Function, inst: Air.Inst.Index, op: Air.Inst.Tag) !CValue {
1912 if (f.liveness.isUnused(inst))
1913 return CValue.none;
1914
1915 const bin_op = f.air.instructions.items(.data)[inst].bin_op;
1916 const lhs = try f.resolveInst(bin_op.lhs);
1917 const rhs = try f.resolveInst(bin_op.rhs);
1918
1919 const writer = f.object.writer();
1920 const inst_ty = f.air.typeOfIndex(inst);
1921 const local = try f.allocLocal(inst_ty, .Const);
1922
1923 try writer.writeAll(" = ");
1924
1925 const lhs_ty = f.air.typeOf(bin_op.lhs);
1926 if (lhs_ty.tag() == .optional) {
1927 // (A && B) || (C && (A == B))
1928 // A = lhs.is_null ; B = rhs.is_null ; C = rhs.payload == lhs.payload
1929
1930 try writer.writeAll(if (op == .cmp_eq) "((" else "!((");
1931 try f.writeCValue(writer, lhs);
1932 try writer.writeAll(".is_null && ");
1933 try f.writeCValue(writer, rhs);
1934 try writer.writeAll(".is_null) || (");
1935 try f.writeCValue(writer, lhs);
1936 try writer.writeAll(".payload == ");
1937 try f.writeCValue(writer, rhs);
1938 try writer.writeAll(".payload && ");
1939 try f.writeCValue(writer, lhs);
1940 try writer.writeAll(".is_null == ");
1941 try f.writeCValue(writer, rhs);
1942 try writer.writeAll(".is_null));\n");
1943
1944 return local;
1945 }
1946
1947 const operator = if (op == .cmp_eq) "==" else "!=";
1948 try f.writeCValue(writer, lhs);
1949 try writer.print("{s}", .{operator});
1950 try f.writeCValue(writer, rhs);
1951 try writer.writeAll(";\n");
1952
1953 return local;
1954}
1955
1911fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {1956fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: [*:0]const u8) !CValue {
1912 if (f.liveness.isUnused(inst))1957 if (f.liveness.isUnused(inst))
1913 return CValue.none;1958 return CValue.none;
...@@ -2104,8 +2149,8 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -2104,8 +2149,8 @@ fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue {
21042149
2105 const writer = f.object.writer();2150 const writer = f.object.writer();
2106 const inst_ty = f.air.typeOfIndex(inst);2151 const inst_ty = f.air.typeOfIndex(inst);
2107 if (inst_ty.zigTypeTag() == .Pointer and2152 if (inst_ty.isPtrAtRuntime() and
2108 f.air.typeOf(ty_op.operand).zigTypeTag() == .Pointer)2153 f.air.typeOf(ty_op.operand).isPtrAtRuntime())
2109 {2154 {
2110 const local = try f.allocLocal(inst_ty, .Const);2155 const local = try f.allocLocal(inst_ty, .Const);
2111 try writer.writeAll(" = (");2156 try writer.writeAll(" = (");
...@@ -2503,7 +2548,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -2503,7 +2548,7 @@ fn airUnwrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue {
2503 return local;2548 return local;
2504}2549}
25052550
2506fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {2551fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, maybe_addrof: []const u8) !CValue {
2507 if (f.liveness.isUnused(inst))2552 if (f.liveness.isUnused(inst))
2508 return CValue.none;2553 return CValue.none;
25092554
...@@ -2519,7 +2564,6 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {...@@ -2519,7 +2564,6 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue {
25192564
2520 const inst_ty = f.air.typeOfIndex(inst);2565 const inst_ty = f.air.typeOfIndex(inst);
2521 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";2566 const maybe_deref = if (operand_ty.zigTypeTag() == .Pointer) "->" else ".";
2522 const maybe_addrof = if (inst_ty.zigTypeTag() == .Pointer) "&" else "";
25232567
2524 const local = try f.allocLocal(inst_ty, .Const);2568 const local = try f.allocLocal(inst_ty, .Const);
2525 try writer.print(" = {s}(", .{maybe_addrof});2569 try writer.print(" = {s}(", .{maybe_addrof});
test/behavior.zig+4-1
...@@ -41,6 +41,8 @@ test {...@@ -41,6 +41,8 @@ test {
41 _ = @import("behavior/member_func.zig");41 _ = @import("behavior/member_func.zig");
42 _ = @import("behavior/translate_c_macros.zig");42 _ = @import("behavior/translate_c_macros.zig");
43 _ = @import("behavior/generics.zig");43 _ = @import("behavior/generics.zig");
44 _ = @import("behavior/error.zig");
45 _ = @import("behavior/optional.zig");
4446
45 if (builtin.object_format != .c) {47 if (builtin.object_format != .c) {
46 // Tests that pass for stage1 and stage2 but not the C backend.48 // Tests that pass for stage1 and stage2 but not the C backend.
...@@ -55,6 +57,7 @@ test {...@@ -55,6 +57,7 @@ test {
55 _ = @import("behavior/bugs/2006.zig");57 _ = @import("behavior/bugs/2006.zig");
56 _ = @import("behavior/bugs/3112.zig");58 _ = @import("behavior/bugs/3112.zig");
57 _ = @import("behavior/cast_llvm.zig");59 _ = @import("behavior/cast_llvm.zig");
60 _ = @import("behavior/error.zig");
58 _ = @import("behavior/eval.zig");61 _ = @import("behavior/eval.zig");
59 _ = @import("behavior/floatop.zig");62 _ = @import("behavior/floatop.zig");
60 _ = @import("behavior/fn.zig");63 _ = @import("behavior/fn.zig");
...@@ -63,7 +66,7 @@ test {...@@ -63,7 +66,7 @@ test {
63 _ = @import("behavior/math.zig");66 _ = @import("behavior/math.zig");
64 _ = @import("behavior/maximum_minimum.zig");67 _ = @import("behavior/maximum_minimum.zig");
65 _ = @import("behavior/null_llvm.zig");68 _ = @import("behavior/null_llvm.zig");
66 _ = @import("behavior/optional.zig");69 _ = @import("behavior/optional_llvm.zig");
67 _ = @import("behavior/popcount.zig");70 _ = @import("behavior/popcount.zig");
68 _ = @import("behavior/saturating_arithmetic.zig");71 _ = @import("behavior/saturating_arithmetic.zig");
69 _ = @import("behavior/sizeof_and_typeof.zig");72 _ = @import("behavior/sizeof_and_typeof.zig");
test/behavior/optional.zig-19
...@@ -18,25 +18,6 @@ test "passing an optional integer as a parameter" {...@@ -18,25 +18,6 @@ test "passing an optional integer as a parameter" {
18 comptime try expect(S.entry());18 comptime try expect(S.entry());
19}19}
2020
21test "self-referential struct through a slice of optional" {
22 const S = struct {
23 const Node = struct {
24 children: []?Node,
25 data: ?u8,
26
27 fn new() Node {
28 return Node{
29 .children = undefined,
30 .data = null,
31 };
32 }
33 };
34 };
35
36 var n = S.Node.new();
37 try expect(n.data == null);
38}
39
40pub const EmptyStruct = struct {};21pub const EmptyStruct = struct {};
4122
42test "optional pointer to size zero struct" {23test "optional pointer to size zero struct" {
test/behavior/optional_llvm.zig created+23
...@@ -0,0 +1,23 @@
1const std = @import("std");
2const testing = std.testing;
3const expect = testing.expect;
4const expectEqual = testing.expectEqual;
5
6test "self-referential struct through a slice of optional" {
7 const S = struct {
8 const Node = struct {
9 children: []?Node,
10 data: ?u8,
11
12 fn new() Node {
13 return Node{
14 .children = undefined,
15 .data = null,
16 };
17 }
18 };
19 };
20
21 var n = S.Node.new();
22 try expect(n.data == null);
23}