diff --git a/doc/docgen.zig b/doc/docgen.zig index 502ffda784..bf53bed717 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -954,8 +954,6 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: var, source_token: Tok .AngleBracketAngleBracketRight, .AngleBracketAngleBracketRightEqual, .Tilde, - .BracketStarBracket, - .BracketStarCBracket, => try writeEscaped(out, src[token.start..token.end]), .Invalid, .Invalid_ampersands => return parseError( diff --git a/doc/langref.html.in b/doc/langref.html.in index 0084e7e199..be6f09a93e 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -563,7 +563,7 @@ const mem = @import("std").mem; test "string literals" { const bytes = "hello"; - assert(@typeOf(bytes) == *const [5]null u8); + assert(@typeOf(bytes) == *const [5:0]u8); assert(bytes.len == 5); assert(bytes[1] == 'e'); assert(bytes[5] == 0); @@ -1795,7 +1795,7 @@ test "null terminated array" { assert(@typeOf(array) == [4:0]u8); assert(array.len == 4); - assert(slice[4] == 0); + assert(array[4] == 0); } {#code_end#} {#see_also|Sentinel-Terminated Pointers|Sentinel-Terminated Slices#} @@ -4863,7 +4863,7 @@ const assert = std.debug.assert; const mem = std.mem; test "cast *[1][*]const u8 to [*]const ?[*]const u8" { - const window_name = [1][*]const u8{c"window name"}; + const window_name = [1][*]const u8{"window name"}; const x: [*]const ?[*]const u8 = &window_name; assert(mem.eql(u8, std.mem.toSliceConst(u8, x[0].?), "window name")); } @@ -4905,7 +4905,7 @@ test "float widening" { {#code_end#} {#header_close#} {#header_open|Type Coercion: Arrays and Pointers#} - {#code_begin|test#} + {#code_begin|test|coerce_arrays_and_ptrs#} const std = @import("std"); const assert = std.debug.assert; @@ -4944,7 +4944,7 @@ test "[N]T to ?[]const T" { // In this cast, the array length becomes the slice length. test "*[N]T to []T" { - var buf: [5]u8 = "hello"; + var buf: [5]u8 = "hello".*; const x: []u8 = &buf; assert(std.mem.eql(u8, x, "hello")); @@ -4956,7 +4956,7 @@ test "*[N]T to []T" { // Single-item pointers to arrays can be coerced to // unknown length pointers. test "*[N]T to [*]T" { - var buf: [5]u8 = "hello"; + var buf: [5]u8 = "hello".*; const x: [*]u8 = &buf; assert(x[4] == 'o'); // x[5] would be an uncaught out of bounds pointer dereference! @@ -4964,7 +4964,7 @@ test "*[N]T to [*]T" { // Likewise, it works when the destination type is an optional. test "*[N]T to ?[*]T" { - var buf: [5]u8 = "hello"; + var buf: [5]u8 = "hello".*; const x: ?[*]u8 = &buf; assert(x.?[4] == 'o'); } @@ -5135,7 +5135,7 @@ test "coercion of zero bit types" { This kind of type resolution chooses a type that all peer types can coerce into. Here are some examples:

- {#code_begin|test#} + {#code_begin|test|peer_type_resolution#} const std = @import("std"); const assert = std.debug.assert; const mem = std.mem; @@ -5202,13 +5202,13 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 { } test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" { { - var data = "hi"; + var data = "hi".*; const slice = data[0..]; assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); } comptime { - var data = "hi"; + var data = "hi".*; const slice = data[0..]; assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0); assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1); @@ -8673,7 +8673,7 @@ pub fn main() void {

At compile-time:

{#code_begin|test_err|index 5 outside array of size 5#} comptime { - const array = "hello"; + const array: [5]u8 = "hello".*; const garbage = array[5]; } {#code_end#} @@ -9649,22 +9649,6 @@ test "assert in release fast mode" { {#see_also|Primitive Types#} {#header_close#} - {#header_open|C String Literals#} - {#code_begin|exe#} - {#link_libc#} -extern fn puts([*]const u8) void; - -pub fn main() void { - puts(c"this has a null terminator"); - puts( - c\\and so - c\\does this - c\\multiline C string literal - ); -} - {#code_end#} - {#see_also|String Literals and Character Literals#} - {#header_close#} {#header_open|Import from C Header File#}

@@ -9679,7 +9663,7 @@ const c = @cImport({ @cInclude("stdio.h"); }); pub fn main() void { - _ = c.printf(c"hello\n"); + _ = c.printf("hello\n"); } {#code_end#}

diff --git a/src/analyze.cpp b/src/analyze.cpp index d065f85c67..c41f5b652e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -496,7 +496,7 @@ static void append_ptr_type_attrs(Buf *type_name, ZigType *ptr_type) { } else if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) { buf_appendf(type_name, ":%" PRIu32, ptr_type->data.pointer.vector_index); } - buf_appendf(type_name, ")"); + buf_appendf(type_name, ") "); } buf_appendf(type_name, "%s%s%s", const_str, volatile_str, allow_zero_str); if (ptr_type->data.pointer.inferred_struct_field != nullptr) { @@ -861,22 +861,6 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { entry->data.structure.fields[slice_len_index]->gen_index = 0; } - ZigType *child_type = ptr_type->data.pointer.child_type; - if (ptr_type->data.pointer.is_const || ptr_type->data.pointer.is_volatile || - ptr_type->data.pointer.explicit_alignment != 0 || ptr_type->data.pointer.allow_zero) - { - ZigType *peer_ptr_type = get_pointer_to_type_extra(g, child_type, false, false, - PtrLenUnknown, 0, 0, 0, false); - ZigType *peer_slice_type = get_slice_type(g, peer_ptr_type); - - entry->size_in_bits = peer_slice_type->size_in_bits; - entry->abi_size = peer_slice_type->abi_size; - entry->abi_align = peer_slice_type->abi_align; - - *parent_pointer = entry; - return entry; - } - if (type_has_bits(ptr_type)) { entry->size_in_bits = ptr_type->size_in_bits + g->builtin_types.entry_usize->size_in_bits; entry->abi_size = ptr_type->abi_size + g->builtin_types.entry_usize->abi_size; diff --git a/src/codegen.cpp b/src/codegen.cpp index 9a269ec41f..3e2744efc6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3752,7 +3752,7 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); assert(array_type->data.structure.is_slice); - ZigType *ptr_type = instruction->base.value.type; + ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; if (!type_has_bits(ptr_type)) { if (safety_check_on) { assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMIntegerTypeKind); @@ -3769,7 +3769,8 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutable *executable, IrI assert(len_index != SIZE_MAX); LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)len_index, ""); LLVMValueRef len = gen_load_untyped(g, len_ptr, 0, false, ""); - add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, len); + LLVMIntPredicate upper_op = (ptr_type->data.pointer.sentinel != nullptr) ? LLVMIntULE : LLVMIntULT; + add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, upper_op, len); } size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; diff --git a/src/ir.cpp b/src/ir.cpp index df76d3f963..5e051c9781 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -18244,13 +18244,16 @@ static IrInstruction *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstructi static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) { assert(ptr_type->id == ZigTypeIdPointer); - return get_pointer_to_type_extra(g, + 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, new_align, 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.allow_zero, + ptr_type->data.pointer.vector_index, + ptr_type->data.pointer.inferred_struct_field, + ptr_type->data.pointer.sentinel); } 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 ConstExprValue *len_field = array_ptr_val->data.x_struct.fields[slice_len_index]; IrInstruction *result = ir_const(ira, &elem_ptr_instruction->base, return_type); ConstExprValue *out_val = &result->value; + ZigType *slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; uint64_t slice_len = bigint_as_u64(&len_field->data.x_bigint); - if (index >= slice_len) { + uint64_t full_slice_len = slice_len + + ((slice_ptr_type->data.pointer.sentinel != nullptr) ? 1 : 0); + if (index >= full_slice_len) { ir_add_error_node(ira, elem_ptr_instruction->base.source_node, buf_sprintf("index %" ZIG_PRI_u64 " outside slice of size %" ZIG_PRI_u64, index, slice_len)); @@ -18615,8 +18621,13 @@ static IrInstruction *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstruct { size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index; uint64_t new_index = offset + index; - ir_assert(new_index < ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len, + if (ptr_field->data.x_ptr.data.base_array.array_val->data.x_array.special != + ConstArraySpecialBuf) + { + ir_assert(new_index < + ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len, &elem_ptr_instruction->base); + } out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; out_val->data.x_ptr.data.base_array.array_val = ptr_field->data.x_ptr.data.base_array.array_val; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 20122b681a..d92808858a 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -70,14 +70,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( "disallow coercion from non-null-terminated pointer to null-terminated pointer", - \\extern fn puts(s: [*]null const u8) c_int; + \\extern fn puts(s: [*:0]const u8) c_int; \\pub fn main() void { \\ const no_zero_array = [_]u8{'h', 'e', 'l', 'l', 'o'}; \\ const no_zero_ptr: [*]const u8 = &no_zero_array; \\ _ = puts(no_zero_ptr); \\} , - "tmp.zig:5:14: error: expected type '[*]null const u8', found '[*]const u8'", + "tmp.zig:5:14: error: expected type '[*:0]const u8', found '[*]const u8'", ); cases.add( @@ -784,7 +784,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ strValue = strValue orelse ""; \\} , - "tmp.zig:3:32: error: expected type '[*c]u8', found '*const [0]null u8'", + "tmp.zig:3:32: error: expected type '[*c]u8', found '*const [0:0]u8'", "tmp.zig:3:32: note: cast discards const qualifier", ); @@ -2442,12 +2442,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { ); cases.add( - "var not allowed in structs", + "var makes structs required to be comptime known", \\export fn entry() void { - \\ var s = (struct{v: var}){.v=@as(i32, 10)}; + \\ const S = struct{v: var}; + \\ var s = S{.v=@as(i32, 10)}; \\} , - "tmp.zig:2:23: error: invalid token: 'var'", + "tmp.zig:3:4: error: variable of type 'S' must be const or comptime", ); cases.add( @@ -3357,7 +3358,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ return a; \\} , - "tmp.zig:3:12: error: expected type 'i32', found '*const [1]null u8'", + "tmp.zig:3:12: error: expected type 'i32', found '*const [1:0]u8'", ); cases.add( @@ -6212,7 +6213,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\} \\pub extern fn foo(format: *const u8, ...) void; , - "tmp.zig:2:16: error: expected type '*const u8', found '[5]null u8'", + "tmp.zig:2:16: error: expected type '*const u8', found '[5:0]u8'", ); cases.add( diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig index ea0a6fe9f4..3c394e39a1 100644 --- a/test/stage1/behavior/slice.zig +++ b/test/stage1/behavior/slice.zig @@ -65,3 +65,16 @@ test "slice type with custom alignment" { slice[1].anything = 42; expect(array[1].anything == 42); } + +test "access len index of sentinel-terminated slice" { + const S = struct { + fn doTheTest() void { + var slice: [:0]const u8 = "hello"; + + expect(slice.len == 5); + expect(slice[5] == 0); + } + }; + S.doTheTest(); + comptime S.doTheTest(); +}