stage2: fix if expressions on error unions
AstGen had the then-else logic backwards for if expressions
on error unions. This commit fixes it.
Turns out AstGen only really needs `is_non_null` and `is_non_err`,
and does not need the `is_null` or `is_err` variants. So I removed the
`is_null{,_ptr}` and `is_err{,_ptr}` ZIR instructions (-4) and
added `is_non_err`, `is_non_err_ptr` ZIR instructions (+2) for
a total of (-2) ZIR instructions, giving us a tiny bit more headroom
within the 256 tag limit. This required swapping the order of
then/else blocks in a handful of cases, but ultimately means the
ZIR will be in the same as source order, which is convenient
when debugging.
AIR code on the other hand, gains the `is_non_err` and `is_non_err_ptr`
instructions.
Sema: fix logic in zirErrUnionCode and zirErrUnionCodePtr returning the
wrong result type.
This commit is contained in:
@@ -786,7 +786,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
|
||||
rl,
|
||||
node,
|
||||
node_datas[node].lhs,
|
||||
.is_err_ptr,
|
||||
.is_non_err_ptr,
|
||||
.err_union_payload_unsafe_ptr,
|
||||
.err_union_code_ptr,
|
||||
node_datas[node].rhs,
|
||||
@@ -798,7 +798,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
|
||||
rl,
|
||||
node,
|
||||
node_datas[node].lhs,
|
||||
.is_err,
|
||||
.is_non_err,
|
||||
.err_union_payload_unsafe,
|
||||
.err_union_code,
|
||||
node_datas[node].rhs,
|
||||
@@ -813,7 +813,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
|
||||
rl,
|
||||
node,
|
||||
node_datas[node].lhs,
|
||||
.is_null_ptr,
|
||||
.is_non_null_ptr,
|
||||
.optional_payload_unsafe_ptr,
|
||||
undefined,
|
||||
node_datas[node].rhs,
|
||||
@@ -825,7 +825,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr
|
||||
rl,
|
||||
node,
|
||||
node_datas[node].lhs,
|
||||
.is_null,
|
||||
.is_non_null,
|
||||
.optional_payload_unsafe,
|
||||
undefined,
|
||||
node_datas[node].rhs,
|
||||
@@ -1948,11 +1948,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner
|
||||
.float128,
|
||||
.int_type,
|
||||
.is_non_null,
|
||||
.is_null,
|
||||
.is_non_null_ptr,
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_err_ptr,
|
||||
.is_non_err,
|
||||
.is_non_err_ptr,
|
||||
.mod_rem,
|
||||
.mul,
|
||||
.mulwrap,
|
||||
@@ -4621,8 +4619,8 @@ fn tryExpr(
|
||||
};
|
||||
const err_ops = switch (rl) {
|
||||
// zig fmt: off
|
||||
.ref => [3]Zir.Inst.Tag{ .is_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr },
|
||||
else => [3]Zir.Inst.Tag{ .is_err, .err_union_code, .err_union_payload_unsafe },
|
||||
.ref => [3]Zir.Inst.Tag{ .is_non_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr },
|
||||
else => [3]Zir.Inst.Tag{ .is_non_err, .err_union_code, .err_union_payload_unsafe },
|
||||
// zig fmt: on
|
||||
};
|
||||
// This could be a pointer or value depending on the `operand_rl` parameter.
|
||||
@@ -4640,20 +4638,20 @@ fn tryExpr(
|
||||
var then_scope = parent_gz.makeSubBlock(scope);
|
||||
defer then_scope.instructions.deinit(astgen.gpa);
|
||||
|
||||
const err_code = try then_scope.addUnNode(err_ops[1], operand, node);
|
||||
try genDefers(&then_scope, &fn_block.base, scope, .{ .both = err_code });
|
||||
const then_result = try then_scope.addUnNode(.ret_node, err_code, node);
|
||||
block_scope.break_count += 1;
|
||||
// This could be a pointer or value depending on `err_ops[2]`.
|
||||
const unwrapped_payload = try then_scope.addUnNode(err_ops[2], operand, node);
|
||||
const then_result = switch (rl) {
|
||||
.ref => unwrapped_payload,
|
||||
else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node),
|
||||
};
|
||||
|
||||
var else_scope = parent_gz.makeSubBlock(scope);
|
||||
defer else_scope.instructions.deinit(astgen.gpa);
|
||||
|
||||
block_scope.break_count += 1;
|
||||
// This could be a pointer or value depending on `err_ops[2]`.
|
||||
const unwrapped_payload = try else_scope.addUnNode(err_ops[2], operand, node);
|
||||
const else_result = switch (rl) {
|
||||
.ref => unwrapped_payload,
|
||||
else => try rvalue(&else_scope, block_scope.break_result_loc, unwrapped_payload, node),
|
||||
};
|
||||
const err_code = try else_scope.addUnNode(err_ops[1], operand, node);
|
||||
try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
|
||||
const else_result = try else_scope.addUnNode(.ret_node, err_code, node);
|
||||
|
||||
return finishThenElseBlock(
|
||||
parent_gz,
|
||||
@@ -4711,18 +4709,28 @@ fn orelseCatchExpr(
|
||||
var then_scope = parent_gz.makeSubBlock(scope);
|
||||
defer then_scope.instructions.deinit(astgen.gpa);
|
||||
|
||||
// This could be a pointer or value depending on `unwrap_op`.
|
||||
const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);
|
||||
const then_result = switch (rl) {
|
||||
.ref => unwrapped_payload,
|
||||
else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node),
|
||||
};
|
||||
|
||||
var else_scope = parent_gz.makeSubBlock(scope);
|
||||
defer else_scope.instructions.deinit(astgen.gpa);
|
||||
|
||||
var err_val_scope: Scope.LocalVal = undefined;
|
||||
const then_sub_scope = blk: {
|
||||
const payload = payload_token orelse break :blk &then_scope.base;
|
||||
const else_sub_scope = blk: {
|
||||
const payload = payload_token orelse break :blk &else_scope.base;
|
||||
if (mem.eql(u8, tree.tokenSlice(payload), "_")) {
|
||||
return astgen.failTok(payload, "discard of error capture; omit it instead", .{});
|
||||
}
|
||||
const err_name = try astgen.identAsString(payload);
|
||||
err_val_scope = .{
|
||||
.parent = &then_scope.base,
|
||||
.gen_zir = &then_scope,
|
||||
.parent = &else_scope.base,
|
||||
.gen_zir = &else_scope,
|
||||
.name = err_name,
|
||||
.inst = try then_scope.addUnNode(unwrap_code_op, operand, node),
|
||||
.inst = try else_scope.addUnNode(unwrap_code_op, operand, node),
|
||||
.token_src = payload,
|
||||
.id_cat = .@"capture",
|
||||
};
|
||||
@@ -4730,23 +4738,13 @@ fn orelseCatchExpr(
|
||||
};
|
||||
|
||||
block_scope.break_count += 1;
|
||||
const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, rhs);
|
||||
try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
|
||||
const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_loc, rhs);
|
||||
try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
|
||||
|
||||
// We hold off on the break instructions as well as copying the then/else
|
||||
// instructions into place until we know whether to keep store_to_block_ptr
|
||||
// instructions or not.
|
||||
|
||||
var else_scope = parent_gz.makeSubBlock(scope);
|
||||
defer else_scope.instructions.deinit(astgen.gpa);
|
||||
|
||||
// This could be a pointer or value depending on `unwrap_op`.
|
||||
const unwrapped_payload = try else_scope.addUnNode(unwrap_op, operand, node);
|
||||
const else_result = switch (rl) {
|
||||
.ref => unwrapped_payload,
|
||||
else => try rvalue(&else_scope, block_scope.break_result_loc, unwrapped_payload, node),
|
||||
};
|
||||
|
||||
return finishThenElseBlock(
|
||||
parent_gz,
|
||||
rl,
|
||||
@@ -4964,7 +4962,7 @@ fn ifExpr(
|
||||
if (if_full.error_token) |_| {
|
||||
const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
|
||||
const err_union = try expr(&block_scope, &block_scope.base, cond_rl, if_full.ast.cond_expr);
|
||||
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_err_ptr else .is_err;
|
||||
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
|
||||
break :c .{
|
||||
.inst = err_union,
|
||||
.bool_bit = try block_scope.addUnNode(tag, err_union, node),
|
||||
@@ -5221,7 +5219,7 @@ fn whileExpr(
|
||||
if (while_full.error_token) |_| {
|
||||
const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
|
||||
const err_union = try expr(&continue_scope, &continue_scope.base, cond_rl, while_full.ast.cond_expr);
|
||||
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_err_ptr else .is_err;
|
||||
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
|
||||
break :c .{
|
||||
.inst = err_union,
|
||||
.bool_bit = try continue_scope.addUnNode(tag, err_union, node),
|
||||
@@ -6229,23 +6227,25 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref
|
||||
}
|
||||
|
||||
// Emit conditional branch for generating errdefers.
|
||||
const is_err = try gz.addUnNode(.is_err, operand, node);
|
||||
const is_non_err = try gz.addUnNode(.is_non_err, operand, node);
|
||||
const condbr = try gz.addCondBr(.condbr, node);
|
||||
|
||||
var then_scope = gz.makeSubBlock(scope);
|
||||
defer then_scope.instructions.deinit(astgen.gpa);
|
||||
const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{
|
||||
.both = try then_scope.addUnNode(.err_union_code, operand, node),
|
||||
};
|
||||
try genDefers(&then_scope, defer_outer, scope, which_ones);
|
||||
|
||||
try genDefers(&then_scope, defer_outer, scope, .normal_only);
|
||||
_ = try then_scope.addUnNode(.ret_node, operand, node);
|
||||
|
||||
var else_scope = gz.makeSubBlock(scope);
|
||||
defer else_scope.instructions.deinit(astgen.gpa);
|
||||
try genDefers(&else_scope, defer_outer, scope, .normal_only);
|
||||
|
||||
const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{
|
||||
.both = try else_scope.addUnNode(.err_union_code, operand, node),
|
||||
};
|
||||
try genDefers(&else_scope, defer_outer, scope, which_ones);
|
||||
_ = try else_scope.addUnNode(.ret_node, operand, node);
|
||||
|
||||
try setCondBrPayload(condbr, is_err, &then_scope, &else_scope);
|
||||
try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope);
|
||||
|
||||
return Zir.Inst.Ref.unreachable_value;
|
||||
},
|
||||
|
||||
55
src/Sema.zig
55
src/Sema.zig
@@ -225,12 +225,10 @@ pub fn analyzeBody(
|
||||
.float => try sema.zirFloat(block, inst),
|
||||
.float128 => try sema.zirFloat128(block, inst),
|
||||
.int_type => try sema.zirIntType(block, inst),
|
||||
.is_err => try sema.zirIsErr(block, inst),
|
||||
.is_err_ptr => try sema.zirIsErrPtr(block, inst),
|
||||
.is_non_null => try sema.zirIsNull(block, inst, true),
|
||||
.is_non_null_ptr => try sema.zirIsNullPtr(block, inst, true),
|
||||
.is_null => try sema.zirIsNull(block, inst, false),
|
||||
.is_null_ptr => try sema.zirIsNullPtr(block, inst, false),
|
||||
.is_non_err => try sema.zirIsNonErr(block, inst),
|
||||
.is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst),
|
||||
.is_non_null => try sema.zirIsNonNull(block, inst),
|
||||
.is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst),
|
||||
.loop => try sema.zirLoop(block, inst),
|
||||
.merge_error_sets => try sema.zirMergeErrorSets(block, inst),
|
||||
.negate => try sema.zirNegate(block, inst, .sub),
|
||||
@@ -2981,17 +2979,19 @@ fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner
|
||||
if (operand.ty.zigTypeTag() != .ErrorUnion)
|
||||
return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty});
|
||||
|
||||
const result_ty = operand.ty.castTag(.error_union).?.data.error_set;
|
||||
|
||||
if (operand.value()) |val| {
|
||||
assert(val.getError() != null);
|
||||
const data = val.castTag(.error_union).?.data;
|
||||
return sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = operand.ty.castTag(.error_union).?.data.error_set,
|
||||
.ty = result_ty,
|
||||
.val = data,
|
||||
});
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err, operand);
|
||||
return block.addUnOp(src, result_ty, .unwrap_errunion_err, operand);
|
||||
}
|
||||
|
||||
/// Pointer in, value out
|
||||
@@ -3007,18 +3007,20 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In
|
||||
if (operand.ty.elemType().zigTypeTag() != .ErrorUnion)
|
||||
return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()});
|
||||
|
||||
const result_ty = operand.ty.elemType().castTag(.error_union).?.data.error_set;
|
||||
|
||||
if (operand.value()) |pointer_val| {
|
||||
const val = try pointer_val.pointerDeref(sema.arena);
|
||||
assert(val.getError() != null);
|
||||
const data = val.castTag(.error_union).?.data;
|
||||
return sema.mod.constInst(sema.arena, src, .{
|
||||
.ty = operand.ty.elemType().castTag(.error_union).?.data.error_set,
|
||||
.ty = result_ty,
|
||||
.val = data,
|
||||
});
|
||||
}
|
||||
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand);
|
||||
return block.addUnOp(src, result_ty, .unwrap_errunion_err_ptr, operand);
|
||||
}
|
||||
|
||||
fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void {
|
||||
@@ -5298,11 +5300,10 @@ fn zirBoolBr(
|
||||
return &block_inst.base;
|
||||
}
|
||||
|
||||
fn zirIsNull(
|
||||
fn zirIsNonNull(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
inst: Zir.Inst.Index,
|
||||
invert_logic: bool,
|
||||
) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
@@ -5310,14 +5311,13 @@ fn zirIsNull(
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const src = inst_data.src();
|
||||
const operand = try sema.resolveInst(inst_data.operand);
|
||||
return sema.analyzeIsNull(block, src, operand, invert_logic);
|
||||
return sema.analyzeIsNull(block, src, operand, true);
|
||||
}
|
||||
|
||||
fn zirIsNullPtr(
|
||||
fn zirIsNonNullPtr(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
inst: Zir.Inst.Index,
|
||||
invert_logic: bool,
|
||||
) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
@@ -5326,19 +5326,19 @@ fn zirIsNullPtr(
|
||||
const src = inst_data.src();
|
||||
const ptr = try sema.resolveInst(inst_data.operand);
|
||||
const loaded = try sema.analyzeLoad(block, src, ptr, src);
|
||||
return sema.analyzeIsNull(block, src, loaded, invert_logic);
|
||||
return sema.analyzeIsNull(block, src, loaded, true);
|
||||
}
|
||||
|
||||
fn zirIsErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
|
||||
fn zirIsNonErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const operand = try sema.resolveInst(inst_data.operand);
|
||||
return sema.analyzeIsErr(block, inst_data.src(), operand);
|
||||
return sema.analyzeIsNonErr(block, inst_data.src(), operand);
|
||||
}
|
||||
|
||||
fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
|
||||
fn zirIsNonErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
@@ -5346,7 +5346,7 @@ fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro
|
||||
const src = inst_data.src();
|
||||
const ptr = try sema.resolveInst(inst_data.operand);
|
||||
const loaded = try sema.analyzeLoad(block, src, ptr, src);
|
||||
return sema.analyzeIsErr(block, src, loaded);
|
||||
return sema.analyzeIsNonErr(block, src, loaded);
|
||||
}
|
||||
|
||||
fn zirCondbr(
|
||||
@@ -7219,20 +7219,25 @@ fn analyzeIsNull(
|
||||
return block.addUnOp(src, result_ty, inst_tag, operand);
|
||||
}
|
||||
|
||||
fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Inst) InnerError!*Inst {
|
||||
fn analyzeIsNonErr(
|
||||
sema: *Sema,
|
||||
block: *Scope.Block,
|
||||
src: LazySrcLoc,
|
||||
operand: *Inst,
|
||||
) InnerError!*Inst {
|
||||
const ot = operand.ty.zigTypeTag();
|
||||
if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, false);
|
||||
if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, true);
|
||||
if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, true);
|
||||
if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, false);
|
||||
assert(ot == .ErrorUnion);
|
||||
const result_ty = Type.initTag(.bool);
|
||||
if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| {
|
||||
if (err_union.isUndef()) {
|
||||
return sema.mod.constUndef(sema.arena, src, result_ty);
|
||||
}
|
||||
return sema.mod.constBool(sema.arena, src, err_union.getError() != null);
|
||||
return sema.mod.constBool(sema.arena, src, err_union.getError() == null);
|
||||
}
|
||||
try sema.requireRuntimeBlock(block, src);
|
||||
return block.addUnOp(src, result_ty, .is_err, operand);
|
||||
return block.addUnOp(src, result_ty, .is_non_err, operand);
|
||||
}
|
||||
|
||||
fn analyzeSlice(
|
||||
|
||||
32
src/Zir.zig
32
src/Zir.zig
@@ -398,21 +398,15 @@ pub const Inst = struct {
|
||||
/// Return a boolean false if an optional is null. `x != null`
|
||||
/// Uses the `un_node` field.
|
||||
is_non_null,
|
||||
/// Return a boolean true if an optional is null. `x == null`
|
||||
/// Uses the `un_node` field.
|
||||
is_null,
|
||||
/// Return a boolean false if an optional is null. `x.* != null`
|
||||
/// Uses the `un_node` field.
|
||||
is_non_null_ptr,
|
||||
/// Return a boolean true if an optional is null. `x.* == null`
|
||||
/// Return a boolean false if value is an error
|
||||
/// Uses the `un_node` field.
|
||||
is_null_ptr,
|
||||
/// Return a boolean true if value is an error
|
||||
is_non_err,
|
||||
/// Return a boolean false if dereferenced pointer is an error
|
||||
/// Uses the `un_node` field.
|
||||
is_err,
|
||||
/// Return a boolean true if dereferenced pointer is an error
|
||||
/// Uses the `un_node` field.
|
||||
is_err_ptr,
|
||||
is_non_err_ptr,
|
||||
/// A labeled block of code that loops forever. At the end of the body will have either
|
||||
/// a `repeat` instruction or a `repeat_inline` instruction.
|
||||
/// Uses the `pl_node` field. The AST node is either a for loop or while loop.
|
||||
@@ -1046,11 +1040,9 @@ pub const Inst = struct {
|
||||
.float128,
|
||||
.int_type,
|
||||
.is_non_null,
|
||||
.is_null,
|
||||
.is_non_null_ptr,
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_err_ptr,
|
||||
.is_non_err,
|
||||
.is_non_err_ptr,
|
||||
.mod_rem,
|
||||
.mul,
|
||||
.mulwrap,
|
||||
@@ -1306,11 +1298,9 @@ pub const Inst = struct {
|
||||
.float128 = .pl_node,
|
||||
.int_type = .int_type,
|
||||
.is_non_null = .un_node,
|
||||
.is_null = .un_node,
|
||||
.is_non_null_ptr = .un_node,
|
||||
.is_null_ptr = .un_node,
|
||||
.is_err = .un_node,
|
||||
.is_err_ptr = .un_node,
|
||||
.is_non_err = .un_node,
|
||||
.is_non_err_ptr = .un_node,
|
||||
.loop = .pl_node,
|
||||
.repeat = .node,
|
||||
.repeat_inline = .node,
|
||||
@@ -2857,11 +2847,9 @@ const Writer = struct {
|
||||
.err_union_code,
|
||||
.err_union_code_ptr,
|
||||
.is_non_null,
|
||||
.is_null,
|
||||
.is_non_null_ptr,
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_err_ptr,
|
||||
.is_non_err,
|
||||
.is_non_err_ptr,
|
||||
.typeof,
|
||||
.typeof_elem,
|
||||
.struct_init_empty,
|
||||
|
||||
12
src/air.zig
12
src/air.zig
@@ -90,8 +90,12 @@ pub const Inst = struct {
|
||||
is_non_null_ptr,
|
||||
/// E!T => bool
|
||||
is_err,
|
||||
/// E!T => bool (inverted logic)
|
||||
is_non_err,
|
||||
/// *E!T => bool
|
||||
is_err_ptr,
|
||||
/// *E!T => bool (inverted logic)
|
||||
is_non_err_ptr,
|
||||
bool_and,
|
||||
bool_or,
|
||||
/// Read a value from a pointer.
|
||||
@@ -154,7 +158,9 @@ pub const Inst = struct {
|
||||
.is_null,
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_non_err,
|
||||
.is_err_ptr,
|
||||
.is_non_err_ptr,
|
||||
.ptrtoint,
|
||||
.floatcast,
|
||||
.intcast,
|
||||
@@ -759,7 +765,9 @@ const DumpAir = struct {
|
||||
.is_null,
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_non_err,
|
||||
.is_err_ptr,
|
||||
.is_non_err_ptr,
|
||||
.ptrtoint,
|
||||
.floatcast,
|
||||
.intcast,
|
||||
@@ -888,11 +896,13 @@ const DumpAir = struct {
|
||||
.bitcast,
|
||||
.not,
|
||||
.is_non_null,
|
||||
.is_null,
|
||||
.is_non_null_ptr,
|
||||
.is_null,
|
||||
.is_null_ptr,
|
||||
.is_err,
|
||||
.is_err_ptr,
|
||||
.is_non_err,
|
||||
.is_non_err_ptr,
|
||||
.ptrtoint,
|
||||
.floatcast,
|
||||
.intcast,
|
||||
|
||||
@@ -859,6 +859,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
.is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?),
|
||||
.is_null => return self.genIsNull(inst.castTag(.is_null).?),
|
||||
.is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?),
|
||||
.is_non_err => return self.genIsNonErr(inst.castTag(.is_non_err).?),
|
||||
.is_non_err_ptr => return self.genIsNonErrPtr(inst.castTag(.is_non_err_ptr).?),
|
||||
.is_err => return self.genIsErr(inst.castTag(.is_err).?),
|
||||
.is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?),
|
||||
.load => return self.genLoad(inst.castTag(.load).?),
|
||||
@@ -2972,6 +2974,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
|
||||
return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{});
|
||||
}
|
||||
|
||||
fn genIsNonErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
|
||||
switch (arch) {
|
||||
else => return self.fail(inst.base.src, "TODO implement is_non_err for {}", .{self.target.cpu.arch}),
|
||||
}
|
||||
}
|
||||
|
||||
fn genIsNonErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
|
||||
return self.fail(inst.base.src, "TODO load the operand and call genIsNonErr", .{});
|
||||
}
|
||||
|
||||
fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {
|
||||
// A loop is a setup to be able to jump back to the beginning.
|
||||
const start_index = self.code.items.len;
|
||||
|
||||
@@ -895,8 +895,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi
|
||||
.ref => try genRef(o, inst.castTag(.ref).?),
|
||||
.struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?),
|
||||
|
||||
.is_err => try genIsErr(o, inst.castTag(.is_err).?),
|
||||
.is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?),
|
||||
.is_err => try genIsErr(o, inst.castTag(.is_err).?, "", "!="),
|
||||
.is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", "=="),
|
||||
.is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "[0]", "!="),
|
||||
.is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "[0]", "=="),
|
||||
|
||||
.unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?),
|
||||
.unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?),
|
||||
@@ -1446,15 +1448,14 @@ fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue {
|
||||
return local;
|
||||
}
|
||||
|
||||
fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue {
|
||||
fn genIsErr(o: *Object, inst: *Inst.UnOp, deref_suffix: []const u8, op_str: []const u8) !CValue {
|
||||
const writer = o.writer();
|
||||
const maybe_deref = if (inst.base.tag == .is_err_ptr) "[0]" else "";
|
||||
const operand = try o.resolveInst(inst.operand);
|
||||
|
||||
const local = try o.allocLocal(Type.initTag(.bool), .Const);
|
||||
try writer.writeAll(" = (");
|
||||
try o.writeCValue(writer, operand);
|
||||
try writer.print("){s}.error != 0;\n", .{maybe_deref});
|
||||
try writer.print("){s}.error {s} 0;\n", .{ deref_suffix, op_str });
|
||||
return local;
|
||||
}
|
||||
|
||||
|
||||
@@ -814,7 +814,8 @@ pub const Context = struct {
|
||||
.constant => unreachable,
|
||||
.dbg_stmt => WValue.none,
|
||||
.div => self.genBinOp(inst.castTag(.div).?, .div),
|
||||
.is_err => self.genIsErr(inst.castTag(.is_err).?),
|
||||
.is_err => self.genIsErr(inst.castTag(.is_err).?, .i32_ne),
|
||||
.is_non_err => self.genIsErr(inst.castTag(.is_non_err).?, .i32_eq),
|
||||
.load => self.genLoad(inst.castTag(.load).?),
|
||||
.loop => self.genLoop(inst.castTag(.loop).?),
|
||||
.mul => self.genBinOp(inst.castTag(.mul).?, .mul),
|
||||
@@ -1278,7 +1279,7 @@ pub const Context = struct {
|
||||
return .none;
|
||||
}
|
||||
|
||||
fn genIsErr(self: *Context, inst: *Inst.UnOp) InnerError!WValue {
|
||||
fn genIsErr(self: *Context, inst: *Inst.UnOp, opcode: wasm.Opcode) InnerError!WValue {
|
||||
const operand = self.resolveInst(inst.operand);
|
||||
const offset = self.code.items.len;
|
||||
const writer = self.code.writer();
|
||||
@@ -1289,9 +1290,7 @@ pub const Context = struct {
|
||||
try writer.writeByte(wasm.opcode(.i32_const));
|
||||
try leb.writeILEB128(writer, @as(i32, 0));
|
||||
|
||||
// we want to break out of the condition if they're *not* equal,
|
||||
// because that means there's an error.
|
||||
try writer.writeByte(wasm.opcode(.i32_ne));
|
||||
try writer.writeByte(@enumToInt(opcode));
|
||||
|
||||
return WValue{ .code_offset = offset };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user