authorgravatar for jacoblevgw@gmail.comJacob G-W <jacoblevgw@gmail.com> 2020-12-31 17:10:49-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-02-25 16:41:16-08:00
log153c97ac9ec8deafb0777ae424f00695c18e3bd9
tree4bc0b2c23e2d4844252efd98f453a2100043b14f
parent7edb204edfa41e11776ac009da5a20fb1c907f5f

improve stage2 to allow catch at comptime:

* add error_union value tag. * add analyzeIsErr * add Value.isError * add TZIR wrap_errunion_payload and wrap_errunion_err for wrapping from T -> E!T and E -> E!T * add anlyzeInstUnwrapErrCode and analyzeInstUnwrapErr * add analyzeInstEnsureErrPayloadVoid: * add wrapErrorUnion * add comptime error comparison for tests * tests!

7 files changed, 479 insertions(+), 12 deletions(-)

src/Module.zig+63-3
...@@ -2871,7 +2871,15 @@ pub fn analyzeIsNull(...@@ -2871,7 +2871,15 @@ pub fn analyzeIsNull(
2871}2871}
28722872
2873pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst {2873pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst {
2874 return self.fail(scope, src, "TODO implement analysis of iserr", .{});2874 const ot = operand.ty.zigTypeTag();
2875 if (ot != .ErrorSet and ot != .ErrorUnion) return self.constBool(scope, src, false);
2876 if (ot == .ErrorSet) return self.constBool(scope, src, true);
2877 assert(ot == .ErrorUnion);
2878 if (operand.value()) |err_union| {
2879 return self.constBool(scope, src, err_union.getError() != null);
2880 }
2881 const b = try self.requireRuntimeBlock(scope, src);
2882 return self.addUnOp(b, src, Type.initTag(.bool), .is_err, operand);
2875}2883}
28762884
2877pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst {2885pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst {
...@@ -3174,6 +3182,52 @@ fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*In...@@ -3174,6 +3182,52 @@ fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*In
3174 return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst);3182 return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst);
3175}3183}
31763184
3185fn wrapErrorUnion(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
3186 // TODO deal with inferred error sets
3187 const err_union = dest_type.castTag(.error_union).?;
3188 if (inst.value()) |val| {
3189 const to_wrap = if (inst.ty.zigTypeTag() != .ErrorSet) blk: {
3190 _ = try self.coerce(scope, err_union.data.payload, inst);
3191 break :blk val;
3192 } else switch (err_union.data.error_set.tag()) {
3193 .anyerror => val,
3194 .error_set_single => blk: {
3195 const n = err_union.data.error_set.castTag(.error_set_single).?.data;
3196 if (!mem.eql(u8, val.castTag(.@"error").?.data.name, n))
3197 return self.fail(scope, inst.src, "expected type '{}', found type '{}'", .{ err_union.data.error_set, inst.ty });
3198 break :blk val;
3199 },
3200 .error_set => blk: {
3201 const f = err_union.data.error_set.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields;
3202 if (f.get(val.castTag(.@"error").?.data.name) == null)
3203 return self.fail(scope, inst.src, "expected type '{}', found type '{}'", .{ err_union.data.error_set, inst.ty });
3204 break :blk val;
3205 },
3206 else => unreachable,
3207 };
3208
3209 return self.constInst(scope, inst.src, .{
3210 .ty = dest_type,
3211 // creating a SubValue for the error_union payload
3212 .val = try Value.Tag.error_union.create(
3213 scope.arena(),
3214 to_wrap,
3215 ),
3216 });
3217 }
3218
3219 const b = try self.requireRuntimeBlock(scope, inst.src);
3220
3221 // we are coercing from E to E!T
3222 if (inst.ty.zigTypeTag() == .ErrorSet) {
3223 var coerced = try self.coerce(scope, err_union.data.error_set, inst);
3224 return self.addUnOp(b, inst.src, dest_type, .wrap_errunion_err, coerced);
3225 } else {
3226 var coerced = try self.coerce(scope, err_union.data.payload, inst);
3227 return self.addUnOp(b, inst.src, dest_type, .wrap_errunion_payload, coerced);
3228 }
3229}
3230
3177fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {3231fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
3178 const int_payload = try scope.arena().create(Type.Payload.Bits);3232 const int_payload = try scope.arena().create(Type.Payload.Bits);
3179 int_payload.* = .{3233 int_payload.* = .{
...@@ -3240,7 +3294,7 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty...@@ -3240,7 +3294,7 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty
3240 return chosen.ty;3294 return chosen.ty;
3241}3295}
32423296
3243pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {3297pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) InnerError!*Inst {
3244 // If the types are the same, we can return the operand.3298 // If the types are the same, we can return the operand.
3245 if (dest_type.eql(inst.ty))3299 if (dest_type.eql(inst.ty))
3246 return inst;3300 return inst;
...@@ -3274,6 +3328,11 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst...@@ -3274,6 +3328,11 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
3274 }3328 }
3275 }3329 }
32763330
3331 // T to E!T or E to E!T
3332 if (dest_type.tag() == .error_union) {
3333 return try self.wrapErrorUnion(scope, dest_type, inst);
3334 }
3335
3277 // Coercions where the source is a single pointer to an array.3336 // Coercions where the source is a single pointer to an array.
3278 src_array_ptr: {3337 src_array_ptr: {
3279 if (!inst.ty.isSinglePointer()) break :src_array_ptr;3338 if (!inst.ty.isSinglePointer()) break :src_array_ptr;
...@@ -3352,7 +3411,7 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst...@@ -3352,7 +3411,7 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
3352 return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty });3411 return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty });
3353}3412}
33543413
3355pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*Inst {3414pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) InnerError!?*Inst {
3356 const val = inst.value() orelse return null;3415 const val = inst.value() orelse return null;
3357 const src_zig_tag = inst.ty.zigTypeTag();3416 const src_zig_tag = inst.ty.zigTypeTag();
3358 const dst_zig_tag = dest_type.zigTypeTag();3417 const dst_zig_tag = dest_type.zigTypeTag();
...@@ -3843,6 +3902,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {...@@ -3843,6 +3902,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
3843pub const PanicId = enum {3902pub const PanicId = enum {
3844 unreach,3903 unreach,
3845 unwrap_null,3904 unwrap_null,
3905 unwrap_errunion,
3846};3906};
38473907
3848pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {3908pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
src/codegen.zig+62
...@@ -909,7 +909,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -909,7 +909,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
909 .unreach => return MCValue{ .unreach = {} },909 .unreach => return MCValue{ .unreach = {} },
910 .optional_payload => return self.genOptionalPayload(inst.castTag(.optional_payload).?),910 .optional_payload => return self.genOptionalPayload(inst.castTag(.optional_payload).?),
911 .optional_payload_ptr => return self.genOptionalPayloadPtr(inst.castTag(.optional_payload_ptr).?),911 .optional_payload_ptr => return self.genOptionalPayloadPtr(inst.castTag(.optional_payload_ptr).?),
912 .unwrap_errunion_err => return self.genUnwrapErrErr(inst.castTag(.unwrap_errunion_err).?),
913 .unwrap_errunion_payload => return self.genUnwrapErrPayload(inst.castTag(.unwrap_errunion_payload).?),
914 .unwrap_errunion_err_ptr => return self.genUnwrapErrErrPtr(inst.castTag(.unwrap_errunion_err_ptr).?),
915 .unwrap_errunion_payload_ptr => return self.genUnwrapErrPayloadPtr(inst.castTag(.unwrap_errunion_payload_ptr).?),
912 .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),916 .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
917 .wrap_errunion_payload => return self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?),
918 .wrap_errunion_err => return self.genWrapErrUnionErr(inst.castTag(.wrap_errunion_err).?),
913 .varptr => return self.genVarPtr(inst.castTag(.varptr).?),919 .varptr => return self.genVarPtr(inst.castTag(.varptr).?),
914 .xor => return self.genXor(inst.castTag(.xor).?),920 .xor => return self.genXor(inst.castTag(.xor).?),
915 }921 }
...@@ -1170,6 +1176,41 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1170,6 +1176,41 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1170 }1176 }
1171 }1177 }
11721178
1179 fn genUnwrapErrErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1180 // No side effects, so if it's unreferenced, do nothing.
1181 if (inst.base.isUnused())
1182 return MCValue.dead;
1183 switch (arch) {
1184 else => return self.fail(inst.base.src, "TODO implement unwrap error union error for {}", .{self.target.cpu.arch}),
1185 }
1186 }
1187
1188 fn genUnwrapErrPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1189 // No side effects, so if it's unreferenced, do nothing.
1190 if (inst.base.isUnused())
1191 return MCValue.dead;
1192 switch (arch) {
1193 else => return self.fail(inst.base.src, "TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}),
1194 }
1195 }
1196 // *(E!T) -> E
1197 fn genUnwrapErrErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1198 // No side effects, so if it's unreferenced, do nothing.
1199 if (inst.base.isUnused())
1200 return MCValue.dead;
1201 switch (arch) {
1202 else => return self.fail(inst.base.src, "TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch}),
1203 }
1204 }
1205 // *(E!T) -> *T
1206 fn genUnwrapErrPayloadPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1207 // No side effects, so if it's unreferenced, do nothing.
1208 if (inst.base.isUnused())
1209 return MCValue.dead;
1210 switch (arch) {
1211 else => return self.fail(inst.base.src, "TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch}),
1212 }
1213 }
1173 fn genWrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1214 fn genWrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1174 const optional_ty = inst.base.ty;1215 const optional_ty = inst.base.ty;
11751216
...@@ -1186,6 +1227,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1186,6 +1227,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1186 }1227 }
1187 }1228 }
11881229
1230 /// T to E!T
1231 fn genWrapErrUnionPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1232 // No side effects, so if it's unreferenced, do nothing.
1233 if (inst.base.isUnused())
1234 return MCValue.dead;
1235
1236 switch (arch) {
1237 else => return self.fail(inst.base.src, "TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}),
1238 }
1239 }
1240
1241 /// E to E!T
1242 fn genWrapErrUnionErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1243 // No side effects, so if it's unreferenced, do nothing.
1244 if (inst.base.isUnused())
1245 return MCValue.dead;
1246
1247 switch (arch) {
1248 else => return self.fail(inst.base.src, "TODO implement wrap errunion error for {}", .{self.target.cpu.arch}),
1249 }
1250 }
1189 fn genVarPtr(self: *Self, inst: *ir.Inst.VarPtr) !MCValue {1251 fn genVarPtr(self: *Self, inst: *ir.Inst.VarPtr) !MCValue {
1190 // No side effects, so if it's unreferenced, do nothing.1252 // No side effects, so if it's unreferenced, do nothing.
1191 if (inst.base.isUnused())1253 if (inst.base.isUnused())
src/ir.zig+18
...@@ -114,6 +114,18 @@ pub const Inst = struct {...@@ -114,6 +114,18 @@ pub const Inst = struct {
114 // *?T => *T114 // *?T => *T
115 optional_payload_ptr,115 optional_payload_ptr,
116 wrap_optional,116 wrap_optional,
117 /// E!T -> T
118 unwrap_errunion_payload,
119 /// E!T -> E
120 unwrap_errunion_err,
121 /// *(E!T) -> *T
122 unwrap_errunion_payload_ptr,
123 /// *(E!T) -> E
124 unwrap_errunion_err_ptr,
125 /// wrap from T to E!T
126 wrap_errunion_payload,
127 /// wrap from E to E!T
128 wrap_errunion_err,
117 xor,129 xor,
118 switchbr,130 switchbr,
119131
...@@ -143,6 +155,12 @@ pub const Inst = struct {...@@ -143,6 +155,12 @@ pub const Inst = struct {
143 .optional_payload,155 .optional_payload,
144 .optional_payload_ptr,156 .optional_payload_ptr,
145 .wrap_optional,157 .wrap_optional,
158 .unwrap_errunion_payload,
159 .unwrap_errunion_err,
160 .unwrap_errunion_payload_ptr,
161 .unwrap_errunion_err_ptr,
162 .wrap_errunion_payload,
163 .wrap_errunion_err,
146 => UnOp,164 => UnOp,
147165
148 .add,166 .add,
src/value.zig+116-2
...@@ -102,6 +102,7 @@ pub const Value = extern union {...@@ -102,6 +102,7 @@ pub const Value = extern union {
102 enum_literal,102 enum_literal,
103 error_set,103 error_set,
104 @"error",104 @"error",
105 error_union,
105 /// This is a special value that tracks a set of types that have been stored106 /// This is a special value that tracks a set of types that have been stored
106 /// to an inferred allocation. It does not support any of the normal value queries.107 /// to an inferred allocation. It does not support any of the normal value queries.
107 inferred_alloc,108 inferred_alloc,
...@@ -174,6 +175,7 @@ pub const Value = extern union {...@@ -174,6 +175,7 @@ pub const Value = extern union {
174175
175 .ref_val,176 .ref_val,
176 .repeated,177 .repeated,
178 .error_union,
177 => Payload.SubValue,179 => Payload.SubValue,
178180
179 .bytes,181 .bytes,
...@@ -388,9 +390,17 @@ pub const Value = extern union {...@@ -388,9 +390,17 @@ pub const Value = extern union {
388 return Value{ .ptr_otherwise = &new_payload.base };390 return Value{ .ptr_otherwise = &new_payload.base };
389 },391 },
390 .@"error" => return self.copyPayloadShallow(allocator, Payload.Error),392 .@"error" => return self.copyPayloadShallow(allocator, Payload.Error),
393 .error_union => {
394 const payload = self.castTag(.error_union).?;
395 const new_payload = try allocator.create(Payload.SubValue);
396 new_payload.* = .{
397 .base = payload.base,
398 .data = try payload.data.copy(allocator),
399 };
400 return Value{ .ptr_otherwise = &new_payload.base };
401 },
391402
392 .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),403 .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet),
393
394 .inferred_alloc => unreachable,404 .inferred_alloc => unreachable,
395 }405 }
396 }406 }
...@@ -510,6 +520,8 @@ pub const Value = extern union {...@@ -510,6 +520,8 @@ pub const Value = extern union {
510 return out_stream.writeAll("}");520 return out_stream.writeAll("}");
511 },521 },
512 .@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}),522 .@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}),
523 // TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that
524 .error_union => return out_stream.print("error_union_val({})", .{val.castTag(.error_union).?.data}),
513 .inferred_alloc => return out_stream.writeAll("(inferred allocation value)"),525 .inferred_alloc => return out_stream.writeAll("(inferred allocation value)"),
514 };526 };
515 }527 }
...@@ -622,6 +634,7 @@ pub const Value = extern union {...@@ -622,6 +634,7 @@ pub const Value = extern union {
622 .float_128,634 .float_128,
623 .enum_literal,635 .enum_literal,
624 .@"error",636 .@"error",
637 .error_union,
625 .empty_struct_value,638 .empty_struct_value,
626 .inferred_alloc,639 .inferred_alloc,
627 => unreachable,640 => unreachable,
...@@ -692,6 +705,7 @@ pub const Value = extern union {...@@ -692,6 +705,7 @@ pub const Value = extern union {
692 .empty_array,705 .empty_array,
693 .enum_literal,706 .enum_literal,
694 .error_set,707 .error_set,
708 .error_union,
695 .@"error",709 .@"error",
696 .empty_struct_value,710 .empty_struct_value,
697 .inferred_alloc,711 .inferred_alloc,
...@@ -779,6 +793,7 @@ pub const Value = extern union {...@@ -779,6 +793,7 @@ pub const Value = extern union {
779 .enum_literal,793 .enum_literal,
780 .error_set,794 .error_set,
781 .@"error",795 .@"error",
796 .error_union,
782 .empty_struct_value,797 .empty_struct_value,
783 .inferred_alloc,798 .inferred_alloc,
784 => unreachable,799 => unreachable,
...@@ -865,6 +880,7 @@ pub const Value = extern union {...@@ -865,6 +880,7 @@ pub const Value = extern union {
865 .enum_literal,880 .enum_literal,
866 .error_set,881 .error_set,
867 .@"error",882 .@"error",
883 .error_union,
868 .empty_struct_value,884 .empty_struct_value,
869 .inferred_alloc,885 .inferred_alloc,
870 => unreachable,886 => unreachable,
...@@ -979,6 +995,7 @@ pub const Value = extern union {...@@ -979,6 +995,7 @@ pub const Value = extern union {
979 .enum_literal,995 .enum_literal,
980 .error_set,996 .error_set,
981 .@"error",997 .@"error",
998 .error_union,
982 .empty_struct_value,999 .empty_struct_value,
983 .inferred_alloc,1000 .inferred_alloc,
984 => unreachable,1001 => unreachable,
...@@ -1069,6 +1086,7 @@ pub const Value = extern union {...@@ -1069,6 +1086,7 @@ pub const Value = extern union {
1069 .enum_literal,1086 .enum_literal,
1070 .error_set,1087 .error_set,
1071 .@"error",1088 .@"error",
1089 .error_union,
1072 .empty_struct_value,1090 .empty_struct_value,
1073 .inferred_alloc,1091 .inferred_alloc,
1074 => unreachable,1092 => unreachable,
...@@ -1228,6 +1246,7 @@ pub const Value = extern union {...@@ -1228,6 +1246,7 @@ pub const Value = extern union {
1228 .enum_literal,1246 .enum_literal,
1229 .error_set,1247 .error_set,
1230 .@"error",1248 .@"error",
1249 .error_union,
1231 .empty_struct_value,1250 .empty_struct_value,
1232 .inferred_alloc,1251 .inferred_alloc,
1233 => unreachable,1252 => unreachable,
...@@ -1305,6 +1324,7 @@ pub const Value = extern union {...@@ -1305,6 +1324,7 @@ pub const Value = extern union {
1305 .enum_literal,1324 .enum_literal,
1306 .error_set,1325 .error_set,
1307 .@"error",1326 .@"error",
1327 .error_union,
1308 .empty_struct_value,1328 .empty_struct_value,
1309 .inferred_alloc,1329 .inferred_alloc,
1310 => unreachable,1330 => unreachable,
...@@ -1543,7 +1563,10 @@ pub const Value = extern union {...@@ -1543,7 +1563,10 @@ pub const Value = extern union {
1543 hasher.update(payload.name);1563 hasher.update(payload.name);
1544 std.hash.autoHash(&hasher, payload.value);1564 std.hash.autoHash(&hasher, payload.value);
1545 },1565 },
15461566 .error_union => {
1567 const payload = self.castTag(.error_union).?.data;
1568 std.hash.autoHash(&hasher, payload.hash());
1569 },
1547 .inferred_alloc => unreachable,1570 .inferred_alloc => unreachable,
1548 }1571 }
1549 return hasher.final();1572 return hasher.final();
...@@ -1621,6 +1644,7 @@ pub const Value = extern union {...@@ -1621,6 +1644,7 @@ pub const Value = extern union {
1621 .enum_literal,1644 .enum_literal,
1622 .error_set,1645 .error_set,
1623 .@"error",1646 .@"error",
1647 .error_union,
1624 .empty_struct_value,1648 .empty_struct_value,
1625 .inferred_alloc,1649 .inferred_alloc,
1626 => unreachable,1650 => unreachable,
...@@ -1707,6 +1731,7 @@ pub const Value = extern union {...@@ -1707,6 +1731,7 @@ pub const Value = extern union {
1707 .enum_literal,1731 .enum_literal,
1708 .error_set,1732 .error_set,
1709 .@"error",1733 .@"error",
1734 .error_union,
1710 .empty_struct_value,1735 .empty_struct_value,
1711 .inferred_alloc,1736 .inferred_alloc,
1712 => unreachable,1737 => unreachable,
...@@ -1810,6 +1835,7 @@ pub const Value = extern union {...@@ -1810,6 +1835,7 @@ pub const Value = extern union {
1810 .enum_literal,1835 .enum_literal,
1811 .error_set,1836 .error_set,
1812 .@"error",1837 .@"error",
1838 .error_union,
1813 .empty_struct_value,1839 .empty_struct_value,
1814 => false,1840 => false,
18151841
...@@ -1820,6 +1846,93 @@ pub const Value = extern union {...@@ -1820,6 +1846,93 @@ pub const Value = extern union {
1820 };1846 };
1821 }1847 }
18221848
1849 /// Valid for all types. Asserts the value is not undefined and not unreachable.
1850 pub fn getError(self: Value) ?[]const u8 {
1851 return switch (self.tag()) {
1852 .ty,
1853 .int_type,
1854 .u8_type,
1855 .i8_type,
1856 .u16_type,
1857 .i16_type,
1858 .u32_type,
1859 .i32_type,
1860 .u64_type,
1861 .i64_type,
1862 .usize_type,
1863 .isize_type,
1864 .c_short_type,
1865 .c_ushort_type,
1866 .c_int_type,
1867 .c_uint_type,
1868 .c_long_type,
1869 .c_ulong_type,
1870 .c_longlong_type,
1871 .c_ulonglong_type,
1872 .c_longdouble_type,
1873 .f16_type,
1874 .f32_type,
1875 .f64_type,
1876 .f128_type,
1877 .c_void_type,
1878 .bool_type,
1879 .void_type,
1880 .type_type,
1881 .anyerror_type,
1882 .comptime_int_type,
1883 .comptime_float_type,
1884 .noreturn_type,
1885 .null_type,
1886 .undefined_type,
1887 .fn_noreturn_no_args_type,
1888 .fn_void_no_args_type,
1889 .fn_naked_noreturn_no_args_type,
1890 .fn_ccc_void_no_args_type,
1891 .single_const_pointer_to_comptime_int_type,
1892 .const_slice_u8_type,
1893 .enum_literal_type,
1894 .anyframe_type,
1895 .zero,
1896 .one,
1897 .null_value,
1898 .empty_array,
1899 .bool_true,
1900 .bool_false,
1901 .function,
1902 .extern_fn,
1903 .variable,
1904 .int_u64,
1905 .int_i64,
1906 .int_big_positive,
1907 .int_big_negative,
1908 .ref_val,
1909 .decl_ref,
1910 .elem_ptr,
1911 .bytes,
1912 .repeated,
1913 .float_16,
1914 .float_32,
1915 .float_64,
1916 .float_128,
1917 .void_value,
1918 .enum_literal,
1919 .error_set,
1920 .empty_struct_value,
1921 => null,
1922
1923 .error_union => {
1924 const data = self.castTag(.error_union).?.data;
1925 return if (data.tag() == .@"error")
1926 data.castTag(.@"error").?.data.name
1927 else
1928 null;
1929 },
1930 .@"error" => self.castTag(.@"error").?.data.name,
1931 .undef => unreachable,
1932 .unreachable_value => unreachable,
1933 .inferred_alloc => unreachable,
1934 };
1935 }
1823 /// Valid for all types. Asserts the value is not undefined.1936 /// Valid for all types. Asserts the value is not undefined.
1824 pub fn isFloat(self: Value) bool {1937 pub fn isFloat(self: Value) bool {
1825 return switch (self.tag()) {1938 return switch (self.tag()) {
...@@ -1908,6 +2021,7 @@ pub const Value = extern union {...@@ -1908,6 +2021,7 @@ pub const Value = extern union {
1908 .void_value,2021 .void_value,
1909 .enum_literal,2022 .enum_literal,
1910 .@"error",2023 .@"error",
2024 .error_union,
1911 .empty_struct_value,2025 .empty_struct_value,
1912 .null_value,2026 .null_value,
1913 => false,2027 => false,
src/zir.zig+12
...@@ -1622,6 +1622,12 @@ const DumpTzir = struct {...@@ -1622,6 +1622,12 @@ const DumpTzir = struct {
1622 .optional_payload,1622 .optional_payload,
1623 .optional_payload_ptr,1623 .optional_payload_ptr,
1624 .wrap_optional,1624 .wrap_optional,
1625 .wrap_errunion_payload,
1626 .wrap_errunion_err,
1627 .unwrap_errunion_payload,
1628 .unwrap_errunion_err,
1629 .unwrap_errunion_payload_ptr,
1630 .unwrap_errunion_err_ptr,
1625 => {1631 => {
1626 const un_op = inst.cast(ir.Inst.UnOp).?;1632 const un_op = inst.cast(ir.Inst.UnOp).?;
1627 try dtz.findConst(un_op.operand);1633 try dtz.findConst(un_op.operand);
...@@ -1733,6 +1739,12 @@ const DumpTzir = struct {...@@ -1733,6 +1739,12 @@ const DumpTzir = struct {
1733 .optional_payload,1739 .optional_payload,
1734 .optional_payload_ptr,1740 .optional_payload_ptr,
1735 .wrap_optional,1741 .wrap_optional,
1742 .wrap_errunion_err,
1743 .wrap_errunion_payload,
1744 .unwrap_errunion_err,
1745 .unwrap_errunion_payload,
1746 .unwrap_errunion_payload_ptr,
1747 .unwrap_errunion_err_ptr,
1736 => {1748 => {
1737 const un_op = inst.cast(ir.Inst.UnOp).?;1749 const un_op = inst.cast(ir.Inst.UnOp).?;
1738 const kinky = try dtz.writeInst(writer, un_op.operand);1750 const kinky = try dtz.writeInst(writer, un_op.operand);
src/zir_sema.zig+101-6
...@@ -1263,34 +1263,124 @@ fn zirOptionalPayload(...@@ -1263,34 +1263,124 @@ fn zirOptionalPayload(
1263fn zirErrUnionPayload(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {1263fn zirErrUnionPayload(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {
1264 const tracy = trace(@src());1264 const tracy = trace(@src());
1265 defer tracy.end();1265 defer tracy.end();
1266 return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.zirErrUnionPayload", .{});1266
1267 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
1268 if (operand.ty.zigTypeTag() != .ErrorUnion)
1269 return mod.fail(scope, operand.src, "expected error union type, found '{}'", .{operand.ty});
1270
1271 if (operand.value()) |val| {
1272 if (val.getError()) |name| {
1273 return mod.fail(scope, unwrap.base.src, "caught unexpected error '{s}'", .{name});
1274 }
1275 const data = val.castTag(.error_union).?.data;
1276 return mod.constInst(scope, unwrap.base.src, .{
1277 .ty = operand.ty.castTag(.error_union).?.data.payload,
1278 .val = data,
1279 });
1280 }
1281 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
1282 if (safety_check and mod.wantSafety(scope)) {
1283 const is_non_err = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .is_err, operand);
1284 try mod.addSafetyCheck(b, is_non_err, .unwrap_errunion);
1285 }
1286 return mod.addUnOp(b, unwrap.base.src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_payload, operand);
1267}1287}
12681288
1269/// Pointer in, pointer out1289/// Pointer in, pointer out
1270fn zirErrUnionPayloadPtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {1290fn zirErrUnionPayloadPtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {
1271 const tracy = trace(@src());1291 const tracy = trace(@src());
1272 defer tracy.end();1292 defer tracy.end();
1273 return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.zirErrUnionPayloadPtr", .{});1293
1294 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
1295 assert(operand.ty.zigTypeTag() == .Pointer);
1296
1297 if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)
1298 return mod.fail(scope, unwrap.base.src, "expected error union type, found {}", .{operand.ty.elemType()});
1299
1300 const operand_pointer_ty = try mod.simplePtrType(scope, unwrap.base.src, operand.ty.elemType().castTag(.error_union).?.data.payload, !operand.ty.isConstPtr(), .One);
1301
1302 if (operand.value()) |pointer_val| {
1303 const val = try pointer_val.pointerDeref(scope.arena());
1304 if (val.getError()) |name| {
1305 return mod.fail(scope, unwrap.base.src, "caught unexpected error '{s}'", .{name});
1306 }
1307 const data = val.castTag(.error_union).?.data;
1308 // The same Value represents the pointer to the error union and the payload.
1309 return mod.constInst(scope, unwrap.base.src, .{
1310 .ty = operand_pointer_ty,
1311 .val = try Value.Tag.ref_val.create(
1312 scope.arena(),
1313 data,
1314 ),
1315 });
1316 }
1317
1318 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
1319 if (safety_check and mod.wantSafety(scope)) {
1320 const is_non_err = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .is_err, operand);
1321 try mod.addSafetyCheck(b, is_non_err, .unwrap_errunion);
1322 }
1323 return mod.addUnOp(b, unwrap.base.src, operand_pointer_ty, .unwrap_errunion_payload_ptr, operand);
1274}1324}
12751325
1276/// Value in, value out1326/// Value in, value out
1277fn zirErrUnionCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {1327fn zirErrUnionCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {
1278 const tracy = trace(@src());1328 const tracy = trace(@src());
1279 defer tracy.end();1329 defer tracy.end();
1280 return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.zirErrUnionCode", .{});1330
1331 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
1332 if (operand.ty.zigTypeTag() != .ErrorUnion)
1333 return mod.fail(scope, unwrap.base.src, "expected error union type, found '{}'", .{operand.ty});
1334
1335 if (operand.value()) |val| {
1336 assert(val.getError() != null);
1337 const data = val.castTag(.error_union).?.data;
1338 return mod.constInst(scope, unwrap.base.src, .{
1339 .ty = operand.ty.castTag(.error_union).?.data.error_set,
1340 .val = data,
1341 });
1342 }
1343
1344 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
1345 return mod.addUnOp(b, unwrap.base.src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err, operand);
1281}1346}
12821347
1283/// Pointer in, value out1348/// Pointer in, value out
1284fn zirErrUnionCodePtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {1349fn zirErrUnionCodePtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {
1285 const tracy = trace(@src());1350 const tracy = trace(@src());
1286 defer tracy.end();1351 defer tracy.end();
1287 return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.zirErrUnionCodePtr", .{});1352
1353 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
1354 assert(operand.ty.zigTypeTag() == .Pointer);
1355
1356 if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)
1357 return mod.fail(scope, unwrap.base.src, "expected error union type, found {}", .{operand.ty.elemType()});
1358
1359 if (operand.value()) |pointer_val| {
1360 const val = try pointer_val.pointerDeref(scope.arena());
1361 assert(val.getError() != null);
1362 const data = val.castTag(.error_union).?.data;
1363 return mod.constInst(scope, unwrap.base.src, .{
1364 .ty = operand.ty.elemType().castTag(.error_union).?.data.error_set,
1365 .val = data,
1366 });
1367 }
1368
1369 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
1370 return mod.addUnOp(b, unwrap.base.src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand);
1288}1371}
12891372
1290fn zirEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {1373fn zirEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {
1291 const tracy = trace(@src());1374 const tracy = trace(@src());
1292 defer tracy.end();1375 defer tracy.end();
1293 return mod.fail(scope, unwrap.base.src, "TODO implement zirEnsureErrPayloadVoid", .{});1376
1377 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
1378 if (operand.ty.zigTypeTag() != .ErrorUnion)
1379 return mod.fail(scope, unwrap.base.src, "expected error union type, found '{}'", .{operand.ty});
1380 if (operand.ty.castTag(.error_union).?.data.payload.zigTypeTag() != .Void) {
1381 return mod.fail(scope, unwrap.base.src, "expression value is ignored", .{});
1382 }
1383 return mod.constVoid(scope, unwrap.base.src);
1294}1384}
12951385
1296fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {1386fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst {
...@@ -2106,7 +2196,12 @@ fn zirCmp(...@@ -2106,7 +2196,12 @@ fn zirCmp(
2106 if (!is_equality_cmp) {2196 if (!is_equality_cmp) {
2107 return mod.fail(scope, inst.base.src, "{s} operator not allowed for errors", .{@tagName(op)});2197 return mod.fail(scope, inst.base.src, "{s} operator not allowed for errors", .{@tagName(op)});
2108 }2198 }
2109 return mod.fail(scope, inst.base.src, "TODO implement equality comparison between errors", .{});2199 if (rhs.value()) |rval| {
2200 if (lhs.value()) |lval| {
2201 return mod.constBool(scope, inst.base.src, (lval.castTag(.@"error").?.data.value == rval.castTag(.@"error").?.data.value) == (op == .eq));
2202 }
2203 }
2204 return mod.fail(scope, inst.base.src, "TODO implement equality comparison between runtime errors", .{});
2110 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {2205 } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) {
2111 // This operation allows any combination of integer and float types, regardless of the2206 // This operation allows any combination of integer and float types, regardless of the
2112 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for2207 // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for
test/stage2/test.zig+107-1
...@@ -1397,7 +1397,6 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1397,7 +1397,6 @@ pub fn addCases(ctx: *TestContext) !void {
1397 "",1397 "",
1398 );1398 );
1399 }1399 }
1400
1401 {1400 {
1402 var case = ctx.exe("passing u0 to function", linux_x64);1401 var case = ctx.exe("passing u0 to function", linux_x64);
1403 case.addCompareOutput(1402 case.addCompareOutput(
...@@ -1419,4 +1418,111 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1419,4 +1418,111 @@ pub fn addCases(ctx: *TestContext) !void {
1419 "",1418 "",
1420 );1419 );
1421 }1420 }
1421 {
1422 var case = ctx.exe("catch at comptime", linux_x64);
1423 case.addCompareOutput(
1424 \\export fn _start() noreturn {
1425 \\ const i: anyerror!u64 = 0;
1426 \\ const caught = i catch 5;
1427 \\ assert(caught == 0);
1428 \\ exit();
1429 \\}
1430 \\fn assert(b: bool) void {
1431 \\ if (!b) unreachable;
1432 \\}
1433 \\fn exit() noreturn {
1434 \\ asm volatile ("syscall"
1435 \\ :
1436 \\ : [number] "{rax}" (231),
1437 \\ [arg1] "{rdi}" (0)
1438 \\ : "rcx", "r11", "memory"
1439 \\ );
1440 \\ unreachable;
1441 \\}
1442 ,
1443 "",
1444 );
1445 case.addCompareOutput(
1446 \\export fn _start() noreturn {
1447 \\ const i: anyerror!u64 = error.B;
1448 \\ const caught = i catch 5;
1449 \\ assert(caught == 5);
1450 \\ exit();
1451 \\}
1452 \\fn assert(b: bool) void {
1453 \\ if (!b) unreachable;
1454 \\}
1455 \\fn exit() noreturn {
1456 \\ asm volatile ("syscall"
1457 \\ :
1458 \\ : [number] "{rax}" (231),
1459 \\ [arg1] "{rdi}" (0)
1460 \\ : "rcx", "r11", "memory"
1461 \\ );
1462 \\ unreachable;
1463 \\}
1464 ,
1465 "",
1466 );
1467 case.addCompareOutput(
1468 \\export fn _start() noreturn {
1469 \\ const a: anyerror!comptime_int = 42;
1470 \\ const b: *const comptime_int = &(a catch unreachable);
1471 \\ assert(b.* == 42);
1472 \\
1473 \\ exit();
1474 \\}
1475 \\fn assert(b: bool) void {
1476 \\ if (!b) unreachable; // assertion failure
1477 \\}
1478 \\fn exit() noreturn {
1479 \\ asm volatile ("syscall"
1480 \\ :
1481 \\ : [number] "{rax}" (231),
1482 \\ [arg1] "{rdi}" (0)
1483 \\ : "rcx", "r11", "memory"
1484 \\ );
1485 \\ unreachable;
1486 \\}
1487 , "");
1488 case.addCompareOutput(
1489 \\export fn _start() noreturn {
1490 \\const a: anyerror!u32 = error.B;
1491 \\_ = &(a catch |err| assert(err == error.B));
1492 \\exit();
1493 \\}
1494 \\fn assert(b: bool) void {
1495 \\ if (!b) unreachable;
1496 \\}
1497 \\fn exit() noreturn {
1498 \\ asm volatile ("syscall"
1499 \\ :
1500 \\ : [number] "{rax}" (231),
1501 \\ [arg1] "{rdi}" (0)
1502 \\ : "rcx", "r11", "memory"
1503 \\ );
1504 \\ unreachable;
1505 \\}
1506 , "");
1507 case.addCompareOutput(
1508 \\export fn _start() noreturn {
1509 \\ const a: anyerror!u32 = error.Bar;
1510 \\ a catch |err| assert(err == error.Bar);
1511 \\
1512 \\ exit();
1513 \\}
1514 \\fn assert(b: bool) void {
1515 \\ if (!b) unreachable;
1516 \\}
1517 \\fn exit() noreturn {
1518 \\ asm volatile ("syscall"
1519 \\ :
1520 \\ : [number] "{rax}" (231),
1521 \\ [arg1] "{rdi}" (0)
1522 \\ : "rcx", "r11", "memory"
1523 \\ );
1524 \\ unreachable;
1525 \\}
1526 , "");
1527 }
1422}1528}