feature. See also
. The project being documented here (as the example) is the Zig library itself.
AstGen.orelseCatchExpr
fn orelseCatchExpr(
parent_gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
node: Ast.Node.Index,
cond_op: Zir.Inst.Tag,
unwrap_op: Zir.Inst.Tag,
unwrap_code_op: Zir.Inst.Tag,
payload_token: ?Ast.TokenIndex,
) InnerError!Zir.Inst.Ref
File
Code
fn orelseCatchExpr(
parent_gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
node: Ast.Node.Index,
cond_op: Zir.Inst.Tag,
unwrap_op: Zir.Inst.Tag,
unwrap_code_op: Zir.Inst.Tag,
payload_token: ?Ast.TokenIndex,
) InnerError!Zir.Inst.Ref {
const astgen = parent_gz.astgen;
const tree = astgen.tree;
const lhs, const rhs = tree.nodeData(node).node_and_node;
const need_rl = astgen.nodes_need_rl.contains(node);
const block_ri: ResultInfo = if (need_rl) ri else .{
.rl = switch (ri.rl) {
.ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
.inferred_ptr => .none,
else => ri.rl,
},
.ctx = ri.ctx,
};
// result pointer and aren't forwarding it.
const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
const do_err_trace = astgen.fn_block != null and (cond_op == .is_non_err or cond_op == .is_non_err_ptr);
var block_scope = parent_gz.makeSubBlock(scope);
block_scope.setBreakResultInfo(block_ri);
defer block_scope.unstack();
const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) {
.ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none },
.ref_const => .{ .rl = .ref_const, .ctx = if (do_err_trace) .error_handling_expr else .none },
else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none },
};
// We cannot use `block_scope.break_result_info` because that has the bare
// type, whereas this expression has the optional type. Later we make
// up for this fact by calling rvalue on the else branch.
const operand = try reachableExpr(&block_scope, &block_scope.base, operand_ri, lhs, rhs);
const cond = try block_scope.addUnNode(cond_op, operand, node);
const condbr = try block_scope.addCondBr(.condbr, node);
const block = try parent_gz.makeBlockInst(.block, node);
try block_scope.setBlockBody(block);
try parent_gz.instructions.append(astgen.gpa, block);
var then_scope = block_scope.makeSubBlock(scope);
defer then_scope.unstack();
const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);
const then_result = switch (ri.rl) {
.ref, .ref_const, .ref_coerced_ty => unwrapped_payload,
else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node),
};
_ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node);
var else_scope = block_scope.makeSubBlock(scope);
defer else_scope.unstack();
// so signal to Sema that it should save the new index for restoring later.
if (do_err_trace and nodeMayAppendToErrorTrace(tree, lhs))
_ = try else_scope.addSaveErrRetIndex(.always);
var err_val_scope: Scope.LocalVal = undefined;
const else_sub_scope = blk: {
const payload = payload_token orelse break :blk &else_scope.base;
const err_str = tree.tokenSlice(payload);
if (mem.eql(u8, err_str, "_")) {
try astgen.appendErrorTok(payload, "discard of error capture; omit it instead", .{});
break :blk &else_scope.base;
}
const err_name = try astgen.identAsString(payload);
try astgen.detectLocalShadowing(scope, err_name, payload, err_str, .capture);
err_val_scope = .{
.parent = &else_scope.base,
.gen_zir = &else_scope,
.name = err_name,
.inst = try else_scope.addUnNode(unwrap_code_op, operand, node),
.token_src = payload,
.id_cat = .capture,
};
break :blk &err_val_scope.base;
};
const else_result = else_result: {
if (tree.fullSwitch(rhs)) |switch_full| no_switch_on_err: {
if (tree.nodeTag(node) != .@"catch") break :no_switch_on_err;
const catch_token = tree.nodeMainToken(node);
const capture_token = if (tree.tokenTag(catch_token + 1) == .pipe) token: {
break :token catch_token + 2;
} else break :no_switch_on_err;
if (switch_full.label_token == null) break :no_switch_on_err;
if (tree.nodeTag(switch_full.ast.condition) != .identifier) break :no_switch_on_err;
if (!try astgen.tokenIdentEql(capture_token, tree.nodeMainToken(switch_full.ast.condition))) break :no_switch_on_err;
break :else_result try switchExpr(
&else_scope,
else_sub_scope,
block_scope.break_result_info,
rhs,
switch_full,
.{ .peer_break_target = .{
.block_inst = block,
.block_ri = block_ri,
} },
);
}
break :else_result try fullBodyExpr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs, .allow_branch_hint);
};
if (!else_scope.endsWithNoReturn()) {
if (do_err_trace)
try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, rhs, else_result);
_ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, rhs);
}
try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
try setCondBrPayload(condbr, cond, &then_scope, &else_scope);
if (need_result_rvalue) {
return rvalue(parent_gz, ri, block.toRef(), node);
} else {
return block.toRef();
}
}