authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-08-01 19:33:18+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-08-02 18:34:30+03:00
log14f0b70570aa1c50c7316851293899000615bc94
treeff1677912bc9d33137a5d3ffc0b1ba63a61138cb
parent292906fb2378bd37e02abf09e2be7f86ee657fac

Sema: add safety for sentinel slice


6 files changed, 140 insertions(+), 6 deletions(-)

lib/std/builtin.zig+11
...@@ -846,6 +846,17 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn...@@ -846,6 +846,17 @@ pub fn default_panic(msg: []const u8, error_return_trace: ?*StackTrace) noreturn
846 }846 }
847}847}
848848
849pub fn checkNonScalarSentinel(expected: anytype, actual: @TypeOf(expected)) void {
850 if (!std.meta.eql(expected, actual)) {
851 panicSentinelMismatch(expected, actual);
852 }
853}
854
855pub fn panicSentinelMismatch(expected: anytype, actual: @TypeOf(expected)) noreturn {
856 @setCold(true);
857 std.debug.panic("sentinel mismatch: expected {any}, found {any}", .{ expected, actual });
858}
859
849pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn {860pub fn panicUnwrapError(st: ?*StackTrace, err: anyerror) noreturn {
850 @setCold(true);861 @setCold(true);
851 std.debug.panicExtra(st, "attempt to unwrap error: {s}", .{@errorName(err)});862 std.debug.panicExtra(st, "attempt to unwrap error: {s}", .{@errorName(err)});
src/Sema.zig+84-2
...@@ -20148,6 +20148,77 @@ fn panicIndexOutOfBounds(...@@ -20148,6 +20148,77 @@ fn panicIndexOutOfBounds(
20148 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);20148 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
20149}20149}
2015020150
20151fn panicSentinelMismatch(
20152 sema: *Sema,
20153 parent_block: *Block,
20154 src: LazySrcLoc,
20155 maybe_sentinel: ?Value,
20156 sentinel_ty: Type,
20157 ptr: Air.Inst.Ref,
20158 sentinel_index: Air.Inst.Ref,
20159) !void {
20160 const expected_sentinel_val = maybe_sentinel orelse return;
20161 const expected_sentinel = try sema.addConstant(sentinel_ty, expected_sentinel_val);
20162
20163 const ptr_ty = sema.typeOf(ptr);
20164 const actual_sentinel = if (ptr_ty.isSlice())
20165 try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index)
20166 else blk: {
20167 const elem_ptr_ty = try sema.elemPtrType(ptr_ty, null);
20168 const sentinel_ptr = try parent_block.addPtrElemPtr(ptr, sentinel_index, elem_ptr_ty);
20169 break :blk try parent_block.addTyOp(.load, sentinel_ty, sentinel_ptr);
20170 };
20171
20172 const ok = if (sentinel_ty.zigTypeTag() == .Vector) ok: {
20173 const eql =
20174 try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq, try sema.addType(sentinel_ty));
20175 break :ok try parent_block.addInst(.{
20176 .tag = .reduce,
20177 .data = .{ .reduce = .{
20178 .operand = eql,
20179 .operation = .And,
20180 } },
20181 });
20182 } else if (sentinel_ty.isSelfComparable(true))
20183 try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel)
20184 else {
20185 const panic_fn = try sema.getBuiltin(parent_block, src, "checkNonScalarSentinel");
20186 const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel };
20187 _ = try sema.analyzeCall(parent_block, panic_fn, src, src, .auto, false, &args, null);
20188 return;
20189 };
20190 const gpa = sema.gpa;
20191
20192 var fail_block: Block = .{
20193 .parent = parent_block,
20194 .sema = sema,
20195 .src_decl = parent_block.src_decl,
20196 .namespace = parent_block.namespace,
20197 .wip_capture_scope = parent_block.wip_capture_scope,
20198 .instructions = .{},
20199 .inlining = parent_block.inlining,
20200 .is_comptime = parent_block.is_comptime,
20201 };
20202
20203 defer fail_block.instructions.deinit(gpa);
20204
20205 {
20206 const this_feature_is_implemented_in_the_backend =
20207 sema.mod.comp.bin_file.options.use_llvm;
20208
20209 if (!this_feature_is_implemented_in_the_backend) {
20210 // TODO implement this feature in all the backends and then delete this branch
20211 _ = try fail_block.addNoOp(.breakpoint);
20212 _ = try fail_block.addNoOp(.unreach);
20213 } else {
20214 const panic_fn = try sema.getBuiltin(&fail_block, src, "panicSentinelMismatch");
20215 const args: [2]Air.Inst.Ref = .{ expected_sentinel, actual_sentinel };
20216 _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null);
20217 }
20218 }
20219 try sema.addSafetyCheckExtra(parent_block, ok, &fail_block);
20220}
20221
20151fn safetyPanic(20222fn safetyPanic(
20152 sema: *Sema,20223 sema: *Sema,
20153 block: *Block,20224 block: *Block,
...@@ -25368,6 +25439,7 @@ fn analyzeSlice(...@@ -25368,6 +25439,7 @@ fn analyzeSlice(
25368 }25439 }
25369 break :s null;25440 break :s null;
25370 };25441 };
25442 const slice_sentinel = if (sentinel_opt != .none) sentinel else null;
2537125443
25372 // requirement: start <= end25444 // requirement: start <= end
25373 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {25445 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
...@@ -25447,7 +25519,12 @@ fn analyzeSlice(...@@ -25447,7 +25519,12 @@ fn analyzeSlice(
2544725519
25448 const opt_new_ptr_val = try sema.resolveMaybeUndefVal(block, ptr_src, new_ptr);25520 const opt_new_ptr_val = try sema.resolveMaybeUndefVal(block, ptr_src, new_ptr);
25449 const new_ptr_val = opt_new_ptr_val orelse {25521 const new_ptr_val = opt_new_ptr_val orelse {
25450 return block.addBitCast(return_ty, new_ptr);25522 const result = try block.addBitCast(return_ty, new_ptr);
25523 if (block.wantSafety()) {
25524 // requirement: result[new_len] == slice_sentinel
25525 try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
25526 }
25527 return result;
25451 };25528 };
2545225529
25453 if (!new_ptr_val.isUndef()) {25530 if (!new_ptr_val.isUndef()) {
...@@ -25511,7 +25588,7 @@ fn analyzeSlice(...@@ -25511,7 +25588,7 @@ fn analyzeSlice(
25511 // requirement: start <= end25588 // requirement: start <= end
25512 try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte);25589 try sema.panicIndexOutOfBounds(block, src, start, end, .cmp_lte);
25513 }25590 }
25514 return block.addInst(.{25591 const result = try block.addInst(.{
25515 .tag = .slice,25592 .tag = .slice,
25516 .data = .{ .ty_pl = .{25593 .data = .{ .ty_pl = .{
25517 .ty = try sema.addType(return_ty),25594 .ty = try sema.addType(return_ty),
...@@ -25521,6 +25598,11 @@ fn analyzeSlice(...@@ -25521,6 +25598,11 @@ fn analyzeSlice(
25521 }),25598 }),
25522 } },25599 } },
25523 });25600 });
25601 if (block.wantSafety()) {
25602 // requirement: result[new_len] == slice_sentinel
25603 try sema.panicSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len);
25604 }
25605 return result;
25524}25606}
2552525607
25526/// Asserts that lhs and rhs types are both numeric.25608/// Asserts that lhs and rhs types are both numeric.
test/cases/compile_errors/reify_type_for_tagged_union_with_extra_union_field.zig+1-1
...@@ -31,5 +31,5 @@ export fn entry() void {...@@ -31,5 +31,5 @@ export fn entry() void {
31// backend=stage231// backend=stage2
32// target=native32// target=native
33//33//
34// :13:16: error: no field named 'arst' in enum 'tmp.Tag__enum_264'34// :13:16: error: no field named 'arst' in enum 'tmp.Tag__enum_266'
35// :1:13: note: enum declared here35// :1:13: note: enum declared here
test/cases/safety/array slice sentinel mismatch non-scalar.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "sentinel mismatch: expected tmp.main.S{ .a = 1 }, found tmp.main.S{ .a = 2 }")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10
11pub fn main() !void {
12 const S = struct { a: u32 };
13 var arr = [_]S{ .{ .a = 1 }, .{ .a = 2 } };
14 var s = arr[0..1 :.{ .a = 1 }];
15 _ = s;
16 return error.TestFailed;
17}
18
19// run
20// backend=llvm
21// target=native
test/cases/safety/array slice sentinel mismatch vector.zig created+19
...@@ -0,0 +1,19 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "sentinel mismatch: expected { 0, 0 }, found { 4, 4 }")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10
11pub fn main() !void {
12 var buf: [4]@Vector(2, u32) = .{ .{ 1, 1 }, .{ 2, 2 }, .{ 3, 3 }, .{ 4, 4 } };
13 const slice = buf[0..3 :.{ 0, 0 }];
14 _ = slice;
15 return error.TestFailed;
16}
17// run
18// backend=llvm
19// target=native
test/cases/safety/array slice sentinel mismatch.zig +4-3
...@@ -2,17 +2,18 @@ const std = @import("std");...@@ -2,17 +2,18 @@ const std = @import("std");
22
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace) noreturn {
4 _ = stack_trace;4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "sentinel mismatch")) {5 if (std.mem.eql(u8, message, "sentinel mismatch: expected 0, found 4")) {
6 std.process.exit(0);6 std.process.exit(0);
7 }7 }
8 std.process.exit(1);8 std.process.exit(1);
9}9}
10
10pub fn main() !void {11pub fn main() !void {
11 var buf: [4]u8 = undefined;12 var buf: [4]u8 = .{ 1, 2, 3, 4 };
12 const slice = buf[0..3 :0];13 const slice = buf[0..3 :0];
13 _ = slice;14 _ = slice;
14 return error.TestFailed;15 return error.TestFailed;
15}16}
16// run17// run
17// backend=stage118// backend=llvm
18// target=native19// target=native