authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-14 03:26:33-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-11-21 20:43:41-05:00
log1aa978f32e88b4c83fde95f62938c97c7c22164c
tree75f26a3f9cd18bd980b6fc1c5cab8a4f7f10e2d8
parente3404e3c78307092e849dc0609f77932b263e3fc
signaturelock-open Commit is signed but in an unrecognized format.

implement null terminated pointers


10 files changed, 75 insertions(+), 3 deletions(-)

lib/std/builtin.zig+1
...@@ -144,6 +144,7 @@ pub const TypeInfo = union(enum) {...@@ -144,6 +144,7 @@ pub const TypeInfo = union(enum) {
144 alignment: comptime_int,144 alignment: comptime_int,
145 child: type,145 child: type,
146 is_allowzero: bool,146 is_allowzero: bool,
147 is_null_terminated: bool,
147148
148 /// This data structure is used by the Zig language code generation and149 /// This data structure is used by the Zig language code generation and
149 /// therefore must be kept in sync with the compiler implementation.150 /// therefore must be kept in sync with the compiler implementation.
lib/std/meta.zig+1
...@@ -558,3 +558,4 @@ pub fn refAllDecls(comptime T: type) void {...@@ -558,3 +558,4 @@ pub fn refAllDecls(comptime T: type) void {
558 if (!builtin.is_test) return;558 if (!builtin.is_test) return;
559 _ = declarations(T);559 _ = declarations(T);
560}560}
561
src/all_types.hpp+3
...@@ -55,6 +55,7 @@ enum PtrLen {...@@ -55,6 +55,7 @@ enum PtrLen {
55 PtrLenUnknown,55 PtrLenUnknown,
56 PtrLenSingle,56 PtrLenSingle,
57 PtrLenC,57 PtrLenC,
58 PtrLenNull,
58};59};
5960
60// This one corresponds to the builtin.zig enum.61// This one corresponds to the builtin.zig enum.
...@@ -825,6 +826,7 @@ struct AstNodePointerType {...@@ -825,6 +826,7 @@ struct AstNodePointerType {
825 Token *allow_zero_token;826 Token *allow_zero_token;
826 bool is_const;827 bool is_const;
827 bool is_volatile;828 bool is_volatile;
829 bool is_null_terminated;
828};830};
829831
830struct AstNodeInferredArrayType {832struct AstNodeInferredArrayType {
...@@ -838,6 +840,7 @@ struct AstNodeArrayType {...@@ -838,6 +840,7 @@ struct AstNodeArrayType {
838 Token *allow_zero_token;840 Token *allow_zero_token;
839 bool is_const;841 bool is_const;
840 bool is_volatile;842 bool is_volatile;
843 bool is_null_terminated;
841};844};
842845
843struct AstNodeUsingNamespace {846struct AstNodeUsingNamespace {
src/analyze.cpp+3-1
...@@ -460,6 +460,8 @@ static const char *ptr_len_to_star_str(PtrLen ptr_len) {...@@ -460,6 +460,8 @@ static const char *ptr_len_to_star_str(PtrLen ptr_len) {
460 return "[*]";460 return "[*]";
461 case PtrLenC:461 case PtrLenC:
462 return "[*c]";462 return "[*c]";
463 case PtrLenNull:
464 return "[*]null ";
463 }465 }
464 zig_unreachable();466 zig_unreachable();
465}467}
...@@ -7032,7 +7034,7 @@ uint32_t type_id_hash(TypeId x) {...@@ -7032,7 +7034,7 @@ uint32_t type_id_hash(TypeId x) {
7032 return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type);7034 return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type);
7033 case ZigTypeIdPointer:7035 case ZigTypeIdPointer:
7034 return hash_ptr(x.data.pointer.child_type) +7036 return hash_ptr(x.data.pointer.child_type) +
7035 ((x.data.pointer.ptr_len == PtrLenSingle) ? (uint32_t)1120226602 : (uint32_t)3200913342) +7037 (uint32_t)x.data.pointer.ptr_len * 1120226602u +
7036 (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +7038 (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) +
7037 (x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) +7039 (x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) +
7038 (x.data.pointer.allow_zero ? (uint32_t)3324284834 : (uint32_t)3584904923) +7040 (x.data.pointer.allow_zero ? (uint32_t)3324284834 : (uint32_t)3584904923) +
src/dump_analysis.cpp+4
...@@ -992,6 +992,10 @@ static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) {...@@ -992,6 +992,10 @@ static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) {
992 jw_object_field(jw, "len");992 jw_object_field(jw, "len");
993 jw_int(jw, 3);993 jw_int(jw, 3);
994 break;994 break;
995 case PtrLenNull:
996 jw_object_field(jw, "len");
997 jw_int(jw, 4);
998 break;
995 }999 }
996 anal_dump_pointer_attrs(ctx, ty);1000 anal_dump_pointer_attrs(ctx, ty);
997 break;1001 break;
src/ir.cpp+25-2
...@@ -6043,13 +6043,25 @@ static PtrLen star_token_to_ptr_len(TokenId token_id) {...@@ -6043,13 +6043,25 @@ static PtrLen star_token_to_ptr_len(TokenId token_id) {
60436043
6044static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) {6044static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) {
6045 assert(node->type == NodeTypePointerType);6045 assert(node->type == NodeTypePointerType);
6046
6046 PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id);6047 PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id);
6048 if (node->data.pointer_type.is_null_terminated) {
6049 if (ptr_len == PtrLenUnknown) {
6050 ptr_len = PtrLenNull;
6051 } else {
6052 exec_add_error_node(irb->codegen, irb->exec, node,
6053 buf_sprintf("null-terminated pointer must be specified with [*] token"));
6054 return irb->codegen->invalid_instruction;
6055 }
6056 }
6057
6047 bool is_const = node->data.pointer_type.is_const;6058 bool is_const = node->data.pointer_type.is_const;
6048 bool is_volatile = node->data.pointer_type.is_volatile;6059 bool is_volatile = node->data.pointer_type.is_volatile;
6049 bool is_allow_zero = node->data.pointer_type.allow_zero_token != nullptr;6060 bool is_allow_zero = node->data.pointer_type.allow_zero_token != nullptr;
6050 AstNode *expr_node = node->data.pointer_type.op_expr;6061 AstNode *expr_node = node->data.pointer_type.op_expr;
6051 AstNode *align_expr = node->data.pointer_type.align_expr;6062 AstNode *align_expr = node->data.pointer_type.align_expr;
60526063
6064
6053 IrInstruction *align_value;6065 IrInstruction *align_value;
6054 if (align_expr != nullptr) {6066 if (align_expr != nullptr) {
6055 align_value = ir_gen_node(irb, align_expr, scope);6067 align_value = ir_gen_node(irb, align_expr, scope);
...@@ -9793,6 +9805,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted...@@ -9793,6 +9805,7 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
9793 // alignment can be decreased9805 // alignment can be decreased
9794 // bit offset attributes must match exactly9806 // bit offset attributes must match exactly
9795 // PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one9807 // PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one
9808 // PtrLenNull can coerce into PtrLenUnknown
9796 ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type);9809 ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type);
9797 ZigType *actual_ptr_type = get_src_ptr_type(actual_type);9810 ZigType *actual_ptr_type = get_src_ptr_type(actual_type);
9798 bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type);9811 bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type);
...@@ -9843,7 +9856,10 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted...@@ -9843,7 +9856,10 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
9843 return result;9856 return result;
9844 }9857 }
9845 bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len;9858 bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len;
9846 if ((ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr) &&9859 bool ok_null_term_ptrs =
9860 actual_ptr_type->data.pointer.ptr_len == PtrLenNull ||
9861 wanted_ptr_type->data.pointer.ptr_len == PtrLenUnknown;
9862 if ((ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr || ok_null_term_ptrs) &&
9847 type_has_bits(wanted_type) == type_has_bits(actual_type) &&9863 type_has_bits(wanted_type) == type_has_bits(actual_type) &&
9848 (!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&9864 (!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&
9849 (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&9865 (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&
...@@ -14532,6 +14548,7 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {...@@ -14532,6 +14548,7 @@ static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) {
14532 case PtrLenSingle:14548 case PtrLenSingle:
14533 return false;14549 return false;
14534 case PtrLenUnknown:14550 case PtrLenUnknown:
14551 case PtrLenNull:
14535 case PtrLenC:14552 case PtrLenC:
14536 break;14553 break;
14537 }14554 }
...@@ -21166,6 +21183,7 @@ static BuiltinPtrSize ptr_len_to_size_enum_index(PtrLen ptr_len) {...@@ -21166,6 +21183,7 @@ static BuiltinPtrSize ptr_len_to_size_enum_index(PtrLen ptr_len) {
21166 case PtrLenSingle:21183 case PtrLenSingle:
21167 return BuiltinPtrSizeOne;21184 return BuiltinPtrSizeOne;
21168 case PtrLenUnknown:21185 case PtrLenUnknown:
21186 case PtrLenNull:
21169 return BuiltinPtrSizeMany;21187 return BuiltinPtrSizeMany;
21170 case PtrLenC:21188 case PtrLenC:
21171 return BuiltinPtrSizeC;21189 return BuiltinPtrSizeC;
...@@ -21210,7 +21228,7 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty...@@ -21210,7 +21228,7 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty
21210 result->special = ConstValSpecialStatic;21228 result->special = ConstValSpecialStatic;
21211 result->type = type_info_pointer_type;21229 result->type = type_info_pointer_type;
2121221230
21213 ConstExprValue **fields = alloc_const_vals_ptrs(6);21231 ConstExprValue **fields = alloc_const_vals_ptrs(7);
21214 result->data.x_struct.fields = fields;21232 result->data.x_struct.fields = fields;
2121521233
21216 // size: Size21234 // size: Size
...@@ -21246,6 +21264,11 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty...@@ -21246,6 +21264,11 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty
21246 fields[5]->special = ConstValSpecialStatic;21264 fields[5]->special = ConstValSpecialStatic;
21247 fields[5]->type = ira->codegen->builtin_types.entry_bool;21265 fields[5]->type = ira->codegen->builtin_types.entry_bool;
21248 fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero;21266 fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero;
21267 // is_null_terminated: bool
21268 ensure_field_index(result->type, "is_null_terminated", 6);
21269 fields[6]->special = ConstValSpecialStatic;
21270 fields[6]->type = ira->codegen->builtin_types.entry_bool;
21271 fields[6]->data.x_bool = attrs_type->data.pointer.ptr_len == PtrLenNull;
2124921272
21250 return result;21273 return result;
21251};21274};
src/parser.cpp+10
...@@ -2618,6 +2618,11 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {...@@ -2618,6 +2618,11 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
2618 if (array != nullptr) {2618 if (array != nullptr) {
2619 assert(array->type == NodeTypeArrayType);2619 assert(array->type == NodeTypeArrayType);
2620 while (true) {2620 while (true) {
2621 if (eat_token_if(pc, TokenIdKeywordNull) != nullptr) {
2622 array->data.array_type.is_null_terminated = true;
2623 continue;
2624 }
2625
2621 Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero);2626 Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero);
2622 if (allowzero_token != nullptr) {2627 if (allowzero_token != nullptr) {
2623 array->data.array_type.allow_zero_token = allowzero_token;2628 array->data.array_type.allow_zero_token = allowzero_token;
...@@ -2653,6 +2658,11 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {...@@ -2653,6 +2658,11 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
2653 if (child == nullptr)2658 if (child == nullptr)
2654 child = ptr;2659 child = ptr;
2655 while (true) {2660 while (true) {
2661 if (eat_token_if(pc, TokenIdKeywordNull) != nullptr) {
2662 child->data.pointer_type.is_null_terminated = true;
2663 continue;
2664 }
2665
2656 Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero);2666 Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero);
2657 if (allowzero_token != nullptr) {2667 if (allowzero_token != nullptr) {
2658 child->data.pointer_type.allow_zero_token = allowzero_token;2668 child->data.pointer_type.allow_zero_token = allowzero_token;
src/translate_c.cpp+2
...@@ -291,6 +291,7 @@ static TokenId ptr_len_to_token_id(PtrLen ptr_len) {...@@ -291,6 +291,7 @@ static TokenId ptr_len_to_token_id(PtrLen ptr_len) {
291 case PtrLenSingle:291 case PtrLenSingle:
292 return TokenIdStar;292 return TokenIdStar;
293 case PtrLenUnknown:293 case PtrLenUnknown:
294 case PtrLenNull:
294 return TokenIdBracketStarBracket;295 return TokenIdBracketStarBracket;
295 case PtrLenC:296 case PtrLenC:
296 return TokenIdBracketStarCBracket;297 return TokenIdBracketStarCBracket;
...@@ -302,6 +303,7 @@ static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_vo...@@ -302,6 +303,7 @@ static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_vo
302 AstNode *node = trans_create_node(c, NodeTypePointerType);303 AstNode *node = trans_create_node(c, NodeTypePointerType);
303 node->data.pointer_type.star_token = allocate<ZigToken>(1);304 node->data.pointer_type.star_token = allocate<ZigToken>(1);
304 node->data.pointer_type.star_token->id = ptr_len_to_token_id(ptr_len);305 node->data.pointer_type.star_token->id = ptr_len_to_token_id(ptr_len);
306 node->data.pointer_type.is_null_terminated = (ptr_len == PtrLenNull);
305 node->data.pointer_type.is_const = is_const;307 node->data.pointer_type.is_const = is_const;
306 node->data.pointer_type.is_volatile = is_volatile;308 node->data.pointer_type.is_volatile = is_volatile;
307 node->data.pointer_type.op_expr = child_node;309 node->data.pointer_type.op_expr = child_node;
test/compile_errors.zig+12
...@@ -68,6 +68,18 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -68,6 +68,18 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
68 "tmp.zig:9:27: error: @atomicRmw on enum only works with .Xchg",68 "tmp.zig:9:27: error: @atomicRmw on enum only works with .Xchg",
69 );69 );
7070
71 cases.add(
72 "disallow coercion from non-null-terminated pointer to null-terminated pointer",
73 \\extern fn puts(s: [*]null const u8) c_int;
74 \\pub fn main() void {
75 \\ const no_zero_array = [_]u8{'h', 'e', 'l', 'l', 'o'};
76 \\ const no_zero_ptr: [*]const u8 = &no_zero_array;
77 \\ _ = puts(no_zero_ptr);
78 \\}
79 ,
80 "tmp.zig:5:14: error: expected type '[*]null const u8', found '[*]const u8'",
81 );
82
71 cases.add(83 cases.add(
72 "atomic orderings of atomicStore Acquire or AcqRel",84 "atomic orderings of atomicStore Acquire or AcqRel",
73 \\export fn entry() void {85 \\export fn entry() void {
test/stage1/behavior/pointers.zig+14
...@@ -200,3 +200,17 @@ test "assign null directly to C pointer and test null equality" {...@@ -200,3 +200,17 @@ test "assign null directly to C pointer and test null equality" {
200 }200 }
201 comptime expect((y1 orelse &othery) == y1);201 comptime expect((y1 orelse &othery) == y1);
202}202}
203
204test "null terminated pointer" {
205 const S = struct {
206 fn doTheTest() void {
207 var array_with_zero = [_]u8{'h', 'e', 'l', 'l', 'o', 0};
208 var zero_ptr: [*]null const u8 = @ptrCast([*]null const u8, &array_with_zero);
209 var no_zero_ptr: [*]const u8 = zero_ptr;
210 expect(std.mem.eql(u8, std.mem.toSliceConst(u8, no_zero_ptr), "hello"));
211 }
212 };
213 S.doTheTest();
214 // TODO test fails at comptime
215 //comptime S.doTheTest();
216}