From c87032770fbedc7f7923663d86cb958e8f11b780 Mon Sep 17 00:00:00 2001 From: g-w1 Date: Tue, 29 Dec 2020 12:46:56 -0500 Subject: [PATCH] clean up code by factoring code from Module.zig -> type.zig so that it can be reused in peer type resolution and coercion --- src/Module.zig | 62 +++----------------------------------------- src/type.zig | 42 ++++++++++++++++++++++++++++++ test/stage2/test.zig | 2 +- 3 files changed, 46 insertions(+), 60 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index ca22820609b8..abb5411966ff 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2829,65 +2829,9 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst // error set widening if (inst.ty.zigTypeTag() == .ErrorSet and dest_type.zigTypeTag() == .ErrorSet) { - const gotten_err_set = inst.ty.getErrs(); - switch (dest_type.tag()) { - .error_set => { - switch (gotten_err_set) { - .multiple => |fields| { - const dt_e = dest_type.getErrs().multiple; - // we can do > because if they were equal then Type.eql wouldn't have let control flow get this far - if (dt_e.size > fields.size) blk: { - var it = fields.iterator(); - while (it.next()) |entry| { - if (dt_e.get(entry.key) == null) break :blk; // the smaller set has a key not in the larger set - } - var it_dest = dt_e.iterator(); - while (it_dest.next()) |entry| { - try fields.put(self.gpa, entry.key, entry.value); - } - } - return inst; - }, - .err_single => |name| blk: { // dont have to deinit anything because it is a slice - const dt_e = dest_type.getErrs().multiple; - if (dt_e.get(name) == null) break :blk; // the smaller set has a key not in the larger set - var new_decl_arena = std.heap.ArenaAllocator.init(self.gpa); - errdefer new_decl_arena.deinit(); - const payload_val = try scope.arena().create(Value.Payload.ErrorSet); - payload_val.* = .{ - .fields = .{}, - .decl = undefined, // populated below - }; - payload_val.fields = try dt_e.clone(&new_decl_arena.allocator); - // TODO create name in format "error:line:column" - const new_decl = try self.createAnonymousDecl(scope, &new_decl_arena, .{ - .ty = Type.initTag(.type), - .val = Value.initPayload(&payload_val.base), - }); - payload_val.decl = new_decl; - const payload_ty = try scope.arena().create(Type.Payload.ErrorSet); - payload_ty.* = .{ .decl = new_decl }; - inst.ty = Type.initPayload(&payload_ty.base); - return inst; - }, - else => unreachable, - } - }, - .error_set_single => {}, // we do nothing, because if they were equal Type.eql would have caught and if not, then not coercible - .anyerror => { - switch (gotten_err_set) { - .multiple => |fields| { - inst.ty = Type.initTag(.anyerror); - return inst; - }, - .err_single => |_| { - inst.ty = Type.initTag(.anyerror); - return inst; - }, - else => unreachable, - } - }, - else => unreachable, + if (dest_type.errorSetFitsInAnother(inst.ty)) { + inst.ty = dest_type; + return inst; } } diff --git a/src/type.zig b/src/type.zig index 333b38a0cf7b..5dc6108a7096 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1602,6 +1602,48 @@ pub const Type = extern union { .error_set => return .{ .multiple = &self.cast(Payload.ErrorSet).?.decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.ErrorSet).?.fields }, }; } + /// Asserts the type is error_set or error_set_single or anyerror and that if it is error_set, it's decl has been analyzed. + /// Returns true if fitee can fit into fitter, and false if not. + pub fn errorSetFitsInAnother(fitter: Type, fitee: Type) bool { + if (fitter.eql(fitee)) return true; + switch (fitee.getErrs()) { + .multiple => |fitee_set| { + switch (fitter.getErrs()) { + .multiple => |fitter_set| { + // we can do < because if they were equal then Type.eql wouldn't have let control flow get this far + if (fitee_set.size < fitter_set.size) { + var it = fitter_set.iterator(); + while (it.next()) |entry| { + // the smaller set has a key not in the larger set + if (fitee_set.get(entry.key) == null) return false; + } + return true; + } else return false; + }, + // we return false, because if they were equal Type.eql would have caught and if not, then not coercible + .err_single => return false, + // any set can fit into anyerror + .anyerror => return true, + } + }, + .err_single => |fitee_name| { + switch (fitter.getErrs()) { + .multiple => |fitter_set| { + // the smaller set has a key not in the larger set + if (fitter_set.get(fitee_name) == null) return false; + return true; + }, + // we return false, because if they were equal Type.eql would have caught and if not, then not coercible + .err_single => return false, + // any set can fit into anyerror + .anyerror => return true, + } + }, + + // if they were both anyerror, then Type.eql would have caught it, and if not, then nothing else can fit into anyerorr + .anyerror => return false, + } + } /// Asserts the type is a pointer or array type. pub fn elemType(self: Type) Type { return switch (self.tag()) { diff --git a/test/stage2/test.zig b/test/stage2/test.zig index b274422f7369..89138496061b 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -362,7 +362,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ } \\ unreachable; // because it will give error above \\} - , &[_][]const u8{":6:14: error: expected _start__anon_13, found error{Z}"}); + , &[_][]const u8{":6:14: error: expected _start__anon_12, found error{Z}"}); case.addError( \\export fn _start() noreturn { \\ const T = error{ A, B, C, D };