stage2: fix coercion of error set to error union
When returning an error set or an error union from a function which has an inferred error set, it populates the error names in addition to the set of functions. This can have false negatives, meaning that after checking the map of an unresolved error set, one must do full error set resolution before emitting a compile error.
This commit is contained in:
55
src/Sema.zig
55
src/Sema.zig
@@ -1179,6 +1179,18 @@ fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: T
|
||||
return sema.fail(block, src, "remainder division with '{}' and '{}': signed integers and floats must use @rem or @mod", .{ lhs_ty, rhs_ty });
|
||||
}
|
||||
|
||||
fn failWithErrorSetCodeMissing(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
src: LazySrcLoc,
|
||||
dest_err_set_ty: Type,
|
||||
src_err_set_ty: Type,
|
||||
) CompileError {
|
||||
return sema.fail(block, src, "expected type '{}', found type '{}'", .{
|
||||
dest_err_set_ty, src_err_set_ty,
|
||||
});
|
||||
}
|
||||
|
||||
/// We don't return a pointer to the new error note because the pointer
|
||||
/// becomes invalid when you add another one.
|
||||
fn errNote(
|
||||
@@ -12858,47 +12870,30 @@ fn wrapErrorUnion(
|
||||
}
|
||||
switch (dest_err_set_ty.tag()) {
|
||||
.anyerror => {},
|
||||
.error_set_single => {
|
||||
.error_set_single => ok: {
|
||||
const expected_name = val.castTag(.@"error").?.data.name;
|
||||
const n = dest_err_set_ty.castTag(.error_set_single).?.data;
|
||||
if (!mem.eql(u8, expected_name, n)) {
|
||||
return sema.fail(
|
||||
block,
|
||||
inst_src,
|
||||
"expected type '{}', found type '{}'",
|
||||
.{ dest_err_set_ty, inst_ty },
|
||||
);
|
||||
}
|
||||
if (mem.eql(u8, expected_name, n)) break :ok;
|
||||
return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
|
||||
},
|
||||
.error_set => {
|
||||
.error_set => ok: {
|
||||
const expected_name = val.castTag(.@"error").?.data.name;
|
||||
const error_set = dest_err_set_ty.castTag(.error_set).?.data;
|
||||
const names = error_set.names_ptr[0..error_set.names_len];
|
||||
// TODO this is O(N). I'm putting off solving this until we solve inferred
|
||||
// error sets at the same time.
|
||||
const found = for (names) |name| {
|
||||
if (mem.eql(u8, expected_name, name)) break true;
|
||||
} else false;
|
||||
if (!found) {
|
||||
return sema.fail(
|
||||
block,
|
||||
inst_src,
|
||||
"expected type '{}', found type '{}'",
|
||||
.{ dest_err_set_ty, inst_ty },
|
||||
);
|
||||
for (names) |name| {
|
||||
if (mem.eql(u8, expected_name, name)) break :ok;
|
||||
}
|
||||
return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
|
||||
},
|
||||
.error_set_inferred => {
|
||||
.error_set_inferred => ok: {
|
||||
const err_set_payload = dest_err_set_ty.castTag(.error_set_inferred).?.data;
|
||||
if (err_set_payload.is_anyerror) break :ok;
|
||||
const expected_name = val.castTag(.@"error").?.data.name;
|
||||
const map = &dest_err_set_ty.castTag(.error_set_inferred).?.data.map;
|
||||
if (!map.contains(expected_name)) {
|
||||
return sema.fail(
|
||||
block,
|
||||
inst_src,
|
||||
"expected type '{}', found type '{}'",
|
||||
.{ dest_err_set_ty, inst_ty },
|
||||
);
|
||||
}
|
||||
if (err_set_payload.map.contains(expected_name)) break :ok;
|
||||
// TODO error set resolution here before emitting a compile error
|
||||
return sema.failWithErrorSetCodeMissing(block, inst_src, dest_err_set_ty, inst_ty);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
@@ -3869,6 +3869,11 @@ pub const Type = extern union {
|
||||
.error_set_inferred => {
|
||||
const func = err_set_ty.castTag(.error_set_inferred).?.data.func;
|
||||
try self.functions.put(gpa, func, {});
|
||||
var it = func.owner_decl.ty.fnReturnType().errorUnionSet()
|
||||
.castTag(.error_set_inferred).?.data.map.iterator();
|
||||
while (it.next()) |entry| {
|
||||
try self.map.put(gpa, entry.key_ptr.*, {});
|
||||
}
|
||||
},
|
||||
.anyerror => {
|
||||
self.is_anyerror = true;
|
||||
|
||||
Reference in New Issue
Block a user