@@ -838,7 +838,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
838838
839839 .if_simple,
840840 .@"if",
841- => return ifExpr(gz, scope, ri.br(), node, tree.fullIf(node).?),
841+ => {
842+ const if_full = tree.fullIf(node).?;
843+ if (if_full.error_token) |error_token| {
844+ const tag = node_tags[if_full.ast.else_expr];
845+ if ((tag == .@"switch" or tag == .switch_comma) and
846+ std.mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(error_token + 4)))
847+ {
848+ return switchExprErrUnion(gz, scope, ri.br(), node, .@"if");
849+ }
850+ }
851+ return ifExpr(gz, scope, ri.br(), node, if_full);
852+ },
842853
843854 .while_simple,
844855 .while_cont,
@@ -1019,7 +1030,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
10191030 token_tags[catch_token + 4] == .keyword_switch)
10201031 {
10211032 if (std.mem.eql(u8, tree.tokenSlice(catch_token + 2), tree.tokenSlice(catch_token + 6))) {
1022- return switchExprErrUnion(gz, scope, ri.br(), node);
1033+ return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch" );
10231034 }
10241035 }
10251036 break :blk catch_token + 2;
@@ -6860,7 +6871,8 @@ fn switchExprErrUnion(
68606871 parent_gz: *GenZir,
68616872 scope: *Scope,
68626873 ri: ResultInfo,
6863- catch_node: Ast.Node.Index,
6874+ catch_or_if_node: Ast.Node.Index,
6875+ node_ty: enum { @"catch", @"if" },
68646876) InnerError!Zir.Inst.Ref {
68656877 const astgen = parent_gz.astgen;
68666878 const gpa = astgen.gpa;
@@ -6869,21 +6881,42 @@ fn switchExprErrUnion(
68696881 const node_tags = tree.nodes.items(.tag);
68706882 const main_tokens = tree.nodes.items(.main_token);
68716883 const token_tags = tree.tokens.items(.tag);
6872- const operand_node = node_datas[catch_node].lhs;
6873- const switch_node = node_datas[catch_node].rhs;
6884+
6885+ const if_full = switch (node_ty) {
6886+ .@"catch" => undefined,
6887+ .@"if" => tree.fullIf(catch_or_if_node).?,
6888+ };
6889+
6890+ const switch_node, const operand_node, const error_payload = switch (node_ty) {
6891+ .@"catch" => .{
6892+ node_datas[catch_or_if_node].rhs,
6893+ node_datas[catch_or_if_node].lhs,
6894+ main_tokens[catch_or_if_node] + 2,
6895+ },
6896+ .@"if" => .{
6897+ if_full.ast.else_expr,
6898+ if_full.ast.cond_expr,
6899+ if_full.error_token.?,
6900+ },
6901+ };
6902+ assert(node_tags[switch_node] == .@"switch" or node_tags[switch_node] == .switch_comma);
6903+
68746904 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
68756905 const case_nodes = tree.extra_data[extra.start..extra.end];
68766906
6877- const need_rl = astgen.nodes_need_rl.contains(catch_node );
6907+ const need_rl = astgen.nodes_need_rl.contains(catch_or_if_node );
68786908 const block_ri: ResultInfo = if (need_rl) ri else .{
68796909 .rl = switch (ri.rl) {
6880- .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, catch_node )).? },
6910+ .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, catch_or_if_node )).? },
68816911 .inferred_ptr => .none,
68826912 else => ri.rl,
68836913 },
68846914 .ctx = ri.ctx,
68856915 };
68866916
6917+ const payload_is_ref = node_ty == .@"if" and
6918+ if_full.payload_token != null and token_tags[if_full.payload_token.?] == .asterisk;
6919+
68876920 // We need to call `rvalue` to write through to the pointer only if we had a
68886921 // result pointer and aren't forwarding it.
68896922 const LocTag = @typeInfo(ResultInfo.Loc).Union.tag_type.?;
@@ -6951,12 +6984,15 @@ fn switchExprErrUnion(
69516984 }
69526985 }
69536986
6954- const operand_ri: ResultInfo = .{ .rl = .none, .ctx = .error_handling_expr };
6987+ const operand_ri: ResultInfo = .{
6988+ .rl = if (payload_is_ref) .ref else .none,
6989+ .ctx = .error_handling_expr,
6990+ };
69556991
69566992 astgen.advanceSourceCursorToNode(operand_node);
69576993 const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
69586994
6959- const raw_operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, node_datas[catch_node].rhs );
6995+ const raw_operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, switch_node );
69606996 const item_ri: ResultInfo = .{ .rl = .none };
69616997
69626998 // This contains the data that goes into the `extra` array for the SwitchBlockErrUnion, except
@@ -6997,13 +7033,93 @@ fn switchExprErrUnion(
69977033
69987034 try case_scope.addDbgBlockBegin();
69997035
7000- const unwrapped_payload = try case_scope.addUnNode(.err_union_payload_unsafe, raw_operand, catch_node);
7001- const case_result = switch (ri.rl) {
7002- .ref, .ref_coerced_ty => unwrapped_payload,
7003- else => try rvalue(&case_scope, block_scope.break_result_info, unwrapped_payload, catch_node),
7004- };
7005- try case_scope.addDbgBlockEnd();
7006- _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, catch_node);
7036+ const unwrap_payload_tag: Zir.Inst.Tag = if (payload_is_ref)
7037+ .err_union_payload_unsafe_ptr
7038+ else
7039+ .err_union_payload_unsafe;
7040+
7041+ const unwrapped_payload = try case_scope.addUnNode(
7042+ unwrap_payload_tag,
7043+ raw_operand,
7044+ catch_or_if_node,
7045+ );
7046+
7047+ switch (node_ty) {
7048+ .@"catch" => {
7049+ const case_result = switch (ri.rl) {
7050+ .ref, .ref_coerced_ty => unwrapped_payload,
7051+ else => try rvalue(
7052+ &case_scope,
7053+ block_scope.break_result_info,
7054+ unwrapped_payload,
7055+ catch_or_if_node,
7056+ ),
7057+ };
7058+ try case_scope.addDbgBlockEnd();
7059+ _ = try case_scope.addBreakWithSrcNode(
7060+ .@"break",
7061+ switch_block,
7062+ case_result,
7063+ catch_or_if_node,
7064+ );
7065+ },
7066+ .@"if" => {
7067+ var payload_val_scope: Scope.LocalVal = undefined;
7068+
7069+ try case_scope.addDbgBlockBegin();
7070+ const then_node = if_full.ast.then_expr;
7071+ const then_sub_scope = s: {
7072+ assert(if_full.error_token != null);
7073+ if (if_full.payload_token) |payload_token| {
7074+ const token_name_index = payload_token + @intFromBool(payload_is_ref);
7075+ const ident_name = try astgen.identAsString(token_name_index);
7076+ const token_name_str = tree.tokenSlice(token_name_index);
7077+ if (mem.eql(u8, "_", token_name_str))
7078+ break :s &case_scope.base;
7079+ try astgen.detectLocalShadowing(
7080+ &case_scope.base,
7081+ ident_name,
7082+ token_name_index,
7083+ token_name_str,
7084+ .capture,
7085+ );
7086+ payload_val_scope = .{
7087+ .parent = &case_scope.base,
7088+ .gen_zir = &case_scope,
7089+ .name = ident_name,
7090+ .inst = unwrapped_payload,
7091+ .token_src = payload_token,
7092+ .id_cat = .capture,
7093+ };
7094+ try case_scope.addDbgVar(.dbg_var_val, ident_name, unwrapped_payload);
7095+ break :s &payload_val_scope.base;
7096+ } else {
7097+ _ = try case_scope.addUnNode(
7098+ .ensure_err_union_payload_void,
7099+ raw_operand,
7100+ catch_or_if_node,
7101+ );
7102+ break :s &case_scope.base;
7103+ }
7104+ };
7105+ const then_result = try expr(
7106+ &case_scope,
7107+ then_sub_scope,
7108+ block_scope.break_result_info,
7109+ then_node,
7110+ );
7111+ try checkUsed(parent_gz, &case_scope.base, then_sub_scope);
7112+ if (!case_scope.endsWithNoReturn()) {
7113+ try case_scope.addDbgBlockEnd();
7114+ _ = try case_scope.addBreakWithSrcNode(
7115+ .@"break",
7116+ switch_block,
7117+ then_result,
7118+ then_node,
7119+ );
7120+ }
7121+ },
7122+ }
70077123
70087124 const case_slice = case_scope.instructionsSlice();
70097125 // Since we use the switch_block_err_union instruction itself to refer
@@ -7020,9 +7136,18 @@ fn switchExprErrUnion(
70207136 };
70217137 const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
70227138 try payloads.ensureUnusedCapacity(gpa, body_len);
7139+ const capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = switch (node_ty) {
7140+ .@"catch" => .none,
7141+ .@"if" => if (if_full.payload_token == null)
7142+ .none
7143+ else if (payload_is_ref)
7144+ .by_ref
7145+ else
7146+ .by_val,
7147+ };
70237148 payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
70247149 .body_len = @intCast(body_len),
7025- .capture = .none ,
7150+ .capture = capture ,
70267151 .is_inline = false,
70277152 .has_tag_capture = false,
70287153 });
@@ -7032,16 +7157,15 @@ fn switchExprErrUnion(
70327157 appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
70337158 }
70347159
7035- const err_name, const error_payload = blk: {
7036- const error_payload = main_tokens[catch_node] + 2;
7160+ const err_name = blk: {
70377161 const err_str = tree.tokenSlice(error_payload);
70387162 if (mem.eql(u8, err_str, "_")) {
70397163 return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{});
70407164 }
70417165 const err_name = try astgen.identAsString(error_payload);
70427166 try astgen.detectLocalShadowing(scope, err_name, error_payload, err_str, .capture);
70437167
7044- break :blk .{ err_name, error_payload } ;
7168+ break :blk err_name;
70457169 };
70467170
70477171 // allocate a shared dummy instruction for the error capture
@@ -7234,6 +7358,7 @@ fn switchExprErrUnion(
72347358 .has_else = has_else,
72357359 .scalar_cases_len = @intCast(scalar_cases_len),
72367360 .any_uses_err_capture = any_uses_err_capture,
7361+ .payload_is_ref = payload_is_ref,
72377362 },
72387363 });
72397364
0 commit comments