Commit ac7028f559

Mitchell Hashimoto <mitchell.hashimoto@gmail.com>
2022-03-03 04:01:55
stage2: implement @errSetCast (#11039)
1 parent f5e2e30
Changed files (3)
src
test
behavior
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`.
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;
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);
 }