From 00f42909adf23a92905aa7211d74ed5b5397b397 Mon Sep 17 00:00:00 2001 From: Pat Tullmann Date: Thu, 21 Sep 2023 07:50:48 -0700 Subject: [PATCH] langref: small fixes to wording and examples Simplify wording and add some formatting in several locations. Expand sentinel array tests to highlight (non-)handling of internal sentinels. Fix format of symbol names in function pointers example. Clarify wording a bit on the builin atomic* documentation. Remove the (second) builtin compileLog example that demonstrated a lack of compileLog entries. * langref: address comments from rohlem Use "0-terminated" instead of "null-terminated". Undo some changes that were not as clear an improvement as I though. * langref: remove stray "" Thanks to rohlem for spotting this typo. --- doc/langref.html.in | 118 +++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 62 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index b3a7470e3e..b4a3646877 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2523,19 +2523,28 @@ test "multidimensional arrays" { {#header_open|Sentinel-Terminated Arrays#}

The syntax {#syntax#}[N:x]T{#endsyntax#} describes an array which has a sentinel element of value {#syntax#}x{#endsyntax#} at the - index corresponding to {#syntax#}len{#endsyntax#}. + index corresponding to the length {#syntax#}N{#endsyntax#}.

{#code_begin|test|test_null_terminated_array#} const std = @import("std"); const expect = std.testing.expect; -test "null terminated array" { +test "0-terminated sentinel array" { const array = [_:0]u8 {1, 2, 3, 4}; try expect(@TypeOf(array) == [4:0]u8); try expect(array.len == 4); try expect(array[4] == 0); } + +test "extra 0s in 0-terminated sentinel array" { + // The sentinel value may appear earlier, but does not influence the compile-time 'len'. + const array = [_:0]u8 {1, 0, 0, 4}; + + try expect(@TypeOf(array) == [4:0]u8); + try expect(array.len == 4); + try expect(array[4] == 0); +} {#code_end#} {#see_also|Sentinel-Terminated Pointers|Sentinel-Terminated Slices#} {#header_close#} @@ -3052,8 +3061,6 @@ test "using slices for strings" { } test "slice pointer" { - var a: []u8 = undefined; - try expect(@TypeOf(a) == []u8); var array: [10]u8 = undefined; const ptr = &array; try expect(@TypeOf(ptr) == *[10]u8); @@ -3062,10 +3069,10 @@ test "slice pointer" { var start: usize = 0; var end: usize = 5; const slice = ptr[start..end]; - slice[2] = 3; - try expect(slice[2] == 3); // The slice is mutable because we sliced a mutable pointer. try expect(@TypeOf(slice) == []u8); + slice[2] = 3; + try expect(array[2] == 3); // Again, slicing with comptime-known indexes will produce another pointer // to an array: @@ -3088,7 +3095,7 @@ test "slice pointer" { const std = @import("std"); const expect = std.testing.expect; -test "null terminated slice" { +test "0-terminated slice" { const slice: [:0]const u8 = "hello"; try expect(slice.len == 5); @@ -3104,7 +3111,7 @@ test "null terminated slice" { const std = @import("std"); const expect = std.testing.expect; -test "null terminated slicing" { +test "0-terminated slicing" { var array = [_]u8{ 3, 2, 1, 0, 3, 2, 1, 0 }; var runtime_length: usize = 3; const slice = array[0..runtime_length :0]; @@ -3590,7 +3597,7 @@ const std = @import("std"); const expect = std.testing.expect; test "fully anonymous struct" { - try dump(.{ + try check(.{ .int = @as(u32, 1234), .float = @as(f64, 12.34), .b = true, @@ -3598,7 +3605,7 @@ test "fully anonymous struct" { }); } -fn dump(args: anytype) !void { +fn check(args: anytype) !void { try expect(args.int == 1234); try expect(args.float == 12.34); try expect(args.b); @@ -3813,8 +3820,8 @@ test "switch using enum literals" { {#header_open|Non-exhaustive enum#}

- A Non-exhaustive enum can be created by adding a trailing '_' field. - It must specify a tag type and cannot consume every enumeration value. + A non-exhaustive enum can be created by adding a trailing {#syntax#}_{#endsyntax#} field. + The enum must specify a tag type and cannot consume every enumeration value.

{#link|@enumFromInt#} on a non-exhaustive enum involves the safety semantics @@ -3822,8 +3829,8 @@ test "switch using enum literals" { a well-defined enum value.

- A switch on a non-exhaustive enum can include a '_' prong as an alternative to an {#syntax#}else{#endsyntax#} prong - with the difference being that it makes it a compile error if all the known tag names are not handled by the switch. + A switch on a non-exhaustive enum can include a {#syntax#}_{#endsyntax#} prong as an alternative to an {#syntax#}else{#endsyntax#} prong. + With a {#syntax#}_{#endsyntax#} prong the compiler errors if all the known tag names are not handled by the switch.

{#code_begin|test|test_switch_non-exhaustive#} const std = @import("std"); @@ -5268,14 +5275,14 @@ fn shiftLeftOne(a: u32) callconv(.Inline) u32 { pub fn sub2(a: i8, b: i8) i8 { return a - b; } // Function pointers are prefixed with `*const `. -const call2_op = *const fn (a: i8, b: i8) i8; -fn do_op(fn_call: call2_op, op1: i8, op2: i8) i8 { - return fn_call(op1, op2); +const Call2Op = *const fn (a: i8, b: i8) i8; +fn doOp(fnCall: Call2Op, op1: i8, op2: i8) i8 { + return fnCall(op1, op2); } test "function" { - try expect(do_op(add, 5, 6) == 11); - try expect(do_op(sub2, 5, 6) == -1); + try expect(doOp(add, 5, 6) == 11); + try expect(doOp(sub2, 5, 6) == -1); } {#code_end#}

There is a difference between a function body and a function pointer. @@ -6515,7 +6522,7 @@ test "coerce to optionals" { try expect(y == null); } {#code_end#} -

It works nested inside the {#link|Error Union Type#}, too:

+

Optionals work nested inside the {#link|Error Union Type#}, too:

{#code_begin|test|test_coerce_optional_wrapped_error_union#} const std = @import("std"); const expect = std.testing.expect; @@ -6841,7 +6848,8 @@ test "turn HashMap into a set with void" { {#syntax#}void{#endsyntax#} has a known size of 0 bytes, and {#syntax#}anyopaque{#endsyntax#} has an unknown, but non-zero, size.

- Expressions of type {#syntax#}void{#endsyntax#} are the only ones whose value can be ignored. For example: + Expressions of type {#syntax#}void{#endsyntax#} are the only ones whose value can be ignored. For example, ignoring + a non-{#syntax#}void{#endsyntax#} expression is a compile error:

{#code_begin|test_err|test_expression_ignored|ignored#} test "ignoring expression value" { @@ -6852,7 +6860,7 @@ fn foo() i32 { return 1234; } {#code_end#} -

However, if the expression has type {#syntax#}void{#endsyntax#}, there will be no error. Function return values can also be explicitly ignored by assigning them to {#syntax#}_{#endsyntax#}.

+

However, if the expression has type {#syntax#}void{#endsyntax#}, there will be no error. Expression results can be explicitly ignored by assigning them to {#syntax#}_{#endsyntax#}.

{#code_begin|test|test_void_ignored#} test "void is ignored" { returnsVoid(); @@ -7110,12 +7118,10 @@ fn performFn(start_value: i32) i32 { } {#end_syntax_block#}

- Note that this happens even in a debug build; in a release build these generated functions still - pass through rigorous LLVM optimizations. The important thing to note, however, is not that this - is a way to write more optimized code, but that it is a way to make sure that what should happen - at compile-time, does happen at compile-time. This catches more errors and as demonstrated - later in this article, allows expressiveness that in other languages requires using macros, - generated code, or a preprocessor to accomplish. + Note that this happens even in a debug build. + This is not a way to write more optimized code, but it is a way to make sure that what should happen + at compile-time, does happen at compile-time. This catches more errors and allows expressiveness + that in other languages requires using macros, generated code, or a preprocessor to accomplish.

{#header_close#} {#header_open|Compile-Time Expressions#} @@ -7297,9 +7303,8 @@ test "variable values" { {#header_close#} {#header_open|Generic Data Structures#}

- Zig uses these capabilities to implement generic data structures without introducing any - special-case syntax. If you followed along so far, you may already know how to create a - generic data structure. + Zig uses comptime capabilities to implement generic data structures without introducing any + special-case syntax.

Here is an example of a generic {#syntax#}List{#endsyntax#} data structure. @@ -7321,7 +7326,6 @@ var list = List(i32){ {#code_end#}

That's it. It's a function that returns an anonymous {#syntax#}struct{#endsyntax#}. - To keep the language small and uniform, all aggregate types in Zig are anonymous. For the purposes of error messages and debugging, Zig infers the name {#syntax#}"List(i32)"{#endsyntax#} from the function name and parameters invoked when creating the anonymous struct. @@ -7754,6 +7758,9 @@ test "global assembly" {

TODO: @fence()

TODO: @atomic rmw

TODO: builtin atomic memory ordering enum

+ + {#see_also|@atomicLoad|@atomicStore|@atomicRmw|@fence|@cmpxchgWeak|@cmpxchgStrong#} + {#header_close#} {#header_open|Async Functions#} @@ -7824,7 +7831,7 @@ comptime { {#header_open|@atomicLoad#}
{#syntax#}@atomicLoad(comptime T: type, ptr: *const T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}

- This builtin function atomically dereferences a pointer and returns the value. + This builtin function atomically dereferences a pointer to a {#syntax#}T{#endsyntax#} and returns the value.

{#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float, @@ -7836,14 +7843,15 @@ comptime { {#header_open|@atomicRmw#}

{#syntax#}@atomicRmw(comptime T: type, ptr: *T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) T{#endsyntax#}

- This builtin function atomically modifies memory and then returns the previous value. + This builtin function dereferences a pointer to a {#syntax#}T{#endsyntax#} and atomically + modifies the value and returns the previous value.

{#syntax#}T{#endsyntax#} must be a pointer, a {#syntax#}bool{#endsyntax#}, a float, an integer or an enum.

- Supported operations: + Supported values for the {#syntax#}op{#endsyntax#} parameter: