Skip to content

Commit

Permalink
improve stage2 to allow catch at comptime:
Browse files Browse the repository at this point in the history
* add error_union value tag.
* add analyzeIsErr
* add Value.isError
* add TZIR wrap_errunion_payload and wrap_errunion_err for
  wrapping from T -> E!T and E -> E!T
* add anlyzeInstUnwrapErrCode and analyzeInstUnwrapErr
* add analyzeInstEnsureErrPayloadVoid:
* Fix bug in astgen where .? was in wrong spot causing segfault when using this check
* add wrapErrorUnion
* add comptime error comparison for tests
* tests!!!
  • Loading branch information
g-w1 committed Feb 3, 2021
1 parent 2d447b5 commit 43aa51e
Show file tree
Hide file tree
Showing 8 changed files with 485 additions and 13 deletions.
70 changes: 67 additions & 3 deletions src/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2533,7 +2533,15 @@ pub fn analyzeIsNull(
}

pub fn analyzeIsErr(self: *Module, scope: *Scope, src: usize, operand: *Inst) InnerError!*Inst {
return self.fail(scope, src, "TODO implement analysis of iserr", .{});
const ot = operand.ty.zigTypeTag();
if (ot != .ErrorSet and ot != .ErrorUnion) return self.constBool(scope, src, false);
if (ot == .ErrorSet) return self.constBool(scope, src, true);
assert(ot == .ErrorUnion);
if (operand.value()) |err_union| {
return self.constBool(scope, src, err_union.getError() != null);
}
const b = try self.requireRuntimeBlock(scope, src);
return self.addUnOp(b, src, Type.initTag(.bool), .is_err, operand);
}

pub fn analyzeSlice(self: *Module, scope: *Scope, src: usize, array_ptr: *Inst, start: *Inst, end_opt: ?*Inst, sentinel_opt: ?*Inst) InnerError!*Inst {
Expand Down Expand Up @@ -2836,6 +2844,56 @@ fn wrapOptional(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*In
return self.addUnOp(b, inst.src, dest_type, .wrap_optional, inst);
}

fn wrapErrorUnion(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
// TODO deal with inferred error sets
const err_union = dest_type.castTag(.error_union).?;
if (inst.value()) |val| {
return self.constInst(scope, inst.src, .{
.ty = dest_type,
// creating a SubValue for the error_union payload
.val = try Value.Tag.error_union.create(
scope.arena(),
blk: {
if (inst.ty.zigTypeTag() != .ErrorSet) {
// .? because we know it has a value
_ = try self.coerce(scope, err_union.data.payload, inst);
break :blk val;
} else {
switch (err_union.data.error_set.tag()) {
// TODO these should be moved to coerce with error union widening
.anyerror => break :blk val,
.error_set_single => {
const n = err_union.data.error_set.castTag(.error_set_single).?.data;
if (!mem.eql(u8, val.castTag(.@"error").?.data.name, n))
return self.fail(scope, inst.src, "expected type '{}', found type '{}'", .{ err_union.data.error_set, inst.ty });
break :blk val;
},
.error_set => {
const f = err_union.data.error_set.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields;
if (f.get(val.castTag(.@"error").?.data.name) == null)
return self.fail(scope, inst.src, "expected type '{}', found type '{}'", .{ err_union.data.error_set, inst.ty });
break :blk val;
},
else => unreachable,
}
}
},
),
});
}

const b = try self.requireRuntimeBlock(scope, inst.src);

// we are coercing from E to E!T
if (inst.ty.zigTypeTag() == .ErrorSet) {
var coerced = try self.coerce(scope, err_union.data.error_set, inst);
return self.addUnOp(b, inst.src, dest_type, .wrap_errunion_err, coerced);
} else {
var coerced = try self.coerce(scope, err_union.data.payload, inst);
return self.addUnOp(b, inst.src, dest_type, .wrap_errunion_payload, coerced);
}
}

fn makeIntType(self: *Module, scope: *Scope, signed: bool, bits: u16) !Type {
const int_payload = try scope.arena().create(Type.Payload.Bits);
int_payload.* = .{
Expand Down Expand Up @@ -2902,7 +2960,7 @@ pub fn resolvePeerTypes(self: *Module, scope: *Scope, instructions: []*Inst) !Ty
return chosen.ty;
}

pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst {
pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) InnerError!*Inst {
// If the types are the same, we can return the operand.
if (dest_type.eql(inst.ty))
return inst;
Expand Down Expand Up @@ -2936,6 +2994,11 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
}
}

// T to E!T or E to E!T
if (dest_type.tag() == .error_union) {
return try self.wrapErrorUnion(scope, dest_type, inst);
}

// Coercions where the source is a single pointer to an array.
src_array_ptr: {
if (!inst.ty.isSinglePointer()) break :src_array_ptr;
Expand Down Expand Up @@ -3014,7 +3077,7 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst
return self.fail(scope, inst.src, "expected {}, found {}", .{ dest_type, inst.ty });
}

pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !?*Inst {
pub fn coerceNum(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) InnerError!?*Inst {
const val = inst.value() orelse return null;
const src_zig_tag = inst.ty.zigTypeTag();
const dst_zig_tag = dest_type.zigTypeTag();
Expand Down Expand Up @@ -3504,6 +3567,7 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void {
pub const PanicId = enum {
unreach,
unwrap_null,
unwrap_errunion,
};

pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void {
Expand Down
4 changes: 2 additions & 2 deletions src/astgen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1762,8 +1762,8 @@ const CondKind = union(enum) {

fn thenSubScope(self: CondKind, mod: *Module, then_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
if (self == .bool) return &then_scope.base;

const payload = payload_node.?.castTag(.PointerPayload) orelse {
const payload =
payload_node.?.castTag(.PointerPayload) orelse {
// condition is error union and payload is not explicitly ignored
_ = try addZIRUnOp(mod, &then_scope.base, src, .ensure_err_payload_void, self.err_union.?);
return &then_scope.base;
Expand Down
62 changes: 62 additions & 0 deletions src/codegen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -880,7 +880,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
.unreach => return MCValue{ .unreach = {} },
.optional_payload => return self.genOptionalPayload(inst.castTag(.optional_payload).?),
.optional_payload_ptr => return self.genOptionalPayloadPtr(inst.castTag(.optional_payload_ptr).?),
.unwrap_errunion_err => return self.genUnwrapErrErr(inst.castTag(.unwrap_errunion_err).?),
.unwrap_errunion_payload => return self.genUnwrapErrPayload(inst.castTag(.unwrap_errunion_payload).?),
.unwrap_errunion_err_ptr => return self.genUnwrapErrErrPtr(inst.castTag(.unwrap_errunion_err_ptr).?),
.unwrap_errunion_payload_ptr => return self.genUnwrapErrPayloadPtr(inst.castTag(.unwrap_errunion_payload_ptr).?),
.wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
.wrap_errunion_payload => return self.genWrapErrUnionPayload(inst.castTag(.wrap_errunion_payload).?),
.wrap_errunion_err => return self.genWrapErrUnionErr(inst.castTag(.wrap_errunion_err).?),
.varptr => return self.genVarPtr(inst.castTag(.varptr).?),
.xor => return self.genXor(inst.castTag(.xor).?),
}
Expand Down Expand Up @@ -1141,6 +1147,41 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
}

fn genUnwrapErrErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
// No side effects, so if it's unreferenced, do nothing.
if (inst.base.isUnused())
return MCValue.dead;
switch (arch) {
else => return self.fail(inst.base.src, "TODO implement unwrap error union error for {}", .{self.target.cpu.arch}),
}
}

fn genUnwrapErrPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
// No side effects, so if it's unreferenced, do nothing.
if (inst.base.isUnused())
return MCValue.dead;
switch (arch) {
else => return self.fail(inst.base.src, "TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}),
}
}
// *(E!T) -> E
fn genUnwrapErrErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
// No side effects, so if it's unreferenced, do nothing.
if (inst.base.isUnused())
return MCValue.dead;
switch (arch) {
else => return self.fail(inst.base.src, "TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch}),
}
}
// *(E!T) -> *T
fn genUnwrapErrPayloadPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
// No side effects, so if it's unreferenced, do nothing.
if (inst.base.isUnused())
return MCValue.dead;
switch (arch) {
else => return self.fail(inst.base.src, "TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch}),
}
}
fn genWrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
const optional_ty = inst.base.ty;

Expand All @@ -1157,6 +1198,27 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
}
}

/// T to E!T
fn genWrapErrUnionPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
// No side effects, so if it's unreferenced, do nothing.
if (inst.base.isUnused())
return MCValue.dead;

switch (arch) {
else => return self.fail(inst.base.src, "TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}),
}
}

/// E to E!T
fn genWrapErrUnionErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
// No side effects, so if it's unreferenced, do nothing.
if (inst.base.isUnused())
return MCValue.dead;

switch (arch) {
else => return self.fail(inst.base.src, "TODO implement wrap errunion error for {}", .{self.target.cpu.arch}),
}
}
fn genVarPtr(self: *Self, inst: *ir.Inst.VarPtr) !MCValue {
// No side effects, so if it's unreferenced, do nothing.
if (inst.base.isUnused())
Expand Down
18 changes: 18 additions & 0 deletions src/ir.zig
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,18 @@ pub const Inst = struct {
// *?T => *T
optional_payload_ptr,
wrap_optional,
/// E!T -> T
unwrap_errunion_payload,
/// E!T -> E
unwrap_errunion_err,
/// *(E!T) -> *T
unwrap_errunion_payload_ptr,
/// *(E!T) -> E
unwrap_errunion_err_ptr,
/// wrap from T to E!T
wrap_errunion_payload,
/// wrap from E to E!T
wrap_errunion_err,
xor,
switchbr,

Expand Down Expand Up @@ -143,6 +155,12 @@ pub const Inst = struct {
.optional_payload,
.optional_payload_ptr,
.wrap_optional,
.unwrap_errunion_payload,
.unwrap_errunion_err,
.unwrap_errunion_payload_ptr,
.unwrap_errunion_err_ptr,
.wrap_errunion_payload,
.wrap_errunion_err,
=> UnOp,

.add,
Expand Down
Loading

0 comments on commit 43aa51e

Please sign in to comment.