authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-07 20:47:21-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-07-07 20:47:21-07:00
logc2e66d9bab396a69514ec7c3c41fb0404e542f21
treefbc8b06cd459cd5e4ad2b39384c9d5d73e25c8ee
parent5c8bd443d92c6306f60857720103ae46ca7b8b3e

stage2: basic inferred error set support

* Inferred error sets are stored in the return Type of the function, owned by the Module.Fn. So it cleans up that memory in deinit(). * Sema: update the inferred error set in zirRetErrValue - Update relevant code in wrapErrorUnion * C backend: improve some some instructions to take advantage of liveness analysis to avoid being emitted when unused. * C backend: when an error union has a payload type with no runtime bits, emit the error union as the same type as the error set.

5 files changed, 113 insertions(+), 21 deletions(-)

src/Module.zig+13-2
......@@ -777,8 +777,19 @@ pub const Fn = struct {
777777 }
778778
779779 pub fn deinit(func: *Fn, gpa: *Allocator) void {
780 _ = func;
781 _ = gpa;
780 if (func.getInferredErrorSet()) |map| {
781 map.deinit(gpa);
782 }
783 }
784
785 pub fn getInferredErrorSet(func: *Fn) ?*std.StringHashMapUnmanaged(void) {
786 const ret_ty = func.owner_decl.ty.fnReturnType();
787 if (ret_ty.zigTypeTag() == .ErrorUnion) {
788 if (ret_ty.errorUnionSet().castTag(.error_set_inferred)) |payload| {
789 return &payload.data.map;
790 }
791 }
792 return null;
782793 }
783794};
784795
src/Sema.zig+18-7
......@@ -3139,7 +3139,10 @@ fn funcCommon(
31393139 }
31403140
31413141 const return_type = if (!inferred_error_set) bare_return_type else blk: {
3142 const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, new_func);
3142 const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, .{
3143 .func = new_func,
3144 .map = .{},
3145 });
31433146 break :blk try Type.Tag.error_union.create(sema.arena, .{
31443147 .error_set = error_set_ty,
31453148 .payload = bare_return_type,
......@@ -5424,12 +5427,8 @@ fn zirRetErrValue(
54245427
54255428 // Add the error tag to the inferred error set of the in-scope function.
54265429 if (sema.func) |func| {
5427 const fn_ty = func.owner_decl.ty;
5428 const fn_ret_ty = fn_ty.fnReturnType();
5429 if (fn_ret_ty.zigTypeTag() == .ErrorUnion and
5430 fn_ret_ty.errorUnionSet().tag() == .error_set_inferred)
5431 {
5432 return sema.mod.fail(&block.base, src, "TODO: Sema.zirRetErrValue", .{});
5430 if (func.getInferredErrorSet()) |map| {
5431 _ = try map.getOrPut(sema.gpa, err_name);
54335432 }
54345433 }
54355434 // Return the error code from the function.
......@@ -7535,6 +7534,18 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst
75357534 );
75367535 }
75377536 },
7537 .error_set_inferred => {
7538 const expected_name = val.castTag(.@"error").?.data.name;
7539 const map = &err_union.data.error_set.castTag(.error_set_inferred).?.data.map;
7540 if (!map.contains(expected_name)) {
7541 return sema.mod.fail(
7542 &block.base,
7543 inst.src,
7544 "expected type '{}', found type '{}'",
7545 .{ err_union.data.error_set, inst.ty },
7546 );
7547 }
7548 },
75387549 else => unreachable,
75397550 }
75407551
src/codegen/c.zig+57-10
......@@ -360,6 +360,12 @@ pub const DeclGen = struct {
360360 const error_type = t.errorUnionSet();
361361 const payload_type = t.errorUnionChild();
362362 const data = val.castTag(.error_union).?.data;
363
364 if (!payload_type.hasCodeGenBits()) {
365 // We use the error type directly as the type.
366 return dg.renderValue(writer, error_type, data);
367 }
368
363369 try writer.writeByte('(');
364370 try dg.renderType(writer, t);
365371 try writer.writeAll("){");
......@@ -604,6 +610,10 @@ pub const DeclGen = struct {
604610 const child_type = t.errorUnionChild();
605611 const err_set_type = t.errorUnionSet();
606612
613 if (!child_type.hasCodeGenBits()) {
614 return dg.renderType(w, err_set_type);
615 }
616
607617 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
608618 defer buffer.deinit();
609619 const bw = buffer.writer();
......@@ -613,7 +623,7 @@ pub const DeclGen = struct {
613623 try bw.writeAll(" payload; uint16_t error; } ");
614624 const name_index = buffer.items.len;
615625 if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {
616 const func = inf_err_set_payload.data;
626 const func = inf_err_set_payload.data.func;
617627 try bw.print("zig_E_{s};\n", .{func.owner_decl.name});
618628 } else {
619629 try bw.print("zig_E_{s}_{s};\n", .{
......@@ -895,10 +905,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
895905 .ref => try genRef(o, inst.castTag(.ref).?),
896906 .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?),
897907
898 .is_err => try genIsErr(o, inst.castTag(.is_err).?, "", "!="),
899 .is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", "=="),
900 .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "[0]", "!="),
901 .is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "[0]", "=="),
908 .is_err => try genIsErr(o, inst.castTag(.is_err).?, "", ".", "!="),
909 .is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", ".", "=="),
910 .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "*", "->", "!="),
911 .is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "*", "->", "=="),
902912
903913 .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),
904914 .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),
......@@ -1384,9 +1394,25 @@ fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue {
13841394
13851395// *(E!T) -> E NOT *E
13861396fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
1397 if (inst.base.isUnused())
1398 return CValue.none;
1399
13871400 const writer = o.writer();
13881401 const operand = try o.resolveInst(inst.operand);
13891402
1403 const payload_ty = inst.operand.ty.errorUnionChild();
1404 if (!payload_ty.hasCodeGenBits()) {
1405 if (inst.operand.ty.zigTypeTag() == .Pointer) {
1406 const local = try o.allocLocal(inst.base.ty, .Const);
1407 try writer.writeAll(" = *");
1408 try o.writeCValue(writer, operand);
1409 try writer.writeAll(";\n");
1410 return local;
1411 } else {
1412 return operand;
1413 }
1414 }
1415
13901416 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";
13911417
13921418 const local = try o.allocLocal(inst.base.ty, .Const);
......@@ -1396,10 +1422,19 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
13961422 try writer.print("){s}error;\n", .{maybe_deref});
13971423 return local;
13981424}
1425
13991426fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
1427 if (inst.base.isUnused())
1428 return CValue.none;
1429
14001430 const writer = o.writer();
14011431 const operand = try o.resolveInst(inst.operand);
14021432
1433 const payload_ty = inst.operand.ty.errorUnionChild();
1434 if (!payload_ty.hasCodeGenBits()) {
1435 return CValue.none;
1436 }
1437
14031438 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";
14041439 const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else "";
14051440
......@@ -1448,14 +1483,26 @@ fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
14481483 return local;
14491484}
14501485
1451fn genIsErr(o: *Object, inst: *Inst.UnOp, deref_suffix: []const u8, op_str: []const u8) !CValue {
1486fn genIsErr(
1487 o: *Object,
1488 inst: *Inst.UnOp,
1489 deref_prefix: [*:0]const u8,
1490 deref_suffix: [*:0]const u8,
1491 op_str: [*:0]const u8,
1492) !CValue {
14521493 const writer = o.writer();
14531494 const operand = try o.resolveInst(inst.operand);
1454
14551495 const local = try o.allocLocal(Type.initTag(.bool), .Const);
1456 try writer.writeAll(" = (");
1457 try o.writeCValue(writer, operand);
1458 try writer.print("){s}.error {s} 0;\n", .{ deref_suffix, op_str });
1496 const payload_ty = inst.operand.ty.errorUnionChild();
1497 if (!payload_ty.hasCodeGenBits()) {
1498 try writer.print(" = {s}", .{deref_prefix});
1499 try o.writeCValue(writer, operand);
1500 try writer.print(" {s} 0;\n", .{op_str});
1501 } else {
1502 try writer.writeAll(" = ");
1503 try o.writeCValue(writer, operand);
1504 try writer.print("{s}error {s} 0;\n", .{ deref_suffix, op_str });
1505 }
14591506 return local;
14601507}
14611508
src/type.zig+5-2
......@@ -1041,7 +1041,7 @@ pub const Type = extern union {
10411041 return writer.writeAll(std.mem.spanZ(error_set.owner_decl.name));
10421042 },
10431043 .error_set_inferred => {
1044 const func = ty.castTag(.error_set_inferred).?.data;
1044 const func = ty.castTag(.error_set_inferred).?.data.func;
10451045 return writer.print("(inferred error set of {s})", .{func.owner_decl.name});
10461046 },
10471047 .error_set_single => {
......@@ -3154,7 +3154,10 @@ pub const Type = extern union {
31543154 pub const base_tag = Tag.error_set_inferred;
31553155
31563156 base: Payload = Payload{ .tag = base_tag },
3157 data: *Module.Fn,
3157 data: struct {
3158 func: *Module.Fn,
3159 map: std.StringHashMapUnmanaged(void),
3160 },
31583161 };
31593162
31603163 pub const Pointer = struct {
test/stage2/cbe.zig+20
......@@ -804,6 +804,26 @@ pub fn addCases(ctx: *TestContext) !void {
804804 });
805805 }
806806
807 {
808 var case = ctx.exeFromCompiledC("inferred error sets", .{});
809
810 case.addCompareOutput(
811 \\pub export fn main() c_int {
812 \\ if (foo()) |_| {
813 \\ @panic("test fail");
814 \\ } else |err| {
815 \\ if (err != error.ItBroke) {
816 \\ @panic("test fail");
817 \\ }
818 \\ }
819 \\ return 0;
820 \\}
821 \\fn foo() !void {
822 \\ return error.ItBroke;
823 \\}
824 , "");
825 }
826
807827 ctx.h("simple header", linux_x64,
808828 \\export fn start() void{}
809829 ,