From bea447a6378fbc65eb79f31c5f1770c75850074c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 Oct 2021 15:45:11 -0700 Subject: [PATCH] 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. --- src/Sema.zig | 55 ++++++++++++++++++++++++---------------------------- src/type.zig | 5 +++++ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 063a00f876..e3e8982fac 100644 --- a/src/Sema.zig +++ b/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, } diff --git a/src/type.zig b/src/type.zig index 324901faf0..2bf268bc5f 100644 --- a/src/type.zig +++ b/src/type.zig @@ -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;