authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-08-27 18:48:01+03:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-09-12 01:52:44-04:00
loge323cf1264f390911dcc2efea71d46be1d631d92
treeffe4c716e299e94ef1ed797f32aed8451e8090c9
parentc97d64b677eb891144fb356e1f4b9011c60cc0e2

stage2: change how defers are stored in Zir

Storing defers this way has the benefits that the defer doesn't get analyzed multiple times in AstGen, it takes up less space, and it makes Sema aware of defers allowing for 'unreachable else prong' error on error sets in generic code. The disadvantage is that it is a bit more complex and errdefers with payloads now emit a placeholder instruction (but those are rare). Sema.zig before: Total ZIR bytes: 3.7794370651245117MiB Instructions: 238996 (2.051319122314453MiB) String Table Bytes: 89.2802734375KiB Extra Data Items: 430144 (1.640869140625MiB) Sema.zig after: Total ZIR bytes: 3.3344192504882812MiB Instructions: 211829 (1.8181428909301758MiB) String Table Bytes: 89.2802734375KiB Extra Data Items: 374611 (1.4290275573730469MiB)

9 files changed, 261 insertions(+), 148 deletions(-)

doc/langref.html.in-5
...@@ -5700,7 +5700,6 @@ fn bar() !void {...@@ -5700,7 +5700,6 @@ fn bar() !void {
5700 try quux();5700 try quux();
5701 } else |err| switch (err) {5701 } else |err| switch (err) {
5702 error.FileNotFound => try hello(),5702 error.FileNotFound => try hello(),
5703 else => try another(),
5704 }5703 }
5705}5704}
57065705
...@@ -5716,10 +5715,6 @@ fn hello() !void {...@@ -5716,10 +5715,6 @@ fn hello() !void {
5716 try bang2();5715 try bang2();
5717}5716}
57185717
5719fn another() !void {
5720 try bang1();
5721}
5722
5723fn bang1() !void {5718fn bang1() !void {
5724 return error.FileNotFound;5719 return error.FileNotFound;
5725}5720}
src/AstGen.zig+134-138
...@@ -135,7 +135,6 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {...@@ -135,7 +135,6 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
135 var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};135 var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
136 var gen_scope: GenZir = .{136 var gen_scope: GenZir = .{
137 .force_comptime = true,137 .force_comptime = true,
138 .in_defer = false,
139 .parent = &top_scope.base,138 .parent = &top_scope.base,
140 .anon_name_strategy = .parent,139 .anon_name_strategy = .parent,
141 .decl_node_index = 0,140 .decl_node_index = 0,
...@@ -1856,6 +1855,16 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -1856,6 +1855,16 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
1856 .gen_zir => {1855 .gen_zir => {
1857 const block_gz = scope.cast(GenZir).?;1856 const block_gz = scope.cast(GenZir).?;
18581857
1858 if (block_gz.cur_defer_node != 0) {
1859 return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
1860 try astgen.errNoteNode(
1861 block_gz.cur_defer_node,
1862 "defer expression here",
1863 .{},
1864 ),
1865 });
1866 }
1867
1859 const block_inst = blk: {1868 const block_inst = blk: {
1860 if (break_label != 0) {1869 if (break_label != 0) {
1861 if (block_gz.label) |*label| {1870 if (block_gz.label) |*label| {
...@@ -1919,17 +1928,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn...@@ -1919,17 +1928,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
1919 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,1928 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
1920 .namespace => break,1929 .namespace => break,
1921 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,1930 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
1922 .defer_gen => {
1923 const defer_gen = scope.cast(Scope.DeferGen).?;
1924
1925 return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
1926 try astgen.errNoteNode(
1927 defer_gen.defer_node,
1928 "defer expression here",
1929 .{},
1930 ),
1931 });
1932 },
1933 .top => unreachable,1931 .top => unreachable,
1934 }1932 }
1935 }1933 }
...@@ -1953,6 +1951,16 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)...@@ -1953,6 +1951,16 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
1953 switch (scope.tag) {1951 switch (scope.tag) {
1954 .gen_zir => {1952 .gen_zir => {
1955 const gen_zir = scope.cast(GenZir).?;1953 const gen_zir = scope.cast(GenZir).?;
1954
1955 if (gen_zir.cur_defer_node != 0) {
1956 return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{
1957 try astgen.errNoteNode(
1958 gen_zir.cur_defer_node,
1959 "defer expression here",
1960 .{},
1961 ),
1962 });
1963 }
1956 const continue_block = gen_zir.continue_block;1964 const continue_block = gen_zir.continue_block;
1957 if (continue_block == 0) {1965 if (continue_block == 0) {
1958 scope = gen_zir.parent;1966 scope = gen_zir.parent;
...@@ -1985,21 +1993,9 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)...@@ -1985,21 +1993,9 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
1985 .defer_normal => {1993 .defer_normal => {
1986 const defer_scope = scope.cast(Scope.Defer).?;1994 const defer_scope = scope.cast(Scope.Defer).?;
1987 scope = defer_scope.parent;1995 scope = defer_scope.parent;
1988 const expr_node = node_datas[defer_scope.defer_node].rhs;1996 try parent_gz.addDefer(defer_scope.index, defer_scope.len);
1989 try unusedResultDeferExpr(parent_gz, defer_scope, defer_scope.parent, expr_node);
1990 },1997 },
1991 .defer_error => scope = scope.cast(Scope.Defer).?.parent,1998 .defer_error => scope = scope.cast(Scope.Defer).?.parent,
1992 .defer_gen => {
1993 const defer_gen = scope.cast(Scope.DeferGen).?;
1994
1995 return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{
1996 try astgen.errNoteNode(
1997 defer_gen.defer_node,
1998 "defer expression here",
1999 .{},
2000 ),
2001 });
2002 },
2003 .namespace => break,1999 .namespace => break,
2004 .top => unreachable,2000 .top => unreachable,
2005 }2001 }
...@@ -2064,7 +2060,6 @@ fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.Toke...@@ -2064,7 +2060,6 @@ fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.Toke
2064 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,2060 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2065 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,2061 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2066 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,2062 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2067 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
2068 .namespace => break,2063 .namespace => break,
2069 .top => unreachable,2064 .top => unreachable,
2070 }2065 }
...@@ -2208,8 +2203,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod...@@ -2208,8 +2203,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
2208 .simple_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.simpleVarDecl(statement)),2203 .simple_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.simpleVarDecl(statement)),
2209 .aligned_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.alignedVarDecl(statement)),2204 .aligned_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.alignedVarDecl(statement)),
22102205
2211 .@"defer" => scope = try makeDeferScope(gz.astgen, scope, statement, block_arena_allocator, .defer_normal),2206 .@"defer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal),
2212 .@"errdefer" => scope = try makeDeferScope(gz.astgen, scope, statement, block_arena_allocator, .defer_error),2207 .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error),
22132208
2214 .assign => try assign(gz, scope, statement),2209 .assign => try assign(gz, scope, statement),
22152210
...@@ -2253,28 +2248,6 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod...@@ -2253,28 +2248,6 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
2253 try checkUsed(gz, parent_scope, scope);2248 try checkUsed(gz, parent_scope, scope);
2254}2249}
22552250
2256fn unusedResultDeferExpr(gz: *GenZir, defer_scope: *Scope.Defer, expr_scope: *Scope, expr_node: Ast.Node.Index) InnerError!void {
2257 const astgen = gz.astgen;
2258 const prev_offset = astgen.source_offset;
2259 const prev_line = astgen.source_line;
2260 const prev_column = astgen.source_column;
2261 defer {
2262 astgen.source_offset = prev_offset;
2263 astgen.source_line = prev_line;
2264 astgen.source_column = prev_column;
2265 }
2266 astgen.source_offset = defer_scope.source_offset;
2267 astgen.source_line = defer_scope.source_line;
2268 astgen.source_column = defer_scope.source_column;
2269
2270 var defer_gen: Scope.DeferGen = .{
2271 .parent = expr_scope,
2272 .defer_node = defer_scope.defer_node,
2273 };
2274
2275 _ = try unusedResultExpr(gz, &defer_gen.base, expr_node);
2276}
2277
2278/// Returns AST source node of the thing that is noreturn if the statement is2251/// Returns AST source node of the thing that is noreturn if the statement is
2279/// definitely `noreturn`. Otherwise returns 0.2252/// definitely `noreturn`. Otherwise returns 0.
2280fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.Index {2253fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
...@@ -2584,6 +2557,9 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2584,6 +2557,9 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2584 .validate_struct_init_ty,2557 .validate_struct_init_ty,
2585 .validate_deref,2558 .validate_deref,
2586 => break :b true,2559 => break :b true,
2560
2561 .@"defer" => unreachable,
2562 .defer_err_code => unreachable,
2587 }2563 }
2588 } else switch (maybe_unused_result) {2564 } else switch (maybe_unused_result) {
2589 .none => unreachable,2565 .none => unreachable,
...@@ -2603,15 +2579,12 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2603,15 +2579,12 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2603 return noreturn_src_node;2579 return noreturn_src_node;
2604}2580}
26052581
2606fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct {2582fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct {
2607 have_any: bool,2583 have_any: bool,
2608 have_normal: bool,2584 have_normal: bool,
2609 have_err: bool,2585 have_err: bool,
2610 need_err_code: bool,2586 need_err_code: bool,
2611} {2587} {
2612 const tree = astgen.tree;
2613 const node_datas = tree.nodes.items(.data);
2614
2615 var have_normal = false;2588 var have_normal = false;
2616 var have_err = false;2589 var have_err = false;
2617 var need_err_code = false;2590 var need_err_code = false;
...@@ -2621,7 +2594,6 @@ fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct...@@ -2621,7 +2594,6 @@ fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct
2621 .gen_zir => scope = scope.cast(GenZir).?.parent,2594 .gen_zir => scope = scope.cast(GenZir).?.parent,
2622 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,2595 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2623 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,2596 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2624 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
2625 .defer_normal => {2597 .defer_normal => {
2626 const defer_scope = scope.cast(Scope.Defer).?;2598 const defer_scope = scope.cast(Scope.Defer).?;
2627 scope = defer_scope.parent;2599 scope = defer_scope.parent;
...@@ -2634,7 +2606,7 @@ fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct...@@ -2634,7 +2606,7 @@ fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct
26342606
2635 have_err = true;2607 have_err = true;
26362608
2637 const have_err_payload = node_datas[defer_scope.defer_node].lhs != 0;2609 const have_err_payload = defer_scope.remapped_err_code != 0;
2638 need_err_code = need_err_code or have_err_payload;2610 need_err_code = need_err_code or have_err_payload;
2639 },2611 },
2640 .namespace => unreachable,2612 .namespace => unreachable,
...@@ -2661,9 +2633,7 @@ fn genDefers(...@@ -2661,9 +2633,7 @@ fn genDefers(
2661 inner_scope: *Scope,2633 inner_scope: *Scope,
2662 which_ones: DefersToEmit,2634 which_ones: DefersToEmit,
2663) InnerError!void {2635) InnerError!void {
2664 const astgen = gz.astgen;2636 const gpa = gz.astgen.gpa;
2665 const tree = astgen.tree;
2666 const node_datas = tree.nodes.items(.data);
26672637
2668 var scope = inner_scope;2638 var scope = inner_scope;
2669 while (scope != outer_scope) {2639 while (scope != outer_scope) {
...@@ -2671,51 +2641,40 @@ fn genDefers(...@@ -2671,51 +2641,40 @@ fn genDefers(
2671 .gen_zir => scope = scope.cast(GenZir).?.parent,2641 .gen_zir => scope = scope.cast(GenZir).?.parent,
2672 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,2642 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
2673 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,2643 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2674 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
2675 .defer_normal => {2644 .defer_normal => {
2676 const defer_scope = scope.cast(Scope.Defer).?;2645 const defer_scope = scope.cast(Scope.Defer).?;
2677 scope = defer_scope.parent;2646 scope = defer_scope.parent;
2678 const expr_node = node_datas[defer_scope.defer_node].rhs;2647 try gz.addDefer(defer_scope.index, defer_scope.len);
2679 const prev_in_defer = gz.in_defer;
2680 gz.in_defer = true;
2681 defer gz.in_defer = prev_in_defer;
2682 try unusedResultDeferExpr(gz, defer_scope, defer_scope.parent, expr_node);
2683 },2648 },
2684 .defer_error => {2649 .defer_error => {
2685 const defer_scope = scope.cast(Scope.Defer).?;2650 const defer_scope = scope.cast(Scope.Defer).?;
2686 scope = defer_scope.parent;2651 scope = defer_scope.parent;
2687 switch (which_ones) {2652 switch (which_ones) {
2688 .both_sans_err => {2653 .both_sans_err => {
2689 const expr_node = node_datas[defer_scope.defer_node].rhs;2654 try gz.addDefer(defer_scope.index, defer_scope.len);
2690 const prev_in_defer = gz.in_defer;
2691 gz.in_defer = true;
2692 defer gz.in_defer = prev_in_defer;
2693 try unusedResultDeferExpr(gz, defer_scope, defer_scope.parent, expr_node);
2694 },2655 },
2695 .both => |err_code| {2656 .both => |err_code| {
2696 const expr_node = node_datas[defer_scope.defer_node].rhs;2657 if (defer_scope.remapped_err_code == 0) {
2697 const payload_token = node_datas[defer_scope.defer_node].lhs;2658 try gz.addDefer(defer_scope.index, defer_scope.len);
2698 const prev_in_defer = gz.in_defer;2659 } else {
2699 gz.in_defer = true;2660 try gz.instructions.ensureUnusedCapacity(gpa, 1);
2700 defer gz.in_defer = prev_in_defer;2661 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
2701 var local_val_scope: Scope.LocalVal = undefined;2662
2702 try gz.addDbgBlockBegin();2663 const payload_index = try gz.astgen.addExtra(Zir.Inst.DeferErrCode{
2703 const sub_scope = if (payload_token == 0) defer_scope.parent else blk: {2664 .remapped_err_code = defer_scope.remapped_err_code,
2704 const ident_name = try astgen.identAsString(payload_token);2665 .index = defer_scope.index,
2705 local_val_scope = .{2666 .len = defer_scope.len,
2706 .parent = defer_scope.parent,2667 });
2707 .gen_zir = gz,2668 const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
2708 .name = ident_name,2669 gz.astgen.instructions.appendAssumeCapacity(.{
2709 .inst = err_code,2670 .tag = .defer_err_code,
2710 .token_src = payload_token,2671 .data = .{ .defer_err_code = .{
2711 .id_cat = .@"capture",2672 .err_code = err_code,
2712 };2673 .payload_index = payload_index,
2713 try gz.addDbgVar(.dbg_var_val, ident_name, err_code);2674 } },
2714 break :blk &local_val_scope.base;2675 });
2715 };2676 gz.instructions.appendAssumeCapacity(new_index);
2716 try unusedResultDeferExpr(gz, defer_scope, sub_scope, expr_node);2677 }
2717 try checkUsed(gz, scope, sub_scope);
2718 try gz.addDbgBlockEnd();
2719 },2678 },
2720 .normal_only => continue,2679 .normal_only => continue,
2721 }2680 }
...@@ -2752,35 +2711,68 @@ fn checkUsed(...@@ -2752,35 +2711,68 @@ fn checkUsed(
2752 scope = s.parent;2711 scope = s.parent;
2753 },2712 },
2754 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,2713 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2755 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
2756 .namespace => unreachable,2714 .namespace => unreachable,
2757 .top => unreachable,2715 .top => unreachable,
2758 }2716 }
2759 }2717 }
2760}2718}
27612719
2762fn makeDeferScope(2720fn deferStmt(
2763 astgen: *AstGen,2721 gz: *GenZir,
2764 scope: *Scope,2722 scope: *Scope,
2765 node: Ast.Node.Index,2723 node: Ast.Node.Index,
2766 block_arena: Allocator,2724 block_arena: Allocator,
2767 scope_tag: Scope.Tag,2725 scope_tag: Scope.Tag,
2768) InnerError!*Scope {2726) InnerError!*Scope {
2769 const tree = astgen.tree;2727 var defer_gen = gz.makeSubBlock(scope);
2728 defer_gen.cur_defer_node = node;
2729 defer_gen.any_defer_node = node;
2730 defer defer_gen.unstack();
2731
2732 const tree = gz.astgen.tree;
2770 const node_datas = tree.nodes.items(.data);2733 const node_datas = tree.nodes.items(.data);
2771 const expr_node = node_datas[node].rhs;2734 const expr_node = node_datas[node].rhs;
2772 const token_starts = tree.tokens.items(.start);2735
2773 const node_start = token_starts[tree.firstToken(expr_node)];2736 const payload_token = node_datas[node].lhs;
2737 var local_val_scope: Scope.LocalVal = undefined;
2738 var remapped_err_code: Zir.Inst.Index = 0;
2739 const have_err_code = scope_tag == .defer_error and payload_token != 0;
2740 const sub_scope = if (!have_err_code) &defer_gen.base else blk: {
2741 try gz.addDbgBlockBegin();
2742 const ident_name = try gz.astgen.identAsString(payload_token);
2743 remapped_err_code = @intCast(u32, try gz.astgen.instructions.addOne(gz.astgen.gpa));
2744 const remapped_err_code_ref = Zir.indexToRef(remapped_err_code);
2745 local_val_scope = .{
2746 .parent = &defer_gen.base,
2747 .gen_zir = gz,
2748 .name = ident_name,
2749 .inst = remapped_err_code_ref,
2750 .token_src = payload_token,
2751 .id_cat = .@"capture",
2752 };
2753 try gz.addDbgVar(.dbg_var_val, ident_name, remapped_err_code_ref);
2754 break :blk &local_val_scope.base;
2755 };
2756 _ = try unusedResultExpr(&defer_gen, sub_scope, expr_node);
2757 try checkUsed(gz, scope, sub_scope);
2758 if (have_err_code) try gz.addDbgBlockEnd();
2759 _ = try defer_gen.addBreak(.break_inline, 0, .void_value);
2760
2761 const body = defer_gen.instructionsSlice();
2762 const body_len = gz.astgen.countBodyLenAfterFixups(body);
2763
2764 const index = @intCast(u32, gz.astgen.extra.items.len);
2765 try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len);
2766 gz.astgen.appendBodyWithFixups(body);
2767
2774 const defer_scope = try block_arena.create(Scope.Defer);2768 const defer_scope = try block_arena.create(Scope.Defer);
2775 astgen.advanceSourceCursor(node_start);
27762769
2777 defer_scope.* = .{2770 defer_scope.* = .{
2778 .base = .{ .tag = scope_tag },2771 .base = .{ .tag = scope_tag },
2779 .parent = scope,2772 .parent = scope,
2780 .defer_node = node,2773 .index = index,
2781 .source_offset = astgen.source_offset,2774 .len = body_len,
2782 .source_line = astgen.source_line,2775 .remapped_err_code = remapped_err_code,
2783 .source_column = astgen.source_column,
2784 };2776 };
2785 return &defer_scope.base;2777 return &defer_scope.base;
2786}2778}
...@@ -3461,7 +3453,6 @@ fn fnDecl(...@@ -3461,7 +3453,6 @@ fn fnDecl(
34613453
3462 var decl_gz: GenZir = .{3454 var decl_gz: GenZir = .{
3463 .force_comptime = true,3455 .force_comptime = true,
3464 .in_defer = false,
3465 .decl_node_index = fn_proto.ast.proto_node,3456 .decl_node_index = fn_proto.ast.proto_node,
3466 .decl_line = astgen.source_line,3457 .decl_line = astgen.source_line,
3467 .parent = scope,3458 .parent = scope,
...@@ -3473,7 +3464,6 @@ fn fnDecl(...@@ -3473,7 +3464,6 @@ fn fnDecl(
34733464
3474 var fn_gz: GenZir = .{3465 var fn_gz: GenZir = .{
3475 .force_comptime = false,3466 .force_comptime = false,
3476 .in_defer = false,
3477 .decl_node_index = fn_proto.ast.proto_node,3467 .decl_node_index = fn_proto.ast.proto_node,
3478 .decl_line = decl_gz.decl_line,3468 .decl_line = decl_gz.decl_line,
3479 .parent = &decl_gz.base,3469 .parent = &decl_gz.base,
...@@ -3812,7 +3802,6 @@ fn globalVarDecl(...@@ -3812,7 +3802,6 @@ fn globalVarDecl(
3812 .decl_line = astgen.source_line,3802 .decl_line = astgen.source_line,
3813 .astgen = astgen,3803 .astgen = astgen,
3814 .force_comptime = true,3804 .force_comptime = true,
3815 .in_defer = false,
3816 .anon_name_strategy = .parent,3805 .anon_name_strategy = .parent,
3817 .instructions = gz.instructions,3806 .instructions = gz.instructions,
3818 .instructions_top = gz.instructions.items.len,3807 .instructions_top = gz.instructions.items.len,
...@@ -3964,7 +3953,6 @@ fn comptimeDecl(...@@ -3964,7 +3953,6 @@ fn comptimeDecl(
39643953
3965 var decl_block: GenZir = .{3954 var decl_block: GenZir = .{
3966 .force_comptime = true,3955 .force_comptime = true,
3967 .in_defer = false,
3968 .decl_node_index = node,3956 .decl_node_index = node,
3969 .decl_line = astgen.source_line,3957 .decl_line = astgen.source_line,
3970 .parent = scope,3958 .parent = scope,
...@@ -4019,7 +4007,6 @@ fn usingnamespaceDecl(...@@ -4019,7 +4007,6 @@ fn usingnamespaceDecl(
40194007
4020 var decl_block: GenZir = .{4008 var decl_block: GenZir = .{
4021 .force_comptime = true,4009 .force_comptime = true,
4022 .in_defer = false,
4023 .decl_node_index = node,4010 .decl_node_index = node,
4024 .decl_line = astgen.source_line,4011 .decl_line = astgen.source_line,
4025 .parent = scope,4012 .parent = scope,
...@@ -4067,7 +4054,6 @@ fn testDecl(...@@ -4067,7 +4054,6 @@ fn testDecl(
40674054
4068 var decl_block: GenZir = .{4055 var decl_block: GenZir = .{
4069 .force_comptime = true,4056 .force_comptime = true,
4070 .in_defer = false,
4071 .decl_node_index = node,4057 .decl_node_index = node,
4072 .decl_line = astgen.source_line,4058 .decl_line = astgen.source_line,
4073 .parent = scope,4059 .parent = scope,
...@@ -4132,7 +4118,6 @@ fn testDecl(...@@ -4132,7 +4118,6 @@ fn testDecl(
4132 .local_val, .local_ptr => unreachable, // a test cannot be in a local scope4118 .local_val, .local_ptr => unreachable, // a test cannot be in a local scope
4133 .gen_zir => s = s.cast(GenZir).?.parent,4119 .gen_zir => s = s.cast(GenZir).?.parent,
4134 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,4120 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
4135 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
4136 .namespace => {4121 .namespace => {
4137 const ns = s.cast(Scope.Namespace).?;4122 const ns = s.cast(Scope.Namespace).?;
4138 if (ns.decls.get(name_str_index)) |i| {4123 if (ns.decls.get(name_str_index)) |i| {
...@@ -4164,7 +4149,6 @@ fn testDecl(...@@ -4164,7 +4149,6 @@ fn testDecl(
41644149
4165 var fn_block: GenZir = .{4150 var fn_block: GenZir = .{
4166 .force_comptime = false,4151 .force_comptime = false,
4167 .in_defer = false,
4168 .decl_node_index = node,4152 .decl_node_index = node,
4169 .decl_line = decl_block.decl_line,4153 .decl_line = decl_block.decl_line,
4170 .parent = &decl_block.base,4154 .parent = &decl_block.base,
...@@ -4288,7 +4272,6 @@ fn structDeclInner(...@@ -4288,7 +4272,6 @@ fn structDeclInner(
4288 .decl_line = decl_line,4272 .decl_line = decl_line,
4289 .astgen = astgen,4273 .astgen = astgen,
4290 .force_comptime = true,4274 .force_comptime = true,
4291 .in_defer = false,
4292 .instructions = gz.instructions,4275 .instructions = gz.instructions,
4293 .instructions_top = gz.instructions.items.len,4276 .instructions_top = gz.instructions.items.len,
4294 };4277 };
...@@ -4489,7 +4472,6 @@ fn unionDeclInner(...@@ -4489,7 +4472,6 @@ fn unionDeclInner(
4489 .decl_line = astgen.source_line,4472 .decl_line = astgen.source_line,
4490 .astgen = astgen,4473 .astgen = astgen,
4491 .force_comptime = true,4474 .force_comptime = true,
4492 .in_defer = false,
4493 .instructions = gz.instructions,4475 .instructions = gz.instructions,
4494 .instructions_top = gz.instructions.items.len,4476 .instructions_top = gz.instructions.items.len,
4495 };4477 };
...@@ -4773,7 +4755,6 @@ fn containerDecl(...@@ -4773,7 +4755,6 @@ fn containerDecl(
4773 .decl_line = astgen.source_line,4755 .decl_line = astgen.source_line,
4774 .astgen = astgen,4756 .astgen = astgen,
4775 .force_comptime = true,4757 .force_comptime = true,
4776 .in_defer = false,
4777 .instructions = gz.instructions,4758 .instructions = gz.instructions,
4778 .instructions_top = gz.instructions.items.len,4759 .instructions_top = gz.instructions.items.len,
4779 };4760 };
...@@ -4879,7 +4860,6 @@ fn containerDecl(...@@ -4879,7 +4860,6 @@ fn containerDecl(
4879 .decl_line = astgen.source_line,4860 .decl_line = astgen.source_line,
4880 .astgen = astgen,4861 .astgen = astgen,
4881 .force_comptime = true,4862 .force_comptime = true,
4882 .in_defer = false,
4883 .instructions = gz.instructions,4863 .instructions = gz.instructions,
4884 .instructions_top = gz.instructions.items.len,4864 .instructions_top = gz.instructions.items.len,
4885 };4865 };
...@@ -5110,7 +5090,15 @@ fn tryExpr(...@@ -5110,7 +5090,15 @@ fn tryExpr(
5110 return astgen.failNode(node, "'try' outside function scope", .{});5090 return astgen.failNode(node, "'try' outside function scope", .{});
5111 };5091 };
51125092
5113 if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{});5093 if (parent_gz.any_defer_node != 0) {
5094 return astgen.failNodeNotes(node, "'try' not allowed inside defer expression", .{}, &.{
5095 try astgen.errNoteNode(
5096 parent_gz.any_defer_node,
5097 "defer expression here",
5098 .{},
5099 ),
5100 });
5101 }
51145102
5115 // Ensure debug line/column information is emitted for this try expression.5103 // Ensure debug line/column information is emitted for this try expression.
5116 // Then we will save the line/column so that we can emit another one that goes5104 // Then we will save the line/column so that we can emit another one that goes
...@@ -6656,7 +6644,15 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref...@@ -6656,7 +6644,15 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
6656 return astgen.failNode(node, "'return' outside function scope", .{});6644 return astgen.failNode(node, "'return' outside function scope", .{});
6657 }6645 }
66586646
6659 if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{});6647 if (gz.any_defer_node != 0) {
6648 return astgen.failNodeNotes(node, "cannot return from defer expression", .{}, &.{
6649 try astgen.errNoteNode(
6650 gz.any_defer_node,
6651 "defer expression here",
6652 .{},
6653 ),
6654 });
6655 }
66606656
6661 // Ensure debug line/column information is emitted for this return expression.6657 // Ensure debug line/column information is emitted for this return expression.
6662 // Then we will save the line/column so that we can emit another one that goes6658 // Then we will save the line/column so that we can emit another one that goes
...@@ -6683,7 +6679,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref...@@ -6683,7 +6679,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
6683 // for detecting whether to add something to the function's inferred error set.6679 // for detecting whether to add something to the function's inferred error set.
6684 const ident_token = node_datas[operand_node].rhs;6680 const ident_token = node_datas[operand_node].rhs;
6685 const err_name_str_index = try astgen.identAsString(ident_token);6681 const err_name_str_index = try astgen.identAsString(ident_token);
6686 const defer_counts = countDefers(astgen, defer_outer, scope);6682 const defer_counts = countDefers(defer_outer, scope);
6687 if (!defer_counts.need_err_code) {6683 if (!defer_counts.need_err_code) {
6688 try genDefers(gz, defer_outer, scope, .both_sans_err);6684 try genDefers(gz, defer_outer, scope, .both_sans_err);
6689 try emitDbgStmt(gz, ret_line, ret_column);6685 try emitDbgStmt(gz, ret_line, ret_column);
...@@ -6724,7 +6720,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref...@@ -6724,7 +6720,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref
6724 return Zir.Inst.Ref.unreachable_value;6720 return Zir.Inst.Ref.unreachable_value;
6725 },6721 },
6726 .maybe => {6722 .maybe => {
6727 const defer_counts = countDefers(astgen, defer_outer, scope);6723 const defer_counts = countDefers(defer_outer, scope);
6728 if (!defer_counts.have_err) {6724 if (!defer_counts.have_err) {
6729 // Only regular defers; no branch needed.6725 // Only regular defers; no branch needed.
6730 try genDefers(gz, defer_outer, scope, .normal_only);6726 try genDefers(gz, defer_outer, scope, .normal_only);
...@@ -6921,7 +6917,6 @@ fn localVarRef(...@@ -6921,7 +6917,6 @@ fn localVarRef(
6921 },6917 },
6922 .gen_zir => s = s.cast(GenZir).?.parent,6918 .gen_zir => s = s.cast(GenZir).?.parent,
6923 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,6919 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
6924 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
6925 .namespace => {6920 .namespace => {
6926 const ns = s.cast(Scope.Namespace).?;6921 const ns = s.cast(Scope.Namespace).?;
6927 if (ns.decls.get(name_str_index)) |i| {6922 if (ns.decls.get(name_str_index)) |i| {
...@@ -7550,7 +7545,6 @@ fn builtinCall(...@@ -7550,7 +7545,6 @@ fn builtinCall(
7550 },7545 },
7551 .gen_zir => s = s.cast(GenZir).?.parent,7546 .gen_zir => s = s.cast(GenZir).?.parent,
7552 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,7547 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
7553 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
7554 .namespace => {7548 .namespace => {
7555 const ns = s.cast(Scope.Namespace).?;7549 const ns = s.cast(Scope.Namespace).?;
7556 if (ns.decls.get(decl_name)) |i| {7550 if (ns.decls.get(decl_name)) |i| {
...@@ -10046,7 +10040,6 @@ const Scope = struct {...@@ -10046,7 +10040,6 @@ const Scope = struct {
10046 local_ptr,10040 local_ptr,
10047 defer_normal,10041 defer_normal,
10048 defer_error,10042 defer_error,
10049 defer_gen,
10050 namespace,10043 namespace,
10051 top,10044 top,
10052 };10045 };
...@@ -10105,10 +10098,9 @@ const Scope = struct {...@@ -10105,10 +10098,9 @@ const Scope = struct {
10105 base: Scope,10098 base: Scope,
10106 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.10099 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
10107 parent: *Scope,10100 parent: *Scope,
10108 defer_node: Ast.Node.Index,10101 index: u32,
10109 source_offset: u32,10102 len: u32,
10110 source_line: u32,10103 remapped_err_code: Zir.Inst.Index = 0,
10111 source_column: u32,
10112 };10104 };
1011310105
10114 /// Represents a global scope that has any number of declarations in it.10106 /// Represents a global scope that has any number of declarations in it.
...@@ -10144,13 +10136,6 @@ const Scope = struct {...@@ -10144,13 +10136,6 @@ const Scope = struct {
10144 const base_tag: Scope.Tag = .top;10136 const base_tag: Scope.Tag = .top;
10145 base: Scope = Scope{ .tag = base_tag },10137 base: Scope = Scope{ .tag = base_tag },
10146 };10138 };
10147
10148 const DeferGen = struct {
10149 const base_tag: Scope.Tag = .defer_gen;
10150 base: Scope = Scope{ .tag = base_tag },
10151 parent: *Scope,
10152 defer_node: Ast.Node.Index,
10153 };
10154};10139};
1015510140
10156/// This is a temporary structure; references to it are valid only10141/// This is a temporary structure; references to it are valid only
...@@ -10161,7 +10146,6 @@ const GenZir = struct {...@@ -10161,7 +10146,6 @@ const GenZir = struct {
10161 force_comptime: bool,10146 force_comptime: bool,
10162 /// This is set to true for inline loops; false otherwise.10147 /// This is set to true for inline loops; false otherwise.
10163 is_inline: bool = false,10148 is_inline: bool = false,
10164 in_defer: bool,
10165 c_import: bool = false,10149 c_import: bool = false,
10166 /// How decls created in this scope should be named.10150 /// How decls created in this scope should be named.
10167 anon_name_strategy: Zir.Inst.NameStrategy = .anon,10151 anon_name_strategy: Zir.Inst.NameStrategy = .anon,
...@@ -10204,6 +10188,10 @@ const GenZir = struct {...@@ -10204,6 +10188,10 @@ const GenZir = struct {
1020410188
10205 suspend_node: Ast.Node.Index = 0,10189 suspend_node: Ast.Node.Index = 0,
10206 nosuspend_node: Ast.Node.Index = 0,10190 nosuspend_node: Ast.Node.Index = 0,
10191 /// Set if this GenZir is a defer.
10192 cur_defer_node: Ast.Node.Index = 0,
10193 // Set if this GenZir is a defer or it is inside a defer.
10194 any_defer_node: Ast.Node.Index = 0,
1020710195
10208 /// Namespace members are lazy. When executing a decl within a namespace,10196 /// Namespace members are lazy. When executing a decl within a namespace,
10209 /// any references to external instructions need to be treated specially.10197 /// any references to external instructions need to be treated specially.
...@@ -10244,7 +10232,6 @@ const GenZir = struct {...@@ -10244,7 +10232,6 @@ const GenZir = struct {
10244 fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {10232 fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
10245 return .{10233 return .{
10246 .force_comptime = gz.force_comptime,10234 .force_comptime = gz.force_comptime,
10247 .in_defer = gz.in_defer,
10248 .c_import = gz.c_import,10235 .c_import = gz.c_import,
10249 .decl_node_index = gz.decl_node_index,10236 .decl_node_index = gz.decl_node_index,
10250 .decl_line = gz.decl_line,10237 .decl_line = gz.decl_line,
...@@ -10253,6 +10240,7 @@ const GenZir = struct {...@@ -10253,6 +10240,7 @@ const GenZir = struct {
10253 .astgen = gz.astgen,10240 .astgen = gz.astgen,
10254 .suspend_node = gz.suspend_node,10241 .suspend_node = gz.suspend_node,
10255 .nosuspend_node = gz.nosuspend_node,10242 .nosuspend_node = gz.nosuspend_node,
10243 .any_defer_node = gz.any_defer_node,
10256 .instructions = gz.instructions,10244 .instructions = gz.instructions,
10257 .instructions_top = gz.instructions.items.len,10245 .instructions_top = gz.instructions.items.len,
10258 };10246 };
...@@ -11107,6 +11095,16 @@ const GenZir = struct {...@@ -11107,6 +11095,16 @@ const GenZir = struct {
11107 });11095 });
11108 }11096 }
1110911097
11098 fn addDefer(gz: *GenZir, index: u32, len: u32) !void {
11099 _ = try gz.add(.{
11100 .tag = .@"defer",
11101 .data = .{ .@"defer" = .{
11102 .index = index,
11103 .len = len,
11104 } },
11105 });
11106 }
11107
11110 fn addDecl(11108 fn addDecl(
11111 gz: *GenZir,11109 gz: *GenZir,
11112 tag: Zir.Inst.Tag,11110 tag: Zir.Inst.Tag,
...@@ -11670,7 +11668,6 @@ fn detectLocalShadowing(...@@ -11670,7 +11668,6 @@ fn detectLocalShadowing(
11670 },11668 },
11671 .gen_zir => s = s.cast(GenZir).?.parent,11669 .gen_zir => s = s.cast(GenZir).?.parent,
11672 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,11670 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
11673 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
11674 .top => break,11671 .top => break,
11675 };11672 };
11676}11673}
...@@ -11809,7 +11806,6 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast....@@ -11809,7 +11806,6 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.
11809 .namespace => s = s.cast(Scope.Namespace).?.parent,11806 .namespace => s = s.cast(Scope.Namespace).?.parent,
11810 .gen_zir => s = s.cast(GenZir).?.parent,11807 .gen_zir => s = s.cast(GenZir).?.parent,
11811 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,11808 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
11812 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
11813 .top => break,11809 .top => break,
11814 };11810 };
11815 gop.value_ptr.* = member_node;11811 gop.value_ptr.* = member_node;
src/Sema.zig+29-5
...@@ -1461,6 +1461,29 @@ fn analyzeBodyInner(...@@ -1461,6 +1461,29 @@ fn analyzeBodyInner(
1461 // break break_data.inst;1461 // break break_data.inst;
1462 // }1462 // }
1463 //},1463 //},
1464 .@"defer" => blk: {
1465 const inst_data = sema.code.instructions.items(.data)[inst].@"defer";
1466 const defer_body = sema.code.extra[inst_data.index..][0..inst_data.len];
1467 const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) {
1468 error.ComptimeBreak => sema.comptime_break_inst,
1469 else => |e| return e,
1470 };
1471 if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn;
1472 break :blk Air.Inst.Ref.void_value;
1473 },
1474 .defer_err_code => blk: {
1475 const inst_data = sema.code.instructions.items(.data)[inst].defer_err_code;
1476 const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data;
1477 const defer_body = sema.code.extra[extra.index..][0..extra.len];
1478 const err_code = try sema.resolveInst(inst_data.err_code);
1479 try sema.inst_map.put(sema.gpa, extra.remapped_err_code, err_code);
1480 const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) {
1481 error.ComptimeBreak => sema.comptime_break_inst,
1482 else => |e| return e,
1483 };
1484 if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn;
1485 break :blk Air.Inst.Ref.void_value;
1486 },
1464 };1487 };
1465 if (sema.typeOf(air_inst).isNoReturn())1488 if (sema.typeOf(air_inst).isNoReturn())
1466 break always_noreturn;1489 break always_noreturn;
...@@ -9394,11 +9417,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9394,11 +9417,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9394 }9417 }
93959418
9396 if (special_prong == .@"else" and seen_errors.count() == operand_ty.errorSetNames().len) {9419 if (special_prong == .@"else" and seen_errors.count() == operand_ty.errorSetNames().len) {
9397
9398 // TODO re-enable if defer implementation is improved
9399 // https://github.com/ziglang/zig/issues/11798
9400 if (true) break :else_validation;
9401
9402 // In order to enable common patterns for generic code allow simple else bodies9420 // In order to enable common patterns for generic code allow simple else bodies
9403 // else => unreachable,9421 // else => unreachable,
9404 // else => return,9422 // else => return,
...@@ -9415,6 +9433,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -9415,6 +9433,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
9415 .as_node,9433 .as_node,
9416 .ret_node,9434 .ret_node,
9417 .@"unreachable",9435 .@"unreachable",
9436 .@"defer",
9437 .defer_err_code,
9438 .err_union_code,
9439 .ret_err_value_code,
9440 .is_non_err,
9441 .condbr,
9418 => {},9442 => {},
9419 else => break,9443 else => break,
9420 } else break :else_validation;9444 } else break :else_validation;
src/Zir.zig+30
...@@ -995,6 +995,13 @@ pub const Inst = struct {...@@ -995,6 +995,13 @@ pub const Inst = struct {
995 /// closure_capture instruction ref.995 /// closure_capture instruction ref.
996 closure_get,996 closure_get,
997997
998 /// A defer statement.
999 /// Uses the `defer` union field.
1000 @"defer",
1001 /// An errdefer statement with a code.
1002 /// Uses the `err_defer_code` union field.
1003 defer_err_code,
1004
998 /// The ZIR instruction tag is one of the `Extended` ones.1005 /// The ZIR instruction tag is one of the `Extended` ones.
999 /// Uses the `extended` union field.1006 /// Uses the `extended` union field.
1000 extended,1007 extended,
...@@ -1244,6 +1251,8 @@ pub const Inst = struct {...@@ -1244,6 +1251,8 @@ pub const Inst = struct {
1244 .try_ptr,1251 .try_ptr,
1245 //.try_inline,1252 //.try_inline,
1246 //.try_ptr_inline,1253 //.try_ptr_inline,
1254 .@"defer",
1255 .defer_err_code,
1247 => false,1256 => false,
12481257
1249 .@"break",1258 .@"break",
...@@ -1311,6 +1320,8 @@ pub const Inst = struct {...@@ -1311,6 +1320,8 @@ pub const Inst = struct {
1311 .memcpy,1320 .memcpy,
1312 .memset,1321 .memset,
1313 .check_comptime_control_flow,1322 .check_comptime_control_flow,
1323 .@"defer",
1324 .defer_err_code,
1314 => true,1325 => true,
13151326
1316 .param,1327 .param,
...@@ -1819,6 +1830,9 @@ pub const Inst = struct {...@@ -1819,6 +1830,9 @@ pub const Inst = struct {
1819 .closure_capture = .un_tok,1830 .closure_capture = .un_tok,
1820 .closure_get = .inst_node,1831 .closure_get = .inst_node,
18211832
1833 .@"defer" = .@"defer",
1834 .defer_err_code = .defer_err_code,
1835
1822 .extended = .extended,1836 .extended = .extended,
1823 });1837 });
1824 };1838 };
...@@ -2575,6 +2589,14 @@ pub const Inst = struct {...@@ -2575,6 +2589,14 @@ pub const Inst = struct {
2575 return zir.nullTerminatedString(self.str);2589 return zir.nullTerminatedString(self.str);
2576 }2590 }
2577 },2591 },
2592 @"defer": struct {
2593 index: u32,
2594 len: u32,
2595 },
2596 defer_err_code: struct {
2597 err_code: Ref,
2598 payload_index: u32,
2599 },
25782600
2579 // Make sure we don't accidentally add a field to make this union2601 // Make sure we don't accidentally add a field to make this union
2580 // bigger than expected. Note that in Debug builds, Zig is allowed2602 // bigger than expected. Note that in Debug builds, Zig is allowed
...@@ -2611,6 +2633,8 @@ pub const Inst = struct {...@@ -2611,6 +2633,8 @@ pub const Inst = struct {
2611 dbg_stmt,2633 dbg_stmt,
2612 inst_node,2634 inst_node,
2613 str_op,2635 str_op,
2636 @"defer",
2637 defer_err_code,
2614 };2638 };
2615 };2639 };
26162640
...@@ -3550,6 +3574,12 @@ pub const Inst = struct {...@@ -3550,6 +3574,12 @@ pub const Inst = struct {
3550 line: u32,3574 line: u32,
3551 column: u32,3575 column: u32,
3552 };3576 };
3577
3578 pub const DeferErrCode = struct {
3579 remapped_err_code: Index,
3580 index: u32,
3581 len: u32,
3582 };
3553};3583};
35543584
3555pub const SpecialProng = enum { none, @"else", under };3585pub const SpecialProng = enum { none, @"else", under };
src/print_zir.zig+23
...@@ -446,6 +446,9 @@ const Writer = struct {...@@ -446,6 +446,9 @@ const Writer = struct {
446446
447 .closure_get => try self.writeInstNode(stream, inst),447 .closure_get => try self.writeInstNode(stream, inst),
448448
449 .@"defer" => try self.writeDefer(stream, inst),
450 .defer_err_code => try self.writeDeferErrCode(stream, inst),
451
449 .extended => try self.writeExtended(stream, inst),452 .extended => try self.writeExtended(stream, inst),
450 }453 }
451 }454 }
...@@ -2364,6 +2367,26 @@ const Writer = struct {...@@ -2364,6 +2367,26 @@ const Writer = struct {
2364 try stream.print("{d}, {d})", .{ inst_data.line + 1, inst_data.column + 1 });2367 try stream.print("{d}, {d})", .{ inst_data.line + 1, inst_data.column + 1 });
2365 }2368 }
23662369
2370 fn writeDefer(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
2371 const inst_data = self.code.instructions.items(.data)[inst].@"defer";
2372 const body = self.code.extra[inst_data.index..][0..inst_data.len];
2373 try self.writeBracedBody(stream, body);
2374 try stream.writeByte(')');
2375 }
2376
2377 fn writeDeferErrCode(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
2378 const inst_data = self.code.instructions.items(.data)[inst].defer_err_code;
2379 const extra = self.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data;
2380
2381 try self.writeInstRef(stream, Zir.indexToRef(extra.remapped_err_code));
2382 try stream.writeAll(" = ");
2383 try self.writeInstRef(stream, inst_data.err_code);
2384 try stream.writeAll(", ");
2385 const body = self.code.extra[extra.index..][0..extra.len];
2386 try self.writeBracedBody(stream, body);
2387 try stream.writeByte(')');
2388 }
2389
2367 fn writeInstRef(self: *Writer, stream: anytype, ref: Zir.Inst.Ref) !void {2390 fn writeInstRef(self: *Writer, stream: anytype, ref: Zir.Inst.Ref) !void {
2368 var i: usize = @enumToInt(ref);2391 var i: usize = @enumToInt(ref);
23692392
test/behavior/defer.zig+17
...@@ -127,3 +127,20 @@ test "errdefer with payload" {...@@ -127,3 +127,20 @@ test "errdefer with payload" {
127 try S.doTheTest();127 try S.doTheTest();
128 comptime try S.doTheTest();128 comptime try S.doTheTest();
129}129}
130
131test "simple else prong doesn't emit an error for unreachable else prong" {
132 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
133
134 const S = struct {
135 fn foo() error{Foo}!void {
136 return error.Foo;
137 }
138 };
139 var a: u32 = 0;
140 defer a += 1;
141 S.foo() catch |err| switch (err) {
142 error.Foo => a += 1,
143 else => |e| return e,
144 };
145 try expect(a == 1);
146}
test/cases/compile_errors/return_from_defer_expression.zig+1
...@@ -19,3 +19,4 @@ export fn entry() usize { return @sizeOf(@TypeOf(testTrickyDefer)); }...@@ -19,3 +19,4 @@ export fn entry() usize { return @sizeOf(@TypeOf(testTrickyDefer)); }
19// target=native19// target=native
20//20//
21// :4:11: error: 'try' not allowed inside defer expression21// :4:11: error: 'try' not allowed inside defer expression
22// :4:5: note: defer expression here
test/cases/compile_errors/uncreachable_else_prong_err_set.zig created+25
...@@ -0,0 +1,25 @@
1pub export fn complex() void {
2 var a: error{ Foo, Bar } = error.Foo;
3 switch (a) {
4 error.Foo => unreachable,
5 error.Bar => unreachable,
6 else => {
7 @compileError("<something complex here>");
8 },
9 }
10}
11
12pub export fn simple() void {
13 var a: error{ Foo, Bar } = error.Foo;
14 switch (a) {
15 error.Foo => unreachable,
16 error.Bar => unreachable,
17 else => |e| return e,
18 }
19}
20
21// error
22// backend=llvm
23// target=native
24//
25// :6:14: error: unreachable else prong; all cases already handled
test/cases/returns_in_try.zig+2
...@@ -13,4 +13,6 @@ pub fn b() !void {...@@ -13,4 +13,6 @@ pub fn b() !void {
13// error13// error
14//14//
15// :7:11: error: 'try' not allowed inside defer expression15// :7:11: error: 'try' not allowed inside defer expression
16// :7:5: note: defer expression here
16// :10:11: error: cannot return from defer expression17// :10:11: error: cannot return from defer expression
18// :10:5: note: defer expression here