diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2022-09-12 16:11:18 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2022-09-12 16:11:18 -0700 |
| commit | 011663eea590dddff79b73c72bfeda5dd7f947cc (patch) | |
| tree | c51299eb6d22d3762674540de062d7e4723c9a92 /src | |
| parent | 01e89fec71c781113f4be6f65af8e1702c8dde7c (diff) | |
| parent | e323cf1264f390911dcc2efea71d46be1d631d92 (diff) | |
| download | zig-011663eea590dddff79b73c72bfeda5dd7f947cc.tar.gz zig-011663eea590dddff79b73c72bfeda5dd7f947cc.zip | |
Merge remote-tracking branch 'origin/master' into llvm15
Diffstat (limited to 'src')
| -rw-r--r-- | src/AstGen.zig | 272 | ||||
| -rw-r--r-- | src/Sema.zig | 34 | ||||
| -rw-r--r-- | src/Zir.zig | 30 | ||||
| -rw-r--r-- | src/print_zir.zig | 23 |
4 files changed, 216 insertions, 143 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig index 1502b97017..591c48e4d6 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -135,7 +135,6 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir { var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; var gen_scope: GenZir = .{ .force_comptime = true, - .in_defer = false, .parent = &top_scope.base, .anon_name_strategy = .parent, .decl_node_index = 0, @@ -1856,6 +1855,16 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn .gen_zir => { const block_gz = scope.cast(GenZir).?; + if (block_gz.cur_defer_node != 0) { + return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{ + try astgen.errNoteNode( + block_gz.cur_defer_node, + "defer expression here", + .{}, + ), + }); + } + const block_inst = blk: { if (break_label != 0) { if (block_gz.label) |*label| { @@ -1919,17 +1928,6 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, .namespace => break, .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, - .defer_gen => { - const defer_gen = scope.cast(Scope.DeferGen).?; - - return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{ - try astgen.errNoteNode( - defer_gen.defer_node, - "defer expression here", - .{}, - ), - }); - }, .top => unreachable, } } @@ -1953,6 +1951,16 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) switch (scope.tag) { .gen_zir => { const gen_zir = scope.cast(GenZir).?; + + if (gen_zir.cur_defer_node != 0) { + return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{ + try astgen.errNoteNode( + gen_zir.cur_defer_node, + "defer expression here", + .{}, + ), + }); + } const continue_block = gen_zir.continue_block; if (continue_block == 0) { scope = gen_zir.parent; @@ -1985,21 +1993,9 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) .defer_normal => { const defer_scope = scope.cast(Scope.Defer).?; scope = defer_scope.parent; - const expr_node = node_datas[defer_scope.defer_node].rhs; - try unusedResultDeferExpr(parent_gz, defer_scope, defer_scope.parent, expr_node); + try parent_gz.addDefer(defer_scope.index, defer_scope.len); }, .defer_error => scope = scope.cast(Scope.Defer).?.parent, - .defer_gen => { - const defer_gen = scope.cast(Scope.DeferGen).?; - - return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{ - try astgen.errNoteNode( - defer_gen.defer_node, - "defer expression here", - .{}, - ), - }); - }, .namespace => break, .top => unreachable, } @@ -2064,7 +2060,6 @@ fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.Toke .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, - .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent, .namespace => break, .top => unreachable, } @@ -2208,8 +2203,8 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod .simple_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.simpleVarDecl(statement)), .aligned_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.alignedVarDecl(statement)), - .@"defer" => scope = try makeDeferScope(gz.astgen, scope, statement, block_arena_allocator, .defer_normal), - .@"errdefer" => scope = try makeDeferScope(gz.astgen, scope, statement, block_arena_allocator, .defer_error), + .@"defer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal), + .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error), .assign => try assign(gz, scope, statement), @@ -2253,28 +2248,6 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Nod try checkUsed(gz, parent_scope, scope); } -fn unusedResultDeferExpr(gz: *GenZir, defer_scope: *Scope.Defer, expr_scope: *Scope, expr_node: Ast.Node.Index) InnerError!void { - const astgen = gz.astgen; - const prev_offset = astgen.source_offset; - const prev_line = astgen.source_line; - const prev_column = astgen.source_column; - defer { - astgen.source_offset = prev_offset; - astgen.source_line = prev_line; - astgen.source_column = prev_column; - } - astgen.source_offset = defer_scope.source_offset; - astgen.source_line = defer_scope.source_line; - astgen.source_column = defer_scope.source_column; - - var defer_gen: Scope.DeferGen = .{ - .parent = expr_scope, - .defer_node = defer_scope.defer_node, - }; - - _ = try unusedResultExpr(gz, &defer_gen.base, expr_node); -} - /// Returns AST source node of the thing that is noreturn if the statement is /// definitely `noreturn`. Otherwise returns 0. fn 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 .validate_struct_init_ty, .validate_deref, => break :b true, + + .@"defer" => unreachable, + .defer_err_code => unreachable, } } else switch (maybe_unused_result) { .none => unreachable, @@ -2603,15 +2579,12 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As return noreturn_src_node; } -fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct { +fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct { have_any: bool, have_normal: bool, have_err: bool, need_err_code: bool, } { - const tree = astgen.tree; - const node_datas = tree.nodes.items(.data); - var have_normal = false; var have_err = false; var need_err_code = false; @@ -2621,7 +2594,6 @@ fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct .gen_zir => scope = scope.cast(GenZir).?.parent, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, - .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent, .defer_normal => { const defer_scope = scope.cast(Scope.Defer).?; scope = defer_scope.parent; @@ -2634,7 +2606,7 @@ fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct have_err = true; - const have_err_payload = node_datas[defer_scope.defer_node].lhs != 0; + const have_err_payload = defer_scope.remapped_err_code != 0; need_err_code = need_err_code or have_err_payload; }, .namespace => unreachable, @@ -2661,9 +2633,7 @@ fn genDefers( inner_scope: *Scope, which_ones: DefersToEmit, ) InnerError!void { - const astgen = gz.astgen; - const tree = astgen.tree; - const node_datas = tree.nodes.items(.data); + const gpa = gz.astgen.gpa; var scope = inner_scope; while (scope != outer_scope) { @@ -2671,51 +2641,40 @@ fn genDefers( .gen_zir => scope = scope.cast(GenZir).?.parent, .local_val => scope = scope.cast(Scope.LocalVal).?.parent, .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, - .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent, .defer_normal => { const defer_scope = scope.cast(Scope.Defer).?; scope = defer_scope.parent; - const expr_node = node_datas[defer_scope.defer_node].rhs; - const prev_in_defer = gz.in_defer; - gz.in_defer = true; - defer gz.in_defer = prev_in_defer; - try unusedResultDeferExpr(gz, defer_scope, defer_scope.parent, expr_node); + try gz.addDefer(defer_scope.index, defer_scope.len); }, .defer_error => { const defer_scope = scope.cast(Scope.Defer).?; scope = defer_scope.parent; switch (which_ones) { .both_sans_err => { - const expr_node = node_datas[defer_scope.defer_node].rhs; - const prev_in_defer = gz.in_defer; - gz.in_defer = true; - defer gz.in_defer = prev_in_defer; - try unusedResultDeferExpr(gz, defer_scope, defer_scope.parent, expr_node); + try gz.addDefer(defer_scope.index, defer_scope.len); }, .both => |err_code| { - const expr_node = node_datas[defer_scope.defer_node].rhs; - const payload_token = node_datas[defer_scope.defer_node].lhs; - const prev_in_defer = gz.in_defer; - gz.in_defer = true; - defer gz.in_defer = prev_in_defer; - var local_val_scope: Scope.LocalVal = undefined; - try gz.addDbgBlockBegin(); - const sub_scope = if (payload_token == 0) defer_scope.parent else blk: { - const ident_name = try astgen.identAsString(payload_token); - local_val_scope = .{ - .parent = defer_scope.parent, - .gen_zir = gz, - .name = ident_name, - .inst = err_code, - .token_src = payload_token, - .id_cat = .@"capture", - }; - try gz.addDbgVar(.dbg_var_val, ident_name, err_code); - break :blk &local_val_scope.base; - }; - try unusedResultDeferExpr(gz, defer_scope, sub_scope, expr_node); - try checkUsed(gz, scope, sub_scope); - try gz.addDbgBlockEnd(); + if (defer_scope.remapped_err_code == 0) { + try gz.addDefer(defer_scope.index, defer_scope.len); + } else { + try gz.instructions.ensureUnusedCapacity(gpa, 1); + try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1); + + const payload_index = try gz.astgen.addExtra(Zir.Inst.DeferErrCode{ + .remapped_err_code = defer_scope.remapped_err_code, + .index = defer_scope.index, + .len = defer_scope.len, + }); + const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len); + gz.astgen.instructions.appendAssumeCapacity(.{ + .tag = .defer_err_code, + .data = .{ .defer_err_code = .{ + .err_code = err_code, + .payload_index = payload_index, + } }, + }); + gz.instructions.appendAssumeCapacity(new_index); + } }, .normal_only => continue, } @@ -2752,35 +2711,68 @@ fn checkUsed( scope = s.parent; }, .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent, - .defer_gen => scope = scope.cast(Scope.DeferGen).?.parent, .namespace => unreachable, .top => unreachable, } } } -fn makeDeferScope( - astgen: *AstGen, +fn deferStmt( + gz: *GenZir, scope: *Scope, node: Ast.Node.Index, block_arena: Allocator, scope_tag: Scope.Tag, ) InnerError!*Scope { - const tree = astgen.tree; + var defer_gen = gz.makeSubBlock(scope); + defer_gen.cur_defer_node = node; + defer_gen.any_defer_node = node; + defer defer_gen.unstack(); + + const tree = gz.astgen.tree; const node_datas = tree.nodes.items(.data); const expr_node = node_datas[node].rhs; - const token_starts = tree.tokens.items(.start); - const node_start = token_starts[tree.firstToken(expr_node)]; + + const payload_token = node_datas[node].lhs; + var local_val_scope: Scope.LocalVal = undefined; + var remapped_err_code: Zir.Inst.Index = 0; + const have_err_code = scope_tag == .defer_error and payload_token != 0; + const sub_scope = if (!have_err_code) &defer_gen.base else blk: { + try gz.addDbgBlockBegin(); + const ident_name = try gz.astgen.identAsString(payload_token); + remapped_err_code = @intCast(u32, try gz.astgen.instructions.addOne(gz.astgen.gpa)); + const remapped_err_code_ref = Zir.indexToRef(remapped_err_code); + local_val_scope = .{ + .parent = &defer_gen.base, + .gen_zir = gz, + .name = ident_name, + .inst = remapped_err_code_ref, + .token_src = payload_token, + .id_cat = .@"capture", + }; + try gz.addDbgVar(.dbg_var_val, ident_name, remapped_err_code_ref); + break :blk &local_val_scope.base; + }; + _ = try unusedResultExpr(&defer_gen, sub_scope, expr_node); + try checkUsed(gz, scope, sub_scope); + if (have_err_code) try gz.addDbgBlockEnd(); + _ = try defer_gen.addBreak(.break_inline, 0, .void_value); + + const body = defer_gen.instructionsSlice(); + const body_len = gz.astgen.countBodyLenAfterFixups(body); + + const index = @intCast(u32, gz.astgen.extra.items.len); + try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len); + gz.astgen.appendBodyWithFixups(body); + const defer_scope = try block_arena.create(Scope.Defer); - astgen.advanceSourceCursor(node_start); defer_scope.* = .{ .base = .{ .tag = scope_tag }, .parent = scope, - .defer_node = node, - .source_offset = astgen.source_offset, - .source_line = astgen.source_line, - .source_column = astgen.source_column, + .index = index, + .len = body_len, + .remapped_err_code = remapped_err_code, }; return &defer_scope.base; } @@ -3461,7 +3453,6 @@ fn fnDecl( var decl_gz: GenZir = .{ .force_comptime = true, - .in_defer = false, .decl_node_index = fn_proto.ast.proto_node, .decl_line = astgen.source_line, .parent = scope, @@ -3473,7 +3464,6 @@ fn fnDecl( var fn_gz: GenZir = .{ .force_comptime = false, - .in_defer = false, .decl_node_index = fn_proto.ast.proto_node, .decl_line = decl_gz.decl_line, .parent = &decl_gz.base, @@ -3812,7 +3802,6 @@ fn globalVarDecl( .decl_line = astgen.source_line, .astgen = astgen, .force_comptime = true, - .in_defer = false, .anon_name_strategy = .parent, .instructions = gz.instructions, .instructions_top = gz.instructions.items.len, @@ -3964,7 +3953,6 @@ fn comptimeDecl( var decl_block: GenZir = .{ .force_comptime = true, - .in_defer = false, .decl_node_index = node, .decl_line = astgen.source_line, .parent = scope, @@ -4019,7 +4007,6 @@ fn usingnamespaceDecl( var decl_block: GenZir = .{ .force_comptime = true, - .in_defer = false, .decl_node_index = node, .decl_line = astgen.source_line, .parent = scope, @@ -4067,7 +4054,6 @@ fn testDecl( var decl_block: GenZir = .{ .force_comptime = true, - .in_defer = false, .decl_node_index = node, .decl_line = astgen.source_line, .parent = scope, @@ -4132,7 +4118,6 @@ fn testDecl( .local_val, .local_ptr => unreachable, // a test cannot be in a local scope .gen_zir => s = s.cast(GenZir).?.parent, .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, - .defer_gen => s = s.cast(Scope.DeferGen).?.parent, .namespace => { const ns = s.cast(Scope.Namespace).?; if (ns.decls.get(name_str_index)) |i| { @@ -4164,7 +4149,6 @@ fn testDecl( var fn_block: GenZir = .{ .force_comptime = false, - .in_defer = false, .decl_node_index = node, .decl_line = decl_block.decl_line, .parent = &decl_block.base, @@ -4288,7 +4272,6 @@ fn structDeclInner( .decl_line = decl_line, .astgen = astgen, .force_comptime = true, - .in_defer = false, .instructions = gz.instructions, .instructions_top = gz.instructions.items.len, }; @@ -4489,7 +4472,6 @@ fn unionDeclInner( .decl_line = astgen.source_line, .astgen = astgen, .force_comptime = true, - .in_defer = false, .instructions = gz.instructions, .instructions_top = gz.instructions.items.len, }; @@ -4773,7 +4755,6 @@ fn containerDecl( .decl_line = astgen.source_line, .astgen = astgen, .force_comptime = true, - .in_defer = false, .instructions = gz.instructions, .instructions_top = gz.instructions.items.len, }; @@ -4879,7 +4860,6 @@ fn containerDecl( .decl_line = astgen.source_line, .astgen = astgen, .force_comptime = true, - .in_defer = false, .instructions = gz.instructions, .instructions_top = gz.instructions.items.len, }; @@ -5110,7 +5090,15 @@ fn tryExpr( return astgen.failNode(node, "'try' outside function scope", .{}); }; - if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{}); + if (parent_gz.any_defer_node != 0) { + return astgen.failNodeNotes(node, "'try' not allowed inside defer expression", .{}, &.{ + try astgen.errNoteNode( + parent_gz.any_defer_node, + "defer expression here", + .{}, + ), + }); + } // Ensure debug line/column information is emitted for this try expression. // 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 return astgen.failNode(node, "'return' outside function scope", .{}); } - if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{}); + if (gz.any_defer_node != 0) { + return astgen.failNodeNotes(node, "cannot return from defer expression", .{}, &.{ + try astgen.errNoteNode( + gz.any_defer_node, + "defer expression here", + .{}, + ), + }); + } // Ensure debug line/column information is emitted for this return expression. // 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 // for detecting whether to add something to the function's inferred error set. const ident_token = node_datas[operand_node].rhs; const err_name_str_index = try astgen.identAsString(ident_token); - const defer_counts = countDefers(astgen, defer_outer, scope); + const defer_counts = countDefers(defer_outer, scope); if (!defer_counts.need_err_code) { try genDefers(gz, defer_outer, scope, .both_sans_err); 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 return Zir.Inst.Ref.unreachable_value; }, .maybe => { - const defer_counts = countDefers(astgen, defer_outer, scope); + const defer_counts = countDefers(defer_outer, scope); if (!defer_counts.have_err) { // Only regular defers; no branch needed. try genDefers(gz, defer_outer, scope, .normal_only); @@ -6921,7 +6917,6 @@ fn localVarRef( }, .gen_zir => s = s.cast(GenZir).?.parent, .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, - .defer_gen => s = s.cast(Scope.DeferGen).?.parent, .namespace => { const ns = s.cast(Scope.Namespace).?; if (ns.decls.get(name_str_index)) |i| { @@ -7550,7 +7545,6 @@ fn builtinCall( }, .gen_zir => s = s.cast(GenZir).?.parent, .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, - .defer_gen => s = s.cast(Scope.DeferGen).?.parent, .namespace => { const ns = s.cast(Scope.Namespace).?; if (ns.decls.get(decl_name)) |i| { @@ -10046,7 +10040,6 @@ const Scope = struct { local_ptr, defer_normal, defer_error, - defer_gen, namespace, top, }; @@ -10105,10 +10098,9 @@ const Scope = struct { base: Scope, /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`. parent: *Scope, - defer_node: Ast.Node.Index, - source_offset: u32, - source_line: u32, - source_column: u32, + index: u32, + len: u32, + remapped_err_code: Zir.Inst.Index = 0, }; /// Represents a global scope that has any number of declarations in it. @@ -10144,13 +10136,6 @@ const Scope = struct { const base_tag: Scope.Tag = .top; base: Scope = Scope{ .tag = base_tag }, }; - - const DeferGen = struct { - const base_tag: Scope.Tag = .defer_gen; - base: Scope = Scope{ .tag = base_tag }, - parent: *Scope, - defer_node: Ast.Node.Index, - }; }; /// This is a temporary structure; references to it are valid only @@ -10161,7 +10146,6 @@ const GenZir = struct { force_comptime: bool, /// This is set to true for inline loops; false otherwise. is_inline: bool = false, - in_defer: bool, c_import: bool = false, /// How decls created in this scope should be named. anon_name_strategy: Zir.Inst.NameStrategy = .anon, @@ -10204,6 +10188,10 @@ const GenZir = struct { suspend_node: Ast.Node.Index = 0, nosuspend_node: Ast.Node.Index = 0, + /// Set if this GenZir is a defer. + cur_defer_node: Ast.Node.Index = 0, + // Set if this GenZir is a defer or it is inside a defer. + any_defer_node: Ast.Node.Index = 0, /// Namespace members are lazy. When executing a decl within a namespace, /// any references to external instructions need to be treated specially. @@ -10244,7 +10232,6 @@ const GenZir = struct { fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir { return .{ .force_comptime = gz.force_comptime, - .in_defer = gz.in_defer, .c_import = gz.c_import, .decl_node_index = gz.decl_node_index, .decl_line = gz.decl_line, @@ -10253,6 +10240,7 @@ const GenZir = struct { .astgen = gz.astgen, .suspend_node = gz.suspend_node, .nosuspend_node = gz.nosuspend_node, + .any_defer_node = gz.any_defer_node, .instructions = gz.instructions, .instructions_top = gz.instructions.items.len, }; @@ -11107,6 +11095,16 @@ const GenZir = struct { }); } + fn addDefer(gz: *GenZir, index: u32, len: u32) !void { + _ = try gz.add(.{ + .tag = .@"defer", + .data = .{ .@"defer" = .{ + .index = index, + .len = len, + } }, + }); + } + fn addDecl( gz: *GenZir, tag: Zir.Inst.Tag, @@ -11670,7 +11668,6 @@ fn detectLocalShadowing( }, .gen_zir => s = s.cast(GenZir).?.parent, .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, - .defer_gen => s = s.cast(Scope.DeferGen).?.parent, .top => break, }; } @@ -11809,7 +11806,6 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast. .namespace => s = s.cast(Scope.Namespace).?.parent, .gen_zir => s = s.cast(GenZir).?.parent, .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent, - .defer_gen => s = s.cast(Scope.DeferGen).?.parent, .top => break, }; gop.value_ptr.* = member_node; diff --git a/src/Sema.zig b/src/Sema.zig index 15e891ef87..f5e4402ea8 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1461,6 +1461,29 @@ fn analyzeBodyInner( // break break_data.inst; // } //}, + .@"defer" => blk: { + const inst_data = sema.code.instructions.items(.data)[inst].@"defer"; + const defer_body = sema.code.extra[inst_data.index..][0..inst_data.len]; + const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) { + error.ComptimeBreak => sema.comptime_break_inst, + else => |e| return e, + }; + if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn; + break :blk Air.Inst.Ref.void_value; + }, + .defer_err_code => blk: { + const inst_data = sema.code.instructions.items(.data)[inst].defer_err_code; + const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data; + const defer_body = sema.code.extra[extra.index..][0..extra.len]; + const err_code = try sema.resolveInst(inst_data.err_code); + try sema.inst_map.put(sema.gpa, extra.remapped_err_code, err_code); + const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) { + error.ComptimeBreak => sema.comptime_break_inst, + else => |e| return e, + }; + if (break_inst != defer_body[defer_body.len - 1]) break always_noreturn; + break :blk Air.Inst.Ref.void_value; + }, }; if (sema.typeOf(air_inst).isNoReturn()) break always_noreturn; @@ -9394,11 +9417,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError } if (special_prong == .@"else" and seen_errors.count() == operand_ty.errorSetNames().len) { - - // TODO re-enable if defer implementation is improved - // https://github.com/ziglang/zig/issues/11798 - if (true) break :else_validation; - // In order to enable common patterns for generic code allow simple else bodies // else => unreachable, // else => return, @@ -9415,6 +9433,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError .as_node, .ret_node, .@"unreachable", + .@"defer", + .defer_err_code, + .err_union_code, + .ret_err_value_code, + .is_non_err, + .condbr, => {}, else => break, } else break :else_validation; diff --git a/src/Zir.zig b/src/Zir.zig index 3ce086e10b..9621aefcf6 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -995,6 +995,13 @@ pub const Inst = struct { /// closure_capture instruction ref. closure_get, + /// A defer statement. + /// Uses the `defer` union field. + @"defer", + /// An errdefer statement with a code. + /// Uses the `err_defer_code` union field. + defer_err_code, + /// The ZIR instruction tag is one of the `Extended` ones. /// Uses the `extended` union field. extended, @@ -1244,6 +1251,8 @@ pub const Inst = struct { .try_ptr, //.try_inline, //.try_ptr_inline, + .@"defer", + .defer_err_code, => false, .@"break", @@ -1311,6 +1320,8 @@ pub const Inst = struct { .memcpy, .memset, .check_comptime_control_flow, + .@"defer", + .defer_err_code, => true, .param, @@ -1819,6 +1830,9 @@ pub const Inst = struct { .closure_capture = .un_tok, .closure_get = .inst_node, + .@"defer" = .@"defer", + .defer_err_code = .defer_err_code, + .extended = .extended, }); }; @@ -2575,6 +2589,14 @@ pub const Inst = struct { return zir.nullTerminatedString(self.str); } }, + @"defer": struct { + index: u32, + len: u32, + }, + defer_err_code: struct { + err_code: Ref, + payload_index: u32, + }, // Make sure we don't accidentally add a field to make this union // bigger than expected. Note that in Debug builds, Zig is allowed @@ -2611,6 +2633,8 @@ pub const Inst = struct { dbg_stmt, inst_node, str_op, + @"defer", + defer_err_code, }; }; @@ -3550,6 +3574,12 @@ pub const Inst = struct { line: u32, column: u32, }; + + pub const DeferErrCode = struct { + remapped_err_code: Index, + index: u32, + len: u32, + }; }; pub const SpecialProng = enum { none, @"else", under }; diff --git a/src/print_zir.zig b/src/print_zir.zig index 976d012138..fb1d2960c4 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -446,6 +446,9 @@ const Writer = struct { .closure_get => try self.writeInstNode(stream, inst), + .@"defer" => try self.writeDefer(stream, inst), + .defer_err_code => try self.writeDeferErrCode(stream, inst), + .extended => try self.writeExtended(stream, inst), } } @@ -2364,6 +2367,26 @@ const Writer = struct { try stream.print("{d}, {d})", .{ inst_data.line + 1, inst_data.column + 1 }); } + fn writeDefer(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].@"defer"; + const body = self.code.extra[inst_data.index..][0..inst_data.len]; + try self.writeBracedBody(stream, body); + try stream.writeByte(')'); + } + + fn writeDeferErrCode(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].defer_err_code; + const extra = self.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data; + + try self.writeInstRef(stream, Zir.indexToRef(extra.remapped_err_code)); + try stream.writeAll(" = "); + try self.writeInstRef(stream, inst_data.err_code); + try stream.writeAll(", "); + const body = self.code.extra[extra.index..][0..extra.len]; + try self.writeBracedBody(stream, body); + try stream.writeByte(')'); + } + fn writeInstRef(self: *Writer, stream: anytype, ref: Zir.Inst.Ref) !void { var i: usize = @enumToInt(ref); |
