Skip to content

Commit

Permalink
clean up code by factoring code from Module.zig -> type.zig so that i…
Browse files Browse the repository at this point in the history
…t can be reused in peer type resolution and coercion
  • Loading branch information
g-w1 committed Dec 29, 2020
1 parent bafad89 commit 0b6e935
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 60 deletions.
62 changes: 3 additions & 59 deletions src/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2888,65 +2888,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;
}
}

Expand Down
42 changes: 42 additions & 0 deletions src/type.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1692,6 +1692,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()) {
Expand Down
2 changes: 1 addition & 1 deletion test/stage2/test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand Down

0 comments on commit 0b6e935

Please sign in to comment.