| author | |
| committer | |
| log | 799fedf612aa8742c446b015c12d21707a1dbec0 |
| tree | 1d9efbc4cb3f6dda9631160784a074e58d6bd479 |
| parent | f81b2531cb4904064446f84a06f6e09e4120e28a |
* Value: rename `error_union` to `eu_payload` and clarify the intended
usage in the doc comments. The way error unions is represented with
Value is fixed to not have ambiguous values.
* Fix codegen for error union constants in all the backends.
* Implement the AIR instructions having to do with error unions in the
LLVM backend.9 files changed, 155 insertions(+), 117 deletions(-)
src/Sema.zig+7-9| ... | ... | @@ -3468,7 +3468,7 @@ fn zirErrUnionPayload( |
| 3468 | 3468 | if (val.getError()) |name| { |
| 3469 | 3469 | return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name}); |
| 3470 | 3470 | } |
| 3471 | const data = val.castTag(.error_union).?.data; | |
| 3471 | const data = val.castTag(.eu_payload).?.data; | |
| 3472 | 3472 | const result_ty = operand_ty.errorUnionPayload(); |
| 3473 | 3473 | return sema.addConstant(result_ty, data); |
| 3474 | 3474 | } |
| ... | ... | @@ -3539,8 +3539,7 @@ fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compi |
| 3539 | 3539 | |
| 3540 | 3540 | if (try sema.resolveDefinedValue(block, src, operand)) |val| { |
| 3541 | 3541 | assert(val.getError() != null); |
| 3542 | const data = val.castTag(.error_union).?.data; | |
| 3543 | return sema.addConstant(result_ty, data); | |
| 3542 | return sema.addConstant(result_ty, val); | |
| 3544 | 3543 | } |
| 3545 | 3544 | |
| 3546 | 3545 | try sema.requireRuntimeBlock(block, src); |
| ... | ... | @@ -3566,8 +3565,7 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co |
| 3566 | 3565 | if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { |
| 3567 | 3566 | if (try pointer_val.pointerDeref(sema.arena)) |val| { |
| 3568 | 3567 | assert(val.getError() != null); |
| 3569 | const data = val.castTag(.error_union).?.data; | |
| 3570 | return sema.addConstant(result_ty, data); | |
| 3568 | return sema.addConstant(result_ty, val); | |
| 3571 | 3569 | } |
| 3572 | 3570 | } |
| 3573 | 3571 | |
| ... | ... | @@ -8900,7 +8898,9 @@ fn wrapErrorUnion( |
| 8900 | 8898 | if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| { |
| 8901 | 8899 | if (inst_ty.zigTypeTag() != .ErrorSet) { |
| 8902 | 8900 | _ = try sema.coerce(block, dest_payload_ty, inst, inst_src); |
| 8903 | } else switch (dest_err_set_ty.tag()) { | |
| 8901 | return sema.addConstant(dest_type, try Value.Tag.eu_payload.create(sema.arena, val)); | |
| 8902 | } | |
| 8903 | switch (dest_err_set_ty.tag()) { | |
| 8904 | 8904 | .anyerror => {}, |
| 8905 | 8905 | .error_set_single => { |
| 8906 | 8906 | const expected_name = val.castTag(.@"error").?.data.name; |
| ... | ... | @@ -8946,9 +8946,7 @@ fn wrapErrorUnion( |
| 8946 | 8946 | }, |
| 8947 | 8947 | else => unreachable, |
| 8948 | 8948 | } |
| 8949 | ||
| 8950 | // Create a SubValue for the error_union payload. | |
| 8951 | return sema.addConstant(dest_type, try Value.Tag.error_union.create(sema.arena, val)); | |
| 8949 | return sema.addConstant(dest_type, val); | |
| 8952 | 8950 | } |
| 8953 | 8951 | |
| 8954 | 8952 | try sema.requireRuntimeBlock(block, inst_src); |
src/codegen.zig+1-1| ... | ... | @@ -4815,7 +4815,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 4815 | 4815 | .ErrorUnion => { |
| 4816 | 4816 | const error_type = typed_value.ty.errorUnionSet(); |
| 4817 | 4817 | const payload_type = typed_value.ty.errorUnionPayload(); |
| 4818 | const sub_val = typed_value.val.castTag(.error_union).?.data; | |
| 4818 | const sub_val = typed_value.val.castTag(.eu_payload).?.data; | |
| 4819 | 4819 | |
| 4820 | 4820 | if (!payload_type.hasCodeGenBits()) { |
| 4821 | 4821 | // We use the error type directly as the type. |
src/codegen/c.zig+9-16| ... | ... | @@ -350,32 +350,25 @@ pub const DeclGen = struct { |
| 350 | 350 | .ErrorUnion => { |
| 351 | 351 | const error_type = t.errorUnionSet(); |
| 352 | 352 | const payload_type = t.errorUnionPayload(); |
| 353 | const sub_val = val.castTag(.error_union).?.data; | |
| 354 | 353 | |
| 355 | 354 | if (!payload_type.hasCodeGenBits()) { |
| 356 | 355 | // We use the error type directly as the type. |
| 357 | return dg.renderValue(writer, error_type, sub_val); | |
| 356 | const err_val = if (val.errorUnionIsPayload()) Value.initTag(.zero) else val; | |
| 357 | return dg.renderValue(writer, error_type, err_val); | |
| 358 | 358 | } |
| 359 | 359 | |
| 360 | 360 | try writer.writeByte('('); |
| 361 | 361 | try dg.renderType(writer, t); |
| 362 | 362 | try writer.writeAll("){"); |
| 363 | if (val.getError()) |_| { | |
| 364 | try writer.writeAll(" .error = "); | |
| 365 | try dg.renderValue( | |
| 366 | writer, | |
| 367 | error_type, | |
| 368 | sub_val, | |
| 369 | ); | |
| 370 | try writer.writeAll(" }"); | |
| 371 | } else { | |
| 363 | if (val.castTag(.eu_payload)) |pl| { | |
| 364 | const payload_val = pl.data; | |
| 372 | 365 | try writer.writeAll(" .payload = "); |
| 373 | try dg.renderValue( | |
| 374 | writer, | |
| 375 | payload_type, | |
| 376 | sub_val, | |
| 377 | ); | |
| 366 | try dg.renderValue(writer, payload_type, payload_val); | |
| 378 | 367 | try writer.writeAll(", .error = 0 }"); |
| 368 | } else { | |
| 369 | try writer.writeAll(" .error = "); | |
| 370 | try dg.renderValue(writer, error_type, val); | |
| 371 | try writer.writeAll(" }"); | |
| 379 | 372 | } |
| 380 | 373 | }, |
| 381 | 374 | .Enum => { |
src/codegen/llvm.zig+51-27| ... | ... | @@ -593,7 +593,7 @@ pub const DeclGen = struct { |
| 593 | 593 | try self.llvmType(ptr_type), |
| 594 | 594 | try self.llvmType(Type.initTag(.usize)), |
| 595 | 595 | }; |
| 596 | return self.context.structType(&fields, 2, .False); | |
| 596 | return self.context.structType(&fields, fields.len, .False); | |
| 597 | 597 | } else { |
| 598 | 598 | const elem_type = try self.llvmType(t.elemType()); |
| 599 | 599 | return elem_type.pointerType(0); |
| ... | ... | @@ -621,10 +621,14 @@ pub const DeclGen = struct { |
| 621 | 621 | .ErrorUnion => { |
| 622 | 622 | const error_type = t.errorUnionSet(); |
| 623 | 623 | const payload_type = t.errorUnionPayload(); |
| 624 | const llvm_error_type = try self.llvmType(error_type); | |
| 624 | 625 | if (!payload_type.hasCodeGenBits()) { |
| 625 | return self.llvmType(error_type); | |
| 626 | return llvm_error_type; | |
| 626 | 627 | } |
| 627 | return self.todo("implement llvmType for error unions", .{}); | |
| 628 | const llvm_payload_type = try self.llvmType(payload_type); | |
| 629 | ||
| 630 | const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type }; | |
| 631 | return self.context.structType(&fields, fields.len, .False); | |
| 628 | 632 | }, |
| 629 | 633 | .ErrorSet => { |
| 630 | 634 | return self.context.intType(16); |
| ... | ... | @@ -846,14 +850,25 @@ pub const DeclGen = struct { |
| 846 | 850 | .ErrorUnion => { |
| 847 | 851 | const error_type = tv.ty.errorUnionSet(); |
| 848 | 852 | const payload_type = tv.ty.errorUnionPayload(); |
| 849 | const sub_val = tv.val.castTag(.error_union).?.data; | |
| 853 | const is_pl = tv.val.errorUnionIsPayload(); | |
| 850 | 854 | |
| 851 | 855 | if (!payload_type.hasCodeGenBits()) { |
| 852 | 856 | // We use the error type directly as the type. |
| 853 | return self.genTypedValue(.{ .ty = error_type, .val = sub_val }); | |
| 857 | const err_val = if (!is_pl) tv.val else Value.initTag(.zero); | |
| 858 | return self.genTypedValue(.{ .ty = error_type, .val = err_val }); | |
| 854 | 859 | } |
| 855 | 860 | |
| 856 | return self.todo("implement error union const of type '{}'", .{tv.ty}); | |
| 861 | const fields: [2]*const llvm.Value = .{ | |
| 862 | try self.genTypedValue(.{ | |
| 863 | .ty = error_type, | |
| 864 | .val = if (is_pl) Value.initTag(.zero) else tv.val, | |
| 865 | }), | |
| 866 | try self.genTypedValue(.{ | |
| 867 | .ty = payload_type, | |
| 868 | .val = if (tv.val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef), | |
| 869 | }), | |
| 870 | }; | |
| 871 | return self.context.constStruct(&fields, fields.len, .False); | |
| 857 | 872 | }, |
| 858 | 873 | .Struct => { |
| 859 | 874 | const fields_len = tv.ty.structFieldCount(); |
| ... | ... | @@ -984,10 +999,10 @@ pub const FuncGen = struct { |
| 984 | 999 | .is_non_null_ptr => try self.airIsNonNull(inst, true), |
| 985 | 1000 | .is_null => try self.airIsNull(inst, false), |
| 986 | 1001 | .is_null_ptr => try self.airIsNull(inst, true), |
| 987 | .is_non_err => try self.airIsErr(inst, true, false), | |
| 988 | .is_non_err_ptr => try self.airIsErr(inst, true, true), | |
| 989 | .is_err => try self.airIsErr(inst, false, false), | |
| 990 | .is_err_ptr => try self.airIsErr(inst, false, true), | |
| 1002 | .is_non_err => try self.airIsErr(inst, .EQ, false), | |
| 1003 | .is_non_err_ptr => try self.airIsErr(inst, .EQ, true), | |
| 1004 | .is_err => try self.airIsErr(inst, .NE, false), | |
| 1005 | .is_err_ptr => try self.airIsErr(inst, .NE, true), | |
| 991 | 1006 | |
| 992 | 1007 | .alloc => try self.airAlloc(inst), |
| 993 | 1008 | .arg => try self.airArg(inst), |
| ... | ... | @@ -1098,7 +1113,7 @@ pub const FuncGen = struct { |
| 1098 | 1113 | const inst_ty = self.air.typeOfIndex(inst); |
| 1099 | 1114 | |
| 1100 | 1115 | switch (self.air.typeOf(bin_op.lhs).zigTypeTag()) { |
| 1101 | .Int, .Bool, .Pointer => { | |
| 1116 | .Int, .Bool, .Pointer, .ErrorSet => { | |
| 1102 | 1117 | const is_signed = inst_ty.isSignedInt(); |
| 1103 | 1118 | const operation = switch (op) { |
| 1104 | 1119 | .eq => .EQ, |
| ... | ... | @@ -1256,12 +1271,7 @@ pub const FuncGen = struct { |
| 1256 | 1271 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1257 | 1272 | |
| 1258 | 1273 | const base_ptr = ptr: { |
| 1259 | const index_type = self.context.intType(32); | |
| 1260 | const indices: [2]*const llvm.Value = .{ | |
| 1261 | index_type.constNull(), | |
| 1262 | index_type.constInt(0, .False), | |
| 1263 | }; | |
| 1264 | const ptr_field_ptr = self.builder.buildInBoundsGEP(lhs, &indices, 2, ""); | |
| 1274 | const ptr_field_ptr = self.builder.buildStructGEP(lhs, 0, ""); | |
| 1265 | 1275 | break :ptr self.builder.buildLoad(ptr_field_ptr, ""); |
| 1266 | 1276 | }; |
| 1267 | 1277 | |
| ... | ... | @@ -1472,7 +1482,7 @@ pub const FuncGen = struct { |
| 1472 | 1482 | index_type.constInt(1, .False), |
| 1473 | 1483 | }; |
| 1474 | 1484 | |
| 1475 | return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, 2, ""), ""); | |
| 1485 | return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""), ""); | |
| 1476 | 1486 | } else { |
| 1477 | 1487 | return self.builder.buildExtractValue(operand, 1, ""); |
| 1478 | 1488 | } |
| ... | ... | @@ -1488,7 +1498,7 @@ pub const FuncGen = struct { |
| 1488 | 1498 | fn airIsErr( |
| 1489 | 1499 | self: *FuncGen, |
| 1490 | 1500 | inst: Air.Inst.Index, |
| 1491 | invert_logic: bool, | |
| 1501 | op: llvm.IntPredicate, | |
| 1492 | 1502 | operand_is_ptr: bool, |
| 1493 | 1503 | ) !?*const llvm.Value { |
| 1494 | 1504 | if (self.liveness.isUnused(inst)) |
| ... | ... | @@ -1498,16 +1508,22 @@ pub const FuncGen = struct { |
| 1498 | 1508 | const operand = try self.resolveInst(un_op); |
| 1499 | 1509 | const err_union_ty = self.air.typeOf(un_op); |
| 1500 | 1510 | const payload_ty = err_union_ty.errorUnionPayload(); |
| 1511 | const err_set_ty = try self.dg.llvmType(Type.initTag(.anyerror)); | |
| 1512 | const zero = err_set_ty.constNull(); | |
| 1501 | 1513 | |
| 1502 | 1514 | if (!payload_ty.hasCodeGenBits()) { |
| 1503 | 1515 | const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand; |
| 1504 | const op: llvm.IntPredicate = if (invert_logic) .EQ else .NE; | |
| 1505 | const err_set_ty = try self.dg.llvmType(Type.initTag(.anyerror)); | |
| 1506 | const zero = err_set_ty.constNull(); | |
| 1507 | 1516 | return self.builder.buildICmp(op, loaded, zero, ""); |
| 1508 | 1517 | } |
| 1509 | 1518 | |
| 1510 | return self.todo("implement 'airIsErr' for error unions with nonzero payload", .{}); | |
| 1519 | if (operand_is_ptr) { | |
| 1520 | const err_field_ptr = self.builder.buildStructGEP(operand, 0, ""); | |
| 1521 | const loaded = self.builder.buildLoad(err_field_ptr, ""); | |
| 1522 | return self.builder.buildICmp(op, loaded, zero, ""); | |
| 1523 | } | |
| 1524 | ||
| 1525 | const loaded = self.builder.buildExtractValue(operand, 0, ""); | |
| 1526 | return self.builder.buildICmp(op, loaded, zero, ""); | |
| 1511 | 1527 | } |
| 1512 | 1528 | |
| 1513 | 1529 | fn airOptionalPayload( |
| ... | ... | @@ -1552,9 +1568,11 @@ pub const FuncGen = struct { |
| 1552 | 1568 | return null; |
| 1553 | 1569 | } |
| 1554 | 1570 | |
| 1555 | _ = operand; | |
| 1556 | _ = operand_is_ptr; | |
| 1557 | return self.todo("implement llvm codegen for 'airErrUnionPayload' for type {}", .{self.air.typeOf(ty_op.operand)}); | |
| 1571 | if (operand_is_ptr) { | |
| 1572 | return self.builder.buildStructGEP(operand, 1, ""); | |
| 1573 | } | |
| 1574 | ||
| 1575 | return self.builder.buildExtractValue(operand, 1, ""); | |
| 1558 | 1576 | } |
| 1559 | 1577 | |
| 1560 | 1578 | fn airErrUnionErr( |
| ... | ... | @@ -1574,7 +1592,13 @@ pub const FuncGen = struct { |
| 1574 | 1592 | if (!operand_is_ptr) return operand; |
| 1575 | 1593 | return self.builder.buildLoad(operand, ""); |
| 1576 | 1594 | } |
| 1577 | return self.todo("implement llvm codegen for 'airErrUnionErr'", .{}); | |
| 1595 | ||
| 1596 | if (operand_is_ptr) { | |
| 1597 | const err_field_ptr = self.builder.buildStructGEP(operand, 0, ""); | |
| 1598 | return self.builder.buildLoad(err_field_ptr, ""); | |
| 1599 | } | |
| 1600 | ||
| 1601 | return self.builder.buildExtractValue(operand, 0, ""); | |
| 1578 | 1602 | } |
| 1579 | 1603 | |
| 1580 | 1604 | fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value { |
src/codegen/wasm.zig+9-9| ... | ... | @@ -1167,12 +1167,18 @@ pub const Context = struct { |
| 1167 | 1167 | try leb.writeULEB128(writer, error_index); |
| 1168 | 1168 | }, |
| 1169 | 1169 | .ErrorUnion => { |
| 1170 | const data = val.castTag(.error_union).?.data; | |
| 1171 | 1170 | const error_type = ty.errorUnionSet(); |
| 1172 | 1171 | const payload_type = ty.errorUnionPayload(); |
| 1173 | if (val.getError()) |_| { | |
| 1172 | if (val.castTag(.eu_payload)) |pl| { | |
| 1173 | const payload_val = pl.data; | |
| 1174 | // no error, so write a '0' const | |
| 1175 | try writer.writeByte(wasm.opcode(.i32_const)); | |
| 1176 | try leb.writeULEB128(writer, @as(u32, 0)); | |
| 1177 | // after the error code, we emit the payload | |
| 1178 | try self.emitConstant(payload_val, payload_type); | |
| 1179 | } else { | |
| 1174 | 1180 | // write the error val |
| 1175 | try self.emitConstant(data, error_type); | |
| 1181 | try self.emitConstant(val, error_type); | |
| 1176 | 1182 | |
| 1177 | 1183 | // no payload, so write a '0' const |
| 1178 | 1184 | const opcode: wasm.Opcode = buildOpcode(.{ |
| ... | ... | @@ -1181,12 +1187,6 @@ pub const Context = struct { |
| 1181 | 1187 | }); |
| 1182 | 1188 | try writer.writeByte(wasm.opcode(opcode)); |
| 1183 | 1189 | try leb.writeULEB128(writer, @as(u32, 0)); |
| 1184 | } else { | |
| 1185 | // no error, so write a '0' const | |
| 1186 | try writer.writeByte(wasm.opcode(.i32_const)); | |
| 1187 | try leb.writeULEB128(writer, @as(u32, 0)); | |
| 1188 | // after the error code, we emit the payload | |
| 1189 | try self.emitConstant(data, payload_type); | |
| 1190 | 1190 | } |
| 1191 | 1191 | }, |
| 1192 | 1192 | .Optional => { |
src/value.zig+31-12| ... | ... | @@ -129,7 +129,13 @@ pub const Value = extern union { |
| 129 | 129 | /// A specific enum tag, indicated by the field index (declaration order). |
| 130 | 130 | enum_field_index, |
| 131 | 131 | @"error", |
| 132 | error_union, | |
| 132 | /// When the type is error union: | |
| 133 | /// * If the tag is `.@"error"`, the error union is an error. | |
| 134 | /// * If the tag is `.eu_payload`, the error union is a payload. | |
| 135 | /// * A nested error such as `((anyerror!T1)!T2)` in which the the outer error union | |
| 136 | /// is non-error, but the inner error union is an error, is represented as | |
| 137 | /// a tag of `.eu_payload`, with a sub-tag of `.@"error"`. | |
| 138 | eu_payload, | |
| 133 | 139 | /// A pointer to the payload of an error union, based on a pointer to an error union. |
| 134 | 140 | eu_payload_ptr, |
| 135 | 141 | /// An instance of a struct. |
| ... | ... | @@ -228,7 +234,7 @@ pub const Value = extern union { |
| 228 | 234 | => Payload.Decl, |
| 229 | 235 | |
| 230 | 236 | .repeated, |
| 231 | .error_union, | |
| 237 | .eu_payload, | |
| 232 | 238 | .eu_payload_ptr, |
| 233 | 239 | => Payload.SubValue, |
| 234 | 240 | |
| ... | ... | @@ -450,7 +456,7 @@ pub const Value = extern union { |
| 450 | 456 | return Value{ .ptr_otherwise = &new_payload.base }; |
| 451 | 457 | }, |
| 452 | 458 | .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes), |
| 453 | .repeated, .error_union, .eu_payload_ptr => { | |
| 459 | .repeated, .eu_payload, .eu_payload_ptr => { | |
| 454 | 460 | const payload = self.cast(Payload.SubValue).?; |
| 455 | 461 | const new_payload = try allocator.create(Payload.SubValue); |
| 456 | 462 | new_payload.* = .{ |
| ... | ... | @@ -642,7 +648,10 @@ pub const Value = extern union { |
| 642 | 648 | .float_128 => return out_stream.print("{}", .{val.castTag(.float_128).?.data}), |
| 643 | 649 | .@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}), |
| 644 | 650 | // TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that |
| 645 | .error_union => return out_stream.print("error_union_val({})", .{val.castTag(.error_union).?.data}), | |
| 651 | .eu_payload => { | |
| 652 | try out_stream.writeAll("(eu_payload) "); | |
| 653 | val = val.castTag(.eu_payload).?.data; | |
| 654 | }, | |
| 646 | 655 | .inferred_alloc => return out_stream.writeAll("(inferred allocation value)"), |
| 647 | 656 | .inferred_alloc_comptime => return out_stream.writeAll("(inferred comptime allocation value)"), |
| 648 | 657 | .eu_payload_ptr => { |
| ... | ... | @@ -1241,7 +1250,7 @@ pub const Value = extern union { |
| 1241 | 1250 | .eu_payload_ptr => blk: { |
| 1242 | 1251 | const err_union_ptr = self.castTag(.eu_payload_ptr).?.data; |
| 1243 | 1252 | const err_union_val = (try err_union_ptr.pointerDeref(allocator)) orelse return null; |
| 1244 | break :blk err_union_val.castTag(.error_union).?.data; | |
| 1253 | break :blk err_union_val.castTag(.eu_payload).?.data; | |
| 1245 | 1254 | }, |
| 1246 | 1255 | |
| 1247 | 1256 | .zero, |
| ... | ... | @@ -1351,16 +1360,16 @@ pub const Value = extern union { |
| 1351 | 1360 | } |
| 1352 | 1361 | |
| 1353 | 1362 | /// Valid for all types. Asserts the value is not undefined and not unreachable. |
| 1363 | /// Prefer `errorUnionIsPayload` to find out whether something is an error or not | |
| 1364 | /// because it works without having to figure out the string. | |
| 1354 | 1365 | pub fn getError(self: Value) ?[]const u8 { |
| 1355 | 1366 | return switch (self.tag()) { |
| 1356 | .error_union => { | |
| 1357 | const data = self.castTag(.error_union).?.data; | |
| 1358 | return if (data.tag() == .@"error") | |
| 1359 | data.castTag(.@"error").?.data.name | |
| 1360 | else | |
| 1361 | null; | |
| 1362 | }, | |
| 1363 | 1367 | .@"error" => self.castTag(.@"error").?.data.name, |
| 1368 | .int_u64 => @panic("TODO"), | |
| 1369 | .int_i64 => @panic("TODO"), | |
| 1370 | .int_big_positive => @panic("TODO"), | |
| 1371 | .int_big_negative => @panic("TODO"), | |
| 1372 | .one => @panic("TODO"), | |
| 1364 | 1373 | .undef => unreachable, |
| 1365 | 1374 | .unreachable_value => unreachable, |
| 1366 | 1375 | .inferred_alloc => unreachable, |
| ... | ... | @@ -1369,6 +1378,16 @@ pub const Value = extern union { |
| 1369 | 1378 | else => null, |
| 1370 | 1379 | }; |
| 1371 | 1380 | } |
| 1381 | ||
| 1382 | /// Assumes the type is an error union. Returns true if and only if the value is | |
| 1383 | /// the error union payload, not an error. | |
| 1384 | pub fn errorUnionIsPayload(val: Value) bool { | |
| 1385 | return switch (val.tag()) { | |
| 1386 | .eu_payload => true, | |
| 1387 | else => false, | |
| 1388 | }; | |
| 1389 | } | |
| 1390 | ||
| 1372 | 1391 | /// Valid for all types. Asserts the value is not undefined. |
| 1373 | 1392 | pub fn isFloat(self: Value) bool { |
| 1374 | 1393 | return switch (self.tag()) { |
test/behavior.zig+2-1| ... | ... | @@ -7,6 +7,7 @@ test { |
| 7 | 7 | _ = @import("behavior/generics.zig"); |
| 8 | 8 | _ = @import("behavior/eval.zig"); |
| 9 | 9 | _ = @import("behavior/pointers.zig"); |
| 10 | _ = @import("behavior/if.zig"); | |
| 10 | 11 | |
| 11 | 12 | if (!builtin.zig_is_stage2) { |
| 12 | 13 | // Tests that only pass for stage1. |
| ... | ... | @@ -100,7 +101,7 @@ test { |
| 100 | 101 | _ = @import("behavior/generics_stage1.zig"); |
| 101 | 102 | _ = @import("behavior/hasdecl.zig"); |
| 102 | 103 | _ = @import("behavior/hasfield.zig"); |
| 103 | _ = @import("behavior/if.zig"); | |
| 104 | _ = @import("behavior/if_stage1.zig"); | |
| 104 | 105 | _ = @import("behavior/import.zig"); |
| 105 | 106 | _ = @import("behavior/incomplete_struct_param_tld.zig"); |
| 106 | 107 | _ = @import("behavior/inttoptr.zig"); |
test/behavior/if.zig-42| ... | ... | @@ -65,45 +65,3 @@ test "labeled break inside comptime if inside runtime if" { |
| 65 | 65 | } |
| 66 | 66 | try expect(answer == 42); |
| 67 | 67 | } |
| 68 | ||
| 69 | test "const result loc, runtime if cond, else unreachable" { | |
| 70 | const Num = enum { | |
| 71 | One, | |
| 72 | Two, | |
| 73 | }; | |
| 74 | ||
| 75 | var t = true; | |
| 76 | const x = if (t) Num.Two else unreachable; | |
| 77 | try expect(x == .Two); | |
| 78 | } | |
| 79 | ||
| 80 | test "if prongs cast to expected type instead of peer type resolution" { | |
| 81 | const S = struct { | |
| 82 | fn doTheTest(f: bool) !void { | |
| 83 | var x: i32 = 0; | |
| 84 | x = if (f) 1 else 2; | |
| 85 | try expect(x == 2); | |
| 86 | ||
| 87 | var b = true; | |
| 88 | const y: i32 = if (b) 1 else 2; | |
| 89 | try expect(y == 1); | |
| 90 | } | |
| 91 | }; | |
| 92 | try S.doTheTest(false); | |
| 93 | comptime try S.doTheTest(false); | |
| 94 | } | |
| 95 | ||
| 96 | test "while copies its payload" { | |
| 97 | const S = struct { | |
| 98 | fn doTheTest() !void { | |
| 99 | var tmp: ?i32 = 10; | |
| 100 | if (tmp) |value| { | |
| 101 | // Modify the original variable | |
| 102 | tmp = null; | |
| 103 | try expectEqual(@as(i32, 10), value); | |
| 104 | } else unreachable; | |
| 105 | } | |
| 106 | }; | |
| 107 | try S.doTheTest(); | |
| 108 | comptime try S.doTheTest(); | |
| 109 | } |
test/behavior/if_stage1.zig created+45| ... | ... | @@ -0,0 +1,45 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | const expectEqual = std.testing.expectEqual; | |
| 4 | ||
| 5 | test "const result loc, runtime if cond, else unreachable" { | |
| 6 | const Num = enum { | |
| 7 | One, | |
| 8 | Two, | |
| 9 | }; | |
| 10 | ||
| 11 | var t = true; | |
| 12 | const x = if (t) Num.Two else unreachable; | |
| 13 | try expect(x == .Two); | |
| 14 | } | |
| 15 | ||
| 16 | test "if prongs cast to expected type instead of peer type resolution" { | |
| 17 | const S = struct { | |
| 18 | fn doTheTest(f: bool) !void { | |
| 19 | var x: i32 = 0; | |
| 20 | x = if (f) 1 else 2; | |
| 21 | try expect(x == 2); | |
| 22 | ||
| 23 | var b = true; | |
| 24 | const y: i32 = if (b) 1 else 2; | |
| 25 | try expect(y == 1); | |
| 26 | } | |
| 27 | }; | |
| 28 | try S.doTheTest(false); | |
| 29 | comptime try S.doTheTest(false); | |
| 30 | } | |
| 31 | ||
| 32 | test "while copies its payload" { | |
| 33 | const S = struct { | |
| 34 | fn doTheTest() !void { | |
| 35 | var tmp: ?i32 = 10; | |
| 36 | if (tmp) |value| { | |
| 37 | // Modify the original variable | |
| 38 | tmp = null; | |
| 39 | try expectEqual(@as(i32, 10), value); | |
| 40 | } else unreachable; | |
| 41 | } | |
| 42 | }; | |
| 43 | try S.doTheTest(); | |
| 44 | comptime try S.doTheTest(); | |
| 45 | } |