authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-12-18 16:43:56-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-12-20 18:28:56-05:00
log26f3c2d0614f4fb37752b1931cb0b43aed2696d2
tree18e51d6ed267ded8c6d87b5158c8866dcf1a2bf6
parent51cbd968203f348051b8c2bdc005ca5294a79ceb
signature Commit is signed but in an unrecognized format.

fix std.mem.addNullByte and implement sentinel slicing

see #3770

9 files changed, 119 insertions(+), 39 deletions(-)

lib/std/buffer.zig+2-2
......@@ -82,11 +82,11 @@ pub const Buffer = struct {
8282 }
8383
8484 pub fn toSlice(self: Buffer) [:0]u8 {
85 return self.list.toSlice()[0..self.len()];
85 return self.list.toSlice()[0..self.len() :0];
8686 }
8787
8888 pub fn toSliceConst(self: Buffer) [:0]const u8 {
89 return self.list.toSliceConst()[0..self.len()];
89 return self.list.toSliceConst()[0..self.len() :0];
9090 }
9191
9292 pub fn shrink(self: *Buffer, new_len: usize) void {
lib/std/cstr.zig+10-2
......@@ -31,13 +31,21 @@ fn testCStrFnsImpl() void {
3131 testing.expect(mem.len(u8, "123456789") == 9);
3232}
3333
34/// Returns a mutable slice with 1 more byte of length which is a null byte.
34/// Returns a mutable, null-terminated slice with the same length as `slice`.
3535/// Caller owns the returned memory.
3636pub fn addNullByte(allocator: *mem.Allocator, slice: []const u8) ![:0]u8 {
3737 const result = try allocator.alloc(u8, slice.len + 1);
3838 mem.copy(u8, result, slice);
3939 result[slice.len] = 0;
40 return result;
40 return result[0..slice.len :0];
41}
42
43test "addNullByte" {
44 var buf: [30]u8 = undefined;
45 const allocator = &std.heap.FixedBufferAllocator.init(&buf).allocator;
46 const slice = try addNullByte(allocator, "hello"[0..4]);
47 testing.expect(slice.len == 4);
48 testing.expect(slice[4] == 0);
4149}
4250
4351pub const NullTerminated2DArray = struct {
lib/std/fs.zig+4-5
......@@ -221,16 +221,16 @@ pub const AtomicFile = struct {
221221 }
222222
223223 tmp_path_buf[tmp_path_len] = 0;
224 const tmp_path_slice = tmp_path_buf[0..tmp_path_len :0];
224225
225226 const my_cwd = cwd();
226227
227228 while (true) {
228229 try crypto.randomBytes(rand_buf[0..]);
229 b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], &rand_buf);
230 b64_fs_encoder.encode(tmp_path_slice[dirname_component_len..tmp_path_len], &rand_buf);
230231
231 // TODO https://github.com/ziglang/zig/issues/3770 to clean up this @ptrCast
232232 const file = my_cwd.createFileC(
233 @ptrCast([*:0]u8, &tmp_path_buf),
233 tmp_path_slice,
234234 .{ .mode = mode, .exclusive = true },
235235 ) catch |err| switch (err) {
236236 error.PathAlreadyExists => continue,
......@@ -1488,8 +1488,7 @@ pub fn openSelfExe() OpenSelfExeError!File {
14881488 var buf: [MAX_PATH_BYTES]u8 = undefined;
14891489 const self_exe_path = try selfExePath(&buf);
14901490 buf[self_exe_path.len] = 0;
1491 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
1492 return openFileAbsoluteC(@ptrCast([*:0]u8, self_exe_path.ptr), .{});
1491 return openFileAbsoluteC(self_exe_path[0..self_exe_path.len :0].ptr, .{});
14931492}
14941493
14951494test "openSelfExe" {
lib/std/mem.zig+3-2
......@@ -231,9 +231,10 @@ pub const Allocator = struct {
231231 pub fn free(self: *Allocator, memory: var) void {
232232 const Slice = @typeInfo(@TypeOf(memory)).Pointer;
233233 const bytes = @sliceToBytes(memory);
234 if (bytes.len == 0) return;
234 const bytes_len = bytes.len + @boolToInt(Slice.sentinel != null);
235 if (bytes_len == 0) return;
235236 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr));
236 const shrink_result = self.shrinkFn(self, non_const_ptr[0..bytes.len], Slice.alignment, 0, 1);
237 const shrink_result = self.shrinkFn(self, non_const_ptr[0..bytes_len], Slice.alignment, 0, 1);
237238 assert(shrink_result.len == 0);
238239 }
239240};
lib/std/os.zig+9-16
......@@ -805,9 +805,9 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e
805805 mem.copy(u8, &path_buf, search_path);
806806 path_buf[search_path.len] = '/';
807807 mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice);
808 path_buf[search_path.len + file_slice.len + 1] = 0;
809 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
810 err = execveC(@ptrCast([*:0]u8, &path_buf), child_argv, envp);
808 const path_len = search_path.len + file_slice.len + 1;
809 path_buf[path_len] = 0;
810 err = execveC(path_buf[0..path_len :0].ptr, child_argv, envp);
811811 switch (err) {
812812 error.AccessDenied => seen_eacces = true,
813813 error.FileNotFound, error.NotDir => {},
......@@ -841,18 +841,14 @@ pub fn execvpe(
841841 const arg_buf = try allocator.alloc(u8, arg.len + 1);
842842 @memcpy(arg_buf.ptr, arg.ptr, arg.len);
843843 arg_buf[arg.len] = 0;
844
845 // TODO avoid @ptrCast using slice syntax with https://github.com/ziglang/zig/issues/3770
846 argv_buf[i] = @ptrCast([*:0]u8, arg_buf.ptr);
844 argv_buf[i] = arg_buf[0..arg.len :0].ptr;
847845 }
848846 argv_buf[argv_slice.len] = null;
847 const argv_ptr = argv_buf[0..argv_slice.len :null].ptr;
849848
850849 const envp_buf = try createNullDelimitedEnvMap(allocator, env_map);
851850 defer freeNullDelimitedEnvMap(allocator, envp_buf);
852851
853 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
854 const argv_ptr = @ptrCast([*:null]?[*:0]u8, argv_buf.ptr);
855
856852 return execvpeC(argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr);
857853}
858854
......@@ -869,16 +865,13 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.
869865 @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len);
870866 env_buf[pair.key.len] = '=';
871867 @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len);
872 env_buf[env_buf.len - 1] = 0;
873
874 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
875 envp_buf[i] = @ptrCast([*:0]u8, env_buf.ptr);
868 const len = env_buf.len - 1;
869 env_buf[len] = 0;
870 envp_buf[i] = env_buf[0..len :0].ptr;
876871 }
877872 assert(i == envp_count);
878873 }
879 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770
880 assert(envp_buf[envp_count] == null);
881 return @ptrCast([*:null]?[*:0]u8, envp_buf.ptr)[0..envp_count];
874 return envp_buf[0..envp_count :null];
882875}
883876
884877pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) void {
src/all_types.hpp+2
......@@ -810,6 +810,7 @@ struct AstNodeSliceExpr {
810810 AstNode *array_ref_expr;
811811 AstNode *start;
812812 AstNode *end;
813 AstNode *sentinel; // can be null
813814};
814815
815816struct AstNodeFieldAccessExpr {
......@@ -3388,6 +3389,7 @@ struct IrInstructionSliceSrc {
33883389 IrInstruction *ptr;
33893390 IrInstruction *start;
33903391 IrInstruction *end;
3392 IrInstruction *sentinel;
33913393 ResultLoc *result_loc;
33923394};
33933395
src/ir.cpp+71-11
......@@ -2967,18 +2967,21 @@ static IrInstruction *ir_build_memcpy(IrBuilder *irb, Scope *scope, AstNode *sou
29672967}
29682968
29692969static IrInstruction *ir_build_slice_src(IrBuilder *irb, Scope *scope, AstNode *source_node,
2970 IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on, ResultLoc *result_loc)
2970 IrInstruction *ptr, IrInstruction *start, IrInstruction *end, IrInstruction *sentinel,
2971 bool safety_check_on, ResultLoc *result_loc)
29712972{
29722973 IrInstructionSliceSrc *instruction = ir_build_instruction<IrInstructionSliceSrc>(irb, scope, source_node);
29732974 instruction->ptr = ptr;
29742975 instruction->start = start;
29752976 instruction->end = end;
2977 instruction->sentinel = sentinel;
29762978 instruction->safety_check_on = safety_check_on;
29772979 instruction->result_loc = result_loc;
29782980
29792981 ir_ref_instruction(ptr, irb->current_basic_block);
29802982 ir_ref_instruction(start, irb->current_basic_block);
29812983 if (end) ir_ref_instruction(end, irb->current_basic_block);
2984 if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block);
29822985
29832986 return &instruction->base;
29842987}
......@@ -8483,6 +8486,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node,
84838486 AstNode *array_node = slice_expr->array_ref_expr;
84848487 AstNode *start_node = slice_expr->start;
84858488 AstNode *end_node = slice_expr->end;
8489 AstNode *sentinel_node = slice_expr->sentinel;
84868490
84878491 IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr, nullptr);
84888492 if (ptr_value == irb->codegen->invalid_instruction)
......@@ -8501,7 +8505,17 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node,
85018505 end_value = nullptr;
85028506 }
85038507
8504 IrInstruction *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value, true, result_loc);
8508 IrInstruction *sentinel_value;
8509 if (sentinel_node) {
8510 sentinel_value = ir_gen_node(irb, sentinel_node, scope);
8511 if (sentinel_value == irb->codegen->invalid_instruction)
8512 return irb->codegen->invalid_instruction;
8513 } else {
8514 sentinel_value = nullptr;
8515 }
8516
8517 IrInstruction *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value,
8518 sentinel_value, true, result_loc);
85058519 return ir_lval_wrap(irb, scope, slice, lval, result_loc);
85068520}
85078521
......@@ -10533,6 +10547,18 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
1053310547 result.id = ConstCastResultIdInvalid;
1053410548 return result;
1053510549 }
10550 bool ok_sentinels =
10551 wanted_ptr_type->data.pointer.sentinel == nullptr ||
10552 (actual_ptr_type->data.pointer.sentinel != nullptr &&
10553 const_values_equal(ira->codegen, wanted_ptr_type->data.pointer.sentinel,
10554 actual_ptr_type->data.pointer.sentinel));
10555 if (!ok_sentinels) {
10556 result.id = ConstCastResultIdPtrSentinel;
10557 result.data.bad_ptr_sentinel = allocate_nonzero<ConstCastPtrSentinel>(1);
10558 result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type;
10559 result.data.bad_ptr_sentinel->actual_type = actual_ptr_type;
10560 return result;
10561 }
1053610562 if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&
1053710563 (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&
1053810564 actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host &&
......@@ -19313,6 +19339,20 @@ static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_ali
1931319339 ptr_type->data.pointer.sentinel);
1931419340}
1931519341
19342static ZigType *adjust_ptr_sentinel(CodeGen *g, ZigType *ptr_type, ZigValue *new_sentinel) {
19343 assert(ptr_type->id == ZigTypeIdPointer);
19344 return get_pointer_to_type_extra2(g,
19345 ptr_type->data.pointer.child_type,
19346 ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile,
19347 ptr_type->data.pointer.ptr_len,
19348 ptr_type->data.pointer.explicit_alignment,
19349 ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes,
19350 ptr_type->data.pointer.allow_zero,
19351 ptr_type->data.pointer.vector_index,
19352 ptr_type->data.pointer.inferred_struct_field,
19353 new_sentinel);
19354}
19355
1931619356static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) {
1931719357 assert(is_slice(slice_type));
1931819358 ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index]->type_entry,
......@@ -25051,50 +25091,70 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction
2505125091 end = nullptr;
2505225092 }
2505325093
25054 ZigType *return_type;
25094 ZigType *non_sentinel_slice_ptr_type;
25095 ZigType *elem_type;
2505525096
2505625097 if (array_type->id == ZigTypeIdArray) {
25098 elem_type = array_type->data.array.child_type;
2505725099 bool is_comptime_const = ptr_ptr->value->special == ConstValSpecialStatic &&
2505825100 ptr_ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst;
25059 ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type,
25101 non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type,
2506025102 ptr_ptr_type->data.pointer.is_const || is_comptime_const,
2506125103 ptr_ptr_type->data.pointer.is_volatile,
2506225104 PtrLenUnknown,
2506325105 ptr_ptr_type->data.pointer.explicit_alignment, 0, 0, false);
25064 return_type = get_slice_type(ira->codegen, slice_ptr_type);
2506525106 } else if (array_type->id == ZigTypeIdPointer) {
2506625107 if (array_type->data.pointer.ptr_len == PtrLenSingle) {
2506725108 ZigType *main_type = array_type->data.pointer.child_type;
2506825109 if (main_type->id == ZigTypeIdArray) {
25069 ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen,
25070 main_type->data.pointer.child_type,
25110 elem_type = main_type->data.pointer.child_type;
25111 non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen,
25112 elem_type,
2507125113 array_type->data.pointer.is_const, array_type->data.pointer.is_volatile,
2507225114 PtrLenUnknown,
2507325115 array_type->data.pointer.explicit_alignment, 0, 0, false);
25074 return_type = get_slice_type(ira->codegen, slice_ptr_type);
2507525116 } else {
2507625117 ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer"));
2507725118 return ira->codegen->invalid_instruction;
2507825119 }
2507925120 } else {
25121 elem_type = array_type->data.pointer.child_type;
2508025122 if (array_type->data.pointer.ptr_len == PtrLenC) {
2508125123 array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown);
2508225124 }
25083 return_type = get_slice_type(ira->codegen, array_type);
25125 non_sentinel_slice_ptr_type = array_type;
2508425126 if (!end) {
2508525127 ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value"));
2508625128 return ira->codegen->invalid_instruction;
2508725129 }
2508825130 }
2508925131 } else if (is_slice(array_type)) {
25090 ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry;
25091 return_type = get_slice_type(ira->codegen, ptr_type);
25132 non_sentinel_slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry;
25133 elem_type = non_sentinel_slice_ptr_type->data.pointer.child_type;
2509225134 } else {
2509325135 ir_add_error(ira, &instruction->base,
2509425136 buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name)));
2509525137 return ira->codegen->invalid_instruction;
2509625138 }
2509725139
25140 ZigType *return_type;
25141 ZigValue *sentinel_val = nullptr;
25142 if (instruction->sentinel) {
25143 IrInstruction *uncasted_sentinel = instruction->sentinel->child;
25144 if (type_is_invalid(uncasted_sentinel->value->type))
25145 return ira->codegen->invalid_instruction;
25146 IrInstruction *sentinel = ir_implicit_cast(ira, uncasted_sentinel, elem_type);
25147 if (type_is_invalid(sentinel->value->type))
25148 return ira->codegen->invalid_instruction;
25149 sentinel_val = ir_resolve_const(ira, sentinel, UndefBad);
25150 if (sentinel_val == nullptr)
25151 return ira->codegen->invalid_instruction;
25152 ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, sentinel_val);
25153 return_type = get_slice_type(ira->codegen, slice_ptr_type);
25154 } else {
25155 return_type = get_slice_type(ira->codegen, non_sentinel_slice_ptr_type);
25156 }
25157
2509825158 if (instr_is_comptime(ptr_ptr) &&
2509925159 value_is_comptime(casted_start->value) &&
2510025160 (!end || value_is_comptime(end->value)))
src/parser.cpp+7-1
......@@ -2723,7 +2723,7 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
27232723}
27242724
27252725// SuffixOp
2726// <- LBRACKET Expr (DOT2 Expr?)? RBRACKET
2726// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET
27272727// / DOT IDENTIFIER
27282728// / DOTASTERISK
27292729// / DOTQUESTIONMARK
......@@ -2733,12 +2733,17 @@ static AstNode *ast_parse_suffix_op(ParseContext *pc) {
27332733 AstNode *start = ast_expect(pc, ast_parse_expr);
27342734 AstNode *end = nullptr;
27352735 if (eat_token_if(pc, TokenIdEllipsis2) != nullptr) {
2736 AstNode *sentinel = nullptr;
27362737 end = ast_parse_expr(pc);
2738 if (eat_token_if(pc, TokenIdColon) != nullptr) {
2739 sentinel = ast_parse_expr(pc);
2740 }
27372741 expect_token(pc, TokenIdRBracket);
27382742
27392743 AstNode *res = ast_create_node(pc, NodeTypeSliceExpr, lbracket);
27402744 res->data.slice_expr.start = start;
27412745 res->data.slice_expr.end = end;
2746 res->data.slice_expr.sentinel = sentinel;
27422747 return res;
27432748 }
27442749
......@@ -3041,6 +3046,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
30413046 visit_field(&node->data.slice_expr.array_ref_expr, visit, context);
30423047 visit_field(&node->data.slice_expr.start, visit, context);
30433048 visit_field(&node->data.slice_expr.end, visit, context);
3049 visit_field(&node->data.slice_expr.sentinel, visit, context);
30443050 break;
30453051 case NodeTypeFieldAccessExpr:
30463052 visit_field(&node->data.field_access_expr.struct_expr, visit, context);
test/compile_errors.zig+11
......@@ -2,6 +2,17 @@ const tests = @import("tests.zig");
22const builtin = @import("builtin");
33
44pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.add("slice sentinel mismatch",
6 \\fn foo() [:0]u8 {
7 \\ var x: []u8 = undefined;
8 \\ return x;
9 \\}
10 \\comptime { _ = foo; }
11 , &[_][]const u8{
12 "tmp.zig:3:12: error: expected type '[:0]u8', found '[]u8'",
13 "tmp.zig:3:12: note: destination pointer requires a terminating '0' sentinel",
14 });
15
516 cases.add("intToPtr with misaligned address",
617 \\pub fn main() void {
718 \\ var y = @intToPtr([*]align(4) u8, 5);