mirror of
https://github.com/ziglang/zig.git
synced 2025-12-16 19:23:08 +00:00
stage2: implement peer type resolution between error unions
This commit is contained in:
parent
d5131e91eb
commit
bfada7c514
201
src/Sema.zig
201
src/Sema.zig
@ -17610,6 +17610,21 @@ fn resolvePeerTypes(
|
|||||||
const target = sema.mod.getTarget();
|
const target = sema.mod.getTarget();
|
||||||
|
|
||||||
var chosen = instructions[0];
|
var chosen = instructions[0];
|
||||||
|
var err_set_ty: ?Type = blk: {
|
||||||
|
const chosen_ty = sema.typeOf(chosen);
|
||||||
|
|
||||||
|
// TODO: is this the right handling of generic poison?
|
||||||
|
if (chosen_ty.tag() == .generic_poison or chosen_ty.zigTypeTag() != .ErrorSet)
|
||||||
|
break :blk null;
|
||||||
|
|
||||||
|
// If our chosen type is inferred, we have to resolve it now.
|
||||||
|
if (chosen_ty.castTag(.error_set_inferred)) |inferred| {
|
||||||
|
try sema.resolveInferredErrorSet(inferred.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
break :blk chosen_ty;
|
||||||
|
};
|
||||||
|
|
||||||
var any_are_null = false;
|
var any_are_null = false;
|
||||||
var make_the_slice_const = false;
|
var make_the_slice_const = false;
|
||||||
var convert_to_slice = false;
|
var convert_to_slice = false;
|
||||||
@ -17711,7 +17726,175 @@ fn resolvePeerTypes(
|
|||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
},
|
},
|
||||||
|
.ErrorSet => {
|
||||||
|
if (chosen_ty_tag == .ErrorSet) {
|
||||||
|
assert(err_set_ty != null);
|
||||||
|
|
||||||
|
// If chosen type is anyerror, then we can use the prev type
|
||||||
|
if (err_set_ty.?.isAnyError()) 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 candidate is anyerror then we use it because it
|
||||||
|
// is trivially a supserset of previous error set
|
||||||
|
if (candidate_ty.isAnyError()) {
|
||||||
|
err_set_ty = candidate_ty;
|
||||||
|
chosen = candidate;
|
||||||
|
chosen_i = candidate_i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If chosen is superset of candidate, keep it.
|
||||||
|
// If candidate is superset of chosen, switch it.
|
||||||
|
// If neither is a superset, merge errors.
|
||||||
|
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
|
||||||
|
|
||||||
|
var cand_is_superset = true;
|
||||||
|
for (err_set_ty.?.errorSetNames()) |name| {
|
||||||
|
if (!candidate_ty.errorSetHasField(name)) {
|
||||||
|
cand_is_superset = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cand_is_superset) {
|
||||||
|
// Swap to candidate
|
||||||
|
err_set_ty = candidate_ty;
|
||||||
|
chosen = candidate;
|
||||||
|
chosen_i = candidate_i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge errors
|
||||||
|
err_set_ty = try err_set_ty.?.errorSetMerge(sema.arena, candidate_ty);
|
||||||
|
chosen = candidate;
|
||||||
|
chosen_i = candidate_i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
},
|
||||||
.ErrorUnion => {
|
.ErrorUnion => {
|
||||||
|
if (chosen_ty_tag == .ErrorSet) {
|
||||||
|
if (err_set_ty.?.isAnyError()) {
|
||||||
|
chosen = candidate;
|
||||||
|
chosen_i = candidate_i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const eu_set_ty = candidate_ty.errorUnionSet();
|
||||||
|
if (eu_set_ty.castTag(.error_set_inferred)) |inferred| {
|
||||||
|
try sema.resolveInferredErrorSet(inferred.data);
|
||||||
|
}
|
||||||
|
if (eu_set_ty.isAnyError()) {
|
||||||
|
err_set_ty = eu_set_ty;
|
||||||
|
chosen = candidate;
|
||||||
|
chosen_i = candidate_i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)) {
|
||||||
|
cand_is_superset = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cand_is_superset) {
|
||||||
|
// Swap to candidate
|
||||||
|
err_set_ty = candidate_ty;
|
||||||
|
chosen = candidate;
|
||||||
|
chosen_i = candidate_i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not a superset, create merged error set
|
||||||
|
err_set_ty = try err_set_ty.?.errorSetMerge(sema.arena, eu_set_ty);
|
||||||
|
chosen = candidate;
|
||||||
|
chosen_i = candidate_i + 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chosen_ty_tag == .ErrorUnion) {
|
||||||
|
const chosen_payload_ty = chosen_ty.errorUnionPayload();
|
||||||
|
const candidate_payload_ty = candidate_ty.errorUnionPayload();
|
||||||
|
|
||||||
|
const coerce_chosen = (try sema.coerceInMemoryAllowed(block, chosen_payload_ty, candidate_payload_ty, false, target, src, src)) == .ok;
|
||||||
|
const coerce_candidate = (try sema.coerceInMemoryAllowed(block, candidate_payload_ty, chosen_payload_ty, false, target, src, src)) == .ok;
|
||||||
|
|
||||||
|
if (coerce_chosen or coerce_candidate) {
|
||||||
|
// If we can coerce to the candidate, we switch to that
|
||||||
|
// type. This is the same logic as the bare (non-union)
|
||||||
|
// coercion check we do at the top of this func.
|
||||||
|
if (coerce_candidate) {
|
||||||
|
chosen = candidate;
|
||||||
|
chosen_i = candidate_i + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chosen_set_ty = chosen_ty.errorUnionSet();
|
||||||
|
const candidate_set_ty = chosen_ty.errorUnionSet();
|
||||||
|
|
||||||
|
// If our error sets match already, then we are done.
|
||||||
|
if (chosen_set_ty.eql(candidate_set_ty)) continue;
|
||||||
|
|
||||||
|
// They don't match, so we need to figure out if we
|
||||||
|
// need to merge them, use the superset, etc. This
|
||||||
|
// requires resolution.
|
||||||
|
if (chosen_set_ty.castTag(.error_set_inferred)) |inferred| {
|
||||||
|
try sema.resolveInferredErrorSet(inferred.data);
|
||||||
|
}
|
||||||
|
if (candidate_set_ty.castTag(.error_set_inferred)) |inferred| {
|
||||||
|
try sema.resolveInferredErrorSet(inferred.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chosen_set_ty.isAnyError()) {
|
||||||
|
err_set_ty = chosen_set_ty;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (candidate_set_ty.isAnyError()) {
|
||||||
|
err_set_ty = candidate_set_ty;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err_set_ty == null) err_set_ty = chosen_set_ty;
|
||||||
|
|
||||||
|
// If the previous error set type is a superset, we're done.
|
||||||
|
var prev_is_superset = true;
|
||||||
|
for (candidate_set_ty.errorSetNames()) |name| {
|
||||||
|
if (!chosen_set_ty.errorSetHasField(name)) {
|
||||||
|
prev_is_superset = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (prev_is_superset) continue; // use previous
|
||||||
|
|
||||||
|
var cand_is_superset = true;
|
||||||
|
for (chosen_set_ty.errorSetNames()) |name| {
|
||||||
|
if (!candidate_set_ty.errorSetHasField(name)) {
|
||||||
|
cand_is_superset = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cand_is_superset) {
|
||||||
|
err_set_ty = candidate_ty;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge errors
|
||||||
|
err_set_ty = try chosen_set_ty.errorSetMerge(sema.arena, candidate_ty);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const payload_ty = candidate_ty.errorUnionPayload();
|
const payload_ty = candidate_ty.errorUnionPayload();
|
||||||
if (chosen_ty_tag == .Pointer and
|
if (chosen_ty_tag == .Pointer and
|
||||||
chosen_ty.ptrSize() == .One and
|
chosen_ty.ptrSize() == .One and
|
||||||
@ -17962,6 +18145,24 @@ fn resolvePeerTypes(
|
|||||||
return Type.ptr(sema.arena, target, info.data);
|
return Type.ptr(sema.arena, target, info.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (err_set_ty) |ty| switch (chosen_ty.zigTypeTag()) {
|
||||||
|
.ErrorSet => return ty,
|
||||||
|
|
||||||
|
.ErrorUnion => {
|
||||||
|
const payload_ty = chosen_ty.errorUnionPayload();
|
||||||
|
return try Module.errorUnionType(sema.arena, ty, payload_ty);
|
||||||
|
},
|
||||||
|
|
||||||
|
.ComptimeInt, .ComptimeFloat => return sema.fail(block, src, "unable to make error union out of number literal", .{}),
|
||||||
|
|
||||||
|
.Null => return sema.fail(block, src, "unable to make error union out of null literal", .{}),
|
||||||
|
|
||||||
|
else => {
|
||||||
|
// Create error union of our error set and the chosen type
|
||||||
|
return try Module.errorUnionType(sema.arena, ty, chosen_ty);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
return chosen_ty;
|
return chosen_ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
src/type.zig
17
src/type.zig
@ -4216,6 +4216,23 @@ pub const Type = extern union {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Merge ty with ty2.
|
||||||
|
/// Asserts that ty and ty2 are both error sets and are resolved.
|
||||||
|
pub fn errorSetMerge(ty: Type, arena: Allocator, ty2: Type) !Type {
|
||||||
|
const lhs_names = ty.errorSetNames();
|
||||||
|
const rhs_names = ty2.errorSetNames();
|
||||||
|
var names = Module.ErrorSet.NameMap{};
|
||||||
|
try names.ensureUnusedCapacity(arena, @intCast(u32, lhs_names.len + rhs_names.len));
|
||||||
|
for (lhs_names) |name| {
|
||||||
|
names.putAssumeCapacityNoClobber(name, {});
|
||||||
|
}
|
||||||
|
for (rhs_names) |name| {
|
||||||
|
names.putAssumeCapacity(name, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return try Tag.error_set_merged.create(arena, names);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn enumFields(ty: Type) Module.EnumFull.NameMap {
|
pub fn enumFields(ty: Type) Module.EnumFull.NameMap {
|
||||||
return switch (ty.tag()) {
|
return switch (ty.tag()) {
|
||||||
.enum_full, .enum_nonexhaustive => ty.cast(Payload.EnumFull).?.data.fields,
|
.enum_full, .enum_nonexhaustive => ty.cast(Payload.EnumFull).?.data.fields,
|
||||||
|
|||||||
@ -264,8 +264,6 @@ fn testErrToIntWithOnePossibleValue(
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "error union peer type resolution" {
|
test "error union peer type resolution" {
|
||||||
if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
|
|
||||||
|
|
||||||
try testErrorUnionPeerTypeResolution(1);
|
try testErrorUnionPeerTypeResolution(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user