diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index 1f7d48ccb9..09833988ae 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -863,10 +863,9 @@ pub fn panicOutOfBounds(index: usize, len: usize) noreturn { std.debug.panicExtra(null, @returnAddress(), "index out of bounds: index {d}, len {d}", .{ index, len }); } -pub noinline fn returnError(st: *StackTrace) void { +pub fn panicStartGreaterThanEnd(start: usize, end: usize) noreturn { @setCold(true); - @setRuntimeSafety(false); - addErrRetTraceAddr(st, @returnAddress()); + std.debug.panicExtra(null, @returnAddress(), "start index {d} is larger than end index {d}", .{ start, end }); } pub const panic_messages = struct { @@ -889,6 +888,12 @@ pub const panic_messages = struct { pub const invalid_enum_value = "invalid enum value"; }; +pub noinline fn returnError(st: *StackTrace) void { + @setCold(true); + @setRuntimeSafety(false); + addErrRetTraceAddr(st, @returnAddress()); +} + pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void { if (st.index < st.instruction_addresses.len) st.instruction_addresses[st.index] = addr; diff --git a/src/Sema.zig b/src/Sema.zig index b8b8f3738a..d35c4e3881 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -12437,7 +12437,7 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. else try sema.resolveInst(.zero); - return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src); + return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true); } fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -12460,7 +12460,7 @@ fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! else try sema.resolveInst(.zero); - return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src); + return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true); } fn zirArithmetic( @@ -12480,7 +12480,7 @@ fn zirArithmetic( const lhs = try sema.resolveInst(extra.lhs); const rhs = try sema.resolveInst(extra.rhs); - return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src); + return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src, true); } fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -13776,6 +13776,7 @@ fn analyzeArithmetic( src: LazySrcLoc, lhs_src: LazySrcLoc, rhs_src: LazySrcLoc, + want_safety: bool, ) CompileError!Air.Inst.Ref { const lhs_ty = sema.typeOf(lhs); const rhs_ty = sema.typeOf(rhs); @@ -14204,7 +14205,7 @@ fn analyzeArithmetic( }; try sema.requireRuntimeBlock(block, src, rs.src); - if (block.wantSafety()) { + if (block.wantSafety() and want_safety) { if (scalar_tag == .Int) { const maybe_op_ov: ?Air.Inst.Tag = switch (rs.air_tag) { .add => .add_with_overflow, @@ -22334,6 +22335,47 @@ fn panicIndexOutOfBounds( try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); } +fn panicStartLargerThanEnd( + sema: *Sema, + parent_block: *Block, + src: LazySrcLoc, + start: Air.Inst.Ref, + end: Air.Inst.Ref, +) !void { + assert(!parent_block.is_comptime); + const ok = try parent_block.addBinOp(.cmp_lte, start, end); + const gpa = sema.gpa; + + var fail_block: Block = .{ + .parent = parent_block, + .sema = sema, + .src_decl = parent_block.src_decl, + .namespace = parent_block.namespace, + .wip_capture_scope = parent_block.wip_capture_scope, + .instructions = .{}, + .inlining = parent_block.inlining, + .is_comptime = false, + }; + + defer fail_block.instructions.deinit(gpa); + + { + const this_feature_is_implemented_in_the_backend = + sema.mod.comp.bin_file.options.use_llvm; + + if (!this_feature_is_implemented_in_the_backend) { + // TODO implement this feature in all the backends and then delete this branch + _ = try fail_block.addNoOp(.breakpoint); + _ = try fail_block.addNoOp(.unreach); + } else { + const panic_fn = try sema.getBuiltin("panicStartGreaterThanEnd"); + const args: [2]Air.Inst.Ref = .{ start, end }; + _ = try sema.analyzeCall(&fail_block, panic_fn, src, src, .auto, false, &args, null); + } + } + try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); +} + fn panicSentinelMismatch( sema: *Sema, parent_block: *Block, @@ -28028,7 +28070,11 @@ fn analyzeSlice( } } - const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src); + if (block.wantSafety() and !block.is_comptime) { + // requirement: start <= end + try sema.panicStartLargerThanEnd(block, src, start, end); + } + const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false); const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data; @@ -28063,10 +28109,10 @@ fn analyzeSlice( const actual_len = if (slice_ty.sentinel() == null) slice_len_inst else - try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src); + try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); const actual_end = if (slice_sentinel != null) - try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src) + try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) else end; @@ -28131,11 +28177,11 @@ fn analyzeSlice( if (slice_ty.sentinel() == null) break :blk slice_len_inst; // we have to add one because slice lengths don't include the sentinel - break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src); + break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); } else null; if (opt_len_inst) |len_inst| { const actual_end = if (slice_sentinel != null) - try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src) + try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) else end; try sema.panicIndexOutOfBounds(block, src, actual_end, len_inst, .cmp_lte); diff --git a/test/cases/safety/slice start index greater than end index.zig b/test/cases/safety/slice start index greater than end index.zig new file mode 100644 index 0000000000..ef0485ae60 --- /dev/null +++ b/test/cases/safety/slice start index greater than end index.zig @@ -0,0 +1,23 @@ +const std = @import("std"); + +pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn { + _ = stack_trace; + if (std.mem.eql(u8, message, "start index 10 is larger than end index 1")) { + std.process.exit(0); + } + std.process.exit(1); +} + +pub fn main() !void { + var a: usize = 1; + var b: usize = 10; + var buf: [16]u8 = undefined; + + const slice = buf[b..a]; + _ = slice; + return error.TestFailed; +} + +// run +// backend=llvm +// target=native