| ... | @@ -14888,10 +14888,83 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir | ... | @@ -14888,10 +14888,83 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir |
| 14888 | const operand = try sema.analyzeLoad(block, src, ret_ptr, src); | 14888 | const operand = try sema.analyzeLoad(block, src, ret_ptr, src); |
| 14889 | return sema.analyzeRet(block, operand, src); | 14889 | return sema.analyzeRet(block, operand, src); |
| 14890 | } | 14890 | } |
| | 14891 | |
| | 14892 | if (sema.wantErrorReturnTracing()) { |
| | 14893 | const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr); |
| | 14894 | return retWithErrTracing(sema, block, src, is_non_err, .ret_load, ret_ptr); |
| | 14895 | } |
| | 14896 | |
| 14891 | _ = try block.addUnOp(.ret_load, ret_ptr); | 14897 | _ = try block.addUnOp(.ret_load, ret_ptr); |
| 14892 | return always_noreturn; | 14898 | return always_noreturn; |
| 14893 | } | 14899 | } |
| 14894 | | 14900 | |
| | 14901 | fn retWithErrTracing( |
| | 14902 | sema: *Sema, |
| | 14903 | block: *Block, |
| | 14904 | src: LazySrcLoc, |
| | 14905 | is_non_err: Air.Inst.Ref, |
| | 14906 | ret_tag: Air.Inst.Tag, |
| | 14907 | operand: Air.Inst.Ref, |
| | 14908 | ) CompileError!Zir.Inst.Index { |
| | 14909 | const need_check = switch (is_non_err) { |
| | 14910 | .bool_true => { |
| | 14911 | _ = try block.addUnOp(ret_tag, operand); |
| | 14912 | return always_noreturn; |
| | 14913 | }, |
| | 14914 | .bool_false => false, |
| | 14915 | else => true, |
| | 14916 | }; |
| | 14917 | const gpa = sema.gpa; |
| | 14918 | const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); |
| | 14919 | const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); |
| | 14920 | const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty); |
| | 14921 | const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); |
| | 14922 | const return_err_fn = try sema.getBuiltin(block, src, "returnError"); |
| | 14923 | const args: [1]Air.Inst.Ref = .{err_return_trace}; |
| | 14924 | |
| | 14925 | if (!need_check) { |
| | 14926 | _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null); |
| | 14927 | _ = try block.addUnOp(ret_tag, operand); |
| | 14928 | return always_noreturn; |
| | 14929 | } |
| | 14930 | |
| | 14931 | var then_block = block.makeSubBlock(); |
| | 14932 | defer then_block.instructions.deinit(gpa); |
| | 14933 | _ = try then_block.addUnOp(ret_tag, operand); |
| | 14934 | |
| | 14935 | var else_block = block.makeSubBlock(); |
| | 14936 | defer else_block.instructions.deinit(gpa); |
| | 14937 | _ = try sema.analyzeCall(&else_block, return_err_fn, src, src, .never_inline, false, &args, null); |
| | 14938 | _ = try else_block.addUnOp(ret_tag, operand); |
| | 14939 | |
| | 14940 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).Struct.fields.len + |
| | 14941 | then_block.instructions.items.len + else_block.instructions.items.len + |
| | 14942 | @typeInfo(Air.Block).Struct.fields.len + 1); |
| | 14943 | |
| | 14944 | const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ |
| | 14945 | .then_body_len = @intCast(u32, then_block.instructions.items.len), |
| | 14946 | .else_body_len = @intCast(u32, else_block.instructions.items.len), |
| | 14947 | }); |
| | 14948 | sema.air_extra.appendSliceAssumeCapacity(then_block.instructions.items); |
| | 14949 | sema.air_extra.appendSliceAssumeCapacity(else_block.instructions.items); |
| | 14950 | |
| | 14951 | _ = try block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ |
| | 14952 | .operand = is_non_err, |
| | 14953 | .payload = cond_br_payload, |
| | 14954 | } } }); |
| | 14955 | |
| | 14956 | return always_noreturn; |
| | 14957 | } |
| | 14958 | |
| | 14959 | fn wantErrorReturnTracing(sema: *Sema) bool { |
| | 14960 | // TODO implement this feature in all the backends and then delete this check. |
| | 14961 | const backend_supports_error_return_tracing = sema.mod.comp.bin_file.options.use_llvm; |
| | 14962 | |
| | 14963 | return sema.fn_ret_ty.isError() and |
| | 14964 | sema.mod.comp.bin_file.options.error_return_tracing and |
| | 14965 | backend_supports_error_return_tracing; |
| | 14966 | } |
| | 14967 | |
| 14895 | fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { | 14968 | fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { |
| 14896 | assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion); | 14969 | assert(sema.fn_ret_ty.zigTypeTag() == .ErrorUnion); |
| 14897 | | 14970 | |
| ... | @@ -14915,8 +14988,6 @@ fn analyzeRet( | ... | @@ -14915,8 +14988,6 @@ fn analyzeRet( |
| 14915 | uncasted_operand: Air.Inst.Ref, | 14988 | uncasted_operand: Air.Inst.Ref, |
| 14916 | src: LazySrcLoc, | 14989 | src: LazySrcLoc, |
| 14917 | ) CompileError!Zir.Inst.Index { | 14990 | ) CompileError!Zir.Inst.Index { |
| 14918 | const gpa = sema.gpa; | | |
| 14919 | | | |
| 14920 | // Special case for returning an error to an inferred error set; we need to | 14991 | // Special case for returning an error to an inferred error set; we need to |
| 14921 | // add the error tag to the inferred error set of the in-scope function, so | 14992 | // add the error tag to the inferred error set of the in-scope function, so |
| 14922 | // that the coercion below works correctly. | 14993 | // that the coercion below works correctly. |
| ... | @@ -14934,65 +15005,18 @@ fn analyzeRet( | ... | @@ -14934,65 +15005,18 @@ fn analyzeRet( |
| 14934 | return error.ComptimeReturn; | 15005 | return error.ComptimeReturn; |
| 14935 | } | 15006 | } |
| 14936 | // We are inlining a function call; rewrite the `ret` as a `break`. | 15007 | // We are inlining a function call; rewrite the `ret` as a `break`. |
| 14937 | try inlining.merges.results.append(gpa, operand); | 15008 | try inlining.merges.results.append(sema.gpa, operand); |
| 14938 | _ = try block.addBr(inlining.merges.block_inst, operand); | 15009 | _ = try block.addBr(inlining.merges.block_inst, operand); |
| 14939 | return always_noreturn; | 15010 | return always_noreturn; |
| 14940 | } | 15011 | } |
| 14941 | | 15012 | |
| 14942 | try sema.resolveTypeLayout(block, src, sema.fn_ret_ty); | 15013 | try sema.resolveTypeLayout(block, src, sema.fn_ret_ty); |
| 14943 | | 15014 | |
| 14944 | // TODO implement this feature in all the backends and then delete this check. | 15015 | if (sema.wantErrorReturnTracing()) { |
| 14945 | const backend_supports_error_return_tracing = | | |
| 14946 | sema.mod.comp.bin_file.options.use_llvm; | | |
| 14947 | | | |
| 14948 | if (sema.fn_ret_ty.isError() and | | |
| 14949 | sema.mod.comp.bin_file.options.error_return_tracing and | | |
| 14950 | backend_supports_error_return_tracing) | | |
| 14951 | ret_err: { | | |
| 14952 | // Avoid adding a frame to the error return trace in case the value is comptime-known | 15016 | // Avoid adding a frame to the error return trace in case the value is comptime-known |
| 14953 | // to be not an error. | 15017 | // to be not an error. |
| 14954 | const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, src, operand); | 15018 | const is_non_err = try sema.analyzeIsNonErr(block, src, operand); |
| 14955 | const need_check = switch (is_non_err) { | 15019 | return retWithErrTracing(sema, block, src, is_non_err, .ret, operand); |
| 14956 | .bool_true => break :ret_err, | | |
| 14957 | .bool_false => false, | | |
| 14958 | else => true, | | |
| 14959 | }; | | |
| 14960 | | | |
| 14961 | const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); | | |
| 14962 | const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); | | |
| 14963 | const ptr_stack_trace_ty = try Type.Tag.single_mut_pointer.create(sema.arena, stack_trace_ty); | | |
| 14964 | const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); | | |
| 14965 | const return_err_fn = try sema.getBuiltin(block, src, "returnError"); | | |
| 14966 | const args: [1]Air.Inst.Ref = .{err_return_trace}; | | |
| 14967 | | | |
| 14968 | if (!need_check) { | | |
| 14969 | _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null); | | |
| 14970 | break :ret_err; | | |
| 14971 | } | | |
| 14972 | | | |
| 14973 | const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len); | | |
| 14974 | try sema.air_instructions.append(gpa, .{ | | |
| 14975 | .tag = .block, | | |
| 14976 | .data = .{ .ty_pl = .{ | | |
| 14977 | .ty = .void_type, | | |
| 14978 | .payload = undefined, | | |
| 14979 | } }, | | |
| 14980 | }); | | |
| 14981 | | | |
| 14982 | var child_block = block.makeSubBlock(); | | |
| 14983 | defer child_block.instructions.deinit(gpa); | | |
| 14984 | | | |
| 14985 | var then_block = child_block.makeSubBlock(); | | |
| 14986 | defer then_block.instructions.deinit(gpa); | | |
| 14987 | _ = try then_block.addUnOp(.ret, operand); | | |
| 14988 | | | |
| 14989 | var else_block = child_block.makeSubBlock(); | | |
| 14990 | defer else_block.instructions.deinit(gpa); | | |
| 14991 | _ = try sema.analyzeCall(block, return_err_fn, src, src, .never_inline, false, &args, null); | | |
| 14992 | _ = try else_block.addUnOp(.ret, operand); | | |
| 14993 | | | |
| 14994 | _ = try finishCondBr(sema, block, &child_block, &then_block, &else_block, is_non_err, block_inst); | | |
| 14995 | return always_noreturn; | | |
| 14996 | } | 15020 | } |
| 14997 | | 15021 | |
| 14998 | _ = try block.addUnOp(.ret, operand); | 15022 | _ = try block.addUnOp(.ret, operand); |
| ... | @@ -25474,6 +25498,27 @@ fn analyzeIsNull( | ... | @@ -25474,6 +25498,27 @@ fn analyzeIsNull( |
| 25474 | return block.addUnOp(air_tag, operand); | 25498 | return block.addUnOp(air_tag, operand); |
| 25475 | } | 25499 | } |
| 25476 | | 25500 | |
| | 25501 | fn analyzePtrIsNonErrComptimeOnly( |
| | 25502 | sema: *Sema, |
| | 25503 | block: *Block, |
| | 25504 | src: LazySrcLoc, |
| | 25505 | operand: Air.Inst.Ref, |
| | 25506 | ) CompileError!Air.Inst.Ref { |
| | 25507 | const ptr_ty = sema.typeOf(operand); |
| | 25508 | assert(ptr_ty.zigTypeTag() == .Pointer); |
| | 25509 | const child_ty = ptr_ty.childType(); |
| | 25510 | |
| | 25511 | const child_tag = child_ty.zigTypeTag(); |
| | 25512 | if (child_tag != .ErrorSet and child_tag != .ErrorUnion) return Air.Inst.Ref.bool_true; |
| | 25513 | if (child_tag == .ErrorSet) return Air.Inst.Ref.bool_false; |
| | 25514 | assert(child_tag == .ErrorUnion); |
| | 25515 | |
| | 25516 | _ = block; |
| | 25517 | _ = src; |
| | 25518 | |
| | 25519 | return Air.Inst.Ref.none; |
| | 25520 | } |
| | 25521 | |
| 25477 | fn analyzeIsNonErrComptimeOnly( | 25522 | fn analyzeIsNonErrComptimeOnly( |
| 25478 | sema: *Sema, | 25523 | sema: *Sema, |
| 25479 | block: *Block, | 25524 | block: *Block, |
| ... | @@ -25572,6 +25617,21 @@ fn analyzeIsNonErr( | ... | @@ -25572,6 +25617,21 @@ fn analyzeIsNonErr( |
| 25572 | } | 25617 | } |
| 25573 | } | 25618 | } |
| 25574 | | 25619 | |
| | 25620 | fn analyzePtrIsNonErr( |
| | 25621 | sema: *Sema, |
| | 25622 | block: *Block, |
| | 25623 | src: LazySrcLoc, |
| | 25624 | operand: Air.Inst.Ref, |
| | 25625 | ) CompileError!Air.Inst.Ref { |
| | 25626 | const result = try sema.analyzePtrIsNonErrComptimeOnly(block, src, operand); |
| | 25627 | if (result == .none) { |
| | 25628 | try sema.requireRuntimeBlock(block, src, null); |
| | 25629 | return block.addUnOp(.is_non_err_ptr, operand); |
| | 25630 | } else { |
| | 25631 | return result; |
| | 25632 | } |
| | 25633 | } |
| | 25634 | |
| 25575 | fn analyzeSlice( | 25635 | fn analyzeSlice( |
| 25576 | sema: *Sema, | 25636 | sema: *Sema, |
| 25577 | block: *Block, | 25637 | block: *Block, |