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 {
57005700 try quux();
57015701 } else |err| switch (err) {
57025702 error.FileNotFound => try hello(),
5703 else => try another(),
57045703 }
57055704}
57065705
......@@ -5716,10 +5715,6 @@ fn hello() !void {
57165715 try bang2();
57175716}
57185717
5719fn another() !void {
5720 try bang1();
5721}
5722
57235718fn bang1() !void {
57245719 return error.FileNotFound;
57255720}
src/AstGen.zig+134-138
......@@ -135,7 +135,6 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
135135 var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
136136 var gen_scope: GenZir = .{
137137 .force_comptime = true,
138 .in_defer = false,
139138 .parent = &top_scope.base,
140139 .anon_name_strategy = .parent,
141140 .decl_node_index = 0,
......@@ -1856,6 +1855,16 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
18561855 .gen_zir => {
18571856 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
18591868 const block_inst = blk: {
18601869 if (break_label != 0) {
18611870 if (block_gz.label) |*label| {
......@@ -1919,17 +1928,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
19191928 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
19201929 .namespace => break,
19211930 .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 },
19331931 .top => unreachable,
19341932 }
19351933 }
......@@ -1953,6 +1951,16 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
19531951 switch (scope.tag) {
19541952 .gen_zir => {
19551953 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 }
19561964 const continue_block = gen_zir.continue_block;
19571965 if (continue_block == 0) {
19581966 scope = gen_zir.parent;
......@@ -1985,21 +1993,9 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
19851993 .defer_normal => {
19861994 const defer_scope = scope.cast(Scope.Defer).?;
19871995 scope = defer_scope.parent;
1988 const expr_node = node_datas[defer_scope.defer_node].rhs;
1989 try unusedResultDeferExpr(parent_gz, defer_scope, defer_scope.parent, expr_node);
1996 try parent_gz.addDefer(defer_scope.index, defer_scope.len);
19901997 },
19911998 .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 },
20031999 .namespace => break,
20042000 .top => unreachable,
20052001 }
......@@ -2064,7 +2060,6 @@ fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.Toke
20642060 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
20652061 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
20662062 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2067 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
20682063 .namespace => break,
20692064 .top => unreachable,
20702065 }
......@@ -2208,8 +2203,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
22082203 .simple_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.simpleVarDecl(statement)),
22092204 .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),
2212 .@"errdefer" => scope = try makeDeferScope(gz.astgen, scope, statement, block_arena_allocator, .defer_error),
2206 .@"defer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal),
2207 .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error),
22132208
22142209 .assign => try assign(gz, scope, statement),
22152210
......@@ -2253,28 +2248,6 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod
22532248 try checkUsed(gz, parent_scope, scope);
22542249}
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
22782251/// Returns AST source node of the thing that is noreturn if the statement is
22792252/// definitely `noreturn`. Otherwise returns 0.
22802253fn 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
25842557 .validate_struct_init_ty,
25852558 .validate_deref,
25862559 => break :b true,
2560
2561 .@"defer" => unreachable,
2562 .defer_err_code => unreachable,
25872563 }
25882564 } else switch (maybe_unused_result) {
25892565 .none => unreachable,
......@@ -2603,15 +2579,12 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
26032579 return noreturn_src_node;
26042580}
26052581
2606fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct {
2582fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct {
26072583 have_any: bool,
26082584 have_normal: bool,
26092585 have_err: bool,
26102586 need_err_code: bool,
26112587} {
2612 const tree = astgen.tree;
2613 const node_datas = tree.nodes.items(.data);
2614
26152588 var have_normal = false;
26162589 var have_err = false;
26172590 var need_err_code = false;
......@@ -2621,7 +2594,6 @@ fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct
26212594 .gen_zir => scope = scope.cast(GenZir).?.parent,
26222595 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
26232596 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2624 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
26252597 .defer_normal => {
26262598 const defer_scope = scope.cast(Scope.Defer).?;
26272599 scope = defer_scope.parent;
......@@ -2634,7 +2606,7 @@ fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct
26342606
26352607 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;
26382610 need_err_code = need_err_code or have_err_payload;
26392611 },
26402612 .namespace => unreachable,
......@@ -2661,9 +2633,7 @@ fn genDefers(
26612633 inner_scope: *Scope,
26622634 which_ones: DefersToEmit,
26632635) InnerError!void {
2664 const astgen = gz.astgen;
2665 const tree = astgen.tree;
2666 const node_datas = tree.nodes.items(.data);
2636 const gpa = gz.astgen.gpa;
26672637
26682638 var scope = inner_scope;
26692639 while (scope != outer_scope) {
......@@ -2671,51 +2641,40 @@ fn genDefers(
26712641 .gen_zir => scope = scope.cast(GenZir).?.parent,
26722642 .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
26732643 .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
2674 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
26752644 .defer_normal => {
26762645 const defer_scope = scope.cast(Scope.Defer).?;
26772646 scope = defer_scope.parent;
2678 const expr_node = node_datas[defer_scope.defer_node].rhs;
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);
2647 try gz.addDefer(defer_scope.index, defer_scope.len);
26832648 },
26842649 .defer_error => {
26852650 const defer_scope = scope.cast(Scope.Defer).?;
26862651 scope = defer_scope.parent;
26872652 switch (which_ones) {
26882653 .both_sans_err => {
2689 const expr_node = node_datas[defer_scope.defer_node].rhs;
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);
2654 try gz.addDefer(defer_scope.index, defer_scope.len);
26942655 },
26952656 .both => |err_code| {
2696 const expr_node = node_datas[defer_scope.defer_node].rhs;
2697 const payload_token = node_datas[defer_scope.defer_node].lhs;
2698 const prev_in_defer = gz.in_defer;
2699 gz.in_defer = true;
2700 defer gz.in_defer = prev_in_defer;
2701 var local_val_scope: Scope.LocalVal = undefined;
2702 try gz.addDbgBlockBegin();
2703 const sub_scope = if (payload_token == 0) defer_scope.parent else blk: {
2704 const ident_name = try astgen.identAsString(payload_token);
2705 local_val_scope = .{
2706 .parent = defer_scope.parent,
2707 .gen_zir = gz,
2708 .name = ident_name,
2709 .inst = err_code,
2710 .token_src = payload_token,
2711 .id_cat = .@"capture",
2712 };
2713 try gz.addDbgVar(.dbg_var_val, ident_name, err_code);
2714 break :blk &local_val_scope.base;
2715 };
2716 try unusedResultDeferExpr(gz, defer_scope, sub_scope, expr_node);
2717 try checkUsed(gz, scope, sub_scope);
2718 try gz.addDbgBlockEnd();
2657 if (defer_scope.remapped_err_code == 0) {
2658 try gz.addDefer(defer_scope.index, defer_scope.len);
2659 } else {
2660 try gz.instructions.ensureUnusedCapacity(gpa, 1);
2661 try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
2662
2663 const payload_index = try gz.astgen.addExtra(Zir.Inst.DeferErrCode{
2664 .remapped_err_code = defer_scope.remapped_err_code,
2665 .index = defer_scope.index,
2666 .len = defer_scope.len,
2667 });
2668 const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
2669 gz.astgen.instructions.appendAssumeCapacity(.{
2670 .tag = .defer_err_code,
2671 .data = .{ .defer_err_code = .{
2672 .err_code = err_code,
2673 .payload_index = payload_index,
2674 } },
2675 });
2676 gz.instructions.appendAssumeCapacity(new_index);
2677 }
27192678 },
27202679 .normal_only => continue,
27212680 }
......@@ -2752,35 +2711,68 @@ fn checkUsed(
27522711 scope = s.parent;
27532712 },
27542713 .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
2755 .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent,
27562714 .namespace => unreachable,
27572715 .top => unreachable,
27582716 }
27592717 }
27602718}
27612719
2762fn makeDeferScope(
2763 astgen: *AstGen,
2720fn deferStmt(
2721 gz: *GenZir,
27642722 scope: *Scope,
27652723 node: Ast.Node.Index,
27662724 block_arena: Allocator,
27672725 scope_tag: Scope.Tag,
27682726) 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;
27702733 const node_datas = tree.nodes.items(.data);
27712734 const expr_node = node_datas[node].rhs;
2772 const token_starts = tree.tokens.items(.start);
2773 const node_start = token_starts[tree.firstToken(expr_node)];
2735
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
27742768 const defer_scope = try block_arena.create(Scope.Defer);
2775 astgen.advanceSourceCursor(node_start);
27762769
27772770 defer_scope.* = .{
27782771 .base = .{ .tag = scope_tag },
27792772 .parent = scope,
2780 .defer_node = node,
2781 .source_offset = astgen.source_offset,
2782 .source_line = astgen.source_line,
2783 .source_column = astgen.source_column,
2773 .index = index,
2774 .len = body_len,
2775 .remapped_err_code = remapped_err_code,
27842776 };
27852777 return &defer_scope.base;
27862778}
......@@ -3461,7 +3453,6 @@ fn fnDecl(
34613453
34623454 var decl_gz: GenZir = .{
34633455 .force_comptime = true,
3464 .in_defer = false,
34653456 .decl_node_index = fn_proto.ast.proto_node,
34663457 .decl_line = astgen.source_line,
34673458 .parent = scope,
......@@ -3473,7 +3464,6 @@ fn fnDecl(
34733464
34743465 var fn_gz: GenZir = .{
34753466 .force_comptime = false,
3476 .in_defer = false,
34773467 .decl_node_index = fn_proto.ast.proto_node,
34783468 .decl_line = decl_gz.decl_line,
34793469 .parent = &decl_gz.base,
......@@ -3812,7 +3802,6 @@ fn globalVarDecl(
38123802 .decl_line = astgen.source_line,
38133803 .astgen = astgen,
38143804 .force_comptime = true,
3815 .in_defer = false,
38163805 .anon_name_strategy = .parent,
38173806 .instructions = gz.instructions,
38183807 .instructions_top = gz.instructions.items.len,
......@@ -3964,7 +3953,6 @@ fn comptimeDecl(
39643953
39653954 var decl_block: GenZir = .{
39663955 .force_comptime = true,
3967 .in_defer = false,
39683956 .decl_node_index = node,
39693957 .decl_line = astgen.source_line,
39703958 .parent = scope,
......@@ -4019,7 +4007,6 @@ fn usingnamespaceDecl(
40194007
40204008 var decl_block: GenZir = .{
40214009 .force_comptime = true,
4022 .in_defer = false,
40234010 .decl_node_index = node,
40244011 .decl_line = astgen.source_line,
40254012 .parent = scope,
......@@ -4067,7 +4054,6 @@ fn testDecl(
40674054
40684055 var decl_block: GenZir = .{
40694056 .force_comptime = true,
4070 .in_defer = false,
40714057 .decl_node_index = node,
40724058 .decl_line = astgen.source_line,
40734059 .parent = scope,
......@@ -4132,7 +4118,6 @@ fn testDecl(
41324118 .local_val, .local_ptr => unreachable, // a test cannot be in a local scope
41334119 .gen_zir => s = s.cast(GenZir).?.parent,
41344120 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
4135 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
41364121 .namespace => {
41374122 const ns = s.cast(Scope.Namespace).?;
41384123 if (ns.decls.get(name_str_index)) |i| {
......@@ -4164,7 +4149,6 @@ fn testDecl(
41644149
41654150 var fn_block: GenZir = .{
41664151 .force_comptime = false,
4167 .in_defer = false,
41684152 .decl_node_index = node,
41694153 .decl_line = decl_block.decl_line,
41704154 .parent = &decl_block.base,
......@@ -4288,7 +4272,6 @@ fn structDeclInner(
42884272 .decl_line = decl_line,
42894273 .astgen = astgen,
42904274 .force_comptime = true,
4291 .in_defer = false,
42924275 .instructions = gz.instructions,
42934276 .instructions_top = gz.instructions.items.len,
42944277 };
......@@ -4489,7 +4472,6 @@ fn unionDeclInner(
44894472 .decl_line = astgen.source_line,
44904473 .astgen = astgen,
44914474 .force_comptime = true,
4492 .in_defer = false,
44934475 .instructions = gz.instructions,
44944476 .instructions_top = gz.instructions.items.len,
44954477 };
......@@ -4773,7 +4755,6 @@ fn containerDecl(
47734755 .decl_line = astgen.source_line,
47744756 .astgen = astgen,
47754757 .force_comptime = true,
4776 .in_defer = false,
47774758 .instructions = gz.instructions,
47784759 .instructions_top = gz.instructions.items.len,
47794760 };
......@@ -4879,7 +4860,6 @@ fn containerDecl(
48794860 .decl_line = astgen.source_line,
48804861 .astgen = astgen,
48814862 .force_comptime = true,
4882 .in_defer = false,
48834863 .instructions = gz.instructions,
48844864 .instructions_top = gz.instructions.items.len,
48854865 };
......@@ -5110,7 +5090,15 @@ fn tryExpr(
51105090 return astgen.failNode(node, "'try' outside function scope", .{});
51115091 };
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
51155103 // Ensure debug line/column information is emitted for this try expression.
51165104 // 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
66566644 return astgen.failNode(node, "'return' outside function scope", .{});
66576645 }
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
66616657 // Ensure debug line/column information is emitted for this return expression.
66626658 // 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
66836679 // for detecting whether to add something to the function's inferred error set.
66846680 const ident_token = node_datas[operand_node].rhs;
66856681 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);
66876683 if (!defer_counts.need_err_code) {
66886684 try genDefers(gz, defer_outer, scope, .both_sans_err);
66896685 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
67246720 return Zir.Inst.Ref.unreachable_value;
67256721 },
67266722 .maybe => {
6727 const defer_counts = countDefers(astgen, defer_outer, scope);
6723 const defer_counts = countDefers(defer_outer, scope);
67286724 if (!defer_counts.have_err) {
67296725 // Only regular defers; no branch needed.
67306726 try genDefers(gz, defer_outer, scope, .normal_only);
......@@ -6921,7 +6917,6 @@ fn localVarRef(
69216917 },
69226918 .gen_zir => s = s.cast(GenZir).?.parent,
69236919 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
6924 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
69256920 .namespace => {
69266921 const ns = s.cast(Scope.Namespace).?;
69276922 if (ns.decls.get(name_str_index)) |i| {
......@@ -7550,7 +7545,6 @@ fn builtinCall(
75507545 },
75517546 .gen_zir => s = s.cast(GenZir).?.parent,
75527547 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
7553 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
75547548 .namespace => {
75557549 const ns = s.cast(Scope.Namespace).?;
75567550 if (ns.decls.get(decl_name)) |i| {
......@@ -10046,7 +10040,6 @@ const Scope = struct {
1004610040 local_ptr,
1004710041 defer_normal,
1004810042 defer_error,
10049 defer_gen,
1005010043 namespace,
1005110044 top,
1005210045 };
......@@ -10105,10 +10098,9 @@ const Scope = struct {
1010510098 base: Scope,
1010610099 /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
1010710100 parent: *Scope,
10108 defer_node: Ast.Node.Index,
10109 source_offset: u32,
10110 source_line: u32,
10111 source_column: u32,
10101 index: u32,
10102 len: u32,
10103 remapped_err_code: Zir.Inst.Index = 0,
1011210104 };
1011310105
1011410106 /// Represents a global scope that has any number of declarations in it.
......@@ -10144,13 +10136,6 @@ const Scope = struct {
1014410136 const base_tag: Scope.Tag = .top;
1014510137 base: Scope = Scope{ .tag = base_tag },
1014610138 };
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 };
1015410139};
1015510140
1015610141/// This is a temporary structure; references to it are valid only
......@@ -10161,7 +10146,6 @@ const GenZir = struct {
1016110146 force_comptime: bool,
1016210147 /// This is set to true for inline loops; false otherwise.
1016310148 is_inline: bool = false,
10164 in_defer: bool,
1016510149 c_import: bool = false,
1016610150 /// How decls created in this scope should be named.
1016710151 anon_name_strategy: Zir.Inst.NameStrategy = .anon,
......@@ -10204,6 +10188,10 @@ const GenZir = struct {
1020410188
1020510189 suspend_node: Ast.Node.Index = 0,
1020610190 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
1020810196 /// Namespace members are lazy. When executing a decl within a namespace,
1020910197 /// any references to external instructions need to be treated specially.
......@@ -10244,7 +10232,6 @@ const GenZir = struct {
1024410232 fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
1024510233 return .{
1024610234 .force_comptime = gz.force_comptime,
10247 .in_defer = gz.in_defer,
1024810235 .c_import = gz.c_import,
1024910236 .decl_node_index = gz.decl_node_index,
1025010237 .decl_line = gz.decl_line,
......@@ -10253,6 +10240,7 @@ const GenZir = struct {
1025310240 .astgen = gz.astgen,
1025410241 .suspend_node = gz.suspend_node,
1025510242 .nosuspend_node = gz.nosuspend_node,
10243 .any_defer_node = gz.any_defer_node,
1025610244 .instructions = gz.instructions,
1025710245 .instructions_top = gz.instructions.items.len,
1025810246 };
......@@ -11107,6 +11095,16 @@ const GenZir = struct {
1110711095 });
1110811096 }
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
1111011108 fn addDecl(
1111111109 gz: *GenZir,
1111211110 tag: Zir.Inst.Tag,
......@@ -11670,7 +11668,6 @@ fn detectLocalShadowing(
1167011668 },
1167111669 .gen_zir => s = s.cast(GenZir).?.parent,
1167211670 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
11673 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
1167411671 .top => break,
1167511672 };
1167611673}
......@@ -11809,7 +11806,6 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.
1180911806 .namespace => s = s.cast(Scope.Namespace).?.parent,
1181011807 .gen_zir => s = s.cast(GenZir).?.parent,
1181111808 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
11812 .defer_gen => s = s.cast(Scope.DeferGen).?.parent,
1181311809 .top => break,
1181411810 };
1181511811 gop.value_ptr.* = member_node;
src/Sema.zig+29-5
......@@ -1461,6 +1461,29 @@ fn analyzeBodyInner(
14611461 // break break_data.inst;
14621462 // }
14631463 //},
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 },
14641487 };
14651488 if (sema.typeOf(air_inst).isNoReturn())
14661489 break always_noreturn;
......@@ -9394,11 +9417,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
93949417 }
93959418
93969419 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
94029420 // In order to enable common patterns for generic code allow simple else bodies
94039421 // else => unreachable,
94049422 // else => return,
......@@ -9415,6 +9433,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
94159433 .as_node,
94169434 .ret_node,
94179435 .@"unreachable",
9436 .@"defer",
9437 .defer_err_code,
9438 .err_union_code,
9439 .ret_err_value_code,
9440 .is_non_err,
9441 .condbr,
94189442 => {},
94199443 else => break,
94209444 } else break :else_validation;
src/Zir.zig+30
......@@ -995,6 +995,13 @@ pub const Inst = struct {
995995 /// closure_capture instruction ref.
996996 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
9981005 /// The ZIR instruction tag is one of the `Extended` ones.
9991006 /// Uses the `extended` union field.
10001007 extended,
......@@ -1244,6 +1251,8 @@ pub const Inst = struct {
12441251 .try_ptr,
12451252 //.try_inline,
12461253 //.try_ptr_inline,
1254 .@"defer",
1255 .defer_err_code,
12471256 => false,
12481257
12491258 .@"break",
......@@ -1311,6 +1320,8 @@ pub const Inst = struct {
13111320 .memcpy,
13121321 .memset,
13131322 .check_comptime_control_flow,
1323 .@"defer",
1324 .defer_err_code,
13141325 => true,
13151326
13161327 .param,
......@@ -1819,6 +1830,9 @@ pub const Inst = struct {
18191830 .closure_capture = .un_tok,
18201831 .closure_get = .inst_node,
18211832
1833 .@"defer" = .@"defer",
1834 .defer_err_code = .defer_err_code,
1835
18221836 .extended = .extended,
18231837 });
18241838 };
......@@ -2575,6 +2589,14 @@ pub const Inst = struct {
25752589 return zir.nullTerminatedString(self.str);
25762590 }
25772591 },
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
25792601 // Make sure we don't accidentally add a field to make this union
25802602 // bigger than expected. Note that in Debug builds, Zig is allowed
......@@ -2611,6 +2633,8 @@ pub const Inst = struct {
26112633 dbg_stmt,
26122634 inst_node,
26132635 str_op,
2636 @"defer",
2637 defer_err_code,
26142638 };
26152639 };
26162640
......@@ -3550,6 +3574,12 @@ pub const Inst = struct {
35503574 line: u32,
35513575 column: u32,
35523576 };
3577
3578 pub const DeferErrCode = struct {
3579 remapped_err_code: Index,
3580 index: u32,
3581 len: u32,
3582 };
35533583};
35543584
35553585pub const SpecialProng = enum { none, @"else", under };
src/print_zir.zig+23
......@@ -446,6 +446,9 @@ const Writer = struct {
446446
447447 .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
449452 .extended => try self.writeExtended(stream, inst),
450453 }
451454 }
......@@ -2364,6 +2367,26 @@ const Writer = struct {
23642367 try stream.print("{d}, {d})", .{ inst_data.line + 1, inst_data.column + 1 });
23652368 }
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
23672390 fn writeInstRef(self: *Writer, stream: anytype, ref: Zir.Inst.Ref) !void {
23682391 var i: usize = @enumToInt(ref);
23692392
test/behavior/defer.zig+17
......@@ -127,3 +127,20 @@ test "errdefer with payload" {
127127 try S.doTheTest();
128128 comptime try S.doTheTest();
129129}
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)); }
1919// target=native
2020//
2121// :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 {
1313// error
1414//
1515// :7:11: error: 'try' not allowed inside defer expression
16// :7:5: note: defer expression here
1617// :10:11: error: cannot return from defer expression
18// :10:5: note: defer expression here