Sema: improve panic for slice start index being greater than end index

Closes #13689
This commit is contained in:
Veikka Tuominen 2022-11-29 14:29:50 +02:00
parent 6f9c7e33b9
commit 6337c04244
3 changed files with 86 additions and 12 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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