Merge pull request #3940 from ziglang/sentinel-slicing
fix std.mem.addNullByte and implement sentinel slicingmaster
commit
bc95c63cf2
|
@ -82,11 +82,11 @@ pub const Buffer = struct {
|
|||
}
|
||||
|
||||
pub fn toSlice(self: Buffer) [:0]u8 {
|
||||
return self.list.toSlice()[0..self.len()];
|
||||
return self.list.toSlice()[0..self.len() :0];
|
||||
}
|
||||
|
||||
pub fn toSliceConst(self: Buffer) [:0]const u8 {
|
||||
return self.list.toSliceConst()[0..self.len()];
|
||||
return self.list.toSliceConst()[0..self.len() :0];
|
||||
}
|
||||
|
||||
pub fn shrink(self: *Buffer, new_len: usize) void {
|
||||
|
|
|
@ -31,13 +31,21 @@ fn testCStrFnsImpl() void {
|
|||
testing.expect(mem.len(u8, "123456789") == 9);
|
||||
}
|
||||
|
||||
/// Returns a mutable slice with 1 more byte of length which is a null byte.
|
||||
/// Returns a mutable, null-terminated slice with the same length as `slice`.
|
||||
/// Caller owns the returned memory.
|
||||
pub fn addNullByte(allocator: *mem.Allocator, slice: []const u8) ![:0]u8 {
|
||||
const result = try allocator.alloc(u8, slice.len + 1);
|
||||
mem.copy(u8, result, slice);
|
||||
result[slice.len] = 0;
|
||||
return result;
|
||||
return result[0..slice.len :0];
|
||||
}
|
||||
|
||||
test "addNullByte" {
|
||||
var buf: [30]u8 = undefined;
|
||||
const allocator = &std.heap.FixedBufferAllocator.init(&buf).allocator;
|
||||
const slice = try addNullByte(allocator, "hello"[0..4]);
|
||||
testing.expect(slice.len == 4);
|
||||
testing.expect(slice[4] == 0);
|
||||
}
|
||||
|
||||
pub const NullTerminated2DArray = struct {
|
||||
|
|
|
@ -219,7 +219,7 @@ pub fn panic(comptime format: []const u8, args: var) noreturn {
|
|||
}
|
||||
|
||||
/// TODO multithreaded awareness
|
||||
var panicking: u8 = 0; // TODO make this a bool
|
||||
var panicking: u8 = 0;
|
||||
|
||||
pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn {
|
||||
@setCold(true);
|
||||
|
@ -230,21 +230,25 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
|
|||
resetSegfaultHandler();
|
||||
}
|
||||
|
||||
if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) {
|
||||
// Panicked during a panic.
|
||||
|
||||
// TODO detect if a different thread caused the panic, because in that case
|
||||
// we would want to return here instead of calling abort, so that the thread
|
||||
// which first called panic can finish printing a stack trace.
|
||||
os.abort();
|
||||
switch (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst)) {
|
||||
0 => {
|
||||
const stderr = getStderrStream();
|
||||
stderr.print(format ++ "\n", args) catch os.abort();
|
||||
if (trace) |t| {
|
||||
dumpStackTrace(t.*);
|
||||
}
|
||||
dumpCurrentStackTrace(first_trace_addr);
|
||||
},
|
||||
1 => {
|
||||
// TODO detect if a different thread caused the panic, because in that case
|
||||
// we would want to return here instead of calling abort, so that the thread
|
||||
// which first called panic can finish printing a stack trace.
|
||||
warn("Panicked during a panic. Aborting.\n", .{});
|
||||
},
|
||||
else => {
|
||||
// Panicked while printing "Panicked during a panic."
|
||||
},
|
||||
}
|
||||
const stderr = getStderrStream();
|
||||
stderr.print(format ++ "\n", args) catch os.abort();
|
||||
if (trace) |t| {
|
||||
dumpStackTrace(t.*);
|
||||
}
|
||||
dumpCurrentStackTrace(first_trace_addr);
|
||||
|
||||
os.abort();
|
||||
}
|
||||
|
||||
|
|
|
@ -221,16 +221,16 @@ pub const AtomicFile = struct {
|
|||
}
|
||||
|
||||
tmp_path_buf[tmp_path_len] = 0;
|
||||
const tmp_path_slice = tmp_path_buf[0..tmp_path_len :0];
|
||||
|
||||
const my_cwd = cwd();
|
||||
|
||||
while (true) {
|
||||
try crypto.randomBytes(rand_buf[0..]);
|
||||
b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], &rand_buf);
|
||||
b64_fs_encoder.encode(tmp_path_slice[dirname_component_len..tmp_path_len], &rand_buf);
|
||||
|
||||
// TODO https://github.com/ziglang/zig/issues/3770 to clean up this @ptrCast
|
||||
const file = my_cwd.createFileC(
|
||||
@ptrCast([*:0]u8, &tmp_path_buf),
|
||||
tmp_path_slice,
|
||||
.{ .mode = mode, .exclusive = true },
|
||||
) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => continue,
|
||||
|
@ -1488,8 +1488,7 @@ pub fn openSelfExe() OpenSelfExeError!File {
|
|||
var buf: [MAX_PATH_BYTES]u8 = undefined;
|
||||
const self_exe_path = try selfExePath(&buf);
|
||||
buf[self_exe_path.len] = 0;
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
|
||||
return openFileAbsoluteC(@ptrCast([*:0]u8, self_exe_path.ptr), .{});
|
||||
return openFileAbsoluteC(self_exe_path[0..self_exe_path.len :0].ptr, .{});
|
||||
}
|
||||
|
||||
test "openSelfExe" {
|
||||
|
|
|
@ -231,9 +231,10 @@ pub const Allocator = struct {
|
|||
pub fn free(self: *Allocator, memory: var) void {
|
||||
const Slice = @typeInfo(@TypeOf(memory)).Pointer;
|
||||
const bytes = @sliceToBytes(memory);
|
||||
if (bytes.len == 0) return;
|
||||
const bytes_len = bytes.len + @boolToInt(Slice.sentinel != null);
|
||||
if (bytes_len == 0) return;
|
||||
const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr));
|
||||
const shrink_result = self.shrinkFn(self, non_const_ptr[0..bytes.len], Slice.alignment, 0, 1);
|
||||
const shrink_result = self.shrinkFn(self, non_const_ptr[0..bytes_len], Slice.alignment, 0, 1);
|
||||
assert(shrink_result.len == 0);
|
||||
}
|
||||
};
|
||||
|
@ -363,11 +364,11 @@ pub fn len(comptime T: type, ptr: [*:0]const T) usize {
|
|||
}
|
||||
|
||||
pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T {
|
||||
return ptr[0..len(T, ptr)];
|
||||
return ptr[0..len(T, ptr) :0];
|
||||
}
|
||||
|
||||
pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T {
|
||||
return ptr[0..len(T, ptr)];
|
||||
return ptr[0..len(T, ptr) :0];
|
||||
}
|
||||
|
||||
/// Returns true if all elements in a slice are equal to the scalar value provided
|
||||
|
|
|
@ -805,9 +805,9 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e
|
|||
mem.copy(u8, &path_buf, search_path);
|
||||
path_buf[search_path.len] = '/';
|
||||
mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice);
|
||||
path_buf[search_path.len + file_slice.len + 1] = 0;
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
|
||||
err = execveC(@ptrCast([*:0]u8, &path_buf), child_argv, envp);
|
||||
const path_len = search_path.len + file_slice.len + 1;
|
||||
path_buf[path_len] = 0;
|
||||
err = execveC(path_buf[0..path_len :0].ptr, child_argv, envp);
|
||||
switch (err) {
|
||||
error.AccessDenied => seen_eacces = true,
|
||||
error.FileNotFound, error.NotDir => {},
|
||||
|
@ -841,18 +841,14 @@ pub fn execvpe(
|
|||
const arg_buf = try allocator.alloc(u8, arg.len + 1);
|
||||
@memcpy(arg_buf.ptr, arg.ptr, arg.len);
|
||||
arg_buf[arg.len] = 0;
|
||||
|
||||
// TODO avoid @ptrCast using slice syntax with https://github.com/ziglang/zig/issues/3770
|
||||
argv_buf[i] = @ptrCast([*:0]u8, arg_buf.ptr);
|
||||
argv_buf[i] = arg_buf[0..arg.len :0].ptr;
|
||||
}
|
||||
argv_buf[argv_slice.len] = null;
|
||||
const argv_ptr = argv_buf[0..argv_slice.len :null].ptr;
|
||||
|
||||
const envp_buf = try createNullDelimitedEnvMap(allocator, env_map);
|
||||
defer freeNullDelimitedEnvMap(allocator, envp_buf);
|
||||
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
|
||||
const argv_ptr = @ptrCast([*:null]?[*:0]u8, argv_buf.ptr);
|
||||
|
||||
return execvpeC(argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr);
|
||||
}
|
||||
|
||||
|
@ -869,16 +865,13 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.
|
|||
@memcpy(env_buf.ptr, pair.key.ptr, pair.key.len);
|
||||
env_buf[pair.key.len] = '=';
|
||||
@memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len);
|
||||
env_buf[env_buf.len - 1] = 0;
|
||||
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
|
||||
envp_buf[i] = @ptrCast([*:0]u8, env_buf.ptr);
|
||||
const len = env_buf.len - 1;
|
||||
env_buf[len] = 0;
|
||||
envp_buf[i] = env_buf[0..len :0].ptr;
|
||||
}
|
||||
assert(i == envp_count);
|
||||
}
|
||||
// TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
|
||||
assert(envp_buf[envp_count] == null);
|
||||
return @ptrCast([*:null]?[*:0]u8, envp_buf.ptr)[0..envp_count];
|
||||
return envp_buf[0..envp_count :null];
|
||||
}
|
||||
|
||||
pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) void {
|
||||
|
|
|
@ -1701,6 +1701,7 @@ pub const Node = struct {
|
|||
pub const Slice = struct {
|
||||
start: *Node,
|
||||
end: ?*Node,
|
||||
sentinel: ?*Node,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1732,6 +1733,10 @@ pub const Node = struct {
|
|||
if (i < 1) return end;
|
||||
i -= 1;
|
||||
}
|
||||
if (range.sentinel) |sentinel| {
|
||||
if (i < 1) return sentinel;
|
||||
i -= 1;
|
||||
}
|
||||
},
|
||||
.ArrayInitializer => |*exprs| {
|
||||
if (i < exprs.len) return exprs.at(i).*;
|
||||
|
|
|
@ -2331,7 +2331,7 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
|
|||
}
|
||||
|
||||
/// SuffixOp
|
||||
/// <- LBRACKET Expr (DOT2 Expr?)? RBRACKET
|
||||
/// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET
|
||||
/// / DOT IDENTIFIER
|
||||
/// / DOTASTERISK
|
||||
/// / DOTQUESTIONMARK
|
||||
|
@ -2349,11 +2349,16 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
|
|||
|
||||
if (eatToken(it, .Ellipsis2) != null) {
|
||||
const end_expr = try parseExpr(arena, it, tree);
|
||||
const sentinel: ?*ast.Node = if (eatToken(it, .Colon) != null)
|
||||
try parseExpr(arena, it, tree)
|
||||
else
|
||||
null;
|
||||
break :blk OpAndToken{
|
||||
.op = Op{
|
||||
.Slice = Op.Slice{
|
||||
.start = index_expr,
|
||||
.end = end_expr,
|
||||
.sentinel = sentinel,
|
||||
},
|
||||
},
|
||||
.token = try expectToken(it, tree, .RBracket),
|
||||
|
|
|
@ -419,10 +419,13 @@ test "zig fmt: pointer of unknown length" {
|
|||
test "zig fmt: spaces around slice operator" {
|
||||
try testCanonical(
|
||||
\\var a = b[c..d];
|
||||
\\var a = b[c..d :0];
|
||||
\\var a = b[c + 1 .. d];
|
||||
\\var a = b[c + 1 ..];
|
||||
\\var a = b[c .. d + 1];
|
||||
\\var a = b[c .. d + 1 :0];
|
||||
\\var a = b[c.a..d.e];
|
||||
\\var a = b[c.a..d.e :0];
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
|
|
@ -689,7 +689,13 @@ fn renderExpression(
|
|||
try renderExpression(allocator, stream, tree, indent, start_col, range.start, after_start_space);
|
||||
try renderToken(tree, stream, dotdot, indent, start_col, after_op_space); // ..
|
||||
if (range.end) |end| {
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, end, Space.None);
|
||||
const after_end_space = if (range.sentinel != null) Space.Space else Space.None;
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, end, after_end_space);
|
||||
}
|
||||
if (range.sentinel) |sentinel| {
|
||||
const colon = tree.prevToken(sentinel.firstToken());
|
||||
try renderToken(tree, stream, colon, indent, start_col, Space.None); // :
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None);
|
||||
}
|
||||
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ]
|
||||
},
|
||||
|
|
|
@ -810,6 +810,7 @@ struct AstNodeSliceExpr {
|
|||
AstNode *array_ref_expr;
|
||||
AstNode *start;
|
||||
AstNode *end;
|
||||
AstNode *sentinel; // can be null
|
||||
};
|
||||
|
||||
struct AstNodeFieldAccessExpr {
|
||||
|
@ -1778,6 +1779,7 @@ enum PanicMsgId {
|
|||
PanicMsgIdResumedFnPendingAwait,
|
||||
PanicMsgIdBadNoAsyncCall,
|
||||
PanicMsgIdResumeNotSuspendedFn,
|
||||
PanicMsgIdBadSentinel,
|
||||
|
||||
PanicMsgIdCount,
|
||||
};
|
||||
|
@ -3388,6 +3390,7 @@ struct IrInstructionSliceSrc {
|
|||
IrInstruction *ptr;
|
||||
IrInstruction *start;
|
||||
IrInstruction *end;
|
||||
IrInstruction *sentinel;
|
||||
ResultLoc *result_loc;
|
||||
};
|
||||
|
||||
|
|
|
@ -941,6 +941,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
|
|||
return buf_create_from_str("async function called with noasync suspended");
|
||||
case PanicMsgIdResumeNotSuspendedFn:
|
||||
return buf_create_from_str("resumed a non-suspended function");
|
||||
case PanicMsgIdBadSentinel:
|
||||
return buf_create_from_str("sentinel mismatch");
|
||||
}
|
||||
zig_unreachable();
|
||||
}
|
||||
|
@ -1419,6 +1421,27 @@ static void add_bounds_check(CodeGen *g, LLVMValueRef target_val,
|
|||
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
}
|
||||
|
||||
static void add_sentinel_check(CodeGen *g, LLVMValueRef sentinel_elem_ptr, ZigValue *sentinel) {
|
||||
LLVMValueRef expected_sentinel = gen_const_val(g, sentinel, "");
|
||||
|
||||
LLVMValueRef actual_sentinel = gen_load_untyped(g, sentinel_elem_ptr, 0, false, "");
|
||||
LLVMValueRef ok_bit;
|
||||
if (sentinel->type->id == ZigTypeIdFloat) {
|
||||
ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, actual_sentinel, expected_sentinel, "");
|
||||
} else {
|
||||
ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, actual_sentinel, expected_sentinel, "");
|
||||
}
|
||||
|
||||
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SentinelFail");
|
||||
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SentinelOk");
|
||||
LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, fail_block);
|
||||
gen_safety_crash(g, PanicMsgIdBadSentinel);
|
||||
|
||||
LLVMPositionBuilderAtEnd(g->builder, ok_block);
|
||||
}
|
||||
|
||||
static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) {
|
||||
LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type));
|
||||
LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, "");
|
||||
|
@ -5244,6 +5267,9 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
|
|||
|
||||
bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base);
|
||||
|
||||
ZigType *res_slice_ptr_type = instruction->base.value->type->data.structure.fields[slice_ptr_index]->type_entry;
|
||||
ZigValue *sentinel = res_slice_ptr_type->data.pointer.sentinel;
|
||||
|
||||
if (array_type->id == ZigTypeIdArray ||
|
||||
(array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle))
|
||||
{
|
||||
|
@ -5265,6 +5291,15 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
|
|||
LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type,
|
||||
array_type->data.array.len, false);
|
||||
add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end);
|
||||
|
||||
if (sentinel != nullptr) {
|
||||
LLVMValueRef indices[] = {
|
||||
LLVMConstNull(g->builtin_types.entry_usize->llvm_type),
|
||||
end_val,
|
||||
};
|
||||
LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
|
||||
add_sentinel_check(g, sentinel_elem_ptr, sentinel);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!type_has_bits(array_type)) {
|
||||
|
@ -5297,6 +5332,10 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
|
|||
|
||||
if (want_runtime_safety) {
|
||||
add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
|
||||
if (sentinel != nullptr) {
|
||||
LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &end_val, 1, "");
|
||||
add_sentinel_check(g, sentinel_elem_ptr, sentinel);
|
||||
}
|
||||
}
|
||||
|
||||
if (type_has_bits(array_type)) {
|
||||
|
@ -5337,18 +5376,24 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
|
|||
end_val = prev_end;
|
||||
}
|
||||
|
||||
LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, "");
|
||||
LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, "");
|
||||
|
||||
if (want_runtime_safety) {
|
||||
assert(prev_end);
|
||||
add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
|
||||
if (instruction->end) {
|
||||
add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end);
|
||||
|
||||
if (sentinel != nullptr) {
|
||||
LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &end_val, 1, "");
|
||||
add_sentinel_check(g, sentinel_elem_ptr, sentinel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, "");
|
||||
LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, "");
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, "");
|
||||
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, (unsigned)len_index, "");
|
||||
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, "");
|
||||
gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
|
||||
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, "");
|
||||
|
|
112
src/ir.cpp
112
src/ir.cpp
|
@ -2967,18 +2967,21 @@ static IrInstruction *ir_build_memcpy(IrBuilder *irb, Scope *scope, AstNode *sou
|
|||
}
|
||||
|
||||
static IrInstruction *ir_build_slice_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on, ResultLoc *result_loc)
|
||||
IrInstruction *ptr, IrInstruction *start, IrInstruction *end, IrInstruction *sentinel,
|
||||
bool safety_check_on, ResultLoc *result_loc)
|
||||
{
|
||||
IrInstructionSliceSrc *instruction = ir_build_instruction<IrInstructionSliceSrc>(irb, scope, source_node);
|
||||
instruction->ptr = ptr;
|
||||
instruction->start = start;
|
||||
instruction->end = end;
|
||||
instruction->sentinel = sentinel;
|
||||
instruction->safety_check_on = safety_check_on;
|
||||
instruction->result_loc = result_loc;
|
||||
|
||||
ir_ref_instruction(ptr, irb->current_basic_block);
|
||||
ir_ref_instruction(start, irb->current_basic_block);
|
||||
if (end) ir_ref_instruction(end, irb->current_basic_block);
|
||||
if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block);
|
||||
|
||||
return &instruction->base;
|
||||
}
|
||||
|
@ -8483,6 +8486,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node,
|
|||
AstNode *array_node = slice_expr->array_ref_expr;
|
||||
AstNode *start_node = slice_expr->start;
|
||||
AstNode *end_node = slice_expr->end;
|
||||
AstNode *sentinel_node = slice_expr->sentinel;
|
||||
|
||||
IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr, nullptr);
|
||||
if (ptr_value == irb->codegen->invalid_instruction)
|
||||
|
@ -8501,7 +8505,17 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node,
|
|||
end_value = nullptr;
|
||||
}
|
||||
|
||||
IrInstruction *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value, true, result_loc);
|
||||
IrInstruction *sentinel_value;
|
||||
if (sentinel_node) {
|
||||
sentinel_value = ir_gen_node(irb, sentinel_node, scope);
|
||||
if (sentinel_value == irb->codegen->invalid_instruction)
|
||||
return irb->codegen->invalid_instruction;
|
||||
} else {
|
||||
sentinel_value = nullptr;
|
||||
}
|
||||
|
||||
IrInstruction *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value,
|
||||
sentinel_value, true, result_loc);
|
||||
return ir_lval_wrap(irb, scope, slice, lval, result_loc);
|
||||
}
|
||||
|
||||
|
@ -10533,6 +10547,18 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
|
|||
result.id = ConstCastResultIdInvalid;
|
||||
return result;
|
||||
}
|
||||
bool ok_sentinels =
|
||||
wanted_ptr_type->data.pointer.sentinel == nullptr ||
|
||||
(actual_ptr_type->data.pointer.sentinel != nullptr &&
|
||||
const_values_equal(ira->codegen, wanted_ptr_type->data.pointer.sentinel,
|
||||
actual_ptr_type->data.pointer.sentinel));
|
||||
if (!ok_sentinels) {
|
||||
result.id = ConstCastResultIdPtrSentinel;
|
||||
result.data.bad_ptr_sentinel = allocate_nonzero<ConstCastPtrSentinel>(1);
|
||||
result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type;
|
||||
result.data.bad_ptr_sentinel->actual_type = actual_ptr_type;
|
||||
return result;
|
||||
}
|
||||
if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&
|
||||
(!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&
|
||||
actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host &&
|
||||
|
@ -14441,6 +14467,32 @@ static bool optional_value_is_null(ZigValue *val) {
|
|||
}
|
||||
}
|
||||
|
||||
static void set_optional_value_to_null(ZigValue *val) {
|
||||
assert(val->special == ConstValSpecialStatic);
|
||||
if (val->type->id == ZigTypeIdNull) return; // nothing to do
|
||||
assert(val->type->id == ZigTypeIdOptional);
|
||||
if (get_codegen_ptr_type(val->type) != nullptr) {
|
||||
val->data.x_ptr.special = ConstPtrSpecialNull;
|
||||
} else if (is_opt_err_set(val->type)) {
|
||||
val->data.x_err_set = nullptr;
|
||||
} else {
|
||||
val->data.x_optional = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_optional_payload(ZigValue *opt_val, ZigValue *payload) {
|
||||
assert(opt_val->special == ConstValSpecialStatic);
|
||||
assert(opt_val->type->id == ZigTypeIdOptional);
|
||||
if (payload == nullptr) {
|
||||
set_optional_value_to_null(opt_val);
|
||||
} else if (is_opt_err_set(opt_val->type)) {
|
||||
assert(payload->type->id == ZigTypeIdErrorSet);
|
||||
opt_val->data.x_err_set = payload->data.x_err_set;
|
||||
} else {
|
||||
opt_val->data.x_optional = payload;
|
||||
}
|
||||
}
|
||||
|
||||
static IrInstruction *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_type,
|
||||
ZigValue *op1_val, ZigValue *op2_val, IrInstructionBinOp *bin_op_instruction, IrBinOp op_id,
|
||||
bool one_possible_value) {
|
||||
|
@ -19313,6 +19365,20 @@ static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_ali
|
|||
ptr_type->data.pointer.sentinel);
|
||||
}
|
||||
|
||||
static ZigType *adjust_ptr_sentinel(CodeGen *g, ZigType *ptr_type, ZigValue *new_sentinel) {
|
||||
assert(ptr_type->id == ZigTypeIdPointer);
|
||||
return get_pointer_to_type_extra2(g,
|
||||
ptr_type->data.pointer.child_type,
|
||||
ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
|
||||
ptr_type->data.pointer.ptr_len,
|
||||
ptr_type->data.pointer.explicit_alignment,
|
||||
ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes,
|
||||
ptr_type->data.pointer.allow_zero,
|
||||
ptr_type->data.pointer.vector_index,
|
||||
ptr_type->data.pointer.inferred_struct_field,
|
||||
new_sentinel);
|
||||
}
|
||||
|
||||
static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) {
|
||||
assert(is_slice(slice_type));
|
||||
ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index]->type_entry,
|
||||
|
@ -22691,7 +22757,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent
|
|||
fields[6]->special = ConstValSpecialStatic;
|
||||
if (attrs_type->data.pointer.child_type->id != ZigTypeIdOpaque) {
|
||||
fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type);
|
||||
fields[6]->data.x_optional = attrs_type->data.pointer.sentinel;
|
||||
set_optional_payload(fields[6], attrs_type->data.pointer.sentinel);
|
||||
} else {
|
||||
fields[6]->type = ira->codegen->builtin_types.entry_null;
|
||||
}
|
||||
|
@ -25051,50 +25117,72 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction
|
|||
end = nullptr;
|
||||
}
|
||||
|
||||
ZigType *return_type;
|
||||
ZigType *non_sentinel_slice_ptr_type;
|
||||
ZigType *elem_type;
|
||||
|
||||
if (array_type->id == ZigTypeIdArray) {
|
||||
elem_type = array_type->data.array.child_type;
|
||||
bool is_comptime_const = ptr_ptr->value->special == ConstValSpecialStatic &&
|
||||
ptr_ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst;
|
||||
ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type,
|
||||
non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type,
|
||||
ptr_ptr_type->data.pointer.is_const || is_comptime_const,
|
||||
ptr_ptr_type->data.pointer.is_volatile,
|
||||
PtrLenUnknown,
|
||||
ptr_ptr_type->data.pointer.explicit_alignment, 0, 0, false);
|
||||
return_type = get_slice_type(ira->codegen, slice_ptr_type);
|
||||
} else if (array_type->id == ZigTypeIdPointer) {
|
||||
if (array_type->data.pointer.ptr_len == PtrLenSingle) {
|
||||
ZigType *main_type = array_type->data.pointer.child_type;
|
||||
if (main_type->id == ZigTypeIdArray) {
|
||||
ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen,
|
||||
main_type->data.pointer.child_type,
|
||||
elem_type = main_type->data.pointer.child_type;
|
||||
non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen,
|
||||
elem_type,
|
||||
array_type->data.pointer.is_const, array_type->data.pointer.is_volatile,
|
||||
PtrLenUnknown,
|
||||
array_type->data.pointer.explicit_alignment, 0, 0, false);
|
||||
return_type = get_slice_type(ira->codegen, slice_ptr_type);
|
||||
} else {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer"));
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
} else {
|
||||
elem_type = array_type->data.pointer.child_type;
|
||||
if (array_type->data.pointer.ptr_len == PtrLenC) {
|
||||
array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown);
|
||||
}
|
||||
return_type = get_slice_type(ira->codegen, array_type);
|
||||
ZigType *maybe_sentineled_slice_ptr_type = array_type;
|
||||
non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr);
|
||||
if (!end) {
|
||||
ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value"));
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
}
|
||||
} else if (is_slice(array_type)) {
|
||||
ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry;
|
||||
return_type = get_slice_type(ira->codegen, ptr_type);
|
||||
ZigType *maybe_sentineled_slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry;
|
||||
non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr);
|
||||
elem_type = non_sentinel_slice_ptr_type->data.pointer.child_type;
|
||||
} else {
|
||||
ir_add_error(ira, &instruction->base,
|
||||
buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name)));
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
|
||||
ZigType *return_type;
|
||||
ZigValue *sentinel_val = nullptr;
|
||||
if (instruction->sentinel) {
|
||||
IrInstruction *uncasted_sentinel = instruction->sentinel->child;
|
||||
if (type_is_invalid(uncasted_sentinel->value->type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
IrInstruction *sentinel = ir_implicit_cast(ira, uncasted_sentinel, elem_type);
|
||||
if (type_is_invalid(sentinel->value->type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
sentinel_val = ir_resolve_const(ira, sentinel, UndefBad);
|
||||
if (sentinel_val == nullptr)
|
||||
return ira->codegen->invalid_instruction;
|
||||
ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, sentinel_val);
|
||||
return_type = get_slice_type(ira->codegen, slice_ptr_type);
|
||||
} else {
|
||||
return_type = get_slice_type(ira->codegen, non_sentinel_slice_ptr_type);
|
||||
}
|
||||
|
||||
if (instr_is_comptime(ptr_ptr) &&
|
||||
value_is_comptime(casted_start->value) &&
|
||||
(!end || value_is_comptime(end->value)))
|
||||
|
|
|
@ -2723,7 +2723,7 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
|
|||
}
|
||||
|
||||
// SuffixOp
|
||||
// <- LBRACKET Expr (DOT2 Expr?)? RBRACKET
|
||||
// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET
|
||||
// / DOT IDENTIFIER
|
||||
// / DOTASTERISK
|
||||
// / DOTQUESTIONMARK
|
||||
|
@ -2733,12 +2733,17 @@ static AstNode *ast_parse_suffix_op(ParseContext *pc) {
|
|||
AstNode *start = ast_expect(pc, ast_parse_expr);
|
||||
AstNode *end = nullptr;
|
||||
if (eat_token_if(pc, TokenIdEllipsis2) != nullptr) {
|
||||
AstNode *sentinel = nullptr;
|
||||
end = ast_parse_expr(pc);
|
||||
if (eat_token_if(pc, TokenIdColon) != nullptr) {
|
||||
sentinel = ast_parse_expr(pc);
|
||||
}
|
||||
expect_token(pc, TokenIdRBracket);
|
||||
|
||||
AstNode *res = ast_create_node(pc, NodeTypeSliceExpr, lbracket);
|
||||
res->data.slice_expr.start = start;
|
||||
res->data.slice_expr.end = end;
|
||||
res->data.slice_expr.sentinel = sentinel;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -3041,6 +3046,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
|
|||
visit_field(&node->data.slice_expr.array_ref_expr, visit, context);
|
||||
visit_field(&node->data.slice_expr.start, visit, context);
|
||||
visit_field(&node->data.slice_expr.end, visit, context);
|
||||
visit_field(&node->data.slice_expr.sentinel, visit, context);
|
||||
break;
|
||||
case NodeTypeFieldAccessExpr:
|
||||
visit_field(&node->data.field_access_expr.struct_expr, visit, context);
|
||||
|
|
|
@ -2,6 +2,17 @@ const tests = @import("tests.zig");
|
|||
const builtin = @import("builtin");
|
||||
|
||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
cases.add("slice sentinel mismatch",
|
||||
\\fn foo() [:0]u8 {
|
||||
\\ var x: []u8 = undefined;
|
||||
\\ return x;
|
||||
\\}
|
||||
\\comptime { _ = foo; }
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:3:12: error: expected type '[:0]u8', found '[]u8'",
|
||||
"tmp.zig:3:12: note: destination pointer requires a terminating '0' sentinel",
|
||||
});
|
||||
|
||||
cases.add("intToPtr with misaligned address",
|
||||
\\pub fn main() void {
|
||||
\\ var y = @intToPtr([*]align(4) u8, 5);
|
||||
|
|
|
@ -1,12 +1,85 @@
|
|||
const tests = @import("tests.zig");
|
||||
|
||||
pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
cases.addRuntimeSafety("intToPtr with misaligned address",
|
||||
cases.addRuntimeSafety("slice sentinel mismatch - optional pointers",
|
||||
\\const std = @import("std");
|
||||
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
|
||||
\\ if (@import("std").mem.eql(u8, message, "incorrect alignment")) {
|
||||
\\ @import("std").os.exit(126); // good
|
||||
\\ if (std.mem.eql(u8, message, "sentinel mismatch")) {
|
||||
\\ std.process.exit(126); // good
|
||||
\\ }
|
||||
\\ @import("std").os.exit(0); // test failed
|
||||
\\ std.process.exit(0); // test failed
|
||||
\\}
|
||||
\\pub fn main() void {
|
||||
\\ var buf: [4]?*i32 = undefined;
|
||||
\\ const slice = buf[0..3 :null];
|
||||
\\}
|
||||
);
|
||||
|
||||
cases.addRuntimeSafety("slice sentinel mismatch - floats",
|
||||
\\const std = @import("std");
|
||||
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
|
||||
\\ if (std.mem.eql(u8, message, "sentinel mismatch")) {
|
||||
\\ std.process.exit(126); // good
|
||||
\\ }
|
||||
\\ std.process.exit(0); // test failed
|
||||
\\}
|
||||
\\pub fn main() void {
|
||||
\\ var buf: [4]f32 = undefined;
|
||||
\\ const slice = buf[0..3 :1.2];
|
||||
\\}
|
||||
);
|
||||
|
||||
cases.addRuntimeSafety("pointer slice sentinel mismatch",
|
||||
\\const std = @import("std");
|
||||
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
|
||||
\\ if (std.mem.eql(u8, message, "sentinel mismatch")) {
|
||||
\\ std.process.exit(126); // good
|
||||
\\ }
|
||||
\\ std.process.exit(0); // test failed
|
||||
\\}
|
||||
\\pub fn main() void {
|
||||
\\ var buf: [4]u8 = undefined;
|
||||
\\ const ptr = buf[0..].ptr;
|
||||
\\ const slice = ptr[0..3 :0];
|
||||
\\}
|
||||
);
|
||||
|
||||
cases.addRuntimeSafety("slice slice sentinel mismatch",
|
||||
\\const std = @import("std");
|
||||
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
|
||||
\\ if (std.mem.eql(u8, message, "sentinel mismatch")) {
|
||||
\\ std.process.exit(126); // good
|
||||
\\ }
|
||||
\\ std.process.exit(0); // test failed
|
||||
\\}
|
||||
\\pub fn main() void {
|
||||
\\ var buf: [4]u8 = undefined;
|
||||
\\ const slice = buf[0..];
|
||||
\\ const slice2 = slice[0..3 :0];
|
||||
\\}
|
||||
);
|
||||
|
||||
cases.addRuntimeSafety("array slice sentinel mismatch",
|
||||
\\const std = @import("std");
|
||||
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
|
||||
\\ if (std.mem.eql(u8, message, "sentinel mismatch")) {
|
||||
\\ std.process.exit(126); // good
|
||||
\\ }
|
||||
\\ std.process.exit(0); // test failed
|
||||
\\}
|
||||
\\pub fn main() void {
|
||||
\\ var buf: [4]u8 = undefined;
|
||||
\\ const slice = buf[0..3 :0];
|
||||
\\}
|
||||
);
|
||||
|
||||
cases.addRuntimeSafety("intToPtr with misaligned address",
|
||||
\\const std = @import("std");
|
||||
\\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
|
||||
\\ if (std.mem.eql(u8, message, "incorrect alignment")) {
|
||||
\\ std.os.exit(126); // good
|
||||
\\ }
|
||||
\\ std.os.exit(0); // test failed
|
||||
\\}
|
||||
\\pub fn main() void {
|
||||
\\ var x: usize = 5;
|
||||
|
|
|
@ -78,3 +78,22 @@ test "access len index of sentinel-terminated slice" {
|
|||
S.doTheTest();
|
||||
comptime S.doTheTest();
|
||||
}
|
||||
|
||||
test "obtaining a null terminated slice" {
|
||||
// here we have a normal array
|
||||
var buf: [50]u8 = undefined;
|
||||
|
||||
buf[0] = 'a';
|
||||
buf[1] = 'b';
|
||||
buf[2] = 'c';
|
||||
buf[3] = 0;
|
||||
|
||||
// now we obtain a null terminated slice:
|
||||
const ptr = buf[0..3 :0];
|
||||
|
||||
var runtime_len: usize = 3;
|
||||
const ptr2 = buf[0..runtime_len :0];
|
||||
// ptr2 is a null-terminated slice
|
||||
comptime expect(@TypeOf(ptr2) == [:0]u8);
|
||||
comptime expect(@TypeOf(ptr2[0..2]) == []u8);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue