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 {...@@ -777,8 +777,19 @@ pub const Fn = struct {
777 }777 }
778778
779 pub fn deinit(func: *Fn, gpa: *Allocator) void {779 pub fn deinit(func: *Fn, gpa: *Allocator) void {
780 _ = func;780 if (func.getInferredErrorSet()) |map| {
781 _ = gpa;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;
782 }793 }
783};794};
784795
src/Sema.zig+18-7
...@@ -3139,7 +3139,10 @@ fn funcCommon(...@@ -3139,7 +3139,10 @@ fn funcCommon(
3139 }3139 }
31403140
3141 const return_type = if (!inferred_error_set) bare_return_type else blk: {3141 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 });
3143 break :blk try Type.Tag.error_union.create(sema.arena, .{3146 break :blk try Type.Tag.error_union.create(sema.arena, .{
3144 .error_set = error_set_ty,3147 .error_set = error_set_ty,
3145 .payload = bare_return_type,3148 .payload = bare_return_type,
...@@ -5424,12 +5427,8 @@ fn zirRetErrValue(...@@ -5424,12 +5427,8 @@ fn zirRetErrValue(
54245427
5425 // Add the error tag to the inferred error set of the in-scope function.5428 // Add the error tag to the inferred error set of the in-scope function.
5426 if (sema.func) |func| {5429 if (sema.func) |func| {
5427 const fn_ty = func.owner_decl.ty;5430 if (func.getInferredErrorSet()) |map| {
5428 const fn_ret_ty = fn_ty.fnReturnType();5431 _ = try map.getOrPut(sema.gpa, err_name);
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", .{});
5433 }5432 }
5434 }5433 }
5435 // Return the error code from the function.5434 // Return the error code from the function.
...@@ -7535,6 +7534,18 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst...@@ -7535,6 +7534,18 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst
7535 );7534 );
7536 }7535 }
7537 },7536 },
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 },
7538 else => unreachable,7549 else => unreachable,
7539 }7550 }
75407551
src/codegen/c.zig+57-10
...@@ -360,6 +360,12 @@ pub const DeclGen = struct {...@@ -360,6 +360,12 @@ pub const DeclGen = struct {
360 const error_type = t.errorUnionSet();360 const error_type = t.errorUnionSet();
361 const payload_type = t.errorUnionChild();361 const payload_type = t.errorUnionChild();
362 const data = val.castTag(.error_union).?.data;362 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
363 try writer.writeByte('(');369 try writer.writeByte('(');
364 try dg.renderType(writer, t);370 try dg.renderType(writer, t);
365 try writer.writeAll("){");371 try writer.writeAll("){");
...@@ -604,6 +610,10 @@ pub const DeclGen = struct {...@@ -604,6 +610,10 @@ pub const DeclGen = struct {
604 const child_type = t.errorUnionChild();610 const child_type = t.errorUnionChild();
605 const err_set_type = t.errorUnionSet();611 const err_set_type = t.errorUnionSet();
606612
613 if (!child_type.hasCodeGenBits()) {
614 return dg.renderType(w, err_set_type);
615 }
616
607 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);617 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
608 defer buffer.deinit();618 defer buffer.deinit();
609 const bw = buffer.writer();619 const bw = buffer.writer();
...@@ -613,7 +623,7 @@ pub const DeclGen = struct {...@@ -613,7 +623,7 @@ pub const DeclGen = struct {
613 try bw.writeAll(" payload; uint16_t error; } ");623 try bw.writeAll(" payload; uint16_t error; } ");
614 const name_index = buffer.items.len;624 const name_index = buffer.items.len;
615 if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {625 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;
617 try bw.print("zig_E_{s};\n", .{func.owner_decl.name});627 try bw.print("zig_E_{s};\n", .{func.owner_decl.name});
618 } else {628 } else {
619 try bw.print("zig_E_{s}_{s};\n", .{629 try bw.print("zig_E_{s}_{s};\n", .{
...@@ -895,10 +905,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi...@@ -895,10 +905,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
895 .ref => try genRef(o, inst.castTag(.ref).?),905 .ref => try genRef(o, inst.castTag(.ref).?),
896 .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?),906 .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?),
897907
898 .is_err => try genIsErr(o, inst.castTag(.is_err).?, "", "!="),908 .is_err => try genIsErr(o, inst.castTag(.is_err).?, "", ".", "!="),
899 .is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", "=="),909 .is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", ".", "=="),
900 .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "[0]", "!="),910 .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "*", "->", "!="),
901 .is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "[0]", "=="),911 .is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "*", "->", "=="),
902912
903 .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),913 .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),
904 .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),914 .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),
...@@ -1384,9 +1394,25 @@ fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue {...@@ -1384,9 +1394,25 @@ fn genStructFieldPtr(o: *Object, inst: *Inst.StructFieldPtr) !CValue {
13841394
1385// *(E!T) -> E NOT *E1395// *(E!T) -> E NOT *E
1386fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {1396fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
1397 if (inst.base.isUnused())
1398 return CValue.none;
1399
1387 const writer = o.writer();1400 const writer = o.writer();
1388 const operand = try o.resolveInst(inst.operand);1401 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
1390 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";1416 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";
13911417
1392 const local = try o.allocLocal(inst.base.ty, .Const);1418 const local = try o.allocLocal(inst.base.ty, .Const);
...@@ -1396,10 +1422,19 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1396,10 +1422,19 @@ fn genUnwrapErrUnionErr(o: *Object, inst: *Inst.UnOp) !CValue {
1396 try writer.print("){s}error;\n", .{maybe_deref});1422 try writer.print("){s}error;\n", .{maybe_deref});
1397 return local;1423 return local;
1398}1424}
1425
1399fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {1426fn genUnwrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
1427 if (inst.base.isUnused())
1428 return CValue.none;
1429
1400 const writer = o.writer();1430 const writer = o.writer();
1401 const operand = try o.resolveInst(inst.operand);1431 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
1403 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";1438 const maybe_deref = if (inst.operand.ty.zigTypeTag() == .Pointer) "->" else ".";
1404 const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else "";1439 const maybe_addrof = if (inst.base.ty.zigTypeTag() == .Pointer) "&" else "";
14051440
...@@ -1448,14 +1483,26 @@ fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {...@@ -1448,14 +1483,26 @@ fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
1448 return local;1483 return local;
1449}1484}
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 {
1452 const writer = o.writer();1493 const writer = o.writer();
1453 const operand = try o.resolveInst(inst.operand);1494 const operand = try o.resolveInst(inst.operand);
1454
1455 const local = try o.allocLocal(Type.initTag(.bool), .Const);1495 const local = try o.allocLocal(Type.initTag(.bool), .Const);
1456 try writer.writeAll(" = (");1496 const payload_ty = inst.operand.ty.errorUnionChild();
1457 try o.writeCValue(writer, operand);1497 if (!payload_ty.hasCodeGenBits()) {
1458 try writer.print("){s}.error {s} 0;\n", .{ deref_suffix, op_str });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 }
1459 return local;1506 return local;
1460}1507}
14611508
src/type.zig+5-2
...@@ -1041,7 +1041,7 @@ pub const Type = extern union {...@@ -1041,7 +1041,7 @@ pub const Type = extern union {
1041 return writer.writeAll(std.mem.spanZ(error_set.owner_decl.name));1041 return writer.writeAll(std.mem.spanZ(error_set.owner_decl.name));
1042 },1042 },
1043 .error_set_inferred => {1043 .error_set_inferred => {
1044 const func = ty.castTag(.error_set_inferred).?.data;1044 const func = ty.castTag(.error_set_inferred).?.data.func;
1045 return writer.print("(inferred error set of {s})", .{func.owner_decl.name});1045 return writer.print("(inferred error set of {s})", .{func.owner_decl.name});
1046 },1046 },
1047 .error_set_single => {1047 .error_set_single => {
...@@ -3154,7 +3154,10 @@ pub const Type = extern union {...@@ -3154,7 +3154,10 @@ pub const Type = extern union {
3154 pub const base_tag = Tag.error_set_inferred;3154 pub const base_tag = Tag.error_set_inferred;
31553155
3156 base: Payload = Payload{ .tag = base_tag },3156 base: Payload = Payload{ .tag = base_tag },
3157 data: *Module.Fn,3157 data: struct {
3158 func: *Module.Fn,
3159 map: std.StringHashMapUnmanaged(void),
3160 },
3158 };3161 };
31593162
3160 pub const Pointer = struct {3163 pub const Pointer = struct {
test/stage2/cbe.zig+20
...@@ -804,6 +804,26 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -804,6 +804,26 @@ pub fn addCases(ctx: *TestContext) !void {
804 });804 });
805 }805 }
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
807 ctx.h("simple header", linux_x64,827 ctx.h("simple header", linux_x64,
808 \\export fn start() void{}828 \\export fn start() void{}
809 ,829 ,