authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-29 14:53:54+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-29 21:44:08+02:00
log17ff002bc0ac55850e647fc3a70a43d1d874f6ab
tree700cbc62d0ec95cc2b660cb8d33ab0970e39c9f8
parent6337c04244d9c27cc6535340347d4c127f4742eb

Sema: improve safety panic for access of inactive union field


4 files changed, 45 insertions(+), 83 deletions(-)

doc/langref.html.in+1-1
......@@ -3803,7 +3803,7 @@ test "switch on non-exhaustive enum" {
38033803 {#link|Accessing the non-active field|Wrong Union Field Access#} is
38043804 safety-checked {#link|Undefined Behavior#}:
38053805 </p>
3806 {#code_begin|test_err|inactive union field#}
3806 {#code_begin|test_err|access of union field 'float' while field 'int' is active#}
38073807const Payload = union {
38083808 int: i64,
38093809 float: f64,
lib/std/builtin.zig+5
......@@ -868,6 +868,11 @@ pub fn panicStartGreaterThanEnd(start: usize, end: usize) noreturn {
868868 std.debug.panicExtra(null, @returnAddress(), "start index {d} is larger than end index {d}", .{ start, end });
869869}
870870
871pub fn panicInactiveUnionField(active: anytype, wanted: @TypeOf(active)) noreturn {
872 @setCold(true);
873 std.debug.panicExtra(null, @returnAddress(), "access of union field '{s}' while field '{s}' is active", .{ @tagName(wanted), @tagName(active) });
874}
875
871876pub const panic_messages = struct {
872877 pub const unreach = "reached unreachable code";
873878 pub const unwrap_null = "attempt to use null value";
src/Sema.zig+38-81
......@@ -22120,7 +22120,6 @@ pub const PanicId = enum {
2212022120 shr_overflow,
2212122121 divide_by_zero,
2212222122 exact_division_remainder,
22123 /// TODO make this call `std.builtin.panicInactiveUnionField`.
2212422123 inactive_union_field,
2212522124 integer_part_out_of_bounds,
2212622125 corrupt_switch,
......@@ -22296,90 +22295,40 @@ fn panicUnwrapError(
2229622295fn panicIndexOutOfBounds(
2229722296 sema: *Sema,
2229822297 parent_block: *Block,
22299 src: LazySrcLoc,
2230022298 index: Air.Inst.Ref,
2230122299 len: Air.Inst.Ref,
2230222300 cmp_op: Air.Inst.Tag,
2230322301) !void {
2230422302 assert(!parent_block.is_comptime);
2230522303 const ok = try parent_block.addBinOp(cmp_op, index, len);
22306 const gpa = sema.gpa;
22307
22308 var fail_block: Block = .{
22309 .parent = parent_block,
22310 .sema = sema,
22311 .src_decl = parent_block.src_decl,
22312 .namespace = parent_block.namespace,
22313 .wip_capture_scope = parent_block.wip_capture_scope,
22314 .instructions = .{},
22315 .inlining = parent_block.inlining,
22316 .is_comptime = false,
22317 };
22318
22319 defer fail_block.instructions.deinit(gpa);
22320
22321 {
22322 const this_feature_is_implemented_in_the_backend =
22323 sema.mod.comp.bin_file.options.use_llvm;
22324
22325 if (!this_feature_is_implemented_in_the_backend) {
22326 // TODO implement this feature in all the backends and then delete this branch
22327 _ = try fail_block.addNoOp(.breakpoint);
22328 _ = try fail_block.addNoOp(.unreach);
22329 } else {
22330 const panic_fn = try sema.getBuiltin("panicOutOfBounds");
22331 const args: [2]Air.Inst.Ref = .{ index, len };
22332 _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null);
22333 }
22334 }
22335 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
22304 try sema.safetyPanicFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len });
2233622305}
2233722306
2233822307fn panicStartLargerThanEnd(
2233922308 sema: *Sema,
2234022309 parent_block: *Block,
22341 src: LazySrcLoc,
2234222310 start: Air.Inst.Ref,
2234322311 end: Air.Inst.Ref,
2234422312) !void {
2234522313 assert(!parent_block.is_comptime);
2234622314 const ok = try parent_block.addBinOp(.cmp_lte, start, end);
22347 const gpa = sema.gpa;
22348
22349 var fail_block: Block = .{
22350 .parent = parent_block,
22351 .sema = sema,
22352 .src_decl = parent_block.src_decl,
22353 .namespace = parent_block.namespace,
22354 .wip_capture_scope = parent_block.wip_capture_scope,
22355 .instructions = .{},
22356 .inlining = parent_block.inlining,
22357 .is_comptime = false,
22358 };
22359
22360 defer fail_block.instructions.deinit(gpa);
22361
22362 {
22363 const this_feature_is_implemented_in_the_backend =
22364 sema.mod.comp.bin_file.options.use_llvm;
22315 try sema.safetyPanicFormatted(parent_block, ok, "panicStartGreaterThanEnd", &.{ start, end });
22316}
2236522317
22366 if (!this_feature_is_implemented_in_the_backend) {
22367 // TODO implement this feature in all the backends and then delete this branch
22368 _ = try fail_block.addNoOp(.breakpoint);
22369 _ = try fail_block.addNoOp(.unreach);
22370 } else {
22371 const panic_fn = try sema.getBuiltin("panicStartGreaterThanEnd");
22372 const args: [2]Air.Inst.Ref = .{ start, end };
22373 _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null);
22374 }
22375 }
22376 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
22318fn panicInactiveUnionField(
22319 sema: *Sema,
22320 parent_block: *Block,
22321 active_tag: Air.Inst.Ref,
22322 wanted_tag: Air.Inst.Ref,
22323) !void {
22324 assert(!parent_block.is_comptime);
22325 const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag);
22326 try sema.safetyPanicFormatted(parent_block, ok, "panicInactiveUnionField", &.{ active_tag, wanted_tag });
2237722327}
2237822328
2237922329fn panicSentinelMismatch(
2238022330 sema: *Sema,
2238122331 parent_block: *Block,
22382 src: LazySrcLoc,
2238322332 maybe_sentinel: ?Value,
2238422333 sentinel_ty: Type,
2238522334 ptr: Air.Inst.Ref,
......@@ -22413,9 +22362,20 @@ fn panicSentinelMismatch(
2241322362 else {
2241422363 const panic_fn = try sema.getBuiltin("checkNonScalarSentinel");
2241522364 const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel };
22416 _ = try sema.analyzeCall(parent_block, panic_fn, src, src, .auto, false, &args, null);
22365 _ = try sema.analyzeCall(parent_block, panic_fn, sema.src, sema.src, .auto, false, &args, null);
2241722366 return;
2241822367 };
22368
22369 try sema.safetyPanicFormatted(parent_block, ok, "panicSentinelMismatch", &.{ expected_sentinel, actual_sentinel });
22370}
22371
22372fn safetyPanicFormatted(
22373 sema: *Sema,
22374 parent_block: *Block,
22375 ok: Air.Inst.Ref,
22376 func: []const u8,
22377 args: []const Air.Inst.Ref,
22378) CompileError!void {
2241922379 const gpa = sema.gpa;
2242022380
2242122381 var fail_block: Block = .{
......@@ -22440,9 +22400,8 @@ fn panicSentinelMismatch(
2244022400 _ = try fail_block.addNoOp(.breakpoint);
2244122401 _ = try fail_block.addNoOp(.unreach);
2244222402 } else {
22443 const panic_fn = try sema.getBuiltin("panicSentinelMismatch");
22444 const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel };
22445 _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null);
22403 const panic_fn = try sema.getBuiltin(func);
22404 _ = try sema.analyzeCall(&fail_block, panic_fn, sema.src, sema.src, .auto, false, args, null);
2244622405 }
2244722406 }
2244822407 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
......@@ -23465,8 +23424,7 @@ fn unionFieldPtr(
2346523424 // TODO would it be better if get_union_tag supported pointers to unions?
2346623425 const union_val = try block.addTyOp(.load, union_ty, union_ptr);
2346723426 const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_val);
23468 const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
23469 try sema.addSafetyCheck(block, ok, .inactive_union_field);
23427 try sema.panicInactiveUnionField(block, active_tag, wanted_tag);
2347023428 }
2347123429 if (field.ty.zigTypeTag() == .NoReturn) {
2347223430 _ = try block.addNoOp(.unreach);
......@@ -23537,8 +23495,7 @@ fn unionFieldVal(
2353723495 const wanted_tag_val = try Value.Tag.enum_field_index.create(sema.arena, enum_field_index);
2353823496 const wanted_tag = try sema.addConstant(union_obj.tag_ty, wanted_tag_val);
2353923497 const active_tag = try block.addTyOp(.get_union_tag, union_obj.tag_ty, union_byval);
23540 const ok = try block.addBinOp(.cmp_eq, active_tag, wanted_tag);
23541 try sema.addSafetyCheck(block, ok, .inactive_union_field);
23498 try sema.panicInactiveUnionField(block, active_tag, wanted_tag);
2354223499 }
2354323500 if (field.ty.zigTypeTag() == .NoReturn) {
2354423501 _ = try block.addNoOp(.unreach);
......@@ -23849,7 +23806,7 @@ fn elemValArray(
2384923806 if (maybe_index_val == null) {
2385023807 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
2385123808 const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt;
23852 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
23809 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
2385323810 }
2385423811 }
2385523812 return block.addBinOp(.array_elem_val, array, elem_index);
......@@ -23910,7 +23867,7 @@ fn elemPtrArray(
2391023867 if (block.wantSafety() and offset == null) {
2391123868 const len_inst = try sema.addIntUnsigned(Type.usize, array_len);
2391223869 const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt;
23913 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
23870 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
2391423871 }
2391523872
2391623873 return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty);
......@@ -23966,7 +23923,7 @@ fn elemValSlice(
2396623923 else
2396723924 try block.addTyOp(.slice_len, Type.usize, slice);
2396823925 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
23969 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
23926 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
2397023927 }
2397123928 try sema.queueFullTypeResolution(sema.typeOf(slice));
2397223929 return block.addBinOp(.slice_elem_val, slice, elem_index);
......@@ -24025,7 +23982,7 @@ fn elemPtrSlice(
2402523982 break :len try block.addTyOp(.slice_len, Type.usize, slice);
2402623983 };
2402723984 const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
24028 try sema.panicIndexOutOfBounds(block, elem_index_src, elem_index, len_inst, cmp_op);
23985 try sema.panicIndexOutOfBounds(block, elem_index, len_inst, cmp_op);
2402923986 }
2403023987 return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty);
2403123988}
......@@ -28072,7 +28029,7 @@ fn analyzeSlice(
2807228029
2807328030 if (block.wantSafety() and !block.is_comptime) {
2807428031 // requirement: start <= end
28075 try sema.panicStartLargerThanEnd(block, src, start, end);
28032 try sema.panicStartLargerThanEnd(block, start, end);
2807628033 }
2807728034 const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
2807828035 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
......@@ -28116,11 +28073,11 @@ fn analyzeSlice(
2811628073 else
2811728074 end;
2811828075
28119 try sema.panicIndexOutOfBounds(block, src, actual_end, actual_len, .cmp_lte);
28076 try sema.panicIndexOutOfBounds(block, actual_end, actual_len, .cmp_lte);
2812028077 }
2812128078
2812228079 // requirement: result[new_len] == slice_sentinel
28123 try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
28080 try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len);
2812428081 }
2812528082 return result;
2812628083 };
......@@ -28184,11 +28141,11 @@ fn analyzeSlice(
2818428141 try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true)
2818528142 else
2818628143 end;
28187 try sema.panicIndexOutOfBounds(block, src, actual_end, len_inst, .cmp_lte);
28144 try sema.panicIndexOutOfBounds(block, actual_end, len_inst, .cmp_lte);
2818828145 }
2818928146
2819028147 // requirement: start <= end
28191 try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte);
28148 try sema.panicIndexOutOfBounds(block, start, end, .cmp_lte);
2819228149 }
2819328150 const result = try block.addInst(.{
2819428151 .tag = .slice,
......@@ -28202,7 +28159,7 @@ fn analyzeSlice(
2820228159 });
2820328160 if (block.wantSafety()) {
2820428161 // requirement: result[new_len] == slice_sentinel
28205 try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
28162 try sema.panicSentinelMismatch(block, slice_sentinel, elem_ty, result, new_len);
2820628163 }
2820728164 return result;
2820828165}
test/cases/safety/bad union field access.zig +1-1
......@@ -2,7 +2,7 @@ const std = @import("std");
22
33pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
44 _ = stack_trace;
5 if (std.mem.eql(u8, message, "access of inactive union field")) {
5 if (std.mem.eql(u8, message, "access of union field 'float' while field 'int' is active")) {
66 std.process.exit(0);
77 }
88 std.process.exit(1);