authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-06-02 16:50:33-07:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-05 10:37:08+02:00
log0224ad19b82bb307a2c246f2e30826af599aa895
treee620d0d10e6d406ec5da5cb8e6f63ad520bdfe6e
parent33826a6a2e035d2a2be65314ed80a6b7abaf7f12

AstGen: introduce `try` instruction

This introduces two ZIR instructions: * `try` * `try_inline` This is part of an effort to implement #11772.

4 files changed, 150 insertions(+), 67 deletions(-)

src/AstGen.zig+33-53
...@@ -2425,6 +2425,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner...@@ -2425,6 +2425,8 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
2425 .param_type,2425 .param_type,
2426 .ret_ptr,2426 .ret_ptr,
2427 .ret_type,2427 .ret_type,
2428 .@"try",
2429 .try_inline,
2428 => break :b false,2430 => break :b false,
24292431
2430 .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {2432 .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {
...@@ -4871,68 +4873,30 @@ fn tryExpr(...@@ -4871,68 +4873,30 @@ fn tryExpr(
48714873
4872 if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{});4874 if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{});
48734875
4874 var block_scope = parent_gz.makeSubBlock(scope);4876 const operand_rl: ResultLoc = switch (rl) {
4875 block_scope.setBreakResultLoc(rl);
4876 defer block_scope.unstack();
4877
4878 const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {
4879 .ref => .ref,4877 .ref => .ref,
4880 else => .none,4878 else => .none,
4881 };4879 };
4882 const err_ops = switch (operand_rl) {4880 // This could be a pointer or value depending on the `rl` parameter.
4883 // zig fmt: off4881 const operand = try expr(parent_gz, scope, operand_rl, operand_node);
4884 .ref => [3]Zir.Inst.Tag{ .is_non_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr },4882 const is_inline = parent_gz.force_comptime;
4885 else => [3]Zir.Inst.Tag{ .is_non_err, .err_union_code, .err_union_payload_unsafe },4883 const block_tag: Zir.Inst.Tag = if (is_inline) .try_inline else .@"try";
4886 // zig fmt: on4884 const try_inst = try parent_gz.makeBlockInst(block_tag, node);
4887 };4885 try parent_gz.instructions.append(astgen.gpa, try_inst);
4888 // This could be a pointer or value depending on the `operand_rl` parameter.
4889 // We cannot use `block_scope.break_result_loc` because that has the bare
4890 // type, whereas this expression has the optional type. Later we make
4891 // up for this fact by calling rvalue on the else branch.
4892 const operand = try expr(&block_scope, &block_scope.base, operand_rl, operand_node);
4893 const cond = try block_scope.addUnNode(err_ops[0], operand, node);
4894 const condbr = try block_scope.addCondBr(.condbr, node);
48954886
4896 const block = try parent_gz.makeBlockInst(.block, node);
4897 try block_scope.setBlockBody(block);
4898 // block_scope unstacked now, can add new instructions to parent_gz
4899 try parent_gz.instructions.append(astgen.gpa, block);
4900
4901 var then_scope = parent_gz.makeSubBlock(scope);
4902 defer then_scope.unstack();
4903
4904 block_scope.break_count += 1;
4905 // This could be a pointer or value depending on `err_ops[2]`.
4906 const unwrapped_payload = try then_scope.addUnNode(err_ops[2], operand, node);
4907 const then_result = switch (rl) {
4908 .ref => unwrapped_payload,
4909 else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node),
4910 };
4911
4912 // else_scope will be stacked on then_scope as both are stacked on parent_gz
4913 var else_scope = parent_gz.makeSubBlock(scope);4887 var else_scope = parent_gz.makeSubBlock(scope);
4914 defer else_scope.unstack();4888 defer else_scope.unstack();
49154889
4916 const err_code = try else_scope.addUnNode(err_ops[1], operand, node);4890 const err_tag = switch (rl) {
4891 .ref => Zir.Inst.Tag.err_union_code_ptr,
4892 else => Zir.Inst.Tag.err_union_code,
4893 };
4894 const err_code = try else_scope.addUnNode(err_tag, operand, node);
4917 try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });4895 try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
4918 const else_result = try else_scope.addUnNode(.ret_node, err_code, node);4896 _ = try else_scope.addUnNode(.ret_node, err_code, node);
49194897
4920 const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";4898 try else_scope.setTryBody(try_inst, operand);
4921 return finishThenElseBlock(4899 return indexToRef(try_inst);
4922 parent_gz,
4923 rl,
4924 node,
4925 &block_scope,
4926 &then_scope,
4927 &else_scope,
4928 condbr,
4929 cond,
4930 then_result,
4931 else_result,
4932 block,
4933 block,
4934 break_tag,
4935 );
4936}4900}
49374901
4938fn orelseCatchExpr(4902fn orelseCatchExpr(
...@@ -10011,6 +9975,22 @@ const GenZir = struct {...@@ -10011,6 +9975,22 @@ const GenZir = struct {
10011 gz.unstack();9975 gz.unstack();
10012 }9976 }
100139977
9978 /// Assumes nothing stacked on `gz`. Unstacks `gz`.
9979 fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void {
9980 const gpa = gz.astgen.gpa;
9981 const body = gz.instructionsSlice();
9982 try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Try).Struct.fields.len + body.len);
9983 const zir_datas = gz.astgen.instructions.items(.data);
9984 zir_datas[inst].pl_node.payload_index = gz.astgen.addExtraAssumeCapacity(
9985 Zir.Inst.Try{
9986 .operand = operand,
9987 .body_len = @intCast(u32, body.len),
9988 },
9989 );
9990 gz.astgen.extra.appendSliceAssumeCapacity(body);
9991 gz.unstack();
9992 }
9993
10014 /// Must be called with the following stack set up:9994 /// Must be called with the following stack set up:
10015 /// * gz (bottom)9995 /// * gz (bottom)
10016 /// * align_gz9996 /// * align_gz
src/Sema.zig+63-8
...@@ -1322,6 +1322,13 @@ fn analyzeBodyInner(...@@ -1322,6 +1322,13 @@ fn analyzeBodyInner(
1322 break break_data.inst;1322 break break_data.inst;
1323 }1323 }
1324 },1324 },
1325 .@"try" => blk: {
1326 if (!block.is_comptime) break :blk try sema.zirTry(block, inst);
1327 @panic("TODO");
1328 },
1329 .try_inline => {
1330 @panic("TODO");
1331 },
1325 };1332 };
1326 if (sema.typeOf(air_inst).isNoReturn())1333 if (sema.typeOf(air_inst).isNoReturn())
1327 break always_noreturn;1334 break always_noreturn;
...@@ -6415,32 +6422,43 @@ fn zirErrUnionPayload(...@@ -6415,32 +6422,43 @@ fn zirErrUnionPayload(
6415 const src = inst_data.src();6422 const src = inst_data.src();
6416 const operand = try sema.resolveInst(inst_data.operand);6423 const operand = try sema.resolveInst(inst_data.operand);
6417 const operand_src = src;6424 const operand_src = src;
6418 const operand_ty = sema.typeOf(operand);6425 const err_union_ty = sema.typeOf(operand);
6419 if (operand_ty.zigTypeTag() != .ErrorUnion) {6426 if (err_union_ty.zigTypeTag() != .ErrorUnion) {
6420 return sema.fail(block, operand_src, "expected error union type, found '{}'", .{6427 return sema.fail(block, operand_src, "expected error union type, found '{}'", .{
6421 operand_ty.fmt(sema.mod),6428 err_union_ty.fmt(sema.mod),
6422 });6429 });
6423 }6430 }
6431 return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, safety_check);
6432}
64246433
6425 const result_ty = operand_ty.errorUnionPayload();6434fn analyzeErrUnionPayload(
6426 if (try sema.resolveDefinedValue(block, src, operand)) |val| {6435 sema: *Sema,
6436 block: *Block,
6437 src: LazySrcLoc,
6438 err_union_ty: Type,
6439 operand: Zir.Inst.Ref,
6440 operand_src: LazySrcLoc,
6441 safety_check: bool,
6442) CompileError!Air.Inst.Ref {
6443 const payload_ty = err_union_ty.errorUnionPayload();
6444 if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| {
6427 if (val.getError()) |name| {6445 if (val.getError()) |name| {
6428 return sema.fail(block, src, "caught unexpected error '{s}'", .{name});6446 return sema.fail(block, src, "caught unexpected error '{s}'", .{name});
6429 }6447 }
6430 const data = val.castTag(.eu_payload).?.data;6448 const data = val.castTag(.eu_payload).?.data;
6431 return sema.addConstant(result_ty, data);6449 return sema.addConstant(payload_ty, data);
6432 }6450 }
64336451
6434 try sema.requireRuntimeBlock(block, src);6452 try sema.requireRuntimeBlock(block, src);
64356453
6436 // If the error set has no fields then no safety check is needed.6454 // If the error set has no fields then no safety check is needed.
6437 if (safety_check and block.wantSafety() and6455 if (safety_check and block.wantSafety() and
6438 operand_ty.errorUnionSet().errorSetCardinality() != .zero)6456 err_union_ty.errorUnionSet().errorSetCardinality() != .zero)
6439 {6457 {
6440 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);6458 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
6441 }6459 }
64426460
6443 return block.addTyOp(.unwrap_errunion_payload, result_ty, operand);6461 return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand);
6444}6462}
64456463
6446/// Pointer in, pointer out.6464/// Pointer in, pointer out.
...@@ -12958,6 +12976,43 @@ fn zirCondbr(...@@ -12958,6 +12976,43 @@ fn zirCondbr(
12958 return always_noreturn;12976 return always_noreturn;
12959}12977}
1296012978
12979fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Ref {
12980 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
12981 const src = inst_data.src();
12982 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
12983 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
12984 const body = sema.code.extra[extra.end..][0..extra.data.body_len];
12985 const operand = try sema.resolveInst(extra.data.operand);
12986 const is_ptr = sema.typeOf(operand).zigTypeTag() == .Pointer;
12987 const err_union = if (is_ptr)
12988 try sema.analyzeLoad(parent_block, src, operand, operand_src)
12989 else
12990 operand;
12991 const err_union_ty = sema.typeOf(err_union);
12992 if (err_union_ty.zigTypeTag() != .ErrorUnion) {
12993 return sema.fail(parent_block, operand_src, "expected error union type, found '{}'", .{
12994 err_union_ty.fmt(sema.mod),
12995 });
12996 }
12997 const is_non_err = try sema.analyzeIsNonErr(parent_block, operand_src, err_union);
12998
12999 if (try sema.resolveDefinedValue(parent_block, operand_src, is_non_err)) |is_non_err_val| {
13000 if (is_non_err_val.toBool()) {
13001 if (is_ptr) {
13002 return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false);
13003 } else {
13004 return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, operand, operand_src, false);
13005 }
13006 }
13007 // We can analyze the body directly in the parent block because we know there are
13008 // no breaks from the body possible, and that the body is noreturn.
13009 return sema.resolveBody(parent_block, body, inst);
13010 }
13011 _ = body;
13012 _ = is_non_err;
13013 @panic("TODO");
13014}
13015
12961// A `break` statement is inside a runtime condition, but trying to13016// A `break` statement is inside a runtime condition, but trying to
12962// break from an inline loop. In such case we must convert it to13017// break from an inline loop. In such case we must convert it to
12963// a runtime break.13018// a runtime break.
src/Zir.zig+33
...@@ -319,6 +319,19 @@ pub const Inst = struct {...@@ -319,6 +319,19 @@ pub const Inst = struct {
319 /// only the taken branch is analyzed. The then block and else block must319 /// only the taken branch is analyzed. The then block and else block must
320 /// terminate with an "inline" variant of a noreturn instruction.320 /// terminate with an "inline" variant of a noreturn instruction.
321 condbr_inline,321 condbr_inline,
322 /// Given an operand which is an error union, splits control flow. In
323 /// case of error, control flow goes into the block that is part of this
324 /// instruction, which is guaranteed to end with a return instruction
325 /// and never breaks out of the block.
326 /// In the case of non-error, control flow proceeds to the next instruction
327 /// after the `try`, with the result of this instruction being the unwrapped
328 /// payload value, as if `err_union_payload_unsafe` was executed on the operand.
329 /// Uses the `pl_node` union field. Payload is `Try`.
330 @"try",
331 /// Same as `try` except the operand is coerced to a comptime value, and
332 /// only the taken branch is analyzed. The block must terminate with an "inline"
333 /// variant of a noreturn instruction.
334 try_inline,
322 /// An error set type definition. Contains a list of field names.335 /// An error set type definition. Contains a list of field names.
323 /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`.336 /// Uses the `pl_node` union field. Payload is `ErrorSetDecl`.
324 error_set_decl,337 error_set_decl,
...@@ -1231,6 +1244,8 @@ pub const Inst = struct {...@@ -1231,6 +1244,8 @@ pub const Inst = struct {
1231 .closure_capture,1244 .closure_capture,
1232 .ret_ptr,1245 .ret_ptr,
1233 .ret_type,1246 .ret_type,
1247 .@"try",
1248 .try_inline,
1234 => false,1249 => false,
12351250
1236 .@"break",1251 .@"break",
...@@ -1509,6 +1524,8 @@ pub const Inst = struct {...@@ -1509,6 +1524,8 @@ pub const Inst = struct {
1509 .repeat,1524 .repeat,
1510 .repeat_inline,1525 .repeat_inline,
1511 .panic,1526 .panic,
1527 .@"try",
1528 .try_inline,
1512 => false,1529 => false,
15131530
1514 .extended => switch (data.extended.opcode) {1531 .extended => switch (data.extended.opcode) {
...@@ -1569,6 +1586,8 @@ pub const Inst = struct {...@@ -1569,6 +1586,8 @@ pub const Inst = struct {
1569 .coerce_result_ptr = .bin,1586 .coerce_result_ptr = .bin,
1570 .condbr = .pl_node,1587 .condbr = .pl_node,
1571 .condbr_inline = .pl_node,1588 .condbr_inline = .pl_node,
1589 .@"try" = .pl_node,
1590 .try_inline = .pl_node,
1572 .error_set_decl = .pl_node,1591 .error_set_decl = .pl_node,
1573 .error_set_decl_anon = .pl_node,1592 .error_set_decl_anon = .pl_node,
1574 .error_set_decl_func = .pl_node,1593 .error_set_decl_func = .pl_node,
...@@ -2803,6 +2822,14 @@ pub const Inst = struct {...@@ -2803,6 +2822,14 @@ pub const Inst = struct {
2803 else_body_len: u32,2822 else_body_len: u32,
2804 };2823 };
28052824
2825 /// This data is stored inside extra, trailed by:
2826 /// * 0. body: Index // for each `body_len`.
2827 pub const Try = struct {
2828 /// The error union to unwrap.
2829 operand: Ref,
2830 body_len: u32,
2831 };
2832
2806 /// Stored in extra. Depending on the flags in Data, there will be up to 52833 /// Stored in extra. Depending on the flags in Data, there will be up to 5
2807 /// trailing Ref fields:2834 /// trailing Ref fields:
2808 /// 0. sentinel: Ref // if `has_sentinel` flag is set2835 /// 0. sentinel: Ref // if `has_sentinel` flag is set
...@@ -3739,6 +3766,12 @@ fn findDeclsInner(...@@ -3739,6 +3766,12 @@ fn findDeclsInner(
3739 try zir.findDeclsBody(list, then_body);3766 try zir.findDeclsBody(list, then_body);
3740 try zir.findDeclsBody(list, else_body);3767 try zir.findDeclsBody(list, else_body);
3741 },3768 },
3769 .@"try", .try_inline => {
3770 const inst_data = datas[inst].pl_node;
3771 const extra = zir.extraData(Inst.Try, inst_data.payload_index);
3772 const body = zir.extra[extra.end..][0..extra.data.body_len];
3773 try zir.findDeclsBody(list, body);
3774 },
3742 .switch_block => return findDeclsSwitch(zir, list, inst),3775 .switch_block => return findDeclsSwitch(zir, list, inst),
37433776
3744 .suspend_block => @panic("TODO iterate suspend block"),3777 .suspend_block => @panic("TODO iterate suspend block"),
src/print_zir.zig+21-6
...@@ -374,17 +374,21 @@ const Writer = struct {...@@ -374,17 +374,21 @@ const Writer = struct {
374 .validate_array_init_comptime,374 .validate_array_init_comptime,
375 .c_import,375 .c_import,
376 .typeof_builtin,376 .typeof_builtin,
377 => try self.writePlNodeBlock(stream, inst),377 => try self.writeBlock(stream, inst),
378378
379 .condbr,379 .condbr,
380 .condbr_inline,380 .condbr_inline,
381 => try self.writePlNodeCondBr(stream, inst),381 => try self.writeCondBr(stream, inst),
382
383 .@"try",
384 .try_inline,
385 => try self.writeTry(stream, inst),
382386
383 .error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent),387 .error_set_decl => try self.writeErrorSetDecl(stream, inst, .parent),
384 .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),388 .error_set_decl_anon => try self.writeErrorSetDecl(stream, inst, .anon),
385 .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),389 .error_set_decl_func => try self.writeErrorSetDecl(stream, inst, .func),
386390
387 .switch_block => try self.writePlNodeSwitchBlock(stream, inst),391 .switch_block => try self.writeSwitchBlock(stream, inst),
388392
389 .field_ptr,393 .field_ptr,
390 .field_val,394 .field_val,
...@@ -1171,7 +1175,7 @@ const Writer = struct {...@@ -1171,7 +1175,7 @@ const Writer = struct {
1171 try self.writeSrc(stream, inst_data.src());1175 try self.writeSrc(stream, inst_data.src());
1172 }1176 }
11731177
1174 fn writePlNodeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {1178 fn writeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1175 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1179 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1176 try self.writePlNodeBlockWithoutSrc(stream, inst);1180 try self.writePlNodeBlockWithoutSrc(stream, inst);
1177 try self.writeSrc(stream, inst_data.src());1181 try self.writeSrc(stream, inst_data.src());
...@@ -1185,7 +1189,7 @@ const Writer = struct {...@@ -1185,7 +1189,7 @@ const Writer = struct {
1185 try stream.writeAll(") ");1189 try stream.writeAll(") ");
1186 }1190 }
11871191
1188 fn writePlNodeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {1192 fn writeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1189 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1193 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1190 const extra = self.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);1194 const extra = self.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
1191 const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len];1195 const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len];
...@@ -1199,6 +1203,17 @@ const Writer = struct {...@@ -1199,6 +1203,17 @@ const Writer = struct {
1199 try self.writeSrc(stream, inst_data.src());1203 try self.writeSrc(stream, inst_data.src());
1200 }1204 }
12011205
1206 fn writeTry(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1207 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1208 const extra = self.code.extraData(Zir.Inst.Try, inst_data.payload_index);
1209 const body = self.code.extra[extra.end..][0..extra.data.body_len];
1210 try self.writeInstRef(stream, extra.data.operand);
1211 try stream.writeAll(", ");
1212 try self.writeBracedBody(stream, body);
1213 try stream.writeAll(") ");
1214 try self.writeSrc(stream, inst_data.src());
1215 }
1216
1202 fn writeStructDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {1217 fn writeStructDecl(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
1203 const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);1218 const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
12041219
...@@ -1746,7 +1761,7 @@ const Writer = struct {...@@ -1746,7 +1761,7 @@ const Writer = struct {
1746 try self.writeSrc(stream, inst_data.src());1761 try self.writeSrc(stream, inst_data.src());
1747 }1762 }
17481763
1749 fn writePlNodeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {1764 fn writeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
1750 const inst_data = self.code.instructions.items(.data)[inst].pl_node;1765 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
1751 const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);1766 const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
17521767