authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-07 20:34:28-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-08-07 20:34:28-07:00
log799fedf612aa8742c446b015c12d21707a1dbec0
tree1d9efbc4cb3f6dda9631160784a074e58d6bd479
parentf81b2531cb4904064446f84a06f6e09e4120e28a

stage2: pass some error union tests

* 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,7 +3468,7 @@ fn zirErrUnionPayload(
3468 if (val.getError()) |name| {3468 if (val.getError()) |name| {
3469 return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name});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 const result_ty = operand_ty.errorUnionPayload();3472 const result_ty = operand_ty.errorUnionPayload();
3473 return sema.addConstant(result_ty, data);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,8 +3539,7 @@ fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Compi
35393539
3540 if (try sema.resolveDefinedValue(block, src, operand)) |val| {3540 if (try sema.resolveDefinedValue(block, src, operand)) |val| {
3541 assert(val.getError() != null);3541 assert(val.getError() != null);
3542 const data = val.castTag(.error_union).?.data;3542 return sema.addConstant(result_ty, val);
3543 return sema.addConstant(result_ty, data);
3544 }3543 }
35453544
3546 try sema.requireRuntimeBlock(block, src);3545 try sema.requireRuntimeBlock(block, src);
...@@ -3566,8 +3565,7 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co...@@ -3566,8 +3565,7 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Co
3566 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {3565 if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| {
3567 if (try pointer_val.pointerDeref(sema.arena)) |val| {3566 if (try pointer_val.pointerDeref(sema.arena)) |val| {
3568 assert(val.getError() != null);3567 assert(val.getError() != null);
3569 const data = val.castTag(.error_union).?.data;3568 return sema.addConstant(result_ty, val);
3570 return sema.addConstant(result_ty, data);
3571 }3569 }
3572 }3570 }
35733571
...@@ -8900,7 +8898,9 @@ fn wrapErrorUnion(...@@ -8900,7 +8898,9 @@ fn wrapErrorUnion(
8900 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {8898 if (try sema.resolveMaybeUndefVal(block, inst_src, inst)) |val| {
8901 if (inst_ty.zigTypeTag() != .ErrorSet) {8899 if (inst_ty.zigTypeTag() != .ErrorSet) {
8902 _ = try sema.coerce(block, dest_payload_ty, inst, inst_src);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 .anyerror => {},8904 .anyerror => {},
8905 .error_set_single => {8905 .error_set_single => {
8906 const expected_name = val.castTag(.@"error").?.data.name;8906 const expected_name = val.castTag(.@"error").?.data.name;
...@@ -8946,9 +8946,7 @@ fn wrapErrorUnion(...@@ -8946,9 +8946,7 @@ fn wrapErrorUnion(
8946 },8946 },
8947 else => unreachable,8947 else => unreachable,
8948 }8948 }
89498949 return sema.addConstant(dest_type, val);
8950 // Create a SubValue for the error_union payload.
8951 return sema.addConstant(dest_type, try Value.Tag.error_union.create(sema.arena, val));
8952 }8950 }
89538951
8954 try sema.requireRuntimeBlock(block, inst_src);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,7 +4815,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
4815 .ErrorUnion => {4815 .ErrorUnion => {
4816 const error_type = typed_value.ty.errorUnionSet();4816 const error_type = typed_value.ty.errorUnionSet();
4817 const payload_type = typed_value.ty.errorUnionPayload();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;
48194819
4820 if (!payload_type.hasCodeGenBits()) {4820 if (!payload_type.hasCodeGenBits()) {
4821 // We use the error type directly as the type.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,32 +350,25 @@ pub const DeclGen = struct {
350 .ErrorUnion => {350 .ErrorUnion => {
351 const error_type = t.errorUnionSet();351 const error_type = t.errorUnionSet();
352 const payload_type = t.errorUnionPayload();352 const payload_type = t.errorUnionPayload();
353 const sub_val = val.castTag(.error_union).?.data;
354353
355 if (!payload_type.hasCodeGenBits()) {354 if (!payload_type.hasCodeGenBits()) {
356 // We use the error type directly as the type.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 }
359359
360 try writer.writeByte('(');360 try writer.writeByte('(');
361 try dg.renderType(writer, t);361 try dg.renderType(writer, t);
362 try writer.writeAll("){");362 try writer.writeAll("){");
363 if (val.getError()) |_| {363 if (val.castTag(.eu_payload)) |pl| {
364 try writer.writeAll(" .error = ");364 const payload_val = pl.data;
365 try dg.renderValue(
366 writer,
367 error_type,
368 sub_val,
369 );
370 try writer.writeAll(" }");
371 } else {
372 try writer.writeAll(" .payload = ");365 try writer.writeAll(" .payload = ");
373 try dg.renderValue(366 try dg.renderValue(writer, payload_type, payload_val);
374 writer,
375 payload_type,
376 sub_val,
377 );
378 try writer.writeAll(", .error = 0 }");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 .Enum => {374 .Enum => {
src/codegen/llvm.zig+51-27
...@@ -593,7 +593,7 @@ pub const DeclGen = struct {...@@ -593,7 +593,7 @@ pub const DeclGen = struct {
593 try self.llvmType(ptr_type),593 try self.llvmType(ptr_type),
594 try self.llvmType(Type.initTag(.usize)),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 } else {597 } else {
598 const elem_type = try self.llvmType(t.elemType());598 const elem_type = try self.llvmType(t.elemType());
599 return elem_type.pointerType(0);599 return elem_type.pointerType(0);
...@@ -621,10 +621,14 @@ pub const DeclGen = struct {...@@ -621,10 +621,14 @@ pub const DeclGen = struct {
621 .ErrorUnion => {621 .ErrorUnion => {
622 const error_type = t.errorUnionSet();622 const error_type = t.errorUnionSet();
623 const payload_type = t.errorUnionPayload();623 const payload_type = t.errorUnionPayload();
624 const llvm_error_type = try self.llvmType(error_type);
624 if (!payload_type.hasCodeGenBits()) {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 .ErrorSet => {633 .ErrorSet => {
630 return self.context.intType(16);634 return self.context.intType(16);
...@@ -846,14 +850,25 @@ pub const DeclGen = struct {...@@ -846,14 +850,25 @@ pub const DeclGen = struct {
846 .ErrorUnion => {850 .ErrorUnion => {
847 const error_type = tv.ty.errorUnionSet();851 const error_type = tv.ty.errorUnionSet();
848 const payload_type = tv.ty.errorUnionPayload();852 const payload_type = tv.ty.errorUnionPayload();
849 const sub_val = tv.val.castTag(.error_union).?.data;853 const is_pl = tv.val.errorUnionIsPayload();
850854
851 if (!payload_type.hasCodeGenBits()) {855 if (!payload_type.hasCodeGenBits()) {
852 // We use the error type directly as the type.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 }
855860
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 .Struct => {873 .Struct => {
859 const fields_len = tv.ty.structFieldCount();874 const fields_len = tv.ty.structFieldCount();
...@@ -984,10 +999,10 @@ pub const FuncGen = struct {...@@ -984,10 +999,10 @@ pub const FuncGen = struct {
984 .is_non_null_ptr => try self.airIsNonNull(inst, true),999 .is_non_null_ptr => try self.airIsNonNull(inst, true),
985 .is_null => try self.airIsNull(inst, false),1000 .is_null => try self.airIsNull(inst, false),
986 .is_null_ptr => try self.airIsNull(inst, true),1001 .is_null_ptr => try self.airIsNull(inst, true),
987 .is_non_err => try self.airIsErr(inst, true, false),1002 .is_non_err => try self.airIsErr(inst, .EQ, false),
988 .is_non_err_ptr => try self.airIsErr(inst, true, true),1003 .is_non_err_ptr => try self.airIsErr(inst, .EQ, true),
989 .is_err => try self.airIsErr(inst, false, false),1004 .is_err => try self.airIsErr(inst, .NE, false),
990 .is_err_ptr => try self.airIsErr(inst, false, true),1005 .is_err_ptr => try self.airIsErr(inst, .NE, true),
9911006
992 .alloc => try self.airAlloc(inst),1007 .alloc => try self.airAlloc(inst),
993 .arg => try self.airArg(inst),1008 .arg => try self.airArg(inst),
...@@ -1098,7 +1113,7 @@ pub const FuncGen = struct {...@@ -1098,7 +1113,7 @@ pub const FuncGen = struct {
1098 const inst_ty = self.air.typeOfIndex(inst);1113 const inst_ty = self.air.typeOfIndex(inst);
10991114
1100 switch (self.air.typeOf(bin_op.lhs).zigTypeTag()) {1115 switch (self.air.typeOf(bin_op.lhs).zigTypeTag()) {
1101 .Int, .Bool, .Pointer => {1116 .Int, .Bool, .Pointer, .ErrorSet => {
1102 const is_signed = inst_ty.isSignedInt();1117 const is_signed = inst_ty.isSignedInt();
1103 const operation = switch (op) {1118 const operation = switch (op) {
1104 .eq => .EQ,1119 .eq => .EQ,
...@@ -1256,12 +1271,7 @@ pub const FuncGen = struct {...@@ -1256,12 +1271,7 @@ pub const FuncGen = struct {
1256 const rhs = try self.resolveInst(bin_op.rhs);1271 const rhs = try self.resolveInst(bin_op.rhs);
12571272
1258 const base_ptr = ptr: {1273 const base_ptr = ptr: {
1259 const index_type = self.context.intType(32);1274 const ptr_field_ptr = self.builder.buildStructGEP(lhs, 0, "");
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, "");
1265 break :ptr self.builder.buildLoad(ptr_field_ptr, "");1275 break :ptr self.builder.buildLoad(ptr_field_ptr, "");
1266 };1276 };
12671277
...@@ -1472,7 +1482,7 @@ pub const FuncGen = struct {...@@ -1472,7 +1482,7 @@ pub const FuncGen = struct {
1472 index_type.constInt(1, .False),1482 index_type.constInt(1, .False),
1473 };1483 };
14741484
1475 return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, 2, ""), "");1485 return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, indices.len, ""), "");
1476 } else {1486 } else {
1477 return self.builder.buildExtractValue(operand, 1, "");1487 return self.builder.buildExtractValue(operand, 1, "");
1478 }1488 }
...@@ -1488,7 +1498,7 @@ pub const FuncGen = struct {...@@ -1488,7 +1498,7 @@ pub const FuncGen = struct {
1488 fn airIsErr(1498 fn airIsErr(
1489 self: *FuncGen,1499 self: *FuncGen,
1490 inst: Air.Inst.Index,1500 inst: Air.Inst.Index,
1491 invert_logic: bool,1501 op: llvm.IntPredicate,
1492 operand_is_ptr: bool,1502 operand_is_ptr: bool,
1493 ) !?*const llvm.Value {1503 ) !?*const llvm.Value {
1494 if (self.liveness.isUnused(inst))1504 if (self.liveness.isUnused(inst))
...@@ -1498,16 +1508,22 @@ pub const FuncGen = struct {...@@ -1498,16 +1508,22 @@ pub const FuncGen = struct {
1498 const operand = try self.resolveInst(un_op);1508 const operand = try self.resolveInst(un_op);
1499 const err_union_ty = self.air.typeOf(un_op);1509 const err_union_ty = self.air.typeOf(un_op);
1500 const payload_ty = err_union_ty.errorUnionPayload();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();
15011513
1502 if (!payload_ty.hasCodeGenBits()) {1514 if (!payload_ty.hasCodeGenBits()) {
1503 const loaded = if (operand_is_ptr) self.builder.buildLoad(operand, "") else operand;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 return self.builder.buildICmp(op, loaded, zero, "");1516 return self.builder.buildICmp(op, loaded, zero, "");
1508 }1517 }
15091518
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 }
15121528
1513 fn airOptionalPayload(1529 fn airOptionalPayload(
...@@ -1552,9 +1568,11 @@ pub const FuncGen = struct {...@@ -1552,9 +1568,11 @@ pub const FuncGen = struct {
1552 return null;1568 return null;
1553 }1569 }
15541570
1555 _ = operand;1571 if (operand_is_ptr) {
1556 _ = operand_is_ptr;1572 return self.builder.buildStructGEP(operand, 1, "");
1557 return self.todo("implement llvm codegen for 'airErrUnionPayload' for type {}", .{self.air.typeOf(ty_op.operand)});1573 }
1574
1575 return self.builder.buildExtractValue(operand, 1, "");
1558 }1576 }
15591577
1560 fn airErrUnionErr(1578 fn airErrUnionErr(
...@@ -1574,7 +1592,13 @@ pub const FuncGen = struct {...@@ -1574,7 +1592,13 @@ pub const FuncGen = struct {
1574 if (!operand_is_ptr) return operand;1592 if (!operand_is_ptr) return operand;
1575 return self.builder.buildLoad(operand, "");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 }
15791603
1580 fn airWrapOptional(self: *FuncGen, inst: Air.Inst.Index) !?*const llvm.Value {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,12 +1167,18 @@ pub const Context = struct {
1167 try leb.writeULEB128(writer, error_index);1167 try leb.writeULEB128(writer, error_index);
1168 },1168 },
1169 .ErrorUnion => {1169 .ErrorUnion => {
1170 const data = val.castTag(.error_union).?.data;
1171 const error_type = ty.errorUnionSet();1170 const error_type = ty.errorUnionSet();
1172 const payload_type = ty.errorUnionPayload();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 // write the error val1180 // write the error val
1175 try self.emitConstant(data, error_type);1181 try self.emitConstant(val, error_type);
11761182
1177 // no payload, so write a '0' const1183 // no payload, so write a '0' const
1178 const opcode: wasm.Opcode = buildOpcode(.{1184 const opcode: wasm.Opcode = buildOpcode(.{
...@@ -1181,12 +1187,6 @@ pub const Context = struct {...@@ -1181,12 +1187,6 @@ pub const Context = struct {
1181 });1187 });
1182 try writer.writeByte(wasm.opcode(opcode));1188 try writer.writeByte(wasm.opcode(opcode));
1183 try leb.writeULEB128(writer, @as(u32, 0));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 .Optional => {1192 .Optional => {
src/value.zig+31-12
...@@ -129,7 +129,13 @@ pub const Value = extern union {...@@ -129,7 +129,13 @@ pub const Value = extern union {
129 /// A specific enum tag, indicated by the field index (declaration order).129 /// A specific enum tag, indicated by the field index (declaration order).
130 enum_field_index,130 enum_field_index,
131 @"error",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 /// A pointer to the payload of an error union, based on a pointer to an error union.139 /// A pointer to the payload of an error union, based on a pointer to an error union.
134 eu_payload_ptr,140 eu_payload_ptr,
135 /// An instance of a struct.141 /// An instance of a struct.
...@@ -228,7 +234,7 @@ pub const Value = extern union {...@@ -228,7 +234,7 @@ pub const Value = extern union {
228 => Payload.Decl,234 => Payload.Decl,
229235
230 .repeated,236 .repeated,
231 .error_union,237 .eu_payload,
232 .eu_payload_ptr,238 .eu_payload_ptr,
233 => Payload.SubValue,239 => Payload.SubValue,
234240
...@@ -450,7 +456,7 @@ pub const Value = extern union {...@@ -450,7 +456,7 @@ pub const Value = extern union {
450 return Value{ .ptr_otherwise = &new_payload.base };456 return Value{ .ptr_otherwise = &new_payload.base };
451 },457 },
452 .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),458 .bytes => return self.copyPayloadShallow(allocator, Payload.Bytes),
453 .repeated, .error_union, .eu_payload_ptr => {459 .repeated, .eu_payload, .eu_payload_ptr => {
454 const payload = self.cast(Payload.SubValue).?;460 const payload = self.cast(Payload.SubValue).?;
455 const new_payload = try allocator.create(Payload.SubValue);461 const new_payload = try allocator.create(Payload.SubValue);
456 new_payload.* = .{462 new_payload.* = .{
...@@ -642,7 +648,10 @@ pub const Value = extern union {...@@ -642,7 +648,10 @@ pub const Value = extern union {
642 .float_128 => return out_stream.print("{}", .{val.castTag(.float_128).?.data}),648 .float_128 => return out_stream.print("{}", .{val.castTag(.float_128).?.data}),
643 .@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}),649 .@"error" => return out_stream.print("error.{s}", .{val.castTag(.@"error").?.data.name}),
644 // TODO to print this it should be error{ Set, Items }!T(val), but we need the type for that650 // 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 .inferred_alloc => return out_stream.writeAll("(inferred allocation value)"),655 .inferred_alloc => return out_stream.writeAll("(inferred allocation value)"),
647 .inferred_alloc_comptime => return out_stream.writeAll("(inferred comptime allocation value)"),656 .inferred_alloc_comptime => return out_stream.writeAll("(inferred comptime allocation value)"),
648 .eu_payload_ptr => {657 .eu_payload_ptr => {
...@@ -1241,7 +1250,7 @@ pub const Value = extern union {...@@ -1241,7 +1250,7 @@ pub const Value = extern union {
1241 .eu_payload_ptr => blk: {1250 .eu_payload_ptr => blk: {
1242 const err_union_ptr = self.castTag(.eu_payload_ptr).?.data;1251 const err_union_ptr = self.castTag(.eu_payload_ptr).?.data;
1243 const err_union_val = (try err_union_ptr.pointerDeref(allocator)) orelse return null;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 },
12461255
1247 .zero,1256 .zero,
...@@ -1351,16 +1360,16 @@ pub const Value = extern union {...@@ -1351,16 +1360,16 @@ pub const Value = extern union {
1351 }1360 }
13521361
1353 /// Valid for all types. Asserts the value is not undefined and not unreachable.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 pub fn getError(self: Value) ?[]const u8 {1365 pub fn getError(self: Value) ?[]const u8 {
1355 return switch (self.tag()) {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 .@"error" => self.castTag(.@"error").?.data.name,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 .undef => unreachable,1373 .undef => unreachable,
1365 .unreachable_value => unreachable,1374 .unreachable_value => unreachable,
1366 .inferred_alloc => unreachable,1375 .inferred_alloc => unreachable,
...@@ -1369,6 +1378,16 @@ pub const Value = extern union {...@@ -1369,6 +1378,16 @@ pub const Value = extern union {
1369 else => null,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 /// Valid for all types. Asserts the value is not undefined.1391 /// Valid for all types. Asserts the value is not undefined.
1373 pub fn isFloat(self: Value) bool {1392 pub fn isFloat(self: Value) bool {
1374 return switch (self.tag()) {1393 return switch (self.tag()) {
test/behavior.zig+2-1
...@@ -7,6 +7,7 @@ test {...@@ -7,6 +7,7 @@ test {
7 _ = @import("behavior/generics.zig");7 _ = @import("behavior/generics.zig");
8 _ = @import("behavior/eval.zig");8 _ = @import("behavior/eval.zig");
9 _ = @import("behavior/pointers.zig");9 _ = @import("behavior/pointers.zig");
10 _ = @import("behavior/if.zig");
1011
11 if (!builtin.zig_is_stage2) {12 if (!builtin.zig_is_stage2) {
12 // Tests that only pass for stage1.13 // Tests that only pass for stage1.
...@@ -100,7 +101,7 @@ test {...@@ -100,7 +101,7 @@ test {
100 _ = @import("behavior/generics_stage1.zig");101 _ = @import("behavior/generics_stage1.zig");
101 _ = @import("behavior/hasdecl.zig");102 _ = @import("behavior/hasdecl.zig");
102 _ = @import("behavior/hasfield.zig");103 _ = @import("behavior/hasfield.zig");
103 _ = @import("behavior/if.zig");104 _ = @import("behavior/if_stage1.zig");
104 _ = @import("behavior/import.zig");105 _ = @import("behavior/import.zig");
105 _ = @import("behavior/incomplete_struct_param_tld.zig");106 _ = @import("behavior/incomplete_struct_param_tld.zig");
106 _ = @import("behavior/inttoptr.zig");107 _ = @import("behavior/inttoptr.zig");
test/behavior/if.zig-42
...@@ -65,45 +65,3 @@ test "labeled break inside comptime if inside runtime if" {...@@ -65,45 +65,3 @@ test "labeled break inside comptime if inside runtime if" {
65 }65 }
66 try expect(answer == 42);66 try expect(answer == 42);
67}67}
68
69test "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
80test "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
96test "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 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const expectEqual = std.testing.expectEqual;
4
5test "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
16test "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
32test "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}