authorgravatar for jacoblevgw@gmail.comJacob G-W <jacoblevgw@gmail.com> 2021-03-26 17:54:41-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-28 18:22:01-07:00
log0005b346375f1fbe7bc42c22d658e3218bbd599d
treed88211b36f2be7689a138bf0d5f1a2e62f695323
parentf80f8a7a7835db5f8b13aab23b4ee79e88c25e63

stage2: implement sema for @errorToInt and @intToError


10 files changed, 177 insertions(+), 7 deletions(-)

src/AstGen.zig+12-2
......@@ -1237,6 +1237,8 @@ fn blockExprStmts(
12371237 .bit_not,
12381238 .error_set,
12391239 .error_value,
1240 .error_to_int,
1241 .int_to_error,
12401242 .slice_start,
12411243 .slice_end,
12421244 .slice_sentinel,
......@@ -3370,6 +3372,16 @@ fn builtinCall(
33703372 const result = try gz.addUnNode(.import, target, node);
33713373 return rvalue(gz, scope, rl, result, node);
33723374 },
3375 .error_to_int => {
3376 const target = try expr(gz, scope, .none, params[0]);
3377 const result = try gz.addUnNode(.error_to_int, target, node);
3378 return rvalue(gz, scope, rl, result, node);
3379 },
3380 .int_to_error => {
3381 const target = try expr(gz, scope, .{ .ty = .u16_type }, params[0]);
3382 const result = try gz.addUnNode(.int_to_error, target, node);
3383 return rvalue(gz, scope, rl, result, node);
3384 },
33733385 .compile_error => {
33743386 const target = try expr(gz, scope, .none, params[0]);
33753387 const result = try gz.addUnNode(.compile_error, target, node);
......@@ -3439,7 +3451,6 @@ fn builtinCall(
34393451 .enum_to_int,
34403452 .error_name,
34413453 .error_return_trace,
3442 .error_to_int,
34433454 .err_set_cast,
34443455 .@"export",
34453456 .fence,
......@@ -3448,7 +3459,6 @@ fn builtinCall(
34483459 .has_decl,
34493460 .has_field,
34503461 .int_to_enum,
3451 .int_to_error,
34523462 .int_to_float,
34533463 .int_to_ptr,
34543464 .memcpy,
src/Compilation.zig+3
......@@ -941,6 +941,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
941941 };
942942
943943 const module = try arena.create(Module);
944 errdefer module.deinit();
944945 module.* = .{
945946 .gpa = gpa,
946947 .comp = comp,
......@@ -948,7 +949,9 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
948949 .root_scope = root_scope,
949950 .zig_cache_artifact_directory = zig_cache_artifact_directory,
950951 .emit_h = options.emit_h,
952 .error_name_list = try std.ArrayListUnmanaged([]const u8).initCapacity(gpa, 1),
951953 };
954 module.error_name_list.appendAssumeCapacity("(no error)");
952955 break :blk module;
953956 } else blk: {
954957 if (options.emit_h != null) return error.NoZigModuleForCHeader;
src/Module.zig+25-3
......@@ -80,6 +80,9 @@ deletion_set: ArrayListUnmanaged(*Decl) = .{},
8080/// Error tags and their values, tag names are duped with mod.gpa.
8181global_error_set: std.StringHashMapUnmanaged(u16) = .{},
8282
83/// error u16 -> []const u8 for fast lookups for @intToError at comptime
84error_name_list: ArrayListUnmanaged([]const u8) = .{},
85
8386/// Keys are fully qualified paths
8487import_table: std.StringArrayHashMapUnmanaged(*Scope.File) = .{},
8588
......@@ -1570,7 +1573,22 @@ pub const SrcLoc = struct {
15701573 const token_starts = tree.tokens.items(.start);
15711574 return token_starts[tok_index];
15721575 },
1573 .node_offset_builtin_call_arg0 => @panic("TODO"),
1576 .node_offset_builtin_call_arg0 => |node_off| {
1577 const decl = src_loc.container.decl;
1578 const tree = decl.container.file_scope.base.tree();
1579 const node_datas = tree.nodes.items(.data);
1580 const node_tags = tree.nodes.items(.tag);
1581 const node = decl.relativeToNodeIndex(node_off);
1582 const param = switch (node_tags[node]) {
1583 .builtin_call_two, .builtin_call_two_comma => node_datas[node].lhs,
1584 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs],
1585 else => unreachable,
1586 };
1587 const main_tokens = tree.nodes.items(.main_token);
1588 const tok_index = main_tokens[param];
1589 const token_starts = tree.tokens.items(.start);
1590 return token_starts[tok_index];
1591 },
15741592 .node_offset_builtin_call_arg1 => @panic("TODO"),
15751593 .node_offset_builtin_call_argn => unreachable, // Handled specially in `Sema`.
15761594 .node_offset_array_access_index => @panic("TODO"),
......@@ -1893,6 +1911,8 @@ pub fn deinit(mod: *Module) void {
18931911 }
18941912 mod.global_error_set.deinit(gpa);
18951913
1914 mod.error_name_list.deinit(gpa);
1915
18961916 for (mod.import_table.items()) |entry| {
18971917 entry.value.destroy(gpa);
18981918 }
......@@ -3346,10 +3366,12 @@ pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged
33463366 const gop = try mod.global_error_set.getOrPut(mod.gpa, name);
33473367 if (gop.found_existing)
33483368 return gop.entry.*;
3349 errdefer mod.global_error_set.removeAssertDiscard(name);
33503369
3370 errdefer mod.global_error_set.removeAssertDiscard(name);
3371 try mod.error_name_list.ensureCapacity(mod.gpa, mod.error_name_list.items.len + 1);
33513372 gop.entry.key = try mod.gpa.dupe(u8, name);
3352 gop.entry.value = @intCast(u16, mod.global_error_set.count() - 1);
3373 gop.entry.value = @intCast(u16, mod.error_name_list.items.len);
3374 mod.error_name_list.appendAssumeCapacity(gop.entry.key);
33533375 return gop.entry.*;
33543376}
33553377
src/Sema.zig+62
......@@ -177,6 +177,8 @@ pub fn analyzeBody(
177177 .error_set => try sema.zirErrorSet(block, inst),
178178 .error_union_type => try sema.zirErrorUnionType(block, inst),
179179 .error_value => try sema.zirErrorValue(block, inst),
180 .error_to_int => try sema.zirErrorToInt(block, inst),
181 .int_to_error => try sema.zirIntToError(block, inst),
180182 .field_ptr => try sema.zirFieldPtr(block, inst),
181183 .field_ptr_named => try sema.zirFieldPtrNamed(block, inst),
182184 .field_val => try sema.zirFieldVal(block, inst),
......@@ -1460,6 +1462,65 @@ fn zirErrorValue(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
14601462 });
14611463}
14621464
1465fn zirErrorToInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1466 const tracy = trace(@src());
1467 defer tracy.end();
1468
1469 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1470 const src = inst_data.src();
1471 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1472 const op = try sema.resolveInst(inst_data.operand);
1473 const op_coerced = try sema.coerce(block, Type.initTag(.anyerror), op, operand_src);
1474
1475 if (op_coerced.value()) |val| {
1476 const payload = try sema.arena.create(Value.Payload.U64);
1477 payload.* = .{
1478 .base = .{ .tag = .int_u64 },
1479 .data = (try sema.mod.getErrorValue(val.castTag(.@"error").?.data.name)).value,
1480 };
1481 return sema.mod.constInst(sema.arena, src, .{
1482 .ty = Type.initTag(.u16),
1483 .val = Value.initPayload(&payload.base),
1484 });
1485 }
1486
1487 try sema.requireRuntimeBlock(block, src);
1488 return block.addUnOp(src, Type.initTag(.u16), .error_to_int, op_coerced);
1489}
1490
1491fn zirIntToError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
1492 const tracy = trace(@src());
1493 defer tracy.end();
1494
1495 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
1496 const src = inst_data.src();
1497 const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
1498
1499 const op = try sema.resolveInst(inst_data.operand);
1500
1501 if (try sema.resolveDefinedValue(block, operand_src, op)) |value| {
1502 const int = value.toUnsignedInt();
1503 if (int > sema.mod.global_error_set.count() or int == 0)
1504 return sema.mod.fail(&block.base, operand_src, "integer value {d} represents no error", .{int});
1505 const payload = try sema.arena.create(Value.Payload.Error);
1506 payload.* = .{
1507 .base = .{ .tag = .@"error" },
1508 .data = .{ .name = sema.mod.error_name_list.items[int] },
1509 };
1510 return sema.mod.constInst(sema.arena, src, .{
1511 .ty = Type.initTag(.anyerror),
1512 .val = Value.initPayload(&payload.base),
1513 });
1514 }
1515 try sema.requireRuntimeBlock(block, src);
1516 if (block.wantSafety()) {
1517 return sema.mod.fail(&block.base, src, "TODO: get max errors in compilation", .{});
1518 // const is_gt_max = @panic("TODO get max errors in compilation");
1519 // try sema.addSafetyCheck(block, is_gt_max, .invalid_error_code);
1520 }
1521 return block.addUnOp(src, Type.initTag(.anyerror), .int_to_error, op);
1522}
1523
14631524fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
14641525 const tracy = trace(@src());
14651526 defer tracy.end();
......@@ -3242,6 +3303,7 @@ pub const PanicId = enum {
32423303 unreach,
32433304 unwrap_null,
32443305 unwrap_errunion,
3306 invalid_error_code,
32453307};
32463308
32473309fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
src/codegen.zig+10
......@@ -898,6 +898,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
898898 .is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?),
899899 .is_err => return self.genIsErr(inst.castTag(.is_err).?),
900900 .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?),
901 .error_to_int => return self.genErrorToInt(inst.castTag(.error_to_int).?),
902 .int_to_error => return self.genIntToError(inst.castTag(.int_to_error).?),
901903 .load => return self.genLoad(inst.castTag(.load).?),
902904 .loop => return self.genLoop(inst.castTag(.loop).?),
903905 .not => return self.genNot(inst.castTag(.not).?),
......@@ -2557,6 +2559,14 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
25572559 return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{});
25582560 }
25592561
2562 fn genErrorToInt(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
2563 return self.resolveInst(inst.operand);
2564 }
2565
2566 fn genIntToError(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
2567 return self.resolveInst(inst.operand);
2568 }
2569
25602570 fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {
25612571 // A loop is a setup to be able to jump back to the beginning.
25622572 const start_index = self.code.items.len;
src/codegen/c.zig+10
......@@ -569,6 +569,8 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
569569 .optional_payload_ptr => try genOptionalPayload(o, inst.castTag(.optional_payload_ptr).?),
570570 .is_err => try genIsErr(o, inst.castTag(.is_err).?),
571571 .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?),
572 .error_to_int => try genErrorToInt(o, inst.castTag(.error_to_int).?),
573 .int_to_error => try genIntToError(o, inst.castTag(.int_to_error).?),
572574 .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),
573575 .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),
574576 .unwrap_errunion_payload_ptr => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload_ptr).?),
......@@ -1072,6 +1074,14 @@ fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue {
10721074 return local;
10731075}
10741076
1077fn genIntToError(o: *Object, inst: *Inst.UnOp) !CValue {
1078 return o.resolveInst(inst.operand);
1079}
1080
1081fn genErrorToInt(o: *Object, inst: *Inst.UnOp) !CValue {
1082 return o.resolveInst(inst.operand);
1083}
1084
10751085fn IndentWriter(comptime UnderlyingWriter: type) type {
10761086 return struct {
10771087 const Self = @This();
src/ir.zig+10
......@@ -92,6 +92,10 @@ pub const Inst = struct {
9292 is_err,
9393 /// *E!T => bool
9494 is_err_ptr,
95 /// E => u16
96 error_to_int,
97 /// u16 => E
98 int_to_error,
9599 bool_and,
96100 bool_or,
97101 /// Read a value from a pointer.
......@@ -152,6 +156,8 @@ pub const Inst = struct {
152156 .is_null_ptr,
153157 .is_err,
154158 .is_err_ptr,
159 .int_to_error,
160 .error_to_int,
155161 .ptrtoint,
156162 .floatcast,
157163 .intcast,
......@@ -696,6 +702,8 @@ const DumpTzir = struct {
696702 .is_null_ptr,
697703 .is_err,
698704 .is_err_ptr,
705 .error_to_int,
706 .int_to_error,
699707 .ptrtoint,
700708 .floatcast,
701709 .intcast,
......@@ -817,6 +825,8 @@ const DumpTzir = struct {
817825 .is_null_ptr,
818826 .is_err,
819827 .is_err_ptr,
828 .error_to_int,
829 .int_to_error,
820830 .ptrtoint,
821831 .floatcast,
822832 .intcast,
src/link/C.zig+1-2
......@@ -185,8 +185,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void {
185185 if (module.global_error_set.size == 0) break :render_errors;
186186 var it = module.global_error_set.iterator();
187187 while (it.next()) |entry| {
188 // + 1 because 0 represents no error
189 try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value + 1 });
188 try err_typedef_writer.print("#define zig_error_{s} {d}\n", .{ entry.key, entry.value });
190189 }
191190 try err_typedef_writer.writeByte('\n');
192191 }
src/zir.zig+8
......@@ -365,6 +365,10 @@ pub const Inst = struct {
365365 /// Make an integer type out of signedness and bit count.
366366 /// Payload is `int_type`
367367 int_type,
368 /// Convert an error type to `u16`
369 error_to_int,
370 /// Convert a `u16` to `anyerror`
371 int_to_error,
368372 /// Return a boolean false if an optional is null. `x != null`
369373 /// Uses the `un_node` field.
370374 is_non_null,
......@@ -728,6 +732,8 @@ pub const Inst = struct {
728732 .err_union_payload_unsafe_ptr,
729733 .err_union_code,
730734 .err_union_code_ptr,
735 .error_to_int,
736 .int_to_error,
731737 .ptr_type,
732738 .ptr_type_simple,
733739 .ensure_err_payload_void,
......@@ -1414,6 +1420,8 @@ const Writer = struct {
14141420 .err_union_payload_unsafe_ptr,
14151421 .err_union_code,
14161422 .err_union_code_ptr,
1423 .int_to_error,
1424 .error_to_int,
14171425 .is_non_null,
14181426 .is_null,
14191427 .is_non_null_ptr,
test/stage2/cbe.zig+36
......@@ -54,6 +54,42 @@ pub fn addCases(ctx: *TestContext) !void {
5454 , "Hello, world!" ++ std.cstr.line_sep);
5555 }
5656
57 {
58 var case = ctx.exeFromCompiledC("@intToError", .{});
59
60 case.addCompareOutput(
61 \\pub export fn main() c_int {
62 \\ // comptime checks
63 \\ const a = error.A;
64 \\ const b = error.B;
65 \\ const c = @intToError(2);
66 \\ const d = @intToError(1);
67 \\ if (!(c == b)) unreachable;
68 \\ if (!(a == d)) unreachable;
69 \\ // runtime checks
70 \\ var x = error.A;
71 \\ var y = error.B;
72 \\ var z = @intToError(2);
73 \\ var f = @intToError(1);
74 \\ if (!(y == z)) unreachable;
75 \\ if (!(x == f)) unreachable;
76 \\ return 0;
77 \\}
78 , "");
79 case.addError(
80 \\pub export fn main() c_int {
81 \\ const c = @intToError(0);
82 \\ return 0;
83 \\}
84 , &.{":2:27: error: integer value 0 represents no error"});
85 case.addError(
86 \\pub export fn main() c_int {
87 \\ const c = @intToError(3);
88 \\ return 0;
89 \\}
90 , &.{":2:27: error: integer value 3 represents no error"});
91 }
92
5793 {
5894 var case = ctx.exeFromCompiledC("x86_64-linux inline assembly", linux_x64);
5995