diff --git a/src/Sema.zig b/src/Sema.zig index 04119617ff..269f726f57 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -17635,20 +17635,8 @@ fn resolvePeerTypes( if (candidate_ty.eql(chosen_ty)) continue; - // If the candidate can coerce into our chosen type, we're done. - // If the chosen type can coerce into the candidate, use that. - if ((try sema.coerceInMemoryAllowed(block, chosen_ty, candidate_ty, false, target, src, src)) == .ok) { - continue; - } - if ((try sema.coerceInMemoryAllowed(block, candidate_ty, chosen_ty, false, target, src, src)) == .ok) { - chosen = candidate; - chosen_i = candidate_i + 1; - continue; - } - const candidate_ty_tag = candidate_ty.zigTypeTag(); const chosen_ty_tag = chosen_ty.zigTypeTag(); - switch (candidate_ty_tag) { .NoReturn, .Undefined => continue, @@ -17780,6 +17768,51 @@ fn resolvePeerTypes( chosen_i = candidate_i + 1; continue; } + + // At this point, we must resolve any inferred error sets + if (candidate_ty.castTag(.error_set_inferred)) |inferred| { + try sema.resolveInferredErrorSet(inferred.data); + } + + // If anything is anyerror, we use anyerror always + if (candidate_ty.isAnyError()) { + err_set_ty = candidate_ty; + continue; + } + if (err_set_ty) |ty| + if (ty.isAnyError()) continue; + + if (err_set_ty == null) { + // Error unions are lazy, we're forced to resolve now. + // Otherwise, our candidate type cause we've never seen + // error sets up to this point + if (chosen_ty_tag == .ErrorUnion) { + err_set_ty = chosen_ty.errorUnionSet(); + + if (err_set_ty.?.castTag(.error_set_inferred)) |inferred| { + try sema.resolveInferredErrorSet(inferred.data); + } + + if (err_set_ty.?.isAnyError()) continue; + } else { + err_set_ty = candidate_ty; + continue; + } + } + + // If previous is superset, keep the previous + var prev_is_superset = true; + for (candidate_ty.errorSetNames()) |name| { + if (!err_set_ty.?.errorSetHasField(name)) { + prev_is_superset = false; + break; + } + } + if (prev_is_superset) continue; // use previous + + // Merge + err_set_ty = try err_set_ty.?.errorSetMerge(sema.arena, candidate_ty); + continue; }, .ErrorUnion => { if (chosen_ty_tag == .ErrorSet) { @@ -17803,14 +17836,14 @@ fn resolvePeerTypes( // If candidate is a superset of the error type, then use it. var cand_is_superset = true; for (err_set_ty.?.errorSetNames()) |name| { - if (!candidate_ty.errorSetHasField(name)) { + if (!eu_set_ty.errorSetHasField(name)) { cand_is_superset = false; break; } } if (cand_is_superset) { // Swap to candidate - err_set_ty = candidate_ty; + err_set_ty = eu_set_ty; chosen = candidate; chosen_i = candidate_i + 1; continue; @@ -18085,6 +18118,17 @@ fn resolvePeerTypes( else => {}, } + // If the candidate can coerce into our chosen type, we're done. + // If the chosen type can coerce into the candidate, use that. + if ((try sema.coerceInMemoryAllowed(block, chosen_ty, candidate_ty, false, target, src, src)) == .ok) { + continue; + } + if ((try sema.coerceInMemoryAllowed(block, candidate_ty, chosen_ty, false, target, src, src)) == .ok) { + chosen = candidate; + chosen_i = candidate_i + 1; + continue; + } + // At this point, we hit a compile error. We need to recover // the source locations. const chosen_src = candidate_srcs.resolve( diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 4ce4faa375..33f4eca8b7 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -615,6 +615,96 @@ test "peer type resolution: unreachable, error set, unreachable" { try expect(transformed_err == error.SystemResources); } +test "peer cast: error set any anyerror" { + const a: error{ One, Two } = undefined; + const b: anyerror = undefined; + try expect(@TypeOf(a, b) == anyerror); + try expect(@TypeOf(b, a) == anyerror); +} + +test "peer type resolution: error set supersets" { + const a: error{ One, Two } = undefined; + const b: error{One} = undefined; + + // A superset of B + { + const ty = @TypeOf(a, b); + const error_set_info = @typeInfo(ty); + try expect(error_set_info == .ErrorSet); + 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")); + } + + // B superset of A + { + const ty = @TypeOf(b, a); + const error_set_info = @typeInfo(ty); + try expect(error_set_info == .ErrorSet); + 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 type resolution: disjoint error sets" { + const a: error{ One, Two } = undefined; + const b: error{Three} = undefined; + + // note: order of error set members doesn't member, may want to sort + + { + const ty = @TypeOf(a, b); + const error_set_info = @typeInfo(ty); + try expect(error_set_info == .ErrorSet); + try expect(error_set_info.ErrorSet.?.len == 3); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One")); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Two")); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Three")); + } + + { + const ty = @TypeOf(b, a); + const error_set_info = @typeInfo(ty); + try expect(error_set_info == .ErrorSet); + try expect(error_set_info.ErrorSet.?.len == 3); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "Three")); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "One")); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Two")); + } +} + +test "peer type resolution: error union and error set" { + const a: error{Three} = 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); + + const error_set_info = @typeInfo(info.ErrorUnion.error_set); + try expect(error_set_info.ErrorSet.?.len == 3); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "Three")); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "One")); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Two")); + } + + { + const ty = @TypeOf(b, a); + const info = @typeInfo(ty); + try expect(info == .ErrorUnion); + + const error_set_info = @typeInfo(info.ErrorUnion.error_set); + try expect(error_set_info.ErrorSet.?.len == 3); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[0].name, "One")); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[1].name, "Two")); + try expect(mem.eql(u8, error_set_info.ErrorSet.?[2].name, "Three")); + } +} + 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