diff --git a/src/Sema.zig b/src/Sema.zig index 269f726f57..e5cad45e90 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17814,8 +17814,8 @@ fn resolvePeerTypes( err_set_ty = try err_set_ty.?.errorSetMerge(sema.arena, candidate_ty); continue; }, - .ErrorUnion => { - if (chosen_ty_tag == .ErrorSet) { + .ErrorUnion => switch (chosen_ty_tag) { + .ErrorSet => { if (err_set_ty.?.isAnyError()) { chosen = candidate; chosen_i = candidate_i + 1; @@ -17854,9 +17854,9 @@ fn resolvePeerTypes( chosen = candidate; chosen_i = candidate_i + 1; continue; - } + }, - if (chosen_ty_tag == .ErrorUnion) { + .ErrorUnion => { const chosen_payload_ty = chosen_ty.errorUnionPayload(); const candidate_payload_ty = candidate_ty.errorUnionPayload(); @@ -17926,30 +17926,57 @@ fn resolvePeerTypes( err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_ty); continue; } - } + }, - const payload_ty = candidate_ty.errorUnionPayload(); - if (chosen_ty_tag == .Pointer and - chosen_ty.ptrSize() == .One and - chosen_ty.childType().zigTypeTag() == .Array and - payload_ty.isSlice()) - { - const chosen_child_ty = chosen_ty.childType(); - const chosen_elem_ty = chosen_child_ty.elemType2(); - const candidate_elem_ty = payload_ty.elemType2(); - if ((try sema.coerceInMemoryAllowed(block, candidate_elem_ty, chosen_elem_ty, false, target, src, src)) == .ok) { + .Pointer => { + const payload_ty = candidate_ty.errorUnionPayload(); + if (chosen_ty.ptrSize() == .One and + chosen_ty.childType().zigTypeTag() == .Array and + payload_ty.isSlice()) + { + const chosen_child_ty = chosen_ty.childType(); + const chosen_elem_ty = chosen_child_ty.elemType2(); + const candidate_elem_ty = payload_ty.elemType2(); + if ((try sema.coerceInMemoryAllowed(block, candidate_elem_ty, chosen_elem_ty, false, target, src, src)) == .ok) { + chosen = candidate; + chosen_i = candidate_i + 1; + + convert_to_slice = false; // it already is a slice + + // If the prev pointer is const then we need to const + if (chosen_child_ty.isConstPtr()) + make_the_slice_const = true; + + continue; + } + } + }, + + else => { + // Chosen coercing into payload type + // Then merge error sets (if any) + const payload_ty = candidate_ty.errorUnionPayload(); + if ((try sema.coerceInMemoryAllowed(block, payload_ty, chosen_ty, false, target, src, src)) == .ok) { chosen = candidate; chosen_i = candidate_i + 1; - convert_to_slice = false; // it already is a slice + if (err_set_ty) |ty| { + const cand_set_ty = candidate_ty.errorUnionSet(); + if (cand_set_ty.castTag(.error_set_inferred)) |inferred| { + try sema.resolveInferredErrorSet(inferred.data); + } + if (cand_set_ty.isAnyError()) { + err_set_ty = cand_set_ty; + continue; + } + if (ty.isAnyError()) continue; - // If the prev pointer is const then we need to const - if (chosen_child_ty.isConstPtr()) - make_the_slice_const = true; + err_set_ty = try err_set_ty.?.errorSetMerge(sema.arena, cand_set_ty); + } continue; } - } + }, }, .Pointer => { if (candidate_ty.ptrSize() == .C) { @@ -18115,6 +18142,12 @@ fn resolvePeerTypes( continue; } }, + .ErrorUnion => { + const payload_ty = chosen_ty.errorUnionPayload(); + if ((try sema.coerceInMemoryAllowed(block, payload_ty, candidate_ty, false, target, src, src)) == .ok) { + continue; + } + }, else => {}, } diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 33f4eca8b7..14494a54c6 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -705,6 +705,37 @@ test "peer type resolution: error union and error set" { } } +test "peer type resolution: error union after non-error" { + const a: u32 = undefined; + const b: error{ One, Two }!u32 = undefined; + + // note: order of error set members doesn't member, may want to sort + + { + const ty = @TypeOf(a, b); + const info = @typeInfo(ty); + try expect(info == .ErrorUnion); + try expect(info.ErrorUnion.payload == u32); + + const error_set_info = @typeInfo(info.ErrorUnion.error_set); + try expect(error_set_info.ErrorSet.?.len == 2); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One")); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Two")); + } + + { + const ty = @TypeOf(b, a); + const info = @typeInfo(ty); + try expect(info == .ErrorUnion); + try expect(info.ErrorUnion.payload == u32); + + const error_set_info = @typeInfo(info.ErrorUnion.error_set); + try expect(error_set_info.ErrorSet.?.len == 2); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One")); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Two")); + } +} + test "peer cast *[0]T to E![]const T" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO