| author | |
| committer | |
| log | 9e683f0f35e21c6dce127a46c9d8c3da2d23744d |
| tree | 00d1d4ac4b3757cd45068df5272a9dcbbac6c165 |
| parent | fbac7afa0f280eb271e802046ae1f3f836a9f6b2 |
| signature |
This is mainly useful in conjunction with Decl Literals (#9938).
Resolves: #197775 files changed, 122 insertions(+), 3 deletions(-)
lib/std/zig/AstGen.zig+14-3| ... | ... | @@ -2914,6 +2914,8 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As |
| 2914 | 2914 | .validate_array_init_result_ty, |
| 2915 | 2915 | .validate_ptr_array_init, |
| 2916 | 2916 | .validate_ref_ty, |
| 2917 | .try_operand_ty, | |
| 2918 | .try_ref_operand_ty, | |
| 2917 | 2919 | => break :b true, |
| 2918 | 2920 | |
| 2919 | 2921 | .@"defer" => unreachable, |
| ... | ... | @@ -5887,9 +5889,18 @@ fn tryExpr( |
| 5887 | 5889 | } |
| 5888 | 5890 | const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column }; |
| 5889 | 5891 | |
| 5890 | const operand_ri: ResultInfo = switch (ri.rl) { | |
| 5891 | .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = .error_handling_expr }, | |
| 5892 | else => .{ .rl = .none, .ctx = .error_handling_expr }, | |
| 5892 | const operand_ri: ResultInfo = .{ | |
| 5893 | .rl = switch (ri.rl) { | |
| 5894 | .ref => .ref, | |
| 5895 | .ref_coerced_ty => |payload_ptr_ty| .{ | |
| 5896 | .ref_coerced_ty = try parent_gz.addUnNode(.try_ref_operand_ty, payload_ptr_ty, node), | |
| 5897 | }, | |
| 5898 | else => if (try ri.rl.resultType(parent_gz, node)) |payload_ty| .{ | |
| 5899 | // `coerced_ty` is OK due to the `rvalue` call below | |
| 5900 | .coerced_ty = try parent_gz.addUnNode(.try_operand_ty, payload_ty, node), | |
| 5901 | } else .none, | |
| 5902 | }, | |
| 5903 | .ctx = .error_handling_expr, | |
| 5893 | 5904 | }; |
| 5894 | 5905 | // This could be a pointer or value depending on the `ri` parameter. |
| 5895 | 5906 | const operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, node); |
lib/std/zig/Zir.zig+16| ... | ... | @@ -684,6 +684,14 @@ pub const Inst = struct { |
| 684 | 684 | /// operator. Emit a compile error if not. |
| 685 | 685 | /// Uses the `un_tok` union field. Token is the `&` operator. Operand is the type. |
| 686 | 686 | validate_ref_ty, |
| 687 | /// Given a type `T`, construct the type `E!T`, where `E` is this function's error set, to be used | |
| 688 | /// as the result type of a `try` operand. Generic poison is propagated. | |
| 689 | /// Uses the `un_node` union field. Node is the `try` expression. Operand is the type `T`. | |
| 690 | try_operand_ty, | |
| 691 | /// Given a type `*T`, construct the type `*E!T`, where `E` is this function's error set, to be used | |
| 692 | /// as the result type of a `try` operand whose address is taken with `&`. Generic poison is propagated. | |
| 693 | /// Uses the `un_node` union field. Node is the `try` expression. Operand is the type `*T`. | |
| 694 | try_ref_operand_ty, | |
| 687 | 695 | |
| 688 | 696 | // The following tags all relate to struct initialization expressions. |
| 689 | 697 | |
| ... | ... | @@ -1254,6 +1262,8 @@ pub const Inst = struct { |
| 1254 | 1262 | .array_init_elem_type, |
| 1255 | 1263 | .array_init_elem_ptr, |
| 1256 | 1264 | .validate_ref_ty, |
| 1265 | .try_operand_ty, | |
| 1266 | .try_ref_operand_ty, | |
| 1257 | 1267 | .restore_err_ret_index_unconditional, |
| 1258 | 1268 | .restore_err_ret_index_fn_entry, |
| 1259 | 1269 | => false, |
| ... | ... | @@ -1324,6 +1334,8 @@ pub const Inst = struct { |
| 1324 | 1334 | .validate_array_init_result_ty, |
| 1325 | 1335 | .validate_ptr_array_init, |
| 1326 | 1336 | .validate_ref_ty, |
| 1337 | .try_operand_ty, | |
| 1338 | .try_ref_operand_ty, | |
| 1327 | 1339 | => true, |
| 1328 | 1340 | |
| 1329 | 1341 | .param, |
| ... | ... | @@ -1698,6 +1710,8 @@ pub const Inst = struct { |
| 1698 | 1710 | .opt_eu_base_ptr_init = .un_node, |
| 1699 | 1711 | .coerce_ptr_elem_ty = .pl_node, |
| 1700 | 1712 | .validate_ref_ty = .un_tok, |
| 1713 | .try_operand_ty = .un_node, | |
| 1714 | .try_ref_operand_ty = .un_node, | |
| 1701 | 1715 | |
| 1702 | 1716 | .int_from_ptr = .un_node, |
| 1703 | 1717 | .compile_error = .un_node, |
| ... | ... | @@ -3834,6 +3848,8 @@ fn findDeclsInner( |
| 3834 | 3848 | .opt_eu_base_ptr_init, |
| 3835 | 3849 | .coerce_ptr_elem_ty, |
| 3836 | 3850 | .validate_ref_ty, |
| 3851 | .try_operand_ty, | |
| 3852 | .try_ref_operand_ty, | |
| 3837 | 3853 | .struct_init_empty, |
| 3838 | 3854 | .struct_init_empty_result, |
| 3839 | 3855 | .struct_init_empty_ref_result, |
src/Sema.zig+71| ... | ... | @@ -1177,6 +1177,8 @@ fn analyzeBodyInner( |
| 1177 | 1177 | .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst), |
| 1178 | 1178 | .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst), |
| 1179 | 1179 | .coerce_ptr_elem_ty => try sema.zirCoercePtrElemTy(block, inst), |
| 1180 | .try_operand_ty => try sema.zirTryOperandTy(block, inst, false), | |
| 1181 | .try_ref_operand_ty => try sema.zirTryOperandTy(block, inst, true), | |
| 1180 | 1182 | |
| 1181 | 1183 | .clz => try sema.zirBitCount(block, inst, .clz, Value.clz), |
| 1182 | 1184 | .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz), |
| ... | ... | @@ -2024,6 +2026,22 @@ fn genericPoisonReason(sema: *Sema, block: *Block, ref: Zir.Inst.Ref) GenericPoi |
| 2024 | 2026 | const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; |
| 2025 | 2027 | cur = un_node.operand; |
| 2026 | 2028 | }, |
| 2029 | .try_operand_ty => { | |
| 2030 | // Either the input type was itself poison, or it was a slice, which we cannot translate | |
| 2031 | // to an overall result type. | |
| 2032 | const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; | |
| 2033 | const operand_ref = sema.resolveInst(un_node.operand) catch |err| switch (err) { | |
| 2034 | error.GenericPoison => unreachable, // this is a type, not a value | |
| 2035 | }; | |
| 2036 | if (operand_ref == .generic_poison_type) { | |
| 2037 | // The input was poison -- keep looking. | |
| 2038 | cur = un_node.operand; | |
| 2039 | continue; | |
| 2040 | } | |
| 2041 | // We got a poison because the result type was a slice. This is a tricky case -- let's just | |
| 2042 | // not bother explaining it to the user for now... | |
| 2043 | return .unknown; | |
| 2044 | }, | |
| 2027 | 2045 | .struct_init_field_type => { |
| 2028 | 2046 | const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node; |
| 2029 | 2047 | const extra = sema.code.extraData(Zir.Inst.FieldType, pl_node.payload_index).data; |
| ... | ... | @@ -4423,6 +4441,59 @@ fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE |
| 4423 | 4441 | } |
| 4424 | 4442 | } |
| 4425 | 4443 | |
| 4444 | fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { | |
| 4445 | const pt = sema.pt; | |
| 4446 | const zcu = pt.zcu; | |
| 4447 | const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; | |
| 4448 | const src = block.nodeOffset(un_node.src_node); | |
| 4449 | ||
| 4450 | const operand_ty = sema.resolveType(block, src, un_node.operand) catch |err| switch (err) { | |
| 4451 | error.GenericPoison => return .generic_poison_type, | |
| 4452 | else => |e| return e, | |
| 4453 | }; | |
| 4454 | ||
| 4455 | const payload_ty = if (is_ref) ty: { | |
| 4456 | if (!operand_ty.isSinglePointer(zcu)) { | |
| 4457 | return .generic_poison_type; // we can't get a meaningful result type here, since it will be `*E![n]T`, and we don't know `n`. | |
| 4458 | } | |
| 4459 | break :ty operand_ty.childType(zcu); | |
| 4460 | } else operand_ty; | |
| 4461 | ||
| 4462 | const err_set_ty = err_set: { | |
| 4463 | // There are awkward cases, like `?E`. Our strategy is to repeatedly unwrap optionals | |
| 4464 | // until we hit an error union or set. | |
| 4465 | var cur_ty = sema.fn_ret_ty; | |
| 4466 | while (true) { | |
| 4467 | switch (cur_ty.zigTypeTag(zcu)) { | |
| 4468 | .error_set => break :err_set cur_ty, | |
| 4469 | .error_union => break :err_set cur_ty.errorUnionSet(zcu), | |
| 4470 | .optional => cur_ty = cur_ty.optionalChild(zcu), | |
| 4471 | else => return sema.failWithOwnedErrorMsg(block, msg: { | |
| 4472 | const msg = try sema.errMsg(src, "expected '{}', found error set", .{sema.fn_ret_ty.fmt(pt)}); | |
| 4473 | errdefer msg.destroy(sema.gpa); | |
| 4474 | const ret_ty_src: LazySrcLoc = .{ | |
| 4475 | .base_node_inst = sema.getOwnerFuncDeclInst(), | |
| 4476 | .offset = .{ .node_offset_fn_type_ret_ty = 0 }, | |
| 4477 | }; | |
| 4478 | try sema.errNote(ret_ty_src, msg, "function cannot return an error", .{}); | |
| 4479 | break :msg msg; | |
| 4480 | }), | |
| 4481 | } | |
| 4482 | } | |
| 4483 | }; | |
| 4484 | ||
| 4485 | const eu_ty = try pt.errorUnionType(err_set_ty, payload_ty); | |
| 4486 | ||
| 4487 | if (is_ref) { | |
| 4488 | var ptr_info = operand_ty.ptrInfo(zcu); | |
| 4489 | ptr_info.child = eu_ty.toIntern(); | |
| 4490 | const eu_ptr_ty = try pt.ptrTypeSema(ptr_info); | |
| 4491 | return Air.internedToRef(eu_ptr_ty.toIntern()); | |
| 4492 | } else { | |
| 4493 | return Air.internedToRef(eu_ty.toIntern()); | |
| 4494 | } | |
| 4495 | } | |
| 4496 | ||
| 4426 | 4497 | fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 4427 | 4498 | const pt = sema.pt; |
| 4428 | 4499 | const zcu = pt.zcu; |
src/print_zir.zig+2| ... | ... | @@ -277,6 +277,8 @@ const Writer = struct { |
| 277 | 277 | .opt_eu_base_ptr_init, |
| 278 | 278 | .restore_err_ret_index_unconditional, |
| 279 | 279 | .restore_err_ret_index_fn_entry, |
| 280 | .try_operand_ty, | |
| 281 | .try_ref_operand_ty, | |
| 280 | 282 | => try self.writeUnNode(stream, inst), |
| 281 | 283 | |
| 282 | 284 | .ref, |
test/behavior/try.zig+19| ... | ... | @@ -67,3 +67,22 @@ test "`try`ing an if/else expression" { |
| 67 | 67 | |
| 68 | 68 | try std.testing.expectError(error.Test, S.getError2()); |
| 69 | 69 | } |
| 70 | ||
| 71 | test "try forwards result location" { | |
| 72 | if (builtin.zig_backend == .stage2_x86) return error.SkipZigTest; // TODO | |
| 73 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | |
| 74 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | |
| 75 | if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; | |
| 76 | if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; | |
| 77 | ||
| 78 | const S = struct { | |
| 79 | fn foo(err: bool) error{Foo}!u32 { | |
| 80 | const result: error{ Foo, Bar }!u32 = if (err) error.Foo else 123; | |
| 81 | const res_int: u32 = try @errorCast(result); | |
| 82 | return res_int; | |
| 83 | } | |
| 84 | }; | |
| 85 | ||
| 86 | try expect((S.foo(false) catch return error.TestUnexpectedResult) == 123); | |
| 87 | try std.testing.expectError(error.Foo, S.foo(true)); | |
| 88 | } |