ifExpr
Function parameters
Parameters
- parent_gz:*GenZir
- scope:*Scope
- node:Ast.Node.Index
- if_full:Ast.full.If
Functions in this namespace
Functions
Source
Implementation
fn ifExpr(
parent_gz: *GenZir,
scope: *Scope,
ri: ResultInfo,
node: Ast.Node.Index,
if_full: Ast.full.If,
) InnerError!Zir.Inst.Ref {
const astgen = parent_gz.astgen;
const tree = astgen.tree;
const do_err_trace = astgen.fn_block != null and if_full.error_token != null;
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,
};
// We need to call `rvalue` to write through to the pointer only if we had a
// 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);
var block_scope = parent_gz.makeSubBlock(scope);
block_scope.setBreakResultInfo(block_ri);
defer block_scope.unstack();
const payload_is_ref = if (if_full.payload_token) |payload_token|
tree.tokenTag(payload_token) == .asterisk
else
false;
try emitDbgNode(parent_gz, if_full.ast.cond_expr);
const cond: struct {
inst: Zir.Inst.Ref,
bool_bit: Zir.Inst.Ref,
} = c: {
if (if_full.error_token) |_| {
const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none, .ctx = .error_handling_expr };
const err_union = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr);
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, if_full.ast.cond_expr),
};
} else if (if_full.payload_token) |_| {
const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
const optional = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr);
const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
break :c .{
.inst = optional,
.bool_bit = try block_scope.addUnNode(tag, optional, if_full.ast.cond_expr),
};
} else {
const cond = try expr(&block_scope, &block_scope.base, coerced_bool_ri, if_full.ast.cond_expr);
break :c .{
.inst = cond,
.bool_bit = cond,
};
}
};
const condbr = try block_scope.addCondBr(.condbr, node);
const block = try parent_gz.makeBlockInst(.block, node);
try block_scope.setBlockBody(block);
// block_scope unstacked now, can add new instructions to parent_gz
try parent_gz.instructions.append(astgen.gpa, block);
var then_scope = parent_gz.makeSubBlock(scope);
defer then_scope.unstack();
var payload_val_scope: Scope.LocalVal = undefined;
const then_node = if_full.ast.then_expr;
const then_sub_scope = s: {
if (if_full.error_token != null) {
if (if_full.payload_token) |payload_token| {
const tag: Zir.Inst.Tag = if (payload_is_ref)
.err_union_payload_unsafe_ptr
else
.err_union_payload_unsafe;
const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node);
const token_name_index = payload_token + @intFromBool(payload_is_ref);
const ident_name = try astgen.identAsString(token_name_index);
const token_name_str = tree.tokenSlice(token_name_index);
if (mem.eql(u8, "_", token_name_str)) {
if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
break :s &then_scope.base;
}
try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str, .capture);
payload_val_scope = .{
.parent = &then_scope.base,
.gen_zir = &then_scope,
.name = ident_name,
.inst = payload_inst,
.token_src = token_name_index,
.id_cat = .capture,
};
try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
break :s &payload_val_scope.base;
} else {
_ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
break :s &then_scope.base;
}
} else if (if_full.payload_token) |payload_token| {
const ident_token = payload_token + @intFromBool(payload_is_ref);
const tag: Zir.Inst.Tag = if (payload_is_ref)
.optional_payload_unsafe_ptr
else
.optional_payload_unsafe;
const ident_bytes = tree.tokenSlice(ident_token);
if (mem.eql(u8, "_", ident_bytes)) {
if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
break :s &then_scope.base;
}
const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node);
const ident_name = try astgen.identAsString(ident_token);
try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
payload_val_scope = .{
.parent = &then_scope.base,
.gen_zir = &then_scope,
.name = ident_name,
.inst = payload_inst,
.token_src = ident_token,
.id_cat = .capture,
};
try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
break :s &payload_val_scope.base;
} else {
break :s &then_scope.base;
}
};
const then_result = try fullBodyExpr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node, .allow_branch_hint);
try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
if (!then_scope.endsWithNoReturn()) {
_ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, then_node);
}
var else_scope = parent_gz.makeSubBlock(scope);
defer else_scope.unstack();
// We know that the operand (almost certainly) modified the error return trace,
// so signal to Sema that it should save the new index for restoring later.
if (do_err_trace and nodeMayAppendToErrorTrace(tree, if_full.ast.cond_expr))
_ = try else_scope.addSaveErrRetIndex(.always);
if (if_full.ast.else_expr.unwrap()) |else_node| {
const sub_scope = s: {
if (if_full.error_token) |error_token| {
const tag: Zir.Inst.Tag = if (payload_is_ref)
.err_union_code_ptr
else
.err_union_code;
const payload_inst = try else_scope.addUnNode(tag, cond.inst, if_full.ast.cond_expr);
const ident_name = try astgen.identAsString(error_token);
const error_token_str = tree.tokenSlice(error_token);
if (mem.eql(u8, "_", error_token_str))
break :s &else_scope.base;
try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str, .capture);
payload_val_scope = .{
.parent = &else_scope.base,
.gen_zir = &else_scope,
.name = ident_name,
.inst = payload_inst,
.token_src = error_token,
.id_cat = .capture,
};
try else_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
break :s &payload_val_scope.base;
} else {
break :s &else_scope.base;
}
};
const else_result = try fullBodyExpr(&else_scope, sub_scope, block_scope.break_result_info, else_node, .allow_branch_hint);
if (!else_scope.endsWithNoReturn()) {
// As our last action before the break, "pop" the error trace if needed
if (do_err_trace)
try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, else_node, else_result);
_ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, else_node);
}
try checkUsed(parent_gz, &else_scope.base, sub_scope);
} else {
const result = try rvalue(&else_scope, ri, .void_value, node);
_ = try else_scope.addBreak(.@"break", block, result);
}
try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope);
if (need_result_rvalue) {
return rvalue(parent_gz, ri, block.toRef(), node);
} else {
return block.toRef();
}
}