authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-04-24 17:41:47-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-04-25 00:09:24-07:00
log1b90888f576b4863f4a61213a9ca32b97aa57859
treeecf4d98a6b296b9aa36fd0c20a729413862f6b91
parent9d64332a5959b4955fe1a1eac793b48932b4a8a8

migrate langref documentation generation to the build system


283 files changed, 7777 insertions(+), 6403 deletions(-)

build.zig+56-17
...@@ -9,7 +9,7 @@ const fs = std.fs;...@@ -9,7 +9,7 @@ const fs = std.fs;
9const InstallDirectoryOptions = std.Build.InstallDirectoryOptions;9const InstallDirectoryOptions = std.Build.InstallDirectoryOptions;
10const assert = std.debug.assert;10const assert = std.debug.assert;
1111
12const zig_version = std.SemanticVersion{ .major = 0, .minor = 13, .patch = 0 };12const zig_version: std.SemanticVersion = .{ .major = 0, .minor = 13, .patch = 0 };
13const stack_size = 32 * 1024 * 1024;13const stack_size = 32 * 1024 * 1024;
1414
15pub fn build(b: *std.Build) !void {15pub fn build(b: *std.Build) !void {
...@@ -32,22 +32,7 @@ pub fn build(b: *std.Build) !void {...@@ -32,22 +32,7 @@ pub fn build(b: *std.Build) !void {
32 const std_docs = b.option(bool, "std-docs", "include standard library autodocs") orelse false;32 const std_docs = b.option(bool, "std-docs", "include standard library autodocs") orelse false;
33 const no_bin = b.option(bool, "no-bin", "skip emitting compiler binary") orelse false;33 const no_bin = b.option(bool, "no-bin", "skip emitting compiler binary") orelse false;
3434
35 const docgen_exe = b.addExecutable(.{35 const langref_file = generateLangRef(b);
36 .name = "docgen",
37 .root_source_file = b.path("tools/docgen.zig"),
38 .target = b.host,
39 .optimize = .Debug,
40 .single_threaded = single_threaded,
41 });
42
43 const docgen_cmd = b.addRunArtifact(docgen_exe);
44 docgen_cmd.addArgs(&.{ "--zig", b.graph.zig_exe });
45 if (b.zig_lib_dir) |p| {
46 docgen_cmd.addArg("--zig-lib-dir");
47 docgen_cmd.addDirectoryArg(p);
48 }
49 docgen_cmd.addFileArg(b.path("doc/langref.html.in"));
50 const langref_file = docgen_cmd.addOutputFileArg("langref.html");
51 const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "doc/langref.html");36 const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "doc/langref.html");
52 if (!skip_install_langref) {37 if (!skip_install_langref) {
53 b.getInstallStep().dependOn(&install_langref.step);38 b.getInstallStep().dependOn(&install_langref.step);
...@@ -1256,3 +1241,57 @@ const llvm_libs = [_][]const u8{...@@ -1256,3 +1241,57 @@ const llvm_libs = [_][]const u8{
1256 "LLVMSupport",1241 "LLVMSupport",
1257 "LLVMDemangle",1242 "LLVMDemangle",
1258};1243};
1244
1245fn generateLangRef(b: *std.Build) std.Build.LazyPath {
1246 const doctest_exe = b.addExecutable(.{
1247 .name = "doctest",
1248 .root_source_file = b.path("tools/doctest.zig"),
1249 .target = b.host,
1250 .optimize = .Debug,
1251 });
1252
1253 var dir = b.build_root.handle.openDir("doc/langref", .{ .iterate = true }) catch |err| {
1254 std.debug.panic("unable to open 'doc/langref' directory: {s}", .{@errorName(err)});
1255 };
1256 defer dir.close();
1257
1258 var wf = b.addWriteFiles();
1259
1260 var it = dir.iterateAssumeFirstIteration();
1261 while (it.next() catch @panic("failed to read dir")) |entry| {
1262 if (std.mem.startsWith(u8, entry.name, ".") or entry.kind != .file)
1263 continue;
1264
1265 const out_basename = b.fmt("{s}.out", .{std.fs.path.stem(entry.name)});
1266 const cmd = b.addRunArtifact(doctest_exe);
1267 cmd.addArgs(&.{
1268 "--zig", b.graph.zig_exe,
1269 // TODO: enhance doctest to use "--listen=-" rather than operating
1270 // in a temporary directory
1271 "--cache-root", b.cache_root.path orelse ".",
1272 });
1273 if (b.zig_lib_dir) |p| {
1274 cmd.addArg("--zig-lib-dir");
1275 cmd.addDirectoryArg(p);
1276 }
1277 cmd.addArgs(&.{"-i"});
1278 cmd.addFileArg(b.path(b.fmt("doc/langref/{s}", .{entry.name})));
1279
1280 cmd.addArgs(&.{"-o"});
1281 _ = wf.addCopyFile(cmd.addOutputFileArg(out_basename), out_basename);
1282 }
1283
1284 const docgen_exe = b.addExecutable(.{
1285 .name = "docgen",
1286 .root_source_file = b.path("tools/docgen.zig"),
1287 .target = b.host,
1288 .optimize = .Debug,
1289 });
1290
1291 const docgen_cmd = b.addRunArtifact(docgen_exe);
1292 docgen_cmd.addArgs(&.{"--code-dir"});
1293 docgen_cmd.addDirectoryArg(wf.getDirectory());
1294
1295 docgen_cmd.addFileArg(b.path("doc/langref.html.in"));
1296 return docgen_cmd.addOutputFileArg("langref.html");
1297}
ci/aarch64-linux-debug.sh+1
...@@ -53,6 +53,7 @@ ninja install...@@ -53,6 +53,7 @@ ninja install
53echo "Looking for non-conforming code formatting..."53echo "Looking for non-conforming code formatting..."
54stage3-debug/bin/zig fmt --check .. \54stage3-debug/bin/zig fmt --check .. \
55 --exclude ../test/cases/ \55 --exclude ../test/cases/ \
56 --exclude ../doc/ \
56 --exclude ../build-debug57 --exclude ../build-debug
5758
58# simultaneously test building self-hosted without LLVM and with 32-bit arm59# simultaneously test building self-hosted without LLVM and with 32-bit arm
ci/aarch64-linux-release.sh+1
...@@ -53,6 +53,7 @@ ninja install...@@ -53,6 +53,7 @@ ninja install
53echo "Looking for non-conforming code formatting..."53echo "Looking for non-conforming code formatting..."
54stage3-release/bin/zig fmt --check .. \54stage3-release/bin/zig fmt --check .. \
55 --exclude ../test/cases/ \55 --exclude ../test/cases/ \
56 --exclude ../doc/ \
56 --exclude ../build-release57 --exclude ../build-release
5758
58# simultaneously test building self-hosted without LLVM and with 32-bit arm59# simultaneously test building self-hosted without LLVM and with 32-bit arm
ci/x86_64-linux-debug.sh+1
...@@ -61,6 +61,7 @@ ninja install...@@ -61,6 +61,7 @@ ninja install
61echo "Looking for non-conforming code formatting..."61echo "Looking for non-conforming code formatting..."
62stage3-debug/bin/zig fmt --check .. \62stage3-debug/bin/zig fmt --check .. \
63 --exclude ../test/cases/ \63 --exclude ../test/cases/ \
64 --exclude ../doc/ \
64 --exclude ../build-debug65 --exclude ../build-debug
6566
66# simultaneously test building self-hosted without LLVM and with 32-bit arm67# simultaneously test building self-hosted without LLVM and with 32-bit arm
ci/x86_64-linux-release.sh+1
...@@ -61,6 +61,7 @@ ninja install...@@ -61,6 +61,7 @@ ninja install
61echo "Looking for non-conforming code formatting..."61echo "Looking for non-conforming code formatting..."
62stage3-release/bin/zig fmt --check .. \62stage3-release/bin/zig fmt --check .. \
63 --exclude ../test/cases/ \63 --exclude ../test/cases/ \
64 --exclude ../doc/ \
64 --exclude ../build-debug \65 --exclude ../build-debug \
65 --exclude ../build-release66 --exclude ../build-release
6667
doc/langref.html.in+380-5123
...@@ -363,26 +363,15 @@...@@ -363,26 +363,15 @@
363363
364 {#header_open|Hello World#}364 {#header_open|Hello World#}
365365
366 {#code_begin|exe|hello#}366 {#code|hello.zig#}
367const std = @import("std");
368367
369pub fn main() !void {
370 const stdout = std.io.getStdOut().writer();
371 try stdout.print("Hello, {s}!\n", .{"world"});
372}
373 {#code_end#}
374 <p>368 <p>
375 Most of the time, it is more appropriate to write to stderr rather than stdout, and369 Most of the time, it is more appropriate to write to stderr rather than stdout, and
376 whether or not the message is successfully written to the stream is irrelevant.370 whether or not the message is successfully written to the stream is irrelevant.
377 For this common case, there is a simpler API:371 For this common case, there is a simpler API:
378 </p>372 </p>
379 {#code_begin|exe|hello_again#}373 {#code|hello_again.zig#}
380const std = @import("std");
381374
382pub fn main() void {
383 std.debug.print("Hello, world!\n", .{});
384}
385 {#code_end#}
386 <p>375 <p>
387 In this case, the {#syntax#}!{#endsyntax#} may be omitted from the return376 In this case, the {#syntax#}!{#endsyntax#} may be omitted from the return
388 type because no errors are returned from the function.377 type because no errors are returned from the function.
...@@ -398,18 +387,8 @@ pub fn main() void {...@@ -398,18 +387,8 @@ pub fn main() void {
398 The generated documentation is still experimental, and can be produced with:387 The generated documentation is still experimental, and can be produced with:
399 </p>388 </p>
400 {#shell_samp#}zig test -femit-docs main.zig{#end_shell_samp#}389 {#shell_samp#}zig test -femit-docs main.zig{#end_shell_samp#}
401 {#code_begin|exe|comments#}390 {#code|comments.zig#}
402const print = @import("std").debug.print;
403
404pub fn main() void {
405 // Comments in Zig start with "//" and end at the next LF byte (end of line).
406 // The line below is a comment and won't be executed.
407
408 //print("Hello?", .{});
409391
410 print("Hello, world!\n", .{}); // another comment
411}
412 {#code_end#}
413 <p>392 <p>
414 There are no multiline comments in Zig (e.g. like <code class="c">/* */</code>393 There are no multiline comments in Zig (e.g. like <code class="c">/* */</code>
415 comments in C). This allows Zig to have the property that each line394 comments in C). This allows Zig to have the property that each line
...@@ -422,40 +401,17 @@ pub fn main() void {...@@ -422,40 +401,17 @@ pub fn main() void {
422 multiple doc comments in a row are merged together to form a multiline401 multiple doc comments in a row are merged together to form a multiline
423 doc comment. The doc comment documents whatever immediately follows it.402 doc comment. The doc comment documents whatever immediately follows it.
424 </p>403 </p>
425 {#code_begin|syntax|doc_comments#}404 {#code|doc_comments.zig#}
426/// A structure for storing a timestamp, with nanosecond precision (this is a405
427/// multiline doc comment).
428const Timestamp = struct {
429 /// The number of seconds since the epoch (this is also a doc comment).
430 seconds: i64, // signed so we can represent pre-1970 (not a doc comment)
431 /// The number of nanoseconds past the second (doc comment again).
432 nanos: u32,
433
434 /// Returns a `Timestamp` struct representing the Unix epoch; that is, the
435 /// moment of 1970 Jan 1 00:00:00 UTC (this is a doc comment too).
436 pub fn unixEpoch() Timestamp {
437 return Timestamp{
438 .seconds = 0,
439 .nanos = 0,
440 };
441 }
442};
443 {#code_end#}
444 <p>406 <p>
445 Doc comments are only allowed in certain places; it is a compile error to407 Doc comments are only allowed in certain places; it is a compile error to
446 have a doc comment in an unexpected place, such as in the middle of an expression,408 have a doc comment in an unexpected place, such as in the middle of an expression,
447 or just before a non-doc comment.409 or just before a non-doc comment.
448 </p>410 </p>
449 {#code_begin|obj_err|invalid_doc-comment|expected type expression, found 'a document comment'#}411 {#code|invalid_doc-comment.zig#}
450/// doc-comment412
451//! top-level doc-comment413 {#code|unattached_doc-comment.zig#}
452const std = @import("std");
453 {#code_end#}
454 {#code_begin|obj_err|unattached_doc-comment|unattached documentation comment#}
455pub fn main() void {}
456414
457/// End of file
458 {#code_end#}
459 <p>415 <p>
460 Doc comments can be interleaved with normal comments. Currently, when producing416 Doc comments can be interleaved with normal comments. Currently, when producing
461 the package documentation, normal comments are merged with doc comments.417 the package documentation, normal comments are merged with doc comments.
...@@ -470,71 +426,13 @@ pub fn main() void {}...@@ -470,71 +426,13 @@ pub fn main() void {}
470 It is a compile error if a top-level doc comment is not placed at the start426 It is a compile error if a top-level doc comment is not placed at the start
471 of a {#link|container|Containers#}, before any expressions.427 of a {#link|container|Containers#}, before any expressions.
472 </p>428 </p>
473 {#code_begin|syntax|tldoc_comments#}429 {#code|tldoc_comments.zig#}
474//! This module provides functions for retrieving the current date and
475//! time with varying degrees of precision and accuracy. It does not
476//! depend on libc, but will use functions from it if available.
477430
478const S = struct {
479 //! Top level comments are allowed inside a container other than a module,
480 //! but it is not very useful. Currently, when producing the package
481 //! documentation, these comments are ignored.
482};
483 {#code_end#}
484 {#header_close#}431 {#header_close#}
485 {#header_close#}432 {#header_close#}
486 {#header_open|Values#}433 {#header_open|Values#}
487 {#code_begin|exe|values#}434 {#code|values.zig#}
488// Top-level declarations are order-independent:435
489const print = std.debug.print;
490const std = @import("std");
491const os = std.os;
492const assert = std.debug.assert;
493
494pub fn main() void {
495 // integers
496 const one_plus_one: i32 = 1 + 1;
497 print("1 + 1 = {}\n", .{one_plus_one});
498
499 // floats
500 const seven_div_three: f32 = 7.0 / 3.0;
501 print("7.0 / 3.0 = {}\n", .{seven_div_three});
502
503 // boolean
504 print("{}\n{}\n{}\n", .{
505 true and false,
506 true or false,
507 !true,
508 });
509
510 // optional
511 var optional_value: ?[]const u8 = null;
512 assert(optional_value == null);
513
514 print("\noptional 1\ntype: {}\nvalue: {?s}\n", .{
515 @TypeOf(optional_value), optional_value,
516 });
517
518 optional_value = "hi";
519 assert(optional_value != null);
520
521 print("\noptional 2\ntype: {}\nvalue: {?s}\n", .{
522 @TypeOf(optional_value), optional_value,
523 });
524
525 // error union
526 var number_or_error: anyerror!i32 = error.ArgNotFound;
527
528 print("\nerror union 1\ntype: {}\nvalue: {!}\n", .{
529 @TypeOf(number_or_error), number_or_error, });
530
531 number_or_error = 1234;
532
533 print("\nerror union 2\ntype: {}\nvalue: {!}\n", .{
534 @TypeOf(number_or_error), number_or_error,
535 });
536}
537 {#code_end#}
538 {#header_open|Primitive Types#}436 {#header_open|Primitive Types#}
539 <div class="table-wrapper">437 <div class="table-wrapper">
540 <table>438 <table>
...@@ -785,27 +683,8 @@ pub fn main() void {...@@ -785,27 +683,8 @@ pub fn main() void {
785 {#link|Integer Literals#}. All {#link|Escape Sequences#} are valid in both string literals683 {#link|Integer Literals#}. All {#link|Escape Sequences#} are valid in both string literals
786 and Unicode code point literals.684 and Unicode code point literals.
787 </p>685 </p>
788 {#code_begin|exe|string_literals#}686 {#code|string_literals.zig#}
789const print = @import("std").debug.print;687
790const mem = @import("std").mem; // will be used to compare bytes
791
792pub fn main() void {
793 const bytes = "hello";
794 print("{}\n", .{@TypeOf(bytes)}); // *const [5:0]u8
795 print("{d}\n", .{bytes.len}); // 5
796 print("{c}\n", .{bytes[1]}); // 'e'
797 print("{d}\n", .{bytes[5]}); // 0
798 print("{}\n", .{'e' == '\x65'}); // true
799 print("{d}\n", .{'\u{1f4a9}'}); // 128169
800 print("{d}\n", .{'💯'}); // 128175
801 print("{u}\n", .{'âš¡'});
802 print("{}\n", .{mem.eql(u8, "hello", "h\x65llo")}); // true
803 print("{}\n", .{mem.eql(u8, "💯", "\xf0\x9f\x92\xaf")}); // also true
804 const invalid_utf8 = "\xff\xfe"; // non-UTF-8 strings are possible with \xNN notation.
805 print("0x{x}\n", .{invalid_utf8[1]}); // indexing them returns individual bytes...
806 print("0x{x}\n", .{"💯"[1]}); // ...as does indexing part-way through non-ASCII characters
807}
808 {#code_end#}
809 {#see_also|Arrays|Source Encoding#}688 {#see_also|Arrays|Source Encoding#}
810 {#header_open|Escape Sequences#}689 {#header_open|Escape Sequences#}
811 <div class="table-wrapper">690 <div class="table-wrapper">
...@@ -864,68 +743,26 @@ pub fn main() void {...@@ -864,68 +743,26 @@ pub fn main() void {
864 However, if the next line begins with {#syntax#}\\{#endsyntax#} then a newline is appended and743 However, if the next line begins with {#syntax#}\\{#endsyntax#} then a newline is appended and
865 the string literal continues.744 the string literal continues.
866 </p>745 </p>
867 {#code_begin|syntax|multiline_string_literals#}746 {#code|multiline_string_literals.zig#}
868const hello_world_in_c =747
869 \\#include <stdio.h>
870 \\
871 \\int main(int argc, char **argv) {
872 \\ printf("hello world\n");
873 \\ return 0;
874 \\}
875;
876 {#code_end#}
877 {#see_also|@embedFile#}748 {#see_also|@embedFile#}
878 {#header_close#}749 {#header_close#}
879 {#header_close#}750 {#header_close#}
880 {#header_open|Assignment#}751 {#header_open|Assignment#}
881 <p>Use the {#syntax#}const{#endsyntax#} keyword to assign a value to an identifier:</p>752 <p>Use the {#syntax#}const{#endsyntax#} keyword to assign a value to an identifier:</p>
882 {#code_begin|exe_build_err|constant_identifier_cannot_change#}753 {#code|constant_identifier_cannot_change.zig#}
883const x = 1234;
884754
885fn foo() void {
886 // It works at file scope as well as inside functions.
887 const y = 5678;
888
889 // Once assigned, an identifier cannot be changed.
890 y += 1;
891}
892
893pub fn main() void {
894 foo();
895}
896 {#code_end#}
897 <p>{#syntax#}const{#endsyntax#} applies to all of the bytes that the identifier immediately addresses. {#link|Pointers#} have their own const-ness.</p>755 <p>{#syntax#}const{#endsyntax#} applies to all of the bytes that the identifier immediately addresses. {#link|Pointers#} have their own const-ness.</p>
898 <p>If you need a variable that you can modify, use the {#syntax#}var{#endsyntax#} keyword:</p>756 <p>If you need a variable that you can modify, use the {#syntax#}var{#endsyntax#} keyword:</p>
899 {#code_begin|exe|mutable_var#}757 {#code|mutable_var.zig#}
900const print = @import("std").debug.print;
901758
902pub fn main() void {
903 var y: i32 = 5678;
904
905 y += 1;
906
907 print("{d}", .{y});
908}
909 {#code_end#}
910 <p>Variables must be initialized:</p>759 <p>Variables must be initialized:</p>
911 {#code_begin|exe_build_err|var_must_be_initialized#}760 {#code|var_must_be_initialized.zig#}
912pub fn main() void {
913 var x: i32;
914761
915 x = 1;
916}
917 {#code_end#}
918 {#header_open|undefined#}762 {#header_open|undefined#}
919 <p>Use {#syntax#}undefined{#endsyntax#} to leave variables uninitialized:</p>763 <p>Use {#syntax#}undefined{#endsyntax#} to leave variables uninitialized:</p>
920 {#code_begin|exe|assign_undefined#}764 {#code|assign_undefined.zig#}
921const print = @import("std").debug.print;
922765
923pub fn main() void {
924 var x: i32 = undefined;
925 x = 1;
926 print("{d}", .{x});
927}
928 {#code_end#}
929 <p>766 <p>
930 {#syntax#}undefined{#endsyntax#} can be {#link|coerced|Type Coercion#} to any type.767 {#syntax#}undefined{#endsyntax#} can be {#link|coerced|Type Coercion#} to any type.
931 Once this happens, it is no longer possible to detect that the value is {#syntax#}undefined{#endsyntax#}.768 Once this happens, it is no longer possible to detect that the value is {#syntax#}undefined{#endsyntax#}.
...@@ -945,29 +782,8 @@ pub fn main() void {...@@ -945,29 +782,8 @@ pub fn main() void {
945 <p>782 <p>
946 Code written within one or more {#syntax#}test{#endsyntax#} declarations can be used to ensure behavior meets expectations:783 Code written within one or more {#syntax#}test{#endsyntax#} declarations can be used to ensure behavior meets expectations:
947 </p>784 </p>
948 {#code_begin|test|testing_introduction#}785 {#code|testing_introduction.zig#}
949const std = @import("std");
950
951test "expect addOne adds one to 41" {
952
953 // The Standard Library contains useful functions to help create tests.
954 // `expect` is a function that verifies its argument is true.
955 // It will return an error if its argument is false to indicate a failure.
956 // `try` is used to return an error to the test runner to notify it that the test failed.
957 try std.testing.expect(addOne(41) == 42);
958}
959786
960test addOne {
961 // A test name can also be written using an identifier.
962 // This is a doctest, and serves as documentation for `addOne`.
963 try std.testing.expect(addOne(41) == 42);
964}
965
966/// The function `addOne` adds one to the number given as its argument.
967fn addOne(number: i32) i32 {
968 return number + 1;
969}
970 {#code_end#}
971 <p>787 <p>
972 The <code class="file">testing_introduction.zig</code> code sample tests the {#link|function|Functions#}788 The <code class="file">testing_introduction.zig</code> code sample tests the {#link|function|Functions#}
973 {#syntax#}addOne{#endsyntax#} to ensure that it returns {#syntax#}42{#endsyntax#} given the input789 {#syntax#}addOne{#endsyntax#} to ensure that it returns {#syntax#}42{#endsyntax#} given the input
...@@ -1044,17 +860,8 @@ fn addOne(number: i32) i32 {...@@ -1044,17 +860,8 @@ fn addOne(number: i32) i32 {
1044 When a test returns an error, the test is considered a failure and its {#link|error return trace|Error Return Traces#}860 When a test returns an error, the test is considered a failure and its {#link|error return trace|Error Return Traces#}
1045 is output to standard error. The total number of failures will be reported after all tests have run.861 is output to standard error. The total number of failures will be reported after all tests have run.
1046 </p>862 </p>
1047 {#code_begin|test_err|testing_failure#}863 {#code|testing_failure.zig#}
1048const std = @import("std");
1049
1050test "expect this to fail" {
1051 try std.testing.expect(false);
1052}
1053864
1054test "expect this to succeed" {
1055 try std.testing.expect(true);
1056}
1057 {#code_end#}
1058 {#header_close#}865 {#header_close#}
1059 {#header_open|Skip Tests#}866 {#header_open|Skip Tests#}
1060 <p>867 <p>
...@@ -1068,11 +875,8 @@ test "expect this to succeed" {...@@ -1068,11 +875,8 @@ test "expect this to succeed" {
1068 {#syntax#}error.SkipZigTest{#endsyntax#} and the default test runner will consider the test as being skipped.875 {#syntax#}error.SkipZigTest{#endsyntax#} and the default test runner will consider the test as being skipped.
1069 The total number of skipped tests will be reported after all tests have run.876 The total number of skipped tests will be reported after all tests have run.
1070 </p>877 </p>
1071 {#code_begin|test|testing_skip#}878 {#code|testing_skip.zig#}
1072test "this will be skipped" {879
1073 return error.SkipZigTest;
1074}
1075 {#code_end#}
1076 {#header_close#}880 {#header_close#}
1077881
1078 {#header_open|Report Memory Leaks#}882 {#header_open|Report Memory Leaks#}
...@@ -1081,17 +885,8 @@ test "this will be skipped" {...@@ -1081,17 +885,8 @@ test "this will be skipped" {
1081 {#syntax#}std.testing.allocator{#endsyntax#}, the default test runner will report any leaks that are885 {#syntax#}std.testing.allocator{#endsyntax#}, the default test runner will report any leaks that are
1082 found from using the testing allocator:886 found from using the testing allocator:
1083 </p>887 </p>
1084 {#code_begin|test_err|testing_detect_leak|1 tests leaked memory#}888 {#code|testing_detect_leak.zig#}
1085const std = @import("std");
1086889
1087test "detect leak" {
1088 var list = std.ArrayList(u21).init(std.testing.allocator);
1089 // missing `defer list.deinit();`
1090 try list.append('☔');
1091
1092 try std.testing.expect(list.items.len == 1);
1093}
1094 {#code_end#}
1095 {#see_also|defer|Memory#}890 {#see_also|defer|Memory#}
1096 {#header_close#}891 {#header_close#}
1097 {#header_open|Detecting Test Build#}892 {#header_open|Detecting Test Build#}
...@@ -1099,19 +894,8 @@ test "detect leak" {...@@ -1099,19 +894,8 @@ test "detect leak" {
1099 Use the {#link|compile variable|Compile Variables#} {#syntax#}@import("builtin").is_test{#endsyntax#}894 Use the {#link|compile variable|Compile Variables#} {#syntax#}@import("builtin").is_test{#endsyntax#}
1100 to detect a test build:895 to detect a test build:
1101 </p>896 </p>
1102 {#code_begin|test|testing_detect_test#}897 {#code|testing_detect_test.zig#}
1103const std = @import("std");
1104const builtin = @import("builtin");
1105const expect = std.testing.expect;
1106
1107test "builtin.is_test" {
1108 try expect(isATest());
1109}
1110898
1111fn isATest() bool {
1112 return builtin.is_test;
1113}
1114 {#code_end#}
1115 {#header_close#}899 {#header_close#}
1116 {#header_open|Test Output and Logging#}900 {#header_open|Test Output and Logging#}
1117 <p>901 <p>
...@@ -1124,28 +908,8 @@ fn isATest() bool {...@@ -1124,28 +908,8 @@ fn isATest() bool {
1124 you create tests. In addition to the <code>expect</code> function, this document uses a couple of more functions908 you create tests. In addition to the <code>expect</code> function, this document uses a couple of more functions
1125 as exemplified here:909 as exemplified here:
1126 </p>910 </p>
1127 {#code_begin|test|testing_namespace#}911 {#code|testing_namespace.zig#}
1128const std = @import("std");
1129
1130test "expectEqual demo" {
1131 const expected: i32 = 42;
1132 const actual = 42;
1133
1134 // The first argument to `expectEqual` is the known, expected, result.
1135 // The second argument is the result of some expression.
1136 // The actual's type is casted to the type of expected.
1137 try std.testing.expectEqual(expected, actual);
1138}
1139
1140test "expectError demo" {
1141 const expected_error = error.DemoError;
1142 const actual_error_union: anyerror!void = error.DemoError;
1143912
1144 // `expectError` will fail when the actual error is different than
1145 // the expected error.
1146 try std.testing.expectError(expected_error, actual_error_union);
1147}
1148 {#code_end#}
1149 <p>The Zig Standard Library also contains functions to compare {#link|Slices#}, strings, and more. See the rest of the913 <p>The Zig Standard Library also contains functions to compare {#link|Slices#}, strings, and more. See the rest of the
1150 {#syntax#}std.testing{#endsyntax#} namespace in the {#link|Zig Standard Library#} for more available functions.</p>914 {#syntax#}std.testing{#endsyntax#} namespace in the {#link|Zig Standard Library#} for more available functions.</p>
1151 {#header_close#}915 {#header_close#}
...@@ -1186,20 +950,8 @@ test "expectError demo" {...@@ -1186,20 +950,8 @@ test "expectError demo" {
1186 <p>950 <p>
1187 If a name that does not fit these requirements is needed, such as for linking with external libraries, the {#syntax#}@""{#endsyntax#} syntax may be used.951 If a name that does not fit these requirements is needed, such as for linking with external libraries, the {#syntax#}@""{#endsyntax#} syntax may be used.
1188 </p>952 </p>
1189 {#code_begin|syntax|identifiers#}953 {#code|identifiers.zig#}
1190const @"identifier with spaces in it" = 0xff;
1191const @"1SmallStep4Man" = 112358;
1192
1193const c = @import("std").c;
1194pub extern "c" fn @"error"() void;
1195pub extern "c" fn @"fstat$INODE64"(fd: c.fd_t, buf: *c.Stat) c_int;
1196954
1197const Color = enum {
1198 red,
1199 @"really red",
1200};
1201const color: Color = .@"really red";
1202 {#code_end#}
1203 {#header_close#}955 {#header_close#}
1204956
1205 {#header_open|Container Level Variables#}957 {#header_open|Container Level Variables#}
...@@ -1209,92 +961,29 @@ const color: Color = .@"really red";...@@ -1209,92 +961,29 @@ const color: Color = .@"really red";
1209 {#link|comptime#}. If a container level variable is {#syntax#}const{#endsyntax#} then its value is961 {#link|comptime#}. If a container level variable is {#syntax#}const{#endsyntax#} then its value is
1210 {#syntax#}comptime{#endsyntax#}-known, otherwise it is runtime-known.962 {#syntax#}comptime{#endsyntax#}-known, otherwise it is runtime-known.
1211 </p>963 </p>
1212 {#code_begin|test|test_container_level_variables#}964 {#code|test_container_level_variables.zig#}
1213var y: i32 = add(10, x);
1214const x: i32 = add(12, 34);
1215
1216test "container level variables" {
1217 try expect(x == 46);
1218 try expect(y == 56);
1219}
1220
1221fn add(a: i32, b: i32) i32 {
1222 return a + b;
1223}
1224965
1225const std = @import("std");
1226const expect = std.testing.expect;
1227 {#code_end#}
1228 <p>966 <p>
1229 Container level variables may be declared inside a {#link|struct#}, {#link|union#}, {#link|enum#}, or {#link|opaque#}:967 Container level variables may be declared inside a {#link|struct#}, {#link|union#}, {#link|enum#}, or {#link|opaque#}:
1230 </p>968 </p>
1231 {#code_begin|test|test_namespaced_container_level_variable#}969 {#code|test_namespaced_container_level_variable.zig#}
1232const std = @import("std");
1233const expect = std.testing.expect;
1234
1235test "namespaced container level variable" {
1236 try expect(foo() == 1235);
1237 try expect(foo() == 1236);
1238}
1239
1240const S = struct {
1241 var x: i32 = 1234;
1242};
1243970
1244fn foo() i32 {
1245 S.x += 1;
1246 return S.x;
1247}
1248 {#code_end#}
1249 {#header_close#}971 {#header_close#}
1250972
1251 {#header_open|Static Local Variables#}973 {#header_open|Static Local Variables#}
1252 <p>974 <p>
1253 It is also possible to have local variables with static lifetime by using containers inside functions.975 It is also possible to have local variables with static lifetime by using containers inside functions.
1254 </p>976 </p>
1255 {#code_begin|test|test_static_local_variable#}977 {#code|test_static_local_variable.zig#}
1256const std = @import("std");
1257const expect = std.testing.expect;
1258
1259test "static local variable" {
1260 try expect(foo() == 1235);
1261 try expect(foo() == 1236);
1262}
1263978
1264fn foo() i32 {
1265 const S = struct {
1266 var x: i32 = 1234;
1267 };
1268 S.x += 1;
1269 return S.x;
1270}
1271 {#code_end#}
1272 {#header_close#}979 {#header_close#}
1273980
1274 {#header_open|Thread Local Variables#}981 {#header_open|Thread Local Variables#}
1275 <p>A variable may be specified to be a thread-local variable using the982 <p>A variable may be specified to be a thread-local variable using the
1276 {#syntax#}threadlocal{#endsyntax#} keyword,983 {#syntax#}threadlocal{#endsyntax#} keyword,
1277 which makes each thread work with a separate instance of the variable:</p>984 which makes each thread work with a separate instance of the variable:</p>
1278 {#code_begin|test|test_thread_local_variables#}985 {#code|test_thread_local_variables.zig#}
1279const std = @import("std");
1280const assert = std.debug.assert;
1281
1282threadlocal var x: i32 = 1234;
1283
1284test "thread local storage" {
1285 const thread1 = try std.Thread.spawn(.{}, testTls, .{});
1286 const thread2 = try std.Thread.spawn(.{}, testTls, .{});
1287 testTls();
1288 thread1.join();
1289 thread2.join();
1290}
1291986
1292fn testTls() void {
1293 assert(x == 1234);
1294 x += 1;
1295 assert(x == 1235);
1296}
1297 {#code_end#}
1298 <p>987 <p>
1299 For {#link|Single Threaded Builds#}, all thread local variables are treated as regular {#link|Container Level Variables#}.988 For {#link|Single Threaded Builds#}, all thread local variables are treated as regular {#link|Container Level Variables#}.
1300 </p>989 </p>
...@@ -1319,45 +1008,15 @@ fn testTls() void {...@@ -1319,45 +1008,15 @@ fn testTls() void {
1319 All variables declared in a {#syntax#}comptime{#endsyntax#} expression are implicitly1008 All variables declared in a {#syntax#}comptime{#endsyntax#} expression are implicitly
1320 {#syntax#}comptime{#endsyntax#} variables.1009 {#syntax#}comptime{#endsyntax#} variables.
1321 </p>1010 </p>
1322 {#code_begin|test|test_comptime_variables#}1011 {#code|test_comptime_variables.zig#}
1323const std = @import("std");
1324const expect = std.testing.expect;
13251012
1326test "comptime vars" {
1327 var x: i32 = 1;
1328 comptime var y: i32 = 1;
1329
1330 x += 1;
1331 y += 1;
1332
1333 try expect(x == 2);
1334 try expect(y == 2);
1335
1336 if (y != 2) {
1337 // This compile error never triggers because y is a comptime variable,
1338 // and so `y != 2` is a comptime value, and this if is statically evaluated.
1339 @compileError("wrong y value");
1340 }
1341}
1342 {#code_end#}
1343 {#header_close#}1013 {#header_close#}
1344 {#header_close#}1014 {#header_close#}
13451015
1346 {#header_open|Integers#}1016 {#header_open|Integers#}
1347 {#header_open|Integer Literals#}1017 {#header_open|Integer Literals#}
1348 {#code_begin|syntax|integer_literals#}1018 {#code|integer_literals.zig#}
1349const decimal_int = 98222;1019
1350const hex_int = 0xff;
1351const another_hex_int = 0xFF;
1352const octal_int = 0o755;
1353const binary_int = 0b11110000;
1354
1355// underscores may be placed between two digits as a visual separator
1356const one_billion = 1_000_000_000;
1357const binary_mask = 0b1_1111_1111;
1358const permissions = 0o7_5_5;
1359const big_address = 0xFF80_0000_0000_0000;
1360 {#code_end#}
1361 {#header_close#}1020 {#header_close#}
1362 {#header_open|Runtime Integer Values#}1021 {#header_open|Runtime Integer Values#}
1363 <p>1022 <p>
...@@ -1368,11 +1027,8 @@ const big_address = 0xFF80_0000_0000_0000;...@@ -1368,11 +1027,8 @@ const big_address = 0xFF80_0000_0000_0000;
1368 However, once an integer value is no longer known at compile-time, it must have a1027 However, once an integer value is no longer known at compile-time, it must have a
1369 known size, and is vulnerable to undefined behavior.1028 known size, and is vulnerable to undefined behavior.
1370 </p>1029 </p>
1371 {#code_begin|syntax|runtime_vs_comptime#}1030 {#code|runtime_vs_comptime.zig#}
1372fn divide(a: i32, b: i32) i32 {1031
1373 return a / b;
1374}
1375 {#code_end#}
1376 <p>1032 <p>
1377 In this function, values {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#} are known only at runtime,1033 In this function, values {#syntax#}a{#endsyntax#} and {#syntax#}b{#endsyntax#} are known only at runtime,
1378 and thus this division operation is vulnerable to both {#link|Integer Overflow#} and1034 and thus this division operation is vulnerable to both {#link|Integer Overflow#} and
...@@ -1414,66 +1070,25 @@ fn divide(a: i32, b: i32) i32 {...@@ -1414,66 +1070,25 @@ fn divide(a: i32, b: i32) i32 {
1414 Float literals {#link|coerce|Type Coercion#} to any floating point type,1070 Float literals {#link|coerce|Type Coercion#} to any floating point type,
1415 and to any {#link|integer|Integers#} type when there is no fractional component.1071 and to any {#link|integer|Integers#} type when there is no fractional component.
1416 </p>1072 </p>
1417 {#code_begin|syntax|float_literals#}1073 {#code|float_literals.zig#}
1418const floating_point = 123.0E+77;
1419const another_float = 123.0;
1420const yet_another = 123.0e+77;
14211074
1422const hex_floating_point = 0x103.70p-5;
1423const another_hex_float = 0x103.70;
1424const yet_another_hex_float = 0x103.70P-5;
1425
1426// underscores may be placed between two digits as a visual separator
1427const lightspeed = 299_792_458.000_000;
1428const nanosecond = 0.000_000_001;
1429const more_hex = 0x1234_5678.9ABC_CDEFp-10;
1430 {#code_end#}
1431 <p>1075 <p>
1432 There is no syntax for NaN, infinity, or negative infinity. For these special values,1076 There is no syntax for NaN, infinity, or negative infinity. For these special values,
1433 one must use the standard library:1077 one must use the standard library:
1434 </p>1078 </p>
1435 {#code_begin|syntax|float_special_values#}1079 {#code|float_special_values.zig#}
1436const std = @import("std");
14371080
1438const inf = std.math.inf(f32);
1439const negative_inf = -std.math.inf(f64);
1440const nan = std.math.nan(f128);
1441 {#code_end#}
1442 {#header_close#}1081 {#header_close#}
1443 {#header_open|Floating Point Operations#}1082 {#header_open|Floating Point Operations#}
1444 <p>By default floating point operations use {#syntax#}Strict{#endsyntax#} mode,1083 <p>By default floating point operations use {#syntax#}Strict{#endsyntax#} mode,
1445 but you can switch to {#syntax#}Optimized{#endsyntax#} mode on a per-block basis:</p>1084 but you can switch to {#syntax#}Optimized{#endsyntax#} mode on a per-block basis:</p>
1446 {#code_begin|obj|float_mode_obj#}1085 {#code|float_mode_obj.zig#}
1447 {#code_release_fast#}
1448 {#code_disable_cache#}
1449const std = @import("std");
1450const big = @as(f64, 1 << 40);
1451
1452export fn foo_strict(x: f64) f64 {
1453 return x + big - big;
1454}
14551086
1456export fn foo_optimized(x: f64) f64 {
1457 @setFloatMode(.optimized);
1458 return x + big - big;
1459}
1460 {#code_end#}
1461 <p>For this test we have to separate code into two object files -1087 <p>For this test we have to separate code into two object files -
1462 otherwise the optimizer figures out all the values at compile-time,1088 otherwise the optimizer figures out all the values at compile-time,
1463 which operates in strict mode.</p>1089 which operates in strict mode.</p>
1464 {#code_begin|exe|float_mode_exe#}1090 {#code|float_mode_exe.zig#}
1465 {#code_link_object|float_mode_obj#}
1466const print = @import("std").debug.print;
1467
1468extern fn foo_strict(x: f64) f64;
1469extern fn foo_optimized(x: f64) f64;
14701091
1471pub fn main() void {
1472 const x = 0.001;
1473 print("optimized = {}\n", .{foo_optimized(x)});
1474 print("strict = {}\n", .{foo_strict(x)});
1475}
1476 {#code_end#}
1477 {#see_also|@setFloatMode|Division by Zero#}1092 {#see_also|@setFloatMode|Division by Zero#}
1478 {#header_close#}1093 {#header_close#}
1479 {#header_close#}1094 {#header_close#}
...@@ -2241,141 +1856,16 @@ or...@@ -2241,141 +1856,16 @@ or
2241 {#header_close#}1856 {#header_close#}
2242 {#header_close#}1857 {#header_close#}
2243 {#header_open|Arrays#}1858 {#header_open|Arrays#}
2244 {#code_begin|test|test_arrays#}1859 {#code|test_arrays.zig#}
2245const expect = @import("std").testing.expect;
2246const assert = @import("std").debug.assert;
2247const mem = @import("std").mem;
2248
2249// array literal
2250const message = [_]u8{ 'h', 'e', 'l', 'l', 'o' };
2251
2252// get the size of an array
2253comptime {
2254 assert(message.len == 5);
2255}
2256
2257// A string literal is a single-item pointer to an array.
2258const same_message = "hello";
2259
2260comptime {
2261 assert(mem.eql(u8, &message, same_message));
2262}
2263
2264test "iterate over an array" {
2265 var sum: usize = 0;
2266 for (message) |byte| {
2267 sum += byte;
2268 }
2269 try expect(sum == 'h' + 'e' + 'l' * 2 + 'o');
2270}
2271
2272// modifiable array
2273var some_integers: [100]i32 = undefined;
2274
2275test "modify an array" {
2276 for (&some_integers, 0..) |*item, i| {
2277 item.* = @intCast(i);
2278 }
2279 try expect(some_integers[10] == 10);
2280 try expect(some_integers[99] == 99);
2281}
2282
2283// array concatenation works if the values are known
2284// at compile time
2285const part_one = [_]i32{ 1, 2, 3, 4 };
2286const part_two = [_]i32{ 5, 6, 7, 8 };
2287const all_of_it = part_one ++ part_two;
2288comptime {
2289 assert(mem.eql(i32, &all_of_it, &[_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }));
2290}
2291
2292// remember that string literals are arrays
2293const hello = "hello";
2294const world = "world";
2295const hello_world = hello ++ " " ++ world;
2296comptime {
2297 assert(mem.eql(u8, hello_world, "hello world"));
2298}
2299
2300// ** does repeating patterns
2301const pattern = "ab" ** 3;
2302comptime {
2303 assert(mem.eql(u8, pattern, "ababab"));
2304}
2305
2306// initialize an array to zero
2307const all_zero = [_]u16{0} ** 10;
2308
2309comptime {
2310 assert(all_zero.len == 10);
2311 assert(all_zero[5] == 0);
2312}
2313
2314// use compile-time code to initialize an array
2315var fancy_array = init: {
2316 var initial_value: [10]Point = undefined;
2317 for (&initial_value, 0..) |*pt, i| {
2318 pt.* = Point{
2319 .x = @intCast(i),
2320 .y = @intCast(i * 2),
2321 };
2322 }
2323 break :init initial_value;
2324};
2325const Point = struct {
2326 x: i32,
2327 y: i32,
2328};
2329
2330test "compile-time array initialization" {
2331 try expect(fancy_array[4].x == 4);
2332 try expect(fancy_array[4].y == 8);
2333}
23341860
2335// call a function to initialize an array
2336var more_points = [_]Point{makePoint(3)} ** 10;
2337fn makePoint(x: i32) Point {
2338 return Point{
2339 .x = x,
2340 .y = x * 2,
2341 };
2342}
2343test "array initialization with function calls" {
2344 try expect(more_points[4].x == 3);
2345 try expect(more_points[4].y == 6);
2346 try expect(more_points.len == 10);
2347}
2348 {#code_end#}
2349 {#see_also|for|Slices#}1861 {#see_also|for|Slices#}
23501862
2351 {#header_open|Multidimensional Arrays#}1863 {#header_open|Multidimensional Arrays#}
2352 <p>1864 <p>
2353 Multidimensional arrays can be created by nesting arrays:1865 Multidimensional arrays can be created by nesting arrays:
2354 </p>1866 </p>
2355 {#code_begin|test|test_multidimensional_arrays#}1867 {#code|test_multidimensional_arrays.zig#}
2356const std = @import("std");
2357const expect = std.testing.expect;
23581868
2359const mat4x4 = [4][4]f32{
2360 [_]f32{ 1.0, 0.0, 0.0, 0.0 },
2361 [_]f32{ 0.0, 1.0, 0.0, 1.0 },
2362 [_]f32{ 0.0, 0.0, 1.0, 0.0 },
2363 [_]f32{ 0.0, 0.0, 0.0, 1.0 },
2364};
2365test "multidimensional arrays" {
2366 // Access the 2D array by indexing the outer array, and then the inner array.
2367 try expect(mat4x4[1][1] == 1.0);
2368
2369 // Here we iterate with for loops.
2370 for (mat4x4, 0..) |row, row_index| {
2371 for (row, 0..) |cell, column_index| {
2372 if (row_index == column_index) {
2373 try expect(cell == 1.0);
2374 }
2375 }
2376 }
2377}
2378 {#code_end#}
2379 {#header_close#}1869 {#header_close#}
23801870
2381 {#header_open|Sentinel-Terminated Arrays#}1871 {#header_open|Sentinel-Terminated Arrays#}
...@@ -2383,27 +1873,8 @@ test "multidimensional arrays" {...@@ -2383,27 +1873,8 @@ test "multidimensional arrays" {
2383 The syntax {#syntax#}[N:x]T{#endsyntax#} describes an array which has a sentinel element of value {#syntax#}x{#endsyntax#} at the1873 The syntax {#syntax#}[N:x]T{#endsyntax#} describes an array which has a sentinel element of value {#syntax#}x{#endsyntax#} at the
2384 index corresponding to the length {#syntax#}N{#endsyntax#}.1874 index corresponding to the length {#syntax#}N{#endsyntax#}.
2385 </p>1875 </p>
2386 {#code_begin|test|test_null_terminated_array#}1876 {#code|test_null_terminated_array.zig#}
2387const std = @import("std");
2388const expect = std.testing.expect;
2389
2390test "0-terminated sentinel array" {
2391 const array = [_:0]u8 {1, 2, 3, 4};
23921877
2393 try expect(@TypeOf(array) == [4:0]u8);
2394 try expect(array.len == 4);
2395 try expect(array[4] == 0);
2396}
2397
2398test "extra 0s in 0-terminated sentinel array" {
2399 // The sentinel value may appear earlier, but does not influence the compile-time 'len'.
2400 const array = [_:0]u8 {1, 0, 0, 4};
2401
2402 try expect(@TypeOf(array) == [4:0]u8);
2403 try expect(array.len == 4);
2404 try expect(array[4] == 0);
2405}
2406 {#code_end#}
2407 {#see_also|Sentinel-Terminated Pointers|Sentinel-Terminated Slices#}1878 {#see_also|Sentinel-Terminated Pointers|Sentinel-Terminated Slices#}
2408 {#header_close#}1879 {#header_close#}
2409 {#header_close#}1880 {#header_close#}
...@@ -2445,47 +1916,8 @@ test "extra 0s in 0-terminated sentinel array" {...@@ -2445,47 +1916,8 @@ test "extra 0s in 0-terminated sentinel array" {
2445 although small powers of two (2-64) are most typical. Note that excessively long vector lengths (e.g. 2^20) may1916 although small powers of two (2-64) are most typical. Note that excessively long vector lengths (e.g. 2^20) may
2446 result in compiler crashes on current versions of Zig.1917 result in compiler crashes on current versions of Zig.
2447 </p>1918 </p>
2448 {#code_begin|test|test_vector#}1919 {#code|test_vector.zig#}
2449const std = @import("std");
2450const expectEqual = std.testing.expectEqual;
2451
2452test "Basic vector usage" {
2453 // Vectors have a compile-time known length and base type.
2454 const a = @Vector(4, i32){ 1, 2, 3, 4 };
2455 const b = @Vector(4, i32){ 5, 6, 7, 8 };
2456
2457 // Math operations take place element-wise.
2458 const c = a + b;
2459
2460 // Individual vector elements can be accessed using array indexing syntax.
2461 try expectEqual(6, c[0]);
2462 try expectEqual(8, c[1]);
2463 try expectEqual(10, c[2]);
2464 try expectEqual(12, c[3]);
2465}
24661920
2467test "Conversion between vectors, arrays, and slices" {
2468 // Vectors and fixed-length arrays can be automatically assigned back and forth
2469 const arr1: [4]f32 = [_]f32{ 1.1, 3.2, 4.5, 5.6 };
2470 const vec: @Vector(4, f32) = arr1;
2471 const arr2: [4]f32 = vec;
2472 try expectEqual(arr1, arr2);
2473
2474 // You can also assign from a slice with comptime-known length to a vector using .*
2475 const vec2: @Vector(2, f32) = arr1[1..3].*;
2476
2477 const slice: []const f32 = &arr1;
2478 var offset: u32 = 1; // var to make it runtime-known
2479 _ = &offset; // suppress 'var is never mutated' error
2480 // To extract a comptime-known length from a runtime-known offset,
2481 // first extract a new slice from the starting offset, then an array of
2482 // comptime-known length
2483 const vec3: @Vector(2, f32) = slice[offset..][0..2].*;
2484 try expectEqual(slice[offset], vec2[0]);
2485 try expectEqual(slice[offset + 1], vec2[1]);
2486 try expectEqual(vec2, vec3);
2487}
2488 {#code_end#}
2489 <p>1921 <p>
2490 TODO talk about C ABI interop<br>1922 TODO talk about C ABI interop<br>
2491 TODO consider suggesting std.MultiArrayList1923 TODO consider suggesting std.MultiArrayList
...@@ -2534,76 +1966,13 @@ test "Conversion between vectors, arrays, and slices" {...@@ -2534,76 +1966,13 @@ test "Conversion between vectors, arrays, and slices" {
2534 </li>1966 </li>
2535 </ul>1967 </ul>
2536 <p>Use {#syntax#}&x{#endsyntax#} to obtain a single-item pointer:</p>1968 <p>Use {#syntax#}&x{#endsyntax#} to obtain a single-item pointer:</p>
2537 {#code_begin|test|test_single_item_pointer#}1969 {#code|test_single_item_pointer.zig#}
2538const expect = @import("std").testing.expect;
2539
2540test "address of syntax" {
2541 // Get the address of a variable:
2542 const x: i32 = 1234;
2543 const x_ptr = &x;
2544
2545 // Dereference a pointer:
2546 try expect(x_ptr.* == 1234);
2547
2548 // When you get the address of a const variable, you get a const single-item pointer.
2549 try expect(@TypeOf(x_ptr) == *const i32);
2550
2551 // If you want to mutate the value, you'd need an address of a mutable variable:
2552 var y: i32 = 5678;
2553 const y_ptr = &y;
2554 try expect(@TypeOf(y_ptr) == *i32);
2555 y_ptr.* += 1;
2556 try expect(y_ptr.* == 5679);
2557}
25581970
2559test "pointer array access" {
2560 // Taking an address of an individual element gives a
2561 // single-item pointer. This kind of pointer
2562 // does not support pointer arithmetic.
2563 var array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
2564 const ptr = &array[2];
2565 try expect(@TypeOf(ptr) == *u8);
2566
2567 try expect(array[2] == 3);
2568 ptr.* += 1;
2569 try expect(array[2] == 4);
2570}
2571 {#code_end#}
2572 <p>1971 <p>
2573 Zig supports pointer arithmetic. It's better to assign the pointer to {#syntax#}[*]T{#endsyntax#} and increment that variable. For example, directly incrementing the pointer from a slice will corrupt it.1972 Zig supports pointer arithmetic. It's better to assign the pointer to {#syntax#}[*]T{#endsyntax#} and increment that variable. For example, directly incrementing the pointer from a slice will corrupt it.
2574 </p>1973 </p>
2575 {#code_begin|test|test_pointer_arithmetic#}1974 {#code|test_pointer_arithmetic.zig#}
2576const expect = @import("std").testing.expect;
2577
2578test "pointer arithmetic with many-item pointer" {
2579 const array = [_]i32{ 1, 2, 3, 4 };
2580 var ptr: [*]const i32 = &array;
2581
2582 try expect(ptr[0] == 1);
2583 ptr += 1;
2584 try expect(ptr[0] == 2);
2585
2586 // slicing a many-item pointer without an end is equivalent to
2587 // pointer arithmetic: `ptr[start..] == ptr + start`
2588 try expect(ptr[1..] == ptr + 1);
2589}
2590
2591test "pointer arithmetic with slices" {
2592 var array = [_]i32{ 1, 2, 3, 4 };
2593 var length: usize = 0; // var to make it runtime-known
2594 _ = &length; // suppress 'var is never mutated' error
2595 var slice = array[length..array.len];
25961975
2597 try expect(slice[0] == 1);
2598 try expect(slice.len == 4);
2599
2600 slice.ptr += 1;
2601 // now the slice is in an bad state since len has not been updated
2602
2603 try expect(slice[0] == 2);
2604 try expect(slice.len == 4);
2605}
2606 {#code_end#}
2607 <p>1976 <p>
2608 In Zig, we generally prefer {#link|Slices#} rather than {#link|Sentinel-Terminated Pointers#}.1977 In Zig, we generally prefer {#link|Slices#} rather than {#link|Sentinel-Terminated Pointers#}.
2609 You can turn an array or pointer into a slice using slice syntax.1978 You can turn an array or pointer into a slice using slice syntax.
...@@ -2613,78 +1982,28 @@ test "pointer arithmetic with slices" {...@@ -2613,78 +1982,28 @@ test "pointer arithmetic with slices" {
2613 against this kind of undefined behavior. This is one reason1982 against this kind of undefined behavior. This is one reason
2614 we prefer slices to pointers.1983 we prefer slices to pointers.
2615 </p>1984 </p>
2616 {#code_begin|test|test_slice_bounds#}1985 {#code|test_slice_bounds.zig#}
2617const expect = @import("std").testing.expect;
2618
2619test "pointer slicing" {
2620 var array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
2621 var start: usize = 2; // var to make it runtime-known
2622 _ = &start; // suppress 'var is never mutated' error
2623 const slice = array[start..4];
2624 try expect(slice.len == 2);
26251986
2626 try expect(array[3] == 4);
2627 slice[1] += 1;
2628 try expect(array[3] == 5);
2629}
2630 {#code_end#}
2631 <p>Pointers work at compile-time too, as long as the code does not depend on1987 <p>Pointers work at compile-time too, as long as the code does not depend on
2632 an undefined memory layout:</p>1988 an undefined memory layout:</p>
2633 {#code_begin|test|test_comptime_pointers#}1989 {#code|test_comptime_pointers.zig#}
2634const expect = @import("std").testing.expect;1990
2635
2636test "comptime pointers" {
2637 comptime {
2638 var x: i32 = 1;
2639 const ptr = &x;
2640 ptr.* += 1;
2641 x += 1;
2642 try expect(ptr.* == 3);
2643 }
2644}
2645 {#code_end#}
2646 <p>To convert an integer address into a pointer, use {#syntax#}@ptrFromInt{#endsyntax#}.1991 <p>To convert an integer address into a pointer, use {#syntax#}@ptrFromInt{#endsyntax#}.
2647 To convert a pointer to an integer, use {#syntax#}@intFromPtr{#endsyntax#}:</p>1992 To convert a pointer to an integer, use {#syntax#}@intFromPtr{#endsyntax#}:</p>
2648 {#code_begin|test|test_integer_pointer_conversion#}1993 {#code|test_integer_pointer_conversion.zig#}
2649const expect = @import("std").testing.expect;1994
2650
2651test "@intFromPtr and @ptrFromInt" {
2652 const ptr: *i32 = @ptrFromInt(0xdeadbee0);
2653 const addr = @intFromPtr(ptr);
2654 try expect(@TypeOf(addr) == usize);
2655 try expect(addr == 0xdeadbee0);
2656}
2657 {#code_end#}
2658 <p>Zig is able to preserve memory addresses in comptime code, as long as1995 <p>Zig is able to preserve memory addresses in comptime code, as long as
2659 the pointer is never dereferenced:</p>1996 the pointer is never dereferenced:</p>
2660 {#code_begin|test|test_comptime_pointer_conversion#}1997 {#code|test_comptime_pointer_conversion.zig#}
2661const expect = @import("std").testing.expect;1998
2662
2663test "comptime @ptrFromInt" {
2664 comptime {
2665 // Zig is able to do this at compile-time, as long as
2666 // ptr is never dereferenced.
2667 const ptr: *i32 = @ptrFromInt(0xdeadbee0);
2668 const addr = @intFromPtr(ptr);
2669 try expect(@TypeOf(addr) == usize);
2670 try expect(addr == 0xdeadbee0);
2671 }
2672}
2673 {#code_end#}
2674 {#see_also|Optional Pointers|@ptrFromInt|@intFromPtr|C Pointers#}1999 {#see_also|Optional Pointers|@ptrFromInt|@intFromPtr|C Pointers#}
2675 {#header_open|volatile#}2000 {#header_open|volatile#}
2676 <p>Loads and stores are assumed to not have side effects. If a given load or store2001 <p>Loads and stores are assumed to not have side effects. If a given load or store
2677 should have side effects, such as Memory Mapped Input/Output (MMIO), use {#syntax#}volatile{#endsyntax#}.2002 should have side effects, such as Memory Mapped Input/Output (MMIO), use {#syntax#}volatile{#endsyntax#}.
2678 In the following code, loads and stores with {#syntax#}mmio_ptr{#endsyntax#} are guaranteed to all happen2003 In the following code, loads and stores with {#syntax#}mmio_ptr{#endsyntax#} are guaranteed to all happen
2679 and in the same order as in source code:</p>2004 and in the same order as in source code:</p>
2680 {#code_begin|test|test_volatile#}2005 {#code|test_volatile.zig#}
2681const expect = @import("std").testing.expect;
26822006
2683test "volatile" {
2684 const mmio_ptr: *volatile u8 = @ptrFromInt(0x12345678);
2685 try expect(@TypeOf(mmio_ptr) == *volatile u8);
2686}
2687 {#code_end#}
2688 <p>2007 <p>
2689 Note that {#syntax#}volatile{#endsyntax#} is unrelated to concurrency and {#link|Atomics#}.2008 Note that {#syntax#}volatile{#endsyntax#} is unrelated to concurrency and {#link|Atomics#}.
2690 If you see code that is using {#syntax#}volatile{#endsyntax#} for something other than Memory Mapped2009 If you see code that is using {#syntax#}volatile{#endsyntax#} for something other than Memory Mapped
...@@ -2698,29 +2017,8 @@ test "volatile" {...@@ -2698,29 +2017,8 @@ test "volatile" {
2698 kinds of type conversions are preferable to2017 kinds of type conversions are preferable to
2699 {#syntax#}@ptrCast{#endsyntax#} if possible.2018 {#syntax#}@ptrCast{#endsyntax#} if possible.
2700 </p>2019 </p>
2701 {#code_begin|test|test_pointer_casting#}2020 {#code|test_pointer_casting.zig#}
2702const std = @import("std");
2703const expect = std.testing.expect;
2704
2705test "pointer casting" {
2706 const bytes align(@alignOf(u32)) = [_]u8{ 0x12, 0x12, 0x12, 0x12 };
2707 const u32_ptr: *const u32 = @ptrCast(&bytes);
2708 try expect(u32_ptr.* == 0x12121212);
2709
2710 // Even this example is contrived - there are better ways to do the above than
2711 // pointer casting. For example, using a slice narrowing cast:
2712 const u32_value = std.mem.bytesAsSlice(u32, bytes[0..])[0];
2713 try expect(u32_value == 0x12121212);
27142021
2715 // And even another way, the most straightforward way to do it:
2716 try expect(@as(u32, @bitCast(bytes)) == 0x12121212);
2717}
2718
2719test "pointer child type" {
2720 // pointer types have a `child` field which tells you the type they point to.
2721 try expect(@typeInfo(*u32).Pointer.child == u32);
2722}
2723 {#code_end#}
2724 {#header_open|Alignment#}2022 {#header_open|Alignment#}
2725 <p>2023 <p>
2726 Each type has an <strong>alignment</strong> - a number of bytes such that,2024 Each type has an <strong>alignment</strong> - a number of bytes such that,
...@@ -2736,21 +2034,8 @@ test "pointer child type" {...@@ -2736,21 +2034,8 @@ test "pointer child type" {
2736 In Zig, a pointer type has an alignment value. If the value is equal to the2034 In Zig, a pointer type has an alignment value. If the value is equal to the
2737 alignment of the underlying type, it can be omitted from the type:2035 alignment of the underlying type, it can be omitted from the type:
2738 </p>2036 </p>
2739 {#code_begin|test|test_variable_alignment#}2037 {#code|test_variable_alignment.zig#}
2740const std = @import("std");2038
2741const builtin = @import("builtin");
2742const expect = std.testing.expect;
2743
2744test "variable alignment" {
2745 var x: i32 = 1234;
2746 const align_of_i32 = @alignOf(@TypeOf(x));
2747 try expect(@TypeOf(&x) == *i32);
2748 try expect(*i32 == *align(align_of_i32) i32);
2749 if (builtin.target.cpu.arch == .x86_64) {
2750 try expect(@typeInfo(*i32).Pointer.alignment == 4);
2751 }
2752}
2753 {#code_end#}
2754 <p>In the same way that a {#syntax#}*i32{#endsyntax#} can be {#link|coerced|Type Coercion#} to a2039 <p>In the same way that a {#syntax#}*i32{#endsyntax#} can be {#link|coerced|Type Coercion#} to a
2755 {#syntax#}*const i32{#endsyntax#}, a pointer with a larger alignment can be implicitly2040 {#syntax#}*const i32{#endsyntax#}, a pointer with a larger alignment can be implicitly
2756 cast to a pointer with a smaller alignment, but not vice versa.2041 cast to a pointer with a smaller alignment, but not vice versa.
...@@ -2759,60 +2044,16 @@ test "variable alignment" {...@@ -2759,60 +2044,16 @@ test "variable alignment" {
2759 You can specify alignment on variables and functions. If you do this, then2044 You can specify alignment on variables and functions. If you do this, then
2760 pointers to them get the specified alignment:2045 pointers to them get the specified alignment:
2761 </p>2046 </p>
2762 {#code_begin|test|test_variable_func_alignment#}2047 {#code|test_variable_func_alignment.zig#}
2763const expect = @import("std").testing.expect;
2764
2765var foo: u8 align(4) = 100;
27662048
2767test "global variable alignment" {
2768 try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
2769 try expect(@TypeOf(&foo) == *align(4) u8);
2770 const as_pointer_to_array: *align(4) [1]u8 = &foo;
2771 const as_slice: []align(4) u8 = as_pointer_to_array;
2772 const as_unaligned_slice: []u8 = as_slice;
2773 try expect(as_unaligned_slice[0] == 100);
2774}
2775
2776fn derp() align(@sizeOf(usize) * 2) i32 {
2777 return 1234;
2778}
2779fn noop1() align(1) void {}
2780fn noop4() align(4) void {}
2781
2782test "function alignment" {
2783 try expect(derp() == 1234);
2784 try expect(@TypeOf(derp) == fn () i32);
2785 try expect(@TypeOf(&derp) == *align(@sizeOf(usize) * 2) const fn () i32);
2786
2787 noop1();
2788 try expect(@TypeOf(noop1) == fn () void);
2789 try expect(@TypeOf(&noop1) == *align(1) const fn () void);
2790
2791 noop4();
2792 try expect(@TypeOf(noop4) == fn () void);
2793 try expect(@TypeOf(&noop4) == *align(4) const fn () void);
2794}
2795 {#code_end#}
2796 <p>2049 <p>
2797 If you have a pointer or a slice that has a small alignment, but you know that it actually2050 If you have a pointer or a slice that has a small alignment, but you know that it actually
2798 has a bigger alignment, use {#link|@alignCast#} to change the2051 has a bigger alignment, use {#link|@alignCast#} to change the
2799 pointer into a more aligned pointer. This is a no-op at runtime, but inserts a2052 pointer into a more aligned pointer. This is a no-op at runtime, but inserts a
2800 {#link|safety check|Incorrect Pointer Alignment#}:2053 {#link|safety check|Incorrect Pointer Alignment#}:
2801 </p>2054 </p>
2802 {#code_begin|test_safety|test_incorrect_pointer_alignment|incorrect alignment#}2055 {#code|test_incorrect_pointer_alignment.zig#}
2803const std = @import("std");
28042056
2805test "pointer alignment safety" {
2806 var array align(4) = [_]u32{ 0x11111111, 0x11111111 };
2807 const bytes = std.mem.sliceAsBytes(array[0..]);
2808 try std.testing.expect(foo(bytes) == 0x11111111);
2809}
2810fn foo(bytes: []u8) u32 {
2811 const slice4 = bytes[1..5];
2812 const int_slice = std.mem.bytesAsSlice(u32, @as([]align(4) u8, @alignCast(slice4)));
2813 return int_slice[0];
2814}
2815 {#code_end#}
2816 {#header_close#}2057 {#header_close#}
28172058
2818 {#header_open|allowzero#}2059 {#header_open|allowzero#}
...@@ -2824,17 +2065,8 @@ fn foo(bytes: []u8) u32 {...@@ -2824,17 +2065,8 @@ fn foo(bytes: []u8) u32 {
2824 did not have the {#syntax#}allowzero{#endsyntax#} attribute, this would be a2065 did not have the {#syntax#}allowzero{#endsyntax#} attribute, this would be a
2825 {#link|Pointer Cast Invalid Null#} panic:2066 {#link|Pointer Cast Invalid Null#} panic:
2826 </p>2067 </p>
2827 {#code_begin|test|test_allowzero#}2068 {#code|test_allowzero.zig#}
2828const std = @import("std");
2829const expect = std.testing.expect;
28302069
2831test "allowzero" {
2832 var zero: usize = 0; // var to make to runtime-known
2833 _ = &zero; // suppress 'var is never mutated' error
2834 const ptr: *allowzero i32 = @ptrFromInt(zero);
2835 try expect(@intFromPtr(ptr) == 0);
2836}
2837 {#code_end#}
2838 {#header_close#}2070 {#header_close#}
28392071
2840 {#header_open|Sentinel-Terminated Pointers#}2072 {#header_open|Sentinel-Terminated Pointers#}
...@@ -2843,21 +2075,8 @@ test "allowzero" {...@@ -2843,21 +2075,8 @@ test "allowzero" {
2843 has a length determined by a sentinel value. This provides protection2075 has a length determined by a sentinel value. This provides protection
2844 against buffer overflow and overreads.2076 against buffer overflow and overreads.
2845 </p>2077 </p>
2846 {#code_begin|exe_build_err|sentinel-terminated_pointer#}2078 {#code|sentinel-terminated_pointer.zig#}
2847 {#link_libc#}
2848const std = @import("std");
2849
2850// This is also available as `std.c.printf`.
2851pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
2852
2853pub fn main() anyerror!void {
2854 _ = printf("Hello, world!\n"); // OK
28552079
2856 const msg = "Hello, world!\n";
2857 const non_null_terminated_msg: [msg.len]u8 = msg.*;
2858 _ = printf(&non_null_terminated_msg);
2859}
2860 {#code_end#}
2861 {#see_also|Sentinel-Terminated Slices|Sentinel-Terminated Arrays#}2080 {#see_also|Sentinel-Terminated Slices|Sentinel-Terminated Arrays#}
2862 {#header_close#}2081 {#header_close#}
2863 {#header_close#}2082 {#header_close#}
...@@ -2869,99 +2088,11 @@ pub fn main() anyerror!void {...@@ -2869,99 +2088,11 @@ pub fn main() anyerror!void {
2869 compile-time, whereas the slice's length is known at runtime.2088 compile-time, whereas the slice's length is known at runtime.
2870 Both can be accessed with the `len` field.2089 Both can be accessed with the `len` field.
2871 </p>2090 </p>
2872 {#code_begin|test_safety|test_basic_slices|index out of bounds#}2091 {#code|test_basic_slices.zig#}
2873const expect = @import("std").testing.expect;2092
2874
2875test "basic slices" {
2876 var array = [_]i32{ 1, 2, 3, 4 };
2877 var known_at_runtime_zero: usize = 0;
2878 _ = &known_at_runtime_zero;
2879 const slice = array[known_at_runtime_zero..array.len];
2880 try expect(@TypeOf(slice) == []i32);
2881 try expect(&slice[0] == &array[0]);
2882 try expect(slice.len == array.len);
2883
2884 // If you slice with comptime-known start and end positions, the result is
2885 // a pointer to an array, rather than a slice.
2886 const array_ptr = array[0..array.len];
2887 try expect(@TypeOf(array_ptr) == *[array.len]i32);
2888
2889 // You can perform a slice-by-length by slicing twice. This allows the compiler
2890 // to perform some optimisations like recognising a comptime-known length when
2891 // the start position is only known at runtime.
2892 var runtime_start: usize = 1;
2893 _ = &runtime_start;
2894 const length = 2;
2895 const array_ptr_len = array[runtime_start..][0..length];
2896 try expect(@TypeOf(array_ptr_len) == *[length]i32);
2897
2898 // Using the address-of operator on a slice gives a single-item pointer.
2899 try expect(@TypeOf(&slice[0]) == *i32);
2900 // Using the `ptr` field gives a many-item pointer.
2901 try expect(@TypeOf(slice.ptr) == [*]i32);
2902 try expect(@intFromPtr(slice.ptr) == @intFromPtr(&slice[0]));
2903
2904 // Slices have array bounds checking. If you try to access something out
2905 // of bounds, you'll get a safety check failure:
2906 slice[10] += 1;
2907
2908 // Note that `slice.ptr` does not invoke safety checking, while `&slice[0]`
2909 // asserts that the slice has len > 0.
2910}
2911 {#code_end#}
2912 <p>This is one reason we prefer slices to pointers.</p>2093 <p>This is one reason we prefer slices to pointers.</p>
2913 {#code_begin|test|test_slices#}2094 {#code|test_slices.zig#}
2914const std = @import("std");
2915const expect = std.testing.expect;
2916const mem = std.mem;
2917const fmt = std.fmt;
2918
2919test "using slices for strings" {
2920 // Zig has no concept of strings. String literals are const pointers
2921 // to null-terminated arrays of u8, and by convention parameters
2922 // that are "strings" are expected to be UTF-8 encoded slices of u8.
2923 // Here we coerce *const [5:0]u8 and *const [6:0]u8 to []const u8
2924 const hello: []const u8 = "hello";
2925 const world: []const u8 = "世界";
2926
2927 var all_together: [100]u8 = undefined;
2928 // You can use slice syntax with at least one runtime-known index on an
2929 // array to convert an array into a slice.
2930 var start: usize = 0;
2931 _ = &start;
2932 const all_together_slice = all_together[start..];
2933 // String concatenation example.
2934 const hello_world = try fmt.bufPrint(all_together_slice, "{s} {s}", .{ hello, world });
2935
2936 // Generally, you can use UTF-8 and not worry about whether something is a
2937 // string. If you don't need to deal with individual characters, no need
2938 // to decode.
2939 try expect(mem.eql(u8, hello_world, "hello 世界"));
2940}
29412095
2942test "slice pointer" {
2943 var array: [10]u8 = undefined;
2944 const ptr = &array;
2945 try expect(@TypeOf(ptr) == *[10]u8);
2946
2947 // A pointer to an array can be sliced just like an array:
2948 var start: usize = 0;
2949 var end: usize = 5;
2950 _ = .{ &start, &end };
2951 const slice = ptr[start..end];
2952 // The slice is mutable because we sliced a mutable pointer.
2953 try expect(@TypeOf(slice) == []u8);
2954 slice[2] = 3;
2955 try expect(array[2] == 3);
2956
2957 // Again, slicing with comptime-known indexes will produce another pointer
2958 // to an array:
2959 const ptr2 = slice[2..3];
2960 try expect(ptr2.len == 1);
2961 try expect(ptr2[0] == 3);
2962 try expect(@TypeOf(ptr2) == *[1]u8);
2963}
2964 {#code_end#}
2965 {#see_also|Pointers|for|Arrays#}2096 {#see_also|Pointers|for|Arrays#}
29662097
2967 {#header_open|Sentinel-Terminated Slices#}2098 {#header_open|Sentinel-Terminated Slices#}
...@@ -2971,206 +2102,28 @@ test "slice pointer" {...@@ -2971,206 +2102,28 @@ test "slice pointer" {
2971 guarantee that there are no sentinel elements before that. Sentinel-terminated slices allow element2102 guarantee that there are no sentinel elements before that. Sentinel-terminated slices allow element
2972 access to the {#syntax#}len{#endsyntax#} index.2103 access to the {#syntax#}len{#endsyntax#} index.
2973 </p>2104 </p>
2974 {#code_begin|test|test_null_terminated_slice#}2105 {#code|test_null_terminated_slice.zig#}
2975const std = @import("std");
2976const expect = std.testing.expect;
2977
2978test "0-terminated slice" {
2979 const slice: [:0]const u8 = "hello";
29802106
2981 try expect(slice.len == 5);
2982 try expect(slice[5] == 0);
2983}
2984 {#code_end#}
2985 <p>2107 <p>
2986 Sentinel-terminated slices can also be created using a variation of the slice syntax2108 Sentinel-terminated slices can also be created using a variation of the slice syntax
2987 {#syntax#}data[start..end :x]{#endsyntax#}, where {#syntax#}data{#endsyntax#} is a many-item pointer,2109 {#syntax#}data[start..end :x]{#endsyntax#}, where {#syntax#}data{#endsyntax#} is a many-item pointer,
2988 array or slice and {#syntax#}x{#endsyntax#} is the sentinel value.2110 array or slice and {#syntax#}x{#endsyntax#} is the sentinel value.
2989 </p>2111 </p>
2990 {#code_begin|test|test_null_terminated_slicing#}2112 {#code|test_null_terminated_slicing.zig#}
2991const std = @import("std");
2992const expect = std.testing.expect;
2993
2994test "0-terminated slicing" {
2995 var array = [_]u8{ 3, 2, 1, 0, 3, 2, 1, 0 };
2996 var runtime_length: usize = 3;
2997 _ = &runtime_length;
2998 const slice = array[0..runtime_length :0];
29992113
3000 try expect(@TypeOf(slice) == [:0]u8);
3001 try expect(slice.len == 3);
3002}
3003 {#code_end#}
3004 <p>2114 <p>
3005 Sentinel-terminated slicing asserts that the element in the sentinel position of the backing data is2115 Sentinel-terminated slicing asserts that the element in the sentinel position of the backing data is
3006 actually the sentinel value. If this is not the case, safety-protected {#link|Undefined Behavior#} results.2116 actually the sentinel value. If this is not the case, safety-protected {#link|Undefined Behavior#} results.
3007 </p>2117 </p>
3008 {#code_begin|test_safety|test_sentinel_mismatch|sentinel mismatch#}2118 {#code|test_sentinel_mismatch.zig#}
3009const std = @import("std");
3010const expect = std.testing.expect;
3011
3012test "sentinel mismatch" {
3013 var array = [_]u8{ 3, 2, 1, 0 };
30142119
3015 // Creating a sentinel-terminated slice from the array with a length of 2
3016 // will result in the value `1` occupying the sentinel element position.
3017 // This does not match the indicated sentinel value of `0` and will lead
3018 // to a runtime panic.
3019 var runtime_length: usize = 2;
3020 _ = &runtime_length;
3021 const slice = array[0..runtime_length :0];
3022
3023 _ = slice;
3024}
3025 {#code_end#}
3026 {#see_also|Sentinel-Terminated Pointers|Sentinel-Terminated Arrays#}2120 {#see_also|Sentinel-Terminated Pointers|Sentinel-Terminated Arrays#}
3027 {#header_close#}2121 {#header_close#}
3028 {#header_close#}2122 {#header_close#}
30292123
3030 {#header_open|struct#}2124 {#header_open|struct#}
3031 {#code_begin|test|test_structs#}2125 {#code|test_structs.zig#}
3032// Declare a struct.
3033// Zig gives no guarantees about the order of fields and the size of
3034// the struct but the fields are guaranteed to be ABI-aligned.
3035const Point = struct {
3036 x: f32,
3037 y: f32,
3038};
3039
3040// Maybe we want to pass it to OpenGL so we want to be particular about
3041// how the bytes are arranged.
3042const Point2 = packed struct {
3043 x: f32,
3044 y: f32,
3045};
3046
3047
3048// Declare an instance of a struct.
3049const p = Point {
3050 .x = 0.12,
3051 .y = 0.34,
3052};
3053
3054// Maybe we're not ready to fill out some of the fields.
3055var p2 = Point {
3056 .x = 0.12,
3057 .y = undefined,
3058};
30592126
3060// Structs can have methods
3061// Struct methods are not special, they are only namespaced
3062// functions that you can call with dot syntax.
3063const Vec3 = struct {
3064 x: f32,
3065 y: f32,
3066 z: f32,
3067
3068 pub fn init(x: f32, y: f32, z: f32) Vec3 {
3069 return Vec3 {
3070 .x = x,
3071 .y = y,
3072 .z = z,
3073 };
3074 }
3075
3076 pub fn dot(self: Vec3, other: Vec3) f32 {
3077 return self.x * other.x + self.y * other.y + self.z * other.z;
3078 }
3079};
3080
3081const expect = @import("std").testing.expect;
3082test "dot product" {
3083 const v1 = Vec3.init(1.0, 0.0, 0.0);
3084 const v2 = Vec3.init(0.0, 1.0, 0.0);
3085 try expect(v1.dot(v2) == 0.0);
3086
3087 // Other than being available to call with dot syntax, struct methods are
3088 // not special. You can reference them as any other declaration inside
3089 // the struct:
3090 try expect(Vec3.dot(v1, v2) == 0.0);
3091}
3092
3093// Structs can have declarations.
3094// Structs can have 0 fields.
3095const Empty = struct {
3096 pub const PI = 3.14;
3097};
3098test "struct namespaced variable" {
3099 try expect(Empty.PI == 3.14);
3100 try expect(@sizeOf(Empty) == 0);
3101
3102 // you can still instantiate an empty struct
3103 const does_nothing = Empty {};
3104
3105 _ = does_nothing;
3106}
3107
3108// struct field order is determined by the compiler for optimal performance.
3109// however, you can still calculate a struct base pointer given a field pointer:
3110fn setYBasedOnX(x: *f32, y: f32) void {
3111 const point: *Point = @fieldParentPtr("x", x);
3112 point.y = y;
3113}
3114test "field parent pointer" {
3115 var point = Point {
3116 .x = 0.1234,
3117 .y = 0.5678,
3118 };
3119 setYBasedOnX(&point.x, 0.9);
3120 try expect(point.y == 0.9);
3121}
3122
3123// You can return a struct from a function. This is how we do generics
3124// in Zig:
3125fn LinkedList(comptime T: type) type {
3126 return struct {
3127 pub const Node = struct {
3128 prev: ?*Node,
3129 next: ?*Node,
3130 data: T,
3131 };
3132
3133 first: ?*Node,
3134 last: ?*Node,
3135 len: usize,
3136 };
3137}
3138
3139test "linked list" {
3140 // Functions called at compile-time are memoized. This means you can
3141 // do this:
3142 try expect(LinkedList(i32) == LinkedList(i32));
3143
3144 const list = LinkedList(i32){
3145 .first = null,
3146 .last = null,
3147 .len = 0,
3148 };
3149 try expect(list.len == 0);
3150
3151 // Since types are first class values you can instantiate the type
3152 // by assigning it to a variable:
3153 const ListOfInts = LinkedList(i32);
3154 try expect(ListOfInts == LinkedList(i32));
3155
3156 var node = ListOfInts.Node{
3157 .prev = null,
3158 .next = null,
3159 .data = 1234,
3160 };
3161 const list2 = LinkedList(i32){
3162 .first = &node,
3163 .last = &node,
3164 .len = 1,
3165 };
3166
3167 // When using a pointer to a struct, fields can be accessed directly,
3168 // without explicitly dereferencing the pointer.
3169 // So you can do
3170 try expect(list2.first.?.data == 1234);
3171 // instead of try expect(list2.first.?.*.data == 1234);
3172}
3173 {#code_end#}
31742127
3175 {#header_open|Default Field Values#}2128 {#header_open|Default Field Values#}
3176 <p>2129 <p>
...@@ -3178,21 +2131,8 @@ test "linked list" {...@@ -3178,21 +2131,8 @@ test "linked list" {
3178 value. Such expressions are executed at {#link|comptime#}, and allow the2131 value. Such expressions are executed at {#link|comptime#}, and allow the
3179 field to be omitted in a struct literal expression:2132 field to be omitted in a struct literal expression:
3180 </p>2133 </p>
3181 {#code_begin|test|struct_default_field_values#}2134 {#code|struct_default_field_values.zig#}
3182const Foo = struct {
3183 a: i32 = 1234,
3184 b: i32,
3185};
31862135
3187test "default struct initialization fields" {
3188 const x: Foo = .{
3189 .b = 5,
3190 };
3191 if (x.a + x.b != 1239) {
3192 comptime unreachable;
3193 }
3194}
3195 {#code_end#}
3196 <p>2136 <p>
3197 Default field values are only appropriate when the data invariants of a struct2137 Default field values are only appropriate when the data invariants of a struct
3198 cannot be violated by omitting that field from an initialization.2138 cannot be violated by omitting that field from an initialization.
...@@ -3200,32 +2140,8 @@ test "default struct initialization fields" {...@@ -3200,32 +2140,8 @@ test "default struct initialization fields" {
3200 <p>2140 <p>
3201 For example, here is an inappropriate use of default struct field initialization:2141 For example, here is an inappropriate use of default struct field initialization:
3202 </p>2142 </p>
3203 {#code_begin|exe_err|bad_default_value#}2143 {#code|bad_default_value.zig#}
3204const Threshold = struct {
3205 minimum: f32 = 0.25,
3206 maximum: f32 = 0.75,
3207
3208 const Category = enum { low, medium, high };
3209
3210 fn categorize(t: Threshold, value: f32) Category {
3211 assert(t.maximum >= t.minimum);
3212 if (value < t.minimum) return .low;
3213 if (value > t.maximum) return .high;
3214 return .medium;
3215 }
3216};
3217
3218pub fn main() !void {
3219 var threshold: Threshold = .{
3220 .maximum = 0.20,
3221 };
3222 const category = threshold.categorize(0.90);
3223 try std.io.getStdOut().writeAll(@tagName(category));
3224}
32252144
3226const std = @import("std");
3227const assert = std.debug.assert;
3228 {#code_end#}
3229 <p>2145 <p>
3230 Above you can see the danger of ignoring this principle. The default2146 Above you can see the danger of ignoring this principle. The default
3231 field values caused the data invariant to be violated, causing illegal2147 field values caused the data invariant to be violated, causing illegal
...@@ -3235,17 +2151,8 @@ const assert = std.debug.assert;...@@ -3235,17 +2151,8 @@ const assert = std.debug.assert;
3235 To fix this, remove the default values from all the struct fields, and provide2151 To fix this, remove the default values from all the struct fields, and provide
3236 a named default value:2152 a named default value:
3237 </p>2153 </p>
3238 {#code_begin|syntax|struct_default_value#}2154 {#code|struct_default_value.zig#}
3239const Threshold = struct {
3240 minimum: f32,
3241 maximum: f32,
32422155
3243 const default: Threshold = .{
3244 .minimum = 0.25,
3245 .maximum = 0.75,
3246 };
3247};
3248 {#code_end#}
3249 <p>If a struct value requires a runtime-known value in order to be initialized2156 <p>If a struct value requires a runtime-known value in order to be initialized
3250 without violating data invariants, then use an initialization method that accepts2157 without violating data invariants, then use an initialization method that accepts
3251 those runtime values, and populates the remaining fields.</p>2158 those runtime values, and populates the remaining fields.</p>
...@@ -3282,109 +2189,25 @@ const Threshold = struct {...@@ -3282,109 +2189,25 @@ const Threshold = struct {
3282 in a {#link|@bitCast#} or a {#link|@ptrCast#} to reinterpret memory.2189 in a {#link|@bitCast#} or a {#link|@ptrCast#} to reinterpret memory.
3283 This even works at {#link|comptime#}:2190 This even works at {#link|comptime#}:
3284 </p>2191 </p>
3285 {#code_begin|test|test_packed_structs#}2192 {#code|test_packed_structs.zig#}
3286const std = @import("std");
3287const native_endian = @import("builtin").target.cpu.arch.endian();
3288const expect = std.testing.expect;
3289
3290const Full = packed struct {
3291 number: u16,
3292};
3293const Divided = packed struct {
3294 half1: u8,
3295 quarter3: u4,
3296 quarter4: u4,
3297};
3298
3299test "@bitCast between packed structs" {
3300 try doTheTest();
3301 try comptime doTheTest();
3302}
33032193
3304fn doTheTest() !void {
3305 try expect(@sizeOf(Full) == 2);
3306 try expect(@sizeOf(Divided) == 2);
3307 const full = Full{ .number = 0x1234 };
3308 const divided: Divided = @bitCast(full);
3309 try expect(divided.half1 == 0x34);
3310 try expect(divided.quarter3 == 0x2);
3311 try expect(divided.quarter4 == 0x1);
3312
3313 const ordered: [2]u8 = @bitCast(full);
3314 switch (native_endian) {
3315 .big => {
3316 try expect(ordered[0] == 0x12);
3317 try expect(ordered[1] == 0x34);
3318 },
3319 .little => {
3320 try expect(ordered[0] == 0x34);
3321 try expect(ordered[1] == 0x12);
3322 },
3323 }
3324}
3325 {#code_end#}
3326 <p>2194 <p>
3327 The backing integer is inferred from the fields' total bit width.2195 The backing integer is inferred from the fields' total bit width.
3328 Optionally, it can be explicitly provided and enforced at compile time:2196 Optionally, it can be explicitly provided and enforced at compile time:
3329 </p>2197 </p>
3330 {#code_begin|test_err|test_missized_packed_struct|backing integer type 'u32' has bit size 32 but the struct fields have a total bit size of 24#}2198 {#code|test_missized_packed_struct.zig#}
3331test "missized packed struct" {2199
3332 const S = packed struct(u32) { a: u16, b: u8 };
3333 _ = S{ .a = 4, .b = 2 };
3334}
3335 {#code_end#}
3336 <p>2200 <p>
3337 Zig allows the address to be taken of a non-byte-aligned field:2201 Zig allows the address to be taken of a non-byte-aligned field:
3338 </p>2202 </p>
3339 {#code_begin|test|test_pointer_to_non-byte_aligned_field#}2203 {#code|test_pointer_to_non-byte_aligned_field.zig#}
3340const std = @import("std");
3341const expect = std.testing.expect;
3342
3343const BitField = packed struct {
3344 a: u3,
3345 b: u3,
3346 c: u2,
3347};
3348
3349var foo = BitField{
3350 .a = 1,
3351 .b = 2,
3352 .c = 3,
3353};
33542204
3355test "pointer to non-byte-aligned field" {
3356 const ptr = &foo.b;
3357 try expect(ptr.* == 2);
3358}
3359 {#code_end#}
3360 <p>2205 <p>
3361 However, the pointer to a non-byte-aligned field has special properties and cannot2206 However, the pointer to a non-byte-aligned field has special properties and cannot
3362 be passed when a normal pointer is expected:2207 be passed when a normal pointer is expected:
3363 </p>2208 </p>
3364 {#code_begin|test_err|test_misaligned_pointer|expected type#}2209 {#code|test_misaligned_pointer.zig#}
3365const std = @import("std");
3366const expect = std.testing.expect;
3367
3368const BitField = packed struct {
3369 a: u3,
3370 b: u3,
3371 c: u2,
3372};
3373
3374var bit_field = BitField{
3375 .a = 1,
3376 .b = 2,
3377 .c = 3,
3378};
3379
3380test "pointer to non-byte-aligned field" {
3381 try expect(bar(&bit_field.b) == 2);
3382}
33832210
3384fn bar(x: *const u3) u3 {
3385 return x.*;
3386}
3387 {#code_end#}
3388 <p>2211 <p>
3389 In this case, the function {#syntax#}bar{#endsyntax#} cannot be called because the pointer2212 In this case, the function {#syntax#}bar{#endsyntax#} cannot be called because the pointer
3390 to the non-ABI-aligned field mentions the bit offset, but the function expects an ABI-aligned pointer.2213 to the non-ABI-aligned field mentions the bit offset, but the function expects an ABI-aligned pointer.
...@@ -3392,90 +2215,24 @@ fn bar(x: *const u3) u3 {...@@ -3392,90 +2215,24 @@ fn bar(x: *const u3) u3 {
3392 <p>2215 <p>
3393 Pointers to non-ABI-aligned fields share the same address as the other fields within their host integer:2216 Pointers to non-ABI-aligned fields share the same address as the other fields within their host integer:
3394 </p>2217 </p>
3395 {#code_begin|test|test_packed_struct_field_address#}2218 {#code|test_packed_struct_field_address.zig#}
3396const std = @import("std");
3397const expect = std.testing.expect;
3398
3399const BitField = packed struct {
3400 a: u3,
3401 b: u3,
3402 c: u2,
3403};
3404
3405var bit_field = BitField{
3406 .a = 1,
3407 .b = 2,
3408 .c = 3,
3409};
34102219
3411test "pointers of sub-byte-aligned fields share addresses" {
3412 try expect(@intFromPtr(&bit_field.a) == @intFromPtr(&bit_field.b));
3413 try expect(@intFromPtr(&bit_field.a) == @intFromPtr(&bit_field.c));
3414}
3415 {#code_end#}
3416 <p>2220 <p>
3417 This can be observed with {#link|@bitOffsetOf#} and {#link|offsetOf#}:2221 This can be observed with {#link|@bitOffsetOf#} and {#link|offsetOf#}:
3418 </p>2222 </p>
3419 {#code_begin|test|test_bitOffsetOf_offsetOf#}2223 {#code|test_bitOffsetOf_offsetOf.zig#}
3420const std = @import("std");
3421const expect = std.testing.expect;
3422
3423const BitField = packed struct {
3424 a: u3,
3425 b: u3,
3426 c: u2,
3427};
34282224
3429test "offsets of non-byte-aligned fields" {
3430 comptime {
3431 try expect(@bitOffsetOf(BitField, "a") == 0);
3432 try expect(@bitOffsetOf(BitField, "b") == 3);
3433 try expect(@bitOffsetOf(BitField, "c") == 6);
3434
3435 try expect(@offsetOf(BitField, "a") == 0);
3436 try expect(@offsetOf(BitField, "b") == 0);
3437 try expect(@offsetOf(BitField, "c") == 0);
3438 }
3439}
3440 {#code_end#}
3441 <p>2225 <p>
3442 Packed structs have the same alignment as their backing integer, however, overaligned2226 Packed structs have the same alignment as their backing integer, however, overaligned
3443 pointers to packed structs can override this:2227 pointers to packed structs can override this:
3444 </p>2228 </p>
3445 {#code_begin|test|test_overaligned_packed_struct#}2229 {#code|test_overaligned_packed_struct.zig#}
3446const std = @import("std");
3447const expect = std.testing.expect;
34482230
3449const S = packed struct {
3450 a: u32,
3451 b: u32,
3452};
3453test "overaligned pointer to packed struct" {
3454 var foo: S align(4) = .{ .a = 1, .b = 2 };
3455 const ptr: *align(4) S = &foo;
3456 const ptr_to_b: *u32 = &ptr.b;
3457 try expect(ptr_to_b.* == 2);
3458}
3459 {#code_end#}
3460 <p>2231 <p>
3461 It's also possible to set alignment of struct fields:2232 It's also possible to set alignment of struct fields:
3462 </p>2233 </p>
3463 {#code_begin|test|test_aligned_struct_fields#}2234 {#code|test_aligned_struct_fields.zig#}
3464const std = @import("std");
3465const expectEqual = std.testing.expectEqual;
3466
3467test "aligned struct fields" {
3468 const S = struct {
3469 a: u32 align(2),
3470 b: u32 align(64),
3471 };
3472 var foo = S{ .a = 1, .b = 2 };
34732235
3474 try expectEqual(64, @alignOf(S));
3475 try expectEqual(*align(2) u32, @TypeOf(&foo.a));
3476 try expectEqual(*align(64) u32, @TypeOf(&foo.b));
3477}
3478 {#code_end#}
3479 <p>2236 <p>
3480 Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future.2237 Using packed structs with {#link|volatile#} is problematic, and may be a compile error in the future.
3481 For details on this subscribe to2238 For details on this subscribe to
...@@ -3497,22 +2254,8 @@ test "aligned struct fields" {...@@ -3497,22 +2254,8 @@ test "aligned struct fields" {
3497 <li>If the struct is declared inside another struct, it gets named after both the parent2254 <li>If the struct is declared inside another struct, it gets named after both the parent
3498 struct and the name inferred by the previous rules, separated by a dot.</li>2255 struct and the name inferred by the previous rules, separated by a dot.</li>
3499 </ul>2256 </ul>
3500 {#code_begin|exe|struct_name#}2257 {#code|struct_name.zig#}
3501const std = @import("std");
3502
3503pub fn main() void {
3504 const Foo = struct {};
3505 std.debug.print("variable: {s}\n", .{@typeName(Foo)});
3506 std.debug.print("anonymous: {s}\n", .{@typeName(struct {})});
3507 std.debug.print("function: {s}\n", .{@typeName(List(i32))});
3508}
35092258
3510fn List(comptime T: type) type {
3511 return struct {
3512 x: T,
3513 };
3514}
3515 {#code_end#}
3516 {#header_close#}2259 {#header_close#}
35172260
3518 {#header_open|Anonymous Struct Literals#}2261 {#header_open|Anonymous Struct Literals#}
...@@ -3521,46 +2264,14 @@ fn List(comptime T: type) type {...@@ -3521,46 +2264,14 @@ fn List(comptime T: type) type {
3521 the struct literal will directly instantiate the {#link|result location|Result Location Semantics#},2264 the struct literal will directly instantiate the {#link|result location|Result Location Semantics#},
3522 with no copy:2265 with no copy:
3523 </p>2266 </p>
3524 {#code_begin|test|test_struct_result#}2267 {#code|test_struct_result.zig#}
3525const std = @import("std");
3526const expect = std.testing.expect;
35272268
3528const Point = struct {x: i32, y: i32};
3529
3530test "anonymous struct literal" {
3531 const pt: Point = .{
3532 .x = 13,
3533 .y = 67,
3534 };
3535 try expect(pt.x == 13);
3536 try expect(pt.y == 67);
3537}
3538 {#code_end#}
3539 <p>2269 <p>
3540 The struct type can be inferred. Here the {#link|result location|Result Location Semantics#}2270 The struct type can be inferred. Here the {#link|result location|Result Location Semantics#}
3541 does not include a type, and so Zig infers the type:2271 does not include a type, and so Zig infers the type:
3542 </p>2272 </p>
3543 {#code_begin|test|test_anonymous_struct#}2273 {#code|test_anonymous_struct.zig#}
3544const std = @import("std");
3545const expect = std.testing.expect;
3546
3547test "fully anonymous struct" {
3548 try check(.{
3549 .int = @as(u32, 1234),
3550 .float = @as(f64, 12.34),
3551 .b = true,
3552 .s = "hi",
3553 });
3554}
35552274
3556fn check(args: anytype) !void {
3557 try expect(args.int == 1234);
3558 try expect(args.float == 12.34);
3559 try expect(args.b);
3560 try expect(args.s[0] == 'h');
3561 try expect(args.s[1] == 'i');
3562}
3563 {#code_end#}
3564 {#header_close#}2275 {#header_close#}
35652276
3566 {#header_open|Tuples#}2277 {#header_open|Tuples#}
...@@ -3577,193 +2288,36 @@ fn check(args: anytype) !void {...@@ -3577,193 +2288,36 @@ fn check(args: anytype) !void {
3577 Like arrays, tuples have a .len field, can be indexed (provided the index is comptime-known)2288 Like arrays, tuples have a .len field, can be indexed (provided the index is comptime-known)
3578 and work with the ++ and ** operators. They can also be iterated over with {#link|inline for#}.2289 and work with the ++ and ** operators. They can also be iterated over with {#link|inline for#}.
3579 </p>2290 </p>
3580 {#code_begin|test|test_tuples#}2291 {#code|test_tuples.zig#}
3581const std = @import("std");2292
3582const expect = std.testing.expect;
3583
3584test "tuple" {
3585 const values = .{
3586 @as(u32, 1234),
3587 @as(f64, 12.34),
3588 true,
3589 "hi",
3590 } ++ .{false} ** 2;
3591 try expect(values[0] == 1234);
3592 try expect(values[4] == false);
3593 inline for (values, 0..) |v, i| {
3594 if (i != 2) continue;
3595 try expect(v);
3596 }
3597 try expect(values.len == 6);
3598 try expect(values.@"3"[0] == 'h');
3599}
3600 {#code_end#}
3601 {#header_close#}2293 {#header_close#}
3602 {#see_also|comptime|@fieldParentPtr#}2294 {#see_also|comptime|@fieldParentPtr#}
3603 {#header_close#}2295 {#header_close#}
3604 {#header_open|enum#}2296 {#header_open|enum#}
3605 {#code_begin|test|test_enums#}2297 {#code|test_enums.zig#}
3606const expect = @import("std").testing.expect;
3607const mem = @import("std").mem;
3608
3609// Declare an enum.
3610const Type = enum {
3611 ok,
3612 not_ok,
3613};
3614
3615// Declare a specific enum field.
3616const c = Type.ok;
3617
3618// If you want access to the ordinal value of an enum, you
3619// can specify the tag type.
3620const Value = enum(u2) {
3621 zero,
3622 one,
3623 two,
3624};
3625// Now you can cast between u2 and Value.
3626// The ordinal value starts from 0, counting up by 1 from the previous member.
3627test "enum ordinal value" {
3628 try expect(@intFromEnum(Value.zero) == 0);
3629 try expect(@intFromEnum(Value.one) == 1);
3630 try expect(@intFromEnum(Value.two) == 2);
3631}
3632
3633// You can override the ordinal value for an enum.
3634const Value2 = enum(u32) {
3635 hundred = 100,
3636 thousand = 1000,
3637 million = 1000000,
3638};
3639test "set enum ordinal value" {
3640 try expect(@intFromEnum(Value2.hundred) == 100);
3641 try expect(@intFromEnum(Value2.thousand) == 1000);
3642 try expect(@intFromEnum(Value2.million) == 1000000);
3643}
3644
3645// You can also override only some values.
3646const Value3 = enum(u4) {
3647 a,
3648 b = 8,
3649 c,
3650 d = 4,
3651 e,
3652};
3653test "enum implicit ordinal values and overridden values" {
3654 try expect(@intFromEnum(Value3.a) == 0);
3655 try expect(@intFromEnum(Value3.b) == 8);
3656 try expect(@intFromEnum(Value3.c) == 9);
3657 try expect(@intFromEnum(Value3.d) == 4);
3658 try expect(@intFromEnum(Value3.e) == 5);
3659}
3660
3661// Enums can have methods, the same as structs and unions.
3662// Enum methods are not special, they are only namespaced
3663// functions that you can call with dot syntax.
3664const Suit = enum {
3665 clubs,
3666 spades,
3667 diamonds,
3668 hearts,
3669
3670 pub fn isClubs(self: Suit) bool {
3671 return self == Suit.clubs;
3672 }
3673};
3674test "enum method" {
3675 const p = Suit.spades;
3676 try expect(!p.isClubs());
3677}
3678
3679// An enum can be switched upon.
3680const Foo = enum {
3681 string,
3682 number,
3683 none,
3684};
3685test "enum switch" {
3686 const p = Foo.number;
3687 const what_is_it = switch (p) {
3688 Foo.string => "this is a string",
3689 Foo.number => "this is a number",
3690 Foo.none => "this is a none",
3691 };
3692 try expect(mem.eql(u8, what_is_it, "this is a number"));
3693}
3694
3695// @typeInfo can be used to access the integer tag type of an enum.
3696const Small = enum {
3697 one,
3698 two,
3699 three,
3700 four,
3701};
3702test "std.meta.Tag" {
3703 try expect(@typeInfo(Small).Enum.tag_type == u2);
3704}
3705
3706// @typeInfo tells us the field count and the fields names:
3707test "@typeInfo" {
3708 try expect(@typeInfo(Small).Enum.fields.len == 4);
3709 try expect(mem.eql(u8, @typeInfo(Small).Enum.fields[1].name, "two"));
3710}
37112298
3712// @tagName gives a [:0]const u8 representation of an enum value:
3713test "@tagName" {
3714 try expect(mem.eql(u8, @tagName(Small.three), "three"));
3715}
3716 {#code_end#}
3717 {#see_also|@typeInfo|@tagName|@sizeOf#}2299 {#see_also|@typeInfo|@tagName|@sizeOf#}
37182300
3719 {#header_open|extern enum#}2301 {#header_open|extern enum#}
3720 <p>2302 <p>
3721 By default, enums are not guaranteed to be compatible with the C ABI:2303 By default, enums are not guaranteed to be compatible with the C ABI:
3722 </p>2304 </p>
3723 {#code_begin|obj_err|enum_export_error|parameter of type 'enum_export_error.Foo' not allowed in function with calling convention 'C'#}2305 {#code|enum_export_error.zig#}
3724const Foo = enum { a, b, c };2306
3725export fn entry(foo: Foo) void { _ = foo; }
3726 {#code_end#}
3727 <p>2307 <p>
3728 For a C-ABI-compatible enum, provide an explicit tag type to2308 For a C-ABI-compatible enum, provide an explicit tag type to
3729 the enum:2309 the enum:
3730 </p>2310 </p>
3731 {#code_begin|obj|enum_export#}2311 {#code|enum_export.zig#}
3732const Foo = enum(c_int) { a, b, c };2312
3733export fn entry(foo: Foo) void { _ = foo; }
3734 {#code_end#}
3735 {#header_close#}2313 {#header_close#}
37362314
3737 {#header_open|Enum Literals#}2315 {#header_open|Enum Literals#}
3738 <p>2316 <p>
3739 Enum literals allow specifying the name of an enum field without specifying the enum type:2317 Enum literals allow specifying the name of an enum field without specifying the enum type:
3740 </p>2318 </p>
3741 {#code_begin|test|test_enum_literals#}2319 {#code|test_enum_literals.zig#}
3742const std = @import("std");
3743const expect = std.testing.expect;
3744
3745const Color = enum {
3746 auto,
3747 off,
3748 on,
3749};
3750
3751test "enum literals" {
3752 const color1: Color = .auto;
3753 const color2 = Color.auto;
3754 try expect(color1 == color2);
3755}
37562320
3757test "switch using enum literals" {
3758 const color = Color.on;
3759 const result = switch (color) {
3760 .auto => false,
3761 .on => true,
3762 .off => false,
3763 };
3764 try expect(result);
3765}
3766 {#code_end#}
3767 {#header_close#}2321 {#header_close#}
37682322
3769 {#header_open|Non-exhaustive enum#}2323 {#header_open|Non-exhaustive enum#}
...@@ -3780,33 +2334,8 @@ test "switch using enum literals" {...@@ -3780,33 +2334,8 @@ test "switch using enum literals" {
3780 A switch on a non-exhaustive enum can include a {#syntax#}_{#endsyntax#} prong as an alternative to an {#syntax#}else{#endsyntax#} prong.2334 A switch on a non-exhaustive enum can include a {#syntax#}_{#endsyntax#} prong as an alternative to an {#syntax#}else{#endsyntax#} prong.
3781 With a {#syntax#}_{#endsyntax#} prong the compiler errors if all the known tag names are not handled by the switch.2335 With a {#syntax#}_{#endsyntax#} prong the compiler errors if all the known tag names are not handled by the switch.
3782 </p>2336 </p>
3783 {#code_begin|test|test_switch_non-exhaustive#}2337 {#code|test_switch_non-exhaustive.zig#}
3784const std = @import("std");
3785const expect = std.testing.expect;
3786
3787const Number = enum(u8) {
3788 one,
3789 two,
3790 three,
3791 _,
3792};
37932338
3794test "switch on non-exhaustive enum" {
3795 const number = Number.one;
3796 const result = switch (number) {
3797 .one => true,
3798 .two,
3799 .three => false,
3800 _ => false,
3801 };
3802 try expect(result);
3803 const is_one = switch (number) {
3804 .one => true,
3805 else => false,
3806 };
3807 try expect(is_one);
3808}
3809 {#code_end#}
3810 {#header_close#}2339 {#header_close#}
3811 {#header_close#}2340 {#header_close#}
38122341
...@@ -3821,34 +2350,11 @@ test "switch on non-exhaustive enum" {...@@ -3821,34 +2350,11 @@ test "switch on non-exhaustive enum" {
3821 {#link|Accessing the non-active field|Wrong Union Field Access#} is2350 {#link|Accessing the non-active field|Wrong Union Field Access#} is
3822 safety-checked {#link|Undefined Behavior#}:2351 safety-checked {#link|Undefined Behavior#}:
3823 </p>2352 </p>
3824 {#code_begin|test_err|test_wrong_union_access|access of union field 'float' while field 'int' is active#}2353 {#code|test_wrong_union_access.zig#}
3825const Payload = union {2354
3826 int: i64,
3827 float: f64,
3828 boolean: bool,
3829};
3830test "simple union" {
3831 var payload = Payload{ .int = 1234 };
3832 payload.float = 12.34;
3833}
3834 {#code_end#}
3835 <p>You can activate another field by assigning the entire union:</p>2355 <p>You can activate another field by assigning the entire union:</p>
3836 {#code_begin|test|test_simple_union#}2356 {#code|test_simple_union.zig#}
3837const std = @import("std");2357
3838const expect = std.testing.expect;
3839
3840const Payload = union {
3841 int: i64,
3842 float: f64,
3843 boolean: bool,
3844};
3845test "simple union" {
3846 var payload = Payload{ .int = 1234 };
3847 try expect(payload.int == 1234);
3848 payload = Payload{ .float = 12.34 };
3849 try expect(payload.float == 12.34);
3850}
3851 {#code_end#}
3852 <p>2358 <p>
3853 In order to use {#link|switch#} with a union, it must be a {#link|Tagged union#}.2359 In order to use {#link|switch#} with a union, it must be a {#link|Tagged union#}.
3854 </p>2360 </p>
...@@ -3862,109 +2368,25 @@ test "simple union" {...@@ -3862,109 +2368,25 @@ test "simple union" {
3862 to use with {#link|switch#} expressions.2368 to use with {#link|switch#} expressions.
3863 Tagged unions coerce to their tag type: {#link|Type Coercion: Unions and Enums#}.2369 Tagged unions coerce to their tag type: {#link|Type Coercion: Unions and Enums#}.
3864 </p>2370 </p>
3865 {#code_begin|test|test_tagged_union#}2371 {#code|test_tagged_union.zig#}
3866const std = @import("std");
3867const expect = std.testing.expect;
38682372
3869const ComplexTypeTag = enum {
3870 ok,
3871 not_ok,
3872};
3873const ComplexType = union(ComplexTypeTag) {
3874 ok: u8,
3875 not_ok: void,
3876};
3877
3878test "switch on tagged union" {
3879 const c = ComplexType{ .ok = 42 };
3880 try expect(@as(ComplexTypeTag, c) == ComplexTypeTag.ok);
3881
3882 switch (c) {
3883 ComplexTypeTag.ok => |value| try expect(value == 42),
3884 ComplexTypeTag.not_ok => unreachable,
3885 }
3886}
3887
3888test "get tag type" {
3889 try expect(std.meta.Tag(ComplexType) == ComplexTypeTag);
3890}
3891 {#code_end#}
3892 <p>In order to modify the payload of a tagged union in a switch expression,2373 <p>In order to modify the payload of a tagged union in a switch expression,
3893 place a {#syntax#}*{#endsyntax#} before the variable name to make it a pointer:2374 place a {#syntax#}*{#endsyntax#} before the variable name to make it a pointer:
3894 </p>2375 </p>
3895 {#code_begin|test|test_switch_modify_tagged_union#}2376 {#code|test_switch_modify_tagged_union.zig#}
3896const std = @import("std");
3897const expect = std.testing.expect;
38982377
3899const ComplexTypeTag = enum {
3900 ok,
3901 not_ok,
3902};
3903const ComplexType = union(ComplexTypeTag) {
3904 ok: u8,
3905 not_ok: void,
3906};
3907
3908test "modify tagged union in switch" {
3909 var c = ComplexType{ .ok = 42 };
3910
3911 switch (c) {
3912 ComplexTypeTag.ok => |*value| value.* += 1,
3913 ComplexTypeTag.not_ok => unreachable,
3914 }
3915
3916 try expect(c.ok == 43);
3917}
3918 {#code_end#}
3919 <p>2378 <p>
3920 Unions can be made to infer the enum tag type.2379 Unions can be made to infer the enum tag type.
3921 Further, unions can have methods just like structs and enums.2380 Further, unions can have methods just like structs and enums.
3922 </p>2381 </p>
3923 {#code_begin|test|test_union_method#}2382 {#code|test_union_method.zig#}
3924const std = @import("std");
3925const expect = std.testing.expect;
3926
3927const Variant = union(enum) {
3928 int: i32,
3929 boolean: bool,
39302383
3931 // void can be omitted when inferring enum tag type.
3932 none,
3933
3934 fn truthy(self: Variant) bool {
3935 return switch (self) {
3936 Variant.int => |x_int| x_int != 0,
3937 Variant.boolean => |x_bool| x_bool,
3938 Variant.none => false,
3939 };
3940 }
3941};
3942
3943test "union method" {
3944 var v1 = Variant{ .int = 1 };
3945 var v2 = Variant{ .boolean = false };
3946
3947 try expect(v1.truthy());
3948 try expect(!v2.truthy());
3949}
3950 {#code_end#}
3951 <p>2384 <p>
3952 {#link|@tagName#} can be used to return a {#link|comptime#}2385 {#link|@tagName#} can be used to return a {#link|comptime#}
3953 {#syntax#}[:0]const u8{#endsyntax#} value representing the field name:2386 {#syntax#}[:0]const u8{#endsyntax#} value representing the field name:
3954 </p>2387 </p>
3955 {#code_begin|test|test_tagName#}2388 {#code|test_tagName.zig#}
3956const std = @import("std");
3957const expect = std.testing.expect;
39582389
3959const Small2 = union(enum) {
3960 a: i32,
3961 b: bool,
3962 c: u8,
3963};
3964test "@tagName" {
3965 try expect(std.mem.eql(u8, @tagName(Small2.a), "a"));
3966}
3967 {#code_end#}
3968 {#header_close#}2390 {#header_close#}
39692391
3970 {#header_open|extern union#}2392 {#header_open|extern union#}
...@@ -3983,26 +2405,8 @@ test "@tagName" {...@@ -3983,26 +2405,8 @@ test "@tagName" {
3983 {#header_open|Anonymous Union Literals#}2405 {#header_open|Anonymous Union Literals#}
3984 <p>{#link|Anonymous Struct Literals#} syntax can be used to initialize unions without specifying2406 <p>{#link|Anonymous Struct Literals#} syntax can be used to initialize unions without specifying
3985 the type:</p>2407 the type:</p>
3986 {#code_begin|test|test_anonymous_union#}2408 {#code|test_anonymous_union.zig#}
3987const std = @import("std");
3988const expect = std.testing.expect;
39892409
3990const Number = union {
3991 int: i32,
3992 float: f64,
3993};
3994
3995test "anonymous union literal syntax" {
3996 const i: Number = .{ .int = 42 };
3997 const f = makeNumber();
3998 try expect(i.int == 42);
3999 try expect(f.float == 12.34);
4000}
4001
4002fn makeNumber() Number {
4003 return .{ .float = 12.34 };
4004}
4005 {#code_end#}
4006 {#header_close#}2410 {#header_close#}
40072411
4008 {#header_close#}2412 {#header_close#}
...@@ -4017,214 +2421,55 @@ fn makeNumber() Number {...@@ -4017,214 +2421,55 @@ fn makeNumber() Number {
4017 This is typically used for type safety when interacting with C code that does not expose struct details.2421 This is typically used for type safety when interacting with C code that does not expose struct details.
4018 Example:2422 Example:
4019 </p>2423 </p>
4020 {#code_begin|test_err|test_opaque|expected type '*test_opaque.Derp', found '*test_opaque.Wat'#}2424 {#code|test_opaque.zig#}
4021const Derp = opaque {};
4022const Wat = opaque {};
4023
4024extern fn bar(d: *Derp) void;
4025fn foo(w: *Wat) callconv(.C) void {
4026 bar(w);
4027}
40282425
4029test "call foo" {
4030 foo(undefined);
4031}
4032 {#code_end#}
4033 {#header_close#}2426 {#header_close#}
40342427
4035 {#header_open|Blocks#}2428 {#header_open|Blocks#}
4036 <p>2429 <p>
4037 Blocks are used to limit the scope of variable declarations:2430 Blocks are used to limit the scope of variable declarations:
4038 </p>2431 </p>
4039 {#code_begin|test_err|test_blocks|use of undeclared identifier 'x'#}2432 {#code|test_blocks.zig#}
4040test "access variable after block scope" {2433
4041 {
4042 var x: i32 = 1;
4043 _ = &x;
4044 }
4045 x += 1;
4046}
4047 {#code_end#}
4048 <p>Blocks are expressions. When labeled, {#syntax#}break{#endsyntax#} can be used2434 <p>Blocks are expressions. When labeled, {#syntax#}break{#endsyntax#} can be used
4049 to return a value from the block:2435 to return a value from the block:
4050 </p>2436 </p>
4051 {#code_begin|test|test_labeled_break#}2437 {#code|test_labeled_break.zig#}
4052const std = @import("std");
4053const expect = std.testing.expect;
4054
4055test "labeled break from labeled block expression" {
4056 var y: i32 = 123;
40572438
4058 const x = blk: {
4059 y += 1;
4060 break :blk y;
4061 };
4062 try expect(x == 124);
4063 try expect(y == 124);
4064}
4065 {#code_end#}
4066 <p>Here, {#syntax#}blk{#endsyntax#} can be any name.</p>2439 <p>Here, {#syntax#}blk{#endsyntax#} can be any name.</p>
4067 {#see_also|Labeled while|Labeled for#}2440 {#see_also|Labeled while|Labeled for#}
40682441
4069 {#header_open|Shadowing#}2442 {#header_open|Shadowing#}
4070 <p>{#link|Identifiers#} are never allowed to "hide" other identifiers by using the same name:</p>2443 <p>{#link|Identifiers#} are never allowed to "hide" other identifiers by using the same name:</p>
4071 {#code_begin|test_err|test_shadowing|local variable shadows declaration#}2444 {#code|test_shadowing.zig#}
4072const pi = 3.14;
40732445
4074test "inside test block" {
4075 // Let's even go inside another block
4076 {
4077 var pi: i32 = 1234;
4078 }
4079}
4080 {#code_end#}
4081 <p>2446 <p>
4082 Because of this, when you read Zig code you can always rely on an identifier to consistently mean2447 Because of this, when you read Zig code you can always rely on an identifier to consistently mean
4083 the same thing within the scope it is defined. Note that you can, however, use the same name if2448 the same thing within the scope it is defined. Note that you can, however, use the same name if
4084 the scopes are separate:2449 the scopes are separate:
4085 </p>2450 </p>
4086 {#code_begin|test|test_scopes#}2451 {#code|test_scopes.zig#}
4087test "separate scopes" {2452
4088 {
4089 const pi = 3.14;
4090 _ = pi;
4091 }
4092 {
4093 var pi: bool = true;
4094 _ = &pi;
4095 }
4096}
4097 {#code_end#}
4098 {#header_close#}2453 {#header_close#}
40992454
4100 {#header_open|Empty Blocks#}2455 {#header_open|Empty Blocks#}
4101 <p>An empty block is equivalent to {#syntax#}void{}{#endsyntax#}:</p>2456 <p>An empty block is equivalent to {#syntax#}void{}{#endsyntax#}:</p>
4102 {#code_begin|test|test_empty_block#}2457 {#code|test_empty_block.zig#}
4103const std = @import("std");2458
4104const expect = std.testing.expect;
4105
4106test {
4107 const a = {};
4108 const b = void{};
4109 try expect(@TypeOf(a) == void);
4110 try expect(@TypeOf(b) == void);
4111 try expect(a == b);
4112}
4113 {#code_end#}
4114 {#header_close#}2459 {#header_close#}
4115 {#header_close#}2460 {#header_close#}
41162461
4117 {#header_open|switch#}2462 {#header_open|switch#}
4118 {#code_begin|test|test_switch#}2463 {#code|test_switch.zig#}
4119const std = @import("std");
4120const builtin = @import("builtin");
4121const expect = std.testing.expect;
4122
4123test "switch simple" {
4124 const a: u64 = 10;
4125 const zz: u64 = 103;
4126
4127 // All branches of a switch expression must be able to be coerced to a
4128 // common type.
4129 //
4130 // Branches cannot fallthrough. If fallthrough behavior is desired, combine
4131 // the cases and use an if.
4132 const b = switch (a) {
4133 // Multiple cases can be combined via a ','
4134 1, 2, 3 => 0,
4135
4136 // Ranges can be specified using the ... syntax. These are inclusive
4137 // of both ends.
4138 5...100 => 1,
4139
4140 // Branches can be arbitrarily complex.
4141 101 => blk: {
4142 const c: u64 = 5;
4143 break :blk c * 2 + 1;
4144 },
41452464
4146 // Switching on arbitrary expressions is allowed as long as the
4147 // expression is known at compile-time.
4148 zz => zz,
4149 blk: {
4150 const d: u32 = 5;
4151 const e: u32 = 100;
4152 break :blk d + e;
4153 } => 107,
4154
4155 // The else branch catches everything not already captured.
4156 // Else branches are mandatory unless the entire range of values
4157 // is handled.
4158 else => 9,
4159 };
4160
4161 try expect(b == 1);
4162}
4163
4164// Switch expressions can be used outside a function:
4165const os_msg = switch (builtin.target.os.tag) {
4166 .linux => "we found a linux user",
4167 else => "not a linux user",
4168};
4169
4170// Inside a function, switch statements implicitly are compile-time
4171// evaluated if the target expression is compile-time known.
4172test "switch inside function" {
4173 switch (builtin.target.os.tag) {
4174 .fuchsia => {
4175 // On an OS other than fuchsia, block is not even analyzed,
4176 // so this compile error is not triggered.
4177 // On fuchsia this compile error would be triggered.
4178 @compileError("fuchsia not supported");
4179 },
4180 else => {},
4181 }
4182}
4183 {#code_end#}
4184 <p>2465 <p>
4185 {#syntax#}switch{#endsyntax#} can be used to capture the field values2466 {#syntax#}switch{#endsyntax#} can be used to capture the field values
4186 of a {#link|Tagged union#}. Modifications to the field values can be2467 of a {#link|Tagged union#}. Modifications to the field values can be
4187 done by placing a {#syntax#}*{#endsyntax#} before the capture variable name,2468 done by placing a {#syntax#}*{#endsyntax#} before the capture variable name,
4188 turning it into a pointer.2469 turning it into a pointer.
4189 </p>2470 </p>
4190 {#code_begin|test|test_switch_tagged_union#}2471 {#code|test_switch_tagged_union.zig#}
4191const expect = @import("std").testing.expect;
4192
4193test "switch on tagged union" {
4194 const Point = struct {
4195 x: u8,
4196 y: u8,
4197 };
4198 const Item = union(enum) {
4199 a: u32,
4200 c: Point,
4201 d,
4202 e: u32,
4203 };
4204
4205 var a = Item{ .c = Point{ .x = 1, .y = 2 } };
4206
4207 // Switching on more complex enums is allowed.
4208 const b = switch (a) {
4209 // A capture group is allowed on a match, and will return the enum
4210 // value matched. If the payload types of both cases are the same
4211 // they can be put into the same switch prong.
4212 Item.a, Item.e => |item| item,
4213
4214 // A reference to the matched value can be obtained using `*` syntax.
4215 Item.c => |*item| blk: {
4216 item.*.x += 1;
4217 break :blk 6;
4218 },
42192472
4220 // No else is required if the types cases was exhaustively handled
4221 Item.d => 8,
4222 };
4223
4224 try expect(b == 6);
4225 try expect(a.c.x == 2);
4226}
4227 {#code_end#}
4228 {#see_also|comptime|enum|@compileError|Compile Variables#}2473 {#see_also|comptime|enum|@compileError|Compile Variables#}
42292474
4230 {#header_open|Exhaustive Switching#}2475 {#header_open|Exhaustive Switching#}
...@@ -4232,21 +2477,8 @@ test "switch on tagged union" {...@@ -4232,21 +2477,8 @@ test "switch on tagged union" {
4232 When a {#syntax#}switch{#endsyntax#} expression does not have an {#syntax#}else{#endsyntax#} clause,2477 When a {#syntax#}switch{#endsyntax#} expression does not have an {#syntax#}else{#endsyntax#} clause,
4233 it must exhaustively list all the possible values. Failure to do so is a compile error:2478 it must exhaustively list all the possible values. Failure to do so is a compile error:
4234 </p>2479 </p>
4235 {#code_begin|test_err|test_unhandled_enumeration_value|unhandled enumeration value#}2480 {#code|test_unhandled_enumeration_value.zig#}
4236const Color = enum {
4237 auto,
4238 off,
4239 on,
4240};
42412481
4242test "exhaustive switching" {
4243 const color = Color.off;
4244 switch (color) {
4245 Color.auto => {},
4246 Color.on => {},
4247 }
4248}
4249 {#code_end#}
4250 {#header_close#}2482 {#header_close#}
42512483
4252 {#header_open|Switching with Enum Literals#}2484 {#header_open|Switching with Enum Literals#}
...@@ -4254,26 +2486,8 @@ test "exhaustive switching" {...@@ -4254,26 +2486,8 @@ test "exhaustive switching" {
4254 {#link|Enum Literals#} can be useful to use with {#syntax#}switch{#endsyntax#} to avoid2486 {#link|Enum Literals#} can be useful to use with {#syntax#}switch{#endsyntax#} to avoid
4255 repetitively specifying {#link|enum#} or {#link|union#} types:2487 repetitively specifying {#link|enum#} or {#link|union#} types:
4256 </p>2488 </p>
4257 {#code_begin|test|test_exhaustive_switch#}2489 {#code|test_exhaustive_switch.zig#}
4258const std = @import("std");
4259const expect = std.testing.expect;
4260
4261const Color = enum {
4262 auto,
4263 off,
4264 on,
4265};
42662490
4267test "enum literals with switch" {
4268 const color = Color.off;
4269 const result = switch (color) {
4270 .auto => false,
4271 .on => false,
4272 .off => true,
4273 };
4274 try expect(result);
4275}
4276 {#code_end#}
4277 {#header_close#}2491 {#header_close#}
42782492
4279 {#header_open|Inline Switch Prongs#}2493 {#header_open|Inline Switch Prongs#}
...@@ -4282,136 +2496,23 @@ test "enum literals with switch" {...@@ -4282,136 +2496,23 @@ test "enum literals with switch" {
4282 the prong's body for each possible value it could have, making the2496 the prong's body for each possible value it could have, making the
4283 captured value {#link|comptime#}.2497 captured value {#link|comptime#}.
4284 </p>2498 </p>
4285 {#code_begin|test|test_inline_switch#}2499 {#code|test_inline_switch.zig#}
4286const std = @import("std");
4287const expect = std.testing.expect;
4288const expectError = std.testing.expectError;
4289
4290fn isFieldOptional(comptime T: type, field_index: usize) !bool {
4291 const fields = @typeInfo(T).Struct.fields;
4292 return switch (field_index) {
4293 // This prong is analyzed twice with `idx` being a
4294 // comptime-known value each time.
4295 inline 0, 1 => |idx| @typeInfo(fields[idx].type) == .Optional,
4296 else => return error.IndexOutOfBounds,
4297 };
4298}
42992500
4300const Struct1 = struct { a: u32, b: ?u32 };
4301
4302test "using @typeInfo with runtime values" {
4303 var index: usize = 0;
4304 try expect(!try isFieldOptional(Struct1, index));
4305 index += 1;
4306 try expect(try isFieldOptional(Struct1, index));
4307 index += 1;
4308 try expectError(error.IndexOutOfBounds, isFieldOptional(Struct1, index));
4309}
4310
4311// Calls to `isFieldOptional` on `Struct1` get unrolled to an equivalent
4312// of this function:
4313fn isFieldOptionalUnrolled(field_index: usize) !bool {
4314 return switch (field_index) {
4315 0 => false,
4316 1 => true,
4317 else => return error.IndexOutOfBounds,
4318 };
4319}
4320 {#code_end#}
4321 <p>The {#syntax#}inline{#endsyntax#} keyword may also be combined with ranges:</p>2501 <p>The {#syntax#}inline{#endsyntax#} keyword may also be combined with ranges:</p>
4322 {#code_begin|syntax|inline_prong_range#}2502 {#code|inline_prong_range.zig#}
4323fn isFieldOptional(comptime T: type, field_index: usize) !bool {2503
4324 const fields = @typeInfo(T).Struct.fields;
4325 return switch (field_index) {
4326 inline 0...fields.len - 1 => |idx| @typeInfo(fields[idx].type) == .Optional,
4327 else => return error.IndexOutOfBounds,
4328 };
4329}
4330 {#code_end#}
4331 <p>2504 <p>
4332 {#syntax#}inline else{#endsyntax#} prongs can be used as a type safe2505 {#syntax#}inline else{#endsyntax#} prongs can be used as a type safe
4333 alternative to {#syntax#}inline for{#endsyntax#} loops:2506 alternative to {#syntax#}inline for{#endsyntax#} loops:
4334 </p>2507 </p>
4335 {#code_begin|test|test_inline_else#}2508 {#code|test_inline_else.zig#}
4336const std = @import("std");
4337const expect = std.testing.expect;
4338
4339const SliceTypeA = extern struct {
4340 len: usize,
4341 ptr: [*]u32,
4342};
4343const SliceTypeB = extern struct {
4344 ptr: [*]SliceTypeA,
4345 len: usize,
4346};
4347const AnySlice = union(enum) {
4348 a: SliceTypeA,
4349 b: SliceTypeB,
4350 c: []const u8,
4351 d: []AnySlice,
4352};
43532509
4354fn withFor(any: AnySlice) usize {
4355 const Tag = @typeInfo(AnySlice).Union.tag_type.?;
4356 inline for (@typeInfo(Tag).Enum.fields) |field| {
4357 // With `inline for` the function gets generated as
4358 // a series of `if` statements relying on the optimizer
4359 // to convert it to a switch.
4360 if (field.value == @intFromEnum(any)) {
4361 return @field(any, field.name).len;
4362 }
4363 }
4364 // When using `inline for` the compiler doesn't know that every
4365 // possible case has been handled requiring an explicit `unreachable`.
4366 unreachable;
4367}
4368
4369fn withSwitch(any: AnySlice) usize {
4370 return switch (any) {
4371 // With `inline else` the function is explicitly generated
4372 // as the desired switch and the compiler can check that
4373 // every possible case is handled.
4374 inline else => |slice| slice.len,
4375 };
4376}
4377
4378test "inline for and inline else similarity" {
4379 const any = AnySlice{ .c = "hello" };
4380 try expect(withFor(any) == 5);
4381 try expect(withSwitch(any) == 5);
4382}
4383 {#code_end#}
4384 <p>2510 <p>
4385 When using an inline prong switching on an union an additional2511 When using an inline prong switching on an union an additional
4386 capture can be used to obtain the union's enum tag value.2512 capture can be used to obtain the union's enum tag value.
4387 </p>2513 </p>
4388 {#code_begin|test|test_inline_switch_union_tag#}2514 {#code|test_inline_switch_union_tag.zig#}
4389const std = @import("std");
4390const expect = std.testing.expect;
43912515
4392const U = union(enum) {
4393 a: u32,
4394 b: f32,
4395};
4396
4397fn getNum(u: U) u32 {
4398 switch (u) {
4399 // Here `num` is a runtime-known value that is either
4400 // `u.a` or `u.b` and `tag` is `u`'s comptime-known tag value.
4401 inline else => |num, tag| {
4402 if (tag == .b) {
4403 return @intFromFloat(num);
4404 }
4405 return num;
4406 }
4407 }
4408}
4409
4410test "test" {
4411 const u = U{ .b = 42 };
4412 try expect(getNum(u) == 42);
4413}
4414 {#code_end#}
4415 {#see_also|inline while|inline for#}2516 {#see_also|inline while|inline for#}
4416 {#header_close#}2517 {#header_close#}
4417 {#header_close#}2518 {#header_close#}
...@@ -4421,72 +2522,24 @@ test "test" {...@@ -4421,72 +2522,24 @@ test "test" {
4421 A while loop is used to repeatedly execute an expression until2522 A while loop is used to repeatedly execute an expression until
4422 some condition is no longer true.2523 some condition is no longer true.
4423 </p>2524 </p>
4424 {#code_begin|test|test_while#}2525 {#code|test_while.zig#}
4425const expect = @import("std").testing.expect;
44262526
4427test "while basic" {
4428 var i: usize = 0;
4429 while (i < 10) {
4430 i += 1;
4431 }
4432 try expect(i == 10);
4433}
4434 {#code_end#}
4435 <p>2527 <p>
4436 Use {#syntax#}break{#endsyntax#} to exit a while loop early.2528 Use {#syntax#}break{#endsyntax#} to exit a while loop early.
4437 </p>2529 </p>
4438 {#code_begin|test|test_while_break#}2530 {#code|test_while_break.zig#}
4439const expect = @import("std").testing.expect;
44402531
4441test "while break" {
4442 var i: usize = 0;
4443 while (true) {
4444 if (i == 10)
4445 break;
4446 i += 1;
4447 }
4448 try expect(i == 10);
4449}
4450 {#code_end#}
4451 <p>2532 <p>
4452 Use {#syntax#}continue{#endsyntax#} to jump back to the beginning of the loop.2533 Use {#syntax#}continue{#endsyntax#} to jump back to the beginning of the loop.
4453 </p>2534 </p>
4454 {#code_begin|test|test_while_continue#}2535 {#code|test_while_continue.zig#}
4455const expect = @import("std").testing.expect;
44562536
4457test "while continue" {
4458 var i: usize = 0;
4459 while (true) {
4460 i += 1;
4461 if (i < 10)
4462 continue;
4463 break;
4464 }
4465 try expect(i == 10);
4466}
4467 {#code_end#}
4468 <p>2537 <p>
4469 While loops support a continue expression which is executed when the loop2538 While loops support a continue expression which is executed when the loop
4470 is continued. The {#syntax#}continue{#endsyntax#} keyword respects this expression.2539 is continued. The {#syntax#}continue{#endsyntax#} keyword respects this expression.
4471 </p>2540 </p>
4472 {#code_begin|test|test_while_continue_expression#}2541 {#code|test_while_continue_expression.zig#}
4473const expect = @import("std").testing.expect;
4474
4475test "while loop continue expression" {
4476 var i: usize = 0;
4477 while (i < 10) : (i += 1) {}
4478 try expect(i == 10);
4479}
44802542
4481test "while loop continue expression, more complicated" {
4482 var i: usize = 1;
4483 var j: usize = 1;
4484 while (i * j < 2000) : ({ i *= 2; j *= 3; }) {
4485 const my_ij = i * j;
4486 try expect(my_ij < 2000);
4487 }
4488}
4489 {#code_end#}
4490 <p>2543 <p>
4491 While loops are expressions. The result of the expression is the2544 While loops are expressions. The result of the expression is the
4492 result of the {#syntax#}else{#endsyntax#} clause of a while loop, which is executed when2545 result of the {#syntax#}else{#endsyntax#} clause of a while loop, which is executed when
...@@ -4498,44 +2551,13 @@ test "while loop continue expression, more complicated" {...@@ -4498,44 +2551,13 @@ test "while loop continue expression, more complicated" {
4498 When you {#syntax#}break{#endsyntax#} from a while loop, the {#syntax#}else{#endsyntax#} branch is not2551 When you {#syntax#}break{#endsyntax#} from a while loop, the {#syntax#}else{#endsyntax#} branch is not
4499 evaluated.2552 evaluated.
4500 </p>2553 </p>
4501 {#code_begin|test|test_while_else#}2554 {#code|test_while_else.zig#}
4502const expect = @import("std").testing.expect;
4503
4504test "while else" {
4505 try expect(rangeHasNumber(0, 10, 5));
4506 try expect(!rangeHasNumber(0, 10, 15));
4507}
45082555
4509fn rangeHasNumber(begin: usize, end: usize, number: usize) bool {
4510 var i = begin;
4511 return while (i < end) : (i += 1) {
4512 if (i == number) {
4513 break true;
4514 }
4515 } else false;
4516}
4517 {#code_end#}
4518 {#header_open|Labeled while#}2556 {#header_open|Labeled while#}
4519 <p>When a {#syntax#}while{#endsyntax#} loop is labeled, it can be referenced from a {#syntax#}break{#endsyntax#}2557 <p>When a {#syntax#}while{#endsyntax#} loop is labeled, it can be referenced from a {#syntax#}break{#endsyntax#}
4520 or {#syntax#}continue{#endsyntax#} from within a nested loop:</p>2558 or {#syntax#}continue{#endsyntax#} from within a nested loop:</p>
4521 {#code_begin|test|test_while_nested_break#}2559 {#code|test_while_nested_break.zig#}
4522test "nested break" {
4523 outer: while (true) {
4524 while (true) {
4525 break :outer;
4526 }
4527 }
4528}
45292560
4530test "nested continue" {
4531 var i: usize = 0;
4532 outer: while (i < 10) : (i += 1) {
4533 while (true) {
4534 continue :outer;
4535 }
4536 }
4537}
4538 {#code_end#}
4539 {#header_close#}2561 {#header_close#}
4540 {#header_open|while with Optionals#}2562 {#header_open|while with Optionals#}
4541 <p>2563 <p>
...@@ -4551,45 +2573,8 @@ test "nested continue" {...@@ -4551,45 +2573,8 @@ test "nested continue" {
4551 The {#syntax#}else{#endsyntax#} branch is allowed on optional iteration. In this case, it will2573 The {#syntax#}else{#endsyntax#} branch is allowed on optional iteration. In this case, it will
4552 be executed on the first null value encountered.2574 be executed on the first null value encountered.
4553 </p>2575 </p>
4554 {#code_begin|test|test_while_null_capture#}2576 {#code|test_while_null_capture.zig#}
4555const expect = @import("std").testing.expect;
4556
4557test "while null capture" {
4558 var sum1: u32 = 0;
4559 numbers_left = 3;
4560 while (eventuallyNullSequence()) |value| {
4561 sum1 += value;
4562 }
4563 try expect(sum1 == 3);
4564
4565 // null capture with an else block
4566 var sum2: u32 = 0;
4567 numbers_left = 3;
4568 while (eventuallyNullSequence()) |value| {
4569 sum2 += value;
4570 } else {
4571 try expect(sum2 == 3);
4572 }
45732577
4574 // null capture with a continue expression
4575 var i: u32 = 0;
4576 var sum3: u32 = 0;
4577 numbers_left = 3;
4578 while (eventuallyNullSequence()) |value| : (i += 1) {
4579 sum3 += value;
4580 }
4581 try expect(i == 3);
4582}
4583
4584var numbers_left: u32 = undefined;
4585fn eventuallyNullSequence() ?u32 {
4586 return if (numbers_left == 0) null else blk: {
4587 numbers_left -= 1;
4588 break :blk numbers_left;
4589 };
4590}
4591
4592 {#code_end#}
4593 {#header_close#}2578 {#header_close#}
45942579
4595 {#header_open|while with Error Unions#}2580 {#header_open|while with Error Unions#}
...@@ -4603,28 +2588,8 @@ fn eventuallyNullSequence() ?u32 {...@@ -4603,28 +2588,8 @@ fn eventuallyNullSequence() ?u32 {
4603 When the {#syntax#}else |x|{#endsyntax#} syntax is present on a {#syntax#}while{#endsyntax#} expression,2588 When the {#syntax#}else |x|{#endsyntax#} syntax is present on a {#syntax#}while{#endsyntax#} expression,
4604 the while condition must have an {#link|Error Union Type#}.2589 the while condition must have an {#link|Error Union Type#}.
4605 </p>2590 </p>
4606 {#code_begin|test|test_while_error_capture#}2591 {#code|test_while_error_capture.zig#}
4607const expect = @import("std").testing.expect;
4608
4609test "while error union capture" {
4610 var sum1: u32 = 0;
4611 numbers_left = 3;
4612 while (eventuallyErrorSequence()) |value| {
4613 sum1 += value;
4614 } else |err| {
4615 try expect(err == error.ReachedZero);
4616 }
4617}
4618
4619var numbers_left: u32 = undefined;
46202592
4621fn eventuallyErrorSequence() anyerror!u32 {
4622 return if (numbers_left == 0) error.ReachedZero else blk: {
4623 numbers_left -= 1;
4624 break :blk numbers_left;
4625 };
4626}
4627 {#code_end#}
4628 {#header_close#}2593 {#header_close#}
46292594
4630 {#header_open|inline while#}2595 {#header_open|inline while#}
...@@ -4633,28 +2598,8 @@ fn eventuallyErrorSequence() anyerror!u32 {...@@ -4633,28 +2598,8 @@ fn eventuallyErrorSequence() anyerror!u32 {
4633 allows the code to do some things which only work at compile time,2598 allows the code to do some things which only work at compile time,
4634 such as use types as first class values.2599 such as use types as first class values.
4635 </p>2600 </p>
4636 {#code_begin|test|test_inline_while#}2601 {#code|test_inline_while.zig#}
4637const expect = @import("std").testing.expect;
4638
4639test "inline while loop" {
4640 comptime var i = 0;
4641 var sum: usize = 0;
4642 inline while (i < 3) : (i += 1) {
4643 const T = switch (i) {
4644 0 => f32,
4645 1 => i8,
4646 2 => bool,
4647 else => unreachable,
4648 };
4649 sum += typeNameLength(T);
4650 }
4651 try expect(sum == 9);
4652}
46532602
4654fn typeNameLength(comptime T: type) usize {
4655 return @typeName(T).len;
4656}
4657 {#code_end#}
4658 <p>2603 <p>
4659 It is recommended to use {#syntax#}inline{#endsyntax#} loops only for one of these reasons:2604 It is recommended to use {#syntax#}inline{#endsyntax#} loops only for one of these reasons:
4660 </p>2605 </p>
...@@ -4668,124 +2613,13 @@ fn typeNameLength(comptime T: type) usize {...@@ -4668,124 +2613,13 @@ fn typeNameLength(comptime T: type) usize {
4668 {#see_also|if|Optionals|Errors|comptime|unreachable#}2613 {#see_also|if|Optionals|Errors|comptime|unreachable#}
4669 {#header_close#}2614 {#header_close#}
4670 {#header_open|for#}2615 {#header_open|for#}
4671 {#code_begin|test|test_for#}2616 {#code|test_for.zig#}
4672const expect = @import("std").testing.expect;
4673
4674test "for basics" {
4675 const items = [_]i32 { 4, 5, 3, 4, 0 };
4676 var sum: i32 = 0;
4677
4678 // For loops iterate over slices and arrays.
4679 for (items) |value| {
4680 // Break and continue are supported.
4681 if (value == 0) {
4682 continue;
4683 }
4684 sum += value;
4685 }
4686 try expect(sum == 16);
4687
4688 // To iterate over a portion of a slice, reslice.
4689 for (items[0..1]) |value| {
4690 sum += value;
4691 }
4692 try expect(sum == 20);
4693
4694 // To access the index of iteration, specify a second condition as well
4695 // as a second capture value.
4696 var sum2: i32 = 0;
4697 for (items, 0..) |_, i| {
4698 try expect(@TypeOf(i) == usize);
4699 sum2 += @as(i32, @intCast(i));
4700 }
4701 try expect(sum2 == 10);
4702
4703 // To iterate over consecutive integers, use the range syntax.
4704 // Unbounded range is always a compile error.
4705 var sum3 : usize = 0;
4706 for (0..5) |i| {
4707 sum3 += i;
4708 }
4709 try expect(sum3 == 10);
4710}
4711
4712test "multi object for" {
4713 const items = [_]usize{ 1, 2, 3 };
4714 const items2 = [_]usize{ 4, 5, 6 };
4715 var count: usize = 0;
47162617
4717 // Iterate over multiple objects.
4718 // All lengths must be equal at the start of the loop, otherwise detectable
4719 // illegal behavior occurs.
4720 for (items, items2) |i, j| {
4721 count += i + j;
4722 }
4723
4724 try expect(count == 21);
4725}
4726
4727test "for reference" {
4728 var items = [_]i32{ 3, 4, 2 };
4729
4730 // Iterate over the slice by reference by
4731 // specifying that the capture value is a pointer.
4732 for (&items) |*value| {
4733 value.* += 1;
4734 }
4735
4736 try expect(items[0] == 4);
4737 try expect(items[1] == 5);
4738 try expect(items[2] == 3);
4739}
4740
4741test "for else" {
4742 // For allows an else attached to it, the same as a while loop.
4743 const items = [_]?i32{ 3, 4, null, 5 };
4744
4745 // For loops can also be used as expressions.
4746 // Similar to while loops, when you break from a for loop, the else branch is not evaluated.
4747 var sum: i32 = 0;
4748 const result = for (items) |value| {
4749 if (value != null) {
4750 sum += value.?;
4751 }
4752 } else blk: {
4753 try expect(sum == 12);
4754 break :blk sum;
4755 };
4756 try expect(result == 12);
4757}
4758 {#code_end#}
4759 {#header_open|Labeled for#}2618 {#header_open|Labeled for#}
4760 <p>When a {#syntax#}for{#endsyntax#} loop is labeled, it can be referenced from a {#syntax#}break{#endsyntax#}2619 <p>When a {#syntax#}for{#endsyntax#} loop is labeled, it can be referenced from a {#syntax#}break{#endsyntax#}
4761 or {#syntax#}continue{#endsyntax#} from within a nested loop:</p>2620 or {#syntax#}continue{#endsyntax#} from within a nested loop:</p>
4762 {#code_begin|test|test_for_nested_break#}2621 {#code|test_for_nested_break.zig#}
4763const std = @import("std");
4764const expect = std.testing.expect;
4765
4766test "nested break" {
4767 var count: usize = 0;
4768 outer: for (1..6) |_| {
4769 for (1..6) |_| {
4770 count += 1;
4771 break :outer;
4772 }
4773 }
4774 try expect(count == 1);
4775}
47762622
4777test "nested continue" {
4778 var count: usize = 0;
4779 outer: for (1..9) |_| {
4780 for (1..6) |_| {
4781 count += 1;
4782 continue :outer;
4783 }
4784 }
4785
4786 try expect(count == 8);
4787}
4788 {#code_end#}
4789 {#header_close#}2623 {#header_close#}
4790 {#header_open|inline for#}2624 {#header_open|inline for#}
4791 <p>2625 <p>
...@@ -4795,28 +2629,8 @@ test "nested continue" {...@@ -4795,28 +2629,8 @@ test "nested continue" {
4795 The capture value and iterator value of inlined for loops are2629 The capture value and iterator value of inlined for loops are
4796 compile-time known.2630 compile-time known.
4797 </p>2631 </p>
4798 {#code_begin|test|test_inline_for#}2632 {#code|test_inline_for.zig#}
4799const expect = @import("std").testing.expect;
4800
4801test "inline for loop" {
4802 const nums = [_]i32{2, 4, 6};
4803 var sum: usize = 0;
4804 inline for (nums) |i| {
4805 const T = switch (i) {
4806 2 => f32,
4807 4 => i8,
4808 6 => bool,
4809 else => unreachable,
4810 };
4811 sum += typeNameLength(T);
4812 }
4813 try expect(sum == 9);
4814}
48152633
4816fn typeNameLength(comptime T: type) usize {
4817 return @typeName(T).len;
4818}
4819 {#code_end#}
4820 <p>2634 <p>
4821 It is recommended to use {#syntax#}inline{#endsyntax#} loops only for one of these reasons:2635 It is recommended to use {#syntax#}inline{#endsyntax#} loops only for one of these reasons:
4822 </p>2636 </p>
...@@ -4830,229 +2644,25 @@ fn typeNameLength(comptime T: type) usize {...@@ -4830,229 +2644,25 @@ fn typeNameLength(comptime T: type) usize {
4830 {#see_also|while|comptime|Arrays|Slices#}2644 {#see_also|while|comptime|Arrays|Slices#}
4831 {#header_close#}2645 {#header_close#}
4832 {#header_open|if#}2646 {#header_open|if#}
4833 {#code_begin|test|test_if#}2647 {#code|test_if.zig#}
4834// If expressions have three uses, corresponding to the three types:
4835// * bool
4836// * ?T
4837// * anyerror!T
4838
4839const expect = @import("std").testing.expect;
4840
4841test "if expression" {
4842 // If expressions are used instead of a ternary expression.
4843 const a: u32 = 5;
4844 const b: u32 = 4;
4845 const result = if (a != b) 47 else 3089;
4846 try expect(result == 47);
4847}
4848
4849test "if boolean" {
4850 // If expressions test boolean conditions.
4851 const a: u32 = 5;
4852 const b: u32 = 4;
4853 if (a != b) {
4854 try expect(true);
4855 } else if (a == 9) {
4856 unreachable;
4857 } else {
4858 unreachable;
4859 }
4860}
4861
4862test "if error union" {
4863 // If expressions test for errors.
4864 // Note the |err| capture on the else.
4865
4866 const a: anyerror!u32 = 0;
4867 if (a) |value| {
4868 try expect(value == 0);
4869 } else |err| {
4870 _ = err;
4871 unreachable;
4872 }
48732648
4874 const b: anyerror!u32 = error.BadValue;
4875 if (b) |value| {
4876 _ = value;
4877 unreachable;
4878 } else |err| {
4879 try expect(err == error.BadValue);
4880 }
4881
4882 // The else and |err| capture is strictly required.
4883 if (a) |value| {
4884 try expect(value == 0);
4885 } else |_| {}
4886
4887 // To check only the error value, use an empty block expression.
4888 if (b) |_| {} else |err| {
4889 try expect(err == error.BadValue);
4890 }
4891
4892 // Access the value by reference using a pointer capture.
4893 var c: anyerror!u32 = 3;
4894 if (c) |*value| {
4895 value.* = 9;
4896 } else |_| {
4897 unreachable;
4898 }
4899
4900 if (c) |value| {
4901 try expect(value == 9);
4902 } else |_| {
4903 unreachable;
4904 }
4905}
4906 {#code_end#}
4907 {#header_open|if with Optionals#}2649 {#header_open|if with Optionals#}
49082650
4909 {#code_begin|test|test_if_optionals#}2651 {#code|test_if_optionals.zig#}
4910const expect = @import("std").testing.expect;
4911
4912test "if optional" {
4913 // If expressions test for null.
4914
4915 const a: ?u32 = 0;
4916 if (a) |value| {
4917 try expect(value == 0);
4918 } else {
4919 unreachable;
4920 }
4921
4922 const b: ?u32 = null;
4923 if (b) |_| {
4924 unreachable;
4925 } else {
4926 try expect(true);
4927 }
4928
4929 // The else is not required.
4930 if (a) |value| {
4931 try expect(value == 0);
4932 }
4933
4934 // To test against null only, use the binary equality operator.
4935 if (b == null) {
4936 try expect(true);
4937 }
4938
4939 // Access the value by reference using a pointer capture.
4940 var c: ?u32 = 3;
4941 if (c) |*value| {
4942 value.* = 2;
4943 }
4944
4945 if (c) |value| {
4946 try expect(value == 2);
4947 } else {
4948 unreachable;
4949 }
4950}
4951
4952test "if error union with optional" {
4953 // If expressions test for errors before unwrapping optionals.
4954 // The |optional_value| capture's type is ?u32.
4955
4956 const a: anyerror!?u32 = 0;
4957 if (a) |optional_value| {
4958 try expect(optional_value.? == 0);
4959 } else |err| {
4960 _ = err;
4961 unreachable;
4962 }
4963
4964 const b: anyerror!?u32 = null;
4965 if (b) |optional_value| {
4966 try expect(optional_value == null);
4967 } else |_| {
4968 unreachable;
4969 }
4970
4971 const c: anyerror!?u32 = error.BadValue;
4972 if (c) |optional_value| {
4973 _ = optional_value;
4974 unreachable;
4975 } else |err| {
4976 try expect(err == error.BadValue);
4977 }
4978
4979 // Access the value by reference by using a pointer capture each time.
4980 var d: anyerror!?u32 = 3;
4981 if (d) |*optional_value| {
4982 if (optional_value.*) |*value| {
4983 value.* = 9;
4984 }
4985 } else |_| {
4986 unreachable;
4987 }
49882652
4989 if (d) |optional_value| {
4990 try expect(optional_value.? == 9);
4991 } else |_| {
4992 unreachable;
4993 }
4994}
4995 {#code_end#}
4996 {#header_close#}2653 {#header_close#}
4997 {#see_also|Optionals|Errors#}2654 {#see_also|Optionals|Errors#}
4998 {#header_close#}2655 {#header_close#}
4999 {#header_open|defer#}2656 {#header_open|defer#}
5000 <p>Executes an expression unconditionally at scope exit.</p>2657 <p>Executes an expression unconditionally at scope exit.</p>
5001 {#code_begin|test|test_defer#}2658 {#code|test_defer.zig#}
5002const std = @import("std");
5003const expect = std.testing.expect;
5004const print = std.debug.print;
5005
5006fn deferExample() !usize {
5007 var a: usize = 1;
5008
5009 {
5010 defer a = 2;
5011 a = 1;
5012 }
5013 try expect(a == 2);
5014
5015 a = 5;
5016 return a;
5017}
50182659
5019test "defer basics" {
5020 try expect((try deferExample()) == 5);
5021}
5022 {#code_end#}
5023 <p>Defer expressions are evaluated in reverse order.</p>2660 <p>Defer expressions are evaluated in reverse order.</p>
5024 {#code_begin|test|defer_unwind#}2661 {#code|defer_unwind.zig#}
5025const std = @import("std");
5026const expect = std.testing.expect;
5027const print = std.debug.print;
5028
5029test "defer unwinding" {
5030 print("\n", .{});
50312662
5032 defer {
5033 print("1 ", .{});
5034 }
5035 defer {
5036 print("2 ", .{});
5037 }
5038 if (false) {
5039 // defers are not run if they are never executed.
5040 defer {
5041 print("3 ", .{});
5042 }
5043 }
5044}
5045 {#code_end#}
5046 <p>Inside a defer expression the return statement is not allowed.</p>2663 <p>Inside a defer expression the return statement is not allowed.</p>
5047 {#code_begin|test_err|test_invalid_defer|cannot return from defer expression#}2664 {#code|test_invalid_defer.zig#}
5048fn deferInvalidExample() !void {
5049 defer {
5050 return error.DeferError;
5051 }
50522665
5053 return error.DeferError;
5054}
5055 {#code_end#}
5056 {#see_also|Errors#}2666 {#see_also|Errors#}
5057 {#header_close#}2667 {#header_close#}
5058 {#header_open|unreachable#}2668 {#header_open|unreachable#}
...@@ -5065,45 +2675,15 @@ fn deferInvalidExample() !void {...@@ -5065,45 +2675,15 @@ fn deferInvalidExample() !void {
5065 will never be hit to perform optimizations.2675 will never be hit to perform optimizations.
5066 </p>2676 </p>
5067 {#header_open|Basics#}2677 {#header_open|Basics#}
5068 {#code_begin|test|test_unreachable#}2678 {#code|test_unreachable.zig#}
5069// unreachable is used to assert that control flow will never reach a2679
5070// particular location:
5071test "basic math" {
5072 const x = 1;
5073 const y = 2;
5074 if (x + y != 3) {
5075 unreachable;
5076 }
5077}
5078 {#code_end#}
5079 <p>In fact, this is how {#syntax#}std.debug.assert{#endsyntax#} is implemented:</p>2680 <p>In fact, this is how {#syntax#}std.debug.assert{#endsyntax#} is implemented:</p>
5080 {#code_begin|test_err|test_assertion_failure#}2681 {#code|test_assertion_failure.zig#}
5081// This is how std.debug.assert is implemented
5082fn assert(ok: bool) void {
5083 if (!ok) unreachable; // assertion failure
5084}
50852682
5086// This test will fail because we hit unreachable.
5087test "this will fail" {
5088 assert(false);
5089}
5090 {#code_end#}
5091 {#header_close#}2683 {#header_close#}
5092 {#header_open|At Compile-Time#}2684 {#header_open|At Compile-Time#}
5093 {#code_begin|test_err|test_comptime_unreachable|unreachable code#}2685 {#code|test_comptime_unreachable.zig#}
5094const assert = @import("std").debug.assert;
5095
5096test "type of unreachable" {
5097 comptime {
5098 // The type of unreachable is noreturn.
5099
5100 // However this assertion will still fail to compile because
5101 // unreachable expressions are compile errors.
51022686
5103 assert(@TypeOf(unreachable) == noreturn);
5104 }
5105}
5106 {#code_end#}
5107 {#see_also|Zig Test|Build Mode|comptime#}2687 {#see_also|Zig Test|Build Mode|comptime#}
5108 {#header_close#}2688 {#header_close#}
5109 {#header_close#}2689 {#header_close#}
...@@ -5121,101 +2701,16 @@ test "type of unreachable" {...@@ -5121,101 +2701,16 @@ test "type of unreachable" {
5121 <p>When resolving types together, such as {#syntax#}if{#endsyntax#} clauses or {#syntax#}switch{#endsyntax#} prongs,2701 <p>When resolving types together, such as {#syntax#}if{#endsyntax#} clauses or {#syntax#}switch{#endsyntax#} prongs,
5122 the {#syntax#}noreturn{#endsyntax#} type is compatible with every other type. Consider:2702 the {#syntax#}noreturn{#endsyntax#} type is compatible with every other type. Consider:
5123 </p>2703 </p>
5124 {#code_begin|test|test_noreturn#}2704 {#code|test_noreturn.zig#}
5125fn foo(condition: bool, b: u32) void {
5126 const a = if (condition) b else return;
5127 _ = a;
5128 @panic("do something with a");
5129}
5130test "noreturn" {
5131 foo(false, 1);
5132}
5133 {#code_end#}
5134 <p>Another use case for {#syntax#}noreturn{#endsyntax#} is the {#syntax#}exit{#endsyntax#} function:</p>
5135 {#code_begin|test|test_noreturn_from_exit#}
5136 {#target_windows#}
5137const std = @import("std");
5138const builtin = @import("builtin");
5139const native_arch = builtin.cpu.arch;
5140const expect = std.testing.expect;
5141
5142const WINAPI: std.builtin.CallingConvention = if (native_arch == .x86) .Stdcall else .C;
5143extern "kernel32" fn ExitProcess(exit_code: c_uint) callconv(WINAPI) noreturn;
5144
5145test "foo" {
5146 const value = bar() catch ExitProcess(1);
5147 try expect(value == 1234);
5148}
51492705
5150fn bar() anyerror!u32 {2706 <p>Another use case for {#syntax#}noreturn{#endsyntax#} is the {#syntax#}exit{#endsyntax#} function:</p>
5151 return 1234;2707 {#code|test_noreturn_from_exit.zig#}
5152}
51532708
5154 {#code_end#}
5155 {#header_close#}2709 {#header_close#}
51562710
5157 {#header_open|Functions#}2711 {#header_open|Functions#}
5158 {#code_begin|test|test_functions#}2712 {#code|test_functions.zig#}
5159const std = @import("std");
5160const builtin = @import("builtin");
5161const native_arch = builtin.cpu.arch;
5162const expect = std.testing.expect;
5163
5164// Functions are declared like this
5165fn add(a: i8, b: i8) i8 {
5166 if (a == 0) {
5167 return b;
5168 }
5169
5170 return a + b;
5171}
51722713
5173// The export specifier makes a function externally visible in the generated
5174// object file, and makes it use the C ABI.
5175export fn sub(a: i8, b: i8) i8 { return a - b; }
5176
5177// The extern specifier is used to declare a function that will be resolved
5178// at link time, when linking statically, or at runtime, when linking
5179// dynamically. The quoted identifier after the extern keyword specifies
5180// the library that has the function. (e.g. "c" -> libc.so)
5181// The callconv specifier changes the calling convention of the function.
5182const WINAPI: std.builtin.CallingConvention = if (native_arch == .x86) .Stdcall else .C;
5183extern "kernel32" fn ExitProcess(exit_code: u32) callconv(WINAPI) noreturn;
5184extern "c" fn atan2(a: f64, b: f64) f64;
5185
5186// The @setCold builtin tells the optimizer that a function is rarely called.
5187fn abort() noreturn {
5188 @setCold(true);
5189 while (true) {}
5190}
5191
5192// The naked calling convention makes a function not have any function prologue or epilogue.
5193// This can be useful when integrating with assembly.
5194fn _start() callconv(.Naked) noreturn {
5195 abort();
5196}
5197
5198// The inline calling convention forces a function to be inlined at all call sites.
5199// If the function cannot be inlined, it is a compile-time error.
5200fn shiftLeftOne(a: u32) callconv(.Inline) u32 {
5201 return a << 1;
5202}
5203
5204// The pub specifier allows the function to be visible when importing.
5205// Another file can use @import and call sub2
5206pub fn sub2(a: i8, b: i8) i8 { return a - b; }
5207
5208// Function pointers are prefixed with `*const `.
5209const Call2Op = *const fn (a: i8, b: i8) i8;
5210fn doOp(fnCall: Call2Op, op1: i8, op2: i8) i8 {
5211 return fnCall(op1, op2);
5212}
5213
5214test "function" {
5215 try expect(doOp(add, 5, 6) == 11);
5216 try expect(doOp(sub2, 5, 6) == -1);
5217}
5218 {#code_end#}
5219 <p>There is a difference between a function <em>body</em> and a function <em>pointer</em>.2714 <p>There is a difference between a function <em>body</em> and a function <em>pointer</em>.
5220 Function bodies are {#link|comptime#}-only types while function {#link|Pointers#} may be2715 Function bodies are {#link|comptime#}-only types while function {#link|Pointers#} may be
5221 runtime-known.</p>2716 runtime-known.</p>
...@@ -5232,26 +2727,8 @@ test "function" {...@@ -5232,26 +2727,8 @@ test "function" {
5232 as parameters, Zig may choose to copy and pass by value, or pass by reference, whichever way2727 as parameters, Zig may choose to copy and pass by value, or pass by reference, whichever way
5233 Zig decides will be faster. This is made possible, in part, by the fact that parameters are immutable.2728 Zig decides will be faster. This is made possible, in part, by the fact that parameters are immutable.
5234 </p>2729 </p>
5235 {#code_begin|test|test_pass_by_reference_or_value#}2730 {#code|test_pass_by_reference_or_value.zig#}
5236const Point = struct {
5237 x: i32,
5238 y: i32,
5239};
5240
5241fn foo(point: Point) i32 {
5242 // Here, `point` could be a reference, or a copy. The function body
5243 // can ignore the difference and treat it as a value. Be very careful
5244 // taking the address of the parameter - it should be treated as if
5245 // the address will become invalid when the function returns.
5246 return point.x + point.y;
5247}
5248
5249const expect = @import("std").testing.expect;
52502731
5251test "pass struct to function" {
5252 try expect(foo(Point{ .x = 1, .y = 2 }) == 3);
5253}
5254 {#code_end#}
5255 <p>2732 <p>
5256 For extern functions, Zig follows the C ABI for passing structs and unions by value.2733 For extern functions, Zig follows the C ABI for passing structs and unions by value.
5257 </p>2734 </p>
...@@ -5262,21 +2739,8 @@ test "pass struct to function" {...@@ -5262,21 +2739,8 @@ test "pass struct to function" {
5262 In this case the parameter types will be inferred when the function is called.2739 In this case the parameter types will be inferred when the function is called.
5263 Use {#link|@TypeOf#} and {#link|@typeInfo#} to get information about the inferred type.2740 Use {#link|@TypeOf#} and {#link|@typeInfo#} to get information about the inferred type.
5264 </p>2741 </p>
5265 {#code_begin|test|test_fn_type_inference#}2742 {#code|test_fn_type_inference.zig#}
5266const expect = @import("std").testing.expect;
5267
5268fn addFortyTwo(x: anytype) @TypeOf(x) {
5269 return x + 42;
5270}
52712743
5272test "fn type inference" {
5273 try expect(addFortyTwo(1) == 43);
5274 try expect(@TypeOf(addFortyTwo(1)) == comptime_int);
5275 const y: i64 = 2;
5276 try expect(addFortyTwo(y) == 44);
5277 try expect(@TypeOf(addFortyTwo(y)) == i64);
5278}
5279 {#code_end#}
52802744
5281 {#header_close#}2745 {#header_close#}
52822746
...@@ -5292,17 +2756,8 @@ test "fn type inference" {...@@ -5292,17 +2756,8 @@ test "fn type inference" {
5292 compile-time known are treated as {#link|Compile Time Parameters#}. This can potentially2756 compile-time known are treated as {#link|Compile Time Parameters#}. This can potentially
5293 propagate all the way to the return value:2757 propagate all the way to the return value:
5294 </p>2758 </p>
5295 {#code_begin|test|inline_call#}2759 {#code|inline_call.zig#}
5296test "inline function call" {
5297 if (foo(1200, 34) != 1234) {
5298 @compileError("bad");
5299 }
5300}
53012760
5302inline fn foo(a: i32, b: i32) i32 {
5303 return a + b;
5304}
5305 {#code_end#}
5306 <p>If {#syntax#}inline{#endsyntax#} is removed, the test fails with the compile error2761 <p>If {#syntax#}inline{#endsyntax#} is removed, the test fails with the compile error
5307 instead of passing.</p>2762 instead of passing.</p>
5308 <p>It is generally better to let the compiler decide when to inline a2763 <p>It is generally better to let the compiler decide when to inline a
...@@ -5318,18 +2773,8 @@ inline fn foo(a: i32, b: i32) i32 {...@@ -5318,18 +2773,8 @@ inline fn foo(a: i32, b: i32) i32 {
5318 {#header_close#}2773 {#header_close#}
53192774
5320 {#header_open|Function Reflection#}2775 {#header_open|Function Reflection#}
5321 {#code_begin|test|test_fn_reflection#}2776 {#code|test_fn_reflection.zig#}
5322const std = @import("std");
5323const math = std.math;
5324const testing = std.testing;
5325
5326test "fn reflection" {
5327 try testing.expect(@typeInfo(@TypeOf(testing.expect)).Fn.params[0].type.? == bool);
5328 try testing.expect(@typeInfo(@TypeOf(testing.tmpDir)).Fn.return_type.? == testing.TmpDir);
53292777
5330 try testing.expect(@typeInfo(@TypeOf(math.Log2Int)).Fn.is_generic);
5331}
5332 {#code_end#}
5333 {#header_close#}2778 {#header_close#}
5334 {#header_close#}2779 {#header_close#}
5335 {#header_open|Errors#}2780 {#header_open|Errors#}
...@@ -5348,60 +2793,21 @@ test "fn reflection" {...@@ -5348,60 +2793,21 @@ test "fn reflection" {
5348 <p>2793 <p>
5349 You can {#link|coerce|Type Coercion#} an error from a subset to a superset:2794 You can {#link|coerce|Type Coercion#} an error from a subset to a superset:
5350 </p>2795 </p>
5351 {#code_begin|test|test_coerce_error_subset_to_superset#}2796 {#code|test_coerce_error_subset_to_superset.zig#}
5352const std = @import("std");
5353
5354const FileOpenError = error {
5355 AccessDenied,
5356 OutOfMemory,
5357 FileNotFound,
5358};
5359
5360const AllocationError = error {
5361 OutOfMemory,
5362};
5363
5364test "coerce subset to superset" {
5365 const err = foo(AllocationError.OutOfMemory);
5366 try std.testing.expect(err == FileOpenError.OutOfMemory);
5367}
53682797
5369fn foo(err: AllocationError) FileOpenError {
5370 return err;
5371}
5372 {#code_end#}
5373 <p>2798 <p>
5374 But you cannot {#link|coerce|Type Coercion#} an error from a superset to a subset:2799 But you cannot {#link|coerce|Type Coercion#} an error from a superset to a subset:
5375 </p>2800 </p>
5376 {#code_begin|test_err|test_coerce_error_superset_to_subset|not a member of destination error set#}2801 {#code|test_coerce_error_superset_to_subset.zig#}
5377const FileOpenError = error {
5378 AccessDenied,
5379 OutOfMemory,
5380 FileNotFound,
5381};
5382
5383const AllocationError = error {
5384 OutOfMemory,
5385};
5386
5387test "coerce superset to subset" {
5388 foo(FileOpenError.OutOfMemory) catch {};
5389}
53902802
5391fn foo(err: FileOpenError) AllocationError {
5392 return err;
5393}
5394 {#code_end#}
5395 <p>2803 <p>
5396 There is a shortcut for declaring an error set with only 1 value, and then getting that value:2804 There is a shortcut for declaring an error set with only 1 value, and then getting that value:
5397 </p>2805 </p>
5398 {#code_begin|syntax|single_value_error_set_shortcut#}2806 {#code|single_value_error_set_shortcut.zig#}
5399const err = error.FileNotFound;2807
5400 {#code_end#}
5401 <p>This is equivalent to:</p>2808 <p>This is equivalent to:</p>
5402 {#code_begin|syntax|single_value_error_set#}2809 {#code|single_value_error_set.zig#}
5403const err = (error {FileNotFound}).FileNotFound;2810
5404 {#code_end#}
5405 <p>2811 <p>
5406 This becomes useful when using {#link|Inferred Error Sets#}.2812 This becomes useful when using {#link|Inferred Error Sets#}.
5407 </p>2813 </p>
...@@ -5432,47 +2838,8 @@ const err = (error {FileNotFound}).FileNotFound;...@@ -5432,47 +2838,8 @@ const err = (error {FileNotFound}).FileNotFound;
5432 <p>2838 <p>
5433 Here is a function to parse a string into a 64-bit integer:2839 Here is a function to parse a string into a 64-bit integer:
5434 </p>2840 </p>
5435 {#code_begin|test|error_union_parsing_u64#}2841 {#code|error_union_parsing_u64.zig#}
5436const std = @import("std");
5437const maxInt = std.math.maxInt;
5438
5439pub fn parseU64(buf: []const u8, radix: u8) !u64 {
5440 var x: u64 = 0;
5441
5442 for (buf) |c| {
5443 const digit = charToDigit(c);
54442842
5445 if (digit >= radix) {
5446 return error.InvalidChar;
5447 }
5448
5449 // x *= radix
5450 var ov = @mulWithOverflow(x, radix);
5451 if (ov[1] != 0) return error.OverFlow;
5452
5453 // x += digit
5454 ov = @addWithOverflow(ov[0], digit);
5455 if (ov[1] != 0) return error.OverFlow;
5456 x = ov[0];
5457 }
5458
5459 return x;
5460}
5461
5462fn charToDigit(c: u8) u8 {
5463 return switch (c) {
5464 '0' ... '9' => c - '0',
5465 'A' ... 'Z' => c - 'A' + 10,
5466 'a' ... 'z' => c - 'a' + 10,
5467 else => maxInt(u8),
5468 };
5469}
5470
5471test "parse u64" {
5472 const result = try parseU64("1234", 10);
5473 try std.testing.expect(result == 1234);
5474}
5475 {#code_end#}
5476 <p>2843 <p>
5477 Notice the return type is {#syntax#}!u64{#endsyntax#}. This means that the function2844 Notice the return type is {#syntax#}!u64{#endsyntax#}. This means that the function
5478 either returns an unsigned 64 bit integer, or an error. We left off the error set2845 either returns an unsigned 64 bit integer, or an error. We left off the error set
...@@ -5495,14 +2862,8 @@ test "parse u64" {...@@ -5495,14 +2862,8 @@ test "parse u64" {
5495 </ul>2862 </ul>
5496 {#header_open|catch#}2863 {#header_open|catch#}
5497 <p>If you want to provide a default value, you can use the {#syntax#}catch{#endsyntax#} binary operator:</p>2864 <p>If you want to provide a default value, you can use the {#syntax#}catch{#endsyntax#} binary operator:</p>
5498 {#code_begin|syntax|catch#}2865 {#code|catch.zig#}
5499const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
55002866
5501fn doAThing(str: []u8) void {
5502 const number = parseU64(str, 10) catch 13;
5503 _ = number; // ...
5504}
5505 {#code_end#}
5506 <p>2867 <p>
5507 In this code, {#syntax#}number{#endsyntax#} will be equal to the successfully parsed string, or2868 In this code, {#syntax#}number{#endsyntax#} will be equal to the successfully parsed string, or
5508 a default value of 13. The type of the right hand side of the binary {#syntax#}catch{#endsyntax#} operator must2869 a default value of 13. The type of the right hand side of the binary {#syntax#}catch{#endsyntax#} operator must
...@@ -5513,40 +2874,19 @@ fn doAThing(str: []u8) void {...@@ -5513,40 +2874,19 @@ fn doAThing(str: []u8) void {
5513 {#syntax#}catch{#endsyntax#} after performing some logic, you2874 {#syntax#}catch{#endsyntax#} after performing some logic, you
5514 can combine {#syntax#}catch{#endsyntax#} with named {#link|Blocks#}:2875 can combine {#syntax#}catch{#endsyntax#} with named {#link|Blocks#}:
5515 </p>2876 </p>
5516 {#code_begin|syntax|handle_error_with_catch_block.zig#}2877 {#code|handle_error_with_catch_block.zig.zig#}
5517const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
55182878
5519fn doAThing(str: []u8) void {
5520 const number = parseU64(str, 10) catch blk: {
5521 // do things
5522 break :blk 13;
5523 };
5524 _ = number; // number is now initialized
5525}
5526 {#code_end#}
5527 {#header_close#}2879 {#header_close#}
5528 {#header_open|try#}2880 {#header_open|try#}
5529 <p>Let's say you wanted to return the error if you got one, otherwise continue with the2881 <p>Let's say you wanted to return the error if you got one, otherwise continue with the
5530 function logic:</p>2882 function logic:</p>
5531 {#code_begin|syntax|catch_err_return#}2883 {#code|catch_err_return.zig#}
5532const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
55332884
5534fn doAThing(str: []u8) !void {
5535 const number = parseU64(str, 10) catch |err| return err;
5536 _ = number; // ...
5537}
5538 {#code_end#}
5539 <p>2885 <p>
5540 There is a shortcut for this. The {#syntax#}try{#endsyntax#} expression:2886 There is a shortcut for this. The {#syntax#}try{#endsyntax#} expression:
5541 </p>2887 </p>
5542 {#code_begin|syntax|try#}2888 {#code|try.zig#}
5543const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
55442889
5545fn doAThing(str: []u8) !void {
5546 const number = try parseU64(str, 10);
5547 _ = number; // ...
5548}
5549 {#code_end#}
5550 <p>2890 <p>
5551 {#syntax#}try{#endsyntax#} evaluates an error union expression. If it is an error, it returns2891 {#syntax#}try{#endsyntax#} evaluates an error union expression. If it is an error, it returns
5552 from the current function with the same error. Otherwise, the expression results in2892 from the current function with the same error. Otherwise, the expression results in
...@@ -5653,174 +2993,26 @@ fn createFoo(param: i32) !Foo {...@@ -5653,174 +2993,26 @@ fn createFoo(param: i32) !Foo {
5653 It should be noted that {#syntax#}errdefer{#endsyntax#} statements only last until the end of the block2993 It should be noted that {#syntax#}errdefer{#endsyntax#} statements only last until the end of the block
5654 they are written in, and therefore are not run if an error is returned outside of that block:2994 they are written in, and therefore are not run if an error is returned outside of that block:
5655 </p>2995 </p>
5656 {#code_begin|test_err|test_errdefer_slip_ups|1 tests leaked memory#}2996 {#code|test_errdefer_slip_ups.zig#}
5657const std = @import("std");
5658const Allocator = std.mem.Allocator;
5659
5660const Foo = struct {
5661 data: u32,
5662};
5663
5664fn tryToAllocateFoo(allocator: Allocator) !*Foo {
5665 return allocator.create(Foo);
5666}
5667
5668fn deallocateFoo(allocator: Allocator, foo: *Foo) void {
5669 allocator.destroy(foo);
5670}
56712997
5672fn getFooData() !u32 {
5673 return 666;
5674}
5675
5676fn createFoo(allocator: Allocator, param: i32) !*Foo {
5677 const foo = getFoo: {
5678 var foo = try tryToAllocateFoo(allocator);
5679 errdefer deallocateFoo(allocator, foo); // Only lasts until the end of getFoo
5680
5681 // Calls deallocateFoo on error
5682 foo.data = try getFooData();
5683
5684 break :getFoo foo;
5685 };
5686
5687 // Outside of the scope of the errdefer, so
5688 // deallocateFoo will not be called here
5689 if (param > 1337) return error.InvalidParam;
5690
5691 return foo;
5692}
5693
5694test "createFoo" {
5695 try std.testing.expectError(error.InvalidParam, createFoo(std.testing.allocator, 2468));
5696}
5697 {#code_end#}
5698 <p>2998 <p>
5699 To ensure that {#syntax#}deallocateFoo{#endsyntax#} is properly called2999 To ensure that {#syntax#}deallocateFoo{#endsyntax#} is properly called
5700 when returning an error, you must add an {#syntax#}errdefer{#endsyntax#} outside of the block:3000 when returning an error, you must add an {#syntax#}errdefer{#endsyntax#} outside of the block:
5701 </p>3001 </p>
5702 {#code_begin|test|test_errdefer_block#}3002 {#code|test_errdefer_block.zig#}
5703const std = @import("std");
5704const Allocator = std.mem.Allocator;
5705
5706const Foo = struct {
5707 data: u32,
5708};
5709
5710fn tryToAllocateFoo(allocator: Allocator) !*Foo {
5711 return allocator.create(Foo);
5712}
57133003
5714fn deallocateFoo(allocator: Allocator, foo: *Foo) void {
5715 allocator.destroy(foo);
5716}
5717
5718fn getFooData() !u32 {
5719 return 666;
5720}
5721
5722fn createFoo(allocator: Allocator, param: i32) !*Foo {
5723 const foo = getFoo: {
5724 var foo = try tryToAllocateFoo(allocator);
5725 errdefer deallocateFoo(allocator, foo);
5726
5727 foo.data = try getFooData();
5728
5729 break :getFoo foo;
5730 };
5731 // This lasts for the rest of the function
5732 errdefer deallocateFoo(allocator, foo);
5733
5734 // Error is now properly handled by errdefer
5735 if (param > 1337) return error.InvalidParam;
5736
5737 return foo;
5738}
5739
5740test "createFoo" {
5741 try std.testing.expectError(error.InvalidParam, createFoo(std.testing.allocator, 2468));
5742}
5743 {#code_end#}
5744 <p>3004 <p>
5745 The fact that errdefers only last for the block they are declared in is3005 The fact that errdefers only last for the block they are declared in is
5746 especially important when using loops:3006 especially important when using loops:
5747 </p>3007 </p>
5748 {#code_begin|test_err|test_errdefer_loop_leak|3 errors were logged#}3008 {#code|test_errdefer_loop_leak.zig#}
5749const std = @import("std");
5750const Allocator = std.mem.Allocator;
5751
5752const Foo = struct {
5753 data: *u32
5754};
57553009
5756fn getData() !u32 {
5757 return 666;
5758}
5759
5760fn genFoos(allocator: Allocator, num: usize) ![]Foo {
5761 const foos = try allocator.alloc(Foo, num);
5762 errdefer allocator.free(foos);
5763
5764 for (foos, 0..) |*foo, i| {
5765 foo.data = try allocator.create(u32);
5766 // This errdefer does not last between iterations
5767 errdefer allocator.destroy(foo.data);
5768
5769 // The data for the first 3 foos will be leaked
5770 if(i >= 3) return error.TooManyFoos;
5771
5772 foo.data.* = try getData();
5773 }
5774
5775 return foos;
5776}
5777
5778test "genFoos" {
5779 try std.testing.expectError(error.TooManyFoos, genFoos(std.testing.allocator, 5));
5780}
5781 {#code_end#}
5782 <p>3010 <p>
5783 Special care must be taken with code that allocates in a loop3011 Special care must be taken with code that allocates in a loop
5784 to make sure that no memory is leaked when returning an error:3012 to make sure that no memory is leaked when returning an error:
5785 </p>3013 </p>
5786 {#code_begin|test|test_errdefer_loop#}3014 {#code|test_errdefer_loop.zig#}
5787const std = @import("std");
5788const Allocator = std.mem.Allocator;
5789
5790const Foo = struct {
5791 data: *u32
5792};
57933015
5794fn getData() !u32 {
5795 return 666;
5796}
5797
5798fn genFoos(allocator: Allocator, num: usize) ![]Foo {
5799 const foos = try allocator.alloc(Foo, num);
5800 errdefer allocator.free(foos);
5801
5802 // Used to track how many foos have been initialized
5803 // (including their data being allocated)
5804 var num_allocated: usize = 0;
5805 errdefer for (foos[0..num_allocated]) |foo| {
5806 allocator.destroy(foo.data);
5807 };
5808 for (foos, 0..) |*foo, i| {
5809 foo.data = try allocator.create(u32);
5810 num_allocated += 1;
5811
5812 if (i >= 3) return error.TooManyFoos;
5813
5814 foo.data.* = try getData();
5815 }
5816
5817 return foos;
5818}
5819
5820test "genFoos" {
5821 try std.testing.expectError(error.TooManyFoos, genFoos(std.testing.allocator, 5));
5822}
5823 {#code_end#}
5824 {#header_close#}3016 {#header_close#}
5825 <p>3017 <p>
5826 A couple of other tidbits about error handling:3018 A couple of other tidbits about error handling:
...@@ -5841,25 +3033,8 @@ test "genFoos" {...@@ -5841,25 +3033,8 @@ test "genFoos" {
58413033
5842 <p>An error union is created with the {#syntax#}!{#endsyntax#} binary operator.3034 <p>An error union is created with the {#syntax#}!{#endsyntax#} binary operator.
5843 You can use compile-time reflection to access the child type of an error union:</p>3035 You can use compile-time reflection to access the child type of an error union:</p>
5844 {#code_begin|test|test_error_union#}3036 {#code|test_error_union.zig#}
5845const expect = @import("std").testing.expect;
5846
5847test "error union" {
5848 var foo: anyerror!i32 = undefined;
58493037
5850 // Coerce from child type of an error union:
5851 foo = 1234;
5852
5853 // Coerce from an error set:
5854 foo = error.SomeError;
5855
5856 // Use compile-time reflection to access the payload type of an error union:
5857 try comptime expect(@typeInfo(@TypeOf(foo)).ErrorUnion.payload == i32);
5858
5859 // Use compile-time reflection to access the error set type of an error union:
5860 try comptime expect(@typeInfo(@TypeOf(foo)).ErrorUnion.error_set == anyerror);
5861}
5862 {#code_end#}
5863 {#header_open|Merging Error Sets#}3038 {#header_open|Merging Error Sets#}
5864 <p>3039 <p>
5865 Use the {#syntax#}||{#endsyntax#} operator to merge two error sets together. The resulting3040 Use the {#syntax#}||{#endsyntax#} operator to merge two error sets together. The resulting
...@@ -5873,129 +3048,37 @@ test "error union" {...@@ -5873,129 +3048,37 @@ test "error union" {
5873 {#syntax#}LinuxFileOpenError || WindowsFileOpenError{#endsyntax#} for the error set of opening3048 {#syntax#}LinuxFileOpenError || WindowsFileOpenError{#endsyntax#} for the error set of opening
5874 files.3049 files.
5875 </p>3050 </p>
5876 {#code_begin|test|test_merging_error_sets#}3051 {#code|test_merging_error_sets.zig#}
5877const A = error{
5878 NotDir,
5879
5880 /// A doc comment
5881 PathNotFound,
5882};
5883const B = error{
5884 OutOfMemory,
5885
5886 /// B doc comment
5887 PathNotFound,
5888};
58893052
5890const C = A || B;
5891
5892fn foo() C!void {
5893 return error.NotDir;
5894}
5895
5896test "merge error sets" {
5897 if (foo()) {
5898 @panic("unexpected");
5899 } else |err| switch (err) {
5900 error.OutOfMemory => @panic("unexpected"),
5901 error.PathNotFound => @panic("unexpected"),
5902 error.NotDir => {},
5903 }
5904}
5905 {#code_end#}
5906 {#header_close#}3053 {#header_close#}
5907 {#header_open|Inferred Error Sets#}3054 {#header_open|Inferred Error Sets#}
5908 <p>3055 <p>
5909 Because many functions in Zig return a possible error, Zig supports inferring the error set.3056 Because many functions in Zig return a possible error, Zig supports inferring the error set.
5910 To infer the error set for a function, prepend the {#syntax#}!{#endsyntax#} operator to the function’s return type, like {#syntax#}!T{#endsyntax#}:3057 To infer the error set for a function, prepend the {#syntax#}!{#endsyntax#} operator to the function’s return type, like {#syntax#}!T{#endsyntax#}:
5911 </p>3058 </p>
5912 {#code_begin|test|test_inferred_error_sets#}3059 {#code|test_inferred_error_sets.zig#}
5913// With an inferred error set
5914pub fn add_inferred(comptime T: type, a: T, b: T) !T {
5915 const ov = @addWithOverflow(a, b);
5916 if (ov[1] != 0) return error.Overflow;
5917 return ov[0];
5918}
5919
5920// With an explicit error set
5921pub fn add_explicit(comptime T: type, a: T, b: T) Error!T {
5922 const ov = @addWithOverflow(a, b);
5923 if (ov[1] != 0) return error.Overflow;
5924 return ov[0];
5925}
5926
5927const Error = error {
5928 Overflow,
5929};
5930
5931const std = @import("std");
59323060
5933test "inferred error set" {
5934 if (add_inferred(u8, 255, 1)) |_| unreachable else |err| switch (err) {
5935 error.Overflow => {}, // ok
5936 }
5937}
5938 {#code_end#}
5939 <p>3061 <p>
5940 When a function has an inferred error set, that function becomes generic and thus it becomes3062 When a function has an inferred error set, that function becomes generic and thus it becomes
5941 trickier to do certain things with it, such as obtain a function pointer, or have an error3063 trickier to do certain things with it, such as obtain a function pointer, or have an error
5942 set that is consistent across different build targets. Additionally, inferred error sets3064 set that is consistent across different build targets. Additionally, inferred error sets
5943 are incompatible with recursion.3065 are incompatible with recursion.
5944 </p>3066 </p>
5945 <p>3067 <p>
5946 In these situations, it is recommended to use an explicit error set. You can generally start3068 In these situations, it is recommended to use an explicit error set. You can generally start
5947 with an empty error set and let compile errors guide you toward completing the set.3069 with an empty error set and let compile errors guide you toward completing the set.
5948 </p>3070 </p>
5949 <p>3071 <p>
5950 These limitations may be overcome in a future version of Zig.3072 These limitations may be overcome in a future version of Zig.
5951 </p>3073 </p>
5952 {#header_close#}3074 {#header_close#}
5953 {#header_close#}3075 {#header_close#}
5954 {#header_open|Error Return Traces#}3076 {#header_open|Error Return Traces#}
5955 <p>3077 <p>
5956 Error Return Traces show all the points in the code that an error was returned to the calling function. This makes it practical to use {#link|try#} everywhere and then still be able to know what happened if an error ends up bubbling all the way out of your application.3078 Error Return Traces show all the points in the code that an error was returned to the calling function. This makes it practical to use {#link|try#} everywhere and then still be able to know what happened if an error ends up bubbling all the way out of your application.
5957 </p>3079 </p>
5958 {#code_begin|exe_err|error_return_trace#}3080 {#code|error_return_trace.zig#}
5959pub fn main() !void {
5960 try foo(12);
5961}
5962
5963fn foo(x: i32) !void {
5964 if (x >= 5) {
5965 try bar();
5966 } else {
5967 try bang2();
5968 }
5969}
5970
5971fn bar() !void {
5972 if (baz()) {
5973 try quux();
5974 } else |err| switch (err) {
5975 error.FileNotFound => try hello(),
5976 }
5977}
5978
5979fn baz() !void {
5980 try bang1();
5981}
5982
5983fn quux() !void {
5984 try bang2();
5985}
5986
5987fn hello() !void {
5988 try bang2();
5989}
5990
5991fn bang1() !void {
5992 return error.FileNotFound;
5993}
59943081
5995fn bang2() !void {
5996 return error.PermissionDenied;
5997}
5998 {#code_end#}
5999 <p>3082 <p>
6000 Look closely at this example. This is no stack trace.3083 Look closely at this example. This is no stack trace.
6001 </p>3084 </p>
...@@ -6004,47 +3087,8 @@ fn bang2() !void {...@@ -6004,47 +3087,8 @@ fn bang2() !void {
6004 but the original error that started this whole thing was {#syntax#}FileNotFound{#endsyntax#}. In the {#syntax#}bar{#endsyntax#} function, the code handles the original error code,3087 but the original error that started this whole thing was {#syntax#}FileNotFound{#endsyntax#}. In the {#syntax#}bar{#endsyntax#} function, the code handles the original error code,
6005 and then returns another one, from the switch statement. Error Return Traces make this clear, whereas a stack trace would look like this:3088 and then returns another one, from the switch statement. Error Return Traces make this clear, whereas a stack trace would look like this:
6006 </p>3089 </p>
6007 {#code_begin|exe_err|stack_trace#}3090 {#code|stack_trace.zig#}
6008pub fn main() void {
6009 foo(12);
6010}
6011
6012fn foo(x: i32) void {
6013 if (x >= 5) {
6014 bar();
6015 } else {
6016 bang2();
6017 }
6018}
6019
6020fn bar() void {
6021 if (baz()) {
6022 quux();
6023 } else {
6024 hello();
6025 }
6026}
6027
6028fn baz() bool {
6029 return bang1();
6030}
6031
6032fn quux() void {
6033 bang2();
6034}
6035
6036fn hello() void {
6037 bang2();
6038}
60393091
6040fn bang1() bool {
6041 return false;
6042}
6043
6044fn bang2() void {
6045 @panic("PermissionDenied");
6046}
6047 {#code_end#}
6048 <p>3092 <p>
6049 Here, the stack trace does not explain how the control3093 Here, the stack trace does not explain how the control
6050 flow in {#syntax#}bar{#endsyntax#} got to the {#syntax#}hello(){#endsyntax#} call.3094 flow in {#syntax#}bar{#endsyntax#} got to the {#syntax#}hello(){#endsyntax#} call.
...@@ -6126,13 +3170,8 @@ fn __zig_return_error(stack_trace: *StackTrace) void {...@@ -6126,13 +3170,8 @@ fn __zig_return_error(stack_trace: *StackTrace) void {
6126 The question mark symbolizes the optional type. You can convert a type to an optional3170 The question mark symbolizes the optional type. You can convert a type to an optional
6127 type by putting a question mark in front of it, like this:3171 type by putting a question mark in front of it, like this:
6128 </p>3172 </p>
6129 {#code_begin|syntax|optional_integer#}3173 {#code|optional_integer.zig#}
6130// normal integer
6131const normal_int: i32 = 1234;
61323174
6133// optional integer
6134const optional_int: ?i32 = 5678;
6135 {#code_end#}
6136 <p>3175 <p>
6137 Now the variable {#syntax#}optional_int{#endsyntax#} could be an {#syntax#}i32{#endsyntax#}, or {#syntax#}null{#endsyntax#}.3176 Now the variable {#syntax#}optional_int{#endsyntax#} could be an {#syntax#}i32{#endsyntax#}, or {#syntax#}null{#endsyntax#}.
6138 </p>3177 </p>
...@@ -6198,20 +3237,8 @@ void do_a_thing(struct Foo *foo) {...@@ -6198,20 +3237,8 @@ void do_a_thing(struct Foo *foo) {
6198 <p>3237 <p>
6199 In Zig you can accomplish the same thing:3238 In Zig you can accomplish the same thing:
6200 </p>3239 </p>
6201 {#code_begin|syntax|checking_null_in_zig#}3240 {#code|checking_null_in_zig.zig#}
6202const Foo = struct{};
6203fn doSomethingWithFoo(foo: *Foo) void { _ = foo; }
6204
6205fn doAThing(optional_foo: ?*Foo) void {
6206 // do some stuff
6207
6208 if (optional_foo) |foo| {
6209 doSomethingWithFoo(foo);
6210 }
62113241
6212 // do some stuff
6213}
6214 {#code_end#}
6215 <p>3242 <p>
6216 Once again, the notable thing here is that inside the if block,3243 Once again, the notable thing here is that inside the if block,
6217 {#syntax#}foo{#endsyntax#} is no longer an optional pointer, it is a pointer, which3244 {#syntax#}foo{#endsyntax#} is no longer an optional pointer, it is a pointer, which
...@@ -6227,51 +3254,22 @@ fn doAThing(optional_foo: ?*Foo) void {...@@ -6227,51 +3254,22 @@ fn doAThing(optional_foo: ?*Foo) void {
6227 {#header_open|Optional Type#}3254 {#header_open|Optional Type#}
6228 <p>An optional is created by putting {#syntax#}?{#endsyntax#} in front of a type. You can use compile-time3255 <p>An optional is created by putting {#syntax#}?{#endsyntax#} in front of a type. You can use compile-time
6229 reflection to access the child type of an optional:</p>3256 reflection to access the child type of an optional:</p>
6230 {#code_begin|test|test_optional_type#}3257 {#code|test_optional_type.zig#}
6231const expect = @import("std").testing.expect;
6232
6233test "optional type" {
6234 // Declare an optional and coerce from null:
6235 var foo: ?i32 = null;
62363258
6237 // Coerce from child type of an optional
6238 foo = 1234;
6239
6240 // Use compile-time reflection to access the child type of the optional:
6241 try comptime expect(@typeInfo(@TypeOf(foo)).Optional.child == i32);
6242}
6243 {#code_end#}
6244 {#header_close#}3259 {#header_close#}
6245 {#header_open|null#}3260 {#header_open|null#}
6246 <p>3261 <p>
6247 Just like {#link|undefined#}, {#syntax#}null{#endsyntax#} has its own type, and the only way to use it is to3262 Just like {#link|undefined#}, {#syntax#}null{#endsyntax#} has its own type, and the only way to use it is to
6248 cast it to a different type:3263 cast it to a different type:
6249 </p>3264 </p>
6250 {#code_begin|syntax|null#}3265 {#code|null.zig#}
6251const optional_value: ?i32 = null;3266
6252 {#code_end#}
6253 {#header_close#}3267 {#header_close#}
6254 {#header_open|Optional Pointers#}3268 {#header_open|Optional Pointers#}
6255 <p>An optional pointer is guaranteed to be the same size as a pointer. The {#syntax#}null{#endsyntax#} of3269 <p>An optional pointer is guaranteed to be the same size as a pointer. The {#syntax#}null{#endsyntax#} of
6256 the optional is guaranteed to be address 0.</p>3270 the optional is guaranteed to be address 0.</p>
6257 {#code_begin|test|test_optional_pointer#}3271 {#code|test_optional_pointer.zig#}
6258const expect = @import("std").testing.expect;
6259
6260test "optional pointers" {
6261 // Pointers cannot be null. If you want a null pointer, use the optional
6262 // prefix `?` to make the pointer type optional.
6263 var ptr: ?*i32 = null;
6264
6265 var x: i32 = 1;
6266 ptr = &x;
6267
6268 try expect(ptr.?.* == 1);
62693272
6270 // Optional pointers are the same size as normal pointers, because pointer
6271 // value 0 is used as the null value.
6272 try expect(@sizeOf(?*i32) == @sizeOf(*i32));
6273}
6274 {#code_end#}
6275 {#header_close#}3273 {#header_close#}
62763274
6277 {#see_also|while with Optionals|if with Optionals#}3275 {#see_also|while with Optionals|if with Optionals#}
...@@ -6288,28 +3286,8 @@ test "optional pointers" {...@@ -6288,28 +3286,8 @@ test "optional pointers" {
6288 <p>3286 <p>
6289 Type coercion occurs when one type is expected, but different type is provided:3287 Type coercion occurs when one type is expected, but different type is provided:
6290 </p>3288 </p>
6291 {#code_begin|test|test_type_coercion#}3289 {#code|test_type_coercion.zig#}
6292test "type coercion - variable declaration" {
6293 const a: u8 = 1;
6294 const b: u16 = a;
6295 _ = b;
6296}
6297
6298test "type coercion - function call" {
6299 const a: u8 = 1;
6300 foo(a);
6301}
63023290
6303fn foo(b: u16) void {
6304 _ = b;
6305}
6306
6307test "type coercion - @as builtin" {
6308 const a: u8 = 1;
6309 const b = @as(u16, a);
6310 _ = b;
6311}
6312 {#code_end#}
6313 <p>3291 <p>
6314 Type coercions are only allowed when it is completely unambiguous how to get from one type to another,3292 Type coercions are only allowed when it is completely unambiguous how to get from one type to another,
6315 and the transformation is guaranteed to be safe. There is one exception, which is {#link|C Pointers#}.3293 and the transformation is guaranteed to be safe. There is one exception, which is {#link|C Pointers#}.
...@@ -6328,65 +3306,21 @@ test "type coercion - @as builtin" {...@@ -6328,65 +3306,21 @@ test "type coercion - @as builtin" {
6328 <p>3306 <p>
6329 These casts are no-ops at runtime since the value representation does not change.3307 These casts are no-ops at runtime since the value representation does not change.
6330 </p>3308 </p>
6331 {#code_begin|test|test_no_op_casts#}3309 {#code|test_no_op_casts.zig#}
6332test "type coercion - const qualification" {
6333 var a: i32 = 1;
6334 const b: *i32 = &a;
6335 foo(b);
6336}
63373310
6338fn foo(_: *const i32) void {}
6339 {#code_end#}
6340 <p>3311 <p>
6341 In addition, pointers coerce to const optional pointers:3312 In addition, pointers coerce to const optional pointers:
6342 </p>3313 </p>
6343 {#code_begin|test|test_pointer_coerce_const_optional#}3314 {#code|test_pointer_coerce_const_optional.zig#}
6344const std = @import("std");
6345const expect = std.testing.expect;
6346const mem = std.mem;
63473315
6348test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
6349 const window_name = [1][*]const u8{"window name"};
6350 const x: [*]const ?[*]const u8 = &window_name;
6351 try expect(mem.eql(u8, std.mem.sliceTo(@as([*:0]const u8, @ptrCast(x[0].?)), 0), "window name"));
6352}
6353 {#code_end#}
6354 {#header_close#}3316 {#header_close#}
6355 {#header_open|Type Coercion: Integer and Float Widening#}3317 {#header_open|Type Coercion: Integer and Float Widening#}
6356 <p>3318 <p>
6357 {#link|Integers#} coerce to integer types which can represent every value of the old type, and likewise3319 {#link|Integers#} coerce to integer types which can represent every value of the old type, and likewise
6358 {#link|Floats#} coerce to float types which can represent every value of the old type.3320 {#link|Floats#} coerce to float types which can represent every value of the old type.
6359 </p>3321 </p>
6360 {#code_begin|test|test_integer_widening#}3322 {#code|test_integer_widening.zig#}
6361const std = @import("std");
6362const builtin = @import("builtin");
6363const expect = std.testing.expect;
6364const mem = std.mem;
6365
6366test "integer widening" {
6367 const a: u8 = 250;
6368 const b: u16 = a;
6369 const c: u32 = b;
6370 const d: u64 = c;
6371 const e: u64 = d;
6372 const f: u128 = e;
6373 try expect(f == a);
6374}
6375
6376test "implicit unsigned integer to signed integer" {
6377 const a: u8 = 250;
6378 const b: i16 = a;
6379 try expect(b == 250);
6380}
63813323
6382test "float widening" {
6383 const a: f16 = 12.34;
6384 const b: f32 = a;
6385 const c: f64 = b;
6386 const d: f128 = c;
6387 try expect(d == a);
6388}
6389 {#code_end#}
6390 {#header_close#}3324 {#header_close#}
6391 {#header_open|Type Coercion: Float to Int#}3325 {#header_open|Type Coercion: Float to Int#}
6392 <p>3326 <p>
...@@ -6397,203 +3331,45 @@ test "float widening" {...@@ -6397,203 +3331,45 @@ test "float widening" {
6397 <li>Cast {#syntax#}54.0{#endsyntax#} to {#syntax#}comptime_int{#endsyntax#} resulting in {#syntax#}@as(comptime_int, 10){#endsyntax#}, which is casted to {#syntax#}@as(f32, 10){#endsyntax#}</li>3331 <li>Cast {#syntax#}54.0{#endsyntax#} to {#syntax#}comptime_int{#endsyntax#} resulting in {#syntax#}@as(comptime_int, 10){#endsyntax#}, which is casted to {#syntax#}@as(f32, 10){#endsyntax#}</li>
6398 <li>Cast {#syntax#}5{#endsyntax#} to {#syntax#}comptime_float{#endsyntax#} resulting in {#syntax#}@as(comptime_float, 10.8){#endsyntax#}, which is casted to {#syntax#}@as(f32, 10.8){#endsyntax#}</li>3332 <li>Cast {#syntax#}5{#endsyntax#} to {#syntax#}comptime_float{#endsyntax#} resulting in {#syntax#}@as(comptime_float, 10.8){#endsyntax#}, which is casted to {#syntax#}@as(f32, 10.8){#endsyntax#}</li>
6399 </ul>3333 </ul>
6400 {#code_begin|test_err|test_ambiguous_coercion#}3334 {#code|test_ambiguous_coercion.zig#}
6401// Compile time coercion of float to int3335
6402test "implicit cast to comptime_int" {
6403 const f: f32 = 54.0 / 5;
6404 _ = f;
6405}
6406 {#code_end#}
6407 {#header_close#}3336 {#header_close#}
6408 {#header_open|Type Coercion: Slices, Arrays and Pointers#}3337 {#header_open|Type Coercion: Slices, Arrays and Pointers#}
6409 {#code_begin|test|test_coerce_slices_arrays_and_pointers#}3338 {#code|test_coerce_slices_arrays_and_pointers.zig#}
6410const std = @import("std");
6411const expect = std.testing.expect;
6412
6413// You can assign constant pointers to arrays to a slice with
6414// const modifier on the element type. Useful in particular for
6415// String literals.
6416test "*const [N]T to []const T" {
6417 const x1: []const u8 = "hello";
6418 const x2: []const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
6419 try expect(std.mem.eql(u8, x1, x2));
6420
6421 const y: []const f32 = &[2]f32{ 1.2, 3.4 };
6422 try expect(y[0] == 1.2);
6423}
6424
6425// Likewise, it works when the destination type is an error union.
6426test "*const [N]T to E![]const T" {
6427 const x1: anyerror![]const u8 = "hello";
6428 const x2: anyerror![]const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
6429 try expect(std.mem.eql(u8, try x1, try x2));
6430
6431 const y: anyerror![]const f32 = &[2]f32{ 1.2, 3.4 };
6432 try expect((try y)[0] == 1.2);
6433}
6434
6435// Likewise, it works when the destination type is an optional.
6436test "*const [N]T to ?[]const T" {
6437 const x1: ?[]const u8 = "hello";
6438 const x2: ?[]const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
6439 try expect(std.mem.eql(u8, x1.?, x2.?));
6440
6441 const y: ?[]const f32 = &[2]f32{ 1.2, 3.4 };
6442 try expect(y.?[0] == 1.2);
6443}
6444
6445// In this cast, the array length becomes the slice length.
6446test "*[N]T to []T" {
6447 var buf: [5]u8 = "hello".*;
6448 const x: []u8 = &buf;
6449 try expect(std.mem.eql(u8, x, "hello"));
64503339
6451 const buf2 = [2]f32{ 1.2, 3.4 };
6452 const x2: []const f32 = &buf2;
6453 try expect(std.mem.eql(f32, x2, &[2]f32{ 1.2, 3.4 }));
6454}
6455
6456// Single-item pointers to arrays can be coerced to many-item pointers.
6457test "*[N]T to [*]T" {
6458 var buf: [5]u8 = "hello".*;
6459 const x: [*]u8 = &buf;
6460 try expect(x[4] == 'o');
6461 // x[5] would be an uncaught out of bounds pointer dereference!
6462}
6463
6464// Likewise, it works when the destination type is an optional.
6465test "*[N]T to ?[*]T" {
6466 var buf: [5]u8 = "hello".*;
6467 const x: ?[*]u8 = &buf;
6468 try expect(x.?[4] == 'o');
6469}
6470
6471// Single-item pointers can be cast to len-1 single-item arrays.
6472test "*T to *[1]T" {
6473 var x: i32 = 1234;
6474 const y: *[1]i32 = &x;
6475 const z: [*]i32 = y;
6476 try expect(z[0] == 1234);
6477}
6478 {#code_end#}
6479 {#see_also|C Pointers#}3340 {#see_also|C Pointers#}
6480 {#header_close#}3341 {#header_close#}
6481 {#header_open|Type Coercion: Optionals#}3342 {#header_open|Type Coercion: Optionals#}
6482 <p>3343 <p>
6483 The payload type of {#link|Optionals#}, as well as {#link|null#}, coerce to the optional type.3344 The payload type of {#link|Optionals#}, as well as {#link|null#}, coerce to the optional type.
6484 </p>3345 </p>
6485 {#code_begin|test|test_coerce_optionals#}3346 {#code|test_coerce_optionals.zig#}
6486const std = @import("std");
6487const expect = std.testing.expect;
6488
6489test "coerce to optionals" {
6490 const x: ?i32 = 1234;
6491 const y: ?i32 = null;
64923347
6493 try expect(x.? == 1234);
6494 try expect(y == null);
6495}
6496 {#code_end#}
6497 <p>Optionals work nested inside the {#link|Error Union Type#}, too:</p>3348 <p>Optionals work nested inside the {#link|Error Union Type#}, too:</p>
6498 {#code_begin|test|test_coerce_optional_wrapped_error_union#}3349 {#code|test_coerce_optional_wrapped_error_union.zig#}
6499const std = @import("std");
6500const expect = std.testing.expect;
6501
6502test "coerce to optionals wrapped in error union" {
6503 const x: anyerror!?i32 = 1234;
6504 const y: anyerror!?i32 = null;
65053350
6506 try expect((try x).? == 1234);
6507 try expect((try y) == null);
6508}
6509 {#code_end#}
6510 {#header_close#}3351 {#header_close#}
6511 {#header_open|Type Coercion: Error Unions#}3352 {#header_open|Type Coercion: Error Unions#}
6512 <p>The payload type of an {#link|Error Union Type#} as well as the {#link|Error Set Type#}3353 <p>The payload type of an {#link|Error Union Type#} as well as the {#link|Error Set Type#}
6513 coerce to the error union type:3354 coerce to the error union type:
6514 </p>3355 </p>
6515 {#code_begin|test|test_coerce_to_error_union#}3356 {#code|test_coerce_to_error_union.zig#}
6516const std = @import("std");
6517const expect = std.testing.expect;
6518
6519test "coercion to error unions" {
6520 const x: anyerror!i32 = 1234;
6521 const y: anyerror!i32 = error.Failure;
65223357
6523 try expect((try x) == 1234);
6524 try std.testing.expectError(error.Failure, y);
6525}
6526 {#code_end#}
6527 {#header_close#}3358 {#header_close#}
6528 {#header_open|Type Coercion: Compile-Time Known Numbers#}3359 {#header_open|Type Coercion: Compile-Time Known Numbers#}
6529 <p>When a number is {#link|comptime#}-known to be representable in the destination type,3360 <p>When a number is {#link|comptime#}-known to be representable in the destination type,
6530 it may be coerced:3361 it may be coerced:
6531 </p>3362 </p>
6532 {#code_begin|test|test_coerce_large_to_small#}3363 {#code|test_coerce_large_to_small.zig#}
6533const std = @import("std");
6534const expect = std.testing.expect;
65353364
6536test "coercing large integer type to smaller one when value is comptime-known to fit" {
6537 const x: u64 = 255;
6538 const y: u8 = x;
6539 try expect(y == 255);
6540}
6541 {#code_end#}
6542 {#header_close#}3365 {#header_close#}
6543 {#header_open|Type Coercion: Unions and Enums#}3366 {#header_open|Type Coercion: Unions and Enums#}
6544 <p>Tagged unions can be coerced to enums, and enums can be coerced to tagged unions3367 <p>Tagged unions can be coerced to enums, and enums can be coerced to tagged unions
6545 when they are {#link|comptime#}-known to be a field of the union that has only one possible value, such as3368 when they are {#link|comptime#}-known to be a field of the union that has only one possible value, such as
6546 {#link|void#}:3369 {#link|void#}:
6547 </p>3370 </p>
6548 {#code_begin|test|test_coerce_unions_enums#}3371 {#code|test_coerce_unions_enums.zig#}
6549const std = @import("std");
6550const expect = std.testing.expect;
6551
6552const E = enum {
6553 one,
6554 two,
6555 three,
6556};
6557
6558const U = union(E) {
6559 one: i32,
6560 two: f32,
6561 three,
6562};
6563
6564const U2 = union(enum) {
6565 a: void,
6566 b: f32,
6567
6568 fn tag(self: U2) usize {
6569 switch (self) {
6570 .a => return 1,
6571 .b => return 2,
6572 }
6573 }
6574};
6575
6576test "coercion between unions and enums" {
6577 const u = U{ .two = 12.34 };
6578 const e: E = u; // coerce union to enum
6579 try expect(e == E.two);
65803372
6581 const three = E.three;
6582 const u_2: U = three; // coerce enum to union
6583 try expect(u_2 == E.three);
6584
6585 const u_3: U = .three; // coerce enum literal to union
6586 try expect(u_3 == E.three);
6587
6588 const u_4: U2 = .a; // coerce enum literal to union with inferred enum tag type.
6589 try expect(u_4.tag() == 1);
6590
6591 // The following example is invalid.
6592 // error: coercion from enum '@TypeOf(.enum_literal)' to union 'test_coerce_unions_enum.U2' must initialize 'f32' field 'b'
6593 //var u_5: U2 = .b;
6594 //try expect(u_5.tag() == 2);
6595}
6596 {#code_end#}
6597 {#see_also|union|enum#}3373 {#see_also|union|enum#}
6598 {#header_close#}3374 {#header_close#}
6599 {#header_open|Type Coercion: undefined#}3375 {#header_open|Type Coercion: undefined#}
...@@ -6602,17 +3378,8 @@ test "coercion between unions and enums" {...@@ -6602,17 +3378,8 @@ test "coercion between unions and enums" {
66023378
6603 {#header_open|Type Coercion: Tuples to Arrays#}3379 {#header_open|Type Coercion: Tuples to Arrays#}
6604 <p>{#link|Tuples#} can be coerced to arrays, if all of the fields have the same type.</p>3380 <p>{#link|Tuples#} can be coerced to arrays, if all of the fields have the same type.</p>
6605 {#code_begin|test|test_coerce_tuples_arrays#}3381 {#code|test_coerce_tuples_arrays.zig#}
6606const std = @import("std");3382
6607const expect = std.testing.expect;
6608
6609const Tuple = struct{ u8, u8 };
6610test "coercion from homogenous tuple to array" {
6611 const tuple: Tuple = .{5, 6};
6612 const array: [2]u8 = tuple;
6613 _ = array;
6614}
6615 {#code_end#}
6616 {#header_close#}3383 {#header_close#}
6617 {#header_close#}3384 {#header_close#}
66183385
...@@ -6657,126 +3424,8 @@ test "coercion from homogenous tuple to array" {...@@ -6657,126 +3424,8 @@ test "coercion from homogenous tuple to array" {
6657 This kind of type resolution chooses a type that all peer types can coerce into. Here are3424 This kind of type resolution chooses a type that all peer types can coerce into. Here are
6658 some examples:3425 some examples:
6659 </p>3426 </p>
6660 {#code_begin|test|test_peer_type_resolution#}3427 {#code|test_peer_type_resolution.zig#}
6661const std = @import("std");
6662const expect = std.testing.expect;
6663const mem = std.mem;
6664
6665test "peer resolve int widening" {
6666 const a: i8 = 12;
6667 const b: i16 = 34;
6668 const c = a + b;
6669 try expect(c == 46);
6670 try expect(@TypeOf(c) == i16);
6671}
6672
6673test "peer resolve arrays of different size to const slice" {
6674 try expect(mem.eql(u8, boolToStr(true), "true"));
6675 try expect(mem.eql(u8, boolToStr(false), "false"));
6676 try comptime expect(mem.eql(u8, boolToStr(true), "true"));
6677 try comptime expect(mem.eql(u8, boolToStr(false), "false"));
6678}
6679fn boolToStr(b: bool) []const u8 {
6680 return if (b) "true" else "false";
6681}
6682
6683test "peer resolve array and const slice" {
6684 try testPeerResolveArrayConstSlice(true);
6685 try comptime testPeerResolveArrayConstSlice(true);
6686}
6687fn testPeerResolveArrayConstSlice(b: bool) !void {
6688 const value1 = if (b) "aoeu" else @as([]const u8, "zz");
6689 const value2 = if (b) @as([]const u8, "zz") else "aoeu";
6690 try expect(mem.eql(u8, value1, "aoeu"));
6691 try expect(mem.eql(u8, value2, "zz"));
6692}
6693
6694test "peer type resolution: ?T and T" {
6695 try expect(peerTypeTAndOptionalT(true, false).? == 0);
6696 try expect(peerTypeTAndOptionalT(false, false).? == 3);
6697 comptime {
6698 try expect(peerTypeTAndOptionalT(true, false).? == 0);
6699 try expect(peerTypeTAndOptionalT(false, false).? == 3);
6700 }
6701}
6702fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize {
6703 if (c) {
6704 return if (b) null else @as(usize, 0);
6705 }
6706
6707 return @as(usize, 3);
6708}
6709
6710test "peer type resolution: *[0]u8 and []const u8" {
6711 try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
6712 try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
6713 comptime {
6714 try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
6715 try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
6716 }
6717}
6718fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {
6719 if (a) {
6720 return &[_]u8{};
6721 }
6722
6723 return slice[0..1];
6724}
6725test "peer type resolution: *[0]u8, []const u8, and anyerror![]u8" {
6726 {
6727 var data = "hi".*;
6728 const slice = data[0..];
6729 try expect((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0);
6730 try expect((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
6731 }
6732 comptime {
6733 var data = "hi".*;
6734 const slice = data[0..];
6735 try expect((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0);
6736 try expect((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
6737 }
6738}
6739fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 {
6740 if (a) {
6741 return &[_]u8{};
6742 }
6743
6744 return slice[0..1];
6745}
6746
6747test "peer type resolution: *const T and ?*T" {
6748 const a: *const usize = @ptrFromInt(0x123456780);
6749 const b: ?*usize = @ptrFromInt(0x123456780);
6750 try expect(a == b);
6751 try expect(b == a);
6752}
67533428
6754test "peer type resolution: error union switch" {
6755 // The non-error and error cases are only peers if the error case is just a switch expression;
6756 // the pattern `if (x) {...} else |err| blk: { switch (err) {...} }` does not consider the
6757 // non-error and error case to be peers.
6758 var a: error{ A, B, C }!u32 = 0;
6759 _ = &a;
6760 const b = if (a) |x|
6761 x + 3
6762 else |err| switch (err) {
6763 error.A => 0,
6764 error.B => 1,
6765 error.C => null,
6766 };
6767 try expect(@TypeOf(b) == ?u32);
6768
6769 // The non-error and error cases are only peers if the error case is just a switch expression;
6770 // the pattern `x catch |err| blk: { switch (err) {...} }` does not consider the unwrapped `x`
6771 // and error case to be peers.
6772 const c = a catch |err| switch (err) {
6773 error.A => 0,
6774 error.B => 1,
6775 error.C => null,
6776 };
6777 try expect(@TypeOf(c) == ?u32);
6778}
6779 {#code_end#}
6780 {#header_close#}3429 {#header_close#}
6781 {#header_close#}3430 {#header_close#}
67823431
...@@ -6795,14 +3444,8 @@ test "peer type resolution: error union switch" {...@@ -6795,14 +3444,8 @@ test "peer type resolution: error union switch" {
6795 require 0 bits to represent. Code that makes use of these types is3444 require 0 bits to represent. Code that makes use of these types is
6796 not included in the final generated code:3445 not included in the final generated code:
6797 </p>3446 </p>
6798 {#code_begin|syntax|zero_bit_types#}3447 {#code|zero_bit_types.zig#}
6799export fn entry() void {3448
6800 var x: void = {};
6801 var y: void = {};
6802 x = y;
6803 y = x;
6804}
6805 {#code_end#}
6806 <p>When this turns into machine code, there is no code generated in the3449 <p>When this turns into machine code, there is no code generated in the
6807 body of {#syntax#}entry{#endsyntax#}, even in {#link|Debug#} mode. For example, on x86_64:</p>3450 body of {#syntax#}entry{#endsyntax#}, even in {#link|Debug#} mode. For example, on x86_64:</p>
6808 <pre><code>0000000000000010 &lt;entry&gt;:3451 <pre><code>0000000000000010 &lt;entry&gt;:
...@@ -6819,24 +3462,8 @@ export fn entry() void {...@@ -6819,24 +3462,8 @@ export fn entry() void {
6819 {#syntax#}Map(Key, Value){#endsyntax#}, one can pass {#syntax#}void{#endsyntax#} for the {#syntax#}Value{#endsyntax#}3462 {#syntax#}Map(Key, Value){#endsyntax#}, one can pass {#syntax#}void{#endsyntax#} for the {#syntax#}Value{#endsyntax#}
6820 type to make it into a {#syntax#}Set{#endsyntax#}:3463 type to make it into a {#syntax#}Set{#endsyntax#}:
6821 </p>3464 </p>
6822 {#code_begin|test|test_void_in_hashmap#}3465 {#code|test_void_in_hashmap.zig#}
6823const std = @import("std");
6824const expect = std.testing.expect;
6825
6826test "turn HashMap into a set with void" {
6827 var map = std.AutoHashMap(i32, void).init(std.testing.allocator);
6828 defer map.deinit();
6829
6830 try map.put(1, {});
6831 try map.put(2, {});
68323466
6833 try expect(map.contains(2));
6834 try expect(!map.contains(3));
6835
6836 _ = map.remove(2);
6837 try expect(!map.contains(2));
6838}
6839 {#code_end#}
6840 <p>Note that this is different from using a dummy value for the hash map value.3467 <p>Note that this is different from using a dummy value for the hash map value.
6841 By using {#syntax#}void{#endsyntax#} as the type of the value, the hash map entry type has no value field, and3468 By using {#syntax#}void{#endsyntax#} as the type of the value, the hash map entry type has no value field, and
6842 thus the hash map takes up less space. Further, all the code that deals with storing and loading the3469 thus the hash map takes up less space. Further, all the code that deals with storing and loading the
...@@ -6850,31 +3477,11 @@ test "turn HashMap into a set with void" {...@@ -6850,31 +3477,11 @@ test "turn HashMap into a set with void" {
6850 Expressions of type {#syntax#}void{#endsyntax#} are the only ones whose value can be ignored. For example, ignoring3477 Expressions of type {#syntax#}void{#endsyntax#} are the only ones whose value can be ignored. For example, ignoring
6851 a non-{#syntax#}void{#endsyntax#} expression is a compile error:3478 a non-{#syntax#}void{#endsyntax#} expression is a compile error:
6852 </p>3479 </p>
6853 {#code_begin|test_err|test_expression_ignored|ignored#}3480 {#code|test_expression_ignored.zig#}
6854test "ignoring expression value" {
6855 foo();
6856}
68573481
6858fn foo() i32 {
6859 return 1234;
6860}
6861 {#code_end#}
6862 <p>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#}. </p>3482 <p>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#}. </p>
6863 {#code_begin|test|test_void_ignored#}3483 {#code|test_void_ignored.zig#}
6864test "void is ignored" {
6865 returnsVoid();
6866}
6867
6868test "explicitly ignoring expression value" {
6869 _ = foo();
6870}
6871
6872fn returnsVoid() void {}
68733484
6874fn foo() i32 {
6875 return 1234;
6876}
6877 {#code_end#}
6878 {#header_close#}3485 {#header_close#}
6879 {#header_close#}3486 {#header_close#}
68803487
...@@ -6914,18 +3521,8 @@ fn foo() i32 {...@@ -6914,18 +3521,8 @@ fn foo() i32 {
6914 <p>3521 <p>
6915 We can break down the result types for each component of a simple expression as follows:3522 We can break down the result types for each component of a simple expression as follows:
6916 </p>3523 </p>
6917 {#code_begin|test|result_type_propagation#}3524 {#code|result_type_propagation.zig#}
6918const expectEqual = @import("std").testing.expectEqual;3525
6919test "result type propagates through struct initializer" {
6920 const S = struct { x: u32 };
6921 const val: u64 = 123;
6922 const s: S = .{ .x = @intCast(val) };
6923 // .{ .x = @intCast(val) } has result type `S` due to the type annotation
6924 // @intCast(val) has result type `u32` due to the type of the field `S.x`
6925 // val has no result type, as it is permitted to be any integer type
6926 try expectEqual(@as(u32, 123), s.x);
6927}
6928 {#code_end#}
6929 <p>3526 <p>
6930 This result type information is useful for the aforementioned cast builtins, as well as to avoid3527 This result type information is useful for the aforementioned cast builtins, as well as to avoid
6931 the construction of pre-coercion values, and to avoid the need for explicit type coercions in some3528 the construction of pre-coercion values, and to avoid the need for explicit type coercions in some
...@@ -7045,19 +3642,8 @@ test "result type propagates through struct initializer" {...@@ -7045,19 +3642,8 @@ test "result type propagates through struct initializer" {
7045 expression depends on the previous value of the aggregate. The easiest way to demonstrate this is by3642 expression depends on the previous value of the aggregate. The easiest way to demonstrate this is by
7046 attempting to swap fields of a struct or array - the following logic looks sound, but in fact is not:3643 attempting to swap fields of a struct or array - the following logic looks sound, but in fact is not:
7047 </p>3644 </p>
7048 {#code_begin|test_err|result_location_interfering_with_swap#}3645 {#code|result_location_interfering_with_swap.zig#}
7049const expect = @import("std").testing.expect;3646
7050test "attempt to swap array elements with array initializer" {
7051 var arr: [2]u32 = .{ 1, 2 };
7052 arr = .{ arr[1], arr[0] };
7053 // The previous line is equivalent to the following two lines:
7054 // arr[0] = arr[1];
7055 // arr[1] = arr[0];
7056 // So this fails!
7057 try expect(arr[0] == 2); // succeeds
7058 try expect(arr[1] == 1); // fails
7059}
7060 {#code_end#}
7061 <p>3647 <p>
7062 The following table details how some common expressions propagate result locations, where3648 The following table details how some common expressions propagate result locations, where
7063 {#syntax#}x{#endsyntax#} and {#syntax#}y{#endsyntax#} are arbitrary sub-expressions. Note that3649 {#syntax#}x{#endsyntax#} and {#syntax#}y{#endsyntax#} are arbitrary sub-expressions. Note that
...@@ -7151,14 +3737,8 @@ test "attempt to swap array elements with array initializer" {...@@ -7151,14 +3737,8 @@ test "attempt to swap array elements with array initializer" {
7151 declarations of the operand, which must be a {#link|struct#}, {#link|union#}, {#link|enum#},3737 declarations of the operand, which must be a {#link|struct#}, {#link|union#}, {#link|enum#},
7152 or {#link|opaque#}, into the namespace:3738 or {#link|opaque#}, into the namespace:
7153 </p>3739 </p>
7154 {#code_begin|test|test_usingnamespace#}3740 {#code|test_usingnamespace.zig#}
7155test "using std namespace" {3741
7156 const S = struct {
7157 usingnamespace @import("std");
7158 };
7159 try S.testing.expect(true);
7160}
7161 {#code_end#}
7162 <p>3742 <p>
7163 {#syntax#}usingnamespace{#endsyntax#} has an important use case when organizing the public3743 {#syntax#}usingnamespace{#endsyntax#} has an important use case when organizing the public
7164 API of a file or package. For example, one might have <code class="file">c.zig</code> with all of the3744 API of a file or package. For example, one might have <code class="file">c.zig</code> with all of the
...@@ -7193,17 +3773,8 @@ pub usingnamespace @cImport({...@@ -7193,17 +3773,8 @@ pub usingnamespace @cImport({
7193 <p>3773 <p>
7194 Compile-time parameters is how Zig implements generics. It is compile-time duck typing.3774 Compile-time parameters is how Zig implements generics. It is compile-time duck typing.
7195 </p>3775 </p>
7196 {#code_begin|syntax|compile-time_duck_typing#}3776 {#code|compile-time_duck_typing.zig#}
7197fn max(comptime T: type, a: T, b: T) T {3777
7198 return if (a > b) a else b;
7199}
7200fn gimmeTheBiggerFloat(a: f32, b: f32) f32 {
7201 return max(f32, a, b);
7202}
7203fn gimmeTheBiggerInteger(a: u64, b: u64) u64 {
7204 return max(u64, a, b);
7205}
7206 {#code_end#}
7207 <p>3778 <p>
7208 In Zig, types are first-class citizens. They can be assigned to variables, passed as parameters to functions,3779 In Zig, types are first-class citizens. They can be assigned to variables, passed as parameters to functions,
7209 and returned from functions. However, they can only be used in expressions which are known at <em>compile-time</em>,3780 and returned from functions. However, they can only be used in expressions which are known at <em>compile-time</em>,
...@@ -7219,21 +3790,8 @@ fn gimmeTheBiggerInteger(a: u64, b: u64) u64 {...@@ -7219,21 +3790,8 @@ fn gimmeTheBiggerInteger(a: u64, b: u64) u64 {
7219 <p>3790 <p>
7220 For example, if we were to introduce another function to the above snippet:3791 For example, if we were to introduce another function to the above snippet:
7221 </p>3792 </p>
7222 {#code_begin|test_err|test_unresolved_comptime_value|unable to resolve comptime value#}3793 {#code|test_unresolved_comptime_value.zig#}
7223fn max(comptime T: type, a: T, b: T) T {3794
7224 return if (a > b) a else b;
7225}
7226test "try to pass a runtime type" {
7227 foo(false);
7228}
7229fn foo(condition: bool) void {
7230 const result = max(
7231 if (condition) f32 else u64,
7232 1234,
7233 5678);
7234 _ = result;
7235}
7236 {#code_end#}
7237 <p>3795 <p>
7238 This is an error because the programmer attempted to pass a value only known at run-time3796 This is an error because the programmer attempted to pass a value only known at run-time
7239 to a function which expects a value known at compile-time.3797 to a function which expects a value known at compile-time.
...@@ -7245,33 +3803,15 @@ fn foo(condition: bool) void {...@@ -7245,33 +3803,15 @@ fn foo(condition: bool) void {
7245 <p>3803 <p>
7246 For example:3804 For example:
7247 </p>3805 </p>
7248 {#code_begin|test_err|test_comptime_mismatched_type|operator > not allowed for type 'bool'#}3806 {#code|test_comptime_mismatched_type.zig#}
7249fn max(comptime T: type, a: T, b: T) T {3807
7250 return if (a > b) a else b;
7251}
7252test "try to compare bools" {
7253 _ = max(bool, true, false);
7254}
7255 {#code_end#}
7256 <p>3808 <p>
7257 On the flip side, inside the function definition with the {#syntax#}comptime{#endsyntax#} parameter, the3809 On the flip side, inside the function definition with the {#syntax#}comptime{#endsyntax#} parameter, the
7258 value is known at compile-time. This means that we actually could make this work for the bool type3810 value is known at compile-time. This means that we actually could make this work for the bool type
7259 if we wanted to:3811 if we wanted to:
7260 </p>3812 </p>
7261 {#code_begin|test|test_comptime_max_with_bool#}3813 {#code|test_comptime_max_with_bool.zig#}
7262fn max(comptime T: type, a: T, b: T) T {3814
7263 if (T == bool) {
7264 return a or b;
7265 } else if (a > b) {
7266 return a;
7267 } else {
7268 return b;
7269 }
7270}
7271test "try to compare bools" {
7272 try @import("std").testing.expect(max(bool, false, true) == true);
7273}
7274 {#code_end#}
7275 <p>3815 <p>
7276 This works because Zig implicitly inlines {#syntax#}if{#endsyntax#} expressions when the condition3816 This works because Zig implicitly inlines {#syntax#}if{#endsyntax#} expressions when the condition
7277 is known at compile-time, and the compiler guarantees that it will skip analysis of3817 is known at compile-time, and the compiler guarantees that it will skip analysis of
...@@ -7281,13 +3821,8 @@ test "try to compare bools" {...@@ -7281,13 +3821,8 @@ test "try to compare bools" {
7281 This means that the actual function generated for {#syntax#}max{#endsyntax#} in this situation looks like3821 This means that the actual function generated for {#syntax#}max{#endsyntax#} in this situation looks like
7282 this:3822 this:
7283 </p>3823 </p>
7284 {#code_begin|syntax|compiler_generated_function#}3824 {#code|compiler_generated_function.zig#}
7285fn max(a: bool, b: bool) bool {3825
7286 {
7287 return a or b;
7288 }
7289}
7290 {#code_end#}
7291 <p>3826 <p>
7292 All the code that dealt with compile-time known values is eliminated and we are left with only3827 All the code that dealt with compile-time known values is eliminated and we are left with only
7293 the necessary run-time code to accomplish the task.3828 the necessary run-time code to accomplish the task.
...@@ -7310,40 +3845,8 @@ fn max(a: bool, b: bool) bool {...@@ -7310,40 +3845,8 @@ fn max(a: bool, b: bool) bool {
7310 <p>3845 <p>
7311 For example:3846 For example:
7312 </p>3847 </p>
7313 {#code_begin|test|test_comptime_evaluation#}3848 {#code|test_comptime_evaluation.zig#}
7314const expect = @import("std").testing.expect;
7315
7316const CmdFn = struct {
7317 name: []const u8,
7318 func: fn(i32) i32,
7319};
7320
7321const cmd_fns = [_]CmdFn{
7322 CmdFn {.name = "one", .func = one},
7323 CmdFn {.name = "two", .func = two},
7324 CmdFn {.name = "three", .func = three},
7325};
7326fn one(value: i32) i32 { return value + 1; }
7327fn two(value: i32) i32 { return value + 2; }
7328fn three(value: i32) i32 { return value + 3; }
7329
7330fn performFn(comptime prefix_char: u8, start_value: i32) i32 {
7331 var result: i32 = start_value;
7332 comptime var i = 0;
7333 inline while (i < cmd_fns.len) : (i += 1) {
7334 if (cmd_fns[i].name[0] == prefix_char) {
7335 result = cmd_fns[i].func(result);
7336 }
7337 }
7338 return result;
7339}
73403849
7341test "perform fn" {
7342 try expect(performFn('t', 1) == 6);
7343 try expect(performFn('o', 0) == 1);
7344 try expect(performFn('w', 99) == 99);
7345}
7346 {#code_end#}
7347 <p>3850 <p>
7348 This example is a bit contrived, because the compile-time evaluation component is unnecessary;3851 This example is a bit contrived, because the compile-time evaluation component is unnecessary;
7349 this code would work fine if it was all done at run-time. But it does end up generating3852 this code would work fine if it was all done at run-time. But it does end up generating
...@@ -7391,15 +3894,8 @@ fn performFn(start_value: i32) i32 {...@@ -7391,15 +3894,8 @@ fn performFn(start_value: i32) i32 {
7391 use a {#syntax#}comptime{#endsyntax#} expression to guarantee that the expression will be evaluated at compile-time.3894 use a {#syntax#}comptime{#endsyntax#} expression to guarantee that the expression will be evaluated at compile-time.
7392 If this cannot be accomplished, the compiler will emit an error. For example:3895 If this cannot be accomplished, the compiler will emit an error. For example:
7393 </p>3896 </p>
7394 {#code_begin|test_err|test_comptime_call_extern_function|comptime call of extern function#}3897 {#code|test_comptime_call_extern_function.zig#}
7395extern fn exit() noreturn;
73963898
7397test "foo" {
7398 comptime {
7399 exit();
7400 }
7401}
7402 {#code_end#}
7403 <p>3899 <p>
7404 It doesn't make sense that a program could call {#syntax#}exit(){#endsyntax#} (or any other external function)3900 It doesn't make sense that a program could call {#syntax#}exit(){#endsyntax#} (or any other external function)
7405 at compile-time, so this is a compile error. However, a {#syntax#}comptime{#endsyntax#} expression does much3901 at compile-time, so this is a compile error. However, a {#syntax#}comptime{#endsyntax#} expression does much
...@@ -7424,37 +3920,13 @@ test "foo" {...@@ -7424,37 +3920,13 @@ test "foo" {
7424 <p>3920 <p>
7425 Let's look at an example:3921 Let's look at an example:
7426 </p>3922 </p>
7427 {#code_begin|test|test_fibonacci_recursion#}3923 {#code|test_fibonacci_recursion.zig#}
7428const expect = @import("std").testing.expect;
74293924
7430fn fibonacci(index: u32) u32 {
7431 if (index < 2) return index;
7432 return fibonacci(index - 1) + fibonacci(index - 2);
7433}
7434
7435test "fibonacci" {
7436 // test fibonacci at run-time
7437 try expect(fibonacci(7) == 13);
7438
7439 // test fibonacci at compile-time
7440 try comptime expect(fibonacci(7) == 13);
7441}
7442 {#code_end#}
7443 <p>3925 <p>
7444 Imagine if we had forgotten the base case of the recursive function and tried to run the tests:3926 Imagine if we had forgotten the base case of the recursive function and tried to run the tests:
7445 </p>3927 </p>
7446 {#code_begin|test_err|test_fibonacci_comptime_overflow|overflow of integer type#}3928 {#code|test_fibonacci_comptime_overflow.zig#}
7447const expect = @import("std").testing.expect;
74483929
7449fn fibonacci(index: u32) u32 {
7450 //if (index < 2) return index;
7451 return fibonacci(index - 1) + fibonacci(index - 2);
7452}
7453
7454test "fibonacci" {
7455 try comptime expect(fibonacci(7) == 13);
7456}
7457 {#code_end#}
7458 <p>3930 <p>
7459 The compiler produces an error which is a stack trace from trying to evaluate the3931 The compiler produces an error which is a stack trace from trying to evaluate the
7460 function at compile-time.3932 function at compile-time.
...@@ -7464,18 +3936,8 @@ test "fibonacci" {...@@ -7464,18 +3936,8 @@ test "fibonacci" {
7464 undefined behavior, which is always a compile error if the compiler knows it happened.3936 undefined behavior, which is always a compile error if the compiler knows it happened.
7465 But what would have happened if we used a signed integer?3937 But what would have happened if we used a signed integer?
7466 </p>3938 </p>
7467 {#code_begin|syntax|fibonacci_comptime_infinite_recursion#}3939 {#code|fibonacci_comptime_infinite_recursion.zig#}
7468const assert = @import("std").debug.assert;
7469
7470fn fibonacci(index: i32) i32 {
7471 //if (index < 2) return index;
7472 return fibonacci(index - 1) + fibonacci(index - 2);
7473}
74743940
7475test "fibonacci" {
7476 try comptime assert(fibonacci(7) == 13);
7477}
7478 {#code_end#}
7479 <p>3941 <p>
7480 The compiler is supposed to notice that evaluating this function at3942 The compiler is supposed to notice that evaluating this function at
7481 compile-time took more than 1000 branches, and thus emits an error and3943 compile-time took more than 1000 branches, and thus emits an error and
...@@ -7494,61 +3956,16 @@ test "fibonacci" {...@@ -7494,61 +3956,16 @@ test "fibonacci" {
7494 What if we fix the base case, but put the wrong value in the3956 What if we fix the base case, but put the wrong value in the
7495 {#syntax#}expect{#endsyntax#} line?3957 {#syntax#}expect{#endsyntax#} line?
7496 </p>3958 </p>
7497 {#code_begin|test_err|test_fibonacci_comptime_unreachable|reached unreachable#}3959 {#code|test_fibonacci_comptime_unreachable.zig#}
7498const assert = @import("std").debug.assert;
7499
7500fn fibonacci(index: i32) i32 {
7501 if (index < 2) return index;
7502 return fibonacci(index - 1) + fibonacci(index - 2);
7503}
75043960
7505test "fibonacci" {
7506 try comptime assert(fibonacci(7) == 99999);
7507}
7508 {#code_end#}
75093961
7510 <p>3962 <p>
7511 At {#link|container|Containers#} level (outside of any function), all expressions are implicitly3963 At {#link|container|Containers#} level (outside of any function), all expressions are implicitly
7512 {#syntax#}comptime{#endsyntax#} expressions. This means that we can use functions to3964 {#syntax#}comptime{#endsyntax#} expressions. This means that we can use functions to
7513 initialize complex static data. For example:3965 initialize complex static data. For example:
7514 </p>3966 </p>
7515 {#code_begin|test|test_container-level_comptime_expressions#}3967 {#code|test_container-level_comptime_expressions.zig#}
7516const first_25_primes = firstNPrimes(25);
7517const sum_of_first_25_primes = sum(&first_25_primes);
7518
7519fn firstNPrimes(comptime n: usize) [n]i32 {
7520 var prime_list: [n]i32 = undefined;
7521 var next_index: usize = 0;
7522 var test_number: i32 = 2;
7523 while (next_index < prime_list.len) : (test_number += 1) {
7524 var test_prime_index: usize = 0;
7525 var is_prime = true;
7526 while (test_prime_index < next_index) : (test_prime_index += 1) {
7527 if (test_number % prime_list[test_prime_index] == 0) {
7528 is_prime = false;
7529 break;
7530 }
7531 }
7532 if (is_prime) {
7533 prime_list[next_index] = test_number;
7534 next_index += 1;
7535 }
7536 }
7537 return prime_list;
7538}
7539
7540fn sum(numbers: []const i32) i32 {
7541 var result: i32 = 0;
7542 for (numbers) |x| {
7543 result += x;
7544 }
7545 return result;
7546}
75473968
7548test "variable values" {
7549 try @import("std").testing.expect(sum_of_first_25_primes == 1060);
7550}
7551 {#code_end#}
7552 <p>3969 <p>
7553 When we compile this program, Zig generates the constants3970 When we compile this program, Zig generates the constants
7554 with the answer pre-computed. Here are the lines from the generated LLVM IR:3971 with the answer pre-computed. Here are the lines from the generated LLVM IR:
...@@ -7570,21 +3987,8 @@ test "variable values" {...@@ -7570,21 +3987,8 @@ test "variable values" {
7570 <p>3987 <p>
7571 Here is an example of a generic {#syntax#}List{#endsyntax#} data structure.3988 Here is an example of a generic {#syntax#}List{#endsyntax#} data structure.
7572 </p>3989 </p>
7573 {#code_begin|syntax|generic_data_structure#}3990 {#code|generic_data_structure.zig#}
7574fn List(comptime T: type) type {
7575 return struct {
7576 items: []T,
7577 len: usize,
7578 };
7579}
75803991
7581// The generic List data structure can be instantiated by passing in a type:
7582var buffer: [10]i32 = undefined;
7583var list = List(i32){
7584 .items = &buffer,
7585 .len = 0,
7586};
7587 {#code_end#}
7588 <p>3992 <p>
7589 That's it. It's a function that returns an anonymous {#syntax#}struct{#endsyntax#}.3993 That's it. It's a function that returns an anonymous {#syntax#}struct{#endsyntax#}.
7590 For the purposes of error messages and debugging, Zig infers the name3994 For the purposes of error messages and debugging, Zig infers the name
...@@ -7594,22 +3998,8 @@ var list = List(i32){...@@ -7594,22 +3998,8 @@ var list = List(i32){
7594 <p>3998 <p>
7595 To explicitly give a type a name, we assign it to a constant.3999 To explicitly give a type a name, we assign it to a constant.
7596 </p>4000 </p>
7597 {#code_begin|syntax|anonymous_struct_name#}4001 {#code|anonymous_struct_name.zig#}
7598const Node = struct {
7599 next: ?*Node,
7600 name: []const u8,
7601};
7602
7603var node_a = Node{
7604 .next = null,
7605 .name = "Node A",
7606};
76074002
7608var node_b = Node{
7609 .next = &node_a,
7610 .name = "Node B",
7611};
7612 {#code_end#}
7613 <p>4003 <p>
7614 In this example, the {#syntax#}Node{#endsyntax#} struct refers to itself.4004 In this example, the {#syntax#}Node{#endsyntax#} struct refers to itself.
7615 This works because all top level declarations are order-independent.4005 This works because all top level declarations are order-independent.
...@@ -7622,100 +4012,15 @@ var node_b = Node{...@@ -7622,100 +4012,15 @@ var node_b = Node{
7622 <p>4012 <p>
7623 Putting all of this together, let's see how {#syntax#}print{#endsyntax#} works in Zig.4013 Putting all of this together, let's see how {#syntax#}print{#endsyntax#} works in Zig.
7624 </p>4014 </p>
7625 {#code_begin|exe|print#}4015 {#code|print.zig#}
7626const print = @import("std").debug.print;
76274016
7628const a_number: i32 = 1234;
7629const a_string = "foobar";
7630
7631pub fn main() void {
7632 print("here is a string: '{s}' here is a number: {}\n", .{a_string, a_number});
7633}
7634 {#code_end#}
76354017
7636 <p>4018 <p>
7637 Let's crack open the implementation of this and see how it works:4019 Let's crack open the implementation of this and see how it works:
7638 </p>4020 </p>
76394021
7640 {#code_begin|syntax|poc_print_fn#}4022 {#code|poc_print_fn.zig#}
7641const Writer = struct {
7642 /// Calls print and then flushes the buffer.
7643 pub fn print(self: *Writer, comptime format: []const u8, args: anytype) anyerror!void {
7644 const State = enum {
7645 start,
7646 open_brace,
7647 close_brace,
7648 };
7649
7650 comptime var start_index: usize = 0;
7651 comptime var state = State.start;
7652 comptime var next_arg: usize = 0;
7653
7654 inline for (format, 0..) |c, i| {
7655 switch (state) {
7656 State.start => switch (c) {
7657 '{' => {
7658 if (start_index < i) try self.write(format[start_index..i]);
7659 state = State.open_brace;
7660 },
7661 '}' => {
7662 if (start_index < i) try self.write(format[start_index..i]);
7663 state = State.close_brace;
7664 },
7665 else => {},
7666 },
7667 State.open_brace => switch (c) {
7668 '{' => {
7669 state = State.start;
7670 start_index = i;
7671 },
7672 '}' => {
7673 try self.printValue(args[next_arg]);
7674 next_arg += 1;
7675 state = State.start;
7676 start_index = i + 1;
7677 },
7678 's' => {
7679 continue;
7680 },
7681 else => @compileError("Unknown format character: " ++ [1]u8{c}),
7682 },
7683 State.close_brace => switch (c) {
7684 '}' => {
7685 state = State.start;
7686 start_index = i;
7687 },
7688 else => @compileError("Single '}' encountered in format string"),
7689 },
7690 }
7691 }
7692 comptime {
7693 if (args.len != next_arg) {
7694 @compileError("Unused arguments");
7695 }
7696 if (state != State.start) {
7697 @compileError("Incomplete format string: " ++ format);
7698 }
7699 }
7700 if (start_index < format.len) {
7701 try self.write(format[start_index..format.len]);
7702 }
7703 try self.flush();
7704 }
77054023
7706 fn write(self: *Writer, value: []const u8) !void {
7707 _ = self;
7708 _ = value;
7709 }
7710 pub fn printValue(self: *Writer, value: anytype) !void {
7711 _ = self;
7712 _ = value;
7713 }
7714 fn flush(self: *Writer) !void {
7715 _ = self;
7716 }
7717};
7718 {#code_end#}
7719 <p>4024 <p>
7720 This is a proof of concept implementation; the actual function in the standard library has more4025 This is a proof of concept implementation; the actual function in the standard library has more
7721 formatting capabilities.4026 formatting capabilities.
...@@ -7741,56 +4046,13 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {...@@ -7741,56 +4046,13 @@ pub fn print(self: *Writer, arg0: []const u8, arg1: i32) !void {
7741 {#syntax#}printValue{#endsyntax#} is a function that takes a parameter of any type, and does different things depending4046 {#syntax#}printValue{#endsyntax#} is a function that takes a parameter of any type, and does different things depending
7742 on the type:4047 on the type:
7743 </p>4048 </p>
7744 {#code_begin|syntax|poc_printValue_fn#}4049 {#code|poc_printValue_fn.zig#}
7745 const Writer = struct {
7746 pub fn printValue(self: *Writer, value: anytype) !void {
7747 switch (@typeInfo(@TypeOf(value))) {
7748 .Int => {
7749 return self.writeInt(value);
7750 },
7751 .Float => {
7752 return self.writeFloat(value);
7753 },
7754 .Pointer => {
7755 return self.write(value);
7756 },
7757 else => {
7758 @compileError("Unable to print type '" ++ @typeName(@TypeOf(value)) ++ "'");
7759 },
7760 }
7761 }
77624050
7763 fn write(self: *Writer, value: []const u8) !void {
7764 _ = self;
7765 _ = value;
7766 }
7767 fn writeInt(self: *Writer, value: anytype) !void {
7768 _ = self;
7769 _ = value;
7770 }
7771 fn writeFloat(self: *Writer, value: anytype) !void {
7772 _ = self;
7773 _ = value;
7774 }
7775};
7776 {#code_end#}
7777 <p>4051 <p>
7778 And now, what happens if we give too many arguments to {#syntax#}print{#endsyntax#}?4052 And now, what happens if we give too many arguments to {#syntax#}print{#endsyntax#}?
7779 </p>4053 </p>
7780 {#code_begin|test_err|test_print_too_many_args|unused argument in 'here is a string: '{s}' here is a number: {}#}4054 {#code|test_print_too_many_args.zig#}
7781const print = @import("std").debug.print;
77824055
7783const a_number: i32 = 1234;
7784const a_string = "foobar";
7785
7786test "print too many arguments" {
7787 print("here is a string: '{s}' here is a number: {}\n", .{
7788 a_string,
7789 a_number,
7790 a_number,
7791 });
7792}
7793 {#code_end#}
7794 <p>4056 <p>
7795 Zig gives programmers the tools needed to protect themselves against their own mistakes.4057 Zig gives programmers the tools needed to protect themselves against their own mistakes.
7796 </p>4058 </p>
...@@ -7798,17 +4060,8 @@ test "print too many arguments" {...@@ -7798,17 +4060,8 @@ test "print too many arguments" {
7798 Zig doesn't care whether the format argument is a string literal,4060 Zig doesn't care whether the format argument is a string literal,
7799 only that it is a compile-time known value that can be coerced to a {#syntax#}[]const u8{#endsyntax#}:4061 only that it is a compile-time known value that can be coerced to a {#syntax#}[]const u8{#endsyntax#}:
7800 </p>4062 </p>
7801 {#code_begin|exe|print_comptime-known_format#}4063 {#code|print_comptime-known_format.zig#}
7802const print = @import("std").debug.print;
78034064
7804const a_number: i32 = 1234;
7805const a_string = "foobar";
7806const fmt = "here is a string: '{s}' here is a number: {}\n";
7807
7808pub fn main() void {
7809 print(fmt, .{a_string, a_number});
7810}
7811 {#code_end#}
7812 <p>4065 <p>
7813 This works fine.4066 This works fine.
7814 </p>4067 </p>
...@@ -7827,103 +4080,13 @@ pub fn main() void {...@@ -7827,103 +4080,13 @@ pub fn main() void {
7827 can use inline assembly. Here is an example of implementing Hello, World on x86_64 Linux4080 can use inline assembly. Here is an example of implementing Hello, World on x86_64 Linux
7828 using inline assembly:4081 using inline assembly:
7829 </p>4082 </p>
7830 {#code_begin|exe|inline_assembly#}4083 {#code|inline_assembly.zig#}
7831 {#target_linux_x86_64#}
7832pub fn main() noreturn {
7833 const msg = "hello world\n";
7834 _ = syscall3(SYS_write, STDOUT_FILENO, @intFromPtr(msg), msg.len);
7835 _ = syscall1(SYS_exit, 0);
7836 unreachable;
7837}
7838
7839pub const SYS_write = 1;
7840pub const SYS_exit = 60;
78414084
7842pub const STDOUT_FILENO = 1;
7843
7844pub fn syscall1(number: usize, arg1: usize) usize {
7845 return asm volatile ("syscall"
7846 : [ret] "={rax}" (-> usize),
7847 : [number] "{rax}" (number),
7848 [arg1] "{rdi}" (arg1),
7849 : "rcx", "r11"
7850 );
7851}
7852
7853pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
7854 return asm volatile ("syscall"
7855 : [ret] "={rax}" (-> usize),
7856 : [number] "{rax}" (number),
7857 [arg1] "{rdi}" (arg1),
7858 [arg2] "{rsi}" (arg2),
7859 [arg3] "{rdx}" (arg3),
7860 : "rcx", "r11"
7861 );
7862}
7863 {#code_end#}
7864 <p>4085 <p>
7865 Dissecting the syntax:4086 Dissecting the syntax:
7866 </p>4087 </p>
7867 {#code_begin|syntax|Assembly Syntax Explained#}4088 {#code|Assembly Syntax Explained.zig#}
7868pub fn syscall1(number: usize, arg1: usize) usize {4089
7869 // Inline assembly is an expression which returns a value.
7870 // the `asm` keyword begins the expression.
7871 return asm
7872 // `volatile` is an optional modifier that tells Zig this
7873 // inline assembly expression has side-effects. Without
7874 // `volatile`, Zig is allowed to delete the inline assembly
7875 // code if the result is unused.
7876 volatile (
7877 // Next is a comptime string which is the assembly code.
7878 // Inside this string one may use `%[ret]`, `%[number]`,
7879 // or `%[arg1]` where a register is expected, to specify
7880 // the register that Zig uses for the argument or return value,
7881 // if the register constraint strings are used. However in
7882 // the below code, this is not used. A literal `%` can be
7883 // obtained by escaping it with a double percent: `%%`.
7884 // Often multiline string syntax comes in handy here.
7885 \\syscall
7886 // Next is the output. It is possible in the future Zig will
7887 // support multiple outputs, depending on how
7888 // https://github.com/ziglang/zig/issues/215 is resolved.
7889 // It is allowed for there to be no outputs, in which case
7890 // this colon would be directly followed by the colon for the inputs.
7891 :
7892 // This specifies the name to be used in `%[ret]` syntax in
7893 // the above assembly string. This example does not use it,
7894 // but the syntax is mandatory.
7895 [ret]
7896 // Next is the output constraint string. This feature is still
7897 // considered unstable in Zig, and so LLVM/GCC documentation
7898 // must be used to understand the semantics.
7899 // http://releases.llvm.org/10.0.0/docs/LangRef.html#inline-asm-constraint-string
7900 // https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
7901 // In this example, the constraint string means "the result value of
7902 // this inline assembly instruction is whatever is in $rax".
7903 "={rax}"
7904 // Next is either a value binding, or `->` and then a type. The
7905 // type is the result type of the inline assembly expression.
7906 // If it is a value binding, then `%[ret]` syntax would be used
7907 // to refer to the register bound to the value.
7908 (-> usize),
7909 // Next is the list of inputs.
7910 // The constraint for these inputs means, "when the assembly code is
7911 // executed, $rax shall have the value of `number` and $rdi shall have
7912 // the value of `arg1`". Any number of input parameters is allowed,
7913 // including none.
7914 : [number] "{rax}" (number),
7915 [arg1] "{rdi}" (arg1),
7916 // Next is the list of clobbers. These declare a set of registers whose
7917 // values will not be preserved by the execution of this assembly code.
7918 // These do not include output or input registers. The special clobber
7919 // value of "memory" means that the assembly writes to arbitrary undeclared
7920 // memory locations - not only the memory pointed to by a declared indirect
7921 // output. In this example we list $rcx and $r11 because it is known the
7922 // kernel syscall does not preserve these registers.
7923 : "rcx", "r11"
7924 );
7925}
7926 {#code_end#}
7927 <p>4090 <p>
7928 For x86 and x86_64 targets, the syntax is AT&amp;T syntax, rather than the more4091 For x86 and x86_64 targets, the syntax is AT&amp;T syntax, rather than the more
7929 popular Intel syntax. This is due to technical constraints; assembly parsing is4092 popular Intel syntax. This is due to technical constraints; assembly parsing is
...@@ -7991,27 +4154,8 @@ pub fn syscall1(number: usize, arg1: usize) usize {...@@ -7991,27 +4154,8 @@ pub fn syscall1(number: usize, arg1: usize) usize {
7991 verbatim into one long string and assembled together. There are no template substitution rules regarding4154 verbatim into one long string and assembled together. There are no template substitution rules regarding
7992 <code>%</code> as there are in inline assembly expressions.4155 <code>%</code> as there are in inline assembly expressions.
7993 </p>4156 </p>
7994 {#code_begin|test|test_global_assembly#}4157 {#code|test_global_assembly.zig#}
7995 {#target_linux_x86_64#}
7996const std = @import("std");
7997const expect = std.testing.expect;
7998
7999comptime {
8000 asm (
8001 \\.global my_func;
8002 \\.type my_func, @function;
8003 \\my_func:
8004 \\ lea (%rdi,%rsi,1),%eax
8005 \\ retq
8006 );
8007}
8008
8009extern fn my_func(a: i32, b: i32) i32;
80104158
8011test "global assembly" {
8012 try expect(my_func(12, 34) == 46);
8013}
8014 {#code_end#}
8015 {#header_close#}4159 {#header_close#}
8016 {#header_close#}4160 {#header_close#}
80174161
...@@ -8254,56 +4398,14 @@ comptime {...@@ -8254,56 +4398,14 @@ comptime {
8254 <p>4398 <p>
8255 Calls a function, in the same way that invoking an expression with parentheses does:4399 Calls a function, in the same way that invoking an expression with parentheses does:
8256 </p>4400 </p>
8257 {#code_begin|test|test_call_builtin#}4401 {#code|test_call_builtin.zig#}
8258const expect = @import("std").testing.expect;
8259
8260test "noinline function call" {
8261 try expect(@call(.auto, add, .{3, 9}) == 12);
8262}
82634402
8264fn add(a: i32, b: i32) i32 {
8265 return a + b;
8266}
8267 {#code_end#}
8268 <p>4403 <p>
8269 {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The4404 {#syntax#}@call{#endsyntax#} allows more flexibility than normal function call syntax does. The
8270 {#syntax#}CallModifier{#endsyntax#} enum is reproduced here:4405 {#syntax#}CallModifier{#endsyntax#} enum is reproduced here:
8271 </p>4406 </p>
8272 {#code_begin|syntax|builtin.CallModifier struct#}4407 {#code|builtin.CallModifier struct.zig#}
8273pub const CallModifier = enum {
8274 /// Equivalent to function call syntax.
8275 auto,
8276
8277 /// Equivalent to async keyword used with function call syntax.
8278 async_kw,
8279
8280 /// Prevents tail call optimization. This guarantees that the return
8281 /// address will point to the callsite, as opposed to the callsite's
8282 /// callsite. If the call is otherwise required to be tail-called
8283 /// or inlined, a compile error is emitted instead.
8284 never_tail,
8285
8286 /// Guarantees that the call will not be inlined. If the call is
8287 /// otherwise required to be inlined, a compile error is emitted instead.
8288 never_inline,
8289
8290 /// Asserts that the function call will not suspend. This allows a
8291 /// non-async function to call an async function.
8292 no_async,
8293
8294 /// Guarantees that the call will be generated with tail call optimization.
8295 /// If this is not possible, a compile error is emitted instead.
8296 always_tail,
8297
8298 /// Guarantees that the call will inlined at the callsite.
8299 /// If this is not possible, a compile error is emitted instead.
8300 always_inline,
83014408
8302 /// Evaluates the call at compile-time. If the call cannot be completed at
8303 /// compile-time, a compile error is emitted instead.
8304 compile_time,
8305};
8306 {#code_end#}
8307 {#header_close#}4409 {#header_close#}
83084410
8309 {#header_open|@cDefine#}4411 {#header_open|@cDefine#}
...@@ -8389,17 +4491,8 @@ pub const CallModifier = enum {...@@ -8389,17 +4491,8 @@ pub const CallModifier = enum {
8389 if the current value is not the given expected value. It's the equivalent of this code,4491 if the current value is not the given expected value. It's the equivalent of this code,
8390 except atomic:4492 except atomic:
8391 </p>4493 </p>
8392 {#code_begin|syntax|not_atomic_cmpxchgStrong#}4494 {#code|not_atomic_cmpxchgStrong.zig#}
8393fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_value: T) ?T {4495
8394 const old_value = ptr.*;
8395 if (old_value == expected_value) {
8396 ptr.* = new_value;
8397 return null;
8398 } else {
8399 return old_value;
8400 }
8401}
8402 {#code_end#}
8403 <p>4496 <p>
8404 If you are using cmpxchg in a retry loop, {#link|@cmpxchgWeak#} is the better choice, because it can be implemented4497 If you are using cmpxchg in a retry loop, {#link|@cmpxchgWeak#} is the better choice, because it can be implemented
8405 more efficiently in machine instructions.4498 more efficiently in machine instructions.
...@@ -8473,22 +4566,8 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val...@@ -8473,22 +4566,8 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
8473 This function can be used to do "printf debugging" on4566 This function can be used to do "printf debugging" on
8474 compile-time executing code.4567 compile-time executing code.
8475 </p>4568 </p>
8476 {#code_begin|test_err|test_compileLog_builtin|found compile log statement#}4569 {#code|test_compileLog_builtin.zig#}
8477const print = @import("std").debug.print;
8478
8479const num1 = blk: {
8480 var val1: i32 = 99;
8481 @compileLog("comptime val1 = ", val1);
8482 val1 = val1 + 1;
8483 break :blk val1;
8484};
8485
8486test "main" {
8487 @compileLog("comptime in main");
84884570
8489 print("Runtime in main, num1 = {}.\n", .{num1});
8490}
8491 {#code_end#}
8492 {#header_close#}4571 {#header_close#}
84934572
8494 {#header_open|@constCast#}4573 {#header_open|@constCast#}
...@@ -8695,22 +4774,15 @@ test "main" {...@@ -8695,22 +4774,15 @@ test "main" {
8695 {#syntax#}options.linkage{#endsyntax#} is {#syntax#}Strong{#endsyntax#}, this is equivalent to4774 {#syntax#}options.linkage{#endsyntax#} is {#syntax#}Strong{#endsyntax#}, this is equivalent to
8696 the {#syntax#}export{#endsyntax#} keyword used on a function:4775 the {#syntax#}export{#endsyntax#} keyword used on a function:
8697 </p>4776 </p>
8698 {#code_begin|obj|export_builtin#}4777 {#code|export_builtin.zig#}
8699comptime {
8700 @export(internalName, .{ .name = "foo", .linkage = .strong });
8701}
87024778
8703fn internalName() callconv(.C) void {}
8704 {#code_end#}
8705 <p>This is equivalent to:</p>4779 <p>This is equivalent to:</p>
8706 {#code_begin|obj|export_builtin_equivalent_code#}4780 {#code|export_builtin_equivalent_code.zig#}
8707export fn foo() void {}4781
8708 {#code_end#}
8709 <p>Note that even when using {#syntax#}export{#endsyntax#}, the {#syntax#}@"foo"{#endsyntax#} syntax for4782 <p>Note that even when using {#syntax#}export{#endsyntax#}, the {#syntax#}@"foo"{#endsyntax#} syntax for
8710 {#link|identifiers|Identifiers#} can be used to choose any string for the symbol name:</p>4783 {#link|identifiers|Identifiers#} can be used to choose any string for the symbol name:</p>
8711 {#code_begin|obj|export_any_symbol_name#}4784 {#code|export_any_symbol_name.zig#}
8712export fn @"A function name that is a complete sentence."() void {}4785
8713 {#code_end#}
8714 <p>4786 <p>
8715 When looking at the resulting object, you can see the symbol is used verbatim:4787 When looking at the resulting object, you can see the symbol is used verbatim:
8716 </p>4788 </p>
...@@ -8740,36 +4812,8 @@ export fn @"A function name that is a complete sentence."() void {}...@@ -8740,36 +4812,8 @@ export fn @"A function name that is a complete sentence."() void {}
8740 <pre>{#syntax#}@field(lhs: anytype, comptime field_name: []const u8) (field){#endsyntax#}</pre>4812 <pre>{#syntax#}@field(lhs: anytype, comptime field_name: []const u8) (field){#endsyntax#}</pre>
8741 <p>Performs field access by a compile-time string. Works on both fields and declarations.4813 <p>Performs field access by a compile-time string. Works on both fields and declarations.
8742 </p>4814 </p>
8743 {#code_begin|test|test_field_builtin#}4815 {#code|test_field_builtin.zig#}
8744const std = @import("std");
8745
8746const Point = struct {
8747 x: u32,
8748 y: u32,
8749
8750 pub var z: u32 = 1;
8751};
8752
8753test "field access by string" {
8754 const expect = std.testing.expect;
8755 var p = Point{ .x = 0, .y = 0 };
8756
8757 @field(p, "x") = 4;
8758 @field(p, "y") = @field(p, "x") + 1;
8759
8760 try expect(@field(p, "x") == 4);
8761 try expect(@field(p, "y") == 5);
8762}
87634816
8764test "decl access by string" {
8765 const expect = std.testing.expect;
8766
8767 try expect(@field(Point, "z") == 1);
8768
8769 @field(Point, "z") = 2;
8770 try expect(@field(Point, "z") == 2);
8771}
8772 {#code_end#}
87734817
8774 {#header_close#}4818 {#header_close#}
87754819
...@@ -8818,30 +4862,8 @@ test "decl access by string" {...@@ -8818,30 +4862,8 @@ test "decl access by string" {
8818 Returns whether or not a {#link|container|Containers#} has a declaration4862 Returns whether or not a {#link|container|Containers#} has a declaration
8819 matching {#syntax#}name{#endsyntax#}.4863 matching {#syntax#}name{#endsyntax#}.
8820 </p>4864 </p>
8821 {#code_begin|test|test_hasDecl_builtin#}4865 {#code|test_hasDecl_builtin.zig#}
8822const std = @import("std");
8823const expect = std.testing.expect;
8824
8825const Foo = struct {
8826 nope: i32,
8827
8828 pub var blah = "xxx";
8829 const hi = 1;
8830};
8831
8832test "@hasDecl" {
8833 try expect(@hasDecl(Foo, "blah"));
8834
8835 // Even though `hi` is private, @hasDecl returns true because this test is
8836 // in the same file scope as Foo. It would return false if Foo was declared
8837 // in a different file.
8838 try expect(@hasDecl(Foo, "hi"));
88394866
8840 // @hasDecl is for declarations; not fields.
8841 try expect(!@hasDecl(Foo, "nope"));
8842 try expect(!@hasDecl(Foo, "nope1234"));
8843}
8844 {#code_end#}
8845 {#see_also|@hasField#}4867 {#see_also|@hasField#}
8846 {#header_close#}4868 {#header_close#}
88474869
...@@ -8910,14 +4932,8 @@ test "@hasDecl" {...@@ -8910,14 +4932,8 @@ test "@hasDecl" {
8910 Attempting to convert a number which is out of range of the destination type results in4932 Attempting to convert a number which is out of range of the destination type results in
8911 safety-protected {#link|Undefined Behavior#}.4933 safety-protected {#link|Undefined Behavior#}.
8912 </p>4934 </p>
8913 {#code_begin|test_err|test_intCast_builtin|cast truncated bits#}4935 {#code|test_intCast_builtin.zig#}
8914test "integer cast panic" {4936
8915 var a: u16 = 0xabcd; // runtime-known
8916 _ = &a;
8917 const b: u8 = @intCast(a);
8918 _ = b;
8919}
8920 {#code_end#}
8921 <p>4937 <p>
8922 To truncate the significant bits of a number out of range of the destination type, use {#link|@truncate#}.4938 To truncate the significant bits of a number out of range of the destination type, use {#link|@truncate#}.
8923 </p>4939 </p>
...@@ -9066,19 +5082,8 @@ test "integer cast panic" {...@@ -9066,19 +5082,8 @@ test "integer cast panic" {
9066 designers targeting Wasm. So unless you are writing a new allocator from scratch, you should use5082 designers targeting Wasm. So unless you are writing a new allocator from scratch, you should use
9067 something like {#syntax#}@import("std").heap.WasmPageAllocator{#endsyntax#}.5083 something like {#syntax#}@import("std").heap.WasmPageAllocator{#endsyntax#}.
9068 </p>5084 </p>
9069 {#code_begin|test|test_wasmMemoryGrow_builtin#}5085 {#code|test_wasmMemoryGrow_builtin.zig#}
9070const std = @import("std");
9071const native_arch = @import("builtin").target.cpu.arch;
9072const expect = std.testing.expect;
9073
9074test "@wasmMemoryGrow" {
9075 if (native_arch != .wasm32) return error.SkipZigTest;
90765086
9077 const prev = @wasmMemorySize(0);
9078 try expect(prev == @wasmMemoryGrow(0, 1));
9079 try expect(prev + 1 == @wasmMemorySize(0));
9080}
9081 {#code_end#}
9082 {#see_also|@wasmMemorySize#}5087 {#see_also|@wasmMemorySize#}
9083 {#header_close#}5088 {#header_close#}
90845089
...@@ -9257,24 +5262,11 @@ test "@wasmMemoryGrow" {...@@ -9257,24 +5262,11 @@ test "@wasmMemoryGrow" {
9257 <p>5262 <p>
9258 Example:5263 Example:
9259 </p>5264 </p>
9260 {#code_begin|test_err|test_without_setEvalBranchQuota_builtin|evaluation exceeded 1000 backwards branches#}5265 {#code|test_without_setEvalBranchQuota_builtin.zig#}
9261test "foo" {5266
9262 comptime {
9263 var i = 0;
9264 while (i < 1001) : (i += 1) {}
9265 }
9266}
9267 {#code_end#}
9268 <p>Now we use {#syntax#}@setEvalBranchQuota{#endsyntax#}:</p>5267 <p>Now we use {#syntax#}@setEvalBranchQuota{#endsyntax#}:</p>
9269 {#code_begin|test|test_setEvalBranchQuota_builtin#}5268 {#code|test_setEvalBranchQuota_builtin.zig#}
9270test "foo" {5269
9271 comptime {
9272 @setEvalBranchQuota(1001);
9273 var i = 0;
9274 while (i < 1001) : (i += 1) {}
9275 }
9276}
9277 {#code_end#}
92785270
9279 {#see_also|comptime#}5271 {#see_also|comptime#}
9280 {#header_close#}5272 {#header_close#}
...@@ -9312,30 +5304,8 @@ test "foo" {...@@ -9312,30 +5304,8 @@ test "foo" {
9312 <p>5304 <p>
9313 Sets whether runtime safety checks are enabled for the scope that contains the function call.5305 Sets whether runtime safety checks are enabled for the scope that contains the function call.
9314 </p>5306 </p>
9315 {#code_begin|test_safety|test_setRuntimeSafety_builtin|integer overflow#}5307 {#code|test_setRuntimeSafety_builtin.zig#}
9316 {#code_release_fast#}5308
9317test "@setRuntimeSafety" {
9318 // The builtin applies to the scope that it is called in. So here, integer overflow
9319 // will not be caught in ReleaseFast and ReleaseSmall modes:
9320 // var x: u8 = 255;
9321 // x += 1; // undefined behavior in ReleaseFast/ReleaseSmall modes.
9322 {
9323 // However this block has safety enabled, so safety checks happen here,
9324 // even in ReleaseFast and ReleaseSmall modes.
9325 @setRuntimeSafety(true);
9326 var x: u8 = 255;
9327 x += 1;
9328
9329 {
9330 // The value can be overridden at any scope. So here integer overflow
9331 // would not be caught in any build mode.
9332 @setRuntimeSafety(false);
9333 // var x: u8 = 255;
9334 // x += 1; // undefined behavior in all build modes.
9335 }
9336 }
9337}
9338 {#code_end#}
9339 <p>Note: it is <a href="https://github.com/ziglang/zig/issues/978">planned</a> to replace5309 <p>Note: it is <a href="https://github.com/ziglang/zig/issues/978">planned</a> to replace
9340 {#syntax#}@setRuntimeSafety{#endsyntax#} with <code>@optimizeFor</code></p>5310 {#syntax#}@setRuntimeSafety{#endsyntax#} with <code>@optimizeFor</code></p>
93415311
...@@ -9420,26 +5390,8 @@ test "@setRuntimeSafety" {...@@ -9420,26 +5390,8 @@ test "@setRuntimeSafety" {
9420 {#link|pointer|Pointers#}, or {#syntax#}bool{#endsyntax#}. The mask may be any vector length, and its5390 {#link|pointer|Pointers#}, or {#syntax#}bool{#endsyntax#}. The mask may be any vector length, and its
9421 length determines the result length.5391 length determines the result length.
9422 </p>5392 </p>
9423 {#code_begin|test|test_shuffle_builtin#}5393 {#code|test_shuffle_builtin.zig#}
9424const std = @import("std");
9425const expect = std.testing.expect;
9426
9427test "vector @shuffle" {
9428 const a = @Vector(7, u8){ 'o', 'l', 'h', 'e', 'r', 'z', 'w' };
9429 const b = @Vector(4, u8){ 'w', 'd', '!', 'x' };
94305394
9431 // To shuffle within a single vector, pass undefined as the second argument.
9432 // Notice that we can re-order, duplicate, or omit elements of the input vector
9433 const mask1 = @Vector(5, i32){ 2, 3, 1, 1, 0 };
9434 const res1: @Vector(5, u8) = @shuffle(u8, a, undefined, mask1);
9435 try expect(std.mem.eql(u8, &@as([5]u8, res1), "hello"));
9436
9437 // Combining two vectors
9438 const mask2 = @Vector(6, i32){ -1, 0, 4, 1, -2, -3 };
9439 const res2: @Vector(6, u8) = @shuffle(u8, a, b, mask2);
9440 try expect(std.mem.eql(u8, &@as([6]u8, res2), "world!"));
9441}
9442 {#code_end#}
9443 {#see_also|Vectors#}5395 {#see_also|Vectors#}
9444 {#header_close#}5396 {#header_close#}
94455397
...@@ -9468,16 +5420,8 @@ test "vector @shuffle" {...@@ -9468,16 +5420,8 @@ test "vector @shuffle" {
9468 Produces a vector where each element is the value {#syntax#}scalar{#endsyntax#}.5420 Produces a vector where each element is the value {#syntax#}scalar{#endsyntax#}.
9469 The return type and thus the length of the vector is inferred.5421 The return type and thus the length of the vector is inferred.
9470 </p>5422 </p>
9471 {#code_begin|test|test_splat_builtin#}5423 {#code|test_splat_builtin.zig#}
9472const std = @import("std");
9473const expect = std.testing.expect;
94745424
9475test "vector @splat" {
9476 const scalar: u32 = 5;
9477 const result: @Vector(4, u32) = @splat(scalar);
9478 try expect(std.mem.eql(u32, &@as([4]u32, result), &[_]u32{ 5, 5, 5, 5 }));
9479}
9480 {#code_end#}
9481 <p>5425 <p>
9482 {#syntax#}scalar{#endsyntax#} must be an {#link|integer|Integers#}, {#link|bool|Primitive Types#},5426 {#syntax#}scalar{#endsyntax#} must be an {#link|integer|Integers#}, {#link|bool|Primitive Types#},
9483 {#link|float|Floats#}, or {#link|pointer|Pointers#}.5427 {#link|float|Floats#}, or {#link|pointer|Pointers#}.
...@@ -9510,21 +5454,8 @@ test "vector @splat" {...@@ -9510,21 +5454,8 @@ test "vector @splat" {
9510 types the operation associativity is preserved, unless the float mode is5454 types the operation associativity is preserved, unless the float mode is
9511 set to {#syntax#}Optimized{#endsyntax#}.5455 set to {#syntax#}Optimized{#endsyntax#}.
9512 </p>5456 </p>
9513 {#code_begin|test|test_reduce_builtin#}5457 {#code|test_reduce_builtin.zig#}
9514const std = @import("std");5458
9515const expect = std.testing.expect;
9516
9517test "vector @reduce" {
9518 const V = @Vector(4, i32);
9519 const value = V{ 1, -1, 1, -1 };
9520 const result = value > @as(V, @splat(0));
9521 // result is { true, false, true, false };
9522 try comptime expect(@TypeOf(result) == @Vector(4, bool));
9523 const is_all_true = @reduce(.And, result);
9524 try comptime expect(@TypeOf(is_all_true) == bool);
9525 try expect(is_all_true == false);
9526}
9527 {#code_end#}
9528 {#see_also|Vectors|@setFloatMode#}5459 {#see_also|Vectors|@setFloatMode#}
9529 {#header_close#}5460 {#header_close#}
95305461
...@@ -9533,23 +5464,8 @@ test "vector @reduce" {...@@ -9533,23 +5464,8 @@ test "vector @reduce" {
9533 <p>5464 <p>
9534 Returns a {#syntax#}SourceLocation{#endsyntax#} struct representing the function's name and location in the source code. This must be called in a function.5465 Returns a {#syntax#}SourceLocation{#endsyntax#} struct representing the function's name and location in the source code. This must be called in a function.
9535 </p>5466 </p>
9536 {#code_begin|test|test_src_builtin#}5467 {#code|test_src_builtin.zig#}
9537const std = @import("std");
9538const expect = std.testing.expect;
9539
9540test "@src" {
9541 try doTheTest();
9542}
9543
9544fn doTheTest() !void {
9545 const src = @src();
95465468
9547 try expect(src.line == 9);
9548 try expect(src.column == 17);
9549 try expect(std.mem.endsWith(u8, src.fn_name, "doTheTest"));
9550 try expect(std.mem.endsWith(u8, src.file, "test_src_builtin.zig"));
9551}
9552 {#code_end#}
9553 {#header_close#}5469 {#header_close#}
9554 {#header_open|@sqrt#}5470 {#header_open|@sqrt#}
9555 <pre>{#syntax#}@sqrt(value: anytype) @TypeOf(value){#endsyntax#}</pre>5471 <pre>{#syntax#}@sqrt(value: anytype) @TypeOf(value){#endsyntax#}</pre>
...@@ -9718,28 +5634,8 @@ fn doTheTest() !void {...@@ -9718,28 +5634,8 @@ fn doTheTest() !void {
9718 Returns the innermost struct, enum, or union that this function call is inside.5634 Returns the innermost struct, enum, or union that this function call is inside.
9719 This can be useful for an anonymous struct that needs to refer to itself:5635 This can be useful for an anonymous struct that needs to refer to itself:
9720 </p>5636 </p>
9721 {#code_begin|test|test_this_builtin#}5637 {#code|test_this_builtin.zig#}
9722const std = @import("std");
9723const expect = std.testing.expect;
9724
9725test "@This()" {
9726 var items = [_]i32{ 1, 2, 3, 4 };
9727 const list = List(i32){ .items = items[0..] };
9728 try expect(list.length() == 4);
9729}
9730
9731fn List(comptime T: type) type {
9732 return struct {
9733 const Self = @This();
9734
9735 items: []T,
97365638
9737 fn length(self: Self) usize {
9738 return self.items.len;
9739 }
9740 };
9741}
9742 {#code_end#}
9743 <p>5639 <p>
9744 When {#syntax#}@This(){#endsyntax#} is used at file scope, it returns a reference to the5640 When {#syntax#}@This(){#endsyntax#} is used at file scope, it returns a reference to the
9745 struct that corresponds to the current file.5641 struct that corresponds to the current file.
...@@ -9772,16 +5668,8 @@ fn List(comptime T: type) type {...@@ -9772,16 +5668,8 @@ fn List(comptime T: type) type {
9772 <p>5668 <p>
9773 Calling {#syntax#}@truncate{#endsyntax#} on a number out of range of the destination type is well defined and working code:5669 Calling {#syntax#}@truncate{#endsyntax#} on a number out of range of the destination type is well defined and working code:
9774 </p>5670 </p>
9775 {#code_begin|test|test_truncate_builtin#}5671 {#code|test_truncate_builtin.zig#}
9776const std = @import("std");
9777const expect = std.testing.expect;
97785672
9779test "integer truncation" {
9780 const a: u16 = 0xabcd;
9781 const b: u8 = @truncate(a);
9782 try expect(b == 0xcd);
9783}
9784 {#code_end#}
9785 <p>5673 <p>
9786 Use {#link|@intCast#} to convert numbers guaranteed to fit the destination type.5674 Use {#link|@intCast#} to convert numbers guaranteed to fit the destination type.
9787 </p>5675 </p>
...@@ -9858,22 +5746,8 @@ test "integer truncation" {...@@ -9858,22 +5746,8 @@ test "integer truncation" {
9858 <p>5746 <p>
9859 The expressions are evaluated, however they are guaranteed to have no <em>runtime</em> side-effects:5747 The expressions are evaluated, however they are guaranteed to have no <em>runtime</em> side-effects:
9860 </p>5748 </p>
9861 {#code_begin|test|test_TypeOf_builtin#}5749 {#code|test_TypeOf_builtin.zig#}
9862const std = @import("std");
9863const expect = std.testing.expect;
9864
9865test "no runtime side effects" {
9866 var data: i32 = 0;
9867 const T = @TypeOf(foo(i32, &data));
9868 try comptime expect(T == i32);
9869 try expect(data == 0);
9870}
98715750
9872fn foo(comptime T: type, ptr: *T) T {
9873 ptr.* += 1;
9874 return ptr.*;
9875}
9876 {#code_end#}
9877 {#header_close#}5751 {#header_close#}
98785752
9879 {#header_open|@unionInit#}5753 {#header_open|@unionInit#}
...@@ -9936,19 +5810,8 @@ fn foo(comptime T: type, ptr: *T) T {...@@ -9936,19 +5810,8 @@ fn foo(comptime T: type, ptr: *T) T {
9936 <p>5810 <p>
9937 To add standard build options to a <code class="file">build.zig</code> file:5811 To add standard build options to a <code class="file">build.zig</code> file:
9938 </p>5812 </p>
9939 {#code_begin|syntax|build#}5813 {#code|build.zig#}
9940const std = @import("std");5814
9941
9942pub fn build(b: *std.Build) void {
9943 const optimize = b.standardOptimizeOption(.{});
9944 const exe = b.addExecutable(.{
9945 .name = "example",
9946 .root_source_file = .{ .path = "example.zig" },
9947 .optimize = optimize,
9948 });
9949 b.default_step.dependOn(&exe.step);
9950}
9951 {#code_end#}
9952 <p>5815 <p>
9953 This causes these options to be available:5816 This causes these options to be available:
9954 </p>5817 </p>
...@@ -10026,95 +5889,42 @@ pub fn build(b: *std.Build) void {...@@ -10026,95 +5889,42 @@ pub fn build(b: *std.Build) void {
10026 <p>5889 <p>
10027 When a safety check fails, Zig crashes with a stack trace, like this:5890 When a safety check fails, Zig crashes with a stack trace, like this:
10028 </p>5891 </p>
10029 {#code_begin|test_err|test_undefined_behavior|reached unreachable code#}5892 {#code|test_undefined_behavior.zig#}
10030test "safety check" {5893
10031 unreachable;
10032}
10033 {#code_end#}
10034 {#header_open|Reaching Unreachable Code#}5894 {#header_open|Reaching Unreachable Code#}
10035 <p>At compile-time:</p>5895 <p>At compile-time:</p>
10036 {#code_begin|test_err|test_comptime_reaching_unreachable|reached unreachable code#}5896 {#code|test_comptime_reaching_unreachable.zig#}
10037comptime {5897
10038 assert(false);
10039}
10040fn assert(ok: bool) void {
10041 if (!ok) unreachable; // assertion failure
10042}
10043 {#code_end#}
10044 <p>At runtime:</p>5898 <p>At runtime:</p>
10045 {#code_begin|exe_err|runtime_reaching_unreachable#}5899 {#code|runtime_reaching_unreachable.zig#}
10046const std = @import("std");
100475900
10048pub fn main() void {
10049 std.debug.assert(false);
10050}
10051 {#code_end#}
10052 {#header_close#}5901 {#header_close#}
10053 {#header_open|Index out of Bounds#}5902 {#header_open|Index out of Bounds#}
10054 <p>At compile-time:</p>5903 <p>At compile-time:</p>
10055 {#code_begin|test_err|test_comptime_index_out_of_bounds|index 5 outside array of length 5#}5904 {#code|test_comptime_index_out_of_bounds.zig#}
10056comptime {5905
10057 const array: [5]u8 = "hello".*;
10058 const garbage = array[5];
10059 _ = garbage;
10060}
10061 {#code_end#}
10062 <p>At runtime:</p>5906 <p>At runtime:</p>
10063 {#code_begin|exe_err|runtime_index_out_of_bounds#}5907 {#code|runtime_index_out_of_bounds.zig#}
10064pub fn main() void {
10065 const x = foo("hello");
10066 _ = x;
10067}
100685908
10069fn foo(x: []const u8) u8 {
10070 return x[5];
10071}
10072 {#code_end#}
10073 {#header_close#}5909 {#header_close#}
10074 {#header_open|Cast Negative Number to Unsigned Integer#}5910 {#header_open|Cast Negative Number to Unsigned Integer#}
10075 <p>At compile-time:</p>5911 <p>At compile-time:</p>
10076 {#code_begin|test_err|test_comptime_invalid_cast|type 'u32' cannot represent integer value '-1'#}5912 {#code|test_comptime_invalid_cast.zig#}
10077comptime {5913
10078 const value: i32 = -1;
10079 const unsigned: u32 = @intCast(value);
10080 _ = unsigned;
10081}
10082 {#code_end#}
10083 <p>At runtime:</p>5914 <p>At runtime:</p>
10084 {#code_begin|exe_err|runtime_invalid_cast#}5915 {#code|runtime_invalid_cast.zig#}
10085const std = @import("std");5916
10086
10087pub fn main() void {
10088 var value: i32 = -1; // runtime-known
10089 _ = &value;
10090 const unsigned: u32 = @intCast(value);
10091 std.debug.print("value: {}\n", .{unsigned});
10092}
10093 {#code_end#}
10094 <p>5917 <p>
10095 To obtain the maximum value of an unsigned integer, use {#syntax#}std.math.maxInt{#endsyntax#}.5918 To obtain the maximum value of an unsigned integer, use {#syntax#}std.math.maxInt{#endsyntax#}.
10096 </p>5919 </p>
10097 {#header_close#}5920 {#header_close#}
10098 {#header_open|Cast Truncates Data#}5921 {#header_open|Cast Truncates Data#}
10099 <p>At compile-time:</p>5922 <p>At compile-time:</p>
10100 {#code_begin|test_err|test_comptime_invalid_cast_truncate|type 'u8' cannot represent integer value '300'#}5923 {#code|test_comptime_invalid_cast_truncate.zig#}
10101comptime {5924
10102 const spartan_count: u16 = 300;
10103 const byte: u8 = @intCast(spartan_count);
10104 _ = byte;
10105}
10106 {#code_end#}
10107 <p>At runtime:</p>5925 <p>At runtime:</p>
10108 {#code_begin|exe_err|runtime_invalid_cast_truncate#}5926 {#code|runtime_invalid_cast_truncate.zig#}
10109const std = @import("std");5927
10110
10111pub fn main() void {
10112 var spartan_count: u16 = 300; // runtime-known
10113 _ = &spartan_count;
10114 const byte: u8 = @intCast(spartan_count);
10115 std.debug.print("value: {}\n", .{byte});
10116}
10117 {#code_end#}
10118 <p>5928 <p>
10119 To truncate bits, use {#link|@truncate#}.5929 To truncate bits, use {#link|@truncate#}.
10120 </p>5930 </p>
...@@ -10133,22 +5943,11 @@ pub fn main() void {...@@ -10133,22 +5943,11 @@ pub fn main() void {
10133 <li>{#link|@divExact#} (division)</li>5943 <li>{#link|@divExact#} (division)</li>
10134 </ul>5944 </ul>
10135 <p>Example with addition at compile-time:</p>5945 <p>Example with addition at compile-time:</p>
10136 {#code_begin|test_err|test_comptime_overflow|overflow of integer type 'u8' with value '256'#}5946 {#code|test_comptime_overflow.zig#}
10137comptime {5947
10138 var byte: u8 = 255;
10139 byte += 1;
10140}
10141 {#code_end#}
10142 <p>At runtime:</p>5948 <p>At runtime:</p>
10143 {#code_begin|exe_err|runtime_overflow#}5949 {#code|runtime_overflow.zig#}
10144const std = @import("std");
101455950
10146pub fn main() void {
10147 var byte: u8 = 255;
10148 byte += 1;
10149 std.debug.print("value: {}\n", .{byte});
10150}
10151 {#code_end#}
10152 {#header_close#}5951 {#header_close#}
10153 {#header_open|Standard Library Math Functions#}5952 {#header_open|Standard Library Math Functions#}
10154 <p>These functions provided by the standard library return possible errors.</p>5953 <p>These functions provided by the standard library return possible errors.</p>
...@@ -10162,20 +5961,8 @@ pub fn main() void {...@@ -10162,20 +5961,8 @@ pub fn main() void {
10162 <li>{#syntax#}@import("std").math.shl{#endsyntax#}</li>5961 <li>{#syntax#}@import("std").math.shl{#endsyntax#}</li>
10163 </ul>5962 </ul>
10164 <p>Example of catching an overflow for addition:</p>5963 <p>Example of catching an overflow for addition:</p>
10165 {#code_begin|exe_err|math_add#}5964 {#code|math_add.zig#}
10166const math = @import("std").math;
10167const print = @import("std").debug.print;
10168pub fn main() !void {
10169 var byte: u8 = 255;
10170
10171 byte = if (math.add(u8, byte, 1)) |result| result else |err| {
10172 print("unable to add one: {s}\n", .{@errorName(err)});
10173 return err;
10174 };
101755965
10176 print("result: {}\n", .{byte});
10177}
10178 {#code_end#}
10179 {#header_close#}5966 {#header_close#}
10180 {#header_open|Builtin Overflow Functions#}5967 {#header_open|Builtin Overflow Functions#}
10181 <p>5968 <p>
...@@ -10191,19 +5978,8 @@ pub fn main() !void {...@@ -10191,19 +5978,8 @@ pub fn main() !void {
10191 <p>5978 <p>
10192 Example of {#link|@addWithOverflow#}:5979 Example of {#link|@addWithOverflow#}:
10193 </p>5980 </p>
10194 {#code_begin|exe|addWithOverflow_builtin#}5981 {#code|addWithOverflow_builtin.zig#}
10195const print = @import("std").debug.print;
10196pub fn main() void {
10197 const byte: u8 = 255;
101985982
10199 const ov = @addWithOverflow(byte, 10);
10200 if (ov[1] != 0) {
10201 print("overflowed result: {}\n", .{ov[0]});
10202 } else {
10203 print("result: {}\n", .{ov[0]});
10204 }
10205}
10206 {#code_end#}
10207 {#header_close#}5983 {#header_close#}
10208 {#header_open|Wrapping Operations#}5984 {#header_open|Wrapping Operations#}
10209 <p>5985 <p>
...@@ -10215,409 +5991,131 @@ pub fn main() void {...@@ -10215,409 +5991,131 @@ pub fn main() void {
10215 <li>{#syntax#}-%{#endsyntax#} (wraparound negation)</li>5991 <li>{#syntax#}-%{#endsyntax#} (wraparound negation)</li>
10216 <li>{#syntax#}*%{#endsyntax#} (wraparound multiplication)</li>5992 <li>{#syntax#}*%{#endsyntax#} (wraparound multiplication)</li>
10217 </ul>5993 </ul>
10218 {#code_begin|test|test_wraparound_semantics#}5994 {#code|test_wraparound_semantics.zig#}
10219const std = @import("std");5995
10220const expect = std.testing.expect;
10221const minInt = std.math.minInt;
10222const maxInt = std.math.maxInt;
10223
10224test "wraparound addition and subtraction" {
10225 const x: i32 = maxInt(i32);
10226 const min_val = x +% 1;
10227 try expect(min_val == minInt(i32));
10228 const max_val = min_val -% 1;
10229 try expect(max_val == maxInt(i32));
10230}
10231 {#code_end#}
10232 {#header_close#}5996 {#header_close#}
10233 {#header_close#}5997 {#header_close#}
10234 {#header_open|Exact Left Shift Overflow#}5998 {#header_open|Exact Left Shift Overflow#}
10235 <p>At compile-time:</p>5999 <p>At compile-time:</p>
10236 {#code_begin|test_err|test_comptime_shlExact_overwlow|operation caused overflow#}6000 {#code|test_comptime_shlExact_overwlow.zig#}
10237comptime {6001
10238 const x = @shlExact(@as(u8, 0b01010101), 2);
10239 _ = x;
10240}
10241 {#code_end#}
10242 <p>At runtime:</p>6002 <p>At runtime:</p>
10243 {#code_begin|exe_err|runtime_shlExact_overflow#}6003 {#code|runtime_shlExact_overflow.zig#}
10244const std = @import("std");6004
10245
10246pub fn main() void {
10247 var x: u8 = 0b01010101; // runtime-known
10248 _ = &x;
10249 const y = @shlExact(x, 2);
10250 std.debug.print("value: {}\n", .{y});
10251}
10252 {#code_end#}
10253 {#header_close#}6005 {#header_close#}
10254 {#header_open|Exact Right Shift Overflow#}6006 {#header_open|Exact Right Shift Overflow#}
10255 <p>At compile-time:</p>6007 <p>At compile-time:</p>
10256 {#code_begin|test_err|test_comptime_shrExact_overflow|exact shift shifted out 1 bits#}6008 {#code|test_comptime_shrExact_overflow.zig#}
10257comptime {6009
10258 const x = @shrExact(@as(u8, 0b10101010), 2);
10259 _ = x;
10260}
10261 {#code_end#}
10262 <p>At runtime:</p>6010 <p>At runtime:</p>
10263 {#code_begin|exe_err|runtime_shrExact_overflow#}6011 {#code|runtime_shrExact_overflow.zig#}
10264const std = @import("std");6012
10265
10266pub fn main() void {
10267 var x: u8 = 0b10101010; // runtime-known
10268 _ = &x;
10269 const y = @shrExact(x, 2);
10270 std.debug.print("value: {}\n", .{y});
10271}
10272 {#code_end#}
10273 {#header_close#}6013 {#header_close#}
10274 {#header_open|Division by Zero#}6014 {#header_open|Division by Zero#}
10275 <p>At compile-time:</p>6015 <p>At compile-time:</p>
10276 {#code_begin|test_err|test_comptime_division_by_zero|division by zero#}6016 {#code|test_comptime_division_by_zero.zig#}
10277comptime {6017
10278 const a: i32 = 1;
10279 const b: i32 = 0;
10280 const c = a / b;
10281 _ = c;
10282}
10283 {#code_end#}
10284 <p>At runtime:</p>6018 <p>At runtime:</p>
10285 {#code_begin|exe_err|runtime_division_by_zero#}6019 {#code|runtime_division_by_zero.zig#}
10286const std = @import("std");6020
10287
10288pub fn main() void {
10289 var a: u32 = 1;
10290 var b: u32 = 0;
10291 _ = .{ &a, &b };
10292 const c = a / b;
10293 std.debug.print("value: {}\n", .{c});
10294}
10295 {#code_end#}
10296 {#header_close#}6021 {#header_close#}
10297 {#header_open|Remainder Division by Zero#}6022 {#header_open|Remainder Division by Zero#}
10298 <p>At compile-time:</p>6023 <p>At compile-time:</p>
10299 {#code_begin|test_err|test_comptime_remainder_division_by_zero|division by zero#}6024 {#code|test_comptime_remainder_division_by_zero.zig#}
10300comptime {6025
10301 const a: i32 = 10;
10302 const b: i32 = 0;
10303 const c = a % b;
10304 _ = c;
10305}
10306 {#code_end#}
10307 <p>At runtime:</p>6026 <p>At runtime:</p>
10308 {#code_begin|exe_err|runtime_remainder_division_by_zero#}6027 {#code|runtime_remainder_division_by_zero.zig#}
10309const std = @import("std");6028
10310
10311pub fn main() void {
10312 var a: u32 = 10;
10313 var b: u32 = 0;
10314 _ = .{ &a, &b };
10315 const c = a % b;
10316 std.debug.print("value: {}\n", .{c});
10317}
10318 {#code_end#}
10319 {#header_close#}6029 {#header_close#}
10320 {#header_open|Exact Division Remainder#}6030 {#header_open|Exact Division Remainder#}
10321 <p>At compile-time:</p>6031 <p>At compile-time:</p>
10322 {#code_begin|test_err|test_comptime_divExact_remainder|exact division produced remainder#}6032 {#code|test_comptime_divExact_remainder.zig#}
10323comptime {6033
10324 const a: u32 = 10;
10325 const b: u32 = 3;
10326 const c = @divExact(a, b);
10327 _ = c;
10328}
10329 {#code_end#}
10330 <p>At runtime:</p>6034 <p>At runtime:</p>
10331 {#code_begin|exe_err|runtime_divExact_remainder#}6035 {#code|runtime_divExact_remainder.zig#}
10332const std = @import("std");6036
10333
10334pub fn main() void {
10335 var a: u32 = 10;
10336 var b: u32 = 3;
10337 _ = .{ &a, &b };
10338 const c = @divExact(a, b);
10339 std.debug.print("value: {}\n", .{c});
10340}
10341 {#code_end#}
10342 {#header_close#}6037 {#header_close#}
10343 {#header_open|Attempt to Unwrap Null#}6038 {#header_open|Attempt to Unwrap Null#}
10344 <p>At compile-time:</p>6039 <p>At compile-time:</p>
10345 {#code_begin|test_err|test_comptime_unwrap_null|unable to unwrap null#}6040 {#code|test_comptime_unwrap_null.zig#}
10346comptime {6041
10347 const optional_number: ?i32 = null;
10348 const number = optional_number.?;
10349 _ = number;
10350}
10351 {#code_end#}
10352 <p>At runtime:</p>6042 <p>At runtime:</p>
10353 {#code_begin|exe_err|runtime_unwrap_null#}6043 {#code|runtime_unwrap_null.zig#}
10354const std = @import("std");6044
10355
10356pub fn main() void {
10357 var optional_number: ?i32 = null;
10358 _ = &optional_number;
10359 const number = optional_number.?;
10360 std.debug.print("value: {}\n", .{number});
10361}
10362 {#code_end#}
10363 <p>One way to avoid this crash is to test for null instead of assuming non-null, with6045 <p>One way to avoid this crash is to test for null instead of assuming non-null, with
10364 the {#syntax#}if{#endsyntax#} expression:</p>6046 the {#syntax#}if{#endsyntax#} expression:</p>
10365 {#code_begin|exe|testing_null_with_if#}6047 {#code|testing_null_with_if.zig#}
10366const print = @import("std").debug.print;
10367pub fn main() void {
10368 const optional_number: ?i32 = null;
103696048
10370 if (optional_number) |number| {
10371 print("got number: {}\n", .{number});
10372 } else {
10373 print("it's null\n", .{});
10374 }
10375}
10376 {#code_end#}
10377 {#see_also|Optionals#}6049 {#see_also|Optionals#}
10378 {#header_close#}6050 {#header_close#}
10379 {#header_open|Attempt to Unwrap Error#}6051 {#header_open|Attempt to Unwrap Error#}
10380 <p>At compile-time:</p>6052 <p>At compile-time:</p>
10381 {#code_begin|test_err|test_comptime_unwrap_error|caught unexpected error 'UnableToReturnNumber'#}6053 {#code|test_comptime_unwrap_error.zig#}
10382comptime {
10383 const number = getNumberOrFail() catch unreachable;
10384 _ = number;
10385}
103866054
10387fn getNumberOrFail() !i32 {
10388 return error.UnableToReturnNumber;
10389}
10390 {#code_end#}
10391 <p>At runtime:</p>6055 <p>At runtime:</p>
10392 {#code_begin|exe_err|runtime_unwrap_error#}6056 {#code|runtime_unwrap_error.zig#}
10393const std = @import("std");
103946057
10395pub fn main() void {
10396 const number = getNumberOrFail() catch unreachable;
10397 std.debug.print("value: {}\n", .{number});
10398}
10399
10400fn getNumberOrFail() !i32 {
10401 return error.UnableToReturnNumber;
10402}
10403 {#code_end#}
10404 <p>One way to avoid this crash is to test for an error instead of assuming a successful result, with6058 <p>One way to avoid this crash is to test for an error instead of assuming a successful result, with
10405 the {#syntax#}if{#endsyntax#} expression:</p>6059 the {#syntax#}if{#endsyntax#} expression:</p>
10406 {#code_begin|exe|testing_error_with_if#}6060 {#code|testing_error_with_if.zig#}
10407const print = @import("std").debug.print;
10408
10409pub fn main() void {
10410 const result = getNumberOrFail();
104116061
10412 if (result) |number| {
10413 print("got number: {}\n", .{number});
10414 } else |err| {
10415 print("got error: {s}\n", .{@errorName(err)});
10416 }
10417}
10418
10419fn getNumberOrFail() !i32 {
10420 return error.UnableToReturnNumber;
10421}
10422 {#code_end#}
10423 {#see_also|Errors#}6062 {#see_also|Errors#}
10424 {#header_close#}6063 {#header_close#}
10425 {#header_open|Invalid Error Code#}6064 {#header_open|Invalid Error Code#}
10426 <p>At compile-time:</p>6065 <p>At compile-time:</p>
10427 {#code_begin|test_err|test_comptime_invalid_error_code|integer value '11' represents no error#}6066 {#code|test_comptime_invalid_error_code.zig#}
10428comptime {6067
10429 const err = error.AnError;
10430 const number = @intFromError(err) + 10;
10431 const invalid_err = @errorFromInt(number);
10432 _ = invalid_err;
10433}
10434 {#code_end#}
10435 <p>At runtime:</p>6068 <p>At runtime:</p>
10436 {#code_begin|exe_err|runtime_invalid_error_code#}6069 {#code|runtime_invalid_error_code.zig#}
10437const std = @import("std");6070
10438
10439pub fn main() void {
10440 const err = error.AnError;
10441 var number = @intFromError(err) + 500;
10442 _ = &number;
10443 const invalid_err = @errorFromInt(number);
10444 std.debug.print("value: {}\n", .{invalid_err});
10445}
10446 {#code_end#}
10447 {#header_close#}6071 {#header_close#}
10448 {#header_open|Invalid Enum Cast#}6072 {#header_open|Invalid Enum Cast#}
10449 <p>At compile-time:</p>6073 <p>At compile-time:</p>
10450 {#code_begin|test_err|test_comptime_invalid_enum_cast|enum 'test_comptime_invalid_enum_cast.Foo' has no tag with value '3'#}6074 {#code|test_comptime_invalid_enum_cast.zig#}
10451const Foo = enum {
10452 a,
10453 b,
10454 c,
10455};
10456comptime {
10457 const a: u2 = 3;
10458 const b: Foo = @enumFromInt(a);
10459 _ = b;
10460}
10461 {#code_end#}
10462 <p>At runtime:</p>
10463 {#code_begin|exe_err|runtime_invalid_enum_cast#}
10464const std = @import("std");
104656075
10466const Foo = enum {6076 <p>At runtime:</p>
10467 a,6077 {#code|runtime_invalid_enum_cast.zig#}
10468 b,
10469 c,
10470};
104716078
10472pub fn main() void {
10473 var a: u2 = 3;
10474 _ = &a;
10475 const b: Foo = @enumFromInt(a);
10476 std.debug.print("value: {s}\n", .{@tagName(b)});
10477}
10478 {#code_end#}
10479 {#header_close#}6079 {#header_close#}
104806080
10481 {#header_open|Invalid Error Set Cast#}6081 {#header_open|Invalid Error Set Cast#}
10482 <p>At compile-time:</p>6082 <p>At compile-time:</p>
10483 {#code_begin|test_err|test_comptime_invalid_error_set_cast|'error.B' not a member of error set 'error{A,C}'#}6083 {#code|test_comptime_invalid_error_set_cast.zig#}
10484const Set1 = error{6084
10485 A,
10486 B,
10487};
10488const Set2 = error{
10489 A,
10490 C,
10491};
10492comptime {
10493 _ = @as(Set2, @errorCast(Set1.B));
10494}
10495 {#code_end#}
10496 <p>At runtime:</p>6085 <p>At runtime:</p>
10497 {#code_begin|exe_err|runtime_invalid_error_set_cast#}6086 {#code|runtime_invalid_error_set_cast.zig#}
10498const std = @import("std");
104996087
10500const Set1 = error{
10501 A,
10502 B,
10503};
10504const Set2 = error{
10505 A,
10506 C,
10507};
10508pub fn main() void {
10509 foo(Set1.B);
10510}
10511fn foo(set1: Set1) void {
10512 const x: Set2 = @errorCast(set1);
10513 std.debug.print("value: {}\n", .{x});
10514}
10515 {#code_end#}
10516 {#header_close#}6088 {#header_close#}
105176089
10518 {#header_open|Incorrect Pointer Alignment#}6090 {#header_open|Incorrect Pointer Alignment#}
10519 <p>At compile-time:</p>6091 <p>At compile-time:</p>
10520 {#code_begin|test_err|test_comptime_incorrect_pointer_alignment|pointer address 0x1 is not aligned to 4 bytes#}6092 {#code|test_comptime_incorrect_pointer_alignment.zig#}
10521comptime {6093
10522 const ptr: *align(1) i32 = @ptrFromInt(0x1);
10523 const aligned: *align(4) i32 = @alignCast(ptr);
10524 _ = aligned;
10525}
10526 {#code_end#}
10527 <p>At runtime:</p>6094 <p>At runtime:</p>
10528 {#code_begin|exe_err|runtime_incorrect_pointer_alignment#}6095 {#code|runtime_incorrect_pointer_alignment.zig#}
10529const mem = @import("std").mem;6096
10530pub fn main() !void {
10531 var array align(4) = [_]u32{ 0x11111111, 0x11111111 };
10532 const bytes = mem.sliceAsBytes(array[0..]);
10533 if (foo(bytes) != 0x11111111) return error.Wrong;
10534}
10535fn foo(bytes: []u8) u32 {
10536 const slice4 = bytes[1..5];
10537 const int_slice = mem.bytesAsSlice(u32, @as([]align(4) u8, @alignCast(slice4)));
10538 return int_slice[0];
10539}
10540 {#code_end#}
10541 {#header_close#}6097 {#header_close#}
10542 {#header_open|Wrong Union Field Access#}6098 {#header_open|Wrong Union Field Access#}
10543 <p>At compile-time:</p>6099 <p>At compile-time:</p>
10544 {#code_begin|test_err|test_comptime_wrong_union_field_access|access of union field 'float' while field 'int' is active#}6100 {#code|test_comptime_wrong_union_field_access.zig#}
10545comptime {
10546 var f = Foo{ .int = 42 };
10547 f.float = 12.34;
10548}
105496101
10550const Foo = union {
10551 float: f32,
10552 int: u32,
10553};
10554 {#code_end#}
10555 <p>At runtime:</p>6102 <p>At runtime:</p>
10556 {#code_begin|exe_err|runtime_wrong_union_field_access#}6103 {#code|runtime_wrong_union_field_access.zig#}
10557const std = @import("std");
10558
10559const Foo = union {
10560 float: f32,
10561 int: u32,
10562};
105636104
10564pub fn main() void {
10565 var f = Foo{ .int = 42 };
10566 bar(&f);
10567}
10568
10569fn bar(f: *Foo) void {
10570 f.float = 12.34;
10571 std.debug.print("value: {}\n", .{f.float});
10572}
10573 {#code_end#}
10574 <p>6105 <p>
10575 This safety is not available for {#syntax#}extern{#endsyntax#} or {#syntax#}packed{#endsyntax#} unions.6106 This safety is not available for {#syntax#}extern{#endsyntax#} or {#syntax#}packed{#endsyntax#} unions.
10576 </p>6107 </p>
10577 <p>6108 <p>
10578 To change the active field of a union, assign the entire union, like this:6109 To change the active field of a union, assign the entire union, like this:
10579 </p>6110 </p>
10580 {#code_begin|exe|change_active_union_field#}6111 {#code|change_active_union_field.zig#}
10581const std = @import("std");
10582
10583const Foo = union {
10584 float: f32,
10585 int: u32,
10586};
10587
10588pub fn main() void {
10589 var f = Foo{ .int = 42 };
10590 bar(&f);
10591}
105926112
10593fn bar(f: *Foo) void {
10594 f.* = Foo{ .float = 12.34 };
10595 std.debug.print("value: {}\n", .{f.float});
10596}
10597 {#code_end#}
10598 <p>6113 <p>
10599 To change the active field of a union when a meaningful value for the field is not known,6114 To change the active field of a union when a meaningful value for the field is not known,
10600 use {#link|undefined#}, like this:6115 use {#link|undefined#}, like this:
10601 </p>6116 </p>
10602 {#code_begin|exe|undefined_active_union_field#}6117 {#code|undefined_active_union_field.zig#}
10603const std = @import("std");
10604
10605const Foo = union {
10606 float: f32,
10607 int: u32,
10608};
10609
10610pub fn main() void {
10611 var f = Foo{ .int = 42 };
10612 f = Foo{ .float = undefined };
10613 bar(&f);
10614 std.debug.print("value: {}\n", .{f.float});
10615}
106166118
10617fn bar(f: *Foo) void {
10618 f.float = 12.34;
10619}
10620 {#code_end#}
10621 {#see_also|union|extern union#}6119 {#see_also|union|extern union#}
10622 {#header_close#}6120 {#header_close#}
106236121
...@@ -10627,22 +6125,11 @@ fn bar(f: *Foo) void {...@@ -10627,22 +6125,11 @@ fn bar(f: *Foo) void {
10627 integer type's range.6125 integer type's range.
10628 </p>6126 </p>
10629 <p>At compile-time:</p>6127 <p>At compile-time:</p>
10630 {#code_begin|test_err|test_comptime_out_of_bounds_float_to_integer_cast|float value '4294967296' cannot be stored in integer type 'i32'#}6128 {#code|test_comptime_out_of_bounds_float_to_integer_cast.zig#}
10631comptime {6129
10632 const float: f32 = 4294967296;
10633 const int: i32 = @intFromFloat(float);
10634 _ = int;
10635}
10636 {#code_end#}
10637 <p>At runtime:</p>6130 <p>At runtime:</p>
10638 {#code_begin|exe_err|runtime_out_of_bounds_float_to_integer_cast#}6131 {#code|runtime_out_of_bounds_float_to_integer_cast.zig#}
10639pub fn main() void {6132
10640 var float: f32 = 4294967296; // runtime-known
10641 _ = &float;
10642 const int: i32 = @intFromFloat(float);
10643 _ = int;
10644}
10645 {#code_end#}
10646 {#header_close#}6133 {#header_close#}
106476134
10648 {#header_open|Pointer Cast Invalid Null#}6135 {#header_open|Pointer Cast Invalid Null#}
...@@ -10652,22 +6139,11 @@ pub fn main() void {...@@ -10652,22 +6139,11 @@ pub fn main() void {
10652 allow address zero, but normal {#link|Pointers#} do not.6139 allow address zero, but normal {#link|Pointers#} do not.
10653 </p>6140 </p>
10654 <p>At compile-time:</p>6141 <p>At compile-time:</p>
10655 {#code_begin|test_err|test_comptime_invalid_null_pointer_cast|null pointer casted to type#}6142 {#code|test_comptime_invalid_null_pointer_cast.zig#}
10656comptime {6143
10657 const opt_ptr: ?*i32 = null;
10658 const ptr: *i32 = @ptrCast(opt_ptr);
10659 _ = ptr;
10660}
10661 {#code_end#}
10662 <p>At runtime:</p>6144 <p>At runtime:</p>
10663 {#code_begin|exe_err|runtime_invalid_null_pointer_cast#}6145 {#code|runtime_invalid_null_pointer_cast.zig#}
10664pub fn main() void {6146
10665 var opt_ptr: ?*i32 = null;
10666 _ = &opt_ptr;
10667 const ptr: *i32 = @ptrCast(opt_ptr);
10668 _ = ptr;
10669}
10670 {#code_end#}
10671 {#header_close#}6147 {#header_close#}
106726148
10673 {#header_close#}6149 {#header_close#}
...@@ -10689,26 +6165,8 @@ pub fn main() void {...@@ -10689,26 +6165,8 @@ pub fn main() void {
10689 {#syntax#}std.ArrayList{#endsyntax#} accept an {#syntax#}Allocator{#endsyntax#} parameter in6165 {#syntax#}std.ArrayList{#endsyntax#} accept an {#syntax#}Allocator{#endsyntax#} parameter in
10690 their initialization functions:6166 their initialization functions:
10691 </p>6167 </p>
10692 {#code_begin|test|test_allocator#}6168 {#code|test_allocator.zig#}
10693const std = @import("std");
10694const Allocator = std.mem.Allocator;
10695const expect = std.testing.expect;
10696
10697test "using an allocator" {
10698 var buffer: [100]u8 = undefined;
10699 var fba = std.heap.FixedBufferAllocator.init(&buffer);
10700 const allocator = fba.allocator();
10701 const result = try concat(allocator, "foo", "bar");
10702 try expect(std.mem.eql(u8, "foobar", result));
10703}
107046169
10705fn concat(allocator: Allocator, a: []const u8, b: []const u8) ![]u8 {
10706 const result = try allocator.alloc(u8, a.len + b.len);
10707 @memcpy(result[0..a.len], a);
10708 @memcpy(result[a.len..], b);
10709 return result;
10710}
10711 {#code_end#}
10712 <p>6170 <p>
10713 In the above example, 100 bytes of stack memory are used to initialize a6171 In the above example, 100 bytes of stack memory are used to initialize a
10714 {#syntax#}FixedBufferAllocator{#endsyntax#}, which is then passed to a function.6172 {#syntax#}FixedBufferAllocator{#endsyntax#}, which is then passed to a function.
...@@ -10743,19 +6201,8 @@ fn concat(allocator: Allocator, a: []const u8, b: []const u8) ![]u8 {...@@ -10743,19 +6201,8 @@ fn concat(allocator: Allocator, a: []const u8, b: []const u8) ![]u8 {
10743 cyclical pattern (such as a video game main loop, or a web server request handler),6201 cyclical pattern (such as a video game main loop, or a web server request handler),
10744 such that it would make sense to free everything at once at the end?6202 such that it would make sense to free everything at once at the end?
10745 In this case, it is recommended to follow this pattern:6203 In this case, it is recommended to follow this pattern:
10746 {#code_begin|exe|cli_allocation#}6204 {#code|cli_allocation.zig#}
10747const std = @import("std");
10748
10749pub fn main() !void {
10750 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
10751 defer arena.deinit();
10752
10753 const allocator = arena.allocator();
107546205
10755 const ptr = try allocator.create(i32);
10756 std.debug.print("ptr={*}\n", .{ptr});
10757}
10758 {#code_end#}
10759 When using this kind of allocator, there is no need to free anything manually. Everything6206 When using this kind of allocator, there is no need to free anything manually. Everything
10760 gets freed at once with the call to {#syntax#}arena.deinit(){#endsyntax#}.6207 gets freed at once with the call to {#syntax#}arena.deinit(){#endsyntax#}.
10761 </li>6208 </li>
...@@ -10793,25 +6240,11 @@ pub fn main() !void {...@@ -10793,25 +6240,11 @@ pub fn main() !void {
10793 <p>String literals such as {#syntax#}"hello"{#endsyntax#} are in the global constant data section.6240 <p>String literals such as {#syntax#}"hello"{#endsyntax#} are in the global constant data section.
10794 This is why it is an error to pass a string literal to a mutable slice, like this:6241 This is why it is an error to pass a string literal to a mutable slice, like this:
10795 </p>6242 </p>
10796 {#code_begin|test_err|test_string_literal_to_slice|expected type '[]u8', found '*const [5:0]u8'#}6243 {#code|test_string_literal_to_slice.zig#}
10797fn foo(s: []u8) void {
10798 _ = s;
10799}
108006244
10801test "string literal to mutable slice" {
10802 foo("hello");
10803}
10804 {#code_end#}
10805 <p>However if you make the slice constant, then it works:</p>6245 <p>However if you make the slice constant, then it works:</p>
10806 {#code_begin|test|test_string_literal_to_const_slice#}6246 {#code|test_string_literal_to_const_slice.zig#}
10807fn foo(s: []const u8) void {
10808 _ = s;
10809}
108106247
10811test "string literal to constant slice" {
10812 foo("hello");
10813}
10814 {#code_end#}
10815 <p>6248 <p>
10816 Just like string literals, {#syntax#}const{#endsyntax#} declarations, when the value is known at {#link|comptime#},6249 Just like string literals, {#syntax#}const{#endsyntax#} declarations, when the value is known at {#link|comptime#},
10817 are stored in the global constant data section. Also {#link|Compile Time Variables#} are stored6250 are stored in the global constant data section. Also {#link|Compile Time Variables#} are stored
...@@ -10939,10 +6372,8 @@ test "string literal to constant slice" {...@@ -10939,10 +6372,8 @@ test "string literal to constant slice" {
10939 which the compiler makes available to every Zig source file. It contains6372 which the compiler makes available to every Zig source file. It contains
10940 compile-time constants such as the current target, endianness, and release mode.6373 compile-time constants such as the current target, endianness, and release mode.
10941 </p>6374 </p>
10942 {#code_begin|syntax|compile_variables#}6375 {#code|compile_variables.zig#}
10943const builtin = @import("builtin");6376
10944const separator = if (builtin.os.tag == .windows) '\\' else '/';
10945 {#code_end#}
10946 <p>6377 <p>
10947 Example of what is imported with {#syntax#}@import("builtin"){#endsyntax#}:6378 Example of what is imported with {#syntax#}@import("builtin"){#endsyntax#}:
10948 </p>6379 </p>
...@@ -11029,17 +6460,8 @@ const separator = if (builtin.os.tag == .windows) '\\' else '/';...@@ -11029,17 +6460,8 @@ const separator = if (builtin.os.tag == .windows) '\\' else '/';
11029 The {#syntax#}@cImport{#endsyntax#} builtin function can be used6460 The {#syntax#}@cImport{#endsyntax#} builtin function can be used
11030 to directly import symbols from <code class="file">.h</code> files:6461 to directly import symbols from <code class="file">.h</code> files:
11031 </p>6462 </p>
11032 {#code_begin|exe|cImport_builtin#}6463 {#code|cImport_builtin.zig#}
11033 {#link_libc#}6464
11034const c = @cImport({
11035 // See https://github.com/ziglang/zig/issues/515
11036 @cDefine("_NO_CRT_STDIO_INLINE", "1");
11037 @cInclude("stdio.h");
11038});
11039pub fn main() void {
11040 _ = c.printf("hello\n");
11041}
11042 {#code_end#}
11043 <p>6465 <p>
11044 The {#syntax#}@cImport{#endsyntax#} function takes an expression as a parameter.6466 The {#syntax#}@cImport{#endsyntax#} function takes an expression as a parameter.
11045 This expression is evaluated at compile-time and is used to control6467 This expression is evaluated at compile-time and is used to control
...@@ -11147,17 +6569,8 @@ pub extern fn do_something(foo: enum_FOO) c_int;{#end_shell_samp#}...@@ -11147,17 +6569,8 @@ pub extern fn do_something(foo: enum_FOO) c_int;{#end_shell_samp#}
11147 To see where the cached files are stored when compiling code that uses {#syntax#}@cImport{#endsyntax#},6569 To see where the cached files are stored when compiling code that uses {#syntax#}@cImport{#endsyntax#},
11148 use the <kbd>--verbose-cimport</kbd> flag:6570 use the <kbd>--verbose-cimport</kbd> flag:
11149 </p>6571 </p>
11150 {#code_begin|exe|verbose_cimport_flag#}6572 {#code|verbose_cimport_flag.zig#}
11151 {#link_libc#}6573
11152 {#code_verbose_cimport#}
11153const c = @cImport({
11154 @cDefine("_NO_CRT_STDIO_INLINE", "1");
11155 @cInclude("stdio.h");
11156});
11157pub fn main() void {
11158 _ = c;
11159}
11160 {#code_end#}
11161 <p>6574 <p>
11162 <code class="file">cimport.h</code> contains the file to translate (constructed from calls to6575 <code class="file">cimport.h</code> contains the file to translate (constructed from calls to
11163 {#syntax#}@cInclude{#endsyntax#}, {#syntax#}@cDefine{#endsyntax#}, and {#syntax#}@cUndef{#endsyntax#}),6576 {#syntax#}@cInclude{#endsyntax#}, {#syntax#}@cDefine{#endsyntax#}, and {#syntax#}@cUndef{#endsyntax#}),
...@@ -11211,16 +6624,8 @@ int foo(void) {...@@ -11211,16 +6624,8 @@ int foo(void) {
11211}6624}
11212 {#end_syntax_block#}6625 {#end_syntax_block#}
11213 {#shell_samp#}$ zig translate-c macro.c > macro.zig{#end_shell_samp#}6626 {#shell_samp#}$ zig translate-c macro.c > macro.zig{#end_shell_samp#}
11214 {#code_begin|syntax|macro#}6627 {#code|macro.zig#}
11215pub export fn foo() c_int {6628
11216 var a: c_int = 1;
11217 _ = &a;
11218 var b: c_int = 2;
11219 _ = &b;
11220 return a + b;
11221}
11222pub const MAKELOCAL = @compileError("unable to translate C expr: unexpected token .Equal"); // macro.c:1:9
11223 {#code_end#}
11224 <p>Note that {#syntax#}foo{#endsyntax#} was translated correctly despite using a non-translatable6629 <p>Note that {#syntax#}foo{#endsyntax#} was translated correctly despite using a non-translatable
11225 macro. {#syntax#}MAKELOCAL{#endsyntax#} was demoted to {#syntax#}@compileError{#endsyntax#} since6630 macro. {#syntax#}MAKELOCAL{#endsyntax#} was demoted to {#syntax#}@compileError{#endsyntax#} since
11226 it cannot be expressed as a Zig function; this simply means that you cannot directly use6631 it cannot be expressed as a Zig function; this simply means that you cannot directly use
...@@ -11267,53 +6672,13 @@ pub const MAKELOCAL = @compileError("unable to translate C expr: unexpected toke...@@ -11267,53 +6672,13 @@ pub const MAKELOCAL = @compileError("unable to translate C expr: unexpected toke
112676672
11268 {#header_open|C Variadic Functions#}6673 {#header_open|C Variadic Functions#}
11269 <p>Zig supports extern variadic functions.</p>6674 <p>Zig supports extern variadic functions.</p>
11270 {#code_begin|test|test_variadic_function#}6675 {#code|test_variadic_function.zig#}
11271 {#link_libc#}
11272 {#code_verbose_cimport#}
11273const std = @import("std");
11274const testing = std.testing;
11275
11276pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
112776676
11278test "variadic function" {
11279 try testing.expect(printf("Hello, world!\n") == 14);
11280 try testing.expect(@typeInfo(@TypeOf(printf)).Fn.is_var_args);
11281}
11282 {#code_end#}
11283 <p>6677 <p>
11284 Variadic functions can be implemented using {#link|@cVaStart#}, {#link|@cVaEnd#}, {#link|@cVaArg#} and {#link|@cVaCopy#}.6678 Variadic functions can be implemented using {#link|@cVaStart#}, {#link|@cVaEnd#}, {#link|@cVaArg#} and {#link|@cVaCopy#}.
11285 </p>6679 </p>
11286 {#code_begin|test|test_defining_variadic_function#}6680 {#code|test_defining_variadic_function.zig#}
11287const std = @import("std");
11288const testing = std.testing;
11289const builtin = @import("builtin");
11290
11291fn add(count: c_int, ...) callconv(.C) c_int {
11292 var ap = @cVaStart();
11293 defer @cVaEnd(&ap);
11294 var i: usize = 0;
11295 var sum: c_int = 0;
11296 while (i < count) : (i += 1) {
11297 sum += @cVaArg(&ap, c_int);
11298 }
11299 return sum;
11300}
11301
11302test "defining a variadic function" {
11303 if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) {
11304 // https://github.com/ziglang/zig/issues/14096
11305 return error.SkipZigTest;
11306 }
11307 if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) {
11308 // https://github.com/ziglang/zig/issues/16961
11309 return error.SkipZigTest;
11310 }
113116681
11312 try std.testing.expectEqual(@as(c_int, 0), add(0));
11313 try std.testing.expectEqual(@as(c_int, 1), add(1, @as(c_int, 1)));
11314 try std.testing.expectEqual(@as(c_int, 3), add(2, @as(c_int, 1), @as(c_int, 2)));
11315}
11316 {#code_end#}
11317 {#header_close#}6682 {#header_close#}
11318 {#header_open|Exporting a C Library#}6683 {#header_open|Exporting a C Library#}
11319 <p>6684 <p>
...@@ -11321,11 +6686,8 @@ test "defining a variadic function" {...@@ -11321,11 +6686,8 @@ test "defining a variadic function" {
11321 to call into. The {#syntax#}export{#endsyntax#} keyword in front of functions, variables, and types causes them to6686 to call into. The {#syntax#}export{#endsyntax#} keyword in front of functions, variables, and types causes them to
11322 be part of the library API:6687 be part of the library API:
11323 </p>6688 </p>
11324 {#code_begin|syntax|mathtest#}6689 {#code|mathtest.zig#}
11325export fn add(a: i32, b: i32) i32 {6690
11326 return a + b;
11327}
11328 {#code_end#}
11329 <p>To make a static library:</p>6691 <p>To make a static library:</p>
11330 {#shell_samp#}$ zig build-lib mathtest.zig{#end_shell_samp#}6692 {#shell_samp#}$ zig build-lib mathtest.zig{#end_shell_samp#}
11331 <p>To make a shared library:</p>6693 <p>To make a shared library:</p>
...@@ -11342,30 +6704,8 @@ int main(int argc, char **argv) {...@@ -11342,30 +6704,8 @@ int main(int argc, char **argv) {
11342 return 0;6704 return 0;
11343}6705}
11344 {#end_syntax_block#}6706 {#end_syntax_block#}
11345 {#code_begin|syntax|build_c#}6707 {#code|build_c.zig#}
11346const std = @import("std");6708
11347
11348pub fn build(b: *std.Build) void {
11349 const lib = b.addSharedLibrary(.{
11350 .name = "mathtest",
11351 .root_source_file = .{ .path = "mathtest.zig" },
11352 .version = .{ .major = 1, .minor = 0, .patch = 0 },
11353 });
11354 const exe = b.addExecutable(.{
11355 .name = "test",
11356 });
11357 exe.addCSourceFile(.{ .file = .{ .path = "test.c" }, .flags = &.{"-std=c99"} });
11358 exe.linkLibrary(lib);
11359 exe.linkSystemLibrary("c");
11360
11361 b.default_step.dependOn(&exe.step);
11362
11363 const run_cmd = exe.run();
11364
11365 const test_step = b.step("test", "Test the program");
11366 test_step.dependOn(&run_cmd.step);
11367}
11368 {#code_end#}
11369 {#shell_samp#}$ zig build test6709 {#shell_samp#}$ zig build test
113701379{#end_shell_samp#}67101379{#end_shell_samp#}
11371 {#see_also|export#}6711 {#see_also|export#}
...@@ -11374,23 +6714,8 @@ pub fn build(b: *std.Build) void {...@@ -11374,23 +6714,8 @@ pub fn build(b: *std.Build) void {
11374 <p>6714 <p>
11375 You can mix Zig object files with any other object files that respect the C ABI. Example:6715 You can mix Zig object files with any other object files that respect the C ABI. Example:
11376 </p>6716 </p>
11377 {#code_begin|syntax|base64#}6717 {#code|base64.zig#}
11378const base64 = @import("std").base64;6718
11379
11380export fn decode_base_64(
11381 dest_ptr: [*]u8,
11382 dest_len: usize,
11383 source_ptr: [*]const u8,
11384 source_len: usize,
11385) usize {
11386 const src = source_ptr[0..source_len];
11387 const dest = dest_ptr[0..dest_len];
11388 const base64_decoder = base64.standard.Decoder;
11389 const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable;
11390 base64_decoder.decode(dest[0..decoded_size], src) catch unreachable;
11391 return decoded_size;
11392}
11393 {#code_end#}
11394 {#syntax_block|c|test.c#}6719 {#syntax_block|c|test.c#}
11395// This header is generated by zig from base64.zig6720// This header is generated by zig from base64.zig
11396#include "base64.h"6721#include "base64.h"
...@@ -11409,24 +6734,8 @@ int main(int argc, char **argv) {...@@ -11409,24 +6734,8 @@ int main(int argc, char **argv) {
11409 return 0;6734 return 0;
11410}6735}
11411 {#end_syntax_block#}6736 {#end_syntax_block#}
11412 {#code_begin|syntax|build_object#}6737 {#code|build_object.zig#}
11413const std = @import("std");6738
11414
11415pub fn build(b: *std.Build) void {
11416 const obj = b.addObject(.{
11417 .name = "base64",
11418 .root_source_file = .{ .path = "base64.zig" },
11419 });
11420
11421 const exe = b.addExecutable(.{
11422 .name = "test",
11423 });
11424 exe.addCSourceFile(.{ .file = .{ .path = "test.c" }, .flags = &.{"-std=c99",} });
11425 exe.addObject(obj);
11426 exe.linkSystemLibrary("c");
11427 b.installArtifact(exe);
11428}
11429 {#code_end#}
11430 {#shell_samp#}$ zig build6739 {#shell_samp#}$ zig build
11431$ ./zig-out/bin/test6740$ ./zig-out/bin/test
11432all your base are belong to us{#end_shell_samp#}6741all your base are belong to us{#end_shell_samp#}
...@@ -11438,16 +6747,8 @@ all your base are belong to us{#end_shell_samp#}...@@ -11438,16 +6747,8 @@ all your base are belong to us{#end_shell_samp#}
11438 {#header_open|Freestanding#}6747 {#header_open|Freestanding#}
11439 <p>For host environments like the web browser and nodejs, build as an executable using the freestanding6748 <p>For host environments like the web browser and nodejs, build as an executable using the freestanding
11440 OS target. Here's an example of running Zig code compiled to WebAssembly with nodejs.</p>6749 OS target. Here's an example of running Zig code compiled to WebAssembly with nodejs.</p>
11441 {#code_begin|exe|math#}6750 {#code|math.zig#}
11442 {#target_wasm#}6751
11443 {#additonal_option|-fno-entry#}
11444 {#additonal_option|--export=add#}
11445extern fn print(i32) void;
11446
11447export fn add(a: i32, b: i32) void {
11448 print(a + b);
11449}
11450 {#code_end#}
11451 {#syntax_block|javascript|test.js#}6752 {#syntax_block|javascript|test.js#}
11452const fs = require('fs');6753const fs = require('fs');
11453const source = fs.readFileSync("./math.wasm");6754const source = fs.readFileSync("./math.wasm");
...@@ -11467,47 +6768,16 @@ The result is 3{#end_shell_samp#}...@@ -11467,47 +6768,16 @@ The result is 3{#end_shell_samp#}
11467 {#header_open|WASI#}6768 {#header_open|WASI#}
11468 <p>Zig's support for WebAssembly System Interface (WASI) is under active development.6769 <p>Zig's support for WebAssembly System Interface (WASI) is under active development.
11469 Example of using the standard library and reading command line arguments:</p>6770 Example of using the standard library and reading command line arguments:</p>
11470 {#code_begin|exe|wasi_args#}6771 {#code|wasi_args.zig#}
11471 {#target_wasi#}6772
11472const std = @import("std");
11473
11474pub fn main() !void {
11475 var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
11476 const gpa = general_purpose_allocator.allocator();
11477 const args = try std.process.argsAlloc(gpa);
11478 defer std.process.argsFree(gpa, args);
11479
11480 for (args, 0..) |arg, i| {
11481 std.debug.print("{}: {s}\n", .{ i, arg });
11482 }
11483}
11484 {#code_end#}
11485 {#shell_samp#}$ wasmtime wasi_args.wasm 123 hello6773 {#shell_samp#}$ wasmtime wasi_args.wasm 123 hello
114860: wasi_args.wasm67740: wasi_args.wasm
114871: 12367751: 123
114882: hello{#end_shell_samp#}67762: hello{#end_shell_samp#}
11489 <p>A more interesting example would be extracting the list of preopens from the runtime.6777 <p>A more interesting example would be extracting the list of preopens from the runtime.
11490 This is now supported in the standard library via {#syntax#}std.fs.wasi.Preopens{#endsyntax#}:</p>6778 This is now supported in the standard library via {#syntax#}std.fs.wasi.Preopens{#endsyntax#}:</p>
11491 {#code_begin|exe|wasi_preopens#}6779 {#code|wasi_preopens.zig#}
11492 {#target_wasi#}
11493const std = @import("std");
11494const fs = std.fs;
114956780
11496pub fn main() !void {
11497 var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
11498 const gpa = general_purpose_allocator.allocator();
11499
11500 var arena_instance = std.heap.ArenaAllocator.init(gpa);
11501 defer arena_instance.deinit();
11502 const arena = arena_instance.allocator();
11503
11504 const preopens = try fs.wasi.preopensAlloc(arena);
11505
11506 for (preopens.names, 0..) |preopen, i| {
11507 std.debug.print("{}: {s}\n", .{ i, preopen });
11508 }
11509}
11510 {#code_end#}
11511 {#shell_samp#}$ wasmtime --dir=. wasi_preopens.wasm6781 {#shell_samp#}$ wasmtime --dir=. wasi_preopens.wasm
115120: stdin67820: stdin
115131: stdout67831: stdout
...@@ -11573,21 +6843,8 @@ coding style....@@ -11573,21 +6843,8 @@ coding style.
11573 <p>Every declaration is assigned a <strong>fully qualified6843 <p>Every declaration is assigned a <strong>fully qualified
11574 namespace</strong> by the compiler, creating a tree structure. Choose names based6844 namespace</strong> by the compiler, creating a tree structure. Choose names based
11575 on the fully-qualified namespace, and avoid redundant name segments.</p>6845 on the fully-qualified namespace, and avoid redundant name segments.</p>
11576 {#code_begin|exe|redundant_fqn#}6846 {#code|redundant_fqn.zig#}
11577const std = @import("std");
11578
11579pub const json = struct {
11580 pub const JsonValue = union(enum) {
11581 number: f64,
11582 boolean: bool,
11583 // ...
11584 };
11585};
115866847
11587pub fn main() void {
11588 std.debug.print("{s}\n", .{@typeName(json.JsonValue)});
11589}
11590 {#code_end#}
11591 <p>In this example, "json" is repeated in the fully-qualified namespace. The solution6848 <p>In this example, "json" is repeated in the fully-qualified namespace. The solution
11592 is to delete <code>Json</code> from <code>JsonValue</code>. In this example we have6849 is to delete <code>Json</code> from <code>JsonValue</code>. In this example we have
11593 an empty struct named <code>json</code> but remember that files also act6850 an empty struct named <code>json</code> but remember that files also act
doc/langref/Assembly Syntax Explained.zig created+60
...@@ -0,0 +1,60 @@
1pub fn syscall1(number: usize, arg1: usize) usize {
2 // Inline assembly is an expression which returns a value.
3 // the `asm` keyword begins the expression.
4 return asm
5 // `volatile` is an optional modifier that tells Zig this
6 // inline assembly expression has side-effects. Without
7 // `volatile`, Zig is allowed to delete the inline assembly
8 // code if the result is unused.
9 volatile (
10 // Next is a comptime string which is the assembly code.
11 // Inside this string one may use `%[ret]`, `%[number]`,
12 // or `%[arg1]` where a register is expected, to specify
13 // the register that Zig uses for the argument or return value,
14 // if the register constraint strings are used. However in
15 // the below code, this is not used. A literal `%` can be
16 // obtained by escaping it with a double percent: `%%`.
17 // Often multiline string syntax comes in handy here.
18 \\syscall
19 // Next is the output. It is possible in the future Zig will
20 // support multiple outputs, depending on how
21 // https://github.com/ziglang/zig/issues/215 is resolved.
22 // It is allowed for there to be no outputs, in which case
23 // this colon would be directly followed by the colon for the inputs.
24 :
25 // This specifies the name to be used in `%[ret]` syntax in
26 // the above assembly string. This example does not use it,
27 // but the syntax is mandatory.
28 [ret]
29 // Next is the output constraint string. This feature is still
30 // considered unstable in Zig, and so LLVM/GCC documentation
31 // must be used to understand the semantics.
32 // http://releases.llvm.org/10.0.0/docs/LangRef.html#inline-asm-constraint-string
33 // https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
34 // In this example, the constraint string means "the result value of
35 // this inline assembly instruction is whatever is in $rax".
36 "={rax}"
37 // Next is either a value binding, or `->` and then a type. The
38 // type is the result type of the inline assembly expression.
39 // If it is a value binding, then `%[ret]` syntax would be used
40 // to refer to the register bound to the value.
41 (-> usize),
42 // Next is the list of inputs.
43 // The constraint for these inputs means, "when the assembly code is
44 // executed, $rax shall have the value of `number` and $rdi shall have
45 // the value of `arg1`". Any number of input parameters is allowed,
46 // including none.
47 : [number] "{rax}" (number),
48 [arg1] "{rdi}" (arg1),
49 // Next is the list of clobbers. These declare a set of registers whose
50 // values will not be preserved by the execution of this assembly code.
51 // These do not include output or input registers. The special clobber
52 // value of "memory" means that the assembly writes to arbitrary undeclared
53 // memory locations - not only the memory pointed to by a declared indirect
54 // output. In this example we list $rcx and $r11 because it is known the
55 // kernel syscall does not preserve these registers.
56 : "rcx", "r11"
57 );
58}
59
60// syntax
doc/langref/addWithOverflow_builtin.zig created+13
...@@ -0,0 +1,13 @@
1const print = @import("std").debug.print;
2pub fn main() void {
3 const byte: u8 = 255;
4
5 const ov = @addWithOverflow(byte, 10);
6 if (ov[1] != 0) {
7 print("overflowed result: {}\n", .{ov[0]});
8 } else {
9 print("result: {}\n", .{ov[0]});
10 }
11}
12
13// exe=succeed
doc/langref/anonymous_struct_name.zig created+16
...@@ -0,0 +1,16 @@
1const Node = struct {
2 next: ?*Node,
3 name: []const u8,
4};
5
6var node_a = Node{
7 .next = null,
8 .name = "Node A",
9};
10
11var node_b = Node{
12 .next = &node_a,
13 .name = "Node B",
14};
15
16// syntax
doc/langref/assign_undefined.zig created+9
...@@ -0,0 +1,9 @@
1const print = @import("std").debug.print;
2
3pub fn main() void {
4 var x: i32 = undefined;
5 x = 1;
6 print("{d}", .{x});
7}
8
9// exe=succeed
doc/langref/bad_default_value.zig created+26
...@@ -0,0 +1,26 @@
1const Threshold = struct {
2 minimum: f32 = 0.25,
3 maximum: f32 = 0.75,
4
5 const Category = enum { low, medium, high };
6
7 fn categorize(t: Threshold, value: f32) Category {
8 assert(t.maximum >= t.minimum);
9 if (value < t.minimum) return .low;
10 if (value > t.maximum) return .high;
11 return .medium;
12 }
13};
14
15pub fn main() !void {
16 var threshold: Threshold = .{
17 .maximum = 0.20,
18 };
19 const category = threshold.categorize(0.90);
20 try std.io.getStdOut().writeAll(@tagName(category));
21}
22
23const std = @import("std");
24const assert = std.debug.assert;
25
26// exe=fail
doc/langref/base64.zig created+17
...@@ -0,0 +1,17 @@
1const base64 = @import("std").base64;
2
3export fn decode_base_64(
4 dest_ptr: [*]u8,
5 dest_len: usize,
6 source_ptr: [*]const u8,
7 source_len: usize,
8) usize {
9 const src = source_ptr[0..source_len];
10 const dest = dest_ptr[0..dest_len];
11 const base64_decoder = base64.standard.Decoder;
12 const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable;
13 base64_decoder.decode(dest[0..decoded_size], src) catch unreachable;
14 return decoded_size;
15}
16
17// syntax
doc/langref/build.zig created+13
...@@ -0,0 +1,13 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const optimize = b.standardOptimizeOption(.{});
5 const exe = b.addExecutable(.{
6 .name = "example",
7 .root_source_file = .{ .path = "example.zig" },
8 .optimize = optimize,
9 });
10 b.default_step.dependOn(&exe.step);
11}
12
13// syntax
doc/langref/build_c.zig created+24
...@@ -0,0 +1,24 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const lib = b.addSharedLibrary(.{
5 .name = "mathtest",
6 .root_source_file = .{ .path = "mathtest.zig" },
7 .version = .{ .major = 1, .minor = 0, .patch = 0 },
8 });
9 const exe = b.addExecutable(.{
10 .name = "test",
11 });
12 exe.addCSourceFile(.{ .file = .{ .path = "test.c" }, .flags = &.{"-std=c99"} });
13 exe.linkLibrary(lib);
14 exe.linkSystemLibrary("c");
15
16 b.default_step.dependOn(&exe.step);
17
18 const run_cmd = exe.run();
19
20 const test_step = b.step("test", "Test the program");
21 test_step.dependOn(&run_cmd.step);
22}
23
24// syntax
doc/langref/build_object.zig created+18
...@@ -0,0 +1,18 @@
1const std = @import("std");
2
3pub fn build(b: *std.Build) void {
4 const obj = b.addObject(.{
5 .name = "base64",
6 .root_source_file = .{ .path = "base64.zig" },
7 });
8
9 const exe = b.addExecutable(.{
10 .name = "test",
11 });
12 exe.addCSourceFile(.{ .file = .{ .path = "test.c" }, .flags = &.{"-std=c99",} });
13 exe.addObject(obj);
14 exe.linkSystemLibrary("c");
15 b.installArtifact(exe);
16}
17
18// syntax
doc/langref/builtin.CallModifier struct.zig created+35
...@@ -0,0 +1,35 @@
1pub const CallModifier = enum {
2 /// Equivalent to function call syntax.
3 auto,
4
5 /// Equivalent to async keyword used with function call syntax.
6 async_kw,
7
8 /// Prevents tail call optimization. This guarantees that the return
9 /// address will point to the callsite, as opposed to the callsite's
10 /// callsite. If the call is otherwise required to be tail-called
11 /// or inlined, a compile error is emitted instead.
12 never_tail,
13
14 /// Guarantees that the call will not be inlined. If the call is
15 /// otherwise required to be inlined, a compile error is emitted instead.
16 never_inline,
17
18 /// Asserts that the function call will not suspend. This allows a
19 /// non-async function to call an async function.
20 no_async,
21
22 /// Guarantees that the call will be generated with tail call optimization.
23 /// If this is not possible, a compile error is emitted instead.
24 always_tail,
25
26 /// Guarantees that the call will inlined at the callsite.
27 /// If this is not possible, a compile error is emitted instead.
28 always_inline,
29
30 /// Evaluates the call at compile-time. If the call cannot be completed at
31 /// compile-time, a compile error is emitted instead.
32 compile_time,
33};
34
35// syntax
doc/langref/cImport_builtin.zig created+11
...@@ -0,0 +1,11 @@
1const c = @cImport({
2 // See https://github.com/ziglang/zig/issues/515
3 @cDefine("_NO_CRT_STDIO_INLINE", "1");
4 @cInclude("stdio.h");
5});
6pub fn main() void {
7 _ = c.printf("hello\n");
8}
9
10// exe=succeed
11// link_libc
doc/langref/catch.zig created+8
...@@ -0,0 +1,8 @@
1const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
2
3fn doAThing(str: []u8) void {
4 const number = parseU64(str, 10) catch 13;
5 _ = number; // ...
6}
7
8// syntax
doc/langref/catch_err_return.zig created+8
...@@ -0,0 +1,8 @@
1const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
2
3fn doAThing(str: []u8) !void {
4 const number = parseU64(str, 10) catch |err| return err;
5 _ = number; // ...
6}
7
8// syntax
doc/langref/change_active_union_field.zig created+18
...@@ -0,0 +1,18 @@
1const std = @import("std");
2
3const Foo = union {
4 float: f32,
5 int: u32,
6};
7
8pub fn main() void {
9 var f = Foo{ .int = 42 };
10 bar(&f);
11}
12
13fn bar(f: *Foo) void {
14 f.* = Foo{ .float = 12.34 };
15 std.debug.print("value: {}\n", .{f.float});
16}
17
18// exe=succeed
doc/langref/checking_null_in_zig.zig created+14
...@@ -0,0 +1,14 @@
1const Foo = struct{};
2fn doSomethingWithFoo(foo: *Foo) void { _ = foo; }
3
4fn doAThing(optional_foo: ?*Foo) void {
5 // do some stuff
6
7 if (optional_foo) |foo| {
8 doSomethingWithFoo(foo);
9 }
10
11 // do some stuff
12}
13
14// syntax
doc/langref/cli_allocation.zig created+13
...@@ -0,0 +1,13 @@
1const std = @import("std");
2
3pub fn main() !void {
4 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
5 defer arena.deinit();
6
7 const allocator = arena.allocator();
8
9 const ptr = try allocator.create(i32);
10 std.debug.print("ptr={*}\n", .{ptr});
11}
12
13// exe=succeed
doc/langref/comments.zig created+12
...@@ -0,0 +1,12 @@
1const print = @import("std").debug.print;
2
3pub fn main() void {
4 // Comments in Zig start with "//" and end at the next LF byte (end of line).
5 // The line below is a comment and won't be executed.
6
7 //print("Hello?", .{});
8
9 print("Hello, world!\n", .{}); // another comment
10}
11
12// exe=succeed
doc/langref/compile-time_duck_typing.zig created+11
...@@ -0,0 +1,11 @@
1fn max(comptime T: type, a: T, b: T) T {
2 return if (a > b) a else b;
3}
4fn gimmeTheBiggerFloat(a: f32, b: f32) f32 {
5 return max(f32, a, b);
6}
7fn gimmeTheBiggerInteger(a: u64, b: u64) u64 {
8 return max(u64, a, b);
9}
10
11// syntax
doc/langref/compile_variables.zig created+4
...@@ -0,0 +1,4 @@
1const builtin = @import("builtin");
2const separator = if (builtin.os.tag == .windows) '\\' else '/';
3
4// syntax
doc/langref/compiler_generated_function.zig created+7
...@@ -0,0 +1,7 @@
1fn max(a: bool, b: bool) bool {
2 {
3 return a or b;
4 }
5}
6
7// syntax
doc/langref/constant_identifier_cannot_change.zig created+15
...@@ -0,0 +1,15 @@
1const x = 1234;
2
3fn foo() void {
4 // It works at file scope as well as inside functions.
5 const y = 5678;
6
7 // Once assigned, an identifier cannot be changed.
8 y += 1;
9}
10
11pub fn main() void {
12 foo();
13}
14
15// exe=build_fail
doc/langref/defer_unwind.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const print = std.debug.print;
4
5test "defer unwinding" {
6 print("\n", .{});
7
8 defer {
9 print("1 ", .{});
10 }
11 defer {
12 print("2 ", .{});
13 }
14 if (false) {
15 // defers are not run if they are never executed.
16 defer {
17 print("3 ", .{});
18 }
19 }
20}
21
22// test
doc/langref/doc_comments.zig created+19
...@@ -0,0 +1,19 @@
1/// A structure for storing a timestamp, with nanosecond precision (this is a
2/// multiline doc comment).
3const Timestamp = struct {
4 /// The number of seconds since the epoch (this is also a doc comment).
5 seconds: i64, // signed so we can represent pre-1970 (not a doc comment)
6 /// The number of nanoseconds past the second (doc comment again).
7 nanos: u32,
8
9 /// Returns a `Timestamp` struct representing the Unix epoch; that is, the
10 /// moment of 1970 Jan 1 00:00:00 UTC (this is a doc comment too).
11 pub fn unixEpoch() Timestamp {
12 return Timestamp{
13 .seconds = 0,
14 .nanos = 0,
15 };
16 }
17};
18
19// syntax
doc/langref/enum_export.zig created+4
...@@ -0,0 +1,4 @@
1const Foo = enum(c_int) { a, b, c };
2export fn entry(foo: Foo) void { _ = foo; }
3
4// obj
doc/langref/enum_export_error.zig created+4
...@@ -0,0 +1,4 @@
1const Foo = enum { a, b, c };
2export fn entry(foo: Foo) void { _ = foo; }
3
4// obj=parameter of type 'enum_export_error.Foo' not allowed in function with calling convention 'C'
doc/langref/error_return_trace.zig created+41
...@@ -0,0 +1,41 @@
1pub fn main() !void {
2 try foo(12);
3}
4
5fn foo(x: i32) !void {
6 if (x >= 5) {
7 try bar();
8 } else {
9 try bang2();
10 }
11}
12
13fn bar() !void {
14 if (baz()) {
15 try quux();
16 } else |err| switch (err) {
17 error.FileNotFound => try hello(),
18 }
19}
20
21fn baz() !void {
22 try bang1();
23}
24
25fn quux() !void {
26 try bang2();
27}
28
29fn hello() !void {
30 try bang2();
31}
32
33fn bang1() !void {
34 return error.FileNotFound;
35}
36
37fn bang2() !void {
38 return error.PermissionDenied;
39}
40
41// exe=fail
doc/langref/error_union_parsing_u64.zig created+41
...@@ -0,0 +1,41 @@
1const std = @import("std");
2const maxInt = std.math.maxInt;
3
4pub fn parseU64(buf: []const u8, radix: u8) !u64 {
5 var x: u64 = 0;
6
7 for (buf) |c| {
8 const digit = charToDigit(c);
9
10 if (digit >= radix) {
11 return error.InvalidChar;
12 }
13
14 // x *= radix
15 var ov = @mulWithOverflow(x, radix);
16 if (ov[1] != 0) return error.OverFlow;
17
18 // x += digit
19 ov = @addWithOverflow(ov[0], digit);
20 if (ov[1] != 0) return error.OverFlow;
21 x = ov[0];
22 }
23
24 return x;
25}
26
27fn charToDigit(c: u8) u8 {
28 return switch (c) {
29 '0' ... '9' => c - '0',
30 'A' ... 'Z' => c - 'A' + 10,
31 'a' ... 'z' => c - 'a' + 10,
32 else => maxInt(u8),
33 };
34}
35
36test "parse u64" {
37 const result = try parseU64("1234", 10);
38 try std.testing.expect(result == 1234);
39}
40
41// test
doc/langref/export_any_symbol_name.zig created+3
...@@ -0,0 +1,3 @@
1export fn @"A function name that is a complete sentence."() void {}
2
3// obj
doc/langref/export_builtin.zig created+7
...@@ -0,0 +1,7 @@
1comptime {
2 @export(internalName, .{ .name = "foo", .linkage = .strong });
3}
4
5fn internalName() callconv(.C) void {}
6
7// obj
doc/langref/export_builtin_equivalent_code.zig created+3
...@@ -0,0 +1,3 @@
1export fn foo() void {}
2
3// obj
doc/langref/fibonacci_comptime_infinite_recursion.zig created+12
...@@ -0,0 +1,12 @@
1const assert = @import("std").debug.assert;
2
3fn fibonacci(index: i32) i32 {
4 //if (index < 2) return index;
5 return fibonacci(index - 1) + fibonacci(index - 2);
6}
7
8test "fibonacci" {
9 try comptime assert(fibonacci(7) == 13);
10}
11
12// syntax
doc/langref/float_literals.zig created+14
...@@ -0,0 +1,14 @@
1const floating_point = 123.0E+77;
2const another_float = 123.0;
3const yet_another = 123.0e+77;
4
5const hex_floating_point = 0x103.70p-5;
6const another_hex_float = 0x103.70;
7const yet_another_hex_float = 0x103.70P-5;
8
9// underscores may be placed between two digits as a visual separator
10const lightspeed = 299_792_458.000_000;
11const nanosecond = 0.000_000_001;
12const more_hex = 0x1234_5678.9ABC_CDEFp-10;
13
14// syntax
doc/langref/float_mode_exe.zig created+12
...@@ -0,0 +1,12 @@
1const print = @import("std").debug.print;
2
3extern fn foo_strict(x: f64) f64;
4extern fn foo_optimized(x: f64) f64;
5
6pub fn main() void {
7 const x = 0.001;
8 print("optimized = {}\n", .{foo_optimized(x)});
9 print("strict = {}\n", .{foo_strict(x)});
10}
11
12// syntax
doc/langref/float_mode_obj.zig created+15
...@@ -0,0 +1,15 @@
1const std = @import("std");
2const big = @as(f64, 1 << 40);
3
4export fn foo_strict(x: f64) f64 {
5 return x + big - big;
6}
7
8export fn foo_optimized(x: f64) f64 {
9 @setFloatMode(.optimized);
10 return x + big - big;
11}
12
13// obj
14// optimize=ReleaseFast
15// disable_cache
doc/langref/float_special_values.zig created+7
...@@ -0,0 +1,7 @@
1const std = @import("std");
2
3const inf = std.math.inf(f32);
4const negative_inf = -std.math.inf(f64);
5const nan = std.math.nan(f128);
6
7// syntax
doc/langref/generic_data_structure.zig created+15
...@@ -0,0 +1,15 @@
1fn List(comptime T: type) type {
2 return struct {
3 items: []T,
4 len: usize,
5 };
6}
7
8// The generic List data structure can be instantiated by passing in a type:
9var buffer: [10]i32 = undefined;
10var list = List(i32){
11 .items = &buffer,
12 .len = 0,
13};
14
15// syntax
doc/langref/handle_error_with_catch_block.zig.zig created+11
...@@ -0,0 +1,11 @@
1const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
2
3fn doAThing(str: []u8) void {
4 const number = parseU64(str, 10) catch blk: {
5 // do things
6 break :blk 13;
7 };
8 _ = number; // number is now initialized
9}
10
11// syntax
doc/langref/hello.zig created+8
...@@ -0,0 +1,8 @@
1const std = @import("std");
2
3pub fn main() !void {
4 const stdout = std.io.getStdOut().writer();
5 try stdout.print("Hello, {s}!\n", .{"world"});
6}
7
8// exe=succeed
doc/langref/hello_again.zig created+7
...@@ -0,0 +1,7 @@
1const std = @import("std");
2
3pub fn main() void {
4 std.debug.print("Hello, world!\n", .{});
5}
6
7// exe=succeed
doc/langref/identifiers.zig created+14
...@@ -0,0 +1,14 @@
1const @"identifier with spaces in it" = 0xff;
2const @"1SmallStep4Man" = 112358;
3
4const c = @import("std").c;
5pub extern "c" fn @"error"() void;
6pub extern "c" fn @"fstat$INODE64"(fd: c.fd_t, buf: *c.Stat) c_int;
7
8const Color = enum {
9 red,
10 @"really red",
11};
12const color: Color = .@"really red";
13
14// syntax
doc/langref/inline_assembly.zig created+34
...@@ -0,0 +1,34 @@
1pub fn main() noreturn {
2 const msg = "hello world\n";
3 _ = syscall3(SYS_write, STDOUT_FILENO, @intFromPtr(msg), msg.len);
4 _ = syscall1(SYS_exit, 0);
5 unreachable;
6}
7
8pub const SYS_write = 1;
9pub const SYS_exit = 60;
10
11pub const STDOUT_FILENO = 1;
12
13pub fn syscall1(number: usize, arg1: usize) usize {
14 return asm volatile ("syscall"
15 : [ret] "={rax}" (-> usize),
16 : [number] "{rax}" (number),
17 [arg1] "{rdi}" (arg1),
18 : "rcx", "r11"
19 );
20}
21
22pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
23 return asm volatile ("syscall"
24 : [ret] "={rax}" (-> usize),
25 : [number] "{rax}" (number),
26 [arg1] "{rdi}" (arg1),
27 [arg2] "{rsi}" (arg2),
28 [arg3] "{rdx}" (arg3),
29 : "rcx", "r11"
30 );
31}
32
33// exe=succeed
34// target=x86_64-linux
doc/langref/inline_call.zig created+11
...@@ -0,0 +1,11 @@
1test "inline function call" {
2 if (foo(1200, 34) != 1234) {
3 @compileError("bad");
4 }
5}
6
7inline fn foo(a: i32, b: i32) i32 {
8 return a + b;
9}
10
11// test
doc/langref/inline_prong_range.zig created+9
...@@ -0,0 +1,9 @@
1fn isFieldOptional(comptime T: type, field_index: usize) !bool {
2 const fields = @typeInfo(T).Struct.fields;
3 return switch (field_index) {
4 inline 0...fields.len - 1 => |idx| @typeInfo(fields[idx].type) == .Optional,
5 else => return error.IndexOutOfBounds,
6 };
7}
8
9// syntax
doc/langref/integer_literals.zig created+13
...@@ -0,0 +1,13 @@
1const decimal_int = 98222;
2const hex_int = 0xff;
3const another_hex_int = 0xFF;
4const octal_int = 0o755;
5const binary_int = 0b11110000;
6
7// underscores may be placed between two digits as a visual separator
8const one_billion = 1_000_000_000;
9const binary_mask = 0b1_1111_1111;
10const permissions = 0o7_5_5;
11const big_address = 0xFF80_0000_0000_0000;
12
13// syntax
doc/langref/invalid_doc-comment.zig created+5
...@@ -0,0 +1,5 @@
1/// doc-comment
2//! top-level doc-comment
3const std = @import("std");
4
5// obj=expected type expression, found 'a document comment'
doc/langref/macro.zig created+10
...@@ -0,0 +1,10 @@
1pub export fn foo() c_int {
2 var a: c_int = 1;
3 _ = &a;
4 var b: c_int = 2;
5 _ = &b;
6 return a + b;
7}
8pub const MAKELOCAL = @compileError("unable to translate C expr: unexpected token .Equal"); // macro.c:1:9
9
10// syntax
doc/langref/math.zig created+10
...@@ -0,0 +1,10 @@
1extern fn print(i32) void;
2
3export fn add(a: i32, b: i32) void {
4 print(a + b);
5}
6
7// exe=succeed
8// target=wasm32-freestanding
9// additional_option=-fno-entry
10// additional_option=--export=add
doc/langref/math_add.zig created+14
...@@ -0,0 +1,14 @@
1const math = @import("std").math;
2const print = @import("std").debug.print;
3pub fn main() !void {
4 var byte: u8 = 255;
5
6 byte = if (math.add(u8, byte, 1)) |result| result else |err| {
7 print("unable to add one: {s}\n", .{@errorName(err)});
8 return err;
9 };
10
11 print("result: {}\n", .{byte});
12}
13
14// exe=fail
doc/langref/mathtest.zig created+5
...@@ -0,0 +1,5 @@
1export fn add(a: i32, b: i32) i32 {
2 return a + b;
3}
4
5// syntax
doc/langref/multiline_string_literals.zig created+10
...@@ -0,0 +1,10 @@
1const hello_world_in_c =
2 \\#include <stdio.h>
3 \\
4 \\int main(int argc, char **argv) {
5 \\ printf("hello world\n");
6 \\ return 0;
7 \\}
8;
9
10// syntax
doc/langref/mutable_var.zig created+11
...@@ -0,0 +1,11 @@
1const print = @import("std").debug.print;
2
3pub fn main() void {
4 var y: i32 = 5678;
5
6 y += 1;
7
8 print("{d}", .{y});
9}
10
11// exe=succeed
doc/langref/not_atomic_cmpxchgStrong.zig created+11
...@@ -0,0 +1,11 @@
1fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_value: T) ?T {
2 const old_value = ptr.*;
3 if (old_value == expected_value) {
4 ptr.* = new_value;
5 return null;
6 } else {
7 return old_value;
8 }
9}
10
11// syntax
doc/langref/null.zig created+3
...@@ -0,0 +1,3 @@
1const optional_value: ?i32 = null;
2
3// syntax
doc/langref/optional_integer.zig created+7
...@@ -0,0 +1,7 @@
1// normal integer
2const normal_int: i32 = 1234;
3
4// optional integer
5const optional_int: ?i32 = 5678;
6
7// syntax
doc/langref/poc_printValue_fn.zig created+33
...@@ -0,0 +1,33 @@
1const Writer = struct {
2 pub fn printValue(self: *Writer, value: anytype) !void {
3 switch (@typeInfo(@TypeOf(value))) {
4 .Int => {
5 return self.writeInt(value);
6 },
7 .Float => {
8 return self.writeFloat(value);
9 },
10 .Pointer => {
11 return self.write(value);
12 },
13 else => {
14 @compileError("Unable to print type '" ++ @typeName(@TypeOf(value)) ++ "'");
15 },
16 }
17 }
18
19 fn write(self: *Writer, value: []const u8) !void {
20 _ = self;
21 _ = value;
22 }
23 fn writeInt(self: *Writer, value: anytype) !void {
24 _ = self;
25 _ = value;
26 }
27 fn writeFloat(self: *Writer, value: anytype) !void {
28 _ = self;
29 _ = value;
30 }
31};
32
33// syntax
doc/langref/poc_print_fn.zig created+79
...@@ -0,0 +1,79 @@
1const Writer = struct {
2 /// Calls print and then flushes the buffer.
3 pub fn print(self: *Writer, comptime format: []const u8, args: anytype) anyerror!void {
4 const State = enum {
5 start,
6 open_brace,
7 close_brace,
8 };
9
10 comptime var start_index: usize = 0;
11 comptime var state = State.start;
12 comptime var next_arg: usize = 0;
13
14 inline for (format, 0..) |c, i| {
15 switch (state) {
16 State.start => switch (c) {
17 '{' => {
18 if (start_index < i) try self.write(format[start_index..i]);
19 state = State.open_brace;
20 },
21 '}' => {
22 if (start_index < i) try self.write(format[start_index..i]);
23 state = State.close_brace;
24 },
25 else => {},
26 },
27 State.open_brace => switch (c) {
28 '{' => {
29 state = State.start;
30 start_index = i;
31 },
32 '}' => {
33 try self.printValue(args[next_arg]);
34 next_arg += 1;
35 state = State.start;
36 start_index = i + 1;
37 },
38 's' => {
39 continue;
40 },
41 else => @compileError("Unknown format character: " ++ [1]u8{c}),
42 },
43 State.close_brace => switch (c) {
44 '}' => {
45 state = State.start;
46 start_index = i;
47 },
48 else => @compileError("Single '}' encountered in format string"),
49 },
50 }
51 }
52 comptime {
53 if (args.len != next_arg) {
54 @compileError("Unused arguments");
55 }
56 if (state != State.start) {
57 @compileError("Incomplete format string: " ++ format);
58 }
59 }
60 if (start_index < format.len) {
61 try self.write(format[start_index..format.len]);
62 }
63 try self.flush();
64 }
65
66 fn write(self: *Writer, value: []const u8) !void {
67 _ = self;
68 _ = value;
69 }
70 pub fn printValue(self: *Writer, value: anytype) !void {
71 _ = self;
72 _ = value;
73 }
74 fn flush(self: *Writer) !void {
75 _ = self;
76 }
77};
78
79// syntax
doc/langref/print.zig created+10
...@@ -0,0 +1,10 @@
1const print = @import("std").debug.print;
2
3const a_number: i32 = 1234;
4const a_string = "foobar";
5
6pub fn main() void {
7 print("here is a string: '{s}' here is a number: {}\n", .{a_string, a_number});
8}
9
10// exe=succeed
doc/langref/print_comptime-known_format.zig created+11
...@@ -0,0 +1,11 @@
1const print = @import("std").debug.print;
2
3const a_number: i32 = 1234;
4const a_string = "foobar";
5const fmt = "here is a string: '{s}' here is a number: {}\n";
6
7pub fn main() void {
8 print(fmt, .{a_string, a_number});
9}
10
11// exe=succeed
doc/langref/redundant_fqn.zig created+15
...@@ -0,0 +1,15 @@
1const std = @import("std");
2
3pub const json = struct {
4 pub const JsonValue = union(enum) {
5 number: f64,
6 boolean: bool,
7 // ...
8 };
9};
10
11pub fn main() void {
12 std.debug.print("{s}\n", .{@typeName(json.JsonValue)});
13}
14
15// exe=succeed
doc/langref/result_location_interfering_with_swap.zig created+13
...@@ -0,0 +1,13 @@
1const expect = @import("std").testing.expect;
2test "attempt to swap array elements with array initializer" {
3 var arr: [2]u32 = .{ 1, 2 };
4 arr = .{ arr[1], arr[0] };
5 // The previous line is equivalent to the following two lines:
6 // arr[0] = arr[1];
7 // arr[1] = arr[0];
8 // So this fails!
9 try expect(arr[0] == 2); // succeeds
10 try expect(arr[1] == 1); // fails
11}
12
13// test_error=
doc/langref/result_type_propagation.zig created+12
...@@ -0,0 +1,12 @@
1const expectEqual = @import("std").testing.expectEqual;
2test "result type propagates through struct initializer" {
3 const S = struct { x: u32 };
4 const val: u64 = 123;
5 const s: S = .{ .x = @intCast(val) };
6 // .{ .x = @intCast(val) } has result type `S` due to the type annotation
7 // @intCast(val) has result type `u32` due to the type of the field `S.x`
8 // val has no result type, as it is permitted to be any integer type
9 try expectEqual(@as(u32, 123), s.x);
10}
11
12// test
doc/langref/runtime_divExact_remainder.zig created+11
...@@ -0,0 +1,11 @@
1const std = @import("std");
2
3pub fn main() void {
4 var a: u32 = 10;
5 var b: u32 = 3;
6 _ = .{ &a, &b };
7 const c = @divExact(a, b);
8 std.debug.print("value: {}\n", .{c});
9}
10
11// exe=fail
doc/langref/runtime_division_by_zero.zig created+11
...@@ -0,0 +1,11 @@
1const std = @import("std");
2
3pub fn main() void {
4 var a: u32 = 1;
5 var b: u32 = 0;
6 _ = .{ &a, &b };
7 const c = a / b;
8 std.debug.print("value: {}\n", .{c});
9}
10
11// exe=fail
doc/langref/runtime_incorrect_pointer_alignment.zig created+13
...@@ -0,0 +1,13 @@
1const mem = @import("std").mem;
2pub fn main() !void {
3 var array align(4) = [_]u32{ 0x11111111, 0x11111111 };
4 const bytes = mem.sliceAsBytes(array[0..]);
5 if (foo(bytes) != 0x11111111) return error.Wrong;
6}
7fn foo(bytes: []u8) u32 {
8 const slice4 = bytes[1..5];
9 const int_slice = mem.bytesAsSlice(u32, @as([]align(4) u8, @alignCast(slice4)));
10 return int_slice[0];
11}
12
13// exe=fail
doc/langref/runtime_index_out_of_bounds.zig created+10
...@@ -0,0 +1,10 @@
1pub fn main() void {
2 const x = foo("hello");
3 _ = x;
4}
5
6fn foo(x: []const u8) u8 {
7 return x[5];
8}
9
10// exe=fail
doc/langref/runtime_invalid_cast.zig created+10
...@@ -0,0 +1,10 @@
1const std = @import("std");
2
3pub fn main() void {
4 var value: i32 = -1; // runtime-known
5 _ = &value;
6 const unsigned: u32 = @intCast(value);
7 std.debug.print("value: {}\n", .{unsigned});
8}
9
10// exe=fail
doc/langref/runtime_invalid_cast_truncate.zig created+10
...@@ -0,0 +1,10 @@
1const std = @import("std");
2
3pub fn main() void {
4 var spartan_count: u16 = 300; // runtime-known
5 _ = &spartan_count;
6 const byte: u8 = @intCast(spartan_count);
7 std.debug.print("value: {}\n", .{byte});
8}
9
10// exe=fail
doc/langref/runtime_invalid_enum_cast.zig created+16
...@@ -0,0 +1,16 @@
1const std = @import("std");
2
3const Foo = enum {
4 a,
5 b,
6 c,
7};
8
9pub fn main() void {
10 var a: u2 = 3;
11 _ = &a;
12 const b: Foo = @enumFromInt(a);
13 std.debug.print("value: {s}\n", .{@tagName(b)});
14}
15
16// exe=fail
doc/langref/runtime_invalid_error_code.zig created+11
...@@ -0,0 +1,11 @@
1const std = @import("std");
2
3pub fn main() void {
4 const err = error.AnError;
5 var number = @intFromError(err) + 500;
6 _ = &number;
7 const invalid_err = @errorFromInt(number);
8 std.debug.print("value: {}\n", .{invalid_err});
9}
10
11// exe=fail
doc/langref/runtime_invalid_error_set_cast.zig created+19
...@@ -0,0 +1,19 @@
1const std = @import("std");
2
3const Set1 = error{
4 A,
5 B,
6};
7const Set2 = error{
8 A,
9 C,
10};
11pub fn main() void {
12 foo(Set1.B);
13}
14fn foo(set1: Set1) void {
15 const x: Set2 = @errorCast(set1);
16 std.debug.print("value: {}\n", .{x});
17}
18
19// exe=fail
doc/langref/runtime_invalid_null_pointer_cast.zig created+8
...@@ -0,0 +1,8 @@
1pub fn main() void {
2 var opt_ptr: ?*i32 = null;
3 _ = &opt_ptr;
4 const ptr: *i32 = @ptrCast(opt_ptr);
5 _ = ptr;
6}
7
8// exe=fail
doc/langref/runtime_out_of_bounds_float_to_integer_cast.zig created+8
...@@ -0,0 +1,8 @@
1pub fn main() void {
2 var float: f32 = 4294967296; // runtime-known
3 _ = &float;
4 const int: i32 = @intFromFloat(float);
5 _ = int;
6}
7
8// exe=fail
doc/langref/runtime_overflow.zig created+9
...@@ -0,0 +1,9 @@
1const std = @import("std");
2
3pub fn main() void {
4 var byte: u8 = 255;
5 byte += 1;
6 std.debug.print("value: {}\n", .{byte});
7}
8
9// exe=fail
doc/langref/runtime_reaching_unreachable.zig created+7
...@@ -0,0 +1,7 @@
1const std = @import("std");
2
3pub fn main() void {
4 std.debug.assert(false);
5}
6
7// exe=fail
doc/langref/runtime_remainder_division_by_zero.zig created+11
...@@ -0,0 +1,11 @@
1const std = @import("std");
2
3pub fn main() void {
4 var a: u32 = 10;
5 var b: u32 = 0;
6 _ = .{ &a, &b };
7 const c = a % b;
8 std.debug.print("value: {}\n", .{c});
9}
10
11// exe=fail
doc/langref/runtime_shlExact_overflow.zig created+10
...@@ -0,0 +1,10 @@
1const std = @import("std");
2
3pub fn main() void {
4 var x: u8 = 0b01010101; // runtime-known
5 _ = &x;
6 const y = @shlExact(x, 2);
7 std.debug.print("value: {}\n", .{y});
8}
9
10// exe=fail
doc/langref/runtime_shrExact_overflow.zig created+10
...@@ -0,0 +1,10 @@
1const std = @import("std");
2
3pub fn main() void {
4 var x: u8 = 0b10101010; // runtime-known
5 _ = &x;
6 const y = @shrExact(x, 2);
7 std.debug.print("value: {}\n", .{y});
8}
9
10// exe=fail
doc/langref/runtime_unwrap_error.zig created+12
...@@ -0,0 +1,12 @@
1const std = @import("std");
2
3pub fn main() void {
4 const number = getNumberOrFail() catch unreachable;
5 std.debug.print("value: {}\n", .{number});
6}
7
8fn getNumberOrFail() !i32 {
9 return error.UnableToReturnNumber;
10}
11
12// exe=fail
doc/langref/runtime_unwrap_null.zig created+10
...@@ -0,0 +1,10 @@
1const std = @import("std");
2
3pub fn main() void {
4 var optional_number: ?i32 = null;
5 _ = &optional_number;
6 const number = optional_number.?;
7 std.debug.print("value: {}\n", .{number});
8}
9
10// exe=fail
doc/langref/runtime_vs_comptime.zig created+5
...@@ -0,0 +1,5 @@
1fn divide(a: i32, b: i32) i32 {
2 return a / b;
3}
4
5// syntax
doc/langref/runtime_wrong_union_field_access.zig created+18
...@@ -0,0 +1,18 @@
1const std = @import("std");
2
3const Foo = union {
4 float: f32,
5 int: u32,
6};
7
8pub fn main() void {
9 var f = Foo{ .int = 42 };
10 bar(&f);
11}
12
13fn bar(f: *Foo) void {
14 f.float = 12.34;
15 std.debug.print("value: {}\n", .{f.float});
16}
17
18// exe=fail
doc/langref/sentinel-terminated_pointer.zig created+15
...@@ -0,0 +1,15 @@
1const std = @import("std");
2
3// This is also available as `std.c.printf`.
4pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
5
6pub fn main() anyerror!void {
7 _ = printf("Hello, world!\n"); // OK
8
9 const msg = "Hello, world!\n";
10 const non_null_terminated_msg: [msg.len]u8 = msg.*;
11 _ = printf(&non_null_terminated_msg);
12}
13
14// exe=build_fail
15// link_libc
doc/langref/single_value_error_set.zig created+3
...@@ -0,0 +1,3 @@
1const err = (error {FileNotFound}).FileNotFound;
2
3// syntax
doc/langref/single_value_error_set_shortcut.zig created+3
...@@ -0,0 +1,3 @@
1const err = error.FileNotFound;
2
3// syntax
doc/langref/stack_trace.zig created+41
...@@ -0,0 +1,41 @@
1pub fn main() void {
2 foo(12);
3}
4
5fn foo(x: i32) void {
6 if (x >= 5) {
7 bar();
8 } else {
9 bang2();
10 }
11}
12
13fn bar() void {
14 if (baz()) {
15 quux();
16 } else {
17 hello();
18 }
19}
20
21fn baz() bool {
22 return bang1();
23}
24
25fn quux() void {
26 bang2();
27}
28
29fn hello() void {
30 bang2();
31}
32
33fn bang1() bool {
34 return false;
35}
36
37fn bang2() void {
38 @panic("PermissionDenied");
39}
40
41// exe=fail
doc/langref/string_literals.zig created+21
...@@ -0,0 +1,21 @@
1const print = @import("std").debug.print;
2const mem = @import("std").mem; // will be used to compare bytes
3
4pub fn main() void {
5 const bytes = "hello";
6 print("{}\n", .{@TypeOf(bytes)}); // *const [5:0]u8
7 print("{d}\n", .{bytes.len}); // 5
8 print("{c}\n", .{bytes[1]}); // 'e'
9 print("{d}\n", .{bytes[5]}); // 0
10 print("{}\n", .{'e' == '\x65'}); // true
11 print("{d}\n", .{'\u{1f4a9}'}); // 128169
12 print("{d}\n", .{'💯'}); // 128175
13 print("{u}\n", .{'âš¡'});
14 print("{}\n", .{mem.eql(u8, "hello", "h\x65llo")}); // true
15 print("{}\n", .{mem.eql(u8, "💯", "\xf0\x9f\x92\xaf")}); // also true
16 const invalid_utf8 = "\xff\xfe"; // non-UTF-8 strings are possible with \xNN notation.
17 print("0x{x}\n", .{invalid_utf8[1]}); // indexing them returns individual bytes...
18 print("0x{x}\n", .{"💯"[1]}); // ...as does indexing part-way through non-ASCII characters
19}
20
21// exe=succeed
doc/langref/struct_default_field_values.zig created+15
...@@ -0,0 +1,15 @@
1const Foo = struct {
2 a: i32 = 1234,
3 b: i32,
4};
5
6test "default struct initialization fields" {
7 const x: Foo = .{
8 .b = 5,
9 };
10 if (x.a + x.b != 1239) {
11 comptime unreachable;
12 }
13}
14
15// test
doc/langref/struct_default_value.zig created+11
...@@ -0,0 +1,11 @@
1const Threshold = struct {
2 minimum: f32,
3 maximum: f32,
4
5 const default: Threshold = .{
6 .minimum = 0.25,
7 .maximum = 0.75,
8 };
9};
10
11// syntax
doc/langref/struct_name.zig created+16
...@@ -0,0 +1,16 @@
1const std = @import("std");
2
3pub fn main() void {
4 const Foo = struct {};
5 std.debug.print("variable: {s}\n", .{@typeName(Foo)});
6 std.debug.print("anonymous: {s}\n", .{@typeName(struct {})});
7 std.debug.print("function: {s}\n", .{@typeName(List(i32))});
8}
9
10fn List(comptime T: type) type {
11 return struct {
12 x: T,
13 };
14}
15
16// exe=succeed
doc/langref/test_TypeOf_builtin.zig created+16
...@@ -0,0 +1,16 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "no runtime side effects" {
5 var data: i32 = 0;
6 const T = @TypeOf(foo(i32, &data));
7 try comptime expect(T == i32);
8 try expect(data == 0);
9}
10
11fn foo(comptime T: type, ptr: *T) T {
12 ptr.* += 1;
13 return ptr.*;
14}
15
16// test
doc/langref/test_aligned_struct_fields.zig created+16
...@@ -0,0 +1,16 @@
1const std = @import("std");
2const expectEqual = std.testing.expectEqual;
3
4test "aligned struct fields" {
5 const S = struct {
6 a: u32 align(2),
7 b: u32 align(64),
8 };
9 var foo = S{ .a = 1, .b = 2 };
10
11 try expectEqual(64, @alignOf(S));
12 try expectEqual(*align(2) u32, @TypeOf(&foo.a));
13 try expectEqual(*align(64) u32, @TypeOf(&foo.b));
14}
15
16// test
doc/langref/test_allocator.zig created+20
...@@ -0,0 +1,20 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3const expect = std.testing.expect;
4
5test "using an allocator" {
6 var buffer: [100]u8 = undefined;
7 var fba = std.heap.FixedBufferAllocator.init(&buffer);
8 const allocator = fba.allocator();
9 const result = try concat(allocator, "foo", "bar");
10 try expect(std.mem.eql(u8, "foobar", result));
11}
12
13fn concat(allocator: Allocator, a: []const u8, b: []const u8) ![]u8 {
14 const result = try allocator.alloc(u8, a.len + b.len);
15 @memcpy(result[0..a.len], a);
16 @memcpy(result[a.len..], b);
17 return result;
18}
19
20// test
doc/langref/test_allowzero.zig created+11
...@@ -0,0 +1,11 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "allowzero" {
5 var zero: usize = 0; // var to make to runtime-known
6 _ = &zero; // suppress 'var is never mutated' error
7 const ptr: *allowzero i32 = @ptrFromInt(zero);
8 try expect(@intFromPtr(ptr) == 0);
9}
10
11// test
doc/langref/test_ambiguous_coercion.zig created+7
...@@ -0,0 +1,7 @@
1// Compile time coercion of float to int
2test "implicit cast to comptime_int" {
3 const f: f32 = 54.0 / 5;
4 _ = f;
5}
6
7// test_error=
doc/langref/test_anonymous_struct.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "fully anonymous struct" {
5 try check(.{
6 .int = @as(u32, 1234),
7 .float = @as(f64, 12.34),
8 .b = true,
9 .s = "hi",
10 });
11}
12
13fn check(args: anytype) !void {
14 try expect(args.int == 1234);
15 try expect(args.float == 12.34);
16 try expect(args.b);
17 try expect(args.s[0] == 'h');
18 try expect(args.s[1] == 'i');
19}
20
21// test
doc/langref/test_anonymous_union.zig created+20
...@@ -0,0 +1,20 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const Number = union {
5 int: i32,
6 float: f64,
7};
8
9test "anonymous union literal syntax" {
10 const i: Number = .{ .int = 42 };
11 const f = makeNumber();
12 try expect(i.int == 42);
13 try expect(f.float == 12.34);
14}
15
16fn makeNumber() Number {
17 return .{ .float = 12.34 };
18}
19
20// test
doc/langref/test_arrays.zig created+105
...@@ -0,0 +1,105 @@
1const expect = @import("std").testing.expect;
2const assert = @import("std").debug.assert;
3const mem = @import("std").mem;
4
5// array literal
6const message = [_]u8{ 'h', 'e', 'l', 'l', 'o' };
7
8// get the size of an array
9comptime {
10 assert(message.len == 5);
11}
12
13// A string literal is a single-item pointer to an array.
14const same_message = "hello";
15
16comptime {
17 assert(mem.eql(u8, &message, same_message));
18}
19
20test "iterate over an array" {
21 var sum: usize = 0;
22 for (message) |byte| {
23 sum += byte;
24 }
25 try expect(sum == 'h' + 'e' + 'l' * 2 + 'o');
26}
27
28// modifiable array
29var some_integers: [100]i32 = undefined;
30
31test "modify an array" {
32 for (&some_integers, 0..) |*item, i| {
33 item.* = @intCast(i);
34 }
35 try expect(some_integers[10] == 10);
36 try expect(some_integers[99] == 99);
37}
38
39// array concatenation works if the values are known
40// at compile time
41const part_one = [_]i32{ 1, 2, 3, 4 };
42const part_two = [_]i32{ 5, 6, 7, 8 };
43const all_of_it = part_one ++ part_two;
44comptime {
45 assert(mem.eql(i32, &all_of_it, &[_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 }));
46}
47
48// remember that string literals are arrays
49const hello = "hello";
50const world = "world";
51const hello_world = hello ++ " " ++ world;
52comptime {
53 assert(mem.eql(u8, hello_world, "hello world"));
54}
55
56// ** does repeating patterns
57const pattern = "ab" ** 3;
58comptime {
59 assert(mem.eql(u8, pattern, "ababab"));
60}
61
62// initialize an array to zero
63const all_zero = [_]u16{0} ** 10;
64
65comptime {
66 assert(all_zero.len == 10);
67 assert(all_zero[5] == 0);
68}
69
70// use compile-time code to initialize an array
71var fancy_array = init: {
72 var initial_value: [10]Point = undefined;
73 for (&initial_value, 0..) |*pt, i| {
74 pt.* = Point{
75 .x = @intCast(i),
76 .y = @intCast(i * 2),
77 };
78 }
79 break :init initial_value;
80};
81const Point = struct {
82 x: i32,
83 y: i32,
84};
85
86test "compile-time array initialization" {
87 try expect(fancy_array[4].x == 4);
88 try expect(fancy_array[4].y == 8);
89}
90
91// call a function to initialize an array
92var more_points = [_]Point{makePoint(3)} ** 10;
93fn makePoint(x: i32) Point {
94 return Point{
95 .x = x,
96 .y = x * 2,
97 };
98}
99test "array initialization with function calls" {
100 try expect(more_points[4].x == 3);
101 try expect(more_points[4].y == 6);
102 try expect(more_points.len == 10);
103}
104
105// test
doc/langref/test_assertion_failure.zig created+11
...@@ -0,0 +1,11 @@
1// This is how std.debug.assert is implemented
2fn assert(ok: bool) void {
3 if (!ok) unreachable; // assertion failure
4}
5
6// This test will fail because we hit unreachable.
7test "this will fail" {
8 assert(false);
9}
10
11// test_error=
doc/langref/test_basic_slices.zig created+40
...@@ -0,0 +1,40 @@
1const expect = @import("std").testing.expect;
2
3test "basic slices" {
4 var array = [_]i32{ 1, 2, 3, 4 };
5 var known_at_runtime_zero: usize = 0;
6 _ = &known_at_runtime_zero;
7 const slice = array[known_at_runtime_zero..array.len];
8 try expect(@TypeOf(slice) == []i32);
9 try expect(&slice[0] == &array[0]);
10 try expect(slice.len == array.len);
11
12 // If you slice with comptime-known start and end positions, the result is
13 // a pointer to an array, rather than a slice.
14 const array_ptr = array[0..array.len];
15 try expect(@TypeOf(array_ptr) == *[array.len]i32);
16
17 // You can perform a slice-by-length by slicing twice. This allows the compiler
18 // to perform some optimisations like recognising a comptime-known length when
19 // the start position is only known at runtime.
20 var runtime_start: usize = 1;
21 _ = &runtime_start;
22 const length = 2;
23 const array_ptr_len = array[runtime_start..][0..length];
24 try expect(@TypeOf(array_ptr_len) == *[length]i32);
25
26 // Using the address-of operator on a slice gives a single-item pointer.
27 try expect(@TypeOf(&slice[0]) == *i32);
28 // Using the `ptr` field gives a many-item pointer.
29 try expect(@TypeOf(slice.ptr) == [*]i32);
30 try expect(@intFromPtr(slice.ptr) == @intFromPtr(&slice[0]));
31
32 // Slices have array bounds checking. If you try to access something out
33 // of bounds, you'll get a safety check failure:
34 slice[10] += 1;
35
36 // Note that `slice.ptr` does not invoke safety checking, while `&slice[0]`
37 // asserts that the slice has len > 0.
38}
39
40// test_safety=index out of bounds
doc/langref/test_bitOffsetOf_offsetOf.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const BitField = packed struct {
5 a: u3,
6 b: u3,
7 c: u2,
8};
9
10test "offsets of non-byte-aligned fields" {
11 comptime {
12 try expect(@bitOffsetOf(BitField, "a") == 0);
13 try expect(@bitOffsetOf(BitField, "b") == 3);
14 try expect(@bitOffsetOf(BitField, "c") == 6);
15
16 try expect(@offsetOf(BitField, "a") == 0);
17 try expect(@offsetOf(BitField, "b") == 0);
18 try expect(@offsetOf(BitField, "c") == 0);
19 }
20}
21
22// test
doc/langref/test_blocks.zig created+9
...@@ -0,0 +1,9 @@
1test "access variable after block scope" {
2 {
3 var x: i32 = 1;
4 _ = &x;
5 }
6 x += 1;
7}
8
9// test_error=use of undeclared identifier 'x'
doc/langref/test_call_builtin.zig created+11
...@@ -0,0 +1,11 @@
1const expect = @import("std").testing.expect;
2
3test "noinline function call" {
4 try expect(@call(.auto, add, .{3, 9}) == 12);
5}
6
7fn add(a: i32, b: i32) i32 {
8 return a + b;
9}
10
11// test
doc/langref/test_coerce_error_subset_to_superset.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("std");
2
3const FileOpenError = error {
4 AccessDenied,
5 OutOfMemory,
6 FileNotFound,
7};
8
9const AllocationError = error {
10 OutOfMemory,
11};
12
13test "coerce subset to superset" {
14 const err = foo(AllocationError.OutOfMemory);
15 try std.testing.expect(err == FileOpenError.OutOfMemory);
16}
17
18fn foo(err: AllocationError) FileOpenError {
19 return err;
20}
21
22// test
doc/langref/test_coerce_error_superset_to_subset.zig created+19
...@@ -0,0 +1,19 @@
1const FileOpenError = error {
2 AccessDenied,
3 OutOfMemory,
4 FileNotFound,
5};
6
7const AllocationError = error {
8 OutOfMemory,
9};
10
11test "coerce superset to subset" {
12 foo(FileOpenError.OutOfMemory) catch {};
13}
14
15fn foo(err: FileOpenError) AllocationError {
16 return err;
17}
18
19// test_error=not a member of destination error set
doc/langref/test_coerce_large_to_small.zig created+10
...@@ -0,0 +1,10 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "coercing large integer type to smaller one when value is comptime-known to fit" {
5 const x: u64 = 255;
6 const y: u8 = x;
7 try expect(y == 255);
8}
9
10// test
doc/langref/test_coerce_optional_wrapped_error_union.zig created+12
...@@ -0,0 +1,12 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "coerce to optionals wrapped in error union" {
5 const x: anyerror!?i32 = 1234;
6 const y: anyerror!?i32 = null;
7
8 try expect((try x).? == 1234);
9 try expect((try y) == null);
10}
11
12// test
doc/langref/test_coerce_optionals.zig created+12
...@@ -0,0 +1,12 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "coerce to optionals" {
5 const x: ?i32 = 1234;
6 const y: ?i32 = null;
7
8 try expect(x.? == 1234);
9 try expect(y == null);
10}
11
12// test
doc/langref/test_coerce_slices_arrays_and_pointers.zig created+70
...@@ -0,0 +1,70 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4// You can assign constant pointers to arrays to a slice with
5// const modifier on the element type. Useful in particular for
6// String literals.
7test "*const [N]T to []const T" {
8 const x1: []const u8 = "hello";
9 const x2: []const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
10 try expect(std.mem.eql(u8, x1, x2));
11
12 const y: []const f32 = &[2]f32{ 1.2, 3.4 };
13 try expect(y[0] == 1.2);
14}
15
16// Likewise, it works when the destination type is an error union.
17test "*const [N]T to E![]const T" {
18 const x1: anyerror![]const u8 = "hello";
19 const x2: anyerror![]const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
20 try expect(std.mem.eql(u8, try x1, try x2));
21
22 const y: anyerror![]const f32 = &[2]f32{ 1.2, 3.4 };
23 try expect((try y)[0] == 1.2);
24}
25
26// Likewise, it works when the destination type is an optional.
27test "*const [N]T to ?[]const T" {
28 const x1: ?[]const u8 = "hello";
29 const x2: ?[]const u8 = &[5]u8{ 'h', 'e', 'l', 'l', 111 };
30 try expect(std.mem.eql(u8, x1.?, x2.?));
31
32 const y: ?[]const f32 = &[2]f32{ 1.2, 3.4 };
33 try expect(y.?[0] == 1.2);
34}
35
36// In this cast, the array length becomes the slice length.
37test "*[N]T to []T" {
38 var buf: [5]u8 = "hello".*;
39 const x: []u8 = &buf;
40 try expect(std.mem.eql(u8, x, "hello"));
41
42 const buf2 = [2]f32{ 1.2, 3.4 };
43 const x2: []const f32 = &buf2;
44 try expect(std.mem.eql(f32, x2, &[2]f32{ 1.2, 3.4 }));
45}
46
47// Single-item pointers to arrays can be coerced to many-item pointers.
48test "*[N]T to [*]T" {
49 var buf: [5]u8 = "hello".*;
50 const x: [*]u8 = &buf;
51 try expect(x[4] == 'o');
52 // x[5] would be an uncaught out of bounds pointer dereference!
53}
54
55// Likewise, it works when the destination type is an optional.
56test "*[N]T to ?[*]T" {
57 var buf: [5]u8 = "hello".*;
58 const x: ?[*]u8 = &buf;
59 try expect(x.?[4] == 'o');
60}
61
62// Single-item pointers can be cast to len-1 single-item arrays.
63test "*T to *[1]T" {
64 var x: i32 = 1234;
65 const y: *[1]i32 = &x;
66 const z: [*]i32 = y;
67 try expect(z[0] == 1234);
68}
69
70// test
doc/langref/test_coerce_to_error_union.zig created+12
...@@ -0,0 +1,12 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "coercion to error unions" {
5 const x: anyerror!i32 = 1234;
6 const y: anyerror!i32 = error.Failure;
7
8 try expect((try x) == 1234);
9 try std.testing.expectError(error.Failure, y);
10}
11
12// test
doc/langref/test_coerce_tuples_arrays.zig created+11
...@@ -0,0 +1,11 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const Tuple = struct{ u8, u8 };
5test "coercion from homogenous tuple to array" {
6 const tuple: Tuple = .{5, 6};
7 const array: [2]u8 = tuple;
8 _ = array;
9}
10
11// test
doc/langref/test_coerce_unions_enums.zig created+49
...@@ -0,0 +1,49 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const E = enum {
5 one,
6 two,
7 three,
8};
9
10const U = union(E) {
11 one: i32,
12 two: f32,
13 three,
14};
15
16const U2 = union(enum) {
17 a: void,
18 b: f32,
19
20 fn tag(self: U2) usize {
21 switch (self) {
22 .a => return 1,
23 .b => return 2,
24 }
25 }
26};
27
28test "coercion between unions and enums" {
29 const u = U{ .two = 12.34 };
30 const e: E = u; // coerce union to enum
31 try expect(e == E.two);
32
33 const three = E.three;
34 const u_2: U = three; // coerce enum to union
35 try expect(u_2 == E.three);
36
37 const u_3: U = .three; // coerce enum literal to union
38 try expect(u_3 == E.three);
39
40 const u_4: U2 = .a; // coerce enum literal to union with inferred enum tag type.
41 try expect(u_4.tag() == 1);
42
43 // The following example is invalid.
44 // error: coercion from enum '@TypeOf(.enum_literal)' to union 'test_coerce_unions_enum.U2' must initialize 'f32' field 'b'
45 //var u_5: U2 = .b;
46 //try expect(u_5.tag() == 2);
47}
48
49// test
doc/langref/test_compileLog_builtin.zig created+16
...@@ -0,0 +1,16 @@
1const print = @import("std").debug.print;
2
3const num1 = blk: {
4 var val1: i32 = 99;
5 @compileLog("comptime val1 = ", val1);
6 val1 = val1 + 1;
7 break :blk val1;
8};
9
10test "main" {
11 @compileLog("comptime in main");
12
13 print("Runtime in main, num1 = {}.\n", .{num1});
14}
15
16// test_error=found compile log statement
doc/langref/test_comptime_call_extern_function.zig created+9
...@@ -0,0 +1,9 @@
1extern fn exit() noreturn;
2
3test "foo" {
4 comptime {
5 exit();
6 }
7}
8
9// test_error=comptime call of extern function
doc/langref/test_comptime_divExact_remainder.zig created+8
...@@ -0,0 +1,8 @@
1comptime {
2 const a: u32 = 10;
3 const b: u32 = 3;
4 const c = @divExact(a, b);
5 _ = c;
6}
7
8// test_error=exact division produced remainder
doc/langref/test_comptime_division_by_zero.zig created+8
...@@ -0,0 +1,8 @@
1comptime {
2 const a: i32 = 1;
3 const b: i32 = 0;
4 const c = a / b;
5 _ = c;
6}
7
8// test_error=division by zero
doc/langref/test_comptime_evaluation.zig created+34
...@@ -0,0 +1,34 @@
1const expect = @import("std").testing.expect;
2
3const CmdFn = struct {
4 name: []const u8,
5 func: fn(i32) i32,
6};
7
8const cmd_fns = [_]CmdFn{
9 CmdFn {.name = "one", .func = one},
10 CmdFn {.name = "two", .func = two},
11 CmdFn {.name = "three", .func = three},
12};
13fn one(value: i32) i32 { return value + 1; }
14fn two(value: i32) i32 { return value + 2; }
15fn three(value: i32) i32 { return value + 3; }
16
17fn performFn(comptime prefix_char: u8, start_value: i32) i32 {
18 var result: i32 = start_value;
19 comptime var i = 0;
20 inline while (i < cmd_fns.len) : (i += 1) {
21 if (cmd_fns[i].name[0] == prefix_char) {
22 result = cmd_fns[i].func(result);
23 }
24 }
25 return result;
26}
27
28test "perform fn" {
29 try expect(performFn('t', 1) == 6);
30 try expect(performFn('o', 0) == 1);
31 try expect(performFn('w', 99) == 99);
32}
33
34// test
doc/langref/test_comptime_incorrect_pointer_alignment.zig created+7
...@@ -0,0 +1,7 @@
1comptime {
2 const ptr: *align(1) i32 = @ptrFromInt(0x1);
3 const aligned: *align(4) i32 = @alignCast(ptr);
4 _ = aligned;
5}
6
7// test_error=pointer address 0x1 is not aligned to 4 bytes
doc/langref/test_comptime_index_out_of_bounds.zig created+7
...@@ -0,0 +1,7 @@
1comptime {
2 const array: [5]u8 = "hello".*;
3 const garbage = array[5];
4 _ = garbage;
5}
6
7// test_error=index 5 outside array of length 5
doc/langref/test_comptime_invalid_cast.zig created+7
...@@ -0,0 +1,7 @@
1comptime {
2 const value: i32 = -1;
3 const unsigned: u32 = @intCast(value);
4 _ = unsigned;
5}
6
7// test_error=type 'u32' cannot represent integer value '-1'
doc/langref/test_comptime_invalid_cast_truncate.zig created+7
...@@ -0,0 +1,7 @@
1comptime {
2 const spartan_count: u16 = 300;
3 const byte: u8 = @intCast(spartan_count);
4 _ = byte;
5}
6
7// test_error=type 'u8' cannot represent integer value '300'
doc/langref/test_comptime_invalid_enum_cast.zig created+12
...@@ -0,0 +1,12 @@
1const Foo = enum {
2 a,
3 b,
4 c,
5};
6comptime {
7 const a: u2 = 3;
8 const b: Foo = @enumFromInt(a);
9 _ = b;
10}
11
12// test_error=enum 'test_comptime_invalid_enum_cast.Foo' has no tag with value '3'
doc/langref/test_comptime_invalid_error_code.zig created+8
...@@ -0,0 +1,8 @@
1comptime {
2 const err = error.AnError;
3 const number = @intFromError(err) + 10;
4 const invalid_err = @errorFromInt(number);
5 _ = invalid_err;
6}
7
8// test_error=integer value '11' represents no error
doc/langref/test_comptime_invalid_error_set_cast.zig created+13
...@@ -0,0 +1,13 @@
1const Set1 = error{
2 A,
3 B,
4};
5const Set2 = error{
6 A,
7 C,
8};
9comptime {
10 _ = @as(Set2, @errorCast(Set1.B));
11}
12
13// test_error='error.B' not a member of error set 'error{A,C}'
doc/langref/test_comptime_invalid_null_pointer_cast.zig created+7
...@@ -0,0 +1,7 @@
1comptime {
2 const opt_ptr: ?*i32 = null;
3 const ptr: *i32 = @ptrCast(opt_ptr);
4 _ = ptr;
5}
6
7// test_error=null pointer casted to type
doc/langref/test_comptime_max_with_bool.zig created+14
...@@ -0,0 +1,14 @@
1fn max(comptime T: type, a: T, b: T) T {
2 if (T == bool) {
3 return a or b;
4 } else if (a > b) {
5 return a;
6 } else {
7 return b;
8 }
9}
10test "try to compare bools" {
11 try @import("std").testing.expect(max(bool, false, true) == true);
12}
13
14// test
doc/langref/test_comptime_mismatched_type.zig created+8
...@@ -0,0 +1,8 @@
1fn max(comptime T: type, a: T, b: T) T {
2 return if (a > b) a else b;
3}
4test "try to compare bools" {
5 _ = max(bool, true, false);
6}
7
8// test_error=operator > not allowed for type 'bool'
doc/langref/test_comptime_out_of_bounds_float_to_integer_cast.zig created+7
...@@ -0,0 +1,7 @@
1comptime {
2 const float: f32 = 4294967296;
3 const int: i32 = @intFromFloat(float);
4 _ = int;
5}
6
7// test_error=float value '4294967296' cannot be stored in integer type 'i32'
doc/langref/test_comptime_overflow.zig created+6
...@@ -0,0 +1,6 @@
1comptime {
2 var byte: u8 = 255;
3 byte += 1;
4}
5
6// test_error=overflow of integer type 'u8' with value '256'
doc/langref/test_comptime_pointer_conversion.zig created+14
...@@ -0,0 +1,14 @@
1const expect = @import("std").testing.expect;
2
3test "comptime @ptrFromInt" {
4 comptime {
5 // Zig is able to do this at compile-time, as long as
6 // ptr is never dereferenced.
7 const ptr: *i32 = @ptrFromInt(0xdeadbee0);
8 const addr = @intFromPtr(ptr);
9 try expect(@TypeOf(addr) == usize);
10 try expect(addr == 0xdeadbee0);
11 }
12}
13
14// test
doc/langref/test_comptime_pointers.zig created+13
...@@ -0,0 +1,13 @@
1const expect = @import("std").testing.expect;
2
3test "comptime pointers" {
4 comptime {
5 var x: i32 = 1;
6 const ptr = &x;
7 ptr.* += 1;
8 x += 1;
9 try expect(ptr.* == 3);
10 }
11}
12
13// test
doc/langref/test_comptime_reaching_unreachable.zig created+8
...@@ -0,0 +1,8 @@
1comptime {
2 assert(false);
3}
4fn assert(ok: bool) void {
5 if (!ok) unreachable; // assertion failure
6}
7
8// test_error=reached unreachable code
doc/langref/test_comptime_remainder_division_by_zero.zig created+8
...@@ -0,0 +1,8 @@
1comptime {
2 const a: i32 = 10;
3 const b: i32 = 0;
4 const c = a % b;
5 _ = c;
6}
7
8// test_error=division by zero
doc/langref/test_comptime_shlExact_overwlow.zig created+6
...@@ -0,0 +1,6 @@
1comptime {
2 const x = @shlExact(@as(u8, 0b01010101), 2);
3 _ = x;
4}
5
6// test_error=operation caused overflow
doc/langref/test_comptime_shrExact_overflow.zig created+6
...@@ -0,0 +1,6 @@
1comptime {
2 const x = @shrExact(@as(u8, 0b10101010), 2);
3 _ = x;
4}
5
6// test_error=exact shift shifted out 1 bits
doc/langref/test_comptime_unreachable.zig created+14
...@@ -0,0 +1,14 @@
1const assert = @import("std").debug.assert;
2
3test "type of unreachable" {
4 comptime {
5 // The type of unreachable is noreturn.
6
7 // However this assertion will still fail to compile because
8 // unreachable expressions are compile errors.
9
10 assert(@TypeOf(unreachable) == noreturn);
11 }
12}
13
14// test_error=unreachable code
doc/langref/test_comptime_unwrap_error.zig created+10
...@@ -0,0 +1,10 @@
1comptime {
2 const number = getNumberOrFail() catch unreachable;
3 _ = number;
4}
5
6fn getNumberOrFail() !i32 {
7 return error.UnableToReturnNumber;
8}
9
10// test_error=caught unexpected error 'UnableToReturnNumber'
doc/langref/test_comptime_unwrap_null.zig created+7
...@@ -0,0 +1,7 @@
1comptime {
2 const optional_number: ?i32 = null;
3 const number = optional_number.?;
4 _ = number;
5}
6
7// test_error=unable to unwrap null
doc/langref/test_comptime_variables.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "comptime vars" {
5 var x: i32 = 1;
6 comptime var y: i32 = 1;
7
8 x += 1;
9 y += 1;
10
11 try expect(x == 2);
12 try expect(y == 2);
13
14 if (y != 2) {
15 // This compile error never triggers because y is a comptime variable,
16 // and so `y != 2` is a comptime value, and this if is statically evaluated.
17 @compileError("wrong y value");
18 }
19}
20
21// test
doc/langref/test_comptime_wrong_union_field_access.zig created+11
...@@ -0,0 +1,11 @@
1comptime {
2 var f = Foo{ .int = 42 };
3 f.float = 12.34;
4}
5
6const Foo = union {
7 float: f32,
8 int: u32,
9};
10
11// test_error=access of union field 'float' while field 'int' is active
doc/langref/test_container-level_comptime_expressions.zig created+37
...@@ -0,0 +1,37 @@
1const first_25_primes = firstNPrimes(25);
2const sum_of_first_25_primes = sum(&first_25_primes);
3
4fn firstNPrimes(comptime n: usize) [n]i32 {
5 var prime_list: [n]i32 = undefined;
6 var next_index: usize = 0;
7 var test_number: i32 = 2;
8 while (next_index < prime_list.len) : (test_number += 1) {
9 var test_prime_index: usize = 0;
10 var is_prime = true;
11 while (test_prime_index < next_index) : (test_prime_index += 1) {
12 if (test_number % prime_list[test_prime_index] == 0) {
13 is_prime = false;
14 break;
15 }
16 }
17 if (is_prime) {
18 prime_list[next_index] = test_number;
19 next_index += 1;
20 }
21 }
22 return prime_list;
23}
24
25fn sum(numbers: []const i32) i32 {
26 var result: i32 = 0;
27 for (numbers) |x| {
28 result += x;
29 }
30 return result;
31}
32
33test "variable values" {
34 try @import("std").testing.expect(sum_of_first_25_primes == 1060);
35}
36
37// test
doc/langref/test_container_level_variables.zig created+16
...@@ -0,0 +1,16 @@
1var y: i32 = add(10, x);
2const x: i32 = add(12, 34);
3
4test "container level variables" {
5 try expect(x == 46);
6 try expect(y == 56);
7}
8
9fn add(a: i32, b: i32) i32 {
10 return a + b;
11}
12
13const std = @import("std");
14const expect = std.testing.expect;
15
16// test
doc/langref/test_defer.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const print = std.debug.print;
4
5fn deferExample() !usize {
6 var a: usize = 1;
7
8 {
9 defer a = 2;
10 a = 1;
11 }
12 try expect(a == 2);
13
14 a = 5;
15 return a;
16}
17
18test "defer basics" {
19 try expect((try deferExample()) == 5);
20}
21
22// test
doc/langref/test_defining_variadic_function.zig created+31
...@@ -0,0 +1,31 @@
1const std = @import("std");
2const testing = std.testing;
3const builtin = @import("builtin");
4
5fn add(count: c_int, ...) callconv(.C) c_int {
6 var ap = @cVaStart();
7 defer @cVaEnd(&ap);
8 var i: usize = 0;
9 var sum: c_int = 0;
10 while (i < count) : (i += 1) {
11 sum += @cVaArg(&ap, c_int);
12 }
13 return sum;
14}
15
16test "defining a variadic function" {
17 if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .macos) {
18 // https://github.com/ziglang/zig/issues/14096
19 return error.SkipZigTest;
20 }
21 if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) {
22 // https://github.com/ziglang/zig/issues/16961
23 return error.SkipZigTest;
24 }
25
26 try std.testing.expectEqual(@as(c_int, 0), add(0));
27 try std.testing.expectEqual(@as(c_int, 1), add(1, @as(c_int, 1)));
28 try std.testing.expectEqual(@as(c_int, 3), add(2, @as(c_int, 1), @as(c_int, 2)));
29}
30
31// test
doc/langref/test_empty_block.zig created+12
...@@ -0,0 +1,12 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test {
5 const a = {};
6 const b = void{};
7 try expect(@TypeOf(a) == void);
8 try expect(@TypeOf(b) == void);
9 try expect(a == b);
10}
11
12// test
doc/langref/test_enum_literals.zig created+26
...@@ -0,0 +1,26 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const Color = enum {
5 auto,
6 off,
7 on,
8};
9
10test "enum literals" {
11 const color1: Color = .auto;
12 const color2 = Color.auto;
13 try expect(color1 == color2);
14}
15
16test "switch using enum literals" {
17 const color = Color.on;
18 const result = switch (color) {
19 .auto => false,
20 .on => true,
21 .off => false,
22 };
23 try expect(result);
24}
25
26// test
doc/langref/test_enums.zig created+112
...@@ -0,0 +1,112 @@
1const expect = @import("std").testing.expect;
2const mem = @import("std").mem;
3
4// Declare an enum.
5const Type = enum {
6 ok,
7 not_ok,
8};
9
10// Declare a specific enum field.
11const c = Type.ok;
12
13// If you want access to the ordinal value of an enum, you
14// can specify the tag type.
15const Value = enum(u2) {
16 zero,
17 one,
18 two,
19};
20// Now you can cast between u2 and Value.
21// The ordinal value starts from 0, counting up by 1 from the previous member.
22test "enum ordinal value" {
23 try expect(@intFromEnum(Value.zero) == 0);
24 try expect(@intFromEnum(Value.one) == 1);
25 try expect(@intFromEnum(Value.two) == 2);
26}
27
28// You can override the ordinal value for an enum.
29const Value2 = enum(u32) {
30 hundred = 100,
31 thousand = 1000,
32 million = 1000000,
33};
34test "set enum ordinal value" {
35 try expect(@intFromEnum(Value2.hundred) == 100);
36 try expect(@intFromEnum(Value2.thousand) == 1000);
37 try expect(@intFromEnum(Value2.million) == 1000000);
38}
39
40// You can also override only some values.
41const Value3 = enum(u4) {
42 a,
43 b = 8,
44 c,
45 d = 4,
46 e,
47};
48test "enum implicit ordinal values and overridden values" {
49 try expect(@intFromEnum(Value3.a) == 0);
50 try expect(@intFromEnum(Value3.b) == 8);
51 try expect(@intFromEnum(Value3.c) == 9);
52 try expect(@intFromEnum(Value3.d) == 4);
53 try expect(@intFromEnum(Value3.e) == 5);
54}
55
56// Enums can have methods, the same as structs and unions.
57// Enum methods are not special, they are only namespaced
58// functions that you can call with dot syntax.
59const Suit = enum {
60 clubs,
61 spades,
62 diamonds,
63 hearts,
64
65 pub fn isClubs(self: Suit) bool {
66 return self == Suit.clubs;
67 }
68};
69test "enum method" {
70 const p = Suit.spades;
71 try expect(!p.isClubs());
72}
73
74// An enum can be switched upon.
75const Foo = enum {
76 string,
77 number,
78 none,
79};
80test "enum switch" {
81 const p = Foo.number;
82 const what_is_it = switch (p) {
83 Foo.string => "this is a string",
84 Foo.number => "this is a number",
85 Foo.none => "this is a none",
86 };
87 try expect(mem.eql(u8, what_is_it, "this is a number"));
88}
89
90// @typeInfo can be used to access the integer tag type of an enum.
91const Small = enum {
92 one,
93 two,
94 three,
95 four,
96};
97test "std.meta.Tag" {
98 try expect(@typeInfo(Small).Enum.tag_type == u2);
99}
100
101// @typeInfo tells us the field count and the fields names:
102test "@typeInfo" {
103 try expect(@typeInfo(Small).Enum.fields.len == 4);
104 try expect(mem.eql(u8, @typeInfo(Small).Enum.fields[1].name, "two"));
105}
106
107// @tagName gives a [:0]const u8 representation of an enum value:
108test "@tagName" {
109 try expect(mem.eql(u8, @tagName(Small.three), "three"));
110}
111
112// test
doc/langref/test_errdefer_block.zig created+42
...@@ -0,0 +1,42 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3
4const Foo = struct {
5 data: u32,
6};
7
8fn tryToAllocateFoo(allocator: Allocator) !*Foo {
9 return allocator.create(Foo);
10}
11
12fn deallocateFoo(allocator: Allocator, foo: *Foo) void {
13 allocator.destroy(foo);
14}
15
16fn getFooData() !u32 {
17 return 666;
18}
19
20fn createFoo(allocator: Allocator, param: i32) !*Foo {
21 const foo = getFoo: {
22 var foo = try tryToAllocateFoo(allocator);
23 errdefer deallocateFoo(allocator, foo);
24
25 foo.data = try getFooData();
26
27 break :getFoo foo;
28 };
29 // This lasts for the rest of the function
30 errdefer deallocateFoo(allocator, foo);
31
32 // Error is now properly handled by errdefer
33 if (param > 1337) return error.InvalidParam;
34
35 return foo;
36}
37
38test "createFoo" {
39 try std.testing.expectError(error.InvalidParam, createFoo(std.testing.allocator, 2468));
40}
41
42// test
doc/langref/test_errdefer_loop.zig created+38
...@@ -0,0 +1,38 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3
4const Foo = struct {
5 data: *u32
6};
7
8fn getData() !u32 {
9 return 666;
10}
11
12fn genFoos(allocator: Allocator, num: usize) ![]Foo {
13 const foos = try allocator.alloc(Foo, num);
14 errdefer allocator.free(foos);
15
16 // Used to track how many foos have been initialized
17 // (including their data being allocated)
18 var num_allocated: usize = 0;
19 errdefer for (foos[0..num_allocated]) |foo| {
20 allocator.destroy(foo.data);
21 };
22 for (foos, 0..) |*foo, i| {
23 foo.data = try allocator.create(u32);
24 num_allocated += 1;
25
26 if (i >= 3) return error.TooManyFoos;
27
28 foo.data.* = try getData();
29 }
30
31 return foos;
32}
33
34test "genFoos" {
35 try std.testing.expectError(error.TooManyFoos, genFoos(std.testing.allocator, 5));
36}
37
38// test
doc/langref/test_errdefer_loop_leak.zig created+34
...@@ -0,0 +1,34 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3
4const Foo = struct {
5 data: *u32
6};
7
8fn getData() !u32 {
9 return 666;
10}
11
12fn genFoos(allocator: Allocator, num: usize) ![]Foo {
13 const foos = try allocator.alloc(Foo, num);
14 errdefer allocator.free(foos);
15
16 for (foos, 0..) |*foo, i| {
17 foo.data = try allocator.create(u32);
18 // This errdefer does not last between iterations
19 errdefer allocator.destroy(foo.data);
20
21 // The data for the first 3 foos will be leaked
22 if(i >= 3) return error.TooManyFoos;
23
24 foo.data.* = try getData();
25 }
26
27 return foos;
28}
29
30test "genFoos" {
31 try std.testing.expectError(error.TooManyFoos, genFoos(std.testing.allocator, 5));
32}
33
34// test_error=3 errors were logged
doc/langref/test_errdefer_slip_ups.zig created+42
...@@ -0,0 +1,42 @@
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3
4const Foo = struct {
5 data: u32,
6};
7
8fn tryToAllocateFoo(allocator: Allocator) !*Foo {
9 return allocator.create(Foo);
10}
11
12fn deallocateFoo(allocator: Allocator, foo: *Foo) void {
13 allocator.destroy(foo);
14}
15
16fn getFooData() !u32 {
17 return 666;
18}
19
20fn createFoo(allocator: Allocator, param: i32) !*Foo {
21 const foo = getFoo: {
22 var foo = try tryToAllocateFoo(allocator);
23 errdefer deallocateFoo(allocator, foo); // Only lasts until the end of getFoo
24
25 // Calls deallocateFoo on error
26 foo.data = try getFooData();
27
28 break :getFoo foo;
29 };
30
31 // Outside of the scope of the errdefer, so
32 // deallocateFoo will not be called here
33 if (param > 1337) return error.InvalidParam;
34
35 return foo;
36}
37
38test "createFoo" {
39 try std.testing.expectError(error.InvalidParam, createFoo(std.testing.allocator, 2468));
40}
41
42// test_error=1 tests leaked memory
doc/langref/test_error_union.zig created+19
...@@ -0,0 +1,19 @@
1const expect = @import("std").testing.expect;
2
3test "error union" {
4 var foo: anyerror!i32 = undefined;
5
6 // Coerce from child type of an error union:
7 foo = 1234;
8
9 // Coerce from an error set:
10 foo = error.SomeError;
11
12 // Use compile-time reflection to access the payload type of an error union:
13 try comptime expect(@typeInfo(@TypeOf(foo)).ErrorUnion.payload == i32);
14
15 // Use compile-time reflection to access the error set type of an error union:
16 try comptime expect(@typeInfo(@TypeOf(foo)).ErrorUnion.error_set == anyerror);
17}
18
19// test
doc/langref/test_exhaustive_switch.zig created+20
...@@ -0,0 +1,20 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const Color = enum {
5 auto,
6 off,
7 on,
8};
9
10test "enum literals with switch" {
11 const color = Color.off;
12 const result = switch (color) {
13 .auto => false,
14 .on => false,
15 .off => true,
16 };
17 try expect(result);
18}
19
20// test
doc/langref/test_expression_ignored.zig created+9
...@@ -0,0 +1,9 @@
1test "ignoring expression value" {
2 foo();
3}
4
5fn foo() i32 {
6 return 1234;
7}
8
9// test_error=ignored
doc/langref/test_fibonacci_comptime_overflow.zig created+12
...@@ -0,0 +1,12 @@
1const expect = @import("std").testing.expect;
2
3fn fibonacci(index: u32) u32 {
4 //if (index < 2) return index;
5 return fibonacci(index - 1) + fibonacci(index - 2);
6}
7
8test "fibonacci" {
9 try comptime expect(fibonacci(7) == 13);
10}
11
12// test_error=overflow of integer type
doc/langref/test_fibonacci_comptime_unreachable.zig created+12
...@@ -0,0 +1,12 @@
1const assert = @import("std").debug.assert;
2
3fn fibonacci(index: i32) i32 {
4 if (index < 2) return index;
5 return fibonacci(index - 1) + fibonacci(index - 2);
6}
7
8test "fibonacci" {
9 try comptime assert(fibonacci(7) == 99999);
10}
11
12// test_error=reached unreachable
doc/langref/test_fibonacci_recursion.zig created+16
...@@ -0,0 +1,16 @@
1const expect = @import("std").testing.expect;
2
3fn fibonacci(index: u32) u32 {
4 if (index < 2) return index;
5 return fibonacci(index - 1) + fibonacci(index - 2);
6}
7
8test "fibonacci" {
9 // test fibonacci at run-time
10 try expect(fibonacci(7) == 13);
11
12 // test fibonacci at compile-time
13 try comptime expect(fibonacci(7) == 13);
14}
15
16// test
doc/langref/test_field_builtin.zig created+30
...@@ -0,0 +1,30 @@
1const std = @import("std");
2
3const Point = struct {
4 x: u32,
5 y: u32,
6
7 pub var z: u32 = 1;
8};
9
10test "field access by string" {
11 const expect = std.testing.expect;
12 var p = Point{ .x = 0, .y = 0 };
13
14 @field(p, "x") = 4;
15 @field(p, "y") = @field(p, "x") + 1;
16
17 try expect(@field(p, "x") == 4);
18 try expect(@field(p, "y") == 5);
19}
20
21test "decl access by string" {
22 const expect = std.testing.expect;
23
24 try expect(@field(Point, "z") == 1);
25
26 @field(Point, "z") = 2;
27 try expect(@field(Point, "z") == 2);
28}
29
30// test
doc/langref/test_fn_reflection.zig created+12
...@@ -0,0 +1,12 @@
1const std = @import("std");
2const math = std.math;
3const testing = std.testing;
4
5test "fn reflection" {
6 try testing.expect(@typeInfo(@TypeOf(testing.expect)).Fn.params[0].type.? == bool);
7 try testing.expect(@typeInfo(@TypeOf(testing.tmpDir)).Fn.return_type.? == testing.TmpDir);
8
9 try testing.expect(@typeInfo(@TypeOf(math.Log2Int)).Fn.is_generic);
10}
11
12// test
doc/langref/test_fn_type_inference.zig created+15
...@@ -0,0 +1,15 @@
1const expect = @import("std").testing.expect;
2
3fn addFortyTwo(x: anytype) @TypeOf(x) {
4 return x + 42;
5}
6
7test "fn type inference" {
8 try expect(addFortyTwo(1) == 43);
9 try expect(@TypeOf(addFortyTwo(1)) == comptime_int);
10 const y: i64 = 2;
11 try expect(addFortyTwo(y) == 44);
12 try expect(@TypeOf(addFortyTwo(y)) == i64);
13}
14
15// test
doc/langref/test_for.zig created+88
...@@ -0,0 +1,88 @@
1const expect = @import("std").testing.expect;
2
3test "for basics" {
4 const items = [_]i32 { 4, 5, 3, 4, 0 };
5 var sum: i32 = 0;
6
7 // For loops iterate over slices and arrays.
8 for (items) |value| {
9 // Break and continue are supported.
10 if (value == 0) {
11 continue;
12 }
13 sum += value;
14 }
15 try expect(sum == 16);
16
17 // To iterate over a portion of a slice, reslice.
18 for (items[0..1]) |value| {
19 sum += value;
20 }
21 try expect(sum == 20);
22
23 // To access the index of iteration, specify a second condition as well
24 // as a second capture value.
25 var sum2: i32 = 0;
26 for (items, 0..) |_, i| {
27 try expect(@TypeOf(i) == usize);
28 sum2 += @as(i32, @intCast(i));
29 }
30 try expect(sum2 == 10);
31
32 // To iterate over consecutive integers, use the range syntax.
33 // Unbounded range is always a compile error.
34 var sum3 : usize = 0;
35 for (0..5) |i| {
36 sum3 += i;
37 }
38 try expect(sum3 == 10);
39}
40
41test "multi object for" {
42 const items = [_]usize{ 1, 2, 3 };
43 const items2 = [_]usize{ 4, 5, 6 };
44 var count: usize = 0;
45
46 // Iterate over multiple objects.
47 // All lengths must be equal at the start of the loop, otherwise detectable
48 // illegal behavior occurs.
49 for (items, items2) |i, j| {
50 count += i + j;
51 }
52
53 try expect(count == 21);
54}
55
56test "for reference" {
57 var items = [_]i32{ 3, 4, 2 };
58
59 // Iterate over the slice by reference by
60 // specifying that the capture value is a pointer.
61 for (&items) |*value| {
62 value.* += 1;
63 }
64
65 try expect(items[0] == 4);
66 try expect(items[1] == 5);
67 try expect(items[2] == 3);
68}
69
70test "for else" {
71 // For allows an else attached to it, the same as a while loop.
72 const items = [_]?i32{ 3, 4, null, 5 };
73
74 // For loops can also be used as expressions.
75 // Similar to while loops, when you break from a for loop, the else branch is not evaluated.
76 var sum: i32 = 0;
77 const result = for (items) |value| {
78 if (value != null) {
79 sum += value.?;
80 }
81 } else blk: {
82 try expect(sum == 12);
83 break :blk sum;
84 };
85 try expect(result == 12);
86}
87
88// test
doc/langref/test_for_nested_break.zig created+27
...@@ -0,0 +1,27 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "nested break" {
5 var count: usize = 0;
6 outer: for (1..6) |_| {
7 for (1..6) |_| {
8 count += 1;
9 break :outer;
10 }
11 }
12 try expect(count == 1);
13}
14
15test "nested continue" {
16 var count: usize = 0;
17 outer: for (1..9) |_| {
18 for (1..6) |_| {
19 count += 1;
20 continue :outer;
21 }
22 }
23
24 try expect(count == 8);
25}
26
27// test
doc/langref/test_functions.zig created+61
...@@ -0,0 +1,61 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const native_arch = builtin.cpu.arch;
4const expect = std.testing.expect;
5
6// Functions are declared like this
7fn add(a: i8, b: i8) i8 {
8 if (a == 0) {
9 return b;
10 }
11
12 return a + b;
13}
14
15// The export specifier makes a function externally visible in the generated
16// object file, and makes it use the C ABI.
17export fn sub(a: i8, b: i8) i8 { return a - b; }
18
19// The extern specifier is used to declare a function that will be resolved
20// at link time, when linking statically, or at runtime, when linking
21// dynamically. The quoted identifier after the extern keyword specifies
22// the library that has the function. (e.g. "c" -> libc.so)
23// The callconv specifier changes the calling convention of the function.
24const WINAPI: std.builtin.CallingConvention = if (native_arch == .x86) .Stdcall else .C;
25extern "kernel32" fn ExitProcess(exit_code: u32) callconv(WINAPI) noreturn;
26extern "c" fn atan2(a: f64, b: f64) f64;
27
28// The @setCold builtin tells the optimizer that a function is rarely called.
29fn abort() noreturn {
30 @setCold(true);
31 while (true) {}
32}
33
34// The naked calling convention makes a function not have any function prologue or epilogue.
35// This can be useful when integrating with assembly.
36fn _start() callconv(.Naked) noreturn {
37 abort();
38}
39
40// The inline calling convention forces a function to be inlined at all call sites.
41// If the function cannot be inlined, it is a compile-time error.
42fn shiftLeftOne(a: u32) callconv(.Inline) u32 {
43 return a << 1;
44}
45
46// The pub specifier allows the function to be visible when importing.
47// Another file can use @import and call sub2
48pub fn sub2(a: i8, b: i8) i8 { return a - b; }
49
50// Function pointers are prefixed with `*const `.
51const Call2Op = *const fn (a: i8, b: i8) i8;
52fn doOp(fnCall: Call2Op, op1: i8, op2: i8) i8 {
53 return fnCall(op1, op2);
54}
55
56test "function" {
57 try expect(doOp(add, 5, 6) == 11);
58 try expect(doOp(sub2, 5, 6) == -1);
59}
60
61// test
doc/langref/test_global_assembly.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4comptime {
5 asm (
6 \\.global my_func;
7 \\.type my_func, @function;
8 \\my_func:
9 \\ lea (%rdi,%rsi,1),%eax
10 \\ retq
11 );
12}
13
14extern fn my_func(a: i32, b: i32) i32;
15
16test "global assembly" {
17 try expect(my_func(12, 34) == 46);
18}
19
20// test
21// target=x86_64-linux
doc/langref/test_hasDecl_builtin.zig created+24
...@@ -0,0 +1,24 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const Foo = struct {
5 nope: i32,
6
7 pub var blah = "xxx";
8 const hi = 1;
9};
10
11test "@hasDecl" {
12 try expect(@hasDecl(Foo, "blah"));
13
14 // Even though `hi` is private, @hasDecl returns true because this test is
15 // in the same file scope as Foo. It would return false if Foo was declared
16 // in a different file.
17 try expect(@hasDecl(Foo, "hi"));
18
19 // @hasDecl is for declarations; not fields.
20 try expect(!@hasDecl(Foo, "nope"));
21 try expect(!@hasDecl(Foo, "nope1234"));
22}
23
24// test
doc/langref/test_if.zig created+74
...@@ -0,0 +1,74 @@
1// If expressions have three uses, corresponding to the three types:
2// * bool
3// * ?T
4// * anyerror!T
5
6const expect = @import("std").testing.expect;
7
8test "if expression" {
9 // If expressions are used instead of a ternary expression.
10 const a: u32 = 5;
11 const b: u32 = 4;
12 const result = if (a != b) 47 else 3089;
13 try expect(result == 47);
14}
15
16test "if boolean" {
17 // If expressions test boolean conditions.
18 const a: u32 = 5;
19 const b: u32 = 4;
20 if (a != b) {
21 try expect(true);
22 } else if (a == 9) {
23 unreachable;
24 } else {
25 unreachable;
26 }
27}
28
29test "if error union" {
30 // If expressions test for errors.
31 // Note the |err| capture on the else.
32
33 const a: anyerror!u32 = 0;
34 if (a) |value| {
35 try expect(value == 0);
36 } else |err| {
37 _ = err;
38 unreachable;
39 }
40
41 const b: anyerror!u32 = error.BadValue;
42 if (b) |value| {
43 _ = value;
44 unreachable;
45 } else |err| {
46 try expect(err == error.BadValue);
47 }
48
49 // The else and |err| capture is strictly required.
50 if (a) |value| {
51 try expect(value == 0);
52 } else |_| {}
53
54 // To check only the error value, use an empty block expression.
55 if (b) |_| {} else |err| {
56 try expect(err == error.BadValue);
57 }
58
59 // Access the value by reference using a pointer capture.
60 var c: anyerror!u32 = 3;
61 if (c) |*value| {
62 value.* = 9;
63 } else |_| {
64 unreachable;
65 }
66
67 if (c) |value| {
68 try expect(value == 9);
69 } else |_| {
70 unreachable;
71 }
72}
73
74// test
doc/langref/test_if_optionals.zig created+87
...@@ -0,0 +1,87 @@
1const expect = @import("std").testing.expect;
2
3test "if optional" {
4 // If expressions test for null.
5
6 const a: ?u32 = 0;
7 if (a) |value| {
8 try expect(value == 0);
9 } else {
10 unreachable;
11 }
12
13 const b: ?u32 = null;
14 if (b) |_| {
15 unreachable;
16 } else {
17 try expect(true);
18 }
19
20 // The else is not required.
21 if (a) |value| {
22 try expect(value == 0);
23 }
24
25 // To test against null only, use the binary equality operator.
26 if (b == null) {
27 try expect(true);
28 }
29
30 // Access the value by reference using a pointer capture.
31 var c: ?u32 = 3;
32 if (c) |*value| {
33 value.* = 2;
34 }
35
36 if (c) |value| {
37 try expect(value == 2);
38 } else {
39 unreachable;
40 }
41}
42
43test "if error union with optional" {
44 // If expressions test for errors before unwrapping optionals.
45 // The |optional_value| capture's type is ?u32.
46
47 const a: anyerror!?u32 = 0;
48 if (a) |optional_value| {
49 try expect(optional_value.? == 0);
50 } else |err| {
51 _ = err;
52 unreachable;
53 }
54
55 const b: anyerror!?u32 = null;
56 if (b) |optional_value| {
57 try expect(optional_value == null);
58 } else |_| {
59 unreachable;
60 }
61
62 const c: anyerror!?u32 = error.BadValue;
63 if (c) |optional_value| {
64 _ = optional_value;
65 unreachable;
66 } else |err| {
67 try expect(err == error.BadValue);
68 }
69
70 // Access the value by reference by using a pointer capture each time.
71 var d: anyerror!?u32 = 3;
72 if (d) |*optional_value| {
73 if (optional_value.*) |*value| {
74 value.* = 9;
75 }
76 } else |_| {
77 unreachable;
78 }
79
80 if (d) |optional_value| {
81 try expect(optional_value.? == 9);
82 } else |_| {
83 unreachable;
84 }
85}
86
87// test
doc/langref/test_incorrect_pointer_alignment.zig created+14
...@@ -0,0 +1,14 @@
1const std = @import("std");
2
3test "pointer alignment safety" {
4 var array align(4) = [_]u32{ 0x11111111, 0x11111111 };
5 const bytes = std.mem.sliceAsBytes(array[0..]);
6 try std.testing.expect(foo(bytes) == 0x11111111);
7}
8fn foo(bytes: []u8) u32 {
9 const slice4 = bytes[1..5];
10 const int_slice = std.mem.bytesAsSlice(u32, @as([]align(4) u8, @alignCast(slice4)));
11 return int_slice[0];
12}
13
14// test_safety=incorrect alignment
doc/langref/test_inferred_error_sets.zig created+27
...@@ -0,0 +1,27 @@
1// With an inferred error set
2pub fn add_inferred(comptime T: type, a: T, b: T) !T {
3 const ov = @addWithOverflow(a, b);
4 if (ov[1] != 0) return error.Overflow;
5 return ov[0];
6}
7
8// With an explicit error set
9pub fn add_explicit(comptime T: type, a: T, b: T) Error!T {
10 const ov = @addWithOverflow(a, b);
11 if (ov[1] != 0) return error.Overflow;
12 return ov[0];
13}
14
15const Error = error {
16 Overflow,
17};
18
19const std = @import("std");
20
21test "inferred error set" {
22 if (add_inferred(u8, 255, 1)) |_| unreachable else |err| switch (err) {
23 error.Overflow => {}, // ok
24 }
25}
26
27// test
doc/langref/test_inline_else.zig created+49
...@@ -0,0 +1,49 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const SliceTypeA = extern struct {
5 len: usize,
6 ptr: [*]u32,
7};
8const SliceTypeB = extern struct {
9 ptr: [*]SliceTypeA,
10 len: usize,
11};
12const AnySlice = union(enum) {
13 a: SliceTypeA,
14 b: SliceTypeB,
15 c: []const u8,
16 d: []AnySlice,
17};
18
19fn withFor(any: AnySlice) usize {
20 const Tag = @typeInfo(AnySlice).Union.tag_type.?;
21 inline for (@typeInfo(Tag).Enum.fields) |field| {
22 // With `inline for` the function gets generated as
23 // a series of `if` statements relying on the optimizer
24 // to convert it to a switch.
25 if (field.value == @intFromEnum(any)) {
26 return @field(any, field.name).len;
27 }
28 }
29 // When using `inline for` the compiler doesn't know that every
30 // possible case has been handled requiring an explicit `unreachable`.
31 unreachable;
32}
33
34fn withSwitch(any: AnySlice) usize {
35 return switch (any) {
36 // With `inline else` the function is explicitly generated
37 // as the desired switch and the compiler can check that
38 // every possible case is handled.
39 inline else => |slice| slice.len,
40 };
41}
42
43test "inline for and inline else similarity" {
44 const any = AnySlice{ .c = "hello" };
45 try expect(withFor(any) == 5);
46 try expect(withSwitch(any) == 5);
47}
48
49// test
doc/langref/test_inline_for.zig created+22
...@@ -0,0 +1,22 @@
1const expect = @import("std").testing.expect;
2
3test "inline for loop" {
4 const nums = [_]i32{2, 4, 6};
5 var sum: usize = 0;
6 inline for (nums) |i| {
7 const T = switch (i) {
8 2 => f32,
9 4 => i8,
10 6 => bool,
11 else => unreachable,
12 };
13 sum += typeNameLength(T);
14 }
15 try expect(sum == 9);
16}
17
18fn typeNameLength(comptime T: type) usize {
19 return @typeName(T).len;
20}
21
22// test
doc/langref/test_inline_switch.zig created+36
...@@ -0,0 +1,36 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const expectError = std.testing.expectError;
4
5fn isFieldOptional(comptime T: type, field_index: usize) !bool {
6 const fields = @typeInfo(T).Struct.fields;
7 return switch (field_index) {
8 // This prong is analyzed twice with `idx` being a
9 // comptime-known value each time.
10 inline 0, 1 => |idx| @typeInfo(fields[idx].type) == .Optional,
11 else => return error.IndexOutOfBounds,
12 };
13}
14
15const Struct1 = struct { a: u32, b: ?u32 };
16
17test "using @typeInfo with runtime values" {
18 var index: usize = 0;
19 try expect(!try isFieldOptional(Struct1, index));
20 index += 1;
21 try expect(try isFieldOptional(Struct1, index));
22 index += 1;
23 try expectError(error.IndexOutOfBounds, isFieldOptional(Struct1, index));
24}
25
26// Calls to `isFieldOptional` on `Struct1` get unrolled to an equivalent
27// of this function:
28fn isFieldOptionalUnrolled(field_index: usize) !bool {
29 return switch (field_index) {
30 0 => false,
31 1 => true,
32 else => return error.IndexOutOfBounds,
33 };
34}
35
36// test
doc/langref/test_inline_switch_union_tag.zig created+27
...@@ -0,0 +1,27 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const U = union(enum) {
5 a: u32,
6 b: f32,
7};
8
9fn getNum(u: U) u32 {
10 switch (u) {
11 // Here `num` is a runtime-known value that is either
12 // `u.a` or `u.b` and `tag` is `u`'s comptime-known tag value.
13 inline else => |num, tag| {
14 if (tag == .b) {
15 return @intFromFloat(num);
16 }
17 return num;
18 }
19 }
20}
21
22test "test" {
23 const u = U{ .b = 42 };
24 try expect(getNum(u) == 42);
25}
26
27// test
doc/langref/test_inline_while.zig created+22
...@@ -0,0 +1,22 @@
1const expect = @import("std").testing.expect;
2
3test "inline while loop" {
4 comptime var i = 0;
5 var sum: usize = 0;
6 inline while (i < 3) : (i += 1) {
7 const T = switch (i) {
8 0 => f32,
9 1 => i8,
10 2 => bool,
11 else => unreachable,
12 };
13 sum += typeNameLength(T);
14 }
15 try expect(sum == 9);
16}
17
18fn typeNameLength(comptime T: type) usize {
19 return @typeName(T).len;
20}
21
22// test
doc/langref/test_intCast_builtin.zig created+8
...@@ -0,0 +1,8 @@
1test "integer cast panic" {
2 var a: u16 = 0xabcd; // runtime-known
3 _ = &a;
4 const b: u8 = @intCast(a);
5 _ = b;
6}
7
8// test_error=cast truncated bits
doc/langref/test_integer_pointer_conversion.zig created+10
...@@ -0,0 +1,10 @@
1const expect = @import("std").testing.expect;
2
3test "@intFromPtr and @ptrFromInt" {
4 const ptr: *i32 = @ptrFromInt(0xdeadbee0);
5 const addr = @intFromPtr(ptr);
6 try expect(@TypeOf(addr) == usize);
7 try expect(addr == 0xdeadbee0);
8}
9
10// test
doc/langref/test_integer_widening.zig created+30
...@@ -0,0 +1,30 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4const mem = std.mem;
5
6test "integer widening" {
7 const a: u8 = 250;
8 const b: u16 = a;
9 const c: u32 = b;
10 const d: u64 = c;
11 const e: u64 = d;
12 const f: u128 = e;
13 try expect(f == a);
14}
15
16test "implicit unsigned integer to signed integer" {
17 const a: u8 = 250;
18 const b: i16 = a;
19 try expect(b == 250);
20}
21
22test "float widening" {
23 const a: f16 = 12.34;
24 const b: f32 = a;
25 const c: f64 = b;
26 const d: f128 = c;
27 try expect(d == a);
28}
29
30// test
doc/langref/test_invalid_defer.zig created+9
...@@ -0,0 +1,9 @@
1fn deferInvalidExample() !void {
2 defer {
3 return error.DeferError;
4 }
5
6 return error.DeferError;
7}
8
9// test_error=cannot return from defer expression
doc/langref/test_labeled_break.zig created+15
...@@ -0,0 +1,15 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "labeled break from labeled block expression" {
5 var y: i32 = 123;
6
7 const x = blk: {
8 y += 1;
9 break :blk y;
10 };
11 try expect(x == 124);
12 try expect(y == 124);
13}
14
15// test
doc/langref/test_merging_error_sets.zig created+30
...@@ -0,0 +1,30 @@
1const A = error{
2 NotDir,
3
4 /// A doc comment
5 PathNotFound,
6};
7const B = error{
8 OutOfMemory,
9
10 /// B doc comment
11 PathNotFound,
12};
13
14const C = A || B;
15
16fn foo() C!void {
17 return error.NotDir;
18}
19
20test "merge error sets" {
21 if (foo()) {
22 @panic("unexpected");
23 } else |err| switch (err) {
24 error.OutOfMemory => @panic("unexpected"),
25 error.PathNotFound => @panic("unexpected"),
26 error.NotDir => {},
27 }
28}
29
30// test
doc/langref/test_misaligned_pointer.zig created+24
...@@ -0,0 +1,24 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const BitField = packed struct {
5 a: u3,
6 b: u3,
7 c: u2,
8};
9
10var bit_field = BitField{
11 .a = 1,
12 .b = 2,
13 .c = 3,
14};
15
16test "pointer to non-byte-aligned field" {
17 try expect(bar(&bit_field.b) == 2);
18}
19
20fn bar(x: *const u3) u3 {
21 return x.*;
22}
23
24// test_error=expected type
doc/langref/test_missized_packed_struct.zig created+6
...@@ -0,0 +1,6 @@
1test "missized packed struct" {
2 const S = packed struct(u32) { a: u16, b: u8 };
3 _ = S{ .a = 4, .b = 2 };
4}
5
6// test_error=backing integer type 'u32' has bit size 32 but the struct fields have a total bit size of 24
doc/langref/test_multidimensional_arrays.zig created+24
...@@ -0,0 +1,24 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const mat4x4 = [4][4]f32{
5 [_]f32{ 1.0, 0.0, 0.0, 0.0 },
6 [_]f32{ 0.0, 1.0, 0.0, 1.0 },
7 [_]f32{ 0.0, 0.0, 1.0, 0.0 },
8 [_]f32{ 0.0, 0.0, 0.0, 1.0 },
9};
10test "multidimensional arrays" {
11 // Access the 2D array by indexing the outer array, and then the inner array.
12 try expect(mat4x4[1][1] == 1.0);
13
14 // Here we iterate with for loops.
15 for (mat4x4, 0..) |row, row_index| {
16 for (row, 0..) |cell, column_index| {
17 if (row_index == column_index) {
18 try expect(cell == 1.0);
19 }
20 }
21 }
22}
23
24// test
doc/langref/test_namespaced_container_level_variable.zig created+18
...@@ -0,0 +1,18 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "namespaced container level variable" {
5 try expect(foo() == 1235);
6 try expect(foo() == 1236);
7}
8
9const S = struct {
10 var x: i32 = 1234;
11};
12
13fn foo() i32 {
14 S.x += 1;
15 return S.x;
16}
17
18// test
doc/langref/test_no_op_casts.zig created+9
...@@ -0,0 +1,9 @@
1test "type coercion - const qualification" {
2 var a: i32 = 1;
3 const b: *i32 = &a;
4 foo(b);
5}
6
7fn foo(_: *const i32) void {}
8
9// test
doc/langref/test_noreturn.zig created+10
...@@ -0,0 +1,10 @@
1fn foo(condition: bool, b: u32) void {
2 const a = if (condition) b else return;
3 _ = a;
4 @panic("do something with a");
5}
6test "noreturn" {
7 foo(false, 1);
8}
9
10// test
doc/langref/test_noreturn_from_exit.zig created+19
...@@ -0,0 +1,19 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const native_arch = builtin.cpu.arch;
4const expect = std.testing.expect;
5
6const WINAPI: std.builtin.CallingConvention = if (native_arch == .x86) .Stdcall else .C;
7extern "kernel32" fn ExitProcess(exit_code: c_uint) callconv(WINAPI) noreturn;
8
9test "foo" {
10 const value = bar() catch ExitProcess(1);
11 try expect(value == 1234);
12}
13
14fn bar() anyerror!u32 {
15 return 1234;
16}
17
18// test
19// target=x86_64-windows
doc/langref/test_null_terminated_array.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "0-terminated sentinel array" {
5 const array = [_:0]u8 {1, 2, 3, 4};
6
7 try expect(@TypeOf(array) == [4:0]u8);
8 try expect(array.len == 4);
9 try expect(array[4] == 0);
10}
11
12test "extra 0s in 0-terminated sentinel array" {
13 // The sentinel value may appear earlier, but does not influence the compile-time 'len'.
14 const array = [_:0]u8 {1, 0, 0, 4};
15
16 try expect(@TypeOf(array) == [4:0]u8);
17 try expect(array.len == 4);
18 try expect(array[4] == 0);
19}
20
21// test
doc/langref/test_null_terminated_slice.zig created+11
...@@ -0,0 +1,11 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "0-terminated slice" {
5 const slice: [:0]const u8 = "hello";
6
7 try expect(slice.len == 5);
8 try expect(slice[5] == 0);
9}
10
11// test
doc/langref/test_null_terminated_slicing.zig created+14
...@@ -0,0 +1,14 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "0-terminated slicing" {
5 var array = [_]u8{ 3, 2, 1, 0, 3, 2, 1, 0 };
6 var runtime_length: usize = 3;
7 _ = &runtime_length;
8 const slice = array[0..runtime_length :0];
9
10 try expect(@TypeOf(slice) == [:0]u8);
11 try expect(slice.len == 3);
12}
13
14// test
doc/langref/test_opaque.zig created+13
...@@ -0,0 +1,13 @@
1const Derp = opaque {};
2const Wat = opaque {};
3
4extern fn bar(d: *Derp) void;
5fn foo(w: *Wat) callconv(.C) void {
6 bar(w);
7}
8
9test "call foo" {
10 foo(undefined);
11}
12
13// test_error=expected type '*test_opaque.Derp', found '*test_opaque.Wat'
doc/langref/test_optional_pointer.zig created+18
...@@ -0,0 +1,18 @@
1const expect = @import("std").testing.expect;
2
3test "optional pointers" {
4 // Pointers cannot be null. If you want a null pointer, use the optional
5 // prefix `?` to make the pointer type optional.
6 var ptr: ?*i32 = null;
7
8 var x: i32 = 1;
9 ptr = &x;
10
11 try expect(ptr.?.* == 1);
12
13 // Optional pointers are the same size as normal pointers, because pointer
14 // value 0 is used as the null value.
15 try expect(@sizeOf(?*i32) == @sizeOf(*i32));
16}
17
18// test
doc/langref/test_optional_type.zig created+14
...@@ -0,0 +1,14 @@
1const expect = @import("std").testing.expect;
2
3test "optional type" {
4 // Declare an optional and coerce from null:
5 var foo: ?i32 = null;
6
7 // Coerce from child type of an optional
8 foo = 1234;
9
10 // Use compile-time reflection to access the child type of the optional:
11 try comptime expect(@typeInfo(@TypeOf(foo)).Optional.child == i32);
12}
13
14// test
doc/langref/test_overaligned_packed_struct.zig created+15
...@@ -0,0 +1,15 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const S = packed struct {
5 a: u32,
6 b: u32,
7};
8test "overaligned pointer to packed struct" {
9 var foo: S align(4) = .{ .a = 1, .b = 2 };
10 const ptr: *align(4) S = &foo;
11 const ptr_to_b: *u32 = &ptr.b;
12 try expect(ptr_to_b.* == 2);
13}
14
15// test
doc/langref/test_packed_struct_field_address.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const BitField = packed struct {
5 a: u3,
6 b: u3,
7 c: u2,
8};
9
10var bit_field = BitField{
11 .a = 1,
12 .b = 2,
13 .c = 3,
14};
15
16test "pointers of sub-byte-aligned fields share addresses" {
17 try expect(@intFromPtr(&bit_field.a) == @intFromPtr(&bit_field.b));
18 try expect(@intFromPtr(&bit_field.a) == @intFromPtr(&bit_field.c));
19}
20
21// test
doc/langref/test_packed_structs.zig created+41
...@@ -0,0 +1,41 @@
1const std = @import("std");
2const native_endian = @import("builtin").target.cpu.arch.endian();
3const expect = std.testing.expect;
4
5const Full = packed struct {
6 number: u16,
7};
8const Divided = packed struct {
9 half1: u8,
10 quarter3: u4,
11 quarter4: u4,
12};
13
14test "@bitCast between packed structs" {
15 try doTheTest();
16 try comptime doTheTest();
17}
18
19fn doTheTest() !void {
20 try expect(@sizeOf(Full) == 2);
21 try expect(@sizeOf(Divided) == 2);
22 const full = Full{ .number = 0x1234 };
23 const divided: Divided = @bitCast(full);
24 try expect(divided.half1 == 0x34);
25 try expect(divided.quarter3 == 0x2);
26 try expect(divided.quarter4 == 0x1);
27
28 const ordered: [2]u8 = @bitCast(full);
29 switch (native_endian) {
30 .big => {
31 try expect(ordered[0] == 0x12);
32 try expect(ordered[1] == 0x34);
33 },
34 .little => {
35 try expect(ordered[0] == 0x34);
36 try expect(ordered[1] == 0x12);
37 },
38 }
39}
40
41// test
doc/langref/test_pass_by_reference_or_value.zig created+20
...@@ -0,0 +1,20 @@
1const Point = struct {
2 x: i32,
3 y: i32,
4};
5
6fn foo(point: Point) i32 {
7 // Here, `point` could be a reference, or a copy. The function body
8 // can ignore the difference and treat it as a value. Be very careful
9 // taking the address of the parameter - it should be treated as if
10 // the address will become invalid when the function returns.
11 return point.x + point.y;
12}
13
14const expect = @import("std").testing.expect;
15
16test "pass struct to function" {
17 try expect(foo(Point{ .x = 1, .y = 2 }) == 3);
18}
19
20// test
doc/langref/test_peer_type_resolution.zig created+120
...@@ -0,0 +1,120 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const mem = std.mem;
4
5test "peer resolve int widening" {
6 const a: i8 = 12;
7 const b: i16 = 34;
8 const c = a + b;
9 try expect(c == 46);
10 try expect(@TypeOf(c) == i16);
11}
12
13test "peer resolve arrays of different size to const slice" {
14 try expect(mem.eql(u8, boolToStr(true), "true"));
15 try expect(mem.eql(u8, boolToStr(false), "false"));
16 try comptime expect(mem.eql(u8, boolToStr(true), "true"));
17 try comptime expect(mem.eql(u8, boolToStr(false), "false"));
18}
19fn boolToStr(b: bool) []const u8 {
20 return if (b) "true" else "false";
21}
22
23test "peer resolve array and const slice" {
24 try testPeerResolveArrayConstSlice(true);
25 try comptime testPeerResolveArrayConstSlice(true);
26}
27fn testPeerResolveArrayConstSlice(b: bool) !void {
28 const value1 = if (b) "aoeu" else @as([]const u8, "zz");
29 const value2 = if (b) @as([]const u8, "zz") else "aoeu";
30 try expect(mem.eql(u8, value1, "aoeu"));
31 try expect(mem.eql(u8, value2, "zz"));
32}
33
34test "peer type resolution: ?T and T" {
35 try expect(peerTypeTAndOptionalT(true, false).? == 0);
36 try expect(peerTypeTAndOptionalT(false, false).? == 3);
37 comptime {
38 try expect(peerTypeTAndOptionalT(true, false).? == 0);
39 try expect(peerTypeTAndOptionalT(false, false).? == 3);
40 }
41}
42fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize {
43 if (c) {
44 return if (b) null else @as(usize, 0);
45 }
46
47 return @as(usize, 3);
48}
49
50test "peer type resolution: *[0]u8 and []const u8" {
51 try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
52 try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
53 comptime {
54 try expect(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
55 try expect(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
56 }
57}
58fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {
59 if (a) {
60 return &[_]u8{};
61 }
62
63 return slice[0..1];
64}
65test "peer type resolution: *[0]u8, []const u8, and anyerror![]u8" {
66 {
67 var data = "hi".*;
68 const slice = data[0..];
69 try expect((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0);
70 try expect((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
71 }
72 comptime {
73 var data = "hi".*;
74 const slice = data[0..];
75 try expect((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0);
76 try expect((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
77 }
78}
79fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 {
80 if (a) {
81 return &[_]u8{};
82 }
83
84 return slice[0..1];
85}
86
87test "peer type resolution: *const T and ?*T" {
88 const a: *const usize = @ptrFromInt(0x123456780);
89 const b: ?*usize = @ptrFromInt(0x123456780);
90 try expect(a == b);
91 try expect(b == a);
92}
93
94test "peer type resolution: error union switch" {
95 // The non-error and error cases are only peers if the error case is just a switch expression;
96 // the pattern `if (x) {...} else |err| blk: { switch (err) {...} }` does not consider the
97 // non-error and error case to be peers.
98 var a: error{ A, B, C }!u32 = 0;
99 _ = &a;
100 const b = if (a) |x|
101 x + 3
102 else |err| switch (err) {
103 error.A => 0,
104 error.B => 1,
105 error.C => null,
106 };
107 try expect(@TypeOf(b) == ?u32);
108
109 // The non-error and error cases are only peers if the error case is just a switch expression;
110 // the pattern `x catch |err| blk: { switch (err) {...} }` does not consider the unwrapped `x`
111 // and error case to be peers.
112 const c = a catch |err| switch (err) {
113 error.A => 0,
114 error.B => 1,
115 error.C => null,
116 };
117 try expect(@TypeOf(c) == ?u32);
118}
119
120// test
doc/langref/test_pointer_arithmetic.zig created+32
...@@ -0,0 +1,32 @@
1const expect = @import("std").testing.expect;
2
3test "pointer arithmetic with many-item pointer" {
4 const array = [_]i32{ 1, 2, 3, 4 };
5 var ptr: [*]const i32 = &array;
6
7 try expect(ptr[0] == 1);
8 ptr += 1;
9 try expect(ptr[0] == 2);
10
11 // slicing a many-item pointer without an end is equivalent to
12 // pointer arithmetic: `ptr[start..] == ptr + start`
13 try expect(ptr[1..] == ptr + 1);
14}
15
16test "pointer arithmetic with slices" {
17 var array = [_]i32{ 1, 2, 3, 4 };
18 var length: usize = 0; // var to make it runtime-known
19 _ = &length; // suppress 'var is never mutated' error
20 var slice = array[length..array.len];
21
22 try expect(slice[0] == 1);
23 try expect(slice.len == 4);
24
25 slice.ptr += 1;
26 // now the slice is in an bad state since len has not been updated
27
28 try expect(slice[0] == 2);
29 try expect(slice.len == 4);
30}
31
32// test
doc/langref/test_pointer_casting.zig created+23
...@@ -0,0 +1,23 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "pointer casting" {
5 const bytes align(@alignOf(u32)) = [_]u8{ 0x12, 0x12, 0x12, 0x12 };
6 const u32_ptr: *const u32 = @ptrCast(&bytes);
7 try expect(u32_ptr.* == 0x12121212);
8
9 // Even this example is contrived - there are better ways to do the above than
10 // pointer casting. For example, using a slice narrowing cast:
11 const u32_value = std.mem.bytesAsSlice(u32, bytes[0..])[0];
12 try expect(u32_value == 0x12121212);
13
14 // And even another way, the most straightforward way to do it:
15 try expect(@as(u32, @bitCast(bytes)) == 0x12121212);
16}
17
18test "pointer child type" {
19 // pointer types have a `child` field which tells you the type they point to.
20 try expect(@typeInfo(*u32).Pointer.child == u32);
21}
22
23// test
doc/langref/test_pointer_coerce_const_optional.zig created+11
...@@ -0,0 +1,11 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const mem = std.mem;
4
5test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
6 const window_name = [1][*]const u8{"window name"};
7 const x: [*]const ?[*]const u8 = &window_name;
8 try expect(mem.eql(u8, std.mem.sliceTo(@as([*:0]const u8, @ptrCast(x[0].?)), 0), "window name"));
9}
10
11// test
doc/langref/test_pointer_to_non-byte_aligned_field.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const BitField = packed struct {
5 a: u3,
6 b: u3,
7 c: u2,
8};
9
10var foo = BitField{
11 .a = 1,
12 .b = 2,
13 .c = 3,
14};
15
16test "pointer to non-byte-aligned field" {
17 const ptr = &foo.b;
18 try expect(ptr.* == 2);
19}
20
21// test
doc/langref/test_print_too_many_args.zig created+14
...@@ -0,0 +1,14 @@
1const print = @import("std").debug.print;
2
3const a_number: i32 = 1234;
4const a_string = "foobar";
5
6test "print too many arguments" {
7 print("here is a string: '{s}' here is a number: {}\n", .{
8 a_string,
9 a_number,
10 a_number,
11 });
12}
13
14// test_error=unused argument in 'here is a string: '{s}' here is a number: {}
doc/langref/test_reduce_builtin.zig created+15
...@@ -0,0 +1,15 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "vector @reduce" {
5 const V = @Vector(4, i32);
6 const value = V{ 1, -1, 1, -1 };
7 const result = value > @as(V, @splat(0));
8 // result is { true, false, true, false };
9 try comptime expect(@TypeOf(result) == @Vector(4, bool));
10 const is_all_true = @reduce(.And, result);
11 try comptime expect(@TypeOf(is_all_true) == bool);
12 try expect(is_all_true == false);
13}
14
15// test
doc/langref/test_scopes.zig created+12
...@@ -0,0 +1,12 @@
1test "separate scopes" {
2 {
3 const pi = 3.14;
4 _ = pi;
5 }
6 {
7 var pi: bool = true;
8 _ = &pi;
9 }
10}
11
12// test
doc/langref/test_sentinel_mismatch.zig created+18
...@@ -0,0 +1,18 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "sentinel mismatch" {
5 var array = [_]u8{ 3, 2, 1, 0 };
6
7 // Creating a sentinel-terminated slice from the array with a length of 2
8 // will result in the value `1` occupying the sentinel element position.
9 // This does not match the indicated sentinel value of `0` and will lead
10 // to a runtime panic.
11 var runtime_length: usize = 2;
12 _ = &runtime_length;
13 const slice = array[0..runtime_length :0];
14
15 _ = slice;
16}
17
18// test_safety=sentinel mismatch
doc/langref/test_setEvalBranchQuota_builtin.zig created+9
...@@ -0,0 +1,9 @@
1test "foo" {
2 comptime {
3 @setEvalBranchQuota(1001);
4 var i = 0;
5 while (i < 1001) : (i += 1) {}
6 }
7}
8
9// test
doc/langref/test_setRuntimeSafety_builtin.zig created+24
...@@ -0,0 +1,24 @@
1test "@setRuntimeSafety" {
2 // The builtin applies to the scope that it is called in. So here, integer overflow
3 // will not be caught in ReleaseFast and ReleaseSmall modes:
4 // var x: u8 = 255;
5 // x += 1; // undefined behavior in ReleaseFast/ReleaseSmall modes.
6 {
7 // However this block has safety enabled, so safety checks happen here,
8 // even in ReleaseFast and ReleaseSmall modes.
9 @setRuntimeSafety(true);
10 var x: u8 = 255;
11 x += 1;
12
13 {
14 // The value can be overridden at any scope. So here integer overflow
15 // would not be caught in any build mode.
16 @setRuntimeSafety(false);
17 // var x: u8 = 255;
18 // x += 1; // undefined behavior in all build modes.
19 }
20 }
21}
22
23// test_safety=integer overflow
24// optimize=ReleaseFast
doc/langref/test_shadowing.zig created+10
...@@ -0,0 +1,10 @@
1const pi = 3.14;
2
3test "inside test block" {
4 // Let's even go inside another block
5 {
6 var pi: i32 = 1234;
7 }
8}
9
10// test_error=local variable shadows declaration
doc/langref/test_shuffle_builtin.zig created+20
...@@ -0,0 +1,20 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "vector @shuffle" {
5 const a = @Vector(7, u8){ 'o', 'l', 'h', 'e', 'r', 'z', 'w' };
6 const b = @Vector(4, u8){ 'w', 'd', '!', 'x' };
7
8 // To shuffle within a single vector, pass undefined as the second argument.
9 // Notice that we can re-order, duplicate, or omit elements of the input vector
10 const mask1 = @Vector(5, i32){ 2, 3, 1, 1, 0 };
11 const res1: @Vector(5, u8) = @shuffle(u8, a, undefined, mask1);
12 try expect(std.mem.eql(u8, &@as([5]u8, res1), "hello"));
13
14 // Combining two vectors
15 const mask2 = @Vector(6, i32){ -1, 0, 4, 1, -2, -3 };
16 const res2: @Vector(6, u8) = @shuffle(u8, a, b, mask2);
17 try expect(std.mem.eql(u8, &@as([6]u8, res2), "world!"));
18}
19
20// test
doc/langref/test_simple_union.zig created+16
...@@ -0,0 +1,16 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const Payload = union {
5 int: i64,
6 float: f64,
7 boolean: bool,
8};
9test "simple union" {
10 var payload = Payload{ .int = 1234 };
11 try expect(payload.int == 1234);
12 payload = Payload{ .float = 12.34 };
13 try expect(payload.float == 12.34);
14}
15
16// test
doc/langref/test_single_item_pointer.zig created+35
...@@ -0,0 +1,35 @@
1const expect = @import("std").testing.expect;
2
3test "address of syntax" {
4 // Get the address of a variable:
5 const x: i32 = 1234;
6 const x_ptr = &x;
7
8 // Dereference a pointer:
9 try expect(x_ptr.* == 1234);
10
11 // When you get the address of a const variable, you get a const single-item pointer.
12 try expect(@TypeOf(x_ptr) == *const i32);
13
14 // If you want to mutate the value, you'd need an address of a mutable variable:
15 var y: i32 = 5678;
16 const y_ptr = &y;
17 try expect(@TypeOf(y_ptr) == *i32);
18 y_ptr.* += 1;
19 try expect(y_ptr.* == 5679);
20}
21
22test "pointer array access" {
23 // Taking an address of an individual element gives a
24 // single-item pointer. This kind of pointer
25 // does not support pointer arithmetic.
26 var array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
27 const ptr = &array[2];
28 try expect(@TypeOf(ptr) == *u8);
29
30 try expect(array[2] == 3);
31 ptr.* += 1;
32 try expect(array[2] == 4);
33}
34
35// test
doc/langref/test_slice_bounds.zig created+15
...@@ -0,0 +1,15 @@
1const expect = @import("std").testing.expect;
2
3test "pointer slicing" {
4 var array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
5 var start: usize = 2; // var to make it runtime-known
6 _ = &start; // suppress 'var is never mutated' error
7 const slice = array[start..4];
8 try expect(slice.len == 2);
9
10 try expect(array[3] == 4);
11 slice[1] += 1;
12 try expect(array[3] == 5);
13}
14
15// test
doc/langref/test_slices.zig created+52
...@@ -0,0 +1,52 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const mem = std.mem;
4const fmt = std.fmt;
5
6test "using slices for strings" {
7 // Zig has no concept of strings. String literals are const pointers
8 // to null-terminated arrays of u8, and by convention parameters
9 // that are "strings" are expected to be UTF-8 encoded slices of u8.
10 // Here we coerce *const [5:0]u8 and *const [6:0]u8 to []const u8
11 const hello: []const u8 = "hello";
12 const world: []const u8 = "世界";
13
14 var all_together: [100]u8 = undefined;
15 // You can use slice syntax with at least one runtime-known index on an
16 // array to convert an array into a slice.
17 var start: usize = 0;
18 _ = &start;
19 const all_together_slice = all_together[start..];
20 // String concatenation example.
21 const hello_world = try fmt.bufPrint(all_together_slice, "{s} {s}", .{ hello, world });
22
23 // Generally, you can use UTF-8 and not worry about whether something is a
24 // string. If you don't need to deal with individual characters, no need
25 // to decode.
26 try expect(mem.eql(u8, hello_world, "hello 世界"));
27}
28
29test "slice pointer" {
30 var array: [10]u8 = undefined;
31 const ptr = &array;
32 try expect(@TypeOf(ptr) == *[10]u8);
33
34 // A pointer to an array can be sliced just like an array:
35 var start: usize = 0;
36 var end: usize = 5;
37 _ = .{ &start, &end };
38 const slice = ptr[start..end];
39 // The slice is mutable because we sliced a mutable pointer.
40 try expect(@TypeOf(slice) == []u8);
41 slice[2] = 3;
42 try expect(array[2] == 3);
43
44 // Again, slicing with comptime-known indexes will produce another pointer
45 // to an array:
46 const ptr2 = slice[2..3];
47 try expect(ptr2.len == 1);
48 try expect(ptr2[0] == 3);
49 try expect(@TypeOf(ptr2) == *[1]u8);
50}
51
52// test
doc/langref/test_splat_builtin.zig created+10
...@@ -0,0 +1,10 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "vector @splat" {
5 const scalar: u32 = 5;
6 const result: @Vector(4, u32) = @splat(scalar);
7 try expect(std.mem.eql(u32, &@as([4]u32, result), &[_]u32{ 5, 5, 5, 5 }));
8}
9
10// test
doc/langref/test_src_builtin.zig created+17
...@@ -0,0 +1,17 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "@src" {
5 try doTheTest();
6}
7
8fn doTheTest() !void {
9 const src = @src();
10
11 try expect(src.line == 9);
12 try expect(src.column == 17);
13 try expect(std.mem.endsWith(u8, src.fn_name, "doTheTest"));
14 try expect(std.mem.endsWith(u8, src.file, "test_src_builtin.zig"));
15}
16
17// test
doc/langref/test_static_local_variable.zig created+17
...@@ -0,0 +1,17 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "static local variable" {
5 try expect(foo() == 1235);
6 try expect(foo() == 1236);
7}
8
9fn foo() i32 {
10 const S = struct {
11 var x: i32 = 1234;
12 };
13 S.x += 1;
14 return S.x;
15}
16
17// test
doc/langref/test_string_literal_to_const_slice.zig created+9
...@@ -0,0 +1,9 @@
1fn foo(s: []const u8) void {
2 _ = s;
3}
4
5test "string literal to constant slice" {
6 foo("hello");
7}
8
9// test
doc/langref/test_string_literal_to_slice.zig created+9
...@@ -0,0 +1,9 @@
1fn foo(s: []u8) void {
2 _ = s;
3}
4
5test "string literal to mutable slice" {
6 foo("hello");
7}
8
9// test_error=expected type '[]u8', found '*const [5:0]u8'
doc/langref/test_struct_result.zig created+15
...@@ -0,0 +1,15 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const Point = struct {x: i32, y: i32};
5
6test "anonymous struct literal" {
7 const pt: Point = .{
8 .x = 13,
9 .y = 67,
10 };
11 try expect(pt.x == 13);
12 try expect(pt.y == 67);
13}
14
15// test
doc/langref/test_structs.zig created+143
...@@ -0,0 +1,143 @@
1// Declare a struct.
2// Zig gives no guarantees about the order of fields and the size of
3// the struct but the fields are guaranteed to be ABI-aligned.
4const Point = struct {
5 x: f32,
6 y: f32,
7};
8
9// Maybe we want to pass it to OpenGL so we want to be particular about
10// how the bytes are arranged.
11const Point2 = packed struct {
12 x: f32,
13 y: f32,
14};
15
16
17// Declare an instance of a struct.
18const p = Point {
19 .x = 0.12,
20 .y = 0.34,
21};
22
23// Maybe we're not ready to fill out some of the fields.
24var p2 = Point {
25 .x = 0.12,
26 .y = undefined,
27};
28
29// Structs can have methods
30// Struct methods are not special, they are only namespaced
31// functions that you can call with dot syntax.
32const Vec3 = struct {
33 x: f32,
34 y: f32,
35 z: f32,
36
37 pub fn init(x: f32, y: f32, z: f32) Vec3 {
38 return Vec3 {
39 .x = x,
40 .y = y,
41 .z = z,
42 };
43 }
44
45 pub fn dot(self: Vec3, other: Vec3) f32 {
46 return self.x * other.x + self.y * other.y + self.z * other.z;
47 }
48};
49
50const expect = @import("std").testing.expect;
51test "dot product" {
52 const v1 = Vec3.init(1.0, 0.0, 0.0);
53 const v2 = Vec3.init(0.0, 1.0, 0.0);
54 try expect(v1.dot(v2) == 0.0);
55
56 // Other than being available to call with dot syntax, struct methods are
57 // not special. You can reference them as any other declaration inside
58 // the struct:
59 try expect(Vec3.dot(v1, v2) == 0.0);
60}
61
62// Structs can have declarations.
63// Structs can have 0 fields.
64const Empty = struct {
65 pub const PI = 3.14;
66};
67test "struct namespaced variable" {
68 try expect(Empty.PI == 3.14);
69 try expect(@sizeOf(Empty) == 0);
70
71 // you can still instantiate an empty struct
72 const does_nothing = Empty {};
73
74 _ = does_nothing;
75}
76
77// struct field order is determined by the compiler for optimal performance.
78// however, you can still calculate a struct base pointer given a field pointer:
79fn setYBasedOnX(x: *f32, y: f32) void {
80 const point: *Point = @fieldParentPtr("x", x);
81 point.y = y;
82}
83test "field parent pointer" {
84 var point = Point {
85 .x = 0.1234,
86 .y = 0.5678,
87 };
88 setYBasedOnX(&point.x, 0.9);
89 try expect(point.y == 0.9);
90}
91
92// You can return a struct from a function. This is how we do generics
93// in Zig:
94fn LinkedList(comptime T: type) type {
95 return struct {
96 pub const Node = struct {
97 prev: ?*Node,
98 next: ?*Node,
99 data: T,
100 };
101
102 first: ?*Node,
103 last: ?*Node,
104 len: usize,
105 };
106}
107
108test "linked list" {
109 // Functions called at compile-time are memoized. This means you can
110 // do this:
111 try expect(LinkedList(i32) == LinkedList(i32));
112
113 const list = LinkedList(i32){
114 .first = null,
115 .last = null,
116 .len = 0,
117 };
118 try expect(list.len == 0);
119
120 // Since types are first class values you can instantiate the type
121 // by assigning it to a variable:
122 const ListOfInts = LinkedList(i32);
123 try expect(ListOfInts == LinkedList(i32));
124
125 var node = ListOfInts.Node{
126 .prev = null,
127 .next = null,
128 .data = 1234,
129 };
130 const list2 = LinkedList(i32){
131 .first = &node,
132 .last = &node,
133 .len = 1,
134 };
135
136 // When using a pointer to a struct, fields can be accessed directly,
137 // without explicitly dereferencing the pointer.
138 // So you can do
139 try expect(list2.first.?.data == 1234);
140 // instead of try expect(list2.first.?.*.data == 1234);
141}
142
143// test
doc/langref/test_switch.zig created+66
...@@ -0,0 +1,66 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4
5test "switch simple" {
6 const a: u64 = 10;
7 const zz: u64 = 103;
8
9 // All branches of a switch expression must be able to be coerced to a
10 // common type.
11 //
12 // Branches cannot fallthrough. If fallthrough behavior is desired, combine
13 // the cases and use an if.
14 const b = switch (a) {
15 // Multiple cases can be combined via a ','
16 1, 2, 3 => 0,
17
18 // Ranges can be specified using the ... syntax. These are inclusive
19 // of both ends.
20 5...100 => 1,
21
22 // Branches can be arbitrarily complex.
23 101 => blk: {
24 const c: u64 = 5;
25 break :blk c * 2 + 1;
26 },
27
28 // Switching on arbitrary expressions is allowed as long as the
29 // expression is known at compile-time.
30 zz => zz,
31 blk: {
32 const d: u32 = 5;
33 const e: u32 = 100;
34 break :blk d + e;
35 } => 107,
36
37 // The else branch catches everything not already captured.
38 // Else branches are mandatory unless the entire range of values
39 // is handled.
40 else => 9,
41 };
42
43 try expect(b == 1);
44}
45
46// Switch expressions can be used outside a function:
47const os_msg = switch (builtin.target.os.tag) {
48 .linux => "we found a linux user",
49 else => "not a linux user",
50};
51
52// Inside a function, switch statements implicitly are compile-time
53// evaluated if the target expression is compile-time known.
54test "switch inside function" {
55 switch (builtin.target.os.tag) {
56 .fuchsia => {
57 // On an OS other than fuchsia, block is not even analyzed,
58 // so this compile error is not triggered.
59 // On fuchsia this compile error would be triggered.
60 @compileError("fuchsia not supported");
61 },
62 else => {},
63 }
64}
65
66// test
doc/langref/test_switch_modify_tagged_union.zig created+24
...@@ -0,0 +1,24 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const ComplexTypeTag = enum {
5 ok,
6 not_ok,
7};
8const ComplexType = union(ComplexTypeTag) {
9 ok: u8,
10 not_ok: void,
11};
12
13test "modify tagged union in switch" {
14 var c = ComplexType{ .ok = 42 };
15
16 switch (c) {
17 ComplexTypeTag.ok => |*value| value.* += 1,
18 ComplexTypeTag.not_ok => unreachable,
19 }
20
21 try expect(c.ok == 43);
22}
23
24// test
doc/langref/test_switch_non-exhaustive.zig created+27
...@@ -0,0 +1,27 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const Number = enum(u8) {
5 one,
6 two,
7 three,
8 _,
9};
10
11test "switch on non-exhaustive enum" {
12 const number = Number.one;
13 const result = switch (number) {
14 .one => true,
15 .two,
16 .three => false,
17 _ => false,
18 };
19 try expect(result);
20 const is_one = switch (number) {
21 .one => true,
22 else => false,
23 };
24 try expect(is_one);
25}
26
27// test
doc/langref/test_switch_tagged_union.zig created+38
...@@ -0,0 +1,38 @@
1const expect = @import("std").testing.expect;
2
3test "switch on tagged union" {
4 const Point = struct {
5 x: u8,
6 y: u8,
7 };
8 const Item = union(enum) {
9 a: u32,
10 c: Point,
11 d,
12 e: u32,
13 };
14
15 var a = Item{ .c = Point{ .x = 1, .y = 2 } };
16
17 // Switching on more complex enums is allowed.
18 const b = switch (a) {
19 // A capture group is allowed on a match, and will return the enum
20 // value matched. If the payload types of both cases are the same
21 // they can be put into the same switch prong.
22 Item.a, Item.e => |item| item,
23
24 // A reference to the matched value can be obtained using `*` syntax.
25 Item.c => |*item| blk: {
26 item.*.x += 1;
27 break :blk 6;
28 },
29
30 // No else is required if the types cases was exhaustively handled
31 Item.d => 8,
32 };
33
34 try expect(b == 6);
35 try expect(a.c.x == 2);
36}
37
38// test
doc/langref/test_tagName.zig created+13
...@@ -0,0 +1,13 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const Small2 = union(enum) {
5 a: i32,
6 b: bool,
7 c: u8,
8};
9test "@tagName" {
10 try expect(std.mem.eql(u8, @tagName(Small2.a), "a"));
11}
12
13// test
doc/langref/test_tagged_union.zig created+27
...@@ -0,0 +1,27 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const ComplexTypeTag = enum {
5 ok,
6 not_ok,
7};
8const ComplexType = union(ComplexTypeTag) {
9 ok: u8,
10 not_ok: void,
11};
12
13test "switch on tagged union" {
14 const c = ComplexType{ .ok = 42 };
15 try expect(@as(ComplexTypeTag, c) == ComplexTypeTag.ok);
16
17 switch (c) {
18 ComplexTypeTag.ok => |value| try expect(value == 42),
19 ComplexTypeTag.not_ok => unreachable,
20 }
21}
22
23test "get tag type" {
24 try expect(std.meta.Tag(ComplexType) == ComplexTypeTag);
25}
26
27// test
doc/langref/test_this_builtin.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "@This()" {
5 var items = [_]i32{ 1, 2, 3, 4 };
6 const list = List(i32){ .items = items[0..] };
7 try expect(list.length() == 4);
8}
9
10fn List(comptime T: type) type {
11 return struct {
12 const Self = @This();
13
14 items: []T,
15
16 fn length(self: Self) usize {
17 return self.items.len;
18 }
19 };
20}
21
22// test
doc/langref/test_thread_local_variables.zig created+20
...@@ -0,0 +1,20 @@
1const std = @import("std");
2const assert = std.debug.assert;
3
4threadlocal var x: i32 = 1234;
5
6test "thread local storage" {
7 const thread1 = try std.Thread.spawn(.{}, testTls, .{});
8 const thread2 = try std.Thread.spawn(.{}, testTls, .{});
9 testTls();
10 thread1.join();
11 thread2.join();
12}
13
14fn testTls() void {
15 assert(x == 1234);
16 x += 1;
17 assert(x == 1235);
18}
19
20// test
doc/langref/test_truncate_builtin.zig created+10
...@@ -0,0 +1,10 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "integer truncation" {
5 const a: u16 = 0xabcd;
6 const b: u8 = @truncate(a);
7 try expect(b == 0xcd);
8}
9
10// test
doc/langref/test_tuples.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "tuple" {
5 const values = .{
6 @as(u32, 1234),
7 @as(f64, 12.34),
8 true,
9 "hi",
10 } ++ .{false} ** 2;
11 try expect(values[0] == 1234);
12 try expect(values[4] == false);
13 inline for (values, 0..) |v, i| {
14 if (i != 2) continue;
15 try expect(v);
16 }
17 try expect(values.len == 6);
18 try expect(values.@"3"[0] == 'h');
19}
20
21// test
doc/langref/test_type_coercion.zig created+22
...@@ -0,0 +1,22 @@
1test "type coercion - variable declaration" {
2 const a: u8 = 1;
3 const b: u16 = a;
4 _ = b;
5}
6
7test "type coercion - function call" {
8 const a: u8 = 1;
9 foo(a);
10}
11
12fn foo(b: u16) void {
13 _ = b;
14}
15
16test "type coercion - @as builtin" {
17 const a: u8 = 1;
18 const b = @as(u16, a);
19 _ = b;
20}
21
22// test
doc/langref/test_undefined_behavior.zig created+5
...@@ -0,0 +1,5 @@
1test "safety check" {
2 unreachable;
3}
4
5// test_error=reached unreachable code
doc/langref/test_unhandled_enumeration_value.zig created+15
...@@ -0,0 +1,15 @@
1const Color = enum {
2 auto,
3 off,
4 on,
5};
6
7test "exhaustive switching" {
8 const color = Color.off;
9 switch (color) {
10 Color.auto => {},
11 Color.on => {},
12 }
13}
14
15// test_error=unhandled enumeration value
doc/langref/test_union_method.zig created+28
...@@ -0,0 +1,28 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4const Variant = union(enum) {
5 int: i32,
6 boolean: bool,
7
8 // void can be omitted when inferring enum tag type.
9 none,
10
11 fn truthy(self: Variant) bool {
12 return switch (self) {
13 Variant.int => |x_int| x_int != 0,
14 Variant.boolean => |x_bool| x_bool,
15 Variant.none => false,
16 };
17 }
18};
19
20test "union method" {
21 var v1 = Variant{ .int = 1 };
22 var v2 = Variant{ .boolean = false };
23
24 try expect(v1.truthy());
25 try expect(!v2.truthy());
26}
27
28// test
doc/langref/test_unreachable.zig created+11
...@@ -0,0 +1,11 @@
1// unreachable is used to assert that control flow will never reach a
2// particular location:
3test "basic math" {
4 const x = 1;
5 const y = 2;
6 if (x + y != 3) {
7 unreachable;
8 }
9}
10
11// test
doc/langref/test_unresolved_comptime_value.zig created+15
...@@ -0,0 +1,15 @@
1fn max(comptime T: type, a: T, b: T) T {
2 return if (a > b) a else b;
3}
4test "try to pass a runtime type" {
5 foo(false);
6}
7fn foo(condition: bool) void {
8 const result = max(
9 if (condition) f32 else u64,
10 1234,
11 5678);
12 _ = result;
13}
14
15// test_error=unable to resolve comptime value
doc/langref/test_usingnamespace.zig created+8
...@@ -0,0 +1,8 @@
1test "using std namespace" {
2 const S = struct {
3 usingnamespace @import("std");
4 };
5 try S.testing.expect(true);
6}
7
8// test
doc/langref/test_variable_alignment.zig created+15
...@@ -0,0 +1,15 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4
5test "variable alignment" {
6 var x: i32 = 1234;
7 const align_of_i32 = @alignOf(@TypeOf(x));
8 try expect(@TypeOf(&x) == *i32);
9 try expect(*i32 == *align(align_of_i32) i32);
10 if (builtin.target.cpu.arch == .x86_64) {
11 try expect(@typeInfo(*i32).Pointer.alignment == 4);
12 }
13}
14
15// test
doc/langref/test_variable_func_alignment.zig created+34
...@@ -0,0 +1,34 @@
1const expect = @import("std").testing.expect;
2
3var foo: u8 align(4) = 100;
4
5test "global variable alignment" {
6 try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
7 try expect(@TypeOf(&foo) == *align(4) u8);
8 const as_pointer_to_array: *align(4) [1]u8 = &foo;
9 const as_slice: []align(4) u8 = as_pointer_to_array;
10 const as_unaligned_slice: []u8 = as_slice;
11 try expect(as_unaligned_slice[0] == 100);
12}
13
14fn derp() align(@sizeOf(usize) * 2) i32 {
15 return 1234;
16}
17fn noop1() align(1) void {}
18fn noop4() align(4) void {}
19
20test "function alignment" {
21 try expect(derp() == 1234);
22 try expect(@TypeOf(derp) == fn () i32);
23 try expect(@TypeOf(&derp) == *align(@sizeOf(usize) * 2) const fn () i32);
24
25 noop1();
26 try expect(@TypeOf(noop1) == fn () void);
27 try expect(@TypeOf(&noop1) == *align(1) const fn () void);
28
29 noop4();
30 try expect(@TypeOf(noop4) == fn () void);
31 try expect(@TypeOf(&noop4) == *align(4) const fn () void);
32}
33
34// test
doc/langref/test_variadic_function.zig created+13
...@@ -0,0 +1,13 @@
1const std = @import("std");
2const testing = std.testing;
3
4pub extern "c" fn printf(format: [*:0]const u8, ...) c_int;
5
6test "variadic function" {
7 try testing.expect(printf("Hello, world!\n") == 14);
8 try testing.expect(@typeInfo(@TypeOf(printf)).Fn.is_var_args);
9}
10
11// test
12// link_libc
13// verbose_cimport
doc/langref/test_vector.zig created+41
...@@ -0,0 +1,41 @@
1const std = @import("std");
2const expectEqual = std.testing.expectEqual;
3
4test "Basic vector usage" {
5 // Vectors have a compile-time known length and base type.
6 const a = @Vector(4, i32){ 1, 2, 3, 4 };
7 const b = @Vector(4, i32){ 5, 6, 7, 8 };
8
9 // Math operations take place element-wise.
10 const c = a + b;
11
12 // Individual vector elements can be accessed using array indexing syntax.
13 try expectEqual(6, c[0]);
14 try expectEqual(8, c[1]);
15 try expectEqual(10, c[2]);
16 try expectEqual(12, c[3]);
17}
18
19test "Conversion between vectors, arrays, and slices" {
20 // Vectors and fixed-length arrays can be automatically assigned back and forth
21 const arr1: [4]f32 = [_]f32{ 1.1, 3.2, 4.5, 5.6 };
22 const vec: @Vector(4, f32) = arr1;
23 const arr2: [4]f32 = vec;
24 try expectEqual(arr1, arr2);
25
26 // You can also assign from a slice with comptime-known length to a vector using .*
27 const vec2: @Vector(2, f32) = arr1[1..3].*;
28
29 const slice: []const f32 = &arr1;
30 var offset: u32 = 1; // var to make it runtime-known
31 _ = &offset; // suppress 'var is never mutated' error
32 // To extract a comptime-known length from a runtime-known offset,
33 // first extract a new slice from the starting offset, then an array of
34 // comptime-known length
35 const vec3: @Vector(2, f32) = slice[offset..][0..2].*;
36 try expectEqual(slice[offset], vec2[0]);
37 try expectEqual(slice[offset + 1], vec2[1]);
38 try expectEqual(vec2, vec3);
39}
40
41// test
doc/langref/test_void_ignored.zig created+15
...@@ -0,0 +1,15 @@
1test "void is ignored" {
2 returnsVoid();
3}
4
5test "explicitly ignoring expression value" {
6 _ = foo();
7}
8
9fn returnsVoid() void {}
10
11fn foo() i32 {
12 return 1234;
13}
14
15// test
doc/langref/test_void_in_hashmap.zig created+18
...@@ -0,0 +1,18 @@
1const std = @import("std");
2const expect = std.testing.expect;
3
4test "turn HashMap into a set with void" {
5 var map = std.AutoHashMap(i32, void).init(std.testing.allocator);
6 defer map.deinit();
7
8 try map.put(1, {});
9 try map.put(2, {});
10
11 try expect(map.contains(2));
12 try expect(!map.contains(3));
13
14 _ = map.remove(2);
15 try expect(!map.contains(2));
16}
17
18// test
doc/langref/test_volatile.zig created+8
...@@ -0,0 +1,8 @@
1const expect = @import("std").testing.expect;
2
3test "volatile" {
4 const mmio_ptr: *volatile u8 = @ptrFromInt(0x12345678);
5 try expect(@TypeOf(mmio_ptr) == *volatile u8);
6}
7
8// test
doc/langref/test_wasmMemoryGrow_builtin.zig created+13
...@@ -0,0 +1,13 @@
1const std = @import("std");
2const native_arch = @import("builtin").target.cpu.arch;
3const expect = std.testing.expect;
4
5test "@wasmMemoryGrow" {
6 if (native_arch != .wasm32) return error.SkipZigTest;
7
8 const prev = @wasmMemorySize(0);
9 try expect(prev == @wasmMemoryGrow(0, 1));
10 try expect(prev + 1 == @wasmMemorySize(0));
11}
12
13// test
doc/langref/test_while.zig created+11
...@@ -0,0 +1,11 @@
1const expect = @import("std").testing.expect;
2
3test "while basic" {
4 var i: usize = 0;
5 while (i < 10) {
6 i += 1;
7 }
8 try expect(i == 10);
9}
10
11// test
doc/langref/test_while_break.zig created+13
...@@ -0,0 +1,13 @@
1const expect = @import("std").testing.expect;
2
3test "while break" {
4 var i: usize = 0;
5 while (true) {
6 if (i == 10)
7 break;
8 i += 1;
9 }
10 try expect(i == 10);
11}
12
13// test
doc/langref/test_while_continue.zig created+14
...@@ -0,0 +1,14 @@
1const expect = @import("std").testing.expect;
2
3test "while continue" {
4 var i: usize = 0;
5 while (true) {
6 i += 1;
7 if (i < 10)
8 continue;
9 break;
10 }
11 try expect(i == 10);
12}
13
14// test
doc/langref/test_while_continue_expression.zig created+18
...@@ -0,0 +1,18 @@
1const expect = @import("std").testing.expect;
2
3test "while loop continue expression" {
4 var i: usize = 0;
5 while (i < 10) : (i += 1) {}
6 try expect(i == 10);
7}
8
9test "while loop continue expression, more complicated" {
10 var i: usize = 1;
11 var j: usize = 1;
12 while (i * j < 2000) : ({ i *= 2; j *= 3; }) {
13 const my_ij = i * j;
14 try expect(my_ij < 2000);
15 }
16}
17
18// test
doc/langref/test_while_else.zig created+17
...@@ -0,0 +1,17 @@
1const expect = @import("std").testing.expect;
2
3test "while else" {
4 try expect(rangeHasNumber(0, 10, 5));
5 try expect(!rangeHasNumber(0, 10, 15));
6}
7
8fn rangeHasNumber(begin: usize, end: usize, number: usize) bool {
9 var i = begin;
10 return while (i < end) : (i += 1) {
11 if (i == number) {
12 break true;
13 }
14 } else false;
15}
16
17// test
doc/langref/test_while_error_capture.zig created+22
...@@ -0,0 +1,22 @@
1const expect = @import("std").testing.expect;
2
3test "while error union capture" {
4 var sum1: u32 = 0;
5 numbers_left = 3;
6 while (eventuallyErrorSequence()) |value| {
7 sum1 += value;
8 } else |err| {
9 try expect(err == error.ReachedZero);
10 }
11}
12
13var numbers_left: u32 = undefined;
14
15fn eventuallyErrorSequence() anyerror!u32 {
16 return if (numbers_left == 0) error.ReachedZero else blk: {
17 numbers_left -= 1;
18 break :blk numbers_left;
19 };
20}
21
22// test
doc/langref/test_while_nested_break.zig created+18
...@@ -0,0 +1,18 @@
1test "nested break" {
2 outer: while (true) {
3 while (true) {
4 break :outer;
5 }
6 }
7}
8
9test "nested continue" {
10 var i: usize = 0;
11 outer: while (i < 10) : (i += 1) {
12 while (true) {
13 continue :outer;
14 }
15 }
16}
17
18// test
doc/langref/test_while_null_capture.zig created+38
...@@ -0,0 +1,38 @@
1const expect = @import("std").testing.expect;
2
3test "while null capture" {
4 var sum1: u32 = 0;
5 numbers_left = 3;
6 while (eventuallyNullSequence()) |value| {
7 sum1 += value;
8 }
9 try expect(sum1 == 3);
10
11 // null capture with an else block
12 var sum2: u32 = 0;
13 numbers_left = 3;
14 while (eventuallyNullSequence()) |value| {
15 sum2 += value;
16 } else {
17 try expect(sum2 == 3);
18 }
19
20 // null capture with a continue expression
21 var i: u32 = 0;
22 var sum3: u32 = 0;
23 numbers_left = 3;
24 while (eventuallyNullSequence()) |value| : (i += 1) {
25 sum3 += value;
26 }
27 try expect(i == 3);
28}
29
30var numbers_left: u32 = undefined;
31fn eventuallyNullSequence() ?u32 {
32 return if (numbers_left == 0) null else blk: {
33 numbers_left -= 1;
34 break :blk numbers_left;
35 };
36}
37
38// test
doc/langref/test_without_setEvalBranchQuota_builtin.zig created+8
...@@ -0,0 +1,8 @@
1test "foo" {
2 comptime {
3 var i = 0;
4 while (i < 1001) : (i += 1) {}
5 }
6}
7
8// test_error=evaluation exceeded 1000 backwards branches
doc/langref/test_wraparound_semantics.zig created+14
...@@ -0,0 +1,14 @@
1const std = @import("std");
2const expect = std.testing.expect;
3const minInt = std.math.minInt;
4const maxInt = std.math.maxInt;
5
6test "wraparound addition and subtraction" {
7 const x: i32 = maxInt(i32);
8 const min_val = x +% 1;
9 try expect(min_val == minInt(i32));
10 const max_val = min_val -% 1;
11 try expect(max_val == maxInt(i32));
12}
13
14// test
doc/langref/test_wrong_union_access.zig created+11
...@@ -0,0 +1,11 @@
1const Payload = union {
2 int: i64,
3 float: f64,
4 boolean: bool,
5};
6test "simple union" {
7 var payload = Payload{ .int = 1234 };
8 payload.float = 12.34;
9}
10
11// test_error=access of union field 'float' while field 'int' is active
doc/langref/testing_detect_leak.zig created+11
...@@ -0,0 +1,11 @@
1const std = @import("std");
2
3test "detect leak" {
4 var list = std.ArrayList(u21).init(std.testing.allocator);
5 // missing `defer list.deinit();`
6 try list.append('☔');
7
8 try std.testing.expect(list.items.len == 1);
9}
10
11// test_error=1 tests leaked memory
doc/langref/testing_detect_test.zig created+13
...@@ -0,0 +1,13 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const expect = std.testing.expect;
4
5test "builtin.is_test" {
6 try expect(isATest());
7}
8
9fn isATest() bool {
10 return builtin.is_test;
11}
12
13// test
doc/langref/testing_error_with_if.zig created+17
...@@ -0,0 +1,17 @@
1const print = @import("std").debug.print;
2
3pub fn main() void {
4 const result = getNumberOrFail();
5
6 if (result) |number| {
7 print("got number: {}\n", .{number});
8 } else |err| {
9 print("got error: {s}\n", .{@errorName(err)});
10 }
11}
12
13fn getNumberOrFail() !i32 {
14 return error.UnableToReturnNumber;
15}
16
17// exe=succeed
doc/langref/testing_failure.zig created+11
...@@ -0,0 +1,11 @@
1const std = @import("std");
2
3test "expect this to fail" {
4 try std.testing.expect(false);
5}
6
7test "expect this to succeed" {
8 try std.testing.expect(true);
9}
10
11// test_error=
doc/langref/testing_introduction.zig created+23
...@@ -0,0 +1,23 @@
1const std = @import("std");
2
3test "expect addOne adds one to 41" {
4
5 // The Standard Library contains useful functions to help create tests.
6 // `expect` is a function that verifies its argument is true.
7 // It will return an error if its argument is false to indicate a failure.
8 // `try` is used to return an error to the test runner to notify it that the test failed.
9 try std.testing.expect(addOne(41) == 42);
10}
11
12test addOne {
13 // A test name can also be written using an identifier.
14 // This is a doctest, and serves as documentation for `addOne`.
15 try std.testing.expect(addOne(41) == 42);
16}
17
18/// The function `addOne` adds one to the number given as its argument.
19fn addOne(number: i32) i32 {
20 return number + 1;
21}
22
23// test
doc/langref/testing_namespace.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("std");
2
3test "expectEqual demo" {
4 const expected: i32 = 42;
5 const actual = 42;
6
7 // The first argument to `expectEqual` is the known, expected, result.
8 // The second argument is the result of some expression.
9 // The actual's type is casted to the type of expected.
10 try std.testing.expectEqual(expected, actual);
11}
12
13test "expectError demo" {
14 const expected_error = error.DemoError;
15 const actual_error_union: anyerror!void = error.DemoError;
16
17 // `expectError` will fail when the actual error is different than
18 // the expected error.
19 try std.testing.expectError(expected_error, actual_error_union);
20}
21
22// test
doc/langref/testing_null_with_if.zig created+12
...@@ -0,0 +1,12 @@
1const print = @import("std").debug.print;
2pub fn main() void {
3 const optional_number: ?i32 = null;
4
5 if (optional_number) |number| {
6 print("got number: {}\n", .{number});
7 } else {
8 print("it's null\n", .{});
9 }
10}
11
12// exe=succeed
doc/langref/testing_skip.zig created+5
...@@ -0,0 +1,5 @@
1test "this will be skipped" {
2 return error.SkipZigTest;
3}
4
5// test
doc/langref/tldoc_comments.zig created+11
...@@ -0,0 +1,11 @@
1//! This module provides functions for retrieving the current date and
2//! time with varying degrees of precision and accuracy. It does not
3//! depend on libc, but will use functions from it if available.
4
5const S = struct {
6 //! Top level comments are allowed inside a container other than a module,
7 //! but it is not very useful. Currently, when producing the package
8 //! documentation, these comments are ignored.
9};
10
11// syntax
doc/langref/try.zig created+8
...@@ -0,0 +1,8 @@
1const parseU64 = @import("error_union_parsing_u64.zig").parseU64;
2
3fn doAThing(str: []u8) !void {
4 const number = try parseU64(str, 10);
5 _ = number; // ...
6}
7
8// syntax
doc/langref/unattached_doc-comment.zig created+5
...@@ -0,0 +1,5 @@
1pub fn main() void {}
2
3/// End of file
4
5// obj=unattached documentation comment
doc/langref/undefined_active_union_field.zig created+19
...@@ -0,0 +1,19 @@
1const std = @import("std");
2
3const Foo = union {
4 float: f32,
5 int: u32,
6};
7
8pub fn main() void {
9 var f = Foo{ .int = 42 };
10 f = Foo{ .float = undefined };
11 bar(&f);
12 std.debug.print("value: {}\n", .{f.float});
13}
14
15fn bar(f: *Foo) void {
16 f.float = 12.34;
17}
18
19// exe=succeed
doc/langref/values.zig created+51
...@@ -0,0 +1,51 @@
1// Top-level declarations are order-independent:
2const print = std.debug.print;
3const std = @import("std");
4const os = std.os;
5const assert = std.debug.assert;
6
7pub fn main() void {
8 // integers
9 const one_plus_one: i32 = 1 + 1;
10 print("1 + 1 = {}\n", .{one_plus_one});
11
12 // floats
13 const seven_div_three: f32 = 7.0 / 3.0;
14 print("7.0 / 3.0 = {}\n", .{seven_div_three});
15
16 // boolean
17 print("{}\n{}\n{}\n", .{
18 true and false,
19 true or false,
20 !true,
21 });
22
23 // optional
24 var optional_value: ?[]const u8 = null;
25 assert(optional_value == null);
26
27 print("\noptional 1\ntype: {}\nvalue: {?s}\n", .{
28 @TypeOf(optional_value), optional_value,
29 });
30
31 optional_value = "hi";
32 assert(optional_value != null);
33
34 print("\noptional 2\ntype: {}\nvalue: {?s}\n", .{
35 @TypeOf(optional_value), optional_value,
36 });
37
38 // error union
39 var number_or_error: anyerror!i32 = error.ArgNotFound;
40
41 print("\nerror union 1\ntype: {}\nvalue: {!}\n", .{
42 @TypeOf(number_or_error), number_or_error, });
43
44 number_or_error = 1234;
45
46 print("\nerror union 2\ntype: {}\nvalue: {!}\n", .{
47 @TypeOf(number_or_error), number_or_error,
48 });
49}
50
51// exe=succeed
doc/langref/var_must_be_initialized.zig created+7
...@@ -0,0 +1,7 @@
1pub fn main() void {
2 var x: i32;
3
4 x = 1;
5}
6
7// exe=build_fail
doc/langref/verbose_cimport_flag.zig created+11
...@@ -0,0 +1,11 @@
1const c = @cImport({
2 @cDefine("_NO_CRT_STDIO_INLINE", "1");
3 @cInclude("stdio.h");
4});
5pub fn main() void {
6 _ = c;
7}
8
9// exe=succeed
10// link_libc
11// verbose_cimport
doc/langref/wasi_args.zig created+15
...@@ -0,0 +1,15 @@
1const std = @import("std");
2
3pub fn main() !void {
4 var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
5 const gpa = general_purpose_allocator.allocator();
6 const args = try std.process.argsAlloc(gpa);
7 defer std.process.argsFree(gpa, args);
8
9 for (args, 0..) |arg, i| {
10 std.debug.print("{}: {s}\n", .{ i, arg });
11 }
12}
13
14// exe=succeed
15// target=wasm32-wasi
doc/langref/wasi_preopens.zig created+20
...@@ -0,0 +1,20 @@
1const std = @import("std");
2const fs = std.fs;
3
4pub fn main() !void {
5 var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
6 const gpa = general_purpose_allocator.allocator();
7
8 var arena_instance = std.heap.ArenaAllocator.init(gpa);
9 defer arena_instance.deinit();
10 const arena = arena_instance.allocator();
11
12 const preopens = try fs.wasi.preopensAlloc(arena);
13
14 for (preopens.names, 0..) |preopen, i| {
15 std.debug.print("{}: {s}\n", .{ i, preopen });
16 }
17}
18
19// exe=succeed
20// target=wasm32-wasi
doc/langref/zero_bit_types.zig created+8
...@@ -0,0 +1,8 @@
1export fn entry() void {
2 var x: void = {};
3 var y: void = {};
4 x = y;
5 y = x;
6}
7
8// syntax
tools/docgen.zig+50-1263
...@@ -10,99 +10,80 @@ const mem = std.mem;...@@ -10,99 +10,80 @@ const mem = std.mem;
10const testing = std.testing;10const testing = std.testing;
11const Allocator = std.mem.Allocator;11const Allocator = std.mem.Allocator;
12const getExternalExecutor = std.zig.system.getExternalExecutor;12const getExternalExecutor = std.zig.system.getExternalExecutor;
13const fatal = std.zig.fatal;
1314
14const max_doc_file_size = 10 * 1024 * 1024;15const max_doc_file_size = 10 * 1024 * 1024;
1516
16const obj_ext = builtin.object_format.fileExt(builtin.cpu.arch);17const obj_ext = builtin.object_format.fileExt(builtin.cpu.arch);
17const tmp_dir_name = "docgen_tmp";
1818
19const usage =19const usage =
20 \\Usage: docgen [--zig] [--skip-code-tests] input output"20 \\Usage: docgen [options] input output
21 \\21 \\
22 \\ Generates an HTML document from a docgen template.22 \\ Generates an HTML document from a docgen template.
23 \\23 \\
24 \\Options:24 \\Options:
25 \\ --code-dir dir Path to directory containing code example outputs
25 \\ -h, --help Print this help and exit26 \\ -h, --help Print this help and exit
26 \\ --skip-code-tests Skip the doctests
27 \\27 \\
28;28;
2929
30fn fatal(comptime format: []const u8, args: anytype) noreturn {
31 const stderr = io.getStdErr().writer();
32
33 stderr.print("error: " ++ format ++ "\n", args) catch {};
34 process.exit(1);
35}
36
37pub fn main() !void {30pub fn main() !void {
38 var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);31 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
39 defer arena.deinit();32 defer arena_instance.deinit();
4033
41 const allocator = arena.allocator();34 const arena = arena_instance.allocator();
4235
43 var args_it = try process.argsWithAllocator(allocator);36 var args_it = try process.argsWithAllocator(arena);
44 if (!args_it.skip()) @panic("expected self arg");37 if (!args_it.skip()) @panic("expected self arg");
4538
46 var zig_exe: []const u8 = "zig";39 var opt_code_dir: ?[]const u8 = null;
47 var opt_zig_lib_dir: ?[]const u8 = null;40 var opt_input: ?[]const u8 = null;
48 var do_code_tests = true;41 var opt_output: ?[]const u8 = null;
49 var files = [_][]const u8{ "", "" };
5042
51 var i: usize = 0;
52 while (args_it.next()) |arg| {43 while (args_it.next()) |arg| {
53 if (mem.startsWith(u8, arg, "-")) {44 if (mem.startsWith(u8, arg, "-")) {
54 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {45 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
55 const stdout = io.getStdOut().writer();46 const stdout = io.getStdOut().writer();
56 try stdout.writeAll(usage);47 try stdout.writeAll(usage);
57 process.exit(0);48 process.exit(0);
58 } else if (mem.eql(u8, arg, "--zig")) {49 } else if (mem.eql(u8, arg, "--code-dir")) {
59 if (args_it.next()) |param| {
60 zig_exe = param;
61 } else {
62 fatal("expected parameter after --zig", .{});
63 }
64 } else if (mem.eql(u8, arg, "--zig-lib-dir")) {
65 if (args_it.next()) |param| {50 if (args_it.next()) |param| {
66 // Convert relative to absolute because this will be passed51 opt_code_dir = param;
67 // to a child process with a different cwd.
68 opt_zig_lib_dir = try fs.realpathAlloc(allocator, param);
69 } else {52 } else {
70 fatal("expected parameter after --zig-lib-dir", .{});53 fatal("expected parameter after --code-dir", .{});
71 }54 }
72 } else if (mem.eql(u8, arg, "--skip-code-tests")) {
73 do_code_tests = false;
74 } else {55 } else {
75 fatal("unrecognized option: '{s}'", .{arg});56 fatal("unrecognized option: '{s}'", .{arg});
76 }57 }
58 } else if (opt_input == null) {
59 opt_input = arg;
60 } else if (opt_output == null) {
61 opt_output = arg;
77 } else {62 } else {
78 if (i > 1) {63 fatal("unexpected positional argument: '{s}'", .{arg});
79 fatal("too many arguments", .{});
80 }
81 files[i] = arg;
82 i += 1;
83 }64 }
84 }65 }
85 if (i < 2) {66 const input_path = opt_input orelse fatal("missing input file", .{});
86 fatal("not enough arguments", .{});67 const output_path = opt_output orelse fatal("missing output file", .{});
87 }68 const code_dir_path = opt_code_dir orelse fatal("missing --code-dir argument", .{});
8869
89 var in_file = try fs.cwd().openFile(files[0], .{ .mode = .read_only });70 var in_file = try fs.cwd().openFile(input_path, .{});
90 defer in_file.close();71 defer in_file.close();
9172
92 var out_file = try fs.cwd().createFile(files[1], .{});73 var out_file = try fs.cwd().createFile(output_path, .{});
93 defer out_file.close();74 defer out_file.close();
9475
95 const input_file_bytes = try in_file.reader().readAllAlloc(allocator, max_doc_file_size);76 var code_dir = try fs.cwd().openDir(code_dir_path, .{});
77 defer code_dir.close();
9678
97 var buffered_writer = io.bufferedWriter(out_file.writer());79 const input_file_bytes = try in_file.reader().readAllAlloc(arena, max_doc_file_size);
9880
99 var tokenizer = Tokenizer.init(files[0], input_file_bytes);81 var buffered_writer = io.bufferedWriter(out_file.writer());
100 var toc = try genToc(allocator, &tokenizer);
10182
102 try fs.cwd().makePath(tmp_dir_name);83 var tokenizer = Tokenizer.init(input_path, input_file_bytes);
103 defer fs.cwd().deleteTree(tmp_dir_name) catch {};84 var toc = try genToc(arena, &tokenizer);
10485
105 try genHtml(allocator, &tokenizer, &toc, buffered_writer.writer(), zig_exe, opt_zig_lib_dir, do_code_tests);86 try genHtml(arena, &tokenizer, &toc, code_dir, buffered_writer.writer());
106 try buffered_writer.flush();87 try buffered_writer.flush();
107}88}
10889
...@@ -127,7 +108,6 @@ const Tokenizer = struct {...@@ -127,7 +108,6 @@ const Tokenizer = struct {
127 index: usize,108 index: usize,
128 state: State,109 state: State,
129 source_file_name: []const u8,110 source_file_name: []const u8,
130 code_node_count: usize,
131111
132 const State = enum {112 const State = enum {
133 start,113 start,
...@@ -143,7 +123,6 @@ const Tokenizer = struct {...@@ -143,7 +123,6 @@ const Tokenizer = struct {
143 .index = 0,123 .index = 0,
144 .state = .start,124 .state = .start,
145 .source_file_name = source_file_name,125 .source_file_name = source_file_name,
146 .code_node_count = 0,
147 };126 };
148 }127 }
149128
...@@ -311,34 +290,9 @@ const SeeAlsoItem = struct {...@@ -311,34 +290,9 @@ const SeeAlsoItem = struct {
311 token: Token,290 token: Token,
312};291};
313292
314const ExpectedOutcome = enum {
315 succeed,
316 fail,
317 build_fail,
318};
319
320const Code = struct {293const Code = struct {
321 id: Id,
322 name: []const u8,294 name: []const u8,
323 source_token: Token,295 token: Token,
324 just_check_syntax: bool,
325 mode: std.builtin.OptimizeMode,
326 link_objects: []const []const u8,
327 target_str: ?[]const u8,
328 link_libc: bool,
329 link_mode: ?std.builtin.LinkMode,
330 disable_cache: bool,
331 verbose_cimport: bool,
332 additional_options: []const []const u8,
333
334 const Id = union(enum) {
335 @"test",
336 test_error: []const u8,
337 test_safety: []const u8,
338 exe: ExpectedOutcome,
339 obj: ?[]const u8,
340 lib,
341 };
342};296};
343297
344const Link = struct {298const Link = struct {
...@@ -543,127 +497,16 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc {...@@ -543,127 +497,16 @@ fn genToc(allocator: Allocator, tokenizer: *Tokenizer) !Toc {
543 .token = name_tok,497 .token = name_tok,
544 },498 },
545 });499 });
546 } else if (mem.eql(u8, tag_name, "code_begin")) {500 } else if (mem.eql(u8, tag_name, "code")) {
547 _ = try eatToken(tokenizer, .separator);
548 const code_kind_tok = try eatToken(tokenizer, .tag_content);
549 _ = try eatToken(tokenizer, .separator);501 _ = try eatToken(tokenizer, .separator);
550 const name_tok = try eatToken(tokenizer, .tag_content);502 const name_tok = try eatToken(tokenizer, .tag_content);
551 const name = tokenizer.buffer[name_tok.start..name_tok.end];503 _ = try eatToken(tokenizer, .bracket_close);
552 var error_str: []const u8 = "";504 try nodes.append(.{
553 const maybe_sep = tokenizer.next();505 .Code = .{
554 switch (maybe_sep.id) {506 .name = tokenizer.buffer[name_tok.start..name_tok.end],
555 .separator => {507 .token = name_tok,
556 const error_tok = try eatToken(tokenizer, .tag_content);
557 error_str = tokenizer.buffer[error_tok.start..error_tok.end];
558 _ = try eatToken(tokenizer, .bracket_close);
559 },
560 .bracket_close => {},
561 else => return parseError(tokenizer, token, "invalid token", .{}),
562 }
563 const code_kind_str = tokenizer.buffer[code_kind_tok.start..code_kind_tok.end];
564 var code_kind_id: Code.Id = undefined;
565 var just_check_syntax = false;
566 if (mem.eql(u8, code_kind_str, "exe")) {
567 code_kind_id = Code.Id{ .exe = .succeed };
568 } else if (mem.eql(u8, code_kind_str, "exe_err")) {
569 code_kind_id = Code.Id{ .exe = .fail };
570 } else if (mem.eql(u8, code_kind_str, "exe_build_err")) {
571 code_kind_id = Code.Id{ .exe = .build_fail };
572 } else if (mem.eql(u8, code_kind_str, "test")) {
573 code_kind_id = .@"test";
574 } else if (mem.eql(u8, code_kind_str, "test_err")) {
575 code_kind_id = Code.Id{ .test_error = error_str };
576 } else if (mem.eql(u8, code_kind_str, "test_safety")) {
577 code_kind_id = Code.Id{ .test_safety = error_str };
578 } else if (mem.eql(u8, code_kind_str, "obj")) {
579 code_kind_id = Code.Id{ .obj = null };
580 } else if (mem.eql(u8, code_kind_str, "obj_err")) {
581 code_kind_id = Code.Id{ .obj = error_str };
582 } else if (mem.eql(u8, code_kind_str, "lib")) {
583 code_kind_id = Code.Id.lib;
584 } else if (mem.eql(u8, code_kind_str, "syntax")) {
585 code_kind_id = Code.Id{ .obj = null };
586 just_check_syntax = true;
587 } else {
588 return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {s}", .{code_kind_str});
589 }
590
591 var mode: std.builtin.OptimizeMode = .Debug;
592 var link_objects = std.ArrayList([]const u8).init(allocator);
593 defer link_objects.deinit();
594 var target_str: ?[]const u8 = null;
595 var link_libc = false;
596 var link_mode: ?std.builtin.LinkMode = null;
597 var disable_cache = false;
598 var verbose_cimport = false;
599 var additional_options = std.ArrayList([]const u8).init(allocator);
600 defer additional_options.deinit();
601
602 const source_token = while (true) {
603 const content_tok = try eatToken(tokenizer, .content);
604 _ = try eatToken(tokenizer, .bracket_open);
605 const end_code_tag = try eatToken(tokenizer, .tag_content);
606 const end_tag_name = tokenizer.buffer[end_code_tag.start..end_code_tag.end];
607 if (mem.eql(u8, end_tag_name, "code_release_fast")) {
608 mode = .ReleaseFast;
609 } else if (mem.eql(u8, end_tag_name, "code_release_safe")) {
610 mode = .ReleaseSafe;
611 } else if (mem.eql(u8, end_tag_name, "code_disable_cache")) {
612 disable_cache = true;
613 } else if (mem.eql(u8, end_tag_name, "code_verbose_cimport")) {
614 verbose_cimport = true;
615 } else if (mem.eql(u8, end_tag_name, "code_link_object")) {
616 _ = try eatToken(tokenizer, .separator);
617 const obj_tok = try eatToken(tokenizer, .tag_content);
618 try link_objects.append(tokenizer.buffer[obj_tok.start..obj_tok.end]);
619 } else if (mem.eql(u8, end_tag_name, "target_windows")) {
620 target_str = "x86_64-windows";
621 } else if (mem.eql(u8, end_tag_name, "target_linux_x86_64")) {
622 target_str = "x86_64-linux";
623 } else if (mem.eql(u8, end_tag_name, "target_linux_riscv64")) {
624 target_str = "riscv64-linux";
625 } else if (mem.eql(u8, end_tag_name, "target_wasm")) {
626 target_str = "wasm32-freestanding";
627 } else if (mem.eql(u8, end_tag_name, "target_wasi")) {
628 target_str = "wasm32-wasi";
629 } else if (mem.eql(u8, end_tag_name, "link_libc")) {
630 link_libc = true;
631 } else if (mem.eql(u8, end_tag_name, "link_mode_dynamic")) {
632 link_mode = .dynamic;
633 } else if (mem.eql(u8, end_tag_name, "additonal_option")) {
634 _ = try eatToken(tokenizer, .separator);
635 const option = try eatToken(tokenizer, .tag_content);
636 try additional_options.append(tokenizer.buffer[option.start..option.end]);
637 } else if (mem.eql(u8, end_tag_name, "code_end")) {
638 _ = try eatToken(tokenizer, .bracket_close);
639 break content_tok;
640 } else {
641 return parseError(
642 tokenizer,
643 end_code_tag,
644 "invalid token inside code_begin: {s}",
645 .{end_tag_name},
646 );
647 }
648 _ = try eatToken(tokenizer, .bracket_close);
649 } else unreachable; // TODO issue #707
650 try nodes.append(Node{
651 .Code = Code{
652 .id = code_kind_id,
653 .name = name,
654 .source_token = source_token,
655 .just_check_syntax = just_check_syntax,
656 .mode = mode,
657 .link_objects = try link_objects.toOwnedSlice(),
658 .target_str = target_str,
659 .link_libc = link_libc,
660 .link_mode = link_mode,
661 .disable_cache = disable_cache,
662 .verbose_cimport = verbose_cimport,
663 .additional_options = try additional_options.toOwnedSlice(),
664 },508 },
665 });509 });
666 tokenizer.code_node_count += 1;
667 } else if (mem.eql(u8, tag_name, "syntax")) {510 } else if (mem.eql(u8, tag_name, "syntax")) {
668 _ = try eatToken(tokenizer, .bracket_close);511 _ = try eatToken(tokenizer, .bracket_close);
669 const content_tok = try eatToken(tokenizer, .content);512 const content_tok = try eatToken(tokenizer, .content);
...@@ -805,132 +648,6 @@ fn in(slice: []const u8, number: u8) bool {...@@ -805,132 +648,6 @@ fn in(slice: []const u8, number: u8) bool {
805 return false;648 return false;
806}649}
807650
808fn termColor(allocator: Allocator, input: []const u8) ![]u8 {
809 // The SRG sequences generates by the Zig compiler are in the format:
810 // ESC [ <foreground-color> ; <n> m
811 // or
812 // ESC [ <n> m
813 //
814 // where
815 // foreground-color is 31 (red), 32 (green), 36 (cyan)
816 // n is 0 (reset), 1 (bold), 2 (dim)
817 //
818 // Note that 37 (white) is currently not used by the compiler.
819 //
820 // See std.debug.TTY.Color.
821 const supported_sgr_colors = [_]u8{ 31, 32, 36 };
822 const supported_sgr_numbers = [_]u8{ 0, 1, 2 };
823
824 var buf = std.ArrayList(u8).init(allocator);
825 defer buf.deinit();
826
827 var out = buf.writer();
828 var sgr_param_start_index: usize = undefined;
829 var sgr_num: u8 = undefined;
830 var sgr_color: u8 = undefined;
831 var i: usize = 0;
832 var state: enum {
833 start,
834 escape,
835 lbracket,
836 number,
837 after_number,
838 arg,
839 arg_number,
840 expect_end,
841 } = .start;
842 var last_new_line: usize = 0;
843 var open_span_count: usize = 0;
844 while (i < input.len) : (i += 1) {
845 const c = input[i];
846 switch (state) {
847 .start => switch (c) {
848 '\x1b' => state = .escape,
849 '\n' => {
850 try out.writeByte(c);
851 last_new_line = buf.items.len;
852 },
853 else => try out.writeByte(c),
854 },
855 .escape => switch (c) {
856 '[' => state = .lbracket,
857 else => return error.UnsupportedEscape,
858 },
859 .lbracket => switch (c) {
860 '0'...'9' => {
861 sgr_param_start_index = i;
862 state = .number;
863 },
864 else => return error.UnsupportedEscape,
865 },
866 .number => switch (c) {
867 '0'...'9' => {},
868 else => {
869 sgr_num = try std.fmt.parseInt(u8, input[sgr_param_start_index..i], 10);
870 sgr_color = 0;
871 state = .after_number;
872 i -= 1;
873 },
874 },
875 .after_number => switch (c) {
876 ';' => state = .arg,
877 'D' => state = .start,
878 'K' => {
879 buf.items.len = last_new_line;
880 state = .start;
881 },
882 else => {
883 state = .expect_end;
884 i -= 1;
885 },
886 },
887 .arg => switch (c) {
888 '0'...'9' => {
889 sgr_param_start_index = i;
890 state = .arg_number;
891 },
892 else => return error.UnsupportedEscape,
893 },
894 .arg_number => switch (c) {
895 '0'...'9' => {},
896 else => {
897 // Keep the sequence consistent, foreground color first.
898 // 32;1m is equivalent to 1;32m, but the latter will
899 // generate an incorrect HTML class without notice.
900 sgr_color = sgr_num;
901 if (!in(&supported_sgr_colors, sgr_color)) return error.UnsupportedForegroundColor;
902
903 sgr_num = try std.fmt.parseInt(u8, input[sgr_param_start_index..i], 10);
904 if (!in(&supported_sgr_numbers, sgr_num)) return error.UnsupportedNumber;
905
906 state = .expect_end;
907 i -= 1;
908 },
909 },
910 .expect_end => switch (c) {
911 'm' => {
912 state = .start;
913 while (open_span_count != 0) : (open_span_count -= 1) {
914 try out.writeAll("</span>");
915 }
916 if (sgr_num == 0) {
917 if (sgr_color != 0) return error.UnsupportedColor;
918 continue;
919 }
920 if (sgr_color != 0) {
921 try out.print("<span class=\"sgr-{d}_{d}m\">", .{ sgr_color, sgr_num });
922 } else {
923 try out.print("<span class=\"sgr-{d}m\">", .{sgr_num});
924 }
925 open_span_count += 1;
926 },
927 else => return error.UnsupportedEscape,
928 },
929 }
930 }
931 return try buf.toOwnedSlice();
932}
933
934const builtin_types = [_][]const u8{651const builtin_types = [_][]const u8{
935 "f16", "f32", "f64", "f80", "f128",652 "f16", "f32", "f64", "f80", "f128",
936 "c_longdouble", "c_short", "c_ushort", "c_int", "c_uint",653 "c_longdouble", "c_short", "c_ushort", "c_int", "c_uint",
...@@ -1267,30 +984,14 @@ fn printShell(out: anytype, shell_content: []const u8, escape: bool) !void {...@@ -1267,30 +984,14 @@ fn printShell(out: anytype, shell_content: []const u8, escape: bool) !void {
1267 try out.writeAll("</samp></pre></figure>");984 try out.writeAll("</samp></pre></figure>");
1268}985}
1269986
1270// Override this to skip to later tests
1271const debug_start_line = 0;
1272
1273fn genHtml(987fn genHtml(
1274 allocator: Allocator,988 allocator: Allocator,
1275 tokenizer: *Tokenizer,989 tokenizer: *Tokenizer,
1276 toc: *Toc,990 toc: *Toc,
991 code_dir: std.fs.Dir,
1277 out: anytype,992 out: anytype,
1278 zig_exe: []const u8,
1279 opt_zig_lib_dir: ?[]const u8,
1280 do_code_tests: bool,
1281) !void {993) !void {
1282 var progress = Progress{ .dont_print_on_dumb = true };
1283 const root_node = progress.start("Generating docgen examples", toc.nodes.len);
1284 defer root_node.end();
1285
1286 var env_map = try process.getEnvMap(allocator);
1287 try env_map.put("YES_COLOR", "1");
1288
1289 const host = try std.zig.system.resolveTargetQuery(.{});
1290 const builtin_code = try getBuiltinCode(allocator, &env_map, zig_exe, opt_zig_lib_dir);
1291
1292 for (toc.nodes) |node| {994 for (toc.nodes) |node| {
1293 defer root_node.completeOne();
1294 switch (node) {995 switch (node) {
1295 .Content => |data| {996 .Content => |data| {
1296 try out.writeAll(data);997 try out.writeAll(data);
...@@ -1306,6 +1007,7 @@ fn genHtml(...@@ -1306,6 +1007,7 @@ fn genHtml(
1306 },1007 },
1307 .Builtin => |tok| {1008 .Builtin => |tok| {
1308 try out.writeAll("<figure><figcaption class=\"zig-cap\"><cite>@import(\"builtin\")</cite></figcaption><pre>");1009 try out.writeAll("<figure><figcaption class=\"zig-cap\"><cite>@import(\"builtin\")</cite></figcaption><pre>");
1010 const builtin_code = @embedFile("builtin"); // 😎
1309 try tokenizeAndPrintRaw(allocator, tokenizer, out, tok, builtin_code);1011 try tokenizeAndPrintRaw(allocator, tokenizer, out, tok, builtin_code);
1310 try out.writeAll("</pre></figure>");1012 try out.writeAll("</pre></figure>");
1311 },1013 },
...@@ -1337,935 +1039,20 @@ fn genHtml(...@@ -1337,935 +1039,20 @@ fn genHtml(
1337 try printSourceBlock(allocator, tokenizer, out, syntax_block);1039 try printSourceBlock(allocator, tokenizer, out, syntax_block);
1338 },1040 },
1339 .Code => |code| {1041 .Code => |code| {
1340 const name_plus_ext = try std.fmt.allocPrint(allocator, "{s}.zig", .{code.name});1042 const out_basename = try std.fmt.allocPrint(allocator, "{s}.out", .{
1341 const syntax_block = SyntaxBlock{1043 fs.path.stem(code.name),
1342 .source_type = .zig,1044 });
1343 .name = name_plus_ext,1045 defer allocator.free(out_basename);
1344 .source_token = code.source_token,1046
1047 const contents = code_dir.readFileAlloc(allocator, out_basename, std.math.maxInt(u32)) catch |err| {
1048 return parseError(tokenizer, code.token, "unable to open '{s}': {s}", .{
1049 out_basename, @errorName(err),
1050 });
1345 };1051 };
1052 defer allocator.free(contents);
13461053
1347 try printSourceBlock(allocator, tokenizer, out, syntax_block);1054 try out.writeAll(contents);
1348
1349 if (!do_code_tests) {
1350 continue;
1351 }
1352
1353 if (debug_start_line > 0) {
1354 const loc = tokenizer.getTokenLocation(code.source_token);
1355 if (debug_start_line > loc.line) {
1356 continue;
1357 }
1358 }
1359
1360 const raw_source = tokenizer.buffer[code.source_token.start..code.source_token.end];
1361 const trimmed_raw_source = mem.trim(u8, raw_source, " \r\n");
1362 const tmp_source_file_name = try fs.path.join(
1363 allocator,
1364 &[_][]const u8{ tmp_dir_name, name_plus_ext },
1365 );
1366 try fs.cwd().writeFile(tmp_source_file_name, trimmed_raw_source);
1367
1368 var shell_buffer = std.ArrayList(u8).init(allocator);
1369 defer shell_buffer.deinit();
1370 var shell_out = shell_buffer.writer();
1371
1372 switch (code.id) {
1373 .exe => |expected_outcome| code_block: {
1374 var build_args = std.ArrayList([]const u8).init(allocator);
1375 defer build_args.deinit();
1376 try build_args.appendSlice(&[_][]const u8{
1377 zig_exe, "build-exe",
1378 "--name", code.name,
1379 "--color", "on",
1380 name_plus_ext,
1381 });
1382 if (opt_zig_lib_dir) |zig_lib_dir| {
1383 try build_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1384 }
1385
1386 try shell_out.print("$ zig build-exe {s} ", .{name_plus_ext});
1387
1388 switch (code.mode) {
1389 .Debug => {},
1390 else => {
1391 try build_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
1392 try shell_out.print("-O {s} ", .{@tagName(code.mode)});
1393 },
1394 }
1395 for (code.link_objects) |link_object| {
1396 const name_with_ext = try std.fmt.allocPrint(allocator, "{s}{s}", .{ link_object, obj_ext });
1397 try build_args.append(name_with_ext);
1398 try shell_out.print("{s} ", .{name_with_ext});
1399 }
1400 if (code.link_libc) {
1401 try build_args.append("-lc");
1402 try shell_out.print("-lc ", .{});
1403 }
1404
1405 if (code.target_str) |triple| {
1406 try build_args.appendSlice(&[_][]const u8{ "-target", triple });
1407 try shell_out.print("-target {s} ", .{triple});
1408 }
1409 if (code.verbose_cimport) {
1410 try build_args.append("--verbose-cimport");
1411 try shell_out.print("--verbose-cimport ", .{});
1412 }
1413 for (code.additional_options) |option| {
1414 try build_args.append(option);
1415 try shell_out.print("{s} ", .{option});
1416 }
1417
1418 try shell_out.print("\n", .{});
1419
1420 if (expected_outcome == .build_fail) {
1421 const result = try ChildProcess.run(.{
1422 .allocator = allocator,
1423 .argv = build_args.items,
1424 .cwd = tmp_dir_name,
1425 .env_map = &env_map,
1426 .max_output_bytes = max_doc_file_size,
1427 });
1428 switch (result.term) {
1429 .Exited => |exit_code| {
1430 if (exit_code == 0) {
1431 progress.log("", .{});
1432 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
1433 dumpArgs(build_args.items);
1434 return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
1435 }
1436 },
1437 else => {
1438 progress.log("", .{});
1439 print("{s}\nThe following command crashed:\n", .{result.stderr});
1440 dumpArgs(build_args.items);
1441 return parseError(tokenizer, code.source_token, "example compile crashed", .{});
1442 },
1443 }
1444 const escaped_stderr = try escapeHtml(allocator, result.stderr);
1445 const colored_stderr = try termColor(allocator, escaped_stderr);
1446 try shell_out.writeAll(colored_stderr);
1447 break :code_block;
1448 }
1449 const exec_result = run(allocator, &env_map, tmp_dir_name, build_args.items) catch
1450 return parseError(tokenizer, code.source_token, "example failed to compile", .{});
1451
1452 if (code.verbose_cimport) {
1453 const escaped_build_stderr = try escapeHtml(allocator, exec_result.stderr);
1454 try shell_out.writeAll(escaped_build_stderr);
1455 }
1456
1457 if (code.target_str) |triple| {
1458 if (mem.startsWith(u8, triple, "wasm32") or
1459 mem.startsWith(u8, triple, "riscv64-linux") or
1460 (mem.startsWith(u8, triple, "x86_64-linux") and
1461 builtin.os.tag != .linux or builtin.cpu.arch != .x86_64))
1462 {
1463 // skip execution
1464 break :code_block;
1465 }
1466 }
1467
1468 const target_query = try std.Target.Query.parse(.{
1469 .arch_os_abi = code.target_str orelse "native",
1470 });
1471 const target = try std.zig.system.resolveTargetQuery(target_query);
1472
1473 const path_to_exe = try std.fmt.allocPrint(allocator, "./{s}{s}", .{
1474 code.name, target.exeFileExt(),
1475 });
1476 const run_args = &[_][]const u8{path_to_exe};
1477
1478 var exited_with_signal = false;
1479
1480 const result = if (expected_outcome == .fail) blk: {
1481 const result = try ChildProcess.run(.{
1482 .allocator = allocator,
1483 .argv = run_args,
1484 .env_map = &env_map,
1485 .cwd = tmp_dir_name,
1486 .max_output_bytes = max_doc_file_size,
1487 });
1488 switch (result.term) {
1489 .Exited => |exit_code| {
1490 if (exit_code == 0) {
1491 progress.log("", .{});
1492 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
1493 dumpArgs(run_args);
1494 return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
1495 }
1496 },
1497 .Signal => exited_with_signal = true,
1498 else => {},
1499 }
1500 break :blk result;
1501 } else blk: {
1502 break :blk run(allocator, &env_map, tmp_dir_name, run_args) catch return parseError(tokenizer, code.source_token, "example crashed", .{});
1503 };
1504
1505 const escaped_stderr = try escapeHtml(allocator, result.stderr);
1506 const escaped_stdout = try escapeHtml(allocator, result.stdout);
1507
1508 const colored_stderr = try termColor(allocator, escaped_stderr);
1509 const colored_stdout = try termColor(allocator, escaped_stdout);
1510
1511 try shell_out.print("$ ./{s}\n{s}{s}", .{ code.name, colored_stdout, colored_stderr });
1512 if (exited_with_signal) {
1513 try shell_out.print("(process terminated by signal)", .{});
1514 }
1515 try shell_out.writeAll("\n");
1516 },
1517 .@"test" => {
1518 var test_args = std.ArrayList([]const u8).init(allocator);
1519 defer test_args.deinit();
1520
1521 try test_args.appendSlice(&[_][]const u8{
1522 zig_exe, "test",
1523 tmp_source_file_name,
1524 });
1525 if (opt_zig_lib_dir) |zig_lib_dir| {
1526 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1527 }
1528 try shell_out.print("$ zig test {s}.zig ", .{code.name});
1529
1530 switch (code.mode) {
1531 .Debug => {},
1532 else => {
1533 try test_args.appendSlice(&[_][]const u8{
1534 "-O", @tagName(code.mode),
1535 });
1536 try shell_out.print("-O {s} ", .{@tagName(code.mode)});
1537 },
1538 }
1539 if (code.link_libc) {
1540 try test_args.append("-lc");
1541 try shell_out.print("-lc ", .{});
1542 }
1543 if (code.target_str) |triple| {
1544 try test_args.appendSlice(&[_][]const u8{ "-target", triple });
1545 try shell_out.print("-target {s} ", .{triple});
1546
1547 const target_query = try std.Target.Query.parse(.{
1548 .arch_os_abi = triple,
1549 });
1550 const target = try std.zig.system.resolveTargetQuery(
1551 target_query,
1552 );
1553 switch (getExternalExecutor(host, &target, .{
1554 .link_libc = code.link_libc,
1555 })) {
1556 .native => {},
1557 else => {
1558 try test_args.appendSlice(&[_][]const u8{"--test-no-exec"});
1559 try shell_out.writeAll("--test-no-exec");
1560 },
1561 }
1562 }
1563 const result = run(allocator, &env_map, null, test_args.items) catch
1564 return parseError(tokenizer, code.source_token, "test failed", .{});
1565 const escaped_stderr = try escapeHtml(allocator, result.stderr);
1566 const escaped_stdout = try escapeHtml(allocator, result.stdout);
1567 try shell_out.print("\n{s}{s}\n", .{ escaped_stderr, escaped_stdout });
1568 },
1569 .test_error => |error_match| {
1570 var test_args = std.ArrayList([]const u8).init(allocator);
1571 defer test_args.deinit();
1572
1573 try test_args.appendSlice(&[_][]const u8{
1574 zig_exe, "test",
1575 "--color", "on",
1576 tmp_source_file_name,
1577 });
1578 if (opt_zig_lib_dir) |zig_lib_dir| {
1579 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1580 }
1581 try shell_out.print("$ zig test {s}.zig ", .{code.name});
1582
1583 switch (code.mode) {
1584 .Debug => {},
1585 else => {
1586 try test_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
1587 try shell_out.print("-O {s} ", .{@tagName(code.mode)});
1588 },
1589 }
1590 if (code.link_libc) {
1591 try test_args.append("-lc");
1592 try shell_out.print("-lc ", .{});
1593 }
1594 const result = try ChildProcess.run(.{
1595 .allocator = allocator,
1596 .argv = test_args.items,
1597 .env_map = &env_map,
1598 .max_output_bytes = max_doc_file_size,
1599 });
1600 switch (result.term) {
1601 .Exited => |exit_code| {
1602 if (exit_code == 0) {
1603 progress.log("", .{});
1604 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
1605 dumpArgs(test_args.items);
1606 return parseError(tokenizer, code.source_token, "example incorrectly compiled", .{});
1607 }
1608 },
1609 else => {
1610 progress.log("", .{});
1611 print("{s}\nThe following command crashed:\n", .{result.stderr});
1612 dumpArgs(test_args.items);
1613 return parseError(tokenizer, code.source_token, "example compile crashed", .{});
1614 },
1615 }
1616 if (mem.indexOf(u8, result.stderr, error_match) == null) {
1617 progress.log("", .{});
1618 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });
1619 return parseError(tokenizer, code.source_token, "example did not have expected compile error", .{});
1620 }
1621 const escaped_stderr = try escapeHtml(allocator, result.stderr);
1622 const colored_stderr = try termColor(allocator, escaped_stderr);
1623 try shell_out.print("\n{s}\n", .{colored_stderr});
1624 },
1625 .test_safety => |error_match| {
1626 var test_args = std.ArrayList([]const u8).init(allocator);
1627 defer test_args.deinit();
1628
1629 try test_args.appendSlice(&[_][]const u8{
1630 zig_exe, "test",
1631 tmp_source_file_name,
1632 });
1633 if (opt_zig_lib_dir) |zig_lib_dir| {
1634 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1635 }
1636 var mode_arg: []const u8 = "";
1637 switch (code.mode) {
1638 .Debug => {},
1639 .ReleaseSafe => {
1640 try test_args.append("-OReleaseSafe");
1641 mode_arg = "-OReleaseSafe";
1642 },
1643 .ReleaseFast => {
1644 try test_args.append("-OReleaseFast");
1645 mode_arg = "-OReleaseFast";
1646 },
1647 .ReleaseSmall => {
1648 try test_args.append("-OReleaseSmall");
1649 mode_arg = "-OReleaseSmall";
1650 },
1651 }
1652
1653 const result = try ChildProcess.run(.{
1654 .allocator = allocator,
1655 .argv = test_args.items,
1656 .env_map = &env_map,
1657 .max_output_bytes = max_doc_file_size,
1658 });
1659 switch (result.term) {
1660 .Exited => |exit_code| {
1661 if (exit_code == 0) {
1662 progress.log("", .{});
1663 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
1664 dumpArgs(test_args.items);
1665 return parseError(tokenizer, code.source_token, "example test incorrectly succeeded", .{});
1666 }
1667 },
1668 else => {
1669 progress.log("", .{});
1670 print("{s}\nThe following command crashed:\n", .{result.stderr});
1671 dumpArgs(test_args.items);
1672 return parseError(tokenizer, code.source_token, "example compile crashed", .{});
1673 },
1674 }
1675 if (mem.indexOf(u8, result.stderr, error_match) == null) {
1676 progress.log("", .{});
1677 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });
1678 return parseError(tokenizer, code.source_token, "example did not have expected runtime safety error message", .{});
1679 }
1680 const escaped_stderr = try escapeHtml(allocator, result.stderr);
1681 const colored_stderr = try termColor(allocator, escaped_stderr);
1682 try shell_out.print("$ zig test {s}.zig {s}\n{s}\n", .{
1683 code.name,
1684 mode_arg,
1685 colored_stderr,
1686 });
1687 },
1688 .obj => |maybe_error_match| {
1689 const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{s}{s}", .{ code.name, obj_ext });
1690 var build_args = std.ArrayList([]const u8).init(allocator);
1691 defer build_args.deinit();
1692
1693 try build_args.appendSlice(&[_][]const u8{
1694 zig_exe, "build-obj",
1695 "--color", "on",
1696 "--name", code.name,
1697 tmp_source_file_name,
1698 try std.fmt.allocPrint(allocator, "-femit-bin={s}{c}{s}", .{
1699 tmp_dir_name, fs.path.sep, name_plus_obj_ext,
1700 }),
1701 });
1702 if (opt_zig_lib_dir) |zig_lib_dir| {
1703 try build_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1704 }
1705
1706 try shell_out.print("$ zig build-obj {s}.zig ", .{code.name});
1707
1708 switch (code.mode) {
1709 .Debug => {},
1710 else => {
1711 try build_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
1712 try shell_out.print("-O {s} ", .{@tagName(code.mode)});
1713 },
1714 }
1715
1716 if (code.target_str) |triple| {
1717 try build_args.appendSlice(&[_][]const u8{ "-target", triple });
1718 try shell_out.print("-target {s} ", .{triple});
1719 }
1720 for (code.additional_options) |option| {
1721 try build_args.append(option);
1722 try shell_out.print("{s} ", .{option});
1723 }
1724
1725 if (maybe_error_match) |error_match| {
1726 const result = try ChildProcess.run(.{
1727 .allocator = allocator,
1728 .argv = build_args.items,
1729 .env_map = &env_map,
1730 .max_output_bytes = max_doc_file_size,
1731 });
1732 switch (result.term) {
1733 .Exited => |exit_code| {
1734 if (exit_code == 0) {
1735 progress.log("", .{});
1736 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
1737 dumpArgs(build_args.items);
1738 return parseError(tokenizer, code.source_token, "example build incorrectly succeeded", .{});
1739 }
1740 },
1741 else => {
1742 progress.log("", .{});
1743 print("{s}\nThe following command crashed:\n", .{result.stderr});
1744 dumpArgs(build_args.items);
1745 return parseError(tokenizer, code.source_token, "example compile crashed", .{});
1746 },
1747 }
1748 if (mem.indexOf(u8, result.stderr, error_match) == null) {
1749 progress.log("", .{});
1750 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });
1751 return parseError(tokenizer, code.source_token, "example did not have expected compile error message", .{});
1752 }
1753 const escaped_stderr = try escapeHtml(allocator, result.stderr);
1754 const colored_stderr = try termColor(allocator, escaped_stderr);
1755 try shell_out.print("\n{s} ", .{colored_stderr});
1756 } else {
1757 _ = run(allocator, &env_map, null, build_args.items) catch return parseError(tokenizer, code.source_token, "example failed to compile", .{});
1758 }
1759 try shell_out.writeAll("\n");
1760 },
1761 .lib => {
1762 const bin_basename = try std.zig.binNameAlloc(allocator, .{
1763 .root_name = code.name,
1764 .target = builtin.target,
1765 .output_mode = .Lib,
1766 });
1767
1768 var test_args = std.ArrayList([]const u8).init(allocator);
1769 defer test_args.deinit();
1770
1771 try test_args.appendSlice(&[_][]const u8{
1772 zig_exe, "build-lib",
1773 tmp_source_file_name,
1774 try std.fmt.allocPrint(allocator, "-femit-bin={s}{s}{s}", .{
1775 tmp_dir_name, fs.path.sep_str, bin_basename,
1776 }),
1777 });
1778 if (opt_zig_lib_dir) |zig_lib_dir| {
1779 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
1780 }
1781 try shell_out.print("$ zig build-lib {s}.zig ", .{code.name});
1782
1783 switch (code.mode) {
1784 .Debug => {},
1785 else => {
1786 try test_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
1787 try shell_out.print("-O {s} ", .{@tagName(code.mode)});
1788 },
1789 }
1790 if (code.target_str) |triple| {
1791 try test_args.appendSlice(&[_][]const u8{ "-target", triple });
1792 try shell_out.print("-target {s} ", .{triple});
1793 }
1794 if (code.link_mode) |link_mode| {
1795 switch (link_mode) {
1796 .static => {
1797 try test_args.append("-static");
1798 try shell_out.print("-static ", .{});
1799 },
1800 .dynamic => {
1801 try test_args.append("-dynamic");
1802 try shell_out.print("-dynamic ", .{});
1803 },
1804 }
1805 }
1806 for (code.additional_options) |option| {
1807 try test_args.append(option);
1808 try shell_out.print("{s} ", .{option});
1809 }
1810 const result = run(allocator, &env_map, null, test_args.items) catch return parseError(tokenizer, code.source_token, "test failed", .{});
1811 const escaped_stderr = try escapeHtml(allocator, result.stderr);
1812 const escaped_stdout = try escapeHtml(allocator, result.stdout);
1813 try shell_out.print("\n{s}{s}\n", .{ escaped_stderr, escaped_stdout });
1814 },
1815 }
1816
1817 if (!code.just_check_syntax) {
1818 try printShell(out, shell_buffer.items, false);
1819 }
1820 },1055 },
1821 }1056 }
1822 }1057 }
1823}1058}
1824
1825fn run(
1826 allocator: Allocator,
1827 env_map: *process.EnvMap,
1828 cwd: ?[]const u8,
1829 args: []const []const u8,
1830) !ChildProcess.RunResult {
1831 const result = try ChildProcess.run(.{
1832 .allocator = allocator,
1833 .argv = args,
1834 .env_map = env_map,
1835 .cwd = cwd,
1836 .max_output_bytes = max_doc_file_size,
1837 });
1838 switch (result.term) {
1839 .Exited => |exit_code| {
1840 if (exit_code != 0) {
1841 print("{s}\nThe following command exited with code {}:\n", .{ result.stderr, exit_code });
1842 dumpArgs(args);
1843 return error.ChildExitError;
1844 }
1845 },
1846 else => {
1847 print("{s}\nThe following command crashed:\n", .{result.stderr});
1848 dumpArgs(args);
1849 return error.ChildCrashed;
1850 },
1851 }
1852 return result;
1853}
1854
1855fn getBuiltinCode(
1856 allocator: Allocator,
1857 env_map: *process.EnvMap,
1858 zig_exe: []const u8,
1859 opt_zig_lib_dir: ?[]const u8,
1860) ![]const u8 {
1861 if (opt_zig_lib_dir) |zig_lib_dir| {
1862 const result = try run(allocator, env_map, null, &.{
1863 zig_exe, "build-obj", "--show-builtin", "--zig-lib-dir", zig_lib_dir,
1864 });
1865 return result.stdout;
1866 } else {
1867 const result = try run(allocator, env_map, null, &.{
1868 zig_exe, "build-obj", "--show-builtin",
1869 });
1870 return result.stdout;
1871 }
1872}
1873
1874fn dumpArgs(args: []const []const u8) void {
1875 for (args) |arg|
1876 print("{s} ", .{arg})
1877 else
1878 print("\n", .{});
1879}
1880
1881test "term supported colors" {
1882 const test_allocator = testing.allocator;
1883
1884 {
1885 const input = "A\x1b[31;1mred\x1b[0mB";
1886 const expect = "A<span class=\"sgr-31_1m\">red</span>B";
1887
1888 const result = try termColor(test_allocator, input);
1889 defer test_allocator.free(result);
1890 try testing.expectEqualSlices(u8, expect, result);
1891 }
1892
1893 {
1894 const input = "A\x1b[32;1mgreen\x1b[0mB";
1895 const expect = "A<span class=\"sgr-32_1m\">green</span>B";
1896
1897 const result = try termColor(test_allocator, input);
1898 defer test_allocator.free(result);
1899 try testing.expectEqualSlices(u8, expect, result);
1900 }
1901
1902 {
1903 const input = "A\x1b[36;1mcyan\x1b[0mB";
1904 const expect = "A<span class=\"sgr-36_1m\">cyan</span>B";
1905
1906 const result = try termColor(test_allocator, input);
1907 defer test_allocator.free(result);
1908 try testing.expectEqualSlices(u8, expect, result);
1909 }
1910
1911 {
1912 const input = "A\x1b[1mbold\x1b[0mB";
1913 const expect = "A<span class=\"sgr-1m\">bold</span>B";
1914
1915 const result = try termColor(test_allocator, input);
1916 defer test_allocator.free(result);
1917 try testing.expectEqualSlices(u8, expect, result);
1918 }
1919
1920 {
1921 const input = "A\x1b[2mdim\x1b[0mB";
1922 const expect = "A<span class=\"sgr-2m\">dim</span>B";
1923
1924 const result = try termColor(test_allocator, input);
1925 defer test_allocator.free(result);
1926 try testing.expectEqualSlices(u8, expect, result);
1927 }
1928}
1929
1930test "term output from zig" {
1931 // Use data generated by https://github.com/perillo/zig-tty-test-data,
1932 // with zig version 0.11.0-dev.1898+36d47dd19.
1933 const test_allocator = testing.allocator;
1934
1935 {
1936 // 1.1-with-build-progress.out
1937 const input = "Semantic Analysis [1324] \x1b[25D\x1b[0KLLVM Emit Object... \x1b[20D\x1b[0KLLVM Emit Object... \x1b[20D\x1b[0KLLD Link... \x1b[12D\x1b[0K";
1938 const expect = "";
1939
1940 const result = try termColor(test_allocator, input);
1941 defer test_allocator.free(result);
1942 try testing.expectEqualSlices(u8, expect, result);
1943 }
1944
1945 {
1946 // 2.1-with-reference-traces.out
1947 const input = "\x1b[1msrc/2.1-with-reference-traces.zig:3:7: \x1b[31;1merror: \x1b[0m\x1b[1mcannot assign to constant\n\x1b[0m x += 1;\n \x1b[32;1m~~^~~~\n\x1b[0m\x1b[0m\x1b[2mreferenced by:\n main: src/2.1-with-reference-traces.zig:7:5\n callMain: /usr/local/lib/zig/lib/std/start.zig:607:17\n remaining reference traces hidden; use '-freference-trace' to see all reference traces\n\n\x1b[0m";
1948 const expect =
1949 \\<span class="sgr-1m">src/2.1-with-reference-traces.zig:3:7: </span><span class="sgr-31_1m">error: </span><span class="sgr-1m">cannot assign to constant
1950 \\</span> x += 1;
1951 \\ <span class="sgr-32_1m">~~^~~~
1952 \\</span><span class="sgr-2m">referenced by:
1953 \\ main: src/2.1-with-reference-traces.zig:7:5
1954 \\ callMain: /usr/local/lib/zig/lib/std/start.zig:607:17
1955 \\ remaining reference traces hidden; use '-freference-trace' to see all reference traces
1956 \\
1957 \\</span>
1958 ;
1959
1960 const result = try termColor(test_allocator, input);
1961 defer test_allocator.free(result);
1962 try testing.expectEqualSlices(u8, expect, result);
1963 }
1964
1965 {
1966 // 2.2-without-reference-traces.out
1967 const input = "\x1b[1m/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:128:29: \x1b[31;1merror: \x1b[0m\x1b[1minvalid type given to fixedBufferStream\n\x1b[0m else => @compileError(\"invalid type given to fixedBufferStream\"),\n \x1b[32;1m^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\x1b[0m\x1b[1m/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:116:66: \x1b[36;1mnote: \x1b[0m\x1b[1mcalled from here\n\x1b[0mpub fn fixedBufferStream(buffer: anytype) FixedBufferStream(Slice(@TypeOf(buffer))) {\n; \x1b[32;1m~~~~~^~~~~~~~~~~~~~~~~\n\x1b[0m";
1968 const expect =
1969 \\<span class="sgr-1m">/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:128:29: </span><span class="sgr-31_1m">error: </span><span class="sgr-1m">invalid type given to fixedBufferStream
1970 \\</span> else => @compileError("invalid type given to fixedBufferStream"),
1971 \\ <span class="sgr-32_1m">^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1972 \\</span><span class="sgr-1m">/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:116:66: </span><span class="sgr-36_1m">note: </span><span class="sgr-1m">called from here
1973 \\</span>pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(Slice(@TypeOf(buffer))) {
1974 \\; <span class="sgr-32_1m">~~~~~^~~~~~~~~~~~~~~~~
1975 \\</span>
1976 ;
1977
1978 const result = try termColor(test_allocator, input);
1979 defer test_allocator.free(result);
1980 try testing.expectEqualSlices(u8, expect, result);
1981 }
1982
1983 {
1984 // 2.3-with-notes.out
1985 const input = "\x1b[1msrc/2.3-with-notes.zig:6:9: \x1b[31;1merror: \x1b[0m\x1b[1mexpected type '*2.3-with-notes.Derp', found '*2.3-with-notes.Wat'\n\x1b[0m bar(w);\n \x1b[32;1m^\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:6:9: \x1b[36;1mnote: \x1b[0m\x1b[1mpointer type child '2.3-with-notes.Wat' cannot cast into pointer type child '2.3-with-notes.Derp'\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:2:13: \x1b[36;1mnote: \x1b[0m\x1b[1mopaque declared here\n\x1b[0mconst Wat = opaque {};\n \x1b[32;1m^~~~~~~~~\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:1:14: \x1b[36;1mnote: \x1b[0m\x1b[1mopaque declared here\n\x1b[0mconst Derp = opaque {};\n \x1b[32;1m^~~~~~~~~\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:4:18: \x1b[36;1mnote: \x1b[0m\x1b[1mparameter type declared here\n\x1b[0mextern fn bar(d: *Derp) void;\n \x1b[32;1m^~~~~\n\x1b[0m\x1b[0m\x1b[2mreferenced by:\n main: src/2.3-with-notes.zig:10:5\n callMain: /usr/local/lib/zig/lib/std/start.zig:607:17\n remaining reference traces hidden; use '-freference-trace' to see all reference traces\n\n\x1b[0m";
1986 const expect =
1987 \\<span class="sgr-1m">src/2.3-with-notes.zig:6:9: </span><span class="sgr-31_1m">error: </span><span class="sgr-1m">expected type '*2.3-with-notes.Derp', found '*2.3-with-notes.Wat'
1988 \\</span> bar(w);
1989 \\ <span class="sgr-32_1m">^
1990 \\</span><span class="sgr-1m">src/2.3-with-notes.zig:6:9: </span><span class="sgr-36_1m">note: </span><span class="sgr-1m">pointer type child '2.3-with-notes.Wat' cannot cast into pointer type child '2.3-with-notes.Derp'
1991 \\</span><span class="sgr-1m">src/2.3-with-notes.zig:2:13: </span><span class="sgr-36_1m">note: </span><span class="sgr-1m">opaque declared here
1992 \\</span>const Wat = opaque {};
1993 \\ <span class="sgr-32_1m">^~~~~~~~~
1994 \\</span><span class="sgr-1m">src/2.3-with-notes.zig:1:14: </span><span class="sgr-36_1m">note: </span><span class="sgr-1m">opaque declared here
1995 \\</span>const Derp = opaque {};
1996 \\ <span class="sgr-32_1m">^~~~~~~~~
1997 \\</span><span class="sgr-1m">src/2.3-with-notes.zig:4:18: </span><span class="sgr-36_1m">note: </span><span class="sgr-1m">parameter type declared here
1998 \\</span>extern fn bar(d: *Derp) void;
1999 \\ <span class="sgr-32_1m">^~~~~
2000 \\</span><span class="sgr-2m">referenced by:
2001 \\ main: src/2.3-with-notes.zig:10:5
2002 \\ callMain: /usr/local/lib/zig/lib/std/start.zig:607:17
2003 \\ remaining reference traces hidden; use '-freference-trace' to see all reference traces
2004 \\
2005 \\</span>
2006 ;
2007
2008 const result = try termColor(test_allocator, input);
2009 defer test_allocator.free(result);
2010 try testing.expectEqualSlices(u8, expect, result);
2011 }
2012
2013 {
2014 // 3.1-with-error-return-traces.out
2015
2016 const input = "error: Error\n\x1b[1m/home/zig/src/3.1-with-error-return-traces.zig:5:5\x1b[0m: \x1b[2m0x20b008 in callee (3.1-with-error-return-traces)\x1b[0m\n return error.Error;\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.1-with-error-return-traces.zig:9:5\x1b[0m: \x1b[2m0x20b113 in caller (3.1-with-error-return-traces)\x1b[0m\n try callee();\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.1-with-error-return-traces.zig:13:5\x1b[0m: \x1b[2m0x20b153 in main (3.1-with-error-return-traces)\x1b[0m\n try caller();\n \x1b[32;1m^\x1b[0m\n";
2017 const expect =
2018 \\error: Error
2019 \\<span class="sgr-1m">/home/zig/src/3.1-with-error-return-traces.zig:5:5</span>: <span class="sgr-2m">0x20b008 in callee (3.1-with-error-return-traces)</span>
2020 \\ return error.Error;
2021 \\ <span class="sgr-32_1m">^</span>
2022 \\<span class="sgr-1m">/home/zig/src/3.1-with-error-return-traces.zig:9:5</span>: <span class="sgr-2m">0x20b113 in caller (3.1-with-error-return-traces)</span>
2023 \\ try callee();
2024 \\ <span class="sgr-32_1m">^</span>
2025 \\<span class="sgr-1m">/home/zig/src/3.1-with-error-return-traces.zig:13:5</span>: <span class="sgr-2m">0x20b153 in main (3.1-with-error-return-traces)</span>
2026 \\ try caller();
2027 \\ <span class="sgr-32_1m">^</span>
2028 \\
2029 ;
2030
2031 const result = try termColor(test_allocator, input);
2032 defer test_allocator.free(result);
2033 try testing.expectEqualSlices(u8, expect, result);
2034 }
2035
2036 {
2037 // 3.2-with-stack-trace.out
2038 const input = "\x1b[1m/usr/local/lib/zig/lib/std/debug.zig:561:19\x1b[0m: \x1b[2m0x22a107 in writeCurrentStackTrace__anon_5898 (3.2-with-stack-trace)\x1b[0m\n while (it.next()) |return_address| {\n \x1b[32;1m^\x1b[0m\n\x1b[1m/usr/local/lib/zig/lib/std/debug.zig:157:80\x1b[0m: \x1b[2m0x20bb23 in dumpCurrentStackTrace (3.2-with-stack-trace)\x1b[0m\n writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(io.getStdErr()), start_addr) catch |err| {\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.2-with-stack-trace.zig:5:36\x1b[0m: \x1b[2m0x20d3b2 in foo (3.2-with-stack-trace)\x1b[0m\n std.debug.dumpCurrentStackTrace(null);\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.2-with-stack-trace.zig:9:8\x1b[0m: \x1b[2m0x20b458 in main (3.2-with-stack-trace)\x1b[0m\n foo();\n \x1b[32;1m^\x1b[0m\n\x1b[1m/usr/local/lib/zig/lib/std/start.zig:607:22\x1b[0m: \x1b[2m0x20a965 in posixCallMainAndExit (3.2-with-stack-trace)\x1b[0m\n root.main();\n \x1b[32;1m^\x1b[0m\n\x1b[1m/usr/local/lib/zig/lib/std/start.zig:376:5\x1b[0m: \x1b[2m0x20a411 in _start (3.2-with-stack-trace)\x1b[0m\n @call(.never_inline, posixCallMainAndExit, .{});\n \x1b[32;1m^\x1b[0m\n";
2039 const expect =
2040 \\<span class="sgr-1m">/usr/local/lib/zig/lib/std/debug.zig:561:19</span>: <span class="sgr-2m">0x22a107 in writeCurrentStackTrace__anon_5898 (3.2-with-stack-trace)</span>
2041 \\ while (it.next()) |return_address| {
2042 \\ <span class="sgr-32_1m">^</span>
2043 \\<span class="sgr-1m">/usr/local/lib/zig/lib/std/debug.zig:157:80</span>: <span class="sgr-2m">0x20bb23 in dumpCurrentStackTrace (3.2-with-stack-trace)</span>
2044 \\ writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(io.getStdErr()), start_addr) catch |err| {
2045 \\ <span class="sgr-32_1m">^</span>
2046 \\<span class="sgr-1m">/home/zig/src/3.2-with-stack-trace.zig:5:36</span>: <span class="sgr-2m">0x20d3b2 in foo (3.2-with-stack-trace)</span>
2047 \\ std.debug.dumpCurrentStackTrace(null);
2048 \\ <span class="sgr-32_1m">^</span>
2049 \\<span class="sgr-1m">/home/zig/src/3.2-with-stack-trace.zig:9:8</span>: <span class="sgr-2m">0x20b458 in main (3.2-with-stack-trace)</span>
2050 \\ foo();
2051 \\ <span class="sgr-32_1m">^</span>
2052 \\<span class="sgr-1m">/usr/local/lib/zig/lib/std/start.zig:607:22</span>: <span class="sgr-2m">0x20a965 in posixCallMainAndExit (3.2-with-stack-trace)</span>
2053 \\ root.main();
2054 \\ <span class="sgr-32_1m">^</span>
2055 \\<span class="sgr-1m">/usr/local/lib/zig/lib/std/start.zig:376:5</span>: <span class="sgr-2m">0x20a411 in _start (3.2-with-stack-trace)</span>
2056 \\ @call(.never_inline, posixCallMainAndExit, .{});
2057 \\ <span class="sgr-32_1m">^</span>
2058 \\
2059 ;
2060
2061 const result = try termColor(test_allocator, input);
2062 defer test_allocator.free(result);
2063 try testing.expectEqualSlices(u8, expect, result);
2064 }
2065}
2066
2067test "printShell" {
2068 const test_allocator = std.testing.allocator;
2069
2070 {
2071 const shell_out =
2072 \\$ zig build test.zig
2073 ;
2074 const expected =
2075 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig</kbd>
2076 \\</samp></pre></figure>
2077 ;
2078
2079 var buffer = std.ArrayList(u8).init(test_allocator);
2080 defer buffer.deinit();
2081
2082 try printShell(buffer.writer(), shell_out, false);
2083 try testing.expectEqualSlices(u8, expected, buffer.items);
2084 }
2085 {
2086 const shell_out =
2087 \\$ zig build test.zig
2088 \\build output
2089 ;
2090 const expected =
2091 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig</kbd>
2092 \\build output
2093 \\</samp></pre></figure>
2094 ;
2095
2096 var buffer = std.ArrayList(u8).init(test_allocator);
2097 defer buffer.deinit();
2098
2099 try printShell(buffer.writer(), shell_out, false);
2100 try testing.expectEqualSlices(u8, expected, buffer.items);
2101 }
2102 {
2103 const shell_out = "$ zig build test.zig\r\nbuild output\r\n";
2104 const expected =
2105 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig</kbd>
2106 \\build output
2107 \\</samp></pre></figure>
2108 ;
2109
2110 var buffer = std.ArrayList(u8).init(test_allocator);
2111 defer buffer.deinit();
2112
2113 try printShell(buffer.writer(), shell_out, false);
2114 try testing.expectEqualSlices(u8, expected, buffer.items);
2115 }
2116 {
2117 const shell_out =
2118 \\$ zig build test.zig
2119 \\build output
2120 \\$ ./test
2121 ;
2122 const expected =
2123 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig</kbd>
2124 \\build output
2125 \\$ <kbd>./test</kbd>
2126 \\</samp></pre></figure>
2127 ;
2128
2129 var buffer = std.ArrayList(u8).init(test_allocator);
2130 defer buffer.deinit();
2131
2132 try printShell(buffer.writer(), shell_out, false);
2133 try testing.expectEqualSlices(u8, expected, buffer.items);
2134 }
2135 {
2136 const shell_out =
2137 \\$ zig build test.zig
2138 \\
2139 \\$ ./test
2140 \\output
2141 ;
2142 const expected =
2143 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig</kbd>
2144 \\
2145 \\$ <kbd>./test</kbd>
2146 \\output
2147 \\</samp></pre></figure>
2148 ;
2149
2150 var buffer = std.ArrayList(u8).init(test_allocator);
2151 defer buffer.deinit();
2152
2153 try printShell(buffer.writer(), shell_out, false);
2154 try testing.expectEqualSlices(u8, expected, buffer.items);
2155 }
2156 {
2157 const shell_out =
2158 \\$ zig build test.zig
2159 \\$ ./test
2160 \\output
2161 ;
2162 const expected =
2163 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig</kbd>
2164 \\$ <kbd>./test</kbd>
2165 \\output
2166 \\</samp></pre></figure>
2167 ;
2168
2169 var buffer = std.ArrayList(u8).init(test_allocator);
2170 defer buffer.deinit();
2171
2172 try printShell(buffer.writer(), shell_out, false);
2173 try testing.expectEqualSlices(u8, expected, buffer.items);
2174 }
2175 {
2176 const shell_out =
2177 \\$ zig build test.zig \
2178 \\ --build-option
2179 \\build output
2180 \\$ ./test
2181 \\output
2182 ;
2183 const expected =
2184 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig \
2185 \\ --build-option</kbd>
2186 \\build output
2187 \\$ <kbd>./test</kbd>
2188 \\output
2189 \\</samp></pre></figure>
2190 ;
2191
2192 var buffer = std.ArrayList(u8).init(test_allocator);
2193 defer buffer.deinit();
2194
2195 try printShell(buffer.writer(), shell_out, false);
2196 try testing.expectEqualSlices(u8, expected, buffer.items);
2197 }
2198 {
2199 // intentional space after "--build-option1 \"
2200 const shell_out =
2201 \\$ zig build test.zig \
2202 \\ --build-option1 \
2203 \\ --build-option2
2204 \\$ ./test
2205 ;
2206 const expected =
2207 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig \
2208 \\ --build-option1 \
2209 \\ --build-option2</kbd>
2210 \\$ <kbd>./test</kbd>
2211 \\</samp></pre></figure>
2212 ;
2213
2214 var buffer = std.ArrayList(u8).init(test_allocator);
2215 defer buffer.deinit();
2216
2217 try printShell(buffer.writer(), shell_out, false);
2218 try testing.expectEqualSlices(u8, expected, buffer.items);
2219 }
2220 {
2221 const shell_out =
2222 \\$ zig build test.zig \
2223 \\$ ./test
2224 ;
2225 const expected =
2226 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig \
2227 \\$ ./test</kbd>
2228 \\</samp></pre></figure>
2229 ;
2230
2231 var buffer = std.ArrayList(u8).init(test_allocator);
2232 defer buffer.deinit();
2233
2234 try printShell(buffer.writer(), shell_out, false);
2235 try testing.expectEqualSlices(u8, expected, buffer.items);
2236 }
2237 {
2238 const shell_out =
2239 \\$ zig build test.zig
2240 \\$ ./test
2241 \\$1
2242 ;
2243 const expected =
2244 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig</kbd>
2245 \\$ <kbd>./test</kbd>
2246 \\$1
2247 \\</samp></pre></figure>
2248 ;
2249
2250 var buffer = std.ArrayList(u8).init(test_allocator);
2251 defer buffer.deinit();
2252
2253 try printShell(buffer.writer(), shell_out, false);
2254 try testing.expectEqualSlices(u8, expected, buffer.items);
2255 }
2256 {
2257 const shell_out =
2258 \\$zig build test.zig
2259 ;
2260 const expected =
2261 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$zig build test.zig
2262 \\</samp></pre></figure>
2263 ;
2264
2265 var buffer = std.ArrayList(u8).init(test_allocator);
2266 defer buffer.deinit();
2267
2268 try printShell(buffer.writer(), shell_out, false);
2269 try testing.expectEqualSlices(u8, expected, buffer.items);
2270 }
2271}
tools/doctest.zig created+1543
...@@ -0,0 +1,1543 @@
1const builtin = @import("builtin");
2const std = @import("std");
3const fatal = std.zig.fatal;
4const mem = std.mem;
5const fs = std.fs;
6const process = std.process;
7const Allocator = std.mem.Allocator;
8const testing = std.testing;
9const getExternalExecutor = std.zig.system.getExternalExecutor;
10
11const max_doc_file_size = 10 * 1024 * 1024;
12
13const usage =
14 \\Usage: doctest [options] -i input -o output
15 \\
16 \\ Compiles and possibly runs a code example, capturing output and rendering
17 \\ it to HTML documentation.
18 \\
19 \\Options:
20 \\ -h, --help Print this help and exit
21 \\ -i input Source code file path
22 \\ -o output Where to write output HTML docs to
23 \\ --zig zig Path to the zig compiler
24 \\ --zig-lib-dir dir Override the zig compiler library path
25 \\ --cache-root dir Path to local zig-cache/
26 \\
27;
28
29pub fn main() !void {
30 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
31 defer arena_instance.deinit();
32
33 const arena = arena_instance.allocator();
34
35 var args_it = try process.argsWithAllocator(arena);
36 if (!args_it.skip()) fatal("missing argv[0]", .{});
37
38 var opt_input: ?[]const u8 = null;
39 var opt_output: ?[]const u8 = null;
40 var opt_zig: ?[]const u8 = null;
41 var opt_zig_lib_dir: ?[]const u8 = null;
42 var opt_cache_root: ?[]const u8 = null;
43
44 while (args_it.next()) |arg| {
45 if (mem.startsWith(u8, arg, "-")) {
46 if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
47 try std.io.getStdOut().writeAll(usage);
48 process.exit(0);
49 } else if (mem.eql(u8, arg, "-i")) {
50 opt_input = args_it.next() orelse fatal("expected parameter after -i", .{});
51 } else if (mem.eql(u8, arg, "-o")) {
52 opt_output = args_it.next() orelse fatal("expected parameter after -o", .{});
53 } else if (mem.eql(u8, arg, "--zig")) {
54 opt_zig = args_it.next() orelse fatal("expected parameter after --zig", .{});
55 } else if (mem.eql(u8, arg, "--zig-lib-dir")) {
56 opt_zig_lib_dir = args_it.next() orelse fatal("expected parameter after --zig-lib-dir", .{});
57 } else if (mem.eql(u8, arg, "--cache-root")) {
58 opt_cache_root = args_it.next() orelse fatal("expected parameter after --cache-root", .{});
59 } else {
60 fatal("unrecognized option: '{s}'", .{arg});
61 }
62 } else {
63 fatal("unexpected positional argument: '{s}'", .{arg});
64 }
65 }
66
67 const input_path = opt_input orelse fatal("missing input file (-i)", .{});
68 const output_path = opt_output orelse fatal("missing output file (-o)", .{});
69 const zig_path = opt_zig orelse fatal("missing zig compiler path (--zig)", .{});
70 const cache_root = opt_cache_root orelse fatal("missing cache root path (--cache-root)", .{});
71
72 const source_bytes = try fs.cwd().readFileAlloc(arena, input_path, std.math.maxInt(u32));
73 const code = try parseManifest(arena, source_bytes);
74 const source = stripManifest(source_bytes);
75
76 const tmp_dir_path = try std.fmt.allocPrint(arena, "{s}/tmp/{x}", .{
77 cache_root, std.crypto.random.int(u64),
78 });
79 fs.cwd().makePath(tmp_dir_path) catch |err|
80 fatal("unable to create tmp dir '{s}': {s}", .{ tmp_dir_path, @errorName(err) });
81 defer fs.cwd().deleteTree(tmp_dir_path) catch |err| std.log.err("unable to delete '{s}': {s}", .{
82 tmp_dir_path, @errorName(err),
83 });
84
85 var out_file = try fs.cwd().createFile(output_path, .{});
86 defer out_file.close();
87
88 var bw = std.io.bufferedWriter(out_file.writer());
89 const out = bw.writer();
90
91 try printSourceBlock(arena, out, source, fs.path.basename(input_path));
92 try printOutput(arena, out, code, input_path, zig_path, opt_zig_lib_dir, tmp_dir_path);
93
94 try bw.flush();
95}
96
97fn printOutput(
98 arena: Allocator,
99 out: anytype,
100 code: Code,
101 input_path: []const u8,
102 zig_exe: []const u8,
103 opt_zig_lib_dir: ?[]const u8,
104 tmp_dir_path: []const u8,
105) !void {
106 var env_map = try process.getEnvMap(arena);
107 try env_map.put("YES_COLOR", "1");
108
109 const host = try std.zig.system.resolveTargetQuery(.{});
110 const obj_ext = builtin.object_format.fileExt(builtin.cpu.arch);
111 const print = std.debug.print;
112
113 var shell_buffer = std.ArrayList(u8).init(arena);
114 defer shell_buffer.deinit();
115 var shell_out = shell_buffer.writer();
116
117 const code_name = std.fs.path.stem(input_path);
118
119 switch (code.id) {
120 .exe => |expected_outcome| code_block: {
121 var build_args = std.ArrayList([]const u8).init(arena);
122 defer build_args.deinit();
123 try build_args.appendSlice(&[_][]const u8{
124 zig_exe, "build-exe",
125 "--name", code_name,
126 "--color", "on",
127 input_path,
128 });
129 if (opt_zig_lib_dir) |zig_lib_dir| {
130 try build_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
131 }
132
133 try shell_out.print("$ zig build-exe {s}.zig ", .{code_name});
134
135 switch (code.mode) {
136 .Debug => {},
137 else => {
138 try build_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
139 try shell_out.print("-O {s} ", .{@tagName(code.mode)});
140 },
141 }
142 for (code.link_objects) |link_object| {
143 const name_with_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ link_object, obj_ext });
144 try build_args.append(name_with_ext);
145 try shell_out.print("{s} ", .{name_with_ext});
146 }
147 if (code.link_libc) {
148 try build_args.append("-lc");
149 try shell_out.print("-lc ", .{});
150 }
151
152 if (code.target_str) |triple| {
153 try build_args.appendSlice(&[_][]const u8{ "-target", triple });
154 try shell_out.print("-target {s} ", .{triple});
155 }
156 if (code.verbose_cimport) {
157 try build_args.append("--verbose-cimport");
158 try shell_out.print("--verbose-cimport ", .{});
159 }
160 for (code.additional_options) |option| {
161 try build_args.append(option);
162 try shell_out.print("{s} ", .{option});
163 }
164
165 try shell_out.print("\n", .{});
166
167 if (expected_outcome == .build_fail) {
168 const result = try process.Child.run(.{
169 .allocator = arena,
170 .argv = build_args.items,
171 .cwd = tmp_dir_path,
172 .env_map = &env_map,
173 .max_output_bytes = max_doc_file_size,
174 });
175 switch (result.term) {
176 .Exited => |exit_code| {
177 if (exit_code == 0) {
178 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
179 dumpArgs(build_args.items);
180 fatal("example incorrectly compiled", .{});
181 }
182 },
183 else => {
184 print("{s}\nThe following command crashed:\n", .{result.stderr});
185 dumpArgs(build_args.items);
186 fatal("example compile crashed", .{});
187 },
188 }
189 const escaped_stderr = try escapeHtml(arena, result.stderr);
190 const colored_stderr = try termColor(arena, escaped_stderr);
191 try shell_out.writeAll(colored_stderr);
192 break :code_block;
193 }
194 const exec_result = run(arena, &env_map, tmp_dir_path, build_args.items) catch
195 fatal("example failed to compile", .{});
196
197 if (code.verbose_cimport) {
198 const escaped_build_stderr = try escapeHtml(arena, exec_result.stderr);
199 try shell_out.writeAll(escaped_build_stderr);
200 }
201
202 if (code.target_str) |triple| {
203 if (mem.startsWith(u8, triple, "wasm32") or
204 mem.startsWith(u8, triple, "riscv64-linux") or
205 (mem.startsWith(u8, triple, "x86_64-linux") and
206 builtin.os.tag != .linux or builtin.cpu.arch != .x86_64))
207 {
208 // skip execution
209 break :code_block;
210 }
211 }
212
213 const target_query = try std.Target.Query.parse(.{
214 .arch_os_abi = code.target_str orelse "native",
215 });
216 const target = try std.zig.system.resolveTargetQuery(target_query);
217
218 const path_to_exe = try std.fmt.allocPrint(arena, "./{s}{s}", .{
219 code_name, target.exeFileExt(),
220 });
221 const run_args = &[_][]const u8{path_to_exe};
222
223 var exited_with_signal = false;
224
225 const result = if (expected_outcome == .fail) blk: {
226 const result = try process.Child.run(.{
227 .allocator = arena,
228 .argv = run_args,
229 .env_map = &env_map,
230 .cwd = tmp_dir_path,
231 .max_output_bytes = max_doc_file_size,
232 });
233 switch (result.term) {
234 .Exited => |exit_code| {
235 if (exit_code == 0) {
236 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
237 dumpArgs(run_args);
238 fatal("example incorrectly compiled", .{});
239 }
240 },
241 .Signal => exited_with_signal = true,
242 else => {},
243 }
244 break :blk result;
245 } else blk: {
246 break :blk run(arena, &env_map, tmp_dir_path, run_args) catch
247 fatal("example crashed", .{});
248 };
249
250 const escaped_stderr = try escapeHtml(arena, result.stderr);
251 const escaped_stdout = try escapeHtml(arena, result.stdout);
252
253 const colored_stderr = try termColor(arena, escaped_stderr);
254 const colored_stdout = try termColor(arena, escaped_stdout);
255
256 try shell_out.print("$ ./{s}\n{s}{s}", .{ code_name, colored_stdout, colored_stderr });
257 if (exited_with_signal) {
258 try shell_out.print("(process terminated by signal)", .{});
259 }
260 try shell_out.writeAll("\n");
261 },
262 .@"test" => {
263 var test_args = std.ArrayList([]const u8).init(arena);
264 defer test_args.deinit();
265
266 try test_args.appendSlice(&[_][]const u8{
267 zig_exe, "test", input_path,
268 });
269 if (opt_zig_lib_dir) |zig_lib_dir| {
270 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
271 }
272 try shell_out.print("$ zig test {s}.zig ", .{code_name});
273
274 switch (code.mode) {
275 .Debug => {},
276 else => {
277 try test_args.appendSlice(&[_][]const u8{
278 "-O", @tagName(code.mode),
279 });
280 try shell_out.print("-O {s} ", .{@tagName(code.mode)});
281 },
282 }
283 if (code.link_libc) {
284 try test_args.append("-lc");
285 try shell_out.print("-lc ", .{});
286 }
287 if (code.target_str) |triple| {
288 try test_args.appendSlice(&[_][]const u8{ "-target", triple });
289 try shell_out.print("-target {s} ", .{triple});
290
291 const target_query = try std.Target.Query.parse(.{
292 .arch_os_abi = triple,
293 });
294 const target = try std.zig.system.resolveTargetQuery(
295 target_query,
296 );
297 switch (getExternalExecutor(host, &target, .{
298 .link_libc = code.link_libc,
299 })) {
300 .native => {},
301 else => {
302 try test_args.appendSlice(&[_][]const u8{"--test-no-exec"});
303 try shell_out.writeAll("--test-no-exec");
304 },
305 }
306 }
307 const result = run(arena, &env_map, null, test_args.items) catch
308 fatal("test failed", .{});
309 const escaped_stderr = try escapeHtml(arena, result.stderr);
310 const escaped_stdout = try escapeHtml(arena, result.stdout);
311 try shell_out.print("\n{s}{s}\n", .{ escaped_stderr, escaped_stdout });
312 },
313 .test_error => |error_match| {
314 var test_args = std.ArrayList([]const u8).init(arena);
315 defer test_args.deinit();
316
317 try test_args.appendSlice(&[_][]const u8{
318 zig_exe, "test",
319 "--color", "on",
320 input_path,
321 });
322 if (opt_zig_lib_dir) |zig_lib_dir| {
323 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
324 }
325 try shell_out.print("$ zig test {s}.zig ", .{code_name});
326
327 switch (code.mode) {
328 .Debug => {},
329 else => {
330 try test_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
331 try shell_out.print("-O {s} ", .{@tagName(code.mode)});
332 },
333 }
334 if (code.link_libc) {
335 try test_args.append("-lc");
336 try shell_out.print("-lc ", .{});
337 }
338 const result = try process.Child.run(.{
339 .allocator = arena,
340 .argv = test_args.items,
341 .env_map = &env_map,
342 .max_output_bytes = max_doc_file_size,
343 });
344 switch (result.term) {
345 .Exited => |exit_code| {
346 if (exit_code == 0) {
347 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
348 dumpArgs(test_args.items);
349 fatal("example incorrectly compiled", .{});
350 }
351 },
352 else => {
353 print("{s}\nThe following command crashed:\n", .{result.stderr});
354 dumpArgs(test_args.items);
355 fatal("example compile crashed", .{});
356 },
357 }
358 if (mem.indexOf(u8, result.stderr, error_match) == null) {
359 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });
360 fatal("example did not have expected compile error", .{});
361 }
362 const escaped_stderr = try escapeHtml(arena, result.stderr);
363 const colored_stderr = try termColor(arena, escaped_stderr);
364 try shell_out.print("\n{s}\n", .{colored_stderr});
365 },
366 .test_safety => |error_match| {
367 var test_args = std.ArrayList([]const u8).init(arena);
368 defer test_args.deinit();
369
370 try test_args.appendSlice(&[_][]const u8{
371 zig_exe, "test",
372 input_path,
373 });
374 if (opt_zig_lib_dir) |zig_lib_dir| {
375 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
376 }
377 var mode_arg: []const u8 = "";
378 switch (code.mode) {
379 .Debug => {},
380 .ReleaseSafe => {
381 try test_args.append("-OReleaseSafe");
382 mode_arg = "-OReleaseSafe";
383 },
384 .ReleaseFast => {
385 try test_args.append("-OReleaseFast");
386 mode_arg = "-OReleaseFast";
387 },
388 .ReleaseSmall => {
389 try test_args.append("-OReleaseSmall");
390 mode_arg = "-OReleaseSmall";
391 },
392 }
393
394 const result = try process.Child.run(.{
395 .allocator = arena,
396 .argv = test_args.items,
397 .env_map = &env_map,
398 .max_output_bytes = max_doc_file_size,
399 });
400 switch (result.term) {
401 .Exited => |exit_code| {
402 if (exit_code == 0) {
403 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
404 dumpArgs(test_args.items);
405 fatal("example test incorrectly succeeded", .{});
406 }
407 },
408 else => {
409 print("{s}\nThe following command crashed:\n", .{result.stderr});
410 dumpArgs(test_args.items);
411 fatal("example compile crashed", .{});
412 },
413 }
414 if (mem.indexOf(u8, result.stderr, error_match) == null) {
415 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });
416 fatal("example did not have expected runtime safety error message", .{});
417 }
418 const escaped_stderr = try escapeHtml(arena, result.stderr);
419 const colored_stderr = try termColor(arena, escaped_stderr);
420 try shell_out.print("$ zig test {s}.zig {s}\n{s}\n", .{
421 code_name,
422 mode_arg,
423 colored_stderr,
424 });
425 },
426 .obj => |maybe_error_match| {
427 const name_plus_obj_ext = try std.fmt.allocPrint(arena, "{s}{s}", .{ code_name, obj_ext });
428 var build_args = std.ArrayList([]const u8).init(arena);
429 defer build_args.deinit();
430
431 try build_args.appendSlice(&[_][]const u8{
432 zig_exe, "build-obj",
433 "--color", "on",
434 "--name", code_name,
435 input_path,
436 try std.fmt.allocPrint(arena, "-femit-bin={s}{c}{s}", .{
437 tmp_dir_path, fs.path.sep, name_plus_obj_ext,
438 }),
439 });
440 if (opt_zig_lib_dir) |zig_lib_dir| {
441 try build_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
442 }
443
444 try shell_out.print("$ zig build-obj {s}.zig ", .{code_name});
445
446 switch (code.mode) {
447 .Debug => {},
448 else => {
449 try build_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
450 try shell_out.print("-O {s} ", .{@tagName(code.mode)});
451 },
452 }
453
454 if (code.target_str) |triple| {
455 try build_args.appendSlice(&[_][]const u8{ "-target", triple });
456 try shell_out.print("-target {s} ", .{triple});
457 }
458 for (code.additional_options) |option| {
459 try build_args.append(option);
460 try shell_out.print("{s} ", .{option});
461 }
462
463 if (maybe_error_match) |error_match| {
464 const result = try process.Child.run(.{
465 .allocator = arena,
466 .argv = build_args.items,
467 .env_map = &env_map,
468 .max_output_bytes = max_doc_file_size,
469 });
470 switch (result.term) {
471 .Exited => |exit_code| {
472 if (exit_code == 0) {
473 print("{s}\nThe following command incorrectly succeeded:\n", .{result.stderr});
474 dumpArgs(build_args.items);
475 fatal("example build incorrectly succeeded", .{});
476 }
477 },
478 else => {
479 print("{s}\nThe following command crashed:\n", .{result.stderr});
480 dumpArgs(build_args.items);
481 fatal("example compile crashed", .{});
482 },
483 }
484 if (mem.indexOf(u8, result.stderr, error_match) == null) {
485 print("{s}\nExpected to find '{s}' in stderr\n", .{ result.stderr, error_match });
486 fatal("example did not have expected compile error message", .{});
487 }
488 const escaped_stderr = try escapeHtml(arena, result.stderr);
489 const colored_stderr = try termColor(arena, escaped_stderr);
490 try shell_out.print("\n{s} ", .{colored_stderr});
491 } else {
492 _ = run(arena, &env_map, null, build_args.items) catch fatal("example failed to compile", .{});
493 }
494 try shell_out.writeAll("\n");
495 },
496 .lib => {
497 const bin_basename = try std.zig.binNameAlloc(arena, .{
498 .root_name = code_name,
499 .target = builtin.target,
500 .output_mode = .Lib,
501 });
502
503 var test_args = std.ArrayList([]const u8).init(arena);
504 defer test_args.deinit();
505
506 try test_args.appendSlice(&[_][]const u8{
507 zig_exe, "build-lib",
508 input_path,
509 try std.fmt.allocPrint(arena, "-femit-bin={s}{s}{s}", .{
510 tmp_dir_path, fs.path.sep_str, bin_basename,
511 }),
512 });
513 if (opt_zig_lib_dir) |zig_lib_dir| {
514 try test_args.appendSlice(&.{ "--zig-lib-dir", zig_lib_dir });
515 }
516 try shell_out.print("$ zig build-lib {s}.zig ", .{code_name});
517
518 switch (code.mode) {
519 .Debug => {},
520 else => {
521 try test_args.appendSlice(&[_][]const u8{ "-O", @tagName(code.mode) });
522 try shell_out.print("-O {s} ", .{@tagName(code.mode)});
523 },
524 }
525 if (code.target_str) |triple| {
526 try test_args.appendSlice(&[_][]const u8{ "-target", triple });
527 try shell_out.print("-target {s} ", .{triple});
528 }
529 if (code.link_mode) |link_mode| {
530 switch (link_mode) {
531 .static => {
532 try test_args.append("-static");
533 try shell_out.print("-static ", .{});
534 },
535 .dynamic => {
536 try test_args.append("-dynamic");
537 try shell_out.print("-dynamic ", .{});
538 },
539 }
540 }
541 for (code.additional_options) |option| {
542 try test_args.append(option);
543 try shell_out.print("{s} ", .{option});
544 }
545 const result = run(arena, &env_map, null, test_args.items) catch fatal("test failed", .{});
546 const escaped_stderr = try escapeHtml(arena, result.stderr);
547 const escaped_stdout = try escapeHtml(arena, result.stdout);
548 try shell_out.print("\n{s}{s}\n", .{ escaped_stderr, escaped_stdout });
549 },
550 }
551
552 if (!code.just_check_syntax) {
553 try printShell(out, shell_buffer.items, false);
554 }
555}
556
557fn dumpArgs(args: []const []const u8) void {
558 for (args) |arg|
559 std.debug.print("{s} ", .{arg})
560 else
561 std.debug.print("\n", .{});
562}
563
564fn printSourceBlock(arena: Allocator, out: anytype, source_bytes: []const u8, name: []const u8) !void {
565 try out.print("<figure><figcaption class=\"{s}-cap\"><cite class=\"file\">{s}</cite></figcaption><pre>", .{
566 "zig", name,
567 });
568 try tokenizeAndPrint(arena, out, source_bytes);
569 try out.writeAll("</pre></figure>");
570}
571
572fn tokenizeAndPrint(arena: Allocator, out: anytype, raw_src: []const u8) !void {
573 const src_non_terminated = mem.trim(u8, raw_src, " \r\n");
574 const src = try arena.dupeZ(u8, src_non_terminated);
575
576 try out.writeAll("<code>");
577 var tokenizer = std.zig.Tokenizer.init(src);
578 var index: usize = 0;
579 var next_tok_is_fn = false;
580 while (true) {
581 const prev_tok_was_fn = next_tok_is_fn;
582 next_tok_is_fn = false;
583
584 const token = tokenizer.next();
585 if (mem.indexOf(u8, src[index..token.loc.start], "//")) |comment_start_off| {
586 // render one comment
587 const comment_start = index + comment_start_off;
588 const comment_end_off = mem.indexOf(u8, src[comment_start..token.loc.start], "\n");
589 const comment_end = if (comment_end_off) |o| comment_start + o else token.loc.start;
590
591 try writeEscapedLines(out, src[index..comment_start]);
592 try out.writeAll("<span class=\"tok-comment\">");
593 try writeEscaped(out, src[comment_start..comment_end]);
594 try out.writeAll("</span>");
595 index = comment_end;
596 tokenizer.index = index;
597 continue;
598 }
599
600 try writeEscapedLines(out, src[index..token.loc.start]);
601 switch (token.tag) {
602 .eof => break,
603
604 .keyword_addrspace,
605 .keyword_align,
606 .keyword_and,
607 .keyword_asm,
608 .keyword_async,
609 .keyword_await,
610 .keyword_break,
611 .keyword_catch,
612 .keyword_comptime,
613 .keyword_const,
614 .keyword_continue,
615 .keyword_defer,
616 .keyword_else,
617 .keyword_enum,
618 .keyword_errdefer,
619 .keyword_error,
620 .keyword_export,
621 .keyword_extern,
622 .keyword_for,
623 .keyword_if,
624 .keyword_inline,
625 .keyword_noalias,
626 .keyword_noinline,
627 .keyword_nosuspend,
628 .keyword_opaque,
629 .keyword_or,
630 .keyword_orelse,
631 .keyword_packed,
632 .keyword_anyframe,
633 .keyword_pub,
634 .keyword_resume,
635 .keyword_return,
636 .keyword_linksection,
637 .keyword_callconv,
638 .keyword_struct,
639 .keyword_suspend,
640 .keyword_switch,
641 .keyword_test,
642 .keyword_threadlocal,
643 .keyword_try,
644 .keyword_union,
645 .keyword_unreachable,
646 .keyword_usingnamespace,
647 .keyword_var,
648 .keyword_volatile,
649 .keyword_allowzero,
650 .keyword_while,
651 .keyword_anytype,
652 => {
653 try out.writeAll("<span class=\"tok-kw\">");
654 try writeEscaped(out, src[token.loc.start..token.loc.end]);
655 try out.writeAll("</span>");
656 },
657
658 .keyword_fn => {
659 try out.writeAll("<span class=\"tok-kw\">");
660 try writeEscaped(out, src[token.loc.start..token.loc.end]);
661 try out.writeAll("</span>");
662 next_tok_is_fn = true;
663 },
664
665 .string_literal,
666 .multiline_string_literal_line,
667 .char_literal,
668 => {
669 try out.writeAll("<span class=\"tok-str\">");
670 try writeEscaped(out, src[token.loc.start..token.loc.end]);
671 try out.writeAll("</span>");
672 },
673
674 .builtin => {
675 try out.writeAll("<span class=\"tok-builtin\">");
676 try writeEscaped(out, src[token.loc.start..token.loc.end]);
677 try out.writeAll("</span>");
678 },
679
680 .doc_comment,
681 .container_doc_comment,
682 => {
683 try out.writeAll("<span class=\"tok-comment\">");
684 try writeEscaped(out, src[token.loc.start..token.loc.end]);
685 try out.writeAll("</span>");
686 },
687
688 .identifier => {
689 const tok_bytes = src[token.loc.start..token.loc.end];
690 if (mem.eql(u8, tok_bytes, "undefined") or
691 mem.eql(u8, tok_bytes, "null") or
692 mem.eql(u8, tok_bytes, "true") or
693 mem.eql(u8, tok_bytes, "false"))
694 {
695 try out.writeAll("<span class=\"tok-null\">");
696 try writeEscaped(out, tok_bytes);
697 try out.writeAll("</span>");
698 } else if (prev_tok_was_fn) {
699 try out.writeAll("<span class=\"tok-fn\">");
700 try writeEscaped(out, tok_bytes);
701 try out.writeAll("</span>");
702 } else {
703 const is_int = blk: {
704 if (src[token.loc.start] != 'i' and src[token.loc.start] != 'u')
705 break :blk false;
706 var i = token.loc.start + 1;
707 if (i == token.loc.end)
708 break :blk false;
709 while (i != token.loc.end) : (i += 1) {
710 if (src[i] < '0' or src[i] > '9')
711 break :blk false;
712 }
713 break :blk true;
714 };
715 const isType = std.zig.isPrimitive;
716 if (is_int or isType(tok_bytes)) {
717 try out.writeAll("<span class=\"tok-type\">");
718 try writeEscaped(out, tok_bytes);
719 try out.writeAll("</span>");
720 } else {
721 try writeEscaped(out, tok_bytes);
722 }
723 }
724 },
725
726 .number_literal => {
727 try out.writeAll("<span class=\"tok-number\">");
728 try writeEscaped(out, src[token.loc.start..token.loc.end]);
729 try out.writeAll("</span>");
730 },
731
732 .bang,
733 .pipe,
734 .pipe_pipe,
735 .pipe_equal,
736 .equal,
737 .equal_equal,
738 .equal_angle_bracket_right,
739 .bang_equal,
740 .l_paren,
741 .r_paren,
742 .semicolon,
743 .percent,
744 .percent_equal,
745 .l_brace,
746 .r_brace,
747 .l_bracket,
748 .r_bracket,
749 .period,
750 .period_asterisk,
751 .ellipsis2,
752 .ellipsis3,
753 .caret,
754 .caret_equal,
755 .plus,
756 .plus_plus,
757 .plus_equal,
758 .plus_percent,
759 .plus_percent_equal,
760 .plus_pipe,
761 .plus_pipe_equal,
762 .minus,
763 .minus_equal,
764 .minus_percent,
765 .minus_percent_equal,
766 .minus_pipe,
767 .minus_pipe_equal,
768 .asterisk,
769 .asterisk_equal,
770 .asterisk_asterisk,
771 .asterisk_percent,
772 .asterisk_percent_equal,
773 .asterisk_pipe,
774 .asterisk_pipe_equal,
775 .arrow,
776 .colon,
777 .slash,
778 .slash_equal,
779 .comma,
780 .ampersand,
781 .ampersand_equal,
782 .question_mark,
783 .angle_bracket_left,
784 .angle_bracket_left_equal,
785 .angle_bracket_angle_bracket_left,
786 .angle_bracket_angle_bracket_left_equal,
787 .angle_bracket_angle_bracket_left_pipe,
788 .angle_bracket_angle_bracket_left_pipe_equal,
789 .angle_bracket_right,
790 .angle_bracket_right_equal,
791 .angle_bracket_angle_bracket_right,
792 .angle_bracket_angle_bracket_right_equal,
793 .tilde,
794 => try writeEscaped(out, src[token.loc.start..token.loc.end]),
795
796 .invalid, .invalid_periodasterisks => fatal("syntax error", .{}),
797 }
798 index = token.loc.end;
799 }
800 try out.writeAll("</code>");
801}
802
803fn writeEscapedLines(out: anytype, text: []const u8) !void {
804 return writeEscaped(out, text);
805}
806
807const Code = struct {
808 id: Id,
809 mode: std.builtin.OptimizeMode,
810 link_objects: []const []const u8,
811 target_str: ?[]const u8,
812 link_libc: bool,
813 link_mode: ?std.builtin.LinkMode,
814 disable_cache: bool,
815 verbose_cimport: bool,
816 just_check_syntax: bool,
817 additional_options: []const []const u8,
818
819 const Id = union(enum) {
820 @"test",
821 test_error: []const u8,
822 test_safety: []const u8,
823 exe: ExpectedOutcome,
824 obj: ?[]const u8,
825 lib,
826 };
827
828 const ExpectedOutcome = enum {
829 succeed,
830 fail,
831 build_fail,
832 };
833};
834
835fn stripManifest(source_bytes: []const u8) []const u8 {
836 const manifest_start = mem.lastIndexOf(u8, source_bytes, "\n\n// ") orelse
837 fatal("missing manifest comment", .{});
838 return source_bytes[0 .. manifest_start + 1];
839}
840
841fn parseManifest(arena: Allocator, source_bytes: []const u8) !Code {
842 const manifest_start = mem.lastIndexOf(u8, source_bytes, "\n\n// ") orelse
843 fatal("missing manifest comment", .{});
844 var it = mem.tokenizeScalar(u8, source_bytes[manifest_start..], '\n');
845 const first_line = skipPrefix(it.next().?);
846
847 var just_check_syntax = false;
848 const id: Code.Id = if (mem.eql(u8, first_line, "syntax")) blk: {
849 just_check_syntax = true;
850 break :blk .{ .obj = null };
851 } else if (mem.eql(u8, first_line, "test"))
852 .@"test"
853 else if (mem.eql(u8, first_line, "lib"))
854 .lib
855 else if (mem.eql(u8, first_line, "obj"))
856 .{ .obj = null }
857 else if (mem.startsWith(u8, first_line, "test_error="))
858 .{ .test_error = first_line["test_error=".len..] }
859 else if (mem.startsWith(u8, first_line, "test_safety="))
860 .{ .test_safety = first_line["test_safety=".len..] }
861 else if (mem.startsWith(u8, first_line, "exe="))
862 .{ .exe = std.meta.stringToEnum(Code.ExpectedOutcome, first_line["exe=".len..]) orelse
863 fatal("bad exe expected outcome in line '{s}'", .{first_line}) }
864 else if (mem.startsWith(u8, first_line, "obj="))
865 .{ .obj = first_line["obj=".len..] }
866 else
867 fatal("unrecognized manifest id: '{s}'", .{first_line});
868
869 var mode: std.builtin.OptimizeMode = .Debug;
870 var link_mode: ?std.builtin.LinkMode = null;
871 var link_objects: std.ArrayListUnmanaged([]const u8) = .{};
872 var additional_options: std.ArrayListUnmanaged([]const u8) = .{};
873 var target_str: ?[]const u8 = null;
874 var link_libc = false;
875 var disable_cache = false;
876 var verbose_cimport = false;
877
878 while (it.next()) |prefixed_line| {
879 const line = skipPrefix(prefixed_line);
880 if (mem.startsWith(u8, line, "optimize=")) {
881 mode = std.meta.stringToEnum(std.builtin.OptimizeMode, line["optimize=".len..]) orelse
882 fatal("bad optimization mode line: '{s}'", .{line});
883 } else if (mem.startsWith(u8, line, "link_mode=")) {
884 link_mode = std.meta.stringToEnum(std.builtin.LinkMode, line["link_mode=".len..]) orelse
885 fatal("bad link mode line: '{s}'", .{line});
886 } else if (mem.startsWith(u8, line, "link_object=")) {
887 try link_objects.append(arena, line["link_object=".len..]);
888 } else if (mem.startsWith(u8, line, "additional_option=")) {
889 try additional_options.append(arena, line["additional_option=".len..]);
890 } else if (mem.startsWith(u8, line, "target=")) {
891 target_str = line["target=".len..];
892 } else if (mem.eql(u8, line, "link_libc")) {
893 link_libc = true;
894 } else if (mem.eql(u8, line, "disable_cache")) {
895 disable_cache = true;
896 } else if (mem.eql(u8, line, "verbose_cimport")) {
897 verbose_cimport = true;
898 } else {
899 fatal("unrecognized manifest line: {s}", .{line});
900 }
901 }
902
903 return .{
904 .id = id,
905 .mode = mode,
906 .additional_options = try additional_options.toOwnedSlice(arena),
907 .link_objects = try link_objects.toOwnedSlice(arena),
908 .target_str = target_str,
909 .link_libc = link_libc,
910 .link_mode = link_mode,
911 .disable_cache = disable_cache,
912 .verbose_cimport = verbose_cimport,
913 .just_check_syntax = just_check_syntax,
914 };
915}
916
917fn skipPrefix(line: []const u8) []const u8 {
918 if (!mem.startsWith(u8, line, "// ")) {
919 fatal("line does not start with '// ': '{s}", .{line});
920 }
921 return line[3..];
922}
923
924fn escapeHtml(allocator: Allocator, input: []const u8) ![]u8 {
925 var buf = std.ArrayList(u8).init(allocator);
926 defer buf.deinit();
927
928 const out = buf.writer();
929 try writeEscaped(out, input);
930 return try buf.toOwnedSlice();
931}
932
933fn writeEscaped(out: anytype, input: []const u8) !void {
934 for (input) |c| {
935 try switch (c) {
936 '&' => out.writeAll("&amp;"),
937 '<' => out.writeAll("&lt;"),
938 '>' => out.writeAll("&gt;"),
939 '"' => out.writeAll("&quot;"),
940 else => out.writeByte(c),
941 };
942 }
943}
944
945fn termColor(allocator: Allocator, input: []const u8) ![]u8 {
946 // The SRG sequences generates by the Zig compiler are in the format:
947 // ESC [ <foreground-color> ; <n> m
948 // or
949 // ESC [ <n> m
950 //
951 // where
952 // foreground-color is 31 (red), 32 (green), 36 (cyan)
953 // n is 0 (reset), 1 (bold), 2 (dim)
954 //
955 // Note that 37 (white) is currently not used by the compiler.
956 //
957 // See std.debug.TTY.Color.
958 const supported_sgr_colors = [_]u8{ 31, 32, 36 };
959 const supported_sgr_numbers = [_]u8{ 0, 1, 2 };
960
961 var buf = std.ArrayList(u8).init(allocator);
962 defer buf.deinit();
963
964 var out = buf.writer();
965 var sgr_param_start_index: usize = undefined;
966 var sgr_num: u8 = undefined;
967 var sgr_color: u8 = undefined;
968 var i: usize = 0;
969 var state: enum {
970 start,
971 escape,
972 lbracket,
973 number,
974 after_number,
975 arg,
976 arg_number,
977 expect_end,
978 } = .start;
979 var last_new_line: usize = 0;
980 var open_span_count: usize = 0;
981 while (i < input.len) : (i += 1) {
982 const c = input[i];
983 switch (state) {
984 .start => switch (c) {
985 '\x1b' => state = .escape,
986 '\n' => {
987 try out.writeByte(c);
988 last_new_line = buf.items.len;
989 },
990 else => try out.writeByte(c),
991 },
992 .escape => switch (c) {
993 '[' => state = .lbracket,
994 else => return error.UnsupportedEscape,
995 },
996 .lbracket => switch (c) {
997 '0'...'9' => {
998 sgr_param_start_index = i;
999 state = .number;
1000 },
1001 else => return error.UnsupportedEscape,
1002 },
1003 .number => switch (c) {
1004 '0'...'9' => {},
1005 else => {
1006 sgr_num = try std.fmt.parseInt(u8, input[sgr_param_start_index..i], 10);
1007 sgr_color = 0;
1008 state = .after_number;
1009 i -= 1;
1010 },
1011 },
1012 .after_number => switch (c) {
1013 ';' => state = .arg,
1014 'D' => state = .start,
1015 'K' => {
1016 buf.items.len = last_new_line;
1017 state = .start;
1018 },
1019 else => {
1020 state = .expect_end;
1021 i -= 1;
1022 },
1023 },
1024 .arg => switch (c) {
1025 '0'...'9' => {
1026 sgr_param_start_index = i;
1027 state = .arg_number;
1028 },
1029 else => return error.UnsupportedEscape,
1030 },
1031 .arg_number => switch (c) {
1032 '0'...'9' => {},
1033 else => {
1034 // Keep the sequence consistent, foreground color first.
1035 // 32;1m is equivalent to 1;32m, but the latter will
1036 // generate an incorrect HTML class without notice.
1037 sgr_color = sgr_num;
1038 if (!in(&supported_sgr_colors, sgr_color)) return error.UnsupportedForegroundColor;
1039
1040 sgr_num = try std.fmt.parseInt(u8, input[sgr_param_start_index..i], 10);
1041 if (!in(&supported_sgr_numbers, sgr_num)) return error.UnsupportedNumber;
1042
1043 state = .expect_end;
1044 i -= 1;
1045 },
1046 },
1047 .expect_end => switch (c) {
1048 'm' => {
1049 state = .start;
1050 while (open_span_count != 0) : (open_span_count -= 1) {
1051 try out.writeAll("</span>");
1052 }
1053 if (sgr_num == 0) {
1054 if (sgr_color != 0) return error.UnsupportedColor;
1055 continue;
1056 }
1057 if (sgr_color != 0) {
1058 try out.print("<span class=\"sgr-{d}_{d}m\">", .{ sgr_color, sgr_num });
1059 } else {
1060 try out.print("<span class=\"sgr-{d}m\">", .{sgr_num});
1061 }
1062 open_span_count += 1;
1063 },
1064 else => return error.UnsupportedEscape,
1065 },
1066 }
1067 }
1068 return try buf.toOwnedSlice();
1069}
1070
1071// Returns true if number is in slice.
1072fn in(slice: []const u8, number: u8) bool {
1073 return mem.indexOfScalar(u8, slice, number) != null;
1074}
1075
1076fn run(
1077 allocator: Allocator,
1078 env_map: *process.EnvMap,
1079 cwd: ?[]const u8,
1080 args: []const []const u8,
1081) !process.Child.RunResult {
1082 const result = try process.Child.run(.{
1083 .allocator = allocator,
1084 .argv = args,
1085 .env_map = env_map,
1086 .cwd = cwd,
1087 .max_output_bytes = max_doc_file_size,
1088 });
1089 switch (result.term) {
1090 .Exited => |exit_code| {
1091 if (exit_code != 0) {
1092 std.debug.print("{s}\nThe following command exited with code {}:\n", .{ result.stderr, exit_code });
1093 dumpArgs(args);
1094 return error.ChildExitError;
1095 }
1096 },
1097 else => {
1098 std.debug.print("{s}\nThe following command crashed:\n", .{result.stderr});
1099 dumpArgs(args);
1100 return error.ChildCrashed;
1101 },
1102 }
1103 return result;
1104}
1105
1106fn printShell(out: anytype, shell_content: []const u8, escape: bool) !void {
1107 const trimmed_shell_content = mem.trim(u8, shell_content, " \r\n");
1108 try out.writeAll("<figure><figcaption class=\"shell-cap\">Shell</figcaption><pre><samp>");
1109 var cmd_cont: bool = false;
1110 var iter = std.mem.splitScalar(u8, trimmed_shell_content, '\n');
1111 while (iter.next()) |orig_line| {
1112 const line = mem.trimRight(u8, orig_line, " \r");
1113 if (!cmd_cont and line.len > 1 and mem.eql(u8, line[0..2], "$ ") and line[line.len - 1] != '\\') {
1114 try out.writeAll("$ <kbd>");
1115 const s = std.mem.trimLeft(u8, line[1..], " ");
1116 if (escape) {
1117 try writeEscaped(out, s);
1118 } else {
1119 try out.writeAll(s);
1120 }
1121 try out.writeAll("</kbd>" ++ "\n");
1122 } else if (!cmd_cont and line.len > 1 and mem.eql(u8, line[0..2], "$ ") and line[line.len - 1] == '\\') {
1123 try out.writeAll("$ <kbd>");
1124 const s = std.mem.trimLeft(u8, line[1..], " ");
1125 if (escape) {
1126 try writeEscaped(out, s);
1127 } else {
1128 try out.writeAll(s);
1129 }
1130 try out.writeAll("\n");
1131 cmd_cont = true;
1132 } else if (line.len > 0 and line[line.len - 1] != '\\' and cmd_cont) {
1133 if (escape) {
1134 try writeEscaped(out, line);
1135 } else {
1136 try out.writeAll(line);
1137 }
1138 try out.writeAll("</kbd>" ++ "\n");
1139 cmd_cont = false;
1140 } else {
1141 if (escape) {
1142 try writeEscaped(out, line);
1143 } else {
1144 try out.writeAll(line);
1145 }
1146 try out.writeAll("\n");
1147 }
1148 }
1149
1150 try out.writeAll("</samp></pre></figure>");
1151}
1152
1153test "term supported colors" {
1154 const test_allocator = testing.allocator;
1155
1156 {
1157 const input = "A\x1b[31;1mred\x1b[0mB";
1158 const expect = "A<span class=\"sgr-31_1m\">red</span>B";
1159
1160 const result = try termColor(test_allocator, input);
1161 defer test_allocator.free(result);
1162 try testing.expectEqualSlices(u8, expect, result);
1163 }
1164
1165 {
1166 const input = "A\x1b[32;1mgreen\x1b[0mB";
1167 const expect = "A<span class=\"sgr-32_1m\">green</span>B";
1168
1169 const result = try termColor(test_allocator, input);
1170 defer test_allocator.free(result);
1171 try testing.expectEqualSlices(u8, expect, result);
1172 }
1173
1174 {
1175 const input = "A\x1b[36;1mcyan\x1b[0mB";
1176 const expect = "A<span class=\"sgr-36_1m\">cyan</span>B";
1177
1178 const result = try termColor(test_allocator, input);
1179 defer test_allocator.free(result);
1180 try testing.expectEqualSlices(u8, expect, result);
1181 }
1182
1183 {
1184 const input = "A\x1b[1mbold\x1b[0mB";
1185 const expect = "A<span class=\"sgr-1m\">bold</span>B";
1186
1187 const result = try termColor(test_allocator, input);
1188 defer test_allocator.free(result);
1189 try testing.expectEqualSlices(u8, expect, result);
1190 }
1191
1192 {
1193 const input = "A\x1b[2mdim\x1b[0mB";
1194 const expect = "A<span class=\"sgr-2m\">dim</span>B";
1195
1196 const result = try termColor(test_allocator, input);
1197 defer test_allocator.free(result);
1198 try testing.expectEqualSlices(u8, expect, result);
1199 }
1200}
1201
1202test "term output from zig" {
1203 // Use data generated by https://github.com/perillo/zig-tty-test-data,
1204 // with zig version 0.11.0-dev.1898+36d47dd19.
1205 const test_allocator = testing.allocator;
1206
1207 {
1208 // 1.1-with-build-progress.out
1209 const input = "Semantic Analysis [1324] \x1b[25D\x1b[0KLLVM Emit Object... \x1b[20D\x1b[0KLLVM Emit Object... \x1b[20D\x1b[0KLLD Link... \x1b[12D\x1b[0K";
1210 const expect = "";
1211
1212 const result = try termColor(test_allocator, input);
1213 defer test_allocator.free(result);
1214 try testing.expectEqualSlices(u8, expect, result);
1215 }
1216
1217 {
1218 // 2.1-with-reference-traces.out
1219 const input = "\x1b[1msrc/2.1-with-reference-traces.zig:3:7: \x1b[31;1merror: \x1b[0m\x1b[1mcannot assign to constant\n\x1b[0m x += 1;\n \x1b[32;1m~~^~~~\n\x1b[0m\x1b[0m\x1b[2mreferenced by:\n main: src/2.1-with-reference-traces.zig:7:5\n callMain: /usr/local/lib/zig/lib/std/start.zig:607:17\n remaining reference traces hidden; use '-freference-trace' to see all reference traces\n\n\x1b[0m";
1220 const expect =
1221 \\<span class="sgr-1m">src/2.1-with-reference-traces.zig:3:7: </span><span class="sgr-31_1m">error: </span><span class="sgr-1m">cannot assign to constant
1222 \\</span> x += 1;
1223 \\ <span class="sgr-32_1m">~~^~~~
1224 \\</span><span class="sgr-2m">referenced by:
1225 \\ main: src/2.1-with-reference-traces.zig:7:5
1226 \\ callMain: /usr/local/lib/zig/lib/std/start.zig:607:17
1227 \\ remaining reference traces hidden; use '-freference-trace' to see all reference traces
1228 \\
1229 \\</span>
1230 ;
1231
1232 const result = try termColor(test_allocator, input);
1233 defer test_allocator.free(result);
1234 try testing.expectEqualSlices(u8, expect, result);
1235 }
1236
1237 {
1238 // 2.2-without-reference-traces.out
1239 const input = "\x1b[1m/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:128:29: \x1b[31;1merror: \x1b[0m\x1b[1minvalid type given to fixedBufferStream\n\x1b[0m else => @compileError(\"invalid type given to fixedBufferStream\"),\n \x1b[32;1m^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\x1b[0m\x1b[1m/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:116:66: \x1b[36;1mnote: \x1b[0m\x1b[1mcalled from here\n\x1b[0mpub fn fixedBufferStream(buffer: anytype) FixedBufferStream(Slice(@TypeOf(buffer))) {\n; \x1b[32;1m~~~~~^~~~~~~~~~~~~~~~~\n\x1b[0m";
1240 const expect =
1241 \\<span class="sgr-1m">/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:128:29: </span><span class="sgr-31_1m">error: </span><span class="sgr-1m">invalid type given to fixedBufferStream
1242 \\</span> else => @compileError("invalid type given to fixedBufferStream"),
1243 \\ <span class="sgr-32_1m">^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1244 \\</span><span class="sgr-1m">/usr/local/lib/zig/lib/std/io/fixed_buffer_stream.zig:116:66: </span><span class="sgr-36_1m">note: </span><span class="sgr-1m">called from here
1245 \\</span>pub fn fixedBufferStream(buffer: anytype) FixedBufferStream(Slice(@TypeOf(buffer))) {
1246 \\; <span class="sgr-32_1m">~~~~~^~~~~~~~~~~~~~~~~
1247 \\</span>
1248 ;
1249
1250 const result = try termColor(test_allocator, input);
1251 defer test_allocator.free(result);
1252 try testing.expectEqualSlices(u8, expect, result);
1253 }
1254
1255 {
1256 // 2.3-with-notes.out
1257 const input = "\x1b[1msrc/2.3-with-notes.zig:6:9: \x1b[31;1merror: \x1b[0m\x1b[1mexpected type '*2.3-with-notes.Derp', found '*2.3-with-notes.Wat'\n\x1b[0m bar(w);\n \x1b[32;1m^\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:6:9: \x1b[36;1mnote: \x1b[0m\x1b[1mpointer type child '2.3-with-notes.Wat' cannot cast into pointer type child '2.3-with-notes.Derp'\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:2:13: \x1b[36;1mnote: \x1b[0m\x1b[1mopaque declared here\n\x1b[0mconst Wat = opaque {};\n \x1b[32;1m^~~~~~~~~\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:1:14: \x1b[36;1mnote: \x1b[0m\x1b[1mopaque declared here\n\x1b[0mconst Derp = opaque {};\n \x1b[32;1m^~~~~~~~~\n\x1b[0m\x1b[1msrc/2.3-with-notes.zig:4:18: \x1b[36;1mnote: \x1b[0m\x1b[1mparameter type declared here\n\x1b[0mextern fn bar(d: *Derp) void;\n \x1b[32;1m^~~~~\n\x1b[0m\x1b[0m\x1b[2mreferenced by:\n main: src/2.3-with-notes.zig:10:5\n callMain: /usr/local/lib/zig/lib/std/start.zig:607:17\n remaining reference traces hidden; use '-freference-trace' to see all reference traces\n\n\x1b[0m";
1258 const expect =
1259 \\<span class="sgr-1m">src/2.3-with-notes.zig:6:9: </span><span class="sgr-31_1m">error: </span><span class="sgr-1m">expected type '*2.3-with-notes.Derp', found '*2.3-with-notes.Wat'
1260 \\</span> bar(w);
1261 \\ <span class="sgr-32_1m">^
1262 \\</span><span class="sgr-1m">src/2.3-with-notes.zig:6:9: </span><span class="sgr-36_1m">note: </span><span class="sgr-1m">pointer type child '2.3-with-notes.Wat' cannot cast into pointer type child '2.3-with-notes.Derp'
1263 \\</span><span class="sgr-1m">src/2.3-with-notes.zig:2:13: </span><span class="sgr-36_1m">note: </span><span class="sgr-1m">opaque declared here
1264 \\</span>const Wat = opaque {};
1265 \\ <span class="sgr-32_1m">^~~~~~~~~
1266 \\</span><span class="sgr-1m">src/2.3-with-notes.zig:1:14: </span><span class="sgr-36_1m">note: </span><span class="sgr-1m">opaque declared here
1267 \\</span>const Derp = opaque {};
1268 \\ <span class="sgr-32_1m">^~~~~~~~~
1269 \\</span><span class="sgr-1m">src/2.3-with-notes.zig:4:18: </span><span class="sgr-36_1m">note: </span><span class="sgr-1m">parameter type declared here
1270 \\</span>extern fn bar(d: *Derp) void;
1271 \\ <span class="sgr-32_1m">^~~~~
1272 \\</span><span class="sgr-2m">referenced by:
1273 \\ main: src/2.3-with-notes.zig:10:5
1274 \\ callMain: /usr/local/lib/zig/lib/std/start.zig:607:17
1275 \\ remaining reference traces hidden; use '-freference-trace' to see all reference traces
1276 \\
1277 \\</span>
1278 ;
1279
1280 const result = try termColor(test_allocator, input);
1281 defer test_allocator.free(result);
1282 try testing.expectEqualSlices(u8, expect, result);
1283 }
1284
1285 {
1286 // 3.1-with-error-return-traces.out
1287
1288 const input = "error: Error\n\x1b[1m/home/zig/src/3.1-with-error-return-traces.zig:5:5\x1b[0m: \x1b[2m0x20b008 in callee (3.1-with-error-return-traces)\x1b[0m\n return error.Error;\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.1-with-error-return-traces.zig:9:5\x1b[0m: \x1b[2m0x20b113 in caller (3.1-with-error-return-traces)\x1b[0m\n try callee();\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.1-with-error-return-traces.zig:13:5\x1b[0m: \x1b[2m0x20b153 in main (3.1-with-error-return-traces)\x1b[0m\n try caller();\n \x1b[32;1m^\x1b[0m\n";
1289 const expect =
1290 \\error: Error
1291 \\<span class="sgr-1m">/home/zig/src/3.1-with-error-return-traces.zig:5:5</span>: <span class="sgr-2m">0x20b008 in callee (3.1-with-error-return-traces)</span>
1292 \\ return error.Error;
1293 \\ <span class="sgr-32_1m">^</span>
1294 \\<span class="sgr-1m">/home/zig/src/3.1-with-error-return-traces.zig:9:5</span>: <span class="sgr-2m">0x20b113 in caller (3.1-with-error-return-traces)</span>
1295 \\ try callee();
1296 \\ <span class="sgr-32_1m">^</span>
1297 \\<span class="sgr-1m">/home/zig/src/3.1-with-error-return-traces.zig:13:5</span>: <span class="sgr-2m">0x20b153 in main (3.1-with-error-return-traces)</span>
1298 \\ try caller();
1299 \\ <span class="sgr-32_1m">^</span>
1300 \\
1301 ;
1302
1303 const result = try termColor(test_allocator, input);
1304 defer test_allocator.free(result);
1305 try testing.expectEqualSlices(u8, expect, result);
1306 }
1307
1308 {
1309 // 3.2-with-stack-trace.out
1310 const input = "\x1b[1m/usr/local/lib/zig/lib/std/debug.zig:561:19\x1b[0m: \x1b[2m0x22a107 in writeCurrentStackTrace__anon_5898 (3.2-with-stack-trace)\x1b[0m\n while (it.next()) |return_address| {\n \x1b[32;1m^\x1b[0m\n\x1b[1m/usr/local/lib/zig/lib/std/debug.zig:157:80\x1b[0m: \x1b[2m0x20bb23 in dumpCurrentStackTrace (3.2-with-stack-trace)\x1b[0m\n writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(io.getStdErr()), start_addr) catch |err| {\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.2-with-stack-trace.zig:5:36\x1b[0m: \x1b[2m0x20d3b2 in foo (3.2-with-stack-trace)\x1b[0m\n std.debug.dumpCurrentStackTrace(null);\n \x1b[32;1m^\x1b[0m\n\x1b[1m/home/zig/src/3.2-with-stack-trace.zig:9:8\x1b[0m: \x1b[2m0x20b458 in main (3.2-with-stack-trace)\x1b[0m\n foo();\n \x1b[32;1m^\x1b[0m\n\x1b[1m/usr/local/lib/zig/lib/std/start.zig:607:22\x1b[0m: \x1b[2m0x20a965 in posixCallMainAndExit (3.2-with-stack-trace)\x1b[0m\n root.main();\n \x1b[32;1m^\x1b[0m\n\x1b[1m/usr/local/lib/zig/lib/std/start.zig:376:5\x1b[0m: \x1b[2m0x20a411 in _start (3.2-with-stack-trace)\x1b[0m\n @call(.never_inline, posixCallMainAndExit, .{});\n \x1b[32;1m^\x1b[0m\n";
1311 const expect =
1312 \\<span class="sgr-1m">/usr/local/lib/zig/lib/std/debug.zig:561:19</span>: <span class="sgr-2m">0x22a107 in writeCurrentStackTrace__anon_5898 (3.2-with-stack-trace)</span>
1313 \\ while (it.next()) |return_address| {
1314 \\ <span class="sgr-32_1m">^</span>
1315 \\<span class="sgr-1m">/usr/local/lib/zig/lib/std/debug.zig:157:80</span>: <span class="sgr-2m">0x20bb23 in dumpCurrentStackTrace (3.2-with-stack-trace)</span>
1316 \\ writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(io.getStdErr()), start_addr) catch |err| {
1317 \\ <span class="sgr-32_1m">^</span>
1318 \\<span class="sgr-1m">/home/zig/src/3.2-with-stack-trace.zig:5:36</span>: <span class="sgr-2m">0x20d3b2 in foo (3.2-with-stack-trace)</span>
1319 \\ std.debug.dumpCurrentStackTrace(null);
1320 \\ <span class="sgr-32_1m">^</span>
1321 \\<span class="sgr-1m">/home/zig/src/3.2-with-stack-trace.zig:9:8</span>: <span class="sgr-2m">0x20b458 in main (3.2-with-stack-trace)</span>
1322 \\ foo();
1323 \\ <span class="sgr-32_1m">^</span>
1324 \\<span class="sgr-1m">/usr/local/lib/zig/lib/std/start.zig:607:22</span>: <span class="sgr-2m">0x20a965 in posixCallMainAndExit (3.2-with-stack-trace)</span>
1325 \\ root.main();
1326 \\ <span class="sgr-32_1m">^</span>
1327 \\<span class="sgr-1m">/usr/local/lib/zig/lib/std/start.zig:376:5</span>: <span class="sgr-2m">0x20a411 in _start (3.2-with-stack-trace)</span>
1328 \\ @call(.never_inline, posixCallMainAndExit, .{});
1329 \\ <span class="sgr-32_1m">^</span>
1330 \\
1331 ;
1332
1333 const result = try termColor(test_allocator, input);
1334 defer test_allocator.free(result);
1335 try testing.expectEqualSlices(u8, expect, result);
1336 }
1337}
1338
1339test "printShell" {
1340 const test_allocator = std.testing.allocator;
1341
1342 {
1343 const shell_out =
1344 \\$ zig build test.zig
1345 ;
1346 const expected =
1347 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig</kbd>
1348 \\</samp></pre></figure>
1349 ;
1350
1351 var buffer = std.ArrayList(u8).init(test_allocator);
1352 defer buffer.deinit();
1353
1354 try printShell(buffer.writer(), shell_out, false);
1355 try testing.expectEqualSlices(u8, expected, buffer.items);
1356 }
1357 {
1358 const shell_out =
1359 \\$ zig build test.zig
1360 \\build output
1361 ;
1362 const expected =
1363 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig</kbd>
1364 \\build output
1365 \\</samp></pre></figure>
1366 ;
1367
1368 var buffer = std.ArrayList(u8).init(test_allocator);
1369 defer buffer.deinit();
1370
1371 try printShell(buffer.writer(), shell_out, false);
1372 try testing.expectEqualSlices(u8, expected, buffer.items);
1373 }
1374 {
1375 const shell_out = "$ zig build test.zig\r\nbuild output\r\n";
1376 const expected =
1377 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig</kbd>
1378 \\build output
1379 \\</samp></pre></figure>
1380 ;
1381
1382 var buffer = std.ArrayList(u8).init(test_allocator);
1383 defer buffer.deinit();
1384
1385 try printShell(buffer.writer(), shell_out, false);
1386 try testing.expectEqualSlices(u8, expected, buffer.items);
1387 }
1388 {
1389 const shell_out =
1390 \\$ zig build test.zig
1391 \\build output
1392 \\$ ./test
1393 ;
1394 const expected =
1395 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig</kbd>
1396 \\build output
1397 \\$ <kbd>./test</kbd>
1398 \\</samp></pre></figure>
1399 ;
1400
1401 var buffer = std.ArrayList(u8).init(test_allocator);
1402 defer buffer.deinit();
1403
1404 try printShell(buffer.writer(), shell_out, false);
1405 try testing.expectEqualSlices(u8, expected, buffer.items);
1406 }
1407 {
1408 const shell_out =
1409 \\$ zig build test.zig
1410 \\
1411 \\$ ./test
1412 \\output
1413 ;
1414 const expected =
1415 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig</kbd>
1416 \\
1417 \\$ <kbd>./test</kbd>
1418 \\output
1419 \\</samp></pre></figure>
1420 ;
1421
1422 var buffer = std.ArrayList(u8).init(test_allocator);
1423 defer buffer.deinit();
1424
1425 try printShell(buffer.writer(), shell_out, false);
1426 try testing.expectEqualSlices(u8, expected, buffer.items);
1427 }
1428 {
1429 const shell_out =
1430 \\$ zig build test.zig
1431 \\$ ./test
1432 \\output
1433 ;
1434 const expected =
1435 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig</kbd>
1436 \\$ <kbd>./test</kbd>
1437 \\output
1438 \\</samp></pre></figure>
1439 ;
1440
1441 var buffer = std.ArrayList(u8).init(test_allocator);
1442 defer buffer.deinit();
1443
1444 try printShell(buffer.writer(), shell_out, false);
1445 try testing.expectEqualSlices(u8, expected, buffer.items);
1446 }
1447 {
1448 const shell_out =
1449 \\$ zig build test.zig \
1450 \\ --build-option
1451 \\build output
1452 \\$ ./test
1453 \\output
1454 ;
1455 const expected =
1456 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig \
1457 \\ --build-option</kbd>
1458 \\build output
1459 \\$ <kbd>./test</kbd>
1460 \\output
1461 \\</samp></pre></figure>
1462 ;
1463
1464 var buffer = std.ArrayList(u8).init(test_allocator);
1465 defer buffer.deinit();
1466
1467 try printShell(buffer.writer(), shell_out, false);
1468 try testing.expectEqualSlices(u8, expected, buffer.items);
1469 }
1470 {
1471 // intentional space after "--build-option1 \"
1472 const shell_out =
1473 \\$ zig build test.zig \
1474 \\ --build-option1 \
1475 \\ --build-option2
1476 \\$ ./test
1477 ;
1478 const expected =
1479 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig \
1480 \\ --build-option1 \
1481 \\ --build-option2</kbd>
1482 \\$ <kbd>./test</kbd>
1483 \\</samp></pre></figure>
1484 ;
1485
1486 var buffer = std.ArrayList(u8).init(test_allocator);
1487 defer buffer.deinit();
1488
1489 try printShell(buffer.writer(), shell_out, false);
1490 try testing.expectEqualSlices(u8, expected, buffer.items);
1491 }
1492 {
1493 const shell_out =
1494 \\$ zig build test.zig \
1495 \\$ ./test
1496 ;
1497 const expected =
1498 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig \
1499 \\$ ./test</kbd>
1500 \\</samp></pre></figure>
1501 ;
1502
1503 var buffer = std.ArrayList(u8).init(test_allocator);
1504 defer buffer.deinit();
1505
1506 try printShell(buffer.writer(), shell_out, false);
1507 try testing.expectEqualSlices(u8, expected, buffer.items);
1508 }
1509 {
1510 const shell_out =
1511 \\$ zig build test.zig
1512 \\$ ./test
1513 \\$1
1514 ;
1515 const expected =
1516 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$ <kbd>zig build test.zig</kbd>
1517 \\$ <kbd>./test</kbd>
1518 \\$1
1519 \\</samp></pre></figure>
1520 ;
1521
1522 var buffer = std.ArrayList(u8).init(test_allocator);
1523 defer buffer.deinit();
1524
1525 try printShell(buffer.writer(), shell_out, false);
1526 try testing.expectEqualSlices(u8, expected, buffer.items);
1527 }
1528 {
1529 const shell_out =
1530 \\$zig build test.zig
1531 ;
1532 const expected =
1533 \\<figure><figcaption class="shell-cap">Shell</figcaption><pre><samp>$zig build test.zig
1534 \\</samp></pre></figure>
1535 ;
1536
1537 var buffer = std.ArrayList(u8).init(test_allocator);
1538 defer buffer.deinit();
1539
1540 try printShell(buffer.writer(), shell_out, false);
1541 try testing.expectEqualSlices(u8, expected, buffer.items);
1542 }
1543}
tools/migrate_langref.zig created+456
...@@ -0,0 +1,456 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const io = std.io;
4const fs = std.fs;
5const print = std.debug.print;
6const mem = std.mem;
7const testing = std.testing;
8const Allocator = std.mem.Allocator;
9const max_doc_file_size = 10 * 1024 * 1024;
10const fatal = std.zig.fatal;
11
12pub fn main() !void {
13 var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
14 defer arena_instance.deinit();
15 const arena = arena_instance.allocator();
16
17 const args = try std.process.argsAlloc(arena);
18 const input_file = args[1];
19 const output_file = args[2];
20
21 var in_file = try fs.cwd().openFile(input_file, .{ .mode = .read_only });
22 defer in_file.close();
23
24 var out_file = try fs.cwd().createFile(output_file, .{});
25 defer out_file.close();
26
27 var out_dir = try fs.cwd().openDir(fs.path.dirname(output_file).?, .{});
28 defer out_dir.close();
29
30 const input_file_bytes = try in_file.reader().readAllAlloc(arena, std.math.maxInt(u32));
31
32 var buffered_writer = io.bufferedWriter(out_file.writer());
33
34 var tokenizer = Tokenizer.init(input_file, input_file_bytes);
35
36 try walk(arena, &tokenizer, out_dir, buffered_writer.writer());
37
38 try buffered_writer.flush();
39}
40
41const Token = struct {
42 id: Id,
43 start: usize,
44 end: usize,
45
46 const Id = enum {
47 invalid,
48 content,
49 bracket_open,
50 tag_content,
51 separator,
52 bracket_close,
53 eof,
54 };
55};
56
57const Tokenizer = struct {
58 buffer: []const u8,
59 index: usize,
60 state: State,
61 source_file_name: []const u8,
62
63 const State = enum {
64 start,
65 l_bracket,
66 hash,
67 tag_name,
68 eof,
69 };
70
71 fn init(source_file_name: []const u8, buffer: []const u8) Tokenizer {
72 return Tokenizer{
73 .buffer = buffer,
74 .index = 0,
75 .state = .start,
76 .source_file_name = source_file_name,
77 };
78 }
79
80 fn next(self: *Tokenizer) Token {
81 var result = Token{
82 .id = .eof,
83 .start = self.index,
84 .end = undefined,
85 };
86 while (self.index < self.buffer.len) : (self.index += 1) {
87 const c = self.buffer[self.index];
88 switch (self.state) {
89 .start => switch (c) {
90 '{' => {
91 self.state = .l_bracket;
92 },
93 else => {
94 result.id = .content;
95 },
96 },
97 .l_bracket => switch (c) {
98 '#' => {
99 if (result.id != .eof) {
100 self.index -= 1;
101 self.state = .start;
102 break;
103 } else {
104 result.id = .bracket_open;
105 self.index += 1;
106 self.state = .tag_name;
107 break;
108 }
109 },
110 else => {
111 result.id = .content;
112 self.state = .start;
113 },
114 },
115 .tag_name => switch (c) {
116 '|' => {
117 if (result.id != .eof) {
118 break;
119 } else {
120 result.id = .separator;
121 self.index += 1;
122 break;
123 }
124 },
125 '#' => {
126 self.state = .hash;
127 },
128 else => {
129 result.id = .tag_content;
130 },
131 },
132 .hash => switch (c) {
133 '}' => {
134 if (result.id != .eof) {
135 self.index -= 1;
136 self.state = .tag_name;
137 break;
138 } else {
139 result.id = .bracket_close;
140 self.index += 1;
141 self.state = .start;
142 break;
143 }
144 },
145 else => {
146 result.id = .tag_content;
147 self.state = .tag_name;
148 },
149 },
150 .eof => unreachable,
151 }
152 } else {
153 switch (self.state) {
154 .start, .l_bracket, .eof => {},
155 else => {
156 result.id = .invalid;
157 },
158 }
159 self.state = .eof;
160 }
161 result.end = self.index;
162 return result;
163 }
164
165 const Location = struct {
166 line: usize,
167 column: usize,
168 line_start: usize,
169 line_end: usize,
170 };
171
172 fn getTokenLocation(self: *Tokenizer, token: Token) Location {
173 var loc = Location{
174 .line = 0,
175 .column = 0,
176 .line_start = 0,
177 .line_end = 0,
178 };
179 for (self.buffer, 0..) |c, i| {
180 if (i == token.start) {
181 loc.line_end = i;
182 while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {}
183 return loc;
184 }
185 if (c == '\n') {
186 loc.line += 1;
187 loc.column = 0;
188 loc.line_start = i + 1;
189 } else {
190 loc.column += 1;
191 }
192 }
193 return loc;
194 }
195};
196
197fn parseError(tokenizer: *Tokenizer, token: Token, comptime fmt: []const u8, args: anytype) anyerror {
198 const loc = tokenizer.getTokenLocation(token);
199 const args_prefix = .{ tokenizer.source_file_name, loc.line + 1, loc.column + 1 };
200 print("{s}:{d}:{d}: error: " ++ fmt ++ "\n", args_prefix ++ args);
201 if (loc.line_start <= loc.line_end) {
202 print("{s}\n", .{tokenizer.buffer[loc.line_start..loc.line_end]});
203 {
204 var i: usize = 0;
205 while (i < loc.column) : (i += 1) {
206 print(" ", .{});
207 }
208 }
209 {
210 const caret_count = @min(token.end, loc.line_end) - token.start;
211 var i: usize = 0;
212 while (i < caret_count) : (i += 1) {
213 print("~", .{});
214 }
215 }
216 print("\n", .{});
217 }
218 return error.ParseError;
219}
220
221fn assertToken(tokenizer: *Tokenizer, token: Token, id: Token.Id) !void {
222 if (token.id != id) {
223 return parseError(tokenizer, token, "expected {s}, found {s}", .{ @tagName(id), @tagName(token.id) });
224 }
225}
226
227fn eatToken(tokenizer: *Tokenizer, id: Token.Id) !Token {
228 const token = tokenizer.next();
229 try assertToken(tokenizer, token, id);
230 return token;
231}
232
233const ExpectedOutcome = enum {
234 succeed,
235 fail,
236 build_fail,
237};
238
239const Code = struct {
240 id: Id,
241 name: []const u8,
242 source_token: Token,
243 just_check_syntax: bool,
244 mode: std.builtin.OptimizeMode,
245 link_objects: []const []const u8,
246 target_str: ?[]const u8,
247 link_libc: bool,
248 link_mode: ?std.builtin.LinkMode,
249 disable_cache: bool,
250 verbose_cimport: bool,
251 additional_options: []const []const u8,
252
253 const Id = union(enum) {
254 @"test",
255 test_error: []const u8,
256 test_safety: []const u8,
257 exe: ExpectedOutcome,
258 obj: ?[]const u8,
259 lib,
260 };
261};
262
263fn walk(arena: Allocator, tokenizer: *Tokenizer, out_dir: std.fs.Dir, w: anytype) !void {
264 while (true) {
265 const token = tokenizer.next();
266 switch (token.id) {
267 .eof => break,
268 .content,
269 => {
270 try w.writeAll(tokenizer.buffer[token.start..token.end]);
271 },
272 .bracket_open => {
273 const tag_token = try eatToken(tokenizer, .tag_content);
274 const tag_name = tokenizer.buffer[tag_token.start..tag_token.end];
275
276 if (mem.eql(u8, tag_name, "code_begin")) {
277 _ = try eatToken(tokenizer, .separator);
278 const code_kind_tok = try eatToken(tokenizer, .tag_content);
279 _ = try eatToken(tokenizer, .separator);
280 const name_tok = try eatToken(tokenizer, .tag_content);
281 const name = tokenizer.buffer[name_tok.start..name_tok.end];
282 var error_str: []const u8 = "";
283 const maybe_sep = tokenizer.next();
284 switch (maybe_sep.id) {
285 .separator => {
286 const error_tok = try eatToken(tokenizer, .tag_content);
287 error_str = tokenizer.buffer[error_tok.start..error_tok.end];
288 _ = try eatToken(tokenizer, .bracket_close);
289 },
290 .bracket_close => {},
291 else => return parseError(tokenizer, token, "invalid token", .{}),
292 }
293 const code_kind_str = tokenizer.buffer[code_kind_tok.start..code_kind_tok.end];
294 var code_kind_id: Code.Id = undefined;
295 var just_check_syntax = false;
296 if (mem.eql(u8, code_kind_str, "exe")) {
297 code_kind_id = Code.Id{ .exe = .succeed };
298 } else if (mem.eql(u8, code_kind_str, "exe_err")) {
299 code_kind_id = Code.Id{ .exe = .fail };
300 } else if (mem.eql(u8, code_kind_str, "exe_build_err")) {
301 code_kind_id = Code.Id{ .exe = .build_fail };
302 } else if (mem.eql(u8, code_kind_str, "test")) {
303 code_kind_id = .@"test";
304 } else if (mem.eql(u8, code_kind_str, "test_err")) {
305 code_kind_id = Code.Id{ .test_error = error_str };
306 } else if (mem.eql(u8, code_kind_str, "test_safety")) {
307 code_kind_id = Code.Id{ .test_safety = error_str };
308 } else if (mem.eql(u8, code_kind_str, "obj")) {
309 code_kind_id = Code.Id{ .obj = null };
310 } else if (mem.eql(u8, code_kind_str, "obj_err")) {
311 code_kind_id = Code.Id{ .obj = error_str };
312 } else if (mem.eql(u8, code_kind_str, "lib")) {
313 code_kind_id = Code.Id.lib;
314 } else if (mem.eql(u8, code_kind_str, "syntax")) {
315 code_kind_id = Code.Id{ .obj = null };
316 just_check_syntax = true;
317 } else {
318 return parseError(tokenizer, code_kind_tok, "unrecognized code kind: {s}", .{code_kind_str});
319 }
320
321 var mode: std.builtin.OptimizeMode = .Debug;
322 var link_objects = std.ArrayList([]const u8).init(arena);
323 var target_str: ?[]const u8 = null;
324 var link_libc = false;
325 var link_mode: ?std.builtin.LinkMode = null;
326 var disable_cache = false;
327 var verbose_cimport = false;
328 var additional_options = std.ArrayList([]const u8).init(arena);
329
330 const source_token = while (true) {
331 const content_tok = try eatToken(tokenizer, .content);
332 _ = try eatToken(tokenizer, .bracket_open);
333 const end_code_tag = try eatToken(tokenizer, .tag_content);
334 const end_tag_name = tokenizer.buffer[end_code_tag.start..end_code_tag.end];
335 if (mem.eql(u8, end_tag_name, "code_release_fast")) {
336 mode = .ReleaseFast;
337 } else if (mem.eql(u8, end_tag_name, "code_release_safe")) {
338 mode = .ReleaseSafe;
339 } else if (mem.eql(u8, end_tag_name, "code_disable_cache")) {
340 disable_cache = true;
341 } else if (mem.eql(u8, end_tag_name, "code_verbose_cimport")) {
342 verbose_cimport = true;
343 } else if (mem.eql(u8, end_tag_name, "code_link_object")) {
344 _ = try eatToken(tokenizer, .separator);
345 const obj_tok = try eatToken(tokenizer, .tag_content);
346 try link_objects.append(tokenizer.buffer[obj_tok.start..obj_tok.end]);
347 } else if (mem.eql(u8, end_tag_name, "target_windows")) {
348 target_str = "x86_64-windows";
349 } else if (mem.eql(u8, end_tag_name, "target_linux_x86_64")) {
350 target_str = "x86_64-linux";
351 } else if (mem.eql(u8, end_tag_name, "target_linux_riscv64")) {
352 target_str = "riscv64-linux";
353 } else if (mem.eql(u8, end_tag_name, "target_wasm")) {
354 target_str = "wasm32-freestanding";
355 } else if (mem.eql(u8, end_tag_name, "target_wasi")) {
356 target_str = "wasm32-wasi";
357 } else if (mem.eql(u8, end_tag_name, "link_libc")) {
358 link_libc = true;
359 } else if (mem.eql(u8, end_tag_name, "link_mode_dynamic")) {
360 link_mode = .dynamic;
361 } else if (mem.eql(u8, end_tag_name, "additonal_option")) {
362 _ = try eatToken(tokenizer, .separator);
363 const option = try eatToken(tokenizer, .tag_content);
364 try additional_options.append(tokenizer.buffer[option.start..option.end]);
365 } else if (mem.eql(u8, end_tag_name, "code_end")) {
366 _ = try eatToken(tokenizer, .bracket_close);
367 break content_tok;
368 } else {
369 return parseError(
370 tokenizer,
371 end_code_tag,
372 "invalid token inside code_begin: {s}",
373 .{end_tag_name},
374 );
375 }
376 _ = try eatToken(tokenizer, .bracket_close);
377 } else unreachable; // TODO issue #707
378
379 const basename = try std.fmt.allocPrint(arena, "{s}.zig", .{name});
380
381 var file = out_dir.createFile(basename, .{ .exclusive = true }) catch |err| {
382 fatal("unable to create file '{s}': {s}", .{ name, @errorName(err) });
383 };
384 defer file.close();
385
386 const source = tokenizer.buffer[source_token.start..source_token.end];
387 try file.writeAll(std.mem.trim(u8, source[1..], " \t\r\n"));
388 try file.writeAll("\n\n");
389
390 if (just_check_syntax) {
391 try file.writer().print("// syntax\n", .{});
392 } else switch (code_kind_id) {
393 .@"test" => try file.writer().print("// test\n", .{}),
394 .lib => try file.writer().print("// lib\n", .{}),
395 .test_error => |s| try file.writer().print("// test_error={s}\n", .{s}),
396 .test_safety => |s| try file.writer().print("// test_safety={s}\n", .{s}),
397 .exe => |s| try file.writer().print("// exe={s}\n", .{@tagName(s)}),
398 .obj => |opt| if (opt) |s| {
399 try file.writer().print("// obj={s}\n", .{s});
400 } else {
401 try file.writer().print("// obj\n", .{});
402 },
403 }
404
405 if (mode != .Debug)
406 try file.writer().print("// optimize={s}\n", .{@tagName(mode)});
407
408 for (link_objects.items) |link_object| {
409 try file.writer().print("// link_object={s}\n", .{link_object});
410 }
411
412 if (target_str) |s|
413 try file.writer().print("// target={s}\n", .{s});
414
415 if (link_libc) try file.writer().print("// link_libc\n", .{});
416 if (disable_cache) try file.writer().print("// disable_cache\n", .{});
417 if (verbose_cimport) try file.writer().print("// verbose_cimport\n", .{});
418
419 if (link_mode) |m|
420 try file.writer().print("// link_mode={s}\n", .{@tagName(m)});
421
422 for (additional_options.items) |o| {
423 try file.writer().print("// additional_option={s}\n", .{o});
424 }
425 try w.print("{{#code|{s}#}}\n", .{basename});
426 } else {
427 const close_bracket = while (true) {
428 const next = tokenizer.next();
429 if (next.id == .bracket_close) break next;
430 };
431 try w.writeAll(tokenizer.buffer[token.start..close_bracket.end]);
432 }
433 },
434 else => return parseError(tokenizer, token, "invalid token", .{}),
435 }
436 }
437}
438
439fn urlize(allocator: Allocator, input: []const u8) ![]u8 {
440 var buf = std.ArrayList(u8).init(allocator);
441 defer buf.deinit();
442
443 const out = buf.writer();
444 for (input) |c| {
445 switch (c) {
446 'a'...'z', 'A'...'Z', '_', '-', '0'...'9' => {
447 try out.writeByte(c);
448 },
449 ' ' => {
450 try out.writeByte('-');
451 },
452 else => {},
453 }
454 }
455 return try buf.toOwnedSlice();
456}