From ac7028f559d888f61f19589c1ead1f6cdeace736 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 2 Mar 2022 19:01:55 -0800 Subject: [PATCH] stage2: implement @errSetCast (#11039) --- src/Sema.zig | 41 ++++++++++++++++++++++++++++++++++++++++- src/value.zig | 19 +++++++++++++++++-- test/behavior/error.zig | 8 +++++++- 3 files changed, 64 insertions(+), 4 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index ec8057ddd2..c669f662ad 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12465,7 +12465,39 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai fn zirErrSetCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); - return sema.fail(block, src, "TODO: Sema.zirErrSetCast", .{}); + const dest_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; + const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; + const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; + const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); + const operand = sema.resolveInst(extra.rhs); + const operand_ty = sema.typeOf(operand); + try sema.checkErrorSetType(block, dest_ty_src, dest_ty); + try sema.checkErrorSetType(block, operand_src, operand_ty); + + if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { + try sema.resolveInferredErrorSetTy(dest_ty); + + if (!dest_ty.isAnyError()) { + const error_name = val.castTag(.@"error").?.data.name; + if (!dest_ty.errorSetHasField(error_name)) { + return sema.fail( + block, + src, + "error.{s} not a member of error set '{}'", + .{ error_name, dest_ty }, + ); + } + } + + return sema.addConstant(dest_ty, val); + } + + try sema.requireRuntimeBlock(block, src); + if (block.wantSafety()) { + // TODO + } + + return block.addBitCast(dest_ty, operand); } fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -12983,6 +13015,13 @@ fn checkIntOrVector( } } +fn checkErrorSetType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { + switch (ty.zigTypeTag()) { + .ErrorSet => return, + else => return sema.fail(block, src, "expected error set type, found '{}'", .{ty}), + } +} + const SimdBinOp = struct { len: ?usize, /// Coerced to `result_ty`. diff --git a/src/value.zig b/src/value.zig index 5db780c99b..d576074ba3 100644 --- a/src/value.zig +++ b/src/value.zig @@ -2067,10 +2067,25 @@ pub const Value = extern union { } }, .ErrorUnion => { - @panic("TODO implement hashing error union values"); + if (val.tag() == .@"error") { + std.hash.autoHash(hasher, false); // error + const sub_ty = ty.errorUnionSet(); + val.hash(sub_ty, hasher); + return; + } + + if (val.castTag(.eu_payload)) |payload| { + std.hash.autoHash(hasher, true); // payload + const sub_ty = ty.errorUnionPayload(); + payload.data.hash(sub_ty, hasher); + return; + } else unreachable; }, .ErrorSet => { - @panic("TODO implement hashing error set values"); + // just hash the literal error value. this is the most stable + // thing between compiler invocations. we can't use the error + // int cause (1) its not stable and (2) we don't have access to mod. + hasher.update(val.getError().?); }, .Enum => { var enum_space: Payload.U64 = undefined; diff --git a/test/behavior/error.zig b/test/behavior/error.zig index ab95484d3e..7d3dee8d6a 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -209,7 +209,11 @@ fn testErrorSetType() !void { } test "explicit error set cast" { - if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO try testExplicitErrorSetCast(Set1.A); comptime try testExplicitErrorSetCast(Set1.A); @@ -220,7 +224,9 @@ const Set2 = error{ A, C }; fn testExplicitErrorSetCast(set1: Set1) !void { var x = @errSetCast(Set2, set1); + try expect(@TypeOf(x) == Set2); var y = @errSetCast(Set1, x); + try expect(@TypeOf(y) == Set1); try expect(y == error.A); }