| author | |
| committer | |
| log | 153c97ac9ec8deafb0777ae424f00695c18e3bd9 |
| tree | 4bc0b2c23e2d4844252efd98f453a2100043b14f |
| parent | 7edb204edfa41e11776ac009da5a20fb1c907f5f |
* 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 | 2871 | } |
| 2872 | 2872 | |
| 2873 | 2873 | pub 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 | } |
| 2876 | 2884 | |
| 2877 | 2885 | pub 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 | 3182 | return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst); |
| 3175 | 3183 | } |
| 3176 | 3184 | |
| 3185 | fn 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 | ||
| 3177 | 3231 | fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type { |
| 3178 | 3232 | const int_payload = try scope.arena().create(Type.Payload.Bits); |
| 3179 | 3233 | int_payload.* = .{ |
| ... | ... | @@ -3240,7 +3294,7 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty |
| 3240 | 3294 | return chosen.ty; |
| 3241 | 3295 | } |
| 3242 | 3296 | |
| 3243 | pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst { | |
| 3297 | pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) InnerError!*Inst { | |
| 3244 | 3298 | // If the types are the same, we can return the operand. |
| 3245 | 3299 | if (dest_type.eql(inst.ty)) |
| 3246 | 3300 | return inst; |
| ... | ... | @@ -3274,6 +3328,11 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst |
| 3274 | 3328 | } |
| 3275 | 3329 | } |
| 3276 | 3330 | |
| 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 | 3336 | // Coercions where the source is a single pointer to an array. |
| 3278 | 3337 | src_array_ptr: { |
| 3279 | 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 | 3411 | return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty }); |
| 3353 | 3412 | } |
| 3354 | 3413 | |
| 3355 | pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*Inst { | |
| 3414 | pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) InnerError!?*Inst { | |
| 3356 | 3415 | const val = inst.value() orelse return null; |
| 3357 | 3416 | const src_zig_tag = inst.ty.zigTypeTag(); |
| 3358 | 3417 | const dst_zig_tag = dest_type.zigTypeTag(); |
| ... | ... | @@ -3843,6 +3902,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { |
| 3843 | 3902 | pub const PanicId = enum { |
| 3844 | 3903 | unreach, |
| 3845 | 3904 | unwrap_null, |
| 3905 | unwrap_errunion, | |
| 3846 | 3906 | }; |
| 3847 | 3907 | |
| 3848 | 3908 | pub 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 | 909 | .unreach => return MCValue{ .unreach = {} }, |
| 910 | 910 | .optional_payload => return self.genOptionalPayload(inst.castTag(.optional_payload).?), |
| 911 | 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 | 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 | 919 | .varptr => return self.genVarPtr(inst.castTag(.varptr).?), |
| 914 | 920 | .xor => return self.genXor(inst.castTag(.xor).?), |
| 915 | 921 | } |
| ... | ... | @@ -1170,6 +1176,41 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1170 | 1176 | } |
| 1171 | 1177 | } |
| 1172 | 1178 | |
| 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 | 1214 | fn genWrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue { |
| 1174 | 1215 | const optional_ty = inst.base.ty; |
| 1175 | 1216 | |
| ... | ... | @@ -1186,6 +1227,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1186 | 1227 | } |
| 1187 | 1228 | } |
| 1188 | 1229 | |
| 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 | 1251 | fn genVarPtr(self: *Self, inst: *ir.Inst.VarPtr) !MCValue { |
| 1190 | 1252 | // No side effects, so if it's unreferenced, do nothing. |
| 1191 | 1253 | if (inst.base.isUnused()) |
src/ir.zig+18| ... | ... | @@ -114,6 +114,18 @@ pub const Inst = struct { |
| 114 | 114 | // *?T => *T |
| 115 | 115 | optional_payload_ptr, |
| 116 | 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 | 129 | xor, |
| 118 | 130 | switchbr, |
| 119 | 131 | |
| ... | ... | @@ -143,6 +155,12 @@ pub const Inst = struct { |
| 143 | 155 | .optional_payload, |
| 144 | 156 | .optional_payload_ptr, |
| 145 | 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 | 164 | => UnOp, |
| 147 | 165 | |
| 148 | 166 | .add, |
src/value.zig+116-2| ... | ... | @@ -102,6 +102,7 @@ pub const Value = extern union { |
| 102 | 102 | enum_literal, |
| 103 | 103 | error_set, |
| 104 | 104 | @"error", |
| 105 | error_union, | |
| 105 | 106 | /// This is a special value that tracks a set of types that have been stored |
| 106 | 107 | /// to an inferred allocation. It does not support any of the normal value queries. |
| 107 | 108 | inferred_alloc, |
| ... | ... | @@ -174,6 +175,7 @@ pub const Value = extern union { |
| 174 | 175 | |
| 175 | 176 | .ref_val, |
| 176 | 177 | .repeated, |
| 178 | .error_union, | |
| 177 | 179 | => Payload.SubValue, |
| 178 | 180 | |
| 179 | 181 | .bytes, |
| ... | ... | @@ -388,9 +390,17 @@ pub const Value = extern union { |
| 388 | 390 | return Value{ .ptr_otherwise = &new_payload.base }; |
| 389 | 391 | }, |
| 390 | 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 | }, | |
| 391 | 402 | |
| 392 | 403 | .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), |
| 393 | ||
| 394 | 404 | .inferred_alloc => unreachable, |
| 395 | 405 | } |
| 396 | 406 | } |
| ... | ... | @@ -510,6 +520,8 @@ pub const Value = extern union { |
| 510 | 520 | return out_stream.writeAll("}"); |
| 511 | 521 | }, |
| 512 | 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 | 525 | .inferred_alloc => return out_stream.writeAll("(inferred allocation value)"), |
| 514 | 526 | }; |
| 515 | 527 | } |
| ... | ... | @@ -622,6 +634,7 @@ pub const Value = extern union { |
| 622 | 634 | .float_128, |
| 623 | 635 | .enum_literal, |
| 624 | 636 | .@"error", |
| 637 | .error_union, | |
| 625 | 638 | .empty_struct_value, |
| 626 | 639 | .inferred_alloc, |
| 627 | 640 | => unreachable, |
| ... | ... | @@ -692,6 +705,7 @@ pub const Value = extern union { |
| 692 | 705 | .empty_array, |
| 693 | 706 | .enum_literal, |
| 694 | 707 | .error_set, |
| 708 | .error_union, | |
| 695 | 709 | .@"error", |
| 696 | 710 | .empty_struct_value, |
| 697 | 711 | .inferred_alloc, |
| ... | ... | @@ -779,6 +793,7 @@ pub const Value = extern union { |
| 779 | 793 | .enum_literal, |
| 780 | 794 | .error_set, |
| 781 | 795 | .@"error", |
| 796 | .error_union, | |
| 782 | 797 | .empty_struct_value, |
| 783 | 798 | .inferred_alloc, |
| 784 | 799 | => unreachable, |
| ... | ... | @@ -865,6 +880,7 @@ pub const Value = extern union { |
| 865 | 880 | .enum_literal, |
| 866 | 881 | .error_set, |
| 867 | 882 | .@"error", |
| 883 | .error_union, | |
| 868 | 884 | .empty_struct_value, |
| 869 | 885 | .inferred_alloc, |
| 870 | 886 | => unreachable, |
| ... | ... | @@ -979,6 +995,7 @@ pub const Value = extern union { |
| 979 | 995 | .enum_literal, |
| 980 | 996 | .error_set, |
| 981 | 997 | .@"error", |
| 998 | .error_union, | |
| 982 | 999 | .empty_struct_value, |
| 983 | 1000 | .inferred_alloc, |
| 984 | 1001 | => unreachable, |
| ... | ... | @@ -1069,6 +1086,7 @@ pub const Value = extern union { |
| 1069 | 1086 | .enum_literal, |
| 1070 | 1087 | .error_set, |
| 1071 | 1088 | .@"error", |
| 1089 | .error_union, | |
| 1072 | 1090 | .empty_struct_value, |
| 1073 | 1091 | .inferred_alloc, |
| 1074 | 1092 | => unreachable, |
| ... | ... | @@ -1228,6 +1246,7 @@ pub const Value = extern union { |
| 1228 | 1246 | .enum_literal, |
| 1229 | 1247 | .error_set, |
| 1230 | 1248 | .@"error", |
| 1249 | .error_union, | |
| 1231 | 1250 | .empty_struct_value, |
| 1232 | 1251 | .inferred_alloc, |
| 1233 | 1252 | => unreachable, |
| ... | ... | @@ -1305,6 +1324,7 @@ pub const Value = extern union { |
| 1305 | 1324 | .enum_literal, |
| 1306 | 1325 | .error_set, |
| 1307 | 1326 | .@"error", |
| 1327 | .error_union, | |
| 1308 | 1328 | .empty_struct_value, |
| 1309 | 1329 | .inferred_alloc, |
| 1310 | 1330 | => unreachable, |
| ... | ... | @@ -1543,7 +1563,10 @@ pub const Value = extern union { |
| 1543 | 1563 | hasher.update(payload.name); |
| 1544 | 1564 | std.hash.autoHash(&hasher, payload.value); |
| 1545 | 1565 | }, |
| 1546 | ||
| 1566 | .error_union => { | |
| 1567 | const payload = self.castTag(.error_union).?.data; | |
| 1568 | std.hash.autoHash(&hasher, payload.hash()); | |
| 1569 | }, | |
| 1547 | 1570 | .inferred_alloc => unreachable, |
| 1548 | 1571 | } |
| 1549 | 1572 | return hasher.final(); |
| ... | ... | @@ -1621,6 +1644,7 @@ pub const Value = extern union { |
| 1621 | 1644 | .enum_literal, |
| 1622 | 1645 | .error_set, |
| 1623 | 1646 | .@"error", |
| 1647 | .error_union, | |
| 1624 | 1648 | .empty_struct_value, |
| 1625 | 1649 | .inferred_alloc, |
| 1626 | 1650 | => unreachable, |
| ... | ... | @@ -1707,6 +1731,7 @@ pub const Value = extern union { |
| 1707 | 1731 | .enum_literal, |
| 1708 | 1732 | .error_set, |
| 1709 | 1733 | .@"error", |
| 1734 | .error_union, | |
| 1710 | 1735 | .empty_struct_value, |
| 1711 | 1736 | .inferred_alloc, |
| 1712 | 1737 | => unreachable, |
| ... | ... | @@ -1810,6 +1835,7 @@ pub const Value = extern union { |
| 1810 | 1835 | .enum_literal, |
| 1811 | 1836 | .error_set, |
| 1812 | 1837 | .@"error", |
| 1838 | .error_union, | |
| 1813 | 1839 | .empty_struct_value, |
| 1814 | 1840 | => false, |
| 1815 | 1841 | |
| ... | ... | @@ -1820,6 +1846,93 @@ pub const Value = extern union { |
| 1820 | 1846 | }; |
| 1821 | 1847 | } |
| 1822 | 1848 | |
| 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 | 1936 | /// Valid for all types. Asserts the value is not undefined. |
| 1824 | 1937 | pub fn isFloat(self: Value) bool { |
| 1825 | 1938 | return switch (self.tag()) { |
| ... | ... | @@ -1908,6 +2021,7 @@ pub const Value = extern union { |
| 1908 | 2021 | .void_value, |
| 1909 | 2022 | .enum_literal, |
| 1910 | 2023 | .@"error", |
| 2024 | .error_union, | |
| 1911 | 2025 | .empty_struct_value, |
| 1912 | 2026 | .null_value, |
| 1913 | 2027 | => false, |
src/zir.zig+12| ... | ... | @@ -1622,6 +1622,12 @@ const DumpTzir = struct { |
| 1622 | 1622 | .optional_payload, |
| 1623 | 1623 | .optional_payload_ptr, |
| 1624 | 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 | 1632 | const un_op = inst.cast(ir.Inst.UnOp).?; |
| 1627 | 1633 | try dtz.findConst(un_op.operand); |
| ... | ... | @@ -1733,6 +1739,12 @@ const DumpTzir = struct { |
| 1733 | 1739 | .optional_payload, |
| 1734 | 1740 | .optional_payload_ptr, |
| 1735 | 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 | 1749 | const un_op = inst.cast(ir.Inst.UnOp).?; |
| 1738 | 1750 | const kinky = try dtz.writeInst(writer, un_op.operand); |
src/zir_sema.zig+101-6| ... | ... | @@ -1263,34 +1263,124 @@ fn zirOptionalPayload( |
| 1263 | 1263 | fn zirErrUnionPayload(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { |
| 1264 | 1264 | const tracy = trace(@src()); |
| 1265 | 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 | } |
| 1268 | 1288 | |
| 1269 | 1289 | /// Pointer in, pointer out |
| 1270 | 1290 | fn zirErrUnionPayloadPtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { |
| 1271 | 1291 | const tracy = trace(@src()); |
| 1272 | 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 | } |
| 1275 | 1325 | |
| 1276 | 1326 | /// Value in, value out |
| 1277 | 1327 | fn zirErrUnionCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { |
| 1278 | 1328 | const tracy = trace(@src()); |
| 1279 | 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 | } |
| 1282 | 1347 | |
| 1283 | 1348 | /// Pointer in, value out |
| 1284 | 1349 | fn zirErrUnionCodePtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { |
| 1285 | 1350 | const tracy = trace(@src()); |
| 1286 | 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 | } |
| 1289 | 1372 | |
| 1290 | 1373 | fn zirEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { |
| 1291 | 1374 | const tracy = trace(@src()); |
| 1292 | 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 | } |
| 1295 | 1385 | |
| 1296 | 1386 | fn zirFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { |
| ... | ... | @@ -2106,7 +2196,12 @@ fn zirCmp( |
| 2106 | 2196 | if (!is_equality_cmp) { |
| 2107 | 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 | 2205 | } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) { |
| 2111 | 2206 | // This operation allows any combination of integer and float types, regardless of the |
| 2112 | 2207 | // 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 | 1397 | "", |
| 1398 | 1398 | ); |
| 1399 | 1399 | } |
| 1400 | ||
| 1401 | 1400 | { |
| 1402 | 1401 | var case = ctx.exe("passing u0 to function", linux_x64); |
| 1403 | 1402 | case.addCompareOutput( |
| ... | ... | @@ -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 | } |