authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-04-23 11:13:31+03:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-05-16 17:42:51-07:00
logab4ec35b8bb3a19361afa315f77cce5f6054b109
tree9aa1100a74f3beba5b1ca35d4f9c3dc52e360a11
parente369752430a1b3a50e57e11b9f0682d026c62feb

stage2: add runtime safety for unwrapping error


4 files changed, 139 insertions(+), 22 deletions(-)

lib/compiler_rt.zig+11-13
...@@ -198,19 +198,17 @@ comptime {...@@ -198,19 +198,17 @@ comptime {
198 const __trunctfxf2 = @import("compiler_rt/trunc_f80.zig").__trunctfxf2;198 const __trunctfxf2 = @import("compiler_rt/trunc_f80.zig").__trunctfxf2;
199 @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage });199 @export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = linkage });
200200
201 if (builtin.zig_backend == .stage1) { // TODO201 switch (arch) {
202 switch (arch) {202 .i386,
203 .i386,203 .x86_64,
204 .x86_64,204 => {
205 => {205 const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack;
206 const zig_probe_stack = @import("compiler_rt/stack_probe.zig").zig_probe_stack;206 @export(zig_probe_stack, .{
207 @export(zig_probe_stack, .{207 .name = "__zig_probe_stack",
208 .name = "__zig_probe_stack",208 .linkage = linkage,
209 .linkage = linkage,209 });
210 });210 },
211 },211 else => {},
212 else => {},
213 }
214 }212 }
215213
216 const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2;214 const __unordsf2 = @import("compiler_rt/compareXf2.zig").__unordsf2;
lib/std/builtin.zig+4
...@@ -846,6 +846,10 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn...@@ -846,6 +846,10 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
846 }846 }
847}847}
848848
849pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn {
850 std.debug.panicExtra(st, "attempt to unwrap error: {s}", .{@errorName(err)});
851}
852
849pub noinline fn returnError(maybe_st: ?*StackTrace) void {853pub noinline fn returnError(maybe_st: ?*StackTrace) void {
850 @setCold(true);854 @setCold(true);
851 const st = maybe_st orelse return;855 const st = maybe_st orelse return;
src/AstGen.zig+27
...@@ -856,6 +856,33 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr...@@ -856,6 +856,33 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerEr
856 catch_token + 2856 catch_token + 2
857 else857 else
858 null;858 null;
859
860 var rhs = node_datas[node].rhs;
861 while (true) switch (node_tags[rhs]) {
862 .grouped_expression => rhs = node_datas[rhs].lhs,
863 .unreachable_literal => {
864 if (payload_token != null and mem.eql(u8, tree.tokenSlice(payload_token.?), "_")) {
865 return astgen.failTok(payload_token.?, "discard of error capture; omit it instead", .{});
866 } else if (payload_token != null) {
867 return astgen.failTok(payload_token.?, "unused capture", .{});
868 }
869 const lhs = node_datas[node].lhs;
870
871 const operand = try reachableExpr(gz, scope, switch (rl) {
872 .ref => .ref,
873 else => .none,
874 }, lhs, lhs);
875 const result = try gz.addUnNode(switch (rl) {
876 .ref => .err_union_payload_safe_ptr,
877 else => .err_union_payload_safe,
878 }, operand, node);
879 switch (rl) {
880 .none, .coerced_ty, .discard, .ref => return result,
881 else => return rvalue(gz, rl, result, lhs),
882 }
883 },
884 else => break,
885 };
859 switch (rl) {886 switch (rl) {
860 .ref => return orelseCatchExpr(887 .ref => return orelseCatchExpr(
861 gz,888 gz,
src/Sema.zig+97-9
...@@ -6248,8 +6248,7 @@ fn zirErrUnionPayload(...@@ -6248,8 +6248,7 @@ fn zirErrUnionPayload(
6248 }6248 }
6249 try sema.requireRuntimeBlock(block, src);6249 try sema.requireRuntimeBlock(block, src);
6250 if (safety_check and block.wantSafety()) {6250 if (safety_check and block.wantSafety()) {
6251 const is_non_err = try block.addUnOp(.is_err, operand);6251 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err);
6252 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
6253 }6252 }
6254 const result_ty = operand_ty.errorUnionPayload();6253 const result_ty = operand_ty.errorUnionPayload();
6255 return block.addTyOp(.unwrap_errunion_payload, result_ty, operand);6254 return block.addTyOp(.unwrap_errunion_payload, result_ty, operand);
...@@ -6330,8 +6329,7 @@ fn analyzeErrUnionPayloadPtr(...@@ -6330,8 +6329,7 @@ fn analyzeErrUnionPayloadPtr(
63306329
6331 try sema.requireRuntimeBlock(block, src);6330 try sema.requireRuntimeBlock(block, src);
6332 if (safety_check and block.wantSafety()) {6331 if (safety_check and block.wantSafety()) {
6333 const is_non_err = try block.addUnOp(.is_err, operand);6332 try sema.panicUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr);
6334 try sema.addSafetyCheck(block, is_non_err, .unwrap_errunion);
6335 }6333 }
6336 const air_tag: Air.Inst.Tag = if (initializing)6334 const air_tag: Air.Inst.Tag = if (initializing)
6337 .errunion_payload_ptr_set6335 .errunion_payload_ptr_set
...@@ -13400,6 +13398,10 @@ fn zirErrorReturnTrace(...@@ -13400,6 +13398,10 @@ fn zirErrorReturnTrace(
13400 extended: Zir.Inst.Extended.InstData,13398 extended: Zir.Inst.Extended.InstData,
13401) CompileError!Air.Inst.Ref {13399) CompileError!Air.Inst.Ref {
13402 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };13400 const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) };
13401 return sema.getErrorReturnTrace(block, src);
13402}
13403
13404fn getErrorReturnTrace(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError!Air.Inst.Ref {
13403 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");13405 const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace");
13404 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);13406 const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty);
13405 const opt_ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty);13407 const opt_ptr_stack_trace_ty = try Type.Tag.optional_single_mut_pointer.create(sema.arena, stack_trace_ty);
...@@ -16852,7 +16854,6 @@ fn explainWhyTypeIsComptime(...@@ -16852,7 +16854,6 @@ fn explainWhyTypeIsComptime(
16852pub const PanicId = enum {16854pub const PanicId = enum {
16853 unreach,16855 unreach,
16854 unwrap_null,16856 unwrap_null,
16855 unwrap_errunion,
16856 cast_to_null,16857 cast_to_null,
16857 incorrect_alignment,16858 incorrect_alignment,
16858 invalid_error_code,16859 invalid_error_code,
...@@ -16962,12 +16963,100 @@ fn panicWithMsg(...@@ -16962,12 +16963,100 @@ fn panicWithMsg(
16962 try Type.optional(arena, ptr_stack_trace_ty),16963 try Type.optional(arena, ptr_stack_trace_ty),
16963 Value.@"null",16964 Value.@"null",
16964 );16965 );
16965 const args = try arena.create([2]Air.Inst.Ref);16966 const args: [2]Air.Inst.Ref = .{ msg_inst, null_stack_trace };
16966 args.* = .{ msg_inst, null_stack_trace };16967 _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, &args);
16967 _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, args);
16968 return always_noreturn;16968 return always_noreturn;
16969}16969}
1697016970
16971fn panicUnwrapError(
16972 sema: *Sema,
16973 parent_block: *Block,
16974 src: LazySrcLoc,
16975 operand: Air.Inst.Ref,
16976 unwrap_err_tag: Air.Inst.Tag,
16977 is_non_err_tag: Air.Inst.Tag,
16978) !void {
16979 const ok = try parent_block.addUnOp(is_non_err_tag, operand);
16980 const gpa = sema.gpa;
16981
16982 var fail_block: Block = .{
16983 .parent = parent_block,
16984 .sema = sema,
16985 .src_decl = parent_block.src_decl,
16986 .namespace = parent_block.namespace,
16987 .wip_capture_scope = parent_block.wip_capture_scope,
16988 .instructions = .{},
16989 .inlining = parent_block.inlining,
16990 .is_comptime = parent_block.is_comptime,
16991 };
16992
16993 defer fail_block.instructions.deinit(gpa);
16994
16995 {
16996 const this_feature_is_implemented_in_the_backend =
16997 sema.mod.comp.bin_file.options.object_format == .c or
16998 sema.mod.comp.bin_file.options.use_llvm;
16999
17000 if (!this_feature_is_implemented_in_the_backend) {
17001 // TODO implement this feature in all the backends and then delete this branch
17002 _ = try fail_block.addNoOp(.breakpoint);
17003 _ = try fail_block.addNoOp(.unreach);
17004 } else {
17005 const panic_fn = try sema.getBuiltin(&fail_block, src, "panicUnwrapError");
17006 const err = try fail_block.addTyOp(unwrap_err_tag, Type.anyerror, operand);
17007 const err_return_trace = try sema.getErrorReturnTrace(&fail_block, src);
17008 const args: [2]Air.Inst.Ref = .{ err_return_trace, err };
17009 _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args);
17010 }
17011 }
17012
17013 try parent_block.instructions.ensureUnusedCapacity(gpa, 1);
17014
17015 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
17016 1 + // The main block only needs space for the cond_br.
17017 @typeInfo(Air.CondBr).Struct.fields.len +
17018 1 + // The ok branch of the cond_br only needs space for the br.
17019 fail_block.instructions.items.len);
17020
17021 try sema.air_instructions.ensureUnusedCapacity(gpa, 3);
17022 const block_inst = @intCast(Air.Inst.Index, sema.air_instructions.len);
17023 const cond_br_inst = block_inst + 1;
17024 const br_inst = cond_br_inst + 1;
17025 sema.air_instructions.appendAssumeCapacity(.{
17026 .tag = .block,
17027 .data = .{ .ty_pl = .{
17028 .ty = .void_type,
17029 .payload = sema.addExtraAssumeCapacity(Air.Block{
17030 .body_len = 1,
17031 }),
17032 } },
17033 });
17034 sema.air_extra.appendAssumeCapacity(cond_br_inst);
17035
17036 sema.air_instructions.appendAssumeCapacity(.{
17037 .tag = .cond_br,
17038 .data = .{ .pl_op = .{
17039 .operand = ok,
17040 .payload = sema.addExtraAssumeCapacity(Air.CondBr{
17041 .then_body_len = 1,
17042 .else_body_len = @intCast(u32, fail_block.instructions.items.len),
17043 }),
17044 } },
17045 });
17046 sema.air_extra.appendAssumeCapacity(br_inst);
17047 sema.air_extra.appendSliceAssumeCapacity(fail_block.instructions.items);
17048
17049 sema.air_instructions.appendAssumeCapacity(.{
17050 .tag = .br,
17051 .data = .{ .br = .{
17052 .block_inst = block_inst,
17053 .operand = .void_value,
17054 } },
17055 });
17056
17057 parent_block.instructions.appendAssumeCapacity(block_inst);
17058}
17059
16971fn safetyPanic(17060fn safetyPanic(
16972 sema: *Sema,17061 sema: *Sema,
16973 block: *Block,17062 block: *Block,
...@@ -16977,7 +17066,6 @@ fn safetyPanic(...@@ -16977,7 +17066,6 @@ fn safetyPanic(
16977 const msg = switch (panic_id) {17066 const msg = switch (panic_id) {
16978 .unreach => "reached unreachable code",17067 .unreach => "reached unreachable code",
16979 .unwrap_null => "attempt to use null value",17068 .unwrap_null => "attempt to use null value",
16980 .unwrap_errunion => "unreachable error occurred",
16981 .cast_to_null => "cast causes pointer to be null",17069 .cast_to_null => "cast causes pointer to be null",
16982 .incorrect_alignment => "incorrect alignment",17070 .incorrect_alignment => "incorrect alignment",
16983 .invalid_error_code => "invalid error code",17071 .invalid_error_code => "invalid error code",