| author | |
| committer | |
| log | 1aa978f32e88b4c83fde95f62938c97c7c22164c |
| tree | 75f26a3f9cd18bd980b6fc1c5cab8a4f7f10e2d8 |
| parent | e3404e3c78307092e849dc0609f77932b263e3fc |
| signature |
10 files changed, 75 insertions(+), 3 deletions(-)
lib/std/builtin.zig+1| ... | ... | @@ -144,6 +144,7 @@ pub const TypeInfo = union(enum) { |
| 144 | 144 | alignment: comptime_int, |
| 145 | 145 | child: type, |
| 146 | 146 | is_allowzero: bool, |
| 147 | is_null_terminated: bool, | |
| 147 | 148 | |
| 148 | 149 | /// This data structure is used by the Zig language code generation and |
| 149 | 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 | 558 | if (!builtin.is_test) return; |
| 559 | 559 | _ = declarations(T); |
| 560 | 560 | } |
| 561 |
src/all_types.hpp+3| ... | ... | @@ -55,6 +55,7 @@ enum PtrLen { |
| 55 | 55 | PtrLenUnknown, |
| 56 | 56 | PtrLenSingle, |
| 57 | 57 | PtrLenC, |
| 58 | PtrLenNull, | |
| 58 | 59 | }; |
| 59 | 60 | |
| 60 | 61 | // This one corresponds to the builtin.zig enum. |
| ... | ... | @@ -825,6 +826,7 @@ struct AstNodePointerType { |
| 825 | 826 | Token *allow_zero_token; |
| 826 | 827 | bool is_const; |
| 827 | 828 | bool is_volatile; |
| 829 | bool is_null_terminated; | |
| 828 | 830 | }; |
| 829 | 831 | |
| 830 | 832 | struct AstNodeInferredArrayType { |
| ... | ... | @@ -838,6 +840,7 @@ struct AstNodeArrayType { |
| 838 | 840 | Token *allow_zero_token; |
| 839 | 841 | bool is_const; |
| 840 | 842 | bool is_volatile; |
| 843 | bool is_null_terminated; | |
| 841 | 844 | }; |
| 842 | 845 | |
| 843 | 846 | struct AstNodeUsingNamespace { |
src/analyze.cpp+3-1| ... | ... | @@ -460,6 +460,8 @@ static const char *ptr_len_to_star_str(PtrLen ptr_len) { |
| 460 | 460 | return "[*]"; |
| 461 | 461 | case PtrLenC: |
| 462 | 462 | return "[*c]"; |
| 463 | case PtrLenNull: | |
| 464 | return "[*]null "; | |
| 463 | 465 | } |
| 464 | 466 | zig_unreachable(); |
| 465 | 467 | } |
| ... | ... | @@ -7032,7 +7034,7 @@ uint32_t type_id_hash(TypeId x) { |
| 7032 | 7034 | return hash_ptr(x.data.error_union.err_set_type) ^ hash_ptr(x.data.error_union.payload_type); |
| 7033 | 7035 | case ZigTypeIdPointer: |
| 7034 | 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 | 7038 | (x.data.pointer.is_const ? (uint32_t)2749109194 : (uint32_t)4047371087) + |
| 7037 | 7039 | (x.data.pointer.is_volatile ? (uint32_t)536730450 : (uint32_t)1685612214) + |
| 7038 | 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 | 992 | jw_object_field(jw, "len"); |
| 993 | 993 | jw_int(jw, 3); |
| 994 | 994 | break; |
| 995 | case PtrLenNull: | |
| 996 | jw_object_field(jw, "len"); | |
| 997 | jw_int(jw, 4); | |
| 998 | break; | |
| 995 | 999 | } |
| 996 | 1000 | anal_dump_pointer_attrs(ctx, ty); |
| 997 | 1001 | break; |
src/ir.cpp+25-2| ... | ... | @@ -6043,13 +6043,25 @@ static PtrLen star_token_to_ptr_len(TokenId token_id) { |
| 6043 | 6043 | |
| 6044 | 6044 | static IrInstruction *ir_gen_pointer_type(IrBuilder *irb, Scope *scope, AstNode *node) { |
| 6045 | 6045 | assert(node->type == NodeTypePointerType); |
| 6046 | ||
| 6046 | 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 | 6058 | bool is_const = node->data.pointer_type.is_const; |
| 6048 | 6059 | bool is_volatile = node->data.pointer_type.is_volatile; |
| 6049 | 6060 | bool is_allow_zero = node->data.pointer_type.allow_zero_token != nullptr; |
| 6050 | 6061 | AstNode *expr_node = node->data.pointer_type.op_expr; |
| 6051 | 6062 | AstNode *align_expr = node->data.pointer_type.align_expr; |
| 6052 | 6063 | |
| 6064 | ||
| 6053 | 6065 | IrInstruction *align_value; |
| 6054 | 6066 | if (align_expr != nullptr) { |
| 6055 | 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 | 9805 | // alignment can be decreased |
| 9794 | 9806 | // bit offset attributes must match exactly |
| 9795 | 9807 | // PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one |
| 9808 | // PtrLenNull can coerce into PtrLenUnknown | |
| 9796 | 9809 | ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type); |
| 9797 | 9810 | ZigType *actual_ptr_type = get_src_ptr_type(actual_type); |
| 9798 | 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 | 9856 | return result; |
| 9844 | 9857 | } |
| 9845 | 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 | 9863 | type_has_bits(wanted_type) == type_has_bits(actual_type) && |
| 9848 | 9864 | (!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) && |
| 9849 | 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 | 14548 | case PtrLenSingle: |
| 14533 | 14549 | return false; |
| 14534 | 14550 | case PtrLenUnknown: |
| 14551 | case PtrLenNull: | |
| 14535 | 14552 | case PtrLenC: |
| 14536 | 14553 | break; |
| 14537 | 14554 | } |
| ... | ... | @@ -21166,6 +21183,7 @@ static BuiltinPtrSize ptr_len_to_size_enum_index(PtrLen ptr_len) { |
| 21166 | 21183 | case PtrLenSingle: |
| 21167 | 21184 | return BuiltinPtrSizeOne; |
| 21168 | 21185 | case PtrLenUnknown: |
| 21186 | case PtrLenNull: | |
| 21169 | 21187 | return BuiltinPtrSizeMany; |
| 21170 | 21188 | case PtrLenC: |
| 21171 | 21189 | return BuiltinPtrSizeC; |
| ... | ... | @@ -21210,7 +21228,7 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty |
| 21210 | 21228 | result->special = ConstValSpecialStatic; |
| 21211 | 21229 | result->type = type_info_pointer_type; |
| 21212 | 21230 | |
| 21213 | ConstExprValue **fields = alloc_const_vals_ptrs(6); | |
| 21231 | ConstExprValue **fields = alloc_const_vals_ptrs(7); | |
| 21214 | 21232 | result->data.x_struct.fields = fields; |
| 21215 | 21233 | |
| 21216 | 21234 | // size: Size |
| ... | ... | @@ -21246,6 +21264,11 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty |
| 21246 | 21264 | fields[5]->special = ConstValSpecialStatic; |
| 21247 | 21265 | fields[5]->type = ira->codegen->builtin_types.entry_bool; |
| 21248 | 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; | |
| 21249 | 21272 | |
| 21250 | 21273 | return result; |
| 21251 | 21274 | }; |
src/parser.cpp+10| ... | ... | @@ -2618,6 +2618,11 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { |
| 2618 | 2618 | if (array != nullptr) { |
| 2619 | 2619 | assert(array->type == NodeTypeArrayType); |
| 2620 | 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 | 2626 | Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero); |
| 2622 | 2627 | if (allowzero_token != nullptr) { |
| 2623 | 2628 | array->data.array_type.allow_zero_token = allowzero_token; |
| ... | ... | @@ -2653,6 +2658,11 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { |
| 2653 | 2658 | if (child == nullptr) |
| 2654 | 2659 | child = ptr; |
| 2655 | 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 | 2666 | Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero); |
| 2657 | 2667 | if (allowzero_token != nullptr) { |
| 2658 | 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 | 291 | case PtrLenSingle: |
| 292 | 292 | return TokenIdStar; |
| 293 | 293 | case PtrLenUnknown: |
| 294 | case PtrLenNull: | |
| 294 | 295 | return TokenIdBracketStarBracket; |
| 295 | 296 | case PtrLenC: |
| 296 | 297 | return TokenIdBracketStarCBracket; |
| ... | ... | @@ -302,6 +303,7 @@ static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_vo |
| 302 | 303 | AstNode *node = trans_create_node(c, NodeTypePointerType); |
| 303 | 304 | node->data.pointer_type.star_token = allocate<ZigToken>(1); |
| 304 | 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 | 307 | node->data.pointer_type.is_const = is_const; |
| 306 | 308 | node->data.pointer_type.is_volatile = is_volatile; |
| 307 | 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 | 68 | "tmp.zig:9:27: error: @atomicRmw on enum only works with .Xchg", |
| 69 | 69 | ); |
| 70 | 70 | |
| 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 | 83 | cases.add( |
| 72 | 84 | "atomic orderings of atomicStore Acquire or AcqRel", |
| 73 | 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 | 200 | } |
| 201 | 201 | comptime expect((y1 orelse &othery) == y1); |
| 202 | 202 | } |
| 203 | ||
| 204 | test "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 | } |