| author | |
| committer | |
| log | 4c7b52503b064b8be2f2afd4152311aff1941d92 |
| tree | 3f8ea590d0dc73dbfd94b0863aa1bb8c32e4d0b9 |
| parent | 2dd20aa04a35f46a189d0aaa5d2d628e46a77999 |
| signature |
7 files changed, 53 insertions(+), 61 deletions(-)
doc/docgen.zig-2| ... | ... | @@ -954,8 +954,6 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok |
| 954 | 954 | .AngleBracketAngleBracketRight, |
| 955 | 955 | .AngleBracketAngleBracketRightEqual, |
| 956 | 956 | .Tilde, |
| 957 | .BracketStarBracket, | |
| 958 | .BracketStarCBracket, | |
| 959 | 957 | => try writeEscaped(out, src[token.start..token.end]), |
| 960 | 958 | |
| 961 | 959 | .Invalid, .Invalid_ampersands => return parseError( |
doc/langref.html.in+12-28| ... | ... | @@ -563,7 +563,7 @@ const mem = @import("std").mem; |
| 563 | 563 | |
| 564 | 564 | test "string literals" { |
| 565 | 565 | const bytes = "hello"; |
| 566 | assert(@typeOf(bytes) == *const [5]null u8); | |
| 566 | assert(@typeOf(bytes) == *const [5:0]u8); | |
| 567 | 567 | assert(bytes.len == 5); |
| 568 | 568 | assert(bytes[1] == 'e'); |
| 569 | 569 | assert(bytes[5] == 0); |
| ... | ... | @@ -1795,7 +1795,7 @@ test "null terminated array" { |
| 1795 | 1795 | |
| 1796 | 1796 | assert(@typeOf(array) == [4:0]u8); |
| 1797 | 1797 | assert(array.len == 4); |
| 1798 | assert(slice[4] == 0); | |
| 1798 | assert(array[4] == 0); | |
| 1799 | 1799 | } |
| 1800 | 1800 | {#code_end#} |
| 1801 | 1801 | {#see_also|Sentinel-Terminated Pointers|Sentinel-Terminated Slices#} |
| ... | ... | @@ -4863,7 +4863,7 @@ const assert = std.debug.assert; |
| 4863 | 4863 | const mem = std.mem; |
| 4864 | 4864 | |
| 4865 | 4865 | test "cast *[1][*]const u8 to [*]const ?[*]const u8" { |
| 4866 | const window_name = [1][*]const u8{c"window name"}; | |
| 4866 | const window_name = [1][*]const u8{"window name"}; | |
| 4867 | 4867 | const x: [*]const ?[*]const u8 = &window_name; |
| 4868 | 4868 | assert(mem.eql(u8, std.mem.toSliceConst(u8, x[0].?), "window name")); |
| 4869 | 4869 | } |
| ... | ... | @@ -4905,7 +4905,7 @@ test "float widening" { |
| 4905 | 4905 | {#code_end#} |
| 4906 | 4906 | {#header_close#} |
| 4907 | 4907 | {#header_open|Type Coercion: Arrays and Pointers#} |
| 4908 | {#code_begin|test#} | |
| 4908 | {#code_begin|test|coerce_arrays_and_ptrs#} | |
| 4909 | 4909 | const std = @import("std"); |
| 4910 | 4910 | const assert = std.debug.assert; |
| 4911 | 4911 | |
| ... | ... | @@ -4944,7 +4944,7 @@ test "[N]T to ?[]const T" { |
| 4944 | 4944 | |
| 4945 | 4945 | // In this cast, the array length becomes the slice length. |
| 4946 | 4946 | test "*[N]T to []T" { |
| 4947 | var buf: [5]u8 = "hello"; | |
| 4947 | var buf: [5]u8 = "hello".*; | |
| 4948 | 4948 | const x: []u8 = &buf; |
| 4949 | 4949 | assert(std.mem.eql(u8, x, "hello")); |
| 4950 | 4950 | |
| ... | ... | @@ -4956,7 +4956,7 @@ test "*[N]T to []T" { |
| 4956 | 4956 | // Single-item pointers to arrays can be coerced to |
| 4957 | 4957 | // unknown length pointers. |
| 4958 | 4958 | test "*[N]T to [*]T" { |
| 4959 | var buf: [5]u8 = "hello"; | |
| 4959 | var buf: [5]u8 = "hello".*; | |
| 4960 | 4960 | const x: [*]u8 = &buf; |
| 4961 | 4961 | assert(x[4] == 'o'); |
| 4962 | 4962 | // x[5] would be an uncaught out of bounds pointer dereference! |
| ... | ... | @@ -4964,7 +4964,7 @@ test "*[N]T to [*]T" { |
| 4964 | 4964 | |
| 4965 | 4965 | // Likewise, it works when the destination type is an optional. |
| 4966 | 4966 | test "*[N]T to ?[*]T" { |
| 4967 | var buf: [5]u8 = "hello"; | |
| 4967 | var buf: [5]u8 = "hello".*; | |
| 4968 | 4968 | const x: ?[*]u8 = &buf; |
| 4969 | 4969 | assert(x.?[4] == 'o'); |
| 4970 | 4970 | } |
| ... | ... | @@ -5135,7 +5135,7 @@ test "coercion of zero bit types" { |
| 5135 | 5135 | This kind of type resolution chooses a type that all peer types can coerce into. Here are |
| 5136 | 5136 | some examples: |
| 5137 | 5137 | </p> |
| 5138 | {#code_begin|test#} | |
| 5138 | {#code_begin|test|peer_type_resolution#} | |
| 5139 | 5139 | const std = @import("std"); |
| 5140 | 5140 | const assert = std.debug.assert; |
| 5141 | 5141 | const mem = std.mem; |
| ... | ... | @@ -5202,13 +5202,13 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { |
| 5202 | 5202 | } |
| 5203 | 5203 | test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" { |
| 5204 | 5204 | { |
| 5205 | var data = "hi"; | |
| 5205 | var data = "hi".*; | |
| 5206 | 5206 | const slice = data[0..]; |
| 5207 | 5207 | assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); |
| 5208 | 5208 | assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); |
| 5209 | 5209 | } |
| 5210 | 5210 | comptime { |
| 5211 | var data = "hi"; | |
| 5211 | var data = "hi".*; | |
| 5212 | 5212 | const slice = data[0..]; |
| 5213 | 5213 | assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); |
| 5214 | 5214 | assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); |
| ... | ... | @@ -8673,7 +8673,7 @@ pub fn main() void { |
| 8673 | 8673 | <p>At compile-time:</p> |
| 8674 | 8674 | {#code_begin|test_err|index 5 outside array of size 5#} |
| 8675 | 8675 | comptime { |
| 8676 | const array = "hello"; | |
| 8676 | const array: [5]u8 = "hello".*; | |
| 8677 | 8677 | const garbage = array[5]; |
| 8678 | 8678 | } |
| 8679 | 8679 | {#code_end#} |
| ... | ... | @@ -9649,22 +9649,6 @@ test "assert in release fast mode" { |
| 9649 | 9649 | </ul> |
| 9650 | 9650 | {#see_also|Primitive Types#} |
| 9651 | 9651 | {#header_close#} |
| 9652 | {#header_open|C String Literals#} | |
| 9653 | {#code_begin|exe#} | |
| 9654 | {#link_libc#} | |
| 9655 | extern fn puts([*]const u8) void; | |
| 9656 | ||
| 9657 | pub fn main() void { | |
| 9658 | puts(c"this has a null terminator"); | |
| 9659 | puts( | |
| 9660 | c\\and so | |
| 9661 | c\\does this | |
| 9662 | c\\multiline C string literal | |
| 9663 | ); | |
| 9664 | } | |
| 9665 | {#code_end#} | |
| 9666 | {#see_also|String Literals and Character Literals#} | |
| 9667 | {#header_close#} | |
| 9668 | 9652 | |
| 9669 | 9653 | {#header_open|Import from C Header File#} |
| 9670 | 9654 | <p> |
| ... | ... | @@ -9679,7 +9663,7 @@ const c = @cImport({ |
| 9679 | 9663 | @cInclude("stdio.h"); |
| 9680 | 9664 | }); |
| 9681 | 9665 | pub fn main() void { |
| 9682 | _ = c.printf(c"hello\n"); | |
| 9666 | _ = c.printf("hello\n"); | |
| 9683 | 9667 | } |
| 9684 | 9668 | {#code_end#} |
| 9685 | 9669 | <p> |
src/analyze.cpp+1-17| ... | ... | @@ -496,7 +496,7 @@ static void append_ptr_type_attrs(Buf *type_name, ZigType *ptr_type) { |
| 496 | 496 | } else if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) { |
| 497 | 497 | buf_appendf(type_name, ":%" PRIu32, ptr_type->data.pointer.vector_index); |
| 498 | 498 | } |
| 499 | buf_appendf(type_name, ")"); | |
| 499 | buf_appendf(type_name, ") "); | |
| 500 | 500 | } |
| 501 | 501 | buf_appendf(type_name, "%s%s%s", const_str, volatile_str, allow_zero_str); |
| 502 | 502 | if (ptr_type->data.pointer.inferred_struct_field != nullptr) { |
| ... | ... | @@ -861,22 +861,6 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { |
| 861 | 861 | entry->data.structure.fields[slice_len_index]->gen_index = 0; |
| 862 | 862 | } |
| 863 | 863 | |
| 864 | ZigType *child_type = ptr_type->data.pointer.child_type; | |
| 865 | if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile || | |
| 866 | ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero) | |
| 867 | { | |
| 868 | ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false, | |
| 869 | PtrLenUnknown, 0, 0, 0, false); | |
| 870 | ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type); | |
| 871 | ||
| 872 | entry->size_in_bits = peer_slice_type->size_in_bits; | |
| 873 | entry->abi_size = peer_slice_type->abi_size; | |
| 874 | entry->abi_align = peer_slice_type->abi_align; | |
| 875 | ||
| 876 | *parent_pointer = entry; | |
| 877 | return entry; | |
| 878 | } | |
| 879 | ||
| 880 | 864 | if (type_has_bits(ptr_type)) { |
| 881 | 865 | entry->size_in_bits = ptr_type->size_in_bits + g->builtin_types.entry_usize->size_in_bits; |
| 882 | 866 | entry->abi_size = ptr_type->abi_size + g->builtin_types.entry_usize->abi_size; |
src/codegen.cpp+3-2| ... | ... | @@ -3752,7 +3752,7 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI |
| 3752 | 3752 | LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); |
| 3753 | 3753 | assert(array_type->data.structure.is_slice); |
| 3754 | 3754 | |
| 3755 | ZigType *ptr_type = instruction->base.value.type; | |
| 3755 | ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; | |
| 3756 | 3756 | if (!type_has_bits(ptr_type)) { |
| 3757 | 3757 | if (safety_check_on) { |
| 3758 | 3758 | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMIntegerTypeKind); |
| ... | ... | @@ -3769,7 +3769,8 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI |
| 3769 | 3769 | assert(len_index != SIZE_MAX); |
| 3770 | 3770 | LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)len_index, ""); |
| 3771 | 3771 | LLVMValueRef len = gen_load_untyped(g, len_ptr, 0, false, ""); |
| 3772 | add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len); | |
| 3772 | LLVMIntPredicate upper_op = (ptr_type->data.pointer.sentinel != nullptr) ? LLVMIntULE : LLVMIntULT; | |
| 3773 | add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, upper_op, len); | |
| 3773 | 3774 | } |
| 3774 | 3775 | |
| 3775 | 3776 | size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; |
src/ir.cpp+15-4| ... | ... | @@ -18244,13 +18244,16 @@ static IrInstruction *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructi |
| 18244 | 18244 | |
| 18245 | 18245 | static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) { |
| 18246 | 18246 | assert(ptr_type->id == ZigTypeIdPointer); |
| 18247 | return get_pointer_to_type_extra(g, | |
| 18247 | return get_pointer_to_type_extra2(g, | |
| 18248 | 18248 | ptr_type->data.pointer.child_type, |
| 18249 | 18249 | ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, |
| 18250 | 18250 | ptr_type->data.pointer.ptr_len, |
| 18251 | 18251 | new_align, |
| 18252 | 18252 | ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, |
| 18253 | ptr_type->data.pointer.allow_zero); | |
| 18253 | ptr_type->data.pointer.allow_zero, | |
| 18254 | ptr_type->data.pointer.vector_index, | |
| 18255 | ptr_type->data.pointer.inferred_struct_field, | |
| 18256 | ptr_type->data.pointer.sentinel); | |
| 18254 | 18257 | } |
| 18255 | 18258 | |
| 18256 | 18259 | static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) { |
| ... | ... | @@ -18595,8 +18598,11 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct |
| 18595 | 18598 | ConstExprValue *len_field = array_ptr_val->data.x_struct.fields[slice_len_index]; |
| 18596 | 18599 | IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type); |
| 18597 | 18600 | ConstExprValue *out_val = &result->value; |
| 18601 | ZigType *slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; | |
| 18598 | 18602 | uint64_t slice_len = bigint_as_u64(&len_field->data.x_bigint); |
| 18599 | if (index >= slice_len) { | |
| 18603 | uint64_t full_slice_len = slice_len + | |
| 18604 | ((slice_ptr_type->data.pointer.sentinel != nullptr) ? 1 : 0); | |
| 18605 | if (index >= full_slice_len) { | |
| 18600 | 18606 | ir_add_error_node(ira, elem_ptr_instruction->base.source_node, |
| 18601 | 18607 | buf_sprintf("index %" ZIG_PRI_u64 " outside slice of size %" ZIG_PRI_u64, |
| 18602 | 18608 | index, slice_len)); |
| ... | ... | @@ -18615,8 +18621,13 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct |
| 18615 | 18621 | { |
| 18616 | 18622 | size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index; |
| 18617 | 18623 | uint64_t new_index = offset + index; |
| 18618 | ir_assert(new_index < ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len, | |
| 18624 | if (ptr_field->data.x_ptr.data.base_array.array_val->data.x_array.special != | |
| 18625 | ConstArraySpecialBuf) | |
| 18626 | { | |
| 18627 | ir_assert(new_index < | |
| 18628 | ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len, | |
| 18619 | 18629 | &elem_ptr_instruction->base); |
| 18630 | } | |
| 18620 | 18631 | out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; |
| 18621 | 18632 | out_val->data.x_ptr.data.base_array.array_val = |
| 18622 | 18633 | ptr_field->data.x_ptr.data.base_array.array_val; |
test/compile_errors.zig+9-8| ... | ... | @@ -70,14 +70,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 70 | 70 | |
| 71 | 71 | cases.add( |
| 72 | 72 | "disallow coercion from non-null-terminated pointer to null-terminated pointer", |
| 73 | \\extern fn puts(s: [*]null const u8) c_int; | |
| 73 | \\extern fn puts(s: [*:0]const u8) c_int; | |
| 74 | 74 | \\pub fn main() void { |
| 75 | 75 | \\ const no_zero_array = [_]u8{'h', 'e', 'l', 'l', 'o'}; |
| 76 | 76 | \\ const no_zero_ptr: [*]const u8 = &no_zero_array; |
| 77 | 77 | \\ _ = puts(no_zero_ptr); |
| 78 | 78 | \\} |
| 79 | 79 | , |
| 80 | "tmp.zig:5:14: error: expected type '[*]null const u8', found '[*]const u8'", | |
| 80 | "tmp.zig:5:14: error: expected type '[*:0]const u8', found '[*]const u8'", | |
| 81 | 81 | ); |
| 82 | 82 | |
| 83 | 83 | cases.add( |
| ... | ... | @@ -784,7 +784,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 784 | 784 | \\ strValue = strValue orelse ""; |
| 785 | 785 | \\} |
| 786 | 786 | , |
| 787 | "tmp.zig:3:32: error: expected type '[*c]u8', found '*const [0]null u8'", | |
| 787 | "tmp.zig:3:32: error: expected type '[*c]u8', found '*const [0:0]u8'", | |
| 788 | 788 | "tmp.zig:3:32: note: cast discards const qualifier", |
| 789 | 789 | ); |
| 790 | 790 | |
| ... | ... | @@ -2442,12 +2442,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2442 | 2442 | ); |
| 2443 | 2443 | |
| 2444 | 2444 | cases.add( |
| 2445 | "var not allowed in structs", | |
| 2445 | "var makes structs required to be comptime known", | |
| 2446 | 2446 | \\export fn entry() void { |
| 2447 | \\ var s = (struct{v: var}){.v=@as(i32, 10)}; | |
| 2447 | \\ const S = struct{v: var}; | |
| 2448 | \\ var s = S{.v=@as(i32, 10)}; | |
| 2448 | 2449 | \\} |
| 2449 | 2450 | , |
| 2450 | "tmp.zig:2:23: error: invalid token: 'var'", | |
| 2451 | "tmp.zig:3:4: error: variable of type 'S' must be const or comptime", | |
| 2451 | 2452 | ); |
| 2452 | 2453 | |
| 2453 | 2454 | cases.add( |
| ... | ... | @@ -3357,7 +3358,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3357 | 3358 | \\ return a; |
| 3358 | 3359 | \\} |
| 3359 | 3360 | , |
| 3360 | "tmp.zig:3:12: error: expected type 'i32', found '*const [1]null u8'", | |
| 3361 | "tmp.zig:3:12: error: expected type 'i32', found '*const [1:0]u8'", | |
| 3361 | 3362 | ); |
| 3362 | 3363 | |
| 3363 | 3364 | cases.add( |
| ... | ... | @@ -6212,7 +6213,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 6212 | 6213 | \\} |
| 6213 | 6214 | \\pub extern fn foo(format: *const u8, ...) void; |
| 6214 | 6215 | , |
| 6215 | "tmp.zig:2:16: error: expected type '*const u8', found '[5]null u8'", | |
| 6216 | "tmp.zig:2:16: error: expected type '*const u8', found '[5:0]u8'", | |
| 6216 | 6217 | ); |
| 6217 | 6218 | |
| 6218 | 6219 | cases.add( |
test/stage1/behavior/slice.zig+13| ... | ... | @@ -65,3 +65,16 @@ test "slice type with custom alignment" { |
| 65 | 65 | slice[1].anything = 42; |
| 66 | 66 | expect(array[1].anything == 42); |
| 67 | 67 | } |
| 68 | ||
| 69 | test "access len index of sentinel-terminated slice" { | |
| 70 | const S = struct { | |
| 71 | fn doTheTest() void { | |
| 72 | var slice: [:0]const u8 = "hello"; | |
| 73 | ||
| 74 | expect(slice.len == 5); | |
| 75 | expect(slice[5] == 0); | |
| 76 | } | |
| 77 | }; | |
| 78 | S.doTheTest(); | |
| 79 | comptime S.doTheTest(); | |
| 80 | } |