authorgravatar for tgschultz@gmail.comtgschultz <tgschultz@gmail.com> 2018-05-11 21:36:02-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2018-05-11 21:36:02-05:00
log8c1872543c8cf76215cc4bf3ced4637bb1065a4e
tree72dfebb643ab61579e3fb8dd58cd4610ffe876fa
parent7186e92c86982950d0aa7c0c2deef9ef96bc1264
parent6e821078f625a03eb8b7794c983da0f7793366ab
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #1 from zig-lang/master

Sync with zig-lang/zig master

143 files changed, 23753 insertions(+), 5164 deletions(-)

.travis.yml+13-9
...@@ -1,18 +1,22 @@...@@ -1,18 +1,22 @@
1sudo: required1sudo: required
2services:2services:
3 - docker3- docker
4os:4os:
5 - linux5- linux
6 - osx6- osx
7dist: trusty7dist: trusty
8osx_image: xcode8.38osx_image: xcode8.3
9language: cpp9language: cpp
10before_install:10before_install:
11 - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_before_install; fi11- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_before_install; fi
12 - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_before_install; fi12- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_before_install; fi
13install:13install:
14 - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_install; fi14- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_install; fi
15 - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_install; fi15- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_install; fi
16script:16script:
17 - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_script; fi17- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_script; fi
18 - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_script; fi18- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_script; fi
19env:
20 global:
21 - secure: QmJ+eLOxj3Irl5SHxt6lQvrj7++1AIz8bYri6RScAQGHQPIztkmbpBjAkpFgYaWPkZ04ROtamFXdS7oHtJHSECesgPoqM/CHIychQkgpDq30+TsFyYbBpDGHY+N6r2WnQTvg+9EuAp6P365us6qFS0D5zQ3P40c56uMbazFu3J4W1HZP+pLWlLjEXaN88ePhHWqNZyvwGMkLpYl3ghcrE9H4vGZQ7jenRW4UmskLEkuhUPJbQiow3Td8arJiRmLVISzWqneqNraLUpGyUVr4F3Rbjzacfoo3r9ZZynhY0mFsEye82x6TMGgH2xsNGkd91zpQuckWUT+pQv/G6FXpnEnjIJSO2Z5WAxXrx6xB1k2HZ17/4NWLF3fJVhdQJm3mS6odeGzUjgGrl1A42evxU+7VbcofEJq1aMiLgU1jUT2pt+pefCwmKJYLpEsSzuyrVxgvskQz0QpC053TAYSNf2Jj6Qhg9YDWyOeemYmDgffTqErF7AYhc6NKH0s0XKkIiNFSxorkEsfG/Ck1o+15slHNmWZXlmXToxDqFkLDoPvfGKg7koU5YTGvci/F9ZKb1juhGLxZbwap/18zN40BqA+Ip2yDBJAKxsIiwSjSIguy6g/Z1I50s0xNGOr36urfRRQX5H+rqr/xCZ63B6WSe6qBcZboWAQMDn8HLS9Xiwc=
22 - secure: dnb7r5guUeMOX9e7XlPUSZzmga8VW3G9Q1aa7LxEKiTjSnWhu5KpPDe8o1X3Rj6nc5iXDqmBH/C/7eNXPDyXJJWPvpE2YRpGymyUkRaakul0QBKJEaMvwy2SuAfS69CWC+TSzfGRvtSYkdpBhhLvs0h5S819S5jYbCNSCmOKfFucaP5NsHNIZ/I19oIeTPTa0/UnVm7DLFZXZjvbS+czkdyH1DhbT85sLj+XqNTzLePImE68efrjaHnlSy/CzBVJzj55UgD5i9fxNCQWzGWim/SD5xZ0zKtLycSOf6wQN2lCo0lkjw9rDlYz69mM5L9ikfYL9oHDPZnh84oXKglQ5miOHCgqs/qs4439I05lIu8i/EfbFA55YG4NyO3rL9YVOOt5gwiwvJYhDcnkVVzSl0o5bsoZgQfYvPWaIQKNkl3C53zfDQjgqS54CeDzlZpFrQTDQ1RrH8oeVC1gfYAeMabMDadox5rfZmLIN5JTf/F8iD/QdxGcoUvkEENcQgfP9PnubExtexgHGsEmqbm6ORSZ1MkEh2m3fo0f8KE6TbN1UigmcQ8nTkWBHsSmfHnB8HwJQp8mwQmDamXA+Hl3e3w4LOdYkJVlNW1/TTyJJOOvjMQCjF8SJmPHuh+QpqKbSaT9XM/vBhxbIZEufH8kawJKCBBcCNspGMNjhXfNjM0=
CMakeLists.txt+43-8
...@@ -5,6 +5,11 @@ if(NOT CMAKE_BUILD_TYPE)...@@ -5,6 +5,11 @@ if(NOT CMAKE_BUILD_TYPE)
5 "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)5 "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
6endif()6endif()
77
8if(NOT CMAKE_INSTALL_PREFIX)
9 set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}" CACHE STRING
10 "Directory to install zig to" FORCE)
11endif()
12
8project(zig C CXX)13project(zig C CXX)
9set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})14set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
1015
...@@ -410,6 +415,9 @@ set(ZIG_CPP_SOURCES...@@ -410,6 +415,9 @@ set(ZIG_CPP_SOURCES
410415
411set(ZIG_STD_FILES416set(ZIG_STD_FILES
412 "array_list.zig"417 "array_list.zig"
418 "atomic/index.zig"
419 "atomic/queue.zig"
420 "atomic/stack.zig"
413 "base64.zig"421 "base64.zig"
414 "buf_map.zig"422 "buf_map.zig"
415 "buf_set.zig"423 "buf_set.zig"
...@@ -419,33 +427,34 @@ set(ZIG_STD_FILES...@@ -419,33 +427,34 @@ set(ZIG_STD_FILES
419 "c/index.zig"427 "c/index.zig"
420 "c/linux.zig"428 "c/linux.zig"
421 "c/windows.zig"429 "c/windows.zig"
430 "crypto/blake2.zig"
431 "crypto/hmac.zig"
422 "crypto/index.zig"432 "crypto/index.zig"
423 "crypto/md5.zig"433 "crypto/md5.zig"
424 "crypto/sha1.zig"434 "crypto/sha1.zig"
425 "crypto/sha2.zig"435 "crypto/sha2.zig"
426 "crypto/sha3.zig"436 "crypto/sha3.zig"
427 "crypto/blake2.zig"
428 "crypto/hmac.zig"
429 "cstr.zig"437 "cstr.zig"
430 "debug/failing_allocator.zig"438 "debug/failing_allocator.zig"
431 "debug/index.zig"439 "debug/index.zig"
432 "dwarf.zig"440 "dwarf.zig"
433 "elf.zig"441 "elf.zig"
434 "empty.zig"442 "empty.zig"
435 "endian.zig"443 "event.zig"
436 "fmt/errol/enum3.zig"444 "fmt/errol/enum3.zig"
437 "fmt/errol/index.zig"445 "fmt/errol/index.zig"
438 "fmt/errol/lookup.zig"446 "fmt/errol/lookup.zig"
439 "fmt/index.zig"447 "fmt/index.zig"
440 "hash_map.zig"
441 "hash/index.zig"
442 "hash/adler.zig"448 "hash/adler.zig"
443 "hash/crc.zig"449 "hash/crc.zig"
444 "hash/fnv.zig"450 "hash/fnv.zig"
451 "hash/index.zig"
445 "hash/siphash.zig"452 "hash/siphash.zig"
453 "hash_map.zig"
446 "heap.zig"454 "heap.zig"
447 "index.zig"455 "index.zig"
448 "io.zig"456 "io.zig"
457 "json.zig"
449 "linked_list.zig"458 "linked_list.zig"
450 "macho.zig"459 "macho.zig"
451 "math/acos.zig"460 "math/acos.zig"
...@@ -457,6 +466,28 @@ set(ZIG_STD_FILES...@@ -457,6 +466,28 @@ set(ZIG_STD_FILES
457 "math/atanh.zig"466 "math/atanh.zig"
458 "math/cbrt.zig"467 "math/cbrt.zig"
459 "math/ceil.zig"468 "math/ceil.zig"
469 "math/complex/abs.zig"
470 "math/complex/acos.zig"
471 "math/complex/acosh.zig"
472 "math/complex/arg.zig"
473 "math/complex/asin.zig"
474 "math/complex/asinh.zig"
475 "math/complex/atan.zig"
476 "math/complex/atanh.zig"
477 "math/complex/conj.zig"
478 "math/complex/cos.zig"
479 "math/complex/cosh.zig"
480 "math/complex/exp.zig"
481 "math/complex/index.zig"
482 "math/complex/ldexp.zig"
483 "math/complex/log.zig"
484 "math/complex/pow.zig"
485 "math/complex/proj.zig"
486 "math/complex/sin.zig"
487 "math/complex/sinh.zig"
488 "math/complex/sqrt.zig"
489 "math/complex/tan.zig"
490 "math/complex/tanh.zig"
460 "math/copysign.zig"491 "math/copysign.zig"
461 "math/cos.zig"492 "math/cos.zig"
462 "math/cosh.zig"493 "math/cosh.zig"
...@@ -493,25 +524,28 @@ set(ZIG_STD_FILES...@@ -493,25 +524,28 @@ set(ZIG_STD_FILES
493 "math/tan.zig"524 "math/tan.zig"
494 "math/tanh.zig"525 "math/tanh.zig"
495 "math/trunc.zig"526 "math/trunc.zig"
496 "math/x86_64/sqrt.zig"
497 "mem.zig"527 "mem.zig"
498 "net.zig"528 "net.zig"
499 "os/child_process.zig"529 "os/child_process.zig"
500 "os/darwin.zig"530 "os/darwin.zig"
501 "os/darwin_errno.zig"531 "os/darwin_errno.zig"
532 "os/epoch.zig"
502 "os/file.zig"533 "os/file.zig"
503 "os/get_user_id.zig"534 "os/get_user_id.zig"
504 "os/index.zig"535 "os/index.zig"
505 "os/linux/errno.zig"536 "os/linux/errno.zig"
506 "os/linux/i386.zig"
507 "os/linux/index.zig"537 "os/linux/index.zig"
538 "os/linux/vdso.zig"
508 "os/linux/x86_64.zig"539 "os/linux/x86_64.zig"
509 "os/path.zig"540 "os/path.zig"
541 "os/time.zig"
510 "os/windows/error.zig"542 "os/windows/error.zig"
511 "os/windows/index.zig"543 "os/windows/index.zig"
512 "os/windows/util.zig"544 "os/windows/util.zig"
513 "os/zen.zig"545 "os/zen.zig"
514 "rand/index.zig"546 "rand/index.zig"
547 "rand/ziggurat.zig"
548 "segmented_list.zig"
515 "sort.zig"549 "sort.zig"
516 "special/bootstrap.zig"550 "special/bootstrap.zig"
517 "special/bootstrap_lib.zig"551 "special/bootstrap_lib.zig"
...@@ -542,7 +576,8 @@ set(ZIG_STD_FILES...@@ -542,7 +576,8 @@ set(ZIG_STD_FILES
542 "unicode.zig"576 "unicode.zig"
543 "zig/ast.zig"577 "zig/ast.zig"
544 "zig/index.zig"578 "zig/index.zig"
545 "zig/parser.zig"579 "zig/parse.zig"
580 "zig/render.zig"
546 "zig/tokenizer.zig"581 "zig/tokenizer.zig"
547)582)
548583
README.md+4-4
...@@ -141,10 +141,10 @@ libc. Create demo games using Zig....@@ -141,10 +141,10 @@ libc. Create demo games using Zig.
141```141```
142mkdir build142mkdir build
143cd build143cd build
144cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd)144cmake ..
145make145make
146make install146make install
147./zig build --build-file ../build.zig test147bin/zig build --build-file ../build.zig test
148```148```
149149
150##### MacOS150##### MacOS
...@@ -154,9 +154,9 @@ brew install cmake llvm@6...@@ -154,9 +154,9 @@ brew install cmake llvm@6
154brew outdated llvm@6 || brew upgrade llvm@6154brew outdated llvm@6 || brew upgrade llvm@6
155mkdir build155mkdir build
156cd build156cd build
157cmake .. -DCMAKE_PREFIX_PATH=/usr/local/opt/llvm@6/ -DCMAKE_INSTALL_PREFIX=$(pwd)157cmake .. -DCMAKE_PREFIX_PATH=/usr/local/opt/llvm@6/
158make install158make install
159./zig build --build-file ../build.zig test159bin/zig build --build-file ../build.zig test
160```160```
161161
162##### Windows162##### Windows
ci/travis_linux_script+1-1
...@@ -19,5 +19,5 @@ if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then...@@ -19,5 +19,5 @@ if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then
19 echo "secret_key = $AWS_SECRET_ACCESS_KEY" >> ~/.s3cfg19 echo "secret_key = $AWS_SECRET_ACCESS_KEY" >> ~/.s3cfg
20 s3cmd put -P $TRAVIS_BUILD_DIR/artifacts/* s3://ziglang.org/builds/20 s3cmd put -P $TRAVIS_BUILD_DIR/artifacts/* s3://ziglang.org/builds/
21 touch empty21 touch empty
22 s3cmd put -P empty s3://ziglang.org/builds/zig-linux-x86_64-$TRAVIS_BRANCH.tar.xz --add-header=x-amz-website-redirect-location:/builds/$(ls $TRAVIS_BUILD_DIR/artifacts)22 s3cmd put -P empty s3://ziglang.org/builds/zig-linux-x86_64-$TRAVIS_BRANCH.tar.xz --add-header="Cache-Control: max-age=0, must-revalidate" --add-header=x-amz-website-redirect-location:/builds/$(ls $TRAVIS_BUILD_DIR/artifacts)
23fi23fi
deps/lld/ELF/MarkLive.cpp+9
...@@ -301,6 +301,15 @@ template <class ELFT> void elf::markLive() {...@@ -301,6 +301,15 @@ template <class ELFT> void elf::markLive() {
301 // Follow the graph to mark all live sections.301 // Follow the graph to mark all live sections.
302 doGcSections<ELFT>();302 doGcSections<ELFT>();
303303
304 // If all references to a DSO happen to be weak, the DSO is removed from
305 // DT_NEEDED, which creates dangling shared symbols to non-existent DSO.
306 // We'll replace such symbols with undefined ones to fix it.
307 for (Symbol *Sym : Symtab->getSymbols())
308 if (auto *S = dyn_cast<SharedSymbol>(Sym))
309 if (S->isWeak() && !S->getFile<ELFT>().IsNeeded)
310 replaceSymbol<Undefined>(S, nullptr, S->getName(), STB_WEAK, S->StOther,
311 S->Type);
312
304 // Report garbage-collected sections.313 // Report garbage-collected sections.
305 if (Config->PrintGcSections)314 if (Config->PrintGcSections)
306 for (InputSectionBase *Sec : InputSections)315 for (InputSectionBase *Sec : InputSections)
doc/docgen.zig+19
...@@ -749,6 +749,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var...@@ -749,6 +749,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
749 try build_args.append("--release-fast");749 try build_args.append("--release-fast");
750 try out.print(" --release-fast");750 try out.print(" --release-fast");
751 },751 },
752 builtin.Mode.ReleaseSmall => {
753 try build_args.append("--release-small");
754 try out.print(" --release-small");
755 },
752 }756 }
753 for (code.link_objects) |link_object| {757 for (code.link_objects) |link_object| {
754 const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", link_object, obj_ext);758 const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", link_object, obj_ext);
...@@ -810,6 +814,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var...@@ -810,6 +814,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
810 try test_args.append("--release-fast");814 try test_args.append("--release-fast");
811 try out.print(" --release-fast");815 try out.print(" --release-fast");
812 },816 },
817 builtin.Mode.ReleaseSmall => {
818 try test_args.append("--release-small");
819 try out.print(" --release-small");
820 },
813 }821 }
814 if (code.target_windows) {822 if (code.target_windows) {
815 try test_args.appendSlice([][]const u8{823 try test_args.appendSlice([][]const u8{
...@@ -840,6 +848,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var...@@ -840,6 +848,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
840 try test_args.append("--release-fast");848 try test_args.append("--release-fast");
841 try out.print(" --release-fast");849 try out.print(" --release-fast");
842 },850 },
851 builtin.Mode.ReleaseSmall => {
852 try test_args.append("--release-small");
853 try out.print(" --release-small");
854 },
843 }855 }
844 const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size);856 const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size);
845 switch (result.term) {857 switch (result.term) {
...@@ -874,6 +886,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var...@@ -874,6 +886,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
874 builtin.Mode.Debug => {},886 builtin.Mode.Debug => {},
875 builtin.Mode.ReleaseSafe => try test_args.append("--release-safe"),887 builtin.Mode.ReleaseSafe => try test_args.append("--release-safe"),
876 builtin.Mode.ReleaseFast => try test_args.append("--release-fast"),888 builtin.Mode.ReleaseFast => try test_args.append("--release-fast"),
889 builtin.Mode.ReleaseSmall => try test_args.append("--release-small"),
877 }890 }
878891
879 const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size);892 const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size);
...@@ -927,6 +940,12 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var...@@ -927,6 +940,12 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
927 try out.print(" --release-fast");940 try out.print(" --release-fast");
928 }941 }
929 },942 },
943 builtin.Mode.ReleaseSmall => {
944 try build_args.append("--release-small");
945 if (!code.is_inline) {
946 try out.print(" --release-small");
947 }
948 },
930 }949 }
931950
932 if (maybe_error_match) |error_match| {951 if (maybe_error_match) |error_match| {
doc/langref.html.in+509-13
...@@ -1947,8 +1947,24 @@ const Foo = extern enum { A, B, C };...@@ -1947,8 +1947,24 @@ const Foo = extern enum { A, B, C };
1947export fn entry(foo: Foo) void { }1947export fn entry(foo: Foo) void { }
1948 {#code_end#}1948 {#code_end#}
1949 {#header_close#}1949 {#header_close#}
1950 <p>TODO packed enum</p>1950 {#header_open|packed enum#}
1951 {#see_also|@memberName|@memberCount|@tagName#}1951 <p>By default, the size of enums is not guaranteed.</p>
1952 <p><code>packed enum</code> causes the size of the enum to be the same as the size of the integer tag type
1953 of the enum:</p>
1954 {#code_begin|test#}
1955const std = @import("std");
1956
1957test "packed enum" {
1958 const Number = packed enum(u8) {
1959 One,
1960 Two,
1961 Three,
1962 };
1963 std.debug.assert(@sizeOf(Number) == @sizeOf(u8));
1964}
1965 {#code_end#}
1966 {#header_close#}
1967 {#see_also|@memberName|@memberCount|@tagName|@sizeOf#}
1952 {#header_close#}1968 {#header_close#}
1953 {#header_open|union#}1969 {#header_open|union#}
1954 {#code_begin|test|union#}1970 {#code_begin|test|union#}
...@@ -2017,7 +2033,27 @@ test "union variant switch" {...@@ -2017,7 +2033,27 @@ test "union variant switch" {
2017 assert(mem.eql(u8, what_is_it, "this is a number"));2033 assert(mem.eql(u8, what_is_it, "this is a number"));
2018}2034}
20192035
2020// TODO union methods2036// Unions can have methods just like structs and enums:
2037
2038const Variant = union(enum) {
2039 Int: i32,
2040 Bool: bool,
2041
2042 fn truthy(self: &const Variant) bool {
2043 return switch (*self) {
2044 Variant.Int => |x_int| x_int != 0,
2045 Variant.Bool => |x_bool| x_bool,
2046 };
2047 }
2048};
2049
2050test "union method" {
2051 var v1 = Variant { .Int = 1 };
2052 var v2 = Variant { .Bool = false };
2053
2054 assert(v1.truthy());
2055 assert(!v2.truthy());
2056}
20212057
20222058
2023const Small = union {2059const Small = union {
...@@ -3844,6 +3880,25 @@ pub fn main() void {...@@ -3844,6 +3880,25 @@ pub fn main() void {
3844 {#header_open|@ArgType#}3880 {#header_open|@ArgType#}
3845 <p>TODO</p>3881 <p>TODO</p>
3846 {#header_close#}3882 {#header_close#}
3883 {#header_open|@atomicLoad#}
3884 <pre><code class="zig">@atomicLoad(comptime T: type, ptr: &amp;const T, comptime ordering: builtin.AtomicOrder) -&gt; T</code></pre>
3885 <p>
3886 This builtin function atomically dereferences a pointer and returns the value.
3887 </p>
3888 <p>
3889 <code>T</code> must be a pointer type, a <code>bool</code>,
3890 or an integer whose bit count meets these requirements:
3891 </p>
3892 <ul>
3893 <li>At least 8</li>
3894 <li>At most the same as usize</li>
3895 <li>Power of 2</li>
3896 </ul>
3897 <p>
3898 TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe
3899 we can remove this restriction
3900 </p>
3901 {#header_close#}
3847 {#header_open|@atomicRmw#}3902 {#header_open|@atomicRmw#}
3848 <pre><code class="zig">@atomicRmw(comptime T: type, ptr: &amp;T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) -&gt; T</code></pre>3903 <pre><code class="zig">@atomicRmw(comptime T: type, ptr: &amp;T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) -&gt; T</code></pre>
3849 <p>3904 <p>
...@@ -4010,16 +4065,60 @@ comptime {...@@ -4010,16 +4065,60 @@ comptime {
4010 </p>4065 </p>
40114066
4012 {#header_close#}4067 {#header_close#}
4013 {#header_open|@cmpxchg#}4068 {#header_open|@cmpxchgStrong#}
4014 <pre><code class="zig">@cmpxchg(ptr: &T, cmp: T, new: T, success_order: AtomicOrder, fail_order: AtomicOrder) -&gt; bool</code></pre>4069 <pre><code class="zig">@cmpxchgStrong(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -&gt; ?T</code></pre>
4070 <p>
4071 This function performs a strong atomic compare exchange operation. It's the equivalent of this code,
4072 except atomic:
4073 </p>
4074 {#code_begin|syntax#}
4075fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T {
4076 const old_value = *ptr;
4077 if (old_value == expected_value) {
4078 *ptr = new_value;
4079 return null;
4080 } else {
4081 return old_value;
4082 }
4083}
4084 {#code_end#}
4015 <p>4085 <p>
4016 This function performs an atomic compare exchange operation.4086 If you are using cmpxchg in a loop, {#link|@cmpxchgWeak#} is the better choice, because it can be implemented
4087 more efficiently in machine instructions.
4017 </p>4088 </p>
4018 <p>4089 <p>
4019 <code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>.4090 <code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>.
4020 </p>4091 </p>
4021 <p><code>@typeOf(ptr).alignment</code> must be <code>&gt;= @sizeOf(T).</code></p>4092 <p><code>@typeOf(ptr).alignment</code> must be <code>&gt;= @sizeOf(T).</code></p>
4022 {#see_also|Compile Variables#}4093 {#see_also|Compile Variables|cmpxchgWeak#}
4094 {#header_close#}
4095 {#header_open|@cmpxchgWeak#}
4096 <pre><code class="zig">@cmpxchgWeak(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -&gt; ?T</code></pre>
4097 <p>
4098 This function performs a weak atomic compare exchange operation. It's the equivalent of this code,
4099 except atomic:
4100 </p>
4101 {#code_begin|syntax#}
4102fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T {
4103 const old_value = *ptr;
4104 if (old_value == expected_value and usuallyTrueButSometimesFalse()) {
4105 *ptr = new_value;
4106 return null;
4107 } else {
4108 return old_value;
4109 }
4110}
4111 {#code_end#}
4112 <p>
4113 If you are using cmpxchg in a loop, the sporadic failure will be no problem, and <code>cmpxchgWeak</code>
4114 is the better choice, because it can be implemented more efficiently in machine instructions.
4115 However if you need a stronger guarantee, use {#link|@cmpxchgStrong#}.
4116 </p>
4117 <p>
4118 <code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>.
4119 </p>
4120 <p><code>@typeOf(ptr).alignment</code> must be <code>&gt;= @sizeOf(T).</code></p>
4121 {#see_also|Compile Variables|cmpxchgStrong#}
4023 {#header_close#}4122 {#header_close#}
4024 {#header_open|@compileError#}4123 {#header_open|@compileError#}
4025 <pre><code class="zig">@compileError(comptime msg: []u8)</code></pre>4124 <pre><code class="zig">@compileError(comptime msg: []u8)</code></pre>
...@@ -4313,6 +4412,10 @@ fn add(a: i32, b: i32) i32 { return a + b; }...@@ -4313,6 +4412,10 @@ fn add(a: i32, b: i32) i32 { return a + b; }
4313 It does not include functions, variables, or constants.4412 It does not include functions, variables, or constants.
4314 </p>4413 </p>
4315 {#header_close#}4414 {#header_close#}
4415 {#header_open|@field#}
4416 <pre><code class="zig">@field(lhs: var, comptime field_name: []const u8) -&gt; (field)</code></pre>
4417 <p>Preforms field access equivalent to <code>lhs.-&gtfield_name-&lt</code>.</p>
4418 {#header_close#}
4316 {#header_open|@memberType#}4419 {#header_open|@memberType#}
4317 <pre><code class="zig">@memberType(comptime T: type, comptime index: usize) -&gt; type</code></pre>4420 <pre><code class="zig">@memberType(comptime T: type, comptime index: usize) -&gt; type</code></pre>
4318 <p>Returns the field type of a struct or union.</p>4421 <p>Returns the field type of a struct or union.</p>
...@@ -4633,6 +4736,16 @@ pub const FloatMode = enum {...@@ -4633,6 +4736,16 @@ pub const FloatMode = enum {
4633 The result is a target-specific compile time constant.4736 The result is a target-specific compile time constant.
4634 </p>4737 </p>
4635 {#header_close#}4738 {#header_close#}
4739 {#header_open|@sqrt#}
4740 <pre><code class="zig">@sqrt(comptime T: type, value: T) -&gt; T</code></pre>
4741 <p>
4742 Performs the square root of a floating point number. Uses a dedicated hardware instruction
4743 when available. Currently only supports f32 and f64 at runtime. f128 at runtime is TODO.
4744 </p>
4745 <p>
4746 This is a low-level intrinsic. Most code can use <code>std.math.sqrt</code> instead.
4747 </p>
4748 {#header_close#}
4636 {#header_open|@subWithOverflow#}4749 {#header_open|@subWithOverflow#}
4637 <pre><code class="zig">@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -&gt; bool</code></pre>4750 <pre><code class="zig">@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -&gt; bool</code></pre>
4638 <p>4751 <p>
...@@ -4696,6 +4809,182 @@ pub const TypeId = enum {...@@ -4696,6 +4809,182 @@ pub const TypeId = enum {
4696 BoundFn,4809 BoundFn,
4697 ArgTuple,4810 ArgTuple,
4698 Opaque,4811 Opaque,
4812};
4813 {#code_end#}
4814 {#header_close#}
4815 {#header_open|@typeInfo#}
4816 <pre><code class="zig">@typeInfo(comptime T: type) -&gt; @import("builtin").TypeInfo</code></pre>
4817 <p>
4818 Returns information on the type. Returns a value of the following union:
4819 </p>
4820 {#code_begin|syntax#}
4821pub const TypeInfo = union(TypeId) {
4822 Type: void,
4823 Void: void,
4824 Bool: void,
4825 NoReturn: void,
4826 Int: Int,
4827 Float: Float,
4828 Pointer: Pointer,
4829 Array: Array,
4830 Struct: Struct,
4831 FloatLiteral: void,
4832 IntLiteral: void,
4833 UndefinedLiteral: void,
4834 NullLiteral: void,
4835 Nullable: Nullable,
4836 ErrorUnion: ErrorUnion,
4837 ErrorSet: ErrorSet,
4838 Enum: Enum,
4839 Union: Union,
4840 Fn: Fn,
4841 Namespace: void,
4842 Block: void,
4843 BoundFn: Fn,
4844 ArgTuple: void,
4845 Opaque: void,
4846 Promise: Promise,
4847
4848
4849 pub const Int = struct {
4850 is_signed: bool,
4851 bits: u8,
4852 };
4853
4854 pub const Float = struct {
4855 bits: u8,
4856 };
4857
4858 pub const Pointer = struct {
4859 is_const: bool,
4860 is_volatile: bool,
4861 alignment: u32,
4862 child: type,
4863 };
4864
4865 pub const Array = struct {
4866 len: usize,
4867 child: type,
4868 };
4869
4870 pub const ContainerLayout = enum {
4871 Auto,
4872 Extern,
4873 Packed,
4874 };
4875
4876 pub const StructField = struct {
4877 name: []const u8,
4878 offset: ?usize,
4879 field_type: type,
4880 };
4881
4882 pub const Struct = struct {
4883 layout: ContainerLayout,
4884 fields: []StructField,
4885 defs: []Definition,
4886 };
4887
4888 pub const Nullable = struct {
4889 child: type,
4890 };
4891
4892 pub const ErrorUnion = struct {
4893 error_set: type,
4894 payload: type,
4895 };
4896
4897 pub const Error = struct {
4898 name: []const u8,
4899 value: usize,
4900 };
4901
4902 pub const ErrorSet = struct {
4903 errors: []Error,
4904 };
4905
4906 pub const EnumField = struct {
4907 name: []const u8,
4908 value: usize,
4909 };
4910
4911 pub const Enum = struct {
4912 layout: ContainerLayout,
4913 tag_type: type,
4914 fields: []EnumField,
4915 defs: []Definition,
4916 };
4917
4918 pub const UnionField = struct {
4919 name: []const u8,
4920 enum_field: ?EnumField,
4921 field_type: type,
4922 };
4923
4924 pub const Union = struct {
4925 layout: ContainerLayout,
4926 tag_type: type,
4927 fields: []UnionField,
4928 defs: []Definition,
4929 };
4930
4931 pub const CallingConvention = enum {
4932 Unspecified,
4933 C,
4934 Cold,
4935 Naked,
4936 Stdcall,
4937 Async,
4938 };
4939
4940 pub const FnArg = struct {
4941 is_generic: bool,
4942 is_noalias: bool,
4943 arg_type: type,
4944 };
4945
4946 pub const Fn = struct {
4947 calling_convention: CallingConvention,
4948 is_generic: bool,
4949 is_var_args: bool,
4950 return_type: type,
4951 async_allocator_type: type,
4952 args: []FnArg,
4953 };
4954
4955 pub const Promise = struct {
4956 child: type,
4957 };
4958
4959 pub const Definition = struct {
4960 name: []const u8,
4961 is_pub: bool,
4962 data: Data,
4963
4964 pub const Data = union(enum) {
4965 Type: type,
4966 Var: type,
4967 Fn: FnDef,
4968
4969 pub const FnDef = struct {
4970 fn_type: type,
4971 inline_type: Inline,
4972 calling_convention: CallingConvention,
4973 is_var_args: bool,
4974 is_extern: bool,
4975 is_export: bool,
4976 lib_name: ?[]const u8,
4977 return_type: type,
4978 arg_names: [][] const u8,
4979
4980 pub const Inline = enum {
4981 Auto,
4982 Always,
4983 Never,
4984 };
4985 };
4986 };
4987 };
4699};4988};
4700 {#code_end#}4989 {#code_end#}
4701 {#header_close#}4990 {#header_close#}
...@@ -5113,7 +5402,6 @@ pub const Os = enum {...@@ -5113,7 +5402,6 @@ pub const Os = enum {
5113 rtems,5402 rtems,
5114 nacl,5403 nacl,
5115 cnk,5404 cnk,
5116 bitrig,
5117 aix,5405 aix,
5118 cuda,5406 cuda,
5119 nvcl,5407 nvcl,
...@@ -5124,10 +5412,12 @@ pub const Os = enum {...@@ -5124,10 +5412,12 @@ pub const Os = enum {
5124 watchos,5412 watchos,
5125 mesa3d,5413 mesa3d,
5126 contiki,5414 contiki,
5415 amdpal,
5127 zen,5416 zen,
5128};5417};
51295418
5130pub const Arch = enum {5419pub const Arch = enum {
5420 armv8_3a,
5131 armv8_2a,5421 armv8_2a,
5132 armv8_1a,5422 armv8_1a,
5133 armv8,5423 armv8,
...@@ -5147,9 +5437,29 @@ pub const Arch = enum {...@@ -5147,9 +5437,29 @@ pub const Arch = enum {
5147 armv5,5437 armv5,
5148 armv5te,5438 armv5te,
5149 armv4t,5439 armv4t,
5150 armeb,5440 armebv8_3a,
5441 armebv8_2a,
5442 armebv8_1a,
5443 armebv8,
5444 armebv8r,
5445 armebv8m_baseline,
5446 armebv8m_mainline,
5447 armebv7,
5448 armebv7em,
5449 armebv7m,
5450 armebv7s,
5451 armebv7k,
5452 armebv7ve,
5453 armebv6,
5454 armebv6m,
5455 armebv6k,
5456 armebv6t2,
5457 armebv5,
5458 armebv5te,
5459 armebv4t,
5151 aarch64,5460 aarch64,
5152 aarch64_be,5461 aarch64_be,
5462 arc,
5153 avr,5463 avr,
5154 bpfel,5464 bpfel,
5155 bpfeb,5465 bpfeb,
...@@ -5202,6 +5512,7 @@ pub const Arch = enum {...@@ -5202,6 +5512,7 @@ pub const Arch = enum {
5202pub const Environ = enum {5512pub const Environ = enum {
5203 unknown,5513 unknown,
5204 gnu,5514 gnu,
5515 gnuabin32,
5205 gnuabi64,5516 gnuabi64,
5206 gnueabi,5517 gnueabi,
5207 gnueabihf,5518 gnueabihf,
...@@ -5219,6 +5530,7 @@ pub const Environ = enum {...@@ -5219,6 +5530,7 @@ pub const Environ = enum {
5219 amdopencl,5530 amdopencl,
5220 coreclr,5531 coreclr,
5221 opencl,5532 opencl,
5533 simulator,
5222};5534};
52235535
5224pub const ObjectFormat = enum {5536pub const ObjectFormat = enum {
...@@ -5245,10 +5557,23 @@ pub const AtomicOrder = enum {...@@ -5245,10 +5557,23 @@ pub const AtomicOrder = enum {
5245 SeqCst,5557 SeqCst,
5246};5558};
52475559
5560pub const AtomicRmwOp = enum {
5561 Xchg,
5562 Add,
5563 Sub,
5564 And,
5565 Nand,
5566 Or,
5567 Xor,
5568 Max,
5569 Min,
5570};
5571
5248pub const Mode = enum {5572pub const Mode = enum {
5249 Debug,5573 Debug,
5250 ReleaseSafe,5574 ReleaseSafe,
5251 ReleaseFast,5575 ReleaseFast,
5576 ReleaseSmall,
5252};5577};
52535578
5254pub const TypeId = enum {5579pub const TypeId = enum {
...@@ -5267,7 +5592,7 @@ pub const TypeId = enum {...@@ -5267,7 +5592,7 @@ pub const TypeId = enum {
5267 NullLiteral,5592 NullLiteral,
5268 Nullable,5593 Nullable,
5269 ErrorUnion,5594 ErrorUnion,
5270 Error,5595 ErrorSet,
5271 Enum,5596 Enum,
5272 Union,5597 Union,
5273 Fn,5598 Fn,
...@@ -5276,6 +5601,176 @@ pub const TypeId = enum {...@@ -5276,6 +5601,176 @@ pub const TypeId = enum {
5276 BoundFn,5601 BoundFn,
5277 ArgTuple,5602 ArgTuple,
5278 Opaque,5603 Opaque,
5604 Promise,
5605};
5606
5607pub const TypeInfo = union(TypeId) {
5608 Type: void,
5609 Void: void,
5610 Bool: void,
5611 NoReturn: void,
5612 Int: Int,
5613 Float: Float,
5614 Pointer: Pointer,
5615 Array: Array,
5616 Struct: Struct,
5617 FloatLiteral: void,
5618 IntLiteral: void,
5619 UndefinedLiteral: void,
5620 NullLiteral: void,
5621 Nullable: Nullable,
5622 ErrorUnion: ErrorUnion,
5623 ErrorSet: ErrorSet,
5624 Enum: Enum,
5625 Union: Union,
5626 Fn: Fn,
5627 Namespace: void,
5628 Block: void,
5629 BoundFn: Fn,
5630 ArgTuple: void,
5631 Opaque: void,
5632 Promise: Promise,
5633
5634
5635 pub const Int = struct {
5636 is_signed: bool,
5637 bits: u8,
5638 };
5639
5640 pub const Float = struct {
5641 bits: u8,
5642 };
5643
5644 pub const Pointer = struct {
5645 is_const: bool,
5646 is_volatile: bool,
5647 alignment: u32,
5648 child: type,
5649 };
5650
5651 pub const Array = struct {
5652 len: usize,
5653 child: type,
5654 };
5655
5656 pub const ContainerLayout = enum {
5657 Auto,
5658 Extern,
5659 Packed,
5660 };
5661
5662 pub const StructField = struct {
5663 name: []const u8,
5664 offset: ?usize,
5665 field_type: type,
5666 };
5667
5668 pub const Struct = struct {
5669 layout: ContainerLayout,
5670 fields: []StructField,
5671 defs: []Definition,
5672 };
5673
5674 pub const Nullable = struct {
5675 child: type,
5676 };
5677
5678 pub const ErrorUnion = struct {
5679 error_set: type,
5680 payload: type,
5681 };
5682
5683 pub const Error = struct {
5684 name: []const u8,
5685 value: usize,
5686 };
5687
5688 pub const ErrorSet = struct {
5689 errors: []Error,
5690 };
5691
5692 pub const EnumField = struct {
5693 name: []const u8,
5694 value: usize,
5695 };
5696
5697 pub const Enum = struct {
5698 layout: ContainerLayout,
5699 tag_type: type,
5700 fields: []EnumField,
5701 defs: []Definition,
5702 };
5703
5704 pub const UnionField = struct {
5705 name: []const u8,
5706 enum_field: ?EnumField,
5707 field_type: type,
5708 };
5709
5710 pub const Union = struct {
5711 layout: ContainerLayout,
5712 tag_type: type,
5713 fields: []UnionField,
5714 defs: []Definition,
5715 };
5716
5717 pub const CallingConvention = enum {
5718 Unspecified,
5719 C,
5720 Cold,
5721 Naked,
5722 Stdcall,
5723 Async,
5724 };
5725
5726 pub const FnArg = struct {
5727 is_generic: bool,
5728 is_noalias: bool,
5729 arg_type: type,
5730 };
5731
5732 pub const Fn = struct {
5733 calling_convention: CallingConvention,
5734 is_generic: bool,
5735 is_var_args: bool,
5736 return_type: type,
5737 async_allocator_type: type,
5738 args: []FnArg,
5739 };
5740
5741 pub const Promise = struct {
5742 child: type,
5743 };
5744
5745 pub const Definition = struct {
5746 name: []const u8,
5747 is_pub: bool,
5748 data: Data,
5749
5750 pub const Data = union(enum) {
5751 Type: type,
5752 Var: type,
5753 Fn: FnDef,
5754
5755 pub const FnDef = struct {
5756 fn_type: type,
5757 inline_type: Inline,
5758 calling_convention: CallingConvention,
5759 is_var_args: bool,
5760 is_extern: bool,
5761 is_export: bool,
5762 lib_name: ?[]const u8,
5763 return_type: type,
5764 arg_names: [][] const u8,
5765
5766 pub const Inline = enum {
5767 Auto,
5768 Always,
5769 Never,
5770 };
5771 };
5772 };
5773 };
5279};5774};
52805775
5281pub const FloatMode = enum {5776pub const FloatMode = enum {
...@@ -5289,7 +5784,7 @@ pub const Endian = enum {...@@ -5289,7 +5784,7 @@ pub const Endian = enum {
5289};5784};
52905785
5291pub const endian = Endian.Little;5786pub const endian = Endian.Little;
5292pub const is_test = false;5787pub const is_test = true;
5293pub const os = Os.linux;5788pub const os = Os.linux;
5294pub const arch = Arch.x86_64;5789pub const arch = Arch.x86_64;
5295pub const environ = Environ.gnu;5790pub const environ = Environ.gnu;
...@@ -5297,6 +5792,7 @@ pub const object_format = ObjectFormat.elf;...@@ -5297,6 +5792,7 @@ pub const object_format = ObjectFormat.elf;
5297pub const mode = Mode.Debug;5792pub const mode = Mode.Debug;
5298pub const link_libc = false;5793pub const link_libc = false;
5299pub const have_error_return_tracing = true;5794pub const have_error_return_tracing = true;
5795pub const __zig_test_fn_slice = {}; // overwritten later
5300 {#code_end#}5796 {#code_end#}
5301 {#see_also|Build Mode#}5797 {#see_also|Build Mode#}
5302 {#header_close#}5798 {#header_close#}
...@@ -5809,7 +6305,7 @@ Defer(body) = ("defer" | "deferror") body...@@ -5809,7 +6305,7 @@ Defer(body) = ("defer" | "deferror") body
58096305
5810IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))6306IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
58116307
5812SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))6308SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))
58136309
5814IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)6310IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)
58156311
...@@ -5955,7 +6451,7 @@ hljs.registerLanguage("zig", function(t) {...@@ -5955,7 +6451,7 @@ hljs.registerLanguage("zig", function(t) {
5955 a = t.IR + "\\s*\\(",6451 a = t.IR + "\\s*\\(",
5956 c = {6452 c = {
5957 keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",6453 keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",
5958 built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw",6454 built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt field typeInfo",
5959 literal: "true false null undefined"6455 literal: "true false null undefined"
5960 },6456 },
5961 n = [e, t.CLCM, t.CBCM, s, r];6457 n = [e, t.CLCM, t.CBCM, s, r];
src-self-hosted/arg.zig created+284
...@@ -0,0 +1,284 @@
1const std = @import("std");
2const debug = std.debug;
3const mem = std.mem;
4
5const Allocator = mem.Allocator;
6const ArrayList = std.ArrayList;
7const HashMap = std.HashMap;
8
9fn trimStart(slice: []const u8, ch: u8) []const u8 {
10 var i: usize = 0;
11 for (slice) |b| {
12 if (b != '-') break;
13 i += 1;
14 }
15
16 return slice[i..];
17}
18
19fn argInAllowedSet(maybe_set: ?[]const []const u8, arg: []const u8) bool {
20 if (maybe_set) |set| {
21 for (set) |possible| {
22 if (mem.eql(u8, arg, possible)) {
23 return true;
24 }
25 }
26 return false;
27 } else {
28 return true;
29 }
30}
31
32// Modifies the current argument index during iteration
33fn readFlagArguments(allocator: &Allocator, args: []const []const u8, required: usize,
34 allowed_set: ?[]const []const u8, index: &usize) !FlagArg {
35
36 switch (required) {
37 0 => return FlagArg { .None = undefined }, // TODO: Required to force non-tag but value?
38 1 => {
39 if (*index + 1 >= args.len) {
40 return error.MissingFlagArguments;
41 }
42
43 *index += 1;
44 const arg = args[*index];
45
46 if (!argInAllowedSet(allowed_set, arg)) {
47 return error.ArgumentNotInAllowedSet;
48 }
49
50 return FlagArg { .Single = arg };
51 },
52 else => |needed| {
53 var extra = ArrayList([]const u8).init(allocator);
54 errdefer extra.deinit();
55
56 var j: usize = 0;
57 while (j < needed) : (j += 1) {
58 if (*index + 1 >= args.len) {
59 return error.MissingFlagArguments;
60 }
61
62 *index += 1;
63 const arg = args[*index];
64
65 if (!argInAllowedSet(allowed_set, arg)) {
66 return error.ArgumentNotInAllowedSet;
67 }
68
69 try extra.append(arg);
70 }
71
72 return FlagArg { .Many = extra };
73 },
74 }
75}
76
77const HashMapFlags = HashMap([]const u8, FlagArg, std.hash.Fnv1a_32.hash, mem.eql_slice_u8);
78
79// A store for querying found flags and positional arguments.
80pub const Args = struct {
81 flags: HashMapFlags,
82 positionals: ArrayList([]const u8),
83
84 pub fn parse(allocator: &Allocator, comptime spec: []const Flag, args: []const []const u8) !Args {
85 var parsed = Args {
86 .flags = HashMapFlags.init(allocator),
87 .positionals = ArrayList([]const u8).init(allocator),
88 };
89
90 var i: usize = 0;
91 next: while (i < args.len) : (i += 1) {
92 const arg = args[i];
93
94 if (arg.len != 0 and arg[0] == '-') {
95 // TODO: hashmap, although the linear scan is okay for small argument sets as is
96 for (spec) |flag| {
97 if (mem.eql(u8, arg, flag.name)) {
98 const flag_name_trimmed = trimStart(flag.name, '-');
99 const flag_args = readFlagArguments(allocator, args, flag.required, flag.allowed_set, &i) catch |err| {
100 switch (err) {
101 error.ArgumentNotInAllowedSet => {
102 std.debug.warn("argument '{}' is invalid for flag '{}'\n", args[i], arg);
103 std.debug.warn("allowed options are ");
104 for (??flag.allowed_set) |possible| {
105 std.debug.warn("'{}' ", possible);
106 }
107 std.debug.warn("\n");
108 },
109 error.MissingFlagArguments => {
110 std.debug.warn("missing argument for flag: {}\n", arg);
111 },
112 else => {},
113 }
114
115 return err;
116 };
117
118 if (flag.mergable) {
119 var prev =
120 if (parsed.flags.get(flag_name_trimmed)) |entry|
121 entry.value.Many
122 else
123 ArrayList([]const u8).init(allocator);
124
125 // MergeN creation disallows 0 length flag entry (doesn't make sense)
126 switch (flag_args) {
127 FlagArg.None => unreachable,
128 FlagArg.Single => |inner| try prev.append(inner),
129 FlagArg.Many => |inner| try prev.appendSlice(inner.toSliceConst()),
130 }
131
132 _ = try parsed.flags.put(flag_name_trimmed, FlagArg { .Many = prev });
133 } else {
134 _ = try parsed.flags.put(flag_name_trimmed, flag_args);
135 }
136
137 continue :next;
138 }
139 }
140
141 // TODO: Better errors with context, global error state and return is sufficient.
142 std.debug.warn("could not match flag: {}\n", arg);
143 return error.UnknownFlag;
144 } else {
145 try parsed.positionals.append(arg);
146 }
147 }
148
149 return parsed;
150 }
151
152 pub fn deinit(self: &Args) void {
153 self.flags.deinit();
154 self.positionals.deinit();
155 }
156
157 // e.g. --help
158 pub fn present(self: &Args, name: []const u8) bool {
159 return self.flags.contains(name);
160 }
161
162 // e.g. --name value
163 pub fn single(self: &Args, name: []const u8) ?[]const u8 {
164 if (self.flags.get(name)) |entry| {
165 switch (entry.value) {
166 FlagArg.Single => |inner| { return inner; },
167 else => @panic("attempted to retrieve flag with wrong type"),
168 }
169 } else {
170 return null;
171 }
172 }
173
174 // e.g. --names value1 value2 value3
175 pub fn many(self: &Args, name: []const u8) ?[]const []const u8 {
176 if (self.flags.get(name)) |entry| {
177 switch (entry.value) {
178 FlagArg.Many => |inner| { return inner.toSliceConst(); },
179 else => @panic("attempted to retrieve flag with wrong type"),
180 }
181 } else {
182 return null;
183 }
184 }
185};
186
187// Arguments for a flag. e.g. arg1, arg2 in `--command arg1 arg2`.
188const FlagArg = union(enum) {
189 None,
190 Single: []const u8,
191 Many: ArrayList([]const u8),
192};
193
194// Specification for how a flag should be parsed.
195pub const Flag = struct {
196 name: []const u8,
197 required: usize,
198 mergable: bool,
199 allowed_set: ?[]const []const u8,
200
201 pub fn Bool(comptime name: []const u8) Flag {
202 return ArgN(name, 0);
203 }
204
205 pub fn Arg1(comptime name: []const u8) Flag {
206 return ArgN(name, 1);
207 }
208
209 pub fn ArgN(comptime name: []const u8, comptime n: usize) Flag {
210 return Flag {
211 .name = name,
212 .required = n,
213 .mergable = false,
214 .allowed_set = null,
215 };
216 }
217
218 pub fn ArgMergeN(comptime name: []const u8, comptime n: usize) Flag {
219 if (n == 0) {
220 @compileError("n must be greater than 0");
221 }
222
223 return Flag {
224 .name = name,
225 .required = n,
226 .mergable = true,
227 .allowed_set = null,
228 };
229 }
230
231 pub fn Option(comptime name: []const u8, comptime set: []const []const u8) Flag {
232 return Flag {
233 .name = name,
234 .required = 1,
235 .mergable = false,
236 .allowed_set = set,
237 };
238 }
239};
240
241test "parse arguments" {
242 const spec1 = comptime []const Flag {
243 Flag.Bool("--help"),
244 Flag.Bool("--init"),
245 Flag.Arg1("--build-file"),
246 Flag.Option("--color", []const []const u8 { "on", "off", "auto" }),
247 Flag.ArgN("--pkg-begin", 2),
248 Flag.ArgMergeN("--object", 1),
249 Flag.ArgN("--library", 1),
250 };
251
252 const cliargs = []const []const u8 {
253 "build",
254 "--help",
255 "pos1",
256 "--build-file", "build.zig",
257 "--object", "obj1",
258 "--object", "obj2",
259 "--library", "lib1",
260 "--library", "lib2",
261 "--color", "on",
262 "pos2",
263 };
264
265 var args = try Args.parse(std.debug.global_allocator, spec1, cliargs);
266
267 debug.assert(args.present("help"));
268 debug.assert(!args.present("help2"));
269 debug.assert(!args.present("init"));
270
271 debug.assert(mem.eql(u8, ??args.single("build-file"), "build.zig"));
272 debug.assert(mem.eql(u8, ??args.single("color"), "on"));
273
274 const objects = ??args.many("object");
275 debug.assert(mem.eql(u8, objects[0], "obj1"));
276 debug.assert(mem.eql(u8, objects[1], "obj2"));
277
278 debug.assert(mem.eql(u8, ??args.single("library"), "lib2"));
279
280 const pos = args.positionals.toSliceConst();
281 debug.assert(mem.eql(u8, pos[0], "build"));
282 debug.assert(mem.eql(u8, pos[1], "pos1"));
283 debug.assert(mem.eql(u8, pos[2], "pos2"));
284}
src-self-hosted/introspect.zig created+57
...@@ -0,0 +1,57 @@
1// Introspection and determination of system libraries needed by zig.
2
3const std = @import("std");
4const mem = std.mem;
5const os = std.os;
6
7const warn = std.debug.warn;
8
9/// Caller must free result
10pub fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {
11 const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");
12 errdefer allocator.free(test_zig_dir);
13
14 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
15 defer allocator.free(test_index_file);
16
17 var file = try os.File.openRead(allocator, test_index_file);
18 file.close();
19
20 return test_zig_dir;
21}
22
23/// Caller must free result
24pub fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {
25 const self_exe_path = try os.selfExeDirPath(allocator);
26 defer allocator.free(self_exe_path);
27
28 var cur_path: []const u8 = self_exe_path;
29 while (true) {
30 const test_dir = os.path.dirname(cur_path);
31
32 if (mem.eql(u8, test_dir, cur_path)) {
33 break;
34 }
35
36 return testZigInstallPrefix(allocator, test_dir) catch |err| {
37 cur_path = test_dir;
38 continue;
39 };
40 }
41
42 return error.FileNotFound;
43}
44
45pub fn resolveZigLibDir(allocator: &mem.Allocator) ![]u8 {
46 return findZigLibDir(allocator) catch |err| {
47 warn(
48 \\Unable to find zig lib directory: {}.
49 \\Reinstall Zig or use --zig-install-prefix.
50 \\
51 ,
52 @errorName(err)
53 );
54
55 return error.ZigLibDirNotFound;
56 };
57}
src-self-hosted/main.zig+895-701
...@@ -1,613 +1,165 @@...@@ -1,613 +1,165 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;
3const io = std.io;
4const os = std.os;
5const heap = std.heap;
6const warn = std.debug.warn;
7const assert = std.debug.assert;
8const target = @import("target.zig");
9const Target = target.Target;
10const Module = @import("module.zig").Module;
11const ErrColor = Module.ErrColor;
12const Emit = Module.Emit;
13const builtin = @import("builtin");2const builtin = @import("builtin");
14const ArrayList = std.ArrayList;
15const c = @import("c.zig");
163
17const default_zig_cache_name = "zig-cache";4const os = std.os;
5const io = std.io;
6const mem = std.mem;
7const Allocator = mem.Allocator;
8const ArrayList = std.ArrayList;
9const Buffer = std.Buffer;
1810
19const Cmd = enum {11const arg = @import("arg.zig");
20 None,12const c = @import("c.zig");
21 Build,13const introspect = @import("introspect.zig");
22 Test,14const Args = arg.Args;
23 Version,15const Flag = arg.Flag;
24 Zen,16const Module = @import("module.zig").Module;
25 TranslateC,17const Target = @import("target.zig").Target;
26 Targets,18
19var stderr: &io.OutStream(io.FileOutStream.Error) = undefined;
20var stdout: &io.OutStream(io.FileOutStream.Error) = undefined;
21
22const usage =
23 \\usage: zig [command] [options]
24 \\
25 \\Commands:
26 \\
27 \\ build Build project from build.zig
28 \\ build-exe [source] Create executable from source or object files
29 \\ build-lib [source] Create library from source or object files
30 \\ build-obj [source] Create object from source or assembly
31 \\ fmt [source] Parse file and render in canonical zig format
32 \\ run [source] Create executable and run immediately
33 \\ targets List available compilation targets
34 \\ test [source] Create and run a test build
35 \\ translate-c [source] Convert c code to zig code
36 \\ version Print version number and exit
37 \\ zen Print zen of zig and exit
38 \\
39 \\
40 ;
41
42const Command = struct {
43 name: []const u8,
44 exec: fn(&Allocator, []const []const u8) error!void,
27};45};
2846
29fn badArgs(comptime format: []const u8, args: ...) noreturn {
30 var stderr = io.getStdErr() catch std.os.exit(1);
31 var stderr_stream_adapter = io.FileOutStream.init(&stderr);
32 const stderr_stream = &stderr_stream_adapter.stream;
33 stderr_stream.print(format ++ "\n\n", args) catch std.os.exit(1);
34 printUsage(&stderr_stream_adapter.stream) catch std.os.exit(1);
35 std.os.exit(1);
36}
37
38pub fn main() !void {47pub fn main() !void {
39 const allocator = std.heap.c_allocator;48 var allocator = std.heap.c_allocator;
49
50 var stdout_file = try std.io.getStdOut();
51 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
52 stdout = &stdout_out_stream.stream;
53
54 var stderr_file = try std.io.getStdErr();
55 var stderr_out_stream = std.io.FileOutStream.init(&stderr_file);
56 stderr = &stderr_out_stream.stream;
4057
41 const args = try os.argsAlloc(allocator);58 const args = try os.argsAlloc(allocator);
42 defer os.argsFree(allocator, args);59 defer os.argsFree(allocator, args);
4360
44 if (args.len >= 2 and mem.eql(u8, args[1], "build")) {61 if (args.len <= 1) {
45 return buildMain(allocator, args[2..]);62 try stderr.write(usage);
46 }63 os.exit(1);
47
48 if (args.len >= 2 and mem.eql(u8, args[1], "fmt")) {
49 return fmtMain(allocator, args[2..]);
50 }
51
52 var cmd = Cmd.None;
53 var build_kind: Module.Kind = undefined;
54 var build_mode: builtin.Mode = builtin.Mode.Debug;
55 var color = ErrColor.Auto;
56 var emit_file_type = Emit.Binary;
57
58 var strip = false;
59 var is_static = false;
60 var verbose_tokenize = false;
61 var verbose_ast_tree = false;
62 var verbose_ast_fmt = false;
63 var verbose_link = false;
64 var verbose_ir = false;
65 var verbose_llvm_ir = false;
66 var verbose_cimport = false;
67 var mwindows = false;
68 var mconsole = false;
69 var rdynamic = false;
70 var each_lib_rpath = false;
71 var timing_info = false;
72
73 var in_file_arg: ?[]u8 = null;
74 var out_file: ?[]u8 = null;
75 var out_file_h: ?[]u8 = null;
76 var out_name_arg: ?[]u8 = null;
77 var libc_lib_dir_arg: ?[]u8 = null;
78 var libc_static_lib_dir_arg: ?[]u8 = null;
79 var libc_include_dir_arg: ?[]u8 = null;
80 var msvc_lib_dir_arg: ?[]u8 = null;
81 var kernel32_lib_dir_arg: ?[]u8 = null;
82 var zig_install_prefix: ?[]u8 = null;
83 var dynamic_linker_arg: ?[]u8 = null;
84 var cache_dir_arg: ?[]const u8 = null;
85 var target_arch: ?[]u8 = null;
86 var target_os: ?[]u8 = null;
87 var target_environ: ?[]u8 = null;
88 var mmacosx_version_min: ?[]u8 = null;
89 var mios_version_min: ?[]u8 = null;
90 var linker_script_arg: ?[]u8 = null;
91 var test_name_prefix_arg: ?[]u8 = null;
92
93 var test_filters = ArrayList([]const u8).init(allocator);
94 defer test_filters.deinit();
95
96 var lib_dirs = ArrayList([]const u8).init(allocator);
97 defer lib_dirs.deinit();
98
99 var clang_argv = ArrayList([]const u8).init(allocator);
100 defer clang_argv.deinit();
101
102 var llvm_argv = ArrayList([]const u8).init(allocator);
103 defer llvm_argv.deinit();
104
105 var link_libs = ArrayList([]const u8).init(allocator);
106 defer link_libs.deinit();
107
108 var frameworks = ArrayList([]const u8).init(allocator);
109 defer frameworks.deinit();
110
111 var objects = ArrayList([]const u8).init(allocator);
112 defer objects.deinit();
113
114 var asm_files = ArrayList([]const u8).init(allocator);
115 defer asm_files.deinit();
116
117 var rpath_list = ArrayList([]const u8).init(allocator);
118 defer rpath_list.deinit();
119
120 var ver_major: u32 = 0;
121 var ver_minor: u32 = 0;
122 var ver_patch: u32 = 0;
123
124 var arg_i: usize = 1;
125 while (arg_i < args.len) : (arg_i += 1) {
126 const arg = args[arg_i];
127
128 if (arg.len != 0 and arg[0] == '-') {
129 if (mem.eql(u8, arg, "--release-fast")) {
130 build_mode = builtin.Mode.ReleaseFast;
131 } else if (mem.eql(u8, arg, "--release-safe")) {
132 build_mode = builtin.Mode.ReleaseSafe;
133 } else if (mem.eql(u8, arg, "--strip")) {
134 strip = true;
135 } else if (mem.eql(u8, arg, "--static")) {
136 is_static = true;
137 } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
138 verbose_tokenize = true;
139 } else if (mem.eql(u8, arg, "--verbose-ast-tree")) {
140 verbose_ast_tree = true;
141 } else if (mem.eql(u8, arg, "--verbose-ast-fmt")) {
142 verbose_ast_fmt = true;
143 } else if (mem.eql(u8, arg, "--verbose-link")) {
144 verbose_link = true;
145 } else if (mem.eql(u8, arg, "--verbose-ir")) {
146 verbose_ir = true;
147 } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
148 verbose_llvm_ir = true;
149 } else if (mem.eql(u8, arg, "--verbose-cimport")) {
150 verbose_cimport = true;
151 } else if (mem.eql(u8, arg, "-mwindows")) {
152 mwindows = true;
153 } else if (mem.eql(u8, arg, "-mconsole")) {
154 mconsole = true;
155 } else if (mem.eql(u8, arg, "-rdynamic")) {
156 rdynamic = true;
157 } else if (mem.eql(u8, arg, "--each-lib-rpath")) {
158 each_lib_rpath = true;
159 } else if (mem.eql(u8, arg, "--enable-timing-info")) {
160 timing_info = true;
161 } else if (mem.eql(u8, arg, "--test-cmd-bin")) {
162 @panic("TODO --test-cmd-bin");
163 } else if (arg[1] == 'L' and arg.len > 2) {
164 // alias for --library-path
165 try lib_dirs.append(arg[1..]);
166 } else if (mem.eql(u8, arg, "--pkg-begin")) {
167 @panic("TODO --pkg-begin");
168 } else if (mem.eql(u8, arg, "--pkg-end")) {
169 @panic("TODO --pkg-end");
170 } else if (arg_i + 1 >= args.len) {
171 badArgs("expected another argument after {}", arg);
172 } else {
173 arg_i += 1;
174 if (mem.eql(u8, arg, "--output")) {
175 out_file = args[arg_i];
176 } else if (mem.eql(u8, arg, "--output-h")) {
177 out_file_h = args[arg_i];
178 } else if (mem.eql(u8, arg, "--color")) {
179 if (mem.eql(u8, args[arg_i], "auto")) {
180 color = ErrColor.Auto;
181 } else if (mem.eql(u8, args[arg_i], "on")) {
182 color = ErrColor.On;
183 } else if (mem.eql(u8, args[arg_i], "off")) {
184 color = ErrColor.Off;
185 } else {
186 badArgs("--color options are 'auto', 'on', or 'off'");
187 }
188 } else if (mem.eql(u8, arg, "--emit")) {
189 if (mem.eql(u8, args[arg_i], "asm")) {
190 emit_file_type = Emit.Assembly;
191 } else if (mem.eql(u8, args[arg_i], "bin")) {
192 emit_file_type = Emit.Binary;
193 } else if (mem.eql(u8, args[arg_i], "llvm-ir")) {
194 emit_file_type = Emit.LlvmIr;
195 } else {
196 badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
197 }
198 } else if (mem.eql(u8, arg, "--name")) {
199 out_name_arg = args[arg_i];
200 } else if (mem.eql(u8, arg, "--libc-lib-dir")) {
201 libc_lib_dir_arg = args[arg_i];
202 } else if (mem.eql(u8, arg, "--libc-static-lib-dir")) {
203 libc_static_lib_dir_arg = args[arg_i];
204 } else if (mem.eql(u8, arg, "--libc-include-dir")) {
205 libc_include_dir_arg = args[arg_i];
206 } else if (mem.eql(u8, arg, "--msvc-lib-dir")) {
207 msvc_lib_dir_arg = args[arg_i];
208 } else if (mem.eql(u8, arg, "--kernel32-lib-dir")) {
209 kernel32_lib_dir_arg = args[arg_i];
210 } else if (mem.eql(u8, arg, "--zig-install-prefix")) {
211 zig_install_prefix = args[arg_i];
212 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
213 dynamic_linker_arg = args[arg_i];
214 } else if (mem.eql(u8, arg, "-isystem")) {
215 try clang_argv.append("-isystem");
216 try clang_argv.append(args[arg_i]);
217 } else if (mem.eql(u8, arg, "-dirafter")) {
218 try clang_argv.append("-dirafter");
219 try clang_argv.append(args[arg_i]);
220 } else if (mem.eql(u8, arg, "-mllvm")) {
221 try clang_argv.append("-mllvm");
222 try clang_argv.append(args[arg_i]);
223
224 try llvm_argv.append(args[arg_i]);
225 } else if (mem.eql(u8, arg, "--library-path") or mem.eql(u8, arg, "-L")) {
226 try lib_dirs.append(args[arg_i]);
227 } else if (mem.eql(u8, arg, "--library")) {
228 try link_libs.append(args[arg_i]);
229 } else if (mem.eql(u8, arg, "--object")) {
230 try objects.append(args[arg_i]);
231 } else if (mem.eql(u8, arg, "--assembly")) {
232 try asm_files.append(args[arg_i]);
233 } else if (mem.eql(u8, arg, "--cache-dir")) {
234 cache_dir_arg = args[arg_i];
235 } else if (mem.eql(u8, arg, "--target-arch")) {
236 target_arch = args[arg_i];
237 } else if (mem.eql(u8, arg, "--target-os")) {
238 target_os = args[arg_i];
239 } else if (mem.eql(u8, arg, "--target-environ")) {
240 target_environ = args[arg_i];
241 } else if (mem.eql(u8, arg, "-mmacosx-version-min")) {
242 mmacosx_version_min = args[arg_i];
243 } else if (mem.eql(u8, arg, "-mios-version-min")) {
244 mios_version_min = args[arg_i];
245 } else if (mem.eql(u8, arg, "-framework")) {
246 try frameworks.append(args[arg_i]);
247 } else if (mem.eql(u8, arg, "--linker-script")) {
248 linker_script_arg = args[arg_i];
249 } else if (mem.eql(u8, arg, "-rpath")) {
250 try rpath_list.append(args[arg_i]);
251 } else if (mem.eql(u8, arg, "--test-filter")) {
252 try test_filters.append(args[arg_i]);
253 } else if (mem.eql(u8, arg, "--test-name-prefix")) {
254 test_name_prefix_arg = args[arg_i];
255 } else if (mem.eql(u8, arg, "--ver-major")) {
256 ver_major = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
257 } else if (mem.eql(u8, arg, "--ver-minor")) {
258 ver_minor = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
259 } else if (mem.eql(u8, arg, "--ver-patch")) {
260 ver_patch = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
261 } else if (mem.eql(u8, arg, "--test-cmd")) {
262 @panic("TODO --test-cmd");
263 } else {
264 badArgs("invalid argument: {}", arg);
265 }
266 }
267 } else if (cmd == Cmd.None) {
268 if (mem.eql(u8, arg, "build-obj")) {
269 cmd = Cmd.Build;
270 build_kind = Module.Kind.Obj;
271 } else if (mem.eql(u8, arg, "build-exe")) {
272 cmd = Cmd.Build;
273 build_kind = Module.Kind.Exe;
274 } else if (mem.eql(u8, arg, "build-lib")) {
275 cmd = Cmd.Build;
276 build_kind = Module.Kind.Lib;
277 } else if (mem.eql(u8, arg, "version")) {
278 cmd = Cmd.Version;
279 } else if (mem.eql(u8, arg, "zen")) {
280 cmd = Cmd.Zen;
281 } else if (mem.eql(u8, arg, "translate-c")) {
282 cmd = Cmd.TranslateC;
283 } else if (mem.eql(u8, arg, "test")) {
284 cmd = Cmd.Test;
285 build_kind = Module.Kind.Exe;
286 } else {
287 badArgs("unrecognized command: {}", arg);
288 }
289 } else switch (cmd) {
290 Cmd.Build, Cmd.TranslateC, Cmd.Test => {
291 if (in_file_arg == null) {
292 in_file_arg = arg;
293 } else {
294 badArgs("unexpected extra parameter: {}", arg);
295 }
296 },
297 Cmd.Version, Cmd.Zen, Cmd.Targets => {
298 badArgs("unexpected extra parameter: {}", arg);
299 },
300 Cmd.None => unreachable,
301 }
302 }64 }
30365
304 target.initializeAll();66 const commands = []Command {
30567 Command { .name = "build", .exec = cmdBuild },
306 // TODO68 Command { .name = "build-exe", .exec = cmdBuildExe },
307// ZigTarget alloc_target;69 Command { .name = "build-lib", .exec = cmdBuildLib },
308// ZigTarget *target;70 Command { .name = "build-obj", .exec = cmdBuildObj },
309// if (!target_arch && !target_os && !target_environ) {71 Command { .name = "fmt", .exec = cmdFmt },
310// target = nullptr;72 Command { .name = "run", .exec = cmdRun },
311// } else {73 Command { .name = "targets", .exec = cmdTargets },
312// target = &alloc_target;74 Command { .name = "test", .exec = cmdTest },
313// get_unknown_target(target);75 Command { .name = "translate-c", .exec = cmdTranslateC },
314// if (target_arch) {76 Command { .name = "version", .exec = cmdVersion },
315// if (parse_target_arch(target_arch, &target->arch)) {77 Command { .name = "zen", .exec = cmdZen },
316// fprintf(stderr, "invalid --target-arch argument\n");78
317// return usage(arg0);79 // undocumented commands
318// }80 Command { .name = "help", .exec = cmdHelp },
319// }81 Command { .name = "internal", .exec = cmdInternal },
320// if (target_os) {82 };
321// if (parse_target_os(target_os, &target->os)) {83
322// fprintf(stderr, "invalid --target-os argument\n");84 for (commands) |command| {
323// return usage(arg0);85 if (mem.eql(u8, command.name, args[1])) {
324// }86 try command.exec(allocator, args[2..]);
325// }87 return;
326// if (target_environ) {88 }
327// if (parse_target_environ(target_environ, &target->env_type)) {
328// fprintf(stderr, "invalid --target-environ argument\n");
329// return usage(arg0);
330// }
331// }
332// }
333
334 switch (cmd) {
335 Cmd.None => badArgs("expected command"),
336 Cmd.Zen => return printZen(),
337 Cmd.Build, Cmd.Test, Cmd.TranslateC => {
338 if (cmd == Cmd.Build and in_file_arg == null and objects.len == 0 and asm_files.len == 0) {
339 badArgs("expected source file argument or at least one --object or --assembly argument");
340 } else if ((cmd == Cmd.TranslateC or cmd == Cmd.Test) and in_file_arg == null) {
341 badArgs("expected source file argument");
342 } else if (cmd == Cmd.Build and build_kind == Module.Kind.Obj and objects.len != 0) {
343 badArgs("When building an object file, --object arguments are invalid");
344 }
345
346 const root_name = switch (cmd) {
347 Cmd.Build, Cmd.TranslateC => x: {
348 if (out_name_arg) |out_name| {
349 break :x out_name;
350 } else if (in_file_arg) |in_file_path| {
351 const basename = os.path.basename(in_file_path);
352 var it = mem.split(basename, ".");
353 break :x it.next() ?? badArgs("file name cannot be empty");
354 } else {
355 badArgs("--name [name] not provided and unable to infer");
356 }
357 },
358 Cmd.Test => "test",
359 else => unreachable,
360 };
361
362 const zig_root_source_file = if (cmd == Cmd.TranslateC) null else in_file_arg;
363
364 const chosen_cache_dir = cache_dir_arg ?? default_zig_cache_name;
365 const full_cache_dir = try os.path.resolve(allocator, ".", chosen_cache_dir);
366 defer allocator.free(full_cache_dir);
367
368 const zig_lib_dir = try resolveZigLibDir(allocator, zig_install_prefix);
369 errdefer allocator.free(zig_lib_dir);
370
371 const module = try Module.create(allocator, root_name, zig_root_source_file,
372 Target.Native, build_kind, build_mode, zig_lib_dir, full_cache_dir);
373 defer module.destroy();
374
375 module.version_major = ver_major;
376 module.version_minor = ver_minor;
377 module.version_patch = ver_patch;
378
379 module.is_test = cmd == Cmd.Test;
380 if (linker_script_arg) |linker_script| {
381 module.linker_script = linker_script;
382 }
383 module.each_lib_rpath = each_lib_rpath;
384 module.clang_argv = clang_argv.toSliceConst();
385 module.llvm_argv = llvm_argv.toSliceConst();
386 module.strip = strip;
387 module.is_static = is_static;
388
389 if (libc_lib_dir_arg) |libc_lib_dir| {
390 module.libc_lib_dir = libc_lib_dir;
391 }
392 if (libc_static_lib_dir_arg) |libc_static_lib_dir| {
393 module.libc_static_lib_dir = libc_static_lib_dir;
394 }
395 if (libc_include_dir_arg) |libc_include_dir| {
396 module.libc_include_dir = libc_include_dir;
397 }
398 if (msvc_lib_dir_arg) |msvc_lib_dir| {
399 module.msvc_lib_dir = msvc_lib_dir;
400 }
401 if (kernel32_lib_dir_arg) |kernel32_lib_dir| {
402 module.kernel32_lib_dir = kernel32_lib_dir;
403 }
404 if (dynamic_linker_arg) |dynamic_linker| {
405 module.dynamic_linker = dynamic_linker;
406 }
407 module.verbose_tokenize = verbose_tokenize;
408 module.verbose_ast_tree = verbose_ast_tree;
409 module.verbose_ast_fmt = verbose_ast_fmt;
410 module.verbose_link = verbose_link;
411 module.verbose_ir = verbose_ir;
412 module.verbose_llvm_ir = verbose_llvm_ir;
413 module.verbose_cimport = verbose_cimport;
414
415 module.err_color = color;
416
417 module.lib_dirs = lib_dirs.toSliceConst();
418 module.darwin_frameworks = frameworks.toSliceConst();
419 module.rpath_list = rpath_list.toSliceConst();
420
421 for (link_libs.toSliceConst()) |name| {
422 _ = try module.addLinkLib(name, true);
423 }
424
425 module.windows_subsystem_windows = mwindows;
426 module.windows_subsystem_console = mconsole;
427 module.linker_rdynamic = rdynamic;
428
429 if (mmacosx_version_min != null and mios_version_min != null) {
430 badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");
431 }
432
433 if (mmacosx_version_min) |ver| {
434 module.darwin_version_min = Module.DarwinVersionMin { .MacOS = ver };
435 } else if (mios_version_min) |ver| {
436 module.darwin_version_min = Module.DarwinVersionMin { .Ios = ver };
437 }
438
439 module.test_filters = test_filters.toSliceConst();
440 module.test_name_prefix = test_name_prefix_arg;
441 module.out_h_path = out_file_h;
442
443 // TODO
444 //add_package(g, cur_pkg, g->root_package);
445
446 switch (cmd) {
447 Cmd.Build => {
448 module.emit_file_type = emit_file_type;
449
450 module.link_objects = objects.toSliceConst();
451 module.assembly_files = asm_files.toSliceConst();
452
453 try module.build();
454 try module.link(out_file);
455 },
456 Cmd.TranslateC => @panic("TODO translate-c"),
457 Cmd.Test => @panic("TODO test cmd"),
458 else => unreachable,
459 }
460 },
461 Cmd.Version => {
462 var stdout_file = try io.getStdErr();
463 try stdout_file.write(std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
464 try stdout_file.write("\n");
465 },
466 Cmd.Targets => @panic("TODO zig targets"),
467 }89 }
468}
46990
470fn printUsage(stream: var) !void {91 try stderr.print("unknown command: {}\n\n", args[1]);
471 try stream.write(92 try stderr.write(usage);
472 \\Usage: zig [command] [options]
473 \\
474 \\Commands:
475 \\ build build project from build.zig
476 \\ build-exe [source] create executable from source or object files
477 \\ build-lib [source] create library from source or object files
478 \\ build-obj [source] create object from source or assembly
479 \\ fmt [file] parse file and render in canonical zig format
480 \\ translate-c [source] convert c code to zig code
481 \\ targets list available compilation targets
482 \\ test [source] create and run a test build
483 \\ version print version number and exit
484 \\ zen print zen of zig and exit
485 \\Compile Options:
486 \\ --assembly [source] add assembly file to build
487 \\ --cache-dir [path] override the cache directory
488 \\ --color [auto|off|on] enable or disable colored error messages
489 \\ --emit [filetype] emit a specific file format as compilation output
490 \\ --enable-timing-info print timing diagnostics
491 \\ --libc-include-dir [path] directory where libc stdlib.h resides
492 \\ --name [name] override output name
493 \\ --output [file] override destination path
494 \\ --output-h [file] override generated header file path
495 \\ --pkg-begin [name] [path] make package available to import and push current pkg
496 \\ --pkg-end pop current pkg
497 \\ --release-fast build with optimizations on and safety off
498 \\ --release-safe build with optimizations on and safety on
499 \\ --static output will be statically linked
500 \\ --strip exclude debug symbols
501 \\ --target-arch [name] specify target architecture
502 \\ --target-environ [name] specify target environment
503 \\ --target-os [name] specify target operating system
504 \\ --verbose-tokenize enable compiler debug info: tokenization
505 \\ --verbose-ast-tree enable compiler debug info: parsing into an AST (treeview)
506 \\ --verbose-ast-fmt enable compiler debug info: parsing into an AST (render source)
507 \\ --verbose-cimport enable compiler debug info: C imports
508 \\ --verbose-ir enable compiler debug info: Zig IR
509 \\ --verbose-llvm-ir enable compiler debug info: LLVM IR
510 \\ --verbose-link enable compiler debug info: linking
511 \\ --zig-install-prefix [path] override directory where zig thinks it is installed
512 \\ -dirafter [dir] same as -isystem but do it last
513 \\ -isystem [dir] add additional search path for other .h files
514 \\ -mllvm [arg] additional arguments to forward to LLVM's option processing
515 \\Link Options:
516 \\ --ar-path [path] set the path to ar
517 \\ --dynamic-linker [path] set the path to ld.so
518 \\ --each-lib-rpath add rpath for each used dynamic library
519 \\ --libc-lib-dir [path] directory where libc crt1.o resides
520 \\ --libc-static-lib-dir [path] directory where libc crtbegin.o resides
521 \\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
522 \\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
523 \\ --library [lib] link against lib
524 \\ --library-path [dir] add a directory to the library search path
525 \\ --linker-script [path] use a custom linker script
526 \\ --object [obj] add object file to build
527 \\ -L[dir] alias for --library-path
528 \\ -rdynamic add all symbols to the dynamic symbol table
529 \\ -rpath [path] add directory to the runtime library search path
530 \\ -mconsole (windows) --subsystem console to the linker
531 \\ -mwindows (windows) --subsystem windows to the linker
532 \\ -framework [name] (darwin) link against framework
533 \\ -mios-version-min [ver] (darwin) set iOS deployment target
534 \\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target
535 \\ --ver-major [ver] dynamic library semver major version
536 \\ --ver-minor [ver] dynamic library semver minor version
537 \\ --ver-patch [ver] dynamic library semver patch version
538 \\Test Options:
539 \\ --test-filter [text] skip tests that do not match filter
540 \\ --test-name-prefix [text] add prefix to all tests
541 \\ --test-cmd [arg] specify test execution command one arg at a time
542 \\ --test-cmd-bin appends test binary path to test cmd args
543 \\
544 );
545}
546
547fn printZen() !void {
548 var stdout_file = try io.getStdErr();
549 try stdout_file.write(
550 \\
551 \\ * Communicate intent precisely.
552 \\ * Edge cases matter.
553 \\ * Favor reading code over writing code.
554 \\ * Only one obvious way to do things.
555 \\ * Runtime crashes are better than bugs.
556 \\ * Compile errors are better than runtime crashes.
557 \\ * Incremental improvements.
558 \\ * Avoid local maximums.
559 \\ * Reduce the amount one must remember.
560 \\ * Minimize energy spent on coding style.
561 \\ * Together we serve end users.
562 \\
563 \\
564 );
565}93}
56694
567fn buildMain(allocator: &mem.Allocator, argv: []const []const u8) !void {95// cmd:build ///////////////////////////////////////////////////////////////////////////////////////
568 var build_file: [] const u8 = "build.zig";96
569 var cache_dir: ?[] const u8 = null;97const usage_build =
570 var zig_install_prefix: ?[] const u8 = null;98 \\usage: zig build <options>
571 var asked_for_help = false;99 \\
572 var asked_for_init = false;100 \\General Options:
573101 \\ --help Print this help and exit
574 var args = ArrayList([] const u8).init(allocator);102 \\ --init Generate a build.zig template
575 defer args.deinit();103 \\ --build-file [file] Override path to build.zig
576104 \\ --cache-dir [path] Override path to cache directory
577 var zig_exe_path = try os.selfExePath(allocator);105 \\ --verbose Print commands before executing them
578 defer allocator.free(zig_exe_path);106 \\ --prefix [path] Override default install prefix
579107 \\
580 try args.append(""); // Placeholder for zig-cache/build108 \\Project-Specific Options:
581 try args.append(""); // Placeholder for zig_exe_path109 \\
582 try args.append(""); // Placeholder for build_file_dirname110 \\ Project-specific options become available when the build file is found.
583 try args.append(""); // Placeholder for full_cache_dir111 \\
112 \\Advanced Options:
113 \\ --build-file [file] Override path to build.zig
114 \\ --cache-dir [path] Override path to cache directory
115 \\ --verbose-tokenize Enable compiler debug output for tokenization
116 \\ --verbose-ast Enable compiler debug output for parsing into an AST
117 \\ --verbose-link Enable compiler debug output for linking
118 \\ --verbose-ir Enable compiler debug output for Zig IR
119 \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR
120 \\ --verbose-cimport Enable compiler debug output for C imports
121 \\
122 \\
123 ;
124
125const args_build_spec = []Flag {
126 Flag.Bool("--help"),
127 Flag.Bool("--init"),
128 Flag.Arg1("--build-file"),
129 Flag.Arg1("--cache-dir"),
130 Flag.Bool("--verbose"),
131 Flag.Arg1("--prefix"),
132
133 Flag.Arg1("--build-file"),
134 Flag.Arg1("--cache-dir"),
135 Flag.Bool("--verbose-tokenize"),
136 Flag.Bool("--verbose-ast"),
137 Flag.Bool("--verbose-link"),
138 Flag.Bool("--verbose-ir"),
139 Flag.Bool("--verbose-llvm-ir"),
140 Flag.Bool("--verbose-cimport"),
141};
584142
585 var i: usize = 0;143const missing_build_file =
586 while (i < argv.len) : (i += 1) {144 \\No 'build.zig' file found.
587 var arg = argv[i];145 \\
588 if (mem.eql(u8, arg, "--help")) {146 \\Initialize a 'build.zig' template file with `zig build --init`,
589 asked_for_help = true;147 \\or build an executable directly with `zig build-exe $FILENAME.zig`.
590 try args.append(argv[i]);148 \\
591 } else if (mem.eql(u8, arg, "--init")) {149 \\See: `zig build --help` or `zig help` for more options.
592 asked_for_init = true;150 \\
593 try args.append(argv[i]);151 ;
594 } else if (i + 1 < argv.len and mem.eql(u8, arg, "--build-file")) {152
595 build_file = argv[i + 1];153fn cmdBuild(allocator: &Allocator, args: []const []const u8) !void {
596 i += 1;154 var flags = try Args.parse(allocator, args_build_spec, args);
597 } else if (i + 1 < argv.len and mem.eql(u8, arg, "--cache-dir")) {155 defer flags.deinit();
598 cache_dir = argv[i + 1];156
599 i += 1;157 if (flags.present("help")) {
600 } else if (i + 1 < argv.len and mem.eql(u8, arg, "--zig-install-prefix")) {158 try stderr.write(usage_build);
601 try args.append(arg);159 os.exit(0);
602 i += 1;
603 zig_install_prefix = argv[i];
604 try args.append(argv[i]);
605 } else {
606 try args.append(arg);
607 }
608 }160 }
609161
610 const zig_lib_dir = try resolveZigLibDir(allocator, zig_install_prefix);162 const zig_lib_dir = try introspect.resolveZigLibDir(allocator);
611 defer allocator.free(zig_lib_dir);163 defer allocator.free(zig_lib_dir);
612164
613 const zig_std_dir = try os.path.join(allocator, zig_lib_dir, "std");165 const zig_std_dir = try os.path.join(allocator, zig_lib_dir, "std");
...@@ -619,186 +171,828 @@ fn buildMain(allocator: &mem.Allocator, argv: []const []const u8) !void {...@@ -619,186 +171,828 @@ fn buildMain(allocator: &mem.Allocator, argv: []const []const u8) !void {
619 const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");171 const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");
620 defer allocator.free(build_runner_path);172 defer allocator.free(build_runner_path);
621173
622 // g = codegen_create(build_runner_path, ...)174 const build_file = flags.single("build-file") ?? "build.zig";
623 // codegen_set_out_name(g, "build")
624
625 const build_file_abs = try os.path.resolve(allocator, ".", build_file);175 const build_file_abs = try os.path.resolve(allocator, ".", build_file);
626 defer allocator.free(build_file_abs);176 defer allocator.free(build_file_abs);
627177
628 const build_file_basename = os.path.basename(build_file_abs);178 const build_file_exists = os.File.access(allocator, build_file_abs, os.default_file_mode) catch false;
629 const build_file_dirname = os.path.dirname(build_file_abs);
630179
631 var full_cache_dir: []u8 = undefined;180 if (flags.present("init")) {
632 if (cache_dir == null) {181 if (build_file_exists) {
633 full_cache_dir = try os.path.join(allocator, build_file_dirname, "zig-cache");182 try stderr.print("build.zig already exists\n");
634 } else {183 os.exit(1);
635 full_cache_dir = try os.path.resolve(allocator, ".", ??cache_dir, full_cache_dir);184 }
636 }
637 defer allocator.free(full_cache_dir);
638185
639 const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build");186 // need a new scope for proper defer scope finalization on exit
640 defer allocator.free(path_to_build_exe);187 {
641 // codegen_set_cache_dir(g, full_cache_dir)188 const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig");
189 defer allocator.free(build_template_path);
642190
643 args.items[0] = path_to_build_exe;191 try os.copyFile(allocator, build_template_path, build_file_abs);
644 args.items[1] = zig_exe_path;192 try stderr.print("wrote build.zig template\n");
645 args.items[2] = build_file_dirname;193 }
646 args.items[3] = full_cache_dir;
647194
648 var build_file_exists: bool = undefined;195 os.exit(0);
649 if (os.File.openRead(allocator, build_file_abs)) |*file| {
650 file.close();
651 build_file_exists = true;
652 } else |_| {
653 build_file_exists = false;
654 }196 }
655197
656 if (!build_file_exists and asked_for_help) {198 if (!build_file_exists) {
657 // TODO(bnoordhuis) Print help message from std/special/build_runner.zig199 try stderr.write(missing_build_file);
658 return;200 os.exit(1);
659 }201 }
660202
661 if (!build_file_exists and asked_for_init) {203 // TODO: Invoke build.zig entrypoint directly?
662 const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig");204 var zig_exe_path = try os.selfExePath(allocator);
663 defer allocator.free(build_template_path);205 defer allocator.free(zig_exe_path);
664
665 var srcfile = try os.File.openRead(allocator, build_template_path);
666 defer srcfile.close();
667206
668 var dstfile = try os.File.openWrite(allocator, build_file_abs);207 var build_args = ArrayList([]const u8).init(allocator);
669 defer dstfile.close();208 defer build_args.deinit();
670209
671 while (true) {210 const build_file_basename = os.path.basename(build_file_abs);
672 var buffer: [4096]u8 = undefined;211 const build_file_dirname = os.path.dirname(build_file_abs);
673 const n = try srcfile.read(buffer[0..]);
674 if (n == 0) break;
675 try dstfile.write(buffer[0..n]);
676 }
677212
678 return;213 var full_cache_dir: []u8 = undefined;
214 if (flags.single("cache-dir")) |cache_dir| {
215 full_cache_dir = try os.path.resolve(allocator, ".", cache_dir, full_cache_dir);
216 } else {
217 full_cache_dir = try os.path.join(allocator, build_file_dirname, "zig-cache");
679 }218 }
219 defer allocator.free(full_cache_dir);
680220
681 if (!build_file_exists) {221 const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build");
682 warn(222 defer allocator.free(path_to_build_exe);
683 \\No 'build.zig' file found.
684 \\Initialize a 'build.zig' template file with `zig build --init`,
685 \\or build an executable directly with `zig build-exe $FILENAME.zig`.
686 \\See: `zig build --help` or `zig help` for more options.
687 \\
688 );
689 os.exit(1);
690 }
691223
692 // codegen_build(g)224 try build_args.append(path_to_build_exe);
693 // codegen_link(g, path_to_build_exe)225 try build_args.append(zig_exe_path);
694 // codegen_destroy(g)226 try build_args.append(build_file_dirname);
227 try build_args.append(full_cache_dir);
695228
696 var proc = try os.ChildProcess.init(args.toSliceConst(), allocator);229 var proc = try os.ChildProcess.init(build_args.toSliceConst(), allocator);
697 defer proc.deinit();230 defer proc.deinit();
698231
699 var term = try proc.spawnAndWait();232 var term = try proc.spawnAndWait();
700 switch (term) {233 switch (term) {
701 os.ChildProcess.Term.Exited => |status| {234 os.ChildProcess.Term.Exited => |status| {
702 if (status != 0) {235 if (status != 0) {
703 warn("{} exited with status {}\n", args.at(0), status);236 try stderr.print("{} exited with status {}\n", build_args.at(0), status);
704 os.exit(1);237 os.exit(1);
705 }238 }
706 },239 },
707 os.ChildProcess.Term.Signal => |signal| {240 os.ChildProcess.Term.Signal => |signal| {
708 warn("{} killed by signal {}\n", args.at(0), signal);241 try stderr.print("{} killed by signal {}\n", build_args.at(0), signal);
709 os.exit(1);242 os.exit(1);
710 },243 },
711 os.ChildProcess.Term.Stopped => |signal| {244 os.ChildProcess.Term.Stopped => |signal| {
712 warn("{} stopped by signal {}\n", args.at(0), signal);245 try stderr.print("{} stopped by signal {}\n", build_args.at(0), signal);
713 os.exit(1);246 os.exit(1);
714 },247 },
715 os.ChildProcess.Term.Unknown => |status| {248 os.ChildProcess.Term.Unknown => |status| {
716 warn("{} encountered unknown failure {}\n", args.at(0), status);249 try stderr.print("{} encountered unknown failure {}\n", build_args.at(0), status);
250 os.exit(1);
251 },
252 }
253}
254
255// cmd:build-exe ///////////////////////////////////////////////////////////////////////////////////
256
257const usage_build_generic =
258 \\usage: zig build-exe <options> [file]
259 \\ zig build-lib <options> [file]
260 \\ zig build-obj <options> [file]
261 \\
262 \\General Options:
263 \\ --help Print this help and exit
264 \\ --color [auto|off|on] Enable or disable colored error messages
265 \\
266 \\Compile Options:
267 \\ --assembly [source] Add assembly file to build
268 \\ --cache-dir [path] Override the cache directory
269 \\ --emit [filetype] Emit a specific file format as compilation output
270 \\ --enable-timing-info Print timing diagnostics
271 \\ --libc-include-dir [path] Directory where libc stdlib.h resides
272 \\ --name [name] Override output name
273 \\ --output [file] Override destination path
274 \\ --output-h [file] Override generated header file path
275 \\ --pkg-begin [name] [path] Make package available to import and push current pkg
276 \\ --pkg-end Pop current pkg
277 \\ --release-fast Build with optimizations on and safety off
278 \\ --release-safe Build with optimizations on and safety on
279 \\ --static Output will be statically linked
280 \\ --strip Exclude debug symbols
281 \\ --target-arch [name] Specify target architecture
282 \\ --target-environ [name] Specify target environment
283 \\ --target-os [name] Specify target operating system
284 \\ --verbose-tokenize Turn on compiler debug output for tokenization
285 \\ --verbose-ast-tree Turn on compiler debug output for parsing into an AST (tree view)
286 \\ --verbose-ast-fmt Turn on compiler debug output for parsing into an AST (render source)
287 \\ --verbose-link Turn on compiler debug output for linking
288 \\ --verbose-ir Turn on compiler debug output for Zig IR
289 \\ --verbose-llvm-ir Turn on compiler debug output for LLVM IR
290 \\ --verbose-cimport Turn on compiler debug output for C imports
291 \\ -dirafter [dir] Same as -isystem but do it last
292 \\ -isystem [dir] Add additional search path for other .h files
293 \\ -mllvm [arg] Additional arguments to forward to LLVM's option processing
294 \\
295 \\Link Options:
296 \\ --ar-path [path] Set the path to ar
297 \\ --dynamic-linker [path] Set the path to ld.so
298 \\ --each-lib-rpath Add rpath for each used dynamic library
299 \\ --libc-lib-dir [path] Directory where libc crt1.o resides
300 \\ --libc-static-lib-dir [path] Directory where libc crtbegin.o resides
301 \\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
302 \\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
303 \\ --library [lib] Link against lib
304 \\ --forbid-library [lib] Make it an error to link against lib
305 \\ --library-path [dir] Add a directory to the library search path
306 \\ --linker-script [path] Use a custom linker script
307 \\ --object [obj] Add object file to build
308 \\ -rdynamic Add all symbols to the dynamic symbol table
309 \\ -rpath [path] Add directory to the runtime library search path
310 \\ -mconsole (windows) --subsystem console to the linker
311 \\ -mwindows (windows) --subsystem windows to the linker
312 \\ -framework [name] (darwin) link against framework
313 \\ -mios-version-min [ver] (darwin) set iOS deployment target
314 \\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target
315 \\ --ver-major [ver] Dynamic library semver major version
316 \\ --ver-minor [ver] Dynamic library semver minor version
317 \\ --ver-patch [ver] Dynamic library semver patch version
318 \\
319 \\
320 ;
321
322const args_build_generic = []Flag {
323 Flag.Bool("--help"),
324 Flag.Option("--color", []const []const u8 { "auto", "off", "on" }),
325
326 Flag.ArgMergeN("--assembly", 1),
327 Flag.Arg1("--cache-dir"),
328 Flag.Option("--emit", []const []const u8 { "asm", "bin", "llvm-ir" }),
329 Flag.Bool("--enable-timing-info"),
330 Flag.Arg1("--libc-include-dir"),
331 Flag.Arg1("--name"),
332 Flag.Arg1("--output"),
333 Flag.Arg1("--output-h"),
334 // NOTE: Parsed manually after initial check
335 Flag.ArgN("--pkg-begin", 2),
336 Flag.Bool("--pkg-end"),
337 Flag.Bool("--release-fast"),
338 Flag.Bool("--release-safe"),
339 Flag.Bool("--static"),
340 Flag.Bool("--strip"),
341 Flag.Arg1("--target-arch"),
342 Flag.Arg1("--target-environ"),
343 Flag.Arg1("--target-os"),
344 Flag.Bool("--verbose-tokenize"),
345 Flag.Bool("--verbose-ast-tree"),
346 Flag.Bool("--verbose-ast-fmt"),
347 Flag.Bool("--verbose-link"),
348 Flag.Bool("--verbose-ir"),
349 Flag.Bool("--verbose-llvm-ir"),
350 Flag.Bool("--verbose-cimport"),
351 Flag.Arg1("-dirafter"),
352 Flag.ArgMergeN("-isystem", 1),
353 Flag.Arg1("-mllvm"),
354
355 Flag.Arg1("--ar-path"),
356 Flag.Arg1("--dynamic-linker"),
357 Flag.Bool("--each-lib-rpath"),
358 Flag.Arg1("--libc-lib-dir"),
359 Flag.Arg1("--libc-static-lib-dir"),
360 Flag.Arg1("--msvc-lib-dir"),
361 Flag.Arg1("--kernel32-lib-dir"),
362 Flag.ArgMergeN("--library", 1),
363 Flag.ArgMergeN("--forbid-library", 1),
364 Flag.ArgMergeN("--library-path", 1),
365 Flag.Arg1("--linker-script"),
366 Flag.ArgMergeN("--object", 1),
367 // NOTE: Removed -L since it would need to be special-cased and we have an alias in library-path
368 Flag.Bool("-rdynamic"),
369 Flag.Arg1("-rpath"),
370 Flag.Bool("-mconsole"),
371 Flag.Bool("-mwindows"),
372 Flag.ArgMergeN("-framework", 1),
373 Flag.Arg1("-mios-version-min"),
374 Flag.Arg1("-mmacosx-version-min"),
375 Flag.Arg1("--ver-major"),
376 Flag.Arg1("--ver-minor"),
377 Flag.Arg1("--ver-patch"),
378};
379
380fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Module.Kind) !void {
381 var flags = try Args.parse(allocator, args_build_generic, args);
382 defer flags.deinit();
383
384 if (flags.present("help")) {
385 try stderr.write(usage_build_generic);
386 os.exit(0);
387 }
388
389 var build_mode = builtin.Mode.Debug;
390 if (flags.present("release-fast")) {
391 build_mode = builtin.Mode.ReleaseFast;
392 } else if (flags.present("release-safe")) {
393 build_mode = builtin.Mode.ReleaseSafe;
394 }
395
396 var color = Module.ErrColor.Auto;
397 if (flags.single("color")) |color_flag| {
398 if (mem.eql(u8, color_flag, "auto")) {
399 color = Module.ErrColor.Auto;
400 } else if (mem.eql(u8, color_flag, "on")) {
401 color = Module.ErrColor.On;
402 } else if (mem.eql(u8, color_flag, "off")) {
403 color = Module.ErrColor.Off;
404 } else {
405 unreachable;
406 }
407 }
408
409 var emit_type = Module.Emit.Binary;
410 if (flags.single("emit")) |emit_flag| {
411 if (mem.eql(u8, emit_flag, "asm")) {
412 emit_type = Module.Emit.Assembly;
413 } else if (mem.eql(u8, emit_flag, "bin")) {
414 emit_type = Module.Emit.Binary;
415 } else if (mem.eql(u8, emit_flag, "llvm-ir")) {
416 emit_type = Module.Emit.LlvmIr;
417 } else {
418 unreachable;
419 }
420 }
421
422 var cur_pkg = try Module.CliPkg.init(allocator, "", "", null); // TODO: Need a path, name?
423 defer cur_pkg.deinit();
424
425 var i: usize = 0;
426 while (i < args.len) : (i += 1) {
427 const arg_name = args[i];
428 if (mem.eql(u8, "--pkg-begin", arg_name)) {
429 // following two arguments guaranteed to exist due to arg parsing
430 i += 1;
431 const new_pkg_name = args[i];
432 i += 1;
433 const new_pkg_path = args[i];
434
435 var new_cur_pkg = try Module.CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg);
436 try cur_pkg.children.append(new_cur_pkg);
437 cur_pkg = new_cur_pkg;
438 } else if (mem.eql(u8, "--pkg-end", arg_name)) {
439 if (cur_pkg.parent == null) {
440 try stderr.print("encountered --pkg-end with no matching --pkg-begin\n");
441 os.exit(1);
442 }
443 cur_pkg = ??cur_pkg.parent;
444 }
445 }
446
447 if (cur_pkg.parent != null) {
448 try stderr.print("unmatched --pkg-begin\n");
449 os.exit(1);
450 }
451
452 var in_file: ?[]const u8 = undefined;
453 switch (flags.positionals.len) {
454 0 => {
455 try stderr.write("--name [name] not provided and unable to infer\n");
717 os.exit(1);456 os.exit(1);
718 },457 },
458 1 => {
459 in_file = flags.positionals.at(0);
460 },
461 else => {
462 try stderr.write("only one zig input file is accepted during build\n");
463 os.exit(1);
464 },
465 }
466
467 const basename = os.path.basename(??in_file);
468 var it = mem.split(basename, ".");
469 const root_name = it.next() ?? {
470 try stderr.write("file name cannot be empty\n");
471 os.exit(1);
472 };
473
474 const asm_a= flags.many("assembly");
475 const obj_a = flags.many("object");
476 if (in_file == null and (obj_a == null or (??obj_a).len == 0) and (asm_a == null or (??asm_a).len == 0)) {
477 try stderr.write("Expected source file argument or at least one --object or --assembly argument\n");
478 os.exit(1);
479 }
480
481 if (out_type == Module.Kind.Obj and (obj_a != null and (??obj_a).len != 0)) {
482 try stderr.write("When building an object file, --object arguments are invalid\n");
483 os.exit(1);
484 }
485
486 const zig_root_source_file = in_file;
487
488 const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") ?? "zig-cache"[0..]) catch {
489 os.exit(1);
490 };
491 defer allocator.free(full_cache_dir);
492
493 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
494 defer allocator.free(zig_lib_dir);
495
496 var module =
497 try Module.create(
498 allocator,
499 root_name,
500 zig_root_source_file,
501 Target.Native,
502 out_type,
503 build_mode,
504 zig_lib_dir,
505 full_cache_dir
506 );
507 defer module.destroy();
508
509 module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") ?? "0", 10);
510 module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") ?? "0", 10);
511 module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") ?? "0", 10);
512
513 module.is_test = false;
514
515 if (flags.single("linker-script")) |linker_script| {
516 module.linker_script = linker_script;
517 }
518
519 module.each_lib_rpath = flags.present("each-lib-rpath");
520
521 var clang_argv_buf = ArrayList([]const u8).init(allocator);
522 defer clang_argv_buf.deinit();
523 if (flags.many("mllvm")) |mllvm_flags| {
524 for (mllvm_flags) |mllvm| {
525 try clang_argv_buf.append("-mllvm");
526 try clang_argv_buf.append(mllvm);
527 }
528
529 module.llvm_argv = mllvm_flags;
530 module.clang_argv = clang_argv_buf.toSliceConst();
531 }
532
533 module.strip = flags.present("strip");
534 module.is_static = flags.present("static");
535
536 if (flags.single("libc-lib-dir")) |libc_lib_dir| {
537 module.libc_lib_dir = libc_lib_dir;
538 }
539 if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| {
540 module.libc_static_lib_dir = libc_static_lib_dir;
541 }
542 if (flags.single("libc-include-dir")) |libc_include_dir| {
543 module.libc_include_dir = libc_include_dir;
719 }544 }
545 if (flags.single("msvc-lib-dir")) |msvc_lib_dir| {
546 module.msvc_lib_dir = msvc_lib_dir;
547 }
548 if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| {
549 module.kernel32_lib_dir = kernel32_lib_dir;
550 }
551 if (flags.single("dynamic-linker")) |dynamic_linker| {
552 module.dynamic_linker = dynamic_linker;
553 }
554
555 module.verbose_tokenize = flags.present("verbose-tokenize");
556 module.verbose_ast_tree = flags.present("verbose-ast-tree");
557 module.verbose_ast_fmt = flags.present("verbose-ast-fmt");
558 module.verbose_link = flags.present("verbose-link");
559 module.verbose_ir = flags.present("verbose-ir");
560 module.verbose_llvm_ir = flags.present("verbose-llvm-ir");
561 module.verbose_cimport = flags.present("verbose-cimport");
562
563 module.err_color = color;
564
565 if (flags.many("library-path")) |lib_dirs| {
566 module.lib_dirs = lib_dirs;
567 }
568
569 if (flags.many("framework")) |frameworks| {
570 module.darwin_frameworks = frameworks;
571 }
572
573 if (flags.many("rpath")) |rpath_list| {
574 module.rpath_list = rpath_list;
575 }
576
577 if (flags.single("output-h")) |output_h| {
578 module.out_h_path = output_h;
579 }
580
581 module.windows_subsystem_windows = flags.present("mwindows");
582 module.windows_subsystem_console = flags.present("mconsole");
583 module.linker_rdynamic = flags.present("rdynamic");
584
585 if (flags.single("mmacosx-version-min") != null and flags.single("mios-version-min") != null) {
586 try stderr.write("-mmacosx-version-min and -mios-version-min options not allowed together\n");
587 os.exit(1);
588 }
589
590 if (flags.single("mmacosx-version-min")) |ver| {
591 module.darwin_version_min = Module.DarwinVersionMin { .MacOS = ver };
592 }
593 if (flags.single("mios-version-min")) |ver| {
594 module.darwin_version_min = Module.DarwinVersionMin { .Ios = ver };
595 }
596
597 module.emit_file_type = emit_type;
598 if (flags.many("object")) |objects| {
599 module.link_objects = objects;
600 }
601 if (flags.many("assembly")) |assembly_files| {
602 module.assembly_files = assembly_files;
603 }
604
605 try module.build();
606 try module.link(flags.single("out-file") ?? null);
607
608 if (flags.present("print-timing-info")) {
609 // codegen_print_timing_info(g, stderr);
610 }
611
612 try stderr.print("building {}: {}\n", @tagName(out_type), in_file);
720}613}
721614
722fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {615fn cmdBuildExe(allocator: &Allocator, args: []const []const u8) !void {
723 for (file_paths) |file_path| {616 try buildOutputType(allocator, args, Module.Kind.Exe);
617}
618
619// cmd:build-lib ///////////////////////////////////////////////////////////////////////////////////
620
621fn cmdBuildLib(allocator: &Allocator, args: []const []const u8) !void {
622 try buildOutputType(allocator, args, Module.Kind.Lib);
623}
624
625// cmd:build-obj ///////////////////////////////////////////////////////////////////////////////////
626
627fn cmdBuildObj(allocator: &Allocator, args: []const []const u8) !void {
628 try buildOutputType(allocator, args, Module.Kind.Obj);
629}
630
631// cmd:fmt /////////////////////////////////////////////////////////////////////////////////////////
632
633const usage_fmt =
634 \\usage: zig fmt [file]...
635 \\
636 \\ Formats the input files and modifies them in-place.
637 \\
638 \\Options:
639 \\ --help Print this help and exit
640 \\ --keep-backups Retain backup entries for every file
641 \\
642 \\
643 ;
644
645const args_fmt_spec = []Flag {
646 Flag.Bool("--help"),
647 Flag.Bool("--keep-backups"),
648};
649
650fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void {
651 var flags = try Args.parse(allocator, args_fmt_spec, args);
652 defer flags.deinit();
653
654 if (flags.present("help")) {
655 try stderr.write(usage_fmt);
656 os.exit(0);
657 }
658
659 if (flags.positionals.len == 0) {
660 try stderr.write("expected at least one source file argument\n");
661 os.exit(1);
662 }
663
664 for (flags.positionals.toSliceConst()) |file_path| {
724 var file = try os.File.openRead(allocator, file_path);665 var file = try os.File.openRead(allocator, file_path);
725 defer file.close();666 defer file.close();
726667
727 const source_code = io.readFileAlloc(allocator, file_path) catch |err| {668 const source_code = io.readFileAlloc(allocator, file_path) catch |err| {
728 warn("unable to open '{}': {}", file_path, err);669 try stderr.print("unable to open '{}': {}", file_path, err);
729 continue;670 continue;
730 };671 };
731 defer allocator.free(source_code);672 defer allocator.free(source_code);
732673
733 var tokenizer = std.zig.Tokenizer.init(source_code);674 var tree = std.zig.parse(allocator, source_code) catch |err| {
734 var parser = std.zig.Parser.init(&tokenizer, allocator, file_path);675 try stderr.print("error parsing file '{}': {}\n", file_path, err);
735 defer parser.deinit();676 continue;
736677 };
737 var tree = try parser.parse();
738 defer tree.deinit();678 defer tree.deinit();
739679
680
681 var error_it = tree.errors.iterator(0);
682 while (error_it.next()) |parse_error| {
683 const token = tree.tokens.at(parse_error.loc());
684 const loc = tree.tokenLocation(0, parse_error.loc());
685 try stderr.print("{}:{}:{}: error: ", file_path, loc.line + 1, loc.column + 1);
686 try tree.renderError(parse_error, stderr);
687 try stderr.print("\n{}\n", source_code[loc.line_start..loc.line_end]);
688 {
689 var i: usize = 0;
690 while (i < loc.column) : (i += 1) {
691 try stderr.write(" ");
692 }
693 }
694 {
695 const caret_count = token.end - token.start;
696 var i: usize = 0;
697 while (i < caret_count) : (i += 1) {
698 try stderr.write("~");
699 }
700 }
701 try stderr.write("\n");
702 }
703 if (tree.errors.len != 0) {
704 continue;
705 }
706
707 try stderr.print("{}\n", file_path);
708
740 const baf = try io.BufferedAtomicFile.create(allocator, file_path);709 const baf = try io.BufferedAtomicFile.create(allocator, file_path);
741 defer baf.destroy();710 defer baf.destroy();
742711
743 try parser.renderSource(baf.stream(), tree.root_node);712 try std.zig.render(allocator, baf.stream(), &tree);
713 try baf.finish();
744 }714 }
745}715}
746716
747/// Caller must free result717// cmd:targets /////////////////////////////////////////////////////////////////////////////////////
748fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) ![]u8 {718
749 if (zig_install_prefix_arg) |zig_install_prefix| {719fn cmdTargets(allocator: &Allocator, args: []const []const u8) !void {
750 return testZigInstallPrefix(allocator, zig_install_prefix) catch |err| {720 try stdout.write("Architectures:\n");
751 warn("No Zig installation found at prefix {}: {}\n", zig_install_prefix_arg, @errorName(err));721 {
752 return error.ZigInstallationNotFound;722 comptime var i: usize = 0;
753 };723 inline while (i < @memberCount(builtin.Arch)) : (i += 1) {
754 } else {724 comptime const arch_tag = @memberName(builtin.Arch, i);
755 return findZigLibDir(allocator) catch |err| {725 // NOTE: Cannot use empty string, see #918.
756 warn("Unable to find zig lib directory: {}.\nReinstall Zig or use --zig-install-prefix.\n",726 comptime const native_str =
757 @errorName(err));727 if (comptime mem.eql(u8, arch_tag, @tagName(builtin.arch))) " (native)\n" else "\n";
758 return error.ZigLibDirNotFound;728
759 };729 try stdout.print(" {}{}", arch_tag, native_str);
730 }
731 }
732 try stdout.write("\n");
733
734 try stdout.write("Operating Systems:\n");
735 {
736 comptime var i: usize = 0;
737 inline while (i < @memberCount(builtin.Os)) : (i += 1) {
738 comptime const os_tag = @memberName(builtin.Os, i);
739 // NOTE: Cannot use empty string, see #918.
740 comptime const native_str =
741 if (comptime mem.eql(u8, os_tag, @tagName(builtin.os))) " (native)\n" else "\n";
742
743 try stdout.print(" {}{}", os_tag, native_str);
744 }
745 }
746 try stdout.write("\n");
747
748 try stdout.write("Environments:\n");
749 {
750 comptime var i: usize = 0;
751 inline while (i < @memberCount(builtin.Environ)) : (i += 1) {
752 comptime const environ_tag = @memberName(builtin.Environ, i);
753 // NOTE: Cannot use empty string, see #918.
754 comptime const native_str =
755 if (comptime mem.eql(u8, environ_tag, @tagName(builtin.environ))) " (native)\n" else "\n";
756
757 try stdout.print(" {}{}", environ_tag, native_str);
758 }
760 }759 }
761}760}
762761
763/// Caller must free result762// cmd:version /////////////////////////////////////////////////////////////////////////////////////
764fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {763
765 const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");764fn cmdVersion(allocator: &Allocator, args: []const []const u8) !void {
766 errdefer allocator.free(test_zig_dir);765 try stdout.print("{}\n", std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
766}
767
768// cmd:test ////////////////////////////////////////////////////////////////////////////////////////
769
770const usage_test =
771 \\usage: zig test [file]...
772 \\
773 \\Options:
774 \\ --help Print this help and exit
775 \\
776 \\
777 ;
778
779const args_test_spec = []Flag {
780 Flag.Bool("--help"),
781};
782
783
784fn cmdTest(allocator: &Allocator, args: []const []const u8) !void {
785 var flags = try Args.parse(allocator, args_build_spec, args);
786 defer flags.deinit();
767787
768 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");788 if (flags.present("help")) {
769 defer allocator.free(test_index_file);789 try stderr.write(usage_test);
790 os.exit(0);
791 }
770792
771 var file = try os.File.openRead(allocator, test_index_file);793 if (flags.positionals.len != 1) {
772 file.close();794 try stderr.write("expected exactly one zig source file\n");
795 os.exit(1);
796 }
773797
774 return test_zig_dir;798 // compile the test program into the cache and run
799
800 // NOTE: May be overlap with buildOutput, take the shared part out.
801 try stderr.print("testing file {}\n", flags.positionals.at(0));
775}802}
776803
777/// Caller must free result804// cmd:run /////////////////////////////////////////////////////////////////////////////////////////
778fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {805
779 const self_exe_path = try os.selfExeDirPath(allocator);806// Run should be simple and not expose the full set of arguments provided by build-exe. If specific
780 defer allocator.free(self_exe_path);807// build requirements are need, the user should `build-exe` then `run` manually.
808const usage_run =
809 \\usage: zig run [file] -- <runtime args>
810 \\
811 \\Options:
812 \\ --help Print this help and exit
813 \\
814 \\
815 ;
816
817const args_run_spec = []Flag {
818 Flag.Bool("--help"),
819};
820
781821
782 var cur_path: []const u8 = self_exe_path;822fn cmdRun(allocator: &Allocator, args: []const []const u8) !void {
783 while (true) {823 var compile_args = args;
784 const test_dir = os.path.dirname(cur_path);824 var runtime_args: []const []const u8 = []const []const u8 {};
785825
786 if (mem.eql(u8, test_dir, cur_path)) {826 for (args) |argv, i| {
827 if (mem.eql(u8, argv, "--")) {
828 compile_args = args[0..i];
829 runtime_args = args[i+1..];
787 break;830 break;
788 }831 }
832 }
833 var flags = try Args.parse(allocator, args_run_spec, compile_args);
834 defer flags.deinit();
789835
790 return testZigInstallPrefix(allocator, test_dir) catch |err| {836 if (flags.present("help")) {
791 cur_path = test_dir;837 try stderr.write(usage_run);
792 continue;838 os.exit(0);
793 };839 }
840
841 if (flags.positionals.len != 1) {
842 try stderr.write("expected exactly one zig source file\n");
843 os.exit(1);
844 }
845
846 try stderr.print("runtime args:\n");
847 for (runtime_args) |cargs| {
848 try stderr.print("{}\n", cargs);
849 }
850}
851
852// cmd:translate-c /////////////////////////////////////////////////////////////////////////////////
853
854const usage_translate_c =
855 \\usage: zig translate-c [file]
856 \\
857 \\Options:
858 \\ --help Print this help and exit
859 \\ --enable-timing-info Print timing diagnostics
860 \\ --output [path] Output file to write generated zig file (default: stdout)
861 \\
862 \\
863 ;
864
865const args_translate_c_spec = []Flag {
866 Flag.Bool("--help"),
867 Flag.Bool("--enable-timing-info"),
868 Flag.Arg1("--libc-include-dir"),
869 Flag.Arg1("--output"),
870};
871
872fn cmdTranslateC(allocator: &Allocator, args: []const []const u8) !void {
873 var flags = try Args.parse(allocator, args_translate_c_spec, args);
874 defer flags.deinit();
875
876 if (flags.present("help")) {
877 try stderr.write(usage_translate_c);
878 os.exit(0);
879 }
880
881 if (flags.positionals.len != 1) {
882 try stderr.write("expected exactly one c source file\n");
883 os.exit(1);
884 }
885
886 // set up codegen
887
888 const zig_root_source_file = null;
889
890 // NOTE: translate-c shouldn't require setting up the full codegen instance as it does in
891 // the C++ compiler.
892
893 // codegen_create(g);
894 // codegen_set_out_name(g, null);
895 // codegen_translate_c(g, flags.positional.at(0))
896
897 var output_stream = stdout;
898 if (flags.single("output")) |output_file| {
899 var file = try os.File.openWrite(allocator, output_file);
900 defer file.close();
901
902 var file_stream = io.FileOutStream.init(&file);
903 // TODO: Not being set correctly, still stdout
904 output_stream = &file_stream.stream;
905 }
906
907 // ast_render(g, output_stream, g->root_import->root, 4);
908 try output_stream.write("pub const example = 10;\n");
909
910 if (flags.present("enable-timing-info")) {
911 // codegen_print_timing_info(g, stdout);
912 try stderr.write("printing timing info for translate-c\n");
794 }913 }
914}
915
916// cmd:help ////////////////////////////////////////////////////////////////////////////////////////
917
918fn cmdHelp(allocator: &Allocator, args: []const []const u8) !void {
919 try stderr.write(usage);
920}
921
922// cmd:zen /////////////////////////////////////////////////////////////////////////////////////////
923
924const info_zen =
925 \\
926 \\ * Communicate intent precisely.
927 \\ * Edge cases matter.
928 \\ * Favor reading code over writing code.
929 \\ * Only one obvious way to do things.
930 \\ * Runtime crashes are better than bugs.
931 \\ * Compile errors are better than runtime crashes.
932 \\ * Incremental improvements.
933 \\ * Avoid local maximums.
934 \\ * Reduce the amount one must remember.
935 \\ * Minimize energy spent on coding style.
936 \\ * Together we serve end users.
937 \\
938 \\
939 ;
940
941fn cmdZen(allocator: &Allocator, args: []const []const u8) !void {
942 try stdout.write(info_zen);
943}
944
945// cmd:internal ////////////////////////////////////////////////////////////////////////////////////
946
947const usage_internal =
948 \\usage: zig internal [subcommand]
949 \\
950 \\Sub-Commands:
951 \\ build-info Print static compiler build-info
952 \\
953 \\
954 ;
955
956fn cmdInternal(allocator: &Allocator, args: []const []const u8) !void {
957 if (args.len == 0) {
958 try stderr.write(usage_internal);
959 os.exit(1);
960 }
961
962 const sub_commands = []Command {
963 Command { .name = "build-info", .exec = cmdInternalBuildInfo },
964 };
795965
796 // TODO look in hard coded installation path from configuration966 for (sub_commands) |sub_command| {
797 //if (ZIG_INSTALL_PREFIX != nullptr) {967 if (mem.eql(u8, sub_command.name, args[0])) {
798 // if (test_zig_install_prefix(buf_create_from_str(ZIG_INSTALL_PREFIX), out_path)) {968 try sub_command.exec(allocator, args[1..]);
799 // return 0;969 return;
800 // }970 }
801 //}971 }
802972
803 return error.FileNotFound;973 try stderr.print("unknown sub command: {}\n\n", args[0]);
974 try stderr.write(usage_internal);
975}
976
977fn cmdInternalBuildInfo(allocator: &Allocator, args: []const []const u8) !void {
978 try stdout.print(
979 \\ZIG_CMAKE_BINARY_DIR {}
980 \\ZIG_CXX_COMPILER {}
981 \\ZIG_LLVM_CONFIG_EXE {}
982 \\ZIG_LLD_INCLUDE_PATH {}
983 \\ZIG_LLD_LIBRARIES {}
984 \\ZIG_STD_FILES {}
985 \\ZIG_C_HEADER_FILES {}
986 \\ZIG_DIA_GUIDS_LIB {}
987 \\
988 ,
989 std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR),
990 std.cstr.toSliceConst(c.ZIG_CXX_COMPILER),
991 std.cstr.toSliceConst(c.ZIG_LLVM_CONFIG_EXE),
992 std.cstr.toSliceConst(c.ZIG_LLD_INCLUDE_PATH),
993 std.cstr.toSliceConst(c.ZIG_LLD_LIBRARIES),
994 std.cstr.toSliceConst(c.ZIG_STD_FILES),
995 std.cstr.toSliceConst(c.ZIG_C_HEADER_FILES),
996 std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB),
997 );
804}998}
src-self-hosted/module.zig+25-21
...@@ -8,9 +8,7 @@ const c = @import("c.zig");...@@ -8,9 +8,7 @@ const c = @import("c.zig");
8const builtin = @import("builtin");8const builtin = @import("builtin");
9const Target = @import("target.zig").Target;9const Target = @import("target.zig").Target;
10const warn = std.debug.warn;10const warn = std.debug.warn;
11const Tokenizer = std.zig.Tokenizer;
12const Token = std.zig.Token;11const Token = std.zig.Token;
13const Parser = std.zig.Parser;
14const ArrayList = std.ArrayList;12const ArrayList = std.ArrayList;
1513
16pub const Module = struct {14pub const Module = struct {
...@@ -109,6 +107,29 @@ pub const Module = struct {...@@ -109,6 +107,29 @@ pub const Module = struct {
109 LlvmIr,107 LlvmIr,
110 };108 };
111109
110 pub const CliPkg = struct {
111 name: []const u8,
112 path: []const u8,
113 children: ArrayList(&CliPkg),
114 parent: ?&CliPkg,
115
116 pub fn init(allocator: &mem.Allocator, name: []const u8, path: []const u8, parent: ?&CliPkg) !&CliPkg {
117 var pkg = try allocator.create(CliPkg);
118 pkg.name = name;
119 pkg.path = path;
120 pkg.children = ArrayList(&CliPkg).init(allocator);
121 pkg.parent = parent;
122 return pkg;
123 }
124
125 pub fn deinit(self: &CliPkg) void {
126 for (self.children.toSliceConst()) |child| {
127 child.deinit();
128 }
129 self.children.deinit();
130 }
131 };
132
112 pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target,133 pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target,
113 kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module134 kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module
114 {135 {
...@@ -223,34 +244,17 @@ pub const Module = struct {...@@ -223,34 +244,17 @@ pub const Module = struct {
223244
224 warn("{}", source_code);245 warn("{}", source_code);
225246
226 warn("====tokenization:====\n");
227 {
228 var tokenizer = Tokenizer.init(source_code);
229 while (true) {
230 const token = tokenizer.next();
231 tokenizer.dump(token);
232 if (token.id == Token.Id.Eof) {
233 break;
234 }
235 }
236 }
237
238 warn("====parse:====\n");247 warn("====parse:====\n");
239248
240 var tokenizer = Tokenizer.init(source_code);249 var tree = try std.zig.parse(self.allocator, source_code);
241 var parser = Parser.init(&tokenizer, self.allocator, root_src_real_path);
242 defer parser.deinit();
243
244 var tree = try parser.parse();
245 defer tree.deinit();250 defer tree.deinit();
246251
247 var stderr_file = try std.io.getStdErr();252 var stderr_file = try std.io.getStdErr();
248 var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);253 var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
249 const out_stream = &stderr_file_out_stream.stream;254 const out_stream = &stderr_file_out_stream.stream;
250 try parser.renderAst(out_stream, tree.root_node);
251255
252 warn("====fmt:====\n");256 warn("====fmt:====\n");
253 try parser.renderSource(out_stream, tree.root_node);257 try std.zig.render(self.allocator, out_stream, &tree);
254258
255 warn("====ir:====\n");259 warn("====ir:====\n");
256 warn("TODO\n\n");260 warn("TODO\n\n");
src/all_types.hpp+90-15
...@@ -359,7 +359,6 @@ enum NodeType {...@@ -359,7 +359,6 @@ enum NodeType {
359 NodeTypeRoot,359 NodeTypeRoot,
360 NodeTypeFnProto,360 NodeTypeFnProto,
361 NodeTypeFnDef,361 NodeTypeFnDef,
362 NodeTypeFnDecl,
363 NodeTypeParamDecl,362 NodeTypeParamDecl,
364 NodeTypeBlock,363 NodeTypeBlock,
365 NodeTypeGroupedExpr,364 NodeTypeGroupedExpr,
...@@ -453,10 +452,6 @@ struct AstNodeFnDef {...@@ -453,10 +452,6 @@ struct AstNodeFnDef {
453 AstNode *body;452 AstNode *body;
454};453};
455454
456struct AstNodeFnDecl {
457 AstNode *fn_proto;
458};
459
460struct AstNodeParamDecl {455struct AstNodeParamDecl {
461 Buf *name;456 Buf *name;
462 AstNode *type;457 AstNode *type;
...@@ -713,10 +708,6 @@ struct AstNodeSwitchRange {...@@ -713,10 +708,6 @@ struct AstNodeSwitchRange {
713 AstNode *end;708 AstNode *end;
714};709};
715710
716struct AstNodeLabel {
717 Buf *name;
718};
719
720struct AstNodeCompTime {711struct AstNodeCompTime {
721 AstNode *expr;712 AstNode *expr;
722};713};
...@@ -876,6 +867,7 @@ struct AstNodeAwaitExpr {...@@ -876,6 +867,7 @@ struct AstNodeAwaitExpr {
876};867};
877868
878struct AstNodeSuspend {869struct AstNodeSuspend {
870 Buf *name;
879 AstNode *block;871 AstNode *block;
880 AstNode *promise_symbol;872 AstNode *promise_symbol;
881};873};
...@@ -892,7 +884,6 @@ struct AstNode {...@@ -892,7 +884,6 @@ struct AstNode {
892 union {884 union {
893 AstNodeRoot root;885 AstNodeRoot root;
894 AstNodeFnDef fn_def;886 AstNodeFnDef fn_def;
895 AstNodeFnDecl fn_decl;
896 AstNodeFnProto fn_proto;887 AstNodeFnProto fn_proto;
897 AstNodeParamDecl param_decl;888 AstNodeParamDecl param_decl;
898 AstNodeBlock block;889 AstNodeBlock block;
...@@ -917,7 +908,6 @@ struct AstNode {...@@ -917,7 +908,6 @@ struct AstNode {
917 AstNodeSwitchExpr switch_expr;908 AstNodeSwitchExpr switch_expr;
918 AstNodeSwitchProng switch_prong;909 AstNodeSwitchProng switch_prong;
919 AstNodeSwitchRange switch_range;910 AstNodeSwitchRange switch_range;
920 AstNodeLabel label;
921 AstNodeCompTime comptime_expr;911 AstNodeCompTime comptime_expr;
922 AstNodeAsmExpr asm_expr;912 AstNodeAsmExpr asm_expr;
923 AstNodeFieldAccessExpr field_access_expr;913 AstNodeFieldAccessExpr field_access_expr;
...@@ -1302,6 +1292,8 @@ enum BuiltinFnId {...@@ -1302,6 +1292,8 @@ enum BuiltinFnId {
1302 BuiltinFnIdMemberCount,1292 BuiltinFnIdMemberCount,
1303 BuiltinFnIdMemberType,1293 BuiltinFnIdMemberType,
1304 BuiltinFnIdMemberName,1294 BuiltinFnIdMemberName,
1295 BuiltinFnIdField,
1296 BuiltinFnIdTypeInfo,
1305 BuiltinFnIdTypeof,1297 BuiltinFnIdTypeof,
1306 BuiltinFnIdAddWithOverflow,1298 BuiltinFnIdAddWithOverflow,
1307 BuiltinFnIdSubWithOverflow,1299 BuiltinFnIdSubWithOverflow,
...@@ -1321,13 +1313,15 @@ enum BuiltinFnId {...@@ -1321,13 +1313,15 @@ enum BuiltinFnId {
1321 BuiltinFnIdReturnAddress,1313 BuiltinFnIdReturnAddress,
1322 BuiltinFnIdFrameAddress,1314 BuiltinFnIdFrameAddress,
1323 BuiltinFnIdEmbedFile,1315 BuiltinFnIdEmbedFile,
1324 BuiltinFnIdCmpExchange,1316 BuiltinFnIdCmpxchgWeak,
1317 BuiltinFnIdCmpxchgStrong,
1325 BuiltinFnIdFence,1318 BuiltinFnIdFence,
1326 BuiltinFnIdDivExact,1319 BuiltinFnIdDivExact,
1327 BuiltinFnIdDivTrunc,1320 BuiltinFnIdDivTrunc,
1328 BuiltinFnIdDivFloor,1321 BuiltinFnIdDivFloor,
1329 BuiltinFnIdRem,1322 BuiltinFnIdRem,
1330 BuiltinFnIdMod,1323 BuiltinFnIdMod,
1324 BuiltinFnIdSqrt,
1331 BuiltinFnIdTruncate,1325 BuiltinFnIdTruncate,
1332 BuiltinFnIdIntType,1326 BuiltinFnIdIntType,
1333 BuiltinFnIdSetCold,1327 BuiltinFnIdSetCold,
...@@ -1357,6 +1351,7 @@ enum BuiltinFnId {...@@ -1357,6 +1351,7 @@ enum BuiltinFnId {
1357 BuiltinFnIdExport,1351 BuiltinFnIdExport,
1358 BuiltinFnIdErrorReturnTrace,1352 BuiltinFnIdErrorReturnTrace,
1359 BuiltinFnIdAtomicRmw,1353 BuiltinFnIdAtomicRmw,
1354 BuiltinFnIdAtomicLoad,
1360};1355};
13611356
1362struct BuiltinFnEntry {1357struct BuiltinFnEntry {
...@@ -1424,6 +1419,7 @@ enum ZigLLVMFnId {...@@ -1424,6 +1419,7 @@ enum ZigLLVMFnId {
1424 ZigLLVMFnIdOverflowArithmetic,1419 ZigLLVMFnIdOverflowArithmetic,
1425 ZigLLVMFnIdFloor,1420 ZigLLVMFnIdFloor,
1426 ZigLLVMFnIdCeil,1421 ZigLLVMFnIdCeil,
1422 ZigLLVMFnIdSqrt,
1427};1423};
14281424
1429enum AddSubMul {1425enum AddSubMul {
...@@ -1444,7 +1440,7 @@ struct ZigLLVMFnKey {...@@ -1444,7 +1440,7 @@ struct ZigLLVMFnKey {
1444 } clz;1440 } clz;
1445 struct {1441 struct {
1446 uint32_t bit_count;1442 uint32_t bit_count;
1447 } floor_ceil;1443 } floating;
1448 struct {1444 struct {
1449 AddSubMul add_sub_mul;1445 AddSubMul add_sub_mul;
1450 uint32_t bit_count;1446 uint32_t bit_count;
...@@ -1465,6 +1461,7 @@ enum BuildMode {...@@ -1465,6 +1461,7 @@ enum BuildMode {
1465 BuildModeDebug,1461 BuildModeDebug,
1466 BuildModeFastRelease,1462 BuildModeFastRelease,
1467 BuildModeSafeRelease,1463 BuildModeSafeRelease,
1464 BuildModeSmallRelease,
1468};1465};
14691466
1470enum EmitFileType {1467enum EmitFileType {
...@@ -1510,6 +1507,7 @@ struct CodeGen {...@@ -1510,6 +1507,7 @@ struct CodeGen {
1510 HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> exported_symbol_names;1507 HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> exported_symbol_names;
1511 HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> external_prototypes;1508 HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> external_prototypes;
1512 HashMap<Buf *, ConstExprValue *, buf_hash, buf_eql_buf> string_literals_table;1509 HashMap<Buf *, ConstExprValue *, buf_hash, buf_eql_buf> string_literals_table;
1510 HashMap<const TypeTableEntry *, ConstExprValue *, type_ptr_hash, type_ptr_eql> type_info_cache;
15131511
15141512
1515 ZigList<ImportTableEntry *> import_queue;1513 ZigList<ImportTableEntry *> import_queue;
...@@ -1656,6 +1654,8 @@ struct CodeGen {...@@ -1656,6 +1654,8 @@ struct CodeGen {
1656 LLVMValueRef coro_save_fn_val;1654 LLVMValueRef coro_save_fn_val;
1657 LLVMValueRef coro_promise_fn_val;1655 LLVMValueRef coro_promise_fn_val;
1658 LLVMValueRef coro_alloc_helper_fn_val;1656 LLVMValueRef coro_alloc_helper_fn_val;
1657 LLVMValueRef merge_err_ret_traces_fn_val;
1658 LLVMValueRef add_error_return_trace_addr_fn_val;
1659 bool error_during_imports;1659 bool error_during_imports;
16601660
1661 const char **clang_argv;1661 const char **clang_argv;
...@@ -1709,6 +1709,8 @@ struct CodeGen {...@@ -1709,6 +1709,8 @@ struct CodeGen {
1709 ZigList<ZigLLVMDIType **> error_di_types;1709 ZigList<ZigLLVMDIType **> error_di_types;
17101710
1711 ZigList<Buf *> forbidden_libs;1711 ZigList<Buf *> forbidden_libs;
1712
1713 bool no_rosegment_workaround;
1712};1714};
17131715
1714enum VarLinkage {1716enum VarLinkage {
...@@ -1759,6 +1761,7 @@ enum ScopeId {...@@ -1759,6 +1761,7 @@ enum ScopeId {
1759 ScopeIdVarDecl,1761 ScopeIdVarDecl,
1760 ScopeIdCImport,1762 ScopeIdCImport,
1761 ScopeIdLoop,1763 ScopeIdLoop,
1764 ScopeIdSuspend,
1762 ScopeIdFnDef,1765 ScopeIdFnDef,
1763 ScopeIdCompTime,1766 ScopeIdCompTime,
1764 ScopeIdCoroPrelude,1767 ScopeIdCoroPrelude,
...@@ -1854,6 +1857,17 @@ struct ScopeLoop {...@@ -1854,6 +1857,17 @@ struct ScopeLoop {
1854 ZigList<IrBasicBlock *> *incoming_blocks;1857 ZigList<IrBasicBlock *> *incoming_blocks;
1855};1858};
18561859
1860// This scope is created for a suspend block in order to have labeled
1861// suspend for breaking out of a suspend and for detecting if a suspend
1862// block is inside a suspend block.
1863struct ScopeSuspend {
1864 Scope base;
1865
1866 Buf *name;
1867 IrBasicBlock *resume_block;
1868 bool reported_err;
1869};
1870
1857// This scope is created for a comptime expression.1871// This scope is created for a comptime expression.
1858// NodeTypeCompTime, NodeTypeSwitchExpr1872// NodeTypeCompTime, NodeTypeSwitchExpr
1859struct ScopeCompTime {1873struct ScopeCompTime {
...@@ -2025,6 +2039,7 @@ enum IrInstructionId {...@@ -2025,6 +2039,7 @@ enum IrInstructionId {
2025 IrInstructionIdTagType,2039 IrInstructionIdTagType,
2026 IrInstructionIdFieldParentPtr,2040 IrInstructionIdFieldParentPtr,
2027 IrInstructionIdOffsetOf,2041 IrInstructionIdOffsetOf,
2042 IrInstructionIdTypeInfo,
2028 IrInstructionIdTypeId,2043 IrInstructionIdTypeId,
2029 IrInstructionIdSetEvalBranchQuota,2044 IrInstructionIdSetEvalBranchQuota,
2030 IrInstructionIdPtrTypeOf,2045 IrInstructionIdPtrTypeOf,
...@@ -2050,10 +2065,14 @@ enum IrInstructionId {...@@ -2050,10 +2065,14 @@ enum IrInstructionId {
2050 IrInstructionIdCoroPromise,2065 IrInstructionIdCoroPromise,
2051 IrInstructionIdCoroAllocHelper,2066 IrInstructionIdCoroAllocHelper,
2052 IrInstructionIdAtomicRmw,2067 IrInstructionIdAtomicRmw,
2068 IrInstructionIdAtomicLoad,
2053 IrInstructionIdPromiseResultType,2069 IrInstructionIdPromiseResultType,
2054 IrInstructionIdAwaitBookkeeping,2070 IrInstructionIdAwaitBookkeeping,
2055 IrInstructionIdSaveErrRetAddr,2071 IrInstructionIdSaveErrRetAddr,
2056 IrInstructionIdAddImplicitReturnType,2072 IrInstructionIdAddImplicitReturnType,
2073 IrInstructionIdMergeErrRetTraces,
2074 IrInstructionIdMarkErrRetTracePtr,
2075 IrInstructionIdSqrt,
2057};2076};
20582077
2059struct IrInstruction {2078struct IrInstruction {
...@@ -2210,7 +2229,8 @@ struct IrInstructionFieldPtr {...@@ -2210,7 +2229,8 @@ struct IrInstructionFieldPtr {
2210 IrInstruction base;2229 IrInstruction base;
22112230
2212 IrInstruction *container_ptr;2231 IrInstruction *container_ptr;
2213 Buf *field_name;2232 Buf *field_name_buffer;
2233 IrInstruction *field_name_expr;
2214 bool is_const;2234 bool is_const;
2215};2235};
22162236
...@@ -2529,6 +2549,7 @@ struct IrInstructionEmbedFile {...@@ -2529,6 +2549,7 @@ struct IrInstructionEmbedFile {
2529struct IrInstructionCmpxchg {2549struct IrInstructionCmpxchg {
2530 IrInstruction base;2550 IrInstruction base;
25312551
2552 IrInstruction *type_value;
2532 IrInstruction *ptr;2553 IrInstruction *ptr;
2533 IrInstruction *cmp_value;2554 IrInstruction *cmp_value;
2534 IrInstruction *new_value;2555 IrInstruction *new_value;
...@@ -2536,8 +2557,13 @@ struct IrInstructionCmpxchg {...@@ -2536,8 +2557,13 @@ struct IrInstructionCmpxchg {
2536 IrInstruction *failure_order_value;2557 IrInstruction *failure_order_value;
25372558
2538 // if this instruction gets to runtime then we know these values:2559 // if this instruction gets to runtime then we know these values:
2560 TypeTableEntry *type;
2539 AtomicOrder success_order;2561 AtomicOrder success_order;
2540 AtomicOrder failure_order;2562 AtomicOrder failure_order;
2563
2564 bool is_weak;
2565
2566 LLVMValueRef tmp_ptr;
2541};2567};
25422568
2543struct IrInstructionFence {2569struct IrInstructionFence {
...@@ -2835,6 +2861,12 @@ struct IrInstructionOffsetOf {...@@ -2835,6 +2861,12 @@ struct IrInstructionOffsetOf {
2835 IrInstruction *field_name;2861 IrInstruction *field_name;
2836};2862};
28372863
2864struct IrInstructionTypeInfo {
2865 IrInstruction base;
2866
2867 IrInstruction *type_value;
2868};
2869
2838struct IrInstructionTypeId {2870struct IrInstructionTypeId {
2839 IrInstruction base;2871 IrInstruction base;
28402872
...@@ -2892,6 +2924,11 @@ struct IrInstructionExport {...@@ -2892,6 +2924,11 @@ struct IrInstructionExport {
28922924
2893struct IrInstructionErrorReturnTrace {2925struct IrInstructionErrorReturnTrace {
2894 IrInstruction base;2926 IrInstruction base;
2927
2928 enum Nullable {
2929 Null,
2930 NonNull,
2931 } nullable;
2895};2932};
28962933
2897struct IrInstructionErrorUnion {2934struct IrInstructionErrorUnion {
...@@ -3002,6 +3039,15 @@ struct IrInstructionAtomicRmw {...@@ -3002,6 +3039,15 @@ struct IrInstructionAtomicRmw {
3002 AtomicOrder resolved_ordering;3039 AtomicOrder resolved_ordering;
3003};3040};
30043041
3042struct IrInstructionAtomicLoad {
3043 IrInstruction base;
3044
3045 IrInstruction *operand_type;
3046 IrInstruction *ptr;
3047 IrInstruction *ordering;
3048 AtomicOrder resolved_ordering;
3049};
3050
3005struct IrInstructionPromiseResultType {3051struct IrInstructionPromiseResultType {
3006 IrInstruction base;3052 IrInstruction base;
30073053
...@@ -3024,6 +3070,27 @@ struct IrInstructionAddImplicitReturnType {...@@ -3024,6 +3070,27 @@ struct IrInstructionAddImplicitReturnType {
3024 IrInstruction *value;3070 IrInstruction *value;
3025};3071};
30263072
3073struct IrInstructionMergeErrRetTraces {
3074 IrInstruction base;
3075
3076 IrInstruction *coro_promise_ptr;
3077 IrInstruction *src_err_ret_trace_ptr;
3078 IrInstruction *dest_err_ret_trace_ptr;
3079};
3080
3081struct IrInstructionMarkErrRetTracePtr {
3082 IrInstruction base;
3083
3084 IrInstruction *err_ret_trace_ptr;
3085};
3086
3087struct IrInstructionSqrt {
3088 IrInstruction base;
3089
3090 IrInstruction *type;
3091 IrInstruction *op;
3092};
3093
3027static const size_t slice_ptr_index = 0;3094static const size_t slice_ptr_index = 0;
3028static const size_t slice_len_index = 1;3095static const size_t slice_len_index = 1;
30293096
...@@ -3033,10 +3100,18 @@ static const size_t maybe_null_index = 1;...@@ -3033,10 +3100,18 @@ static const size_t maybe_null_index = 1;
3033static const size_t err_union_err_index = 0;3100static const size_t err_union_err_index = 0;
3034static const size_t err_union_payload_index = 1;3101static const size_t err_union_payload_index = 1;
30353102
3103// TODO call graph analysis to find out what this number needs to be for every function
3104static const size_t stack_trace_ptr_count = 30;
3105
3106// these belong to the async function
3107#define RETURN_ADDRESSES_FIELD_NAME "return_addresses"
3108#define ERR_RET_TRACE_FIELD_NAME "err_ret_trace"
3109#define RESULT_FIELD_NAME "result"
3036#define ASYNC_ALLOC_FIELD_NAME "allocFn"3110#define ASYNC_ALLOC_FIELD_NAME "allocFn"
3037#define ASYNC_FREE_FIELD_NAME "freeFn"3111#define ASYNC_FREE_FIELD_NAME "freeFn"
3038#define AWAITER_HANDLE_FIELD_NAME "awaiter_handle"3112#define AWAITER_HANDLE_FIELD_NAME "awaiter_handle"
3039#define RESULT_FIELD_NAME "result"3113// these point to data belonging to the awaiter
3114#define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr"
3040#define RESULT_PTR_FIELD_NAME "result_ptr"3115#define RESULT_PTR_FIELD_NAME "result_ptr"
30413116
30423117
src/analyze.cpp+75-26
...@@ -156,6 +156,14 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {...@@ -156,6 +156,14 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {
156 return scope;156 return scope;
157}157}
158158
159ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) {
160 assert(node->type == NodeTypeSuspend);
161 ScopeSuspend *scope = allocate<ScopeSuspend>(1);
162 init_scope(&scope->base, ScopeIdSuspend, node, parent);
163 scope->name = node->data.suspend.name;
164 return scope;
165}
166
159ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) {167ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) {
160 ScopeFnDef *scope = allocate<ScopeFnDef>(1);168 ScopeFnDef *scope = allocate<ScopeFnDef>(1);
161 init_scope(&scope->base, ScopeIdFnDef, node, parent);169 init_scope(&scope->base, ScopeIdFnDef, node, parent);
...@@ -468,10 +476,30 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)...@@ -468,10 +476,30 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type)
468476
469 TypeTableEntry *awaiter_handle_type = get_maybe_type(g, g->builtin_types.entry_promise);477 TypeTableEntry *awaiter_handle_type = get_maybe_type(g, g->builtin_types.entry_promise);
470 TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false);478 TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false);
471 const char *field_names[] = {AWAITER_HANDLE_FIELD_NAME, RESULT_FIELD_NAME, RESULT_PTR_FIELD_NAME};479
472 TypeTableEntry *field_types[] = {awaiter_handle_type, return_type, result_ptr_type};480 ZigList<const char *> field_names = {};
481 field_names.append(AWAITER_HANDLE_FIELD_NAME);
482 field_names.append(RESULT_FIELD_NAME);
483 field_names.append(RESULT_PTR_FIELD_NAME);
484 if (g->have_err_ret_tracing) {
485 field_names.append(ERR_RET_TRACE_PTR_FIELD_NAME);
486 field_names.append(ERR_RET_TRACE_FIELD_NAME);
487 field_names.append(RETURN_ADDRESSES_FIELD_NAME);
488 }
489
490 ZigList<TypeTableEntry *> field_types = {};
491 field_types.append(awaiter_handle_type);
492 field_types.append(return_type);
493 field_types.append(result_ptr_type);
494 if (g->have_err_ret_tracing) {
495 field_types.append(get_ptr_to_stack_trace_type(g));
496 field_types.append(g->stack_trace_type);
497 field_types.append(get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count));
498 }
499
500 assert(field_names.length == field_types.length);
473 Buf *name = buf_sprintf("AsyncFramePromise(%s)", buf_ptr(&return_type->name));501 Buf *name = buf_sprintf("AsyncFramePromise(%s)", buf_ptr(&return_type->name));
474 TypeTableEntry *entry = get_struct_type(g, buf_ptr(name), field_names, field_types, 3);502 TypeTableEntry *entry = get_struct_type(g, buf_ptr(name), field_names.items, field_types.items, field_names.length);
475503
476 return_type->promise_frame_parent = entry;504 return_type->promise_frame_parent = entry;
477 return entry;505 return entry;
...@@ -1230,7 +1258,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou...@@ -1230,7 +1258,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou
1230 }1258 }
12311259
1232 fn_type_id->param_count = fn_proto->params.length;1260 fn_type_id->param_count = fn_proto->params.length;
1233 fn_type_id->param_info = allocate_nonzero<FnTypeParamInfo>(param_count_alloc);1261 fn_type_id->param_info = allocate<FnTypeParamInfo>(param_count_alloc);
1234 fn_type_id->next_param_index = 0;1262 fn_type_id->next_param_index = 0;
1235 fn_type_id->is_var_args = fn_proto->is_var_args;1263 fn_type_id->is_var_args = fn_proto->is_var_args;
1236}1264}
...@@ -2297,8 +2325,14 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {...@@ -2297,8 +2325,14 @@ static void resolve_enum_zero_bits(CodeGen *g, TypeTableEntry *enum_type) {
2297 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};2325 HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {};
2298 occupied_tag_values.init(field_count);2326 occupied_tag_values.init(field_count);
22992327
2300 TypeTableEntry *tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);2328 TypeTableEntry *tag_int_type;
2329 if (enum_type->data.enumeration.layout == ContainerLayoutExtern) {
2330 tag_int_type = get_c_int_type(g, CIntTypeInt);
2331 } else {
2332 tag_int_type = get_smallest_unsigned_int_type(g, field_count - 1);
2333 }
23012334
2335 // TODO: Are extern enums allowed to have an init_arg_expr?
2302 if (decl_node->data.container_decl.init_arg_expr != nullptr) {2336 if (decl_node->data.container_decl.init_arg_expr != nullptr) {
2303 TypeTableEntry *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);2337 TypeTableEntry *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr);
2304 if (type_is_invalid(wanted_tag_int_type)) {2338 if (type_is_invalid(wanted_tag_int_type)) {
...@@ -3216,7 +3250,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {...@@ -3216,7 +3250,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
3216 break;3250 break;
3217 case NodeTypeContainerDecl:3251 case NodeTypeContainerDecl:
3218 case NodeTypeParamDecl:3252 case NodeTypeParamDecl:
3219 case NodeTypeFnDecl:
3220 case NodeTypeReturnExpr:3253 case NodeTypeReturnExpr:
3221 case NodeTypeDefer:3254 case NodeTypeDefer:
3222 case NodeTypeBlock:3255 case NodeTypeBlock:
...@@ -3597,6 +3630,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) {...@@ -3597,6 +3630,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) {
3597 case ScopeIdVarDecl:3630 case ScopeIdVarDecl:
3598 case ScopeIdCImport:3631 case ScopeIdCImport:
3599 case ScopeIdLoop:3632 case ScopeIdLoop:
3633 case ScopeIdSuspend:
3600 case ScopeIdCompTime:3634 case ScopeIdCompTime:
3601 case ScopeIdCoroPrelude:3635 case ScopeIdCoroPrelude:
3602 scope = scope->parent;3636 scope = scope->parent;
...@@ -4278,7 +4312,8 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {...@@ -4278,7 +4312,8 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
4278static ZigWindowsSDK *get_windows_sdk(CodeGen *g) {4312static ZigWindowsSDK *get_windows_sdk(CodeGen *g) {
4279 if (g->win_sdk == nullptr) {4313 if (g->win_sdk == nullptr) {
4280 if (os_find_windows_sdk(&g->win_sdk)) {4314 if (os_find_windows_sdk(&g->win_sdk)) {
4281 zig_panic("Unable to determine Windows SDK path.");4315 fprintf(stderr, "unable to determine windows sdk path\n");
4316 exit(1);
4282 }4317 }
4283 }4318 }
4284 assert(g->win_sdk != nullptr);4319 assert(g->win_sdk != nullptr);
...@@ -4380,7 +4415,8 @@ void find_libc_include_path(CodeGen *g) {...@@ -4380,7 +4415,8 @@ void find_libc_include_path(CodeGen *g) {
4380 ZigWindowsSDK *sdk = get_windows_sdk(g);4415 ZigWindowsSDK *sdk = get_windows_sdk(g);
4381 g->libc_include_dir = buf_alloc();4416 g->libc_include_dir = buf_alloc();
4382 if (os_get_win32_ucrt_include_path(sdk, g->libc_include_dir)) {4417 if (os_get_win32_ucrt_include_path(sdk, g->libc_include_dir)) {
4383 zig_panic("Unable to determine libc include path.");4418 fprintf(stderr, "Unable to determine libc include path. --libc-include-dir");
4419 exit(1);
4384 }4420 }
4385 } else if (g->zig_target.os == OsLinux) {4421 } else if (g->zig_target.os == OsLinux) {
4386 g->libc_include_dir = get_linux_libc_include_path();4422 g->libc_include_dir = get_linux_libc_include_path();
...@@ -4402,24 +4438,33 @@ void find_libc_lib_path(CodeGen *g) {...@@ -4402,24 +4438,33 @@ void find_libc_lib_path(CodeGen *g) {
4402 if (g->zig_target.os == OsWindows) {4438 if (g->zig_target.os == OsWindows) {
4403 ZigWindowsSDK *sdk = get_windows_sdk(g);4439 ZigWindowsSDK *sdk = get_windows_sdk(g);
44044440
4405 Buf* vc_lib_dir = buf_alloc();4441 if (g->msvc_lib_dir == nullptr) {
4406 if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) {4442 Buf* vc_lib_dir = buf_alloc();
4407 zig_panic("Unable to determine vcruntime path.");4443 if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) {
4444 fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir");
4445 exit(1);
4446 }
4447 g->msvc_lib_dir = vc_lib_dir;
4408 }4448 }
44094449
4410 Buf* ucrt_lib_path = buf_alloc();4450 if (g->libc_lib_dir == nullptr) {
4411 if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) {4451 Buf* ucrt_lib_path = buf_alloc();
4412 zig_panic("Unable to determine ucrt path.");4452 if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) {
4453 fprintf(stderr, "Unable to determine ucrt path. --libc-lib-dir");
4454 exit(1);
4455 }
4456 g->libc_lib_dir = ucrt_lib_path;
4413 }4457 }
44144458
4415 Buf* kern_lib_path = buf_alloc();4459 if (g->kernel32_lib_dir == nullptr) {
4416 if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) {4460 Buf* kern_lib_path = buf_alloc();
4417 zig_panic("Unable to determine kernel32 path.");4461 if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) {
4462 fprintf(stderr, "Unable to determine kernel32 path. --kernel32-lib-dir");
4463 exit(1);
4464 }
4465 g->kernel32_lib_dir = kern_lib_path;
4418 }4466 }
44194467
4420 g->msvc_lib_dir = vc_lib_dir;
4421 g->libc_lib_dir = ucrt_lib_path;
4422 g->kernel32_lib_dir = kern_lib_path;
4423 } else if (g->zig_target.os == OsLinux) {4468 } else if (g->zig_target.os == OsLinux) {
4424 g->libc_lib_dir = get_linux_libc_lib_path("crt1.o");4469 g->libc_lib_dir = get_linux_libc_lib_path("crt1.o");
4425 } else {4470 } else {
...@@ -5782,9 +5827,11 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {...@@ -5782,9 +5827,11 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
5782 case ZigLLVMFnIdClz:5827 case ZigLLVMFnIdClz:
5783 return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;5828 return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;
5784 case ZigLLVMFnIdFloor:5829 case ZigLLVMFnIdFloor:
5785 return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1899859168;5830 return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168;
5786 case ZigLLVMFnIdCeil:5831 case ZigLLVMFnIdCeil:
5787 return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1953839089;5832 return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1953839089;
5833 case ZigLLVMFnIdSqrt:
5834 return (uint32_t)(x.data.floating.bit_count) * (uint32_t)2225366385;
5788 case ZigLLVMFnIdOverflowArithmetic:5835 case ZigLLVMFnIdOverflowArithmetic:
5789 return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +5836 return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
5790 ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +5837 ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +
...@@ -5803,7 +5850,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {...@@ -5803,7 +5850,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
5803 return a.data.clz.bit_count == b.data.clz.bit_count;5850 return a.data.clz.bit_count == b.data.clz.bit_count;
5804 case ZigLLVMFnIdFloor:5851 case ZigLLVMFnIdFloor:
5805 case ZigLLVMFnIdCeil:5852 case ZigLLVMFnIdCeil:
5806 return a.data.floor_ceil.bit_count == b.data.floor_ceil.bit_count;5853 case ZigLLVMFnIdSqrt:
5854 return a.data.floating.bit_count == b.data.floating.bit_count;
5807 case ZigLLVMFnIdOverflowArithmetic:5855 case ZigLLVMFnIdOverflowArithmetic:
5808 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&5856 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
5809 (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&5857 (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
...@@ -5883,8 +5931,8 @@ size_t type_id_len() {...@@ -5883,8 +5931,8 @@ size_t type_id_len() {
5883 return array_length(all_type_ids);5931 return array_length(all_type_ids);
5884}5932}
58855933
5886size_t type_id_index(TypeTableEntryId id) {5934size_t type_id_index(TypeTableEntry *entry) {
5887 switch (id) {5935 switch (entry->id) {
5888 case TypeTableEntryIdInvalid:5936 case TypeTableEntryIdInvalid:
5889 zig_unreachable();5937 zig_unreachable();
5890 case TypeTableEntryIdMetaType:5938 case TypeTableEntryIdMetaType:
...@@ -5904,6 +5952,8 @@ size_t type_id_index(TypeTableEntryId id) {...@@ -5904,6 +5952,8 @@ size_t type_id_index(TypeTableEntryId id) {
5904 case TypeTableEntryIdArray:5952 case TypeTableEntryIdArray:
5905 return 7;5953 return 7;
5906 case TypeTableEntryIdStruct:5954 case TypeTableEntryIdStruct:
5955 if (entry->data.structure.is_slice)
5956 return 25;
5907 return 8;5957 return 8;
5908 case TypeTableEntryIdNumLitFloat:5958 case TypeTableEntryIdNumLitFloat:
5909 return 9;5959 return 9;
...@@ -6089,4 +6139,3 @@ bool type_can_fail(TypeTableEntry *type_entry) {...@@ -6089,4 +6139,3 @@ bool type_can_fail(TypeTableEntry *type_entry) {
6089bool fn_type_can_fail(FnTypeId *fn_type_id) {6139bool fn_type_can_fail(FnTypeId *fn_type_id) {
6090 return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync;6140 return type_can_fail(fn_type_id->return_type) || fn_type_id->cc == CallingConventionAsync;
6091}6141}
6092
src/analyze.hpp+2-1
...@@ -104,6 +104,7 @@ ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent);...@@ -104,6 +104,7 @@ ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent);
104Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var);104Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var);
105ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent);105ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent);
106ScopeLoop *create_loop_scope(AstNode *node, Scope *parent);106ScopeLoop *create_loop_scope(AstNode *node, Scope *parent);
107ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent);
107ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry);108ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry);
108ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import);109ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import);
109Scope *create_comptime_scope(AstNode *node, Scope *parent);110Scope *create_comptime_scope(AstNode *node, Scope *parent);
...@@ -173,7 +174,7 @@ void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value);...@@ -173,7 +174,7 @@ void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value);
173const char *type_id_name(TypeTableEntryId id);174const char *type_id_name(TypeTableEntryId id);
174TypeTableEntryId type_id_at_index(size_t index);175TypeTableEntryId type_id_at_index(size_t index);
175size_t type_id_len();176size_t type_id_len();
176size_t type_id_index(TypeTableEntryId id);177size_t type_id_index(TypeTableEntry *entry);
177TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id);178TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id);
178bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry);179bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry);
179LinkLib *create_link_lib(Buf *name);180LinkLib *create_link_lib(Buf *name);
src/ast_render.cpp+1-4
...@@ -148,8 +148,6 @@ static const char *node_type_str(NodeType node_type) {...@@ -148,8 +148,6 @@ static const char *node_type_str(NodeType node_type) {
148 return "Root";148 return "Root";
149 case NodeTypeFnDef:149 case NodeTypeFnDef:
150 return "FnDef";150 return "FnDef";
151 case NodeTypeFnDecl:
152 return "FnDecl";
153 case NodeTypeFnProto:151 case NodeTypeFnProto:
154 return "FnProto";152 return "FnProto";
155 case NodeTypeParamDecl:153 case NodeTypeParamDecl:
...@@ -730,7 +728,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -730,7 +728,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
730 render_node_grouped(ar, field_node->data.struct_field.type);728 render_node_grouped(ar, field_node->data.struct_field.type);
731 }729 }
732 if (field_node->data.struct_field.value != nullptr) {730 if (field_node->data.struct_field.value != nullptr) {
733 fprintf(ar->f, "= ");731 fprintf(ar->f, " = ");
734 render_node_grouped(ar, field_node->data.struct_field.value);732 render_node_grouped(ar, field_node->data.struct_field.value);
735 }733 }
736 fprintf(ar->f, ",\n");734 fprintf(ar->f, ",\n");
...@@ -1098,7 +1096,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -1098,7 +1096,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
1098 }1096 }
1099 break;1097 break;
1100 }1098 }
1101 case NodeTypeFnDecl:
1102 case NodeTypeParamDecl:1099 case NodeTypeParamDecl:
1103 case NodeTypeTestDecl:1100 case NodeTypeTestDecl:
1104 case NodeTypeStructField:1101 case NodeTypeStructField:
src/bigfloat.cpp+4
...@@ -181,3 +181,7 @@ bool bigfloat_has_fraction(const BigFloat *bigfloat) {...@@ -181,3 +181,7 @@ bool bigfloat_has_fraction(const BigFloat *bigfloat) {
181 f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored);181 f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored);
182 return !f128M_eq(&floored, &bigfloat->value);182 return !f128M_eq(&floored, &bigfloat->value);
183}183}
184
185void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) {
186 f128M_sqrt(&op->value, &dest->value);
187}
src/bigfloat.hpp+1
...@@ -42,6 +42,7 @@ void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2...@@ -42,6 +42,7 @@ void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2
42void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);42void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
43void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);43void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
44void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);44void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
45void bigfloat_sqrt(BigFloat *dest, const BigFloat *op);
45void bigfloat_append_buf(Buf *buf, const BigFloat *op);46void bigfloat_append_buf(Buf *buf, const BigFloat *op);
46Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);47Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);
4748
src/bigint.cpp+9-6
...@@ -86,6 +86,11 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count)...@@ -86,6 +86,11 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count)
86 size_t digits_to_copy = bit_count / 64;86 size_t digits_to_copy = bit_count / 64;
87 size_t leftover_bits = bit_count % 64;87 size_t leftover_bits = bit_count % 64;
88 dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1);88 dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1);
89 if (dest->digit_count == 1 && leftover_bits == 0) {
90 dest->data.digit = op_digits[0];
91 if (dest->data.digit == 0) dest->digit_count = 0;
92 return;
93 }
89 dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);94 dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
90 for (size_t i = 0; i < digits_to_copy; i += 1) {95 for (size_t i = 0; i < digits_to_copy; i += 1) {
91 uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0;96 uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0;
...@@ -1254,12 +1259,11 @@ void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) {...@@ -1254,12 +1259,11 @@ void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2) {
1254 bigint_normalize(dest);1259 bigint_normalize(dest);
1255 return;1260 return;
1256 }1261 }
1257 // TODO this code path is untested1262
1258 uint64_t first_digit = dest->data.digit;
1259 dest->digit_count = max(op1->digit_count, op2->digit_count);1263 dest->digit_count = max(op1->digit_count, op2->digit_count);
1260 dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);1264 dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
1261 dest->data.digits[0] = first_digit;1265
1262 size_t i = 1;1266 size_t i = 0;
1263 for (; i < op1->digit_count && i < op2->digit_count; i += 1) {1267 for (; i < op1->digit_count && i < op2->digit_count; i += 1) {
1264 dest->data.digits[i] = op1_digits[i] & op2_digits[i];1268 dest->data.digits[i] = op1_digits[i] & op2_digits[i];
1265 }1269 }
...@@ -1407,7 +1411,6 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) {...@@ -1407,7 +1411,6 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) {
1407 return;1411 return;
1408 }1412 }
14091413
1410 // TODO this code path is untested
1411 size_t digit_shift_count = shift_amt / 64;1414 size_t digit_shift_count = shift_amt / 64;
1412 size_t leftover_shift_count = shift_amt % 64;1415 size_t leftover_shift_count = shift_amt % 64;
14131416
...@@ -1422,7 +1425,7 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) {...@@ -1422,7 +1425,7 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) {
1422 uint64_t digit = op1_digits[op_digit_index];1425 uint64_t digit = op1_digits[op_digit_index];
1423 size_t dest_digit_index = op_digit_index - digit_shift_count;1426 size_t dest_digit_index = op_digit_index - digit_shift_count;
1424 dest->data.digits[dest_digit_index] = carry | (digit >> leftover_shift_count);1427 dest->data.digits[dest_digit_index] = carry | (digit >> leftover_shift_count);
1425 carry = (0xffffffffffffffffULL << leftover_shift_count) & digit;1428 carry = digit << (64 - leftover_shift_count);
14261429
1427 if (dest_digit_index == 0) { break; }1430 if (dest_digit_index == 0) { break; }
1428 op_digit_index -= 1;1431 op_digit_index -= 1;
src/codegen.cpp+523-65
...@@ -88,6 +88,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out...@@ -88,6 +88,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
88 g->exported_symbol_names.init(8);88 g->exported_symbol_names.init(8);
89 g->external_prototypes.init(8);89 g->external_prototypes.init(8);
90 g->string_literals_table.init(16);90 g->string_literals_table.init(16);
91 g->type_info_cache.init(32);
91 g->is_test_build = false;92 g->is_test_build = false;
92 g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib);93 g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib);
93 buf_resize(&g->global_asm, 0);94 buf_resize(&g->global_asm, 0);
...@@ -408,6 +409,9 @@ static uint32_t get_err_ret_trace_arg_index(CodeGen *g, FnTableEntry *fn_table_e...@@ -408,6 +409,9 @@ static uint32_t get_err_ret_trace_arg_index(CodeGen *g, FnTableEntry *fn_table_e
408 if (!g->have_err_ret_tracing) {409 if (!g->have_err_ret_tracing) {
409 return UINT32_MAX;410 return UINT32_MAX;
410 }411 }
412 if (fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync) {
413 return 0;
414 }
411 TypeTableEntry *fn_type = fn_table_entry->type_entry;415 TypeTableEntry *fn_type = fn_table_entry->type_entry;
412 if (!fn_type_can_fail(&fn_type->data.fn.fn_type_id)) {416 if (!fn_type_can_fail(&fn_type->data.fn.fn_type_id)) {
413 return UINT32_MAX;417 return UINT32_MAX;
...@@ -464,7 +468,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {...@@ -464,7 +468,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
464 fn_table_entry->llvm_value, buf_ptr(&fn_export->name));468 fn_table_entry->llvm_value, buf_ptr(&fn_export->name));
465 }469 }
466 }470 }
467 fn_table_entry->llvm_name = LLVMGetValueName(fn_table_entry->llvm_value);471 fn_table_entry->llvm_name = strdup(LLVMGetValueName(fn_table_entry->llvm_value));
468472
469 switch (fn_table_entry->fn_inline) {473 switch (fn_table_entry->fn_inline) {
470 case FnInlineAlways:474 case FnInlineAlways:
...@@ -509,7 +513,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {...@@ -509,7 +513,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
509 }513 }
510514
511 if (fn_table_entry->body_node != nullptr) {515 if (fn_table_entry->body_node != nullptr) {
512 bool want_fn_safety = g->build_mode != BuildModeFastRelease && !fn_table_entry->def_scope->safety_off;516 bool want_fn_safety = g->build_mode != BuildModeFastRelease &&
517 g->build_mode != BuildModeSmallRelease &&
518 !fn_table_entry->def_scope->safety_off;
513 if (want_fn_safety) {519 if (want_fn_safety) {
514 if (g->libc_link_lib != nullptr) {520 if (g->libc_link_lib != nullptr) {
515 addLLVMFnAttr(fn_table_entry->llvm_value, "sspstrong");521 addLLVMFnAttr(fn_table_entry->llvm_value, "sspstrong");
...@@ -649,6 +655,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {...@@ -649,6 +655,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
649 }655 }
650 case ScopeIdDeferExpr:656 case ScopeIdDeferExpr:
651 case ScopeIdLoop:657 case ScopeIdLoop:
658 case ScopeIdSuspend:
652 case ScopeIdCompTime:659 case ScopeIdCompTime:
653 case ScopeIdCoroPrelude:660 case ScopeIdCoroPrelude:
654 return get_di_scope(g, scope->parent);661 return get_di_scope(g, scope->parent);
...@@ -714,12 +721,12 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,...@@ -714,12 +721,12 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
714 return fn_val;721 return fn_val;
715}722}
716723
717static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) {724static LLVMValueRef get_float_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) {
718 assert(type_entry->id == TypeTableEntryIdFloat);725 assert(type_entry->id == TypeTableEntryIdFloat);
719726
720 ZigLLVMFnKey key = {};727 ZigLLVMFnKey key = {};
721 key.id = fn_id;728 key.id = fn_id;
722 key.data.floor_ceil.bit_count = (uint32_t)type_entry->data.floating.bit_count;729 key.data.floating.bit_count = (uint32_t)type_entry->data.floating.bit_count;
723730
724 auto existing_entry = g->llvm_fn_table.maybe_get(key);731 auto existing_entry = g->llvm_fn_table.maybe_get(key);
725 if (existing_entry)732 if (existing_entry)
...@@ -730,6 +737,8 @@ static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, Zi...@@ -730,6 +737,8 @@ static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, Zi
730 name = "floor";737 name = "floor";
731 } else if (fn_id == ZigLLVMFnIdCeil) {738 } else if (fn_id == ZigLLVMFnIdCeil) {
732 name = "ceil";739 name = "ceil";
740 } else if (fn_id == ZigLLVMFnIdSqrt) {
741 name = "sqrt";
733 } else {742 } else {
734 zig_unreachable();743 zig_unreachable();
735 }744 }
...@@ -812,7 +821,7 @@ static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) {...@@ -812,7 +821,7 @@ static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) {
812}821}
813822
814static bool ir_want_runtime_safety(CodeGen *g, IrInstruction *instruction) {823static bool ir_want_runtime_safety(CodeGen *g, IrInstruction *instruction) {
815 if (g->build_mode == BuildModeFastRelease)824 if (g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease)
816 return false;825 return false;
817826
818 // TODO memoize827 // TODO memoize
...@@ -1114,22 +1123,19 @@ static LLVMValueRef get_return_address_fn_val(CodeGen *g) {...@@ -1114,22 +1123,19 @@ static LLVMValueRef get_return_address_fn_val(CodeGen *g) {
1114 return g->return_address_fn_val;1123 return g->return_address_fn_val;
1115}1124}
11161125
1117static LLVMValueRef get_return_err_fn(CodeGen *g) {1126static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) {
1118 if (g->return_err_fn != nullptr)1127 if (g->add_error_return_trace_addr_fn_val != nullptr)
1119 return g->return_err_fn;1128 return g->add_error_return_trace_addr_fn_val;
1120
1121 assert(g->err_tag_type != nullptr);
11221129
1123 LLVMTypeRef arg_types[] = {1130 LLVMTypeRef arg_types[] = {
1124 // error return trace pointer
1125 get_ptr_to_stack_trace_type(g)->type_ref,1131 get_ptr_to_stack_trace_type(g)->type_ref,
1132 g->builtin_types.entry_usize->type_ref,
1126 };1133 };
1127 LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 1, false);1134 LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false);
11281135
1129 Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_return_error"), false);1136 Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_add_err_ret_trace_addr"), false);
1130 LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);1137 LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
1131 addLLVMFnAttr(fn_val, "noinline"); // so that we can look at return address1138 addLLVMFnAttr(fn_val, "alwaysinline");
1132 addLLVMFnAttr(fn_val, "cold");
1133 LLVMSetLinkage(fn_val, LLVMInternalLinkage);1139 LLVMSetLinkage(fn_val, LLVMInternalLinkage);
1134 LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));1140 LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
1135 addLLVMFnAttr(fn_val, "nounwind");1141 addLLVMFnAttr(fn_val, "nounwind");
...@@ -1151,6 +1157,8 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) {...@@ -1151,6 +1157,8 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) {
1151 // stack_trace.instruction_addresses[stack_trace.index % stack_trace.instruction_addresses.len] = return_address;1157 // stack_trace.instruction_addresses[stack_trace.index % stack_trace.instruction_addresses.len] = return_address;
11521158
1153 LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0);1159 LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0);
1160 LLVMValueRef address_value = LLVMGetParam(fn_val, 1);
1161
1154 size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;1162 size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
1155 LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, "");1163 LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, "");
1156 size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;1164 size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
...@@ -1172,15 +1180,10 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) {...@@ -1172,15 +1180,10 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) {
1172 LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, "");1180 LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, "");
1173 LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, ptr_value, address_indices, 1, "");1181 LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, ptr_value, address_indices, 1, "");
11741182
1175 LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref);
1176 LLVMValueRef return_address_ptr = LLVMBuildCall(g->builder, get_return_address_fn_val(g), &zero, 1, "");
1177 LLVMValueRef return_address = LLVMBuildPtrToInt(g->builder, return_address_ptr, usize_type_ref, "");
1178
1179 LLVMValueRef address_value = LLVMBuildPtrToInt(g->builder, return_address, usize_type_ref, "");
1180 gen_store_untyped(g, address_value, address_slot, 0, false);1183 gen_store_untyped(g, address_value, address_slot, 0, false);
11811184
1182 // stack_trace.index += 1;1185 // stack_trace.index += 1;
1183 LLVMValueRef index_plus_one_val = LLVMBuildAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), "");1186 LLVMValueRef index_plus_one_val = LLVMBuildNUWAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), "");
1184 gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false);1187 gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false);
11851188
1186 // return;1189 // return;
...@@ -1189,6 +1192,187 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) {...@@ -1189,6 +1192,187 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) {
1189 LLVMPositionBuilderAtEnd(g->builder, prev_block);1192 LLVMPositionBuilderAtEnd(g->builder, prev_block);
1190 LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);1193 LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
11911194
1195 g->add_error_return_trace_addr_fn_val = fn_val;
1196 return fn_val;
1197}
1198
1199static LLVMValueRef get_merge_err_ret_traces_fn_val(CodeGen *g) {
1200 if (g->merge_err_ret_traces_fn_val)
1201 return g->merge_err_ret_traces_fn_val;
1202
1203 assert(g->stack_trace_type != nullptr);
1204
1205 LLVMTypeRef param_types[] = {
1206 get_ptr_to_stack_trace_type(g)->type_ref,
1207 get_ptr_to_stack_trace_type(g)->type_ref,
1208 };
1209 LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), param_types, 2, false);
1210
1211 Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_merge_error_return_traces"), false);
1212 LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
1213 LLVMSetLinkage(fn_val, LLVMInternalLinkage);
1214 LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
1215 addLLVMFnAttr(fn_val, "nounwind");
1216 add_uwtable_attr(g, fn_val);
1217 addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
1218 addLLVMArgAttr(fn_val, (unsigned)0, "noalias");
1219 addLLVMArgAttr(fn_val, (unsigned)0, "writeonly");
1220 addLLVMArgAttr(fn_val, (unsigned)1, "nonnull");
1221 addLLVMArgAttr(fn_val, (unsigned)1, "noalias");
1222 addLLVMArgAttr(fn_val, (unsigned)1, "readonly");
1223 if (g->build_mode == BuildModeDebug) {
1224 ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
1225 ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr);
1226 }
1227
1228 // this is above the ZigLLVMClearCurrentDebugLocation
1229 LLVMValueRef add_error_return_trace_addr_fn_val = get_add_error_return_trace_addr_fn(g);
1230
1231 LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry");
1232 LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
1233 LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder);
1234 LLVMPositionBuilderAtEnd(g->builder, entry_block);
1235 ZigLLVMClearCurrentDebugLocation(g->builder);
1236
1237 // var frame_index: usize = undefined;
1238 // var frames_left: usize = undefined;
1239 // if (src_stack_trace.index < src_stack_trace.instruction_addresses.len) {
1240 // frame_index = 0;
1241 // frames_left = src_stack_trace.index;
1242 // if (frames_left == 0) return;
1243 // } else {
1244 // frame_index = (src_stack_trace.index + 1) % src_stack_trace.instruction_addresses.len;
1245 // frames_left = src_stack_trace.instruction_addresses.len;
1246 // }
1247 // while (true) {
1248 // __zig_add_err_ret_trace_addr(dest_stack_trace, src_stack_trace.instruction_addresses[frame_index]);
1249 // frames_left -= 1;
1250 // if (frames_left == 0) return;
1251 // frame_index = (frame_index + 1) % src_stack_trace.instruction_addresses.len;
1252 // }
1253 LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(fn_val, "Return");
1254
1255 LLVMValueRef frame_index_ptr = LLVMBuildAlloca(g->builder, g->builtin_types.entry_usize->type_ref, "frame_index");
1256 LLVMValueRef frames_left_ptr = LLVMBuildAlloca(g->builder, g->builtin_types.entry_usize->type_ref, "frames_left");
1257
1258 LLVMValueRef dest_stack_trace_ptr = LLVMGetParam(fn_val, 0);
1259 LLVMValueRef src_stack_trace_ptr = LLVMGetParam(fn_val, 1);
1260
1261 size_t src_index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
1262 size_t src_addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
1263 LLVMValueRef src_index_field_ptr = LLVMBuildStructGEP(g->builder, src_stack_trace_ptr,
1264 (unsigned)src_index_field_index, "");
1265 LLVMValueRef src_addresses_field_ptr = LLVMBuildStructGEP(g->builder, src_stack_trace_ptr,
1266 (unsigned)src_addresses_field_index, "");
1267 TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
1268 size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
1269 LLVMValueRef src_ptr_field_ptr = LLVMBuildStructGEP(g->builder, src_addresses_field_ptr, (unsigned)ptr_field_index, "");
1270 size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
1271 LLVMValueRef src_len_field_ptr = LLVMBuildStructGEP(g->builder, src_addresses_field_ptr, (unsigned)len_field_index, "");
1272 LLVMValueRef src_index_val = LLVMBuildLoad(g->builder, src_index_field_ptr, "");
1273 LLVMValueRef src_ptr_val = LLVMBuildLoad(g->builder, src_ptr_field_ptr, "");
1274 LLVMValueRef src_len_val = LLVMBuildLoad(g->builder, src_len_field_ptr, "");
1275 LLVMValueRef no_wrap_bit = LLVMBuildICmp(g->builder, LLVMIntULT, src_index_val, src_len_val, "");
1276 LLVMBasicBlockRef no_wrap_block = LLVMAppendBasicBlock(fn_val, "NoWrap");
1277 LLVMBasicBlockRef yes_wrap_block = LLVMAppendBasicBlock(fn_val, "YesWrap");
1278 LLVMBasicBlockRef loop_block = LLVMAppendBasicBlock(fn_val, "Loop");
1279 LLVMBuildCondBr(g->builder, no_wrap_bit, no_wrap_block, yes_wrap_block);
1280
1281 LLVMPositionBuilderAtEnd(g->builder, no_wrap_block);
1282 LLVMValueRef usize_zero = LLVMConstNull(g->builtin_types.entry_usize->type_ref);
1283 LLVMBuildStore(g->builder, usize_zero, frame_index_ptr);
1284 LLVMBuildStore(g->builder, src_index_val, frames_left_ptr);
1285 LLVMValueRef frames_left_eq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, src_index_val, usize_zero, "");
1286 LLVMBuildCondBr(g->builder, frames_left_eq_zero_bit, return_block, loop_block);
1287
1288 LLVMPositionBuilderAtEnd(g->builder, yes_wrap_block);
1289 LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->type_ref, 1, false);
1290 LLVMValueRef plus_one = LLVMBuildNUWAdd(g->builder, src_index_val, usize_one, "");
1291 LLVMValueRef mod_len = LLVMBuildURem(g->builder, plus_one, src_len_val, "");
1292 LLVMBuildStore(g->builder, mod_len, frame_index_ptr);
1293 LLVMBuildStore(g->builder, src_len_val, frames_left_ptr);
1294 LLVMBuildBr(g->builder, loop_block);
1295
1296 LLVMPositionBuilderAtEnd(g->builder, loop_block);
1297 LLVMValueRef ptr_index = LLVMBuildLoad(g->builder, frame_index_ptr, "");
1298 LLVMValueRef addr_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr_val, &ptr_index, 1, "");
1299 LLVMValueRef this_addr_val = LLVMBuildLoad(g->builder, addr_ptr, "");
1300 LLVMValueRef args[] = {dest_stack_trace_ptr, this_addr_val};
1301 ZigLLVMBuildCall(g->builder, add_error_return_trace_addr_fn_val, args, 2, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAlways, "");
1302 LLVMValueRef prev_frames_left = LLVMBuildLoad(g->builder, frames_left_ptr, "");
1303 LLVMValueRef new_frames_left = LLVMBuildNUWSub(g->builder, prev_frames_left, usize_one, "");
1304 LLVMValueRef done_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, new_frames_left, usize_zero, "");
1305 LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(fn_val, "Continue");
1306 LLVMBuildCondBr(g->builder, done_bit, return_block, continue_block);
1307
1308 LLVMPositionBuilderAtEnd(g->builder, return_block);
1309 LLVMBuildRetVoid(g->builder);
1310
1311 LLVMPositionBuilderAtEnd(g->builder, continue_block);
1312 LLVMBuildStore(g->builder, new_frames_left, frames_left_ptr);
1313 LLVMValueRef prev_index = LLVMBuildLoad(g->builder, frame_index_ptr, "");
1314 LLVMValueRef index_plus_one = LLVMBuildNUWAdd(g->builder, prev_index, usize_one, "");
1315 LLVMValueRef index_mod_len = LLVMBuildURem(g->builder, index_plus_one, src_len_val, "");
1316 LLVMBuildStore(g->builder, index_mod_len, frame_index_ptr);
1317 LLVMBuildBr(g->builder, loop_block);
1318
1319 LLVMPositionBuilderAtEnd(g->builder, prev_block);
1320 LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
1321
1322 g->merge_err_ret_traces_fn_val = fn_val;
1323 return fn_val;
1324
1325}
1326
1327static LLVMValueRef get_return_err_fn(CodeGen *g) {
1328 if (g->return_err_fn != nullptr)
1329 return g->return_err_fn;
1330
1331 assert(g->err_tag_type != nullptr);
1332
1333 LLVMTypeRef arg_types[] = {
1334 // error return trace pointer
1335 get_ptr_to_stack_trace_type(g)->type_ref,
1336 };
1337 LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 1, false);
1338
1339 Buf *fn_name = get_mangled_name(g, buf_create_from_str("__zig_return_error"), false);
1340 LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
1341 addLLVMFnAttr(fn_val, "noinline"); // so that we can look at return address
1342 addLLVMFnAttr(fn_val, "cold");
1343 LLVMSetLinkage(fn_val, LLVMInternalLinkage);
1344 LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
1345 addLLVMFnAttr(fn_val, "nounwind");
1346 add_uwtable_attr(g, fn_val);
1347 addLLVMArgAttr(fn_val, (unsigned)0, "nonnull");
1348 if (g->build_mode == BuildModeDebug) {
1349 ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
1350 ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr);
1351 }
1352
1353 // this is above the ZigLLVMClearCurrentDebugLocation
1354 LLVMValueRef add_error_return_trace_addr_fn_val = get_add_error_return_trace_addr_fn(g);
1355
1356 LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry");
1357 LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
1358 LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder);
1359 LLVMPositionBuilderAtEnd(g->builder, entry_block);
1360 ZigLLVMClearCurrentDebugLocation(g->builder);
1361
1362 LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0);
1363
1364 LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref;
1365 LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref);
1366 LLVMValueRef return_address_ptr = LLVMBuildCall(g->builder, get_return_address_fn_val(g), &zero, 1, "");
1367 LLVMValueRef return_address = LLVMBuildPtrToInt(g->builder, return_address_ptr, usize_type_ref, "");
1368
1369 LLVMValueRef args[] = { err_ret_trace_ptr, return_address };
1370 ZigLLVMBuildCall(g->builder, add_error_return_trace_addr_fn_val, args, 2, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAlways, "");
1371 LLVMBuildRetVoid(g->builder);
1372
1373 LLVMPositionBuilderAtEnd(g->builder, prev_block);
1374 LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
1375
1192 g->return_err_fn = fn_val;1376 g->return_err_fn = fn_val;
1193 return fn_val;1377 return fn_val;
1194}1378}
...@@ -1641,7 +1825,6 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut...@@ -1641,7 +1825,6 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut
1641 };1825 };
1642 LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1,1826 LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1,
1643 get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");1827 get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
1644 LLVMSetTailCall(call_instruction, true);
1645 return call_instruction;1828 return call_instruction;
1646}1829}
16471830
...@@ -1723,7 +1906,7 @@ static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, TypeTableEntry *type...@@ -1723,7 +1906,7 @@ static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, TypeTableEntry *type
1723 if (type_entry->id == TypeTableEntryIdInt)1906 if (type_entry->id == TypeTableEntryIdInt)
1724 return val;1907 return val;
17251908
1726 LLVMValueRef floor_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdFloor);1909 LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloor);
1727 return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");1910 return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");
1728}1911}
17291912
...@@ -1731,7 +1914,7 @@ static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, TypeTableEntry *type_...@@ -1731,7 +1914,7 @@ static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, TypeTableEntry *type_
1731 if (type_entry->id == TypeTableEntryIdInt)1914 if (type_entry->id == TypeTableEntryIdInt)
1732 return val;1915 return val;
17331916
1734 LLVMValueRef ceil_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdCeil);1917 LLVMValueRef ceil_fn = get_float_fn(g, type_entry, ZigLLVMFnIdCeil);
1735 return LLVMBuildCall(g->builder, ceil_fn, &val, 1, "");1918 return LLVMBuildCall(g->builder, ceil_fn, &val, 1, "");
1736}1919}
17371920
...@@ -3070,10 +3253,12 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui...@@ -3070,10 +3253,12 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui
3070 fn_name = "cttz";3253 fn_name = "cttz";
3071 key.id = ZigLLVMFnIdCtz;3254 key.id = ZigLLVMFnIdCtz;
3072 key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count;3255 key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count;
3073 } else {3256 } else if (fn_id == BuiltinFnIdClz) {
3074 fn_name = "ctlz";3257 fn_name = "ctlz";
3075 key.id = ZigLLVMFnIdClz;3258 key.id = ZigLLVMFnIdClz;
3076 key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count;3259 key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count;
3260 } else {
3261 zig_unreachable();
3077 }3262 }
30783263
3079 auto existing_entry = g->llvm_fn_table.maybe_get(key);3264 auto existing_entry = g->llvm_fn_table.maybe_get(key);
...@@ -3375,9 +3560,30 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn...@@ -3375,9 +3560,30 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn
3375 LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order);3560 LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order);
33763561
3377 LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val,3562 LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val,
3378 success_order, failure_order);3563 success_order, failure_order, instruction->is_weak);
3564
3565 TypeTableEntry *maybe_type = instruction->base.value.type;
3566 assert(maybe_type->id == TypeTableEntryIdMaybe);
3567 TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
3568
3569 if (type_is_codegen_pointer(child_type)) {
3570 LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, "");
3571 LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, "");
3572 return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(child_type->type_ref), payload_val, "");
3573 }
3574
3575 assert(instruction->tmp_ptr != nullptr);
3576 assert(type_has_bits(instruction->type));
3577
3578 LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, "");
3579 LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, "");
3580 gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val);
33793581
3380 return LLVMBuildExtractValue(g->builder, result_val, 1, "");3582 LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, "");
3583 LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, "");
3584 LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, "");
3585 gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false);
3586 return instruction->tmp_ptr;
3381}3587}
33823588
3383static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) {3589static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) {
...@@ -4204,6 +4410,44 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable,...@@ -4204,6 +4410,44 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable,
4204 return LLVMBuildIntToPtr(g->builder, uncasted_result, operand_type->type_ref, "");4410 return LLVMBuildIntToPtr(g->builder, uncasted_result, operand_type->type_ref, "");
4205}4411}
42064412
4413static LLVMValueRef ir_render_atomic_load(CodeGen *g, IrExecutable *executable,
4414 IrInstructionAtomicLoad *instruction)
4415{
4416 LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering);
4417 LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
4418 LLVMValueRef load_inst = gen_load(g, ptr, instruction->ptr->value.type, "");
4419 LLVMSetOrdering(load_inst, ordering);
4420 return load_inst;
4421}
4422
4423static LLVMValueRef ir_render_merge_err_ret_traces(CodeGen *g, IrExecutable *executable,
4424 IrInstructionMergeErrRetTraces *instruction)
4425{
4426 assert(g->have_err_ret_tracing);
4427
4428 LLVMValueRef src_trace_ptr = ir_llvm_value(g, instruction->src_err_ret_trace_ptr);
4429 LLVMValueRef dest_trace_ptr = ir_llvm_value(g, instruction->dest_err_ret_trace_ptr);
4430
4431 LLVMValueRef args[] = { dest_trace_ptr, src_trace_ptr };
4432 ZigLLVMBuildCall(g->builder, get_merge_err_ret_traces_fn_val(g), args, 2, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
4433 return nullptr;
4434}
4435
4436static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *executable,
4437 IrInstructionMarkErrRetTracePtr *instruction)
4438{
4439 assert(g->have_err_ret_tracing);
4440 g->cur_err_ret_trace_val_stack = ir_llvm_value(g, instruction->err_ret_trace_ptr);
4441 return nullptr;
4442}
4443
4444static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstructionSqrt *instruction) {
4445 LLVMValueRef op = ir_llvm_value(g, instruction->op);
4446 assert(instruction->base.value.type->id == TypeTableEntryIdFloat);
4447 LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdSqrt);
4448 return LLVMBuildCall(g->builder, fn_val, &op, 1, "");
4449}
4450
4207static void set_debug_location(CodeGen *g, IrInstruction *instruction) {4451static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
4208 AstNode *source_node = instruction->source_node;4452 AstNode *source_node = instruction->source_node;
4209 Scope *scope = instruction->scope;4453 Scope *scope = instruction->scope;
...@@ -4259,6 +4503,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -4259,6 +4503,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
4259 case IrInstructionIdDeclRef:4503 case IrInstructionIdDeclRef:
4260 case IrInstructionIdSwitchVar:4504 case IrInstructionIdSwitchVar:
4261 case IrInstructionIdOffsetOf:4505 case IrInstructionIdOffsetOf:
4506 case IrInstructionIdTypeInfo:
4262 case IrInstructionIdTypeId:4507 case IrInstructionIdTypeId:
4263 case IrInstructionIdSetEvalBranchQuota:4508 case IrInstructionIdSetEvalBranchQuota:
4264 case IrInstructionIdPtrTypeOf:4509 case IrInstructionIdPtrTypeOf:
...@@ -4419,8 +4664,16 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -4419,8 +4664,16 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
4419 return ir_render_coro_alloc_helper(g, executable, (IrInstructionCoroAllocHelper *)instruction);4664 return ir_render_coro_alloc_helper(g, executable, (IrInstructionCoroAllocHelper *)instruction);
4420 case IrInstructionIdAtomicRmw:4665 case IrInstructionIdAtomicRmw:
4421 return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction);4666 return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction);
4667 case IrInstructionIdAtomicLoad:
4668 return ir_render_atomic_load(g, executable, (IrInstructionAtomicLoad *)instruction);
4422 case IrInstructionIdSaveErrRetAddr:4669 case IrInstructionIdSaveErrRetAddr:
4423 return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction);4670 return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction);
4671 case IrInstructionIdMergeErrRetTraces:
4672 return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction);
4673 case IrInstructionIdMarkErrRetTracePtr:
4674 return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction);
4675 case IrInstructionIdSqrt:
4676 return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction);
4424 }4677 }
4425 zig_unreachable();4678 zig_unreachable();
4426}4679}
...@@ -5310,38 +5563,14 @@ static void do_code_gen(CodeGen *g) {...@@ -5310,38 +5563,14 @@ static void do_code_gen(CodeGen *g) {
5310 g->cur_err_ret_trace_val_arg = nullptr;5563 g->cur_err_ret_trace_val_arg = nullptr;
5311 }5564 }
53125565
5566 // error return tracing setup
5313 bool is_async = fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;5567 bool is_async = fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
5314 bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn &&5568 bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn && !is_async && !have_err_ret_trace_arg;
5315 (is_async || !have_err_ret_trace_arg);5569 LLVMValueRef err_ret_array_val = nullptr;
5316 if (have_err_ret_trace_stack) {5570 if (have_err_ret_trace_stack) {
5317 // TODO call graph analysis to find out what this number needs to be for every function5571 TypeTableEntry *array_type = get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count);
5318 static const size_t stack_trace_ptr_count = 30;5572 err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses", get_abi_alignment(g, array_type));
5319
5320 TypeTableEntry *usize = g->builtin_types.entry_usize;
5321 TypeTableEntry *array_type = get_array_type(g, usize, stack_trace_ptr_count);
5322 LLVMValueRef err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses",
5323 get_abi_alignment(g, array_type));
5324 g->cur_err_ret_trace_val_stack = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type));5573 g->cur_err_ret_trace_val_stack = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type));
5325 size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
5326 LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)index_field_index, "");
5327 gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false);
5328
5329 size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
5330 LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)addresses_field_index, "");
5331
5332 TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
5333 size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
5334 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, "");
5335 LLVMValueRef zero = LLVMConstNull(usize->type_ref);
5336 LLVMValueRef indices[] = {zero, zero};
5337 LLVMValueRef err_ret_array_val_elem0_ptr = LLVMBuildInBoundsGEP(g->builder, err_ret_array_val,
5338 indices, 2, "");
5339 gen_store(g, err_ret_array_val_elem0_ptr, ptr_field_ptr,
5340 get_pointer_to_type(g, get_pointer_to_type(g, usize, false), false));
5341
5342 size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
5343 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, "");
5344 gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false));
5345 } else {5574 } else {
5346 g->cur_err_ret_trace_val_stack = nullptr;5575 g->cur_err_ret_trace_val_stack = nullptr;
5347 }5576 }
...@@ -5383,6 +5612,9 @@ static void do_code_gen(CodeGen *g) {...@@ -5383,6 +5612,9 @@ static void do_code_gen(CodeGen *g) {
5383 } else if (instruction->id == IrInstructionIdErrWrapCode) {5612 } else if (instruction->id == IrInstructionIdErrWrapCode) {
5384 IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction;5613 IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction;
5385 slot = &err_wrap_code_instruction->tmp_ptr;5614 slot = &err_wrap_code_instruction->tmp_ptr;
5615 } else if (instruction->id == IrInstructionIdCmpxchg) {
5616 IrInstructionCmpxchg *cmpxchg_instruction = (IrInstructionCmpxchg *)instruction;
5617 slot = &cmpxchg_instruction->tmp_ptr;
5386 } else {5618 } else {
5387 zig_unreachable();5619 zig_unreachable();
5388 }5620 }
...@@ -5436,6 +5668,31 @@ static void do_code_gen(CodeGen *g) {...@@ -5436,6 +5668,31 @@ static void do_code_gen(CodeGen *g) {
5436 }5668 }
5437 }5669 }
54385670
5671 // finishing error return trace setup. we have to do this after all the allocas.
5672 if (have_err_ret_trace_stack) {
5673 TypeTableEntry *usize = g->builtin_types.entry_usize;
5674 size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
5675 LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)index_field_index, "");
5676 gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false);
5677
5678 size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
5679 LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)addresses_field_index, "");
5680
5681 TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
5682 size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
5683 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, "");
5684 LLVMValueRef zero = LLVMConstNull(usize->type_ref);
5685 LLVMValueRef indices[] = {zero, zero};
5686 LLVMValueRef err_ret_array_val_elem0_ptr = LLVMBuildInBoundsGEP(g->builder, err_ret_array_val,
5687 indices, 2, "");
5688 TypeTableEntry *ptr_ptr_usize_type = get_pointer_to_type(g, get_pointer_to_type(g, usize, false), false);
5689 gen_store(g, err_ret_array_val_elem0_ptr, ptr_field_ptr, ptr_ptr_usize_type);
5690
5691 size_t len_field_index = slice_type->data.structure.fields[slice_len_index].gen_index;
5692 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, "");
5693 gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false));
5694 }
5695
5439 FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;5696 FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;
54405697
5441 // create debug variable declarations for parameters5698 // create debug variable declarations for parameters
...@@ -5519,10 +5776,12 @@ static void do_code_gen(CodeGen *g) {...@@ -5519,10 +5776,12 @@ static void do_code_gen(CodeGen *g) {
5519 os_path_join(g->cache_dir, o_basename, output_path);5776 os_path_join(g->cache_dir, o_basename, output_path);
5520 ensure_cache_dir(g);5777 ensure_cache_dir(g);
55215778
5779 bool is_small = g->build_mode == BuildModeSmallRelease;
5780
5522 switch (g->emit_file_type) {5781 switch (g->emit_file_type) {
5523 case EmitFileTypeBinary:5782 case EmitFileTypeBinary:
5524 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),5783 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
5525 ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug))5784 ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug, is_small))
5526 {5785 {
5527 zig_panic("unable to write object file %s: %s", buf_ptr(output_path), err_msg);5786 zig_panic("unable to write object file %s: %s", buf_ptr(output_path), err_msg);
5528 }5787 }
...@@ -5532,7 +5791,7 @@ static void do_code_gen(CodeGen *g) {...@@ -5532,7 +5791,7 @@ static void do_code_gen(CodeGen *g) {
55325791
5533 case EmitFileTypeAssembly:5792 case EmitFileTypeAssembly:
5534 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),5793 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
5535 ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug))5794 ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug, is_small))
5536 {5795 {
5537 zig_panic("unable to write assembly file %s: %s", buf_ptr(output_path), err_msg);5796 zig_panic("unable to write assembly file %s: %s", buf_ptr(output_path), err_msg);
5538 }5797 }
...@@ -5541,7 +5800,7 @@ static void do_code_gen(CodeGen *g) {...@@ -5541,7 +5800,7 @@ static void do_code_gen(CodeGen *g) {
55415800
5542 case EmitFileTypeLLVMIr:5801 case EmitFileTypeLLVMIr:
5543 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),5802 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
5544 ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug))5803 ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug, is_small))
5545 {5804 {
5546 zig_panic("unable to write llvm-ir file %s: %s", buf_ptr(output_path), err_msg);5805 zig_panic("unable to write llvm-ir file %s: %s", buf_ptr(output_path), err_msg);
5547 }5806 }
...@@ -5867,6 +6126,8 @@ static void define_builtin_fns(CodeGen *g) {...@@ -5867,6 +6126,8 @@ static void define_builtin_fns(CodeGen *g) {
5867 create_builtin_fn(g, BuiltinFnIdMemberCount, "memberCount", 1);6126 create_builtin_fn(g, BuiltinFnIdMemberCount, "memberCount", 1);
5868 create_builtin_fn(g, BuiltinFnIdMemberType, "memberType", 2);6127 create_builtin_fn(g, BuiltinFnIdMemberType, "memberType", 2);
5869 create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2);6128 create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2);
6129 create_builtin_fn(g, BuiltinFnIdField, "field", 2);
6130 create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1);
5870 create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf6131 create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf
5871 create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4);6132 create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4);
5872 create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4);6133 create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4);
...@@ -5883,7 +6144,8 @@ static void define_builtin_fns(CodeGen *g) {...@@ -5883,7 +6144,8 @@ static void define_builtin_fns(CodeGen *g) {
5883 create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1);6144 create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1);
5884 create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2);6145 create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2);
5885 create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);6146 create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);
5886 create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);6147 create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6);
6148 create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6);
5887 create_builtin_fn(g, BuiltinFnIdFence, "fence", 1);6149 create_builtin_fn(g, BuiltinFnIdFence, "fence", 1);
5888 create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2);6150 create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2);
5889 create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);6151 create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);
...@@ -5906,6 +6168,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -5906,6 +6168,7 @@ static void define_builtin_fns(CodeGen *g) {
5906 create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2);6168 create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2);
5907 create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);6169 create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
5908 create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);6170 create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
6171 create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2);
5909 create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);6172 create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
5910 create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);6173 create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
5911 create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);6174 create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
...@@ -5919,6 +6182,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -5919,6 +6182,7 @@ static void define_builtin_fns(CodeGen *g) {
5919 create_builtin_fn(g, BuiltinFnIdExport, "export", 3);6182 create_builtin_fn(g, BuiltinFnIdExport, "export", 3);
5920 create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);6183 create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);
5921 create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5);6184 create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5);
6185 create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3);
5922}6186}
59236187
5924static const char *bool_to_str(bool b) {6188static const char *bool_to_str(bool b) {
...@@ -5930,6 +6194,7 @@ static const char *build_mode_to_str(BuildMode build_mode) {...@@ -5930,6 +6194,7 @@ static const char *build_mode_to_str(BuildMode build_mode) {
5930 case BuildModeDebug: return "Mode.Debug";6194 case BuildModeDebug: return "Mode.Debug";
5931 case BuildModeSafeRelease: return "Mode.ReleaseSafe";6195 case BuildModeSafeRelease: return "Mode.ReleaseSafe";
5932 case BuildModeFastRelease: return "Mode.ReleaseFast";6196 case BuildModeFastRelease: return "Mode.ReleaseFast";
6197 case BuildModeSmallRelease: return "Mode.ReleaseSmall";
5933 }6198 }
5934 zig_unreachable();6199 zig_unreachable();
5935}6200}
...@@ -5943,6 +6208,8 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -5943,6 +6208,8 @@ static void define_builtin_compile_vars(CodeGen *g) {
5943 os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);6208 os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);
5944 Buf *contents = buf_alloc();6209 Buf *contents = buf_alloc();
59456210
6211 // Modifications to this struct must be coordinated with code that does anything with
6212 // g->stack_trace_type. There are hard-coded references to the field indexes.
5946 buf_append_str(contents,6213 buf_append_str(contents,
5947 "pub const StackTrace = struct {\n"6214 "pub const StackTrace = struct {\n"
5948 " index: usize,\n"6215 " index: usize,\n"
...@@ -6068,6 +6335,7 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -6068,6 +6335,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
6068 " Debug,\n"6335 " Debug,\n"
6069 " ReleaseSafe,\n"6336 " ReleaseSafe,\n"
6070 " ReleaseFast,\n"6337 " ReleaseFast,\n"
6338 " ReleaseSmall,\n"
6071 "};\n\n");6339 "};\n\n");
6072 }6340 }
6073 {6341 {
...@@ -6077,8 +6345,196 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -6077,8 +6345,196 @@ static void define_builtin_compile_vars(CodeGen *g) {
6077 const TypeTableEntryId id = type_id_at_index(i);6345 const TypeTableEntryId id = type_id_at_index(i);
6078 buf_appendf(contents, " %s,\n", type_id_name(id));6346 buf_appendf(contents, " %s,\n", type_id_name(id));
6079 }6347 }
6348 buf_appendf(contents, " Slice,\n");
6080 buf_appendf(contents, "};\n\n");6349 buf_appendf(contents, "};\n\n");
6081 }6350 }
6351 {
6352 buf_appendf(contents,
6353 "pub const TypeInfo = union(TypeId) {\n"
6354 " Type: void,\n"
6355 " Void: void,\n"
6356 " Bool: void,\n"
6357 " NoReturn: void,\n"
6358 " Int: Int,\n"
6359 " Float: Float,\n"
6360 " Pointer: Pointer,\n"
6361 " Slice: Slice,\n"
6362 " Array: Array,\n"
6363 " Struct: Struct,\n"
6364 " FloatLiteral: void,\n"
6365 " IntLiteral: void,\n"
6366 " UndefinedLiteral: void,\n"
6367 " NullLiteral: void,\n"
6368 " Nullable: Nullable,\n"
6369 " ErrorUnion: ErrorUnion,\n"
6370 " ErrorSet: ErrorSet,\n"
6371 " Enum: Enum,\n"
6372 " Union: Union,\n"
6373 " Fn: Fn,\n"
6374 " Namespace: void,\n"
6375 " Block: void,\n"
6376 " BoundFn: Fn,\n"
6377 " ArgTuple: void,\n"
6378 " Opaque: void,\n"
6379 " Promise: Promise,\n"
6380 "\n\n"
6381 " pub const Int = struct {\n"
6382 " is_signed: bool,\n"
6383 " bits: u8,\n"
6384 " };\n"
6385 "\n"
6386 " pub const Float = struct {\n"
6387 " bits: u8,\n"
6388 " };\n"
6389 "\n"
6390 " pub const Pointer = struct {\n"
6391 " is_const: bool,\n"
6392 " is_volatile: bool,\n"
6393 " alignment: u32,\n"
6394 " child: type,\n"
6395 " };\n"
6396 "\n"
6397 " pub const Slice = Pointer;\n"
6398 "\n"
6399 " pub const Array = struct {\n"
6400 " len: usize,\n"
6401 " child: type,\n"
6402 " };\n"
6403 "\n"
6404 " pub const ContainerLayout = enum {\n"
6405 " Auto,\n"
6406 " Extern,\n"
6407 " Packed,\n"
6408 " };\n"
6409 "\n"
6410 " pub const StructField = struct {\n"
6411 " name: []const u8,\n"
6412 " offset: ?usize,\n"
6413 " field_type: type,\n"
6414 " };\n"
6415 "\n"
6416 " pub const Struct = struct {\n"
6417 " layout: ContainerLayout,\n"
6418 " fields: []StructField,\n"
6419 " defs: []Definition,\n"
6420 " };\n"
6421 "\n"
6422 " pub const Nullable = struct {\n"
6423 " child: type,\n"
6424 " };\n"
6425 "\n"
6426 " pub const ErrorUnion = struct {\n"
6427 " error_set: type,\n"
6428 " payload: type,\n"
6429 " };\n"
6430 "\n"
6431 " pub const Error = struct {\n"
6432 " name: []const u8,\n"
6433 " value: usize,\n"
6434 " };\n"
6435 "\n"
6436 " pub const ErrorSet = struct {\n"
6437 " errors: []Error,\n"
6438 " };\n"
6439 "\n"
6440 " pub const EnumField = struct {\n"
6441 " name: []const u8,\n"
6442 " value: usize,\n"
6443 " };\n"
6444 "\n"
6445 " pub const Enum = struct {\n"
6446 " layout: ContainerLayout,\n"
6447 " tag_type: type,\n"
6448 " fields: []EnumField,\n"
6449 " defs: []Definition,\n"
6450 " };\n"
6451 "\n"
6452 " pub const UnionField = struct {\n"
6453 " name: []const u8,\n"
6454 " enum_field: ?EnumField,\n"
6455 " field_type: type,\n"
6456 " };\n"
6457 "\n"
6458 " pub const Union = struct {\n"
6459 " layout: ContainerLayout,\n"
6460 " tag_type: type,\n"
6461 " fields: []UnionField,\n"
6462 " defs: []Definition,\n"
6463 " };\n"
6464 "\n"
6465 " pub const CallingConvention = enum {\n"
6466 " Unspecified,\n"
6467 " C,\n"
6468 " Cold,\n"
6469 " Naked,\n"
6470 " Stdcall,\n"
6471 " Async,\n"
6472 " };\n"
6473 "\n"
6474 " pub const FnArg = struct {\n"
6475 " is_generic: bool,\n"
6476 " is_noalias: bool,\n"
6477 " arg_type: type,\n"
6478 " };\n"
6479 "\n"
6480 " pub const Fn = struct {\n"
6481 " calling_convention: CallingConvention,\n"
6482 " is_generic: bool,\n"
6483 " is_var_args: bool,\n"
6484 " return_type: type,\n"
6485 " async_allocator_type: type,\n"
6486 " args: []FnArg,\n"
6487 " };\n"
6488 "\n"
6489 " pub const Promise = struct {\n"
6490 " child: type,\n"
6491 " };\n"
6492 "\n"
6493 " pub const Definition = struct {\n"
6494 " name: []const u8,\n"
6495 " is_pub: bool,\n"
6496 " data: Data,\n"
6497 "\n"
6498 " pub const Data = union(enum) {\n"
6499 " Type: type,\n"
6500 " Var: type,\n"
6501 " Fn: FnDef,\n"
6502 "\n"
6503 " pub const FnDef = struct {\n"
6504 " fn_type: type,\n"
6505 " inline_type: Inline,\n"
6506 " calling_convention: CallingConvention,\n"
6507 " is_var_args: bool,\n"
6508 " is_extern: bool,\n"
6509 " is_export: bool,\n"
6510 " lib_name: ?[]const u8,\n"
6511 " return_type: type,\n"
6512 " arg_names: [][] const u8,\n"
6513 "\n"
6514 " pub const Inline = enum {\n"
6515 " Auto,\n"
6516 " Always,\n"
6517 " Never,\n"
6518 " };\n"
6519 " };\n"
6520 " };\n"
6521 " };\n"
6522 "};\n\n");
6523 assert(ContainerLayoutAuto == 0);
6524 assert(ContainerLayoutExtern == 1);
6525 assert(ContainerLayoutPacked == 2);
6526
6527 assert(CallingConventionUnspecified == 0);
6528 assert(CallingConventionC == 1);
6529 assert(CallingConventionCold == 2);
6530 assert(CallingConventionNaked == 3);
6531 assert(CallingConventionStdcall == 4);
6532 assert(CallingConventionAsync == 5);
6533
6534 assert(FnInlineAuto == 0);
6535 assert(FnInlineAlways == 1);
6536 assert(FnInlineNever == 2);
6537 }
6082 {6538 {
6083 buf_appendf(contents,6539 buf_appendf(contents,
6084 "pub const FloatMode = enum {\n"6540 "pub const FloatMode = enum {\n"
...@@ -6118,7 +6574,8 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -6118,7 +6574,8 @@ static void define_builtin_compile_vars(CodeGen *g) {
6118 int err;6574 int err;
6119 Buf *abs_full_path = buf_alloc();6575 Buf *abs_full_path = buf_alloc();
6120 if ((err = os_path_real(builtin_zig_path, abs_full_path))) {6576 if ((err = os_path_real(builtin_zig_path, abs_full_path))) {
6121 zig_panic("unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err));6577 fprintf(stderr, "unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err));
6578 exit(1);
6122 }6579 }
61236580
6124 assert(g->root_package);6581 assert(g->root_package);
...@@ -6238,7 +6695,7 @@ static void init(CodeGen *g) {...@@ -6238,7 +6695,7 @@ static void init(CodeGen *g) {
6238 }6695 }
6239 }6696 }
62406697
6241 g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease;6698 g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease && g->build_mode != BuildModeSmallRelease;
62426699
6243 define_builtin_fns(g);6700 define_builtin_fns(g);
6244 define_builtin_compile_vars(g);6701 define_builtin_compile_vars(g);
...@@ -6372,12 +6829,14 @@ static void gen_root_source(CodeGen *g) {...@@ -6372,12 +6829,14 @@ static void gen_root_source(CodeGen *g) {
6372 Buf *abs_full_path = buf_alloc();6829 Buf *abs_full_path = buf_alloc();
6373 int err;6830 int err;
6374 if ((err = os_path_real(rel_full_path, abs_full_path))) {6831 if ((err = os_path_real(rel_full_path, abs_full_path))) {
6375 zig_panic("unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));6832 fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));
6833 exit(1);
6376 }6834 }
63776835
6378 Buf *source_code = buf_alloc();6836 Buf *source_code = buf_alloc();
6379 if ((err = os_fetch_file_path(rel_full_path, source_code, true))) {6837 if ((err = os_fetch_file_path(rel_full_path, source_code, true))) {
6380 zig_panic("unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));6838 fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));
6839 exit(1);
6381 }6840 }
63826841
6383 g->root_import = add_source_file(g, g->root_package, abs_full_path, source_code);6842 g->root_import = add_source_file(g, g->root_package, abs_full_path, source_code);
...@@ -6930,4 +7389,3 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir,...@@ -6930,4 +7389,3 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir,
6930 }7389 }
6931 return pkg;7390 return pkg;
6932}7391}
6933
src/ir.cpp+1745-272
...@@ -110,6 +110,8 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_...@@ -110,6 +110,8 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
110 IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type);110 IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type);
111static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,111static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
112 VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr);112 VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr);
113static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op);
114static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval);
113115
114ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {116ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {
115 assert(const_val->type->id == TypeTableEntryIdPointer);117 assert(const_val->type->id == TypeTableEntryIdPointer);
...@@ -143,6 +145,8 @@ static bool ir_should_inline(IrExecutable *exec, Scope *scope) {...@@ -143,6 +145,8 @@ static bool ir_should_inline(IrExecutable *exec, Scope *scope) {
143 while (scope != nullptr) {145 while (scope != nullptr) {
144 if (scope->id == ScopeIdCompTime)146 if (scope->id == ScopeIdCompTime)
145 return true;147 return true;
148 if (scope->id == ScopeIdFnDef)
149 break;
146 scope = scope->parent;150 scope = scope->parent;
147 }151 }
148 return false;152 return false;
...@@ -613,6 +617,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionOffsetOf *) {...@@ -613,6 +617,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionOffsetOf *) {
613 return IrInstructionIdOffsetOf;617 return IrInstructionIdOffsetOf;
614}618}
615619
620static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeInfo *) {
621 return IrInstructionIdTypeInfo;
622}
623
616static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeId *) {624static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeId *) {
617 return IrInstructionIdTypeId;625 return IrInstructionIdTypeId;
618}626}
...@@ -709,6 +717,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) {...@@ -709,6 +717,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) {
709 return IrInstructionIdAtomicRmw;717 return IrInstructionIdAtomicRmw;
710}718}
711719
720static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) {
721 return IrInstructionIdAtomicLoad;
722}
723
712static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseResultType *) {724static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseResultType *) {
713 return IrInstructionIdPromiseResultType;725 return IrInstructionIdPromiseResultType;
714}726}
...@@ -725,6 +737,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitRetur...@@ -725,6 +737,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitRetur
725 return IrInstructionIdAddImplicitReturnType;737 return IrInstructionIdAddImplicitReturnType;
726}738}
727739
740static constexpr IrInstructionId ir_instruction_id(IrInstructionMergeErrRetTraces *) {
741 return IrInstructionIdMergeErrRetTraces;
742}
743
744static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTracePtr *) {
745 return IrInstructionIdMarkErrRetTracePtr;
746}
747
748static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) {
749 return IrInstructionIdSqrt;
750}
751
728template<typename T>752template<typename T>
729static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {753static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
730 T *special_instruction = allocate<T>(1);754 T *special_instruction = allocate<T>(1);
...@@ -956,25 +980,6 @@ static IrInstruction *ir_build_const_c_str_lit(IrBuilder *irb, Scope *scope, Ast...@@ -956,25 +980,6 @@ static IrInstruction *ir_build_const_c_str_lit(IrBuilder *irb, Scope *scope, Ast
956 return &const_instruction->base;980 return &const_instruction->base;
957}981}
958982
959static IrInstruction *ir_build_const_promise_init(IrBuilder *irb, Scope *scope, AstNode *source_node,
960 TypeTableEntry *return_type)
961{
962 TypeTableEntry *struct_type = get_promise_frame_type(irb->codegen, return_type);
963
964 IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node);
965 const_instruction->base.value.type = struct_type;
966 const_instruction->base.value.special = ConstValSpecialStatic;
967 const_instruction->base.value.data.x_struct.fields = allocate<ConstExprValue>(struct_type->data.structure.src_field_count);
968 const_instruction->base.value.data.x_struct.fields[0].type = struct_type->data.structure.fields[0].type_entry;
969 const_instruction->base.value.data.x_struct.fields[0].special = ConstValSpecialStatic;
970 const_instruction->base.value.data.x_struct.fields[0].data.x_maybe = nullptr;
971 const_instruction->base.value.data.x_struct.fields[1].type = return_type;
972 const_instruction->base.value.data.x_struct.fields[1].special = ConstValSpecialUndef;
973 const_instruction->base.value.data.x_struct.fields[2].type = struct_type->data.structure.fields[2].type_entry;
974 const_instruction->base.value.data.x_struct.fields[2].special = ConstValSpecialUndef;
975 return &const_instruction->base;
976}
977
978static IrInstruction *ir_build_bin_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBinOp op_id,983static IrInstruction *ir_build_bin_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBinOp op_id,
979 IrInstruction *op1, IrInstruction *op2, bool safety_check_on)984 IrInstruction *op1, IrInstruction *op2, bool safety_check_on)
980{985{
...@@ -1035,12 +1040,27 @@ static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_...@@ -1035,12 +1040,27 @@ static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_
1035 return new_instruction;1040 return new_instruction;
1036}1041}
10371042
1043static IrInstruction *ir_build_field_ptr_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node,
1044 IrInstruction *container_ptr, IrInstruction *field_name_expr)
1045{
1046 IrInstructionFieldPtr *instruction = ir_build_instruction<IrInstructionFieldPtr>(irb, scope, source_node);
1047 instruction->container_ptr = container_ptr;
1048 instruction->field_name_buffer = nullptr;
1049 instruction->field_name_expr = field_name_expr;
1050
1051 ir_ref_instruction(container_ptr, irb->current_basic_block);
1052 ir_ref_instruction(field_name_expr, irb->current_basic_block);
1053
1054 return &instruction->base;
1055}
1056
1038static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,1057static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
1039 IrInstruction *container_ptr, Buf *field_name)1058 IrInstruction *container_ptr, Buf *field_name)
1040{1059{
1041 IrInstructionFieldPtr *instruction = ir_build_instruction<IrInstructionFieldPtr>(irb, scope, source_node);1060 IrInstructionFieldPtr *instruction = ir_build_instruction<IrInstructionFieldPtr>(irb, scope, source_node);
1042 instruction->container_ptr = container_ptr;1061 instruction->container_ptr = container_ptr;
1043 instruction->field_name = field_name;1062 instruction->field_name_buffer = field_name;
1063 instruction->field_name_expr = nullptr;
10441064
1045 ir_ref_instruction(container_ptr, irb->current_basic_block);1065 ir_ref_instruction(container_ptr, irb->current_basic_block);
10461066
...@@ -1835,38 +1855,34 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode...@@ -1835,38 +1855,34 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode
1835 return &instruction->base;1855 return &instruction->base;
1836}1856}
18371857
1838static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr,1858static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value,
1839 IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value,1859 IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value,
1840 AtomicOrder success_order, AtomicOrder failure_order)1860 IrInstruction *success_order_value, IrInstruction *failure_order_value,
1861 bool is_weak,
1862 TypeTableEntry *type, AtomicOrder success_order, AtomicOrder failure_order)
1841{1863{
1842 IrInstructionCmpxchg *instruction = ir_build_instruction<IrInstructionCmpxchg>(irb, scope, source_node);1864 IrInstructionCmpxchg *instruction = ir_build_instruction<IrInstructionCmpxchg>(irb, scope, source_node);
1865 instruction->type_value = type_value;
1843 instruction->ptr = ptr;1866 instruction->ptr = ptr;
1844 instruction->cmp_value = cmp_value;1867 instruction->cmp_value = cmp_value;
1845 instruction->new_value = new_value;1868 instruction->new_value = new_value;
1846 instruction->success_order_value = success_order_value;1869 instruction->success_order_value = success_order_value;
1847 instruction->failure_order_value = failure_order_value;1870 instruction->failure_order_value = failure_order_value;
1871 instruction->is_weak = is_weak;
1872 instruction->type = type;
1848 instruction->success_order = success_order;1873 instruction->success_order = success_order;
1849 instruction->failure_order = failure_order;1874 instruction->failure_order = failure_order;
18501875
1876 if (type_value != nullptr) ir_ref_instruction(type_value, irb->current_basic_block);
1851 ir_ref_instruction(ptr, irb->current_basic_block);1877 ir_ref_instruction(ptr, irb->current_basic_block);
1852 ir_ref_instruction(cmp_value, irb->current_basic_block);1878 ir_ref_instruction(cmp_value, irb->current_basic_block);
1853 ir_ref_instruction(new_value, irb->current_basic_block);1879 ir_ref_instruction(new_value, irb->current_basic_block);
1854 ir_ref_instruction(success_order_value, irb->current_basic_block);1880 if (type_value != nullptr) ir_ref_instruction(success_order_value, irb->current_basic_block);
1855 ir_ref_instruction(failure_order_value, irb->current_basic_block);1881 if (type_value != nullptr) ir_ref_instruction(failure_order_value, irb->current_basic_block);
18561882
1857 return &instruction->base;1883 return &instruction->base;
1858}1884}
18591885
1860static IrInstruction *ir_build_cmpxchg_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr,
1861 IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value,
1862 AtomicOrder success_order, AtomicOrder failure_order)
1863{
1864 IrInstruction *new_instruction = ir_build_cmpxchg(irb, old_instruction->scope, old_instruction->source_node,
1865 ptr, cmp_value, new_value, success_order_value, failure_order_value, success_order, failure_order);
1866 ir_link_new_instruction(new_instruction, old_instruction);
1867 return new_instruction;
1868}
1869
1870static IrInstruction *ir_build_fence(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *order_value, AtomicOrder order) {1886static IrInstruction *ir_build_fence(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *order_value, AtomicOrder order) {
1871 IrInstructionFence *instruction = ir_build_instruction<IrInstructionFence>(irb, scope, source_node);1887 IrInstructionFence *instruction = ir_build_instruction<IrInstructionFence>(irb, scope, source_node);
1872 instruction->order_value = order_value;1888 instruction->order_value = order_value;
...@@ -2430,6 +2446,16 @@ static IrInstruction *ir_build_offset_of(IrBuilder *irb, Scope *scope, AstNode *...@@ -2430,6 +2446,16 @@ static IrInstruction *ir_build_offset_of(IrBuilder *irb, Scope *scope, AstNode *
2430 return &instruction->base;2446 return &instruction->base;
2431}2447}
24322448
2449static IrInstruction *ir_build_type_info(IrBuilder *irb, Scope *scope, AstNode *source_node,
2450 IrInstruction *type_value) {
2451 IrInstructionTypeInfo *instruction = ir_build_instruction<IrInstructionTypeInfo>(irb, scope, source_node);
2452 instruction->type_value = type_value;
2453
2454 ir_ref_instruction(type_value, irb->current_basic_block);
2455
2456 return &instruction->base;
2457}
2458
2433static IrInstruction *ir_build_type_id(IrBuilder *irb, Scope *scope, AstNode *source_node,2459static IrInstruction *ir_build_type_id(IrBuilder *irb, Scope *scope, AstNode *source_node,
2434 IrInstruction *type_value)2460 IrInstruction *type_value)
2435{2461{
...@@ -2495,8 +2521,9 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s...@@ -2495,8 +2521,9 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s
2495 return &instruction->base;2521 return &instruction->base;
2496}2522}
24972523
2498static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node) {2524static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstructionErrorReturnTrace::Nullable nullable) {
2499 IrInstructionErrorReturnTrace *instruction = ir_build_instruction<IrInstructionErrorReturnTrace>(irb, scope, source_node);2525 IrInstructionErrorReturnTrace *instruction = ir_build_instruction<IrInstructionErrorReturnTrace>(irb, scope, source_node);
2526 instruction->nullable = nullable;
25002527
2501 return &instruction->base;2528 return &instruction->base;
2502}2529}
...@@ -2679,6 +2706,23 @@ static IrInstruction *ir_build_atomic_rmw(IrBuilder *irb, Scope *scope, AstNode...@@ -2679,6 +2706,23 @@ static IrInstruction *ir_build_atomic_rmw(IrBuilder *irb, Scope *scope, AstNode
2679 return &instruction->base;2706 return &instruction->base;
2680}2707}
26812708
2709static IrInstruction *ir_build_atomic_load(IrBuilder *irb, Scope *scope, AstNode *source_node,
2710 IrInstruction *operand_type, IrInstruction *ptr,
2711 IrInstruction *ordering, AtomicOrder resolved_ordering)
2712{
2713 IrInstructionAtomicLoad *instruction = ir_build_instruction<IrInstructionAtomicLoad>(irb, scope, source_node);
2714 instruction->operand_type = operand_type;
2715 instruction->ptr = ptr;
2716 instruction->ordering = ordering;
2717 instruction->resolved_ordering = resolved_ordering;
2718
2719 if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block);
2720 ir_ref_instruction(ptr, irb->current_basic_block);
2721 if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block);
2722
2723 return &instruction->base;
2724}
2725
2682static IrInstruction *ir_build_promise_result_type(IrBuilder *irb, Scope *scope, AstNode *source_node,2726static IrInstruction *ir_build_promise_result_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
2683 IrInstruction *promise_type)2727 IrInstruction *promise_type)
2684{2728{
...@@ -2717,6 +2761,41 @@ static IrInstruction *ir_build_add_implicit_return_type(IrBuilder *irb, Scope *s...@@ -2717,6 +2761,41 @@ static IrInstruction *ir_build_add_implicit_return_type(IrBuilder *irb, Scope *s
2717 return &instruction->base;2761 return &instruction->base;
2718}2762}
27192763
2764static IrInstruction *ir_build_merge_err_ret_traces(IrBuilder *irb, Scope *scope, AstNode *source_node,
2765 IrInstruction *coro_promise_ptr, IrInstruction *src_err_ret_trace_ptr, IrInstruction *dest_err_ret_trace_ptr)
2766{
2767 IrInstructionMergeErrRetTraces *instruction = ir_build_instruction<IrInstructionMergeErrRetTraces>(irb, scope, source_node);
2768 instruction->coro_promise_ptr = coro_promise_ptr;
2769 instruction->src_err_ret_trace_ptr = src_err_ret_trace_ptr;
2770 instruction->dest_err_ret_trace_ptr = dest_err_ret_trace_ptr;
2771
2772 ir_ref_instruction(coro_promise_ptr, irb->current_basic_block);
2773 ir_ref_instruction(src_err_ret_trace_ptr, irb->current_basic_block);
2774 ir_ref_instruction(dest_err_ret_trace_ptr, irb->current_basic_block);
2775
2776 return &instruction->base;
2777}
2778
2779static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *err_ret_trace_ptr) {
2780 IrInstructionMarkErrRetTracePtr *instruction = ir_build_instruction<IrInstructionMarkErrRetTracePtr>(irb, scope, source_node);
2781 instruction->err_ret_trace_ptr = err_ret_trace_ptr;
2782
2783 ir_ref_instruction(err_ret_trace_ptr, irb->current_basic_block);
2784
2785 return &instruction->base;
2786}
2787
2788static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
2789 IrInstructionSqrt *instruction = ir_build_instruction<IrInstructionSqrt>(irb, scope, source_node);
2790 instruction->type = type;
2791 instruction->op = op;
2792
2793 if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
2794 ir_ref_instruction(op, irb->current_basic_block);
2795
2796 return &instruction->base;
2797}
2798
2720static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {2799static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
2721 results[ReturnKindUnconditional] = 0;2800 results[ReturnKindUnconditional] = 0;
2722 results[ReturnKindError] = 0;2801 results[ReturnKindError] = 0;
...@@ -2741,9 +2820,10 @@ static IrInstruction *ir_mark_gen(IrInstruction *instruction) {...@@ -2741,9 +2820,10 @@ static IrInstruction *ir_mark_gen(IrInstruction *instruction) {
27412820
2742static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers) {2821static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers) {
2743 Scope *scope = inner_scope;2822 Scope *scope = inner_scope;
2823 bool is_noreturn = false;
2744 while (scope != outer_scope) {2824 while (scope != outer_scope) {
2745 if (!scope)2825 if (!scope)
2746 return false;2826 return is_noreturn;
27472827
2748 if (scope->id == ScopeIdDefer) {2828 if (scope->id == ScopeIdDefer) {
2749 AstNode *defer_node = scope->source_node;2829 AstNode *defer_node = scope->source_node;
...@@ -2756,14 +2836,18 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o...@@ -2756,14 +2836,18 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o
2756 Scope *defer_expr_scope = defer_node->data.defer.expr_scope;2836 Scope *defer_expr_scope = defer_node->data.defer.expr_scope;
2757 IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);2837 IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope);
2758 if (defer_expr_value != irb->codegen->invalid_instruction) {2838 if (defer_expr_value != irb->codegen->invalid_instruction) {
2759 ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value));2839 if (defer_expr_value->value.type != nullptr && defer_expr_value->value.type->id == TypeTableEntryIdUnreachable) {
2840 is_noreturn = true;
2841 } else {
2842 ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, defer_expr_value));
2843 }
2760 }2844 }
2761 }2845 }
27622846
2763 }2847 }
2764 scope = scope->parent;2848 scope = scope->parent;
2765 }2849 }
2766 return true;2850 return is_noreturn;
2767}2851}
27682852
2769static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {2853static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {
...@@ -2777,6 +2861,18 @@ static void ir_set_cursor_at_end_and_append_block(IrBuilder *irb, IrBasicBlock *...@@ -2777,6 +2861,18 @@ static void ir_set_cursor_at_end_and_append_block(IrBuilder *irb, IrBasicBlock *
2777 ir_set_cursor_at_end(irb, basic_block);2861 ir_set_cursor_at_end(irb, basic_block);
2778}2862}
27792863
2864static ScopeSuspend *get_scope_suspend(Scope *scope) {
2865 while (scope) {
2866 if (scope->id == ScopeIdSuspend)
2867 return (ScopeSuspend *)scope;
2868 if (scope->id == ScopeIdFnDef)
2869 return nullptr;
2870
2871 scope = scope->parent;
2872 }
2873 return nullptr;
2874}
2875
2780static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {2876static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {
2781 while (scope) {2877 while (scope) {
2782 if (scope->id == ScopeIdDeferExpr)2878 if (scope->id == ScopeIdDeferExpr)
...@@ -2822,34 +2918,6 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode...@@ -2822,34 +2918,6 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode
2822 // the above blocks are rendered by ir_gen after the rest of codegen2918 // the above blocks are rendered by ir_gen after the rest of codegen
2823}2919}
28242920
2825static bool exec_have_err_ret_trace(CodeGen *g, IrExecutable *exec) {
2826 if (!g->have_err_ret_tracing)
2827 return false;
2828 FnTableEntry *fn_entry = exec_fn_entry(exec);
2829 if (fn_entry == nullptr)
2830 return false;
2831 if (exec->is_inline)
2832 return false;
2833 return type_can_fail(fn_entry->type_entry->data.fn.fn_type_id.return_type);
2834}
2835
2836static void ir_gen_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *node) {
2837 if (!exec_have_err_ret_trace(irb->codegen, irb->exec))
2838 return;
2839
2840 bool is_async = exec_is_async(irb->exec);
2841
2842 if (is_async) {
2843 //IrInstruction *err_ret_addr_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_err_ret_addr_ptr);
2844 //IrInstruction *return_address_ptr = ir_build_instr_addr(irb, scope, node);
2845 //IrInstruction *return_address_usize = ir_build_ptr_to_int(irb, scope, node, return_address_ptr);
2846 //ir_build_store_ptr(irb, scope, node, err_ret_addr_ptr, return_address_usize);
2847 return;
2848 }
2849
2850 ir_build_save_err_ret_addr(irb, scope, node);
2851}
2852
2853static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {2921static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
2854 assert(node->type == NodeTypeReturnExpr);2922 assert(node->type == NodeTypeReturnExpr);
28552923
...@@ -2895,8 +2963,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,...@@ -2895,8 +2963,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
28952963
2896 IrInstruction *is_err = ir_build_test_err(irb, scope, node, return_value);2964 IrInstruction *is_err = ir_build_test_err(irb, scope, node, return_value);
28972965
2966 bool should_inline = ir_should_inline(irb->exec, scope);
2898 IrInstruction *is_comptime;2967 IrInstruction *is_comptime;
2899 if (ir_should_inline(irb->exec, scope)) {2968 if (should_inline) {
2900 is_comptime = ir_build_const_bool(irb, scope, node, true);2969 is_comptime = ir_build_const_bool(irb, scope, node, true);
2901 } else {2970 } else {
2902 is_comptime = ir_build_test_comptime(irb, scope, node, is_err);2971 is_comptime = ir_build_test_comptime(irb, scope, node, is_err);
...@@ -2909,7 +2978,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,...@@ -2909,7 +2978,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
2909 if (have_err_defers) {2978 if (have_err_defers) {
2910 ir_gen_defers_for_block(irb, scope, outer_scope, true);2979 ir_gen_defers_for_block(irb, scope, outer_scope, true);
2911 }2980 }
2912 ir_gen_save_err_ret_addr(irb, scope, node);2981 if (irb->codegen->have_err_ret_tracing && !should_inline) {
2982 ir_build_save_err_ret_addr(irb, scope, node);
2983 }
2913 ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);2984 ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
29142985
2915 ir_set_cursor_at_end_and_append_block(irb, ok_block);2986 ir_set_cursor_at_end_and_append_block(irb, ok_block);
...@@ -2938,7 +3009,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,...@@ -2938,7 +3009,8 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
2938 IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn");3009 IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn");
2939 IrBasicBlock *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue");3010 IrBasicBlock *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue");
2940 IrInstruction *is_comptime;3011 IrInstruction *is_comptime;
2941 if (ir_should_inline(irb->exec, scope)) {3012 bool should_inline = ir_should_inline(irb->exec, scope);
3013 if (should_inline) {
2942 is_comptime = ir_build_const_bool(irb, scope, node, true);3014 is_comptime = ir_build_const_bool(irb, scope, node, true);
2943 } else {3015 } else {
2944 is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val);3016 is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val);
...@@ -2946,10 +3018,13 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,...@@ -2946,10 +3018,13 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
2946 ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime));3018 ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime));
29473019
2948 ir_set_cursor_at_end_and_append_block(irb, return_block);3020 ir_set_cursor_at_end_and_append_block(irb, return_block);
2949 ir_gen_defers_for_block(irb, scope, outer_scope, true);3021 if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) {
2950 IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr);3022 IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr);
2951 ir_gen_save_err_ret_addr(irb, scope, node);3023 if (irb->codegen->have_err_ret_tracing && !should_inline) {
2952 ir_gen_async_return(irb, scope, node, err_val, false);3024 ir_build_save_err_ret_addr(irb, scope, node);
3025 }
3026 ir_gen_async_return(irb, scope, node, err_val, false);
3027 }
29533028
2954 ir_set_cursor_at_end_and_append_block(irb, continue_block);3029 ir_set_cursor_at_end_and_append_block(irb, continue_block);
2955 IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false);3030 IrInstruction *unwrapped_ptr = ir_build_unwrap_err_payload(irb, scope, node, err_union_ptr, false);
...@@ -3088,6 +3163,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode...@@ -3088,6 +3163,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
3088 if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) {3163 if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) {
3089 return noreturn_return_value;3164 return noreturn_return_value;
3090 }3165 }
3166
3167 ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
3168 return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
3091 } else {3169 } else {
3092 incoming_blocks.append(irb->current_basic_block);3170 incoming_blocks.append(irb->current_basic_block);
3093 incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node)));3171 incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node)));
...@@ -3481,7 +3559,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode...@@ -3481,7 +3559,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode
3481 return ir_build_load_ptr(irb, scope, node, ptr_instruction);3559 return ir_build_load_ptr(irb, scope, node, ptr_instruction);
3482}3560}
34833561
3484static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {3562static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node) {
3485 assert(node->type == NodeTypeFieldAccessExpr);3563 assert(node->type == NodeTypeFieldAccessExpr);
34863564
3487 AstNode *container_ref_node = node->data.field_access_expr.struct_expr;3565 AstNode *container_ref_node = node->data.field_access_expr.struct_expr;
...@@ -3491,11 +3569,7 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode...@@ -3491,11 +3569,7 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode
3491 if (container_ref_instruction == irb->codegen->invalid_instruction)3569 if (container_ref_instruction == irb->codegen->invalid_instruction)
3492 return container_ref_instruction;3570 return container_ref_instruction;
34933571
3494 IrInstruction *ptr_instruction = ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name);3572 return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name);
3495 if (lval.is_ptr)
3496 return ptr_instruction;
3497
3498 return ir_build_load_ptr(irb, scope, node, ptr_instruction);
3499}3573}
35003574
3501static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *node, IrOverflowOp op) {3575static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *node, IrOverflowOp op) {
...@@ -3526,7 +3600,7 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *...@@ -3526,7 +3600,7 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *
3526 return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr);3600 return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr);
3527}3601}
35283602
3529static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) {3603static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
3530 assert(node->type == NodeTypeFnCallExpr);3604 assert(node->type == NodeTypeFnCallExpr);
35313605
3532 AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;3606 AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
...@@ -3558,7 +3632,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3558,7 +3632,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3558 IrInstruction *arg = ir_gen_node(irb, arg_node, scope);3632 IrInstruction *arg = ir_gen_node(irb, arg_node, scope);
3559 if (arg == irb->codegen->invalid_instruction)3633 if (arg == irb->codegen->invalid_instruction)
3560 return arg;3634 return arg;
3561 return ir_build_typeof(irb, scope, node, arg);3635
3636 IrInstruction *type_of = ir_build_typeof(irb, scope, node, arg);
3637 return ir_lval_wrap(irb, scope, type_of, lval);
3562 }3638 }
3563 case BuiltinFnIdSetCold:3639 case BuiltinFnIdSetCold:
3564 {3640 {
...@@ -3567,7 +3643,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3567,7 +3643,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3567 if (arg0_value == irb->codegen->invalid_instruction)3643 if (arg0_value == irb->codegen->invalid_instruction)
3568 return arg0_value;3644 return arg0_value;
35693645
3570 return ir_build_set_cold(irb, scope, node, arg0_value);3646 IrInstruction *set_cold = ir_build_set_cold(irb, scope, node, arg0_value);
3647 return ir_lval_wrap(irb, scope, set_cold, lval);
3571 }3648 }
3572 case BuiltinFnIdSetRuntimeSafety:3649 case BuiltinFnIdSetRuntimeSafety:
3573 {3650 {
...@@ -3576,7 +3653,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3576,7 +3653,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3576 if (arg0_value == irb->codegen->invalid_instruction)3653 if (arg0_value == irb->codegen->invalid_instruction)
3577 return arg0_value;3654 return arg0_value;
35783655
3579 return ir_build_set_runtime_safety(irb, scope, node, arg0_value);3656 IrInstruction *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value);
3657 return ir_lval_wrap(irb, scope, set_safety, lval);
3580 }3658 }
3581 case BuiltinFnIdSetFloatMode:3659 case BuiltinFnIdSetFloatMode:
3582 {3660 {
...@@ -3590,7 +3668,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3590,7 +3668,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3590 if (arg1_value == irb->codegen->invalid_instruction)3668 if (arg1_value == irb->codegen->invalid_instruction)
3591 return arg1_value;3669 return arg1_value;
35923670
3593 return ir_build_set_float_mode(irb, scope, node, arg0_value, arg1_value);3671 IrInstruction *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value, arg1_value);
3672 return ir_lval_wrap(irb, scope, set_float_mode, lval);
3594 }3673 }
3595 case BuiltinFnIdSizeof:3674 case BuiltinFnIdSizeof:
3596 {3675 {
...@@ -3599,7 +3678,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3599,7 +3678,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3599 if (arg0_value == irb->codegen->invalid_instruction)3678 if (arg0_value == irb->codegen->invalid_instruction)
3600 return arg0_value;3679 return arg0_value;
36013680
3602 return ir_build_size_of(irb, scope, node, arg0_value);3681 IrInstruction *size_of = ir_build_size_of(irb, scope, node, arg0_value);
3682 return ir_lval_wrap(irb, scope, size_of, lval);
3603 }3683 }
3604 case BuiltinFnIdCtz:3684 case BuiltinFnIdCtz:
3605 {3685 {
...@@ -3608,7 +3688,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3608,7 +3688,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3608 if (arg0_value == irb->codegen->invalid_instruction)3688 if (arg0_value == irb->codegen->invalid_instruction)
3609 return arg0_value;3689 return arg0_value;
36103690
3611 return ir_build_ctz(irb, scope, node, arg0_value);3691 IrInstruction *ctz = ir_build_ctz(irb, scope, node, arg0_value);
3692 return ir_lval_wrap(irb, scope, ctz, lval);
3612 }3693 }
3613 case BuiltinFnIdClz:3694 case BuiltinFnIdClz:
3614 {3695 {
...@@ -3617,7 +3698,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3617,7 +3698,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3617 if (arg0_value == irb->codegen->invalid_instruction)3698 if (arg0_value == irb->codegen->invalid_instruction)
3618 return arg0_value;3699 return arg0_value;
36193700
3620 return ir_build_clz(irb, scope, node, arg0_value);3701 IrInstruction *clz = ir_build_clz(irb, scope, node, arg0_value);
3702 return ir_lval_wrap(irb, scope, clz, lval);
3621 }3703 }
3622 case BuiltinFnIdImport:3704 case BuiltinFnIdImport:
3623 {3705 {
...@@ -3626,11 +3708,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3626,11 +3708,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3626 if (arg0_value == irb->codegen->invalid_instruction)3708 if (arg0_value == irb->codegen->invalid_instruction)
3627 return arg0_value;3709 return arg0_value;
36283710
3629 return ir_build_import(irb, scope, node, arg0_value);3711 IrInstruction *import = ir_build_import(irb, scope, node, arg0_value);
3712 return ir_lval_wrap(irb, scope, import, lval);
3630 }3713 }
3631 case BuiltinFnIdCImport:3714 case BuiltinFnIdCImport:
3632 {3715 {
3633 return ir_build_c_import(irb, scope, node);3716 IrInstruction *c_import = ir_build_c_import(irb, scope, node);
3717 return ir_lval_wrap(irb, scope, c_import, lval);
3634 }3718 }
3635 case BuiltinFnIdCInclude:3719 case BuiltinFnIdCInclude:
3636 {3720 {
...@@ -3644,7 +3728,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3644,7 +3728,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3644 return irb->codegen->invalid_instruction;3728 return irb->codegen->invalid_instruction;
3645 }3729 }
36463730
3647 return ir_build_c_include(irb, scope, node, arg0_value);3731 IrInstruction *c_include = ir_build_c_include(irb, scope, node, arg0_value);
3732 return ir_lval_wrap(irb, scope, c_include, lval);
3648 }3733 }
3649 case BuiltinFnIdCDefine:3734 case BuiltinFnIdCDefine:
3650 {3735 {
...@@ -3663,7 +3748,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3663,7 +3748,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3663 return irb->codegen->invalid_instruction;3748 return irb->codegen->invalid_instruction;
3664 }3749 }
36653750
3666 return ir_build_c_define(irb, scope, node, arg0_value, arg1_value);3751 IrInstruction *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value);
3752 return ir_lval_wrap(irb, scope, c_define, lval);
3667 }3753 }
3668 case BuiltinFnIdCUndef:3754 case BuiltinFnIdCUndef:
3669 {3755 {
...@@ -3677,7 +3763,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3677,7 +3763,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3677 return irb->codegen->invalid_instruction;3763 return irb->codegen->invalid_instruction;
3678 }3764 }
36793765
3680 return ir_build_c_undef(irb, scope, node, arg0_value);3766 IrInstruction *c_undef = ir_build_c_undef(irb, scope, node, arg0_value);
3767 return ir_lval_wrap(irb, scope, c_undef, lval);
3681 }3768 }
3682 case BuiltinFnIdMaxValue:3769 case BuiltinFnIdMaxValue:
3683 {3770 {
...@@ -3686,7 +3773,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3686,7 +3773,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3686 if (arg0_value == irb->codegen->invalid_instruction)3773 if (arg0_value == irb->codegen->invalid_instruction)
3687 return arg0_value;3774 return arg0_value;
36883775
3689 return ir_build_max_value(irb, scope, node, arg0_value);3776 IrInstruction *max_value = ir_build_max_value(irb, scope, node, arg0_value);
3777 return ir_lval_wrap(irb, scope, max_value, lval);
3690 }3778 }
3691 case BuiltinFnIdMinValue:3779 case BuiltinFnIdMinValue:
3692 {3780 {
...@@ -3695,7 +3783,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3695,7 +3783,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3695 if (arg0_value == irb->codegen->invalid_instruction)3783 if (arg0_value == irb->codegen->invalid_instruction)
3696 return arg0_value;3784 return arg0_value;
36973785
3698 return ir_build_min_value(irb, scope, node, arg0_value);3786 IrInstruction *min_value = ir_build_min_value(irb, scope, node, arg0_value);
3787 return ir_lval_wrap(irb, scope, min_value, lval);
3699 }3788 }
3700 case BuiltinFnIdCompileErr:3789 case BuiltinFnIdCompileErr:
3701 {3790 {
...@@ -3704,7 +3793,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3704,7 +3793,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3704 if (arg0_value == irb->codegen->invalid_instruction)3793 if (arg0_value == irb->codegen->invalid_instruction)
3705 return arg0_value;3794 return arg0_value;
37063795
3707 return ir_build_compile_err(irb, scope, node, arg0_value);3796 IrInstruction *compile_err = ir_build_compile_err(irb, scope, node, arg0_value);
3797 return ir_lval_wrap(irb, scope, compile_err, lval);
3708 }3798 }
3709 case BuiltinFnIdCompileLog:3799 case BuiltinFnIdCompileLog:
3710 {3800 {
...@@ -3717,7 +3807,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3717,7 +3807,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3717 return irb->codegen->invalid_instruction;3807 return irb->codegen->invalid_instruction;
3718 }3808 }
37193809
3720 return ir_build_compile_log(irb, scope, node, actual_param_count, args);3810 IrInstruction *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args);
3811 return ir_lval_wrap(irb, scope, compile_log, lval);
3721 }3812 }
3722 case BuiltinFnIdErrName:3813 case BuiltinFnIdErrName:
3723 {3814 {
...@@ -3726,7 +3817,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3726,7 +3817,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3726 if (arg0_value == irb->codegen->invalid_instruction)3817 if (arg0_value == irb->codegen->invalid_instruction)
3727 return arg0_value;3818 return arg0_value;
37283819
3729 return ir_build_err_name(irb, scope, node, arg0_value);3820 IrInstruction *err_name = ir_build_err_name(irb, scope, node, arg0_value);
3821 return ir_lval_wrap(irb, scope, err_name, lval);
3730 }3822 }
3731 case BuiltinFnIdEmbedFile:3823 case BuiltinFnIdEmbedFile:
3732 {3824 {
...@@ -3735,9 +3827,11 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3735,9 +3827,11 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3735 if (arg0_value == irb->codegen->invalid_instruction)3827 if (arg0_value == irb->codegen->invalid_instruction)
3736 return arg0_value;3828 return arg0_value;
37373829
3738 return ir_build_embed_file(irb, scope, node, arg0_value);3830 IrInstruction *embed_file = ir_build_embed_file(irb, scope, node, arg0_value);
3831 return ir_lval_wrap(irb, scope, embed_file, lval);
3739 }3832 }
3740 case BuiltinFnIdCmpExchange:3833 case BuiltinFnIdCmpxchgWeak:
3834 case BuiltinFnIdCmpxchgStrong:
3741 {3835 {
3742 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);3836 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
3743 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);3837 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
...@@ -3764,9 +3858,15 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3764,9 +3858,15 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3764 if (arg4_value == irb->codegen->invalid_instruction)3858 if (arg4_value == irb->codegen->invalid_instruction)
3765 return arg4_value;3859 return arg4_value;
37663860
3767 return ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value,3861 AstNode *arg5_node = node->data.fn_call_expr.params.at(5);
3768 arg2_value, arg3_value, arg4_value,3862 IrInstruction *arg5_value = ir_gen_node(irb, arg5_node, scope);
3769 AtomicOrderUnordered, AtomicOrderUnordered);3863 if (arg5_value == irb->codegen->invalid_instruction)
3864 return arg5_value;
3865
3866 IrInstruction *cmpxchg = ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value,
3867 arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak),
3868 nullptr, AtomicOrderUnordered, AtomicOrderUnordered);
3869 return ir_lval_wrap(irb, scope, cmpxchg, lval);
3770 }3870 }
3771 case BuiltinFnIdFence:3871 case BuiltinFnIdFence:
3772 {3872 {
...@@ -3775,7 +3875,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3775,7 +3875,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3775 if (arg0_value == irb->codegen->invalid_instruction)3875 if (arg0_value == irb->codegen->invalid_instruction)
3776 return arg0_value;3876 return arg0_value;
37773877
3778 return ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered);3878 IrInstruction *fence = ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered);
3879 return ir_lval_wrap(irb, scope, fence, lval);
3779 }3880 }
3780 case BuiltinFnIdDivExact:3881 case BuiltinFnIdDivExact:
3781 {3882 {
...@@ -3789,7 +3890,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3789,7 +3890,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3789 if (arg1_value == irb->codegen->invalid_instruction)3890 if (arg1_value == irb->codegen->invalid_instruction)
3790 return arg1_value;3891 return arg1_value;
37913892
3792 return ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true);3893 IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true);
3894 return ir_lval_wrap(irb, scope, bin_op, lval);
3793 }3895 }
3794 case BuiltinFnIdDivTrunc:3896 case BuiltinFnIdDivTrunc:
3795 {3897 {
...@@ -3803,7 +3905,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3803,7 +3905,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3803 if (arg1_value == irb->codegen->invalid_instruction)3905 if (arg1_value == irb->codegen->invalid_instruction)
3804 return arg1_value;3906 return arg1_value;
38053907
3806 return ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true);3908 IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true);
3909 return ir_lval_wrap(irb, scope, bin_op, lval);
3807 }3910 }
3808 case BuiltinFnIdDivFloor:3911 case BuiltinFnIdDivFloor:
3809 {3912 {
...@@ -3817,7 +3920,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3817,7 +3920,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3817 if (arg1_value == irb->codegen->invalid_instruction)3920 if (arg1_value == irb->codegen->invalid_instruction)
3818 return arg1_value;3921 return arg1_value;
38193922
3820 return ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true);3923 IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true);
3924 return ir_lval_wrap(irb, scope, bin_op, lval);
3821 }3925 }
3822 case BuiltinFnIdRem:3926 case BuiltinFnIdRem:
3823 {3927 {
...@@ -3831,7 +3935,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3831,7 +3935,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3831 if (arg1_value == irb->codegen->invalid_instruction)3935 if (arg1_value == irb->codegen->invalid_instruction)
3832 return arg1_value;3936 return arg1_value;
38333937
3834 return ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true);3938 IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true);
3939 return ir_lval_wrap(irb, scope, bin_op, lval);
3835 }3940 }
3836 case BuiltinFnIdMod:3941 case BuiltinFnIdMod:
3837 {3942 {
...@@ -3845,7 +3950,23 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3845,7 +3950,23 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3845 if (arg1_value == irb->codegen->invalid_instruction)3950 if (arg1_value == irb->codegen->invalid_instruction)
3846 return arg1_value;3951 return arg1_value;
38473952
3848 return ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true);3953 IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true);
3954 return ir_lval_wrap(irb, scope, bin_op, lval);
3955 }
3956 case BuiltinFnIdSqrt:
3957 {
3958 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
3959 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
3960 if (arg0_value == irb->codegen->invalid_instruction)
3961 return arg0_value;
3962
3963 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
3964 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
3965 if (arg1_value == irb->codegen->invalid_instruction)
3966 return arg1_value;
3967
3968 IrInstruction *ir_sqrt = ir_build_sqrt(irb, scope, node, arg0_value, arg1_value);
3969 return ir_lval_wrap(irb, scope, ir_sqrt, lval);
3849 }3970 }
3850 case BuiltinFnIdTruncate:3971 case BuiltinFnIdTruncate:
3851 {3972 {
...@@ -3859,7 +3980,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3859,7 +3980,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3859 if (arg1_value == irb->codegen->invalid_instruction)3980 if (arg1_value == irb->codegen->invalid_instruction)
3860 return arg1_value;3981 return arg1_value;
38613982
3862 return ir_build_truncate(irb, scope, node, arg0_value, arg1_value);3983 IrInstruction *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value);
3984 return ir_lval_wrap(irb, scope, truncate, lval);
3863 }3985 }
3864 case BuiltinFnIdIntType:3986 case BuiltinFnIdIntType:
3865 {3987 {
...@@ -3873,7 +3995,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3873,7 +3995,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3873 if (arg1_value == irb->codegen->invalid_instruction)3995 if (arg1_value == irb->codegen->invalid_instruction)
3874 return arg1_value;3996 return arg1_value;
38753997
3876 return ir_build_int_type(irb, scope, node, arg0_value, arg1_value);3998 IrInstruction *int_type = ir_build_int_type(irb, scope, node, arg0_value, arg1_value);
3999 return ir_lval_wrap(irb, scope, int_type, lval);
3877 }4000 }
3878 case BuiltinFnIdMemcpy:4001 case BuiltinFnIdMemcpy:
3879 {4002 {
...@@ -3892,7 +4015,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3892,7 +4015,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3892 if (arg2_value == irb->codegen->invalid_instruction)4015 if (arg2_value == irb->codegen->invalid_instruction)
3893 return arg2_value;4016 return arg2_value;
38944017
3895 return ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value);4018 IrInstruction *ir_memcpy = ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value);
4019 return ir_lval_wrap(irb, scope, ir_memcpy, lval);
3896 }4020 }
3897 case BuiltinFnIdMemset:4021 case BuiltinFnIdMemset:
3898 {4022 {
...@@ -3911,7 +4035,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3911,7 +4035,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3911 if (arg2_value == irb->codegen->invalid_instruction)4035 if (arg2_value == irb->codegen->invalid_instruction)
3912 return arg2_value;4036 return arg2_value;
39134037
3914 return ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value);4038 IrInstruction *ir_memset = ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value);
4039 return ir_lval_wrap(irb, scope, ir_memset, lval);
3915 }4040 }
3916 case BuiltinFnIdMemberCount:4041 case BuiltinFnIdMemberCount:
3917 {4042 {
...@@ -3920,7 +4045,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3920,7 +4045,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3920 if (arg0_value == irb->codegen->invalid_instruction)4045 if (arg0_value == irb->codegen->invalid_instruction)
3921 return arg0_value;4046 return arg0_value;
39224047
3923 return ir_build_member_count(irb, scope, node, arg0_value);4048 IrInstruction *member_count = ir_build_member_count(irb, scope, node, arg0_value);
4049 return ir_lval_wrap(irb, scope, member_count, lval);
3924 }4050 }
3925 case BuiltinFnIdMemberType:4051 case BuiltinFnIdMemberType:
3926 {4052 {
...@@ -3935,7 +4061,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3935,7 +4061,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3935 return arg1_value;4061 return arg1_value;
39364062
39374063
3938 return ir_build_member_type(irb, scope, node, arg0_value, arg1_value);4064 IrInstruction *member_type = ir_build_member_type(irb, scope, node, arg0_value, arg1_value);
4065 return ir_lval_wrap(irb, scope, member_type, lval);
3939 }4066 }
3940 case BuiltinFnIdMemberName:4067 case BuiltinFnIdMemberName:
3941 {4068 {
...@@ -3950,14 +4077,44 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3950,14 +4077,44 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3950 return arg1_value;4077 return arg1_value;
39514078
39524079
3953 return ir_build_member_name(irb, scope, node, arg0_value, arg1_value);4080 IrInstruction *member_name = ir_build_member_name(irb, scope, node, arg0_value, arg1_value);
4081 return ir_lval_wrap(irb, scope, member_name, lval);
4082 }
4083 case BuiltinFnIdField:
4084 {
4085 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4086 IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LVAL_PTR);
4087 if (arg0_value == irb->codegen->invalid_instruction)
4088 return arg0_value;
4089
4090 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4091 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
4092 if (arg1_value == irb->codegen->invalid_instruction)
4093 return arg1_value;
4094
4095 IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, arg0_value, arg1_value);
4096
4097 if (lval.is_ptr)
4098 return ptr_instruction;
4099
4100 return ir_build_load_ptr(irb, scope, node, ptr_instruction);
4101 }
4102 case BuiltinFnIdTypeInfo:
4103 {
4104 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4105 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
4106 if (arg0_value == irb->codegen->invalid_instruction)
4107 return arg0_value;
4108
4109 IrInstruction *type_info = ir_build_type_info(irb, scope, node, arg0_value);
4110 return ir_lval_wrap(irb, scope, type_info, lval);
3954 }4111 }
3955 case BuiltinFnIdBreakpoint:4112 case BuiltinFnIdBreakpoint:
3956 return ir_build_breakpoint(irb, scope, node);4113 return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval);
3957 case BuiltinFnIdReturnAddress:4114 case BuiltinFnIdReturnAddress:
3958 return ir_build_return_address(irb, scope, node);4115 return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval);
3959 case BuiltinFnIdFrameAddress:4116 case BuiltinFnIdFrameAddress:
3960 return ir_build_frame_address(irb, scope, node);4117 return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval);
3961 case BuiltinFnIdAlignOf:4118 case BuiltinFnIdAlignOf:
3962 {4119 {
3963 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);4120 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
...@@ -3965,16 +4122,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3965,16 +4122,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3965 if (arg0_value == irb->codegen->invalid_instruction)4122 if (arg0_value == irb->codegen->invalid_instruction)
3966 return arg0_value;4123 return arg0_value;
39674124
3968 return ir_build_align_of(irb, scope, node, arg0_value);4125 IrInstruction *align_of = ir_build_align_of(irb, scope, node, arg0_value);
4126 return ir_lval_wrap(irb, scope, align_of, lval);
3969 }4127 }
3970 case BuiltinFnIdAddWithOverflow:4128 case BuiltinFnIdAddWithOverflow:
3971 return ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd);4129 return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval);
3972 case BuiltinFnIdSubWithOverflow:4130 case BuiltinFnIdSubWithOverflow:
3973 return ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub);4131 return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval);
3974 case BuiltinFnIdMulWithOverflow:4132 case BuiltinFnIdMulWithOverflow:
3975 return ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul);4133 return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval);
3976 case BuiltinFnIdShlWithOverflow:4134 case BuiltinFnIdShlWithOverflow:
3977 return ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl);4135 return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval);
3978 case BuiltinFnIdTypeName:4136 case BuiltinFnIdTypeName:
3979 {4137 {
3980 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);4138 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
...@@ -3982,7 +4140,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3982,7 +4140,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3982 if (arg0_value == irb->codegen->invalid_instruction)4140 if (arg0_value == irb->codegen->invalid_instruction)
3983 return arg0_value;4141 return arg0_value;
39844142
3985 return ir_build_type_name(irb, scope, node, arg0_value);4143 IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value);
4144 return ir_lval_wrap(irb, scope, type_name, lval);
3986 }4145 }
3987 case BuiltinFnIdCanImplicitCast:4146 case BuiltinFnIdCanImplicitCast:
3988 {4147 {
...@@ -3996,7 +4155,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3996,7 +4155,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3996 if (arg1_value == irb->codegen->invalid_instruction)4155 if (arg1_value == irb->codegen->invalid_instruction)
3997 return arg1_value;4156 return arg1_value;
39984157
3999 return ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value);4158 IrInstruction *can_implicit_cast = ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value);
4159 return ir_lval_wrap(irb, scope, can_implicit_cast, lval);
4000 }4160 }
4001 case BuiltinFnIdPanic:4161 case BuiltinFnIdPanic:
4002 {4162 {
...@@ -4005,7 +4165,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4005,7 +4165,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4005 if (arg0_value == irb->codegen->invalid_instruction)4165 if (arg0_value == irb->codegen->invalid_instruction)
4006 return arg0_value;4166 return arg0_value;
40074167
4008 return ir_build_panic(irb, scope, node, arg0_value);4168 IrInstruction *panic = ir_build_panic(irb, scope, node, arg0_value);
4169 return ir_lval_wrap(irb, scope, panic, lval);
4009 }4170 }
4010 case BuiltinFnIdPtrCast:4171 case BuiltinFnIdPtrCast:
4011 {4172 {
...@@ -4019,7 +4180,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4019,7 +4180,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4019 if (arg1_value == irb->codegen->invalid_instruction)4180 if (arg1_value == irb->codegen->invalid_instruction)
4020 return arg1_value;4181 return arg1_value;
40214182
4022 return ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value);4183 IrInstruction *ptr_cast = ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value);
4184 return ir_lval_wrap(irb, scope, ptr_cast, lval);
4023 }4185 }
4024 case BuiltinFnIdBitCast:4186 case BuiltinFnIdBitCast:
4025 {4187 {
...@@ -4033,7 +4195,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4033,7 +4195,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4033 if (arg1_value == irb->codegen->invalid_instruction)4195 if (arg1_value == irb->codegen->invalid_instruction)
4034 return arg1_value;4196 return arg1_value;
40354197
4036 return ir_build_bit_cast(irb, scope, node, arg0_value, arg1_value);4198 IrInstruction *bit_cast = ir_build_bit_cast(irb, scope, node, arg0_value, arg1_value);
4199 return ir_lval_wrap(irb, scope, bit_cast, lval);
4037 }4200 }
4038 case BuiltinFnIdIntToPtr:4201 case BuiltinFnIdIntToPtr:
4039 {4202 {
...@@ -4047,7 +4210,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4047,7 +4210,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4047 if (arg1_value == irb->codegen->invalid_instruction)4210 if (arg1_value == irb->codegen->invalid_instruction)
4048 return arg1_value;4211 return arg1_value;
40494212
4050 return ir_build_int_to_ptr(irb, scope, node, arg0_value, arg1_value);4213 IrInstruction *int_to_ptr = ir_build_int_to_ptr(irb, scope, node, arg0_value, arg1_value);
4214 return ir_lval_wrap(irb, scope, int_to_ptr, lval);
4051 }4215 }
4052 case BuiltinFnIdPtrToInt:4216 case BuiltinFnIdPtrToInt:
4053 {4217 {
...@@ -4056,7 +4220,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4056,7 +4220,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4056 if (arg0_value == irb->codegen->invalid_instruction)4220 if (arg0_value == irb->codegen->invalid_instruction)
4057 return arg0_value;4221 return arg0_value;
40584222
4059 return ir_build_ptr_to_int(irb, scope, node, arg0_value);4223 IrInstruction *ptr_to_int = ir_build_ptr_to_int(irb, scope, node, arg0_value);
4224 return ir_lval_wrap(irb, scope, ptr_to_int, lval);
4060 }4225 }
4061 case BuiltinFnIdTagName:4226 case BuiltinFnIdTagName:
4062 {4227 {
...@@ -4066,7 +4231,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4066,7 +4231,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4066 return arg0_value;4231 return arg0_value;
40674232
4068 IrInstruction *actual_tag = ir_build_union_tag(irb, scope, node, arg0_value);4233 IrInstruction *actual_tag = ir_build_union_tag(irb, scope, node, arg0_value);
4069 return ir_build_tag_name(irb, scope, node, actual_tag);4234 IrInstruction *tag_name = ir_build_tag_name(irb, scope, node, actual_tag);
4235 return ir_lval_wrap(irb, scope, tag_name, lval);
4070 }4236 }
4071 case BuiltinFnIdTagType:4237 case BuiltinFnIdTagType:
4072 {4238 {
...@@ -4075,7 +4241,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4075,7 +4241,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4075 if (arg0_value == irb->codegen->invalid_instruction)4241 if (arg0_value == irb->codegen->invalid_instruction)
4076 return arg0_value;4242 return arg0_value;
40774243
4078 return ir_build_tag_type(irb, scope, node, arg0_value);4244 IrInstruction *tag_type = ir_build_tag_type(irb, scope, node, arg0_value);
4245 return ir_lval_wrap(irb, scope, tag_type, lval);
4079 }4246 }
4080 case BuiltinFnIdFieldParentPtr:4247 case BuiltinFnIdFieldParentPtr:
4081 {4248 {
...@@ -4094,7 +4261,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4094,7 +4261,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4094 if (arg2_value == irb->codegen->invalid_instruction)4261 if (arg2_value == irb->codegen->invalid_instruction)
4095 return arg2_value;4262 return arg2_value;
40964263
4097 return ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr);4264 IrInstruction *field_parent_ptr = ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr);
4265 return ir_lval_wrap(irb, scope, field_parent_ptr, lval);
4098 }4266 }
4099 case BuiltinFnIdOffsetOf:4267 case BuiltinFnIdOffsetOf:
4100 {4268 {
...@@ -4108,7 +4276,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4108,7 +4276,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4108 if (arg1_value == irb->codegen->invalid_instruction)4276 if (arg1_value == irb->codegen->invalid_instruction)
4109 return arg1_value;4277 return arg1_value;
41104278
4111 return ir_build_offset_of(irb, scope, node, arg0_value, arg1_value);4279 IrInstruction *offset_of = ir_build_offset_of(irb, scope, node, arg0_value, arg1_value);
4280 return ir_lval_wrap(irb, scope, offset_of, lval);
4112 }4281 }
4113 case BuiltinFnIdInlineCall:4282 case BuiltinFnIdInlineCall:
4114 case BuiltinFnIdNoInlineCall:4283 case BuiltinFnIdNoInlineCall:
...@@ -4134,7 +4303,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4134,7 +4303,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4134 }4303 }
4135 FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever;4304 FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever;
41364305
4137 return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr);4306 IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr);
4307 return ir_lval_wrap(irb, scope, call, lval);
4138 }4308 }
4139 case BuiltinFnIdTypeId:4309 case BuiltinFnIdTypeId:
4140 {4310 {
...@@ -4143,7 +4313,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4143,7 +4313,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4143 if (arg0_value == irb->codegen->invalid_instruction)4313 if (arg0_value == irb->codegen->invalid_instruction)
4144 return arg0_value;4314 return arg0_value;
41454315
4146 return ir_build_type_id(irb, scope, node, arg0_value);4316 IrInstruction *type_id = ir_build_type_id(irb, scope, node, arg0_value);
4317 return ir_lval_wrap(irb, scope, type_id, lval);
4147 }4318 }
4148 case BuiltinFnIdShlExact:4319 case BuiltinFnIdShlExact:
4149 {4320 {
...@@ -4157,7 +4328,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4157,7 +4328,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4157 if (arg1_value == irb->codegen->invalid_instruction)4328 if (arg1_value == irb->codegen->invalid_instruction)
4158 return arg1_value;4329 return arg1_value;
41594330
4160 return ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true);4331 IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true);
4332 return ir_lval_wrap(irb, scope, bin_op, lval);
4161 }4333 }
4162 case BuiltinFnIdShrExact:4334 case BuiltinFnIdShrExact:
4163 {4335 {
...@@ -4171,7 +4343,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4171,7 +4343,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4171 if (arg1_value == irb->codegen->invalid_instruction)4343 if (arg1_value == irb->codegen->invalid_instruction)
4172 return arg1_value;4344 return arg1_value;
41734345
4174 return ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true);4346 IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true);
4347 return ir_lval_wrap(irb, scope, bin_op, lval);
4175 }4348 }
4176 case BuiltinFnIdSetEvalBranchQuota:4349 case BuiltinFnIdSetEvalBranchQuota:
4177 {4350 {
...@@ -4180,7 +4353,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4180,7 +4353,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4180 if (arg0_value == irb->codegen->invalid_instruction)4353 if (arg0_value == irb->codegen->invalid_instruction)
4181 return arg0_value;4354 return arg0_value;
41824355
4183 return ir_build_set_eval_branch_quota(irb, scope, node, arg0_value);4356 IrInstruction *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value);
4357 return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval);
4184 }4358 }
4185 case BuiltinFnIdAlignCast:4359 case BuiltinFnIdAlignCast:
4186 {4360 {
...@@ -4194,10 +4368,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4194,10 +4368,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4194 if (arg1_value == irb->codegen->invalid_instruction)4368 if (arg1_value == irb->codegen->invalid_instruction)
4195 return arg1_value;4369 return arg1_value;
41964370
4197 return ir_build_align_cast(irb, scope, node, arg0_value, arg1_value);4371 IrInstruction *align_cast = ir_build_align_cast(irb, scope, node, arg0_value, arg1_value);
4372 return ir_lval_wrap(irb, scope, align_cast, lval);
4198 }4373 }
4199 case BuiltinFnIdOpaqueType:4374 case BuiltinFnIdOpaqueType:
4200 return ir_build_opaque_type(irb, scope, node);4375 {
4376 IrInstruction *opaque_type = ir_build_opaque_type(irb, scope, node);
4377 return ir_lval_wrap(irb, scope, opaque_type, lval);
4378 }
4201 case BuiltinFnIdSetAlignStack:4379 case BuiltinFnIdSetAlignStack:
4202 {4380 {
4203 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);4381 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
...@@ -4205,7 +4383,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4205,7 +4383,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4205 if (arg0_value == irb->codegen->invalid_instruction)4383 if (arg0_value == irb->codegen->invalid_instruction)
4206 return arg0_value;4384 return arg0_value;
42074385
4208 return ir_build_set_align_stack(irb, scope, node, arg0_value);4386 IrInstruction *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value);
4387 return ir_lval_wrap(irb, scope, set_align_stack, lval);
4209 }4388 }
4210 case BuiltinFnIdArgType:4389 case BuiltinFnIdArgType:
4211 {4390 {
...@@ -4219,7 +4398,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4219,7 +4398,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4219 if (arg1_value == irb->codegen->invalid_instruction)4398 if (arg1_value == irb->codegen->invalid_instruction)
4220 return arg1_value;4399 return arg1_value;
42214400
4222 return ir_build_arg_type(irb, scope, node, arg0_value, arg1_value);4401 IrInstruction *arg_type = ir_build_arg_type(irb, scope, node, arg0_value, arg1_value);
4402 return ir_lval_wrap(irb, scope, arg_type, lval);
4223 }4403 }
4224 case BuiltinFnIdExport:4404 case BuiltinFnIdExport:
4225 {4405 {
...@@ -4238,11 +4418,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4238,11 +4418,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4238 if (arg2_value == irb->codegen->invalid_instruction)4418 if (arg2_value == irb->codegen->invalid_instruction)
4239 return arg2_value;4419 return arg2_value;
42404420
4241 return ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value);4421 IrInstruction *ir_export = ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value);
4422 return ir_lval_wrap(irb, scope, ir_export, lval);
4242 }4423 }
4243 case BuiltinFnIdErrorReturnTrace:4424 case BuiltinFnIdErrorReturnTrace:
4244 {4425 {
4245 return ir_build_error_return_trace(irb, scope, node);4426 IrInstruction *error_return_trace = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::Null);
4427 return ir_lval_wrap(irb, scope, error_return_trace, lval);
4246 }4428 }
4247 case BuiltinFnIdAtomicRmw:4429 case BuiltinFnIdAtomicRmw:
4248 {4430 {
...@@ -4276,15 +4458,36 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4276,15 +4458,36 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4276 // these 2 values don't mean anything since we passed non-null values for other args4458 // these 2 values don't mean anything since we passed non-null values for other args
4277 AtomicRmwOp_xchg, AtomicOrderMonotonic);4459 AtomicRmwOp_xchg, AtomicOrderMonotonic);
4278 }4460 }
4461 case BuiltinFnIdAtomicLoad:
4462 {
4463 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4464 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
4465 if (arg0_value == irb->codegen->invalid_instruction)
4466 return arg0_value;
4467
4468 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4469 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
4470 if (arg1_value == irb->codegen->invalid_instruction)
4471 return arg1_value;
4472
4473 AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
4474 IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
4475 if (arg2_value == irb->codegen->invalid_instruction)
4476 return arg2_value;
4477
4478 return ir_build_atomic_load(irb, scope, node, arg0_value, arg1_value, arg2_value,
4479 // this value does not mean anything since we passed non-null values for other arg
4480 AtomicOrderMonotonic);
4481 }
4279 }4482 }
4280 zig_unreachable();4483 zig_unreachable();
4281}4484}
42824485
4283static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) {4486static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
4284 assert(node->type == NodeTypeFnCallExpr);4487 assert(node->type == NodeTypeFnCallExpr);
42854488
4286 if (node->data.fn_call_expr.is_builtin)4489 if (node->data.fn_call_expr.is_builtin)
4287 return ir_gen_builtin_fn_call(irb, scope, node);4490 return ir_gen_builtin_fn_call(irb, scope, node, lval);
42884491
4289 AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;4492 AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
4290 IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);4493 IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
...@@ -4310,7 +4513,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node...@@ -4310,7 +4513,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node
4310 }4513 }
4311 }4514 }
43124515
4313 return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator);4516 IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator);
4517 return ir_lval_wrap(irb, scope, fn_call, lval);
4314}4518}
43154519
4316static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) {4520static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
...@@ -5593,6 +5797,15 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop...@@ -5593,6 +5797,15 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop
5593 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);5797 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
5594}5798}
55955799
5800static IrInstruction *ir_gen_break_from_suspend(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeSuspend *suspend_scope) {
5801 IrInstruction *is_comptime = ir_build_const_bool(irb, break_scope, node, false);
5802
5803 IrBasicBlock *dest_block = suspend_scope->resume_block;
5804 ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
5805
5806 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
5807}
5808
5596static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {5809static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {
5597 assert(node->type == NodeTypeBreak);5810 assert(node->type == NodeTypeBreak);
55985811
...@@ -5632,6 +5845,13 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *...@@ -5632,6 +5845,13 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *
5632 assert(this_block_scope->end_block != nullptr);5845 assert(this_block_scope->end_block != nullptr);
5633 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);5846 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
5634 }5847 }
5848 } else if (search_scope->id == ScopeIdSuspend) {
5849 ScopeSuspend *this_suspend_scope = (ScopeSuspend *)search_scope;
5850 if (node->data.break_expr.name != nullptr &&
5851 (this_suspend_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_suspend_scope->name)))
5852 {
5853 return ir_gen_break_from_suspend(irb, break_scope, node, this_suspend_scope);
5854 }
5635 }5855 }
5636 search_scope = search_scope->parent;5856 search_scope = search_scope->parent;
5637 }5857 }
...@@ -5703,7 +5923,7 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast...@@ -5703,7 +5923,7 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast
57035923
5704 IrBasicBlock *dest_block = loop_scope->continue_block;5924 IrBasicBlock *dest_block = loop_scope->continue_block;
5705 ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, false);5925 ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, false);
5706 return ir_build_br(irb, continue_scope, node, dest_block, is_comptime);5926 return ir_mark_gen(ir_build_br(irb, continue_scope, node, dest_block, is_comptime));
5707}5927}
57085928
5709static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *node) {5929static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *node) {
...@@ -5972,16 +6192,10 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A...@@ -5972,16 +6192,10 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A
5972 buf_init_from_buf(&err_set_type->name, type_name);6192 buf_init_from_buf(&err_set_type->name, type_name);
5973 err_set_type->is_copyable = true;6193 err_set_type->is_copyable = true;
5974 err_set_type->data.error_set.err_count = err_count;6194 err_set_type->data.error_set.err_count = err_count;
59756195 err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref;
5976 if (err_count == 0) {6196 err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type;
5977 err_set_type->zero_bits = true;6197 irb->codegen->error_di_types.append(&err_set_type->di_type);
5978 err_set_type->di_type = irb->codegen->builtin_types.entry_void->di_type;6198 err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count);
5979 } else {
5980 err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref;
5981 err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type;
5982 irb->codegen->error_di_types.append(&err_set_type->di_type);
5983 err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count);
5984 }
59856199
5986 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count);6200 ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count);
59876201
...@@ -6125,6 +6339,13 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast...@@ -6125,6 +6339,13 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
6125 Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);6339 Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
6126 IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_ptr_field_name);6340 IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_ptr_field_name);
61276341
6342 if (irb->codegen->have_err_ret_tracing) {
6343 IrInstruction *err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull);
6344 Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME);
6345 IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);
6346 ir_build_store_ptr(irb, parent_scope, node, err_ret_trace_ptr_field_ptr, err_ret_trace_ptr);
6347 }
6348
6128 Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME);6349 Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME);
6129 IrInstruction *awaiter_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr,6350 IrInstruction *awaiter_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr,
6130 awaiter_handle_field_name);6351 awaiter_handle_field_name);
...@@ -6148,10 +6369,16 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast...@@ -6148,10 +6369,16 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
6148 IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_await_handle);6369 IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_await_handle);
6149 IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, parent_scope, "YesSuspend");6370 IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, parent_scope, "YesSuspend");
6150 IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, parent_scope, "NoSuspend");6371 IrBasicBlock *no_suspend_block = ir_create_basic_block(irb, parent_scope, "NoSuspend");
6151 IrBasicBlock *merge_block = ir_create_basic_block(irb, parent_scope, "Merge");6372 IrBasicBlock *merge_block = ir_create_basic_block(irb, parent_scope, "MergeSuspend");
6152 ir_build_cond_br(irb, parent_scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false);6373 ir_build_cond_br(irb, parent_scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false);
61536374
6154 ir_set_cursor_at_end_and_append_block(irb, no_suspend_block);6375 ir_set_cursor_at_end_and_append_block(irb, no_suspend_block);
6376 if (irb->codegen->have_err_ret_tracing) {
6377 Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME);
6378 IrInstruction *src_err_ret_trace_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, err_ret_trace_field_name);
6379 IrInstruction *dest_err_ret_trace_ptr = ir_build_error_return_trace(irb, parent_scope, node, IrInstructionErrorReturnTrace::NonNull);
6380 ir_build_merge_err_ret_traces(irb, parent_scope, node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);
6381 }
6155 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);6382 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
6156 IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name);6383 IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name);
6157 IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr);6384 IrInstruction *no_suspend_result = ir_build_load_ptr(irb, parent_scope, node, promise_result_ptr);
...@@ -6173,7 +6400,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast...@@ -6173,7 +6400,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast
61736400
6174 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);6401 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
6175 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);6402 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
6176 ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false);6403 ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));
61776404
6178 ir_set_cursor_at_end_and_append_block(irb, resume_block);6405 ir_set_cursor_at_end_and_append_block(irb, resume_block);
6179 IrInstruction *yes_suspend_result = ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr);6406 IrInstruction *yes_suspend_result = ir_build_load_ptr(irb, parent_scope, node, my_result_var_ptr);
...@@ -6205,14 +6432,26 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod...@@ -6205,14 +6432,26 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
6205 ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope);6432 ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope);
6206 if (scope_defer_expr) {6433 if (scope_defer_expr) {
6207 if (!scope_defer_expr->reported_err) {6434 if (!scope_defer_expr->reported_err) {
6208 add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression"));6435 ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression"));
6436 add_error_note(irb->codegen, msg, scope_defer_expr->base.source_node, buf_sprintf("defer here"));
6209 scope_defer_expr->reported_err = true;6437 scope_defer_expr->reported_err = true;
6210 }6438 }
6211 return irb->codegen->invalid_instruction;6439 return irb->codegen->invalid_instruction;
6212 }6440 }
6441 ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope);
6442 if (existing_suspend_scope) {
6443 if (!existing_suspend_scope->reported_err) {
6444 ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside suspend block"));
6445 add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("other suspend block here"));
6446 existing_suspend_scope->reported_err = true;
6447 }
6448 return irb->codegen->invalid_instruction;
6449 }
62136450
6214 Scope *outer_scope = irb->exec->begin_scope;6451 Scope *outer_scope = irb->exec->begin_scope;
62156452
6453 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");
6454 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
62166455
6217 IrInstruction *suspend_code;6456 IrInstruction *suspend_code;
6218 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);6457 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);
...@@ -6231,28 +6470,28 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod...@@ -6231,28 +6470,28 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
6231 } else {6470 } else {
6232 child_scope = parent_scope;6471 child_scope = parent_scope;
6233 }6472 }
6473 ScopeSuspend *suspend_scope = create_suspend_scope(node, child_scope);
6474 suspend_scope->resume_block = resume_block;
6475 child_scope = &suspend_scope->base;
6234 IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);6476 IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);
6235 ir_gen_node(irb, node->data.suspend.block, child_scope);6477 ir_gen_node(irb, node->data.suspend.block, child_scope);
6236 suspend_code = ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false);6478 suspend_code = ir_mark_gen(ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false));
6237 }6479 }
62386480
6239 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");
6240 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
6241
6242 IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);6481 IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);
6243 cases[0].value = ir_build_const_u8(irb, parent_scope, node, 0);6482 cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0));
6244 cases[0].block = resume_block;6483 cases[0].block = resume_block;
6245 cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1);6484 cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1));
6246 cases[1].block = cleanup_block;6485 cases[1].block = cleanup_block;
6247 ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,6486 ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,
6248 2, cases, const_bool_false);6487 2, cases, const_bool_false));
62496488
6250 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);6489 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
6251 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);6490 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
6252 ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false);6491 ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));
62536492
6254 ir_set_cursor_at_end_and_append_block(irb, resume_block);6493 ir_set_cursor_at_end_and_append_block(irb, resume_block);
6255 return ir_build_const_void(irb, parent_scope, node);6494 return ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
6256}6495}
62576496
6258static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,6497static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
...@@ -6268,7 +6507,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop...@@ -6268,7 +6507,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
6268 case NodeTypeSwitchRange:6507 case NodeTypeSwitchRange:
6269 case NodeTypeStructField:6508 case NodeTypeStructField:
6270 case NodeTypeFnDef:6509 case NodeTypeFnDef:
6271 case NodeTypeFnDecl:
6272 case NodeTypeTestDecl:6510 case NodeTypeTestDecl:
6273 zig_unreachable();6511 zig_unreachable();
6274 case NodeTypeBlock:6512 case NodeTypeBlock:
...@@ -6286,7 +6524,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop...@@ -6286,7 +6524,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
6286 case NodeTypeSymbol:6524 case NodeTypeSymbol:
6287 return ir_gen_symbol(irb, scope, node, lval);6525 return ir_gen_symbol(irb, scope, node, lval);
6288 case NodeTypeFnCallExpr:6526 case NodeTypeFnCallExpr:
6289 return ir_lval_wrap(irb, scope, ir_gen_fn_call(irb, scope, node), lval);6527 return ir_gen_fn_call(irb, scope, node, lval);
6290 case NodeTypeIfBoolExpr:6528 case NodeTypeIfBoolExpr:
6291 return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval);6529 return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval);
6292 case NodeTypePrefixOpExpr:6530 case NodeTypePrefixOpExpr:
...@@ -6306,7 +6544,15 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop...@@ -6306,7 +6544,15 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
6306 case NodeTypeReturnExpr:6544 case NodeTypeReturnExpr:
6307 return ir_gen_return(irb, scope, node, lval);6545 return ir_gen_return(irb, scope, node, lval);
6308 case NodeTypeFieldAccessExpr:6546 case NodeTypeFieldAccessExpr:
6309 return ir_gen_field_access(irb, scope, node, lval);6547 {
6548 IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node);
6549 if (ptr_instruction == irb->codegen->invalid_instruction)
6550 return ptr_instruction;
6551 if (lval.is_ptr)
6552 return ptr_instruction;
6553
6554 return ir_build_load_ptr(irb, scope, node, ptr_instruction);
6555 }
6310 case NodeTypeThisLiteral:6556 case NodeTypeThisLiteral:
6311 return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval);6557 return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval);
6312 case NodeTypeBoolLiteral:6558 case NodeTypeBoolLiteral:
...@@ -6407,6 +6653,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -6407,6 +6653,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
6407 IrInstruction *coro_id;6653 IrInstruction *coro_id;
6408 IrInstruction *u8_ptr_type;6654 IrInstruction *u8_ptr_type;
6409 IrInstruction *const_bool_false;6655 IrInstruction *const_bool_false;
6656 IrInstruction *coro_promise_ptr;
6657 IrInstruction *err_ret_trace_ptr;
6410 TypeTableEntry *return_type;6658 TypeTableEntry *return_type;
6411 Buf *result_ptr_field_name;6659 Buf *result_ptr_field_name;
6412 VariableTableEntry *coro_size_var;6660 VariableTableEntry *coro_size_var;
...@@ -6417,9 +6665,12 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -6417,9 +6665,12 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
6417 VariableTableEntry *promise_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);6665 VariableTableEntry *promise_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
64186666
6419 return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type;6667 return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type;
6420 IrInstruction *promise_init = ir_build_const_promise_init(irb, coro_scope, node, return_type);6668 IrInstruction *undef = ir_build_const_undefined(irb, coro_scope, node);
6421 ir_build_var_decl(irb, coro_scope, node, promise_var, nullptr, nullptr, promise_init);6669 TypeTableEntry *coro_frame_type = get_promise_frame_type(irb->codegen, return_type);
6422 IrInstruction *coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var, false, false);6670 IrInstruction *coro_frame_type_value = ir_build_const_type(irb, coro_scope, node, coro_frame_type);
6671 // TODO mark this var decl as "no safety" e.g. disable initializing the undef value to 0xaa
6672 ir_build_var_decl(irb, coro_scope, node, promise_var, coro_frame_type_value, nullptr, undef);
6673 coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var, false, false);
64236674
6424 VariableTableEntry *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);6675 VariableTableEntry *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
6425 IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node);6676 IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node);
...@@ -6452,7 +6703,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -6452,7 +6703,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
6452 ir_set_cursor_at_end_and_append_block(irb, alloc_err_block);6703 ir_set_cursor_at_end_and_append_block(irb, alloc_err_block);
6453 // we can return undefined here, because the caller passes a pointer to the error struct field6704 // we can return undefined here, because the caller passes a pointer to the error struct field
6454 // in the error union result, and we populate it in case of allocation failure.6705 // in the error union result, and we populate it in case of allocation failure.
6455 IrInstruction *undef = ir_build_const_undefined(irb, coro_scope, node);
6456 ir_build_return(irb, coro_scope, node, undef);6706 ir_build_return(irb, coro_scope, node, undef);
64576707
6458 ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block);6708 ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block);
...@@ -6460,13 +6710,35 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -6460,13 +6710,35 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
6460 irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr);6710 irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr);
64616711
6462 Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME);6712 Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME);
6463 irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, coro_scope, node, coro_promise_ptr,6713 irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
6464 awaiter_handle_field_name);6714 awaiter_handle_field_name);
6715 ir_build_store_ptr(irb, scope, node, irb->exec->coro_awaiter_field_ptr, null_value);
6465 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);6716 Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
6466 irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, coro_scope, node, coro_promise_ptr, result_field_name);6717 irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
6467 result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);6718 result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
6468 irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, coro_scope, node, coro_promise_ptr, result_ptr_field_name);6719 irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name);
6469 ir_build_store_ptr(irb, coro_scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr);6720 ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr);
6721 if (irb->codegen->have_err_ret_tracing) {
6722 // initialize the error return trace
6723 Buf *return_addresses_field_name = buf_create_from_str(RETURN_ADDRESSES_FIELD_NAME);
6724 IrInstruction *return_addresses_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, return_addresses_field_name);
6725
6726 Buf *err_ret_trace_field_name = buf_create_from_str(ERR_RET_TRACE_FIELD_NAME);
6727 err_ret_trace_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_field_name);
6728 ir_build_mark_err_ret_trace_ptr(irb, scope, node, err_ret_trace_ptr);
6729
6730 // coordinate with builtin.zig
6731 Buf *index_name = buf_create_from_str("index");
6732 IrInstruction *index_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, index_name);
6733 IrInstruction *zero = ir_build_const_usize(irb, scope, node, 0);
6734 ir_build_store_ptr(irb, scope, node, index_ptr, zero);
6735
6736 Buf *instruction_addresses_name = buf_create_from_str("instruction_addresses");
6737 IrInstruction *addrs_slice_ptr = ir_build_field_ptr(irb, scope, node, err_ret_trace_ptr, instruction_addresses_name);
6738
6739 IrInstruction *slice_value = ir_build_slice(irb, scope, node, return_addresses_ptr, zero, nullptr, false);
6740 ir_build_store_ptr(irb, scope, node, addrs_slice_ptr, slice_value);
6741 }
64706742
64716743
6472 irb->exec->coro_early_final = ir_create_basic_block(irb, scope, "CoroEarlyFinal");6744 irb->exec->coro_early_final = ir_create_basic_block(irb, scope, "CoroEarlyFinal");
...@@ -6517,6 +6789,12 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -6517,6 +6789,12 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
6517 IrInstruction *size_of_ret_val = ir_build_size_of(irb, scope, node, return_type_inst);6789 IrInstruction *size_of_ret_val = ir_build_size_of(irb, scope, node, return_type_inst);
6518 ir_build_memcpy(irb, scope, node, result_ptr_as_u8_ptr, return_value_ptr_as_u8_ptr, size_of_ret_val);6790 ir_build_memcpy(irb, scope, node, result_ptr_as_u8_ptr, return_value_ptr_as_u8_ptr, size_of_ret_val);
6519 }6791 }
6792 if (irb->codegen->have_err_ret_tracing) {
6793 Buf *err_ret_trace_ptr_field_name = buf_create_from_str(ERR_RET_TRACE_PTR_FIELD_NAME);
6794 IrInstruction *err_ret_trace_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr_field_name);
6795 IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr);
6796 ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr);
6797 }
6520 ir_build_br(irb, scope, node, check_free_block, const_bool_false);6798 ir_build_br(irb, scope, node, check_free_block, const_bool_false);
65216799
6522 ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block);6800 ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block);
...@@ -7859,7 +8137,7 @@ static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *...@@ -7859,7 +8137,7 @@ static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *
7859 *errors = reallocate(*errors, old_errors_count, *errors_count);8137 *errors = reallocate(*errors, old_errors_count, *errors_count);
7860}8138}
78618139
7862static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) {8140static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, TypeTableEntry *expected_type, IrInstruction **instructions, size_t instruction_count) {
7863 assert(instruction_count >= 1);8141 assert(instruction_count >= 1);
7864 IrInstruction *prev_inst = instructions[0];8142 IrInstruction *prev_inst = instructions[0];
7865 if (type_is_invalid(prev_inst->value.type)) {8143 if (type_is_invalid(prev_inst->value.type)) {
...@@ -7906,16 +8184,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -7906,16 +8184,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
7906 continue;8184 continue;
7907 }8185 }
79088186
7909 if (prev_type->id == TypeTableEntryIdNullLit) {
7910 prev_inst = cur_inst;
7911 continue;
7912 }
7913
7914 if (cur_type->id == TypeTableEntryIdNullLit) {
7915 any_are_null = true;
7916 continue;
7917 }
7918
7919 if (prev_type->id == TypeTableEntryIdErrorSet) {8187 if (prev_type->id == TypeTableEntryIdErrorSet) {
7920 assert(err_set_type != nullptr);8188 assert(err_set_type != nullptr);
7921 if (cur_type->id == TypeTableEntryIdErrorSet) {8189 if (cur_type->id == TypeTableEntryIdErrorSet) {
...@@ -8175,6 +8443,16 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8175,6 +8443,16 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8175 }8443 }
8176 }8444 }
81778445
8446 if (prev_type->id == TypeTableEntryIdNullLit) {
8447 prev_inst = cur_inst;
8448 continue;
8449 }
8450
8451 if (cur_type->id == TypeTableEntryIdNullLit) {
8452 any_are_null = true;
8453 continue;
8454 }
8455
8178 if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) {8456 if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) {
8179 continue;8457 continue;
8180 }8458 }
...@@ -8358,6 +8636,10 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8358,6 +8636,10 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8358 } else if (err_set_type != nullptr) {8636 } else if (err_set_type != nullptr) {
8359 if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) {8637 if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) {
8360 return err_set_type;8638 return err_set_type;
8639 } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) {
8640 return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type);
8641 } else if (expected_type != nullptr && expected_type->id == TypeTableEntryIdErrorUnion) {
8642 return get_error_union_type(ira->codegen, err_set_type, expected_type->data.error_union.payload_type);
8361 } else {8643 } else {
8362 if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||8644 if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt ||
8363 prev_inst->value.type->id == TypeTableEntryIdNumLitFloat)8645 prev_inst->value.type->id == TypeTableEntryIdNumLitFloat)
...@@ -8369,8 +8651,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod...@@ -8369,8 +8651,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
8369 ir_add_error_node(ira, source_node,8651 ir_add_error_node(ira, source_node,
8370 buf_sprintf("unable to make error union out of null literal"));8652 buf_sprintf("unable to make error union out of null literal"));
8371 return ira->codegen->builtin_types.entry_invalid;8653 return ira->codegen->builtin_types.entry_invalid;
8372 } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) {
8373 return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type->data.error_union.payload_type);
8374 } else {8654 } else {
8375 return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type);8655 return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type);
8376 }8656 }
...@@ -10393,7 +10673,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp...@@ -10393,7 +10673,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
10393 }10673 }
1039410674
10395 IrInstruction *instructions[] = {op1, op2};10675 IrInstruction *instructions[] = {op1, op2};
10396 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, instructions, 2);10676 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2);
10397 if (type_is_invalid(resolved_type))10677 if (type_is_invalid(resolved_type))
10398 return resolved_type;10678 return resolved_type;
10399 type_ensure_zero_bits_known(ira->codegen, resolved_type);10679 type_ensure_zero_bits_known(ira->codegen, resolved_type);
...@@ -10783,7 +11063,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp...@@ -10783,7 +11063,7 @@ static TypeTableEntry *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstructionBinOp
10783 IrInstruction *op1 = bin_op_instruction->op1->other;11063 IrInstruction *op1 = bin_op_instruction->op1->other;
10784 IrInstruction *op2 = bin_op_instruction->op2->other;11064 IrInstruction *op2 = bin_op_instruction->op2->other;
10785 IrInstruction *instructions[] = {op1, op2};11065 IrInstruction *instructions[] = {op1, op2};
10786 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, instructions, 2);11066 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, nullptr, instructions, 2);
10787 if (type_is_invalid(resolved_type))11067 if (type_is_invalid(resolved_type))
10788 return resolved_type;11068 return resolved_type;
10789 IrBinOp op_id = bin_op_instruction->op_id;11069 IrBinOp op_id = bin_op_instruction->op_id;
...@@ -11353,7 +11633,19 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc...@@ -11353,7 +11633,19 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
11353 }11633 }
11354 break;11634 break;
11355 case VarClassRequiredAny:11635 case VarClassRequiredAny:
11356 // OK11636 if (casted_init_value->value.special == ConstValSpecialStatic &&
11637 casted_init_value->value.type->id == TypeTableEntryIdFn &&
11638 casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways)
11639 {
11640 var_class_requires_const = true;
11641 if (!var->src_is_const && !is_comptime_var) {
11642 ErrorMsg *msg = ir_add_error_node(ira, source_node,
11643 buf_sprintf("functions marked inline must be stored in const or comptime var"));
11644 AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node;
11645 add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here"));
11646 result_type = ira->codegen->builtin_types.entry_invalid;
11647 }
11648 }
11357 break;11649 break;
11358 }11650 }
11359 }11651 }
...@@ -11579,18 +11871,25 @@ static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) {...@@ -11579,18 +11871,25 @@ static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) {
11579static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,11871static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
11580 IrInstructionErrorReturnTrace *instruction)11872 IrInstructionErrorReturnTrace *instruction)
11581{11873{
11582 TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);11874 if (instruction->nullable == IrInstructionErrorReturnTrace::Null) {
11583 TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);11875 TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);
11584 if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) {11876 TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);
11585 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);11877 if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) {
11586 out_val->data.x_maybe = nullptr;11878 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
11879 out_val->data.x_maybe = nullptr;
11880 return nullable_type;
11881 }
11882 IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
11883 instruction->base.source_node, instruction->nullable);
11884 ir_link_new_instruction(new_instruction, &instruction->base);
11587 return nullable_type;11885 return nullable_type;
11886 } else {
11887 assert(ira->codegen->have_err_ret_tracing);
11888 IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
11889 instruction->base.source_node, instruction->nullable);
11890 ir_link_new_instruction(new_instruction, &instruction->base);
11891 return get_ptr_to_stack_trace_type(ira->codegen);
11588 }11892 }
11589
11590 IrInstruction *new_instruction = ir_build_error_return_trace(&ira->new_irb, instruction->base.scope,
11591 instruction->base.source_node);
11592 ir_link_new_instruction(new_instruction, &instruction->base);
11593 return nullable_type;
11594}11893}
1159511894
11596static TypeTableEntry *ir_analyze_instruction_error_union(IrAnalyze *ira,11895static TypeTableEntry *ir_analyze_instruction_error_union(IrAnalyze *ira,
...@@ -11755,7 +12054,8 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod...@@ -11755,7 +12054,8 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
11755 }12054 }
11756 }12055 }
1175712056
11758 bool comptime_arg = param_decl_node->data.param_decl.is_inline;12057 bool comptime_arg = param_decl_node->data.param_decl.is_inline ||
12058 casted_arg->value.type->id == TypeTableEntryIdNumLitInt || casted_arg->value.type->id == TypeTableEntryIdNumLitFloat;
1175912059
11760 ConstExprValue *arg_val;12060 ConstExprValue *arg_val;
1176112061
...@@ -11780,6 +12080,12 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod...@@ -11780,6 +12080,12 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod
11780 var->shadowable = !comptime_arg;12080 var->shadowable = !comptime_arg;
1178112081
11782 *next_proto_i += 1;12082 *next_proto_i += 1;
12083 } else if (casted_arg->value.type->id == TypeTableEntryIdNumLitInt ||
12084 casted_arg->value.type->id == TypeTableEntryIdNumLitFloat)
12085 {
12086 ir_add_error(ira, casted_arg,
12087 buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/zig-lang/zig/issues/557"));
12088 return false;
11783 }12089 }
1178412090
11785 if (!comptime_arg) {12091 if (!comptime_arg) {
...@@ -12726,7 +13032,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP...@@ -12726,7 +13032,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP
12726 return first_value->value.type;13032 return first_value->value.type;
12727 }13033 }
1272813034
12729 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node,13035 TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.source_node, nullptr,
12730 new_incoming_values.items, new_incoming_values.length);13036 new_incoming_values.items, new_incoming_values.length);
12731 if (type_is_invalid(resolved_type))13037 if (type_is_invalid(resolved_type))
12732 return resolved_type;13038 return resolved_type;
...@@ -13072,6 +13378,7 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira,...@@ -13072,6 +13378,7 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira,
13072{13378{
13073 if (!is_slice(bare_struct_type)) {13379 if (!is_slice(bare_struct_type)) {
13074 ScopeDecls *container_scope = get_container_scope(bare_struct_type);13380 ScopeDecls *container_scope = get_container_scope(bare_struct_type);
13381 assert(container_scope != nullptr);
13075 auto entry = container_scope->decl_table.maybe_get(field_name);13382 auto entry = container_scope->decl_table.maybe_get(field_name);
13076 Tld *tld = entry ? entry->value : nullptr;13383 Tld *tld = entry ? entry->value : nullptr;
13077 if (tld && tld->id == TldIdFn) {13384 if (tld && tld->id == TldIdFn) {
...@@ -13105,7 +13412,6 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira,...@@ -13105,7 +13412,6 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira,
13105 return ira->codegen->invalid_instruction;13412 return ira->codegen->invalid_instruction;
13106}13413}
1310713414
13108
13109static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,13415static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name,
13110 IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type)13416 IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type)
13111{13417{
...@@ -13167,21 +13473,66 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_...@@ -13167,21 +13473,66 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
13167 } else if (bare_type->id == TypeTableEntryIdUnion) {13473 } else if (bare_type->id == TypeTableEntryIdUnion) {
13168 TypeUnionField *field = find_union_type_field(bare_type, field_name);13474 TypeUnionField *field = find_union_type_field(bare_type, field_name);
13169 if (field) {13475 if (field) {
13170 IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field);13476 if (instr_is_comptime(container_ptr)) {
13171 result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile,13477 ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad);
13172 get_abi_alignment(ira->codegen, field->type_entry), 0, 0);13478 if (!ptr_val)
13173 return result;13479 return ira->codegen->invalid_instruction;
13174 } else {
13175 return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
13176 source_instr, container_ptr, container_type);
13177 }
13178 } else {
13179 zig_unreachable();
13180 }
13181}
1318213480
13183static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) {13481 if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) {
13184 LinkLib *link_lib = add_link_lib(ira->codegen, lib_name);13482 ConstExprValue *union_val = const_ptr_pointee(ira->codegen, ptr_val);
13483 if (type_is_invalid(union_val->type))
13484 return ira->codegen->invalid_instruction;
13485
13486 TypeUnionField *actual_field = find_union_field_by_tag(bare_type, &union_val->data.x_union.tag);
13487 if (actual_field == nullptr)
13488 zig_unreachable();
13489
13490 if (field != actual_field) {
13491 ir_add_error_node(ira, source_instr->source_node,
13492 buf_sprintf("accessing union field '%s' while field '%s' is set", buf_ptr(field_name),
13493 buf_ptr(actual_field->name)));
13494 return ira->codegen->invalid_instruction;
13495 }
13496
13497 ConstExprValue *payload_val = union_val->data.x_union.payload;
13498
13499 TypeTableEntry *field_type = field->type_entry;
13500 if (field_type->id == TypeTableEntryIdVoid)
13501 {
13502 assert(payload_val == nullptr);
13503 payload_val = create_const_vals(1);
13504 payload_val->special = ConstValSpecialStatic;
13505 payload_val->type = field_type;
13506 }
13507
13508 TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, is_const, is_volatile,
13509 get_abi_alignment(ira->codegen, field_type), 0, 0);
13510
13511 IrInstruction *result = ir_get_const(ira, source_instr);
13512 ConstExprValue *const_val = &result->value;
13513 const_val->data.x_ptr.special = ConstPtrSpecialRef;
13514 const_val->data.x_ptr.mut = container_ptr->value.data.x_ptr.mut;
13515 const_val->data.x_ptr.data.ref.pointee = payload_val;
13516 const_val->type = ptr_type;
13517 return result;
13518 }
13519 }
13520
13521 IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field);
13522 result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile,
13523 get_abi_alignment(ira->codegen, field->type_entry), 0, 0);
13524 return result;
13525 } else {
13526 return ir_analyze_container_member_access_inner(ira, bare_type, field_name,
13527 source_instr, container_ptr, container_type);
13528 }
13529 } else {
13530 zig_unreachable();
13531 }
13532}
13533
13534static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) {
13535 LinkLib *link_lib = add_link_lib(ira->codegen, lib_name);
13185 for (size_t i = 0; i < link_lib->symbols.length; i += 1) {13536 for (size_t i = 0; i < link_lib->symbols.length; i += 1) {
13186 Buf *existing_symbol_name = link_lib->symbols.at(i);13537 Buf *existing_symbol_name = link_lib->symbols.at(i);
13187 if (buf_eql_buf(existing_symbol_name, symbol_name)) {13538 if (buf_eql_buf(existing_symbol_name, symbol_name)) {
...@@ -13275,7 +13626,15 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -13275,7 +13626,15 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
13275 zig_unreachable();13626 zig_unreachable();
13276 }13627 }
1327713628
13278 Buf *field_name = field_ptr_instruction->field_name;13629 Buf *field_name = field_ptr_instruction->field_name_buffer;
13630 if (!field_name) {
13631 IrInstruction *field_name_expr = field_ptr_instruction->field_name_expr->other;
13632 field_name = ir_resolve_str(ira, field_name_expr);
13633 if (!field_name)
13634 return ira->codegen->builtin_types.entry_invalid;
13635 }
13636
13637
13279 AstNode *source_node = field_ptr_instruction->base.source_node;13638 AstNode *source_node = field_ptr_instruction->base.source_node;
1328013639
13281 if (type_is_invalid(container_type)) {13640 if (type_is_invalid(container_type)) {
...@@ -13377,7 +13736,16 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -13377,7 +13736,16 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
13377 create_const_enum(child_type, &field->value), child_type,13736 create_const_enum(child_type, &field->value), child_type,
13378 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);13737 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
13379 }13738 }
13380 } else if (child_type->id == TypeTableEntryIdUnion &&13739 }
13740 ScopeDecls *container_scope = get_container_scope(child_type);
13741 if (container_scope != nullptr) {
13742 auto entry = container_scope->decl_table.maybe_get(field_name);
13743 Tld *tld = entry ? entry->value : nullptr;
13744 if (tld) {
13745 return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld);
13746 }
13747 }
13748 if (child_type->id == TypeTableEntryIdUnion &&
13381 (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr ||13749 (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr ||
13382 child_type->data.unionation.decl_node->data.container_decl.auto_enum))13750 child_type->data.unionation.decl_node->data.container_decl.auto_enum))
13383 {13751 {
...@@ -13394,14 +13762,6 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -13394,14 +13762,6 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
13394 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);13762 ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile);
13395 }13763 }
13396 }13764 }
13397 ScopeDecls *container_scope = get_container_scope(child_type);
13398 if (container_scope != nullptr) {
13399 auto entry = container_scope->decl_table.maybe_get(field_name);
13400 Tld *tld = entry ? entry->value : nullptr;
13401 if (tld) {
13402 return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld);
13403 }
13404 }
13405 ir_add_error(ira, &field_ptr_instruction->base,13765 ir_add_error(ira, &field_ptr_instruction->base,
13406 buf_sprintf("container '%s' has no member called '%s'",13766 buf_sprintf("container '%s' has no member called '%s'",
13407 buf_ptr(&child_type->name), buf_ptr(field_name)));13767 buf_ptr(&child_type->name), buf_ptr(field_name)));
...@@ -13570,6 +13930,15 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -13570,6 +13930,15 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
13570 }13930 }
13571 } else if (child_type->id == TypeTableEntryIdFn) {13931 } else if (child_type->id == TypeTableEntryIdFn) {
13572 if (buf_eql_str(field_name, "ReturnType")) {13932 if (buf_eql_str(field_name, "ReturnType")) {
13933 if (child_type->data.fn.fn_type_id.return_type == nullptr) {
13934 // Return type can only ever be null, if the function is generic
13935 assert(child_type->data.fn.is_generic);
13936
13937 ir_add_error(ira, &field_ptr_instruction->base,
13938 buf_sprintf("ReturnType has not been resolved because '%s' is generic", buf_ptr(&child_type->name)));
13939 return ira->codegen->builtin_types.entry_invalid;
13940 }
13941
13573 bool ptr_is_const = true;13942 bool ptr_is_const = true;
13574 bool ptr_is_volatile = false;13943 bool ptr_is_volatile = false;
13575 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,13944 return ir_analyze_const_ptr(ira, &field_ptr_instruction->base,
...@@ -14340,7 +14709,7 @@ static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source...@@ -14340,7 +14709,7 @@ static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source
14340 }14709 }
1434114710
14342 if (value->value.type->id != TypeTableEntryIdUnion) {14711 if (value->value.type->id != TypeTableEntryIdUnion) {
14343 ir_add_error(ira, source_instr,14712 ir_add_error(ira, value,
14344 buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value.type->name)));14713 buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value.type->name)));
14345 return ira->codegen->invalid_instruction;14714 return ira->codegen->invalid_instruction;
14346 }14715 }
...@@ -14492,7 +14861,10 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -14492,7 +14861,10 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
14492 return out_val->type;14861 return out_val->type;
14493 }14862 }
1449414863
14495 assert(target_value_ptr->value.type->id == TypeTableEntryIdPointer);14864 if (target_value_ptr->value.type->id != TypeTableEntryIdPointer) {
14865 ir_add_error(ira, target_value_ptr, buf_sprintf("invalid deref on switch target"));
14866 return ira->codegen->builtin_types.entry_invalid;
14867 }
1449614868
14497 TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type;14869 TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type;
14498 ConstExprValue *pointee_val = nullptr;14870 ConstExprValue *pointee_val = nullptr;
...@@ -15268,6 +15640,8 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,...@@ -15268,6 +15640,8 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,
15268 }15640 }
1526915641
15270 ensure_complete_type(ira->codegen, container_type);15642 ensure_complete_type(ira->codegen, container_type);
15643 if (type_is_invalid(container_type))
15644 return ira->codegen->builtin_types.entry_invalid;
1527115645
15272 TypeStructField *field = find_struct_type_field(container_type, field_name);15646 TypeStructField *field = find_struct_type_field(container_type, field_name);
15273 if (field == nullptr) {15647 if (field == nullptr) {
...@@ -15374,6 +15748,910 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira,...@@ -15374,6 +15748,910 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira,
15374 return ira->codegen->builtin_types.entry_num_lit_int;15748 return ira->codegen->builtin_types.entry_num_lit_int;
15375}15749}
1537615750
15751static void ensure_field_index(TypeTableEntry *type, const char *field_name, size_t index)
15752{
15753 Buf *field_name_buf;
15754
15755 assert(type != nullptr && !type_is_invalid(type));
15756 // Check for our field by creating a buffer in place then using the comma operator to free it so that we don't
15757 // leak memory in debug mode.
15758 assert(find_struct_type_field(type, field_name_buf = buf_create_from_str(field_name))->src_index == index &&
15759 (buf_deinit(field_name_buf), true));
15760}
15761
15762static TypeTableEntry *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, TypeTableEntry *root = nullptr)
15763{
15764 static ConstExprValue *type_info_var = nullptr;
15765 static TypeTableEntry *type_info_type = nullptr;
15766 if (type_info_var == nullptr)
15767 {
15768 type_info_var = get_builtin_value(ira->codegen, "TypeInfo");
15769 assert(type_info_var->type->id == TypeTableEntryIdMetaType);
15770
15771 ensure_complete_type(ira->codegen, type_info_var->data.x_type);
15772 type_info_type = type_info_var->data.x_type;
15773 assert(type_info_type->id == TypeTableEntryIdUnion);
15774 }
15775
15776 if (type_name == nullptr && root == nullptr)
15777 return type_info_type;
15778 else if (type_name == nullptr)
15779 return root;
15780
15781 TypeTableEntry *root_type = (root == nullptr) ? type_info_type : root;
15782
15783 ScopeDecls *type_info_scope = get_container_scope(root_type);
15784 assert(type_info_scope != nullptr);
15785
15786 Buf field_name = BUF_INIT;
15787 buf_init_from_str(&field_name, type_name);
15788 auto entry = type_info_scope->decl_table.get(&field_name);
15789 buf_deinit(&field_name);
15790
15791 TldVar *tld = (TldVar *)entry;
15792 assert(tld->base.id == TldIdVar);
15793
15794 VariableTableEntry *var = tld->var;
15795
15796 ensure_complete_type(ira->codegen, var->value->type);
15797 assert(var->value->type->id == TypeTableEntryIdMetaType);
15798 return var->value->data.x_type;
15799}
15800
15801static void ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope)
15802{
15803 TypeTableEntry *type_info_definition_type = ir_type_info_get_type(ira, "Definition");
15804 ensure_complete_type(ira->codegen, type_info_definition_type);
15805 ensure_field_index(type_info_definition_type, "name", 0);
15806 ensure_field_index(type_info_definition_type, "is_pub", 1);
15807 ensure_field_index(type_info_definition_type, "data", 2);
15808
15809 TypeTableEntry *type_info_definition_data_type = ir_type_info_get_type(ira, "Data", type_info_definition_type);
15810 ensure_complete_type(ira->codegen, type_info_definition_data_type);
15811
15812 TypeTableEntry *type_info_fn_def_type = ir_type_info_get_type(ira, "FnDef", type_info_definition_data_type);
15813 ensure_complete_type(ira->codegen, type_info_fn_def_type);
15814
15815 TypeTableEntry *type_info_fn_def_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_def_type);
15816 ensure_complete_type(ira->codegen, type_info_fn_def_inline_type);
15817
15818 // Loop through our definitions once to figure out how many definitions we will generate info for.
15819 auto decl_it = decls_scope->decl_table.entry_iterator();
15820 decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr;
15821 int definition_count = 0;
15822
15823 while ((curr_entry = decl_it.next()) != nullptr)
15824 {
15825 // If the definition is unresolved, force it to be resolved again.
15826 if (curr_entry->value->resolution == TldResolutionUnresolved)
15827 {
15828 resolve_top_level_decl(ira->codegen, curr_entry->value, false, curr_entry->value->source_node);
15829 if (curr_entry->value->resolution != TldResolutionOk)
15830 {
15831 return;
15832 }
15833 }
15834
15835 // Skip comptime blocks and test functions.
15836 if (curr_entry->value->id != TldIdCompTime)
15837 {
15838 if (curr_entry->value->id == TldIdFn)
15839 {
15840 FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry;
15841 if (fn_entry->is_test)
15842 continue;
15843 }
15844
15845 definition_count += 1;
15846 }
15847 }
15848
15849 ConstExprValue *definition_array = create_const_vals(1);
15850 definition_array->special = ConstValSpecialStatic;
15851 definition_array->type = get_array_type(ira->codegen, type_info_definition_type, definition_count);
15852 definition_array->data.x_array.special = ConstArraySpecialNone;
15853 definition_array->data.x_array.s_none.parent.id = ConstParentIdNone;
15854 definition_array->data.x_array.s_none.elements = create_const_vals(definition_count);
15855 init_const_slice(ira->codegen, out_val, definition_array, 0, definition_count, false);
15856
15857 // Loop through the definitions and generate info.
15858 decl_it = decls_scope->decl_table.entry_iterator();
15859 curr_entry = nullptr;
15860 int definition_index = 0;
15861 while ((curr_entry = decl_it.next()) != nullptr)
15862 {
15863 // Skip comptime blocks and test functions.
15864 if (curr_entry->value->id == TldIdCompTime)
15865 continue;
15866 else if (curr_entry->value->id == TldIdFn)
15867 {
15868 FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry;
15869 if (fn_entry->is_test)
15870 continue;
15871 }
15872
15873 ConstExprValue *definition_val = &definition_array->data.x_array.s_none.elements[definition_index];
15874
15875 definition_val->special = ConstValSpecialStatic;
15876 definition_val->type = type_info_definition_type;
15877
15878 ConstExprValue *inner_fields = create_const_vals(3);
15879 ConstExprValue *name = create_const_str_lit(ira->codegen, curr_entry->key);
15880 init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(curr_entry->key), true);
15881 inner_fields[1].special = ConstValSpecialStatic;
15882 inner_fields[1].type = ira->codegen->builtin_types.entry_bool;
15883 inner_fields[1].data.x_bool = curr_entry->value->visib_mod == VisibModPub;
15884 inner_fields[2].special = ConstValSpecialStatic;
15885 inner_fields[2].type = type_info_definition_data_type;
15886 inner_fields[2].data.x_union.parent.id = ConstParentIdStruct;
15887 inner_fields[2].data.x_union.parent.data.p_struct.struct_val = definition_val;
15888 inner_fields[2].data.x_union.parent.data.p_struct.field_index = 1;
15889
15890 switch (curr_entry->value->id)
15891 {
15892 case TldIdVar:
15893 {
15894 VariableTableEntry *var = ((TldVar *)curr_entry->value)->var;
15895 ensure_complete_type(ira->codegen, var->value->type);
15896 if (var->value->type->id == TypeTableEntryIdMetaType)
15897 {
15898 // We have a variable of type 'type', so it's actually a type definition.
15899 // 0: Data.Type: type
15900 bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0);
15901 inner_fields[2].data.x_union.payload = var->value;
15902 }
15903 else
15904 {
15905 // We have a variable of another type, so we store the type of the variable.
15906 // 1: Data.Var: type
15907 bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 1);
15908
15909 ConstExprValue *payload = create_const_vals(1);
15910 payload->type = ira->codegen->builtin_types.entry_type;
15911 payload->data.x_type = var->value->type;
15912
15913 inner_fields[2].data.x_union.payload = payload;
15914 }
15915
15916 break;
15917 }
15918 case TldIdFn:
15919 {
15920 // 2: Data.Fn: Data.FnDef
15921 bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 2);
15922
15923 FnTableEntry *fn_entry = ((TldFn *)curr_entry->value)->fn_entry;
15924 assert(!fn_entry->is_test);
15925
15926 analyze_fn_body(ira->codegen, fn_entry);
15927 if (fn_entry->anal_state == FnAnalStateInvalid)
15928 return;
15929
15930 AstNodeFnProto *fn_node = (AstNodeFnProto *)(fn_entry->proto_node);
15931
15932 ConstExprValue *fn_def_val = create_const_vals(1);
15933 fn_def_val->special = ConstValSpecialStatic;
15934 fn_def_val->type = type_info_fn_def_type;
15935 fn_def_val->data.x_struct.parent.id = ConstParentIdUnion;
15936 fn_def_val->data.x_struct.parent.data.p_union.union_val = &inner_fields[2];
15937
15938 ConstExprValue *fn_def_fields = create_const_vals(9);
15939 fn_def_val->data.x_struct.fields = fn_def_fields;
15940
15941 // fn_type: type
15942 ensure_field_index(fn_def_val->type, "fn_type", 0);
15943 fn_def_fields[0].special = ConstValSpecialStatic;
15944 fn_def_fields[0].type = ira->codegen->builtin_types.entry_type;
15945 fn_def_fields[0].data.x_type = fn_entry->type_entry;
15946 // inline_type: Data.FnDef.Inline
15947 ensure_field_index(fn_def_val->type, "inline_type", 1);
15948 fn_def_fields[1].special = ConstValSpecialStatic;
15949 fn_def_fields[1].type = type_info_fn_def_inline_type;
15950 bigint_init_unsigned(&fn_def_fields[1].data.x_enum_tag, fn_entry->fn_inline);
15951 // calling_convention: TypeInfo.CallingConvention
15952 ensure_field_index(fn_def_val->type, "calling_convention", 2);
15953 fn_def_fields[2].special = ConstValSpecialStatic;
15954 fn_def_fields[2].type = ir_type_info_get_type(ira, "CallingConvention");
15955 bigint_init_unsigned(&fn_def_fields[2].data.x_enum_tag, fn_node->cc);
15956 // is_var_args: bool
15957 ensure_field_index(fn_def_val->type, "is_var_args", 3);
15958 bool is_varargs = fn_node->is_var_args;
15959 fn_def_fields[3].special = ConstValSpecialStatic;
15960 fn_def_fields[3].type = ira->codegen->builtin_types.entry_bool;
15961 fn_def_fields[3].data.x_bool = is_varargs;
15962 // is_extern: bool
15963 ensure_field_index(fn_def_val->type, "is_extern", 4);
15964 fn_def_fields[4].special = ConstValSpecialStatic;
15965 fn_def_fields[4].type = ira->codegen->builtin_types.entry_bool;
15966 fn_def_fields[4].data.x_bool = fn_node->is_extern;
15967 // is_export: bool
15968 ensure_field_index(fn_def_val->type, "is_export", 5);
15969 fn_def_fields[5].special = ConstValSpecialStatic;
15970 fn_def_fields[5].type = ira->codegen->builtin_types.entry_bool;
15971 fn_def_fields[5].data.x_bool = fn_node->is_export;
15972 // lib_name: ?[]const u8
15973 ensure_field_index(fn_def_val->type, "lib_name", 6);
15974 fn_def_fields[6].special = ConstValSpecialStatic;
15975 fn_def_fields[6].type = get_maybe_type(ira->codegen,
15976 get_slice_type(ira->codegen, get_pointer_to_type(ira->codegen,
15977 ira->codegen->builtin_types.entry_u8, true)));
15978 if (fn_node->is_extern && buf_len(fn_node->lib_name) > 0)
15979 {
15980 fn_def_fields[6].data.x_maybe = create_const_vals(1);
15981 ConstExprValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name);
15982 init_const_slice(ira->codegen, fn_def_fields[6].data.x_maybe, lib_name, 0, buf_len(fn_node->lib_name), true);
15983 }
15984 else
15985 fn_def_fields[6].data.x_maybe = nullptr;
15986 // return_type: type
15987 ensure_field_index(fn_def_val->type, "return_type", 7);
15988 fn_def_fields[7].special = ConstValSpecialStatic;
15989 fn_def_fields[7].type = ira->codegen->builtin_types.entry_type;
15990 if (fn_entry->src_implicit_return_type != nullptr)
15991 fn_def_fields[7].data.x_type = fn_entry->src_implicit_return_type;
15992 else if (fn_entry->type_entry->data.fn.gen_return_type != nullptr)
15993 fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.gen_return_type;
15994 else
15995 fn_def_fields[7].data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type;
15996 // arg_names: [][] const u8
15997 ensure_field_index(fn_def_val->type, "arg_names", 8);
15998 size_t fn_arg_count = fn_entry->variable_list.length;
15999 ConstExprValue *fn_arg_name_array = create_const_vals(1);
16000 fn_arg_name_array->special = ConstValSpecialStatic;
16001 fn_arg_name_array->type = get_array_type(ira->codegen, get_slice_type(ira->codegen,
16002 get_pointer_to_type(ira->codegen, ira->codegen->builtin_types.entry_u8, true)), fn_arg_count);
16003 fn_arg_name_array->data.x_array.special = ConstArraySpecialNone;
16004 fn_arg_name_array->data.x_array.s_none.parent.id = ConstParentIdNone;
16005 fn_arg_name_array->data.x_array.s_none.elements = create_const_vals(fn_arg_count);
16006
16007 init_const_slice(ira->codegen, &fn_def_fields[8], fn_arg_name_array, 0, fn_arg_count, false);
16008
16009 for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++)
16010 {
16011 VariableTableEntry *arg_var = fn_entry->variable_list.at(fn_arg_index);
16012 ConstExprValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.s_none.elements[fn_arg_index];
16013 ConstExprValue *arg_name = create_const_str_lit(ira->codegen, &arg_var->name);
16014 init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, buf_len(&arg_var->name), true);
16015 fn_arg_name_val->data.x_struct.parent.id = ConstParentIdArray;
16016 fn_arg_name_val->data.x_struct.parent.data.p_array.array_val = fn_arg_name_array;
16017 fn_arg_name_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index;
16018 }
16019
16020 inner_fields[2].data.x_union.payload = fn_def_val;
16021 break;
16022 }
16023 case TldIdContainer:
16024 {
16025 TypeTableEntry *type_entry = ((TldContainer *)curr_entry->value)->type_entry;
16026 ensure_complete_type(ira->codegen, type_entry);
16027 // This is a type.
16028 bigint_init_unsigned(&inner_fields[2].data.x_union.tag, 0);
16029
16030 ConstExprValue *payload = create_const_vals(1);
16031 payload->type = ira->codegen->builtin_types.entry_type;
16032 payload->data.x_type = type_entry;
16033
16034 inner_fields[2].data.x_union.payload = payload;
16035
16036 break;
16037 }
16038 default:
16039 zig_unreachable();
16040 }
16041
16042 definition_val->data.x_struct.fields = inner_fields;
16043 definition_index++;
16044 }
16045
16046 assert(definition_index == definition_count);
16047}
16048
16049static ConstExprValue *ir_make_type_info_value(IrAnalyze *ira, TypeTableEntry *type_entry)
16050{
16051 assert(type_entry != nullptr);
16052 assert(!type_is_invalid(type_entry));
16053
16054 ensure_complete_type(ira->codegen, type_entry);
16055
16056 const auto make_enum_field_val = [ira](ConstExprValue *enum_field_val, TypeEnumField *enum_field,
16057 TypeTableEntry *type_info_enum_field_type) {
16058 enum_field_val->special = ConstValSpecialStatic;
16059 enum_field_val->type = type_info_enum_field_type;
16060
16061 ConstExprValue *inner_fields = create_const_vals(2);
16062 inner_fields[1].special = ConstValSpecialStatic;
16063 inner_fields[1].type = ira->codegen->builtin_types.entry_usize;
16064
16065 ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name);
16066 init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true);
16067
16068 bigint_init_bigint(&inner_fields[1].data.x_bigint, &enum_field->value);
16069
16070 enum_field_val->data.x_struct.fields = inner_fields;
16071 };
16072
16073 const auto create_ptr_like_type_info = [ira](const char *name, TypeTableEntry *ptr_type_entry) {
16074 ConstExprValue *result = create_const_vals(1);
16075 result->special = ConstValSpecialStatic;
16076 result->type = ir_type_info_get_type(ira, name);
16077
16078 ConstExprValue *fields = create_const_vals(4);
16079 result->data.x_struct.fields = fields;
16080
16081 // is_const: bool
16082 ensure_field_index(result->type, "is_const", 0);
16083 fields[0].special = ConstValSpecialStatic;
16084 fields[0].type = ira->codegen->builtin_types.entry_bool;
16085 fields[0].data.x_bool = ptr_type_entry->data.pointer.is_const;
16086 // is_volatile: bool
16087 ensure_field_index(result->type, "is_volatile", 1);
16088 fields[1].special = ConstValSpecialStatic;
16089 fields[1].type = ira->codegen->builtin_types.entry_bool;
16090 fields[1].data.x_bool = ptr_type_entry->data.pointer.is_volatile;
16091 // alignment: u32
16092 ensure_field_index(result->type, "alignment", 2);
16093 fields[2].special = ConstValSpecialStatic;
16094 fields[2].type = ira->codegen->builtin_types.entry_u32;
16095 bigint_init_unsigned(&fields[2].data.x_bigint, ptr_type_entry->data.pointer.alignment);
16096 // child: type
16097 ensure_field_index(result->type, "child", 3);
16098 fields[3].special = ConstValSpecialStatic;
16099 fields[3].type = ira->codegen->builtin_types.entry_type;
16100 fields[3].data.x_type = ptr_type_entry->data.pointer.child_type;
16101
16102 return result;
16103 };
16104
16105 ConstExprValue *result = nullptr;
16106 switch (type_entry->id)
16107 {
16108 case TypeTableEntryIdInvalid:
16109 zig_unreachable();
16110 case TypeTableEntryIdMetaType:
16111 case TypeTableEntryIdVoid:
16112 case TypeTableEntryIdBool:
16113 case TypeTableEntryIdUnreachable:
16114 case TypeTableEntryIdNumLitFloat:
16115 case TypeTableEntryIdNumLitInt:
16116 case TypeTableEntryIdUndefLit:
16117 case TypeTableEntryIdNullLit:
16118 case TypeTableEntryIdNamespace:
16119 case TypeTableEntryIdBlock:
16120 case TypeTableEntryIdArgTuple:
16121 case TypeTableEntryIdOpaque:
16122 return nullptr;
16123 default:
16124 {
16125 // Lookup an available value in our cache.
16126 auto entry = ira->codegen->type_info_cache.maybe_get(type_entry);
16127 if (entry != nullptr)
16128 return entry->value;
16129
16130 // Fallthrough if we don't find one.
16131 }
16132 case TypeTableEntryIdInt:
16133 {
16134 result = create_const_vals(1);
16135 result->special = ConstValSpecialStatic;
16136 result->type = ir_type_info_get_type(ira, "Int");
16137
16138 ConstExprValue *fields = create_const_vals(2);
16139 result->data.x_struct.fields = fields;
16140
16141 // is_signed: bool
16142 ensure_field_index(result->type, "is_signed", 0);
16143 fields[0].special = ConstValSpecialStatic;
16144 fields[0].type = ira->codegen->builtin_types.entry_bool;
16145 fields[0].data.x_bool = type_entry->data.integral.is_signed;
16146 // bits: u8
16147 ensure_field_index(result->type, "bits", 1);
16148 fields[1].special = ConstValSpecialStatic;
16149 fields[1].type = ira->codegen->builtin_types.entry_u8;
16150 bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count);
16151
16152 break;
16153 }
16154 case TypeTableEntryIdFloat:
16155 {
16156 result = create_const_vals(1);
16157 result->special = ConstValSpecialStatic;
16158 result->type = ir_type_info_get_type(ira, "Float");
16159
16160 ConstExprValue *fields = create_const_vals(1);
16161 result->data.x_struct.fields = fields;
16162
16163 // bits: u8
16164 ensure_field_index(result->type, "bits", 0);
16165 fields[0].special = ConstValSpecialStatic;
16166 fields[0].type = ira->codegen->builtin_types.entry_u8;
16167 bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count);
16168
16169 break;
16170 }
16171 case TypeTableEntryIdPointer:
16172 {
16173 result = create_ptr_like_type_info("Pointer", type_entry);
16174 break;
16175 }
16176 case TypeTableEntryIdArray:
16177 {
16178 result = create_const_vals(1);
16179 result->special = ConstValSpecialStatic;
16180 result->type = ir_type_info_get_type(ira, "Array");
16181
16182 ConstExprValue *fields = create_const_vals(2);
16183 result->data.x_struct.fields = fields;
16184
16185 // len: usize
16186 ensure_field_index(result->type, "len", 0);
16187 fields[0].special = ConstValSpecialStatic;
16188 fields[0].type = ira->codegen->builtin_types.entry_usize;
16189 bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len);
16190 // child: type
16191 ensure_field_index(result->type, "child", 1);
16192 fields[1].special = ConstValSpecialStatic;
16193 fields[1].type = ira->codegen->builtin_types.entry_type;
16194 fields[1].data.x_type = type_entry->data.array.child_type;
16195
16196 break;
16197 }
16198 case TypeTableEntryIdMaybe:
16199 {
16200 result = create_const_vals(1);
16201 result->special = ConstValSpecialStatic;
16202 result->type = ir_type_info_get_type(ira, "Nullable");
16203
16204 ConstExprValue *fields = create_const_vals(1);
16205 result->data.x_struct.fields = fields;
16206
16207 // child: type
16208 ensure_field_index(result->type, "child", 0);
16209 fields[0].special = ConstValSpecialStatic;
16210 fields[0].type = ira->codegen->builtin_types.entry_type;
16211 fields[0].data.x_type = type_entry->data.maybe.child_type;
16212
16213 break;
16214 }
16215 case TypeTableEntryIdPromise:
16216 {
16217 result = create_const_vals(1);
16218 result->special = ConstValSpecialStatic;
16219 result->type = ir_type_info_get_type(ira, "Promise");
16220
16221 ConstExprValue *fields = create_const_vals(1);
16222 result->data.x_struct.fields = fields;
16223
16224 // @TODO ?type instead of using @typeOf(undefined) when we have no type.
16225 // child: type
16226 ensure_field_index(result->type, "child", 0);
16227 fields[0].special = ConstValSpecialStatic;
16228 fields[0].type = ira->codegen->builtin_types.entry_type;
16229
16230 if (type_entry->data.promise.result_type == nullptr)
16231 fields[0].data.x_type = ira->codegen->builtin_types.entry_undef;
16232 else
16233 fields[0].data.x_type = type_entry->data.promise.result_type;
16234
16235 break;
16236 }
16237 case TypeTableEntryIdEnum:
16238 {
16239 result = create_const_vals(1);
16240 result->special = ConstValSpecialStatic;
16241 result->type = ir_type_info_get_type(ira, "Enum");
16242
16243 ConstExprValue *fields = create_const_vals(4);
16244 result->data.x_struct.fields = fields;
16245
16246 // layout: ContainerLayout
16247 ensure_field_index(result->type, "layout", 0);
16248 fields[0].special = ConstValSpecialStatic;
16249 fields[0].type = ir_type_info_get_type(ira, "ContainerLayout");
16250 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.enumeration.layout);
16251 // tag_type: type
16252 ensure_field_index(result->type, "tag_type", 1);
16253 fields[1].special = ConstValSpecialStatic;
16254 fields[1].type = ira->codegen->builtin_types.entry_type;
16255 fields[1].data.x_type = type_entry->data.enumeration.tag_int_type;
16256 // fields: []TypeInfo.EnumField
16257 ensure_field_index(result->type, "fields", 2);
16258
16259 TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField");
16260 uint32_t enum_field_count = type_entry->data.enumeration.src_field_count;
16261
16262 ConstExprValue *enum_field_array = create_const_vals(1);
16263 enum_field_array->special = ConstValSpecialStatic;
16264 enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count);
16265 enum_field_array->data.x_array.special = ConstArraySpecialNone;
16266 enum_field_array->data.x_array.s_none.parent.id = ConstParentIdNone;
16267 enum_field_array->data.x_array.s_none.elements = create_const_vals(enum_field_count);
16268
16269 init_const_slice(ira->codegen, &fields[2], enum_field_array, 0, enum_field_count, false);
16270
16271 for (uint32_t enum_field_index = 0; enum_field_index < enum_field_count; enum_field_index++)
16272 {
16273 TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index];
16274 ConstExprValue *enum_field_val = &enum_field_array->data.x_array.s_none.elements[enum_field_index];
16275 make_enum_field_val(enum_field_val, enum_field, type_info_enum_field_type);
16276 enum_field_val->data.x_struct.parent.id = ConstParentIdArray;
16277 enum_field_val->data.x_struct.parent.data.p_array.array_val = enum_field_array;
16278 enum_field_val->data.x_struct.parent.data.p_array.elem_index = enum_field_index;
16279 }
16280 // defs: []TypeInfo.Definition
16281 ensure_field_index(result->type, "defs", 3);
16282 ir_make_type_info_defs(ira, &fields[3], type_entry->data.enumeration.decls_scope);
16283
16284 break;
16285 }
16286 case TypeTableEntryIdErrorSet:
16287 {
16288 result = create_const_vals(1);
16289 result->special = ConstValSpecialStatic;
16290 result->type = ir_type_info_get_type(ira, "ErrorSet");
16291
16292 ConstExprValue *fields = create_const_vals(1);
16293 result->data.x_struct.fields = fields;
16294
16295 // errors: []TypeInfo.Error
16296 ensure_field_index(result->type, "errors", 0);
16297
16298 TypeTableEntry *type_info_error_type = ir_type_info_get_type(ira, "Error");
16299 uint32_t error_count = type_entry->data.error_set.err_count;
16300 ConstExprValue *error_array = create_const_vals(1);
16301 error_array->special = ConstValSpecialStatic;
16302 error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count);
16303 error_array->data.x_array.special = ConstArraySpecialNone;
16304 error_array->data.x_array.s_none.parent.id = ConstParentIdNone;
16305 error_array->data.x_array.s_none.elements = create_const_vals(error_count);
16306
16307 init_const_slice(ira->codegen, &fields[0], error_array, 0, error_count, false);
16308 for (uint32_t error_index = 0; error_index < error_count; error_index++)
16309 {
16310 ErrorTableEntry *error = type_entry->data.error_set.errors[error_index];
16311 ConstExprValue *error_val = &error_array->data.x_array.s_none.elements[error_index];
16312
16313 error_val->special = ConstValSpecialStatic;
16314 error_val->type = type_info_error_type;
16315
16316 ConstExprValue *inner_fields = create_const_vals(2);
16317 inner_fields[1].special = ConstValSpecialStatic;
16318 inner_fields[1].type = ira->codegen->builtin_types.entry_usize;
16319
16320 ConstExprValue *name = nullptr;
16321 if (error->cached_error_name_val != nullptr)
16322 name = error->cached_error_name_val;
16323 if (name == nullptr)
16324 name = create_const_str_lit(ira->codegen, &error->name);
16325 init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(&error->name), true);
16326 bigint_init_unsigned(&inner_fields[1].data.x_bigint, error->value);
16327
16328 error_val->data.x_struct.fields = inner_fields;
16329 error_val->data.x_struct.parent.id = ConstParentIdArray;
16330 error_val->data.x_struct.parent.data.p_array.array_val = error_array;
16331 error_val->data.x_struct.parent.data.p_array.elem_index = error_index;
16332 }
16333
16334 break;
16335 }
16336 case TypeTableEntryIdErrorUnion:
16337 {
16338 result = create_const_vals(1);
16339 result->special = ConstValSpecialStatic;
16340 result->type = ir_type_info_get_type(ira, "ErrorUnion");
16341
16342 ConstExprValue *fields = create_const_vals(2);
16343 result->data.x_struct.fields = fields;
16344
16345 // error_set: type
16346 ensure_field_index(result->type, "error_set", 0);
16347 fields[0].special = ConstValSpecialStatic;
16348 fields[0].type = ira->codegen->builtin_types.entry_type;
16349 fields[0].data.x_type = type_entry->data.error_union.err_set_type;
16350
16351 // payload: type
16352 ensure_field_index(result->type, "payload", 1);
16353 fields[1].special = ConstValSpecialStatic;
16354 fields[1].type = ira->codegen->builtin_types.entry_type;
16355 fields[1].data.x_type = type_entry->data.error_union.payload_type;
16356
16357 break;
16358 }
16359 case TypeTableEntryIdUnion:
16360 {
16361 result = create_const_vals(1);
16362 result->special = ConstValSpecialStatic;
16363 result->type = ir_type_info_get_type(ira, "Union");
16364
16365 ConstExprValue *fields = create_const_vals(4);
16366 result->data.x_struct.fields = fields;
16367
16368 // layout: ContainerLayout
16369 ensure_field_index(result->type, "layout", 0);
16370 fields[0].special = ConstValSpecialStatic;
16371 fields[0].type = ir_type_info_get_type(ira, "ContainerLayout");
16372 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.unionation.layout);
16373 // tag_type: type
16374 ensure_field_index(result->type, "tag_type", 1);
16375 fields[1].special = ConstValSpecialStatic;
16376 fields[1].type = ira->codegen->builtin_types.entry_type;
16377 // @TODO ?type instead of using @typeOf(undefined) when we have no type.
16378 AstNode *union_decl_node = type_entry->data.unionation.decl_node;
16379 if (union_decl_node->data.container_decl.auto_enum ||
16380 union_decl_node->data.container_decl.init_arg_expr != nullptr)
16381 {
16382 fields[1].data.x_type = type_entry->data.unionation.tag_type;
16383 }
16384 else
16385 fields[1].data.x_type = ira->codegen->builtin_types.entry_undef;
16386 // fields: []TypeInfo.UnionField
16387 ensure_field_index(result->type, "fields", 2);
16388
16389 TypeTableEntry *type_info_union_field_type = ir_type_info_get_type(ira, "UnionField");
16390 uint32_t union_field_count = type_entry->data.unionation.src_field_count;
16391
16392 ConstExprValue *union_field_array = create_const_vals(1);
16393 union_field_array->special = ConstValSpecialStatic;
16394 union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count);
16395 union_field_array->data.x_array.special = ConstArraySpecialNone;
16396 union_field_array->data.x_array.s_none.parent.id = ConstParentIdNone;
16397 union_field_array->data.x_array.s_none.elements = create_const_vals(union_field_count);
16398
16399 init_const_slice(ira->codegen, &fields[2], union_field_array, 0, union_field_count, false);
16400
16401 TypeTableEntry *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField");
16402
16403 for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++)
16404 {
16405 TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index];
16406 ConstExprValue *union_field_val = &union_field_array->data.x_array.s_none.elements[union_field_index];
16407
16408 union_field_val->special = ConstValSpecialStatic;
16409 union_field_val->type = type_info_union_field_type;
16410
16411 ConstExprValue *inner_fields = create_const_vals(3);
16412 inner_fields[1].special = ConstValSpecialStatic;
16413 inner_fields[1].type = get_maybe_type(ira->codegen, type_info_enum_field_type);
16414
16415 if (fields[1].data.x_type == ira->codegen->builtin_types.entry_undef)
16416 inner_fields[1].data.x_maybe = nullptr;
16417 else
16418 {
16419 inner_fields[1].data.x_maybe = create_const_vals(1);
16420 make_enum_field_val(inner_fields[1].data.x_maybe, union_field->enum_field, type_info_enum_field_type);
16421 }
16422
16423 inner_fields[2].special = ConstValSpecialStatic;
16424 inner_fields[2].type = ira->codegen->builtin_types.entry_type;
16425 inner_fields[2].data.x_type = union_field->type_entry;
16426
16427 ConstExprValue *name = create_const_str_lit(ira->codegen, union_field->name);
16428 init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(union_field->name), true);
16429
16430 union_field_val->data.x_struct.fields = inner_fields;
16431 union_field_val->data.x_struct.parent.id = ConstParentIdArray;
16432 union_field_val->data.x_struct.parent.data.p_array.array_val = union_field_array;
16433 union_field_val->data.x_struct.parent.data.p_array.elem_index = union_field_index;
16434 }
16435 // defs: []TypeInfo.Definition
16436 ensure_field_index(result->type, "defs", 3);
16437 ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope);
16438
16439 break;
16440 }
16441 case TypeTableEntryIdStruct:
16442 {
16443 if (type_entry->data.structure.is_slice) {
16444 Buf ptr_field_name = BUF_INIT;
16445 buf_init_from_str(&ptr_field_name, "ptr");
16446 TypeTableEntry *ptr_type = type_entry->data.structure.fields_by_name.get(&ptr_field_name)->type_entry;
16447 ensure_complete_type(ira->codegen, ptr_type);
16448 buf_deinit(&ptr_field_name);
16449
16450 result = create_ptr_like_type_info("Slice", ptr_type);
16451 break;
16452 }
16453
16454 result = create_const_vals(1);
16455 result->special = ConstValSpecialStatic;
16456 result->type = ir_type_info_get_type(ira, "Struct");
16457
16458 ConstExprValue *fields = create_const_vals(3);
16459 result->data.x_struct.fields = fields;
16460
16461 // layout: ContainerLayout
16462 ensure_field_index(result->type, "layout", 0);
16463 fields[0].special = ConstValSpecialStatic;
16464 fields[0].type = ir_type_info_get_type(ira, "ContainerLayout");
16465 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.structure.layout);
16466 // fields: []TypeInfo.StructField
16467 ensure_field_index(result->type, "fields", 1);
16468
16469 TypeTableEntry *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField");
16470 uint32_t struct_field_count = type_entry->data.structure.src_field_count;
16471
16472 ConstExprValue *struct_field_array = create_const_vals(1);
16473 struct_field_array->special = ConstValSpecialStatic;
16474 struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count);
16475 struct_field_array->data.x_array.special = ConstArraySpecialNone;
16476 struct_field_array->data.x_array.s_none.parent.id = ConstParentIdNone;
16477 struct_field_array->data.x_array.s_none.elements = create_const_vals(struct_field_count);
16478
16479 init_const_slice(ira->codegen, &fields[1], struct_field_array, 0, struct_field_count, false);
16480
16481 for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++)
16482 {
16483 TypeStructField *struct_field = &type_entry->data.structure.fields[struct_field_index];
16484 ConstExprValue *struct_field_val = &struct_field_array->data.x_array.s_none.elements[struct_field_index];
16485
16486 struct_field_val->special = ConstValSpecialStatic;
16487 struct_field_val->type = type_info_struct_field_type;
16488
16489 ConstExprValue *inner_fields = create_const_vals(3);
16490 inner_fields[1].special = ConstValSpecialStatic;
16491 inner_fields[1].type = get_maybe_type(ira->codegen, ira->codegen->builtin_types.entry_usize);
16492
16493 if (!type_has_bits(struct_field->type_entry))
16494 inner_fields[1].data.x_maybe = nullptr;
16495 else
16496 {
16497 size_t byte_offset = LLVMOffsetOfElement(ira->codegen->target_data_ref, type_entry->type_ref, struct_field->gen_index);
16498 inner_fields[1].data.x_maybe = create_const_vals(1);
16499 inner_fields[1].data.x_maybe->type = ira->codegen->builtin_types.entry_usize;
16500 bigint_init_unsigned(&inner_fields[1].data.x_maybe->data.x_bigint, byte_offset);
16501 }
16502
16503 inner_fields[2].special = ConstValSpecialStatic;
16504 inner_fields[2].type = ira->codegen->builtin_types.entry_type;
16505 inner_fields[2].data.x_type = struct_field->type_entry;
16506
16507 ConstExprValue *name = create_const_str_lit(ira->codegen, struct_field->name);
16508 init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(struct_field->name), true);
16509
16510 struct_field_val->data.x_struct.fields = inner_fields;
16511 struct_field_val->data.x_struct.parent.id = ConstParentIdArray;
16512 struct_field_val->data.x_struct.parent.data.p_array.array_val = struct_field_array;
16513 struct_field_val->data.x_struct.parent.data.p_array.elem_index = struct_field_index;
16514 }
16515 // defs: []TypeInfo.Definition
16516 ensure_field_index(result->type, "defs", 2);
16517 ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope);
16518
16519 break;
16520 }
16521 case TypeTableEntryIdFn:
16522 {
16523 result = create_const_vals(1);
16524 result->special = ConstValSpecialStatic;
16525 result->type = ir_type_info_get_type(ira, "Fn");
16526
16527 ConstExprValue *fields = create_const_vals(6);
16528 result->data.x_struct.fields = fields;
16529
16530 // @TODO Fix type = undefined with ?type
16531
16532 // calling_convention: TypeInfo.CallingConvention
16533 ensure_field_index(result->type, "calling_convention", 0);
16534 fields[0].special = ConstValSpecialStatic;
16535 fields[0].type = ir_type_info_get_type(ira, "CallingConvention");
16536 bigint_init_unsigned(&fields[0].data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);
16537 // is_generic: bool
16538 ensure_field_index(result->type, "is_generic", 1);
16539 bool is_generic = type_entry->data.fn.is_generic;
16540 fields[1].special = ConstValSpecialStatic;
16541 fields[1].type = ira->codegen->builtin_types.entry_bool;
16542 fields[1].data.x_bool = is_generic;
16543 // is_varargs: bool
16544 ensure_field_index(result->type, "is_var_args", 2);
16545 bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args;
16546 fields[2].special = ConstValSpecialStatic;
16547 fields[2].type = ira->codegen->builtin_types.entry_bool;
16548 fields[2].data.x_bool = type_entry->data.fn.fn_type_id.is_var_args;
16549 // return_type: type
16550 ensure_field_index(result->type, "return_type", 3);
16551 fields[3].special = ConstValSpecialStatic;
16552 fields[3].type = ira->codegen->builtin_types.entry_type;
16553 if (type_entry->data.fn.fn_type_id.return_type == nullptr)
16554 fields[3].data.x_type = ira->codegen->builtin_types.entry_undef;
16555 else
16556 fields[3].data.x_type = type_entry->data.fn.fn_type_id.return_type;
16557 // async_allocator_type: type
16558 ensure_field_index(result->type, "async_allocator_type", 4);
16559 fields[4].special = ConstValSpecialStatic;
16560 fields[4].type = ira->codegen->builtin_types.entry_type;
16561 if (type_entry->data.fn.fn_type_id.async_allocator_type == nullptr)
16562 fields[4].data.x_type = ira->codegen->builtin_types.entry_undef;
16563 else
16564 fields[4].data.x_type = type_entry->data.fn.fn_type_id.async_allocator_type;
16565 // args: []TypeInfo.FnArg
16566 TypeTableEntry *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg");
16567 size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count -
16568 (is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC);
16569
16570 ConstExprValue *fn_arg_array = create_const_vals(1);
16571 fn_arg_array->special = ConstValSpecialStatic;
16572 fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count);
16573 fn_arg_array->data.x_array.special = ConstArraySpecialNone;
16574 fn_arg_array->data.x_array.s_none.parent.id = ConstParentIdNone;
16575 fn_arg_array->data.x_array.s_none.elements = create_const_vals(fn_arg_count);
16576
16577 init_const_slice(ira->codegen, &fields[5], fn_arg_array, 0, fn_arg_count, false);
16578
16579 for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++)
16580 {
16581 FnTypeParamInfo *fn_param_info = &type_entry->data.fn.fn_type_id.param_info[fn_arg_index];
16582 ConstExprValue *fn_arg_val = &fn_arg_array->data.x_array.s_none.elements[fn_arg_index];
16583
16584 fn_arg_val->special = ConstValSpecialStatic;
16585 fn_arg_val->type = type_info_fn_arg_type;
16586
16587 bool arg_is_generic = fn_param_info->type == nullptr;
16588 if (arg_is_generic) assert(is_generic);
16589
16590 ConstExprValue *inner_fields = create_const_vals(3);
16591 inner_fields[0].special = ConstValSpecialStatic;
16592 inner_fields[0].type = ira->codegen->builtin_types.entry_bool;
16593 inner_fields[0].data.x_bool = arg_is_generic;
16594 inner_fields[1].special = ConstValSpecialStatic;
16595 inner_fields[1].type = ira->codegen->builtin_types.entry_bool;
16596 inner_fields[1].data.x_bool = fn_param_info->is_noalias;
16597 inner_fields[2].special = ConstValSpecialStatic;
16598 inner_fields[2].type = ira->codegen->builtin_types.entry_type;
16599
16600 if (arg_is_generic)
16601 inner_fields[2].data.x_type = ira->codegen->builtin_types.entry_undef;
16602 else
16603 inner_fields[2].data.x_type = fn_param_info->type;
16604
16605 fn_arg_val->data.x_struct.fields = inner_fields;
16606 fn_arg_val->data.x_struct.parent.id = ConstParentIdArray;
16607 fn_arg_val->data.x_struct.parent.data.p_array.array_val = fn_arg_array;
16608 fn_arg_val->data.x_struct.parent.data.p_array.elem_index = fn_arg_index;
16609 }
16610
16611 break;
16612 }
16613 case TypeTableEntryIdBoundFn:
16614 {
16615 TypeTableEntry *fn_type = type_entry->data.bound_fn.fn_type;
16616 assert(fn_type->id == TypeTableEntryIdFn);
16617 result = ir_make_type_info_value(ira, fn_type);
16618
16619 break;
16620 }
16621 }
16622
16623 assert(result != nullptr);
16624 ira->codegen->type_info_cache.put(type_entry, result);
16625 return result;
16626}
16627
16628static TypeTableEntry *ir_analyze_instruction_type_info(IrAnalyze *ira,
16629 IrInstructionTypeInfo *instruction)
16630{
16631 IrInstruction *type_value = instruction->type_value->other;
16632 TypeTableEntry *type_entry = ir_resolve_type(ira, type_value);
16633 if (type_is_invalid(type_entry))
16634 return ira->codegen->builtin_types.entry_invalid;
16635
16636 TypeTableEntry *result_type = ir_type_info_get_type(ira, nullptr);
16637
16638 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
16639 out_val->type = result_type;
16640 bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry));
16641
16642 ConstExprValue *payload = ir_make_type_info_value(ira, type_entry);
16643 out_val->data.x_union.payload = payload;
16644
16645 if (payload != nullptr)
16646 {
16647 assert(payload->type->id == TypeTableEntryIdStruct);
16648 payload->data.x_struct.parent.id = ConstParentIdUnion;
16649 payload->data.x_struct.parent.data.p_union.union_val = out_val;
16650 }
16651
16652 return result_type;
16653}
16654
15377static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira,16655static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira,
15378 IrInstructionTypeId *instruction)16656 IrInstructionTypeId *instruction)
15379{16657{
...@@ -15387,7 +16665,7 @@ static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira,...@@ -15387,7 +16665,7 @@ static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira,
15387 TypeTableEntry *result_type = var_value->data.x_type;16665 TypeTableEntry *result_type = var_value->data.x_type;
1538816666
15389 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);16667 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
15390 bigint_init_unsigned(&out_val->data.x_enum_tag, type_id_index(type_entry->id));16668 bigint_init_unsigned(&out_val->data.x_enum_tag, type_id_index(type_entry));
15391 return result_type;16669 return result_type;
15392}16670}
1539316671
...@@ -15590,10 +16868,20 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr...@@ -15590,10 +16868,20 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr
15590}16868}
1559116869
15592static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) {16870static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) {
16871 TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->other);
16872 if (type_is_invalid(operand_type))
16873 return ira->codegen->builtin_types.entry_invalid;
16874
15593 IrInstruction *ptr = instruction->ptr->other;16875 IrInstruction *ptr = instruction->ptr->other;
15594 if (type_is_invalid(ptr->value.type))16876 if (type_is_invalid(ptr->value.type))
15595 return ira->codegen->builtin_types.entry_invalid;16877 return ira->codegen->builtin_types.entry_invalid;
1559616878
16879 // TODO let this be volatile
16880 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false);
16881 IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr, ptr_type);
16882 if (type_is_invalid(casted_ptr->value.type))
16883 return ira->codegen->builtin_types.entry_invalid;
16884
15597 IrInstruction *cmp_value = instruction->cmp_value->other;16885 IrInstruction *cmp_value = instruction->cmp_value->other;
15598 if (type_is_invalid(cmp_value->value.type))16886 if (type_is_invalid(cmp_value->value.type))
15599 return ira->codegen->builtin_types.entry_invalid;16887 return ira->codegen->builtin_types.entry_invalid;
...@@ -15618,28 +16906,11 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct...@@ -15618,28 +16906,11 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct
15618 if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order))16906 if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order))
15619 return ira->codegen->builtin_types.entry_invalid;16907 return ira->codegen->builtin_types.entry_invalid;
1562016908
15621 if (ptr->value.type->id != TypeTableEntryIdPointer) {16909 IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, operand_type);
15622 ir_add_error(ira, instruction->ptr,
15623 buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&ptr->value.type->name)));
15624 return ira->codegen->builtin_types.entry_invalid;
15625 }
15626
15627 TypeTableEntry *child_type = ptr->value.type->data.pointer.child_type;
15628
15629 uint32_t align_bytes = ptr->value.type->data.pointer.alignment;
15630 uint64_t size_bytes = type_size(ira->codegen, child_type);
15631 if (align_bytes < size_bytes) {
15632 ir_add_error(ira, instruction->ptr,
15633 buf_sprintf("expected pointer alignment of at least %" ZIG_PRI_u64 ", found %" PRIu32,
15634 size_bytes, align_bytes));
15635 return ira->codegen->builtin_types.entry_invalid;
15636 }
15637
15638 IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, child_type);
15639 if (type_is_invalid(casted_cmp_value->value.type))16910 if (type_is_invalid(casted_cmp_value->value.type))
15640 return ira->codegen->builtin_types.entry_invalid;16911 return ira->codegen->builtin_types.entry_invalid;
1564116912
15642 IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, child_type);16913 IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, operand_type);
15643 if (type_is_invalid(casted_new_value->value.type))16914 if (type_is_invalid(casted_new_value->value.type))
15644 return ira->codegen->builtin_types.entry_invalid;16915 return ira->codegen->builtin_types.entry_invalid;
1564516916
...@@ -15664,9 +16935,17 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct...@@ -15664,9 +16935,17 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct
15664 return ira->codegen->builtin_types.entry_invalid;16935 return ira->codegen->builtin_types.entry_invalid;
15665 }16936 }
1566616937
15667 ir_build_cmpxchg_from(&ira->new_irb, &instruction->base, ptr, casted_cmp_value, casted_new_value,16938 if (instr_is_comptime(casted_ptr) && instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) {
15668 success_order_value, failure_order_value, success_order, failure_order);16939 zig_panic("TODO compile-time execution of cmpxchg");
15669 return ira->codegen->builtin_types.entry_bool;16940 }
16941
16942 IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
16943 nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak,
16944 operand_type, success_order, failure_order);
16945 result->value.type = get_maybe_type(ira->codegen, operand_type);
16946 ir_link_new_instruction(result, &instruction->base);
16947 ir_add_alloca(ira, result, result->value.type);
16948 return result->value.type;
15670}16949}
1567116950
15672static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) {16951static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) {
...@@ -16702,6 +17981,11 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc...@@ -16702,6 +17981,11 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc
16702 return ira->codegen->builtin_types.entry_invalid;17981 return ira->codegen->builtin_types.entry_invalid;
1670317982
16704 if (fn_type_id.cc == CallingConventionAsync) {17983 if (fn_type_id.cc == CallingConventionAsync) {
17984 if (instruction->async_allocator_type_value == nullptr) {
17985 ir_add_error(ira, &instruction->base,
17986 buf_sprintf("async fn proto missing allocator type"));
17987 return ira->codegen->builtin_types.entry_invalid;
17988 }
16705 IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->other;17989 IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->other;
16706 fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value);17990 fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value);
16707 if (type_is_invalid(fn_type_id.async_allocator_type))17991 if (type_is_invalid(fn_type_id.async_allocator_type))
...@@ -17560,6 +18844,16 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc...@@ -17560,6 +18844,16 @@ static TypeTableEntry *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstruc
1756018844
17561 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);18845 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
17562 out_val->data.x_type = fn_type_id->param_info[arg_index].type;18846 out_val->data.x_type = fn_type_id->param_info[arg_index].type;
18847 if (out_val->data.x_type == nullptr) {
18848 // Args are only unresolved if our function is generic.
18849 assert(fn_type->data.fn.is_generic);
18850
18851 ir_add_error(ira, arg_index_inst,
18852 buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic",
18853 arg_index, buf_ptr(&fn_type->name)));
18854 return ira->codegen->builtin_types.entry_invalid;
18855 }
18856
17563 return ira->codegen->builtin_types.entry_type;18857 return ira->codegen->builtin_types.entry_type;
17564}18858}
1756518859
...@@ -17795,39 +19089,48 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira,...@@ -17795,39 +19089,48 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira,
17795 return result->value.type;19089 return result->value.type;
17796}19090}
1779719091
17798static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) {19092static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op) {
17799 TypeTableEntry *operand_type = ir_resolve_type(ira, instruction->operand_type->other);19093 TypeTableEntry *operand_type = ir_resolve_type(ira, op);
17800 if (type_is_invalid(operand_type)) {19094 if (type_is_invalid(operand_type))
17801 return ira->codegen->builtin_types.entry_invalid;19095 return ira->codegen->builtin_types.entry_invalid;
17802 }19096
17803 if (operand_type->id == TypeTableEntryIdInt) {19097 if (operand_type->id == TypeTableEntryIdInt) {
17804 if (operand_type->data.integral.bit_count < 8) {19098 if (operand_type->data.integral.bit_count < 8) {
17805 ir_add_error(ira, &instruction->base,19099 ir_add_error(ira, op,
17806 buf_sprintf("expected integer type 8 bits or larger, found %" PRIu32 "-bit integer type",19100 buf_sprintf("expected integer type 8 bits or larger, found %" PRIu32 "-bit integer type",
17807 operand_type->data.integral.bit_count));19101 operand_type->data.integral.bit_count));
17808 return ira->codegen->builtin_types.entry_invalid;19102 return ira->codegen->builtin_types.entry_invalid;
17809 }19103 }
17810 if (operand_type->data.integral.bit_count > ira->codegen->pointer_size_bytes * 8) {19104 if (operand_type->data.integral.bit_count > ira->codegen->pointer_size_bytes * 8) {
17811 ir_add_error(ira, &instruction->base,19105 ir_add_error(ira, op,
17812 buf_sprintf("expected integer type pointer size or smaller, found %" PRIu32 "-bit integer type",19106 buf_sprintf("expected integer type pointer size or smaller, found %" PRIu32 "-bit integer type",
17813 operand_type->data.integral.bit_count));19107 operand_type->data.integral.bit_count));
17814 return ira->codegen->builtin_types.entry_invalid;19108 return ira->codegen->builtin_types.entry_invalid;
17815 }19109 }
17816 if (!is_power_of_2(operand_type->data.integral.bit_count)) {19110 if (!is_power_of_2(operand_type->data.integral.bit_count)) {
17817 ir_add_error(ira, &instruction->base,19111 ir_add_error(ira, op,
17818 buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count));19112 buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count));
17819 return ira->codegen->builtin_types.entry_invalid;19113 return ira->codegen->builtin_types.entry_invalid;
17820 }19114 }
17821 } else if (get_codegen_ptr_type(operand_type) == nullptr) {19115 } else if (get_codegen_ptr_type(operand_type) == nullptr) {
17822 ir_add_error(ira, &instruction->base,19116 ir_add_error(ira, op,
17823 buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name)));19117 buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name)));
17824 return ira->codegen->builtin_types.entry_invalid;19118 return ira->codegen->builtin_types.entry_invalid;
17825 }19119 }
1782619120
19121 return operand_type;
19122}
19123
19124static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) {
19125 TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->other);
19126 if (type_is_invalid(operand_type))
19127 return ira->codegen->builtin_types.entry_invalid;
19128
17827 IrInstruction *ptr_inst = instruction->ptr->other;19129 IrInstruction *ptr_inst = instruction->ptr->other;
17828 if (type_is_invalid(ptr_inst->value.type))19130 if (type_is_invalid(ptr_inst->value.type))
17829 return ira->codegen->builtin_types.entry_invalid;19131 return ira->codegen->builtin_types.entry_invalid;
1783019132
19133 // TODO let this be volatile
17831 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false);19134 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false);
17832 IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);19135 IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);
17833 if (type_is_invalid(casted_ptr->value.type))19136 if (type_is_invalid(casted_ptr->value.type))
...@@ -17856,6 +19159,11 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr...@@ -17856,6 +19159,11 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr
17856 } else {19159 } else {
17857 if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering))19160 if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering))
17858 return ira->codegen->builtin_types.entry_invalid;19161 return ira->codegen->builtin_types.entry_invalid;
19162 if (ordering == AtomicOrderUnordered) {
19163 ir_add_error(ira, instruction->ordering,
19164 buf_sprintf("@atomicRmw atomic ordering must not be Unordered"));
19165 return ira->codegen->builtin_types.entry_invalid;
19166 }
17859 }19167 }
1786019168
17861 if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)19169 if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value.data.x_ptr.mut == ConstPtrMutComptimeVar)
...@@ -17871,6 +19179,49 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr...@@ -17871,6 +19179,49 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr
17871 return result->value.type;19179 return result->value.type;
17872}19180}
1787319181
19182static TypeTableEntry *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstructionAtomicLoad *instruction) {
19183 TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->other);
19184 if (type_is_invalid(operand_type))
19185 return ira->codegen->builtin_types.entry_invalid;
19186
19187 IrInstruction *ptr_inst = instruction->ptr->other;
19188 if (type_is_invalid(ptr_inst->value.type))
19189 return ira->codegen->builtin_types.entry_invalid;
19190
19191 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, true);
19192 IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);
19193 if (type_is_invalid(casted_ptr->value.type))
19194 return ira->codegen->builtin_types.entry_invalid;
19195
19196 AtomicOrder ordering;
19197 if (instruction->ordering == nullptr) {
19198 ordering = instruction->resolved_ordering;
19199 } else {
19200 if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering))
19201 return ira->codegen->builtin_types.entry_invalid;
19202 }
19203
19204 if (ordering == AtomicOrderRelease || ordering == AtomicOrderAcqRel) {
19205 assert(instruction->ordering != nullptr);
19206 ir_add_error(ira, instruction->ordering,
19207 buf_sprintf("@atomicLoad atomic ordering must not be Release or AcqRel"));
19208 return ira->codegen->builtin_types.entry_invalid;
19209 }
19210
19211 if (instr_is_comptime(casted_ptr)) {
19212 IrInstruction *result = ir_get_deref(ira, &instruction->base, casted_ptr);
19213 ir_link_new_instruction(result, &instruction->base);
19214 assert(result->value.type != nullptr);
19215 return result->value.type;
19216 }
19217
19218 IrInstruction *result = ir_build_atomic_load(&ira->new_irb, instruction->base.scope,
19219 instruction->base.source_node, nullptr, casted_ptr, nullptr, ordering);
19220 ir_link_new_instruction(result, &instruction->base);
19221 result->value.type = operand_type;
19222 return result->value.type;
19223}
19224
17874static TypeTableEntry *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) {19225static TypeTableEntry *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) {
17875 TypeTableEntry *promise_type = ir_resolve_type(ira, instruction->promise_type->other);19226 TypeTableEntry *promise_type = ir_resolve_type(ira, instruction->promise_type->other);
17876 if (type_is_invalid(promise_type))19227 if (type_is_invalid(promise_type))
...@@ -17904,6 +19255,39 @@ static TypeTableEntry *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira,...@@ -17904,6 +19255,39 @@ static TypeTableEntry *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira,
17904 return out_val->type;19255 return out_val->type;
17905}19256}
1790619257
19258static TypeTableEntry *ir_analyze_instruction_merge_err_ret_traces(IrAnalyze *ira,
19259 IrInstructionMergeErrRetTraces *instruction)
19260{
19261 IrInstruction *coro_promise_ptr = instruction->coro_promise_ptr->other;
19262 if (type_is_invalid(coro_promise_ptr->value.type))
19263 return ira->codegen->builtin_types.entry_invalid;
19264
19265 assert(coro_promise_ptr->value.type->id == TypeTableEntryIdPointer);
19266 TypeTableEntry *promise_frame_type = coro_promise_ptr->value.type->data.pointer.child_type;
19267 assert(promise_frame_type->id == TypeTableEntryIdStruct);
19268 TypeTableEntry *promise_result_type = promise_frame_type->data.structure.fields[1].type_entry;
19269
19270 if (!type_can_fail(promise_result_type)) {
19271 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
19272 out_val->type = ira->codegen->builtin_types.entry_void;
19273 return out_val->type;
19274 }
19275
19276 IrInstruction *src_err_ret_trace_ptr = instruction->src_err_ret_trace_ptr->other;
19277 if (type_is_invalid(src_err_ret_trace_ptr->value.type))
19278 return ira->codegen->builtin_types.entry_invalid;
19279
19280 IrInstruction *dest_err_ret_trace_ptr = instruction->dest_err_ret_trace_ptr->other;
19281 if (type_is_invalid(dest_err_ret_trace_ptr->value.type))
19282 return ira->codegen->builtin_types.entry_invalid;
19283
19284 IrInstruction *result = ir_build_merge_err_ret_traces(&ira->new_irb, instruction->base.scope,
19285 instruction->base.source_node, coro_promise_ptr, src_err_ret_trace_ptr, dest_err_ret_trace_ptr);
19286 ir_link_new_instruction(result, &instruction->base);
19287 result->value.type = ira->codegen->builtin_types.entry_void;
19288 return result->value.type;
19289}
19290
17907static TypeTableEntry *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) {19291static TypeTableEntry *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) {
17908 IrInstruction *result = ir_build_save_err_ret_addr(&ira->new_irb, instruction->base.scope,19292 IrInstruction *result = ir_build_save_err_ret_addr(&ira->new_irb, instruction->base.scope,
17909 instruction->base.source_node);19293 instruction->base.source_node);
...@@ -17912,6 +19296,80 @@ static TypeTableEntry *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira,...@@ -17912,6 +19296,80 @@ static TypeTableEntry *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira,
17912 return result->value.type;19296 return result->value.type;
17913}19297}
1791419298
19299static TypeTableEntry *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *ira, IrInstructionMarkErrRetTracePtr *instruction) {
19300 IrInstruction *err_ret_trace_ptr = instruction->err_ret_trace_ptr->other;
19301 if (type_is_invalid(err_ret_trace_ptr->value.type))
19302 return ira->codegen->builtin_types.entry_invalid;
19303
19304 IrInstruction *result = ir_build_mark_err_ret_trace_ptr(&ira->new_irb, instruction->base.scope,
19305 instruction->base.source_node, err_ret_trace_ptr);
19306 ir_link_new_instruction(result, &instruction->base);
19307 result->value.type = ira->codegen->builtin_types.entry_void;
19308 return result->value.type;
19309}
19310
19311static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) {
19312 TypeTableEntry *float_type = ir_resolve_type(ira, instruction->type->other);
19313 if (type_is_invalid(float_type))
19314 return ira->codegen->builtin_types.entry_invalid;
19315
19316 IrInstruction *op = instruction->op->other;
19317 if (type_is_invalid(op->value.type))
19318 return ira->codegen->builtin_types.entry_invalid;
19319
19320 bool ok_type = float_type->id == TypeTableEntryIdNumLitFloat || float_type->id == TypeTableEntryIdFloat;
19321 if (!ok_type) {
19322 ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name)));
19323 return ira->codegen->builtin_types.entry_invalid;
19324 }
19325
19326 IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type);
19327 if (type_is_invalid(casted_op->value.type))
19328 return ira->codegen->builtin_types.entry_invalid;
19329
19330 if (instr_is_comptime(casted_op)) {
19331 ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad);
19332 if (!val)
19333 return ira->codegen->builtin_types.entry_invalid;
19334
19335 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
19336
19337 if (float_type->id == TypeTableEntryIdNumLitFloat) {
19338 bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat);
19339 } else if (float_type->id == TypeTableEntryIdFloat) {
19340 switch (float_type->data.floating.bit_count) {
19341 case 32:
19342 out_val->data.x_f32 = sqrtf(val->data.x_f32);
19343 break;
19344 case 64:
19345 out_val->data.x_f64 = sqrt(val->data.x_f64);
19346 break;
19347 case 128:
19348 f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128);
19349 break;
19350 default:
19351 zig_unreachable();
19352 }
19353 } else {
19354 zig_unreachable();
19355 }
19356
19357 return float_type;
19358 }
19359
19360 assert(float_type->id == TypeTableEntryIdFloat);
19361 if (float_type->data.floating.bit_count != 32 && float_type->data.floating.bit_count != 64) {
19362 ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name)));
19363 return ira->codegen->builtin_types.entry_invalid;
19364 }
19365
19366 IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope,
19367 instruction->base.source_node, nullptr, casted_op);
19368 ir_link_new_instruction(result, &instruction->base);
19369 result->value.type = float_type;
19370 return result->value.type;
19371}
19372
17915static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {19373static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
17916 switch (instruction->id) {19374 switch (instruction->id) {
17917 case IrInstructionIdInvalid:19375 case IrInstructionIdInvalid:
...@@ -18095,6 +19553,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -18095,6 +19553,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
18095 return ir_analyze_instruction_field_parent_ptr(ira, (IrInstructionFieldParentPtr *)instruction);19553 return ir_analyze_instruction_field_parent_ptr(ira, (IrInstructionFieldParentPtr *)instruction);
18096 case IrInstructionIdOffsetOf:19554 case IrInstructionIdOffsetOf:
18097 return ir_analyze_instruction_offset_of(ira, (IrInstructionOffsetOf *)instruction);19555 return ir_analyze_instruction_offset_of(ira, (IrInstructionOffsetOf *)instruction);
19556 case IrInstructionIdTypeInfo:
19557 return ir_analyze_instruction_type_info(ira, (IrInstructionTypeInfo *) instruction);
18098 case IrInstructionIdTypeId:19558 case IrInstructionIdTypeId:
18099 return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction);19559 return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction);
18100 case IrInstructionIdSetEvalBranchQuota:19560 case IrInstructionIdSetEvalBranchQuota:
...@@ -18147,6 +19607,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -18147,6 +19607,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
18147 return ir_analyze_instruction_coro_alloc_helper(ira, (IrInstructionCoroAllocHelper *)instruction);19607 return ir_analyze_instruction_coro_alloc_helper(ira, (IrInstructionCoroAllocHelper *)instruction);
18148 case IrInstructionIdAtomicRmw:19608 case IrInstructionIdAtomicRmw:
18149 return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction);19609 return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction);
19610 case IrInstructionIdAtomicLoad:
19611 return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction);
18150 case IrInstructionIdPromiseResultType:19612 case IrInstructionIdPromiseResultType:
18151 return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction);19613 return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction);
18152 case IrInstructionIdAwaitBookkeeping:19614 case IrInstructionIdAwaitBookkeeping:
...@@ -18155,6 +19617,12 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -18155,6 +19617,12 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
18155 return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction);19617 return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction);
18156 case IrInstructionIdAddImplicitReturnType:19618 case IrInstructionIdAddImplicitReturnType:
18157 return ir_analyze_instruction_add_implicit_return_type(ira, (IrInstructionAddImplicitReturnType *)instruction);19619 return ir_analyze_instruction_add_implicit_return_type(ira, (IrInstructionAddImplicitReturnType *)instruction);
19620 case IrInstructionIdMergeErrRetTraces:
19621 return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction);
19622 case IrInstructionIdMarkErrRetTracePtr:
19623 return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);
19624 case IrInstructionIdSqrt:
19625 return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction);
18158 }19626 }
18159 zig_unreachable();19627 zig_unreachable();
18160}19628}
...@@ -18231,7 +19699,7 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl...@@ -18231,7 +19699,7 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl
18231 } else if (ira->src_implicit_return_type_list.length == 0) {19699 } else if (ira->src_implicit_return_type_list.length == 0) {
18232 return codegen->builtin_types.entry_unreachable;19700 return codegen->builtin_types.entry_unreachable;
18233 } else {19701 } else {
18234 return ir_resolve_peer_types(ira, expected_type_source_node, ira->src_implicit_return_type_list.items,19702 return ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items,
18235 ira->src_implicit_return_type_list.length);19703 ira->src_implicit_return_type_list.length);
18236 }19704 }
18237}19705}
...@@ -18282,6 +19750,9 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -18282,6 +19750,9 @@ bool ir_has_side_effects(IrInstruction *instruction) {
18282 case IrInstructionIdAwaitBookkeeping:19750 case IrInstructionIdAwaitBookkeeping:
18283 case IrInstructionIdSaveErrRetAddr:19751 case IrInstructionIdSaveErrRetAddr:
18284 case IrInstructionIdAddImplicitReturnType:19752 case IrInstructionIdAddImplicitReturnType:
19753 case IrInstructionIdMergeErrRetTraces:
19754 case IrInstructionIdMarkErrRetTracePtr:
19755 case IrInstructionIdAtomicRmw:
18285 return true;19756 return true;
1828619757
18287 case IrInstructionIdPhi:19758 case IrInstructionIdPhi:
...@@ -18350,6 +19821,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -18350,6 +19821,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
18350 case IrInstructionIdTagName:19821 case IrInstructionIdTagName:
18351 case IrInstructionIdFieldParentPtr:19822 case IrInstructionIdFieldParentPtr:
18352 case IrInstructionIdOffsetOf:19823 case IrInstructionIdOffsetOf:
19824 case IrInstructionIdTypeInfo:
18353 case IrInstructionIdTypeId:19825 case IrInstructionIdTypeId:
18354 case IrInstructionIdAlignCast:19826 case IrInstructionIdAlignCast:
18355 case IrInstructionIdOpaqueType:19827 case IrInstructionIdOpaqueType:
...@@ -18362,9 +19834,10 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -18362,9 +19834,10 @@ bool ir_has_side_effects(IrInstruction *instruction) {
18362 case IrInstructionIdCoroSize:19834 case IrInstructionIdCoroSize:
18363 case IrInstructionIdCoroSuspend:19835 case IrInstructionIdCoroSuspend:
18364 case IrInstructionIdCoroFree:19836 case IrInstructionIdCoroFree:
18365 case IrInstructionIdAtomicRmw:
18366 case IrInstructionIdCoroPromise:19837 case IrInstructionIdCoroPromise:
18367 case IrInstructionIdPromiseResultType:19838 case IrInstructionIdPromiseResultType:
19839 case IrInstructionIdSqrt:
19840 case IrInstructionIdAtomicLoad:
18368 return false;19841 return false;
1836919842
18370 case IrInstructionIdAsm:19843 case IrInstructionIdAsm:
src/ir_print.cpp+89-4
...@@ -358,9 +358,18 @@ static void ir_print_ptr_type_child(IrPrint *irp, IrInstructionPtrTypeChild *ins...@@ -358,9 +358,18 @@ static void ir_print_ptr_type_child(IrPrint *irp, IrInstructionPtrTypeChild *ins
358}358}
359359
360static void ir_print_field_ptr(IrPrint *irp, IrInstructionFieldPtr *instruction) {360static void ir_print_field_ptr(IrPrint *irp, IrInstructionFieldPtr *instruction) {
361 fprintf(irp->f, "fieldptr ");361 if (instruction->field_name_buffer) {
362 ir_print_other_instruction(irp, instruction->container_ptr);362 fprintf(irp->f, "fieldptr ");
363 fprintf(irp->f, ".%s", buf_ptr(instruction->field_name));363 ir_print_other_instruction(irp, instruction->container_ptr);
364 fprintf(irp->f, ".%s", buf_ptr(instruction->field_name_buffer));
365 } else {
366 assert(instruction->field_name_expr);
367 fprintf(irp->f, "@field(");
368 ir_print_other_instruction(irp, instruction->container_ptr);
369 fprintf(irp->f, ", ");
370 ir_print_other_instruction(irp, instruction->field_name_expr);
371 fprintf(irp->f, ")");
372 }
364}373}
365374
366static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr *instruction) {375static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr *instruction) {
...@@ -957,6 +966,12 @@ static void ir_print_offset_of(IrPrint *irp, IrInstructionOffsetOf *instruction)...@@ -957,6 +966,12 @@ static void ir_print_offset_of(IrPrint *irp, IrInstructionOffsetOf *instruction)
957 fprintf(irp->f, ")");966 fprintf(irp->f, ")");
958}967}
959968
969static void ir_print_type_info(IrPrint *irp, IrInstructionTypeInfo *instruction) {
970 fprintf(irp->f, "@typeInfo(");
971 ir_print_other_instruction(irp, instruction->type_value);
972 fprintf(irp->f, ")");
973}
974
960static void ir_print_type_id(IrPrint *irp, IrInstructionTypeId *instruction) {975static void ir_print_type_id(IrPrint *irp, IrInstructionTypeId *instruction) {
961 fprintf(irp->f, "@typeId(");976 fprintf(irp->f, "@typeId(");
962 ir_print_other_instruction(irp, instruction->type_value);977 ir_print_other_instruction(irp, instruction->type_value);
...@@ -1024,7 +1039,16 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) {...@@ -1024,7 +1039,16 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) {
1024}1039}
10251040
1026static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) {1041static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) {
1027 fprintf(irp->f, "@errorReturnTrace()");1042 fprintf(irp->f, "@errorReturnTrace(");
1043 switch (instruction->nullable) {
1044 case IrInstructionErrorReturnTrace::Null:
1045 fprintf(irp->f, "Null");
1046 break;
1047 case IrInstructionErrorReturnTrace::NonNull:
1048 fprintf(irp->f, "NonNull");
1049 break;
1050 }
1051 fprintf(irp->f, ")");
1028}1052}
10291053
1030static void ir_print_error_union(IrPrint *irp, IrInstructionErrorUnion *instruction) {1054static void ir_print_error_union(IrPrint *irp, IrInstructionErrorUnion *instruction) {
...@@ -1163,6 +1187,24 @@ static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instructio...@@ -1163,6 +1187,24 @@ static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instructio
1163 fprintf(irp->f, ")");1187 fprintf(irp->f, ")");
1164}1188}
11651189
1190static void ir_print_atomic_load(IrPrint *irp, IrInstructionAtomicLoad *instruction) {
1191 fprintf(irp->f, "@atomicLoad(");
1192 if (instruction->operand_type != nullptr) {
1193 ir_print_other_instruction(irp, instruction->operand_type);
1194 } else {
1195 fprintf(irp->f, "[TODO print]");
1196 }
1197 fprintf(irp->f, ",");
1198 ir_print_other_instruction(irp, instruction->ptr);
1199 fprintf(irp->f, ",");
1200 if (instruction->ordering != nullptr) {
1201 ir_print_other_instruction(irp, instruction->ordering);
1202 } else {
1203 fprintf(irp->f, "[TODO print]");
1204 }
1205 fprintf(irp->f, ")");
1206}
1207
1166static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeeping *instruction) {1208static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeeping *instruction) {
1167 fprintf(irp->f, "@awaitBookkeeping(");1209 fprintf(irp->f, "@awaitBookkeeping(");
1168 ir_print_other_instruction(irp, instruction->promise_result_type);1210 ir_print_other_instruction(irp, instruction->promise_result_type);
...@@ -1179,6 +1221,34 @@ static void ir_print_add_implicit_return_type(IrPrint *irp, IrInstructionAddImpl...@@ -1179,6 +1221,34 @@ static void ir_print_add_implicit_return_type(IrPrint *irp, IrInstructionAddImpl
1179 fprintf(irp->f, ")");1221 fprintf(irp->f, ")");
1180}1222}
11811223
1224static void ir_print_merge_err_ret_traces(IrPrint *irp, IrInstructionMergeErrRetTraces *instruction) {
1225 fprintf(irp->f, "@mergeErrRetTraces(");
1226 ir_print_other_instruction(irp, instruction->coro_promise_ptr);
1227 fprintf(irp->f, ",");
1228 ir_print_other_instruction(irp, instruction->src_err_ret_trace_ptr);
1229 fprintf(irp->f, ",");
1230 ir_print_other_instruction(irp, instruction->dest_err_ret_trace_ptr);
1231 fprintf(irp->f, ")");
1232}
1233
1234static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRetTracePtr *instruction) {
1235 fprintf(irp->f, "@markErrRetTracePtr(");
1236 ir_print_other_instruction(irp, instruction->err_ret_trace_ptr);
1237 fprintf(irp->f, ")");
1238}
1239
1240static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) {
1241 fprintf(irp->f, "@sqrt(");
1242 if (instruction->type != nullptr) {
1243 ir_print_other_instruction(irp, instruction->type);
1244 } else {
1245 fprintf(irp->f, "null");
1246 }
1247 fprintf(irp->f, ",");
1248 ir_print_other_instruction(irp, instruction->op);
1249 fprintf(irp->f, ")");
1250}
1251
1182static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {1252static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1183 ir_print_prefix(irp, instruction);1253 ir_print_prefix(irp, instruction);
1184 switch (instruction->id) {1254 switch (instruction->id) {
...@@ -1472,6 +1542,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1472,6 +1542,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1472 case IrInstructionIdOffsetOf:1542 case IrInstructionIdOffsetOf:
1473 ir_print_offset_of(irp, (IrInstructionOffsetOf *)instruction);1543 ir_print_offset_of(irp, (IrInstructionOffsetOf *)instruction);
1474 break;1544 break;
1545 case IrInstructionIdTypeInfo:
1546 ir_print_type_info(irp, (IrInstructionTypeInfo *)instruction);
1547 break;
1475 case IrInstructionIdTypeId:1548 case IrInstructionIdTypeId:
1476 ir_print_type_id(irp, (IrInstructionTypeId *)instruction);1549 ir_print_type_id(irp, (IrInstructionTypeId *)instruction);
1477 break;1550 break;
...@@ -1559,6 +1632,18 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1559,6 +1632,18 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1559 case IrInstructionIdAddImplicitReturnType:1632 case IrInstructionIdAddImplicitReturnType:
1560 ir_print_add_implicit_return_type(irp, (IrInstructionAddImplicitReturnType *)instruction);1633 ir_print_add_implicit_return_type(irp, (IrInstructionAddImplicitReturnType *)instruction);
1561 break;1634 break;
1635 case IrInstructionIdMergeErrRetTraces:
1636 ir_print_merge_err_ret_traces(irp, (IrInstructionMergeErrRetTraces *)instruction);
1637 break;
1638 case IrInstructionIdMarkErrRetTracePtr:
1639 ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction);
1640 break;
1641 case IrInstructionIdSqrt:
1642 ir_print_sqrt(irp, (IrInstructionSqrt *)instruction);
1643 break;
1644 case IrInstructionIdAtomicLoad:
1645 ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction);
1646 break;
1562 }1647 }
1563 fprintf(irp->f, "\n");1648 fprintf(irp->f, "\n");
1564}1649}
src/link.cpp+3
...@@ -217,6 +217,9 @@ static void construct_linker_job_elf(LinkJob *lj) {...@@ -217,6 +217,9 @@ static void construct_linker_job_elf(LinkJob *lj) {
217 lj->args.append(g->linker_script);217 lj->args.append(g->linker_script);
218 }218 }
219219
220 if (g->no_rosegment_workaround) {
221 lj->args.append("--no-rosegment");
222 }
220 lj->args.append("--gc-sections");223 lj->args.append("--gc-sections");
221224
222 lj->args.append("-m");225 lj->args.append("-m");
src/main.cpp+16-25
...@@ -43,6 +43,7 @@ static int usage(const char *arg0) {...@@ -43,6 +43,7 @@ static int usage(const char *arg0) {
43 " --pkg-end pop current pkg\n"43 " --pkg-end pop current pkg\n"
44 " --release-fast build with optimizations on and safety off\n"44 " --release-fast build with optimizations on and safety off\n"
45 " --release-safe build with optimizations on and safety on\n"45 " --release-safe build with optimizations on and safety on\n"
46 " --release-small build with size optimizations on and safety off\n"
46 " --static output will be statically linked\n"47 " --static output will be statically linked\n"
47 " --strip exclude debug symbols\n"48 " --strip exclude debug symbols\n"
48 " --target-arch [name] specify target architecture\n"49 " --target-arch [name] specify target architecture\n"
...@@ -54,7 +55,6 @@ static int usage(const char *arg0) {...@@ -54,7 +55,6 @@ static int usage(const char *arg0) {
54 " --verbose-ir turn on compiler debug output for Zig IR\n"55 " --verbose-ir turn on compiler debug output for Zig IR\n"
55 " --verbose-llvm-ir turn on compiler debug output for LLVM IR\n"56 " --verbose-llvm-ir turn on compiler debug output for LLVM IR\n"
56 " --verbose-cimport turn on compiler debug output for C imports\n"57 " --verbose-cimport turn on compiler debug output for C imports\n"
57 " --zig-install-prefix [path] override directory where zig thinks it is installed\n"
58 " -dirafter [dir] same as -isystem but do it last\n"58 " -dirafter [dir] same as -isystem but do it last\n"
59 " -isystem [dir] add additional search path for other .h files\n"59 " -isystem [dir] add additional search path for other .h files\n"
60 " -mllvm [arg] additional arguments to forward to LLVM's option processing\n"60 " -mllvm [arg] additional arguments to forward to LLVM's option processing\n"
...@@ -74,6 +74,7 @@ static int usage(const char *arg0) {...@@ -74,6 +74,7 @@ static int usage(const char *arg0) {
74 " -L[dir] alias for --library-path\n"74 " -L[dir] alias for --library-path\n"
75 " -rdynamic add all symbols to the dynamic symbol table\n"75 " -rdynamic add all symbols to the dynamic symbol table\n"
76 " -rpath [path] add directory to the runtime library search path\n"76 " -rpath [path] add directory to the runtime library search path\n"
77 " --no-rosegment compromise security to workaround valgrind bug\n"
77 " -mconsole (windows) --subsystem console to the linker\n"78 " -mconsole (windows) --subsystem console to the linker\n"
78 " -mwindows (windows) --subsystem windows to the linker\n"79 " -mwindows (windows) --subsystem windows to the linker\n"
79 " -framework [name] (darwin) link against framework\n"80 " -framework [name] (darwin) link against framework\n"
...@@ -177,6 +178,7 @@ static int find_zig_lib_dir(Buf *out_path) {...@@ -177,6 +178,7 @@ static int find_zig_lib_dir(Buf *out_path) {
177 int err;178 int err;
178179
179 Buf self_exe_path = BUF_INIT;180 Buf self_exe_path = BUF_INIT;
181 buf_resize(&self_exe_path, 0);
180 if (!(err = os_self_exe_path(&self_exe_path))) {182 if (!(err = os_self_exe_path(&self_exe_path))) {
181 Buf *cur_path = &self_exe_path;183 Buf *cur_path = &self_exe_path;
182184
...@@ -199,23 +201,14 @@ static int find_zig_lib_dir(Buf *out_path) {...@@ -199,23 +201,14 @@ static int find_zig_lib_dir(Buf *out_path) {
199 return ErrorFileNotFound;201 return ErrorFileNotFound;
200}202}
201203
202static Buf *resolve_zig_lib_dir(const char *zig_install_prefix_arg) {204static Buf *resolve_zig_lib_dir(void) {
203 int err;205 int err;
204 Buf *result = buf_alloc();206 Buf *result = buf_alloc();
205 if (zig_install_prefix_arg == nullptr) {207 if ((err = find_zig_lib_dir(result))) {
206 if ((err = find_zig_lib_dir(result))) {208 fprintf(stderr, "Unable to find zig lib directory\n");
207 fprintf(stderr, "Unable to find zig lib directory. Reinstall Zig or use --zig-install-prefix.\n");209 exit(EXIT_FAILURE);
208 exit(EXIT_FAILURE);
209 }
210 return result;
211 }
212 Buf *zig_lib_dir_buf = buf_create_from_str(zig_install_prefix_arg);
213 if (test_zig_install_prefix(zig_lib_dir_buf, result)) {
214 return result;
215 }210 }
216211 return result;
217 fprintf(stderr, "No Zig installation found at prefix: %s\n", zig_install_prefix_arg);
218 exit(EXIT_FAILURE);
219}212}
220213
221enum Cmd {214enum Cmd {
...@@ -299,7 +292,6 @@ int main(int argc, char **argv) {...@@ -299,7 +292,6 @@ int main(int argc, char **argv) {
299 const char *libc_include_dir = nullptr;292 const char *libc_include_dir = nullptr;
300 const char *msvc_lib_dir = nullptr;293 const char *msvc_lib_dir = nullptr;
301 const char *kernel32_lib_dir = nullptr;294 const char *kernel32_lib_dir = nullptr;
302 const char *zig_install_prefix = nullptr;
303 const char *dynamic_linker = nullptr;295 const char *dynamic_linker = nullptr;
304 ZigList<const char *> clang_argv = {0};296 ZigList<const char *> clang_argv = {0};
305 ZigList<const char *> llvm_argv = {0};297 ZigList<const char *> llvm_argv = {0};
...@@ -333,6 +325,7 @@ int main(int argc, char **argv) {...@@ -333,6 +325,7 @@ int main(int argc, char **argv) {
333 ZigList<const char *> test_exec_args = {0};325 ZigList<const char *> test_exec_args = {0};
334 int comptime_args_end = 0;326 int comptime_args_end = 0;
335 int runtime_args_start = argc;327 int runtime_args_start = argc;
328 bool no_rosegment_workaround = false;
336329
337 if (argc >= 2 && strcmp(argv[1], "build") == 0) {330 if (argc >= 2 && strcmp(argv[1], "build") == 0) {
338 const char *zig_exe_path = arg0;331 const char *zig_exe_path = arg0;
...@@ -359,17 +352,12 @@ int main(int argc, char **argv) {...@@ -359,17 +352,12 @@ int main(int argc, char **argv) {
359 } else if (i + 1 < argc && strcmp(argv[i], "--cache-dir") == 0) {352 } else if (i + 1 < argc && strcmp(argv[i], "--cache-dir") == 0) {
360 cache_dir = argv[i + 1];353 cache_dir = argv[i + 1];
361 i += 1;354 i += 1;
362 } else if (i + 1 < argc && strcmp(argv[i], "--zig-install-prefix") == 0) {
363 args.append(argv[i]);
364 i += 1;
365 zig_install_prefix = argv[i];
366 args.append(zig_install_prefix);
367 } else {355 } else {
368 args.append(argv[i]);356 args.append(argv[i]);
369 }357 }
370 }358 }
371359
372 Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix);360 Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
373361
374 Buf *zig_std_dir = buf_alloc();362 Buf *zig_std_dir = buf_alloc();
375 os_path_join(zig_lib_dir_buf, buf_create_from_str("std"), zig_std_dir);363 os_path_join(zig_lib_dir_buf, buf_create_from_str("std"), zig_std_dir);
...@@ -497,6 +485,8 @@ int main(int argc, char **argv) {...@@ -497,6 +485,8 @@ int main(int argc, char **argv) {
497 build_mode = BuildModeFastRelease;485 build_mode = BuildModeFastRelease;
498 } else if (strcmp(arg, "--release-safe") == 0) {486 } else if (strcmp(arg, "--release-safe") == 0) {
499 build_mode = BuildModeSafeRelease;487 build_mode = BuildModeSafeRelease;
488 } else if (strcmp(arg, "--release-small") == 0) {
489 build_mode = BuildModeSmallRelease;
500 } else if (strcmp(arg, "--strip") == 0) {490 } else if (strcmp(arg, "--strip") == 0) {
501 strip = true;491 strip = true;
502 } else if (strcmp(arg, "--static") == 0) {492 } else if (strcmp(arg, "--static") == 0) {
...@@ -519,6 +509,8 @@ int main(int argc, char **argv) {...@@ -519,6 +509,8 @@ int main(int argc, char **argv) {
519 mconsole = true;509 mconsole = true;
520 } else if (strcmp(arg, "-rdynamic") == 0) {510 } else if (strcmp(arg, "-rdynamic") == 0) {
521 rdynamic = true;511 rdynamic = true;
512 } else if (strcmp(arg, "--no-rosegment") == 0) {
513 no_rosegment_workaround = true;
522 } else if (strcmp(arg, "--each-lib-rpath") == 0) {514 } else if (strcmp(arg, "--each-lib-rpath") == 0) {
523 each_lib_rpath = true;515 each_lib_rpath = true;
524 } else if (strcmp(arg, "--enable-timing-info") == 0) {516 } else if (strcmp(arg, "--enable-timing-info") == 0) {
...@@ -590,8 +582,6 @@ int main(int argc, char **argv) {...@@ -590,8 +582,6 @@ int main(int argc, char **argv) {
590 msvc_lib_dir = argv[i];582 msvc_lib_dir = argv[i];
591 } else if (strcmp(arg, "--kernel32-lib-dir") == 0) {583 } else if (strcmp(arg, "--kernel32-lib-dir") == 0) {
592 kernel32_lib_dir = argv[i];584 kernel32_lib_dir = argv[i];
593 } else if (strcmp(arg, "--zig-install-prefix") == 0) {
594 zig_install_prefix = argv[i];
595 } else if (strcmp(arg, "--dynamic-linker") == 0) {585 } else if (strcmp(arg, "--dynamic-linker") == 0) {
596 dynamic_linker = argv[i];586 dynamic_linker = argv[i];
597 } else if (strcmp(arg, "-isystem") == 0) {587 } else if (strcmp(arg, "-isystem") == 0) {
...@@ -803,7 +793,7 @@ int main(int argc, char **argv) {...@@ -803,7 +793,7 @@ int main(int argc, char **argv) {
803 full_cache_dir);793 full_cache_dir);
804 }794 }
805795
806 Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix);796 Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
807797
808 CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, zig_lib_dir_buf);798 CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, zig_lib_dir_buf);
809 codegen_set_out_name(g, buf_out_name);799 codegen_set_out_name(g, buf_out_name);
...@@ -858,6 +848,7 @@ int main(int argc, char **argv) {...@@ -858,6 +848,7 @@ int main(int argc, char **argv) {
858848
859 codegen_set_windows_subsystem(g, mwindows, mconsole);849 codegen_set_windows_subsystem(g, mwindows, mconsole);
860 codegen_set_rdynamic(g, rdynamic);850 codegen_set_rdynamic(g, rdynamic);
851 g->no_rosegment_workaround = no_rosegment_workaround;
861 if (mmacosx_version_min && mios_version_min) {852 if (mmacosx_version_min && mios_version_min) {
862 fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n");853 fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n");
863 return EXIT_FAILURE;854 return EXIT_FAILURE;
src/os.cpp+1
...@@ -1007,6 +1007,7 @@ int os_self_exe_path(Buf *out_path) {...@@ -1007,6 +1007,7 @@ int os_self_exe_path(Buf *out_path) {
1007 buf_resize(out_path, buf_len(out_path) * 2);1007 buf_resize(out_path, buf_len(out_path) * 2);
1008 continue;1008 continue;
1009 }1009 }
1010 buf_resize(out_path, amt);
1010 return 0;1011 return 0;
1011 }1012 }
1012#endif1013#endif
src/parser.cpp+23-5
...@@ -648,12 +648,30 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m...@@ -648,12 +648,30 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
648}648}
649649
650/*650/*
651SuspendExpression(body) = "suspend" "|" Symbol "|" body651SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))
652*/652*/
653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
654 size_t orig_token_index = *token_index;654 size_t orig_token_index = *token_index;
655655
656 Token *suspend_token = &pc->tokens->at(*token_index);656 Token *name_token = nullptr;
657 Token *token = &pc->tokens->at(*token_index);
658 if (token->id == TokenIdSymbol) {
659 *token_index += 1;
660 Token *colon_token = &pc->tokens->at(*token_index);
661 if (colon_token->id == TokenIdColon) {
662 *token_index += 1;
663 name_token = token;
664 token = &pc->tokens->at(*token_index);
665 } else if (mandatory) {
666 ast_expect_token(pc, colon_token, TokenIdColon);
667 zig_unreachable();
668 } else {
669 *token_index = orig_token_index;
670 return nullptr;
671 }
672 }
673
674 Token *suspend_token = token;
657 if (suspend_token->id == TokenIdKeywordSuspend) {675 if (suspend_token->id == TokenIdKeywordSuspend) {
658 *token_index += 1;676 *token_index += 1;
659 } else if (mandatory) {677 } else if (mandatory) {
...@@ -675,6 +693,9 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b...@@ -675,6 +693,9 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b
675 }693 }
676694
677 AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);695 AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
696 if (name_token != nullptr) {
697 node->data.suspend.name = token_buf(name_token);
698 }
678 node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);699 node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);
679 ast_eat_token(pc, token_index, TokenIdBinOr);700 ast_eat_token(pc, token_index, TokenIdBinOr);
680 node->data.suspend.block = ast_parse_block(pc, token_index, true);701 node->data.suspend.block = ast_parse_block(pc, token_index, true);
...@@ -2923,9 +2944,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont...@@ -2923,9 +2944,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
2923 visit_field(&node->data.fn_def.fn_proto, visit, context);2944 visit_field(&node->data.fn_def.fn_proto, visit, context);
2924 visit_field(&node->data.fn_def.body, visit, context);2945 visit_field(&node->data.fn_def.body, visit, context);
2925 break;2946 break;
2926 case NodeTypeFnDecl:
2927 visit_field(&node->data.fn_decl.fn_proto, visit, context);
2928 break;
2929 case NodeTypeParamDecl:2947 case NodeTypeParamDecl:
2930 visit_field(&node->data.param_decl.type, visit, context);2948 visit_field(&node->data.param_decl.type, visit, context);
2931 break;2949 break;
src/translate_c.cpp+45-71
...@@ -3667,6 +3667,7 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_...@@ -3667,6 +3667,7 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_
3667 if (existing_entry) {3667 if (existing_entry) {
3668 return existing_entry->value;3668 return existing_entry->value;
3669 }3669 }
3670
3670 QualType child_qt = typedef_decl->getUnderlyingType();3671 QualType child_qt = typedef_decl->getUnderlyingType();
3671 Buf *type_name = buf_create_from_str(decl_name(typedef_decl));3672 Buf *type_name = buf_create_from_str(decl_name(typedef_decl));
36723673
...@@ -3700,16 +3701,19 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_...@@ -3700,16 +3701,19 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_
3700 // use the name of this typedef3701 // use the name of this typedef
3701 // TODO3702 // TODO
37023703
3704 // trans_qual_type here might cause us to look at this typedef again so we put the item in the map first
3705 AstNode *symbol_node = trans_create_node_symbol(c, type_name);
3706 c->decl_table.put(typedef_decl->getCanonicalDecl(), symbol_node);
3707
3703 AstNode *type_node = trans_qual_type(c, child_qt, typedef_decl->getLocation());3708 AstNode *type_node = trans_qual_type(c, child_qt, typedef_decl->getLocation());
3704 if (type_node == nullptr) {3709 if (type_node == nullptr) {
3705 emit_warning(c, typedef_decl->getLocation(), "typedef %s - unresolved child type", buf_ptr(type_name));3710 emit_warning(c, typedef_decl->getLocation(), "typedef %s - unresolved child type", buf_ptr(type_name));
3706 c->decl_table.put(typedef_decl, nullptr);3711 c->decl_table.put(typedef_decl, nullptr);
3712 // TODO add global var with type_name equal to @compileError("unable to resolve C type")
3707 return nullptr;3713 return nullptr;
3708 }3714 }
3709 add_global_var(c, type_name, type_node);3715 add_global_var(c, type_name, type_node);
37103716
3711 AstNode *symbol_node = trans_create_node_symbol(c, type_name);
3712 c->decl_table.put(typedef_decl->getCanonicalDecl(), symbol_node);
3713 return symbol_node;3717 return symbol_node;
3714}3718}
37153719
...@@ -3744,6 +3748,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {...@@ -3744,6 +3748,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {
3744 return demote_enum_to_opaque(c, enum_decl, full_type_name, bare_name);3748 return demote_enum_to_opaque(c, enum_decl, full_type_name, bare_name);
3745 }3749 }
37463750
3751
3747 bool pure_enum = true;3752 bool pure_enum = true;
3748 uint32_t field_count = 0;3753 uint32_t field_count = 0;
3749 for (auto it = enum_def->enumerator_begin(),3754 for (auto it = enum_def->enumerator_begin(),
...@@ -3755,84 +3760,53 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {...@@ -3755,84 +3760,53 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {
3755 pure_enum = false;3760 pure_enum = false;
3756 }3761 }
3757 }3762 }
3758
3759 AstNode *tag_int_type = trans_qual_type(c, enum_decl->getIntegerType(), enum_decl->getLocation());3763 AstNode *tag_int_type = trans_qual_type(c, enum_decl->getIntegerType(), enum_decl->getLocation());
3760 assert(tag_int_type);3764 assert(tag_int_type);
37613765
3762 if (pure_enum) {3766 AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl);
3763 AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl);3767 enum_node->data.container_decl.kind = ContainerKindEnum;
3764 enum_node->data.container_decl.kind = ContainerKindEnum;3768 enum_node->data.container_decl.layout = ContainerLayoutExtern;
3765 enum_node->data.container_decl.layout = ContainerLayoutExtern;3769 // TODO only emit this tag type if the enum tag type is not the default.
3766 // TODO only emit this tag type if the enum tag type is not the default.3770 // I don't know what the default is, need to figure out how clang is deciding.
3767 // I don't know what the default is, need to figure out how clang is deciding.3771 // it appears to at least be different across gcc/msvc
3768 // it appears to at least be different across gcc/msvc3772 if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) &&
3769 if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) &&3773 !c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int))
3770 !c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int))3774 {
3771 {3775 enum_node->data.container_decl.init_arg_expr = tag_int_type;
3772 enum_node->data.container_decl.init_arg_expr = tag_int_type;
3773 }
3774
3775 enum_node->data.container_decl.fields.resize(field_count);
3776 uint32_t i = 0;
3777 for (auto it = enum_def->enumerator_begin(),
3778 it_end = enum_def->enumerator_end();
3779 it != it_end; ++it, i += 1)
3780 {
3781 const EnumConstantDecl *enum_const = *it;
3782
3783 Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
3784 Buf *field_name;
3785 if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) {
3786 field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name));
3787 } else {
3788 field_name = enum_val_name;
3789 }
3790
3791 AstNode *field_node = trans_create_node(c, NodeTypeStructField);
3792 field_node->data.struct_field.name = field_name;
3793 field_node->data.struct_field.type = nullptr;
3794 enum_node->data.container_decl.fields.items[i] = field_node;
3795
3796 // in C each enum value is in the global namespace. so we put them there too.
3797 // at this point we can rely on the enum emitting successfully
3798 if (is_anonymous) {
3799 AstNode *lit_node = trans_create_node_unsigned(c, i);
3800 add_global_var(c, enum_val_name, lit_node);
3801 } else {
3802 AstNode *field_access_node = trans_create_node_field_access(c,
3803 trans_create_node_symbol(c, full_type_name), field_name);
3804 add_global_var(c, enum_val_name, field_access_node);
3805 }
3806 }
3807
3808 if (is_anonymous) {
3809 c->decl_table.put(enum_decl->getCanonicalDecl(), enum_node);
3810 return enum_node;
3811 } else {
3812 AstNode *symbol_node = trans_create_node_symbol(c, full_type_name);
3813 add_global_weak_alias(c, bare_name, full_type_name);
3814 add_global_var(c, full_type_name, enum_node);
3815 c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node);
3816 return enum_node;
3817 }
3818 }3776 }
38193777 enum_node->data.container_decl.fields.resize(field_count);
3820 // TODO after issue #305 is solved, make this be an enum with tag_int_type3778 uint32_t i = 0;
3821 // as the integer type and set the custom enum values
3822 AstNode *enum_node = tag_int_type;
3823
3824
3825 // add variables for all the values with enum_node
3826 for (auto it = enum_def->enumerator_begin(),3779 for (auto it = enum_def->enumerator_begin(),
3827 it_end = enum_def->enumerator_end();3780 it_end = enum_def->enumerator_end();
3828 it != it_end; ++it)3781 it != it_end; ++it, i += 1)
3829 {3782 {
3830 const EnumConstantDecl *enum_const = *it;3783 const EnumConstantDecl *enum_const = *it;
38313784
3832 Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));3785 Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
3833 AstNode *int_node = trans_create_node_apint(c, enum_const->getInitVal());3786 Buf *field_name;
3834 AstNode *var_node = add_global_var(c, enum_val_name, int_node);3787 if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) {
3835 var_node->data.variable_declaration.type = tag_int_type;3788 field_name = buf_slice(enum_val_name, buf_len(bare_name), buf_len(enum_val_name));
3789 } else {
3790 field_name = enum_val_name;
3791 }
3792
3793 AstNode *int_node = pure_enum && !is_anonymous ? nullptr : trans_create_node_apint(c, enum_const->getInitVal());
3794 AstNode *field_node = trans_create_node(c, NodeTypeStructField);
3795 field_node->data.struct_field.name = field_name;
3796 field_node->data.struct_field.type = nullptr;
3797 field_node->data.struct_field.value = int_node;
3798 enum_node->data.container_decl.fields.items[i] = field_node;
3799
3800 // in C each enum value is in the global namespace. so we put them there too.
3801 // at this point we can rely on the enum emitting successfully
3802 if (is_anonymous) {
3803 Buf *enum_val_name = buf_create_from_str(decl_name(enum_const));
3804 add_global_var(c, enum_val_name, int_node);
3805 } else {
3806 AstNode *field_access_node = trans_create_node_field_access(c,
3807 trans_create_node_symbol(c, full_type_name), field_name);
3808 add_global_var(c, enum_val_name, field_access_node);
3809 }
3836 }3810 }
38373811
3838 if (is_anonymous) {3812 if (is_anonymous) {
...@@ -3843,7 +3817,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {...@@ -3843,7 +3817,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) {
3843 add_global_weak_alias(c, bare_name, full_type_name);3817 add_global_weak_alias(c, bare_name, full_type_name);
3844 add_global_var(c, full_type_name, enum_node);3818 add_global_var(c, full_type_name, enum_node);
3845 c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node);3819 c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node);
3846 return symbol_node;3820 return enum_node;
3847 }3821 }
3848}3822}
38493823
src/zig_llvm.cpp+7-5
...@@ -81,7 +81,7 @@ static const bool assertions_on = false;...@@ -81,7 +81,7 @@ static const bool assertions_on = false;
81#endif81#endif
8282
83bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,83bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
84 const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug)84 const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small)
85{85{
86 std::error_code EC;86 std::error_code EC;
87 raw_fd_ostream dest(filename, EC, sys::fs::F_None);87 raw_fd_ostream dest(filename, EC, sys::fs::F_None);
...@@ -100,7 +100,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM...@@ -100,7 +100,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
100 return true;100 return true;
101 }101 }
102 PMBuilder->OptLevel = target_machine->getOptLevel();102 PMBuilder->OptLevel = target_machine->getOptLevel();
103 PMBuilder->SizeLevel = 0;103 PMBuilder->SizeLevel = is_small ? 2 : 0;
104104
105 PMBuilder->DisableTailCalls = is_debug;105 PMBuilder->DisableTailCalls = is_debug;
106 PMBuilder->DisableUnitAtATime = is_debug;106 PMBuilder->DisableUnitAtATime = is_debug;
...@@ -765,10 +765,12 @@ static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) {...@@ -765,10 +765,12 @@ static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) {
765765
766LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,766LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
767 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,767 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
768 LLVMAtomicOrdering failure_ordering)768 LLVMAtomicOrdering failure_ordering, bool is_weak)
769{769{
770 return wrap(unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp), unwrap(new_val),770 AtomicCmpXchgInst *inst = unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp),
771 mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering)));771 unwrap(new_val), mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering));
772 inst->setWeak(is_weak);
773 return wrap(inst);
772}774}
773775
774LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,776LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
src/zig_llvm.h+2-2
...@@ -52,7 +52,7 @@ enum ZigLLVM_EmitOutputType {...@@ -52,7 +52,7 @@ enum ZigLLVM_EmitOutputType {
52};52};
5353
54ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,54ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
55 const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug);55 const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small);
5656
57ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref);57ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref);
5858
...@@ -66,7 +66,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LL...@@ -66,7 +66,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LL
6666
67ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,67ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
68 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,68 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
69 LLVMAtomicOrdering failure_ordering);69 LLVMAtomicOrdering failure_ordering, bool is_weak);
7070
71ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,71ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
72 const char *name);72 const char *name);
std/array_list.zig+64-2
...@@ -28,11 +28,11 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{...@@ -28,11 +28,11 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
28 };28 };
29 }29 }
3030
31 pub fn deinit(l: &Self) void {31 pub fn deinit(l: &const Self) void {
32 l.allocator.free(l.items);32 l.allocator.free(l.items);
33 }33 }
3434
35 pub fn toSlice(l: &Self) []align(A) T {35 pub fn toSlice(l: &const Self) []align(A) T {
36 return l.items[0..l.len];36 return l.items[0..l.len];
37 }37 }
3838
...@@ -44,6 +44,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{...@@ -44,6 +44,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
44 return l.toSliceConst()[n];44 return l.toSliceConst()[n];
45 }45 }
4646
47 pub fn count(self: &const Self) usize {
48 return self.len;
49 }
50
47 /// ArrayList takes ownership of the passed in slice. The slice must have been51 /// ArrayList takes ownership of the passed in slice. The slice must have been
48 /// allocated with `allocator`.52 /// allocated with `allocator`.
49 /// Deinitialize with `deinit` or use `toOwnedSlice`.53 /// Deinitialize with `deinit` or use `toOwnedSlice`.
...@@ -128,6 +132,27 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{...@@ -128,6 +132,27 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{
128 return null;132 return null;
129 return self.pop();133 return self.pop();
130 }134 }
135
136 pub const Iterator = struct {
137 list: &const Self,
138 // how many items have we returned
139 count: usize,
140
141 pub fn next(it: &Iterator) ?T {
142 if (it.count >= it.list.len) return null;
143 const val = it.list.at(it.count);
144 it.count += 1;
145 return val;
146 }
147
148 pub fn reset(it: &Iterator) void {
149 it.count = 0;
150 }
151 };
152
153 pub fn iterator(self: &const Self) Iterator {
154 return Iterator { .list = self, .count = 0 };
155 }
131 };156 };
132}157}
133158
...@@ -143,6 +168,14 @@ test "basic ArrayList test" {...@@ -143,6 +168,14 @@ test "basic ArrayList test" {
143 assert(list.items[i] == i32(i + 1));168 assert(list.items[i] == i32(i + 1));
144 }}169 }}
145170
171 for (list.toSlice()) |v, i| {
172 assert(v == i32(i + 1));
173 }
174
175 for (list.toSliceConst()) |v, i| {
176 assert(v == i32(i + 1));
177 }
178
146 assert(list.pop() == 10);179 assert(list.pop() == 10);
147 assert(list.len == 9);180 assert(list.len == 9);
148181
...@@ -157,6 +190,35 @@ test "basic ArrayList test" {...@@ -157,6 +190,35 @@ test "basic ArrayList test" {
157 assert(list.len == 9);190 assert(list.len == 9);
158}191}
159192
193test "iterator ArrayList test" {
194 var list = ArrayList(i32).init(debug.global_allocator);
195 defer list.deinit();
196
197 try list.append(1);
198 try list.append(2);
199 try list.append(3);
200
201 var count : i32 = 0;
202 var it = list.iterator();
203 while (it.next()) |next| {
204 assert(next == count + 1);
205 count += 1;
206 }
207
208 assert(count == 3);
209 assert(it.next() == null);
210 it.reset();
211 count = 0;
212 while (it.next()) |next| {
213 assert(next == count + 1);
214 count += 1;
215 if (count == 2) break;
216 }
217
218 it.reset();
219 assert(?? it.next() == 1);
220}
221
160test "insert ArrayList test" {222test "insert ArrayList test" {
161 var list = ArrayList(i32).init(debug.global_allocator);223 var list = ArrayList(i32).init(debug.global_allocator);
162 defer list.deinit();224 defer list.deinit();
std/atomic/index.zig created+7
...@@ -0,0 +1,7 @@
1pub const Stack = @import("stack.zig").Stack;
2pub const Queue = @import("queue.zig").Queue;
3
4test "std.atomic" {
5 _ = @import("stack.zig").Stack;
6 _ = @import("queue.zig").Queue;
7}
std/atomic/queue.zig created+126
...@@ -0,0 +1,126 @@
1const builtin = @import("builtin");
2const AtomicOrder = builtin.AtomicOrder;
3const AtomicRmwOp = builtin.AtomicRmwOp;
4
5/// Many reader, many writer, non-allocating, thread-safe, lock-free
6pub fn Queue(comptime T: type) type {
7 return struct {
8 head: &Node,
9 tail: &Node,
10 root: Node,
11
12 pub const Self = this;
13
14 pub const Node = struct {
15 next: ?&Node,
16 data: T,
17 };
18
19 // TODO: well defined copy elision: https://github.com/zig-lang/zig/issues/287
20 pub fn init(self: &Self) void {
21 self.root.next = null;
22 self.head = &self.root;
23 self.tail = &self.root;
24 }
25
26 pub fn put(self: &Self, node: &Node) void {
27 node.next = null;
28
29 const tail = @atomicRmw(&Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
30 _ = @atomicRmw(?&Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst);
31 }
32
33 pub fn get(self: &Self) ?&Node {
34 var head = @atomicLoad(&Node, &self.head, AtomicOrder.SeqCst);
35 while (true) {
36 const node = head.next ?? return null;
37 head = @cmpxchgWeak(&Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return node;
38 }
39 }
40 };
41}
42
43const std = @import("std");
44const Context = struct {
45 allocator: &std.mem.Allocator,
46 queue: &Queue(i32),
47 put_sum: isize,
48 get_sum: isize,
49 get_count: usize,
50 puts_done: u8, // TODO make this a bool
51};
52
53// TODO add lazy evaluated build options and then put puts_per_thread behind
54// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor
55// CI we would use a less aggressive setting since at 1 core, while we still
56// want this test to pass, we need a smaller value since there is so much thrashing
57// we would also use a less aggressive setting when running in valgrind
58const puts_per_thread = 500;
59const put_thread_count = 3;
60
61test "std.atomic.queue" {
62 var direct_allocator = std.heap.DirectAllocator.init();
63 defer direct_allocator.deinit();
64
65 var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024);
66 defer direct_allocator.allocator.free(plenty_of_memory);
67
68 var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
69 var a = &fixed_buffer_allocator.allocator;
70
71 var queue: Queue(i32) = undefined;
72 queue.init();
73 var context = Context {
74 .allocator = a,
75 .queue = &queue,
76 .put_sum = 0,
77 .get_sum = 0,
78 .puts_done = 0,
79 .get_count = 0,
80 };
81
82 var putters: [put_thread_count]&std.os.Thread = undefined;
83 for (putters) |*t| {
84 *t = try std.os.spawnThread(&context, startPuts);
85 }
86 var getters: [put_thread_count]&std.os.Thread = undefined;
87 for (getters) |*t| {
88 *t = try std.os.spawnThread(&context, startGets);
89 }
90
91 for (putters) |t| t.wait();
92 _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
93 for (getters) |t| t.wait();
94
95 std.debug.assert(context.put_sum == context.get_sum);
96 std.debug.assert(context.get_count == puts_per_thread * put_thread_count);
97}
98
99fn startPuts(ctx: &Context) u8 {
100 var put_count: usize = puts_per_thread;
101 var r = std.rand.DefaultPrng.init(0xdeadbeef);
102 while (put_count != 0) : (put_count -= 1) {
103 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
104 const x = @bitCast(i32, r.random.scalar(u32));
105 const node = ctx.allocator.create(Queue(i32).Node) catch unreachable;
106 node.data = x;
107 ctx.queue.put(node);
108 _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
109 }
110 return 0;
111}
112
113fn startGets(ctx: &Context) u8 {
114 while (true) {
115 while (ctx.queue.get()) |node| {
116 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
117 _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
118 _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
119 }
120
121 if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) {
122 break;
123 }
124 }
125 return 0;
126}
std/atomic/stack.zig created+131
...@@ -0,0 +1,131 @@
1const builtin = @import("builtin");
2const AtomicOrder = builtin.AtomicOrder;
3
4/// Many reader, many writer, non-allocating, thread-safe, lock-free
5pub fn Stack(comptime T: type) type {
6 return struct {
7 root: ?&Node,
8
9 pub const Self = this;
10
11 pub const Node = struct {
12 next: ?&Node,
13 data: T,
14 };
15
16 pub fn init() Self {
17 return Self {
18 .root = null,
19 };
20 }
21
22 /// push operation, but only if you are the first item in the stack. if you did not succeed in
23 /// being the first item in the stack, returns the other item that was there.
24 pub fn pushFirst(self: &Self, node: &Node) ?&Node {
25 node.next = null;
26 return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst);
27 }
28
29 pub fn push(self: &Self, node: &Node) void {
30 var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst);
31 while (true) {
32 node.next = root;
33 root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break;
34 }
35 }
36
37 pub fn pop(self: &Self) ?&Node {
38 var root = @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst);
39 while (true) {
40 root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root;
41 }
42 }
43
44 pub fn isEmpty(self: &Self) bool {
45 return @atomicLoad(?&Node, &self.root, AtomicOrder.SeqCst) == null;
46 }
47 };
48}
49
50const std = @import("std");
51const Context = struct {
52 allocator: &std.mem.Allocator,
53 stack: &Stack(i32),
54 put_sum: isize,
55 get_sum: isize,
56 get_count: usize,
57 puts_done: u8, // TODO make this a bool
58};
59// TODO add lazy evaluated build options and then put puts_per_thread behind
60// some option such as: "AggressiveMultithreadedFuzzTest". In the AppVeyor
61// CI we would use a less aggressive setting since at 1 core, while we still
62// want this test to pass, we need a smaller value since there is so much thrashing
63// we would also use a less aggressive setting when running in valgrind
64const puts_per_thread = 500;
65const put_thread_count = 3;
66
67test "std.atomic.stack" {
68 var direct_allocator = std.heap.DirectAllocator.init();
69 defer direct_allocator.deinit();
70
71 var plenty_of_memory = try direct_allocator.allocator.alloc(u8, 300 * 1024);
72 defer direct_allocator.allocator.free(plenty_of_memory);
73
74 var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory);
75 var a = &fixed_buffer_allocator.allocator;
76
77 var stack = Stack(i32).init();
78 var context = Context {
79 .allocator = a,
80 .stack = &stack,
81 .put_sum = 0,
82 .get_sum = 0,
83 .puts_done = 0,
84 .get_count = 0,
85 };
86
87 var putters: [put_thread_count]&std.os.Thread = undefined;
88 for (putters) |*t| {
89 *t = try std.os.spawnThread(&context, startPuts);
90 }
91 var getters: [put_thread_count]&std.os.Thread = undefined;
92 for (getters) |*t| {
93 *t = try std.os.spawnThread(&context, startGets);
94 }
95
96 for (putters) |t| t.wait();
97 _ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
98 for (getters) |t| t.wait();
99
100 std.debug.assert(context.put_sum == context.get_sum);
101 std.debug.assert(context.get_count == puts_per_thread * put_thread_count);
102}
103
104fn startPuts(ctx: &Context) u8 {
105 var put_count: usize = puts_per_thread;
106 var r = std.rand.DefaultPrng.init(0xdeadbeef);
107 while (put_count != 0) : (put_count -= 1) {
108 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
109 const x = @bitCast(i32, r.random.scalar(u32));
110 const node = ctx.allocator.create(Stack(i32).Node) catch unreachable;
111 node.data = x;
112 ctx.stack.push(node);
113 _ = @atomicRmw(isize, &ctx.put_sum, builtin.AtomicRmwOp.Add, x, AtomicOrder.SeqCst);
114 }
115 return 0;
116}
117
118fn startGets(ctx: &Context) u8 {
119 while (true) {
120 while (ctx.stack.pop()) |node| {
121 std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
122 _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
123 _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
124 }
125
126 if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) {
127 break;
128 }
129 }
130 return 0;
131}
std/buf_map.zig+40-23
...@@ -1,6 +1,8 @@...@@ -1,6 +1,8 @@
1const HashMap = @import("hash_map.zig").HashMap;1const std = @import("index.zig");
2const mem = @import("mem.zig");2const HashMap = std.HashMap;
3const mem = std.mem;
3const Allocator = mem.Allocator;4const Allocator = mem.Allocator;
5const assert = std.debug.assert;
46
5/// BufMap copies keys and values before they go into the map, and7/// BufMap copies keys and values before they go into the map, and
6/// frees them when they get removed.8/// frees them when they get removed.
...@@ -16,10 +18,10 @@ pub const BufMap = struct {...@@ -16,10 +18,10 @@ pub const BufMap = struct {
16 return self;18 return self;
17 }19 }
1820
19 pub fn deinit(self: &BufMap) void {21 pub fn deinit(self: &const BufMap) void {
20 var it = self.hash_map.iterator();22 var it = self.hash_map.iterator();
21 while (true) {23 while (true) {
22 const entry = it.next() ?? break; 24 const entry = it.next() ?? break;
23 self.free(entry.key);25 self.free(entry.key);
24 self.free(entry.value);26 self.free(entry.value);
25 }27 }
...@@ -28,21 +30,15 @@ pub const BufMap = struct {...@@ -28,21 +30,15 @@ pub const BufMap = struct {
28 }30 }
2931
30 pub fn set(self: &BufMap, key: []const u8, value: []const u8) !void {32 pub fn set(self: &BufMap, key: []const u8, value: []const u8) !void {
31 if (self.hash_map.get(key)) |entry| {33 self.delete(key);
32 const value_copy = try self.copy(value);34 const key_copy = try self.copy(key);
33 errdefer self.free(value_copy);35 errdefer self.free(key_copy);
34 _ = try self.hash_map.put(key, value_copy);36 const value_copy = try self.copy(value);
35 self.free(entry.value);37 errdefer self.free(value_copy);
36 } else {38 _ = try self.hash_map.put(key_copy, value_copy);
37 const key_copy = try self.copy(key);
38 errdefer self.free(key_copy);
39 const value_copy = try self.copy(value);
40 errdefer self.free(value_copy);
41 _ = try self.hash_map.put(key_copy, value_copy);
42 }
43 }39 }
4440
45 pub fn get(self: &BufMap, key: []const u8) ?[]const u8 {41 pub fn get(self: &const BufMap, key: []const u8) ?[]const u8 {
46 const entry = self.hash_map.get(key) ?? return null;42 const entry = self.hash_map.get(key) ?? return null;
47 return entry.value;43 return entry.value;
48 }44 }
...@@ -54,20 +50,41 @@ pub const BufMap = struct {...@@ -54,20 +50,41 @@ pub const BufMap = struct {
54 }50 }
5551
56 pub fn count(self: &const BufMap) usize {52 pub fn count(self: &const BufMap) usize {
57 return self.hash_map.size;53 return self.hash_map.count();
58 }54 }
5955
60 pub fn iterator(self: &const BufMap) BufMapHashMap.Iterator {56 pub fn iterator(self: &const BufMap) BufMapHashMap.Iterator {
61 return self.hash_map.iterator();57 return self.hash_map.iterator();
62 }58 }
6359
64 fn free(self: &BufMap, value: []const u8) void {60 fn free(self: &const BufMap, value: []const u8) void {
65 self.hash_map.allocator.free(value);61 self.hash_map.allocator.free(value);
66 }62 }
6763
68 fn copy(self: &BufMap, value: []const u8) ![]const u8 {64 fn copy(self: &const BufMap, value: []const u8) ![]const u8 {
69 const result = try self.hash_map.allocator.alloc(u8, value.len);65 return mem.dupe(self.hash_map.allocator, u8, value);
70 mem.copy(u8, result, value);
71 return result;
72 }66 }
73};67};
68
69test "BufMap" {
70 var direct_allocator = std.heap.DirectAllocator.init();
71 defer direct_allocator.deinit();
72
73 var bufmap = BufMap.init(&direct_allocator.allocator);
74 defer bufmap.deinit();
75
76 try bufmap.set("x", "1");
77 assert(mem.eql(u8, ??bufmap.get("x"), "1"));
78 assert(1 == bufmap.count());
79
80 try bufmap.set("x", "2");
81 assert(mem.eql(u8, ??bufmap.get("x"), "2"));
82 assert(1 == bufmap.count());
83
84 try bufmap.set("x", "3");
85 assert(mem.eql(u8, ??bufmap.get("x"), "3"));
86 assert(1 == bufmap.count());
87
88 bufmap.delete("x");
89 assert(0 == bufmap.count());
90}
std/buf_set.zig+23-5
...@@ -1,6 +1,8 @@...@@ -1,6 +1,8 @@
1const std = @import("index.zig");
1const HashMap = @import("hash_map.zig").HashMap;2const HashMap = @import("hash_map.zig").HashMap;
2const mem = @import("mem.zig");3const mem = @import("mem.zig");
3const Allocator = mem.Allocator;4const Allocator = mem.Allocator;
5const assert = std.debug.assert;
46
5pub const BufSet = struct {7pub const BufSet = struct {
6 hash_map: BufSetHashMap,8 hash_map: BufSetHashMap,
...@@ -14,10 +16,10 @@ pub const BufSet = struct {...@@ -14,10 +16,10 @@ pub const BufSet = struct {
14 return self;16 return self;
15 }17 }
1618
17 pub fn deinit(self: &BufSet) void {19 pub fn deinit(self: &const BufSet) void {
18 var it = self.hash_map.iterator();20 var it = self.hash_map.iterator();
19 while (true) {21 while (true) {
20 const entry = it.next() ?? break; 22 const entry = it.next() ?? break;
21 self.free(entry.key);23 self.free(entry.key);
22 }24 }
2325
...@@ -38,7 +40,7 @@ pub const BufSet = struct {...@@ -38,7 +40,7 @@ pub const BufSet = struct {
38 }40 }
3941
40 pub fn count(self: &const BufSet) usize {42 pub fn count(self: &const BufSet) usize {
41 return self.hash_map.size;43 return self.hash_map.count();
42 }44 }
4345
44 pub fn iterator(self: &const BufSet) BufSetHashMap.Iterator {46 pub fn iterator(self: &const BufSet) BufSetHashMap.Iterator {
...@@ -49,14 +51,30 @@ pub const BufSet = struct {...@@ -49,14 +51,30 @@ pub const BufSet = struct {
49 return self.hash_map.allocator;51 return self.hash_map.allocator;
50 }52 }
5153
52 fn free(self: &BufSet, value: []const u8) void {54 fn free(self: &const BufSet, value: []const u8) void {
53 self.hash_map.allocator.free(value);55 self.hash_map.allocator.free(value);
54 }56 }
5557
56 fn copy(self: &BufSet, value: []const u8) ![]const u8 {58 fn copy(self: &const BufSet, value: []const u8) ![]const u8 {
57 const result = try self.hash_map.allocator.alloc(u8, value.len);59 const result = try self.hash_map.allocator.alloc(u8, value.len);
58 mem.copy(u8, result, value);60 mem.copy(u8, result, value);
59 return result;61 return result;
60 }62 }
61};63};
6264
65test "BufSet" {
66 var direct_allocator = std.heap.DirectAllocator.init();
67 defer direct_allocator.deinit();
68
69 var bufset = BufSet.init(&direct_allocator.allocator);
70 defer bufset.deinit();
71
72 try bufset.put("x");
73 assert(bufset.count() == 1);
74 bufset.delete("x");
75 assert(bufset.count() == 0);
76
77 try bufset.put("x");
78 try bufset.put("y");
79 try bufset.put("z");
80}
std/buffer.zig+6-22
...@@ -66,7 +66,7 @@ pub const Buffer = struct {...@@ -66,7 +66,7 @@ pub const Buffer = struct {
66 self.list.deinit();66 self.list.deinit();
67 }67 }
6868
69 pub fn toSlice(self: &Buffer) []u8 {69 pub fn toSlice(self: &const Buffer) []u8 {
70 return self.list.toSlice()[0..self.len()];70 return self.list.toSlice()[0..self.len()];
71 }71 }
7272
...@@ -99,26 +99,10 @@ pub const Buffer = struct {...@@ -99,26 +99,10 @@ pub const Buffer = struct {
99 mem.copy(u8, self.list.toSlice()[old_len..], m);99 mem.copy(u8, self.list.toSlice()[old_len..], m);
100 }100 }
101101
102 // TODO: remove, use OutStream for this
103 pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) !void {
104 return fmt.format(self, append, format, args);
105 }
106
107 // TODO: remove, use OutStream for this
108 pub fn appendByte(self: &Buffer, byte: u8) !void {102 pub fn appendByte(self: &Buffer, byte: u8) !void {
109 return self.appendByteNTimes(byte, 1);103 const old_len = self.len();
110 }104 try self.resize(old_len + 1);
111105 self.list.toSlice()[old_len] = byte;
112 // TODO: remove, use OutStream for this
113 pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) !void {
114 var prev_size: usize = self.len();
115 const new_size = prev_size + count;
116 try self.resize(new_size);
117
118 var i: usize = prev_size;
119 while (i < new_size) : (i += 1) {
120 self.list.items[i] = byte;
121 }
122 }106 }
123107
124 pub fn eql(self: &const Buffer, m: []const u8) bool {108 pub fn eql(self: &const Buffer, m: []const u8) bool {
...@@ -154,7 +138,7 @@ test "simple Buffer" {...@@ -154,7 +138,7 @@ test "simple Buffer" {
154 var buf = try Buffer.init(debug.global_allocator, "");138 var buf = try Buffer.init(debug.global_allocator, "");
155 assert(buf.len() == 0);139 assert(buf.len() == 0);
156 try buf.append("hello");140 try buf.append("hello");
157 try buf.appendByte(' ');141 try buf.append(" ");
158 try buf.append("world");142 try buf.append("world");
159 assert(buf.eql("hello world"));143 assert(buf.eql("hello world"));
160 assert(mem.eql(u8, cstr.toSliceConst(buf.toSliceConst().ptr), buf.toSliceConst()));144 assert(mem.eql(u8, cstr.toSliceConst(buf.toSliceConst().ptr), buf.toSliceConst()));
...@@ -166,5 +150,5 @@ test "simple Buffer" {...@@ -166,5 +150,5 @@ test "simple Buffer" {
166 assert(buf.endsWith("orld"));150 assert(buf.endsWith("orld"));
167151
168 try buf2.resize(4);152 try buf2.resize(4);
169 assert(buf.startsWith(buf2.toSliceConst()));153 assert(buf.startsWith(buf2.toSlice()));
170}154}
std/build.zig+10-5
...@@ -426,15 +426,18 @@ pub const Builder = struct {...@@ -426,15 +426,18 @@ pub const Builder = struct {
426426
427 const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false;427 const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false;
428 const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false;428 const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false;
429 const release_small = self.option(bool, "release-small", "size optimizations on and safety off") ?? false;
429430
430 const mode = if (release_safe and !release_fast)431 const mode = if (release_safe and !release_fast and !release_small)
431 builtin.Mode.ReleaseSafe432 builtin.Mode.ReleaseSafe
432 else if (release_fast and !release_safe)433 else if (release_fast and !release_safe and !release_small)
433 builtin.Mode.ReleaseFast434 builtin.Mode.ReleaseFast
434 else if (!release_fast and !release_safe)435 else if (release_small and !release_fast and !release_safe)
436 builtin.Mode.ReleaseSmall
437 else if (!release_fast and !release_safe and !release_small)
435 builtin.Mode.Debug438 builtin.Mode.Debug
436 else x: {439 else x: {
437 warn("Both -Drelease-safe and -Drelease-fast specified");440 warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)");
438 self.markInvalidUserInput();441 self.markInvalidUserInput();
439 break :x builtin.Mode.Debug;442 break :x builtin.Mode.Debug;
440 };443 };
...@@ -1229,6 +1232,7 @@ pub const LibExeObjStep = struct {...@@ -1229,6 +1232,7 @@ pub const LibExeObjStep = struct {
1229 builtin.Mode.Debug => {},1232 builtin.Mode.Debug => {},
1230 builtin.Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,1233 builtin.Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,
1231 builtin.Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable,1234 builtin.Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable,
1235 builtin.Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable,
1232 }1236 }
12331237
1234 zig_args.append("--cache-dir") catch unreachable;1238 zig_args.append("--cache-dir") catch unreachable;
...@@ -1369,7 +1373,7 @@ pub const LibExeObjStep = struct {...@@ -1369,7 +1373,7 @@ pub const LibExeObjStep = struct {
1369 args.append("ssp-buffer-size=4") catch unreachable;1373 args.append("ssp-buffer-size=4") catch unreachable;
1370 }1374 }
1371 },1375 },
1372 builtin.Mode.ReleaseFast => {1376 builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {
1373 args.append("-O2") catch unreachable;1377 args.append("-O2") catch unreachable;
1374 args.append("-fno-stack-protector") catch unreachable;1378 args.append("-fno-stack-protector") catch unreachable;
1375 },1379 },
...@@ -1706,6 +1710,7 @@ pub const TestStep = struct {...@@ -1706,6 +1710,7 @@ pub const TestStep = struct {
1706 builtin.Mode.Debug => {},1710 builtin.Mode.Debug => {},
1707 builtin.Mode.ReleaseSafe => try zig_args.append("--release-safe"),1711 builtin.Mode.ReleaseSafe => try zig_args.append("--release-safe"),
1708 builtin.Mode.ReleaseFast => try zig_args.append("--release-fast"),1712 builtin.Mode.ReleaseFast => try zig_args.append("--release-fast"),
1713 builtin.Mode.ReleaseSmall => try zig_args.append("--release-small"),
1709 }1714 }
17101715
1711 switch (self.target) {1716 switch (self.target) {
std/c/darwin.zig+31
...@@ -3,10 +3,28 @@ pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int;...@@ -3,10 +3,28 @@ pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int;
33
4pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize;4pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize;
55
6pub extern "c" fn mach_absolute_time() u64;
7pub extern "c" fn mach_timebase_info(tinfo: ?&mach_timebase_info_data) void;
8
6pub use @import("../os/darwin_errno.zig");9pub use @import("../os/darwin_errno.zig");
710
8pub const _errno = __error;11pub const _errno = __error;
912
13pub const timeval = extern struct {
14 tv_sec: isize,
15 tv_usec: isize,
16};
17
18pub const timezone = extern struct {
19 tz_minuteswest: i32,
20 tz_dsttime: i32,
21};
22
23pub const mach_timebase_info_data = struct {
24 numer: u32,
25 denom: u32,
26};
27
10/// Renamed to Stat to not conflict with the stat function.28/// Renamed to Stat to not conflict with the stat function.
11pub const Stat = extern struct {29pub const Stat = extern struct {
12 dev: i32,30 dev: i32,
...@@ -55,3 +73,16 @@ pub const dirent = extern struct {...@@ -55,3 +73,16 @@ pub const dirent = extern struct {
55 d_type: u8,73 d_type: u8,
56 d_name: u8, // field address is address of first byte of name74 d_name: u8, // field address is address of first byte of name
57};75};
76
77pub const sockaddr = extern struct {
78 sa_len: u8,
79 sa_family: sa_family_t,
80 sa_data: [14]u8,
81};
82
83pub const sa_family_t = u8;
84
85pub const pthread_attr_t = extern struct {
86 __sig: c_long,
87 __opaque: [56]u8,
88};
std/c/index.zig+12
...@@ -28,6 +28,7 @@ pub extern "c" fn unlink(path: &const u8) c_int;...@@ -28,6 +28,7 @@ pub extern "c" fn unlink(path: &const u8) c_int;
28pub extern "c" fn getcwd(buf: &u8, size: usize) ?&u8;28pub extern "c" fn getcwd(buf: &u8, size: usize) ?&u8;
29pub extern "c" fn waitpid(pid: c_int, stat_loc: &c_int, options: c_int) c_int;29pub extern "c" fn waitpid(pid: c_int, stat_loc: &c_int, options: c_int) c_int;
30pub extern "c" fn fork() c_int;30pub extern "c" fn fork() c_int;
31pub extern "c" fn access(path: &const u8, mode: c_uint) c_int;
31pub extern "c" fn pipe(fds: &c_int) c_int;32pub extern "c" fn pipe(fds: &c_int) c_int;
32pub extern "c" fn mkdir(path: &const u8, mode: c_uint) c_int;33pub extern "c" fn mkdir(path: &const u8, mode: c_uint) c_int;
33pub extern "c" fn symlink(existing: &const u8, new: &const u8) c_int;34pub extern "c" fn symlink(existing: &const u8, new: &const u8) c_int;
...@@ -40,6 +41,7 @@ pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int;...@@ -40,6 +41,7 @@ pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int;
40pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize;41pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize;
41pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8;42pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8;
42pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int;43pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int;
44pub extern "c" fn gettimeofday(tv: ?&timeval, tz: ?&timezone) c_int;
43pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int;45pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int;
44pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?&timespec) c_int;46pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?&timespec) c_int;
45pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;47pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;
...@@ -51,3 +53,13 @@ pub extern "c" fn malloc(usize) ?&c_void;...@@ -51,3 +53,13 @@ pub extern "c" fn malloc(usize) ?&c_void;
51pub extern "c" fn realloc(&c_void, usize) ?&c_void;53pub extern "c" fn realloc(&c_void, usize) ?&c_void;
52pub extern "c" fn free(&c_void) void;54pub extern "c" fn free(&c_void) void;
53pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int;55pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int;
56
57pub extern "pthread" fn pthread_create(noalias newthread: &pthread_t,
58 noalias attr: ?&const pthread_attr_t, start_routine: extern fn(?&c_void) ?&c_void,
59 noalias arg: ?&c_void) c_int;
60pub extern "pthread" fn pthread_attr_init(attr: &pthread_attr_t) c_int;
61pub extern "pthread" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int;
62pub extern "pthread" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int;
63pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int;
64
65pub const pthread_t = &@OpaqueType();
std/c/linux.zig+5
...@@ -3,3 +3,8 @@ pub use @import("../os/linux/errno.zig");...@@ -3,3 +3,8 @@ pub use @import("../os/linux/errno.zig");
3pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int;3pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int;
4extern "c" fn __errno_location() &c_int;4extern "c" fn __errno_location() &c_int;
5pub const _errno = __errno_location;5pub const _errno = __errno_location;
6
7pub const pthread_attr_t = extern struct {
8 __size: [56]u8,
9 __align: c_long,
10};
std/crypto/throughput_test.zig+11-16
...@@ -1,22 +1,17 @@...@@ -1,22 +1,17 @@
1// Modify the HashFunction variable to the one wanted to test.1// Modify the HashFunction variable to the one wanted to test.
2//2//
3// NOTE: The throughput measurement may be slightly lower than other measurements since we run
4// through our block alignment functions as well. Be aware when comparing against other tests.
5//
6// ```3// ```
7// zig build-exe --release-fast --library c throughput_test.zig4// zig build-exe --release-fast throughput_test.zig
8// ./throughput_test5// ./throughput_test
9// ```6// ```
10const HashFunction = @import("md5.zig").Md5;
11const BytesToHash = 1024 * Mb;
127
13const std = @import("std");8const std = @import("std");
9const time = std.os.time;
10const Timer = time.Timer;
11const HashFunction = @import("md5.zig").Md5;
1412
15const c = @cImport({13const MiB = 1024 * 1024;
16 @cInclude("time.h");14const BytesToHash = 1024 * MiB;
17});
18
19const Mb = 1024 * 1024;
2015
21pub fn main() !void {16pub fn main() !void {
22 var stdout_file = try std.io.getStdOut();17 var stdout_file = try std.io.getStdOut();
...@@ -29,15 +24,15 @@ pub fn main() !void {...@@ -29,15 +24,15 @@ pub fn main() !void {
29 var h = HashFunction.init();24 var h = HashFunction.init();
30 var offset: usize = 0;25 var offset: usize = 0;
3126
32 const start = c.clock();27 var timer = try Timer.start();
28 const start = timer.lap();
33 while (offset < BytesToHash) : (offset += block.len) {29 while (offset < BytesToHash) : (offset += block.len) {
34 h.update(block[0..]);30 h.update(block[0..]);
35 }31 }
36 const end = c.clock();32 const end = timer.read();
3733
38 const elapsed_s = f64((end - start) * c.CLOCKS_PER_SEC) / 1000000;34 const elapsed_s = f64(end - start) / time.ns_per_s;
39 const throughput = u64(BytesToHash / elapsed_s);35 const throughput = u64(BytesToHash / elapsed_s);
4036
41 try stdout.print("{}: ", @typeName(HashFunction));37 try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB));
42 try stdout.print("{} Mb/s\n", throughput);
43}38}
std/debug/index.zig+20-5
...@@ -38,7 +38,7 @@ pub fn getSelfDebugInfo() !&ElfStackTrace {...@@ -38,7 +38,7 @@ pub fn getSelfDebugInfo() !&ElfStackTrace {
38 if (self_debug_info) |info| {38 if (self_debug_info) |info| {
39 return info;39 return info;
40 } else {40 } else {
41 const info = try openSelfDebugInfo(global_allocator);41 const info = try openSelfDebugInfo(getDebugInfoAllocator());
42 self_debug_info = info;42 self_debug_info = info;
43 return info;43 return info;
44 }44 }
...@@ -51,7 +51,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {...@@ -51,7 +51,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
51 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;51 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
52 return;52 return;
53 };53 };
54 writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), start_addr) catch |err| {54 writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty(), start_addr) catch |err| {
55 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;55 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
56 return;56 return;
57 };57 };
...@@ -64,7 +64,7 @@ pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void {...@@ -64,7 +64,7 @@ pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void {
64 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;64 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
65 return;65 return;
66 };66 };
67 writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| {67 writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty()) catch |err| {
68 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;68 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
69 return;69 return;
70 };70 };
...@@ -592,8 +592,8 @@ fn getString(st: &ElfStackTrace, offset: u64) ![]u8 {...@@ -592,8 +592,8 @@ fn getString(st: &ElfStackTrace, offset: u64) ![]u8 {
592}592}
593593
594fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 {594fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 {
595 const buf = try global_allocator.alloc(u8, size);595 const buf = try allocator.alloc(u8, size);
596 errdefer global_allocator.free(buf);596 errdefer allocator.free(buf);
597 if ((try in_stream.read(buf)) < size) return error.EndOfFile;597 if ((try in_stream.read(buf)) < size) return error.EndOfFile;
598 return buf;598 return buf;
599}599}
...@@ -1126,6 +1126,21 @@ fn readILeb128(in_stream: var) !i64 {...@@ -1126,6 +1126,21 @@ fn readILeb128(in_stream: var) !i64 {
1126 }1126 }
1127}1127}
11281128
1129/// This should only be used in temporary test programs.
1129pub const global_allocator = &global_fixed_allocator.allocator;1130pub const global_allocator = &global_fixed_allocator.allocator;
1130var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]);1131var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]);
1131var global_allocator_mem: [100 * 1024]u8 = undefined;1132var global_allocator_mem: [100 * 1024]u8 = undefined;
1133
1134
1135// TODO make thread safe
1136var debug_info_allocator: ?&mem.Allocator = null;
1137var debug_info_direct_allocator: std.heap.DirectAllocator = undefined;
1138var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;
1139fn getDebugInfoAllocator() &mem.Allocator {
1140 if (debug_info_allocator) |a| return a;
1141
1142 debug_info_direct_allocator = std.heap.DirectAllocator.init();
1143 debug_info_arena_allocator = std.heap.ArenaAllocator.init(&debug_info_direct_allocator.allocator);
1144 debug_info_allocator = &debug_info_arena_allocator.allocator;
1145 return &debug_info_arena_allocator.allocator;
1146}
std/elf.zig+611
...@@ -7,6 +7,246 @@ const mem = std.mem;...@@ -7,6 +7,246 @@ const mem = std.mem;
7const debug = std.debug;7const debug = std.debug;
8const InStream = std.stream.InStream;8const InStream = std.stream.InStream;
99
10pub const AT_NULL = 0;
11pub const AT_IGNORE = 1;
12pub const AT_EXECFD = 2;
13pub const AT_PHDR = 3;
14pub const AT_PHENT = 4;
15pub const AT_PHNUM = 5;
16pub const AT_PAGESZ = 6;
17pub const AT_BASE = 7;
18pub const AT_FLAGS = 8;
19pub const AT_ENTRY = 9;
20pub const AT_NOTELF = 10;
21pub const AT_UID = 11;
22pub const AT_EUID = 12;
23pub const AT_GID = 13;
24pub const AT_EGID = 14;
25pub const AT_CLKTCK = 17;
26pub const AT_PLATFORM = 15;
27pub const AT_HWCAP = 16;
28pub const AT_FPUCW = 18;
29pub const AT_DCACHEBSIZE = 19;
30pub const AT_ICACHEBSIZE = 20;
31pub const AT_UCACHEBSIZE = 21;
32pub const AT_IGNOREPPC = 22;
33pub const AT_SECURE = 23;
34pub const AT_BASE_PLATFORM = 24;
35pub const AT_RANDOM = 25;
36pub const AT_HWCAP2 = 26;
37pub const AT_EXECFN = 31;
38pub const AT_SYSINFO = 32;
39pub const AT_SYSINFO_EHDR = 33;
40pub const AT_L1I_CACHESHAPE = 34;
41pub const AT_L1D_CACHESHAPE = 35;
42pub const AT_L2_CACHESHAPE = 36;
43pub const AT_L3_CACHESHAPE = 37;
44pub const AT_L1I_CACHESIZE = 40;
45pub const AT_L1I_CACHEGEOMETRY = 41;
46pub const AT_L1D_CACHESIZE = 42;
47pub const AT_L1D_CACHEGEOMETRY = 43;
48pub const AT_L2_CACHESIZE = 44;
49pub const AT_L2_CACHEGEOMETRY = 45;
50pub const AT_L3_CACHESIZE = 46;
51pub const AT_L3_CACHEGEOMETRY = 47;
52
53pub const DT_NULL = 0;
54pub const DT_NEEDED = 1;
55pub const DT_PLTRELSZ = 2;
56pub const DT_PLTGOT = 3;
57pub const DT_HASH = 4;
58pub const DT_STRTAB = 5;
59pub const DT_SYMTAB = 6;
60pub const DT_RELA = 7;
61pub const DT_RELASZ = 8;
62pub const DT_RELAENT = 9;
63pub const DT_STRSZ = 10;
64pub const DT_SYMENT = 11;
65pub const DT_INIT = 12;
66pub const DT_FINI = 13;
67pub const DT_SONAME = 14;
68pub const DT_RPATH = 15;
69pub const DT_SYMBOLIC = 16;
70pub const DT_REL = 17;
71pub const DT_RELSZ = 18;
72pub const DT_RELENT = 19;
73pub const DT_PLTREL = 20;
74pub const DT_DEBUG = 21;
75pub const DT_TEXTREL = 22;
76pub const DT_JMPREL = 23;
77pub const DT_BIND_NOW = 24;
78pub const DT_INIT_ARRAY = 25;
79pub const DT_FINI_ARRAY = 26;
80pub const DT_INIT_ARRAYSZ = 27;
81pub const DT_FINI_ARRAYSZ = 28;
82pub const DT_RUNPATH = 29;
83pub const DT_FLAGS = 30;
84pub const DT_ENCODING = 32;
85pub const DT_PREINIT_ARRAY = 32;
86pub const DT_PREINIT_ARRAYSZ = 33;
87pub const DT_SYMTAB_SHNDX = 34;
88pub const DT_NUM = 35;
89pub const DT_LOOS = 0x6000000d;
90pub const DT_HIOS = 0x6ffff000;
91pub const DT_LOPROC = 0x70000000;
92pub const DT_HIPROC = 0x7fffffff;
93pub const DT_PROCNUM = DT_MIPS_NUM;
94
95pub const DT_VALRNGLO = 0x6ffffd00;
96pub const DT_GNU_PRELINKED = 0x6ffffdf5;
97pub const DT_GNU_CONFLICTSZ = 0x6ffffdf6;
98pub const DT_GNU_LIBLISTSZ = 0x6ffffdf7;
99pub const DT_CHECKSUM = 0x6ffffdf8;
100pub const DT_PLTPADSZ = 0x6ffffdf9;
101pub const DT_MOVEENT = 0x6ffffdfa;
102pub const DT_MOVESZ = 0x6ffffdfb;
103pub const DT_FEATURE_1 = 0x6ffffdfc;
104pub const DT_POSFLAG_1 = 0x6ffffdfd;
105
106pub const DT_SYMINSZ = 0x6ffffdfe;
107pub const DT_SYMINENT = 0x6ffffdff;
108pub const DT_VALRNGHI = 0x6ffffdff;
109pub const DT_VALNUM = 12;
110
111pub const DT_ADDRRNGLO = 0x6ffffe00;
112pub const DT_GNU_HASH = 0x6ffffef5;
113pub const DT_TLSDESC_PLT = 0x6ffffef6;
114pub const DT_TLSDESC_GOT = 0x6ffffef7;
115pub const DT_GNU_CONFLICT = 0x6ffffef8;
116pub const DT_GNU_LIBLIST = 0x6ffffef9;
117pub const DT_CONFIG = 0x6ffffefa;
118pub const DT_DEPAUDIT = 0x6ffffefb;
119pub const DT_AUDIT = 0x6ffffefc;
120pub const DT_PLTPAD = 0x6ffffefd;
121pub const DT_MOVETAB = 0x6ffffefe;
122pub const DT_SYMINFO = 0x6ffffeff;
123pub const DT_ADDRRNGHI = 0x6ffffeff;
124pub const DT_ADDRNUM = 11;
125
126
127pub const DT_VERSYM = 0x6ffffff0;
128
129pub const DT_RELACOUNT = 0x6ffffff9;
130pub const DT_RELCOUNT = 0x6ffffffa;
131
132
133pub const DT_FLAGS_1 = 0x6ffffffb;
134pub const DT_VERDEF = 0x6ffffffc;
135
136pub const DT_VERDEFNUM = 0x6ffffffd;
137pub const DT_VERNEED = 0x6ffffffe;
138
139pub const DT_VERNEEDNUM = 0x6fffffff;
140pub const DT_VERSIONTAGNUM = 16;
141
142
143
144pub const DT_AUXILIARY = 0x7ffffffd;
145pub const DT_FILTER = 0x7fffffff;
146pub const DT_EXTRANUM = 3;
147
148
149pub const DT_SPARC_REGISTER = 0x70000001;
150pub const DT_SPARC_NUM = 2;
151
152pub const DT_MIPS_RLD_VERSION = 0x70000001;
153pub const DT_MIPS_TIME_STAMP = 0x70000002;
154pub const DT_MIPS_ICHECKSUM = 0x70000003;
155pub const DT_MIPS_IVERSION = 0x70000004;
156pub const DT_MIPS_FLAGS = 0x70000005;
157pub const DT_MIPS_BASE_ADDRESS = 0x70000006;
158pub const DT_MIPS_MSYM = 0x70000007;
159pub const DT_MIPS_CONFLICT = 0x70000008;
160pub const DT_MIPS_LIBLIST = 0x70000009;
161pub const DT_MIPS_LOCAL_GOTNO = 0x7000000a;
162pub const DT_MIPS_CONFLICTNO = 0x7000000b;
163pub const DT_MIPS_LIBLISTNO = 0x70000010;
164pub const DT_MIPS_SYMTABNO = 0x70000011;
165pub const DT_MIPS_UNREFEXTNO = 0x70000012;
166pub const DT_MIPS_GOTSYM = 0x70000013;
167pub const DT_MIPS_HIPAGENO = 0x70000014;
168pub const DT_MIPS_RLD_MAP = 0x70000016;
169pub const DT_MIPS_DELTA_CLASS = 0x70000017;
170pub const DT_MIPS_DELTA_CLASS_NO = 0x70000018;
171
172pub const DT_MIPS_DELTA_INSTANCE = 0x70000019;
173pub const DT_MIPS_DELTA_INSTANCE_NO = 0x7000001a;
174
175pub const DT_MIPS_DELTA_RELOC = 0x7000001b;
176pub const DT_MIPS_DELTA_RELOC_NO = 0x7000001c;
177
178pub const DT_MIPS_DELTA_SYM = 0x7000001d;
179
180pub const DT_MIPS_DELTA_SYM_NO = 0x7000001e;
181
182pub const DT_MIPS_DELTA_CLASSSYM = 0x70000020;
183
184pub const DT_MIPS_DELTA_CLASSSYM_NO = 0x70000021;
185
186pub const DT_MIPS_CXX_FLAGS = 0x70000022;
187pub const DT_MIPS_PIXIE_INIT = 0x70000023;
188pub const DT_MIPS_SYMBOL_LIB = 0x70000024;
189pub const DT_MIPS_LOCALPAGE_GOTIDX = 0x70000025;
190pub const DT_MIPS_LOCAL_GOTIDX = 0x70000026;
191pub const DT_MIPS_HIDDEN_GOTIDX = 0x70000027;
192pub const DT_MIPS_PROTECTED_GOTIDX = 0x70000028;
193pub const DT_MIPS_OPTIONS = 0x70000029;
194pub const DT_MIPS_INTERFACE = 0x7000002a;
195pub const DT_MIPS_DYNSTR_ALIGN = 0x7000002b;
196pub const DT_MIPS_INTERFACE_SIZE = 0x7000002c;
197pub const DT_MIPS_RLD_TEXT_RESOLVE_ADDR = 0x7000002d;
198
199pub const DT_MIPS_PERF_SUFFIX = 0x7000002e;
200
201pub const DT_MIPS_COMPACT_SIZE = 0x7000002f;
202pub const DT_MIPS_GP_VALUE = 0x70000030;
203pub const DT_MIPS_AUX_DYNAMIC = 0x70000031;
204
205pub const DT_MIPS_PLTGOT = 0x70000032;
206
207pub const DT_MIPS_RWPLT = 0x70000034;
208pub const DT_MIPS_RLD_MAP_REL = 0x70000035;
209pub const DT_MIPS_NUM = 0x36;
210
211pub const DT_ALPHA_PLTRO = (DT_LOPROC + 0);
212pub const DT_ALPHA_NUM = 1;
213
214pub const DT_PPC_GOT = (DT_LOPROC + 0);
215pub const DT_PPC_OPT = (DT_LOPROC + 1);
216pub const DT_PPC_NUM = 2;
217
218pub const DT_PPC64_GLINK = (DT_LOPROC + 0);
219pub const DT_PPC64_OPD = (DT_LOPROC + 1);
220pub const DT_PPC64_OPDSZ = (DT_LOPROC + 2);
221pub const DT_PPC64_OPT = (DT_LOPROC + 3);
222pub const DT_PPC64_NUM = 4;
223
224pub const DT_IA_64_PLT_RESERVE = (DT_LOPROC + 0);
225pub const DT_IA_64_NUM = 1;
226
227pub const DT_NIOS2_GP = 0x70000002;
228
229pub const PT_NULL = 0;
230pub const PT_LOAD = 1;
231pub const PT_DYNAMIC = 2;
232pub const PT_INTERP = 3;
233pub const PT_NOTE = 4;
234pub const PT_SHLIB = 5;
235pub const PT_PHDR = 6;
236pub const PT_TLS = 7;
237pub const PT_NUM = 8;
238pub const PT_LOOS = 0x60000000;
239pub const PT_GNU_EH_FRAME = 0x6474e550;
240pub const PT_GNU_STACK = 0x6474e551;
241pub const PT_GNU_RELRO = 0x6474e552;
242pub const PT_LOSUNW = 0x6ffffffa;
243pub const PT_SUNWBSS = 0x6ffffffa;
244pub const PT_SUNWSTACK = 0x6ffffffb;
245pub const PT_HISUNW = 0x6fffffff;
246pub const PT_HIOS = 0x6fffffff;
247pub const PT_LOPROC = 0x70000000;
248pub const PT_HIPROC = 0x7fffffff;
249
10pub const SHT_NULL = 0;250pub const SHT_NULL = 0;
11pub const SHT_PROGBITS = 1;251pub const SHT_PROGBITS = 1;
12pub const SHT_SYMTAB = 2;252pub const SHT_SYMTAB = 2;
...@@ -31,6 +271,45 @@ pub const SHT_HIPROC = 0x7fffffff;...@@ -31,6 +271,45 @@ pub const SHT_HIPROC = 0x7fffffff;
31pub const SHT_LOUSER = 0x80000000;271pub const SHT_LOUSER = 0x80000000;
32pub const SHT_HIUSER = 0xffffffff;272pub const SHT_HIUSER = 0xffffffff;
33273
274pub const STB_LOCAL = 0;
275pub const STB_GLOBAL = 1;
276pub const STB_WEAK = 2;
277pub const STB_NUM = 3;
278pub const STB_LOOS = 10;
279pub const STB_GNU_UNIQUE = 10;
280pub const STB_HIOS = 12;
281pub const STB_LOPROC = 13;
282pub const STB_HIPROC = 15;
283
284pub const STB_MIPS_SPLIT_COMMON = 13;
285
286pub const STT_NOTYPE = 0;
287pub const STT_OBJECT = 1;
288pub const STT_FUNC = 2;
289pub const STT_SECTION = 3;
290pub const STT_FILE = 4;
291pub const STT_COMMON = 5;
292pub const STT_TLS = 6;
293pub const STT_NUM = 7;
294pub const STT_LOOS = 10;
295pub const STT_GNU_IFUNC = 10;
296pub const STT_HIOS = 12;
297pub const STT_LOPROC = 13;
298pub const STT_HIPROC = 15;
299
300pub const STT_SPARC_REGISTER = 13;
301
302pub const STT_PARISC_MILLICODE = 13;
303
304pub const STT_HP_OPAQUE = (STT_LOOS + 0x1);
305pub const STT_HP_STUB = (STT_LOOS + 0x2);
306
307pub const STT_ARM_TFUNC = STT_LOPROC;
308pub const STT_ARM_16BIT = STT_HIPROC;
309
310pub const VER_FLG_BASE = 0x1;
311pub const VER_FLG_WEAK = 0x2;
312
34pub const FileType = enum {313pub const FileType = enum {
35 Relocatable,314 Relocatable,
36 Executable,315 Executable,
...@@ -266,3 +545,335 @@ pub const Elf = struct {...@@ -266,3 +545,335 @@ pub const Elf = struct {
266 try elf.in_file.seekTo(elf_section.offset);545 try elf.in_file.seekTo(elf_section.offset);
267 }546 }
268};547};
548
549pub const EI_NIDENT = 16;
550pub const Elf32_Half = u16;
551pub const Elf64_Half = u16;
552pub const Elf32_Word = u32;
553pub const Elf32_Sword = i32;
554pub const Elf64_Word = u32;
555pub const Elf64_Sword = i32;
556pub const Elf32_Xword = u64;
557pub const Elf32_Sxword = i64;
558pub const Elf64_Xword = u64;
559pub const Elf64_Sxword = i64;
560pub const Elf32_Addr = u32;
561pub const Elf64_Addr = u64;
562pub const Elf32_Off = u32;
563pub const Elf64_Off = u64;
564pub const Elf32_Section = u16;
565pub const Elf64_Section = u16;
566pub const Elf32_Versym = Elf32_Half;
567pub const Elf64_Versym = Elf64_Half;
568pub const Elf32_Ehdr = extern struct {
569 e_ident: [EI_NIDENT]u8,
570 e_type: Elf32_Half,
571 e_machine: Elf32_Half,
572 e_version: Elf32_Word,
573 e_entry: Elf32_Addr,
574 e_phoff: Elf32_Off,
575 e_shoff: Elf32_Off,
576 e_flags: Elf32_Word,
577 e_ehsize: Elf32_Half,
578 e_phentsize: Elf32_Half,
579 e_phnum: Elf32_Half,
580 e_shentsize: Elf32_Half,
581 e_shnum: Elf32_Half,
582 e_shstrndx: Elf32_Half,
583};
584pub const Elf64_Ehdr = extern struct {
585 e_ident: [EI_NIDENT]u8,
586 e_type: Elf64_Half,
587 e_machine: Elf64_Half,
588 e_version: Elf64_Word,
589 e_entry: Elf64_Addr,
590 e_phoff: Elf64_Off,
591 e_shoff: Elf64_Off,
592 e_flags: Elf64_Word,
593 e_ehsize: Elf64_Half,
594 e_phentsize: Elf64_Half,
595 e_phnum: Elf64_Half,
596 e_shentsize: Elf64_Half,
597 e_shnum: Elf64_Half,
598 e_shstrndx: Elf64_Half,
599};
600pub const Elf32_Shdr = extern struct {
601 sh_name: Elf32_Word,
602 sh_type: Elf32_Word,
603 sh_flags: Elf32_Word,
604 sh_addr: Elf32_Addr,
605 sh_offset: Elf32_Off,
606 sh_size: Elf32_Word,
607 sh_link: Elf32_Word,
608 sh_info: Elf32_Word,
609 sh_addralign: Elf32_Word,
610 sh_entsize: Elf32_Word,
611};
612pub const Elf64_Shdr = extern struct {
613 sh_name: Elf64_Word,
614 sh_type: Elf64_Word,
615 sh_flags: Elf64_Xword,
616 sh_addr: Elf64_Addr,
617 sh_offset: Elf64_Off,
618 sh_size: Elf64_Xword,
619 sh_link: Elf64_Word,
620 sh_info: Elf64_Word,
621 sh_addralign: Elf64_Xword,
622 sh_entsize: Elf64_Xword,
623};
624pub const Elf32_Chdr = extern struct {
625 ch_type: Elf32_Word,
626 ch_size: Elf32_Word,
627 ch_addralign: Elf32_Word,
628};
629pub const Elf64_Chdr = extern struct {
630 ch_type: Elf64_Word,
631 ch_reserved: Elf64_Word,
632 ch_size: Elf64_Xword,
633 ch_addralign: Elf64_Xword,
634};
635pub const Elf32_Sym = extern struct {
636 st_name: Elf32_Word,
637 st_value: Elf32_Addr,
638 st_size: Elf32_Word,
639 st_info: u8,
640 st_other: u8,
641 st_shndx: Elf32_Section,
642};
643pub const Elf64_Sym = extern struct {
644 st_name: Elf64_Word,
645 st_info: u8,
646 st_other: u8,
647 st_shndx: Elf64_Section,
648 st_value: Elf64_Addr,
649 st_size: Elf64_Xword,
650};
651pub const Elf32_Syminfo = extern struct {
652 si_boundto: Elf32_Half,
653 si_flags: Elf32_Half,
654};
655pub const Elf64_Syminfo = extern struct {
656 si_boundto: Elf64_Half,
657 si_flags: Elf64_Half,
658};
659pub const Elf32_Rel = extern struct {
660 r_offset: Elf32_Addr,
661 r_info: Elf32_Word,
662};
663pub const Elf64_Rel = extern struct {
664 r_offset: Elf64_Addr,
665 r_info: Elf64_Xword,
666};
667pub const Elf32_Rela = extern struct {
668 r_offset: Elf32_Addr,
669 r_info: Elf32_Word,
670 r_addend: Elf32_Sword,
671};
672pub const Elf64_Rela = extern struct {
673 r_offset: Elf64_Addr,
674 r_info: Elf64_Xword,
675 r_addend: Elf64_Sxword,
676};
677pub const Elf32_Phdr = extern struct {
678 p_type: Elf32_Word,
679 p_offset: Elf32_Off,
680 p_vaddr: Elf32_Addr,
681 p_paddr: Elf32_Addr,
682 p_filesz: Elf32_Word,
683 p_memsz: Elf32_Word,
684 p_flags: Elf32_Word,
685 p_align: Elf32_Word,
686};
687pub const Elf64_Phdr = extern struct {
688 p_type: Elf64_Word,
689 p_flags: Elf64_Word,
690 p_offset: Elf64_Off,
691 p_vaddr: Elf64_Addr,
692 p_paddr: Elf64_Addr,
693 p_filesz: Elf64_Xword,
694 p_memsz: Elf64_Xword,
695 p_align: Elf64_Xword,
696};
697pub const Elf32_Dyn = extern struct {
698 d_tag: Elf32_Sword,
699 d_un: extern union {
700 d_val: Elf32_Word,
701 d_ptr: Elf32_Addr,
702 },
703};
704pub const Elf64_Dyn = extern struct {
705 d_tag: Elf64_Sxword,
706 d_un: extern union {
707 d_val: Elf64_Xword,
708 d_ptr: Elf64_Addr,
709 },
710};
711pub const Elf32_Verdef = extern struct {
712 vd_version: Elf32_Half,
713 vd_flags: Elf32_Half,
714 vd_ndx: Elf32_Half,
715 vd_cnt: Elf32_Half,
716 vd_hash: Elf32_Word,
717 vd_aux: Elf32_Word,
718 vd_next: Elf32_Word,
719};
720pub const Elf64_Verdef = extern struct {
721 vd_version: Elf64_Half,
722 vd_flags: Elf64_Half,
723 vd_ndx: Elf64_Half,
724 vd_cnt: Elf64_Half,
725 vd_hash: Elf64_Word,
726 vd_aux: Elf64_Word,
727 vd_next: Elf64_Word,
728};
729pub const Elf32_Verdaux = extern struct {
730 vda_name: Elf32_Word,
731 vda_next: Elf32_Word,
732};
733pub const Elf64_Verdaux = extern struct {
734 vda_name: Elf64_Word,
735 vda_next: Elf64_Word,
736};
737pub const Elf32_Verneed = extern struct {
738 vn_version: Elf32_Half,
739 vn_cnt: Elf32_Half,
740 vn_file: Elf32_Word,
741 vn_aux: Elf32_Word,
742 vn_next: Elf32_Word,
743};
744pub const Elf64_Verneed = extern struct {
745 vn_version: Elf64_Half,
746 vn_cnt: Elf64_Half,
747 vn_file: Elf64_Word,
748 vn_aux: Elf64_Word,
749 vn_next: Elf64_Word,
750};
751pub const Elf32_Vernaux = extern struct {
752 vna_hash: Elf32_Word,
753 vna_flags: Elf32_Half,
754 vna_other: Elf32_Half,
755 vna_name: Elf32_Word,
756 vna_next: Elf32_Word,
757};
758pub const Elf64_Vernaux = extern struct {
759 vna_hash: Elf64_Word,
760 vna_flags: Elf64_Half,
761 vna_other: Elf64_Half,
762 vna_name: Elf64_Word,
763 vna_next: Elf64_Word,
764};
765pub const Elf32_auxv_t = extern struct {
766 a_type: u32,
767 a_un: extern union {
768 a_val: u32,
769 },
770};
771pub const Elf64_auxv_t = extern struct {
772 a_type: u64,
773 a_un: extern union {
774 a_val: u64,
775 },
776};
777pub const Elf32_Nhdr = extern struct {
778 n_namesz: Elf32_Word,
779 n_descsz: Elf32_Word,
780 n_type: Elf32_Word,
781};
782pub const Elf64_Nhdr = extern struct {
783 n_namesz: Elf64_Word,
784 n_descsz: Elf64_Word,
785 n_type: Elf64_Word,
786};
787pub const Elf32_Move = extern struct {
788 m_value: Elf32_Xword,
789 m_info: Elf32_Word,
790 m_poffset: Elf32_Word,
791 m_repeat: Elf32_Half,
792 m_stride: Elf32_Half,
793};
794pub const Elf64_Move = extern struct {
795 m_value: Elf64_Xword,
796 m_info: Elf64_Xword,
797 m_poffset: Elf64_Xword,
798 m_repeat: Elf64_Half,
799 m_stride: Elf64_Half,
800};
801pub const Elf32_gptab = extern union {
802 gt_header: extern struct {
803 gt_current_g_value: Elf32_Word,
804 gt_unused: Elf32_Word,
805 },
806 gt_entry: extern struct {
807 gt_g_value: Elf32_Word,
808 gt_bytes: Elf32_Word,
809 },
810};
811pub const Elf32_RegInfo = extern struct {
812 ri_gprmask: Elf32_Word,
813 ri_cprmask: [4]Elf32_Word,
814 ri_gp_value: Elf32_Sword,
815};
816pub const Elf_Options = extern struct {
817 kind: u8,
818 size: u8,
819 @"section": Elf32_Section,
820 info: Elf32_Word,
821};
822pub const Elf_Options_Hw = extern struct {
823 hwp_flags1: Elf32_Word,
824 hwp_flags2: Elf32_Word,
825};
826pub const Elf32_Lib = extern struct {
827 l_name: Elf32_Word,
828 l_time_stamp: Elf32_Word,
829 l_checksum: Elf32_Word,
830 l_version: Elf32_Word,
831 l_flags: Elf32_Word,
832};
833pub const Elf64_Lib = extern struct {
834 l_name: Elf64_Word,
835 l_time_stamp: Elf64_Word,
836 l_checksum: Elf64_Word,
837 l_version: Elf64_Word,
838 l_flags: Elf64_Word,
839};
840pub const Elf32_Conflict = Elf32_Addr;
841pub const Elf_MIPS_ABIFlags_v0 = extern struct {
842 version: Elf32_Half,
843 isa_level: u8,
844 isa_rev: u8,
845 gpr_size: u8,
846 cpr1_size: u8,
847 cpr2_size: u8,
848 fp_abi: u8,
849 isa_ext: Elf32_Word,
850 ases: Elf32_Word,
851 flags1: Elf32_Word,
852 flags2: Elf32_Word,
853};
854
855pub const Ehdr = switch(@sizeOf(usize)) {
856 4 => Elf32_Ehdr,
857 8 => Elf64_Ehdr,
858 else => @compileError("expected pointer size of 32 or 64"),
859};
860pub const Phdr = switch(@sizeOf(usize)) {
861 4 => Elf32_Phdr,
862 8 => Elf64_Phdr,
863 else => @compileError("expected pointer size of 32 or 64"),
864};
865pub const Sym = switch(@sizeOf(usize)) {
866 4 => Elf32_Sym,
867 8 => Elf64_Sym,
868 else => @compileError("expected pointer size of 32 or 64"),
869};
870pub const Verdef = switch(@sizeOf(usize)) {
871 4 => Elf32_Verdef,
872 8 => Elf64_Verdef,
873 else => @compileError("expected pointer size of 32 or 64"),
874};
875pub const Verdaux = switch(@sizeOf(usize)) {
876 4 => Elf32_Verdaux,
877 8 => Elf64_Verdaux,
878 else => @compileError("expected pointer size of 32 or 64"),
879};
std/endian.zig deleted-25
...@@ -1,25 +0,0 @@
1const mem = @import("mem.zig");
2const builtin = @import("builtin");
3
4pub fn swapIfLe(comptime T: type, x: T) T {
5 return swapIf(builtin.Endian.Little, T, x);
6}
7
8pub fn swapIfBe(comptime T: type, x: T) T {
9 return swapIf(builtin.Endian.Big, T, x);
10}
11
12pub fn swapIf(endian: builtin.Endian, comptime T: type, x: T) T {
13 return if (builtin.endian == endian) swap(T, x) else x;
14}
15
16pub fn swap(comptime T: type, x: T) T {
17 var buf: [@sizeOf(T)]u8 = undefined;
18 mem.writeInt(buf[0..], x, builtin.Endian.Little);
19 return mem.readInt(buf, T, builtin.Endian.Big);
20}
21
22test "swap" {
23 const debug = @import("debug/index.zig");
24 debug.assert(swap(u32, 0xDEADBEEF) == 0xEFBEADDE);
25}
std/event.zig created+235
...@@ -0,0 +1,235 @@
1const std = @import("index.zig");
2const builtin = @import("builtin");
3const assert = std.debug.assert;
4const event = this;
5const mem = std.mem;
6const posix = std.os.posix;
7
8pub const TcpServer = struct {
9 handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File) void,
10
11 loop: &Loop,
12 sockfd: i32,
13 accept_coro: ?promise,
14 listen_address: std.net.Address,
15
16 waiting_for_emfile_node: PromiseNode,
17
18 const PromiseNode = std.LinkedList(promise).Node;
19
20 pub fn init(loop: &Loop) !TcpServer {
21 const sockfd = try std.os.posixSocket(posix.AF_INET,
22 posix.SOCK_STREAM|posix.SOCK_CLOEXEC|posix.SOCK_NONBLOCK,
23 posix.PROTO_tcp);
24 errdefer std.os.close(sockfd);
25
26 // TODO can't initialize handler coroutine here because we need well defined copy elision
27 return TcpServer {
28 .loop = loop,
29 .sockfd = sockfd,
30 .accept_coro = null,
31 .handleRequestFn = undefined,
32 .waiting_for_emfile_node = undefined,
33 .listen_address = undefined,
34 };
35 }
36
37 pub fn listen(self: &TcpServer, address: &const std.net.Address,
38 handleRequestFn: async<&mem.Allocator> fn (&TcpServer, &const std.net.Address, &const std.os.File)void) !void
39 {
40 self.handleRequestFn = handleRequestFn;
41
42 try std.os.posixBind(self.sockfd, &address.os_addr);
43 try std.os.posixListen(self.sockfd, posix.SOMAXCONN);
44 self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd));
45
46 self.accept_coro = try async<self.loop.allocator> TcpServer.handler(self);
47 errdefer cancel ??self.accept_coro;
48
49 try self.loop.addFd(self.sockfd, ??self.accept_coro);
50 errdefer self.loop.removeFd(self.sockfd);
51
52 }
53
54 pub fn deinit(self: &TcpServer) void {
55 self.loop.removeFd(self.sockfd);
56 if (self.accept_coro) |accept_coro| cancel accept_coro;
57 std.os.close(self.sockfd);
58 }
59
60 pub async fn handler(self: &TcpServer) void {
61 while (true) {
62 var accepted_addr: std.net.Address = undefined;
63 if (std.os.posixAccept(self.sockfd, &accepted_addr.os_addr,
64 posix.SOCK_NONBLOCK | posix.SOCK_CLOEXEC)) |accepted_fd|
65 {
66 var socket = std.os.File.openHandle(accepted_fd);
67 _ = async<self.loop.allocator> self.handleRequestFn(self, accepted_addr, socket) catch |err| switch (err) {
68 error.OutOfMemory => {
69 socket.close();
70 continue;
71 },
72 };
73 } else |err| switch (err) {
74 error.WouldBlock => {
75 suspend; // we will get resumed by epoll_wait in the event loop
76 continue;
77 },
78 error.ProcessFdQuotaExceeded => {
79 errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);
80 suspend |p| {
81 self.waiting_for_emfile_node = PromiseNode.init(p);
82 std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
83 }
84 continue;
85 },
86 error.ConnectionAborted,
87 error.FileDescriptorClosed => continue,
88
89 error.PageFault => unreachable,
90 error.InvalidSyscall => unreachable,
91 error.FileDescriptorNotASocket => unreachable,
92 error.OperationNotSupported => unreachable,
93
94 error.SystemFdQuotaExceeded,
95 error.SystemResources,
96 error.ProtocolFailure,
97 error.BlockedByFirewall,
98 error.Unexpected => {
99 @panic("TODO handle this error");
100 },
101 }
102 }
103 }
104};
105
106pub const Loop = struct {
107 allocator: &mem.Allocator,
108 epollfd: i32,
109 keep_running: bool,
110
111 fn init(allocator: &mem.Allocator) !Loop {
112 const epollfd = try std.os.linuxEpollCreate(std.os.linux.EPOLL_CLOEXEC);
113 return Loop {
114 .keep_running = true,
115 .allocator = allocator,
116 .epollfd = epollfd,
117 };
118 }
119
120 pub fn addFd(self: &Loop, fd: i32, prom: promise) !void {
121 var ev = std.os.linux.epoll_event {
122 .events = std.os.linux.EPOLLIN|std.os.linux.EPOLLOUT|std.os.linux.EPOLLET,
123 .data = std.os.linux.epoll_data {
124 .ptr = @ptrToInt(prom),
125 },
126 };
127 try std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_ADD, fd, &ev);
128 }
129
130 pub fn removeFd(self: &Loop, fd: i32) void {
131 std.os.linuxEpollCtl(self.epollfd, std.os.linux.EPOLL_CTL_DEL, fd, undefined) catch {};
132 }
133
134 async fn waitFd(self: &Loop, fd: i32) !void {
135 defer self.removeFd(fd);
136 suspend |p| {
137 try self.addFd(fd, p);
138 }
139 }
140
141 pub fn stop(self: &Loop) void {
142 // TODO make atomic
143 self.keep_running = false;
144 // TODO activate an fd in the epoll set
145 }
146
147 pub fn run(self: &Loop) void {
148 while (self.keep_running) {
149 var events: [16]std.os.linux.epoll_event = undefined;
150 const count = std.os.linuxEpollWait(self.epollfd, events[0..], -1);
151 for (events[0..count]) |ev| {
152 const p = @intToPtr(promise, ev.data.ptr);
153 resume p;
154 }
155 }
156 }
157};
158
159pub async fn connect(loop: &Loop, _address: &const std.net.Address) !std.os.File {
160 var address = *_address; // TODO https://github.com/zig-lang/zig/issues/733
161
162 const sockfd = try std.os.posixSocket(posix.AF_INET, posix.SOCK_STREAM|posix.SOCK_CLOEXEC|posix.SOCK_NONBLOCK, posix.PROTO_tcp);
163 errdefer std.os.close(sockfd);
164
165 try std.os.posixConnectAsync(sockfd, &address.os_addr);
166 try await try async loop.waitFd(sockfd);
167 try std.os.posixGetSockOptConnectError(sockfd);
168
169 return std.os.File.openHandle(sockfd);
170}
171
172test "listen on a port, send bytes, receive bytes" {
173 if (builtin.os != builtin.Os.linux) {
174 // TODO build abstractions for other operating systems
175 return;
176 }
177 const MyServer = struct {
178 tcp_server: TcpServer,
179
180 const Self = this;
181
182 async<&mem.Allocator> fn handler(tcp_server: &TcpServer, _addr: &const std.net.Address,
183 _socket: &const std.os.File) void
184 {
185 const self = @fieldParentPtr(Self, "tcp_server", tcp_server);
186 var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733
187 defer socket.close();
188 const next_handler = async errorableHandler(self, _addr, socket) catch |err| switch (err) {
189 error.OutOfMemory => @panic("unable to handle connection: out of memory"),
190 };
191 (await next_handler) catch |err| {
192 std.debug.panic("unable to handle connection: {}\n", err);
193 };
194 suspend |p| { cancel p; }
195 }
196
197 async fn errorableHandler(self: &Self, _addr: &const std.net.Address,
198 _socket: &const std.os.File) !void
199 {
200 const addr = *_addr; // TODO https://github.com/zig-lang/zig/issues/733
201 var socket = *_socket; // TODO https://github.com/zig-lang/zig/issues/733
202
203 var adapter = std.io.FileOutStream.init(&socket);
204 var stream = &adapter.stream;
205 try stream.print("hello from server\n");
206 }
207 };
208
209 const ip4addr = std.net.parseIp4("127.0.0.1") catch unreachable;
210 const addr = std.net.Address.initIp4(ip4addr, 0);
211
212 var loop = try Loop.init(std.debug.global_allocator);
213 var server = MyServer {
214 .tcp_server = try TcpServer.init(&loop),
215 };
216 defer server.tcp_server.deinit();
217 try server.tcp_server.listen(addr, MyServer.handler);
218
219 const p = try async<std.debug.global_allocator> doAsyncTest(&loop, server.tcp_server.listen_address);
220 defer cancel p;
221 loop.run();
222}
223
224async fn doAsyncTest(loop: &Loop, address: &const std.net.Address) void {
225 errdefer @panic("test failure");
226
227 var socket_file = try await try async event.connect(loop, address);
228 defer socket_file.close();
229
230 var buf: [512]u8 = undefined;
231 const amt_read = try socket_file.read(buf[0..]);
232 const msg = buf[0..amt_read];
233 assert(mem.eql(u8, msg, "hello from server\n"));
234 loop.stop();
235}
std/fmt/errol/index.zig+76-3
...@@ -12,13 +12,79 @@ pub const FloatDecimal = struct {...@@ -12,13 +12,79 @@ pub const FloatDecimal = struct {
12 exp: i32,12 exp: i32,
13};13};
1414
15pub const RoundMode = enum {
16 // Round only the fractional portion (e.g. 1234.23 has precision 2)
17 Decimal,
18 // Round the entire whole/fractional portion (e.g. 1.23423e3 has precision 5)
19 Scientific,
20};
21
22/// Round a FloatDecimal as returned by errol3 to the specified fractional precision.
23/// All digits after the specified precision should be considered invalid.
24pub fn roundToPrecision(float_decimal: &FloatDecimal, precision: usize, mode: RoundMode) void {
25 // The round digit refers to the index which we should look at to determine
26 // whether we need to round to match the specified precision.
27 var round_digit: usize = 0;
28
29 switch (mode) {
30 RoundMode.Decimal => {
31 if (float_decimal.exp >= 0) {
32 round_digit = precision + usize(float_decimal.exp);
33 } else {
34 // if a small negative exp, then adjust we need to offset by the number
35 // of leading zeros that will occur.
36 const min_exp_required = usize(-float_decimal.exp);
37 if (precision > min_exp_required) {
38 round_digit = precision - min_exp_required;
39 }
40 }
41 },
42 RoundMode.Scientific => {
43 round_digit = 1 + precision;
44 },
45 }
46
47 // It suffices to look at just this digit. We don't round and propagate say 0.04999 to 0.05
48 // first, and then to 0.1 in the case of a {.1} single precision.
49
50 // Find the digit which will signify the round point and start rounding backwards.
51 if (round_digit < float_decimal.digits.len and float_decimal.digits[round_digit] - '0' >= 5) {
52 assert(round_digit >= 0);
53
54 var i = round_digit;
55 while (true) {
56 if (i == 0) {
57 // Rounded all the way past the start. This was of the form 9.999...
58 // Slot the new digit in place and increase the exponent.
59 float_decimal.exp += 1;
60
61 // Re-size the buffer to use the reserved leading byte.
62 const one_before = @intToPtr(&u8, @ptrToInt(&float_decimal.digits[0]) - 1);
63 float_decimal.digits = one_before[0..float_decimal.digits.len + 1];
64 float_decimal.digits[0] = '1';
65 return;
66 }
67
68 i -= 1;
69
70 const new_value = (float_decimal.digits[i] - '0' + 1) % 10;
71 float_decimal.digits[i] = new_value + '0';
72
73 // must continue rounding until non-9
74 if (new_value != 0) {
75 return;
76 }
77 }
78 }
79}
80
15/// Corrected Errol3 double to ASCII conversion.81/// Corrected Errol3 double to ASCII conversion.
16pub fn errol3(value: f64, buffer: []u8) FloatDecimal {82pub fn errol3(value: f64, buffer: []u8) FloatDecimal {
17 const bits = @bitCast(u64, value);83 const bits = @bitCast(u64, value);
18 const i = tableLowerBound(bits);84 const i = tableLowerBound(bits);
19 if (i < enum3.len and enum3[i] == bits) {85 if (i < enum3.len and enum3[i] == bits) {
20 const data = enum3_data[i];86 const data = enum3_data[i];
21 const digits = buffer[0..data.str.len];87 const digits = buffer[1..data.str.len + 1];
22 mem.copy(u8, digits, data.str);88 mem.copy(u8, digits, data.str);
23 return FloatDecimal {89 return FloatDecimal {
24 .digits = digits,90 .digits = digits,
...@@ -98,7 +164,11 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {...@@ -98,7 +164,11 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {
98 }164 }
99165
100 // digit generation166 // digit generation
101 var buf_index: usize = 0;167
168 // We generate digits starting at index 1. If rounding a buffer later then it may be
169 // required to generate a preceeding digit in some cases (9.999) in which case we use
170 // the 0-index for this extra digit.
171 var buf_index: usize = 1;
102 while (true) {172 while (true) {
103 var hdig = u8(math.floor(high.val));173 var hdig = u8(math.floor(high.val));
104 if ((high.val == f64(hdig)) and (high.off < 0))174 if ((high.val == f64(hdig)) and (high.off < 0))
...@@ -128,7 +198,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {...@@ -128,7 +198,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal {
128 buf_index += 1;198 buf_index += 1;
129199
130 return FloatDecimal {200 return FloatDecimal {
131 .digits = buffer[0..buf_index],201 .digits = buffer[1..buf_index],
132 .exp = exp,202 .exp = exp,
133 };203 };
134}204}
...@@ -189,6 +259,9 @@ fn gethi(in: f64) f64 {...@@ -189,6 +259,9 @@ fn gethi(in: f64) f64 {
189/// Normalize the number by factoring in the error.259/// Normalize the number by factoring in the error.
190/// @hp: The float pair.260/// @hp: The float pair.
191fn hpNormalize(hp: &HP) void {261fn hpNormalize(hp: &HP) void {
262 // Required to avoid segfaults causing buffer overrun during errol3 digit output termination.
263 @setFloatMode(this, @import("builtin").FloatMode.Strict);
264
192 const val = hp.val;265 const val = hp.val;
193266
194 hp.val += hp.off;267 hp.val += hp.off;
std/fmt/index.zig+448-122
...@@ -4,29 +4,31 @@ const debug = std.debug;...@@ -4,29 +4,31 @@ const debug = std.debug;
4const assert = debug.assert;4const assert = debug.assert;
5const mem = std.mem;5const mem = std.mem;
6const builtin = @import("builtin");6const builtin = @import("builtin");
7const errol3 = @import("errol/index.zig").errol3;7const errol = @import("errol/index.zig");
88
9const max_int_digits = 65;9const max_int_digits = 65;
1010
11const State = enum { // TODO put inside format function and make sure the name and debug info is correct
12 Start,
13 OpenBrace,
14 CloseBrace,
15 Integer,
16 IntegerWidth,
17 Float,
18 FloatWidth,
19 Character,
20 Buf,
21 BufWidth,
22};
23
24/// Renders fmt string with args, calling output with slices of bytes.11/// Renders fmt string with args, calling output with slices of bytes.
25/// If `output` returns an error, the error is returned from `format` and12/// If `output` returns an error, the error is returned from `format` and
26/// `output` is not called again.13/// `output` is not called again.
27pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void,14pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void,
28 comptime fmt: []const u8, args: ...) Errors!void15 comptime fmt: []const u8, args: ...) Errors!void
29{16{
17 const State = enum {
18 Start,
19 OpenBrace,
20 CloseBrace,
21 Integer,
22 IntegerWidth,
23 Float,
24 FloatWidth,
25 FloatScientific,
26 FloatScientificWidth,
27 Character,
28 Buf,
29 BufWidth,
30 };
31
30 comptime var start_index = 0;32 comptime var start_index = 0;
31 comptime var state = State.Start;33 comptime var state = State.Start;
32 comptime var next_arg = 0;34 comptime var next_arg = 0;
...@@ -86,7 +88,11 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),...@@ -86,7 +88,11 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
86 },88 },
87 's' => {89 's' => {
88 state = State.Buf;90 state = State.Buf;
89 },'.' => {91 },
92 'e' => {
93 state = State.FloatScientific;
94 },
95 '.' => {
90 state = State.Float;96 state = State.Float;
91 },97 },
92 else => @compileError("Unknown format character: " ++ []u8{c}),98 else => @compileError("Unknown format character: " ++ []u8{c}),
...@@ -132,9 +138,33 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),...@@ -132,9 +138,33 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
132 '0' ... '9' => {},138 '0' ... '9' => {},
133 else => @compileError("Unexpected character in format string: " ++ []u8{c}),139 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
134 },140 },
141 State.FloatScientific => switch (c) {
142 '}' => {
143 try formatFloatScientific(args[next_arg], null, context, Errors, output);
144 next_arg += 1;
145 state = State.Start;
146 start_index = i + 1;
147 },
148 '0' ... '9' => {
149 width_start = i;
150 state = State.FloatScientificWidth;
151 },
152 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
153 },
154 State.FloatScientificWidth => switch (c) {
155 '}' => {
156 width = comptime (parseUnsigned(usize, fmt[width_start..i], 10) catch unreachable);
157 try formatFloatScientific(args[next_arg], width, context, Errors, output);
158 next_arg += 1;
159 state = State.Start;
160 start_index = i + 1;
161 },
162 '0' ... '9' => {},
163 else => @compileError("Unexpected character in format string: " ++ []u8{c}),
164 },
135 State.Float => switch (c) {165 State.Float => switch (c) {
136 '}' => {166 '}' => {
137 try formatFloatDecimal(args[next_arg], 0, context, Errors, output);167 try formatFloatDecimal(args[next_arg], null, context, Errors, output);
138 next_arg += 1;168 next_arg += 1;
139 state = State.Start;169 state = State.Start;
140 start_index = i + 1;170 start_index = i + 1;
...@@ -198,7 +228,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@...@@ -198,7 +228,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@
198 return formatInt(value, 10, false, 0, context, Errors, output);228 return formatInt(value, 10, false, 0, context, Errors, output);
199 },229 },
200 builtin.TypeId.Float => {230 builtin.TypeId.Float => {
201 return formatFloat(value, context, Errors, output);231 return formatFloatScientific(value, null, context, Errors, output);
202 },232 },
203 builtin.TypeId.Void => {233 builtin.TypeId.Void => {
204 return output(context, "void");234 return output(context, "void");
...@@ -256,81 +286,237 @@ pub fn formatBuf(buf: []const u8, width: usize,...@@ -256,81 +286,237 @@ pub fn formatBuf(buf: []const u8, width: usize,
256 }286 }
257}287}
258288
259pub fn formatFloat(value: var, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {289// Print a float in scientific notation to the specified precision. Null uses full precision.
290// It should be the case that every full precision, printed value can be re-parsed back to the
291// same type unambiguously.
292pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
260 var x = f64(value);293 var x = f64(value);
261294
262 // Errol doesn't handle these special cases.295 // Errol doesn't handle these special cases.
263 if (math.isNan(x)) {
264 return output(context, "NaN");
265 }
266 if (math.signbit(x)) {296 if (math.signbit(x)) {
267 try output(context, "-");297 try output(context, "-");
268 x = -x;298 x = -x;
269 }299 }
300
301 if (math.isNan(x)) {
302 return output(context, "nan");
303 }
270 if (math.isPositiveInf(x)) {304 if (math.isPositiveInf(x)) {
271 return output(context, "Infinity");305 return output(context, "inf");
272 }306 }
273 if (x == 0.0) {307 if (x == 0.0) {
274 return output(context, "0.0");308 try output(context, "0");
309
310 if (maybe_precision) |precision| {
311 if (precision != 0) {
312 try output(context, ".");
313 var i: usize = 0;
314 while (i < precision) : (i += 1) {
315 try output(context, "0");
316 }
317 }
318 } else {
319 try output(context, ".0");
320 }
321
322 try output(context, "e+00");
323 return;
275 }324 }
276325
277 var buffer: [32]u8 = undefined;326 var buffer: [32]u8 = undefined;
278 const float_decimal = errol3(x, buffer[0..]);327 var float_decimal = errol.errol3(x, buffer[0..]);
279 try output(context, float_decimal.digits[0..1]);328
280 try output(context, ".");329 if (maybe_precision) |precision| {
281 if (float_decimal.digits.len > 1) {330 errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific);
282 const num_digits = if (@typeOf(value) == f32)331
283 math.min(usize(9), float_decimal.digits.len)332 try output(context, float_decimal.digits[0..1]);
284 else333
285 float_decimal.digits.len;334 // {e0} case prints no `.`
286 try output(context, float_decimal.digits[1 .. num_digits]);335 if (precision != 0) {
336 try output(context, ".");
337
338 var printed: usize = 0;
339 if (float_decimal.digits.len > 1) {
340 const num_digits = math.min(float_decimal.digits.len, precision + 1);
341 try output(context, float_decimal.digits[1 .. num_digits]);
342 printed += num_digits - 1;
343 }
344
345 while (printed < precision) : (printed += 1) {
346 try output(context, "0");
347 }
348 }
287 } else {349 } else {
288 try output(context, "0");350 try output(context, float_decimal.digits[0..1]);
351 try output(context, ".");
352 if (float_decimal.digits.len > 1) {
353 const num_digits = if (@typeOf(value) == f32)
354 math.min(usize(9), float_decimal.digits.len)
355 else
356 float_decimal.digits.len;
357
358 try output(context, float_decimal.digits[1 .. num_digits]);
359 } else {
360 try output(context, "0");
361 }
289 }362 }
290363
291 if (float_decimal.exp != 1) {364 try output(context, "e");
292 try output(context, "e");365 const exp = float_decimal.exp - 1;
293 try formatInt(float_decimal.exp - 1, 10, false, 0, context, Errors, output);366
367 if (exp >= 0) {
368 try output(context, "+");
369 if (exp > -10 and exp < 10) {
370 try output(context, "0");
371 }
372 try formatInt(exp, 10, false, 0, context, Errors, output);
373 } else {
374 try output(context, "-");
375 if (exp > -10 and exp < 10) {
376 try output(context, "0");
377 }
378 try formatInt(-exp, 10, false, 0, context, Errors, output);
294 }379 }
295}380}
296381
297pub fn formatFloatDecimal(value: var, precision: usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {382// Print a float of the format x.yyyyy where the number of y is specified by the precision argument.
383// By default floats are printed at full precision (no rounding).
384pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void {
298 var x = f64(value);385 var x = f64(value);
299386
300 // Errol doesn't handle these special cases.387 // Errol doesn't handle these special cases.
301 if (math.isNan(x)) {
302 return output(context, "NaN");
303 }
304 if (math.signbit(x)) {388 if (math.signbit(x)) {
305 try output(context, "-");389 try output(context, "-");
306 x = -x;390 x = -x;
307 }391 }
392
393 if (math.isNan(x)) {
394 return output(context, "nan");
395 }
308 if (math.isPositiveInf(x)) {396 if (math.isPositiveInf(x)) {
309 return output(context, "Infinity");397 return output(context, "inf");
310 }398 }
311 if (x == 0.0) {399 if (x == 0.0) {
312 return output(context, "0.0");400 try output(context, "0");
401
402 if (maybe_precision) |precision| {
403 if (precision != 0) {
404 try output(context, ".");
405 var i: usize = 0;
406 while (i < precision) : (i += 1) {
407 try output(context, "0");
408 }
409 } else {
410 try output(context, ".0");
411 }
412 } else {
413 try output(context, "0");
414 }
415
416 return;
313 }417 }
314418
419 // non-special case, use errol3
315 var buffer: [32]u8 = undefined;420 var buffer: [32]u8 = undefined;
316 const float_decimal = errol3(x, buffer[0..]);421 var float_decimal = errol.errol3(x, buffer[0..]);
317422
318 const num_left_digits = if (float_decimal.exp > 0) usize(float_decimal.exp) else 1;423 if (maybe_precision) |precision| {
319424 errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal);
320 try output(context, float_decimal.digits[0 .. num_left_digits]);425
321 try output(context, ".");426 // exp < 0 means the leading is always 0 as errol result is normalized.
322 if (float_decimal.digits.len > 1) {427 var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0;
323 const num_valid_digtis = if (@typeOf(value) == f32) math.min(usize(7), float_decimal.digits.len)428
324 else429 // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this.
325 float_decimal.digits.len;430 var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len);
326431
327 const num_right_digits = if (precision != 0)432 if (num_digits_whole > 0) {
328 math.min(precision, (num_valid_digtis-num_left_digits))433 // We may have to zero pad, for instance 1e4 requires zero padding.
329 else434 try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]);
330 num_valid_digtis - num_left_digits;435
331 try output(context, float_decimal.digits[num_left_digits .. (num_left_digits + num_right_digits)]);436 var i = num_digits_whole_no_pad;
437 while (i < num_digits_whole) : (i += 1) {
438 try output(context, "0");
439 }
440 } else {
441 try output(context , "0");
442 }
443
444 // {.0} special case doesn't want a trailing '.'
445 if (precision == 0) {
446 return;
447 }
448
449 try output(context, ".");
450
451 // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's.
452 var printed: usize = 0;
453
454 // Zero-fill until we reach significant digits or run out of precision.
455 if (float_decimal.exp <= 0) {
456 const zero_digit_count = usize(-float_decimal.exp);
457 const zeros_to_print = math.min(zero_digit_count, precision);
458
459 var i: usize = 0;
460 while (i < zeros_to_print) : (i += 1) {
461 try output(context, "0");
462 printed += 1;
463 }
464
465 if (printed >= precision) {
466 return;
467 }
468 }
469
470 // Remaining fractional portion, zero-padding if insufficient.
471 debug.assert(precision >= printed);
472 if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) {
473 try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]);
474 return;
475 } else {
476 try output(context, float_decimal.digits[num_digits_whole_no_pad ..]);
477 printed += float_decimal.digits.len - num_digits_whole_no_pad;
478
479 while (printed < precision) : (printed += 1) {
480 try output(context, "0");
481 }
482 }
332 } else {483 } else {
333 try output(context, "0");484 // exp < 0 means the leading is always 0 as errol result is normalized.
485 var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0;
486
487 // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this.
488 var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len);
489
490 if (num_digits_whole > 0) {
491 // We may have to zero pad, for instance 1e4 requires zero padding.
492 try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]);
493
494 var i = num_digits_whole_no_pad;
495 while (i < num_digits_whole) : (i += 1) {
496 try output(context, "0");
497 }
498 } else {
499 try output(context , "0");
500 }
501
502 // Omit `.` if no fractional portion
503 if (float_decimal.exp >= 0 and num_digits_whole_no_pad == float_decimal.digits.len) {
504 return;
505 }
506
507 try output(context, ".");
508
509 // Zero-fill until we reach significant digits or run out of precision.
510 if (float_decimal.exp < 0) {
511 const zero_digit_count = usize(-float_decimal.exp);
512
513 var i: usize = 0;
514 while (i < zero_digit_count) : (i += 1) {
515 try output(context, "0");
516 }
517 }
518
519 try output(context, float_decimal.digits[num_digits_whole_no_pad ..]);
334 }520 }
335}521}
336522
...@@ -465,7 +651,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned...@@ -465,7 +651,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned
465 return x;651 return x;
466}652}
467653
468fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {654pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) {
469 const value = switch (c) {655 const value = switch (c) {
470 '0' ... '9' => c - '0',656 '0' ... '9' => c - '0',
471 'A' ... 'Z' => c - 'A' + 10,657 'A' ... 'Z' => c - 'A' + 10,
...@@ -593,70 +779,210 @@ test "fmt.format" {...@@ -593,70 +779,210 @@ test "fmt.format" {
593 const result = try bufPrint(buf1[0..], "pointer: {}\n", &value);779 const result = try bufPrint(buf1[0..], "pointer: {}\n", &value);
594 assert(mem.startsWith(u8, result, "pointer: Struct@"));780 assert(mem.startsWith(u8, result, "pointer: Struct@"));
595 }781 }
596782 {
597 // TODO get these tests passing in release modes783 var buf1: [32]u8 = undefined;
598 // https://github.com/zig-lang/zig/issues/564784 const value: f32 = 1.34;
599 if (builtin.mode == builtin.Mode.Debug) {785 const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
600 {786 assert(mem.eql(u8, result, "f32: 1.34000003e+00\n"));
601 var buf1: [32]u8 = undefined;787 }
602 const value: f32 = 12.34;788 {
603 const result = try bufPrint(buf1[0..], "f32: {}\n", value);789 var buf1: [32]u8 = undefined;
604 assert(mem.eql(u8, result, "f32: 1.23400001e1\n"));790 const value: f32 = 12.34;
605 }791 const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
606 {792 assert(mem.eql(u8, result, "f32: 1.23400001e+01\n"));
607 var buf1: [32]u8 = undefined;793 }
608 const value: f64 = -12.34e10;794 {
609 const result = try bufPrint(buf1[0..], "f64: {}\n", value);795 var buf1: [32]u8 = undefined;
610 assert(mem.eql(u8, result, "f64: -1.234e11\n"));796 const value: f64 = -12.34e10;
611 }797 const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
612 {798 assert(mem.eql(u8, result, "f64: -1.234e+11\n"));
613 var buf1: [32]u8 = undefined;799 }
614 const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);800 {
615 assert(mem.eql(u8, result, "f64: NaN\n"));801 // This fails on release due to a minor rounding difference.
616 }802 // --release-fast outputs 9.999960000000001e-40 vs. the expected.
617 {803 if (builtin.mode == builtin.Mode.Debug) {
618 var buf1: [32]u8 = undefined;
619 const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
620 assert(mem.eql(u8, result, "f64: Infinity\n"));
621 }
622 {
623 var buf1: [32]u8 = undefined;
624 const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
625 assert(mem.eql(u8, result, "f64: -Infinity\n"));
626 }
627 {
628 var buf1: [32]u8 = undefined;
629 const value: f32 = 1.1234;
630 const result = try bufPrint(buf1[0..], "f32: {.1}\n", value);
631 assert(mem.eql(u8, result, "f32: 1.1\n"));
632 }
633 {
634 var buf1: [32]u8 = undefined;
635 const value: f32 = 1234.567;
636 const result = try bufPrint(buf1[0..], "f32: {.2}\n", value);
637 assert(mem.eql(u8, result, "f32: 1234.56\n"));
638 }
639 {
640 var buf1: [32]u8 = undefined;
641 const value: f32 = -11.1234;
642 const result = try bufPrint(buf1[0..], "f32: {.4}\n", value);
643 // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
644 // -11.12339... is truncated to -11.1233
645 assert(mem.eql(u8, result, "f32: -11.1233\n"));
646 }
647 {
648 var buf1: [32]u8 = undefined;
649 const value: f32 = 91.12345;
650 const result = try bufPrint(buf1[0..], "f32: {.}\n", value);
651 assert(mem.eql(u8, result, "f32: 91.12345\n"));
652 }
653 {
654 var buf1: [32]u8 = undefined;804 var buf1: [32]u8 = undefined;
655 const value: f64 = 91.12345678901235;805 const value: f64 = 9.999960e-40;
656 const result = try bufPrint(buf1[0..], "f64: {.10}\n", value);806 const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
657 assert(mem.eql(u8, result, "f64: 91.1234567890\n"));807 assert(mem.eql(u8, result, "f64: 9.99996e-40\n"));
658 }808 }
659809 }
810 {
811 var buf1: [32]u8 = undefined;
812 const value: f64 = 1.409706e-42;
813 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
814 assert(mem.eql(u8, result, "f64: 1.40971e-42\n"));
815 }
816 {
817 var buf1: [32]u8 = undefined;
818 const value: f64 = @bitCast(f32, u32(814313563));
819 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
820 assert(mem.eql(u8, result, "f64: 1.00000e-09\n"));
821 }
822 {
823 var buf1: [32]u8 = undefined;
824 const value: f64 = @bitCast(f32, u32(1006632960));
825 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
826 assert(mem.eql(u8, result, "f64: 7.81250e-03\n"));
827 }
828 {
829 // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05.
830 // In fact, libc doesn't round a lot of 5 cases up when one past the precision point.
831 var buf1: [32]u8 = undefined;
832 const value: f64 = @bitCast(f32, u32(1203982400));
833 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
834 assert(mem.eql(u8, result, "f64: 1.00001e+05\n"));
835 }
836 {
837 var buf1: [32]u8 = undefined;
838 const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
839 assert(mem.eql(u8, result, "f64: nan\n"));
840 }
841 {
842 var buf1: [32]u8 = undefined;
843 const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64);
844 assert(mem.eql(u8, result, "f64: -nan\n"));
845 }
846 {
847 var buf1: [32]u8 = undefined;
848 const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
849 assert(mem.eql(u8, result, "f64: inf\n"));
850 }
851 {
852 var buf1: [32]u8 = undefined;
853 const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
854 assert(mem.eql(u8, result, "f64: -inf\n"));
855 }
856 {
857 var buf1: [64]u8 = undefined;
858 const value: f64 = 1.52314e+29;
859 const result = try bufPrint(buf1[0..], "f64: {.}\n", value);
860 assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n"));
861 }
862 {
863 var buf1: [32]u8 = undefined;
864 const value: f32 = 1.1234;
865 const result = try bufPrint(buf1[0..], "f32: {.1}\n", value);
866 assert(mem.eql(u8, result, "f32: 1.1\n"));
867 }
868 {
869 var buf1: [32]u8 = undefined;
870 const value: f32 = 1234.567;
871 const result = try bufPrint(buf1[0..], "f32: {.2}\n", value);
872 assert(mem.eql(u8, result, "f32: 1234.57\n"));
873 }
874 {
875 var buf1: [32]u8 = undefined;
876 const value: f32 = -11.1234;
877 const result = try bufPrint(buf1[0..], "f32: {.4}\n", value);
878 // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
879 // -11.12339... is rounded back up to -11.1234
880 assert(mem.eql(u8, result, "f32: -11.1234\n"));
881 }
882 {
883 var buf1: [32]u8 = undefined;
884 const value: f32 = 91.12345;
885 const result = try bufPrint(buf1[0..], "f32: {.5}\n", value);
886 assert(mem.eql(u8, result, "f32: 91.12345\n"));
887 }
888 {
889 var buf1: [32]u8 = undefined;
890 const value: f64 = 91.12345678901235;
891 const result = try bufPrint(buf1[0..], "f64: {.10}\n", value);
892 assert(mem.eql(u8, result, "f64: 91.1234567890\n"));
893 }
894 {
895 var buf1: [32]u8 = undefined;
896 const value: f64 = 0.0;
897 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
898 assert(mem.eql(u8, result, "f64: 0.00000\n"));
899 }
900 {
901 var buf1: [32]u8 = undefined;
902 const value: f64 = 5.700;
903 const result = try bufPrint(buf1[0..], "f64: {.0}\n", value);
904 assert(mem.eql(u8, result, "f64: 6\n"));
905 }
906 {
907 var buf1: [32]u8 = undefined;
908 const value: f64 = 9.999;
909 const result = try bufPrint(buf1[0..], "f64: {.1}\n", value);
910 assert(mem.eql(u8, result, "f64: 10.0\n"));
911 }
912 {
913 var buf1: [32]u8 = undefined;
914 const value: f64 = 1.0;
915 const result = try bufPrint(buf1[0..], "f64: {.3}\n", value);
916 assert(mem.eql(u8, result, "f64: 1.000\n"));
917 }
918 {
919 var buf1: [32]u8 = undefined;
920 const value: f64 = 0.0003;
921 const result = try bufPrint(buf1[0..], "f64: {.8}\n", value);
922 assert(mem.eql(u8, result, "f64: 0.00030000\n"));
923 }
924 {
925 var buf1: [32]u8 = undefined;
926 const value: f64 = 1.40130e-45;
927 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
928 assert(mem.eql(u8, result, "f64: 0.00000\n"));
929 }
930 {
931 var buf1: [32]u8 = undefined;
932 const value: f64 = 9.999960e-40;
933 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
934 assert(mem.eql(u8, result, "f64: 0.00000\n"));
935 }
936 // libc checks
937 {
938 var buf1: [32]u8 = undefined;
939 const value: f64 = f64(@bitCast(f32, u32(916964781)));
940 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
941 assert(mem.eql(u8, result, "f64: 0.00001\n"));
942 }
943 {
944 var buf1: [32]u8 = undefined;
945 const value: f64 = f64(@bitCast(f32, u32(925353389)));
946 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
947 assert(mem.eql(u8, result, "f64: 0.00001\n"));
948 }
949 {
950 var buf1: [32]u8 = undefined;
951 const value: f64 = f64(@bitCast(f32, u32(1036831278)));
952 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
953 assert(mem.eql(u8, result, "f64: 0.10000\n"));
954 }
955 {
956 var buf1: [32]u8 = undefined;
957 const value: f64 = f64(@bitCast(f32, u32(1065353133)));
958 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
959 assert(mem.eql(u8, result, "f64: 1.00000\n"));
960 }
961 {
962 var buf1: [32]u8 = undefined;
963 const value: f64 = f64(@bitCast(f32, u32(1092616192)));
964 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
965 assert(mem.eql(u8, result, "f64: 10.00000\n"));
966 }
967 // libc differences
968 {
969 var buf1: [32]u8 = undefined;
970 // This is 0.015625 exactly according to gdb. We thus round down,
971 // however glibc rounds up for some reason. This occurs for all
972 // floats of the form x.yyyy25 on a precision point.
973 const value: f64 = f64(@bitCast(f32, u32(1015021568)));
974 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
975 assert(mem.eql(u8, result, "f64: 0.01563\n"));
976 }
977 // std-windows-x86_64-Debug-bare test case fails
978 {
979 // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3
980 // also rounds to 630 so I'm inclined to believe libc is not
981 // optimal here.
982 var buf1: [32]u8 = undefined;
983 const value: f64 = f64(@bitCast(f32, u32(1518338049)));
984 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
985 assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));
660 }986 }
661}987}
662988
std/hash_map.zig+62-6
...@@ -54,6 +54,14 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -54,6 +54,14 @@ pub fn HashMap(comptime K: type, comptime V: type,
54 }54 }
55 unreachable; // no next item55 unreachable; // no next item
56 }56 }
57
58 // Reset the iterator to the initial index
59 pub fn reset(it: &Iterator) void {
60 it.count = 0;
61 it.index = 0;
62 // Resetting the modification count too
63 it.initial_modification_count = it.hm.modification_count;
64 }
57 };65 };
5866
59 pub fn init(allocator: &Allocator) Self {67 pub fn init(allocator: &Allocator) Self {
...@@ -66,7 +74,7 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -66,7 +74,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
66 };74 };
67 }75 }
6876
69 pub fn deinit(hm: &Self) void {77 pub fn deinit(hm: &const Self) void {
70 hm.allocator.free(hm.entries);78 hm.allocator.free(hm.entries);
71 }79 }
7280
...@@ -79,6 +87,10 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -79,6 +87,10 @@ pub fn HashMap(comptime K: type, comptime V: type,
79 hm.incrementModificationCount();87 hm.incrementModificationCount();
80 }88 }
8189
90 pub fn count(hm: &const Self) usize {
91 return hm.size;
92 }
93
82 /// Returns the value that was already there.94 /// Returns the value that was already there.
83 pub fn put(hm: &Self, key: K, value: &const V) !?V {95 pub fn put(hm: &Self, key: K, value: &const V) !?V {
84 if (hm.entries.len == 0) {96 if (hm.entries.len == 0) {
...@@ -102,18 +114,19 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -102,18 +114,19 @@ pub fn HashMap(comptime K: type, comptime V: type,
102 return hm.internalPut(key, value);114 return hm.internalPut(key, value);
103 }115 }
104116
105 pub fn get(hm: &Self, key: K) ?&Entry {117 pub fn get(hm: &const Self, key: K) ?&Entry {
106 if (hm.entries.len == 0) {118 if (hm.entries.len == 0) {
107 return null;119 return null;
108 }120 }
109 return hm.internalGet(key);121 return hm.internalGet(key);
110 }122 }
111123
112 pub fn contains(hm: &Self, key: K) bool {124 pub fn contains(hm: &const Self, key: K) bool {
113 return hm.get(key) != null;125 return hm.get(key) != null;
114 }126 }
115127
116 pub fn remove(hm: &Self, key: K) ?&Entry {128 pub fn remove(hm: &Self, key: K) ?&Entry {
129 if (hm.entries.len == 0) return null;
117 hm.incrementModificationCount();130 hm.incrementModificationCount();
118 const start_index = hm.keyToIndex(key);131 const start_index = hm.keyToIndex(key);
119 {var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {132 {var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {
...@@ -217,7 +230,7 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -217,7 +230,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
217 unreachable; // put into a full map230 unreachable; // put into a full map
218 }231 }
219232
220 fn internalGet(hm: &Self, key: K) ?&Entry {233 fn internalGet(hm: &const Self, key: K) ?&Entry {
221 const start_index = hm.keyToIndex(key);234 const start_index = hm.keyToIndex(key);
222 {var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {235 {var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) {
223 const index = (start_index + roll_over) % hm.entries.len;236 const index = (start_index + roll_over) % hm.entries.len;
...@@ -229,14 +242,17 @@ pub fn HashMap(comptime K: type, comptime V: type,...@@ -229,14 +242,17 @@ pub fn HashMap(comptime K: type, comptime V: type,
229 return null;242 return null;
230 }243 }
231244
232 fn keyToIndex(hm: &Self, key: K) usize {245 fn keyToIndex(hm: &const Self, key: K) usize {
233 return usize(hash(key)) % hm.entries.len;246 return usize(hash(key)) % hm.entries.len;
234 }247 }
235 };248 };
236}249}
237250
238test "basic hash map usage" {251test "basic hash map usage" {
239 var map = HashMap(i32, i32, hash_i32, eql_i32).init(debug.global_allocator);252 var direct_allocator = std.heap.DirectAllocator.init();
253 defer direct_allocator.deinit();
254
255 var map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
240 defer map.deinit();256 defer map.deinit();
241257
242 assert((map.put(1, 11) catch unreachable) == null);258 assert((map.put(1, 11) catch unreachable) == null);
...@@ -248,12 +264,52 @@ test "basic hash map usage" {...@@ -248,12 +264,52 @@ test "basic hash map usage" {
248 assert(??(map.put(5, 66) catch unreachable) == 55);264 assert(??(map.put(5, 66) catch unreachable) == 55);
249 assert(??(map.put(5, 55) catch unreachable) == 66);265 assert(??(map.put(5, 55) catch unreachable) == 66);
250266
267 assert(map.contains(2));
251 assert((??map.get(2)).value == 22);268 assert((??map.get(2)).value == 22);
252 _ = map.remove(2);269 _ = map.remove(2);
253 assert(map.remove(2) == null);270 assert(map.remove(2) == null);
254 assert(map.get(2) == null);271 assert(map.get(2) == null);
255}272}
256273
274test "iterator hash map" {
275 var direct_allocator = std.heap.DirectAllocator.init();
276 defer direct_allocator.deinit();
277
278 var reset_map = HashMap(i32, i32, hash_i32, eql_i32).init(&direct_allocator.allocator);
279 defer reset_map.deinit();
280
281 assert((reset_map.put(1, 11) catch unreachable) == null);
282 assert((reset_map.put(2, 22) catch unreachable) == null);
283 assert((reset_map.put(3, 33) catch unreachable) == null);
284
285 var keys = []i32 { 1, 2, 3 };
286 var values = []i32 { 11, 22, 33 };
287
288 var it = reset_map.iterator();
289 var count : usize = 0;
290 while (it.next()) |next| {
291 assert(next.key == keys[count]);
292 assert(next.value == values[count]);
293 count += 1;
294 }
295
296 assert(count == 3);
297 assert(it.next() == null);
298 it.reset();
299 count = 0;
300 while (it.next()) |next| {
301 assert(next.key == keys[count]);
302 assert(next.value == values[count]);
303 count += 1;
304 if (count == 2) break;
305 }
306
307 it.reset();
308 var entry = ?? it.next();
309 assert(entry.key == keys[0]);
310 assert(entry.value == values[0]);
311}
312
257fn hash_i32(x: i32) u32 {313fn hash_i32(x: i32) u32 {
258 return @bitCast(u32, x);314 return @bitCast(u32, x);
259}315}
std/heap.zig+117-18
...@@ -47,13 +47,6 @@ pub const DirectAllocator = struct {...@@ -47,13 +47,6 @@ pub const DirectAllocator = struct {
4747
48 const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void;48 const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void;
4949
50 //pub const canary_bytes = []u8 {48, 239, 128, 46, 18, 49, 147, 9, 195, 59, 203, 3, 245, 54, 9, 122};
51 //pub const want_safety = switch (builtin.mode) {
52 // builtin.Mode.Debug => true,
53 // builtin.Mode.ReleaseSafe => true,
54 // else => false,
55 //};
56
57 pub fn init() DirectAllocator {50 pub fn init() DirectAllocator {
58 return DirectAllocator {51 return DirectAllocator {
59 .allocator = Allocator {52 .allocator = Allocator {
...@@ -79,19 +72,38 @@ pub const DirectAllocator = struct {...@@ -79,19 +72,38 @@ pub const DirectAllocator = struct {
7972
80 switch (builtin.os) {73 switch (builtin.os) {
81 Os.linux, Os.macosx, Os.ios => {74 Os.linux, Os.macosx, Os.ios => {
82 assert(alignment <= os.page_size);
83 const p = os.posix;75 const p = os.posix;
84 const addr = p.mmap(null, n, p.PROT_READ|p.PROT_WRITE,76 const alloc_size = if(alignment <= os.page_size) n else n + alignment;
85 p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0);77 const addr = p.mmap(null, alloc_size, p.PROT_READ|p.PROT_WRITE,
86 if (addr == p.MAP_FAILED) {78 p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0);
87 return error.OutOfMemory;79 if(addr == p.MAP_FAILED) return error.OutOfMemory;
88 }80
89 return @intToPtr(&u8, addr)[0..n];81 if(alloc_size == n) return @intToPtr(&u8, addr)[0..n];
82
83 var aligned_addr = addr & ~usize(alignment - 1);
84 aligned_addr += alignment;
85
86 //We can unmap the unused portions of our mmap, but we must only
87 // pass munmap bytes that exist outside our allocated pages or it
88 // will happily eat us too
89
90 //Since alignment > page_size, we are by definition on a page boundry
91 const unused_start = addr;
92 const unused_len = aligned_addr - 1 - unused_start;
93
94 var err = p.munmap(unused_start, unused_len);
95 debug.assert(p.getErrno(err) == 0);
96
97 //It is impossible that there is an unoccupied page at the top of our
98 // mmap.
99
100 return @intToPtr(&u8, aligned_addr)[0..n];
90 },101 },
91 Os.windows => {102 Os.windows => {
92 const amt = n + alignment + @sizeOf(usize);103 const amt = n + alignment + @sizeOf(usize);
93 const heap_handle = self.heap_handle ?? blk: {104 const heap_handle = self.heap_handle ?? blk: {
94 const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) ?? return error.OutOfMemory;105 const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0)
106 ?? return error.OutOfMemory;
95 self.heap_handle = hh;107 self.heap_handle = hh;
96 break :blk hh;108 break :blk hh;
97 };109 };
...@@ -120,7 +132,7 @@ pub const DirectAllocator = struct {...@@ -120,7 +132,7 @@ pub const DirectAllocator = struct {
120 const rem = @rem(new_addr_end, os.page_size);132 const rem = @rem(new_addr_end, os.page_size);
121 const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);133 const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);
122 if (old_addr_end > new_addr_end_rounded) {134 if (old_addr_end > new_addr_end_rounded) {
123 _ = os.posix.munmap(@intToPtr(&u8, new_addr_end_rounded), old_addr_end - new_addr_end_rounded);135 _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);
124 }136 }
125 return old_mem[0..new_size];137 return old_mem[0..new_size];
126 }138 }
...@@ -158,7 +170,7 @@ pub const DirectAllocator = struct {...@@ -158,7 +170,7 @@ pub const DirectAllocator = struct {
158170
159 switch (builtin.os) {171 switch (builtin.os) {
160 Os.linux, Os.macosx, Os.ios => {172 Os.linux, Os.macosx, Os.ios => {
161 _ = os.posix.munmap(bytes.ptr, bytes.len);173 _ = os.posix.munmap(@ptrToInt(bytes.ptr), bytes.len);
162 },174 },
163 Os.windows => {175 Os.windows => {
164 const record_addr = @ptrToInt(bytes.ptr) + bytes.len;176 const record_addr = @ptrToInt(bytes.ptr) + bytes.len;
...@@ -279,7 +291,7 @@ pub const FixedBufferAllocator = struct {...@@ -279,7 +291,7 @@ pub const FixedBufferAllocator = struct {
279291
280 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {292 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
281 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);293 const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
282 const addr = @ptrToInt(&self.buffer[self.end_index]);294 const addr = @ptrToInt(self.buffer.ptr) + self.end_index;
283 const rem = @rem(addr, alignment);295 const rem = @rem(addr, alignment);
284 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);296 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
285 const adjusted_index = self.end_index + march_forward_bytes;297 const adjusted_index = self.end_index + march_forward_bytes;
...@@ -306,6 +318,54 @@ pub const FixedBufferAllocator = struct {...@@ -306,6 +318,54 @@ pub const FixedBufferAllocator = struct {
306 fn free(allocator: &Allocator, bytes: []u8) void { }318 fn free(allocator: &Allocator, bytes: []u8) void { }
307};319};
308320
321/// lock free
322pub const ThreadSafeFixedBufferAllocator = struct {
323 allocator: Allocator,
324 end_index: usize,
325 buffer: []u8,
326
327 pub fn init(buffer: []u8) ThreadSafeFixedBufferAllocator {
328 return ThreadSafeFixedBufferAllocator {
329 .allocator = Allocator {
330 .allocFn = alloc,
331 .reallocFn = realloc,
332 .freeFn = free,
333 },
334 .buffer = buffer,
335 .end_index = 0,
336 };
337 }
338
339 fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 {
340 const self = @fieldParentPtr(ThreadSafeFixedBufferAllocator, "allocator", allocator);
341 var end_index = @atomicLoad(usize, &self.end_index, builtin.AtomicOrder.SeqCst);
342 while (true) {
343 const addr = @ptrToInt(self.buffer.ptr) + end_index;
344 const rem = @rem(addr, alignment);
345 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
346 const adjusted_index = end_index + march_forward_bytes;
347 const new_end_index = adjusted_index + n;
348 if (new_end_index > self.buffer.len) {
349 return error.OutOfMemory;
350 }
351 end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index,
352 builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index .. new_end_index];
353 }
354 }
355
356 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
357 if (new_size <= old_mem.len) {
358 return old_mem[0..new_size];
359 } else {
360 const result = try alloc(allocator, new_size, alignment);
361 mem.copy(u8, result, old_mem);
362 return result;
363 }
364 }
365
366 fn free(allocator: &Allocator, bytes: []u8) void { }
367};
368
309369
310370
311test "c_allocator" {371test "c_allocator" {
...@@ -322,6 +382,7 @@ test "DirectAllocator" {...@@ -322,6 +382,7 @@ test "DirectAllocator" {
322382
323 const allocator = &direct_allocator.allocator;383 const allocator = &direct_allocator.allocator;
324 try testAllocator(allocator);384 try testAllocator(allocator);
385 try testAllocatorLargeAlignment(allocator);
325}386}
326387
327test "ArenaAllocator" {388test "ArenaAllocator" {
...@@ -332,6 +393,7 @@ test "ArenaAllocator" {...@@ -332,6 +393,7 @@ test "ArenaAllocator" {
332 defer arena_allocator.deinit();393 defer arena_allocator.deinit();
333394
334 try testAllocator(&arena_allocator.allocator);395 try testAllocator(&arena_allocator.allocator);
396 try testAllocatorLargeAlignment(&arena_allocator.allocator);
335}397}
336398
337var test_fixed_buffer_allocator_memory: [30000 * @sizeOf(usize)]u8 = undefined;399var test_fixed_buffer_allocator_memory: [30000 * @sizeOf(usize)]u8 = undefined;
...@@ -339,6 +401,14 @@ test "FixedBufferAllocator" {...@@ -339,6 +401,14 @@ test "FixedBufferAllocator" {
339 var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);401 var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
340402
341 try testAllocator(&fixed_buffer_allocator.allocator);403 try testAllocator(&fixed_buffer_allocator.allocator);
404 try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
405}
406
407test "ThreadSafeFixedBufferAllocator" {
408 var fixed_buffer_allocator = ThreadSafeFixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
409
410 try testAllocator(&fixed_buffer_allocator.allocator);
411 try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
342}412}
343413
344fn testAllocator(allocator: &mem.Allocator) !void {414fn testAllocator(allocator: &mem.Allocator) !void {
...@@ -360,3 +430,32 @@ fn testAllocator(allocator: &mem.Allocator) !void {...@@ -360,3 +430,32 @@ fn testAllocator(allocator: &mem.Allocator) !void {
360430
361 allocator.free(slice);431 allocator.free(slice);
362}432}
433
434fn testAllocatorLargeAlignment(allocator: &mem.Allocator) mem.Allocator.Error!void {
435 //Maybe a platform's page_size is actually the same as or
436 // very near usize?
437 if(os.page_size << 2 > @maxValue(usize)) return;
438
439 const USizeShift = @IntType(false, std.math.log2(usize.bit_count));
440 const large_align = u29(os.page_size << 2);
441
442 var align_mask: usize = undefined;
443 _ = @shlWithOverflow(usize, ~usize(0), USizeShift(@ctz(large_align)), &align_mask);
444
445 var slice = try allocator.allocFn(allocator, 500, large_align);
446 debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
447
448 slice = try allocator.reallocFn(allocator, slice, 100, large_align);
449 debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
450
451 slice = try allocator.reallocFn(allocator, slice, 5000, large_align);
452 debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
453
454 slice = try allocator.reallocFn(allocator, slice, 10, large_align);
455 debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
456
457 slice = try allocator.reallocFn(allocator, slice, 20000, large_align);
458 debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
459
460 allocator.free(slice);
461}
std/index.zig+9-3
...@@ -7,7 +7,9 @@ pub const BufferOutStream = @import("buffer.zig").BufferOutStream;...@@ -7,7 +7,9 @@ pub const BufferOutStream = @import("buffer.zig").BufferOutStream;
7pub const HashMap = @import("hash_map.zig").HashMap;7pub const HashMap = @import("hash_map.zig").HashMap;
8pub const LinkedList = @import("linked_list.zig").LinkedList;8pub const LinkedList = @import("linked_list.zig").LinkedList;
9pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;9pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList;
10pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
1011
12pub const atomic = @import("atomic/index.zig");
11pub const base64 = @import("base64.zig");13pub const base64 = @import("base64.zig");
12pub const build = @import("build.zig");14pub const build = @import("build.zig");
13pub const c = @import("c/index.zig");15pub const c = @import("c/index.zig");
...@@ -17,11 +19,12 @@ pub const debug = @import("debug/index.zig");...@@ -17,11 +19,12 @@ pub const debug = @import("debug/index.zig");
17pub const dwarf = @import("dwarf.zig");19pub const dwarf = @import("dwarf.zig");
18pub const elf = @import("elf.zig");20pub const elf = @import("elf.zig");
19pub const empty_import = @import("empty.zig");21pub const empty_import = @import("empty.zig");
20pub const endian = @import("endian.zig");22pub const event = @import("event.zig");
21pub const fmt = @import("fmt/index.zig");23pub const fmt = @import("fmt/index.zig");
22pub const hash = @import("hash/index.zig");24pub const hash = @import("hash/index.zig");
23pub const heap = @import("heap.zig");25pub const heap = @import("heap.zig");
24pub const io = @import("io.zig");26pub const io = @import("io.zig");
27pub const json = @import("json.zig");
25pub const macho = @import("macho.zig");28pub const macho = @import("macho.zig");
26pub const math = @import("math/index.zig");29pub const math = @import("math/index.zig");
27pub const mem = @import("mem.zig");30pub const mem = @import("mem.zig");
...@@ -34,12 +37,14 @@ pub const zig = @import("zig/index.zig");...@@ -34,12 +37,14 @@ pub const zig = @import("zig/index.zig");
3437
35test "std" {38test "std" {
36 // run tests from these39 // run tests from these
40 _ = @import("atomic/index.zig");
37 _ = @import("array_list.zig");41 _ = @import("array_list.zig");
38 _ = @import("buf_map.zig");42 _ = @import("buf_map.zig");
39 _ = @import("buf_set.zig");43 _ = @import("buf_set.zig");
40 _ = @import("buffer.zig");44 _ = @import("buffer.zig");
41 _ = @import("hash_map.zig");45 _ = @import("hash_map.zig");
42 _ = @import("linked_list.zig");46 _ = @import("linked_list.zig");
47 _ = @import("segmented_list.zig");
4348
44 _ = @import("base64.zig");49 _ = @import("base64.zig");
45 _ = @import("build.zig");50 _ = @import("build.zig");
...@@ -50,15 +55,16 @@ test "std" {...@@ -50,15 +55,16 @@ test "std" {
50 _ = @import("dwarf.zig");55 _ = @import("dwarf.zig");
51 _ = @import("elf.zig");56 _ = @import("elf.zig");
52 _ = @import("empty.zig");57 _ = @import("empty.zig");
53 _ = @import("endian.zig");58 _ = @import("event.zig");
54 _ = @import("fmt/index.zig");59 _ = @import("fmt/index.zig");
55 _ = @import("hash/index.zig");60 _ = @import("hash/index.zig");
56 _ = @import("io.zig");61 _ = @import("io.zig");
62 _ = @import("json.zig");
57 _ = @import("macho.zig");63 _ = @import("macho.zig");
58 _ = @import("math/index.zig");64 _ = @import("math/index.zig");
59 _ = @import("mem.zig");65 _ = @import("mem.zig");
60 _ = @import("heap.zig");
61 _ = @import("net.zig");66 _ = @import("net.zig");
67 _ = @import("heap.zig");
62 _ = @import("os/index.zig");68 _ = @import("os/index.zig");
63 _ = @import("rand/index.zig");69 _ = @import("rand/index.zig");
64 _ = @import("sort.zig");70 _ = @import("sort.zig");
std/io.zig+5
...@@ -486,6 +486,11 @@ pub fn readLine(buf: []u8) !usize {...@@ -486,6 +486,11 @@ pub fn readLine(buf: []u8) !usize {
486 while (true) {486 while (true) {
487 const byte = stream.readByte() catch return error.EndOfFile;487 const byte = stream.readByte() catch return error.EndOfFile;
488 switch (byte) {488 switch (byte) {
489 '\r' => {
490 // trash the following \n
491 _ = stream.readByte() catch return error.EndOfFile;
492 return index;
493 },
489 '\n' => return index,494 '\n' => return index,
490 else => {495 else => {
491 if (index == buf.len) return error.InputTooLong;496 if (index == buf.len) return error.InputTooLong;
std/io_test.zig+17-1
...@@ -1,6 +1,5 @@...@@ -1,6 +1,5 @@
1const std = @import("index.zig");1const std = @import("index.zig");
2const io = std.io;2const io = std.io;
3const allocator = std.debug.global_allocator;
4const DefaultPrng = std.rand.DefaultPrng;3const DefaultPrng = std.rand.DefaultPrng;
5const assert = std.debug.assert;4const assert = std.debug.assert;
6const mem = std.mem;5const mem = std.mem;
...@@ -8,6 +7,9 @@ const os = std.os;...@@ -8,6 +7,9 @@ const os = std.os;
8const builtin = @import("builtin");7const builtin = @import("builtin");
98
10test "write a file, read it, then delete it" {9test "write a file, read it, then delete it" {
10 var raw_bytes: [200 * 1024]u8 = undefined;
11 var allocator = &std.heap.FixedBufferAllocator.init(raw_bytes[0..]).allocator;
12
11 var data: [1024]u8 = undefined;13 var data: [1024]u8 = undefined;
12 var prng = DefaultPrng.init(1234);14 var prng = DefaultPrng.init(1234);
13 prng.random.bytes(data[0..]);15 prng.random.bytes(data[0..]);
...@@ -44,3 +46,17 @@ test "write a file, read it, then delete it" {...@@ -44,3 +46,17 @@ test "write a file, read it, then delete it" {
44 }46 }
45 try os.deleteFile(allocator, tmp_file_name);47 try os.deleteFile(allocator, tmp_file_name);
46}48}
49
50test "BufferOutStream" {
51 var bytes: [100]u8 = undefined;
52 var allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
53
54 var buffer = try std.Buffer.initSize(allocator, 0);
55 var buf_stream = &std.io.BufferOutStream.init(&buffer).stream;
56
57 const x: i32 = 42;
58 const y: i32 = 1234;
59 try buf_stream.print("x: {}\ny: {}\n", x, y);
60
61 assert(mem.eql(u8, buffer.toSlice(), "x: 42\ny: 1234\n"));
62}
std/json.zig created+1304
...@@ -0,0 +1,1304 @@
1// JSON parser conforming to RFC8259.
2//
3// https://tools.ietf.org/html/rfc8259
4
5const std = @import("index.zig");
6const mem = std.mem;
7
8const u1 = @IntType(false, 1);
9const u256 = @IntType(false, 256);
10
11// A single token slice into the parent string.
12//
13// Use `token.slice()` on the inptu at the current position to get the current slice.
14pub const Token = struct {
15 id: Id,
16 // How many bytes do we skip before counting
17 offset: u1,
18 // Whether string contains a \uXXXX sequence and cannot be zero-copied
19 string_has_escape: bool,
20 // Whether number is simple and can be represented by an integer (i.e. no `.` or `e`)
21 number_is_integer: bool,
22 // How many bytes from the current position behind the start of this token is.
23 count: usize,
24
25 pub const Id = enum {
26 ObjectBegin,
27 ObjectEnd,
28 ArrayBegin,
29 ArrayEnd,
30 String,
31 Number,
32 True,
33 False,
34 Null,
35 };
36
37 pub fn init(id: Id, count: usize, offset: u1) Token {
38 return Token {
39 .id = id,
40 .offset = offset,
41 .string_has_escape = false,
42 .number_is_integer = true,
43 .count = count,
44 };
45 }
46
47 pub fn initString(count: usize, has_unicode_escape: bool) Token {
48 return Token {
49 .id = Id.String,
50 .offset = 0,
51 .string_has_escape = has_unicode_escape,
52 .number_is_integer = true,
53 .count = count,
54 };
55 }
56
57 pub fn initNumber(count: usize, number_is_integer: bool) Token {
58 return Token {
59 .id = Id.Number,
60 .offset = 0,
61 .string_has_escape = false,
62 .number_is_integer = number_is_integer,
63 .count = count,
64 };
65 }
66
67 // A marker token is a zero-length
68 pub fn initMarker(id: Id) Token {
69 return Token {
70 .id = id,
71 .offset = 0,
72 .string_has_escape = false,
73 .number_is_integer = true,
74 .count = 0,
75 };
76 }
77
78 // Slice into the underlying input string.
79 pub fn slice(self: &const Token, input: []const u8, i: usize) []const u8 {
80 return input[i + self.offset - self.count .. i + self.offset];
81 }
82};
83
84// A small streaming JSON parser. This accepts input one byte at a time and returns tokens as
85// they are encountered. No copies or allocations are performed during parsing and the entire
86// parsing state requires ~40-50 bytes of stack space.
87//
88// Conforms strictly to RFC8529.
89const StreamingJsonParser = struct {
90 // Current state
91 state: State,
92 // How many bytes we have counted for the current token
93 count: usize,
94 // What state to follow after parsing a string (either property or value string)
95 after_string_state: State,
96 // What state to follow after parsing a value (either top-level or value end)
97 after_value_state: State,
98 // If we stopped now, would the complete parsed string to now be a valid json string
99 complete: bool,
100 // Current token flags to pass through to the next generated, see Token.
101 string_has_escape: bool,
102 number_is_integer: bool,
103
104 // Bit-stack for nested object/map literals (max 255 nestings).
105 stack: u256,
106 stack_used: u8,
107
108 const object_bit = 0;
109 const array_bit = 1;
110 const max_stack_size = @maxValue(u8);
111
112 pub fn init() StreamingJsonParser {
113 var p: StreamingJsonParser = undefined;
114 p.reset();
115 return p;
116 }
117
118 pub fn reset(p: &StreamingJsonParser) void {
119 p.state = State.TopLevelBegin;
120 p.count = 0;
121 // Set before ever read in main transition function
122 p.after_string_state = undefined;
123 p.after_value_state = State.ValueEnd; // handle end of values normally
124 p.stack = 0;
125 p.stack_used = 0;
126 p.complete = false;
127 p.string_has_escape = false;
128 p.number_is_integer = true;
129 }
130
131 pub const State = enum {
132 // These must be first with these explicit values as we rely on them for indexing the
133 // bit-stack directly and avoiding a branch.
134 ObjectSeparator = 0,
135 ValueEnd = 1,
136
137 TopLevelBegin,
138 TopLevelEnd,
139
140 ValueBegin,
141 ValueBeginNoClosing,
142
143 String,
144 StringUtf8Byte3,
145 StringUtf8Byte2,
146 StringUtf8Byte1,
147 StringEscapeCharacter,
148 StringEscapeHexUnicode4,
149 StringEscapeHexUnicode3,
150 StringEscapeHexUnicode2,
151 StringEscapeHexUnicode1,
152
153 Number,
154 NumberMaybeDotOrExponent,
155 NumberMaybeDigitOrDotOrExponent,
156 NumberFractionalRequired,
157 NumberFractional,
158 NumberMaybeExponent,
159 NumberExponent,
160 NumberExponentDigitsRequired,
161 NumberExponentDigits,
162
163 TrueLiteral1,
164 TrueLiteral2,
165 TrueLiteral3,
166
167 FalseLiteral1,
168 FalseLiteral2,
169 FalseLiteral3,
170 FalseLiteral4,
171
172 NullLiteral1,
173 NullLiteral2,
174 NullLiteral3,
175
176 // Only call this function to generate array/object final state.
177 pub fn fromInt(x: var) State {
178 std.debug.assert(x == 0 or x == 1);
179 const T = @TagType(State);
180 return State(T(x));
181 }
182 };
183
184 pub const Error = error {
185 InvalidTopLevel,
186 TooManyNestedItems,
187 TooManyClosingItems,
188 InvalidValueBegin,
189 InvalidValueEnd,
190 UnbalancedBrackets,
191 UnbalancedBraces,
192 UnexpectedClosingBracket,
193 UnexpectedClosingBrace,
194 InvalidNumber,
195 InvalidSeparator,
196 InvalidLiteral,
197 InvalidEscapeCharacter,
198 InvalidUnicodeHexSymbol,
199 InvalidUtf8Byte,
200 InvalidTopLevelTrailing,
201 InvalidControlCharacter,
202 };
203
204 // Give another byte to the parser and obtain any new tokens. This may (rarely) return two
205 // tokens. token2 is always null if token1 is null.
206 //
207 // There is currently no error recovery on a bad stream.
208 pub fn feed(p: &StreamingJsonParser, c: u8, token1: &?Token, token2: &?Token) Error!void {
209 *token1 = null;
210 *token2 = null;
211 p.count += 1;
212
213 // unlikely
214 if (try p.transition(c, token1)) {
215 _ = try p.transition(c, token2);
216 }
217 }
218
219 // Perform a single transition on the state machine and return any possible token.
220 fn transition(p: &StreamingJsonParser, c: u8, token: &?Token) Error!bool {
221 switch (p.state) {
222 State.TopLevelBegin => switch (c) {
223 '{' => {
224 p.stack <<= 1;
225 p.stack |= object_bit;
226 p.stack_used += 1;
227
228 p.state = State.ValueBegin;
229 p.after_string_state = State.ObjectSeparator;
230
231 *token = Token.initMarker(Token.Id.ObjectBegin);
232 },
233 '[' => {
234 p.stack <<= 1;
235 p.stack |= array_bit;
236 p.stack_used += 1;
237
238 p.state = State.ValueBegin;
239 p.after_string_state = State.ValueEnd;
240
241 *token = Token.initMarker(Token.Id.ArrayBegin);
242 },
243 '-' => {
244 p.number_is_integer = true;
245 p.state = State.Number;
246 p.after_value_state = State.TopLevelEnd;
247 p.count = 0;
248 },
249 '0' => {
250 p.number_is_integer = true;
251 p.state = State.NumberMaybeDotOrExponent;
252 p.after_value_state = State.TopLevelEnd;
253 p.count = 0;
254 },
255 '1' ... '9' => {
256 p.number_is_integer = true;
257 p.state = State.NumberMaybeDigitOrDotOrExponent;
258 p.after_value_state = State.TopLevelEnd;
259 p.count = 0;
260 },
261 '"' => {
262 p.state = State.String;
263 p.after_value_state = State.TopLevelEnd;
264 // We don't actually need the following since after_value_state should override.
265 p.after_string_state = State.ValueEnd;
266 p.string_has_escape = false;
267 p.count = 0;
268 },
269 't' => {
270 p.state = State.TrueLiteral1;
271 p.after_value_state = State.TopLevelEnd;
272 p.count = 0;
273 },
274 'f' => {
275 p.state = State.FalseLiteral1;
276 p.after_value_state = State.TopLevelEnd;
277 p.count = 0;
278 },
279 'n' => {
280 p.state = State.NullLiteral1;
281 p.after_value_state = State.TopLevelEnd;
282 p.count = 0;
283 },
284 0x09, 0x0A, 0x0D, 0x20 => {
285 // whitespace
286 },
287 else => {
288 return error.InvalidTopLevel;
289 },
290 },
291
292 State.TopLevelEnd => switch (c) {
293 0x09, 0x0A, 0x0D, 0x20 => {
294 // whitespace
295 },
296 else => {
297 return error.InvalidTopLevelTrailing;
298 },
299 },
300
301 State.ValueBegin => switch (c) {
302 // NOTE: These are shared in ValueEnd as well, think we can reorder states to
303 // be a bit clearer and avoid this duplication.
304 '}' => {
305 // unlikely
306 if (p.stack & 1 != object_bit) {
307 return error.UnexpectedClosingBracket;
308 }
309 if (p.stack_used == 0) {
310 return error.TooManyClosingItems;
311 }
312
313 p.state = State.ValueBegin;
314 p.after_string_state = State.fromInt(p.stack & 1);
315
316 p.stack >>= 1;
317 p.stack_used -= 1;
318
319 switch (p.stack_used) {
320 0 => {
321 p.complete = true;
322 p.state = State.TopLevelEnd;
323 },
324 else => {},
325 }
326
327 *token = Token.initMarker(Token.Id.ObjectEnd);
328 },
329 ']' => {
330 if (p.stack & 1 != array_bit) {
331 return error.UnexpectedClosingBrace;
332 }
333 if (p.stack_used == 0) {
334 return error.TooManyClosingItems;
335 }
336
337 p.state = State.ValueBegin;
338 p.after_string_state = State.fromInt(p.stack & 1);
339
340 p.stack >>= 1;
341 p.stack_used -= 1;
342
343 switch (p.stack_used) {
344 0 => {
345 p.complete = true;
346 p.state = State.TopLevelEnd;
347 },
348 else => {},
349 }
350
351 *token = Token.initMarker(Token.Id.ArrayEnd);
352 },
353 '{' => {
354 if (p.stack_used == max_stack_size) {
355 return error.TooManyNestedItems;
356 }
357
358 p.stack <<= 1;
359 p.stack |= object_bit;
360 p.stack_used += 1;
361
362 p.state = State.ValueBegin;
363 p.after_string_state = State.ObjectSeparator;
364
365 *token = Token.initMarker(Token.Id.ObjectBegin);
366 },
367 '[' => {
368 if (p.stack_used == max_stack_size) {
369 return error.TooManyNestedItems;
370 }
371
372 p.stack <<= 1;
373 p.stack |= array_bit;
374 p.stack_used += 1;
375
376 p.state = State.ValueBegin;
377 p.after_string_state = State.ValueEnd;
378
379 *token = Token.initMarker(Token.Id.ArrayBegin);
380 },
381 '-' => {
382 p.state = State.Number;
383 p.count = 0;
384 },
385 '0' => {
386 p.state = State.NumberMaybeDotOrExponent;
387 p.count = 0;
388 },
389 '1' ... '9' => {
390 p.state = State.NumberMaybeDigitOrDotOrExponent;
391 p.count = 0;
392 },
393 '"' => {
394 p.state = State.String;
395 p.count = 0;
396 },
397 't' => {
398 p.state = State.TrueLiteral1;
399 p.count = 0;
400 },
401 'f' => {
402 p.state = State.FalseLiteral1;
403 p.count = 0;
404 },
405 'n' => {
406 p.state = State.NullLiteral1;
407 p.count = 0;
408 },
409 0x09, 0x0A, 0x0D, 0x20 => {
410 // whitespace
411 },
412 else => {
413 return error.InvalidValueBegin;
414 },
415 },
416
417 // TODO: A bit of duplication here and in the following state, redo.
418 State.ValueBeginNoClosing => switch (c) {
419 '{' => {
420 if (p.stack_used == max_stack_size) {
421 return error.TooManyNestedItems;
422 }
423
424 p.stack <<= 1;
425 p.stack |= object_bit;
426 p.stack_used += 1;
427
428 p.state = State.ValueBegin;
429 p.after_string_state = State.ObjectSeparator;
430
431 *token = Token.initMarker(Token.Id.ObjectBegin);
432 },
433 '[' => {
434 if (p.stack_used == max_stack_size) {
435 return error.TooManyNestedItems;
436 }
437
438 p.stack <<= 1;
439 p.stack |= array_bit;
440 p.stack_used += 1;
441
442 p.state = State.ValueBegin;
443 p.after_string_state = State.ValueEnd;
444
445 *token = Token.initMarker(Token.Id.ArrayBegin);
446 },
447 '-' => {
448 p.state = State.Number;
449 p.count = 0;
450 },
451 '0' => {
452 p.state = State.NumberMaybeDotOrExponent;
453 p.count = 0;
454 },
455 '1' ... '9' => {
456 p.state = State.NumberMaybeDigitOrDotOrExponent;
457 p.count = 0;
458 },
459 '"' => {
460 p.state = State.String;
461 p.count = 0;
462 },
463 't' => {
464 p.state = State.TrueLiteral1;
465 p.count = 0;
466 },
467 'f' => {
468 p.state = State.FalseLiteral1;
469 p.count = 0;
470 },
471 'n' => {
472 p.state = State.NullLiteral1;
473 p.count = 0;
474 },
475 0x09, 0x0A, 0x0D, 0x20 => {
476 // whitespace
477 },
478 else => {
479 return error.InvalidValueBegin;
480 },
481 },
482
483 State.ValueEnd => switch (c) {
484 ',' => {
485 p.after_string_state = State.fromInt(p.stack & 1);
486 p.state = State.ValueBeginNoClosing;
487 },
488 ']' => {
489 if (p.stack_used == 0) {
490 return error.UnbalancedBrackets;
491 }
492
493 p.state = State.ValueEnd;
494 p.after_string_state = State.fromInt(p.stack & 1);
495
496 p.stack >>= 1;
497 p.stack_used -= 1;
498
499 if (p.stack_used == 0) {
500 p.complete = true;
501 p.state = State.TopLevelEnd;
502 }
503
504 *token = Token.initMarker(Token.Id.ArrayEnd);
505 },
506 '}' => {
507 if (p.stack_used == 0) {
508 return error.UnbalancedBraces;
509 }
510
511 p.state = State.ValueEnd;
512 p.after_string_state = State.fromInt(p.stack & 1);
513
514 p.stack >>= 1;
515 p.stack_used -= 1;
516
517 if (p.stack_used == 0) {
518 p.complete = true;
519 p.state = State.TopLevelEnd;
520 }
521
522 *token = Token.initMarker(Token.Id.ObjectEnd);
523 },
524 0x09, 0x0A, 0x0D, 0x20 => {
525 // whitespace
526 },
527 else => {
528 return error.InvalidValueEnd;
529 },
530 },
531
532 State.ObjectSeparator => switch (c) {
533 ':' => {
534 p.state = State.ValueBegin;
535 p.after_string_state = State.ValueEnd;
536 },
537 0x09, 0x0A, 0x0D, 0x20 => {
538 // whitespace
539 },
540 else => {
541 return error.InvalidSeparator;
542 },
543 },
544
545 State.String => switch (c) {
546 0x00 ... 0x1F => {
547 return error.InvalidControlCharacter;
548 },
549 '"' => {
550 p.state = p.after_string_state;
551 if (p.after_value_state == State.TopLevelEnd) {
552 p.state = State.TopLevelEnd;
553 p.complete = true;
554 }
555
556 *token = Token.initString(p.count - 1, p.string_has_escape);
557 },
558 '\\' => {
559 p.state = State.StringEscapeCharacter;
560 },
561 0x20, 0x21, 0x23 ... 0x5B, 0x5D ... 0x7F => {
562 // non-control ascii
563 },
564 0xC0 ... 0xDF => {
565 p.state = State.StringUtf8Byte1;
566 },
567 0xE0 ... 0xEF => {
568 p.state = State.StringUtf8Byte2;
569 },
570 0xF0 ... 0xFF => {
571 p.state = State.StringUtf8Byte3;
572 },
573 else => {
574 return error.InvalidUtf8Byte;
575 },
576 },
577
578 State.StringUtf8Byte3 => switch (c >> 6) {
579 0b10 => p.state = State.StringUtf8Byte2,
580 else => return error.InvalidUtf8Byte,
581 },
582
583 State.StringUtf8Byte2 => switch (c >> 6) {
584 0b10 => p.state = State.StringUtf8Byte1,
585 else => return error.InvalidUtf8Byte,
586 },
587
588 State.StringUtf8Byte1 => switch (c >> 6) {
589 0b10 => p.state = State.String,
590 else => return error.InvalidUtf8Byte,
591 },
592
593 State.StringEscapeCharacter => switch (c) {
594 // NOTE: '/' is allowed as an escaped character but it also is allowed
595 // as unescaped according to the RFC. There is a reported errata which suggests
596 // removing the non-escaped variant but it makes more sense to simply disallow
597 // it as an escape code here.
598 //
599 // The current JSONTestSuite tests rely on both of this behaviour being present
600 // however, so we default to the status quo where both are accepted until this
601 // is further clarified.
602 '"', '\\', '/', 'b', 'f', 'n', 'r', 't' => {
603 p.string_has_escape = true;
604 p.state = State.String;
605 },
606 'u' => {
607 p.string_has_escape = true;
608 p.state = State.StringEscapeHexUnicode4;
609 },
610 else => {
611 return error.InvalidEscapeCharacter;
612 },
613 },
614
615 State.StringEscapeHexUnicode4 => switch (c) {
616 '0' ... '9', 'A' ... 'F', 'a' ... 'f' => {
617 p.state = State.StringEscapeHexUnicode3;
618 },
619 else => return error.InvalidUnicodeHexSymbol,
620 },
621
622 State.StringEscapeHexUnicode3 => switch (c) {
623 '0' ... '9', 'A' ... 'F', 'a' ... 'f' => {
624 p.state = State.StringEscapeHexUnicode2;
625 },
626 else => return error.InvalidUnicodeHexSymbol,
627 },
628
629 State.StringEscapeHexUnicode2 => switch (c) {
630 '0' ... '9', 'A' ... 'F', 'a' ... 'f' => {
631 p.state = State.StringEscapeHexUnicode1;
632 },
633 else => return error.InvalidUnicodeHexSymbol,
634 },
635
636 State.StringEscapeHexUnicode1 => switch (c) {
637 '0' ... '9', 'A' ... 'F', 'a' ... 'f' => {
638 p.state = State.String;
639 },
640 else => return error.InvalidUnicodeHexSymbol,
641 },
642
643 State.Number => {
644 p.complete = p.after_value_state == State.TopLevelEnd;
645 switch (c) {
646 '0' => {
647 p.state = State.NumberMaybeDotOrExponent;
648 },
649 '1' ... '9' => {
650 p.state = State.NumberMaybeDigitOrDotOrExponent;
651 },
652 else => {
653 return error.InvalidNumber;
654 },
655 }
656 },
657
658 State.NumberMaybeDotOrExponent => {
659 p.complete = p.after_value_state == State.TopLevelEnd;
660 switch (c) {
661 '.' => {
662 p.number_is_integer = false;
663 p.state = State.NumberFractionalRequired;
664 },
665 'e', 'E' => {
666 p.number_is_integer = false;
667 p.state = State.NumberExponent;
668 },
669 else => {
670 p.state = p.after_value_state;
671 *token = Token.initNumber(p.count, p.number_is_integer);
672 return true;
673 },
674 }
675 },
676
677 State.NumberMaybeDigitOrDotOrExponent => {
678 p.complete = p.after_value_state == State.TopLevelEnd;
679 switch (c) {
680 '.' => {
681 p.number_is_integer = false;
682 p.state = State.NumberFractionalRequired;
683 },
684 'e', 'E' => {
685 p.number_is_integer = false;
686 p.state = State.NumberExponent;
687 },
688 '0' ... '9' => {
689 // another digit
690 },
691 else => {
692 p.state = p.after_value_state;
693 *token = Token.initNumber(p.count, p.number_is_integer);
694 return true;
695 },
696 }
697 },
698
699 State.NumberFractionalRequired => {
700 p.complete = p.after_value_state == State.TopLevelEnd;
701 switch (c) {
702 '0' ... '9' => {
703 p.state = State.NumberFractional;
704 },
705 else => {
706 return error.InvalidNumber;
707 },
708 }
709 },
710
711 State.NumberFractional => {
712 p.complete = p.after_value_state == State.TopLevelEnd;
713 switch (c) {
714 '0' ... '9' => {
715 // another digit
716 },
717 'e', 'E' => {
718 p.number_is_integer = false;
719 p.state = State.NumberExponent;
720 },
721 else => {
722 p.state = p.after_value_state;
723 *token = Token.initNumber(p.count, p.number_is_integer);
724 return true;
725 },
726 }
727 },
728
729 State.NumberMaybeExponent => {
730 p.complete = p.after_value_state == State.TopLevelEnd;
731 switch (c) {
732 'e', 'E' => {
733 p.number_is_integer = false;
734 p.state = State.NumberExponent;
735 },
736 else => {
737 p.state = p.after_value_state;
738 *token = Token.initNumber(p.count, p.number_is_integer);
739 return true;
740 },
741 }
742 },
743
744 State.NumberExponent => switch (c) {
745 '-', '+', => {
746 p.complete = false;
747 p.state = State.NumberExponentDigitsRequired;
748 },
749 '0' ... '9' => {
750 p.complete = p.after_value_state == State.TopLevelEnd;
751 p.state = State.NumberExponentDigits;
752 },
753 else => {
754 return error.InvalidNumber;
755 },
756 },
757
758 State.NumberExponentDigitsRequired => switch (c) {
759 '0' ... '9' => {
760 p.complete = p.after_value_state == State.TopLevelEnd;
761 p.state = State.NumberExponentDigits;
762 },
763 else => {
764 return error.InvalidNumber;
765 },
766 },
767
768 State.NumberExponentDigits => {
769 p.complete = p.after_value_state == State.TopLevelEnd;
770 switch (c) {
771 '0' ... '9' => {
772 // another digit
773 },
774 else => {
775 p.state = p.after_value_state;
776 *token = Token.initNumber(p.count, p.number_is_integer);
777 return true;
778 },
779 }
780 },
781
782 State.TrueLiteral1 => switch (c) {
783 'r' => p.state = State.TrueLiteral2,
784 else => return error.InvalidLiteral,
785 },
786
787 State.TrueLiteral2 => switch (c) {
788 'u' => p.state = State.TrueLiteral3,
789 else => return error.InvalidLiteral,
790 },
791
792 State.TrueLiteral3 => switch (c) {
793 'e' => {
794 p.state = p.after_value_state;
795 p.complete = p.state == State.TopLevelEnd;
796 *token = Token.init(Token.Id.True, p.count + 1, 1);
797 },
798 else => {
799 return error.InvalidLiteral;
800 },
801 },
802
803 State.FalseLiteral1 => switch (c) {
804 'a' => p.state = State.FalseLiteral2,
805 else => return error.InvalidLiteral,
806 },
807
808 State.FalseLiteral2 => switch (c) {
809 'l' => p.state = State.FalseLiteral3,
810 else => return error.InvalidLiteral,
811 },
812
813 State.FalseLiteral3 => switch (c) {
814 's' => p.state = State.FalseLiteral4,
815 else => return error.InvalidLiteral,
816 },
817
818 State.FalseLiteral4 => switch (c) {
819 'e' => {
820 p.state = p.after_value_state;
821 p.complete = p.state == State.TopLevelEnd;
822 *token = Token.init(Token.Id.False, p.count + 1, 1);
823 },
824 else => {
825 return error.InvalidLiteral;
826 },
827 },
828
829 State.NullLiteral1 => switch (c) {
830 'u' => p.state = State.NullLiteral2,
831 else => return error.InvalidLiteral,
832 },
833
834 State.NullLiteral2 => switch (c) {
835 'l' => p.state = State.NullLiteral3,
836 else => return error.InvalidLiteral,
837 },
838
839 State.NullLiteral3 => switch (c) {
840 'l' => {
841 p.state = p.after_value_state;
842 p.complete = p.state == State.TopLevelEnd;
843 *token = Token.init(Token.Id.Null, p.count + 1, 1);
844 },
845 else => {
846 return error.InvalidLiteral;
847 },
848 },
849 }
850
851 return false;
852 }
853};
854
855// Validate a JSON string. This does not limit number precision so a decoder may not necessarily
856// be able to decode the string even if this returns true.
857pub fn validate(s: []const u8) bool {
858 var p = StreamingJsonParser.init();
859
860 for (s) |c, i| {
861 var token1: ?Token = undefined;
862 var token2: ?Token = undefined;
863
864 p.feed(c, &token1, &token2) catch |err| {
865 return false;
866 };
867 }
868
869 return p.complete;
870}
871
872const Allocator = std.mem.Allocator;
873const ArenaAllocator = std.heap.ArenaAllocator;
874const ArrayList = std.ArrayList;
875const HashMap = std.HashMap;
876
877pub const ValueTree = struct {
878 arena: ArenaAllocator,
879 root: Value,
880
881 pub fn deinit(self: &ValueTree) void {
882 self.arena.deinit();
883 }
884};
885
886pub const ObjectMap = HashMap([]const u8, Value, mem.hash_slice_u8, mem.eql_slice_u8);
887
888pub const Value = union(enum) {
889 Null,
890 Bool: bool,
891 Integer: i64,
892 Float: f64,
893 String: []const u8,
894 Array: ArrayList(Value),
895 Object: ObjectMap,
896
897 pub fn dump(self: &const Value) void {
898 switch (*self) {
899 Value.Null => {
900 std.debug.warn("null");
901 },
902 Value.Bool => |inner| {
903 std.debug.warn("{}", inner);
904 },
905 Value.Integer => |inner| {
906 std.debug.warn("{}", inner);
907 },
908 Value.Float => |inner| {
909 std.debug.warn("{.5}", inner);
910 },
911 Value.String => |inner| {
912 std.debug.warn("\"{}\"", inner);
913 },
914 Value.Array => |inner| {
915 var not_first = false;
916 std.debug.warn("[");
917 for (inner.toSliceConst()) |value| {
918 if (not_first) {
919 std.debug.warn(",");
920 }
921 not_first = true;
922 value.dump();
923 }
924 std.debug.warn("]");
925 },
926 Value.Object => |inner| {
927 var not_first = false;
928 std.debug.warn("{{");
929 var it = inner.iterator();
930
931 while (it.next()) |entry| {
932 if (not_first) {
933 std.debug.warn(",");
934 }
935 not_first = true;
936 std.debug.warn("\"{}\":", entry.key);
937 entry.value.dump();
938 }
939 std.debug.warn("}}");
940 },
941 }
942 }
943
944 pub fn dumpIndent(self: &const Value, indent: usize) void {
945 if (indent == 0) {
946 self.dump();
947 } else {
948 self.dumpIndentLevel(indent, 0);
949 }
950 }
951
952 fn dumpIndentLevel(self: &const Value, indent: usize, level: usize) void {
953 switch (*self) {
954 Value.Null => {
955 std.debug.warn("null");
956 },
957 Value.Bool => |inner| {
958 std.debug.warn("{}", inner);
959 },
960 Value.Integer => |inner| {
961 std.debug.warn("{}", inner);
962 },
963 Value.Float => |inner| {
964 std.debug.warn("{.5}", inner);
965 },
966 Value.String => |inner| {
967 std.debug.warn("\"{}\"", inner);
968 },
969 Value.Array => |inner| {
970 var not_first = false;
971 std.debug.warn("[\n");
972
973 for (inner.toSliceConst()) |value| {
974 if (not_first) {
975 std.debug.warn(",\n");
976 }
977 not_first = true;
978 padSpace(level + indent);
979 value.dumpIndentLevel(indent, level + indent);
980 }
981 std.debug.warn("\n");
982 padSpace(level);
983 std.debug.warn("]");
984 },
985 Value.Object => |inner| {
986 var not_first = false;
987 std.debug.warn("{{\n");
988 var it = inner.iterator();
989
990 while (it.next()) |entry| {
991 if (not_first) {
992 std.debug.warn(",\n");
993 }
994 not_first = true;
995 padSpace(level + indent);
996 std.debug.warn("\"{}\": ", entry.key);
997 entry.value.dumpIndentLevel(indent, level + indent);
998 }
999 std.debug.warn("\n");
1000 padSpace(level);
1001 std.debug.warn("}}");
1002 },
1003 }
1004 }
1005
1006 fn padSpace(indent: usize) void {
1007 var i: usize = 0;
1008 while (i < indent) : (i += 1) {
1009 std.debug.warn(" ");
1010 }
1011 }
1012};
1013
1014// A non-stream JSON parser which constructs a tree of Value's.
1015const JsonParser = struct {
1016 allocator: &Allocator,
1017 state: State,
1018 copy_strings: bool,
1019 // Stores parent nodes and un-combined Values.
1020 stack: ArrayList(Value),
1021
1022 const State = enum {
1023 ObjectKey,
1024 ObjectValue,
1025 ArrayValue,
1026 Simple,
1027 };
1028
1029 pub fn init(allocator: &Allocator, copy_strings: bool) JsonParser {
1030 return JsonParser {
1031 .allocator = allocator,
1032 .state = State.Simple,
1033 .copy_strings = copy_strings,
1034 .stack = ArrayList(Value).init(allocator),
1035 };
1036 }
1037
1038 pub fn deinit(p: &JsonParser) void {
1039 p.stack.deinit();
1040 }
1041
1042 pub fn reset(p: &JsonParser) void {
1043 p.state = State.Simple;
1044 p.stack.shrink(0);
1045 }
1046
1047 pub fn parse(p: &JsonParser, input: []const u8) !ValueTree {
1048 var mp = StreamingJsonParser.init();
1049
1050 var arena = ArenaAllocator.init(p.allocator);
1051 errdefer arena.deinit();
1052
1053 for (input) |c, i| {
1054 var mt1: ?Token = undefined;
1055 var mt2: ?Token = undefined;
1056
1057 try mp.feed(c, &mt1, &mt2);
1058 if (mt1) |t1| {
1059 try p.transition(&arena.allocator, input, i, t1);
1060
1061 if (mt2) |t2| {
1062 try p.transition(&arena.allocator, input, i, t2);
1063 }
1064 }
1065 }
1066
1067 // Handle top-level lonely number values.
1068 {
1069 const i = input.len;
1070 var mt1: ?Token = undefined;
1071 var mt2: ?Token = undefined;
1072
1073 try mp.feed(' ', &mt1, &mt2);
1074 if (mt1) |t1| {
1075 try p.transition(&arena.allocator, input, i, t1);
1076 }
1077 }
1078
1079 if (!mp.complete) {
1080 return error.IncompleteJsonInput;
1081 }
1082
1083 std.debug.assert(p.stack.len == 1);
1084
1085 return ValueTree {
1086 .arena = arena,
1087 .root = p.stack.at(0),
1088 };
1089 }
1090
1091 // Even though p.allocator exists, we take an explicit allocator so that allocation state
1092 // can be cleaned up on error correctly during a `parse` on call.
1093 fn transition(p: &JsonParser, allocator: &Allocator, input: []const u8, i: usize, token: &const Token) !void {
1094 switch (p.state) {
1095 State.ObjectKey => switch (token.id) {
1096 Token.Id.ObjectEnd => {
1097 if (p.stack.len == 1) {
1098 return;
1099 }
1100
1101 var value = p.stack.pop();
1102 try p.pushToParent(value);
1103 },
1104 Token.Id.String => {
1105 try p.stack.append(try p.parseString(allocator, token, input, i));
1106 p.state = State.ObjectValue;
1107 },
1108 else => {
1109 unreachable;
1110 },
1111 },
1112 State.ObjectValue => {
1113 var object = &p.stack.items[p.stack.len - 2].Object;
1114 var key = p.stack.items[p.stack.len - 1].String;
1115
1116 switch (token.id) {
1117 Token.Id.ObjectBegin => {
1118 try p.stack.append(Value { .Object = ObjectMap.init(allocator) });
1119 p.state = State.ObjectKey;
1120 },
1121 Token.Id.ArrayBegin => {
1122 try p.stack.append(Value { .Array = ArrayList(Value).init(allocator) });
1123 p.state = State.ArrayValue;
1124 },
1125 Token.Id.String => {
1126 _ = try object.put(key, try p.parseString(allocator, token, input, i));
1127 _ = p.stack.pop();
1128 p.state = State.ObjectKey;
1129 },
1130 Token.Id.Number => {
1131 _ = try object.put(key, try p.parseNumber(token, input, i));
1132 _ = p.stack.pop();
1133 p.state = State.ObjectKey;
1134 },
1135 Token.Id.True => {
1136 _ = try object.put(key, Value { .Bool = true });
1137 _ = p.stack.pop();
1138 p.state = State.ObjectKey;
1139 },
1140 Token.Id.False => {
1141 _ = try object.put(key, Value { .Bool = false });
1142 _ = p.stack.pop();
1143 p.state = State.ObjectKey;
1144 },
1145 Token.Id.Null => {
1146 _ = try object.put(key, Value.Null);
1147 _ = p.stack.pop();
1148 p.state = State.ObjectKey;
1149 },
1150 else => {
1151 unreachable;
1152 },
1153 }
1154 },
1155 State.ArrayValue => {
1156 var array = &p.stack.items[p.stack.len - 1].Array;
1157
1158 switch (token.id) {
1159 Token.Id.ArrayEnd => {
1160 if (p.stack.len == 1) {
1161 return;
1162 }
1163
1164 var value = p.stack.pop();
1165 try p.pushToParent(value);
1166 },
1167 Token.Id.ObjectBegin => {
1168 try p.stack.append(Value { .Object = ObjectMap.init(allocator) });
1169 p.state = State.ObjectKey;
1170 },
1171 Token.Id.ArrayBegin => {
1172 try p.stack.append(Value { .Array = ArrayList(Value).init(allocator) });
1173 p.state = State.ArrayValue;
1174 },
1175 Token.Id.String => {
1176 try array.append(try p.parseString(allocator, token, input, i));
1177 },
1178 Token.Id.Number => {
1179 try array.append(try p.parseNumber(token, input, i));
1180 },
1181 Token.Id.True => {
1182 try array.append(Value { .Bool = true });
1183 },
1184 Token.Id.False => {
1185 try array.append(Value { .Bool = false });
1186 },
1187 Token.Id.Null => {
1188 try array.append(Value.Null);
1189 },
1190 else => {
1191 unreachable;
1192 },
1193 }
1194 },
1195 State.Simple => switch (token.id) {
1196 Token.Id.ObjectBegin => {
1197 try p.stack.append(Value { .Object = ObjectMap.init(allocator) });
1198 p.state = State.ObjectKey;
1199 },
1200 Token.Id.ArrayBegin => {
1201 try p.stack.append(Value { .Array = ArrayList(Value).init(allocator) });
1202 p.state = State.ArrayValue;
1203 },
1204 Token.Id.String => {
1205 try p.stack.append(try p.parseString(allocator, token, input, i));
1206 },
1207 Token.Id.Number => {
1208 try p.stack.append(try p.parseNumber(token, input, i));
1209 },
1210 Token.Id.True => {
1211 try p.stack.append(Value { .Bool = true });
1212 },
1213 Token.Id.False => {
1214 try p.stack.append(Value { .Bool = false });
1215 },
1216 Token.Id.Null => {
1217 try p.stack.append(Value.Null);
1218 },
1219 Token.Id.ObjectEnd, Token.Id.ArrayEnd => {
1220 unreachable;
1221 },
1222 },
1223 }
1224 }
1225
1226 fn pushToParent(p: &JsonParser, value: &const Value) !void {
1227 switch (p.stack.at(p.stack.len - 1)) {
1228 // Object Parent -> [ ..., object, <key>, value ]
1229 Value.String => |key| {
1230 _ = p.stack.pop();
1231
1232 var object = &p.stack.items[p.stack.len - 1].Object;
1233 _ = try object.put(key, value);
1234 p.state = State.ObjectKey;
1235 },
1236 // Array Parent -> [ ..., <array>, value ]
1237 Value.Array => |*array| {
1238 try array.append(value);
1239 p.state = State.ArrayValue;
1240 },
1241 else => {
1242 unreachable;
1243 },
1244 }
1245 }
1246
1247 fn parseString(p: &JsonParser, allocator: &Allocator, token: &const Token, input: []const u8, i: usize) !Value {
1248 // TODO: We don't strictly have to copy values which do not contain any escape
1249 // characters if flagged with the option.
1250 const slice = token.slice(input, i);
1251 return Value { .String = try mem.dupe(p.allocator, u8, slice) };
1252 }
1253
1254 fn parseNumber(p: &JsonParser, token: &const Token, input: []const u8, i: usize) !Value {
1255 return if (token.number_is_integer)
1256 Value { .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) }
1257 else
1258 @panic("TODO: fmt.parseFloat not yet implemented")
1259 ;
1260 }
1261};
1262
1263const debug = std.debug;
1264
1265test "json parser dynamic" {
1266 var p = JsonParser.init(std.debug.global_allocator, false);
1267 defer p.deinit();
1268
1269 const s =
1270 \\{
1271 \\ "Image": {
1272 \\ "Width": 800,
1273 \\ "Height": 600,
1274 \\ "Title": "View from 15th Floor",
1275 \\ "Thumbnail": {
1276 \\ "Url": "http://www.example.com/image/481989943",
1277 \\ "Height": 125,
1278 \\ "Width": 100
1279 \\ },
1280 \\ "Animated" : false,
1281 \\ "IDs": [116, 943, 234, 38793]
1282 \\ }
1283 \\}
1284 ;
1285
1286 var tree = try p.parse(s);
1287 defer tree.deinit();
1288
1289 var root = tree.root;
1290
1291 var image = (??root.Object.get("Image")).value;
1292
1293 const width = (??image.Object.get("Width")).value;
1294 debug.assert(width.Integer == 800);
1295
1296 const height = (??image.Object.get("Height")).value;
1297 debug.assert(height.Integer == 600);
1298
1299 const title = (??image.Object.get("Title")).value;
1300 debug.assert(mem.eql(u8, title.String, "View from 15th Floor"));
1301
1302 const animated = (??image.Object.get("Animated")).value;
1303 debug.assert(animated.Bool == false);
1304}
std/json_test.zig created+1942
...@@ -0,0 +1,1942 @@
1// RFC 8529 conformance tests.
2//
3// Tests are taken from https://github.com/nst/JSONTestSuite
4// Read also http://seriot.ch/parsing_json.php for a good overview.
5
6const std = @import("index.zig");
7
8fn ok(comptime s: []const u8) void {
9 std.debug.assert(std.json.validate(s));
10}
11
12fn err(comptime s: []const u8) void {
13 std.debug.assert(!std.json.validate(s));
14}
15
16fn any(comptime s: []const u8) void {
17 std.debug.assert(true);
18}
19
20////////////////////////////////////////////////////////////////////////////////////////////////////
21
22test "y_array_arraysWithSpaces" {
23 ok(
24 \\[[] ]
25 );
26}
27
28test "y_array_empty" {
29 ok(
30 \\[]
31 );
32}
33
34test "y_array_empty-string" {
35 ok(
36 \\[""]
37 );
38}
39
40test "y_array_ending_with_newline" {
41 ok(
42 \\["a"]
43 );
44}
45
46test "y_array_false" {
47 ok(
48 \\[false]
49 );
50}
51
52test "y_array_heterogeneous" {
53 ok(
54 \\[null, 1, "1", {}]
55 );
56}
57
58test "y_array_null" {
59 ok(
60 \\[null]
61 );
62}
63
64test "y_array_with_1_and_newline" {
65 ok(
66 \\[1
67 \\]
68 );
69}
70
71test "y_array_with_leading_space" {
72 ok(
73 \\ [1]
74 );
75}
76
77test "y_array_with_several_null" {
78 ok(
79 \\[1,null,null,null,2]
80 );
81}
82
83test "y_array_with_trailing_space" {
84 ok(
85 "[2] "
86 );
87}
88
89test "y_number_0e+1" {
90 ok(
91 \\[0e+1]
92 );
93}
94
95test "y_number_0e1" {
96 ok(
97 \\[0e1]
98 );
99}
100
101test "y_number_after_space" {
102 ok(
103 \\[ 4]
104 );
105}
106
107test "y_number_double_close_to_zero" {
108 ok(
109 \\[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001]
110 );
111}
112
113test "y_number_int_with_exp" {
114 ok(
115 \\[20e1]
116 );
117}
118
119test "y_number" {
120 ok(
121 \\[123e65]
122 );
123}
124
125test "y_number_minus_zero" {
126 ok(
127 \\[-0]
128 );
129}
130
131test "y_number_negative_int" {
132 ok(
133 \\[-123]
134 );
135}
136
137test "y_number_negative_one" {
138 ok(
139 \\[-1]
140 );
141}
142
143test "y_number_negative_zero" {
144 ok(
145 \\[-0]
146 );
147}
148
149test "y_number_real_capital_e" {
150 ok(
151 \\[1E22]
152 );
153}
154
155test "y_number_real_capital_e_neg_exp" {
156 ok(
157 \\[1E-2]
158 );
159}
160
161test "y_number_real_capital_e_pos_exp" {
162 ok(
163 \\[1E+2]
164 );
165}
166
167test "y_number_real_exponent" {
168 ok(
169 \\[123e45]
170 );
171}
172
173test "y_number_real_fraction_exponent" {
174 ok(
175 \\[123.456e78]
176 );
177}
178
179test "y_number_real_neg_exp" {
180 ok(
181 \\[1e-2]
182 );
183}
184
185test "y_number_real_pos_exponent" {
186 ok(
187 \\[1e+2]
188 );
189}
190
191test "y_number_simple_int" {
192 ok(
193 \\[123]
194 );
195}
196
197test "y_number_simple_real" {
198 ok(
199 \\[123.456789]
200 );
201}
202
203test "y_object_basic" {
204 ok(
205 \\{"asd":"sdf"}
206 );
207}
208
209test "y_object_duplicated_key_and_value" {
210 ok(
211 \\{"a":"b","a":"b"}
212 );
213}
214
215test "y_object_duplicated_key" {
216 ok(
217 \\{"a":"b","a":"c"}
218 );
219}
220
221test "y_object_empty" {
222 ok(
223 \\{}
224 );
225}
226
227test "y_object_empty_key" {
228 ok(
229 \\{"":0}
230 );
231}
232
233test "y_object_escaped_null_in_key" {
234 ok(
235 \\{"foo\u0000bar": 42}
236 );
237}
238
239test "y_object_extreme_numbers" {
240 ok(
241 \\{ "min": -1.0e+28, "max": 1.0e+28 }
242 );
243}
244
245test "y_object" {
246 ok(
247 \\{"asd":"sdf", "dfg":"fgh"}
248 );
249}
250
251test "y_object_long_strings" {
252 ok(
253 \\{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
254 );
255}
256
257test "y_object_simple" {
258 ok(
259 \\{"a":[]}
260 );
261}
262
263test "y_object_string_unicode" {
264 ok(
265 \\{"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" }
266 );
267}
268
269test "y_object_with_newlines" {
270 ok(
271 \\{
272 \\"a": "b"
273 \\}
274 );
275}
276
277test "y_string_1_2_3_bytes_UTF-8_sequences" {
278 ok(
279 \\["\u0060\u012a\u12AB"]
280 );
281}
282
283test "y_string_accepted_surrogate_pair" {
284 ok(
285 \\["\uD801\udc37"]
286 );
287}
288
289test "y_string_accepted_surrogate_pairs" {
290 ok(
291 \\["\ud83d\ude39\ud83d\udc8d"]
292 );
293}
294
295test "y_string_allowed_escapes" {
296 ok(
297 \\["\"\\\/\b\f\n\r\t"]
298 );
299}
300
301test "y_string_backslash_and_u_escaped_zero" {
302 ok(
303 \\["\\u0000"]
304 );
305}
306
307test "y_string_backslash_doublequotes" {
308 ok(
309 \\["\""]
310 );
311}
312
313test "y_string_comments" {
314 ok(
315 \\["a/*b*/c/*d//e"]
316 );
317}
318
319test "y_string_double_escape_a" {
320 ok(
321 \\["\\a"]
322 );
323}
324
325test "y_string_double_escape_n" {
326 ok(
327 \\["\\n"]
328 );
329}
330
331test "y_string_escaped_control_character" {
332 ok(
333 \\["\u0012"]
334 );
335}
336
337test "y_string_escaped_noncharacter" {
338 ok(
339 \\["\uFFFF"]
340 );
341}
342
343test "y_string_in_array" {
344 ok(
345 \\["asd"]
346 );
347}
348
349test "y_string_in_array_with_leading_space" {
350 ok(
351 \\[ "asd"]
352 );
353}
354
355test "y_string_last_surrogates_1_and_2" {
356 ok(
357 \\["\uDBFF\uDFFF"]
358 );
359}
360
361test "y_string_nbsp_uescaped" {
362 ok(
363 \\["new\u00A0line"]
364 );
365}
366
367test "y_string_nonCharacterInUTF-8_U+10FFFF" {
368 ok(
369 \\["􏿿"]
370 );
371}
372
373test "y_string_nonCharacterInUTF-8_U+FFFF" {
374 ok(
375 \\["￿"]
376 );
377}
378
379test "y_string_null_escape" {
380 ok(
381 \\["\u0000"]
382 );
383}
384
385test "y_string_one-byte-utf-8" {
386 ok(
387 \\["\u002c"]
388 );
389}
390
391test "y_string_pi" {
392 ok(
393 \\["π"]
394 );
395}
396
397test "y_string_reservedCharacterInUTF-8_U+1BFFF" {
398 ok(
399 \\["𛿿"]
400 );
401}
402
403test "y_string_simple_ascii" {
404 ok(
405 \\["asd "]
406 );
407}
408
409test "y_string_space" {
410 ok(
411 \\" "
412 );
413}
414
415test "y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF" {
416 ok(
417 \\["\uD834\uDd1e"]
418 );
419}
420
421test "y_string_three-byte-utf-8" {
422 ok(
423 \\["\u0821"]
424 );
425}
426
427test "y_string_two-byte-utf-8" {
428 ok(
429 \\["\u0123"]
430 );
431}
432
433test "y_string_u+2028_line_sep" {
434 ok(
435 \\["
"]
436 );
437}
438
439test "y_string_u+2029_par_sep" {
440 ok(
441 \\["
"]
442 );
443}
444
445test "y_string_uescaped_newline" {
446 ok(
447 \\["new\u000Aline"]
448 );
449}
450
451test "y_string_uEscape" {
452 ok(
453 \\["\u0061\u30af\u30EA\u30b9"]
454 );
455}
456
457test "y_string_unescaped_char_delete" {
458 ok(
459 \\[""]
460 );
461}
462
463test "y_string_unicode_2" {
464 ok(
465 \\["⍂㈴⍂"]
466 );
467}
468
469test "y_string_unicodeEscapedBackslash" {
470 ok(
471 \\["\u005C"]
472 );
473}
474
475test "y_string_unicode_escaped_double_quote" {
476 ok(
477 \\["\u0022"]
478 );
479}
480
481test "y_string_unicode" {
482 ok(
483 \\["\uA66D"]
484 );
485}
486
487test "y_string_unicode_U+10FFFE_nonchar" {
488 ok(
489 \\["\uDBFF\uDFFE"]
490 );
491}
492
493test "y_string_unicode_U+1FFFE_nonchar" {
494 ok(
495 \\["\uD83F\uDFFE"]
496 );
497}
498
499test "y_string_unicode_U+200B_ZERO_WIDTH_SPACE" {
500 ok(
501 \\["\u200B"]
502 );
503}
504
505test "y_string_unicode_U+2064_invisible_plus" {
506 ok(
507 \\["\u2064"]
508 );
509}
510
511test "y_string_unicode_U+FDD0_nonchar" {
512 ok(
513 \\["\uFDD0"]
514 );
515}
516
517test "y_string_unicode_U+FFFE_nonchar" {
518 ok(
519 \\["\uFFFE"]
520 );
521}
522
523test "y_string_utf8" {
524 ok(
525 \\["€𝄞"]
526 );
527}
528
529test "y_string_with_del_character" {
530 ok(
531 \\["aa"]
532 );
533}
534
535test "y_structure_lonely_false" {
536 ok(
537 \\false
538 );
539}
540
541test "y_structure_lonely_int" {
542 ok(
543 \\42
544 );
545}
546
547test "y_structure_lonely_negative_real" {
548 ok(
549 \\-0.1
550 );
551}
552
553test "y_structure_lonely_null" {
554 ok(
555 \\null
556 );
557}
558
559test "y_structure_lonely_string" {
560 ok(
561 \\"asd"
562 );
563}
564
565test "y_structure_lonely_true" {
566 ok(
567 \\true
568 );
569}
570
571test "y_structure_string_empty" {
572 ok(
573 \\""
574 );
575}
576
577test "y_structure_trailing_newline" {
578 ok(
579 \\["a"]
580 );
581}
582
583test "y_structure_true_in_array" {
584 ok(
585 \\[true]
586 );
587}
588
589test "y_structure_whitespace_array" {
590 ok(
591 " [] "
592 );
593}
594
595////////////////////////////////////////////////////////////////////////////////////////////////////
596
597test "n_array_1_true_without_comma" {
598 err(
599 \\[1 true]
600 );
601}
602
603test "n_array_a_invalid_utf8" {
604 err(
605 \\[aå]
606 );
607}
608
609test "n_array_colon_instead_of_comma" {
610 err(
611 \\["": 1]
612 );
613}
614
615test "n_array_comma_after_close" {
616 //err(
617 // \\[""],
618 //);
619}
620
621test "n_array_comma_and_number" {
622 err(
623 \\[,1]
624 );
625}
626
627test "n_array_double_comma" {
628 err(
629 \\[1,,2]
630 );
631}
632
633test "n_array_double_extra_comma" {
634 err(
635 \\["x",,]
636 );
637}
638
639test "n_array_extra_close" {
640 err(
641 \\["x"]]
642 );
643}
644
645test "n_array_extra_comma" {
646 //err(
647 // \\["",]
648 //);
649}
650
651test "n_array_incomplete_invalid_value" {
652 err(
653 \\[x
654 );
655}
656
657test "n_array_incomplete" {
658 err(
659 \\["x"
660 );
661}
662
663test "n_array_inner_array_no_comma" {
664 err(
665 \\[3[4]]
666 );
667}
668
669test "n_array_invalid_utf8" {
670 err(
671 \\[ÿ]
672 );
673}
674
675test "n_array_items_separated_by_semicolon" {
676 err(
677 \\[1:2]
678 );
679}
680
681test "n_array_just_comma" {
682 err(
683 \\[,]
684 );
685}
686
687test "n_array_just_minus" {
688 err(
689 \\[-]
690 );
691}
692
693test "n_array_missing_value" {
694 err(
695 \\[ , ""]
696 );
697}
698
699test "n_array_newlines_unclosed" {
700 err(
701 \\["a",
702 \\4
703 \\,1,
704 );
705}
706
707
708test "n_array_number_and_comma" {
709 err(
710 \\[1,]
711 );
712}
713
714test "n_array_number_and_several_commas" {
715 err(
716 \\[1,,]
717 );
718}
719
720test "n_array_spaces_vertical_tab_formfeed" {
721 err(
722 \\[" a"\f]
723 );
724}
725
726test "n_array_star_inside" {
727 err(
728 \\[*]
729 );
730}
731
732test "n_array_unclosed" {
733 err(
734 \\[""
735 );
736}
737
738test "n_array_unclosed_trailing_comma" {
739 err(
740 \\[1,
741 );
742}
743
744test "n_array_unclosed_with_new_lines" {
745 err(
746 \\[1,
747 \\1
748 \\,1
749 );
750}
751
752test "n_array_unclosed_with_object_inside" {
753 err(
754 \\[{}
755 );
756}
757
758test "n_incomplete_false" {
759 err(
760 \\[fals]
761 );
762}
763
764test "n_incomplete_null" {
765 err(
766 \\[nul]
767 );
768}
769
770test "n_incomplete_true" {
771 err(
772 \\[tru]
773 );
774}
775
776test "n_multidigit_number_then_00" {
777 err(
778 \\123
779 );
780}
781
782test "n_number_0.1.2" {
783 err(
784 \\[0.1.2]
785 );
786}
787
788test "n_number_-01" {
789 err(
790 \\[-01]
791 );
792}
793
794test "n_number_0.3e" {
795 err(
796 \\[0.3e]
797 );
798}
799
800test "n_number_0.3e+" {
801 err(
802 \\[0.3e+]
803 );
804}
805
806test "n_number_0_capital_E" {
807 err(
808 \\[0E]
809 );
810}
811
812test "n_number_0_capital_E+" {
813 err(
814 \\[0E+]
815 );
816}
817
818test "n_number_0.e1" {
819 err(
820 \\[0.e1]
821 );
822}
823
824test "n_number_0e" {
825 err(
826 \\[0e]
827 );
828}
829
830test "n_number_0e+" {
831 err(
832 \\[0e+]
833 );
834}
835
836test "n_number_1_000" {
837 err(
838 \\[1 000.0]
839 );
840}
841
842test "n_number_1.0e-" {
843 err(
844 \\[1.0e-]
845 );
846}
847
848test "n_number_1.0e" {
849 err(
850 \\[1.0e]
851 );
852}
853
854test "n_number_1.0e+" {
855 err(
856 \\[1.0e+]
857 );
858}
859
860test "n_number_-1.0." {
861 err(
862 \\[-1.0.]
863 );
864}
865
866test "n_number_1eE2" {
867 err(
868 \\[1eE2]
869 );
870}
871
872test "n_number_.-1" {
873 err(
874 \\[.-1]
875 );
876}
877
878test "n_number_+1" {
879 err(
880 \\[+1]
881 );
882}
883
884test "n_number_.2e-3" {
885 err(
886 \\[.2e-3]
887 );
888}
889
890test "n_number_2.e-3" {
891 err(
892 \\[2.e-3]
893 );
894}
895
896test "n_number_2.e+3" {
897 err(
898 \\[2.e+3]
899 );
900}
901
902test "n_number_2.e3" {
903 err(
904 \\[2.e3]
905 );
906}
907
908test "n_number_-2." {
909 err(
910 \\[-2.]
911 );
912}
913
914test "n_number_9.e+" {
915 err(
916 \\[9.e+]
917 );
918}
919
920test "n_number_expression" {
921 err(
922 \\[1+2]
923 );
924}
925
926test "n_number_hex_1_digit" {
927 err(
928 \\[0x1]
929 );
930}
931
932test "n_number_hex_2_digits" {
933 err(
934 \\[0x42]
935 );
936}
937
938test "n_number_infinity" {
939 err(
940 \\[Infinity]
941 );
942}
943
944test "n_number_+Inf" {
945 err(
946 \\[+Inf]
947 );
948}
949
950test "n_number_Inf" {
951 err(
952 \\[Inf]
953 );
954}
955
956test "n_number_invalid+-" {
957 err(
958 \\[0e+-1]
959 );
960}
961
962test "n_number_invalid-negative-real" {
963 err(
964 \\[-123.123foo]
965 );
966}
967
968test "n_number_invalid-utf-8-in-bigger-int" {
969 err(
970 \\[123å]
971 );
972}
973
974test "n_number_invalid-utf-8-in-exponent" {
975 err(
976 \\[1e1å]
977 );
978}
979
980test "n_number_invalid-utf-8-in-int" {
981 err(
982 \\[0å]
983 );
984}
985
986
987test "n_number_++" {
988 err(
989 \\[++1234]
990 );
991}
992
993test "n_number_minus_infinity" {
994 err(
995 \\[-Infinity]
996 );
997}
998
999test "n_number_minus_sign_with_trailing_garbage" {
1000 err(
1001 \\[-foo]
1002 );
1003}
1004
1005test "n_number_minus_space_1" {
1006 err(
1007 \\[- 1]
1008 );
1009}
1010
1011test "n_number_-NaN" {
1012 err(
1013 \\[-NaN]
1014 );
1015}
1016
1017test "n_number_NaN" {
1018 err(
1019 \\[NaN]
1020 );
1021}
1022
1023test "n_number_neg_int_starting_with_zero" {
1024 err(
1025 \\[-012]
1026 );
1027}
1028
1029test "n_number_neg_real_without_int_part" {
1030 err(
1031 \\[-.123]
1032 );
1033}
1034
1035test "n_number_neg_with_garbage_at_end" {
1036 err(
1037 \\[-1x]
1038 );
1039}
1040
1041test "n_number_real_garbage_after_e" {
1042 err(
1043 \\[1ea]
1044 );
1045}
1046
1047test "n_number_real_with_invalid_utf8_after_e" {
1048 err(
1049 \\[1eå]
1050 );
1051}
1052
1053test "n_number_real_without_fractional_part" {
1054 err(
1055 \\[1.]
1056 );
1057}
1058
1059test "n_number_starting_with_dot" {
1060 err(
1061 \\[.123]
1062 );
1063}
1064
1065test "n_number_U+FF11_fullwidth_digit_one" {
1066 err(
1067 \\[1]
1068 );
1069}
1070
1071test "n_number_with_alpha_char" {
1072 err(
1073 \\[1.8011670033376514H-308]
1074 );
1075}
1076
1077test "n_number_with_alpha" {
1078 err(
1079 \\[1.2a-3]
1080 );
1081}
1082
1083test "n_number_with_leading_zero" {
1084 err(
1085 \\[012]
1086 );
1087}
1088
1089test "n_object_bad_value" {
1090 err(
1091 \\["x", truth]
1092 );
1093}
1094
1095test "n_object_bracket_key" {
1096 err(
1097 \\{[: "x"}
1098 );
1099}
1100
1101test "n_object_comma_instead_of_colon" {
1102 err(
1103 \\{"x", null}
1104 );
1105}
1106
1107test "n_object_double_colon" {
1108 err(
1109 \\{"x"::"b"}
1110 );
1111}
1112
1113test "n_object_emoji" {
1114 err(
1115 \\{🇨🇭}
1116 );
1117}
1118
1119test "n_object_garbage_at_end" {
1120 err(
1121 \\{"a":"a" 123}
1122 );
1123}
1124
1125test "n_object_key_with_single_quotes" {
1126 err(
1127 \\{key: 'value'}
1128 );
1129}
1130
1131test "n_object_lone_continuation_byte_in_key_and_trailing_comma" {
1132 err(
1133 \\{"¹":"0",}
1134 );
1135}
1136
1137test "n_object_missing_colon" {
1138 err(
1139 \\{"a" b}
1140 );
1141}
1142
1143test "n_object_missing_key" {
1144 err(
1145 \\{:"b"}
1146 );
1147}
1148
1149test "n_object_missing_semicolon" {
1150 err(
1151 \\{"a" "b"}
1152 );
1153}
1154
1155test "n_object_missing_value" {
1156 err(
1157 \\{"a":
1158 );
1159}
1160
1161test "n_object_no-colon" {
1162 err(
1163 \\{"a"
1164 );
1165}
1166
1167test "n_object_non_string_key_but_huge_number_instead" {
1168 err(
1169 \\{9999E9999:1}
1170 );
1171}
1172
1173test "n_object_non_string_key" {
1174 err(
1175 \\{1:1}
1176 );
1177}
1178
1179test "n_object_repeated_null_null" {
1180 err(
1181 \\{null:null,null:null}
1182 );
1183}
1184
1185test "n_object_several_trailing_commas" {
1186 err(
1187 \\{"id":0,,,,,}
1188 );
1189}
1190
1191test "n_object_single_quote" {
1192 err(
1193 \\{'a':0}
1194 );
1195}
1196
1197test "n_object_trailing_comma" {
1198 err(
1199 \\{"id":0,}
1200 );
1201}
1202
1203test "n_object_trailing_comment" {
1204 err(
1205 \\{"a":"b"}/**/
1206 );
1207}
1208
1209test "n_object_trailing_comment_open" {
1210 err(
1211 \\{"a":"b"}/**//
1212 );
1213}
1214
1215test "n_object_trailing_comment_slash_open_incomplete" {
1216 err(
1217 \\{"a":"b"}/
1218 );
1219}
1220
1221test "n_object_trailing_comment_slash_open" {
1222 err(
1223 \\{"a":"b"}//
1224 );
1225}
1226
1227test "n_object_two_commas_in_a_row" {
1228 err(
1229 \\{"a":"b",,"c":"d"}
1230 );
1231}
1232
1233test "n_object_unquoted_key" {
1234 err(
1235 \\{a: "b"}
1236 );
1237}
1238
1239test "n_object_unterminated-value" {
1240 err(
1241 \\{"a":"a
1242 );
1243 }
1244
1245test "n_object_with_single_string" {
1246 err(
1247 \\{ "foo" : "bar", "a" }
1248 );
1249}
1250
1251test "n_object_with_trailing_garbage" {
1252 err(
1253 \\{"a":"b"}#
1254 );
1255}
1256
1257test "n_single_space" {
1258 err(
1259 " "
1260 );
1261}
1262
1263test "n_string_1_surrogate_then_escape" {
1264 err(
1265 \\["\uD800\"]
1266 );
1267}
1268
1269test "n_string_1_surrogate_then_escape_u1" {
1270 err(
1271 \\["\uD800\u1"]
1272 );
1273}
1274
1275test "n_string_1_surrogate_then_escape_u1x" {
1276 err(
1277 \\["\uD800\u1x"]
1278 );
1279}
1280
1281test "n_string_1_surrogate_then_escape_u" {
1282 err(
1283 \\["\uD800\u"]
1284 );
1285}
1286
1287test "n_string_accentuated_char_no_quotes" {
1288 err(
1289 \\[é]
1290 );
1291}
1292
1293test "n_string_backslash_00" {
1294 err(
1295 \\["\"]
1296 );
1297}
1298
1299test "n_string_escaped_backslash_bad" {
1300 err(
1301 \\["\\\"]
1302 );
1303}
1304
1305test "n_string_escaped_ctrl_char_tab" {
1306 err(
1307 \\["\ "]
1308 );
1309}
1310
1311test "n_string_escaped_emoji" {
1312 err(
1313 \\["\🌀"]
1314 );
1315}
1316
1317test "n_string_escape_x" {
1318 err(
1319 \\["\x00"]
1320 );
1321}
1322
1323test "n_string_incomplete_escaped_character" {
1324 err(
1325 \\["\u00A"]
1326 );
1327}
1328
1329test "n_string_incomplete_escape" {
1330 err(
1331 \\["\"]
1332 );
1333}
1334
1335test "n_string_incomplete_surrogate_escape_invalid" {
1336 err(
1337 \\["\uD800\uD800\x"]
1338 );
1339}
1340
1341test "n_string_incomplete_surrogate" {
1342 err(
1343 \\["\uD834\uDd"]
1344 );
1345}
1346
1347test "n_string_invalid_backslash_esc" {
1348 err(
1349 \\["\a"]
1350 );
1351}
1352
1353test "n_string_invalid_unicode_escape" {
1354 err(
1355 \\["\uqqqq"]
1356 );
1357}
1358
1359test "n_string_invalid_utf8_after_escape" {
1360 err(
1361 \\["\å"]
1362 );
1363}
1364
1365test "n_string_invalid-utf-8-in-escape" {
1366 err(
1367 \\["\uå"]
1368 );
1369}
1370
1371test "n_string_leading_uescaped_thinspace" {
1372 err(
1373 \\[\u0020"asd"]
1374 );
1375}
1376
1377test "n_string_no_quotes_with_bad_escape" {
1378 err(
1379 \\[\n]
1380 );
1381}
1382
1383test "n_string_single_doublequote" {
1384 err(
1385 \\"
1386 );
1387}
1388
1389test "n_string_single_quote" {
1390 err(
1391 \\['single quote']
1392 );
1393}
1394
1395test "n_string_single_string_no_double_quotes" {
1396 err(
1397 \\abc
1398 );
1399}
1400
1401test "n_string_start_escape_unclosed" {
1402 err(
1403 \\["\
1404 );
1405}
1406
1407test "n_string_unescaped_crtl_char" {
1408 err(
1409 \\["aa"]
1410 );
1411}
1412
1413test "n_string_unescaped_newline" {
1414 err(
1415 \\["new
1416 \\line"]
1417 );
1418}
1419
1420test "n_string_unescaped_tab" {
1421 err(
1422 \\[" "]
1423 );
1424}
1425
1426test "n_string_unicode_CapitalU" {
1427 err(
1428 \\"\UA66D"
1429 );
1430}
1431
1432test "n_string_with_trailing_garbage" {
1433 err(
1434 \\""x
1435 );
1436}
1437
1438test "n_structure_100000_opening_arrays" {
1439 err(
1440 "[" ** 100000
1441 );
1442}
1443
1444test "n_structure_angle_bracket_." {
1445 err(
1446 \\<.>
1447 );
1448}
1449
1450test "n_structure_angle_bracket_null" {
1451 err(
1452 \\[<null>]
1453 );
1454}
1455
1456test "n_structure_array_trailing_garbage" {
1457 err(
1458 \\[1]x
1459 );
1460}
1461
1462test "n_structure_array_with_extra_array_close" {
1463 err(
1464 \\[1]]
1465 );
1466}
1467
1468test "n_structure_array_with_unclosed_string" {
1469 err(
1470 \\["asd]
1471 );
1472}
1473
1474test "n_structure_ascii-unicode-identifier" {
1475 err(
1476 \\aå
1477 );
1478}
1479
1480test "n_structure_capitalized_True" {
1481 err(
1482 \\[True]
1483 );
1484}
1485
1486test "n_structure_close_unopened_array" {
1487 err(
1488 \\1]
1489 );
1490}
1491
1492test "n_structure_comma_instead_of_closing_brace" {
1493 err(
1494 \\{"x": true,
1495 );
1496}
1497
1498test "n_structure_double_array" {
1499 err(
1500 \\[][]
1501 );
1502}
1503
1504test "n_structure_end_array" {
1505 err(
1506 \\]
1507 );
1508}
1509
1510test "n_structure_incomplete_UTF8_BOM" {
1511 err(
1512 \\ï»{}
1513 );
1514}
1515
1516test "n_structure_lone-invalid-utf-8" {
1517 err(
1518 \\å
1519 );
1520}
1521
1522test "n_structure_lone-open-bracket" {
1523 err(
1524 \\[
1525 );
1526}
1527
1528test "n_structure_no_data" {
1529 err(
1530 \\
1531 );
1532}
1533
1534test "n_structure_null-byte-outside-string" {
1535 err(
1536 \\[]
1537 );
1538}
1539
1540test "n_structure_number_with_trailing_garbage" {
1541 err(
1542 \\2@
1543 );
1544}
1545
1546test "n_structure_object_followed_by_closing_object" {
1547 err(
1548 \\{}}
1549 );
1550}
1551
1552test "n_structure_object_unclosed_no_value" {
1553 err(
1554 \\{"":
1555 );
1556}
1557
1558test "n_structure_object_with_comment" {
1559 err(
1560 \\{"a":/*comment*/"b"}
1561 );
1562}
1563
1564test "n_structure_object_with_trailing_garbage" {
1565 err(
1566 \\{"a": true} "x"
1567 );
1568}
1569
1570test "n_structure_open_array_apostrophe" {
1571 err(
1572 \\['
1573 );
1574}
1575
1576test "n_structure_open_array_comma" {
1577 err(
1578 \\[,
1579 );
1580}
1581
1582test "n_structure_open_array_object" {
1583 err(
1584 "[{\"\":" ** 50000
1585 );
1586}
1587
1588test "n_structure_open_array_open_object" {
1589 err(
1590 \\[{
1591 );
1592}
1593
1594test "n_structure_open_array_open_string" {
1595 err(
1596 \\["a
1597 );
1598}
1599
1600test "n_structure_open_array_string" {
1601 err(
1602 \\["a"
1603 );
1604}
1605
1606test "n_structure_open_object_close_array" {
1607 err(
1608 \\{]
1609 );
1610}
1611
1612test "n_structure_open_object_comma" {
1613 err(
1614 \\{,
1615 );
1616}
1617
1618test "n_structure_open_object" {
1619 err(
1620 \\{
1621 );
1622}
1623
1624test "n_structure_open_object_open_array" {
1625 err(
1626 \\{[
1627 );
1628}
1629
1630test "n_structure_open_object_open_string" {
1631 err(
1632 \\{"a
1633 );
1634}
1635
1636test "n_structure_open_object_string_with_apostrophes" {
1637 err(
1638 \\{'a'
1639 );
1640}
1641
1642test "n_structure_open_open" {
1643 err(
1644 \\["\{["\{["\{["\{
1645 );
1646}
1647
1648test "n_structure_single_eacute" {
1649 err(
1650 \\é
1651 );
1652}
1653
1654test "n_structure_single_star" {
1655 err(
1656 \\*
1657 );
1658}
1659
1660test "n_structure_trailing_#" {
1661 err(
1662 \\{"a":"b"}#{}
1663 );
1664}
1665
1666test "n_structure_U+2060_word_joined" {
1667 err(
1668 \\[⁠]
1669 );
1670}
1671
1672test "n_structure_uescaped_LF_before_string" {
1673 err(
1674 \\[\u000A""]
1675 );
1676}
1677
1678test "n_structure_unclosed_array" {
1679 err(
1680 \\[1
1681 );
1682}
1683
1684test "n_structure_unclosed_array_partial_null" {
1685 err(
1686 \\[ false, nul
1687 );
1688}
1689
1690test "n_structure_unclosed_array_unfinished_false" {
1691 err(
1692 \\[ true, fals
1693 );
1694}
1695
1696test "n_structure_unclosed_array_unfinished_true" {
1697 err(
1698 \\[ false, tru
1699 );
1700}
1701
1702test "n_structure_unclosed_object" {
1703 err(
1704 \\{"asd":"asd"
1705 );
1706}
1707
1708test "n_structure_unicode-identifier" {
1709 err(
1710 \\Ã¥
1711 );
1712}
1713
1714test "n_structure_UTF8_BOM_no_data" {
1715 err(
1716 \\
1717 );
1718}
1719
1720test "n_structure_whitespace_formfeed" {
1721 err(
1722 \\[ ]
1723 );
1724}
1725
1726test "n_structure_whitespace_U+2060_word_joiner" {
1727 err(
1728 \\[⁠]
1729 );
1730}
1731
1732////////////////////////////////////////////////////////////////////////////////////////////////////
1733
1734test "i_number_double_huge_neg_exp" {
1735 any(
1736 \\[123.456e-789]
1737 );
1738}
1739
1740test "i_number_huge_exp" {
1741 any(
1742 \\[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]
1743 );
1744}
1745
1746test "i_number_neg_int_huge_exp" {
1747 any(
1748 \\[-1e+9999]
1749 );
1750}
1751
1752test "i_number_pos_double_huge_exp" {
1753 any(
1754 \\[1.5e+9999]
1755 );
1756}
1757
1758test "i_number_real_neg_overflow" {
1759 any(
1760 \\[-123123e100000]
1761 );
1762}
1763
1764test "i_number_real_pos_overflow" {
1765 any(
1766 \\[123123e100000]
1767 );
1768}
1769
1770test "i_number_real_underflow" {
1771 any(
1772 \\[123e-10000000]
1773 );
1774}
1775
1776test "i_number_too_big_neg_int" {
1777 any(
1778 \\[-123123123123123123123123123123]
1779 );
1780}
1781
1782test "i_number_too_big_pos_int" {
1783 any(
1784 \\[100000000000000000000]
1785 );
1786}
1787
1788test "i_number_very_big_negative_int" {
1789 any(
1790 \\[-237462374673276894279832749832423479823246327846]
1791 );
1792}
1793
1794test "i_object_key_lone_2nd_surrogate" {
1795 any(
1796 \\{"\uDFAA":0}
1797 );
1798}
1799
1800test "i_string_1st_surrogate_but_2nd_missing" {
1801 any(
1802 \\["\uDADA"]
1803 );
1804}
1805
1806test "i_string_1st_valid_surrogate_2nd_invalid" {
1807 any(
1808 \\["\uD888\u1234"]
1809 );
1810}
1811
1812test "i_string_incomplete_surrogate_and_escape_valid" {
1813 any(
1814 \\["\uD800\n"]
1815 );
1816}
1817
1818test "i_string_incomplete_surrogate_pair" {
1819 any(
1820 \\["\uDd1ea"]
1821 );
1822}
1823
1824test "i_string_incomplete_surrogates_escape_valid" {
1825 any(
1826 \\["\uD800\uD800\n"]
1827 );
1828}
1829
1830test "i_string_invalid_lonely_surrogate" {
1831 any(
1832 \\["\ud800"]
1833 );
1834}
1835
1836test "i_string_invalid_surrogate" {
1837 any(
1838 \\["\ud800abc"]
1839 );
1840}
1841
1842test "i_string_invalid_utf-8" {
1843 any(
1844 \\["ÿ"]
1845 );
1846}
1847
1848test "i_string_inverted_surrogates_U+1D11E" {
1849 any(
1850 \\["\uDd1e\uD834"]
1851 );
1852}
1853
1854test "i_string_iso_latin_1" {
1855 any(
1856 \\["é"]
1857 );
1858}
1859
1860test "i_string_lone_second_surrogate" {
1861 any(
1862 \\["\uDFAA"]
1863 );
1864}
1865
1866test "i_string_lone_utf8_continuation_byte" {
1867 any(
1868 \\[""]
1869 );
1870}
1871
1872test "i_string_not_in_unicode_range" {
1873 any(
1874 \\["ô¿¿¿"]
1875 );
1876}
1877
1878test "i_string_overlong_sequence_2_bytes" {
1879 any(
1880 \\["À¯"]
1881 );
1882}
1883
1884test "i_string_overlong_sequence_6_bytes" {
1885 any(
1886 \\["üƒ¿¿¿¿"]
1887 );
1888}
1889
1890test "i_string_overlong_sequence_6_bytes_null" {
1891 any(
1892 \\["ü€€€€€"]
1893 );
1894}
1895
1896test "i_string_truncated-utf-8" {
1897 any(
1898 \\["àÿ"]
1899 );
1900}
1901
1902test "i_string_utf16BE_no_BOM" {
1903 any(
1904 \\["é"]
1905 );
1906}
1907
1908test "i_string_utf16LE_no_BOM" {
1909 any(
1910 \\["é"]
1911 );
1912}
1913
1914test "i_string_UTF-16LE_with_BOM" {
1915 any(
1916 \\ÿþ["é"]
1917 );
1918}
1919
1920test "i_string_UTF-8_invalid_sequence" {
1921 any(
1922 \\["日шú"]
1923 );
1924}
1925
1926test "i_string_UTF8_surrogate_U+D800" {
1927 any(
1928 \\["í €"]
1929 );
1930}
1931
1932test "i_structure_500_nested_arrays" {
1933 any(
1934 ("[" ** 500) ++ ("]" ** 500)
1935 );
1936}
1937
1938test "i_structure_UTF-8_BOM_empty_object" {
1939 any(
1940 \\{}
1941 );
1942}
std/linked_list.zig+1
...@@ -161,6 +161,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na...@@ -161,6 +161,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
161 }161 }
162162
163 list.len -= 1;163 list.len -= 1;
164 assert(list.len == 0 or (list.first != null and list.last != null));
164 }165 }
165166
166 /// Remove and return the last node in the list.167 /// Remove and return the last node in the list.
std/math/complex/abs.zig created+18
...@@ -0,0 +1,18 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn abs(z: var) @typeOf(z.re) {
8 const T = @typeOf(z.re);
9 return math.hypot(T, z.re, z.im);
10}
11
12const epsilon = 0.0001;
13
14test "complex.cabs" {
15 const a = Complex(f32).new(5, 3);
16 const c = abs(a);
17 debug.assert(math.approxEq(f32, c, 5.83095, epsilon));
18}
std/math/complex/acos.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn acos(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const q = cmath.asin(z);
10 return Complex(T).new(T(math.pi) / 2 - q.re, -q.im);
11}
12
13const epsilon = 0.0001;
14
15test "complex.cacos" {
16 const a = Complex(f32).new(5, 3);
17 const c = acos(a);
18
19 debug.assert(math.approxEq(f32, c.re, 0.546975, epsilon));
20 debug.assert(math.approxEq(f32, c.im, -2.452914, epsilon));
21}
std/math/complex/acosh.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn acosh(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const q = cmath.acos(z);
10 return Complex(T).new(-q.im, q.re);
11}
12
13const epsilon = 0.0001;
14
15test "complex.cacosh" {
16 const a = Complex(f32).new(5, 3);
17 const c = acosh(a);
18
19 debug.assert(math.approxEq(f32, c.re, 2.452914, epsilon));
20 debug.assert(math.approxEq(f32, c.im, 0.546975, epsilon));
21}
std/math/complex/arg.zig created+18
...@@ -0,0 +1,18 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn arg(z: var) @typeOf(z.re) {
8 const T = @typeOf(z.re);
9 return math.atan2(T, z.im, z.re);
10}
11
12const epsilon = 0.0001;
13
14test "complex.carg" {
15 const a = Complex(f32).new(5, 3);
16 const c = arg(a);
17 debug.assert(math.approxEq(f32, c, 0.540420, epsilon));
18}
std/math/complex/asin.zig created+27
...@@ -0,0 +1,27 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn asin(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const x = z.re;
10 const y = z.im;
11
12 const p = Complex(T).new(1.0 - (x - y) * (x + y), -2.0 * x * y);
13 const q = Complex(T).new(-y, x);
14 const r = cmath.log(q.add(cmath.sqrt(p)));
15
16 return Complex(T).new(r.im, -r.re);
17}
18
19const epsilon = 0.0001;
20
21test "complex.casin" {
22 const a = Complex(f32).new(5, 3);
23 const c = asin(a);
24
25 debug.assert(math.approxEq(f32, c.re, 1.023822, epsilon));
26 debug.assert(math.approxEq(f32, c.im, 2.452914, epsilon));
27}
std/math/complex/asinh.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn asinh(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const q = Complex(T).new(-z.im, z.re);
10 const r = cmath.asin(q);
11 return Complex(T).new(r.im, -r.re);
12}
13
14const epsilon = 0.0001;
15
16test "complex.casinh" {
17 const a = Complex(f32).new(5, 3);
18 const c = asinh(a);
19
20 debug.assert(math.approxEq(f32, c.re, 2.459831, epsilon));
21 debug.assert(math.approxEq(f32, c.im, 0.533999, epsilon));
22}
std/math/complex/atan.zig created+130
...@@ -0,0 +1,130 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn atan(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 return switch (T) {
10 f32 => atan32(z),
11 f64 => atan64(z),
12 else => @compileError("atan not implemented for " ++ @typeName(z)),
13 };
14}
15
16fn redupif32(x: f32) f32 {
17 const DP1 = 3.140625;
18 const DP2 = 9.67502593994140625e-4;
19 const DP3 = 1.509957990978376432e-7;
20
21 var t = x / math.pi;
22 if (t >= 0.0) {
23 t += 0.5;
24 } else {
25 t -= 0.5;
26 }
27
28 const u = f32(i32(t));
29 return ((x - u * DP1) - u * DP2) - t * DP3;
30}
31
32fn atan32(z: &const Complex(f32)) Complex(f32) {
33 const maxnum = 1.0e38;
34
35 const x = z.re;
36 const y = z.im;
37
38 if ((x == 0.0) and (y > 1.0)) {
39 // overflow
40 return Complex(f32).new(maxnum, maxnum);
41 }
42
43 const x2 = x * x;
44 var a = 1.0 - x2 - (y * y);
45 if (a == 0.0) {
46 // overflow
47 return Complex(f32).new(maxnum, maxnum);
48 }
49
50 var t = 0.5 * math.atan2(f32, 2.0 * x, a);
51 var w = redupif32(t);
52
53 t = y - 1.0;
54 a = x2 + t * t;
55 if (a == 0.0) {
56 // overflow
57 return Complex(f32).new(maxnum, maxnum);
58 }
59
60 t = y + 1.0;
61 a = (x2 + (t * t)) / a;
62 return Complex(f32).new(w, 0.25 * math.ln(a));
63}
64
65fn redupif64(x: f64) f64 {
66 const DP1 = 3.14159265160560607910;
67 const DP2 = 1.98418714791870343106e-9;
68 const DP3 = 1.14423774522196636802e-17;
69
70 var t = x / math.pi;
71 if (t >= 0.0) {
72 t += 0.5;
73 } else {
74 t -= 0.5;
75 }
76
77 const u = f64(i64(t));
78 return ((x - u * DP1) - u * DP2) - t * DP3;
79}
80
81fn atan64(z: &const Complex(f64)) Complex(f64) {
82 const maxnum = 1.0e308;
83
84 const x = z.re;
85 const y = z.im;
86
87 if ((x == 0.0) and (y > 1.0)) {
88 // overflow
89 return Complex(f64).new(maxnum, maxnum);
90 }
91
92 const x2 = x * x;
93 var a = 1.0 - x2 - (y * y);
94 if (a == 0.0) {
95 // overflow
96 return Complex(f64).new(maxnum, maxnum);
97 }
98
99 var t = 0.5 * math.atan2(f64, 2.0 * x, a);
100 var w = redupif64(t);
101
102 t = y - 1.0;
103 a = x2 + t * t;
104 if (a == 0.0) {
105 // overflow
106 return Complex(f64).new(maxnum, maxnum);
107 }
108
109 t = y + 1.0;
110 a = (x2 + (t * t)) / a;
111 return Complex(f64).new(w, 0.25 * math.ln(a));
112}
113
114const epsilon = 0.0001;
115
116test "complex.catan32" {
117 const a = Complex(f32).new(5, 3);
118 const c = atan(a);
119
120 debug.assert(math.approxEq(f32, c.re, 1.423679, epsilon));
121 debug.assert(math.approxEq(f32, c.im, 0.086569, epsilon));
122}
123
124test "complex.catan64" {
125 const a = Complex(f64).new(5, 3);
126 const c = atan(a);
127
128 debug.assert(math.approxEq(f64, c.re, 1.423679, epsilon));
129 debug.assert(math.approxEq(f64, c.im, 0.086569, epsilon));
130}
std/math/complex/atanh.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn atanh(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const q = Complex(T).new(-z.im, z.re);
10 const r = cmath.atan(q);
11 return Complex(T).new(r.im, -r.re);
12}
13
14const epsilon = 0.0001;
15
16test "complex.catanh" {
17 const a = Complex(f32).new(5, 3);
18 const c = atanh(a);
19
20 debug.assert(math.approxEq(f32, c.re, 0.146947, epsilon));
21 debug.assert(math.approxEq(f32, c.im, 1.480870, epsilon));
22}
std/math/complex/conj.zig created+17
...@@ -0,0 +1,17 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn conj(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 return Complex(T).new(z.re, -z.im);
10}
11
12test "complex.conj" {
13 const a = Complex(f32).new(5, 3);
14 const c = a.conjugate();
15
16 debug.assert(c.re == 5 and c.im == -3);
17}
std/math/complex/cos.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn cos(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const p = Complex(T).new(-z.im, z.re);
10 return cmath.cosh(p);
11}
12
13const epsilon = 0.0001;
14
15test "complex.ccos" {
16 const a = Complex(f32).new(5, 3);
17 const c = cos(a);
18
19 debug.assert(math.approxEq(f32, c.re, 2.855815, epsilon));
20 debug.assert(math.approxEq(f32, c.im, 9.606383, epsilon));
21}
std/math/complex/cosh.zig created+165
...@@ -0,0 +1,165 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
8
9pub fn cosh(z: var) Complex(@typeOf(z.re)) {
10 const T = @typeOf(z.re);
11 return switch (T) {
12 f32 => cosh32(z),
13 f64 => cosh64(z),
14 else => @compileError("cosh not implemented for " ++ @typeName(z)),
15 };
16}
17
18fn cosh32(z: &const Complex(f32)) Complex(f32) {
19 const x = z.re;
20 const y = z.im;
21
22 const hx = @bitCast(u32, x);
23 const ix = hx & 0x7fffffff;
24
25 const hy = @bitCast(u32, y);
26 const iy = hy & 0x7fffffff;
27
28 if (ix < 0x7f800000 and iy < 0x7f800000) {
29 if (iy == 0) {
30 return Complex(f32).new(math.cosh(x), y);
31 }
32 // small x: normal case
33 if (ix < 0x41100000) {
34 return Complex(f32).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y));
35 }
36
37 // |x|>= 9, so cosh(x) ~= exp(|x|)
38 if (ix < 0x42b17218) {
39 // x < 88.7: exp(|x|) won't overflow
40 const h = math.exp(math.fabs(x)) * 0.5;
41 return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));
42 }
43 // x < 192.7: scale to avoid overflow
44 else if (ix < 0x4340b1e7) {
45 const v = Complex(f32).new(math.fabs(x), y);
46 const r = ldexp_cexp(v, -1);
47 return Complex(f32).new(x, y * math.copysign(f32, 1, x));
48 }
49 // x >= 192.7: result always overflows
50 else {
51 const h = 0x1p127 * x;
52 return Complex(f32).new(h * h * math.cos(y), h * math.sin(y));
53 }
54 }
55
56 if (ix == 0 and iy >= 0x7f800000) {
57 return Complex(f32).new(y - y, math.copysign(f32, 0, x * (y - y)));
58 }
59
60 if (iy == 0 and ix >= 0x7f800000) {
61 if (hx & 0x7fffff == 0) {
62 return Complex(f32).new(x * x, math.copysign(f32, 0, x) * y);
63 }
64 return Complex(f32).new(x, math.copysign(f32, 0, (x + x) * y));
65 }
66
67 if (ix < 0x7f800000 and iy >= 0x7f800000) {
68 return Complex(f32).new(y - y, x * (y - y));
69 }
70
71 if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) {
72 if (iy >= 0x7f800000) {
73 return Complex(f32).new(x * x, x * (y - y));
74 }
75 return Complex(f32).new((x * x) * math.cos(y), x * math.sin(y));
76 }
77
78 return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y));
79}
80
81fn cosh64(z: &const Complex(f64)) Complex(f64) {
82 const x = z.re;
83 const y = z.im;
84
85 const fx = @bitCast(u64, x);
86 const hx = u32(fx >> 32);
87 const lx = @truncate(u32, fx);
88 const ix = hx & 0x7fffffff;
89
90 const fy = @bitCast(u64, y);
91 const hy = u32(fy >> 32);
92 const ly = @truncate(u32, fy);
93 const iy = hy & 0x7fffffff;
94
95 // nearly non-exceptional case where x, y are finite
96 if (ix < 0x7ff00000 and iy < 0x7ff00000) {
97 if (iy | ly == 0) {
98 return Complex(f64).new(math.cosh(x), x * y);
99 }
100 // small x: normal case
101 if (ix < 0x40360000) {
102 return Complex(f64).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y));
103 }
104
105 // |x|>= 22, so cosh(x) ~= exp(|x|)
106 if (ix < 0x40862e42) {
107 // x < 710: exp(|x|) won't overflow
108 const h = math.exp(math.fabs(x)) * 0.5;
109 return Complex(f64).new(h * math.cos(y), math.copysign(f64, h, x) * math.sin(y));
110 }
111 // x < 1455: scale to avoid overflow
112 else if (ix < 0x4096bbaa) {
113 const v = Complex(f64).new(math.fabs(x), y);
114 const r = ldexp_cexp(v, -1);
115 return Complex(f64).new(x, y * math.copysign(f64, 1, x));
116 }
117 // x >= 1455: result always overflows
118 else {
119 const h = 0x1p1023;
120 return Complex(f64).new(h * h * math.cos(y), h * math.sin(y));
121 }
122 }
123
124 if (ix | lx == 0 and iy >= 0x7ff00000) {
125 return Complex(f64).new(y - y, math.copysign(f64, 0, x * (y - y)));
126 }
127
128 if (iy | ly == 0 and ix >= 0x7ff00000) {
129 if ((hx & 0xfffff) | lx == 0) {
130 return Complex(f64).new(x * x, math.copysign(f64, 0, x) * y);
131 }
132 return Complex(f64).new(x * x, math.copysign(f64, 0, (x + x) * y));
133 }
134
135 if (ix < 0x7ff00000 and iy >= 0x7ff00000) {
136 return Complex(f64).new(y - y, x * (y - y));
137 }
138
139 if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) {
140 if (iy >= 0x7ff00000) {
141 return Complex(f64).new(x * x, x * (y - y));
142 }
143 return Complex(f64).new(x * x * math.cos(y), x * math.sin(y));
144 }
145
146 return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y));
147}
148
149const epsilon = 0.0001;
150
151test "complex.ccosh32" {
152 const a = Complex(f32).new(5, 3);
153 const c = cosh(a);
154
155 debug.assert(math.approxEq(f32, c.re, -73.467300, epsilon));
156 debug.assert(math.approxEq(f32, c.im, 10.471557, epsilon));
157}
158
159test "complex.ccosh64" {
160 const a = Complex(f64).new(5, 3);
161 const c = cosh(a);
162
163 debug.assert(math.approxEq(f64, c.re, -73.467300, epsilon));
164 debug.assert(math.approxEq(f64, c.im, 10.471557, epsilon));
165}
std/math/complex/exp.zig created+140
...@@ -0,0 +1,140 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
8
9pub fn exp(z: var) Complex(@typeOf(z.re)) {
10 const T = @typeOf(z.re);
11
12 return switch (T) {
13 f32 => exp32(z),
14 f64 => exp64(z),
15 else => @compileError("exp not implemented for " ++ @typeName(z)),
16 };
17}
18
19fn exp32(z: &const Complex(f32)) Complex(f32) {
20 @setFloatMode(this, @import("builtin").FloatMode.Strict);
21
22 const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955
23 const cexp_overflow = 0x43400074; // (max_exp - min_denom_exp) * ln2
24
25 const x = z.re;
26 const y = z.im;
27
28 const hy = @bitCast(u32, y) & 0x7fffffff;
29 // cexp(x + i0) = exp(x) + i0
30 if (hy == 0) {
31 return Complex(f32).new(math.exp(x), y);
32 }
33
34 const hx = @bitCast(u32, x);
35 // cexp(0 + iy) = cos(y) + isin(y)
36 if ((hx & 0x7fffffff) == 0) {
37 return Complex(f32).new(math.cos(y), math.sin(y));
38 }
39
40 if (hy >= 0x7f800000) {
41 // cexp(finite|nan +- i inf|nan) = nan + i nan
42 if ((hx & 0x7fffffff) != 0x7f800000) {
43 return Complex(f32).new(y - y, y - y);
44 }
45 // cexp(-inf +- i inf|nan) = 0 + i0
46 else if (hx & 0x80000000 != 0) {
47 return Complex(f32).new(0, 0);
48 }
49 // cexp(+inf +- i inf|nan) = inf + i nan
50 else {
51 return Complex(f32).new(x, y - y);
52 }
53 }
54
55 // 88.7 <= x <= 192 so must scale
56 if (hx >= exp_overflow and hx <= cexp_overflow) {
57 return ldexp_cexp(z, 0);
58 }
59 // - x < exp_overflow => exp(x) won't overflow (common)
60 // - x > cexp_overflow, so exp(x) * s overflows for s > 0
61 // - x = +-inf
62 // - x = nan
63 else {
64 const exp_x = math.exp(x);
65 return Complex(f32).new(exp_x * math.cos(y), exp_x * math.sin(y));
66 }
67}
68
69fn exp64(z: &const Complex(f64)) Complex(f64) {
70 const exp_overflow = 0x40862e42; // high bits of max_exp * ln2 ~= 710
71 const cexp_overflow = 0x4096b8e4; // (max_exp - min_denorm_exp) * ln2
72
73 const x = z.re;
74 const y = z.im;
75
76 const fy = @bitCast(u64, y);
77 const hy = u32(fy >> 32) & 0x7fffffff;
78 const ly = @truncate(u32, fy);
79
80 // cexp(x + i0) = exp(x) + i0
81 if (hy | ly == 0) {
82 return Complex(f64).new(math.exp(x), y);
83 }
84
85 const fx = @bitCast(u64, x);
86 const hx = u32(fx >> 32);
87 const lx = @truncate(u32, fx);
88
89 // cexp(0 + iy) = cos(y) + isin(y)
90 if ((hx & 0x7fffffff) | lx == 0) {
91 return Complex(f64).new(math.cos(y), math.sin(y));
92 }
93
94 if (hy >= 0x7ff00000) {
95 // cexp(finite|nan +- i inf|nan) = nan + i nan
96 if (lx != 0 or (hx & 0x7fffffff) != 0x7ff00000) {
97 return Complex(f64).new(y - y, y - y);
98 }
99 // cexp(-inf +- i inf|nan) = 0 + i0
100 else if (hx & 0x80000000 != 0) {
101 return Complex(f64).new(0, 0);
102 }
103 // cexp(+inf +- i inf|nan) = inf + i nan
104 else {
105 return Complex(f64).new(x, y - y);
106 }
107 }
108
109 // 709.7 <= x <= 1454.3 so must scale
110 if (hx >= exp_overflow and hx <= cexp_overflow) {
111 const r = ldexp_cexp(z, 0);
112 return *r;
113 }
114 // - x < exp_overflow => exp(x) won't overflow (common)
115 // - x > cexp_overflow, so exp(x) * s overflows for s > 0
116 // - x = +-inf
117 // - x = nan
118 else {
119 const exp_x = math.exp(x);
120 return Complex(f64).new(exp_x * math.cos(y), exp_x * math.sin(y));
121 }
122}
123
124const epsilon = 0.0001;
125
126test "complex.cexp32" {
127 const a = Complex(f32).new(5, 3);
128 const c = exp(a);
129
130 debug.assert(math.approxEq(f32, c.re, -146.927917, epsilon));
131 debug.assert(math.approxEq(f32, c.im, 20.944065, epsilon));
132}
133
134test "complex.cexp64" {
135 const a = Complex(f32).new(5, 3);
136 const c = exp(a);
137
138 debug.assert(math.approxEq(f64, c.re, -146.927917, epsilon));
139 debug.assert(math.approxEq(f64, c.im, 20.944065, epsilon));
140}
std/math/complex/index.zig created+171
...@@ -0,0 +1,171 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4
5pub const abs = @import("abs.zig").abs;
6pub const acosh = @import("acosh.zig").acosh;
7pub const acos = @import("acos.zig").acos;
8pub const arg = @import("arg.zig").arg;
9pub const asinh = @import("asinh.zig").asinh;
10pub const asin = @import("asin.zig").asin;
11pub const atanh = @import("atanh.zig").atanh;
12pub const atan = @import("atan.zig").atan;
13pub const conj = @import("conj.zig").conj;
14pub const cosh = @import("cosh.zig").cosh;
15pub const cos = @import("cos.zig").cos;
16pub const exp = @import("exp.zig").exp;
17pub const log = @import("log.zig").log;
18pub const pow = @import("pow.zig").pow;
19pub const proj = @import("proj.zig").proj;
20pub const sinh = @import("sinh.zig").sinh;
21pub const sin = @import("sin.zig").sin;
22pub const sqrt = @import("sqrt.zig").sqrt;
23pub const tanh = @import("tanh.zig").tanh;
24pub const tan = @import("tan.zig").tan;
25
26pub fn Complex(comptime T: type) type {
27 return struct {
28 const Self = this;
29
30 re: T,
31 im: T,
32
33 pub fn new(re: T, im: T) Self {
34 return Self {
35 .re = re,
36 .im = im,
37 };
38 }
39
40 pub fn add(self: &const Self, other: &const Self) Self {
41 return Self {
42 .re = self.re + other.re,
43 .im = self.im + other.im,
44 };
45 }
46
47 pub fn sub(self: &const Self, other: &const Self) Self {
48 return Self {
49 .re = self.re - other.re,
50 .im = self.im - other.im,
51 };
52 }
53
54 pub fn mul(self: &const Self, other: &const Self) Self {
55 return Self {
56 .re = self.re * other.re - self.im * other.im,
57 .im = self.im * other.re + self.re * other.im,
58 };
59 }
60
61 pub fn div(self: &const Self, other: &const Self) Self {
62 const re_num = self.re * other.re + self.im * other.im;
63 const im_num = self.im * other.re - self.re * other.im;
64 const den = other.re * other.re + other.im * other.im;
65
66 return Self {
67 .re = re_num / den,
68 .im = im_num / den,
69 };
70 }
71
72 pub fn conjugate(self: &const Self) Self {
73 return Self {
74 .re = self.re,
75 .im = -self.im,
76 };
77 }
78
79 pub fn reciprocal(self: &const Self) Self {
80 const m = self.re * self.re + self.im * self.im;
81 return Self {
82 .re = self.re / m,
83 .im = -self.im / m,
84 };
85 }
86
87 pub fn magnitude(self: &const Self) T {
88 return math.sqrt(self.re * self.re + self.im * self.im);
89 }
90 };
91}
92
93const epsilon = 0.0001;
94
95test "complex.add" {
96 const a = Complex(f32).new(5, 3);
97 const b = Complex(f32).new(2, 7);
98 const c = a.add(b);
99
100 debug.assert(c.re == 7 and c.im == 10);
101}
102
103test "complex.sub" {
104 const a = Complex(f32).new(5, 3);
105 const b = Complex(f32).new(2, 7);
106 const c = a.sub(b);
107
108 debug.assert(c.re == 3 and c.im == -4);
109}
110
111test "complex.mul" {
112 const a = Complex(f32).new(5, 3);
113 const b = Complex(f32).new(2, 7);
114 const c = a.mul(b);
115
116 debug.assert(c.re == -11 and c.im == 41);
117}
118
119test "complex.div" {
120 const a = Complex(f32).new(5, 3);
121 const b = Complex(f32).new(2, 7);
122 const c = a.div(b);
123
124 debug.assert(math.approxEq(f32, c.re, f32(31)/53, epsilon) and
125 math.approxEq(f32, c.im, f32(-29)/53, epsilon));
126}
127
128test "complex.conjugate" {
129 const a = Complex(f32).new(5, 3);
130 const c = a.conjugate();
131
132 debug.assert(c.re == 5 and c.im == -3);
133}
134
135test "complex.reciprocal" {
136 const a = Complex(f32).new(5, 3);
137 const c = a.reciprocal();
138
139 debug.assert(math.approxEq(f32, c.re, f32(5)/34, epsilon) and
140 math.approxEq(f32, c.im, f32(-3)/34, epsilon));
141}
142
143test "complex.magnitude" {
144 const a = Complex(f32).new(5, 3);
145 const c = a.magnitude();
146
147 debug.assert(math.approxEq(f32, c, 5.83095, epsilon));
148}
149
150test "complex.cmath" {
151 _ = @import("abs.zig");
152 _ = @import("acosh.zig");
153 _ = @import("acos.zig");
154 _ = @import("arg.zig");
155 _ = @import("asinh.zig");
156 _ = @import("asin.zig");
157 _ = @import("atanh.zig");
158 _ = @import("atan.zig");
159 _ = @import("conj.zig");
160 _ = @import("cosh.zig");
161 _ = @import("cos.zig");
162 _ = @import("exp.zig");
163 _ = @import("log.zig");
164 _ = @import("pow.zig");
165 _ = @import("proj.zig");
166 _ = @import("sinh.zig");
167 _ = @import("sin.zig");
168 _ = @import("sqrt.zig");
169 _ = @import("tanh.zig");
170 _ = @import("tan.zig");
171}
std/math/complex/ldexp.zig created+75
...@@ -0,0 +1,75 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn ldexp_cexp(z: var, expt: i32) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9
10 return switch (T) {
11 f32 => ldexp_cexp32(z, expt),
12 f64 => ldexp_cexp64(z, expt),
13 else => unreachable,
14 };
15}
16
17fn frexp_exp32(x: f32, expt: &i32) f32 {
18 const k = 235; // reduction constant
19 const kln2 = 162.88958740; // k * ln2
20
21 const exp_x = math.exp(x - kln2);
22 const hx = @bitCast(u32, exp_x);
23 *expt = i32(hx >> 23) - (0x7f + 127) + k;
24 return @bitCast(f32, (hx & 0x7fffff) | ((0x7f + 127) << 23));
25}
26
27fn ldexp_cexp32(z: &const Complex(f32), expt: i32) Complex(f32) {
28 var ex_expt: i32 = undefined;
29 const exp_x = frexp_exp32(z.re, &ex_expt);
30 const exptf = expt + ex_expt;
31
32 const half_expt1 = @divTrunc(exptf, 2);
33 const scale1 = @bitCast(f32, (0x7f + half_expt1) << 23);
34
35 const half_expt2 = exptf - half_expt1;
36 const scale2 = @bitCast(f32, (0x7f + half_expt2) << 23);
37
38 return Complex(f32).new(
39 math.cos(z.im) * exp_x * scale1 * scale2,
40 math.sin(z.im) * exp_x * scale1 * scale2,
41 );
42}
43
44fn frexp_exp64(x: f64, expt: &i32) f64 {
45 const k = 1799; // reduction constant
46 const kln2 = 1246.97177782734161156; // k * ln2
47
48 const exp_x = math.exp(x - kln2);
49
50 const fx = @bitCast(u64, x);
51 const hx = u32(fx >> 32);
52 const lx = @truncate(u32, fx);
53
54 *expt = i32(hx >> 20) - (0x3ff + 1023) + k;
55
56 const high_word = (hx & 0xfffff) | ((0x3ff + 1023) << 20);
57 return @bitCast(f64, (u64(high_word) << 32) | lx);
58}
59
60fn ldexp_cexp64(z: &const Complex(f64), expt: i32) Complex(f64) {
61 var ex_expt: i32 = undefined;
62 const exp_x = frexp_exp64(z.re, &ex_expt);
63 const exptf = i64(expt + ex_expt);
64
65 const half_expt1 = @divTrunc(exptf, 2);
66 const scale1 = @bitCast(f64, (0x3ff + half_expt1) << 20);
67
68 const half_expt2 = exptf - half_expt1;
69 const scale2 = @bitCast(f64, (0x3ff + half_expt2) << 20);
70
71 return Complex(f64).new(
72 math.cos(z.im) * exp_x * scale1 * scale2,
73 math.sin(z.im) * exp_x * scale1 * scale2,
74 );
75}
std/math/complex/log.zig created+23
...@@ -0,0 +1,23 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn log(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const r = cmath.abs(z);
10 const phi = cmath.arg(z);
11
12 return Complex(T).new(math.ln(r), phi);
13}
14
15const epsilon = 0.0001;
16
17test "complex.clog" {
18 const a = Complex(f32).new(5, 3);
19 const c = log(a);
20
21 debug.assert(math.approxEq(f32, c.re, 1.763180, epsilon));
22 debug.assert(math.approxEq(f32, c.im, 0.540419, epsilon));
23}
std/math/complex/pow.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn pow(comptime T: type, z: &const T, c: &const T) T {
8 const p = cmath.log(z);
9 const q = c.mul(p);
10 return cmath.exp(q);
11}
12
13const epsilon = 0.0001;
14
15test "complex.cpow" {
16 const a = Complex(f32).new(5, 3);
17 const b = Complex(f32).new(2.3, -1.3);
18 const c = pow(Complex(f32), a, b);
19
20 debug.assert(math.approxEq(f32, c.re, 58.049110, epsilon));
21 debug.assert(math.approxEq(f32, c.im, -101.003433, epsilon));
22}
std/math/complex/proj.zig created+24
...@@ -0,0 +1,24 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn proj(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9
10 if (math.isInf(z.re) or math.isInf(z.im)) {
11 return Complex(T).new(math.inf(T), math.copysign(T, 0, z.re));
12 }
13
14 return Complex(T).new(z.re, z.im);
15}
16
17const epsilon = 0.0001;
18
19test "complex.cproj" {
20 const a = Complex(f32).new(5, 3);
21 const c = proj(a);
22
23 debug.assert(c.re == 5 and c.im == 3);
24}
std/math/complex/sin.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn sin(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const p = Complex(T).new(-z.im, z.re);
10 const q = cmath.sinh(p);
11 return Complex(T).new(q.im, -q.re);
12}
13
14const epsilon = 0.0001;
15
16test "complex.csin" {
17 const a = Complex(f32).new(5, 3);
18 const c = sin(a);
19
20 debug.assert(math.approxEq(f32, c.re, -9.654126, epsilon));
21 debug.assert(math.approxEq(f32, c.im, 2.841692, epsilon));
22}
std/math/complex/sinh.zig created+164
...@@ -0,0 +1,164 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
8
9pub fn sinh(z: var) Complex(@typeOf(z.re)) {
10 const T = @typeOf(z.re);
11 return switch (T) {
12 f32 => sinh32(z),
13 f64 => sinh64(z),
14 else => @compileError("tan not implemented for " ++ @typeName(z)),
15 };
16}
17
18fn sinh32(z: &const Complex(f32)) Complex(f32) {
19 const x = z.re;
20 const y = z.im;
21
22 const hx = @bitCast(u32, x);
23 const ix = hx & 0x7fffffff;
24
25 const hy = @bitCast(u32, y);
26 const iy = hy & 0x7fffffff;
27
28 if (ix < 0x7f800000 and iy < 0x7f800000) {
29 if (iy == 0) {
30 return Complex(f32).new(math.sinh(x), y);
31 }
32 // small x: normal case
33 if (ix < 0x41100000) {
34 return Complex(f32).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y));
35 }
36
37 // |x|>= 9, so cosh(x) ~= exp(|x|)
38 if (ix < 0x42b17218) {
39 // x < 88.7: exp(|x|) won't overflow
40 const h = math.exp(math.fabs(x)) * 0.5;
41 return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));
42 }
43 // x < 192.7: scale to avoid overflow
44 else if (ix < 0x4340b1e7) {
45 const v = Complex(f32).new(math.fabs(x), y);
46 const r = ldexp_cexp(v, -1);
47 return Complex(f32).new(x * math.copysign(f32, 1, x), y);
48 }
49 // x >= 192.7: result always overflows
50 else {
51 const h = 0x1p127 * x;
52 return Complex(f32).new(h * math.cos(y), h * h * math.sin(y));
53 }
54 }
55
56 if (ix == 0 and iy >= 0x7f800000) {
57 return Complex(f32).new(math.copysign(f32, 0, x * (y - y)), y - y);
58 }
59
60 if (iy == 0 and ix >= 0x7f800000) {
61 if (hx & 0x7fffff == 0) {
62 return Complex(f32).new(x, y);
63 }
64 return Complex(f32).new(x, math.copysign(f32, 0, y));
65 }
66
67 if (ix < 0x7f800000 and iy >= 0x7f800000) {
68 return Complex(f32).new(y - y, x * (y - y));
69 }
70
71 if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) {
72 if (iy >= 0x7f800000) {
73 return Complex(f32).new(x * x, x * (y - y));
74 }
75 return Complex(f32).new(x * math.cos(y), math.inf_f32 * math.sin(y));
76 }
77
78 return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y));
79}
80
81fn sinh64(z: &const Complex(f64)) Complex(f64) {
82 const x = z.re;
83 const y = z.im;
84
85 const fx = @bitCast(u64, x);
86 const hx = u32(fx >> 32);
87 const lx = @truncate(u32, fx);
88 const ix = hx & 0x7fffffff;
89
90 const fy = @bitCast(u64, y);
91 const hy = u32(fy >> 32);
92 const ly = @truncate(u32, fy);
93 const iy = hy & 0x7fffffff;
94
95 if (ix < 0x7ff00000 and iy < 0x7ff00000) {
96 if (iy | ly == 0) {
97 return Complex(f64).new(math.sinh(x), y);
98 }
99 // small x: normal case
100 if (ix < 0x40360000) {
101 return Complex(f64).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y));
102 }
103
104 // |x|>= 22, so cosh(x) ~= exp(|x|)
105 if (ix < 0x40862e42) {
106 // x < 710: exp(|x|) won't overflow
107 const h = math.exp(math.fabs(x)) * 0.5;
108 return Complex(f64).new(math.copysign(f64, h, x) * math.cos(y), h * math.sin(y));
109 }
110 // x < 1455: scale to avoid overflow
111 else if (ix < 0x4096bbaa) {
112 const v = Complex(f64).new(math.fabs(x), y);
113 const r = ldexp_cexp(v, -1);
114 return Complex(f64).new(x * math.copysign(f64, 1, x), y);
115 }
116 // x >= 1455: result always overflows
117 else {
118 const h = 0x1p1023 * x;
119 return Complex(f64).new(h * math.cos(y), h * h * math.sin(y));
120 }
121 }
122
123 if (ix | lx == 0 and iy >= 0x7ff00000) {
124 return Complex(f64).new(math.copysign(f64, 0, x * (y - y)), y - y);
125 }
126
127 if (iy | ly == 0 and ix >= 0x7ff00000) {
128 if ((hx & 0xfffff) | lx == 0) {
129 return Complex(f64).new(x, y);
130 }
131 return Complex(f64).new(x, math.copysign(f64, 0, y));
132 }
133
134 if (ix < 0x7ff00000 and iy >= 0x7ff00000) {
135 return Complex(f64).new(y - y, x * (y - y));
136 }
137
138 if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) {
139 if (iy >= 0x7ff00000) {
140 return Complex(f64).new(x * x, x * (y - y));
141 }
142 return Complex(f64).new(x * math.cos(y), math.inf_f64 * math.sin(y));
143 }
144
145 return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y));
146}
147
148const epsilon = 0.0001;
149
150test "complex.csinh32" {
151 const a = Complex(f32).new(5, 3);
152 const c = sinh(a);
153
154 debug.assert(math.approxEq(f32, c.re, -73.460617, epsilon));
155 debug.assert(math.approxEq(f32, c.im, 10.472508, epsilon));
156}
157
158test "complex.csinh64" {
159 const a = Complex(f64).new(5, 3);
160 const c = sinh(a);
161
162 debug.assert(math.approxEq(f64, c.re, -73.460617, epsilon));
163 debug.assert(math.approxEq(f64, c.im, 10.472508, epsilon));
164}
std/math/complex/sqrt.zig created+133
...@@ -0,0 +1,133 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7// TODO when #733 is solved this can be @typeOf(z) instead of Complex(@typeOf(z.re))
8pub fn sqrt(z: var) Complex(@typeOf(z.re)) {
9 const T = @typeOf(z.re);
10
11 return switch (T) {
12 f32 => sqrt32(z),
13 f64 => sqrt64(z),
14 else => @compileError("sqrt not implemented for " ++ @typeName(z)),
15 };
16}
17
18fn sqrt32(z: &const Complex(f32)) Complex(f32) {
19 const x = z.re;
20 const y = z.im;
21
22 if (x == 0 and y == 0) {
23 return Complex(f32).new(0, y);
24 }
25 if (math.isInf(y)) {
26 return Complex(f32).new(math.inf(f32), y);
27 }
28 if (math.isNan(x)) {
29 // raise invalid if y is not nan
30 const t = (y - y) / (y - y);
31 return Complex(f32).new(x, t);
32 }
33 if (math.isInf(x)) {
34 // sqrt(inf + i nan) = inf + nan i
35 // sqrt(inf + iy) = inf + i0
36 // sqrt(-inf + i nan) = nan +- inf i
37 // sqrt(-inf + iy) = 0 + inf i
38 if (math.signbit(x)) {
39 return Complex(f32).new(math.fabs(x - y), math.copysign(f32, x, y));
40 } else {
41 return Complex(f32).new(x, math.copysign(f32, y - y, y));
42 }
43 }
44
45 // y = nan special case is handled fine below
46
47 // double-precision avoids overflow with correct rounding.
48 const dx = f64(x);
49 const dy = f64(y);
50
51 if (dx >= 0) {
52 const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5);
53 return Complex(f32).new(f32(t), f32(dy / (2.0 * t)));
54 } else {
55 const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5);
56 return Complex(f32).new(f32(math.fabs(y) / (2.0 * t)), f32(math.copysign(f64, t, y)));
57 }
58}
59
60fn sqrt64(z: &const Complex(f64)) Complex(f64) {
61 // may encounter overflow for im,re >= DBL_MAX / (1 + sqrt(2))
62 const threshold = 0x1.a827999fcef32p+1022;
63
64 var x = z.re;
65 var y = z.im;
66
67 if (x == 0 and y == 0) {
68 return Complex(f64).new(0, y);
69 }
70 if (math.isInf(y)) {
71 return Complex(f64).new(math.inf(f64), y);
72 }
73 if (math.isNan(x)) {
74 // raise invalid if y is not nan
75 const t = (y - y) / (y - y);
76 return Complex(f64).new(x, t);
77 }
78 if (math.isInf(x)) {
79 // sqrt(inf + i nan) = inf + nan i
80 // sqrt(inf + iy) = inf + i0
81 // sqrt(-inf + i nan) = nan +- inf i
82 // sqrt(-inf + iy) = 0 + inf i
83 if (math.signbit(x)) {
84 return Complex(f64).new(math.fabs(x - y), math.copysign(f64, x, y));
85 } else {
86 return Complex(f64).new(x, math.copysign(f64, y - y, y));
87 }
88 }
89
90 // y = nan special case is handled fine below
91
92 // scale to avoid overflow
93 var scale = false;
94 if (math.fabs(x) >= threshold or math.fabs(y) >= threshold) {
95 x *= 0.25;
96 y *= 0.25;
97 scale = true;
98 }
99
100 var result: Complex(f64) = undefined;
101 if (x >= 0) {
102 const t = math.sqrt((x + math.hypot(f64, x, y)) * 0.5);
103 result = Complex(f64).new(t, y / (2.0 * t));
104 } else {
105 const t = math.sqrt((-x + math.hypot(f64, x, y)) * 0.5);
106 result = Complex(f64).new(math.fabs(y) / (2.0 * t), math.copysign(f64, t, y));
107 }
108
109 if (scale) {
110 result.re *= 2;
111 result.im *= 2;
112 }
113
114 return result;
115}
116
117const epsilon = 0.0001;
118
119test "complex.csqrt32" {
120 const a = Complex(f32).new(5, 3);
121 const c = sqrt(a);
122
123 debug.assert(math.approxEq(f32, c.re, 2.327117, epsilon));
124 debug.assert(math.approxEq(f32, c.im, 0.644574, epsilon));
125}
126
127test "complex.csqrt64" {
128 const a = Complex(f64).new(5, 3);
129 const c = sqrt(a);
130
131 debug.assert(math.approxEq(f64, c.re, 2.3271175190399496, epsilon));
132 debug.assert(math.approxEq(f64, c.im, 0.6445742373246469, epsilon));
133}
std/math/complex/tan.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn tan(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const q = Complex(T).new(-z.im, z.re);
10 const r = cmath.tanh(q);
11 return Complex(T).new(r.im, -r.re);
12}
13
14const epsilon = 0.0001;
15
16test "complex.ctan" {
17 const a = Complex(f32).new(5, 3);
18 const c = tan(a);
19
20 debug.assert(math.approxEq(f32, c.re, -0.002708233, epsilon));
21 debug.assert(math.approxEq(f32, c.im, 1.004165, epsilon));
22}
std/math/complex/tanh.zig created+111
...@@ -0,0 +1,111 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn tanh(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 return switch (T) {
10 f32 => tanh32(z),
11 f64 => tanh64(z),
12 else => @compileError("tan not implemented for " ++ @typeName(z)),
13 };
14}
15
16fn tanh32(z: &const Complex(f32)) Complex(f32) {
17 const x = z.re;
18 const y = z.im;
19
20 const hx = @bitCast(u32, x);
21 const ix = hx & 0x7fffffff;
22
23 if (ix >= 0x7f800000) {
24 if (ix & 0x7fffff != 0) {
25 const r = if (y == 0) y else x * y;
26 return Complex(f32).new(x, r);
27 }
28 const xx = @bitCast(f32, hx - 0x40000000);
29 const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y);
30 return Complex(f32).new(xx, math.copysign(f32, 0, r));
31 }
32
33 if (!math.isFinite(y)) {
34 const r = if (ix != 0) y - y else x;
35 return Complex(f32).new(r, y - y);
36 }
37
38 // x >= 11
39 if (ix >= 0x41300000) {
40 const exp_mx = math.exp(-math.fabs(x));
41 return Complex(f32).new(math.copysign(f32, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx);
42 }
43
44 // Kahan's algorithm
45 const t = math.tan(y);
46 const beta = 1.0 + t * t;
47 const s = math.sinh(x);
48 const rho = math.sqrt(1 + s * s);
49 const den = 1 + beta * s * s;
50
51 return Complex(f32).new((beta * rho * s) / den, t / den);
52}
53
54fn tanh64(z: &const Complex(f64)) Complex(f64) {
55 const x = z.re;
56 const y = z.im;
57
58 const fx = @bitCast(u64, x);
59 const hx = u32(fx >> 32);
60 const lx = @truncate(u32, fx);
61 const ix = hx & 0x7fffffff;
62
63 if (ix >= 0x7ff00000) {
64 if ((ix & 0x7fffff) | lx != 0) {
65 const r = if (y == 0) y else x * y;
66 return Complex(f64).new(x, r);
67 }
68
69 const xx = @bitCast(f64, (u64(hx - 0x40000000) << 32) | lx);
70 const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y);
71 return Complex(f64).new(xx, math.copysign(f64, 0, r));
72 }
73
74 if (!math.isFinite(y)) {
75 const r = if (ix != 0) y - y else x;
76 return Complex(f64).new(r, y - y);
77 }
78
79 // x >= 22
80 if (ix >= 0x40360000) {
81 const exp_mx = math.exp(-math.fabs(x));
82 return Complex(f64).new(math.copysign(f64, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx);
83 }
84
85 // Kahan's algorithm
86 const t = math.tan(y);
87 const beta = 1.0 + t * t;
88 const s = math.sinh(x);
89 const rho = math.sqrt(1 + s * s);
90 const den = 1 + beta * s * s;
91
92 return Complex(f64).new((beta * rho * s) / den, t / den);
93}
94
95const epsilon = 0.0001;
96
97test "complex.ctanh32" {
98 const a = Complex(f32).new(5, 3);
99 const c = tanh(a);
100
101 debug.assert(math.approxEq(f32, c.re, 0.999913, epsilon));
102 debug.assert(math.approxEq(f32, c.im, -0.000025, epsilon));
103}
104
105test "complex.ctanh64" {
106 const a = Complex(f64).new(5, 3);
107 const c = tanh(a);
108
109 debug.assert(math.approxEq(f64, c.re, 0.999913, epsilon));
110 debug.assert(math.approxEq(f64, c.im, -0.000025, epsilon));
111}
std/math/exp.zig+5
...@@ -6,6 +6,7 @@...@@ -6,6 +6,7 @@
6const std = @import("../index.zig");6const std = @import("../index.zig");
7const math = std.math;7const math = std.math;
8const assert = std.debug.assert;8const assert = std.debug.assert;
9const builtin = @import("builtin");
910
10pub fn exp(x: var) @typeOf(x) {11pub fn exp(x: var) @typeOf(x) {
11 const T = @typeOf(x);12 const T = @typeOf(x);
...@@ -17,6 +18,8 @@ pub fn exp(x: var) @typeOf(x) {...@@ -17,6 +18,8 @@ pub fn exp(x: var) @typeOf(x) {
17}18}
1819
19fn exp32(x_: f32) f32 {20fn exp32(x_: f32) f32 {
21 @setFloatMode(this, builtin.FloatMode.Strict);
22
20 const half = []f32 { 0.5, -0.5 };23 const half = []f32 { 0.5, -0.5 };
21 const ln2hi = 6.9314575195e-1;24 const ln2hi = 6.9314575195e-1;
22 const ln2lo = 1.4286067653e-6;25 const ln2lo = 1.4286067653e-6;
...@@ -94,6 +97,8 @@ fn exp32(x_: f32) f32 {...@@ -94,6 +97,8 @@ fn exp32(x_: f32) f32 {
94}97}
9598
96fn exp64(x_: f64) f64 {99fn exp64(x_: f64) f64 {
100 @setFloatMode(this, builtin.FloatMode.Strict);
101
97 const half = []const f64 { 0.5, -0.5 };102 const half = []const f64 { 0.5, -0.5 };
98 const ln2hi: f64 = 6.93147180369123816490e-01;103 const ln2hi: f64 = 6.93147180369123816490e-01;
99 const ln2lo: f64 = 1.90821492927058770002e-10;104 const ln2lo: f64 = 1.90821492927058770002e-10;
std/math/index.zig+31
...@@ -129,6 +129,9 @@ pub const cos = @import("cos.zig").cos;...@@ -129,6 +129,9 @@ pub const cos = @import("cos.zig").cos;
129pub const sin = @import("sin.zig").sin;129pub const sin = @import("sin.zig").sin;
130pub const tan = @import("tan.zig").tan;130pub const tan = @import("tan.zig").tan;
131131
132pub const complex = @import("complex/index.zig");
133pub const Complex = complex.Complex;
134
132test "math" {135test "math" {
133 _ = @import("nan.zig");136 _ = @import("nan.zig");
134 _ = @import("isnan.zig");137 _ = @import("isnan.zig");
...@@ -172,6 +175,8 @@ test "math" {...@@ -172,6 +175,8 @@ test "math" {
172 _ = @import("sin.zig");175 _ = @import("sin.zig");
173 _ = @import("cos.zig");176 _ = @import("cos.zig");
174 _ = @import("tan.zig");177 _ = @import("tan.zig");
178
179 _ = @import("complex/index.zig");
175}180}
176181
177182
...@@ -553,6 +558,32 @@ test "math.floorPowerOfTwo" {...@@ -553,6 +558,32 @@ test "math.floorPowerOfTwo" {
553 comptime testFloorPowerOfTwo();558 comptime testFloorPowerOfTwo();
554}559}
555560
561pub fn log2_int(comptime T: type, x: T) Log2Int(T) {
562 assert(x != 0);
563 return Log2Int(T)(T.bit_count - 1 - @clz(x));
564}
565
566pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) {
567 assert(x != 0);
568 const log2_val = log2_int(T, x);
569 if (T(1) << log2_val == x)
570 return log2_val;
571 return log2_val + 1;
572}
573
574test "std.math.log2_int_ceil" {
575 assert(log2_int_ceil(u32, 1) == 0);
576 assert(log2_int_ceil(u32, 2) == 1);
577 assert(log2_int_ceil(u32, 3) == 2);
578 assert(log2_int_ceil(u32, 4) == 2);
579 assert(log2_int_ceil(u32, 5) == 3);
580 assert(log2_int_ceil(u32, 6) == 3);
581 assert(log2_int_ceil(u32, 7) == 3);
582 assert(log2_int_ceil(u32, 8) == 3);
583 assert(log2_int_ceil(u32, 9) == 4);
584 assert(log2_int_ceil(u32, 10) == 4);
585}
586
556fn testFloorPowerOfTwo() void {587fn testFloorPowerOfTwo() void {
557 assert(floorPowerOfTwo(u32, 63) == 32);588 assert(floorPowerOfTwo(u32, 63) == 32);
558 assert(floorPowerOfTwo(u32, 64) == 64);589 assert(floorPowerOfTwo(u32, 64) == 64);
std/math/ln.zig+2
...@@ -89,6 +89,8 @@ pub fn ln_32(x_: f32) f32 {...@@ -89,6 +89,8 @@ pub fn ln_32(x_: f32) f32 {
89}89}
9090
91pub fn ln_64(x_: f64) f64 {91pub fn ln_64(x_: f64) f64 {
92 @setFloatMode(this, @import("builtin").FloatMode.Strict);
93
92 const ln2_hi: f64 = 6.93147180369123816490e-01;94 const ln2_hi: f64 = 6.93147180369123816490e-01;
93 const ln2_lo: f64 = 1.90821492927058770002e-10;95 const ln2_lo: f64 = 1.90821492927058770002e-10;
94 const Lg1: f64 = 6.666666666666735130e-01;96 const Lg1: f64 = 6.666666666666735130e-01;
std/math/log2.zig+1-6
...@@ -31,17 +31,12 @@ pub fn log2(x: var) @typeOf(x) {...@@ -31,17 +31,12 @@ pub fn log2(x: var) @typeOf(x) {
31 return result;31 return result;
32 },32 },
33 TypeId.Int => {33 TypeId.Int => {
34 return log2_int(T, x);34 return math.log2_int(T, x);
35 },35 },
36 else => @compileError("log2 not implemented for " ++ @typeName(T)),36 else => @compileError("log2 not implemented for " ++ @typeName(T)),
37 }37 }
38}38}
3939
40pub fn log2_int(comptime T: type, x: T) T {
41 assert(x != 0);
42 return T.bit_count - 1 - T(@clz(x));
43}
44
45pub fn log2_32(x_: f32) f32 {40pub fn log2_32(x_: f32) f32 {
46 const ivln2hi: f32 = 1.4428710938e+00;41 const ivln2hi: f32 = 1.4428710938e+00;
47 const ivln2lo: f32 = -1.7605285393e-04;42 const ivln2lo: f32 = -1.7605285393e-04;
std/math/sqrt.zig+33-262
...@@ -14,26 +14,8 @@ const TypeId = builtin.TypeId;...@@ -14,26 +14,8 @@ const TypeId = builtin.TypeId;
14pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) {14pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) {
15 const T = @typeOf(x);15 const T = @typeOf(x);
16 switch (@typeId(T)) {16 switch (@typeId(T)) {
17 TypeId.FloatLiteral => {17 TypeId.FloatLiteral => return T(@sqrt(f64, x)), // TODO upgrade to f128
18 return T(sqrt64(x));18 TypeId.Float => return @sqrt(T, x),
19 },
20 TypeId.Float => {
21 switch (T) {
22 f32 => {
23 switch (builtin.arch) {
24 builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt32(x),
25 else => return sqrt32(x),
26 }
27 },
28 f64 => {
29 switch (builtin.arch) {
30 builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt64(x),
31 else => return sqrt64(x),
32 }
33 },
34 else => @compileError("sqrt not implemented for " ++ @typeName(T)),
35 }
36 },
37 TypeId.IntLiteral => comptime {19 TypeId.IntLiteral => comptime {
38 if (x > @maxValue(u128)) {20 if (x > @maxValue(u128)) {
39 @compileError("sqrt not implemented for comptime_int greater than 128 bits");21 @compileError("sqrt not implemented for comptime_int greater than 128 bits");
...@@ -43,269 +25,58 @@ pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typ...@@ -43,269 +25,58 @@ pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typ
43 }25 }
44 return T(sqrt_int(u128, x));26 return T(sqrt_int(u128, x));
45 },27 },
46 TypeId.Int => {28 TypeId.Int => return sqrt_int(T, x),
47 return sqrt_int(T, x);
48 },
49 else => @compileError("sqrt not implemented for " ++ @typeName(T)),29 else => @compileError("sqrt not implemented for " ++ @typeName(T)),
50 }30 }
51}31}
5232
53fn sqrt32(x: f32) f32 {
54 const tiny: f32 = 1.0e-30;
55 const sign: i32 = @bitCast(i32, u32(0x80000000));
56 var ix: i32 = @bitCast(i32, x);
57
58 if ((ix & 0x7F800000) == 0x7F800000) {
59 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
60 }
61
62 // zero
63 if (ix <= 0) {
64 if (ix & ~sign == 0) {
65 return x; // sqrt (+-0) = +-0
66 }
67 if (ix < 0) {
68 return math.snan(f32);
69 }
70 }
71
72 // normalize
73 var m = ix >> 23;
74 if (m == 0) {
75 // subnormal
76 var i: i32 = 0;
77 while (ix & 0x00800000 == 0) : (i += 1) {
78 ix <<= 1;
79 }
80 m -= i - 1;
81 }
82
83 m -= 127; // unbias exponent
84 ix = (ix & 0x007FFFFF) | 0x00800000;
85
86 if (m & 1 != 0) { // odd m, double x to even
87 ix += ix;
88 }
89
90 m >>= 1; // m = [m / 2]
91
92 // sqrt(x) bit by bit
93 ix += ix;
94 var q: i32 = 0; // q = sqrt(x)
95 var s: i32 = 0;
96 var r: i32 = 0x01000000; // r = moving bit right -> left
97
98 while (r != 0) {
99 const t = s + r;
100 if (t <= ix) {
101 s = t + r;
102 ix -= t;
103 q += r;
104 }
105 ix += ix;
106 r >>= 1;
107 }
108
109 // floating add to find rounding direction
110 if (ix != 0) {
111 var z = 1.0 - tiny; // inexact
112 if (z >= 1.0) {
113 z = 1.0 + tiny;
114 if (z > 1.0) {
115 q += 2;
116 } else {
117 if (q & 1 != 0) {
118 q += 1;
119 }
120 }
121 }
122 }
123
124 ix = (q >> 1) + 0x3f000000;
125 ix += m << 23;
126 return @bitCast(f32, ix);
127}
128
129// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
130// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
131// potentially some edge cases remaining that are not handled in the same way.
132fn sqrt64(x: f64) f64 {
133 const tiny: f64 = 1.0e-300;
134 const sign: u32 = 0x80000000;
135 const u = @bitCast(u64, x);
136
137 var ix0 = u32(u >> 32);
138 var ix1 = u32(u & 0xFFFFFFFF);
139
140 // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
141 if (ix0 & 0x7FF00000 == 0x7FF00000) {
142 return x * x + x;
143 }
144
145 // sqrt(+-0) = +-0
146 if (x == 0.0) {
147 return x;
148 }
149 // sqrt(-ve) = snan
150 if (ix0 & sign != 0) {
151 return math.snan(f64);
152 }
153
154 // normalize x
155 var m = i32(ix0 >> 20);
156 if (m == 0) {
157 // subnormal
158 while (ix0 == 0) {
159 m -= 21;
160 ix0 |= ix1 >> 11;
161 ix1 <<= 21;
162 }
163
164 // subnormal
165 var i: u32 = 0;
166 while (ix0 & 0x00100000 == 0) : (i += 1) {
167 ix0 <<= 1;
168 }
169 m -= i32(i) - 1;
170 ix0 |= ix1 >> u5(32 - i);
171 ix1 <<= u5(i);
172 }
173
174 // unbias exponent
175 m -= 1023;
176 ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
177 if (m & 1 != 0) {
178 ix0 += ix0 + (ix1 >> 31);
179 ix1 = ix1 +% ix1;
180 }
181 m >>= 1;
182
183 // sqrt(x) bit by bit
184 ix0 += ix0 + (ix1 >> 31);
185 ix1 = ix1 +% ix1;
186
187 var q: u32 = 0;
188 var q1: u32 = 0;
189 var s0: u32 = 0;
190 var s1: u32 = 0;
191 var r: u32 = 0x00200000;
192 var t: u32 = undefined;
193 var t1: u32 = undefined;
194
195 while (r != 0) {
196 t = s0 +% r;
197 if (t <= ix0) {
198 s0 = t + r;
199 ix0 -= t;
200 q += r;
201 }
202 ix0 = ix0 +% ix0 +% (ix1 >> 31);
203 ix1 = ix1 +% ix1;
204 r >>= 1;
205 }
206
207 r = sign;
208 while (r != 0) {
209 t = s1 +% r;
210 t = s0;
211 if (t < ix0 or (t == ix0 and t1 <= ix1)) {
212 s1 = t1 +% r;
213 if (t1 & sign == sign and s1 & sign == 0) {
214 s0 += 1;
215 }
216 ix0 -= t;
217 if (ix1 < t1) {
218 ix0 -= 1;
219 }
220 ix1 = ix1 -% t1;
221 q1 += r;
222 }
223 ix0 = ix0 +% ix0 +% (ix1 >> 31);
224 ix1 = ix1 +% ix1;
225 r >>= 1;
226 }
227
228 // rounding direction
229 if (ix0 | ix1 != 0) {
230 var z = 1.0 - tiny; // raise inexact
231 if (z >= 1.0) {
232 z = 1.0 + tiny;
233 if (q1 == 0xFFFFFFFF) {
234 q1 = 0;
235 q += 1;
236 } else if (z > 1.0) {
237 if (q1 == 0xFFFFFFFE) {
238 q += 1;
239 }
240 q1 += 2;
241 } else {
242 q1 += q1 & 1;
243 }
244 }
245 }
246
247 ix0 = (q >> 1) + 0x3FE00000;
248 ix1 = q1 >> 1;
249 if (q & 1 != 0) {
250 ix1 |= 0x80000000;
251 }
252
253 // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
254 // behaviour at least.
255 var iix0 = i32(ix0);
256 iix0 = iix0 +% (m << 20);
257
258 const uz = (u64(iix0) << 32) | ix1;
259 return @bitCast(f64, uz);
260}
261
262test "math.sqrt" {33test "math.sqrt" {
263 assert(sqrt(f32(0.0)) == sqrt32(0.0));34 assert(sqrt(f32(0.0)) == @sqrt(f32, 0.0));
264 assert(sqrt(f64(0.0)) == sqrt64(0.0));35 assert(sqrt(f64(0.0)) == @sqrt(f64, 0.0));
265}36}
26637
267test "math.sqrt32" {38test "math.sqrt32" {
268 const epsilon = 0.000001;39 const epsilon = 0.000001;
26940
270 assert(sqrt32(0.0) == 0.0);41 assert(@sqrt(f32, 0.0) == 0.0);
271 assert(math.approxEq(f32, sqrt32(2.0), 1.414214, epsilon));42 assert(math.approxEq(f32, @sqrt(f32, 2.0), 1.414214, epsilon));
272 assert(math.approxEq(f32, sqrt32(3.6), 1.897367, epsilon));43 assert(math.approxEq(f32, @sqrt(f32, 3.6), 1.897367, epsilon));
273 assert(sqrt32(4.0) == 2.0);44 assert(@sqrt(f32, 4.0) == 2.0);
274 assert(math.approxEq(f32, sqrt32(7.539840), 2.745877, epsilon));45 assert(math.approxEq(f32, @sqrt(f32, 7.539840), 2.745877, epsilon));
275 assert(math.approxEq(f32, sqrt32(19.230934), 4.385309, epsilon));46 assert(math.approxEq(f32, @sqrt(f32, 19.230934), 4.385309, epsilon));
276 assert(sqrt32(64.0) == 8.0);47 assert(@sqrt(f32, 64.0) == 8.0);
277 assert(math.approxEq(f32, sqrt32(64.1), 8.006248, epsilon));48 assert(math.approxEq(f32, @sqrt(f32, 64.1), 8.006248, epsilon));
278 assert(math.approxEq(f32, sqrt32(8942.230469), 94.563370, epsilon));49 assert(math.approxEq(f32, @sqrt(f32, 8942.230469), 94.563370, epsilon));
279}50}
28051
281test "math.sqrt64" {52test "math.sqrt64" {
282 const epsilon = 0.000001;53 const epsilon = 0.000001;
28354
284 assert(sqrt64(0.0) == 0.0);55 assert(@sqrt(f64, 0.0) == 0.0);
285 assert(math.approxEq(f64, sqrt64(2.0), 1.414214, epsilon));56 assert(math.approxEq(f64, @sqrt(f64, 2.0), 1.414214, epsilon));
286 assert(math.approxEq(f64, sqrt64(3.6), 1.897367, epsilon));57 assert(math.approxEq(f64, @sqrt(f64, 3.6), 1.897367, epsilon));
287 assert(sqrt64(4.0) == 2.0);58 assert(@sqrt(f64, 4.0) == 2.0);
288 assert(math.approxEq(f64, sqrt64(7.539840), 2.745877, epsilon));59 assert(math.approxEq(f64, @sqrt(f64, 7.539840), 2.745877, epsilon));
289 assert(math.approxEq(f64, sqrt64(19.230934), 4.385309, epsilon));60 assert(math.approxEq(f64, @sqrt(f64, 19.230934), 4.385309, epsilon));
290 assert(sqrt64(64.0) == 8.0);61 assert(@sqrt(f64, 64.0) == 8.0);
291 assert(math.approxEq(f64, sqrt64(64.1), 8.006248, epsilon));62 assert(math.approxEq(f64, @sqrt(f64, 64.1), 8.006248, epsilon));
292 assert(math.approxEq(f64, sqrt64(8942.230469), 94.563367, epsilon));63 assert(math.approxEq(f64, @sqrt(f64, 8942.230469), 94.563367, epsilon));
293}64}
29465
295test "math.sqrt32.special" {66test "math.sqrt32.special" {
296 assert(math.isPositiveInf(sqrt32(math.inf(f32))));67 assert(math.isPositiveInf(@sqrt(f32, math.inf(f32))));
297 assert(sqrt32(0.0) == 0.0);68 assert(@sqrt(f32, 0.0) == 0.0);
298 assert(sqrt32(-0.0) == -0.0);69 assert(@sqrt(f32, -0.0) == -0.0);
299 assert(math.isNan(sqrt32(-1.0)));70 assert(math.isNan(@sqrt(f32, -1.0)));
300 assert(math.isNan(sqrt32(math.nan(f32))));71 assert(math.isNan(@sqrt(f32, math.nan(f32))));
301}72}
30273
303test "math.sqrt64.special" {74test "math.sqrt64.special" {
304 assert(math.isPositiveInf(sqrt64(math.inf(f64))));75 assert(math.isPositiveInf(@sqrt(f64, math.inf(f64))));
305 assert(sqrt64(0.0) == 0.0);76 assert(@sqrt(f64, 0.0) == 0.0);
306 assert(sqrt64(-0.0) == -0.0);77 assert(@sqrt(f64, -0.0) == -0.0);
307 assert(math.isNan(sqrt64(-1.0)));78 assert(math.isNan(@sqrt(f64, -1.0)));
308 assert(math.isNan(sqrt64(math.nan(f64))));79 assert(math.isNan(@sqrt(f64, math.nan(f64))));
309}80}
31081
311fn sqrt_int(comptime T: type, value: T) @IntType(false, T.bit_count / 2) {82fn sqrt_int(comptime T: type, value: T) @IntType(false, T.bit_count / 2) {
std/math/x86_64/sqrt.zig deleted-15
...@@ -1,15 +0,0 @@
1pub fn sqrt32(x: f32) f32 {
2 return asm (
3 \\sqrtss %%xmm0, %%xmm0
4 : [ret] "={xmm0}" (-> f32)
5 : [x] "{xmm0}" (x)
6 );
7}
8
9pub fn sqrt64(x: f64) f64 {
10 return asm (
11 \\sqrtsd %%xmm0, %%xmm0
12 : [ret] "={xmm0}" (-> f64)
13 : [x] "{xmm0}" (x)
14 );
15}
std/mem.zig+129-4
...@@ -3,6 +3,7 @@ const debug = std.debug;...@@ -3,6 +3,7 @@ const debug = std.debug;
3const assert = debug.assert;3const assert = debug.assert;
4const math = std.math;4const math = std.math;
5const builtin = @import("builtin");5const builtin = @import("builtin");
6const mem = this;
67
7pub const Allocator = struct {8pub const Allocator = struct {
8 const Error = error {OutOfMemory};9 const Error = error {OutOfMemory};
...@@ -10,6 +11,8 @@ pub const Allocator = struct {...@@ -10,6 +11,8 @@ pub const Allocator = struct {
10 /// Allocate byte_count bytes and return them in a slice, with the11 /// Allocate byte_count bytes and return them in a slice, with the
11 /// slice's pointer aligned at least to alignment bytes.12 /// slice's pointer aligned at least to alignment bytes.
12 /// The returned newly allocated memory is undefined.13 /// The returned newly allocated memory is undefined.
14 /// `alignment` is guaranteed to be >= 1
15 /// `alignment` is guaranteed to be a power of 2
13 allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8,16 allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8,
1417
15 /// If `new_byte_count > old_mem.len`:18 /// If `new_byte_count > old_mem.len`:
...@@ -17,20 +20,37 @@ pub const Allocator = struct {...@@ -17,20 +20,37 @@ pub const Allocator = struct {
17 /// * alignment >= alignment of old_mem.ptr20 /// * alignment >= alignment of old_mem.ptr
18 ///21 ///
19 /// If `new_byte_count <= old_mem.len`:22 /// If `new_byte_count <= old_mem.len`:
20 /// * this function must return successfully. 23 /// * this function must return successfully.
21 /// * alignment <= alignment of old_mem.ptr24 /// * alignment <= alignment of old_mem.ptr
22 ///25 ///
23 /// The returned newly allocated memory is undefined.26 /// The returned newly allocated memory is undefined.
27 /// `alignment` is guaranteed to be >= 1
28 /// `alignment` is guaranteed to be a power of 2
24 reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8,29 reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8,
2530
26 /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`31 /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
27 freeFn: fn (self: &Allocator, old_mem: []u8) void,32 freeFn: fn (self: &Allocator, old_mem: []u8) void,
2833
29 fn create(self: &Allocator, comptime T: type) !&T {34 fn create(self: &Allocator, comptime T: type) !&T {
35 if (@sizeOf(T) == 0) return &{};
30 const slice = try self.alloc(T, 1);36 const slice = try self.alloc(T, 1);
31 return &slice[0];37 return &slice[0];
32 }38 }
3339
40 // TODO once #733 is solved, this will replace create
41 fn construct(self: &Allocator, init: var) t: {
42 // TODO this is a workaround for type getting parsed as Error!&const T
43 const T = @typeOf(init).Child;
44 break :t Error!&T;
45 } {
46 const T = @typeOf(init).Child;
47 if (@sizeOf(T) == 0) return &{};
48 const slice = try self.alloc(T, 1);
49 const ptr = &slice[0];
50 *ptr = *init;
51 return ptr;
52 }
53
34 fn destroy(self: &Allocator, ptr: var) void {54 fn destroy(self: &Allocator, ptr: var) void {
35 self.free(ptr[0..1]);55 self.free(ptr[0..1]);
36 }56 }
...@@ -48,7 +68,7 @@ pub const Allocator = struct {...@@ -48,7 +68,7 @@ pub const Allocator = struct {
48 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;68 const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
49 const byte_slice = try self.allocFn(self, byte_count, alignment);69 const byte_slice = try self.allocFn(self, byte_count, alignment);
50 assert(byte_slice.len == byte_count);70 assert(byte_slice.len == byte_count);
51 // This loop should get optimized out in ReleaseFast mode71 // This loop gets optimized out in ReleaseFast mode
52 for (byte_slice) |*byte| {72 for (byte_slice) |*byte| {
53 *byte = undefined;73 *byte = undefined;
54 }74 }
...@@ -75,7 +95,7 @@ pub const Allocator = struct {...@@ -75,7 +95,7 @@ pub const Allocator = struct {
75 const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment);95 const byte_slice = try self.reallocFn(self, old_byte_slice, byte_count, alignment);
76 assert(byte_slice.len == byte_count);96 assert(byte_slice.len == byte_count);
77 if (n > old_mem.len) {97 if (n > old_mem.len) {
78 // This loop should get optimized out in ReleaseFast mode98 // This loop gets optimized out in ReleaseFast mode
79 for (byte_slice[old_byte_slice.len..]) |*byte| {99 for (byte_slice[old_byte_slice.len..]) |*byte| {
80 *byte = undefined;100 *byte = undefined;
81 }101 }
...@@ -169,6 +189,20 @@ pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T {...@@ -169,6 +189,20 @@ pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T {
169 return new_buf;189 return new_buf;
170}190}
171191
192/// Remove values from the beginning of a slice.
193pub fn trimLeft(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
194 var begin: usize = 0;
195 while (begin < slice.len and indexOfScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {}
196 return slice[begin..];
197}
198
199/// Remove values from the end of a slice.
200pub fn trimRight(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
201 var end: usize = slice.len;
202 while (end > 0 and indexOfScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {}
203 return slice[0..end];
204}
205
172/// Remove values from the beginning and end of a slice.206/// Remove values from the beginning and end of a slice.
173pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {207pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
174 var begin: usize = 0;208 var begin: usize = 0;
...@@ -179,6 +213,8 @@ pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []co...@@ -179,6 +213,8 @@ pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []co
179}213}
180214
181test "mem.trim" {215test "mem.trim" {
216 assert(eql(u8, trimLeft(u8, " foo\n ", " \n"), "foo\n "));
217 assert(eql(u8, trimRight(u8, " foo\n ", " \n"), " foo"));
182 assert(eql(u8, trim(u8, " foo\n ", " \n"), "foo"));218 assert(eql(u8, trim(u8, " foo\n ", " \n"), "foo"));
183 assert(eql(u8, trim(u8, "foo", " \n"), "foo"));219 assert(eql(u8, trim(u8, "foo", " \n"), "foo"));
184}220}
...@@ -188,6 +224,17 @@ pub fn indexOfScalar(comptime T: type, slice: []const T, value: T) ?usize {...@@ -188,6 +224,17 @@ pub fn indexOfScalar(comptime T: type, slice: []const T, value: T) ?usize {
188 return indexOfScalarPos(T, slice, 0, value);224 return indexOfScalarPos(T, slice, 0, value);
189}225}
190226
227/// Linear search for the last index of a scalar value inside a slice.
228pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize {
229 var i: usize = slice.len;
230 while (i != 0) {
231 i -= 1;
232 if (slice[i] == value)
233 return i;
234 }
235 return null;
236}
237
191pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize {238pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize {
192 var i: usize = start_index;239 var i: usize = start_index;
193 while (i < slice.len) : (i += 1) {240 while (i < slice.len) : (i += 1) {
...@@ -201,6 +248,18 @@ pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize...@@ -201,6 +248,18 @@ pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize
201 return indexOfAnyPos(T, slice, 0, values);248 return indexOfAnyPos(T, slice, 0, values);
202}249}
203250
251pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize {
252 var i: usize = slice.len;
253 while (i != 0) {
254 i -= 1;
255 for (values) |value| {
256 if (slice[i] == value)
257 return i;
258 }
259 }
260 return null;
261}
262
204pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize {263pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize {
205 var i: usize = start_index;264 var i: usize = start_index;
206 while (i < slice.len) : (i += 1) {265 while (i < slice.len) : (i += 1) {
...@@ -216,6 +275,22 @@ pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize...@@ -216,6 +275,22 @@ pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize
216 return indexOfPos(T, haystack, 0, needle);275 return indexOfPos(T, haystack, 0, needle);
217}276}
218277
278/// Find the index in a slice of a sub-slice, searching from the end backwards.
279/// To start looking at a different index, slice the haystack first.
280/// TODO is there even a better algorithm for this?
281pub fn lastIndexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize {
282 if (needle.len > haystack.len)
283 return null;
284
285 var i: usize = haystack.len - needle.len;
286 while (true) : (i -= 1) {
287 if (mem.eql(T, haystack[i..i+needle.len], needle))
288 return i;
289 if (i == 0)
290 return null;
291 }
292}
293
219// TODO boyer-moore algorithm294// TODO boyer-moore algorithm
220pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize {295pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize {
221 if (needle.len > haystack.len)296 if (needle.len > haystack.len)
...@@ -232,9 +307,19 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee...@@ -232,9 +307,19 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee
232307
233test "mem.indexOf" {308test "mem.indexOf" {
234 assert(??indexOf(u8, "one two three four", "four") == 14);309 assert(??indexOf(u8, "one two three four", "four") == 14);
310 assert(??lastIndexOf(u8, "one two three two four", "two") == 14);
235 assert(indexOf(u8, "one two three four", "gour") == null);311 assert(indexOf(u8, "one two three four", "gour") == null);
312 assert(lastIndexOf(u8, "one two three four", "gour") == null);
236 assert(??indexOf(u8, "foo", "foo") == 0);313 assert(??indexOf(u8, "foo", "foo") == 0);
314 assert(??lastIndexOf(u8, "foo", "foo") == 0);
237 assert(indexOf(u8, "foo", "fool") == null);315 assert(indexOf(u8, "foo", "fool") == null);
316 assert(lastIndexOf(u8, "foo", "lfoo") == null);
317 assert(lastIndexOf(u8, "foo", "fool") == null);
318
319 assert(??indexOf(u8, "foo foo", "foo") == 0);
320 assert(??lastIndexOf(u8, "foo foo", "foo") == 4);
321 assert(??lastIndexOfAny(u8, "boo, cat", "abo") == 6);
322 assert(??lastIndexOfScalar(u8, "boo", 'o') == 2);
238}323}
239324
240/// Reads an integer from memory with size equal to bytes.len.325/// Reads an integer from memory with size equal to bytes.len.
...@@ -354,9 +439,24 @@ pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool...@@ -354,9 +439,24 @@ pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool
354 return if (needle.len > haystack.len) false else eql(T, haystack[0 .. needle.len], needle);439 return if (needle.len > haystack.len) false else eql(T, haystack[0 .. needle.len], needle);
355}440}
356441
442test "mem.startsWith" {
443 assert(startsWith(u8, "Bob", "Bo"));
444 assert(!startsWith(u8, "Needle in haystack", "haystack"));
445}
446
447pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool {
448 return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle);
449}
450
451
452test "mem.endsWith" {
453 assert(endsWith(u8, "Needle in haystack", "haystack"));
454 assert(!endsWith(u8, "Bob", "Bo"));
455}
456
357pub const SplitIterator = struct {457pub const SplitIterator = struct {
358 buffer: []const u8,458 buffer: []const u8,
359 split_bytes: []const u8, 459 split_bytes: []const u8,
360 index: usize,460 index: usize,
361461
362 pub fn next(self: &SplitIterator) ?[]const u8 {462 pub fn next(self: &SplitIterator) ?[]const u8 {
...@@ -550,3 +650,28 @@ test "std.mem.rotate" {...@@ -550,3 +650,28 @@ test "std.mem.rotate" {
550650
551 assert(eql(i32, arr, []i32{ 1, 2, 4, 5, 3 }));651 assert(eql(i32, arr, []i32{ 1, 2, 4, 5, 3 }));
552}652}
653
654// TODO: When https://github.com/zig-lang/zig/issues/649 is solved these can be done by
655// endian-casting the pointer and then dereferencing
656
657pub fn endianSwapIfLe(comptime T: type, x: T) T {
658 return endianSwapIf(builtin.Endian.Little, T, x);
659}
660
661pub fn endianSwapIfBe(comptime T: type, x: T) T {
662 return endianSwapIf(builtin.Endian.Big, T, x);
663}
664
665pub fn endianSwapIf(endian: builtin.Endian, comptime T: type, x: T) T {
666 return if (builtin.endian == endian) endianSwap(T, x) else x;
667}
668
669pub fn endianSwap(comptime T: type, x: T) T {
670 var buf: [@sizeOf(T)]u8 = undefined;
671 mem.writeInt(buf[0..], x, builtin.Endian.Little);
672 return mem.readInt(buf, T, builtin.Endian.Big);
673}
674
675test "std.mem.endianSwap" {
676 assert(endianSwap(u32, 0xDEADBEEF) == 0xEFBEADDE);
677}
std/net.zig+121-162
...@@ -1,143 +1,120 @@...@@ -1,143 +1,120 @@
1const std = @import("index.zig");1const std = @import("index.zig");
2const linux = std.os.linux;2const builtin = @import("builtin");
3const assert = std.debug.assert;3const assert = std.debug.assert;
4const endian = std.endian;4const net = this;
5const posix = std.os.posix;
6const mem = std.mem;
57
6// TODO don't trust this file, it bit rotted. start over8pub const TmpWinAddr = struct {
79 family: u8,
8const Connection = struct {10 data: [14]u8,
9 socket_fd: i32,
10
11 pub fn send(c: Connection, buf: []const u8) !usize {
12 const send_ret = linux.sendto(c.socket_fd, buf.ptr, buf.len, 0, null, 0);
13 const send_err = linux.getErrno(send_ret);
14 switch (send_err) {
15 0 => return send_ret,
16 linux.EINVAL => unreachable,
17 linux.EFAULT => unreachable,
18 linux.ECONNRESET => return error.ConnectionReset,
19 linux.EINTR => return error.SigInterrupt,
20 // TODO there are more possible errors
21 else => return error.Unexpected,
22 }
23 }
24
25 pub fn recv(c: Connection, buf: []u8) ![]u8 {
26 const recv_ret = linux.recvfrom(c.socket_fd, buf.ptr, buf.len, 0, null, null);
27 const recv_err = linux.getErrno(recv_ret);
28 switch (recv_err) {
29 0 => return buf[0..recv_ret],
30 linux.EINVAL => unreachable,
31 linux.EFAULT => unreachable,
32 linux.ENOTSOCK => return error.NotSocket,
33 linux.EINTR => return error.SigInterrupt,
34 linux.ENOMEM => return error.OutOfMemory,
35 linux.ECONNREFUSED => return error.ConnectionRefused,
36 linux.EBADF => return error.BadFd,
37 // TODO more error values
38 else => return error.Unexpected,
39 }
40 }
41
42 pub fn close(c: Connection) !void {
43 switch (linux.getErrno(linux.close(c.socket_fd))) {
44 0 => return,
45 linux.EBADF => unreachable,
46 linux.EINTR => return error.SigInterrupt,
47 linux.EIO => return error.Io,
48 else => return error.Unexpected,
49 }
50 }
51};11};
5212
53const Address = struct {13pub const OsAddress = switch (builtin.os) {
54 family: u16,14 builtin.Os.windows => TmpWinAddr,
55 scope_id: u32,15 else => posix.sockaddr,
56 addr: [16]u8,
57 sort_key: i32,
58};16};
5917
60pub fn lookup(hostname: []const u8, out_addrs: []Address) ![]Address {18pub const Address = struct {
61 if (hostname.len == 0) {19 os_addr: OsAddress,
6220
63 unreachable; // TODO21 pub fn initIp4(ip4: u32, port: u16) Address {
22 return Address {
23 .os_addr = posix.sockaddr {
24 .in = posix.sockaddr_in {
25 .family = posix.AF_INET,
26 .port = std.mem.endianSwapIfLe(u16, port),
27 .addr = ip4,
28 .zero = []u8{0} ** 8,
29 },
30 },
31 };
64 }32 }
6533
66 unreachable; // TODO34 pub fn initIp6(ip6: &const Ip6Addr, port: u16) Address {
67}35 return Address {
36 .family = posix.AF_INET6,
37 .os_addr = posix.sockaddr {
38 .in6 = posix.sockaddr_in6 {
39 .family = posix.AF_INET6,
40 .port = std.mem.endianSwapIfLe(u16, port),
41 .flowinfo = 0,
42 .addr = ip6.addr,
43 .scope_id = ip6.scope_id,
44 },
45 },
46 };
47 }
6848
69pub fn connectAddr(addr: &Address, port: u16) !Connection {49 pub fn initPosix(addr: &const posix.sockaddr) Address {
70 const socket_ret = linux.socket(addr.family, linux.SOCK_STREAM, linux.PROTO_tcp);50 return Address {
71 const socket_err = linux.getErrno(socket_ret);51 .os_addr = *addr,
72 if (socket_err > 0) {52 };
73 // TODO figure out possible errors from socket()
74 return error.Unexpected;
75 }53 }
76 const socket_fd = i32(socket_ret);
7754
78 const connect_ret = if (addr.family == linux.AF_INET) x: {55 pub fn format(self: &const Address, out_stream: var) !void {
79 var os_addr: linux.sockaddr_in = undefined;56 switch (self.os_addr.in.family) {
80 os_addr.family = addr.family;57 posix.AF_INET => {
81 os_addr.port = endian.swapIfLe(u16, port);58 const native_endian_port = std.mem.endianSwapIfLe(u16, self.os_addr.in.port);
82 @memcpy((&u8)(&os_addr.addr), &addr.addr[0], 4);59 const bytes = ([]const u8)((&self.os_addr.in.addr)[0..1]);
83 @memset(&os_addr.zero[0], 0, @sizeOf(@typeOf(os_addr.zero)));60 try out_stream.print("{}.{}.{}.{}:{}", bytes[0], bytes[1], bytes[2], bytes[3], native_endian_port);
84 break :x linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in));61 },
85 } else if (addr.family == linux.AF_INET6) x: {62 posix.AF_INET6 => {
86 var os_addr: linux.sockaddr_in6 = undefined;63 const native_endian_port = std.mem.endianSwapIfLe(u16, self.os_addr.in6.port);
87 os_addr.family = addr.family;64 try out_stream.print("[TODO render ip6 address]:{}", native_endian_port);
88 os_addr.port = endian.swapIfLe(u16, port);
89 os_addr.flowinfo = 0;
90 os_addr.scope_id = addr.scope_id;
91 @memcpy(&os_addr.addr[0], &addr.addr[0], 16);
92 break :x linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in6));
93 } else {
94 unreachable;
95 };
96 const connect_err = linux.getErrno(connect_ret);
97 if (connect_err > 0) {
98 switch (connect_err) {
99 linux.ETIMEDOUT => return error.TimedOut,
100 else => {
101 // TODO figure out possible errors from connect()
102 return error.Unexpected;
103 },65 },
66 else => try out_stream.write("(unrecognized address family)"),
104 }67 }
105 }68 }
69};
10670
107 return Connection {71pub fn parseIp4(buf: []const u8) !u32 {
108 .socket_fd = socket_fd,72 var result: u32 = undefined;
109 };73 const out_ptr = ([]u8)((&result)[0..1]);
110}
111
112pub fn connect(hostname: []const u8, port: u16) !Connection {
113 var addrs_buf: [1]Address = undefined;
114 const addrs_slice = try lookup(hostname, addrs_buf[0..]);
115 const main_addr = &addrs_slice[0];
116
117 return connectAddr(main_addr, port);
118}
11974
120pub fn parseIpLiteral(buf: []const u8) !Address {75 var x: u8 = 0;
76 var index: u8 = 0;
77 var saw_any_digits = false;
78 for (buf) |c| {
79 if (c == '.') {
80 if (!saw_any_digits) {
81 return error.InvalidCharacter;
82 }
83 if (index == 3) {
84 return error.InvalidEnd;
85 }
86 out_ptr[index] = x;
87 index += 1;
88 x = 0;
89 saw_any_digits = false;
90 } else if (c >= '0' and c <= '9') {
91 saw_any_digits = true;
92 const digit = c - '0';
93 if (@mulWithOverflow(u8, x, 10, &x)) {
94 return error.Overflow;
95 }
96 if (@addWithOverflow(u8, x, digit, &x)) {
97 return error.Overflow;
98 }
99 } else {
100 return error.InvalidCharacter;
101 }
102 }
103 if (index == 3 and saw_any_digits) {
104 out_ptr[index] = x;
105 return result;
106 }
121107
122 return error.InvalidIpLiteral;108 return error.Incomplete;
123}109}
124110
125fn hexDigit(c: u8) u8 {111pub const Ip6Addr = struct {
126 // TODO use switch with range112 scope_id: u32,
127 if ('0' <= c and c <= '9') {113 addr: [16]u8,
128 return c - '0';114};
129 } else if ('A' <= c and c <= 'Z') {
130 return c - 'A' + 10;
131 } else if ('a' <= c and c <= 'z') {
132 return c - 'a' + 10;
133 } else {
134 return @maxValue(u8);
135 }
136}
137115
138fn parseIp6(buf: []const u8) !Address {116pub fn parseIp6(buf: []const u8) !Ip6Addr {
139 var result: Address = undefined;117 var result: Ip6Addr = undefined;
140 result.family = linux.AF_INET6;
141 result.scope_id = 0;118 result.scope_id = 0;
142 const ip_slice = result.addr[0..];119 const ip_slice = result.addr[0..];
143120
...@@ -156,14 +133,14 @@ fn parseIp6(buf: []const u8) !Address {...@@ -156,14 +133,14 @@ fn parseIp6(buf: []const u8) !Address {
156 return error.Overflow;133 return error.Overflow;
157 }134 }
158 } else {135 } else {
159 return error.InvalidChar;136 return error.InvalidCharacter;
160 }137 }
161 } else if (c == ':') {138 } else if (c == ':') {
162 if (!saw_any_digits) {139 if (!saw_any_digits) {
163 return error.InvalidChar;140 return error.InvalidCharacter;
164 }141 }
165 if (index == 14) {142 if (index == 14) {
166 return error.JunkAtEnd;143 return error.InvalidEnd;
167 }144 }
168 ip_slice[index] = @truncate(u8, x >> 8);145 ip_slice[index] = @truncate(u8, x >> 8);
169 index += 1;146 index += 1;
...@@ -174,7 +151,7 @@ fn parseIp6(buf: []const u8) !Address {...@@ -174,7 +151,7 @@ fn parseIp6(buf: []const u8) !Address {
174 saw_any_digits = false;151 saw_any_digits = false;
175 } else if (c == '%') {152 } else if (c == '%') {
176 if (!saw_any_digits) {153 if (!saw_any_digits) {
177 return error.InvalidChar;154 return error.InvalidCharacter;
178 }155 }
179 if (index == 14) {156 if (index == 14) {
180 ip_slice[index] = @truncate(u8, x >> 8);157 ip_slice[index] = @truncate(u8, x >> 8);
...@@ -185,10 +162,7 @@ fn parseIp6(buf: []const u8) !Address {...@@ -185,10 +162,7 @@ fn parseIp6(buf: []const u8) !Address {
185 scope_id = true;162 scope_id = true;
186 saw_any_digits = false;163 saw_any_digits = false;
187 } else {164 } else {
188 const digit = hexDigit(c);165 const digit = try std.fmt.charToDigit(c, 16);
189 if (digit == @maxValue(u8)) {
190 return error.InvalidChar;
191 }
192 if (@mulWithOverflow(u16, x, 16, &x)) {166 if (@mulWithOverflow(u16, x, 16, &x)) {
193 return error.Overflow;167 return error.Overflow;
194 }168 }
...@@ -216,42 +190,27 @@ fn parseIp6(buf: []const u8) !Address {...@@ -216,42 +190,27 @@ fn parseIp6(buf: []const u8) !Address {
216 return error.Incomplete;190 return error.Incomplete;
217}191}
218192
219fn parseIp4(buf: []const u8) !u32 {193test "std.net.parseIp4" {
220 var result: u32 = undefined;194 assert((try parseIp4("127.0.0.1")) == std.mem.endianSwapIfLe(u32, 0x7f000001));
221 const out_ptr = ([]u8)((&result)[0..1]);
222195
223 var x: u8 = 0;196 testParseIp4Fail("256.0.0.1", error.Overflow);
224 var index: u8 = 0;197 testParseIp4Fail("x.0.0.1", error.InvalidCharacter);
225 var saw_any_digits = false;198 testParseIp4Fail("127.0.0.1.1", error.InvalidEnd);
226 for (buf) |c| {199 testParseIp4Fail("127.0.0.", error.Incomplete);
227 if (c == '.') {200 testParseIp4Fail("100..0.1", error.InvalidCharacter);
228 if (!saw_any_digits) {201}
229 return error.InvalidChar;202
230 }203fn testParseIp4Fail(buf: []const u8, expected_err: error) void {
231 if (index == 3) {204 if (parseIp4(buf)) |_| {
232 return error.JunkAtEnd;205 @panic("expected error");
233 }206 } else |e| {
234 out_ptr[index] = x;207 assert(e == expected_err);
235 index += 1;
236 x = 0;
237 saw_any_digits = false;
238 } else if (c >= '0' and c <= '9') {
239 saw_any_digits = true;
240 const digit = c - '0';
241 if (@mulWithOverflow(u8, x, 10, &x)) {
242 return error.Overflow;
243 }
244 if (@addWithOverflow(u8, x, digit, &x)) {
245 return error.Overflow;
246 }
247 } else {
248 return error.InvalidChar;
249 }
250 }
251 if (index == 3 and saw_any_digits) {
252 out_ptr[index] = x;
253 return result;
254 }208 }
209}
255210
256 return error.Incomplete;211test "std.net.parseIp6" {
212 const addr = try parseIp6("FF01:0:0:0:0:0:0:FB");
213 assert(addr.addr[0] == 0xff);
214 assert(addr.addr[1] == 0x01);
215 assert(addr.addr[2] == 0x00);
257}216}
std/os/darwin.zig+27-3
...@@ -41,6 +41,11 @@ pub const SA_64REGSET = 0x0200; /// signal handler with SA_SIGINFO args with 64...@@ -41,6 +41,11 @@ pub const SA_64REGSET = 0x0200; /// signal handler with SA_SIGINFO args with 64
41pub const O_LARGEFILE = 0x0000;41pub const O_LARGEFILE = 0x0000;
42pub const O_PATH = 0x0000;42pub const O_PATH = 0x0000;
4343
44pub const F_OK = 0;
45pub const X_OK = 1;
46pub const W_OK = 2;
47pub const R_OK = 4;
48
44pub const O_RDONLY = 0x0000; /// open for reading only49pub const O_RDONLY = 0x0000; /// open for reading only
45pub const O_WRONLY = 0x0001; /// open for writing only50pub const O_WRONLY = 0x0001; /// open for writing only
46pub const O_RDWR = 0x0002; /// open for reading and writing51pub const O_RDWR = 0x0002; /// open for reading and writing
...@@ -179,7 +184,7 @@ pub fn write(fd: i32, buf: &const u8, nbyte: usize) usize {...@@ -179,7 +184,7 @@ pub fn write(fd: i32, buf: &const u8, nbyte: usize) usize {
179 return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte));184 return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte));
180}185}
181186
182pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,187pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32,
183 offset: isize) usize188 offset: isize) usize
184{189{
185 const ptr_result = c.mmap(@ptrCast(&c_void, address), length,190 const ptr_result = c.mmap(@ptrCast(&c_void, address), length,
...@@ -188,8 +193,8 @@ pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,...@@ -188,8 +193,8 @@ pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,
188 return errnoWrap(isize_result);193 return errnoWrap(isize_result);
189}194}
190195
191pub fn munmap(address: &u8, length: usize) usize {196pub fn munmap(address: usize, length: usize) usize {
192 return errnoWrap(c.munmap(@ptrCast(&c_void, address), length));197 return errnoWrap(c.munmap(@intToPtr(&c_void, address), length));
193}198}
194199
195pub fn unlink(path: &const u8) usize {200pub fn unlink(path: &const u8) usize {
...@@ -209,6 +214,10 @@ pub fn fork() usize {...@@ -209,6 +214,10 @@ pub fn fork() usize {
209 return errnoWrap(c.fork());214 return errnoWrap(c.fork());
210}215}
211216
217pub fn access(path: &const u8, mode: u32) usize {
218 return errnoWrap(c.access(path, mode));
219}
220
212pub fn pipe(fds: &[2]i32) usize {221pub fn pipe(fds: &[2]i32) usize {
213 comptime assert(i32.bit_count == c_int.bit_count);222 comptime assert(i32.bit_count == c_int.bit_count);
214 return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));223 return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));
...@@ -251,6 +260,10 @@ pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) u...@@ -251,6 +260,10 @@ pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) u
251 return errnoWrap(c.readlink(path, buf_ptr, buf_len));260 return errnoWrap(c.readlink(path, buf_ptr, buf_len));
252}261}
253262
263pub fn gettimeofday(tv: ?&timeval, tz: ?&timezone) usize {
264 return errnoWrap(c.gettimeofday(tv, tz));
265}
266
254pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize {267pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize {
255 return errnoWrap(c.nanosleep(req, rem));268 return errnoWrap(c.nanosleep(req, rem));
256}269}
...@@ -301,6 +314,9 @@ pub const timespec = c.timespec;...@@ -301,6 +314,9 @@ pub const timespec = c.timespec;
301pub const Stat = c.Stat;314pub const Stat = c.Stat;
302pub const dirent = c.dirent;315pub const dirent = c.dirent;
303316
317pub const sa_family_t = c.sa_family_t;
318pub const sockaddr = c.sockaddr;
319
304/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.320/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
305pub const Sigaction = struct {321pub const Sigaction = struct {
306 handler: extern fn(i32)void,322 handler: extern fn(i32)void,
...@@ -318,3 +334,11 @@ pub fn sigaddset(set: &sigset_t, signo: u5) void {...@@ -318,3 +334,11 @@ pub fn sigaddset(set: &sigset_t, signo: u5) void {
318fn errnoWrap(value: isize) usize {334fn errnoWrap(value: isize) usize {
319 return @bitCast(usize, if (value == -1) -isize(*c._errno()) else value);335 return @bitCast(usize, if (value == -1) -isize(*c._errno()) else value);
320}336}
337
338
339pub const timezone = c.timezone;
340pub const timeval = c.timeval;
341pub const mach_timebase_info_data = c.mach_timebase_info_data;
342
343pub const mach_absolute_time = c.mach_absolute_time;
344pub const mach_timebase_info = c.mach_timebase_info;
std/os/epoch.zig created+26
...@@ -0,0 +1,26 @@
1/// Epoch reference times in terms of their difference from
2/// posix epoch in seconds.
3pub const posix = 0; //Jan 01, 1970 AD
4pub const dos = 315532800; //Jan 01, 1980 AD
5pub const ios = 978307200; //Jan 01, 2001 AD
6pub const openvms = -3506716800; //Nov 17, 1858 AD
7pub const zos = -2208988800; //Jan 01, 1900 AD
8pub const windows = -11644473600; //Jan 01, 1601 AD
9pub const amiga = 252460800; //Jan 01, 1978 AD
10pub const pickos = -63244800; //Dec 31, 1967 AD
11pub const gps = 315964800; //Jan 06, 1980 AD
12pub const clr = -62135769600; //Jan 01, 0001 AD
13
14pub const unix = posix;
15pub const android = posix;
16pub const os2 = dos;
17pub const bios = dos;
18pub const vfat = dos;
19pub const ntfs = windows;
20pub const ntp = zos;
21pub const jbase = pickos;
22pub const aros = amiga;
23pub const morphos = amiga;
24pub const brew = gps;
25pub const atsc = gps;
26pub const go = clr;
\ No newline at end of file
std/os/file.zig+44-1
...@@ -85,6 +85,47 @@ pub const File = struct {...@@ -85,6 +85,47 @@ pub const File = struct {
85 };85 };
86 }86 }
8787
88 pub fn access(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool {
89 const path_with_null = try std.cstr.addNullByte(allocator, path);
90 defer allocator.free(path_with_null);
91
92 if (is_posix) {
93 // mode is ignored and is always F_OK for now
94 const result = posix.access(path_with_null.ptr, posix.F_OK);
95 const err = posix.getErrno(result);
96 if (err > 0) {
97 return switch (err) {
98 posix.EACCES => error.PermissionDenied,
99 posix.EROFS => error.PermissionDenied,
100 posix.ELOOP => error.PermissionDenied,
101 posix.ETXTBSY => error.PermissionDenied,
102 posix.ENOTDIR => error.NotFound,
103 posix.ENOENT => error.NotFound,
104
105 posix.ENAMETOOLONG => error.NameTooLong,
106 posix.EINVAL => error.BadMode,
107 posix.EFAULT => error.BadPathName,
108 posix.EIO => error.Io,
109 posix.ENOMEM => error.SystemResources,
110 else => os.unexpectedErrorPosix(err),
111 };
112 }
113 return true;
114 } else if (is_windows) {
115 if (os.windows.PathFileExists(path_with_null.ptr) == os.windows.TRUE) {
116 return true;
117 }
118
119 const err = windows.GetLastError();
120 return switch (err) {
121 windows.ERROR.FILE_NOT_FOUND => error.NotFound,
122 windows.ERROR.ACCESS_DENIED => error.PermissionDenied,
123 else => os.unexpectedErrorWindows(err),
124 };
125 } else {
126 @compileError("TODO implement access for this OS");
127 }
128 }
88129
89 /// Upon success, the stream is in an uninitialized state. To continue using it,130 /// Upon success, the stream is in an uninitialized state. To continue using it,
90 /// you must use the open() function.131 /// you must use the open() function.
...@@ -245,7 +286,9 @@ pub const File = struct {...@@ -245,7 +286,9 @@ pub const File = struct {
245 };286 };
246 }287 }
247288
248 return stat.mode;289 // TODO: we should be able to cast u16 to ModeError!u32, making this
290 // explicit cast not necessary
291 return os.FileMode(stat.mode);
249 } else if (is_windows) {292 } else if (is_windows) {
250 return {};293 return {};
251 } else {294 } else {
std/os/index.zig+932-172
...@@ -2,22 +2,42 @@ const std = @import("../index.zig");...@@ -2,22 +2,42 @@ const std = @import("../index.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const Os = builtin.Os;3const Os = builtin.Os;
4const is_windows = builtin.os == Os.windows;4const is_windows = builtin.os == Os.windows;
5const is_posix = switch (builtin.os) {
6 builtin.Os.linux,
7 builtin.Os.macosx => true,
8 else => false,
9};
5const os = this;10const os = this;
611
12test "std.os" {
13 _ = @import("child_process.zig");
14 _ = @import("darwin.zig");
15 _ = @import("darwin_errno.zig");
16 _ = @import("get_user_id.zig");
17 _ = @import("linux/index.zig");
18 _ = @import("path.zig");
19 _ = @import("test.zig");
20 _ = @import("time.zig");
21 _ = @import("windows/index.zig");
22}
23
7pub const windows = @import("windows/index.zig");24pub const windows = @import("windows/index.zig");
8pub const darwin = @import("darwin.zig");25pub const darwin = @import("darwin.zig");
9pub const linux = @import("linux/index.zig");26pub const linux = @import("linux/index.zig");
10pub const zen = @import("zen.zig");27pub const zen = @import("zen.zig");
11pub const posix = switch(builtin.os) {28pub const posix = switch (builtin.os) {
12 Os.linux => linux,29 Os.linux => linux,
13 Os.macosx, Os.ios => darwin,30 Os.macosx,
31 Os.ios => darwin,
14 Os.zen => zen,32 Os.zen => zen,
15 else => @compileError("Unsupported OS"),33 else => @compileError("Unsupported OS"),
16};34};
35pub const net = @import("net.zig");
1736
18pub const ChildProcess = @import("child_process.zig").ChildProcess;37pub const ChildProcess = @import("child_process.zig").ChildProcess;
19pub const path = @import("path.zig");38pub const path = @import("path.zig");
20pub const File = @import("file.zig").File;39pub const File = @import("file.zig").File;
40pub const time = @import("time.zig");
2141
22pub const FileMode = switch (builtin.os) {42pub const FileMode = switch (builtin.os) {
23 Os.windows => void,43 Os.windows => void,
...@@ -40,7 +60,7 @@ pub const windowsWrite = windows_util.windowsWrite;...@@ -40,7 +60,7 @@ pub const windowsWrite = windows_util.windowsWrite;
40pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;60pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty;
41pub const windowsOpen = windows_util.windowsOpen;61pub const windowsOpen = windows_util.windowsOpen;
42pub const windowsLoadDll = windows_util.windowsLoadDll;62pub const windowsLoadDll = windows_util.windowsLoadDll;
43pub const windowsUnloadDll = windows_util.windowsUnloadDll; 63pub const windowsUnloadDll = windows_util.windowsUnloadDll;
44pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;64pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock;
4565
46pub const WindowsWaitError = windows_util.WaitError;66pub const WindowsWaitError = windows_util.WaitError;
...@@ -79,9 +99,9 @@ pub fn getRandomBytes(buf: []u8) !void {...@@ -79,9 +99,9 @@ pub fn getRandomBytes(buf: []u8) !void {
79 switch (err) {99 switch (err) {
80 posix.EINVAL => unreachable,100 posix.EINVAL => unreachable,
81 posix.EFAULT => unreachable,101 posix.EFAULT => unreachable,
82 posix.EINTR => continue,102 posix.EINTR => continue,
83 posix.ENOSYS => {103 posix.ENOSYS => {
84 const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0);104 const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0);
85 defer close(fd);105 defer close(fd);
86106
87 try posixRead(fd, buf);107 try posixRead(fd, buf);
...@@ -92,8 +112,9 @@ pub fn getRandomBytes(buf: []u8) !void {...@@ -92,8 +112,9 @@ pub fn getRandomBytes(buf: []u8) !void {
92 }112 }
93 return;113 return;
94 },114 },
95 Os.macosx, Os.ios => {115 Os.macosx,
96 const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY|posix.O_CLOEXEC, 0);116 Os.ios => {
117 const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0);
97 defer close(fd);118 defer close(fd);
98119
99 try posixRead(fd, buf);120 try posixRead(fd, buf);
...@@ -116,7 +137,20 @@ pub fn getRandomBytes(buf: []u8) !void {...@@ -116,7 +137,20 @@ pub fn getRandomBytes(buf: []u8) !void {
116 }137 }
117 },138 },
118 Os.zen => {139 Os.zen => {
119 const randomness = []u8 {42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45};140 const randomness = []u8 {
141 42,
142 1,
143 7,
144 12,
145 22,
146 17,
147 99,
148 16,
149 26,
150 87,
151 41,
152 45,
153 };
120 var i: usize = 0;154 var i: usize = 0;
121 while (i < buf.len) : (i += 1) {155 while (i < buf.len) : (i += 1) {
122 if (i > randomness.len) return error.Unknown;156 if (i > randomness.len) return error.Unknown;
...@@ -141,7 +175,9 @@ pub fn abort() noreturn {...@@ -141,7 +175,9 @@ pub fn abort() noreturn {
141 c.abort();175 c.abort();
142 }176 }
143 switch (builtin.os) {177 switch (builtin.os) {
144 Os.linux, Os.macosx, Os.ios => {178 Os.linux,
179 Os.macosx,
180 Os.ios => {
145 _ = posix.raise(posix.SIGABRT);181 _ = posix.raise(posix.SIGABRT);
146 _ = posix.raise(posix.SIGKILL);182 _ = posix.raise(posix.SIGKILL);
147 while (true) {}183 while (true) {}
...@@ -163,7 +199,9 @@ pub fn exit(status: u8) noreturn {...@@ -163,7 +199,9 @@ pub fn exit(status: u8) noreturn {
163 c.exit(status);199 c.exit(status);
164 }200 }
165 switch (builtin.os) {201 switch (builtin.os) {
166 Os.linux, Os.macosx, Os.ios => {202 Os.linux,
203 Os.macosx,
204 Os.ios => {
167 posix.exit(status);205 posix.exit(status);
168 },206 },
169 Os.windows => {207 Os.windows => {
...@@ -173,6 +211,13 @@ pub fn exit(status: u8) noreturn {...@@ -173,6 +211,13 @@ pub fn exit(status: u8) noreturn {
173 }211 }
174}212}
175213
214/// When a file descriptor is closed on linux, it pops the first
215/// node from this queue and resumes it.
216/// Async functions which get the EMFILE error code can suspend,
217/// putting their coroutine handle into this list.
218/// TODO make this an atomic linked list
219pub var emfile_promise_queue = std.LinkedList(promise).init();
220
176/// Closes the file handle. Keeps trying if it gets interrupted by a signal.221/// Closes the file handle. Keeps trying if it gets interrupted by a signal.
177pub fn close(handle: FileHandle) void {222pub fn close(handle: FileHandle) void {
178 if (is_windows) {223 if (is_windows) {
...@@ -180,10 +225,12 @@ pub fn close(handle: FileHandle) void {...@@ -180,10 +225,12 @@ pub fn close(handle: FileHandle) void {
180 } else {225 } else {
181 while (true) {226 while (true) {
182 const err = posix.getErrno(posix.close(handle));227 const err = posix.getErrno(posix.close(handle));
183 if (err == posix.EINTR) {228 switch (err) {
184 continue;229 posix.EINTR => continue,
185 } else {230 else => {
186 return;231 if (emfile_promise_queue.popFirst()) |p| resume p.data;
232 return;
233 },
187 }234 }
188 }235 }
189 }236 }
...@@ -203,12 +250,14 @@ pub fn posixRead(fd: i32, buf: []u8) !void {...@@ -203,12 +250,14 @@ pub fn posixRead(fd: i32, buf: []u8) !void {
203 if (err > 0) {250 if (err > 0) {
204 return switch (err) {251 return switch (err) {
205 posix.EINTR => continue,252 posix.EINTR => continue,
206 posix.EINVAL, posix.EFAULT => unreachable,253 posix.EINVAL,
254 posix.EFAULT => unreachable,
207 posix.EAGAIN => error.WouldBlock,255 posix.EAGAIN => error.WouldBlock,
208 posix.EBADF => error.FileClosed,256 posix.EBADF => error.FileClosed,
209 posix.EIO => error.InputOutput,257 posix.EIO => error.InputOutput,
210 posix.EISDIR => error.IsDir,258 posix.EISDIR => error.IsDir,
211 posix.ENOBUFS, posix.ENOMEM => error.SystemResources,259 posix.ENOBUFS,
260 posix.ENOMEM => error.SystemResources,
212 else => unexpectedErrorPosix(err),261 else => unexpectedErrorPosix(err),
213 };262 };
214 }263 }
...@@ -242,18 +291,19 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {...@@ -242,18 +291,19 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {
242 const write_err = posix.getErrno(rc);291 const write_err = posix.getErrno(rc);
243 if (write_err > 0) {292 if (write_err > 0) {
244 return switch (write_err) {293 return switch (write_err) {
245 posix.EINTR => continue,294 posix.EINTR => continue,
246 posix.EINVAL, posix.EFAULT => unreachable,295 posix.EINVAL,
296 posix.EFAULT => unreachable,
247 posix.EAGAIN => PosixWriteError.WouldBlock,297 posix.EAGAIN => PosixWriteError.WouldBlock,
248 posix.EBADF => PosixWriteError.FileClosed,298 posix.EBADF => PosixWriteError.FileClosed,
249 posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired,299 posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired,
250 posix.EDQUOT => PosixWriteError.DiskQuota,300 posix.EDQUOT => PosixWriteError.DiskQuota,
251 posix.EFBIG => PosixWriteError.FileTooBig,301 posix.EFBIG => PosixWriteError.FileTooBig,
252 posix.EIO => PosixWriteError.InputOutput,302 posix.EIO => PosixWriteError.InputOutput,
253 posix.ENOSPC => PosixWriteError.NoSpaceLeft,303 posix.ENOSPC => PosixWriteError.NoSpaceLeft,
254 posix.EPERM => PosixWriteError.AccessDenied,304 posix.EPERM => PosixWriteError.AccessDenied,
255 posix.EPIPE => PosixWriteError.BrokenPipe,305 posix.EPIPE => PosixWriteError.BrokenPipe,
256 else => unexpectedErrorPosix(write_err),306 else => unexpectedErrorPosix(write_err),
257 };307 };
258 }308 }
259 index += rc;309 index += rc;
...@@ -299,7 +349,8 @@ pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 {...@@ -299,7 +349,8 @@ pub fn posixOpenC(file_path: &const u8, flags: u32, perm: usize) !i32 {
299 posix.EFAULT => unreachable,349 posix.EFAULT => unreachable,
300 posix.EINVAL => unreachable,350 posix.EINVAL => unreachable,
301 posix.EACCES => return PosixOpenError.AccessDenied,351 posix.EACCES => return PosixOpenError.AccessDenied,
302 posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig,352 posix.EFBIG,
353 posix.EOVERFLOW => return PosixOpenError.FileTooBig,
303 posix.EISDIR => return PosixOpenError.IsDir,354 posix.EISDIR => return PosixOpenError.IsDir,
304 posix.ELOOP => return PosixOpenError.SymLinkLoop,355 posix.ELOOP => return PosixOpenError.SymLinkLoop,
305 posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded,356 posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded,
...@@ -324,7 +375,8 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void {...@@ -324,7 +375,8 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) !void {
324 const err = posix.getErrno(posix.dup2(old_fd, new_fd));375 const err = posix.getErrno(posix.dup2(old_fd, new_fd));
325 if (err > 0) {376 if (err > 0) {
326 return switch (err) {377 return switch (err) {
327 posix.EBUSY, posix.EINTR => continue,378 posix.EBUSY,
379 posix.EINTR => continue,
328 posix.EMFILE => error.ProcessFdQuotaExceeded,380 posix.EMFILE => error.ProcessFdQuotaExceeded,
329 posix.EINVAL => unreachable,381 posix.EINVAL => unreachable,
330 else => unexpectedErrorPosix(err),382 else => unexpectedErrorPosix(err),
...@@ -359,7 +411,7 @@ pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap)...@@ -359,7 +411,7 @@ pub fn createNullDelimitedEnvMap(allocator: &Allocator, env_map: &const BufMap)
359411
360pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {412pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {
361 for (envp_buf) |env| {413 for (envp_buf) |env| {
362 const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break;414 const env_buf = if (env) |ptr| ptr[0..cstr.len(ptr) + 1] else break;
363 allocator.free(env_buf);415 allocator.free(env_buf);
364 }416 }
365 allocator.free(envp_buf);417 allocator.free(envp_buf);
...@@ -370,9 +422,7 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {...@@ -370,9 +422,7 @@ pub fn freeNullDelimitedEnvMap(allocator: &Allocator, envp_buf: []?&u8) void {
370/// pointers after the args and after the environment variables.422/// pointers after the args and after the environment variables.
371/// `argv[0]` is the executable path.423/// `argv[0]` is the executable path.
372/// This function also uses the PATH environment variable to get the full path to the executable.424/// This function also uses the PATH environment variable to get the full path to the executable.
373pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,425pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap, allocator: &Allocator) !void {
374 allocator: &Allocator) !void
375{
376 const argv_buf = try allocator.alloc(?&u8, argv.len + 1);426 const argv_buf = try allocator.alloc(?&u8, argv.len + 1);
377 mem.set(?&u8, argv_buf, null);427 mem.set(?&u8, argv_buf, null);
378 defer {428 defer {
...@@ -411,7 +461,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,...@@ -411,7 +461,7 @@ pub fn posixExecve(argv: []const []const u8, env_map: &const BufMap,
411 while (it.next()) |search_path| {461 while (it.next()) |search_path| {
412 mem.copy(u8, path_buf, search_path);462 mem.copy(u8, path_buf, search_path);
413 path_buf[search_path.len] = '/';463 path_buf[search_path.len] = '/';
414 mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path);464 mem.copy(u8, path_buf[search_path.len + 1..], exe_path);
415 path_buf[search_path.len + exe_path.len + 1] = 0;465 path_buf[search_path.len + exe_path.len + 1] = 0;
416 err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr));466 err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr));
417 assert(err > 0);467 assert(err > 0);
...@@ -443,10 +493,17 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {...@@ -443,10 +493,17 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
443 assert(err > 0);493 assert(err > 0);
444 return switch (err) {494 return switch (err) {
445 posix.EFAULT => unreachable,495 posix.EFAULT => unreachable,
446 posix.E2BIG, posix.EMFILE, posix.ENAMETOOLONG, posix.ENFILE, posix.ENOMEM => error.SystemResources,496 posix.E2BIG,
447 posix.EACCES, posix.EPERM => error.AccessDenied,497 posix.EMFILE,
448 posix.EINVAL, posix.ENOEXEC => error.InvalidExe,498 posix.ENAMETOOLONG,
449 posix.EIO, posix.ELOOP => error.FileSystem,499 posix.ENFILE,
500 posix.ENOMEM => error.SystemResources,
501 posix.EACCES,
502 posix.EPERM => error.AccessDenied,
503 posix.EINVAL,
504 posix.ENOEXEC => error.InvalidExe,
505 posix.EIO,
506 posix.ELOOP => error.FileSystem,
450 posix.EISDIR => error.IsDir,507 posix.EISDIR => error.IsDir,
451 posix.ENOENT => error.FileNotFound,508 posix.ENOENT => error.FileNotFound,
452 posix.ENOTDIR => error.NotDir,509 posix.ENOTDIR => error.NotDir,
...@@ -455,6 +512,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {...@@ -455,6 +512,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
455 };512 };
456}513}
457514
515pub var linux_aux_raw = []usize {0} ** 38;
458pub var posix_environ_raw: []&u8 = undefined;516pub var posix_environ_raw: []&u8 = undefined;
459517
460/// Caller must free result when done.518/// Caller must free result when done.
...@@ -468,8 +526,7 @@ pub fn getEnvMap(allocator: &Allocator) !BufMap {...@@ -468,8 +526,7 @@ pub fn getEnvMap(allocator: &Allocator) !BufMap {
468526
469 var i: usize = 0;527 var i: usize = 0;
470 while (true) {528 while (true) {
471 if (ptr[i] == 0)529 if (ptr[i] == 0) return result;
472 return result;
473530
474 const key_start = i;531 const key_start = i;
475532
...@@ -507,8 +564,7 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 {...@@ -507,8 +564,7 @@ pub fn getEnvPosix(key: []const u8) ?[]const u8 {
507 var line_i: usize = 0;564 var line_i: usize = 0;
508 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}565 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
509 const this_key = ptr[0..line_i];566 const this_key = ptr[0..line_i];
510 if (!mem.eql(u8, key, this_key))567 if (!mem.eql(u8, key, this_key)) continue;
511 continue;
512568
513 var end_i: usize = line_i;569 var end_i: usize = line_i;
514 while (ptr[end_i] != 0) : (end_i += 1) {}570 while (ptr[end_i] != 0) : (end_i += 1) {}
...@@ -661,8 +717,10 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:...@@ -661,8 +717,10 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
661 const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));717 const err = posix.getErrno(posix.symlink(existing_buf.ptr, new_buf.ptr));
662 if (err > 0) {718 if (err > 0) {
663 return switch (err) {719 return switch (err) {
664 posix.EFAULT, posix.EINVAL => unreachable,720 posix.EFAULT,
665 posix.EACCES, posix.EPERM => error.AccessDenied,721 posix.EINVAL => unreachable,
722 posix.EACCES,
723 posix.EPERM => error.AccessDenied,
666 posix.EDQUOT => error.DiskQuota,724 posix.EDQUOT => error.DiskQuota,
667 posix.EEXIST => error.PathAlreadyExists,725 posix.EEXIST => error.PathAlreadyExists,
668 posix.EIO => error.FileSystem,726 posix.EIO => error.FileSystem,
...@@ -679,9 +737,7 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:...@@ -679,9 +737,7 @@ pub fn symLinkPosix(allocator: &Allocator, existing_path: []const u8, new_path:
679}737}
680738
681// here we replace the standard +/ with -_ so that it can be used in a file name739// here we replace the standard +/ with -_ so that it can be used in a file name
682const b64_fs_encoder = base64.Base64Encoder.init(740const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);
683 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
684 base64.standard_pad_char);
685741
686pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void {742pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path: []const u8) !void {
687 if (symLink(allocator, existing_path, new_path)) {743 if (symLink(allocator, existing_path, new_path)) {
...@@ -700,7 +756,7 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:...@@ -700,7 +756,7 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
700 tmp_path[dirname.len] = os.path.sep;756 tmp_path[dirname.len] = os.path.sep;
701 while (true) {757 while (true) {
702 try getRandomBytes(rand_buf[0..]);758 try getRandomBytes(rand_buf[0..]);
703 b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);759 b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf);
704760
705 if (symLink(allocator, existing_path, tmp_path)) {761 if (symLink(allocator, existing_path, tmp_path)) {
706 return rename(allocator, tmp_path, new_path);762 return rename(allocator, tmp_path, new_path);
...@@ -709,7 +765,6 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:...@@ -709,7 +765,6 @@ pub fn atomicSymLink(allocator: &Allocator, existing_path: []const u8, new_path:
709 else => return err, // TODO zig should know this set does not include PathAlreadyExists765 else => return err, // TODO zig should know this set does not include PathAlreadyExists
710 }766 }
711 }767 }
712
713}768}
714769
715pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void {770pub fn deleteFile(allocator: &Allocator, file_path: []const u8) !void {
...@@ -732,7 +787,8 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void {...@@ -732,7 +787,8 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) !void {
732 return switch (err) {787 return switch (err) {
733 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,788 windows.ERROR.FILE_NOT_FOUND => error.FileNotFound,
734 windows.ERROR.ACCESS_DENIED => error.AccessDenied,789 windows.ERROR.ACCESS_DENIED => error.AccessDenied,
735 windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong,790 windows.ERROR.FILENAME_EXCED_RANGE,
791 windows.ERROR.INVALID_PARAMETER => error.NameTooLong,
736 else => unexpectedErrorWindows(err),792 else => unexpectedErrorWindows(err),
737 };793 };
738 }794 }
...@@ -748,9 +804,11 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void {...@@ -748,9 +804,11 @@ pub fn deleteFilePosix(allocator: &Allocator, file_path: []const u8) !void {
748 const err = posix.getErrno(posix.unlink(buf.ptr));804 const err = posix.getErrno(posix.unlink(buf.ptr));
749 if (err > 0) {805 if (err > 0) {
750 return switch (err) {806 return switch (err) {
751 posix.EACCES, posix.EPERM => error.AccessDenied,807 posix.EACCES,
808 posix.EPERM => error.AccessDenied,
752 posix.EBUSY => error.FileBusy,809 posix.EBUSY => error.FileBusy,
753 posix.EFAULT, posix.EINVAL => unreachable,810 posix.EFAULT,
811 posix.EINVAL => unreachable,
754 posix.EIO => error.FileSystem,812 posix.EIO => error.FileSystem,
755 posix.EISDIR => error.IsDir,813 posix.EISDIR => error.IsDir,
756 posix.ELOOP => error.SymLinkLoop,814 posix.ELOOP => error.SymLinkLoop,
...@@ -828,7 +886,7 @@ pub const AtomicFile = struct {...@@ -828,7 +886,7 @@ pub const AtomicFile = struct {
828886
829 while (true) {887 while (true) {
830 try getRandomBytes(rand_buf[0..]);888 try getRandomBytes(rand_buf[0..]);
831 b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf);889 b64_fs_encoder.encode(tmp_path[dirname.len + 1..], rand_buf);
832890
833 const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) {891 const file = os.File.openWriteNoClobber(allocator, tmp_path, mode) catch |err| switch (err) {
834 error.PathAlreadyExists => continue,892 error.PathAlreadyExists => continue,
...@@ -879,7 +937,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)...@@ -879,7 +937,7 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
879 new_buf[new_path.len] = 0;937 new_buf[new_path.len] = 0;
880938
881 if (is_windows) {939 if (is_windows) {
882 const flags = windows.MOVEFILE_REPLACE_EXISTING|windows.MOVEFILE_WRITE_THROUGH;940 const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
883 if (windows.MoveFileExA(old_buf.ptr, new_buf.ptr, flags) == 0) {941 if (windows.MoveFileExA(old_buf.ptr, new_buf.ptr, flags) == 0) {
884 const err = windows.GetLastError();942 const err = windows.GetLastError();
885 return switch (err) {943 return switch (err) {
...@@ -890,10 +948,12 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)...@@ -890,10 +948,12 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
890 const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr));948 const err = posix.getErrno(posix.rename(old_buf.ptr, new_buf.ptr));
891 if (err > 0) {949 if (err > 0) {
892 return switch (err) {950 return switch (err) {
893 posix.EACCES, posix.EPERM => error.AccessDenied,951 posix.EACCES,
952 posix.EPERM => error.AccessDenied,
894 posix.EBUSY => error.FileBusy,953 posix.EBUSY => error.FileBusy,
895 posix.EDQUOT => error.DiskQuota,954 posix.EDQUOT => error.DiskQuota,
896 posix.EFAULT, posix.EINVAL => unreachable,955 posix.EFAULT,
956 posix.EINVAL => unreachable,
897 posix.EISDIR => error.IsDir,957 posix.EISDIR => error.IsDir,
898 posix.ELOOP => error.SymLinkLoop,958 posix.ELOOP => error.SymLinkLoop,
899 posix.EMLINK => error.LinkQuotaExceeded,959 posix.EMLINK => error.LinkQuotaExceeded,
...@@ -902,7 +962,8 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)...@@ -902,7 +962,8 @@ pub fn rename(allocator: &Allocator, old_path: []const u8, new_path: []const u8)
902 posix.ENOTDIR => error.NotDir,962 posix.ENOTDIR => error.NotDir,
903 posix.ENOMEM => error.SystemResources,963 posix.ENOMEM => error.SystemResources,
904 posix.ENOSPC => error.NoSpaceLeft,964 posix.ENOSPC => error.NoSpaceLeft,
905 posix.EEXIST, posix.ENOTEMPTY => error.PathAlreadyExists,965 posix.EEXIST,
966 posix.ENOTEMPTY => error.PathAlreadyExists,
906 posix.EROFS => error.ReadOnlyFileSystem,967 posix.EROFS => error.ReadOnlyFileSystem,
907 posix.EXDEV => error.RenameAcrossMountPoints,968 posix.EXDEV => error.RenameAcrossMountPoints,
908 else => unexpectedErrorPosix(err),969 else => unexpectedErrorPosix(err),
...@@ -940,7 +1001,8 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void {...@@ -940,7 +1001,8 @@ pub fn makeDirPosix(allocator: &Allocator, dir_path: []const u8) !void {
940 const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755));1001 const err = posix.getErrno(posix.mkdir(path_buf.ptr, 0o755));
941 if (err > 0) {1002 if (err > 0) {
942 return switch (err) {1003 return switch (err) {
943 posix.EACCES, posix.EPERM => error.AccessDenied,1004 posix.EACCES,
1005 posix.EPERM => error.AccessDenied,
944 posix.EDQUOT => error.DiskQuota,1006 posix.EDQUOT => error.DiskQuota,
945 posix.EEXIST => error.PathAlreadyExists,1007 posix.EEXIST => error.PathAlreadyExists,
946 posix.EFAULT => unreachable,1008 posix.EFAULT => unreachable,
...@@ -970,27 +1032,23 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) !void {...@@ -970,27 +1032,23 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) !void {
970 // TODO stat the file and return an error if it's not a directory1032 // TODO stat the file and return an error if it's not a directory
971 // this is important because otherwise a dangling symlink1033 // this is important because otherwise a dangling symlink
972 // could cause an infinite loop1034 // could cause an infinite loop
973 if (end_index == resolved_path.len)1035 if (end_index == resolved_path.len) return;
974 return;
975 } else if (err == error.FileNotFound) {1036 } else if (err == error.FileNotFound) {
976 // march end_index backward until next path component1037 // march end_index backward until next path component
977 while (true) {1038 while (true) {
978 end_index -= 1;1039 end_index -= 1;
979 if (os.path.isSep(resolved_path[end_index]))1040 if (os.path.isSep(resolved_path[end_index])) break;
980 break;
981 }1041 }
982 continue;1042 continue;
983 } else {1043 } else {
984 return err;1044 return err;
985 }1045 }
986 };1046 };
987 if (end_index == resolved_path.len)1047 if (end_index == resolved_path.len) return;
988 return;
989 // march end_index forward until next path component1048 // march end_index forward until next path component
990 while (true) {1049 while (true) {
991 end_index += 1;1050 end_index += 1;
992 if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index]))1051 if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break;
993 break;
994 }1052 }
995 }1053 }
996}1054}
...@@ -1007,15 +1065,18 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {...@@ -1007,15 +1065,18 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {
1007 const err = posix.getErrno(posix.rmdir(path_buf.ptr));1065 const err = posix.getErrno(posix.rmdir(path_buf.ptr));
1008 if (err > 0) {1066 if (err > 0) {
1009 return switch (err) {1067 return switch (err) {
1010 posix.EACCES, posix.EPERM => error.AccessDenied,1068 posix.EACCES,
1069 posix.EPERM => error.AccessDenied,
1011 posix.EBUSY => error.FileBusy,1070 posix.EBUSY => error.FileBusy,
1012 posix.EFAULT, posix.EINVAL => unreachable,1071 posix.EFAULT,
1072 posix.EINVAL => unreachable,
1013 posix.ELOOP => error.SymLinkLoop,1073 posix.ELOOP => error.SymLinkLoop,
1014 posix.ENAMETOOLONG => error.NameTooLong,1074 posix.ENAMETOOLONG => error.NameTooLong,
1015 posix.ENOENT => error.FileNotFound,1075 posix.ENOENT => error.FileNotFound,
1016 posix.ENOMEM => error.SystemResources,1076 posix.ENOMEM => error.SystemResources,
1017 posix.ENOTDIR => error.NotDir,1077 posix.ENOTDIR => error.NotDir,
1018 posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,1078 posix.EEXIST,
1079 posix.ENOTEMPTY => error.DirNotEmpty,
1019 posix.EROFS => error.ReadOnlyFileSystem,1080 posix.EROFS => error.ReadOnlyFileSystem,
1020 else => unexpectedErrorPosix(err),1081 else => unexpectedErrorPosix(err),
1021 };1082 };
...@@ -1025,7 +1086,7 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {...@@ -1025,7 +1086,7 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) !void {
1025/// Whether ::full_path describes a symlink, file, or directory, this function1086/// Whether ::full_path describes a symlink, file, or directory, this function
1026/// removes it. If it cannot be removed because it is a non-empty directory,1087/// removes it. If it cannot be removed because it is a non-empty directory,
1027/// this function recursively removes its entries and then tries again.1088/// this function recursively removes its entries and then tries again.
1028// TODO non-recursive implementation1089/// TODO non-recursive implementation
1029const DeleteTreeError = error {1090const DeleteTreeError = error {
1030 OutOfMemory,1091 OutOfMemory,
1031 AccessDenied,1092 AccessDenied,
...@@ -1067,8 +1128,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!...@@ -1067,8 +1128,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
1067 error.NotDir,1128 error.NotDir,
1068 error.FileSystem,1129 error.FileSystem,
1069 error.FileBusy,1130 error.FileBusy,
1070 error.Unexpected1131 error.Unexpected => return err,
1071 => return err,
1072 }1132 }
1073 {1133 {
1074 var dir = Dir.open(allocator, full_path) catch |err| switch (err) {1134 var dir = Dir.open(allocator, full_path) catch |err| switch (err) {
...@@ -1092,8 +1152,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!...@@ -1092,8 +1152,7 @@ pub fn deleteTree(allocator: &Allocator, full_path: []const u8) DeleteTreeError!
1092 error.SystemResources,1152 error.SystemResources,
1093 error.NoSpaceLeft,1153 error.NoSpaceLeft,
1094 error.PathAlreadyExists,1154 error.PathAlreadyExists,
1095 error.Unexpected1155 error.Unexpected => return err,
1096 => return err,
1097 };1156 };
1098 defer dir.close();1157 defer dir.close();
10991158
...@@ -1123,7 +1182,8 @@ pub const Dir = struct {...@@ -1123,7 +1182,8 @@ pub const Dir = struct {
1123 end_index: usize,1182 end_index: usize,
11241183
1125 const darwin_seek_t = switch (builtin.os) {1184 const darwin_seek_t = switch (builtin.os) {
1126 Os.macosx, Os.ios => i64,1185 Os.macosx,
1186 Os.ios => i64,
1127 else => void,1187 else => void,
1128 };1188 };
11291189
...@@ -1147,12 +1207,14 @@ pub const Dir = struct {...@@ -1147,12 +1207,14 @@ pub const Dir = struct {
1147 pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir {1207 pub fn open(allocator: &Allocator, dir_path: []const u8) !Dir {
1148 const fd = switch (builtin.os) {1208 const fd = switch (builtin.os) {
1149 Os.windows => @compileError("TODO support Dir.open for windows"),1209 Os.windows => @compileError("TODO support Dir.open for windows"),
1150 Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),1210 Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0),
1151 Os.macosx, Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY|posix.O_NONBLOCK|posix.O_DIRECTORY|posix.O_CLOEXEC, 0),1211 Os.macosx,
1212 Os.ios => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, 0),
1152 else => @compileError("Dir.open is not supported for this platform"),1213 else => @compileError("Dir.open is not supported for this platform"),
1153 };1214 };
1154 const darwin_seek_init = switch (builtin.os) {1215 const darwin_seek_init = switch (builtin.os) {
1155 Os.macosx, Os.ios => 0,1216 Os.macosx,
1217 Os.ios => 0,
1156 else => {},1218 else => {},
1157 };1219 };
1158 return Dir {1220 return Dir {
...@@ -1175,7 +1237,8 @@ pub const Dir = struct {...@@ -1175,7 +1237,8 @@ pub const Dir = struct {
1175 pub fn next(self: &Dir) !?Entry {1237 pub fn next(self: &Dir) !?Entry {
1176 switch (builtin.os) {1238 switch (builtin.os) {
1177 Os.linux => return self.nextLinux(),1239 Os.linux => return self.nextLinux(),
1178 Os.macosx, Os.ios => return self.nextDarwin(),1240 Os.macosx,
1241 Os.ios => return self.nextDarwin(),
1179 Os.windows => return self.nextWindows(),1242 Os.windows => return self.nextWindows(),
1180 else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)),1243 else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)),
1181 }1244 }
...@@ -1189,12 +1252,13 @@ pub const Dir = struct {...@@ -1189,12 +1252,13 @@ pub const Dir = struct {
1189 }1252 }
11901253
1191 while (true) {1254 while (true) {
1192 const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len,1255 const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, &self.darwin_seek);
1193 &self.darwin_seek);
1194 const err = posix.getErrno(result);1256 const err = posix.getErrno(result);
1195 if (err > 0) {1257 if (err > 0) {
1196 switch (err) {1258 switch (err) {
1197 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,1259 posix.EBADF,
1260 posix.EFAULT,
1261 posix.ENOTDIR => unreachable,
1198 posix.EINVAL => {1262 posix.EINVAL => {
1199 self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);1263 self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
1200 continue;1264 continue;
...@@ -1202,14 +1266,13 @@ pub const Dir = struct {...@@ -1202,14 +1266,13 @@ pub const Dir = struct {
1202 else => return unexpectedErrorPosix(err),1266 else => return unexpectedErrorPosix(err),
1203 }1267 }
1204 }1268 }
1205 if (result == 0)1269 if (result == 0) return null;
1206 return null;
1207 self.index = 0;1270 self.index = 0;
1208 self.end_index = result;1271 self.end_index = result;
1209 break;1272 break;
1210 }1273 }
1211 }1274 }
1212 const darwin_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);1275 const darwin_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]);
1213 const next_index = self.index + darwin_entry.d_reclen;1276 const next_index = self.index + darwin_entry.d_reclen;
1214 self.index = next_index;1277 self.index = next_index;
12151278
...@@ -1254,7 +1317,9 @@ pub const Dir = struct {...@@ -1254,7 +1317,9 @@ pub const Dir = struct {
1254 const err = posix.getErrno(result);1317 const err = posix.getErrno(result);
1255 if (err > 0) {1318 if (err > 0) {
1256 switch (err) {1319 switch (err) {
1257 posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,1320 posix.EBADF,
1321 posix.EFAULT,
1322 posix.ENOTDIR => unreachable,
1258 posix.EINVAL => {1323 posix.EINVAL => {
1259 self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);1324 self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2);
1260 continue;1325 continue;
...@@ -1262,14 +1327,13 @@ pub const Dir = struct {...@@ -1262,14 +1327,13 @@ pub const Dir = struct {
1262 else => return unexpectedErrorPosix(err),1327 else => return unexpectedErrorPosix(err),
1263 }1328 }
1264 }1329 }
1265 if (result == 0)1330 if (result == 0) return null;
1266 return null;
1267 self.index = 0;1331 self.index = 0;
1268 self.end_index = result;1332 self.end_index = result;
1269 break;1333 break;
1270 }1334 }
1271 }1335 }
1272 const linux_entry = @ptrCast(& align(1) posix.dirent, &self.buf[self.index]);1336 const linux_entry = @ptrCast(&align(1) posix.dirent, &self.buf[self.index]);
1273 const next_index = self.index + linux_entry.d_reclen;1337 const next_index = self.index + linux_entry.d_reclen;
1274 self.index = next_index;1338 self.index = next_index;
12751339
...@@ -1338,7 +1402,8 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {...@@ -1338,7 +1402,8 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {
1338 if (err > 0) {1402 if (err > 0) {
1339 return switch (err) {1403 return switch (err) {
1340 posix.EACCES => error.AccessDenied,1404 posix.EACCES => error.AccessDenied,
1341 posix.EFAULT, posix.EINVAL => unreachable,1405 posix.EFAULT,
1406 posix.EINVAL => unreachable,
1342 posix.EIO => error.FileSystem,1407 posix.EIO => error.FileSystem,
1343 posix.ELOOP => error.SymLinkLoop,1408 posix.ELOOP => error.SymLinkLoop,
1344 posix.ENAMETOOLONG => error.NameTooLong,1409 posix.ENAMETOOLONG => error.NameTooLong,
...@@ -1356,50 +1421,6 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {...@@ -1356,50 +1421,6 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {
1356 }1421 }
1357}1422}
13581423
1359pub fn sleep(seconds: usize, nanoseconds: usize) void {
1360 switch(builtin.os) {
1361 Os.linux, Os.macosx, Os.ios => {
1362 posixSleep(u63(seconds), u63(nanoseconds));
1363 },
1364 Os.windows => {
1365 const milliseconds = seconds * 1000 + nanoseconds / 1000000;
1366 windows.Sleep(windows.DWORD(milliseconds));
1367 },
1368 else => @compileError("Unsupported OS"),
1369 }
1370}
1371
1372const u63 = @IntType(false, 63);
1373pub fn posixSleep(seconds: u63, nanoseconds: u63) void {
1374 var req = posix.timespec {
1375 .tv_sec = seconds,
1376 .tv_nsec = nanoseconds,
1377 };
1378 var rem: posix.timespec = undefined;
1379 while (true) {
1380 const ret_val = posix.nanosleep(&req, &rem);
1381 const err = posix.getErrno(ret_val);
1382 if (err == 0) return;
1383 switch (err) {
1384 posix.EFAULT => unreachable,
1385 posix.EINVAL => {
1386 // Sometimes Darwin returns EINVAL for no reason.
1387 // We treat it as a spurious wakeup.
1388 return;
1389 },
1390 posix.EINTR => {
1391 req = rem;
1392 continue;
1393 },
1394 else => return,
1395 }
1396 }
1397}
1398
1399test "os.sleep" {
1400 sleep(0, 1);
1401}
1402
1403pub fn posix_setuid(uid: u32) !void {1424pub fn posix_setuid(uid: u32) !void {
1404 const err = posix.getErrno(posix.setuid(uid));1425 const err = posix.getErrno(posix.setuid(uid));
1405 if (err == 0) return;1426 if (err == 0) return;
...@@ -1475,8 +1496,7 @@ pub const ArgIteratorPosix = struct {...@@ -1475,8 +1496,7 @@ pub const ArgIteratorPosix = struct {
1475 }1496 }
14761497
1477 pub fn next(self: &ArgIteratorPosix) ?[]const u8 {1498 pub fn next(self: &ArgIteratorPosix) ?[]const u8 {
1478 if (self.index == self.count)1499 if (self.index == self.count) return null;
1479 return null;
14801500
1481 const s = raw[self.index];1501 const s = raw[self.index];
1482 self.index += 1;1502 self.index += 1;
...@@ -1484,8 +1504,7 @@ pub const ArgIteratorPosix = struct {...@@ -1484,8 +1504,7 @@ pub const ArgIteratorPosix = struct {
1484 }1504 }
14851505
1486 pub fn skip(self: &ArgIteratorPosix) bool {1506 pub fn skip(self: &ArgIteratorPosix) bool {
1487 if (self.index == self.count)1507 if (self.index == self.count) return false;
1488 return false;
14891508
1490 self.index += 1;1509 self.index += 1;
1491 return true;1510 return true;
...@@ -1503,7 +1522,9 @@ pub const ArgIteratorWindows = struct {...@@ -1503,7 +1522,9 @@ pub const ArgIteratorWindows = struct {
1503 quote_count: usize,1522 quote_count: usize,
1504 seen_quote_count: usize,1523 seen_quote_count: usize,
15051524
1506 pub const NextError = error{OutOfMemory};1525 pub const NextError = error {
1526 OutOfMemory,
1527 };
15071528
1508 pub fn init() ArgIteratorWindows {1529 pub fn init() ArgIteratorWindows {
1509 return initWithCmdLine(windows.GetCommandLineA());1530 return initWithCmdLine(windows.GetCommandLineA());
...@@ -1526,7 +1547,8 @@ pub const ArgIteratorWindows = struct {...@@ -1526,7 +1547,8 @@ pub const ArgIteratorWindows = struct {
1526 const byte = self.cmd_line[self.index];1547 const byte = self.cmd_line[self.index];
1527 switch (byte) {1548 switch (byte) {
1528 0 => return null,1549 0 => return null,
1529 ' ', '\t' => continue,1550 ' ',
1551 '\t' => continue,
1530 else => break,1552 else => break,
1531 }1553 }
1532 }1554 }
...@@ -1540,7 +1562,8 @@ pub const ArgIteratorWindows = struct {...@@ -1540,7 +1562,8 @@ pub const ArgIteratorWindows = struct {
1540 const byte = self.cmd_line[self.index];1562 const byte = self.cmd_line[self.index];
1541 switch (byte) {1563 switch (byte) {
1542 0 => return false,1564 0 => return false,
1543 ' ', '\t' => continue,1565 ' ',
1566 '\t' => continue,
1544 else => break,1567 else => break,
1545 }1568 }
1546 }1569 }
...@@ -1559,7 +1582,8 @@ pub const ArgIteratorWindows = struct {...@@ -1559,7 +1582,8 @@ pub const ArgIteratorWindows = struct {
1559 '\\' => {1582 '\\' => {
1560 backslash_count += 1;1583 backslash_count += 1;
1561 },1584 },
1562 ' ', '\t' => {1585 ' ',
1586 '\t' => {
1563 if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) {1587 if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) {
1564 return true;1588 return true;
1565 }1589 }
...@@ -1599,7 +1623,8 @@ pub const ArgIteratorWindows = struct {...@@ -1599,7 +1623,8 @@ pub const ArgIteratorWindows = struct {
1599 '\\' => {1623 '\\' => {
1600 backslash_count += 1;1624 backslash_count += 1;
1601 },1625 },
1602 ' ', '\t' => {1626 ' ',
1627 '\t' => {
1603 try self.emitBackslashes(&buf, backslash_count);1628 try self.emitBackslashes(&buf, backslash_count);
1604 backslash_count = 0;1629 backslash_count = 0;
1605 if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) {1630 if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) {
...@@ -1643,7 +1668,6 @@ pub const ArgIteratorWindows = struct {...@@ -1643,7 +1668,6 @@ pub const ArgIteratorWindows = struct {
1643 }1668 }
1644 }1669 }
1645 }1670 }
1646
1647};1671};
16481672
1649pub const ArgIterator = struct {1673pub const ArgIterator = struct {
...@@ -1658,7 +1682,7 @@ pub const ArgIterator = struct {...@@ -1658,7 +1682,7 @@ pub const ArgIterator = struct {
1658 }1682 }
16591683
1660 pub const NextError = ArgIteratorWindows.NextError;1684 pub const NextError = ArgIteratorWindows.NextError;
1661 1685
1662 /// You must free the returned memory when done.1686 /// You must free the returned memory when done.
1663 pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) {1687 pub fn next(self: &ArgIterator, allocator: &Allocator) ?(NextError![]u8) {
1664 if (builtin.os == Os.windows) {1688 if (builtin.os == Os.windows) {
...@@ -1733,15 +1757,47 @@ pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) void {...@@ -1733,15 +1757,47 @@ pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) void {
1733}1757}
17341758
1735test "windows arg parsing" {1759test "windows arg parsing" {
1736 testWindowsCmdLine(c"a b\tc d", [][]const u8{"a", "b", "c", "d"});1760 testWindowsCmdLine(c"a b\tc d", [][]const u8 {
1737 testWindowsCmdLine(c"\"abc\" d e", [][]const u8{"abc", "d", "e"});1761 "a",
1738 testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{"a\\\\\\b", "de fg", "h"});1762 "b",
1739 testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{"a\\\"b", "c", "d"});1763 "c",
1740 testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{"a\\\\b c", "d", "e"});1764 "d",
1741 testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{"a", "b", "c", "\"d", "f"});1765 });
17421766 testWindowsCmdLine(c"\"abc\" d e", [][]const u8 {
1743 testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"",1767 "abc",
1744 [][]const u8{".\\..\\zig-cache\\build", "bin\\zig.exe", ".\\..", ".\\..\\zig-cache", "--help"});1768 "d",
1769 "e",
1770 });
1771 testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8 {
1772 "a\\\\\\b",
1773 "de fg",
1774 "h",
1775 });
1776 testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8 {
1777 "a\\\"b",
1778 "c",
1779 "d",
1780 });
1781 testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8 {
1782 "a\\\\b c",
1783 "d",
1784 "e",
1785 });
1786 testWindowsCmdLine(c"a b\tc \"d f", [][]const u8 {
1787 "a",
1788 "b",
1789 "c",
1790 "\"d",
1791 "f",
1792 });
1793
1794 testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8 {
1795 ".\\..\\zig-cache\\build",
1796 "bin\\zig.exe",
1797 ".\\..",
1798 ".\\..\\zig-cache",
1799 "--help",
1800 });
1745}1801}
17461802
1747fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const u8) void {1803fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const u8) void {
...@@ -1753,27 +1809,16 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const...@@ -1753,27 +1809,16 @@ fn testWindowsCmdLine(input_cmd_line: &const u8, expected_args: []const []const
1753 assert(it.next(debug.global_allocator) == null);1809 assert(it.next(debug.global_allocator) == null);
1754}1810}
17551811
1756test "std.os" {
1757 _ = @import("child_process.zig");
1758 _ = @import("darwin_errno.zig");
1759 _ = @import("darwin.zig");
1760 _ = @import("get_user_id.zig");
1761 _ = @import("linux/errno.zig");
1762 //_ = @import("linux_i386.zig");
1763 _ = @import("linux/x86_64.zig");
1764 _ = @import("linux/index.zig");
1765 _ = @import("path.zig");
1766 _ = @import("windows/index.zig");
1767 _ = @import("test.zig");
1768}
1769
1770
1771// TODO make this a build variable that you can set1812// TODO make this a build variable that you can set
1772const unexpected_error_tracing = false;1813const unexpected_error_tracing = false;
1814const UnexpectedError = error {
1815 /// The Operating System returned an undocumented error code.
1816 Unexpected,
1817};
17731818
1774/// Call this when you made a syscall or something that sets errno1819/// Call this when you made a syscall or something that sets errno
1775/// and you get an unexpected error.1820/// and you get an unexpected error.
1776pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) {1821pub fn unexpectedErrorPosix(errno: usize) UnexpectedError {
1777 if (unexpected_error_tracing) {1822 if (unexpected_error_tracing) {
1778 debug.warn("unexpected errno: {}\n", errno);1823 debug.warn("unexpected errno: {}\n", errno);
1779 debug.dumpCurrentStackTrace(null);1824 debug.dumpCurrentStackTrace(null);
...@@ -1783,7 +1828,7 @@ pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) {...@@ -1783,7 +1828,7 @@ pub fn unexpectedErrorPosix(errno: usize) (error{Unexpected}) {
17831828
1784/// Call this when you made a windows DLL call or something that does SetLastError1829/// Call this when you made a windows DLL call or something that does SetLastError
1785/// and you get an unexpected error.1830/// and you get an unexpected error.
1786pub fn unexpectedErrorWindows(err: windows.DWORD) (error{Unexpected}) {1831pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError {
1787 if (unexpected_error_tracing) {1832 if (unexpected_error_tracing) {
1788 debug.warn("unexpected GetLastError(): {}\n", err);1833 debug.warn("unexpected GetLastError(): {}\n", err);
1789 debug.dumpCurrentStackTrace(null);1834 debug.dumpCurrentStackTrace(null);
...@@ -1799,7 +1844,8 @@ pub fn openSelfExe() !os.File {...@@ -1799,7 +1844,8 @@ pub fn openSelfExe() !os.File {
1799 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);1844 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1800 return os.File.openRead(&fixed_allocator.allocator, proc_file_path);1845 return os.File.openRead(&fixed_allocator.allocator, proc_file_path);
1801 },1846 },
1802 Os.macosx, Os.ios => {1847 Os.macosx,
1848 Os.ios => {
1803 var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;1849 var fixed_buffer_mem: [darwin.PATH_MAX * 2]u8 = undefined;
1804 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);1850 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1805 const self_exe_path = try selfExePath(&fixed_allocator.allocator);1851 const self_exe_path = try selfExePath(&fixed_allocator.allocator);
...@@ -1811,8 +1857,10 @@ pub fn openSelfExe() !os.File {...@@ -1811,8 +1857,10 @@ pub fn openSelfExe() !os.File {
18111857
1812test "openSelfExe" {1858test "openSelfExe" {
1813 switch (builtin.os) {1859 switch (builtin.os) {
1814 Os.linux, Os.macosx, Os.ios => (try openSelfExe()).close(),1860 Os.linux,
1815 else => return, // Unsupported OS.1861 Os.macosx,
1862 Os.ios => (try openSelfExe()).close(),
1863 else => return, // Unsupported OS.
1816 }1864 }
1817}1865}
18181866
...@@ -1849,7 +1897,8 @@ pub fn selfExePath(allocator: &mem.Allocator) ![]u8 {...@@ -1849,7 +1897,8 @@ pub fn selfExePath(allocator: &mem.Allocator) ![]u8 {
1849 try out_path.resize(new_len);1897 try out_path.resize(new_len);
1850 }1898 }
1851 },1899 },
1852 Os.macosx, Os.ios => {1900 Os.macosx,
1901 Os.ios => {
1853 var u32_len: u32 = 0;1902 var u32_len: u32 = 0;
1854 const ret1 = c._NSGetExecutablePath(undefined, &u32_len);1903 const ret1 = c._NSGetExecutablePath(undefined, &u32_len);
1855 assert(ret1 != 0);1904 assert(ret1 != 0);
...@@ -1877,7 +1926,9 @@ pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 {...@@ -1877,7 +1926,9 @@ pub fn selfExeDirPath(allocator: &mem.Allocator) ![]u8 {
1877 const dir = path.dirname(full_exe_path);1926 const dir = path.dirname(full_exe_path);
1878 return allocator.shrink(u8, full_exe_path, dir.len);1927 return allocator.shrink(u8, full_exe_path, dir.len);
1879 },1928 },
1880 Os.windows, Os.macosx, Os.ios => {1929 Os.windows,
1930 Os.macosx,
1931 Os.ios => {
1881 const self_exe_path = try selfExePath(allocator);1932 const self_exe_path = try selfExePath(allocator);
1882 errdefer allocator.free(self_exe_path);1933 errdefer allocator.free(self_exe_path);
1883 const dirname = os.path.dirname(self_exe_path);1934 const dirname = os.path.dirname(self_exe_path);
...@@ -1898,3 +1949,712 @@ pub fn isTty(handle: FileHandle) bool {...@@ -1898,3 +1949,712 @@ pub fn isTty(handle: FileHandle) bool {
1898 }1949 }
1899 }1950 }
1900}1951}
1952
1953pub const PosixSocketError = error {
1954 /// Permission to create a socket of the specified type and/or
1955 /// pro‐tocol is denied.
1956 PermissionDenied,
1957
1958 /// The implementation does not support the specified address family.
1959 AddressFamilyNotSupported,
1960
1961 /// Unknown protocol, or protocol family not available.
1962 ProtocolFamilyNotAvailable,
1963
1964 /// The per-process limit on the number of open file descriptors has been reached.
1965 ProcessFdQuotaExceeded,
1966
1967 /// The system-wide limit on the total number of open files has been reached.
1968 SystemFdQuotaExceeded,
1969
1970 /// Insufficient memory is available. The socket cannot be created until sufficient
1971 /// resources are freed.
1972 SystemResources,
1973
1974 /// The protocol type or the specified protocol is not supported within this domain.
1975 ProtocolNotSupported,
1976};
1977
1978pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 {
1979 const rc = posix.socket(domain, socket_type, protocol);
1980 const err = posix.getErrno(rc);
1981 switch (err) {
1982 0 => return i32(rc),
1983 posix.EACCES => return PosixSocketError.PermissionDenied,
1984 posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported,
1985 posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable,
1986 posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded,
1987 posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded,
1988 posix.ENOBUFS,
1989 posix.ENOMEM => return PosixSocketError.SystemResources,
1990 posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported,
1991 else => return unexpectedErrorPosix(err),
1992 }
1993}
1994
1995pub const PosixBindError = error {
1996 /// The address is protected, and the user is not the superuser.
1997 /// For UNIX domain sockets: Search permission is denied on a component
1998 /// of the path prefix.
1999 AccessDenied,
2000
2001 /// The given address is already in use, or in the case of Internet domain sockets,
2002 /// The port number was specified as zero in the socket
2003 /// address structure, but, upon attempting to bind to an ephemeral port, it was
2004 /// determined that all port numbers in the ephemeral port range are currently in
2005 /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7).
2006 AddressInUse,
2007
2008 /// sockfd is not a valid file descriptor.
2009 InvalidFileDescriptor,
2010
2011 /// The socket is already bound to an address, or addrlen is wrong, or addr is not
2012 /// a valid address for this socket's domain.
2013 InvalidSocketOrAddress,
2014
2015 /// The file descriptor sockfd does not refer to a socket.
2016 FileDescriptorNotASocket,
2017
2018 /// A nonexistent interface was requested or the requested address was not local.
2019 AddressNotAvailable,
2020
2021 /// addr points outside the user's accessible address space.
2022 PageFault,
2023
2024 /// Too many symbolic links were encountered in resolving addr.
2025 SymLinkLoop,
2026
2027 /// addr is too long.
2028 NameTooLong,
2029
2030 /// A component in the directory prefix of the socket pathname does not exist.
2031 FileNotFound,
2032
2033 /// Insufficient kernel memory was available.
2034 SystemResources,
2035
2036 /// A component of the path prefix is not a directory.
2037 NotDir,
2038
2039 /// The socket inode would reside on a read-only filesystem.
2040 ReadOnlyFileSystem,
2041
2042 Unexpected,
2043};
2044
2045/// addr is `&const T` where T is one of the sockaddr
2046pub fn posixBind(fd: i32, addr: &const posix.sockaddr) PosixBindError!void {
2047 const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr));
2048 const err = posix.getErrno(rc);
2049 switch (err) {
2050 0 => return,
2051 posix.EACCES => return PosixBindError.AccessDenied,
2052 posix.EADDRINUSE => return PosixBindError.AddressInUse,
2053 posix.EBADF => return PosixBindError.InvalidFileDescriptor,
2054 posix.EINVAL => return PosixBindError.InvalidSocketOrAddress,
2055 posix.ENOTSOCK => return PosixBindError.FileDescriptorNotASocket,
2056 posix.EADDRNOTAVAIL => return PosixBindError.AddressNotAvailable,
2057 posix.EFAULT => return PosixBindError.PageFault,
2058 posix.ELOOP => return PosixBindError.SymLinkLoop,
2059 posix.ENAMETOOLONG => return PosixBindError.NameTooLong,
2060 posix.ENOENT => return PosixBindError.FileNotFound,
2061 posix.ENOMEM => return PosixBindError.SystemResources,
2062 posix.ENOTDIR => return PosixBindError.NotDir,
2063 posix.EROFS => return PosixBindError.ReadOnlyFileSystem,
2064 else => return unexpectedErrorPosix(err),
2065 }
2066}
2067
2068const PosixListenError = error {
2069 /// Another socket is already listening on the same port.
2070 /// For Internet domain sockets, the socket referred to by sockfd had not previously
2071 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it
2072 /// was determined that all port numbers in the ephemeral port range are currently in
2073 /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
2074 AddressInUse,
2075
2076 /// The argument sockfd is not a valid file descriptor.
2077 InvalidFileDescriptor,
2078
2079 /// The file descriptor sockfd does not refer to a socket.
2080 FileDescriptorNotASocket,
2081
2082 /// The socket is not of a type that supports the listen() operation.
2083 OperationNotSupported,
2084
2085 Unexpected,
2086};
2087
2088pub fn posixListen(sockfd: i32, backlog: u32) PosixListenError!void {
2089 const rc = posix.listen(sockfd, backlog);
2090 const err = posix.getErrno(rc);
2091 switch (err) {
2092 0 => return,
2093 posix.EADDRINUSE => return PosixListenError.AddressInUse,
2094 posix.EBADF => return PosixListenError.InvalidFileDescriptor,
2095 posix.ENOTSOCK => return PosixListenError.FileDescriptorNotASocket,
2096 posix.EOPNOTSUPP => return PosixListenError.OperationNotSupported,
2097 else => return unexpectedErrorPosix(err),
2098 }
2099}
2100
2101pub const PosixAcceptError = error {
2102 /// The socket is marked nonblocking and no connections are present to be accepted.
2103 WouldBlock,
2104
2105 /// sockfd is not an open file descriptor.
2106 FileDescriptorClosed,
2107
2108 ConnectionAborted,
2109
2110 /// The addr argument is not in a writable part of the user address space.
2111 PageFault,
2112
2113 /// Socket is not listening for connections, or addrlen is invalid (e.g., is negative),
2114 /// or invalid value in flags.
2115 InvalidSyscall,
2116
2117 /// The per-process limit on the number of open file descriptors has been reached.
2118 ProcessFdQuotaExceeded,
2119
2120 /// The system-wide limit on the total number of open files has been reached.
2121 SystemFdQuotaExceeded,
2122
2123 /// Not enough free memory. This often means that the memory allocation is limited
2124 /// by the socket buffer limits, not by the system memory.
2125 SystemResources,
2126
2127 /// The file descriptor sockfd does not refer to a socket.
2128 FileDescriptorNotASocket,
2129
2130 /// The referenced socket is not of type SOCK_STREAM.
2131 OperationNotSupported,
2132
2133 ProtocolFailure,
2134
2135 /// Firewall rules forbid connection.
2136 BlockedByFirewall,
2137
2138 Unexpected,
2139};
2140
2141pub fn posixAccept(fd: i32, addr: &posix.sockaddr, flags: u32) PosixAcceptError!i32 {
2142 while (true) {
2143 var sockaddr_size = u32(@sizeOf(posix.sockaddr));
2144 const rc = posix.accept4(fd, addr, &sockaddr_size, flags);
2145 const err = posix.getErrno(rc);
2146 switch (err) {
2147 0 => return i32(rc),
2148 posix.EINTR => continue,
2149 else => return unexpectedErrorPosix(err),
2150
2151 posix.EAGAIN => return PosixAcceptError.WouldBlock,
2152 posix.EBADF => return PosixAcceptError.FileDescriptorClosed,
2153 posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted,
2154 posix.EFAULT => return PosixAcceptError.PageFault,
2155 posix.EINVAL => return PosixAcceptError.InvalidSyscall,
2156 posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded,
2157 posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded,
2158 posix.ENOBUFS,
2159 posix.ENOMEM => return PosixAcceptError.SystemResources,
2160 posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket,
2161 posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported,
2162 posix.EPROTO => return PosixAcceptError.ProtocolFailure,
2163 posix.EPERM => return PosixAcceptError.BlockedByFirewall,
2164 }
2165 }
2166}
2167
2168pub const LinuxEpollCreateError = error {
2169 /// Invalid value specified in flags.
2170 InvalidSyscall,
2171
2172 /// The per-user limit on the number of epoll instances imposed by
2173 /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further
2174 /// details.
2175 /// Or, The per-process limit on the number of open file descriptors has been reached.
2176 ProcessFdQuotaExceeded,
2177
2178 /// The system-wide limit on the total number of open files has been reached.
2179 SystemFdQuotaExceeded,
2180
2181 /// There was insufficient memory to create the kernel object.
2182 SystemResources,
2183
2184 Unexpected,
2185};
2186
2187pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 {
2188 const rc = posix.epoll_create1(flags);
2189 const err = posix.getErrno(rc);
2190 switch (err) {
2191 0 => return i32(rc),
2192 else => return unexpectedErrorPosix(err),
2193
2194 posix.EINVAL => return LinuxEpollCreateError.InvalidSyscall,
2195 posix.EMFILE => return LinuxEpollCreateError.ProcessFdQuotaExceeded,
2196 posix.ENFILE => return LinuxEpollCreateError.SystemFdQuotaExceeded,
2197 posix.ENOMEM => return LinuxEpollCreateError.SystemResources,
2198 }
2199}
2200
2201pub const LinuxEpollCtlError = error {
2202 /// epfd or fd is not a valid file descriptor.
2203 InvalidFileDescriptor,
2204
2205 /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered
2206 /// with this epoll instance.
2207 FileDescriptorAlreadyPresentInSet,
2208
2209 /// epfd is not an epoll file descriptor, or fd is the same as epfd, or the requested
2210 /// operation op is not supported by this interface, or
2211 /// An invalid event type was specified along with EPOLLEXCLUSIVE in events, or
2212 /// op was EPOLL_CTL_MOD and events included EPOLLEXCLUSIVE, or
2213 /// op was EPOLL_CTL_MOD and the EPOLLEXCLUSIVE flag has previously been applied to
2214 /// this epfd, fd pair, or
2215 /// EPOLLEXCLUSIVE was specified in event and fd refers to an epoll instance.
2216 InvalidSyscall,
2217
2218 /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a
2219 /// circular loop of epoll instances monitoring one another.
2220 OperationCausesCircularLoop,
2221
2222 /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll
2223 /// instance.
2224 FileDescriptorNotRegistered,
2225
2226 /// There was insufficient memory to handle the requested op control operation.
2227 SystemResources,
2228
2229 /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while
2230 /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance.
2231 /// See epoll(7) for further details.
2232 UserResourceLimitReached,
2233
2234 /// The target file fd does not support epoll. This error can occur if fd refers to,
2235 /// for example, a regular file or a directory.
2236 FileDescriptorIncompatibleWithEpoll,
2237
2238 Unexpected,
2239};
2240
2241pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: &linux.epoll_event) LinuxEpollCtlError!void {
2242 const rc = posix.epoll_ctl(epfd, op, fd, event);
2243 const err = posix.getErrno(rc);
2244 switch (err) {
2245 0 => return,
2246 else => return unexpectedErrorPosix(err),
2247
2248 posix.EBADF => return LinuxEpollCtlError.InvalidFileDescriptor,
2249 posix.EEXIST => return LinuxEpollCtlError.FileDescriptorAlreadyPresentInSet,
2250 posix.EINVAL => return LinuxEpollCtlError.InvalidSyscall,
2251 posix.ELOOP => return LinuxEpollCtlError.OperationCausesCircularLoop,
2252 posix.ENOENT => return LinuxEpollCtlError.FileDescriptorNotRegistered,
2253 posix.ENOMEM => return LinuxEpollCtlError.SystemResources,
2254 posix.ENOSPC => return LinuxEpollCtlError.UserResourceLimitReached,
2255 posix.EPERM => return LinuxEpollCtlError.FileDescriptorIncompatibleWithEpoll,
2256 }
2257}
2258
2259pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize {
2260 while (true) {
2261 const rc = posix.epoll_wait(epfd, events.ptr, u32(events.len), timeout);
2262 const err = posix.getErrno(rc);
2263 switch (err) {
2264 0 => return rc,
2265 posix.EINTR => continue,
2266 posix.EBADF => unreachable,
2267 posix.EFAULT => unreachable,
2268 posix.EINVAL => unreachable,
2269 else => unreachable,
2270 }
2271 }
2272}
2273
2274pub const PosixGetSockNameError = error {
2275 /// Insufficient resources were available in the system to perform the operation.
2276 SystemResources,
2277
2278 Unexpected,
2279};
2280
2281pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr {
2282 var addr: posix.sockaddr = undefined;
2283 var addrlen: posix.socklen_t = @sizeOf(posix.sockaddr);
2284 const rc = posix.getsockname(sockfd, &addr, &addrlen);
2285 const err = posix.getErrno(rc);
2286 switch (err) {
2287 0 => return addr,
2288 else => return unexpectedErrorPosix(err),
2289
2290 posix.EBADF => unreachable,
2291 posix.EFAULT => unreachable,
2292 posix.EINVAL => unreachable,
2293 posix.ENOTSOCK => unreachable,
2294 posix.ENOBUFS => return PosixGetSockNameError.SystemResources,
2295 }
2296}
2297
2298pub const PosixConnectError = error {
2299 /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket
2300 /// file, or search permission is denied for one of the directories in the path prefix.
2301 /// or
2302 /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or
2303 /// the connection request failed because of a local firewall rule.
2304 PermissionDenied,
2305
2306 /// Local address is already in use.
2307 AddressInUse,
2308
2309 /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an
2310 /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers
2311 /// in the ephemeral port range are currently in use. See the discussion of
2312 /// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
2313 AddressNotAvailable,
2314
2315 /// The passed address didn't have the correct address family in its sa_family field.
2316 AddressFamilyNotSupported,
2317
2318 /// Insufficient entries in the routing cache.
2319 SystemResources,
2320
2321 /// A connect() on a stream socket found no one listening on the remote address.
2322 ConnectionRefused,
2323
2324 /// Network is unreachable.
2325 NetworkUnreachable,
2326
2327 /// Timeout while attempting connection. The server may be too busy to accept new connections. Note
2328 /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
2329 ConnectionTimedOut,
2330
2331 Unexpected,
2332};
2333
2334pub fn posixConnect(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void {
2335 while (true) {
2336 const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr));
2337 const err = posix.getErrno(rc);
2338 switch (err) {
2339 0 => return,
2340 else => return unexpectedErrorPosix(err),
2341
2342 posix.EACCES => return PosixConnectError.PermissionDenied,
2343 posix.EPERM => return PosixConnectError.PermissionDenied,
2344 posix.EADDRINUSE => return PosixConnectError.AddressInUse,
2345 posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
2346 posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
2347 posix.EAGAIN => return PosixConnectError.SystemResources,
2348 posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
2349 posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
2350 posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
2351 posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
2352 posix.EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately.
2353 posix.EINTR => continue,
2354 posix.EISCONN => unreachable, // The socket is already connected.
2355 posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
2356 posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
2357 posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
2358 posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
2359 }
2360 }
2361}
2362
2363/// Same as posixConnect except it is for blocking socket file descriptors.
2364/// It expects to receive EINPROGRESS.
2365pub fn posixConnectAsync(sockfd: i32, sockaddr: &const posix.sockaddr) PosixConnectError!void {
2366 while (true) {
2367 const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr));
2368 const err = posix.getErrno(rc);
2369 switch (err) {
2370 0,
2371 posix.EINPROGRESS => return,
2372 else => return unexpectedErrorPosix(err),
2373
2374 posix.EACCES => return PosixConnectError.PermissionDenied,
2375 posix.EPERM => return PosixConnectError.PermissionDenied,
2376 posix.EADDRINUSE => return PosixConnectError.AddressInUse,
2377 posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
2378 posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
2379 posix.EAGAIN => return PosixConnectError.SystemResources,
2380 posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
2381 posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
2382 posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
2383 posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
2384 posix.EINTR => continue,
2385 posix.EISCONN => unreachable, // The socket is already connected.
2386 posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
2387 posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
2388 posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
2389 posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
2390 }
2391 }
2392}
2393
2394pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void {
2395 var err_code: i32 = undefined;
2396 var size: u32 = @sizeOf(i32);
2397 const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast(&u8, &err_code), &size);
2398 assert(size == 4);
2399 const err = posix.getErrno(rc);
2400 switch (err) {
2401 0 => switch (err_code) {
2402 0 => return,
2403 else => return unexpectedErrorPosix(err),
2404
2405 posix.EACCES => return PosixConnectError.PermissionDenied,
2406 posix.EPERM => return PosixConnectError.PermissionDenied,
2407 posix.EADDRINUSE => return PosixConnectError.AddressInUse,
2408 posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable,
2409 posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported,
2410 posix.EAGAIN => return PosixConnectError.SystemResources,
2411 posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
2412 posix.EBADF => unreachable, // sockfd is not a valid open file descriptor.
2413 posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused,
2414 posix.EFAULT => unreachable, // The socket structure address is outside the user's address space.
2415 posix.EISCONN => unreachable, // The socket is already connected.
2416 posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable,
2417 posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
2418 posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
2419 posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut,
2420 },
2421 else => return unexpectedErrorPosix(err),
2422 posix.EBADF => unreachable, // The argument sockfd is not a valid file descriptor.
2423 posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
2424 posix.EINVAL => unreachable,
2425 posix.ENOPROTOOPT => unreachable, // The option is unknown at the level indicated.
2426 posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
2427 }
2428}
2429
2430pub const Thread = struct {
2431 data: Data,
2432
2433 pub const use_pthreads = is_posix and builtin.link_libc;
2434 const Data = if (use_pthreads) struct {
2435 handle: c.pthread_t,
2436 stack_addr: usize,
2437 stack_len: usize,
2438 } else switch (builtin.os) {
2439 builtin.Os.linux => struct {
2440 pid: i32,
2441 stack_addr: usize,
2442 stack_len: usize,
2443 },
2444 builtin.Os.windows => struct {
2445 handle: windows.HANDLE,
2446 alloc_start: &c_void,
2447 heap_handle: windows.HANDLE,
2448 },
2449 else => @compileError("Unsupported OS"),
2450 };
2451
2452 pub fn wait(self: &const Thread) void {
2453 if (use_pthreads) {
2454 const err = c.pthread_join(self.data.handle, null);
2455 switch (err) {
2456 0 => {},
2457 posix.EINVAL => unreachable,
2458 posix.ESRCH => unreachable,
2459 posix.EDEADLK => unreachable,
2460 else => unreachable,
2461 }
2462 assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
2463 } else switch (builtin.os) {
2464 builtin.Os.linux => {
2465 while (true) {
2466 const pid_value = @atomicLoad(i32, &self.data.pid, builtin.AtomicOrder.SeqCst);
2467 if (pid_value == 0) break;
2468 const rc = linux.futex_wait(@ptrToInt(&self.data.pid), linux.FUTEX_WAIT, pid_value, null);
2469 switch (linux.getErrno(rc)) {
2470 0 => continue,
2471 posix.EINTR => continue,
2472 posix.EAGAIN => continue,
2473 else => unreachable,
2474 }
2475 }
2476 assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
2477 },
2478 builtin.Os.windows => {
2479 assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
2480 assert(windows.CloseHandle(self.data.handle) != 0);
2481 assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0);
2482 },
2483 else => @compileError("Unsupported OS"),
2484 }
2485 }
2486};
2487
2488pub const SpawnThreadError = error {
2489 /// A system-imposed limit on the number of threads was encountered.
2490 /// There are a number of limits that may trigger this error:
2491 /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)),
2492 /// which limits the number of processes and threads for a real
2493 /// user ID, was reached;
2494 /// * the kernel's system-wide limit on the number of processes and
2495 /// threads, /proc/sys/kernel/threads-max, was reached (see
2496 /// proc(5));
2497 /// * the maximum number of PIDs, /proc/sys/kernel/pid_max, was
2498 /// reached (see proc(5)); or
2499 /// * the PID limit (pids.max) imposed by the cgroup "process num‐
2500 /// ber" (PIDs) controller was reached.
2501 ThreadQuotaExceeded,
2502
2503 /// The kernel cannot allocate sufficient memory to allocate a task structure
2504 /// for the child, or to copy those parts of the caller's context that need to
2505 /// be copied.
2506 SystemResources,
2507
2508 /// Not enough userland memory to spawn the thread.
2509 OutOfMemory,
2510
2511 Unexpected,
2512};
2513
2514/// caller must call wait on the returned thread
2515/// fn startFn(@typeOf(context)) T
2516/// where T is u8, noreturn, void, or !void
2517/// caller must call wait on the returned thread
2518pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!&Thread {
2519 // TODO compile-time call graph analysis to determine stack upper bound
2520 // https://github.com/zig-lang/zig/issues/157
2521 const default_stack_size = 8 * 1024 * 1024;
2522
2523 const Context = @typeOf(context);
2524 comptime assert(@ArgType(@typeOf(startFn), 0) == Context);
2525
2526 if (builtin.os == builtin.Os.windows) {
2527 const WinThread = struct {
2528 const OuterContext = struct {
2529 thread: Thread,
2530 inner: Context,
2531 };
2532 extern fn threadMain(arg: windows.LPVOID) windows.DWORD {
2533 if (@sizeOf(Context) == 0) {
2534 return startFn({});
2535 } else {
2536 return startFn(*@ptrCast(&Context, @alignCast(@alignOf(Context), arg)));
2537 }
2538 }
2539 };
2540
2541 const heap_handle = windows.GetProcessHeap() ?? return SpawnThreadError.OutOfMemory;
2542 const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext);
2543 const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory;
2544 errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0);
2545 const bytes = @ptrCast(&u8, bytes_ptr)[0..byte_count];
2546 const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable;
2547 outer_context.inner = context;
2548 outer_context.thread.data.heap_handle = heap_handle;
2549 outer_context.thread.data.alloc_start = bytes_ptr;
2550
2551 const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(&c_void, &outer_context.inner);
2552 outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) ?? {
2553 const err = windows.GetLastError();
2554 return switch (err) {
2555 else => os.unexpectedErrorWindows(err),
2556 };
2557 };
2558 return &outer_context.thread;
2559 }
2560
2561 const MainFuncs = struct {
2562 extern fn linuxThreadMain(ctx_addr: usize) u8 {
2563 if (@sizeOf(Context) == 0) {
2564 return startFn({});
2565 } else {
2566 return startFn(*@intToPtr(&const Context, ctx_addr));
2567 }
2568 }
2569 extern fn posixThreadMain(ctx: ?&c_void) ?&c_void {
2570 if (@sizeOf(Context) == 0) {
2571 _ = startFn({});
2572 return null;
2573 } else {
2574 _ = startFn(*@ptrCast(&const Context, @alignCast(@alignOf(Context), ctx)));
2575 return null;
2576 }
2577 }
2578 };
2579
2580 const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
2581
2582 const mmap_len = default_stack_size;
2583 const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
2584 if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;
2585 errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);
2586
2587 var stack_end: usize = stack_addr + mmap_len;
2588 var arg: usize = undefined;
2589 if (@sizeOf(Context) != 0) {
2590 stack_end -= @sizeOf(Context);
2591 stack_end -= stack_end % @alignOf(Context);
2592 assert(stack_end >= stack_addr);
2593 const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end));
2594 *context_ptr = context;
2595 arg = stack_end;
2596 }
2597
2598 stack_end -= @sizeOf(Thread);
2599 stack_end -= stack_end % @alignOf(Thread);
2600 assert(stack_end >= stack_addr);
2601 const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end));
2602
2603 thread_ptr.data.stack_addr = stack_addr;
2604 thread_ptr.data.stack_len = mmap_len;
2605
2606 if (builtin.os == builtin.Os.windows) {
2607 // use windows API directly
2608 @compileError("TODO support spawnThread for Windows");
2609 } else if (Thread.use_pthreads) {
2610 // use pthreads
2611 var attr: c.pthread_attr_t = undefined;
2612 if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
2613 defer assert(c.pthread_attr_destroy(&attr) == 0);
2614
2615 // align to page
2616 stack_end -= stack_end % os.page_size;
2617 assert(c.pthread_attr_setstack(&attr, @intToPtr(&c_void, stack_addr), stack_end - stack_addr) == 0);
2618
2619 const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(&c_void, arg));
2620 switch (err) {
2621 0 => return thread_ptr,
2622 posix.EAGAIN => return SpawnThreadError.SystemResources,
2623 posix.EPERM => unreachable,
2624 posix.EINVAL => unreachable,
2625 else => return unexpectedErrorPosix(usize(err)),
2626 }
2627 } else if (builtin.os == builtin.Os.linux) {
2628 // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly
2629 const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
2630 const newtls: usize = 0;
2631 const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.pid, newtls, &thread_ptr.data.pid);
2632 const err = posix.getErrno(rc);
2633 switch (err) {
2634 0 => return thread_ptr,
2635 posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded,
2636 posix.EINVAL => unreachable,
2637 posix.ENOMEM => return SpawnThreadError.SystemResources,
2638 posix.ENOSPC => unreachable,
2639 posix.EPERM => unreachable,
2640 posix.EUSERS => unreachable,
2641 else => return unexpectedErrorPosix(err),
2642 }
2643 } else {
2644 @compileError("Unsupported OS");
2645 }
2646}
2647
2648pub fn posixWait(pid: i32) i32 {
2649 var status: i32 = undefined;
2650 while (true) {
2651 const err = posix.getErrno(posix.waitpid(pid, &status, 0));
2652 switch (err) {
2653 0 => return status,
2654 posix.EINTR => continue,
2655 posix.ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
2656 posix.EINVAL => unreachable, // The options argument was invalid
2657 else => unreachable,
2658 }
2659 }
2660}
std/os/linux/i386.zig deleted-505
...@@ -1,505 +0,0 @@
1const std = @import("../../index.zig");
2const linux = std.os.linux;
3const socklen_t = linux.socklen_t;
4const iovec = linux.iovec;
5
6pub const SYS_restart_syscall = 0;
7pub const SYS_exit = 1;
8pub const SYS_fork = 2;
9pub const SYS_read = 3;
10pub const SYS_write = 4;
11pub const SYS_open = 5;
12pub const SYS_close = 6;
13pub const SYS_waitpid = 7;
14pub const SYS_creat = 8;
15pub const SYS_link = 9;
16pub const SYS_unlink = 10;
17pub const SYS_execve = 11;
18pub const SYS_chdir = 12;
19pub const SYS_time = 13;
20pub const SYS_mknod = 14;
21pub const SYS_chmod = 15;
22pub const SYS_lchown = 16;
23pub const SYS_break = 17;
24pub const SYS_oldstat = 18;
25pub const SYS_lseek = 19;
26pub const SYS_getpid = 20;
27pub const SYS_mount = 21;
28pub const SYS_umount = 22;
29pub const SYS_setuid = 23;
30pub const SYS_getuid = 24;
31pub const SYS_stime = 25;
32pub const SYS_ptrace = 26;
33pub const SYS_alarm = 27;
34pub const SYS_oldfstat = 28;
35pub const SYS_pause = 29;
36pub const SYS_utime = 30;
37pub const SYS_stty = 31;
38pub const SYS_gtty = 32;
39pub const SYS_access = 33;
40pub const SYS_nice = 34;
41pub const SYS_ftime = 35;
42pub const SYS_sync = 36;
43pub const SYS_kill = 37;
44pub const SYS_rename = 38;
45pub const SYS_mkdir = 39;
46pub const SYS_rmdir = 40;
47pub const SYS_dup = 41;
48pub const SYS_pipe = 42;
49pub const SYS_times = 43;
50pub const SYS_prof = 44;
51pub const SYS_brk = 45;
52pub const SYS_setgid = 46;
53pub const SYS_getgid = 47;
54pub const SYS_signal = 48;
55pub const SYS_geteuid = 49;
56pub const SYS_getegid = 50;
57pub const SYS_acct = 51;
58pub const SYS_umount2 = 52;
59pub const SYS_lock = 53;
60pub const SYS_ioctl = 54;
61pub const SYS_fcntl = 55;
62pub const SYS_mpx = 56;
63pub const SYS_setpgid = 57;
64pub const SYS_ulimit = 58;
65pub const SYS_oldolduname = 59;
66pub const SYS_umask = 60;
67pub const SYS_chroot = 61;
68pub const SYS_ustat = 62;
69pub const SYS_dup2 = 63;
70pub const SYS_getppid = 64;
71pub const SYS_getpgrp = 65;
72pub const SYS_setsid = 66;
73pub const SYS_sigaction = 67;
74pub const SYS_sgetmask = 68;
75pub const SYS_ssetmask = 69;
76pub const SYS_setreuid = 70;
77pub const SYS_setregid = 71;
78pub const SYS_sigsuspend = 72;
79pub const SYS_sigpending = 73;
80pub const SYS_sethostname = 74;
81pub const SYS_setrlimit = 75;
82pub const SYS_getrlimit = 76;
83pub const SYS_getrusage = 77;
84pub const SYS_gettimeofday = 78;
85pub const SYS_settimeofday = 79;
86pub const SYS_getgroups = 80;
87pub const SYS_setgroups = 81;
88pub const SYS_select = 82;
89pub const SYS_symlink = 83;
90pub const SYS_oldlstat = 84;
91pub const SYS_readlink = 85;
92pub const SYS_uselib = 86;
93pub const SYS_swapon = 87;
94pub const SYS_reboot = 88;
95pub const SYS_readdir = 89;
96pub const SYS_mmap = 90;
97pub const SYS_munmap = 91;
98pub const SYS_truncate = 92;
99pub const SYS_ftruncate = 93;
100pub const SYS_fchmod = 94;
101pub const SYS_fchown = 95;
102pub const SYS_getpriority = 96;
103pub const SYS_setpriority = 97;
104pub const SYS_profil = 98;
105pub const SYS_statfs = 99;
106pub const SYS_fstatfs = 100;
107pub const SYS_ioperm = 101;
108pub const SYS_socketcall = 102;
109pub const SYS_syslog = 103;
110pub const SYS_setitimer = 104;
111pub const SYS_getitimer = 105;
112pub const SYS_stat = 106;
113pub const SYS_lstat = 107;
114pub const SYS_fstat = 108;
115pub const SYS_olduname = 109;
116pub const SYS_iopl = 110;
117pub const SYS_vhangup = 111;
118pub const SYS_idle = 112;
119pub const SYS_vm86old = 113;
120pub const SYS_wait4 = 114;
121pub const SYS_swapoff = 115;
122pub const SYS_sysinfo = 116;
123pub const SYS_ipc = 117;
124pub const SYS_fsync = 118;
125pub const SYS_sigreturn = 119;
126pub const SYS_clone = 120;
127pub const SYS_setdomainname = 121;
128pub const SYS_uname = 122;
129pub const SYS_modify_ldt = 123;
130pub const SYS_adjtimex = 124;
131pub const SYS_mprotect = 125;
132pub const SYS_sigprocmask = 126;
133pub const SYS_create_module = 127;
134pub const SYS_init_module = 128;
135pub const SYS_delete_module = 129;
136pub const SYS_get_kernel_syms = 130;
137pub const SYS_quotactl = 131;
138pub const SYS_getpgid = 132;
139pub const SYS_fchdir = 133;
140pub const SYS_bdflush = 134;
141pub const SYS_sysfs = 135;
142pub const SYS_personality = 136;
143pub const SYS_afs_syscall = 137;
144pub const SYS_setfsuid = 138;
145pub const SYS_setfsgid = 139;
146pub const SYS__llseek = 140;
147pub const SYS_getdents = 141;
148pub const SYS__newselect = 142;
149pub const SYS_flock = 143;
150pub const SYS_msync = 144;
151pub const SYS_readv = 145;
152pub const SYS_writev = 146;
153pub const SYS_getsid = 147;
154pub const SYS_fdatasync = 148;
155pub const SYS__sysctl = 149;
156pub const SYS_mlock = 150;
157pub const SYS_munlock = 151;
158pub const SYS_mlockall = 152;
159pub const SYS_munlockall = 153;
160pub const SYS_sched_setparam = 154;
161pub const SYS_sched_getparam = 155;
162pub const SYS_sched_setscheduler = 156;
163pub const SYS_sched_getscheduler = 157;
164pub const SYS_sched_yield = 158;
165pub const SYS_sched_get_priority_max = 159;
166pub const SYS_sched_get_priority_min = 160;
167pub const SYS_sched_rr_get_interval = 161;
168pub const SYS_nanosleep = 162;
169pub const SYS_mremap = 163;
170pub const SYS_setresuid = 164;
171pub const SYS_getresuid = 165;
172pub const SYS_vm86 = 166;
173pub const SYS_query_module = 167;
174pub const SYS_poll = 168;
175pub const SYS_nfsservctl = 169;
176pub const SYS_setresgid = 170;
177pub const SYS_getresgid = 171;
178pub const SYS_prctl = 172;
179pub const SYS_rt_sigreturn = 173;
180pub const SYS_rt_sigaction = 174;
181pub const SYS_rt_sigprocmask = 175;
182pub const SYS_rt_sigpending = 176;
183pub const SYS_rt_sigtimedwait = 177;
184pub const SYS_rt_sigqueueinfo = 178;
185pub const SYS_rt_sigsuspend = 179;
186pub const SYS_pread64 = 180;
187pub const SYS_pwrite64 = 181;
188pub const SYS_chown = 182;
189pub const SYS_getcwd = 183;
190pub const SYS_capget = 184;
191pub const SYS_capset = 185;
192pub const SYS_sigaltstack = 186;
193pub const SYS_sendfile = 187;
194pub const SYS_getpmsg = 188;
195pub const SYS_putpmsg = 189;
196pub const SYS_vfork = 190;
197pub const SYS_ugetrlimit = 191;
198pub const SYS_mmap2 = 192;
199pub const SYS_truncate64 = 193;
200pub const SYS_ftruncate64 = 194;
201pub const SYS_stat64 = 195;
202pub const SYS_lstat64 = 196;
203pub const SYS_fstat64 = 197;
204pub const SYS_lchown32 = 198;
205pub const SYS_getuid32 = 199;
206pub const SYS_getgid32 = 200;
207pub const SYS_geteuid32 = 201;
208pub const SYS_getegid32 = 202;
209pub const SYS_setreuid32 = 203;
210pub const SYS_setregid32 = 204;
211pub const SYS_getgroups32 = 205;
212pub const SYS_setgroups32 = 206;
213pub const SYS_fchown32 = 207;
214pub const SYS_setresuid32 = 208;
215pub const SYS_getresuid32 = 209;
216pub const SYS_setresgid32 = 210;
217pub const SYS_getresgid32 = 211;
218pub const SYS_chown32 = 212;
219pub const SYS_setuid32 = 213;
220pub const SYS_setgid32 = 214;
221pub const SYS_setfsuid32 = 215;
222pub const SYS_setfsgid32 = 216;
223pub const SYS_pivot_root = 217;
224pub const SYS_mincore = 218;
225pub const SYS_madvise = 219;
226pub const SYS_madvise1 = 219;
227pub const SYS_getdents64 = 220;
228pub const SYS_fcntl64 = 221;
229pub const SYS_gettid = 224;
230pub const SYS_readahead = 225;
231pub const SYS_setxattr = 226;
232pub const SYS_lsetxattr = 227;
233pub const SYS_fsetxattr = 228;
234pub const SYS_getxattr = 229;
235pub const SYS_lgetxattr = 230;
236pub const SYS_fgetxattr = 231;
237pub const SYS_listxattr = 232;
238pub const SYS_llistxattr = 233;
239pub const SYS_flistxattr = 234;
240pub const SYS_removexattr = 235;
241pub const SYS_lremovexattr = 236;
242pub const SYS_fremovexattr = 237;
243pub const SYS_tkill = 238;
244pub const SYS_sendfile64 = 239;
245pub const SYS_futex = 240;
246pub const SYS_sched_setaffinity = 241;
247pub const SYS_sched_getaffinity = 242;
248pub const SYS_set_thread_area = 243;
249pub const SYS_get_thread_area = 244;
250pub const SYS_io_setup = 245;
251pub const SYS_io_destroy = 246;
252pub const SYS_io_getevents = 247;
253pub const SYS_io_submit = 248;
254pub const SYS_io_cancel = 249;
255pub const SYS_fadvise64 = 250;
256pub const SYS_exit_group = 252;
257pub const SYS_lookup_dcookie = 253;
258pub const SYS_epoll_create = 254;
259pub const SYS_epoll_ctl = 255;
260pub const SYS_epoll_wait = 256;
261pub const SYS_remap_file_pages = 257;
262pub const SYS_set_tid_address = 258;
263pub const SYS_timer_create = 259;
264pub const SYS_timer_settime = SYS_timer_create+1;
265pub const SYS_timer_gettime = SYS_timer_create+2;
266pub const SYS_timer_getoverrun = SYS_timer_create+3;
267pub const SYS_timer_delete = SYS_timer_create+4;
268pub const SYS_clock_settime = SYS_timer_create+5;
269pub const SYS_clock_gettime = SYS_timer_create+6;
270pub const SYS_clock_getres = SYS_timer_create+7;
271pub const SYS_clock_nanosleep = SYS_timer_create+8;
272pub const SYS_statfs64 = 268;
273pub const SYS_fstatfs64 = 269;
274pub const SYS_tgkill = 270;
275pub const SYS_utimes = 271;
276pub const SYS_fadvise64_64 = 272;
277pub const SYS_vserver = 273;
278pub const SYS_mbind = 274;
279pub const SYS_get_mempolicy = 275;
280pub const SYS_set_mempolicy = 276;
281pub const SYS_mq_open = 277;
282pub const SYS_mq_unlink = SYS_mq_open+1;
283pub const SYS_mq_timedsend = SYS_mq_open+2;
284pub const SYS_mq_timedreceive = SYS_mq_open+3;
285pub const SYS_mq_notify = SYS_mq_open+4;
286pub const SYS_mq_getsetattr = SYS_mq_open+5;
287pub const SYS_kexec_load = 283;
288pub const SYS_waitid = 284;
289pub const SYS_add_key = 286;
290pub const SYS_request_key = 287;
291pub const SYS_keyctl = 288;
292pub const SYS_ioprio_set = 289;
293pub const SYS_ioprio_get = 290;
294pub const SYS_inotify_init = 291;
295pub const SYS_inotify_add_watch = 292;
296pub const SYS_inotify_rm_watch = 293;
297pub const SYS_migrate_pages = 294;
298pub const SYS_openat = 295;
299pub const SYS_mkdirat = 296;
300pub const SYS_mknodat = 297;
301pub const SYS_fchownat = 298;
302pub const SYS_futimesat = 299;
303pub const SYS_fstatat64 = 300;
304pub const SYS_unlinkat = 301;
305pub const SYS_renameat = 302;
306pub const SYS_linkat = 303;
307pub const SYS_symlinkat = 304;
308pub const SYS_readlinkat = 305;
309pub const SYS_fchmodat = 306;
310pub const SYS_faccessat = 307;
311pub const SYS_pselect6 = 308;
312pub const SYS_ppoll = 309;
313pub const SYS_unshare = 310;
314pub const SYS_set_robust_list = 311;
315pub const SYS_get_robust_list = 312;
316pub const SYS_splice = 313;
317pub const SYS_sync_file_range = 314;
318pub const SYS_tee = 315;
319pub const SYS_vmsplice = 316;
320pub const SYS_move_pages = 317;
321pub const SYS_getcpu = 318;
322pub const SYS_epoll_pwait = 319;
323pub const SYS_utimensat = 320;
324pub const SYS_signalfd = 321;
325pub const SYS_timerfd_create = 322;
326pub const SYS_eventfd = 323;
327pub const SYS_fallocate = 324;
328pub const SYS_timerfd_settime = 325;
329pub const SYS_timerfd_gettime = 326;
330pub const SYS_signalfd4 = 327;
331pub const SYS_eventfd2 = 328;
332pub const SYS_epoll_create1 = 329;
333pub const SYS_dup3 = 330;
334pub const SYS_pipe2 = 331;
335pub const SYS_inotify_init1 = 332;
336pub const SYS_preadv = 333;
337pub const SYS_pwritev = 334;
338pub const SYS_rt_tgsigqueueinfo = 335;
339pub const SYS_perf_event_open = 336;
340pub const SYS_recvmmsg = 337;
341pub const SYS_fanotify_init = 338;
342pub const SYS_fanotify_mark = 339;
343pub const SYS_prlimit64 = 340;
344pub const SYS_name_to_handle_at = 341;
345pub const SYS_open_by_handle_at = 342;
346pub const SYS_clock_adjtime = 343;
347pub const SYS_syncfs = 344;
348pub const SYS_sendmmsg = 345;
349pub const SYS_setns = 346;
350pub const SYS_process_vm_readv = 347;
351pub const SYS_process_vm_writev = 348;
352pub const SYS_kcmp = 349;
353pub const SYS_finit_module = 350;
354pub const SYS_sched_setattr = 351;
355pub const SYS_sched_getattr = 352;
356pub const SYS_renameat2 = 353;
357pub const SYS_seccomp = 354;
358pub const SYS_getrandom = 355;
359pub const SYS_memfd_create = 356;
360pub const SYS_bpf = 357;
361pub const SYS_execveat = 358;
362pub const SYS_socket = 359;
363pub const SYS_socketpair = 360;
364pub const SYS_bind = 361;
365pub const SYS_connect = 362;
366pub const SYS_listen = 363;
367pub const SYS_accept4 = 364;
368pub const SYS_getsockopt = 365;
369pub const SYS_setsockopt = 366;
370pub const SYS_getsockname = 367;
371pub const SYS_getpeername = 368;
372pub const SYS_sendto = 369;
373pub const SYS_sendmsg = 370;
374pub const SYS_recvfrom = 371;
375pub const SYS_recvmsg = 372;
376pub const SYS_shutdown = 373;
377pub const SYS_userfaultfd = 374;
378pub const SYS_membarrier = 375;
379pub const SYS_mlock2 = 376;
380
381
382pub const O_CREAT = 0o100;
383pub const O_EXCL = 0o200;
384pub const O_NOCTTY = 0o400;
385pub const O_TRUNC = 0o1000;
386pub const O_APPEND = 0o2000;
387pub const O_NONBLOCK = 0o4000;
388pub const O_DSYNC = 0o10000;
389pub const O_SYNC = 0o4010000;
390pub const O_RSYNC = 0o4010000;
391pub const O_DIRECTORY = 0o200000;
392pub const O_NOFOLLOW = 0o400000;
393pub const O_CLOEXEC = 0o2000000;
394
395pub const O_ASYNC = 0o20000;
396pub const O_DIRECT = 0o40000;
397pub const O_LARGEFILE = 0o100000;
398pub const O_NOATIME = 0o1000000;
399pub const O_PATH = 0o10000000;
400pub const O_TMPFILE = 0o20200000;
401pub const O_NDELAY = O_NONBLOCK;
402
403pub const F_DUPFD = 0;
404pub const F_GETFD = 1;
405pub const F_SETFD = 2;
406pub const F_GETFL = 3;
407pub const F_SETFL = 4;
408
409pub const F_SETOWN = 8;
410pub const F_GETOWN = 9;
411pub const F_SETSIG = 10;
412pub const F_GETSIG = 11;
413
414pub const F_GETLK = 12;
415pub const F_SETLK = 13;
416pub const F_SETLKW = 14;
417
418pub const F_SETOWN_EX = 15;
419pub const F_GETOWN_EX = 16;
420
421pub const F_GETOWNER_UIDS = 17;
422
423pub inline fn syscall0(number: usize) usize {
424 return asm volatile ("int $0x80"
425 : [ret] "={eax}" (-> usize)
426 : [number] "{eax}" (number));
427}
428
429pub inline fn syscall1(number: usize, arg1: usize) usize {
430 return asm volatile ("int $0x80"
431 : [ret] "={eax}" (-> usize)
432 : [number] "{eax}" (number),
433 [arg1] "{ebx}" (arg1));
434}
435
436pub inline fn syscall2(number: usize, arg1: usize, arg2: usize) usize {
437 return asm volatile ("int $0x80"
438 : [ret] "={eax}" (-> usize)
439 : [number] "{eax}" (number),
440 [arg1] "{ebx}" (arg1),
441 [arg2] "{ecx}" (arg2));
442}
443
444pub inline fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) usize {
445 return asm volatile ("int $0x80"
446 : [ret] "={eax}" (-> usize)
447 : [number] "{eax}" (number),
448 [arg1] "{ebx}" (arg1),
449 [arg2] "{ecx}" (arg2),
450 [arg3] "{edx}" (arg3));
451}
452
453pub inline fn syscall4(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) usize {
454 return asm volatile ("int $0x80"
455 : [ret] "={eax}" (-> usize)
456 : [number] "{eax}" (number),
457 [arg1] "{ebx}" (arg1),
458 [arg2] "{ecx}" (arg2),
459 [arg3] "{edx}" (arg3),
460 [arg4] "{esi}" (arg4));
461}
462
463pub inline fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize,
464 arg4: usize, arg5: usize) usize
465{
466 return asm volatile ("int $0x80"
467 : [ret] "={eax}" (-> usize)
468 : [number] "{eax}" (number),
469 [arg1] "{ebx}" (arg1),
470 [arg2] "{ecx}" (arg2),
471 [arg3] "{edx}" (arg3),
472 [arg4] "{esi}" (arg4),
473 [arg5] "{edi}" (arg5));
474}
475
476pub inline fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize,
477 arg4: usize, arg5: usize, arg6: usize) usize
478{
479 return asm volatile ("int $0x80"
480 : [ret] "={eax}" (-> usize)
481 : [number] "{eax}" (number),
482 [arg1] "{ebx}" (arg1),
483 [arg2] "{ecx}" (arg2),
484 [arg3] "{edx}" (arg3),
485 [arg4] "{esi}" (arg4),
486 [arg5] "{edi}" (arg5),
487 [arg6] "{ebp}" (arg6));
488}
489
490pub nakedcc fn restore() void {
491 asm volatile (
492 \\popl %%eax
493 \\movl $119, %%eax
494 \\int $0x80
495 :
496 :
497 : "rcx", "r11");
498}
499
500pub nakedcc fn restore_rt() void {
501 asm volatile ("int $0x80"
502 :
503 : [number] "{eax}" (usize(SYS_rt_sigreturn))
504 : "rcx", "r11");
505}
std/os/linux/index.zig+621-68
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("../../index.zig");1const std = @import("../../index.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const builtin = @import("builtin");3const builtin = @import("builtin");
4const vdso = @import("vdso.zig");
4pub use switch (builtin.arch) {5pub use switch (builtin.arch) {
5 builtin.Arch.x86_64 => @import("x86_64.zig"),6 builtin.Arch.x86_64 => @import("x86_64.zig"),
6 builtin.Arch.i386 => @import("i386.zig"),7 builtin.Arch.i386 => @import("i386.zig"),
...@@ -14,6 +15,22 @@ pub const STDIN_FILENO = 0;...@@ -14,6 +15,22 @@ pub const STDIN_FILENO = 0;
14pub const STDOUT_FILENO = 1;15pub const STDOUT_FILENO = 1;
15pub const STDERR_FILENO = 2;16pub const STDERR_FILENO = 2;
1617
18pub const FUTEX_WAIT = 0;
19pub const FUTEX_WAKE = 1;
20pub const FUTEX_FD = 2;
21pub const FUTEX_REQUEUE = 3;
22pub const FUTEX_CMP_REQUEUE = 4;
23pub const FUTEX_WAKE_OP = 5;
24pub const FUTEX_LOCK_PI = 6;
25pub const FUTEX_UNLOCK_PI = 7;
26pub const FUTEX_TRYLOCK_PI = 8;
27pub const FUTEX_WAIT_BITSET = 9;
28
29pub const FUTEX_PRIVATE_FLAG = 128;
30
31pub const FUTEX_CLOCK_REALTIME = 256;
32
33
17pub const PROT_NONE = 0;34pub const PROT_NONE = 0;
18pub const PROT_READ = 1;35pub const PROT_READ = 1;
19pub const PROT_WRITE = 2;36pub const PROT_WRITE = 2;
...@@ -38,6 +55,11 @@ pub const MAP_STACK = 0x20000;...@@ -38,6 +55,11 @@ pub const MAP_STACK = 0x20000;
38pub const MAP_HUGETLB = 0x40000;55pub const MAP_HUGETLB = 0x40000;
39pub const MAP_FILE = 0;56pub const MAP_FILE = 0;
4057
58pub const F_OK = 0;
59pub const X_OK = 1;
60pub const W_OK = 2;
61pub const R_OK = 4;
62
41pub const WNOHANG = 1;63pub const WNOHANG = 1;
42pub const WUNTRACED = 2;64pub const WUNTRACED = 2;
43pub const WSTOPPED = 2;65pub const WSTOPPED = 2;
...@@ -101,17 +123,6 @@ pub const SIG_BLOCK = 0;...@@ -101,17 +123,6 @@ pub const SIG_BLOCK = 0;
101pub const SIG_UNBLOCK = 1;123pub const SIG_UNBLOCK = 1;
102pub const SIG_SETMASK = 2;124pub const SIG_SETMASK = 2;
103125
104pub const SOCK_STREAM = 1;
105pub const SOCK_DGRAM = 2;
106pub const SOCK_RAW = 3;
107pub const SOCK_RDM = 4;
108pub const SOCK_SEQPACKET = 5;
109pub const SOCK_DCCP = 6;
110pub const SOCK_PACKET = 10;
111pub const SOCK_CLOEXEC = 0o2000000;
112pub const SOCK_NONBLOCK = 0o4000;
113
114
115pub const PROTO_ip = 0o000;126pub const PROTO_ip = 0o000;
116pub const PROTO_icmp = 0o001;127pub const PROTO_icmp = 0o001;
117pub const PROTO_igmp = 0o002;128pub const PROTO_igmp = 0o002;
...@@ -149,6 +160,20 @@ pub const PROTO_encap = 0o142;...@@ -149,6 +160,20 @@ pub const PROTO_encap = 0o142;
149pub const PROTO_pim = 0o147;160pub const PROTO_pim = 0o147;
150pub const PROTO_raw = 0o377;161pub const PROTO_raw = 0o377;
151162
163pub const SHUT_RD = 0;
164pub const SHUT_WR = 1;
165pub const SHUT_RDWR = 2;
166
167pub const SOCK_STREAM = 1;
168pub const SOCK_DGRAM = 2;
169pub const SOCK_RAW = 3;
170pub const SOCK_RDM = 4;
171pub const SOCK_SEQPACKET = 5;
172pub const SOCK_DCCP = 6;
173pub const SOCK_PACKET = 10;
174pub const SOCK_CLOEXEC = 0o2000000;
175pub const SOCK_NONBLOCK = 0o4000;
176
152pub const PF_UNSPEC = 0;177pub const PF_UNSPEC = 0;
153pub const PF_LOCAL = 1;178pub const PF_LOCAL = 1;
154pub const PF_UNIX = PF_LOCAL;179pub const PF_UNIX = PF_LOCAL;
...@@ -193,7 +218,10 @@ pub const PF_CAIF = 37;...@@ -193,7 +218,10 @@ pub const PF_CAIF = 37;
193pub const PF_ALG = 38;218pub const PF_ALG = 38;
194pub const PF_NFC = 39;219pub const PF_NFC = 39;
195pub const PF_VSOCK = 40;220pub const PF_VSOCK = 40;
196pub const PF_MAX = 41;221pub const PF_KCM = 41;
222pub const PF_QIPCRTR = 42;
223pub const PF_SMC = 43;
224pub const PF_MAX = 44;
197225
198pub const AF_UNSPEC = PF_UNSPEC;226pub const AF_UNSPEC = PF_UNSPEC;
199pub const AF_LOCAL = PF_LOCAL;227pub const AF_LOCAL = PF_LOCAL;
...@@ -239,8 +267,137 @@ pub const AF_CAIF = PF_CAIF;...@@ -239,8 +267,137 @@ pub const AF_CAIF = PF_CAIF;
239pub const AF_ALG = PF_ALG;267pub const AF_ALG = PF_ALG;
240pub const AF_NFC = PF_NFC;268pub const AF_NFC = PF_NFC;
241pub const AF_VSOCK = PF_VSOCK;269pub const AF_VSOCK = PF_VSOCK;
270pub const AF_KCM = PF_KCM;
271pub const AF_QIPCRTR = PF_QIPCRTR;
272pub const AF_SMC = PF_SMC;
242pub const AF_MAX = PF_MAX;273pub const AF_MAX = PF_MAX;
243274
275pub const SO_DEBUG = 1;
276pub const SO_REUSEADDR = 2;
277pub const SO_TYPE = 3;
278pub const SO_ERROR = 4;
279pub const SO_DONTROUTE = 5;
280pub const SO_BROADCAST = 6;
281pub const SO_SNDBUF = 7;
282pub const SO_RCVBUF = 8;
283pub const SO_KEEPALIVE = 9;
284pub const SO_OOBINLINE = 10;
285pub const SO_NO_CHECK = 11;
286pub const SO_PRIORITY = 12;
287pub const SO_LINGER = 13;
288pub const SO_BSDCOMPAT = 14;
289pub const SO_REUSEPORT = 15;
290pub const SO_PASSCRED = 16;
291pub const SO_PEERCRED = 17;
292pub const SO_RCVLOWAT = 18;
293pub const SO_SNDLOWAT = 19;
294pub const SO_RCVTIMEO = 20;
295pub const SO_SNDTIMEO = 21;
296pub const SO_ACCEPTCONN = 30;
297pub const SO_SNDBUFFORCE = 32;
298pub const SO_RCVBUFFORCE = 33;
299pub const SO_PROTOCOL = 38;
300pub const SO_DOMAIN = 39;
301
302pub const SO_SECURITY_AUTHENTICATION = 22;
303pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23;
304pub const SO_SECURITY_ENCRYPTION_NETWORK = 24;
305
306pub const SO_BINDTODEVICE = 25;
307
308pub const SO_ATTACH_FILTER = 26;
309pub const SO_DETACH_FILTER = 27;
310pub const SO_GET_FILTER = SO_ATTACH_FILTER;
311
312pub const SO_PEERNAME = 28;
313pub const SO_TIMESTAMP = 29;
314pub const SCM_TIMESTAMP = SO_TIMESTAMP;
315
316pub const SO_PEERSEC = 31;
317pub const SO_PASSSEC = 34;
318pub const SO_TIMESTAMPNS = 35;
319pub const SCM_TIMESTAMPNS = SO_TIMESTAMPNS;
320pub const SO_MARK = 36;
321pub const SO_TIMESTAMPING = 37;
322pub const SCM_TIMESTAMPING = SO_TIMESTAMPING;
323pub const SO_RXQ_OVFL = 40;
324pub const SO_WIFI_STATUS = 41;
325pub const SCM_WIFI_STATUS = SO_WIFI_STATUS;
326pub const SO_PEEK_OFF = 42;
327pub const SO_NOFCS = 43;
328pub const SO_LOCK_FILTER = 44;
329pub const SO_SELECT_ERR_QUEUE = 45;
330pub const SO_BUSY_POLL = 46;
331pub const SO_MAX_PACING_RATE = 47;
332pub const SO_BPF_EXTENSIONS = 48;
333pub const SO_INCOMING_CPU = 49;
334pub const SO_ATTACH_BPF = 50;
335pub const SO_DETACH_BPF = SO_DETACH_FILTER;
336pub const SO_ATTACH_REUSEPORT_CBPF = 51;
337pub const SO_ATTACH_REUSEPORT_EBPF = 52;
338pub const SO_CNX_ADVICE = 53;
339pub const SCM_TIMESTAMPING_OPT_STATS = 54;
340pub const SO_MEMINFO = 55;
341pub const SO_INCOMING_NAPI_ID = 56;
342pub const SO_COOKIE = 57;
343pub const SCM_TIMESTAMPING_PKTINFO = 58;
344pub const SO_PEERGROUPS = 59;
345pub const SO_ZEROCOPY = 60;
346
347pub const SOL_SOCKET = 1;
348
349pub const SOL_IP = 0;
350pub const SOL_IPV6 = 41;
351pub const SOL_ICMPV6 = 58;
352
353pub const SOL_RAW = 255;
354pub const SOL_DECNET = 261;
355pub const SOL_X25 = 262;
356pub const SOL_PACKET = 263;
357pub const SOL_ATM = 264;
358pub const SOL_AAL = 265;
359pub const SOL_IRDA = 266;
360pub const SOL_NETBEUI = 267;
361pub const SOL_LLC = 268;
362pub const SOL_DCCP = 269;
363pub const SOL_NETLINK = 270;
364pub const SOL_TIPC = 271;
365pub const SOL_RXRPC = 272;
366pub const SOL_PPPOL2TP = 273;
367pub const SOL_BLUETOOTH = 274;
368pub const SOL_PNPIPE = 275;
369pub const SOL_RDS = 276;
370pub const SOL_IUCV = 277;
371pub const SOL_CAIF = 278;
372pub const SOL_ALG = 279;
373pub const SOL_NFC = 280;
374pub const SOL_KCM = 281;
375pub const SOL_TLS = 282;
376
377pub const SOMAXCONN = 128;
378
379pub const MSG_OOB = 0x0001;
380pub const MSG_PEEK = 0x0002;
381pub const MSG_DONTROUTE = 0x0004;
382pub const MSG_CTRUNC = 0x0008;
383pub const MSG_PROXY = 0x0010;
384pub const MSG_TRUNC = 0x0020;
385pub const MSG_DONTWAIT = 0x0040;
386pub const MSG_EOR = 0x0080;
387pub const MSG_WAITALL = 0x0100;
388pub const MSG_FIN = 0x0200;
389pub const MSG_SYN = 0x0400;
390pub const MSG_CONFIRM = 0x0800;
391pub const MSG_RST = 0x1000;
392pub const MSG_ERRQUEUE = 0x2000;
393pub const MSG_NOSIGNAL = 0x4000;
394pub const MSG_MORE = 0x8000;
395pub const MSG_WAITFORONE = 0x10000;
396pub const MSG_BATCH = 0x40000;
397pub const MSG_ZEROCOPY = 0x4000000;
398pub const MSG_FASTOPEN = 0x20000000;
399pub const MSG_CMSG_CLOEXEC = 0x40000000;
400
244pub const DT_UNKNOWN = 0;401pub const DT_UNKNOWN = 0;
245pub const DT_FIFO = 1;402pub const DT_FIFO = 1;
246pub const DT_CHR = 2;403pub const DT_CHR = 2;
...@@ -343,6 +500,126 @@ pub const CLOCK_BOOTTIME_ALARM = 9;...@@ -343,6 +500,126 @@ pub const CLOCK_BOOTTIME_ALARM = 9;
343pub const CLOCK_SGI_CYCLE = 10;500pub const CLOCK_SGI_CYCLE = 10;
344pub const CLOCK_TAI = 11;501pub const CLOCK_TAI = 11;
345502
503pub const CSIGNAL = 0x000000ff;
504pub const CLONE_VM = 0x00000100;
505pub const CLONE_FS = 0x00000200;
506pub const CLONE_FILES = 0x00000400;
507pub const CLONE_SIGHAND = 0x00000800;
508pub const CLONE_PTRACE = 0x00002000;
509pub const CLONE_VFORK = 0x00004000;
510pub const CLONE_PARENT = 0x00008000;
511pub const CLONE_THREAD = 0x00010000;
512pub const CLONE_NEWNS = 0x00020000;
513pub const CLONE_SYSVSEM = 0x00040000;
514pub const CLONE_SETTLS = 0x00080000;
515pub const CLONE_PARENT_SETTID = 0x00100000;
516pub const CLONE_CHILD_CLEARTID = 0x00200000;
517pub const CLONE_DETACHED = 0x00400000;
518pub const CLONE_UNTRACED = 0x00800000;
519pub const CLONE_CHILD_SETTID = 0x01000000;
520pub const CLONE_NEWCGROUP = 0x02000000;
521pub const CLONE_NEWUTS = 0x04000000;
522pub const CLONE_NEWIPC = 0x08000000;
523pub const CLONE_NEWUSER = 0x10000000;
524pub const CLONE_NEWPID = 0x20000000;
525pub const CLONE_NEWNET = 0x40000000;
526pub const CLONE_IO = 0x80000000;
527
528pub const MS_RDONLY = 1;
529pub const MS_NOSUID = 2;
530pub const MS_NODEV = 4;
531pub const MS_NOEXEC = 8;
532pub const MS_SYNCHRONOUS = 16;
533pub const MS_REMOUNT = 32;
534pub const MS_MANDLOCK = 64;
535pub const MS_DIRSYNC = 128;
536pub const MS_NOATIME = 1024;
537pub const MS_NODIRATIME = 2048;
538pub const MS_BIND = 4096;
539pub const MS_MOVE = 8192;
540pub const MS_REC = 16384;
541pub const MS_SILENT = 32768;
542pub const MS_POSIXACL = (1<<16);
543pub const MS_UNBINDABLE = (1<<17);
544pub const MS_PRIVATE = (1<<18);
545pub const MS_SLAVE = (1<<19);
546pub const MS_SHARED = (1<<20);
547pub const MS_RELATIME = (1<<21);
548pub const MS_KERNMOUNT = (1<<22);
549pub const MS_I_VERSION = (1<<23);
550pub const MS_STRICTATIME = (1<<24);
551pub const MS_LAZYTIME = (1<<25);
552pub const MS_NOREMOTELOCK = (1<<27);
553pub const MS_NOSEC = (1<<28);
554pub const MS_BORN = (1<<29);
555pub const MS_ACTIVE = (1<<30);
556pub const MS_NOUSER = (1<<31);
557
558pub const MS_RMT_MASK = (MS_RDONLY|MS_SYNCHRONOUS|MS_MANDLOCK|MS_I_VERSION|MS_LAZYTIME);
559
560pub const MS_MGC_VAL = 0xc0ed0000;
561pub const MS_MGC_MSK = 0xffff0000;
562
563pub const MNT_FORCE = 1;
564pub const MNT_DETACH = 2;
565pub const MNT_EXPIRE = 4;
566pub const UMOUNT_NOFOLLOW = 8;
567
568
569pub const S_IFMT = 0o170000;
570
571pub const S_IFDIR = 0o040000;
572pub const S_IFCHR = 0o020000;
573pub const S_IFBLK = 0o060000;
574pub const S_IFREG = 0o100000;
575pub const S_IFIFO = 0o010000;
576pub const S_IFLNK = 0o120000;
577pub const S_IFSOCK = 0o140000;
578
579pub const S_ISUID = 0o4000;
580pub const S_ISGID = 0o2000;
581pub const S_ISVTX = 0o1000;
582pub const S_IRUSR = 0o400;
583pub const S_IWUSR = 0o200;
584pub const S_IXUSR = 0o100;
585pub const S_IRWXU = 0o700;
586pub const S_IRGRP = 0o040;
587pub const S_IWGRP = 0o020;
588pub const S_IXGRP = 0o010;
589pub const S_IRWXG = 0o070;
590pub const S_IROTH = 0o004;
591pub const S_IWOTH = 0o002;
592pub const S_IXOTH = 0o001;
593pub const S_IRWXO = 0o007;
594
595pub fn S_ISREG(m: u32) bool {
596 return m & S_IFMT == S_IFREG;
597}
598
599pub fn S_ISDIR(m: u32) bool {
600 return m & S_IFMT == S_IFDIR;
601}
602
603pub fn S_ISCHR(m: u32) bool {
604 return m & S_IFMT == S_IFCHR;
605}
606
607pub fn S_ISBLK(m: u32) bool {
608 return m & S_IFMT == S_IFBLK;
609}
610
611pub fn S_ISFIFO(m: u32) bool {
612 return m & S_IFMT == S_IFIFO;
613}
614
615pub fn S_ISLNK(m: u32) bool {
616 return m & S_IFMT == S_IFLNK;
617}
618
619pub fn S_ISSOCK(m: u32) bool {
620 return m & S_IFMT == S_IFSOCK;
621}
622
346pub const TFD_NONBLOCK = O_NONBLOCK;623pub const TFD_NONBLOCK = O_NONBLOCK;
347pub const TFD_CLOEXEC = O_CLOEXEC;624pub const TFD_CLOEXEC = O_CLOEXEC;
348625
...@@ -380,6 +657,10 @@ pub fn chdir(path: &const u8) usize {...@@ -380,6 +657,10 @@ pub fn chdir(path: &const u8) usize {
380 return syscall1(SYS_chdir, @ptrToInt(path));657 return syscall1(SYS_chdir, @ptrToInt(path));
381}658}
382659
660pub fn chroot(path: &const u8) usize {
661 return syscall1(SYS_chroot, @ptrToInt(path));
662}
663
383pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) usize {664pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) usize {
384 return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp));665 return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp));
385}666}
...@@ -388,6 +669,10 @@ pub fn fork() usize {...@@ -388,6 +669,10 @@ pub fn fork() usize {
388 return syscall0(SYS_fork);669 return syscall0(SYS_fork);
389}670}
390671
672pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?&timespec) usize {
673 return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout));
674}
675
391pub fn getcwd(buf: &u8, size: usize) usize {676pub fn getcwd(buf: &u8, size: usize) usize {
392 return syscall2(SYS_getcwd, @ptrToInt(buf), size);677 return syscall2(SYS_getcwd, @ptrToInt(buf), size);
393}678}
...@@ -409,13 +694,25 @@ pub fn mkdir(path: &const u8, mode: u32) usize {...@@ -409,13 +694,25 @@ pub fn mkdir(path: &const u8, mode: u32) usize {
409 return syscall2(SYS_mkdir, @ptrToInt(path), mode);694 return syscall2(SYS_mkdir, @ptrToInt(path), mode);
410}695}
411696
412pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize) usize {697pub fn mount(special: &const u8, dir: &const u8, fstype: &const u8, flags: usize, data: usize) usize {
698 return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data);
699}
700
701pub fn umount(special: &const u8) usize {
702 return syscall2(SYS_umount2, @ptrToInt(special), 0);
703}
704
705pub fn umount2(special: &const u8, flags: u32) usize {
706 return syscall2(SYS_umount2, @ptrToInt(special), flags);
707}
708
709pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
413 return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),710 return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
414 @bitCast(usize, offset));711 @bitCast(usize, offset));
415}712}
416713
417pub fn munmap(address: &u8, length: usize) usize {714pub fn munmap(address: usize, length: usize) usize {
418 return syscall2(SYS_munmap, @ptrToInt(address), length);715 return syscall2(SYS_munmap, address, length);
419}716}
420717
421pub fn read(fd: i32, buf: &u8, count: usize) usize {718pub fn read(fd: i32, buf: &u8, count: usize) usize {
...@@ -434,6 +731,10 @@ pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize {...@@ -434,6 +731,10 @@ pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize {
434 return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset);731 return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset);
435}732}
436733
734pub fn access(path: &const u8, mode: u32) usize {
735 return syscall2(SYS_access, @ptrToInt(path), mode);
736}
737
437pub fn pipe(fd: &[2]i32) usize {738pub fn pipe(fd: &[2]i32) usize {
438 return pipe2(fd, 0);739 return pipe2(fd, 0);
439}740}
...@@ -466,6 +767,16 @@ pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) usize {...@@ -466,6 +767,16 @@ pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) usize {
466 return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode);767 return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode);
467}768}
468769
770/// See also `clone` (from the arch-specific include)
771pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: &i32, child_tid: &i32, newtls: usize) usize {
772 return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls);
773}
774
775/// See also `clone` (from the arch-specific include)
776pub fn clone2(flags: usize, child_stack_ptr: usize) usize {
777 return syscall2(SYS_clone, flags, child_stack_ptr);
778}
779
469pub fn close(fd: i32) usize {780pub fn close(fd: i32) usize {
470 return syscall1(SYS_close, usize(fd));781 return syscall1(SYS_close, usize(fd));
471}782}
...@@ -495,6 +806,45 @@ pub fn waitpid(pid: i32, status: &i32, options: i32) usize {...@@ -495,6 +806,45 @@ pub fn waitpid(pid: i32, status: &i32, options: i32) usize {
495 return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);806 return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);
496}807}
497808
809pub fn clock_gettime(clk_id: i32, tp: &timespec) usize {
810 if (VDSO_CGT_SYM.len != 0) {
811 const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered);
812 if (@ptrToInt(f) != 0) {
813 const rc = f(clk_id, tp);
814 switch (rc) {
815 0, @bitCast(usize, isize(-EINVAL)) => return rc,
816 else => {},
817 }
818 }
819 }
820 return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
821}
822var vdso_clock_gettime = init_vdso_clock_gettime;
823extern fn init_vdso_clock_gettime(clk: i32, ts: &timespec) usize {
824 const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM);
825 var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr);
826 _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f,
827 builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic);
828 if (@ptrToInt(f) == 0) return @bitCast(usize, isize(-ENOSYS));
829 return f(clk, ts);
830}
831
832pub fn clock_getres(clk_id: i32, tp: &timespec) usize {
833 return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
834}
835
836pub fn clock_settime(clk_id: i32, tp: &const timespec) usize {
837 return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
838}
839
840pub fn gettimeofday(tv: &timeval, tz: &timezone) usize {
841 return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz));
842}
843
844pub fn settimeofday(tv: &const timeval, tz: &const timezone) usize {
845 return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz));
846}
847
498pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize {848pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize {
499 return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));849 return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));
500}850}
...@@ -515,6 +865,58 @@ pub fn setregid(rgid: u32, egid: u32) usize {...@@ -515,6 +865,58 @@ pub fn setregid(rgid: u32, egid: u32) usize {
515 return syscall2(SYS_setregid, rgid, egid);865 return syscall2(SYS_setregid, rgid, egid);
516}866}
517867
868pub fn getuid() u32 {
869 return u32(syscall0(SYS_getuid));
870}
871
872pub fn getgid() u32 {
873 return u32(syscall0(SYS_getgid));
874}
875
876pub fn geteuid() u32 {
877 return u32(syscall0(SYS_geteuid));
878}
879
880pub fn getegid() u32 {
881 return u32(syscall0(SYS_getegid));
882}
883
884pub fn seteuid(euid: u32) usize {
885 return syscall1(SYS_seteuid, euid);
886}
887
888pub fn setegid(egid: u32) usize {
889 return syscall1(SYS_setegid, egid);
890}
891
892pub fn getresuid(ruid: &u32, euid: &u32, suid: &u32) usize {
893 return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid));
894}
895
896pub fn getresgid(rgid: &u32, egid: &u32, sgid: &u32) usize {
897 return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid));
898}
899
900pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize {
901 return syscall3(SYS_setresuid, ruid, euid, suid);
902}
903
904pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize {
905 return syscall3(SYS_setresgid, rgid, egid, sgid);
906}
907
908pub fn getgroups(size: usize, list: &u32) usize {
909 return syscall2(SYS_getgroups, size, @ptrToInt(list));
910}
911
912pub fn setgroups(size: usize, list: &const u32) usize {
913 return syscall2(SYS_setgroups, size, @ptrToInt(list));
914}
915
916pub fn getpid() i32 {
917 return @bitCast(i32, u32(syscall0(SYS_getpid)));
918}
919
518pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) usize {920pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) usize {
519 return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8);921 return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8);
520}922}
...@@ -599,30 +1001,27 @@ pub fn sigismember(set: &const sigset_t, sig: u6) bool {...@@ -599,30 +1001,27 @@ pub fn sigismember(set: &const sigset_t, sig: u6) bool {
599 return ((*set)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0;1001 return ((*set)[usize(s) / usize.bit_count] & (usize(1) << (s & (usize.bit_count - 1)))) != 0;
600}1002}
6011003
6021004pub const in_port_t = u16;
603pub const sa_family_t = u16;1005pub const sa_family_t = u16;
604pub const socklen_t = u32;1006pub const socklen_t = u32;
605pub const in_addr = u32;
606pub const in6_addr = [16]u8;
6071007
608pub const sockaddr = extern struct {1008pub const sockaddr = extern union {
609 family: sa_family_t,1009 in: sockaddr_in,
610 port: u16,1010 in6: sockaddr_in6,
611 data: [12]u8,
612};1011};
6131012
614pub const sockaddr_in = extern struct {1013pub const sockaddr_in = extern struct {
615 family: sa_family_t,1014 family: sa_family_t,
616 port: u16,1015 port: in_port_t,
617 addr: in_addr,1016 addr: u32,
618 zero: [8]u8,1017 zero: [8]u8,
619};1018};
6201019
621pub const sockaddr_in6 = extern struct {1020pub const sockaddr_in6 = extern struct {
622 family: sa_family_t,1021 family: sa_family_t,
623 port: u16,1022 port: in_port_t,
624 flowinfo: u32,1023 flowinfo: u32,
625 addr: in6_addr,1024 addr: [16]u8,
626 scope_id: u32,1025 scope_id: u32,
627};1026};
6281027
...@@ -639,16 +1038,16 @@ pub fn getpeername(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) us...@@ -639,16 +1038,16 @@ pub fn getpeername(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) us
639 return syscall3(SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len));1038 return syscall3(SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len));
640}1039}
6411040
642pub fn socket(domain: i32, socket_type: i32, protocol: i32) usize {1041pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize {
643 return syscall3(SYS_socket, usize(domain), usize(socket_type), usize(protocol));1042 return syscall3(SYS_socket, domain, socket_type, protocol);
644}1043}
6451044
646pub fn setsockopt(fd: i32, level: i32, optname: i32, optval: &const u8, optlen: socklen_t) usize {1045pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: &const u8, optlen: socklen_t) usize {
647 return syscall5(SYS_setsockopt, usize(fd), usize(level), usize(optname), usize(optval), @ptrToInt(optlen));1046 return syscall5(SYS_setsockopt, usize(fd), level, optname, usize(optval), @ptrToInt(optlen));
648}1047}
6491048
650pub fn getsockopt(fd: i32, level: i32, optname: i32, noalias optval: &u8, noalias optlen: &socklen_t) usize {1049pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: &u8, noalias optlen: &socklen_t) usize {
651 return syscall5(SYS_getsockopt, usize(fd), usize(level), usize(optname), @ptrToInt(optval), @ptrToInt(optlen));1050 return syscall5(SYS_getsockopt, usize(fd), level, optname, @ptrToInt(optval), @ptrToInt(optlen));
652}1051}
6531052
654pub fn sendmsg(fd: i32, msg: &const msghdr, flags: u32) usize {1053pub fn sendmsg(fd: i32, msg: &const msghdr, flags: u32) usize {
...@@ -677,8 +1076,8 @@ pub fn bind(fd: i32, addr: &const sockaddr, len: socklen_t) usize {...@@ -677,8 +1076,8 @@ pub fn bind(fd: i32, addr: &const sockaddr, len: socklen_t) usize {
677 return syscall3(SYS_bind, usize(fd), @ptrToInt(addr), usize(len));1076 return syscall3(SYS_bind, usize(fd), @ptrToInt(addr), usize(len));
678}1077}
6791078
680pub fn listen(fd: i32, backlog: i32) usize {1079pub fn listen(fd: i32, backlog: u32) usize {
681 return syscall2(SYS_listen, usize(fd), usize(backlog));1080 return syscall2(SYS_listen, usize(fd), backlog);
682}1081}
6831082
684pub fn sendto(fd: i32, buf: &const u8, len: usize, flags: u32, addr: ?&const sockaddr, alen: socklen_t) usize {1083pub fn sendto(fd: i32, buf: &const u8, len: usize, flags: u32, addr: ?&const sockaddr, alen: socklen_t) usize {
...@@ -697,46 +1096,83 @@ pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags:...@@ -697,46 +1096,83 @@ pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags:
697 return syscall4(SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags);1096 return syscall4(SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags);
698}1097}
6991098
700// error NameTooLong;
701// error SystemResources;
702// error Io;
703//
704// pub fn if_nametoindex(name: []u8) !u32 {
705// var ifr: ifreq = undefined;
706//
707// if (name.len >= ifr.ifr_name.len) {
708// return error.NameTooLong;
709// }
710//
711// const socket_ret = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
712// const socket_err = getErrno(socket_ret);
713// if (socket_err > 0) {
714// return error.SystemResources;
715// }
716// const socket_fd = i32(socket_ret);
717// @memcpy(&ifr.ifr_name[0], &name[0], name.len);
718// ifr.ifr_name[name.len] = 0;
719// const ioctl_ret = ioctl(socket_fd, SIOCGIFINDEX, &ifr);
720// close(socket_fd);
721// const ioctl_err = getErrno(ioctl_ret);
722// if (ioctl_err > 0) {
723// return error.Io;
724// }
725// return ifr.ifr_ifindex;
726// }
727
728pub fn fstat(fd: i32, stat_buf: &Stat) usize {1099pub fn fstat(fd: i32, stat_buf: &Stat) usize {
729 return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf));1100 return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf));
730}1101}
7311102
732pub const epoll_data = extern union {1103pub fn stat(pathname: &const u8, statbuf: &Stat) usize {
1104 return syscall2(SYS_stat, @ptrToInt(pathname), @ptrToInt(statbuf));
1105}
1106
1107pub fn lstat(pathname: &const u8, statbuf: &Stat) usize {
1108 return syscall2(SYS_lstat, @ptrToInt(pathname), @ptrToInt(statbuf));
1109}
1110
1111pub fn listxattr(path: &const u8, list: &u8, size: usize) usize {
1112 return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size);
1113}
1114
1115pub fn llistxattr(path: &const u8, list: &u8, size: usize) usize {
1116 return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size);
1117}
1118
1119pub fn flistxattr(fd: usize, list: &u8, size: usize) usize {
1120 return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size);
1121}
1122
1123pub fn getxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize {
1124 return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
1125}
1126
1127pub fn lgetxattr(path: &const u8, name: &const u8, value: &void, size: usize) usize {
1128 return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size);
1129}
1130
1131pub fn fgetxattr(fd: usize, name: &const u8, value: &void, size: usize) usize {
1132 return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size);
1133}
1134
1135pub fn setxattr(path: &const u8, name: &const u8, value: &const void,
1136 size: usize, flags: usize) usize {
1137
1138 return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value),
1139 size, flags);
1140}
1141
1142pub fn lsetxattr(path: &const u8, name: &const u8, value: &const void,
1143 size: usize, flags: usize) usize {
1144
1145 return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value),
1146 size, flags);
1147}
1148
1149pub fn fsetxattr(fd: usize, name: &const u8, value: &const void,
1150 size: usize, flags: usize) usize {
1151
1152 return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value),
1153 size, flags);
1154}
1155
1156pub fn removexattr(path: &const u8, name: &const u8) usize {
1157 return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name));
1158}
1159
1160pub fn lremovexattr(path: &const u8, name: &const u8) usize {
1161 return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name));
1162}
1163
1164pub fn fremovexattr(fd: usize, name: &const u8) usize {
1165 return syscall2(SYS_fremovexattr, fd, @ptrToInt(name));
1166}
1167
1168pub const epoll_data = packed union {
733 ptr: usize,1169 ptr: usize,
734 fd: i32,1170 fd: i32,
735 @"u32": u32,1171 @"u32": u32,
736 @"u64": u64,1172 @"u64": u64,
737};1173};
7381174
739pub const epoll_event = extern struct {1175pub const epoll_event = packed struct {
740 events: u32,1176 events: u32,
741 data: epoll_data,1177 data: epoll_data,
742};1178};
...@@ -749,7 +1185,7 @@ pub fn epoll_create1(flags: usize) usize {...@@ -749,7 +1185,7 @@ pub fn epoll_create1(flags: usize) usize {
749 return syscall1(SYS_epoll_create1, flags);1185 return syscall1(SYS_epoll_create1, flags);
750}1186}
7511187
752pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) usize {1188pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: &epoll_event) usize {
753 return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));1189 return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));
754}1190}
7551191
...@@ -774,9 +1210,126 @@ pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_va...@@ -774,9 +1210,126 @@ pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_va
774 return syscall4(SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value));1210 return syscall4(SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value));
775}1211}
7761212
777test "import linux test" {1213pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330;
778 // TODO lazy analysis should prevent this test from being compiled on windows, but1214pub const _LINUX_CAPABILITY_U32S_1 = 1;
779 // it is still compiled on windows1215
1216pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026;
1217pub const _LINUX_CAPABILITY_U32S_2 = 2;
1218
1219pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522;
1220pub const _LINUX_CAPABILITY_U32S_3 = 2;
1221
1222pub const VFS_CAP_REVISION_MASK = 0xFF000000;
1223pub const VFS_CAP_REVISION_SHIFT = 24;
1224pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK;
1225pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001;
1226
1227pub const VFS_CAP_REVISION_1 = 0x01000000;
1228pub const VFS_CAP_U32_1 = 1;
1229pub const XATTR_CAPS_SZ_1 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_1);
1230
1231pub const VFS_CAP_REVISION_2 = 0x02000000;
1232pub const VFS_CAP_U32_2 = 2;
1233pub const XATTR_CAPS_SZ_2 = @sizeOf(u32)*(1 + 2*VFS_CAP_U32_2);
1234
1235pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2;
1236pub const VFS_CAP_U32 = VFS_CAP_U32_2;
1237pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2;
1238
1239pub const vfs_cap_data = extern struct {
1240 //all of these are mandated as little endian
1241 //when on disk.
1242 const Data = struct {
1243 permitted: u32,
1244 inheritable: u32,
1245 };
1246
1247 magic_etc: u32,
1248 data: [VFS_CAP_U32]Data,
1249};
1250
1251
1252pub const CAP_CHOWN = 0;
1253pub const CAP_DAC_OVERRIDE = 1;
1254pub const CAP_DAC_READ_SEARCH = 2;
1255pub const CAP_FOWNER = 3;
1256pub const CAP_FSETID = 4;
1257pub const CAP_KILL = 5;
1258pub const CAP_SETGID = 6;
1259pub const CAP_SETUID = 7;
1260pub const CAP_SETPCAP = 8;
1261pub const CAP_LINUX_IMMUTABLE = 9;
1262pub const CAP_NET_BIND_SERVICE = 10;
1263pub const CAP_NET_BROADCAST = 11;
1264pub const CAP_NET_ADMIN = 12;
1265pub const CAP_NET_RAW = 13;
1266pub const CAP_IPC_LOCK = 14;
1267pub const CAP_IPC_OWNER = 15;
1268pub const CAP_SYS_MODULE = 16;
1269pub const CAP_SYS_RAWIO = 17;
1270pub const CAP_SYS_CHROOT = 18;
1271pub const CAP_SYS_PTRACE = 19;
1272pub const CAP_SYS_PACCT = 20;
1273pub const CAP_SYS_ADMIN = 21;
1274pub const CAP_SYS_BOOT = 22;
1275pub const CAP_SYS_NICE = 23;
1276pub const CAP_SYS_RESOURCE = 24;
1277pub const CAP_SYS_TIME = 25;
1278pub const CAP_SYS_TTY_CONFIG = 26;
1279pub const CAP_MKNOD = 27;
1280pub const CAP_LEASE = 28;
1281pub const CAP_AUDIT_WRITE = 29;
1282pub const CAP_AUDIT_CONTROL = 30;
1283pub const CAP_SETFCAP = 31;
1284pub const CAP_MAC_OVERRIDE = 32;
1285pub const CAP_MAC_ADMIN = 33;
1286pub const CAP_SYSLOG = 34;
1287pub const CAP_WAKE_ALARM = 35;
1288pub const CAP_BLOCK_SUSPEND = 36;
1289pub const CAP_AUDIT_READ = 37;
1290pub const CAP_LAST_CAP = CAP_AUDIT_READ;
1291
1292pub fn cap_valid(u8: x) bool {
1293 return x >= 0 and x <= CAP_LAST_CAP;
1294}
1295
1296pub fn CAP_TO_MASK(cap: u8) u32 {
1297 return u32(1) << u5(cap & 31);
1298}
1299
1300pub fn CAP_TO_INDEX(cap: u8) u8 {
1301 return cap >> 5;
1302}
1303
1304pub const cap_t = extern struct {
1305 hdrp: &cap_user_header_t,
1306 datap: &cap_user_data_t,
1307};
1308
1309pub const cap_user_header_t = extern struct {
1310 version: u32,
1311 pid: usize,
1312};
1313
1314pub const cap_user_data_t = extern struct {
1315 effective: u32,
1316 permitted: u32,
1317 inheritable: u32,
1318};
1319
1320pub fn unshare(flags: usize) usize {
1321 return syscall1(SYS_unshare, usize(flags));
1322}
1323
1324pub fn capget(hdrp: &cap_user_header_t, datap: &cap_user_data_t) usize {
1325 return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap));
1326}
1327
1328pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize {
1329 return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
1330}
1331
1332test "import" {
780 if (builtin.os == builtin.Os.linux) {1333 if (builtin.os == builtin.Os.linux) {
781 _ = @import("test.zig");1334 _ = @import("test.zig");
782 }1335 }
std/os/linux/test.zig+1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("../../index.zig");1const std = @import("../../index.zig");
2const builtin = @import("builtin");
2const linux = std.os.linux;3const linux = std.os.linux;
3const assert = std.debug.assert;4const assert = std.debug.assert;
45
std/os/linux/vdso.zig created+89
...@@ -0,0 +1,89 @@
1const std = @import("../../index.zig");
2const elf = std.elf;
3const linux = std.os.linux;
4const cstr = std.cstr;
5const mem = std.mem;
6
7pub fn lookup(vername: []const u8, name: []const u8) usize {
8 const vdso_addr = std.os.linux_aux_raw[std.elf.AT_SYSINFO_EHDR];
9 if (vdso_addr == 0) return 0;
10
11 const eh = @intToPtr(&elf.Ehdr, vdso_addr);
12 var ph_addr: usize = vdso_addr + eh.e_phoff;
13 const ph = @intToPtr(&elf.Phdr, ph_addr);
14
15 var maybe_dynv: ?&usize = null;
16 var base: usize = @maxValue(usize);
17 {
18 var i: usize = 0;
19 while (i < eh.e_phnum) : ({i += 1; ph_addr += eh.e_phentsize;}) {
20 const this_ph = @intToPtr(&elf.Phdr, ph_addr);
21 switch (this_ph.p_type) {
22 elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr,
23 elf.PT_DYNAMIC => maybe_dynv = @intToPtr(&usize, vdso_addr + this_ph.p_offset),
24 else => {},
25 }
26 }
27 }
28 const dynv = maybe_dynv ?? return 0;
29 if (base == @maxValue(usize)) return 0;
30
31 var maybe_strings: ?&u8 = null;
32 var maybe_syms: ?&elf.Sym = null;
33 var maybe_hashtab: ?&linux.Elf_Symndx = null;
34 var maybe_versym: ?&u16 = null;
35 var maybe_verdef: ?&elf.Verdef = null;
36
37 {
38 var i: usize = 0;
39 while (dynv[i] != 0) : (i += 2) {
40 const p = base + dynv[i + 1];
41 switch (dynv[i]) {
42 elf.DT_STRTAB => maybe_strings = @intToPtr(&u8, p),
43 elf.DT_SYMTAB => maybe_syms = @intToPtr(&elf.Sym, p),
44 elf.DT_HASH => maybe_hashtab = @intToPtr(&linux.Elf_Symndx, p),
45 elf.DT_VERSYM => maybe_versym = @intToPtr(&u16, p),
46 elf.DT_VERDEF => maybe_verdef = @intToPtr(&elf.Verdef, p),
47 else => {},
48 }
49 }
50 }
51
52 const strings = maybe_strings ?? return 0;
53 const syms = maybe_syms ?? return 0;
54 const hashtab = maybe_hashtab ?? return 0;
55 if (maybe_verdef == null) maybe_versym = null;
56
57
58 const OK_TYPES = (1<<elf.STT_NOTYPE | 1<<elf.STT_OBJECT | 1<<elf.STT_FUNC | 1<<elf.STT_COMMON);
59 const OK_BINDS = (1<<elf.STB_GLOBAL | 1<<elf.STB_WEAK | 1<<elf.STB_GNU_UNIQUE);
60
61 var i: usize = 0;
62 while (i < hashtab[1]) : (i += 1) {
63 if (0==(u32(1)<<u5(syms[i].st_info&0xf) & OK_TYPES)) continue;
64 if (0==(u32(1)<<u5(syms[i].st_info>>4) & OK_BINDS)) continue;
65 if (0==syms[i].st_shndx) continue;
66 if (!mem.eql(u8, name, cstr.toSliceConst(&strings[syms[i].st_name]))) continue;
67 if (maybe_versym) |versym| {
68 if (!checkver(??maybe_verdef, versym[i], vername, strings))
69 continue;
70 }
71 return base + syms[i].st_value;
72 }
73
74 return 0;
75}
76
77fn checkver(def_arg: &elf.Verdef, vsym_arg: i32, vername: []const u8, strings: &u8) bool {
78 var def = def_arg;
79 const vsym = @bitCast(u32, vsym_arg) & 0x7fff;
80 while (true) {
81 if (0==(def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym)
82 break;
83 if (def.vd_next == 0)
84 return false;
85 def = @intToPtr(&elf.Verdef, @ptrToInt(def) + def.vd_next);
86 }
87 const aux = @intToPtr(&elf.Verdaux, @ptrToInt(def ) + def.vd_aux);
88 return mem.eql(u8, vername, cstr.toSliceConst(&strings[aux.vda_name]));
89}
std/os/linux/x86_64.zig+21
...@@ -371,6 +371,13 @@ pub const F_GETOWN_EX = 16;...@@ -371,6 +371,13 @@ pub const F_GETOWN_EX = 16;
371371
372pub const F_GETOWNER_UIDS = 17;372pub const F_GETOWNER_UIDS = 17;
373373
374
375pub const VDSO_USEFUL = true;
376pub const VDSO_CGT_SYM = "__vdso_clock_gettime";
377pub const VDSO_CGT_VER = "LINUX_2.6";
378pub const VDSO_GETCPU_SYM = "__vdso_getcpu";
379pub const VDSO_GETCPU_VER = "LINUX_2.6";
380
374pub fn syscall0(number: usize) usize {381pub fn syscall0(number: usize) usize {
375 return asm volatile ("syscall"382 return asm volatile ("syscall"
376 : [ret] "={rax}" (-> usize)383 : [ret] "={rax}" (-> usize)
...@@ -443,6 +450,9 @@ pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usiz...@@ -443,6 +450,9 @@ pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usiz
443 : "rcx", "r11");450 : "rcx", "r11");
444}451}
445452
453/// This matches the libc clone function.
454pub extern fn clone(func: extern fn(arg: usize) u8, stack: usize, flags: usize, arg: usize, ptid: &i32, tls: usize, ctid: &i32) usize;
455
446pub nakedcc fn restore_rt() void {456pub nakedcc fn restore_rt() void {
447 return asm volatile ("syscall"457 return asm volatile ("syscall"
448 :458 :
...@@ -489,6 +499,16 @@ pub const timespec = extern struct {...@@ -489,6 +499,16 @@ pub const timespec = extern struct {
489 tv_nsec: isize,499 tv_nsec: isize,
490};500};
491501
502pub const timeval = extern struct {
503 tv_sec: isize,
504 tv_usec: isize,
505};
506
507pub const timezone = extern struct {
508 tz_minuteswest: i32,
509 tz_dsttime: i32,
510};
511
492pub const dirent = extern struct {512pub const dirent = extern struct {
493 d_ino: usize,513 d_ino: usize,
494 d_off: usize,514 d_off: usize,
...@@ -496,3 +516,4 @@ pub const dirent = extern struct {...@@ -496,3 +516,4 @@ pub const dirent = extern struct {
496 d_name: u8, // field address is the address of first byte of name516 d_name: u8, // field address is the address of first byte of name
497};517};
498518
519pub const Elf_Symndx = u32;
std/os/test.zig+44
...@@ -6,6 +6,8 @@ const io = std.io;...@@ -6,6 +6,8 @@ const io = std.io;
6const a = std.debug.global_allocator;6const a = std.debug.global_allocator;
77
8const builtin = @import("builtin");8const builtin = @import("builtin");
9const AtomicRmwOp = builtin.AtomicRmwOp;
10const AtomicOrder = builtin.AtomicOrder;
911
10test "makePath, put some files in it, deleteTree" {12test "makePath, put some files in it, deleteTree" {
11 if (builtin.os == builtin.Os.windows) {13 if (builtin.os == builtin.Os.windows) {
...@@ -23,3 +25,45 @@ test "makePath, put some files in it, deleteTree" {...@@ -23,3 +25,45 @@ test "makePath, put some files in it, deleteTree" {
23 assert(err == error.PathNotFound);25 assert(err == error.PathNotFound);
24 }26 }
25}27}
28
29test "access file" {
30 if (builtin.os == builtin.Os.windows) {
31 return;
32 }
33
34 try os.makePath(a, "os_test_tmp");
35 if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
36 unreachable;
37 } else |err| {
38 assert(err == error.NotFound);
39 }
40
41 try io.writeFile(a, "os_test_tmp/file.txt", "");
42 assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true);
43 try os.deleteTree(a, "os_test_tmp");
44}
45
46test "spawn threads" {
47 var shared_ctx: i32 = 1;
48
49 const thread1 = try std.os.spawnThread({}, start1);
50 const thread2 = try std.os.spawnThread(&shared_ctx, start2);
51 const thread3 = try std.os.spawnThread(&shared_ctx, start2);
52 const thread4 = try std.os.spawnThread(&shared_ctx, start2);
53
54 thread1.wait();
55 thread2.wait();
56 thread3.wait();
57 thread4.wait();
58
59 assert(shared_ctx == 4);
60}
61
62fn start1(ctx: void) u8 {
63 return 0;
64}
65
66fn start2(ctx: &i32) u8 {
67 _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
68 return 0;
69}
std/os/time.zig created+288
...@@ -0,0 +1,288 @@
1const std = @import("../index.zig");
2const builtin = @import("builtin");
3const Os = builtin.Os;
4const debug = std.debug;
5
6const windows = std.os.windows;
7const linux = std.os.linux;
8const darwin = std.os.darwin;
9const posix = std.os.posix;
10
11pub const epoch = @import("epoch.zig");
12
13/// Sleep for the specified duration
14pub fn sleep(seconds: usize, nanoseconds: usize) void {
15 switch (builtin.os) {
16 Os.linux, Os.macosx, Os.ios => {
17 posixSleep(u63(seconds), u63(nanoseconds));
18 },
19 Os.windows => {
20 const ns_per_ms = ns_per_s / ms_per_s;
21 const milliseconds = seconds * ms_per_s + nanoseconds / ns_per_ms;
22 windows.Sleep(windows.DWORD(milliseconds));
23 },
24 else => @compileError("Unsupported OS"),
25 }
26}
27
28const u63 = @IntType(false, 63);
29pub fn posixSleep(seconds: u63, nanoseconds: u63) void {
30 var req = posix.timespec {
31 .tv_sec = seconds,
32 .tv_nsec = nanoseconds,
33 };
34 var rem: posix.timespec = undefined;
35 while (true) {
36 const ret_val = posix.nanosleep(&req, &rem);
37 const err = posix.getErrno(ret_val);
38 if (err == 0) return;
39 switch (err) {
40 posix.EFAULT => unreachable,
41 posix.EINVAL => {
42 // Sometimes Darwin returns EINVAL for no reason.
43 // We treat it as a spurious wakeup.
44 return;
45 },
46 posix.EINTR => {
47 req = rem;
48 continue;
49 },
50 else => return,
51 }
52 }
53}
54
55/// Get the posix timestamp, UTC, in seconds
56pub fn timestamp() u64 {
57 return @divFloor(milliTimestamp(), ms_per_s);
58}
59
60/// Get the posix timestamp, UTC, in milliseconds
61pub const milliTimestamp = switch (builtin.os) {
62 Os.windows => milliTimestampWindows,
63 Os.linux => milliTimestampPosix,
64 Os.macosx, Os.ios => milliTimestampDarwin,
65 else => @compileError("Unsupported OS"),
66};
67
68fn milliTimestampWindows() u64 {
69 //FileTime has a granularity of 100 nanoseconds
70 // and uses the NTFS/Windows epoch
71 var ft: i64 = undefined;
72 windows.GetSystemTimeAsFileTime(&ft);
73 const hns_per_ms = (ns_per_s / 100) / ms_per_s;
74 const epoch_adj = epoch.windows * ms_per_s;
75 return u64(@divFloor(ft, hns_per_ms) + epoch_adj);
76}
77
78fn milliTimestampDarwin() u64 {
79 //Sources suggest MacOS 10.12 has support for
80 // posix clock_gettime.
81 var tv: darwin.timeval = undefined;
82 var err = darwin.gettimeofday(&tv, null);
83 debug.assert(err == 0);
84 const sec_ms = u64(tv.tv_sec) * ms_per_s;
85 const usec_ms = @divFloor(u64(tv.tv_usec), us_per_s / ms_per_s);
86 return u64(sec_ms) + u64(usec_ms);
87}
88
89fn milliTimestampPosix() u64 {
90 //From what I can tell there's no reason clock_gettime
91 // should ever fail for us with CLOCK_REALTIME,
92 // seccomp aside.
93 var ts: posix.timespec = undefined;
94 const err = posix.clock_gettime(posix.CLOCK_REALTIME, &ts);
95 debug.assert(err == 0);
96 const sec_ms = u64(ts.tv_sec) * ms_per_s;
97 const nsec_ms = @divFloor(u64(ts.tv_nsec), ns_per_s / ms_per_s);
98 return sec_ms + nsec_ms;
99}
100
101/// Divisions of a second
102pub const ns_per_s = 1000000000;
103pub const us_per_s = 1000000;
104pub const ms_per_s = 1000;
105pub const cs_per_s = 100;
106
107/// Common time divisions
108pub const s_per_min = 60;
109pub const s_per_hour = s_per_min * 60;
110pub const s_per_day = s_per_hour * 24;
111pub const s_per_week = s_per_day * 7;
112
113
114/// A monotonic high-performance timer.
115/// Timer.start() must be called to initialize the struct, which captures
116/// the counter frequency on windows and darwin, records the resolution,
117/// and gives the user an oportunity to check for the existnece of
118/// monotonic clocks without forcing them to check for error on each read.
119/// .resolution is in nanoseconds on all platforms but .start_time's meaning
120/// depends on the OS. On Windows and Darwin it is a hardware counter
121/// value that requires calculation to convert to a meaninful unit.
122pub const Timer = struct {
123
124 //if we used resolution's value when performing the
125 // performance counter calc on windows/darwin, it would
126 // be less precise
127 frequency: switch (builtin.os) {
128 Os.windows => u64,
129 Os.macosx, Os.ios => darwin.mach_timebase_info_data,
130 else => void,
131 },
132 resolution: u64,
133 start_time: u64,
134
135
136 //At some point we may change our minds on RAW, but for now we're
137 // sticking with posix standard MONOTONIC. For more information, see:
138 // https://github.com/zig-lang/zig/pull/933
139 //
140 //const monotonic_clock_id = switch(builtin.os) {
141 // Os.linux => linux.CLOCK_MONOTONIC_RAW,
142 // else => posix.CLOCK_MONOTONIC,
143 //};
144 const monotonic_clock_id = posix.CLOCK_MONOTONIC;
145
146
147 /// Initialize the timer structure.
148 //This gives us an oportunity to grab the counter frequency in windows.
149 //On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000.
150 //On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not
151 // supported, or if the timespec pointer is out of bounds, which should be
152 // impossible here barring cosmic rays or other such occurances of
153 // incredibly bad luck.
154 //On Darwin: This cannot fail, as far as I am able to tell.
155 const TimerError = error{TimerUnsupported, Unexpected};
156 pub fn start() TimerError!Timer {
157 var self: Timer = undefined;
158
159 switch (builtin.os) {
160 Os.windows => {
161 var freq: i64 = undefined;
162 var err = windows.QueryPerformanceFrequency(&freq);
163 if (err == windows.FALSE) return error.TimerUnsupported;
164 self.frequency = u64(freq);
165 self.resolution = @divFloor(ns_per_s, self.frequency);
166
167 var start_time: i64 = undefined;
168 err = windows.QueryPerformanceCounter(&start_time);
169 debug.assert(err != windows.FALSE);
170 self.start_time = u64(start_time);
171 },
172 Os.linux => {
173 //On Linux, seccomp can do arbitrary things to our ability to call
174 // syscalls, including return any errno value it wants and
175 // inconsistently throwing errors. Since we can't account for
176 // abuses of seccomp in a reasonable way, we'll assume that if
177 // seccomp is going to block us it will at least do so consistently
178 var ts: posix.timespec = undefined;
179 var result = posix.clock_getres(monotonic_clock_id, &ts);
180 var errno = posix.getErrno(result);
181 switch (errno) {
182 0 => {},
183 posix.EINVAL => return error.TimerUnsupported,
184 else => return std.os.unexpectedErrorPosix(errno),
185 }
186 self.resolution = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
187
188 result = posix.clock_gettime(monotonic_clock_id, &ts);
189 errno = posix.getErrno(result);
190 if (errno != 0) return std.os.unexpectedErrorPosix(errno);
191 self.start_time = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
192 },
193 Os.macosx, Os.ios => {
194 darwin.mach_timebase_info(&self.frequency);
195 self.resolution = @divFloor(self.frequency.numer, self.frequency.denom);
196 self.start_time = darwin.mach_absolute_time();
197 },
198 else => @compileError("Unsupported OS"),
199 }
200 return self;
201 }
202
203 /// Reads the timer value since start or the last reset in nanoseconds
204 pub fn read(self: &Timer) u64 {
205 var clock = clockNative() - self.start_time;
206 return switch (builtin.os) {
207 Os.windows => @divFloor(clock * ns_per_s, self.frequency),
208 Os.linux => clock,
209 Os.macosx, Os.ios => @divFloor(clock * self.frequency.numer, self.frequency.denom),
210 else => @compileError("Unsupported OS"),
211 };
212 }
213
214 /// Resets the timer value to 0/now.
215 pub fn reset(self: &Timer) void
216 {
217 self.start_time = clockNative();
218 }
219
220 /// Returns the current value of the timer in nanoseconds, then resets it
221 pub fn lap(self: &Timer) u64 {
222 var now = clockNative();
223 var lap_time = self.read();
224 self.start_time = now;
225 return lap_time;
226 }
227
228
229 const clockNative = switch (builtin.os) {
230 Os.windows => clockWindows,
231 Os.linux => clockLinux,
232 Os.macosx, Os.ios => clockDarwin,
233 else => @compileError("Unsupported OS"),
234 };
235
236 fn clockWindows() u64 {
237 var result: i64 = undefined;
238 var err = windows.QueryPerformanceCounter(&result);
239 debug.assert(err != windows.FALSE);
240 return u64(result);
241 }
242
243 fn clockDarwin() u64 {
244 return darwin.mach_absolute_time();
245 }
246
247 fn clockLinux() u64 {
248 var ts: posix.timespec = undefined;
249 var result = posix.clock_gettime(monotonic_clock_id, &ts);
250 debug.assert(posix.getErrno(result) == 0);
251 return u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
252 }
253};
254
255
256
257
258
259test "os.time.sleep" {
260 sleep(0, 1);
261}
262
263test "os.time.timestamp" {
264 const ns_per_ms = (ns_per_s / ms_per_s);
265 const margin = 50;
266
267 const time_0 = milliTimestamp();
268 sleep(0, ns_per_ms);
269 const time_1 = milliTimestamp();
270 const interval = time_1 - time_0;
271 debug.assert(interval > 0 and interval < margin);
272}
273
274test "os.time.Timer" {
275 const ns_per_ms = (ns_per_s / ms_per_s);
276 const margin = ns_per_ms * 50;
277
278 var timer = try Timer.start();
279 sleep(0, 10 * ns_per_ms);
280 const time_0 = timer.read();
281 debug.assert(time_0 > 0 and time_0 < margin);
282
283 const time_1 = timer.lap();
284 debug.assert(time_1 >= time_0);
285
286 timer.reset();
287 debug.assert(timer.read() < time_1);
288}
std/os/windows/index.zig+19
...@@ -28,6 +28,9 @@ pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lp...@@ -28,6 +28,9 @@ pub extern "kernel32" stdcallcc fn CreateProcessA(lpApplicationName: ?LPCSTR, lp
28pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(lpSymlinkFileName: LPCSTR, lpTargetFileName: LPCSTR,28pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(lpSymlinkFileName: LPCSTR, lpTargetFileName: LPCSTR,
29 dwFlags: DWORD) BOOLEAN;29 dwFlags: DWORD) BOOLEAN;
3030
31
32pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
33
31pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;34pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
3235
33pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;36pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
...@@ -61,6 +64,8 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz...@@ -61,6 +64,8 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz
6164
62pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;65pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
6366
67pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?&FILETIME) void;
68
64pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;69pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
65pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;70pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
66pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void, dwBytes: SIZE_T) ?&c_void;71pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void, dwBytes: SIZE_T) ?&c_void;
...@@ -77,6 +82,12 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem...@@ -77,6 +82,12 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
7782
78pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR,83pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR,
79 dwFlags: DWORD) BOOL;84 dwFlags: DWORD) BOOL;
85
86pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: &LARGE_INTEGER) BOOL;
87
88pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: &LARGE_INTEGER) BOOL;
89
90pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL;
8091
81pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void,92pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void,
82 in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,93 in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,
...@@ -137,6 +148,7 @@ pub const UNICODE = false;...@@ -137,6 +148,7 @@ pub const UNICODE = false;
137pub const WCHAR = u16;148pub const WCHAR = u16;
138pub const WORD = u16;149pub const WORD = u16;
139pub const LARGE_INTEGER = i64;150pub const LARGE_INTEGER = i64;
151pub const FILETIME = i64;
140152
141pub const TRUE = 1;153pub const TRUE = 1;
142pub const FALSE = 0;154pub const FALSE = 0;
...@@ -308,3 +320,10 @@ pub const FILE_END = 2;...@@ -308,3 +320,10 @@ pub const FILE_END = 2;
308pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;320pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
309pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;321pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
310pub const HEAP_NO_SERIALIZE = 0x00000001;322pub const HEAP_NO_SERIALIZE = 0x00000001;
323
324pub const PTHREAD_START_ROUTINE = extern fn(LPVOID) DWORD;
325pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
326
327test "import" {
328 _ = @import("util.zig");
329}
std/rand/index.zig+19-5
...@@ -19,6 +19,7 @@ const builtin = @import("builtin");...@@ -19,6 +19,7 @@ const builtin = @import("builtin");
19const assert = std.debug.assert;19const assert = std.debug.assert;
20const mem = std.mem;20const mem = std.mem;
21const math = std.math;21const math = std.math;
22const ziggurat = @import("ziggurat.zig");
2223
23// When you need fast unbiased random numbers24// When you need fast unbiased random numbers
24pub const DefaultPrng = Xoroshiro128;25pub const DefaultPrng = Xoroshiro128;
...@@ -109,15 +110,28 @@ pub const Random = struct {...@@ -109,15 +110,28 @@ pub const Random = struct {
109 }110 }
110 }111 }
111112
112 /// Return a floating point value normally distributed in the range [0, 1].113 /// Return a floating point value normally distributed with mean = 0, stddev = 1.
114 ///
115 /// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean.
113 pub fn floatNorm(r: &Random, comptime T: type) T {116 pub fn floatNorm(r: &Random, comptime T: type) T {
114 // TODO(tiehuis): See https://www.doornik.com/research/ziggurat.pdf117 const value = ziggurat.next_f64(r, ziggurat.NormDist);
115 @compileError("floatNorm is unimplemented");118 switch (T) {
119 f32 => return f32(value),
120 f64 => return value,
121 else => @compileError("unknown floating point type"),
122 }
116 }123 }
117124
118 /// Return a exponentially distributed float between (0, @maxValue(f64))125 /// Return an exponentially distributed float with a rate parameter of 1.
126 ///
127 /// To use a different rate parameter, use: floatExp(...) / desiredRate.
119 pub fn floatExp(r: &Random, comptime T: type) T {128 pub fn floatExp(r: &Random, comptime T: type) T {
120 @compileError("floatExp is unimplemented");129 const value = ziggurat.next_f64(r, ziggurat.ExpDist);
130 switch (T) {
131 f32 => return f32(value),
132 f64 => return value,
133 else => @compileError("unknown floating point type"),
134 }
121 }135 }
122136
123 /// Shuffle a slice into a random order.137 /// Shuffle a slice into a random order.
std/rand/ziggurat.zig created+146
...@@ -0,0 +1,146 @@
1// Implements ZIGNOR [1].
2//
3// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to Generate Normal Random Samples*]
4// (https://www.doornik.com/research/ziggurat.pdf). Nuffield College, Oxford.
5//
6// rust/rand used as a reference;
7//
8// NOTE: This seems interesting but reference code is a bit hard to grok:
9// https://sbarral.github.io/etf.
10
11const std = @import("../index.zig");
12const math = std.math;
13const Random = std.rand.Random;
14
15pub fn next_f64(random: &Random, comptime tables: &const ZigTable) f64 {
16 while (true) {
17 // We manually construct a float from parts as we can avoid an extra random lookup here by
18 // using the unused exponent for the lookup table entry.
19 const bits = random.scalar(u64);
20 const i = usize(bits & 0xff);
21
22 const u = blk: {
23 if (tables.is_symmetric) {
24 // Generate a value in the range [2, 4) and scale into [-1, 1)
25 const repr = ((0x3ff + 1) << 52) | (bits >> 12);
26 break :blk @bitCast(f64, repr) - 3.0;
27 } else {
28 // Generate a value in the range [1, 2) and scale into (0, 1)
29 const repr = (0x3ff << 52) | (bits >> 12);
30 break :blk @bitCast(f64, repr) - (1.0 - math.f64_epsilon / 2.0);
31 }
32 };
33
34 const x = u * tables.x[i];
35 const test_x = if (tables.is_symmetric) math.fabs(x) else x;
36
37 // equivalent to |u| < tables.x[i+1] / tables.x[i] (or u < tables.x[i+1] / tables.x[i])
38 if (test_x < tables.x[i + 1]) {
39 return x;
40 }
41
42 if (i == 0) {
43 return tables.zero_case(random, u);
44 }
45
46 // equivalent to f1 + DRanU() * (f0 - f1) < 1
47 if (tables.f[i + 1] + (tables.f[i] - tables.f[i + 1]) * random.float(f64) < tables.pdf(x)) {
48 return x;
49 }
50 }
51}
52
53pub const ZigTable = struct {
54 r: f64,
55 x: [257]f64,
56 f: [257]f64,
57
58 // probability density function used as a fallback
59 pdf: fn(f64) f64,
60 // whether the distribution is symmetric
61 is_symmetric: bool,
62 // fallback calculation in the case we are in the 0 block
63 zero_case: fn(&Random, f64) f64,
64};
65
66// zigNorInit
67fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64,
68 comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable {
69 var tables: ZigTable = undefined;
70
71 tables.is_symmetric = is_symmetric;
72 tables.r = r;
73 tables.pdf = f;
74 tables.zero_case = zero_case;
75
76 tables.x[0] = v / f(r);
77 tables.x[1] = r;
78
79 for (tables.x[2..256]) |*entry, i| {
80 const last = tables.x[2 + i - 1];
81 *entry = f_inv(v / last + f(last));
82 }
83 tables.x[256] = 0;
84
85 for (tables.f[0..]) |*entry, i| {
86 *entry = f(tables.x[i]);
87 }
88
89 return tables;
90}
91
92// N(0, 1)
93pub const NormDist = blk: {
94 @setEvalBranchQuota(30000);
95 break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case);
96};
97
98const norm_r = 3.6541528853610088;
99const norm_v = 0.00492867323399;
100
101fn norm_f(x: f64) f64 { return math.exp(-x * x / 2.0); }
102fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); }
103fn norm_zero_case(random: &Random, u: f64) f64 {
104 var x: f64 = 1;
105 var y: f64 = 0;
106
107 while (-2.0 * y < x * x) {
108 x = math.ln(random.float(f64)) / norm_r;
109 y = math.ln(random.float(f64));
110 }
111
112 if (u < 0) {
113 return x - norm_r;
114 } else {
115 return norm_r - x;
116 }
117}
118
119test "ziggurant normal dist sanity" {
120 var prng = std.rand.DefaultPrng.init(0);
121 var i: usize = 0;
122 while (i < 1000) : (i += 1) {
123 _ = prng.random.floatNorm(f64);
124 }
125}
126
127// Exp(1)
128pub const ExpDist = blk: {
129 @setEvalBranchQuota(30000);
130 break :blk ZigTableGen(false, exp_r, exp_v, exp_f, exp_f_inv, exp_zero_case);
131};
132
133const exp_r = 7.69711747013104972;
134const exp_v = 0.0039496598225815571993;
135
136fn exp_f(x: f64) f64 { return math.exp(-x); }
137fn exp_f_inv(y: f64) f64 { return -math.ln(y); }
138fn exp_zero_case(random: &Random, _: f64) f64 { return exp_r - math.ln(random.float(f64)); }
139
140test "ziggurant exp dist sanity" {
141 var prng = std.rand.DefaultPrng.init(0);
142 var i: usize = 0;
143 while (i < 1000) : (i += 1) {
144 _ = prng.random.floatExp(f64);
145 }
146}
std/segmented_list.zig created+379
...@@ -0,0 +1,379 @@
1const std = @import("index.zig");
2const assert = std.debug.assert;
3const Allocator = std.mem.Allocator;
4
5// Imagine that `fn at(self: &Self, index: usize) &T` is a customer asking for a box
6// from a warehouse, based on a flat array, boxes ordered from 0 to N - 1.
7// But the warehouse actually stores boxes in shelves of increasing powers of 2 sizes.
8// So when the customer requests a box index, we have to translate it to shelf index
9// and box index within that shelf. Illustration:
10//
11// customer indexes:
12// shelf 0: 0
13// shelf 1: 1 2
14// shelf 2: 3 4 5 6
15// shelf 3: 7 8 9 10 11 12 13 14
16// shelf 4: 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
17// shelf 5: 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
18// ...
19//
20// warehouse indexes:
21// shelf 0: 0
22// shelf 1: 0 1
23// shelf 2: 0 1 2 3
24// shelf 3: 0 1 2 3 4 5 6 7
25// shelf 4: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
26// shelf 5: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
27// ...
28//
29// With this arrangement, here are the equations to get the shelf index and
30// box index based on customer box index:
31//
32// shelf_index = floor(log2(customer_index + 1))
33// shelf_count = ceil(log2(box_count + 1))
34// box_index = customer_index + 1 - 2 ** shelf
35// shelf_size = 2 ** shelf_index
36//
37// Now we complicate it a little bit further by adding a preallocated shelf, which must be
38// a power of 2:
39// prealloc=4
40//
41// customer indexes:
42// prealloc: 0 1 2 3
43// shelf 0: 4 5 6 7 8 9 10 11
44// shelf 1: 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
45// shelf 2: 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
46// ...
47//
48// warehouse indexes:
49// prealloc: 0 1 2 3
50// shelf 0: 0 1 2 3 4 5 6 7
51// shelf 1: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
52// shelf 2: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
53// ...
54//
55// Now the equations are:
56//
57// shelf_index = floor(log2(customer_index + prealloc)) - log2(prealloc) - 1
58// shelf_count = ceil(log2(box_count + prealloc)) - log2(prealloc) - 1
59// box_index = customer_index + prealloc - 2 ** (log2(prealloc) + 1 + shelf)
60// shelf_size = prealloc * 2 ** (shelf_index + 1)
61
62/// This is a stack data structure where pointers to indexes have the same lifetime as the data structure
63/// itself, unlike ArrayList where push() invalidates all existing element pointers.
64/// The tradeoff is that elements are not guaranteed to be contiguous. For that, use ArrayList.
65/// Note however that most elements are contiguous, making this data structure cache-friendly.
66///
67/// Because it never has to copy elements from an old location to a new location, it does not require
68/// its elements to be copyable, and it avoids wasting memory when backed by an ArenaAllocator.
69/// Note that the push() and pop() convenience methods perform a copy, but you can instead use
70/// addOne(), at(), setCapacity(), and shrinkCapacity() to avoid copying items.
71///
72/// This data structure has O(1) push and O(1) pop.
73///
74/// It supports preallocated elements, making it especially well suited when the expected maximum
75/// size is small. `prealloc_item_count` must be 0, or a power of 2.
76pub fn SegmentedList(comptime T: type, comptime prealloc_item_count: usize) type {
77 return struct {
78 const Self = this;
79 const prealloc_exp = blk: {
80 // we don't use the prealloc_exp constant when prealloc_item_count is 0.
81 assert(prealloc_item_count != 0);
82
83 const value = std.math.log2_int(usize, prealloc_item_count);
84 assert((1 << value) == prealloc_item_count); // prealloc_item_count must be a power of 2
85 break :blk @typeOf(1)(value);
86 };
87 const ShelfIndex = std.math.Log2Int(usize);
88
89 prealloc_segment: [prealloc_item_count]T,
90 dynamic_segments: []&T,
91 allocator: &Allocator,
92 len: usize,
93
94 pub const prealloc_count = prealloc_item_count;
95
96 /// Deinitialize with `deinit`
97 pub fn init(allocator: &Allocator) Self {
98 return Self {
99 .allocator = allocator,
100 .len = 0,
101 .prealloc_segment = undefined,
102 .dynamic_segments = []&T{},
103 };
104 }
105
106 pub fn deinit(self: &Self) void {
107 self.freeShelves(ShelfIndex(self.dynamic_segments.len), 0);
108 self.allocator.free(self.dynamic_segments);
109 *self = undefined;
110 }
111
112 pub fn at(self: &Self, i: usize) &T {
113 assert(i < self.len);
114 return self.uncheckedAt(i);
115 }
116
117 pub fn count(self: &const Self) usize {
118 return self.len;
119 }
120
121 pub fn push(self: &Self, item: &const T) !void {
122 const new_item_ptr = try self.addOne();
123 *new_item_ptr = *item;
124 }
125
126 pub fn pushMany(self: &Self, items: []const T) !void {
127 for (items) |item| {
128 try self.push(item);
129 }
130 }
131
132 pub fn pop(self: &Self) ?T {
133 if (self.len == 0)
134 return null;
135
136 const index = self.len - 1;
137 const result = *self.uncheckedAt(index);
138 self.len = index;
139 return result;
140 }
141
142 pub fn addOne(self: &Self) !&T {
143 const new_length = self.len + 1;
144 try self.growCapacity(new_length);
145 const result = self.uncheckedAt(self.len);
146 self.len = new_length;
147 return result;
148 }
149
150 /// Grows or shrinks capacity to match usage.
151 pub fn setCapacity(self: &Self, new_capacity: usize) !void {
152 if (new_capacity <= usize(1) << (prealloc_exp + self.dynamic_segments.len)) {
153 return self.shrinkCapacity(new_capacity);
154 } else {
155 return self.growCapacity(new_capacity);
156 }
157 }
158
159 /// Only grows capacity, or retains current capacity
160 pub fn growCapacity(self: &Self, new_capacity: usize) !void {
161 const new_cap_shelf_count = shelfCount(new_capacity);
162 const old_shelf_count = ShelfIndex(self.dynamic_segments.len);
163 if (new_cap_shelf_count > old_shelf_count) {
164 self.dynamic_segments = try self.allocator.realloc(&T, self.dynamic_segments, new_cap_shelf_count);
165 var i = old_shelf_count;
166 errdefer {
167 self.freeShelves(i, old_shelf_count);
168 self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, old_shelf_count);
169 }
170 while (i < new_cap_shelf_count) : (i += 1) {
171 self.dynamic_segments[i] = (try self.allocator.alloc(T, shelfSize(i))).ptr;
172 }
173 }
174 }
175
176 /// Only shrinks capacity or retains current capacity
177 pub fn shrinkCapacity(self: &Self, new_capacity: usize) void {
178 if (new_capacity <= prealloc_item_count) {
179 const len = ShelfIndex(self.dynamic_segments.len);
180 self.freeShelves(len, 0);
181 self.allocator.free(self.dynamic_segments);
182 self.dynamic_segments = []&T{};
183 return;
184 }
185
186 const new_cap_shelf_count = shelfCount(new_capacity);
187 const old_shelf_count = ShelfIndex(self.dynamic_segments.len);
188 assert(new_cap_shelf_count <= old_shelf_count);
189 if (new_cap_shelf_count == old_shelf_count) {
190 return;
191 }
192
193 self.freeShelves(old_shelf_count, new_cap_shelf_count);
194 self.dynamic_segments = self.allocator.shrink(&T, self.dynamic_segments, new_cap_shelf_count);
195 }
196
197 pub fn uncheckedAt(self: &Self, index: usize) &T {
198 if (index < prealloc_item_count) {
199 return &self.prealloc_segment[index];
200 }
201 const shelf_index = shelfIndex(index);
202 const box_index = boxIndex(index, shelf_index);
203 return &self.dynamic_segments[shelf_index][box_index];
204 }
205
206 fn shelfCount(box_count: usize) ShelfIndex {
207 if (prealloc_item_count == 0) {
208 return std.math.log2_int_ceil(usize, box_count + 1);
209 }
210 return std.math.log2_int_ceil(usize, box_count + prealloc_item_count) - prealloc_exp - 1;
211 }
212
213 fn shelfSize(shelf_index: ShelfIndex) usize {
214 if (prealloc_item_count == 0) {
215 return usize(1) << shelf_index;
216 }
217 return usize(1) << (shelf_index + (prealloc_exp + 1));
218 }
219
220 fn shelfIndex(list_index: usize) ShelfIndex {
221 if (prealloc_item_count == 0) {
222 return std.math.log2_int(usize, list_index + 1);
223 }
224 return std.math.log2_int(usize, list_index + prealloc_item_count) - prealloc_exp - 1;
225 }
226
227 fn boxIndex(list_index: usize, shelf_index: ShelfIndex) usize {
228 if (prealloc_item_count == 0) {
229 return (list_index + 1) - (usize(1) << shelf_index);
230 }
231 return list_index + prealloc_item_count - (usize(1) << ((prealloc_exp + 1) + shelf_index));
232 }
233
234 fn freeShelves(self: &Self, from_count: ShelfIndex, to_count: ShelfIndex) void {
235 var i = from_count;
236 while (i != to_count) {
237 i -= 1;
238 self.allocator.free(self.dynamic_segments[i][0..shelfSize(i)]);
239 }
240 }
241
242 pub const Iterator = struct {
243 list: &Self,
244 index: usize,
245 box_index: usize,
246 shelf_index: ShelfIndex,
247 shelf_size: usize,
248
249 pub fn next(it: &Iterator) ?&T {
250 if (it.index >= it.list.len)
251 return null;
252 if (it.index < prealloc_item_count) {
253 const ptr = &it.list.prealloc_segment[it.index];
254 it.index += 1;
255 if (it.index == prealloc_item_count) {
256 it.box_index = 0;
257 it.shelf_index = 0;
258 it.shelf_size = prealloc_item_count * 2;
259 }
260 return ptr;
261 }
262
263 const ptr = &it.list.dynamic_segments[it.shelf_index][it.box_index];
264 it.index += 1;
265 it.box_index += 1;
266 if (it.box_index == it.shelf_size) {
267 it.shelf_index += 1;
268 it.box_index = 0;
269 it.shelf_size *= 2;
270 }
271 return ptr;
272 }
273
274 pub fn prev(it: &Iterator) ?&T {
275 if (it.index == 0)
276 return null;
277
278 it.index -= 1;
279 if (it.index < prealloc_item_count)
280 return &it.list.prealloc_segment[it.index];
281
282 if (it.box_index == 0) {
283 it.shelf_index -= 1;
284 it.shelf_size /= 2;
285 it.box_index = it.shelf_size - 1;
286 } else {
287 it.box_index -= 1;
288 }
289
290 return &it.list.dynamic_segments[it.shelf_index][it.box_index];
291 }
292
293 pub fn peek(it: &Iterator) ?&T {
294 if (it.index >= it.list.len)
295 return null;
296 if (it.index < prealloc_item_count)
297 return &it.list.prealloc_segment[it.index];
298
299 return &it.list.dynamic_segments[it.shelf_index][it.box_index];
300 }
301 };
302
303 pub fn iterator(self: &Self, start_index: usize) Iterator {
304 var it = Iterator {
305 .list = self,
306 .index = start_index,
307 .shelf_index = undefined,
308 .box_index = undefined,
309 .shelf_size = undefined,
310 };
311 if (start_index >= prealloc_item_count) {
312 it.shelf_index = shelfIndex(start_index);
313 it.box_index = boxIndex(start_index, it.shelf_index);
314 it.shelf_size = shelfSize(it.shelf_index);
315 }
316 return it;
317 }
318 };
319}
320
321test "std.SegmentedList" {
322 var da = std.heap.DirectAllocator.init();
323 defer da.deinit();
324 var a = &da.allocator;
325
326 try testSegmentedList(0, a);
327 try testSegmentedList(1, a);
328 try testSegmentedList(2, a);
329 try testSegmentedList(4, a);
330 try testSegmentedList(8, a);
331 try testSegmentedList(16, a);
332}
333
334fn testSegmentedList(comptime prealloc: usize, allocator: &Allocator) !void {
335 var list = SegmentedList(i32, prealloc).init(allocator);
336 defer list.deinit();
337
338 {var i: usize = 0; while (i < 100) : (i += 1) {
339 try list.push(i32(i + 1));
340 assert(list.len == i + 1);
341 }}
342
343 {var i: usize = 0; while (i < 100) : (i += 1) {
344 assert(*list.at(i) == i32(i + 1));
345 }}
346
347 {
348 var it = list.iterator(0);
349 var x: i32 = 0;
350 while (it.next()) |item| {
351 x += 1;
352 assert(*item == x);
353 }
354 assert(x == 100);
355 while (it.prev()) |item| : (x -= 1) {
356 assert(*item == x);
357 }
358 assert(x == 0);
359 }
360
361 assert(??list.pop() == 100);
362 assert(list.len == 99);
363
364 try list.pushMany([]i32 { 1, 2, 3 });
365 assert(list.len == 102);
366 assert(??list.pop() == 3);
367 assert(??list.pop() == 2);
368 assert(??list.pop() == 1);
369 assert(list.len == 99);
370
371 try list.pushMany([]const i32 {});
372 assert(list.len == 99);
373
374 var i: i32 = 99;
375 while (list.pop()) |item| : (i -= 1) {
376 assert(item == i);
377 list.shrinkCapacity(list.len);
378 }
379}
std/special/bootstrap.zig+19-8
...@@ -48,22 +48,33 @@ extern fn WinMainCRTStartup() noreturn {...@@ -48,22 +48,33 @@ extern fn WinMainCRTStartup() noreturn {
48fn posixCallMainAndExit() noreturn {48fn posixCallMainAndExit() noreturn {
49 const argc = *argc_ptr;49 const argc = *argc_ptr;
50 const argv = @ptrCast(&&u8, &argc_ptr[1]);50 const argv = @ptrCast(&&u8, &argc_ptr[1]);
51 const envp = @ptrCast(&?&u8, &argv[argc + 1]);51 const envp_nullable = @ptrCast(&?&u8, &argv[argc + 1]);
52 var envp_count: usize = 0;
53 while (envp_nullable[envp_count]) |_| : (envp_count += 1) {}
54 const envp = @ptrCast(&&u8, envp_nullable)[0..envp_count];
55 if (builtin.os == builtin.Os.linux) {
56 const auxv = &@ptrCast(&usize, envp.ptr)[envp_count + 1];
57 var i: usize = 0;
58 while (auxv[i] != 0) : (i += 2) {
59 if (auxv[i] < std.os.linux_aux_raw.len) std.os.linux_aux_raw[auxv[i]] = auxv[i+1];
60 }
61 std.debug.assert(std.os.linux_aux_raw[std.elf.AT_PAGESZ] == std.os.page_size);
62 }
63
52 std.os.posix.exit(callMainWithArgs(argc, argv, envp));64 std.os.posix.exit(callMainWithArgs(argc, argv, envp));
53}65}
5466
55fn callMainWithArgs(argc: usize, argv: &&u8, envp: &?&u8) u8 {67fn callMainWithArgs(argc: usize, argv: &&u8, envp: []&u8) u8 {
56 std.os.ArgIteratorPosix.raw = argv[0..argc];68 std.os.ArgIteratorPosix.raw = argv[0..argc];
5769 std.os.posix_environ_raw = envp;
58 var env_count: usize = 0;
59 while (envp[env_count] != null) : (env_count += 1) {}
60 std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count];
61
62 return callMain();70 return callMain();
63}71}
6472
65extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) i32 {73extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) i32 {
66 return callMainWithArgs(usize(c_argc), c_argv, c_envp);74 var env_count: usize = 0;
75 while (c_envp[env_count] != null) : (env_count += 1) {}
76 const envp = @ptrCast(&&u8, c_envp)[0..env_count];
77 return callMainWithArgs(usize(c_argc), c_argv, envp);
67}78}
6879
69fn callMain() u8 {80fn callMain() u8 {
std/special/bootstrap_lib.zig+2-1
...@@ -1,9 +1,10 @@...@@ -1,9 +1,10 @@
1// This file is included in the compilation unit when exporting a library on windows.1// This file is included in the compilation unit when exporting a library on windows.
22
3const std = @import("std");3const std = @import("std");
4const builtin = @import("builtin");
45
5comptime {6comptime {
6 @export("_DllMainCRTStartup", _DllMainCRTStartup);7 @export("_DllMainCRTStartup", _DllMainCRTStartup, builtin.GlobalLinkage.Strong);
7}8}
89
9stdcallcc fn _DllMainCRTStartup(hinstDLL: std.os.windows.HINSTANCE, fdwReason: std.os.windows.DWORD,10stdcallcc fn _DllMainCRTStartup(hinstDLL: std.os.windows.HINSTANCE, fdwReason: std.os.windows.DWORD,
std/special/builtin.zig+272-9
...@@ -14,37 +14,91 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn...@@ -14,37 +14,91 @@ pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn
14 }14 }
15}15}
1616
17// Note that memset does not return `dest`, like the libc API.17export fn memset(dest: ?&u8, c: u8, n: usize) ?&u8 {
18// The semantics of memset is dictated by the corresponding
19// LLVM intrinsics, not by the libc API.
20export fn memset(dest: ?&u8, c: u8, n: usize) void {
21 @setRuntimeSafety(false);18 @setRuntimeSafety(false);
2219
23 var index: usize = 0;20 var index: usize = 0;
24 while (index != n) : (index += 1)21 while (index != n) : (index += 1)
25 (??dest)[index] = c;22 (??dest)[index] = c;
23
24 return dest;
26}25}
2726
28// Note that memcpy does not return `dest`, like the libc API.27export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) ?&u8 {
29// The semantics of memcpy is dictated by the corresponding
30// LLVM intrinsics, not by the libc API.
31export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) void {
32 @setRuntimeSafety(false);28 @setRuntimeSafety(false);
3329
34 var index: usize = 0;30 var index: usize = 0;
35 while (index != n) : (index += 1)31 while (index != n) : (index += 1)
36 (??dest)[index] = (??src)[index];32 (??dest)[index] = (??src)[index];
33
34 return dest;
35}
36
37export fn memmove(dest: ?&u8, src: ?&const u8, n: usize) ?&u8 {
38 @setRuntimeSafety(false);
39
40 if (@ptrToInt(dest) < @ptrToInt(src)) {
41 var index: usize = 0;
42 while (index != n) : (index += 1) {
43 (??dest)[index] = (??src)[index];
44 }
45 } else {
46 var index = n;
47 while (index != 0) {
48 index -= 1;
49 (??dest)[index] = (??src)[index];
50 }
51 }
52
53 return dest;
37}54}
3855
39comptime {56comptime {
40 if (builtin.mode != builtin.Mode.ReleaseFast and builtin.os != builtin.Os.windows) {57 if (builtin.mode != builtin.Mode.ReleaseFast and
58 builtin.mode != builtin.Mode.ReleaseSmall and
59 builtin.os != builtin.Os.windows) {
41 @export("__stack_chk_fail", __stack_chk_fail, builtin.GlobalLinkage.Strong);60 @export("__stack_chk_fail", __stack_chk_fail, builtin.GlobalLinkage.Strong);
42 }61 }
62 if (builtin.os == builtin.Os.linux and builtin.arch == builtin.Arch.x86_64) {
63 @export("clone", clone, builtin.GlobalLinkage.Strong);
64 }
43}65}
44extern fn __stack_chk_fail() noreturn {66extern fn __stack_chk_fail() noreturn {
45 @panic("stack smashing detected");67 @panic("stack smashing detected");
46}68}
4769
70// TODO we should be able to put this directly in std/linux/x86_64.zig but
71// it causes a segfault in release mode. this is a workaround of calling it
72// across .o file boundaries. fix comptime @ptrCast of nakedcc functions.
73nakedcc fn clone() void {
74 asm volatile (
75 \\ xor %%eax,%%eax
76 \\ mov $56,%%al
77 \\ mov %%rdi,%%r11
78 \\ mov %%rdx,%%rdi
79 \\ mov %%r8,%%rdx
80 \\ mov %%r9,%%r8
81 \\ mov 8(%%rsp),%%r10
82 \\ mov %%r11,%%r9
83 \\ and $-16,%%rsi
84 \\ sub $8,%%rsi
85 \\ mov %%rcx,(%%rsi)
86 \\ syscall
87 \\ test %%eax,%%eax
88 \\ jnz 1f
89 \\ xor %%ebp,%%ebp
90 \\ pop %%rdi
91 \\ call *%%r9
92 \\ mov %%eax,%%edi
93 \\ xor %%eax,%%eax
94 \\ mov $60,%%al
95 \\ syscall
96 \\ hlt
97 \\1: ret
98 \\
99 );
100}
101
48const math = @import("../math/index.zig");102const math = @import("../math/index.zig");
49103
50export fn fmodf(x: f32, y: f32) f32 { return generic_fmod(f32, x, y); }104export fn fmodf(x: f32, y: f32) f32 { return generic_fmod(f32, x, y); }
...@@ -142,3 +196,212 @@ fn isNan(comptime T: type, bits: T) bool {...@@ -142,3 +196,212 @@ fn isNan(comptime T: type, bits: T) bool {
142 unreachable;196 unreachable;
143 }197 }
144}198}
199
200// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
201// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
202// potentially some edge cases remaining that are not handled in the same way.
203export fn sqrt(x: f64) f64 {
204 const tiny: f64 = 1.0e-300;
205 const sign: u32 = 0x80000000;
206 const u = @bitCast(u64, x);
207
208 var ix0 = u32(u >> 32);
209 var ix1 = u32(u & 0xFFFFFFFF);
210
211 // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
212 if (ix0 & 0x7FF00000 == 0x7FF00000) {
213 return x * x + x;
214 }
215
216 // sqrt(+-0) = +-0
217 if (x == 0.0) {
218 return x;
219 }
220 // sqrt(-ve) = snan
221 if (ix0 & sign != 0) {
222 return math.snan(f64);
223 }
224
225 // normalize x
226 var m = i32(ix0 >> 20);
227 if (m == 0) {
228 // subnormal
229 while (ix0 == 0) {
230 m -= 21;
231 ix0 |= ix1 >> 11;
232 ix1 <<= 21;
233 }
234
235 // subnormal
236 var i: u32 = 0;
237 while (ix0 & 0x00100000 == 0) : (i += 1) {
238 ix0 <<= 1;
239 }
240 m -= i32(i) - 1;
241 ix0 |= ix1 >> u5(32 - i);
242 ix1 <<= u5(i);
243 }
244
245 // unbias exponent
246 m -= 1023;
247 ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
248 if (m & 1 != 0) {
249 ix0 += ix0 + (ix1 >> 31);
250 ix1 = ix1 +% ix1;
251 }
252 m >>= 1;
253
254 // sqrt(x) bit by bit
255 ix0 += ix0 + (ix1 >> 31);
256 ix1 = ix1 +% ix1;
257
258 var q: u32 = 0;
259 var q1: u32 = 0;
260 var s0: u32 = 0;
261 var s1: u32 = 0;
262 var r: u32 = 0x00200000;
263 var t: u32 = undefined;
264 var t1: u32 = undefined;
265
266 while (r != 0) {
267 t = s0 +% r;
268 if (t <= ix0) {
269 s0 = t + r;
270 ix0 -= t;
271 q += r;
272 }
273 ix0 = ix0 +% ix0 +% (ix1 >> 31);
274 ix1 = ix1 +% ix1;
275 r >>= 1;
276 }
277
278 r = sign;
279 while (r != 0) {
280 t = s1 +% r;
281 t = s0;
282 if (t < ix0 or (t == ix0 and t1 <= ix1)) {
283 s1 = t1 +% r;
284 if (t1 & sign == sign and s1 & sign == 0) {
285 s0 += 1;
286 }
287 ix0 -= t;
288 if (ix1 < t1) {
289 ix0 -= 1;
290 }
291 ix1 = ix1 -% t1;
292 q1 += r;
293 }
294 ix0 = ix0 +% ix0 +% (ix1 >> 31);
295 ix1 = ix1 +% ix1;
296 r >>= 1;
297 }
298
299 // rounding direction
300 if (ix0 | ix1 != 0) {
301 var z = 1.0 - tiny; // raise inexact
302 if (z >= 1.0) {
303 z = 1.0 + tiny;
304 if (q1 == 0xFFFFFFFF) {
305 q1 = 0;
306 q += 1;
307 } else if (z > 1.0) {
308 if (q1 == 0xFFFFFFFE) {
309 q += 1;
310 }
311 q1 += 2;
312 } else {
313 q1 += q1 & 1;
314 }
315 }
316 }
317
318 ix0 = (q >> 1) + 0x3FE00000;
319 ix1 = q1 >> 1;
320 if (q & 1 != 0) {
321 ix1 |= 0x80000000;
322 }
323
324 // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
325 // behaviour at least.
326 var iix0 = i32(ix0);
327 iix0 = iix0 +% (m << 20);
328
329 const uz = (u64(iix0) << 32) | ix1;
330 return @bitCast(f64, uz);
331}
332
333export fn sqrtf(x: f32) f32 {
334 const tiny: f32 = 1.0e-30;
335 const sign: i32 = @bitCast(i32, u32(0x80000000));
336 var ix: i32 = @bitCast(i32, x);
337
338 if ((ix & 0x7F800000) == 0x7F800000) {
339 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
340 }
341
342 // zero
343 if (ix <= 0) {
344 if (ix & ~sign == 0) {
345 return x; // sqrt (+-0) = +-0
346 }
347 if (ix < 0) {
348 return math.snan(f32);
349 }
350 }
351
352 // normalize
353 var m = ix >> 23;
354 if (m == 0) {
355 // subnormal
356 var i: i32 = 0;
357 while (ix & 0x00800000 == 0) : (i += 1) {
358 ix <<= 1;
359 }
360 m -= i - 1;
361 }
362
363 m -= 127; // unbias exponent
364 ix = (ix & 0x007FFFFF) | 0x00800000;
365
366 if (m & 1 != 0) { // odd m, double x to even
367 ix += ix;
368 }
369
370 m >>= 1; // m = [m / 2]
371
372 // sqrt(x) bit by bit
373 ix += ix;
374 var q: i32 = 0; // q = sqrt(x)
375 var s: i32 = 0;
376 var r: i32 = 0x01000000; // r = moving bit right -> left
377
378 while (r != 0) {
379 const t = s + r;
380 if (t <= ix) {
381 s = t + r;
382 ix -= t;
383 q += r;
384 }
385 ix += ix;
386 r >>= 1;
387 }
388
389 // floating add to find rounding direction
390 if (ix != 0) {
391 var z = 1.0 - tiny; // inexact
392 if (z >= 1.0) {
393 z = 1.0 + tiny;
394 if (z > 1.0) {
395 q += 2;
396 } else {
397 if (q & 1 != 0) {
398 q += 1;
399 }
400 }
401 }
402 }
403
404 ix = (q >> 1) + 0x3f000000;
405 ix += m << 23;
406 return @bitCast(f32, ix);
407}
std/special/compiler_rt/fixuint.zig+1-1
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const is_test = @import("builtin").is_test;1const is_test = @import("builtin").is_test;
2const Log2Int = @import("../../math/index.zig").Log2Int;2const Log2Int = @import("std").math.Log2Int;
33
4pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t {4pub fn fixuint(comptime fp_t: type, comptime fixuint_t: type, a: fp_t) fixuint_t {
5 @setRuntimeSafety(is_test);5 @setRuntimeSafety(is_test);
std/special/compiler_rt/fixunsdfdi_test.zig+1-1
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const __fixunsdfdi = @import("fixunsdfdi.zig").__fixunsdfdi;1const __fixunsdfdi = @import("fixunsdfdi.zig").__fixunsdfdi;
2const assert = @import("../../index.zig").debug.assert;2const assert = @import("std").debug.assert;
33
4fn test__fixunsdfdi(a: f64, expected: u64) void {4fn test__fixunsdfdi(a: f64, expected: u64) void {
5 const x = __fixunsdfdi(a);5 const x = __fixunsdfdi(a);
std/special/compiler_rt/fixunsdfsi_test.zig+1-1
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const __fixunsdfsi = @import("fixunsdfsi.zig").__fixunsdfsi;1const __fixunsdfsi = @import("fixunsdfsi.zig").__fixunsdfsi;
2const assert = @import("../../index.zig").debug.assert;2const assert = @import("std").debug.assert;
33
4fn test__fixunsdfsi(a: f64, expected: u32) void {4fn test__fixunsdfsi(a: f64, expected: u32) void {
5 const x = __fixunsdfsi(a);5 const x = __fixunsdfsi(a);
std/special/compiler_rt/fixunsdfti_test.zig+1-1
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const __fixunsdfti = @import("fixunsdfti.zig").__fixunsdfti;1const __fixunsdfti = @import("fixunsdfti.zig").__fixunsdfti;
2const assert = @import("../../index.zig").debug.assert;2const assert = @import("std").debug.assert;
33
4fn test__fixunsdfti(a: f64, expected: u128) void {4fn test__fixunsdfti(a: f64, expected: u128) void {
5 const x = __fixunsdfti(a);5 const x = __fixunsdfti(a);
std/special/compiler_rt/fixunssfdi_test.zig+1-1
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const __fixunssfdi = @import("fixunssfdi.zig").__fixunssfdi;1const __fixunssfdi = @import("fixunssfdi.zig").__fixunssfdi;
2const assert = @import("../../index.zig").debug.assert;2const assert = @import("std").debug.assert;
33
4fn test__fixunssfdi(a: f32, expected: u64) void {4fn test__fixunssfdi(a: f32, expected: u64) void {
5 const x = __fixunssfdi(a);5 const x = __fixunssfdi(a);
std/special/compiler_rt/fixunssfsi_test.zig+1-1
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const __fixunssfsi = @import("fixunssfsi.zig").__fixunssfsi;1const __fixunssfsi = @import("fixunssfsi.zig").__fixunssfsi;
2const assert = @import("../../index.zig").debug.assert;2const assert = @import("std").debug.assert;
33
4fn test__fixunssfsi(a: f32, expected: u32) void {4fn test__fixunssfsi(a: f32, expected: u32) void {
5 const x = __fixunssfsi(a);5 const x = __fixunssfsi(a);
std/special/compiler_rt/fixunssfti_test.zig+1-1
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const __fixunssfti = @import("fixunssfti.zig").__fixunssfti;1const __fixunssfti = @import("fixunssfti.zig").__fixunssfti;
2const assert = @import("../../index.zig").debug.assert;2const assert = @import("std").debug.assert;
33
4fn test__fixunssfti(a: f32, expected: u128) void {4fn test__fixunssfti(a: f32, expected: u128) void {
5 const x = __fixunssfti(a);5 const x = __fixunssfti(a);
std/special/compiler_rt/fixunstfdi_test.zig+1-1
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const __fixunstfdi = @import("fixunstfdi.zig").__fixunstfdi;1const __fixunstfdi = @import("fixunstfdi.zig").__fixunstfdi;
2const assert = @import("../../index.zig").debug.assert;2const assert = @import("std").debug.assert;
33
4fn test__fixunstfdi(a: f128, expected: u64) void {4fn test__fixunstfdi(a: f128, expected: u64) void {
5 const x = __fixunstfdi(a);5 const x = __fixunstfdi(a);
std/special/compiler_rt/fixunstfsi_test.zig+1-1
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const __fixunstfsi = @import("fixunstfsi.zig").__fixunstfsi;1const __fixunstfsi = @import("fixunstfsi.zig").__fixunstfsi;
2const assert = @import("../../index.zig").debug.assert;2const assert = @import("std").debug.assert;
33
4fn test__fixunstfsi(a: f128, expected: u32) void {4fn test__fixunstfsi(a: f128, expected: u32) void {
5 const x = __fixunstfsi(a);5 const x = __fixunstfsi(a);
std/special/compiler_rt/fixunstfti_test.zig+1-1
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1const __fixunstfti = @import("fixunstfti.zig").__fixunstfti;1const __fixunstfti = @import("fixunstfti.zig").__fixunstfti;
2const assert = @import("../../index.zig").debug.assert;2const assert = @import("std").debug.assert;
33
4fn test__fixunstfti(a: f128, expected: u128) void {4fn test__fixunstfti(a: f128, expected: u128) void {
5 const x = __fixunstfti(a);5 const x = __fixunstfti(a);
std/special/compiler_rt/index.zig+20-6
...@@ -32,10 +32,6 @@ comptime {...@@ -32,10 +32,6 @@ comptime {
32 @export("__fixunstfti", @import("fixunstfti.zig").__fixunstfti, linkage);32 @export("__fixunstfti", @import("fixunstfti.zig").__fixunstfti, linkage);
3333
34 @export("__udivmoddi4", @import("udivmoddi4.zig").__udivmoddi4, linkage);34 @export("__udivmoddi4", @import("udivmoddi4.zig").__udivmoddi4, linkage);
35 @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage);
36
37 @export("__udivti3", @import("udivti3.zig").__udivti3, linkage);
38 @export("__umodti3", @import("umodti3.zig").__umodti3, linkage);
3935
40 @export("__udivsi3", __udivsi3, linkage);36 @export("__udivsi3", __udivsi3, linkage);
41 @export("__udivdi3", __udivdi3, linkage);37 @export("__udivdi3", __udivdi3, linkage);
...@@ -62,13 +58,21 @@ comptime {...@@ -62,13 +58,21 @@ comptime {
62 @export("__chkstk", __chkstk, strong_linkage);58 @export("__chkstk", __chkstk, strong_linkage);
63 @export("___chkstk_ms", ___chkstk_ms, linkage);59 @export("___chkstk_ms", ___chkstk_ms, linkage);
64 }60 }
61 @export("__udivti3", @import("udivti3.zig").__udivti3_windows_x86_64, linkage);
62 @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4_windows_x86_64, linkage);
63 @export("__umodti3", @import("umodti3.zig").__umodti3_windows_x86_64, linkage);
65 },64 },
66 else => {},65 else => {},
67 }66 }
67 } else {
68 @export("__udivti3", @import("udivti3.zig").__udivti3, linkage);
69 @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage);
70 @export("__umodti3", @import("umodti3.zig").__umodti3, linkage);
68 }71 }
69}72}
7073
71const assert = @import("../../index.zig").debug.assert;74const std = @import("std");
75const assert = std.debug.assert;
7276
73const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;77const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;
7478
...@@ -77,12 +81,22 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;...@@ -77,12 +81,22 @@ const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;
77pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn {81pub fn panic(msg: []const u8, error_return_trace: ?&builtin.StackTrace) noreturn {
78 @setCold(true);82 @setCold(true);
79 if (is_test) {83 if (is_test) {
80 @import("std").debug.panic("{}", msg);84 std.debug.panic("{}", msg);
81 } else {85 } else {
82 unreachable;86 unreachable;
83 }87 }
84}88}
8589
90pub fn setXmm0(comptime T: type, value: T) void {
91 comptime assert(builtin.arch == builtin.Arch.x86_64);
92 const aligned_value: T align(16) = value;
93 asm volatile (
94 \\movaps (%[ptr]), %%xmm0
95 :
96 : [ptr] "r" (&aligned_value)
97 : "xmm0");
98}
99
86extern fn __udivdi3(a: u64, b: u64) u64 {100extern fn __udivdi3(a: u64, b: u64) u64 {
87 @setRuntimeSafety(is_test);101 @setRuntimeSafety(is_test);
88 return __udivmoddi4(a, b, null);102 return __udivmoddi4(a, b, null);
std/special/compiler_rt/udivmod.zig+1-1
...@@ -9,7 +9,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem:...@@ -9,7 +9,7 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem:
99
10 const SingleInt = @IntType(false, @divExact(DoubleInt.bit_count, 2));10 const SingleInt = @IntType(false, @divExact(DoubleInt.bit_count, 2));
11 const SignedDoubleInt = @IntType(true, DoubleInt.bit_count);11 const SignedDoubleInt = @IntType(true, DoubleInt.bit_count);
12 const Log2SingleInt = @import("../../math/index.zig").Log2Int(SingleInt);12 const Log2SingleInt = @import("std").math.Log2Int(SingleInt);
1313
14 const n = *@ptrCast(&const [2]SingleInt, &a); // TODO issue #42114 const n = *@ptrCast(&const [2]SingleInt, &a); // TODO issue #421
15 const d = *@ptrCast(&const [2]SingleInt, &b); // TODO issue #42115 const d = *@ptrCast(&const [2]SingleInt, &b); // TODO issue #421
std/special/compiler_rt/udivmodti4.zig+6
...@@ -1,11 +1,17 @@...@@ -1,11 +1,17 @@
1const udivmod = @import("udivmod.zig").udivmod;1const udivmod = @import("udivmod.zig").udivmod;
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const compiler_rt = @import("index.zig");
34
4pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) u128 {5pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) u128 {
5 @setRuntimeSafety(builtin.is_test);6 @setRuntimeSafety(builtin.is_test);
6 return udivmod(u128, a, b, maybe_rem);7 return udivmod(u128, a, b, maybe_rem);
7}8}
89
10pub extern fn __udivmodti4_windows_x86_64(a: &const u128, b: &const u128, maybe_rem: ?&u128) void {
11 @setRuntimeSafety(builtin.is_test);
12 compiler_rt.setXmm0(u128, udivmod(u128, *a, *b, maybe_rem));
13}
14
9test "import udivmodti4" {15test "import udivmodti4" {
10 _ = @import("udivmodti4_test.zig");16 _ = @import("udivmodti4_test.zig");
11}17}
std/special/compiler_rt/udivti3.zig+7-2
...@@ -1,7 +1,12 @@...@@ -1,7 +1,12 @@
1const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4;1const udivmodti4 = @import("udivmodti4.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub extern fn __udivti3(a: u128, b: u128) u128 {4pub extern fn __udivti3(a: u128, b: u128) u128 {
5 @setRuntimeSafety(builtin.is_test);5 @setRuntimeSafety(builtin.is_test);
6 return __udivmodti4(a, b, null);6 return udivmodti4.__udivmodti4(a, b, null);
7}
8
9pub extern fn __udivti3_windows_x86_64(a: &const u128, b: &const u128) void {
10 @setRuntimeSafety(builtin.is_test);
11 udivmodti4.__udivmodti4_windows_x86_64(a, b, null);
7}12}
std/special/compiler_rt/umodti3.zig+8-2
...@@ -1,9 +1,15 @@...@@ -1,9 +1,15 @@
1const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4;1const udivmodti4 = @import("udivmodti4.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3const compiler_rt = @import("index.zig");
34
4pub extern fn __umodti3(a: u128, b: u128) u128 {5pub extern fn __umodti3(a: u128, b: u128) u128 {
5 @setRuntimeSafety(builtin.is_test);6 @setRuntimeSafety(builtin.is_test);
6 var r: u128 = undefined;7 var r: u128 = undefined;
7 _ = __udivmodti4(a, b, &r);8 _ = udivmodti4.__udivmodti4(a, b, &r);
8 return r;9 return r;
9}10}
11
12pub extern fn __umodti3_windows_x86_64(a: &const u128, b: &const u128) void {
13 @setRuntimeSafety(builtin.is_test);
14 compiler_rt.setXmm0(u128, __umodti3(*a, *b));
15}
std/unicode.zig+151-12
...@@ -1,6 +1,16 @@...@@ -1,6 +1,16 @@
1const std = @import("./index.zig");1const std = @import("./index.zig");
2const debug = std.debug;2const debug = std.debug;
33
4/// Returns how many bytes the UTF-8 representation would require
5/// for the given codepoint.
6pub fn utf8CodepointSequenceLength(c: u32) !u3 {
7 if (c < 0x80) return u3(1);
8 if (c < 0x800) return u3(2);
9 if (c < 0x10000) return u3(3);
10 if (c < 0x110000) return u3(4);
11 return error.CodepointTooLarge;
12}
13
4/// Given the first byte of a UTF-8 codepoint,14/// Given the first byte of a UTF-8 codepoint,
5/// returns a number 1-4 indicating the total length of the codepoint in bytes.15/// returns a number 1-4 indicating the total length of the codepoint in bytes.
6/// If this byte does not match the form of a UTF-8 start byte, returns Utf8InvalidStartByte.16/// If this byte does not match the form of a UTF-8 start byte, returns Utf8InvalidStartByte.
...@@ -12,11 +22,47 @@ pub fn utf8ByteSequenceLength(first_byte: u8) !u3 {...@@ -12,11 +22,47 @@ pub fn utf8ByteSequenceLength(first_byte: u8) !u3 {
12 return error.Utf8InvalidStartByte;22 return error.Utf8InvalidStartByte;
13}23}
1424
25/// Encodes the given codepoint into a UTF-8 byte sequence.
26/// c: the codepoint.
27/// out: the out buffer to write to. Must have a len >= utf8CodepointSequenceLength(c).
28/// Errors: if c cannot be encoded in UTF-8.
29/// Returns: the number of bytes written to out.
30pub fn utf8Encode(c: u32, out: []u8) !u3 {
31 const length = try utf8CodepointSequenceLength(c);
32 debug.assert(out.len >= length);
33 switch (length) {
34 // The pattern for each is the same
35 // - Increasing the initial shift by 6 each time
36 // - Each time after the first shorten the shifted
37 // value to a max of 0b111111 (63)
38 1 => out[0] = u8(c), // Can just do 0 + codepoint for initial range
39 2 => {
40 out[0] = u8(0b11000000 | (c >> 6));
41 out[1] = u8(0b10000000 | (c & 0b111111));
42 },
43 3 => {
44 if (0xd800 <= c and c <= 0xdfff) return error.Utf8CannotEncodeSurrogateHalf;
45 out[0] = u8(0b11100000 | (c >> 12));
46 out[1] = u8(0b10000000 | ((c >> 6) & 0b111111));
47 out[2] = u8(0b10000000 | (c & 0b111111));
48 },
49 4 => {
50 out[0] = u8(0b11110000 | (c >> 18));
51 out[1] = u8(0b10000000 | ((c >> 12) & 0b111111));
52 out[2] = u8(0b10000000 | ((c >> 6) & 0b111111));
53 out[3] = u8(0b10000000 | (c & 0b111111));
54 },
55 else => unreachable,
56 }
57 return length;
58}
59
60const Utf8DecodeError = Utf8Decode2Error || Utf8Decode3Error || Utf8Decode4Error;
15/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.61/// Decodes the UTF-8 codepoint encoded in the given slice of bytes.
16/// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable.62/// bytes.len must be equal to utf8ByteSequenceLength(bytes[0]) catch unreachable.
17/// If you already know the length at comptime, you can call one of63/// If you already know the length at comptime, you can call one of
18/// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function.64/// utf8Decode2,utf8Decode3,utf8Decode4 directly instead of this function.
19pub fn utf8Decode(bytes: []const u8) !u32 {65pub fn utf8Decode(bytes: []const u8) Utf8DecodeError!u32 {
20 return switch (bytes.len) {66 return switch (bytes.len) {
21 1 => u32(bytes[0]),67 1 => u32(bytes[0]),
22 2 => utf8Decode2(bytes),68 2 => utf8Decode2(bytes),
...@@ -25,7 +71,12 @@ pub fn utf8Decode(bytes: []const u8) !u32 {...@@ -25,7 +71,12 @@ pub fn utf8Decode(bytes: []const u8) !u32 {
25 else => unreachable,71 else => unreachable,
26 };72 };
27}73}
28pub fn utf8Decode2(bytes: []const u8) !u32 {74
75const Utf8Decode2Error = error{
76 Utf8ExpectedContinuation,
77 Utf8OverlongEncoding,
78};
79pub fn utf8Decode2(bytes: []const u8) Utf8Decode2Error!u32 {
29 debug.assert(bytes.len == 2);80 debug.assert(bytes.len == 2);
30 debug.assert(bytes[0] & 0b11100000 == 0b11000000);81 debug.assert(bytes[0] & 0b11100000 == 0b11000000);
31 var value: u32 = bytes[0] & 0b00011111;82 var value: u32 = bytes[0] & 0b00011111;
...@@ -38,7 +89,13 @@ pub fn utf8Decode2(bytes: []const u8) !u32 {...@@ -38,7 +89,13 @@ pub fn utf8Decode2(bytes: []const u8) !u32 {
3889
39 return value;90 return value;
40}91}
41pub fn utf8Decode3(bytes: []const u8) !u32 {92
93const Utf8Decode3Error = error{
94 Utf8ExpectedContinuation,
95 Utf8OverlongEncoding,
96 Utf8EncodesSurrogateHalf,
97};
98pub fn utf8Decode3(bytes: []const u8) Utf8Decode3Error!u32 {
42 debug.assert(bytes.len == 3);99 debug.assert(bytes.len == 3);
43 debug.assert(bytes[0] & 0b11110000 == 0b11100000);100 debug.assert(bytes[0] & 0b11110000 == 0b11100000);
44 var value: u32 = bytes[0] & 0b00001111;101 var value: u32 = bytes[0] & 0b00001111;
...@@ -56,7 +113,13 @@ pub fn utf8Decode3(bytes: []const u8) !u32 {...@@ -56,7 +113,13 @@ pub fn utf8Decode3(bytes: []const u8) !u32 {
56113
57 return value;114 return value;
58}115}
59pub fn utf8Decode4(bytes: []const u8) !u32 {116
117const Utf8Decode4Error = error{
118 Utf8ExpectedContinuation,
119 Utf8OverlongEncoding,
120 Utf8CodepointTooLarge,
121};
122pub fn utf8Decode4(bytes: []const u8) Utf8Decode4Error!u32 {
60 debug.assert(bytes.len == 4);123 debug.assert(bytes.len == 4);
61 debug.assert(bytes[0] & 0b11111000 == 0b11110000);124 debug.assert(bytes[0] & 0b11111000 == 0b11110000);
62 var value: u32 = bytes[0] & 0b00000111;125 var value: u32 = bytes[0] & 0b00000111;
...@@ -158,19 +221,67 @@ const Utf8Iterator = struct {...@@ -158,19 +221,67 @@ const Utf8Iterator = struct {
158 pub fn nextCodepoint(it: &Utf8Iterator) ?u32 {221 pub fn nextCodepoint(it: &Utf8Iterator) ?u32 {
159 const slice = it.nextCodepointSlice() ?? return null;222 const slice = it.nextCodepointSlice() ?? return null;
160223
161 const r = switch (slice.len) {224 switch (slice.len) {
162 1 => u32(slice[0]),225 1 => return u32(slice[0]),
163 2 => utf8Decode2(slice),226 2 => return utf8Decode2(slice) catch unreachable,
164 3 => utf8Decode3(slice),227 3 => return utf8Decode3(slice) catch unreachable,
165 4 => utf8Decode4(slice),228 4 => return utf8Decode4(slice) catch unreachable,
166 else => unreachable,229 else => unreachable,
167 };230 }
168
169 return r catch unreachable;
170 }231 }
171};232};
172233
234test "utf8 encode" {
235 comptime testUtf8Encode() catch unreachable;
236 try testUtf8Encode();
237}
238fn testUtf8Encode() !void {
239 // A few taken from wikipedia a few taken elsewhere
240 var array: [4]u8 = undefined;
241 debug.assert((try utf8Encode(try utf8Decode("€"), array[0..])) == 3);
242 debug.assert(array[0] == 0b11100010);
243 debug.assert(array[1] == 0b10000010);
244 debug.assert(array[2] == 0b10101100);
245
246 debug.assert((try utf8Encode(try utf8Decode("$"), array[0..])) == 1);
247 debug.assert(array[0] == 0b00100100);
248
249 debug.assert((try utf8Encode(try utf8Decode("¢"), array[0..])) == 2);
250 debug.assert(array[0] == 0b11000010);
251 debug.assert(array[1] == 0b10100010);
252
253 debug.assert((try utf8Encode(try utf8Decode("𐍈"), array[0..])) == 4);
254 debug.assert(array[0] == 0b11110000);
255 debug.assert(array[1] == 0b10010000);
256 debug.assert(array[2] == 0b10001101);
257 debug.assert(array[3] == 0b10001000);
258}
259
260test "utf8 encode error" {
261 comptime testUtf8EncodeError();
262 testUtf8EncodeError();
263}
264fn testUtf8EncodeError() void {
265 var array: [4]u8 = undefined;
266 testErrorEncode(0xd800, array[0..], error.Utf8CannotEncodeSurrogateHalf);
267 testErrorEncode(0xdfff, array[0..], error.Utf8CannotEncodeSurrogateHalf);
268 testErrorEncode(0x110000, array[0..], error.CodepointTooLarge);
269 testErrorEncode(0xffffffff, array[0..], error.CodepointTooLarge);
270}
271
272fn testErrorEncode(codePoint: u32, array: []u8, expectedErr: error) void {
273 if (utf8Encode(codePoint, array)) |_| {
274 unreachable;
275 } else |err| {
276 debug.assert(err == expectedErr);
277 }
278}
279
173test "utf8 iterator on ascii" {280test "utf8 iterator on ascii" {
281 comptime testUtf8IteratorOnAscii();
282 testUtf8IteratorOnAscii();
283}
284fn testUtf8IteratorOnAscii() void {
174 const s = Utf8View.initComptime("abc");285 const s = Utf8View.initComptime("abc");
175286
176 var it1 = s.iterator();287 var it1 = s.iterator();
...@@ -187,6 +298,10 @@ test "utf8 iterator on ascii" {...@@ -187,6 +298,10 @@ test "utf8 iterator on ascii" {
187}298}
188299
189test "utf8 view bad" {300test "utf8 view bad" {
301 comptime testUtf8ViewBad();
302 testUtf8ViewBad();
303}
304fn testUtf8ViewBad() void {
190 // Compile-time error.305 // Compile-time error.
191 // const s3 = Utf8View.initComptime("\xfe\xf2");306 // const s3 = Utf8View.initComptime("\xfe\xf2");
192307
...@@ -195,6 +310,10 @@ test "utf8 view bad" {...@@ -195,6 +310,10 @@ test "utf8 view bad" {
195}310}
196311
197test "utf8 view ok" {312test "utf8 view ok" {
313 comptime testUtf8ViewOk();
314 testUtf8ViewOk();
315}
316fn testUtf8ViewOk() void {
198 const s = Utf8View.initComptime("東京市");317 const s = Utf8View.initComptime("東京市");
199318
200 var it1 = s.iterator();319 var it1 = s.iterator();
...@@ -211,6 +330,10 @@ test "utf8 view ok" {...@@ -211,6 +330,10 @@ test "utf8 view ok" {
211}330}
212331
213test "bad utf8 slice" {332test "bad utf8 slice" {
333 comptime testBadUtf8Slice();
334 testBadUtf8Slice();
335}
336fn testBadUtf8Slice() void {
214 debug.assert(utf8ValidateSlice("abc"));337 debug.assert(utf8ValidateSlice("abc"));
215 debug.assert(!utf8ValidateSlice("abc\xc0"));338 debug.assert(!utf8ValidateSlice("abc\xc0"));
216 debug.assert(!utf8ValidateSlice("abc\xc0abc"));339 debug.assert(!utf8ValidateSlice("abc\xc0abc"));
...@@ -218,6 +341,10 @@ test "bad utf8 slice" {...@@ -218,6 +341,10 @@ test "bad utf8 slice" {
218}341}
219342
220test "valid utf8" {343test "valid utf8" {
344 comptime testValidUtf8();
345 testValidUtf8();
346}
347fn testValidUtf8() void {
221 testValid("\x00", 0x0);348 testValid("\x00", 0x0);
222 testValid("\x20", 0x20);349 testValid("\x20", 0x20);
223 testValid("\x7f", 0x7f);350 testValid("\x7f", 0x7f);
...@@ -233,6 +360,10 @@ test "valid utf8" {...@@ -233,6 +360,10 @@ test "valid utf8" {
233}360}
234361
235test "invalid utf8 continuation bytes" {362test "invalid utf8 continuation bytes" {
363 comptime testInvalidUtf8ContinuationBytes();
364 testInvalidUtf8ContinuationBytes();
365}
366fn testInvalidUtf8ContinuationBytes() void {
236 // unexpected continuation367 // unexpected continuation
237 testError("\x80", error.Utf8InvalidStartByte);368 testError("\x80", error.Utf8InvalidStartByte);
238 testError("\xbf", error.Utf8InvalidStartByte);369 testError("\xbf", error.Utf8InvalidStartByte);
...@@ -261,6 +392,10 @@ test "invalid utf8 continuation bytes" {...@@ -261,6 +392,10 @@ test "invalid utf8 continuation bytes" {
261}392}
262393
263test "overlong utf8 codepoint" {394test "overlong utf8 codepoint" {
395 comptime testOverlongUtf8Codepoint();
396 testOverlongUtf8Codepoint();
397}
398fn testOverlongUtf8Codepoint() void {
264 testError("\xc0\x80", error.Utf8OverlongEncoding);399 testError("\xc0\x80", error.Utf8OverlongEncoding);
265 testError("\xc1\xbf", error.Utf8OverlongEncoding);400 testError("\xc1\xbf", error.Utf8OverlongEncoding);
266 testError("\xe0\x80\x80", error.Utf8OverlongEncoding);401 testError("\xe0\x80\x80", error.Utf8OverlongEncoding);
...@@ -270,6 +405,10 @@ test "overlong utf8 codepoint" {...@@ -270,6 +405,10 @@ test "overlong utf8 codepoint" {
270}405}
271406
272test "misc invalid utf8" {407test "misc invalid utf8" {
408 comptime testMiscInvalidUtf8();
409 testMiscInvalidUtf8();
410}
411fn testMiscInvalidUtf8() void {
273 // codepoint out of bounds412 // codepoint out of bounds
274 testError("\xf4\x90\x80\x80", error.Utf8CodepointTooLarge);413 testError("\xf4\x90\x80\x80", error.Utf8CodepointTooLarge);
275 testError("\xf7\xbf\xbf\xbf", error.Utf8CodepointTooLarge);414 testError("\xf7\xbf\xbf\xbf", error.Utf8CodepointTooLarge);
std/zig/ast.zig+2054-525
...@@ -1,647 +1,2176 @@...@@ -1,647 +1,2176 @@
1const std = @import("../index.zig");1const std = @import("../index.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const ArrayList = std.ArrayList;3const SegmentedList = std.SegmentedList;
4const Token = std.zig.Token;
5const mem = std.mem;4const mem = std.mem;
5const Token = std.zig.Token;
6
7pub const TokenIndex = usize;
8
9pub const Tree = struct {
10 source: []const u8,
11 tokens: TokenList,
12 root_node: &Node.Root,
13 arena_allocator: std.heap.ArenaAllocator,
14 errors: ErrorList,
15
16 pub const TokenList = SegmentedList(Token, 64);
17 pub const ErrorList = SegmentedList(Error, 0);
18
19 pub fn deinit(self: &Tree) void {
20 self.arena_allocator.deinit();
21 }
22
23 pub fn renderError(self: &Tree, parse_error: &Error, stream: var) !void {
24 return parse_error.render(&self.tokens, stream);
25 }
26
27 pub fn tokenSlice(self: &Tree, token_index: TokenIndex) []const u8 {
28 return self.tokenSlicePtr(self.tokens.at(token_index));
29 }
30
31 pub fn tokenSlicePtr(self: &Tree, token: &const Token) []const u8 {
32 return self.source[token.start..token.end];
33 }
34
35 pub const Location = struct {
36 line: usize,
37 column: usize,
38 line_start: usize,
39 line_end: usize,
40 };
41
42 pub fn tokenLocationPtr(self: &Tree, start_index: usize, token: &const Token) Location {
43 var loc = Location {
44 .line = 0,
45 .column = 0,
46 .line_start = start_index,
47 .line_end = self.source.len,
48 };
49 const token_start = token.start;
50 for (self.source[start_index..]) |c, i| {
51 if (i + start_index == token_start) {
52 loc.line_end = i + start_index;
53 while (loc.line_end < self.source.len and self.source[loc.line_end] != '\n') : (loc.line_end += 1) {}
54 return loc;
55 }
56 if (c == '\n') {
57 loc.line += 1;
58 loc.column = 0;
59 loc.line_start = i + 1;
60 } else {
61 loc.column += 1;
62 }
63 }
64 return loc;
65 }
66
67 pub fn tokenLocation(self: &Tree, start_index: usize, token_index: TokenIndex) Location {
68 return self.tokenLocationPtr(start_index, self.tokens.at(token_index));
69 }
70};
71
72pub const Error = union(enum) {
73 InvalidToken: InvalidToken,
74 ExpectedVarDeclOrFn: ExpectedVarDeclOrFn,
75 ExpectedAggregateKw: ExpectedAggregateKw,
76 UnattachedDocComment: UnattachedDocComment,
77 ExpectedEqOrSemi: ExpectedEqOrSemi,
78 ExpectedSemiOrLBrace: ExpectedSemiOrLBrace,
79 ExpectedLabelable: ExpectedLabelable,
80 ExpectedInlinable: ExpectedInlinable,
81 ExpectedAsmOutputReturnOrType: ExpectedAsmOutputReturnOrType,
82 ExpectedCall: ExpectedCall,
83 ExpectedCallOrFnProto: ExpectedCallOrFnProto,
84 ExpectedSliceOrRBracket: ExpectedSliceOrRBracket,
85 ExtraAlignQualifier: ExtraAlignQualifier,
86 ExtraConstQualifier: ExtraConstQualifier,
87 ExtraVolatileQualifier: ExtraVolatileQualifier,
88 ExpectedPrimaryExpr: ExpectedPrimaryExpr,
89 ExpectedToken: ExpectedToken,
90 ExpectedCommaOrEnd: ExpectedCommaOrEnd,
91
92 pub fn render(self: &Error, tokens: &Tree.TokenList, stream: var) !void {
93 switch (*self) {
94 // TODO https://github.com/zig-lang/zig/issues/683
95 @TagType(Error).InvalidToken => |*x| return x.render(tokens, stream),
96 @TagType(Error).ExpectedVarDeclOrFn => |*x| return x.render(tokens, stream),
97 @TagType(Error).ExpectedAggregateKw => |*x| return x.render(tokens, stream),
98 @TagType(Error).UnattachedDocComment => |*x| return x.render(tokens, stream),
99 @TagType(Error).ExpectedEqOrSemi => |*x| return x.render(tokens, stream),
100 @TagType(Error).ExpectedSemiOrLBrace => |*x| return x.render(tokens, stream),
101 @TagType(Error).ExpectedLabelable => |*x| return x.render(tokens, stream),
102 @TagType(Error).ExpectedInlinable => |*x| return x.render(tokens, stream),
103 @TagType(Error).ExpectedAsmOutputReturnOrType => |*x| return x.render(tokens, stream),
104 @TagType(Error).ExpectedCall => |*x| return x.render(tokens, stream),
105 @TagType(Error).ExpectedCallOrFnProto => |*x| return x.render(tokens, stream),
106 @TagType(Error).ExpectedSliceOrRBracket => |*x| return x.render(tokens, stream),
107 @TagType(Error).ExtraAlignQualifier => |*x| return x.render(tokens, stream),
108 @TagType(Error).ExtraConstQualifier => |*x| return x.render(tokens, stream),
109 @TagType(Error).ExtraVolatileQualifier => |*x| return x.render(tokens, stream),
110 @TagType(Error).ExpectedPrimaryExpr => |*x| return x.render(tokens, stream),
111 @TagType(Error).ExpectedToken => |*x| return x.render(tokens, stream),
112 @TagType(Error).ExpectedCommaOrEnd => |*x| return x.render(tokens, stream),
113 }
114 }
115
116 pub fn loc(self: &Error) TokenIndex {
117 switch (*self) {
118 // TODO https://github.com/zig-lang/zig/issues/683
119 @TagType(Error).InvalidToken => |x| return x.token,
120 @TagType(Error).ExpectedVarDeclOrFn => |x| return x.token,
121 @TagType(Error).ExpectedAggregateKw => |x| return x.token,
122 @TagType(Error).UnattachedDocComment => |x| return x.token,
123 @TagType(Error).ExpectedEqOrSemi => |x| return x.token,
124 @TagType(Error).ExpectedSemiOrLBrace => |x| return x.token,
125 @TagType(Error).ExpectedLabelable => |x| return x.token,
126 @TagType(Error).ExpectedInlinable => |x| return x.token,
127 @TagType(Error).ExpectedAsmOutputReturnOrType => |x| return x.token,
128 @TagType(Error).ExpectedCall => |x| return x.node.firstToken(),
129 @TagType(Error).ExpectedCallOrFnProto => |x| return x.node.firstToken(),
130 @TagType(Error).ExpectedSliceOrRBracket => |x| return x.token,
131 @TagType(Error).ExtraAlignQualifier => |x| return x.token,
132 @TagType(Error).ExtraConstQualifier => |x| return x.token,
133 @TagType(Error).ExtraVolatileQualifier => |x| return x.token,
134 @TagType(Error).ExpectedPrimaryExpr => |x| return x.token,
135 @TagType(Error).ExpectedToken => |x| return x.token,
136 @TagType(Error).ExpectedCommaOrEnd => |x| return x.token,
137 }
138 }
139
140 pub const InvalidToken = SingleTokenError("Invalid token {}");
141 pub const ExpectedVarDeclOrFn = SingleTokenError("Expected variable declaration or function, found {}");
142 pub const ExpectedAggregateKw = SingleTokenError("Expected " ++
143 @tagName(Token.Id.Keyword_struct) ++ ", " ++ @tagName(Token.Id.Keyword_union) ++ ", or " ++
144 @tagName(Token.Id.Keyword_enum) ++ ", found {}");
145 pub const ExpectedEqOrSemi = SingleTokenError("Expected '=' or ';', found {}");
146 pub const ExpectedSemiOrLBrace = SingleTokenError("Expected ';' or '{{', found {}");
147 pub const ExpectedLabelable = SingleTokenError("Expected 'while', 'for', 'inline', 'suspend', or '{{', found {}");
148 pub const ExpectedInlinable = SingleTokenError("Expected 'while' or 'for', found {}");
149 pub const ExpectedAsmOutputReturnOrType = SingleTokenError("Expected '->' or " ++
150 @tagName(Token.Id.Identifier) ++ ", found {}");
151 pub const ExpectedSliceOrRBracket = SingleTokenError("Expected ']' or '..', found {}");
152 pub const ExpectedPrimaryExpr = SingleTokenError("Expected primary expression, found {}");
153
154 pub const UnattachedDocComment = SimpleError("Unattached documentation comment");
155 pub const ExtraAlignQualifier = SimpleError("Extra align qualifier");
156 pub const ExtraConstQualifier = SimpleError("Extra const qualifier");
157 pub const ExtraVolatileQualifier = SimpleError("Extra volatile qualifier");
158
159 pub const ExpectedCall = struct {
160 node: &Node,
161
162 pub fn render(self: &ExpectedCall, tokens: &Tree.TokenList, stream: var) !void {
163 return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ ", found {}",
164 @tagName(self.node.id));
165 }
166 };
167
168 pub const ExpectedCallOrFnProto = struct {
169 node: &Node,
170
171 pub fn render(self: &ExpectedCallOrFnProto, tokens: &Tree.TokenList, stream: var) !void {
172 return stream.print("expected " ++ @tagName(@TagType(Node.SuffixOp.Op).Call) ++ " or " ++
173 @tagName(Node.Id.FnProto) ++ ", found {}", @tagName(self.node.id));
174 }
175 };
176
177 pub const ExpectedToken = struct {
178 token: TokenIndex,
179 expected_id: @TagType(Token.Id),
180
181 pub fn render(self: &ExpectedToken, tokens: &Tree.TokenList, stream: var) !void {
182 const token_name = @tagName(tokens.at(self.token).id);
183 return stream.print("expected {}, found {}", @tagName(self.expected_id), token_name);
184 }
185 };
186
187 pub const ExpectedCommaOrEnd = struct {
188 token: TokenIndex,
189 end_id: @TagType(Token.Id),
190
191 pub fn render(self: &ExpectedCommaOrEnd, tokens: &Tree.TokenList, stream: var) !void {
192 const token_name = @tagName(tokens.at(self.token).id);
193 return stream.print("expected ',' or {}, found {}", @tagName(self.end_id), token_name);
194 }
195 };
196
197 fn SingleTokenError(comptime msg: []const u8) type {
198 return struct {
199 const ThisError = this;
200
201 token: TokenIndex,
202
203 pub fn render(self: &ThisError, tokens: &Tree.TokenList, stream: var) !void {
204 const token_name = @tagName(tokens.at(self.token).id);
205 return stream.print(msg, token_name);
206 }
207 };
208 }
209
210 fn SimpleError(comptime msg: []const u8) type {
211 return struct {
212 const ThisError = this;
213
214 token: TokenIndex,
215
216 pub fn render(self: &ThisError, tokens: &Tree.TokenList, stream: var) !void {
217 return stream.write(msg);
218 }
219 };
220 }
221};
6222
7pub const Node = struct {223pub const Node = struct {
8 id: Id,224 id: Id,
9 comment: ?&NodeLineComment,
10225
11 pub const Id = enum {226 pub const Id = enum {
227 // Top level
12 Root,228 Root,
229 Use,
230 TestDecl,
231
232 // Statements
13 VarDecl,233 VarDecl,
14 Identifier,234 Defer,
15 FnProto,235
16 ParamDecl,236 // Operators
17 Block,
18 InfixOp,237 InfixOp,
19 PrefixOp,238 PrefixOp,
239 SuffixOp,
240
241 // Control flow
242 Switch,
243 While,
244 For,
245 If,
246 ControlFlowExpression,
247 Suspend,
248
249 // Type expressions
250 VarType,
251 ErrorType,
252 FnProto,
253 PromiseType,
254
255 // Primary expressions
20 IntegerLiteral,256 IntegerLiteral,
21 FloatLiteral,257 FloatLiteral,
22 StringLiteral,258 StringLiteral,
259 MultilineStringLiteral,
260 CharLiteral,
261 BoolLiteral,
262 NullLiteral,
23 UndefinedLiteral,263 UndefinedLiteral,
264 ThisLiteral,
265 Unreachable,
266 Identifier,
267 GroupedExpression,
24 BuiltinCall,268 BuiltinCall,
25 Call,269 ErrorSetDecl,
270 ContainerDecl,
271 Asm,
272 Comptime,
273 Block,
274
275 // Misc
26 LineComment,276 LineComment,
27 TestDecl,277 DocComment,
278 SwitchCase,
279 SwitchElse,
280 Else,
281 Payload,
282 PointerPayload,
283 PointerIndexPayload,
284 StructField,
285 UnionTag,
286 EnumTag,
287 ErrorTag,
288 AsmInput,
289 AsmOutput,
290 AsyncAttribute,
291 ParamDecl,
292 FieldInitializer,
28 };293 };
29294
30 pub fn iterate(base: &Node, index: usize) ?&Node {295 pub fn cast(base: &Node, comptime T: type) ?&T {
31 return switch (base.id) {296 if (base.id == comptime typeToId(T)) {
32 Id.Root => @fieldParentPtr(NodeRoot, "base", base).iterate(index),297 return @fieldParentPtr(T, "base", base);
33 Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).iterate(index),298 }
34 Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).iterate(index),299 return null;
35 Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
36 Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
37 Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index),
38 Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
39 Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
40 Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
41 Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
42 Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).iterate(index),
43 Id.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).iterate(index),
44 Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).iterate(index),
45 Id.Call => @fieldParentPtr(NodeCall, "base", base).iterate(index),
46 Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).iterate(index),
47 Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).iterate(index),
48 };
49 }300 }
50301
51 pub fn firstToken(base: &Node) Token {302 pub fn iterate(base: &Node, index: usize) ?&Node {
52 return switch (base.id) {303 comptime var i = 0;
53 Id.Root => @fieldParentPtr(NodeRoot, "base", base).firstToken(),304 inline while (i < @memberCount(Id)) : (i += 1) {
54 Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).firstToken(),305 if (base.id == @field(Id, @memberName(Id, i))) {
55 Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).firstToken(),306 const T = @field(Node, @memberName(Id, i));
56 Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).firstToken(),307 return @fieldParentPtr(T, "base", base).iterate(index);
57 Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).firstToken(),308 }
58 Id.Block => @fieldParentPtr(NodeBlock, "base", base).firstToken(),309 }
59 Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).firstToken(),310 unreachable;
60 Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).firstToken(),
61 Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).firstToken(),
62 Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).firstToken(),
63 Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).firstToken(),
64 Id.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).firstToken(),
65 Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).firstToken(),
66 Id.Call => @fieldParentPtr(NodeCall, "base", base).firstToken(),
67 Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).firstToken(),
68 Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).firstToken(),
69 };
70 }311 }
71312
72 pub fn lastToken(base: &Node) Token {313 pub fn firstToken(base: &Node) TokenIndex {
73 return switch (base.id) {314 comptime var i = 0;
74 Id.Root => @fieldParentPtr(NodeRoot, "base", base).lastToken(),315 inline while (i < @memberCount(Id)) : (i += 1) {
75 Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).lastToken(),316 if (base.id == @field(Id, @memberName(Id, i))) {
76 Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).lastToken(),317 const T = @field(Node, @memberName(Id, i));
77 Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).lastToken(),318 return @fieldParentPtr(T, "base", base).firstToken();
78 Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).lastToken(),319 }
79 Id.Block => @fieldParentPtr(NodeBlock, "base", base).lastToken(),320 }
80 Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).lastToken(),321 unreachable;
81 Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).lastToken(),
82 Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).lastToken(),
83 Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).lastToken(),
84 Id.StringLiteral => @fieldParentPtr(NodeStringLiteral, "base", base).lastToken(),
85 Id.UndefinedLiteral => @fieldParentPtr(NodeUndefinedLiteral, "base", base).lastToken(),
86 Id.BuiltinCall => @fieldParentPtr(NodeBuiltinCall, "base", base).lastToken(),
87 Id.Call => @fieldParentPtr(NodeCall, "base", base).lastToken(),
88 Id.LineComment => @fieldParentPtr(NodeLineComment, "base", base).lastToken(),
89 Id.TestDecl => @fieldParentPtr(NodeTestDecl, "base", base).lastToken(),
90 };
91 }322 }
92};
93323
94pub const NodeRoot = struct {324 pub fn lastToken(base: &Node) TokenIndex {
95 base: Node,325 comptime var i = 0;
96 decls: ArrayList(&Node),326 inline while (i < @memberCount(Id)) : (i += 1) {
97 eof_token: Token,327 if (base.id == @field(Id, @memberName(Id, i))) {
98328 const T = @field(Node, @memberName(Id, i));
99 pub fn iterate(self: &NodeRoot, index: usize) ?&Node {329 return @fieldParentPtr(T, "base", base).lastToken();
100 if (index < self.decls.len) {330 }
101 return self.decls.items[self.decls.len - index - 1];
102 }331 }
103 return null;332 unreachable;
104 }333 }
105334
106 pub fn firstToken(self: &NodeRoot) Token {335 pub fn typeToId(comptime T: type) Id {
107 return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken();336 comptime var i = 0;
337 inline while (i < @memberCount(Id)) : (i += 1) {
338 if (T == @field(Node, @memberName(Id, i))) {
339 return @field(Id, @memberName(Id, i));
340 }
341 }
342 unreachable;
108 }343 }
109344
110 pub fn lastToken(self: &NodeRoot) Token {345 pub fn requireSemiColon(base: &const Node) bool {
111 return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken();346 var n = base;
347 while (true) {
348 switch (n.id) {
349 Id.Root,
350 Id.StructField,
351 Id.UnionTag,
352 Id.EnumTag,
353 Id.ParamDecl,
354 Id.Block,
355 Id.Payload,
356 Id.PointerPayload,
357 Id.PointerIndexPayload,
358 Id.Switch,
359 Id.SwitchCase,
360 Id.SwitchElse,
361 Id.FieldInitializer,
362 Id.DocComment,
363 Id.LineComment,
364 Id.TestDecl => return false,
365 Id.While => {
366 const while_node = @fieldParentPtr(While, "base", n);
367 if (while_node.@"else") |@"else"| {
368 n = @"else".base;
369 continue;
370 }
371
372 return while_node.body.id != Id.Block;
373 },
374 Id.For => {
375 const for_node = @fieldParentPtr(For, "base", n);
376 if (for_node.@"else") |@"else"| {
377 n = @"else".base;
378 continue;
379 }
380
381 return for_node.body.id != Id.Block;
382 },
383 Id.If => {
384 const if_node = @fieldParentPtr(If, "base", n);
385 if (if_node.@"else") |@"else"| {
386 n = @"else".base;
387 continue;
388 }
389
390 return if_node.body.id != Id.Block;
391 },
392 Id.Else => {
393 const else_node = @fieldParentPtr(Else, "base", n);
394 n = else_node.body;
395 continue;
396 },
397 Id.Defer => {
398 const defer_node = @fieldParentPtr(Defer, "base", n);
399 return defer_node.expr.id != Id.Block;
400 },
401 Id.Comptime => {
402 const comptime_node = @fieldParentPtr(Comptime, "base", n);
403 return comptime_node.expr.id != Id.Block;
404 },
405 Id.Suspend => {
406 const suspend_node = @fieldParentPtr(Suspend, "base", n);
407 if (suspend_node.body) |body| {
408 return body.id != Id.Block;
409 }
410
411 return true;
412 },
413 else => return true,
414 }
415 }
112 }416 }
113};
114417
115pub const NodeVarDecl = struct {418
116 base: Node,419 pub const Root = struct {
117 visib_token: ?Token,420 base: Node,
118 name_token: Token,421 doc_comments: ?&DocComment,
119 eq_token: Token,422 decls: DeclList,
120 mut_token: Token,423 eof_token: TokenIndex,
121 comptime_token: ?Token,424
122 extern_token: ?Token,425 pub const DeclList = SegmentedList(&Node, 4);
123 lib_name: ?&Node,426
124 type_node: ?&Node,427 pub fn iterate(self: &Root, index: usize) ?&Node {
125 align_node: ?&Node,428 if (index < self.decls.len) {
126 init_node: ?&Node,429 return self.decls.items[self.decls.len - index - 1];
127 semicolon_token: Token,430 }
128431 return null;
129 pub fn iterate(self: &NodeVarDecl, index: usize) ?&Node {
130 var i = index;
131
132 if (self.type_node) |type_node| {
133 if (i < 1) return type_node;
134 i -= 1;
135 }432 }
136433
137 if (self.align_node) |align_node| {434 pub fn firstToken(self: &Root) TokenIndex {
138 if (i < 1) return align_node;435 return if (self.decls.len == 0) self.eof_token else (*self.decls.at(0)).firstToken();
139 i -= 1;
140 }436 }
141437
142 if (self.init_node) |init_node| {438 pub fn lastToken(self: &Root) TokenIndex {
143 if (i < 1) return init_node;439 return if (self.decls.len == 0) self.eof_token else (*self.decls.at(self.decls.len - 1)).lastToken();
144 i -= 1;
145 }440 }
441 };
146442
147 return null;443 pub const VarDecl = struct {
148 }444 base: Node,
445 doc_comments: ?&DocComment,
446 visib_token: ?TokenIndex,
447 name_token: TokenIndex,
448 eq_token: TokenIndex,
449 mut_token: TokenIndex,
450 comptime_token: ?TokenIndex,
451 extern_export_token: ?TokenIndex,
452 lib_name: ?&Node,
453 type_node: ?&Node,
454 align_node: ?&Node,
455 init_node: ?&Node,
456 semicolon_token: TokenIndex,
457
458 pub fn iterate(self: &VarDecl, index: usize) ?&Node {
459 var i = index;
460
461 if (self.type_node) |type_node| {
462 if (i < 1) return type_node;
463 i -= 1;
464 }
149465
150 pub fn firstToken(self: &NodeVarDecl) Token {466 if (self.align_node) |align_node| {
151 if (self.visib_token) |visib_token| return visib_token;467 if (i < 1) return align_node;
152 if (self.comptime_token) |comptime_token| return comptime_token;468 i -= 1;
153 if (self.extern_token) |extern_token| return extern_token;469 }
154 assert(self.lib_name == null);
155 return self.mut_token;
156 }
157470
158 pub fn lastToken(self: &NodeVarDecl) Token {471 if (self.init_node) |init_node| {
159 return self.semicolon_token;472 if (i < 1) return init_node;
160 }473 i -= 1;
161};474 }
162475
163pub const NodeIdentifier = struct {476 return null;
164 base: Node,477 }
165 name_token: Token,
166478
167 pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node {479 pub fn firstToken(self: &VarDecl) TokenIndex {
168 return null;480 if (self.visib_token) |visib_token| return visib_token;
169 }481 if (self.comptime_token) |comptime_token| return comptime_token;
482 if (self.extern_export_token) |extern_export_token| return extern_export_token;
483 assert(self.lib_name == null);
484 return self.mut_token;
485 }
170486
171 pub fn firstToken(self: &NodeIdentifier) Token {487 pub fn lastToken(self: &VarDecl) TokenIndex {
172 return self.name_token;488 return self.semicolon_token;
173 }489 }
490 };
174491
175 pub fn lastToken(self: &NodeIdentifier) Token {492 pub const Use = struct {
176 return self.name_token;493 base: Node,
177 }494 doc_comments: ?&DocComment,
178};495 visib_token: ?TokenIndex,
496 expr: &Node,
497 semicolon_token: TokenIndex,
498
499 pub fn iterate(self: &Use, index: usize) ?&Node {
500 var i = index;
179501
180pub const NodeFnProto = struct {502 if (i < 1) return self.expr;
181 base: Node,
182 visib_token: ?Token,
183 fn_token: Token,
184 name_token: ?Token,
185 params: ArrayList(&Node),
186 return_type: ReturnType,
187 var_args_token: ?Token,
188 extern_token: ?Token,
189 inline_token: ?Token,
190 cc_token: ?Token,
191 body_node: ?&Node,
192 lib_name: ?&Node, // populated if this is an extern declaration
193 align_expr: ?&Node, // populated if align(A) is present
194
195 pub const ReturnType = union(enum) {
196 Explicit: &Node,
197 Infer: Token,
198 InferErrorSet: &Node,
199 };
200
201 pub fn iterate(self: &NodeFnProto, index: usize) ?&Node {
202 var i = index;
203
204 if (self.body_node) |body_node| {
205 if (i < 1) return body_node;
206 i -= 1;503 i -= 1;
504
505 return null;
207 }506 }
208507
209 switch (self.return_type) {508 pub fn firstToken(self: &Use) TokenIndex {
210 // TODO allow this and next prong to share bodies since the types are the same509 if (self.visib_token) |visib_token| return visib_token;
211 ReturnType.Explicit => |node| {510 return self.expr.firstToken();
212 if (i < 1) return node;
213 i -= 1;
214 },
215 ReturnType.InferErrorSet => |node| {
216 if (i < 1) return node;
217 i -= 1;
218 },
219 ReturnType.Infer => {},
220 }511 }
221512
222 if (self.align_expr) |align_expr| {513 pub fn lastToken(self: &Use) TokenIndex {
223 if (i < 1) return align_expr;514 return self.semicolon_token;
224 i -= 1;
225 }515 }
516 };
226517
227 if (i < self.params.len) return self.params.items[self.params.len - i - 1];518 pub const ErrorSetDecl = struct {
228 i -= self.params.len;519 base: Node,
520 error_token: TokenIndex,
521 decls: DeclList,
522 rbrace_token: TokenIndex,
229523
230 if (self.lib_name) |lib_name| {524 pub const DeclList = SegmentedList(&Node, 2);
231 if (i < 1) return lib_name;525
232 i -= 1;526 pub fn iterate(self: &ErrorSetDecl, index: usize) ?&Node {
527 var i = index;
528
529 if (i < self.decls.len) return *self.decls.at(i);
530 i -= self.decls.len;
531
532 return null;
233 }533 }
234534
235 return null;535 pub fn firstToken(self: &ErrorSetDecl) TokenIndex {
236 }536 return self.error_token;
537 }
237538
238 pub fn firstToken(self: &NodeFnProto) Token {539 pub fn lastToken(self: &ErrorSetDecl) TokenIndex {
239 if (self.visib_token) |visib_token| return visib_token;540 return self.rbrace_token;
240 if (self.extern_token) |extern_token| return extern_token;541 }
241 assert(self.lib_name == null);542 };
242 if (self.inline_token) |inline_token| return inline_token;543
243 if (self.cc_token) |cc_token| return cc_token;544 pub const ContainerDecl = struct {
244 return self.fn_token;545 base: Node,
245 }546 ltoken: TokenIndex,
547 layout: Layout,
548 kind: Kind,
549 init_arg_expr: InitArg,
550 fields_and_decls: DeclList,
551 rbrace_token: TokenIndex,
552
553 pub const DeclList = Root.DeclList;
554
555 const Layout = enum {
556 Auto,
557 Extern,
558 Packed,
559 };
560
561 const Kind = enum {
562 Struct,
563 Enum,
564 Union,
565 };
566
567 const InitArg = union(enum) {
568 None,
569 Enum: ?&Node,
570 Type: &Node,
571 };
572
573 pub fn iterate(self: &ContainerDecl, index: usize) ?&Node {
574 var i = index;
575
576 switch (self.init_arg_expr) {
577 InitArg.Type => |t| {
578 if (i < 1) return t;
579 i -= 1;
580 },
581 InitArg.None,
582 InitArg.Enum => { }
583 }
246584
247 pub fn lastToken(self: &NodeFnProto) Token {585 if (i < self.fields_and_decls.len) return *self.fields_and_decls.at(i);
248 if (self.body_node) |body_node| return body_node.lastToken();586 i -= self.fields_and_decls.len;
249 switch (self.return_type) {587
250 // TODO allow this and next prong to share bodies since the types are the same588 return null;
251 ReturnType.Explicit => |node| return node.lastToken(),
252 ReturnType.InferErrorSet => |node| return node.lastToken(),
253 ReturnType.Infer => |token| return token,
254 }589 }
255 }
256};
257590
258pub const NodeParamDecl = struct {591 pub fn firstToken(self: &ContainerDecl) TokenIndex {
259 base: Node,592 return self.ltoken;
260 comptime_token: ?Token,593 }
261 noalias_token: ?Token,
262 name_token: ?Token,
263 type_node: &Node,
264 var_args_token: ?Token,
265594
266 pub fn iterate(self: &NodeParamDecl, index: usize) ?&Node {595 pub fn lastToken(self: &ContainerDecl) TokenIndex {
267 var i = index;596 return self.rbrace_token;
597 }
598 };
268599
269 if (i < 1) return self.type_node;600 pub const StructField = struct {
270 i -= 1;601 base: Node,
602 doc_comments: ?&DocComment,
603 visib_token: ?TokenIndex,
604 name_token: TokenIndex,
605 type_expr: &Node,
271606
272 return null;607 pub fn iterate(self: &StructField, index: usize) ?&Node {
273 }608 var i = index;
274609
275 pub fn firstToken(self: &NodeParamDecl) Token {610 if (i < 1) return self.type_expr;
276 if (self.comptime_token) |comptime_token| return comptime_token;611 i -= 1;
277 if (self.noalias_token) |noalias_token| return noalias_token;
278 if (self.name_token) |name_token| return name_token;
279 return self.type_node.firstToken();
280 }
281612
282 pub fn lastToken(self: &NodeParamDecl) Token {613 return null;
283 if (self.var_args_token) |var_args_token| return var_args_token;614 }
284 return self.type_node.lastToken();
285 }
286};
287615
288pub const NodeBlock = struct {616 pub fn firstToken(self: &StructField) TokenIndex {
289 base: Node,617 if (self.visib_token) |visib_token| return visib_token;
290 begin_token: Token,618 return self.name_token;
291 end_token: Token,619 }
292 statements: ArrayList(&Node),
293620
294 pub fn iterate(self: &NodeBlock, index: usize) ?&Node {621 pub fn lastToken(self: &StructField) TokenIndex {
295 var i = index;622 return self.type_expr.lastToken();
623 }
624 };
296625
297 if (i < self.statements.len) return self.statements.items[i];626 pub const UnionTag = struct {
298 i -= self.statements.len;627 base: Node,
628 doc_comments: ?&DocComment,
629 name_token: TokenIndex,
630 type_expr: ?&Node,
631 value_expr: ?&Node,
299632
300 return null;633 pub fn iterate(self: &UnionTag, index: usize) ?&Node {
301 }634 var i = index;
302635
303 pub fn firstToken(self: &NodeBlock) Token {636 if (self.type_expr) |type_expr| {
304 return self.begin_token;637 if (i < 1) return type_expr;
305 }638 i -= 1;
639 }
306640
307 pub fn lastToken(self: &NodeBlock) Token {641 if (self.value_expr) |value_expr| {
308 return self.end_token;642 if (i < 1) return value_expr;
309 }643 i -= 1;
310};644 }
311645
312pub const NodeInfixOp = struct {646 return null;
313 base: Node,647 }
314 op_token: Token,
315 lhs: &Node,
316 op: InfixOp,
317 rhs: &Node,
318
319 const InfixOp = enum {
320 Add,
321 AddWrap,
322 ArrayCat,
323 ArrayMult,
324 Assign,
325 AssignBitAnd,
326 AssignBitOr,
327 AssignBitShiftLeft,
328 AssignBitShiftRight,
329 AssignBitXor,
330 AssignDiv,
331 AssignMinus,
332 AssignMinusWrap,
333 AssignMod,
334 AssignPlus,
335 AssignPlusWrap,
336 AssignTimes,
337 AssignTimesWarp,
338 BangEqual,
339 BitAnd,
340 BitOr,
341 BitShiftLeft,
342 BitShiftRight,
343 BitXor,
344 BoolAnd,
345 BoolOr,
346 Div,
347 EqualEqual,
348 ErrorUnion,
349 GreaterOrEqual,
350 GreaterThan,
351 LessOrEqual,
352 LessThan,
353 MergeErrorSets,
354 Mod,
355 Mult,
356 MultWrap,
357 Period,
358 Sub,
359 SubWrap,
360 UnwrapMaybe,
361 };
362
363 pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node {
364 var i = index;
365
366 if (i < 1) return self.lhs;
367 i -= 1;
368
369 switch (self.op) {
370 InfixOp.Add,
371 InfixOp.AddWrap,
372 InfixOp.ArrayCat,
373 InfixOp.ArrayMult,
374 InfixOp.Assign,
375 InfixOp.AssignBitAnd,
376 InfixOp.AssignBitOr,
377 InfixOp.AssignBitShiftLeft,
378 InfixOp.AssignBitShiftRight,
379 InfixOp.AssignBitXor,
380 InfixOp.AssignDiv,
381 InfixOp.AssignMinus,
382 InfixOp.AssignMinusWrap,
383 InfixOp.AssignMod,
384 InfixOp.AssignPlus,
385 InfixOp.AssignPlusWrap,
386 InfixOp.AssignTimes,
387 InfixOp.AssignTimesWarp,
388 InfixOp.BangEqual,
389 InfixOp.BitAnd,
390 InfixOp.BitOr,
391 InfixOp.BitShiftLeft,
392 InfixOp.BitShiftRight,
393 InfixOp.BitXor,
394 InfixOp.BoolAnd,
395 InfixOp.BoolOr,
396 InfixOp.Div,
397 InfixOp.EqualEqual,
398 InfixOp.ErrorUnion,
399 InfixOp.GreaterOrEqual,
400 InfixOp.GreaterThan,
401 InfixOp.LessOrEqual,
402 InfixOp.LessThan,
403 InfixOp.MergeErrorSets,
404 InfixOp.Mod,
405 InfixOp.Mult,
406 InfixOp.MultWrap,
407 InfixOp.Period,
408 InfixOp.Sub,
409 InfixOp.SubWrap,
410 InfixOp.UnwrapMaybe => {},
411 }
412
413 if (i < 1) return self.rhs;
414 i -= 1;
415648
416 return null;649 pub fn firstToken(self: &UnionTag) TokenIndex {
417 }650 return self.name_token;
651 }
418652
419 pub fn firstToken(self: &NodeInfixOp) Token {653 pub fn lastToken(self: &UnionTag) TokenIndex {
420 return self.lhs.firstToken();654 if (self.value_expr) |value_expr| {
421 }655 return value_expr.lastToken();
656 }
657 if (self.type_expr) |type_expr| {
658 return type_expr.lastToken();
659 }
422660
423 pub fn lastToken(self: &NodeInfixOp) Token {661 return self.name_token;
424 return self.rhs.lastToken();662 }
425 }663 };
426};
427664
428pub const NodePrefixOp = struct {665 pub const EnumTag = struct {
429 base: Node,666 base: Node,
430 op_token: Token,667 doc_comments: ?&DocComment,
431 op: PrefixOp,668 name_token: TokenIndex,
432 rhs: &Node,669 value: ?&Node,
433670
434 const PrefixOp = union(enum) {671 pub fn iterate(self: &EnumTag, index: usize) ?&Node {
435 AddrOf: AddrOfInfo,672 var i = index;
436 BitNot,673
437 BoolNot,674 if (self.value) |value| {
438 Deref,675 if (i < 1) return value;
439 Negation,676 i -= 1;
440 NegationWrap,677 }
441 Return,678
442 Try,679 return null;
443 UnwrapMaybe,
444 };
445 const AddrOfInfo = struct {
446 align_expr: ?&Node,
447 bit_offset_start_token: ?Token,
448 bit_offset_end_token: ?Token,
449 const_token: ?Token,
450 volatile_token: ?Token,
451 };
452
453 pub fn iterate(self: &NodePrefixOp, index: usize) ?&Node {
454 var i = index;
455
456 switch (self.op) {
457 PrefixOp.AddrOf => |addr_of_info| {
458 if (addr_of_info.align_expr) |align_expr| {
459 if (i < 1) return align_expr;
460 i -= 1;
461 }
462 },
463 PrefixOp.BitNot,
464 PrefixOp.BoolNot,
465 PrefixOp.Deref,
466 PrefixOp.Negation,
467 PrefixOp.NegationWrap,
468 PrefixOp.Return,
469 PrefixOp.Try,
470 PrefixOp.UnwrapMaybe => {},
471 }680 }
472681
473 if (i < 1) return self.rhs;682 pub fn firstToken(self: &EnumTag) TokenIndex {
474 i -= 1;683 return self.name_token;
684 }
475685
476 return null;686 pub fn lastToken(self: &EnumTag) TokenIndex {
477 }687 if (self.value) |value| {
688 return value.lastToken();
689 }
478690
479 pub fn firstToken(self: &NodePrefixOp) Token {691 return self.name_token;
480 return self.op_token;692 }
481 }693 };
482694
483 pub fn lastToken(self: &NodePrefixOp) Token {695 pub const ErrorTag = struct {
484 return self.rhs.lastToken();696 base: Node,
485 }697 doc_comments: ?&DocComment,
486};698 name_token: TokenIndex,
487699
488pub const NodeIntegerLiteral = struct {700 pub fn iterate(self: &ErrorTag, index: usize) ?&Node {
489 base: Node,701 var i = index;
490 token: Token,
491702
492 pub fn iterate(self: &NodeIntegerLiteral, index: usize) ?&Node {703 if (self.doc_comments) |comments| {
493 return null;704 if (i < 1) return &comments.base;
494 }705 i -= 1;
706 }
495707
496 pub fn firstToken(self: &NodeIntegerLiteral) Token {708 return null;
497 return self.token;709 }
498 }
499710
500 pub fn lastToken(self: &NodeIntegerLiteral) Token {711 pub fn firstToken(self: &ErrorTag) TokenIndex {
501 return self.token;712 return self.name_token;
502 }713 }
503};
504714
505pub const NodeFloatLiteral = struct {715 pub fn lastToken(self: &ErrorTag) TokenIndex {
506 base: Node,716 return self.name_token;
507 token: Token,717 }
718 };
508719
509 pub fn iterate(self: &NodeFloatLiteral, index: usize) ?&Node {720 pub const Identifier = struct {
510 return null;721 base: Node,
511 }722 token: TokenIndex,
512723
513 pub fn firstToken(self: &NodeFloatLiteral) Token {724 pub fn iterate(self: &Identifier, index: usize) ?&Node {
514 return self.token;725 return null;
515 }726 }
516727
517 pub fn lastToken(self: &NodeFloatLiteral) Token {728 pub fn firstToken(self: &Identifier) TokenIndex {
518 return self.token;729 return self.token;
519 }730 }
520};
521731
522pub const NodeBuiltinCall = struct {732 pub fn lastToken(self: &Identifier) TokenIndex {
523 base: Node,733 return self.token;
524 builtin_token: Token,734 }
525 params: ArrayList(&Node),735 };
526 rparen_token: Token,
527736
528 pub fn iterate(self: &NodeBuiltinCall, index: usize) ?&Node {737 pub const AsyncAttribute = struct {
529 var i = index;738 base: Node,
739 async_token: TokenIndex,
740 allocator_type: ?&Node,
741 rangle_bracket: ?TokenIndex,
530742
531 if (i < self.params.len) return self.params.at(i);743 pub fn iterate(self: &AsyncAttribute, index: usize) ?&Node {
532 i -= self.params.len;744 var i = index;
533745
534 return null;746 if (self.allocator_type) |allocator_type| {
535 }747 if (i < 1) return allocator_type;
748 i -= 1;
749 }
536750
537 pub fn firstToken(self: &NodeBuiltinCall) Token {751 return null;
538 return self.builtin_token;752 }
539 }
540753
541 pub fn lastToken(self: &NodeBuiltinCall) Token {754 pub fn firstToken(self: &AsyncAttribute) TokenIndex {
542 return self.rparen_token;755 return self.async_token;
543 }756 }
544};
545757
546pub const NodeCall = struct {758 pub fn lastToken(self: &AsyncAttribute) TokenIndex {
547 base: Node,759 if (self.rangle_bracket) |rangle_bracket| {
548 callee: &Node,760 return rangle_bracket;
549 params: ArrayList(&Node),761 }
550 rparen_token: Token,
551762
552 pub fn iterate(self: &NodeCall, index: usize) ?&Node {763 return self.async_token;
553 var i = index;764 }
765 };
554766
555 if (i < 1) return self.callee;767 pub const FnProto = struct {
556 i -= 1;768 base: Node,
769 doc_comments: ?&DocComment,
770 visib_token: ?TokenIndex,
771 fn_token: TokenIndex,
772 name_token: ?TokenIndex,
773 params: ParamList,
774 return_type: ReturnType,
775 var_args_token: ?TokenIndex,
776 extern_export_inline_token: ?TokenIndex,
777 cc_token: ?TokenIndex,
778 async_attr: ?&AsyncAttribute,
779 body_node: ?&Node,
780 lib_name: ?&Node, // populated if this is an extern declaration
781 align_expr: ?&Node, // populated if align(A) is present
782
783 pub const ParamList = SegmentedList(&Node, 2);
784
785 pub const ReturnType = union(enum) {
786 Explicit: &Node,
787 InferErrorSet: &Node,
788 };
557789
558 if (i < self.params.len) return self.params.at(i);790 pub fn iterate(self: &FnProto, index: usize) ?&Node {
559 i -= self.params.len;791 var i = index;
560792
561 return null;793 if (self.body_node) |body_node| {
562 }794 if (i < 1) return body_node;
795 i -= 1;
796 }
563797
564 pub fn firstToken(self: &NodeCall) Token {798 switch (self.return_type) {
565 return self.callee.firstToken();799 // TODO allow this and next prong to share bodies since the types are the same
566 }800 ReturnType.Explicit => |node| {
801 if (i < 1) return node;
802 i -= 1;
803 },
804 ReturnType.InferErrorSet => |node| {
805 if (i < 1) return node;
806 i -= 1;
807 },
808 }
567809
568 pub fn lastToken(self: &NodeCall) Token {810 if (self.align_expr) |align_expr| {
569 return self.rparen_token;811 if (i < 1) return align_expr;
570 }812 i -= 1;
571};813 }
572814
573pub const NodeStringLiteral = struct {815 if (i < self.params.len) return self.params.items[self.params.len - i - 1];
574 base: Node,816 i -= self.params.len;
575 token: Token,
576817
577 pub fn iterate(self: &NodeStringLiteral, index: usize) ?&Node {818 if (self.lib_name) |lib_name| {
578 return null;819 if (i < 1) return lib_name;
579 }820 i -= 1;
821 }
580822
581 pub fn firstToken(self: &NodeStringLiteral) Token {823 return null;
582 return self.token;824 }
583 }
584825
585 pub fn lastToken(self: &NodeStringLiteral) Token {826 pub fn firstToken(self: &FnProto) TokenIndex {
586 return self.token;827 if (self.visib_token) |visib_token| return visib_token;
587 }828 if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
588};829 assert(self.lib_name == null);
830 if (self.cc_token) |cc_token| return cc_token;
831 return self.fn_token;
832 }
589833
590pub const NodeUndefinedLiteral = struct {834 pub fn lastToken(self: &FnProto) TokenIndex {
591 base: Node,835 if (self.body_node) |body_node| return body_node.lastToken();
592 token: Token,836 switch (self.return_type) {
837 // TODO allow this and next prong to share bodies since the types are the same
838 ReturnType.Explicit => |node| return node.lastToken(),
839 ReturnType.InferErrorSet => |node| return node.lastToken(),
840 }
841 }
842 };
593843
594 pub fn iterate(self: &NodeUndefinedLiteral, index: usize) ?&Node {844 pub const PromiseType = struct {
595 return null;845 base: Node,
596 }846 promise_token: TokenIndex,
847 result: ?Result,
597848
598 pub fn firstToken(self: &NodeUndefinedLiteral) Token {849 pub const Result = struct {
599 return self.token;850 arrow_token: TokenIndex,
600 }851 return_type: &Node,
852 };
601853
602 pub fn lastToken(self: &NodeUndefinedLiteral) Token {854 pub fn iterate(self: &PromiseType, index: usize) ?&Node {
603 return self.token;855 var i = index;
604 }
605};
606856
607pub const NodeLineComment = struct {857 if (self.result) |result| {
608 base: Node,858 if (i < 1) return result.return_type;
609 lines: ArrayList(Token),859 i -= 1;
860 }
610861
611 pub fn iterate(self: &NodeLineComment, index: usize) ?&Node {862 return null;
612 return null;863 }
613 }
614864
615 pub fn firstToken(self: &NodeLineComment) Token {865 pub fn firstToken(self: &PromiseType) TokenIndex {
616 return self.lines.at(0);866 return self.promise_token;
617 }867 }
618868
619 pub fn lastToken(self: &NodeLineComment) Token {869 pub fn lastToken(self: &PromiseType) TokenIndex {
620 return self.lines.at(self.lines.len - 1);870 if (self.result) |result| return result.return_type.lastToken();
621 }871 return self.promise_token;
622};872 }
873 };
623874
624pub const NodeTestDecl = struct {875 pub const ParamDecl = struct {
625 base: Node,876 base: Node,
626 test_token: Token,877 comptime_token: ?TokenIndex,
627 name_token: Token,878 noalias_token: ?TokenIndex,
628 body_node: &Node,879 name_token: ?TokenIndex,
880 type_node: &Node,
881 var_args_token: ?TokenIndex,
629882
630 pub fn iterate(self: &NodeTestDecl, index: usize) ?&Node {883 pub fn iterate(self: &ParamDecl, index: usize) ?&Node {
631 var i = index;884 var i = index;
632885
633 if (i < 1) return self.body_node;886 if (i < 1) return self.type_node;
634 i -= 1;887 i -= 1;
635888
636 return null;889 return null;
637 }890 }
638891
639 pub fn firstToken(self: &NodeTestDecl) Token {892 pub fn firstToken(self: &ParamDecl) TokenIndex {
640 return self.test_token;893 if (self.comptime_token) |comptime_token| return comptime_token;
641 }894 if (self.noalias_token) |noalias_token| return noalias_token;
895 if (self.name_token) |name_token| return name_token;
896 return self.type_node.firstToken();
897 }
642898
643 pub fn lastToken(self: &NodeTestDecl) Token {899 pub fn lastToken(self: &ParamDecl) TokenIndex {
644 return self.body_node.lastToken();900 if (self.var_args_token) |var_args_token| return var_args_token;
645 }901 return self.type_node.lastToken();
902 }
903 };
904
905 pub const Block = struct {
906 base: Node,
907 label: ?TokenIndex,
908 lbrace: TokenIndex,
909 statements: StatementList,
910 rbrace: TokenIndex,
911
912 pub const StatementList = Root.DeclList;
913
914 pub fn iterate(self: &Block, index: usize) ?&Node {
915 var i = index;
916
917 if (i < self.statements.len) return self.statements.items[i];
918 i -= self.statements.len;
919
920 return null;
921 }
922
923 pub fn firstToken(self: &Block) TokenIndex {
924 if (self.label) |label| {
925 return label;
926 }
927
928 return self.lbrace;
929 }
930
931 pub fn lastToken(self: &Block) TokenIndex {
932 return self.rbrace;
933 }
934 };
935
936 pub const Defer = struct {
937 base: Node,
938 defer_token: TokenIndex,
939 kind: Kind,
940 expr: &Node,
941
942 const Kind = enum {
943 Error,
944 Unconditional,
945 };
946
947 pub fn iterate(self: &Defer, index: usize) ?&Node {
948 var i = index;
949
950 if (i < 1) return self.expr;
951 i -= 1;
952
953 return null;
954 }
955
956 pub fn firstToken(self: &Defer) TokenIndex {
957 return self.defer_token;
958 }
959
960 pub fn lastToken(self: &Defer) TokenIndex {
961 return self.expr.lastToken();
962 }
963 };
964
965 pub const Comptime = struct {
966 base: Node,
967 doc_comments: ?&DocComment,
968 comptime_token: TokenIndex,
969 expr: &Node,
970
971 pub fn iterate(self: &Comptime, index: usize) ?&Node {
972 var i = index;
973
974 if (i < 1) return self.expr;
975 i -= 1;
976
977 return null;
978 }
979
980 pub fn firstToken(self: &Comptime) TokenIndex {
981 return self.comptime_token;
982 }
983
984 pub fn lastToken(self: &Comptime) TokenIndex {
985 return self.expr.lastToken();
986 }
987 };
988
989 pub const Payload = struct {
990 base: Node,
991 lpipe: TokenIndex,
992 error_symbol: &Node,
993 rpipe: TokenIndex,
994
995 pub fn iterate(self: &Payload, index: usize) ?&Node {
996 var i = index;
997
998 if (i < 1) return self.error_symbol;
999 i -= 1;
1000
1001 return null;
1002 }
1003
1004 pub fn firstToken(self: &Payload) TokenIndex {
1005 return self.lpipe;
1006 }
1007
1008 pub fn lastToken(self: &Payload) TokenIndex {
1009 return self.rpipe;
1010 }
1011 };
1012
1013 pub const PointerPayload = struct {
1014 base: Node,
1015 lpipe: TokenIndex,
1016 ptr_token: ?TokenIndex,
1017 value_symbol: &Node,
1018 rpipe: TokenIndex,
1019
1020 pub fn iterate(self: &PointerPayload, index: usize) ?&Node {
1021 var i = index;
1022
1023 if (i < 1) return self.value_symbol;
1024 i -= 1;
1025
1026 return null;
1027 }
1028
1029 pub fn firstToken(self: &PointerPayload) TokenIndex {
1030 return self.lpipe;
1031 }
1032
1033 pub fn lastToken(self: &PointerPayload) TokenIndex {
1034 return self.rpipe;
1035 }
1036 };
1037
1038 pub const PointerIndexPayload = struct {
1039 base: Node,
1040 lpipe: TokenIndex,
1041 ptr_token: ?TokenIndex,
1042 value_symbol: &Node,
1043 index_symbol: ?&Node,
1044 rpipe: TokenIndex,
1045
1046 pub fn iterate(self: &PointerIndexPayload, index: usize) ?&Node {
1047 var i = index;
1048
1049 if (i < 1) return self.value_symbol;
1050 i -= 1;
1051
1052 if (self.index_symbol) |index_symbol| {
1053 if (i < 1) return index_symbol;
1054 i -= 1;
1055 }
1056
1057 return null;
1058 }
1059
1060 pub fn firstToken(self: &PointerIndexPayload) TokenIndex {
1061 return self.lpipe;
1062 }
1063
1064 pub fn lastToken(self: &PointerIndexPayload) TokenIndex {
1065 return self.rpipe;
1066 }
1067 };
1068
1069 pub const Else = struct {
1070 base: Node,
1071 else_token: TokenIndex,
1072 payload: ?&Node,
1073 body: &Node,
1074
1075 pub fn iterate(self: &Else, index: usize) ?&Node {
1076 var i = index;
1077
1078 if (self.payload) |payload| {
1079 if (i < 1) return payload;
1080 i -= 1;
1081 }
1082
1083 if (i < 1) return self.body;
1084 i -= 1;
1085
1086 return null;
1087 }
1088
1089 pub fn firstToken(self: &Else) TokenIndex {
1090 return self.else_token;
1091 }
1092
1093 pub fn lastToken(self: &Else) TokenIndex {
1094 return self.body.lastToken();
1095 }
1096 };
1097
1098 pub const Switch = struct {
1099 base: Node,
1100 switch_token: TokenIndex,
1101 expr: &Node,
1102 /// these can be SwitchCase nodes or LineComment nodes
1103 cases: CaseList,
1104 rbrace: TokenIndex,
1105
1106 pub const CaseList = SegmentedList(&Node, 2);
1107
1108 pub fn iterate(self: &Switch, index: usize) ?&Node {
1109 var i = index;
1110
1111 if (i < 1) return self.expr;
1112 i -= 1;
1113
1114 if (i < self.cases.len) return *self.cases.at(i);
1115 i -= self.cases.len;
1116
1117 return null;
1118 }
1119
1120 pub fn firstToken(self: &Switch) TokenIndex {
1121 return self.switch_token;
1122 }
1123
1124 pub fn lastToken(self: &Switch) TokenIndex {
1125 return self.rbrace;
1126 }
1127 };
1128
1129 pub const SwitchCase = struct {
1130 base: Node,
1131 items: ItemList,
1132 payload: ?&Node,
1133 expr: &Node,
1134
1135 pub const ItemList = SegmentedList(&Node, 1);
1136
1137 pub fn iterate(self: &SwitchCase, index: usize) ?&Node {
1138 var i = index;
1139
1140 if (i < self.items.len) return *self.items.at(i);
1141 i -= self.items.len;
1142
1143 if (self.payload) |payload| {
1144 if (i < 1) return payload;
1145 i -= 1;
1146 }
1147
1148 if (i < 1) return self.expr;
1149 i -= 1;
1150
1151 return null;
1152 }
1153
1154 pub fn firstToken(self: &SwitchCase) TokenIndex {
1155 return (*self.items.at(0)).firstToken();
1156 }
1157
1158 pub fn lastToken(self: &SwitchCase) TokenIndex {
1159 return self.expr.lastToken();
1160 }
1161 };
1162
1163 pub const SwitchElse = struct {
1164 base: Node,
1165 token: TokenIndex,
1166
1167 pub fn iterate(self: &SwitchElse, index: usize) ?&Node {
1168 return null;
1169 }
1170
1171 pub fn firstToken(self: &SwitchElse) TokenIndex {
1172 return self.token;
1173 }
1174
1175 pub fn lastToken(self: &SwitchElse) TokenIndex {
1176 return self.token;
1177 }
1178 };
1179
1180 pub const While = struct {
1181 base: Node,
1182 label: ?TokenIndex,
1183 inline_token: ?TokenIndex,
1184 while_token: TokenIndex,
1185 condition: &Node,
1186 payload: ?&Node,
1187 continue_expr: ?&Node,
1188 body: &Node,
1189 @"else": ?&Else,
1190
1191 pub fn iterate(self: &While, index: usize) ?&Node {
1192 var i = index;
1193
1194 if (i < 1) return self.condition;
1195 i -= 1;
1196
1197 if (self.payload) |payload| {
1198 if (i < 1) return payload;
1199 i -= 1;
1200 }
1201
1202 if (self.continue_expr) |continue_expr| {
1203 if (i < 1) return continue_expr;
1204 i -= 1;
1205 }
1206
1207 if (i < 1) return self.body;
1208 i -= 1;
1209
1210 if (self.@"else") |@"else"| {
1211 if (i < 1) return &@"else".base;
1212 i -= 1;
1213 }
1214
1215 return null;
1216 }
1217
1218 pub fn firstToken(self: &While) TokenIndex {
1219 if (self.label) |label| {
1220 return label;
1221 }
1222
1223 if (self.inline_token) |inline_token| {
1224 return inline_token;
1225 }
1226
1227 return self.while_token;
1228 }
1229
1230 pub fn lastToken(self: &While) TokenIndex {
1231 if (self.@"else") |@"else"| {
1232 return @"else".body.lastToken();
1233 }
1234
1235 return self.body.lastToken();
1236 }
1237 };
1238
1239 pub const For = struct {
1240 base: Node,
1241 label: ?TokenIndex,
1242 inline_token: ?TokenIndex,
1243 for_token: TokenIndex,
1244 array_expr: &Node,
1245 payload: ?&Node,
1246 body: &Node,
1247 @"else": ?&Else,
1248
1249 pub fn iterate(self: &For, index: usize) ?&Node {
1250 var i = index;
1251
1252 if (i < 1) return self.array_expr;
1253 i -= 1;
1254
1255 if (self.payload) |payload| {
1256 if (i < 1) return payload;
1257 i -= 1;
1258 }
1259
1260 if (i < 1) return self.body;
1261 i -= 1;
1262
1263 if (self.@"else") |@"else"| {
1264 if (i < 1) return &@"else".base;
1265 i -= 1;
1266 }
1267
1268 return null;
1269 }
1270
1271 pub fn firstToken(self: &For) TokenIndex {
1272 if (self.label) |label| {
1273 return label;
1274 }
1275
1276 if (self.inline_token) |inline_token| {
1277 return inline_token;
1278 }
1279
1280 return self.for_token;
1281 }
1282
1283 pub fn lastToken(self: &For) TokenIndex {
1284 if (self.@"else") |@"else"| {
1285 return @"else".body.lastToken();
1286 }
1287
1288 return self.body.lastToken();
1289 }
1290 };
1291
1292 pub const If = struct {
1293 base: Node,
1294 if_token: TokenIndex,
1295 condition: &Node,
1296 payload: ?&Node,
1297 body: &Node,
1298 @"else": ?&Else,
1299
1300 pub fn iterate(self: &If, index: usize) ?&Node {
1301 var i = index;
1302
1303 if (i < 1) return self.condition;
1304 i -= 1;
1305
1306 if (self.payload) |payload| {
1307 if (i < 1) return payload;
1308 i -= 1;
1309 }
1310
1311 if (i < 1) return self.body;
1312 i -= 1;
1313
1314 if (self.@"else") |@"else"| {
1315 if (i < 1) return &@"else".base;
1316 i -= 1;
1317 }
1318
1319 return null;
1320 }
1321
1322 pub fn firstToken(self: &If) TokenIndex {
1323 return self.if_token;
1324 }
1325
1326 pub fn lastToken(self: &If) TokenIndex {
1327 if (self.@"else") |@"else"| {
1328 return @"else".body.lastToken();
1329 }
1330
1331 return self.body.lastToken();
1332 }
1333 };
1334
1335 pub const InfixOp = struct {
1336 base: Node,
1337 op_token: TokenIndex,
1338 lhs: &Node,
1339 op: Op,
1340 rhs: &Node,
1341
1342 pub const Op = union(enum) {
1343 Add,
1344 AddWrap,
1345 ArrayCat,
1346 ArrayMult,
1347 Assign,
1348 AssignBitAnd,
1349 AssignBitOr,
1350 AssignBitShiftLeft,
1351 AssignBitShiftRight,
1352 AssignBitXor,
1353 AssignDiv,
1354 AssignMinus,
1355 AssignMinusWrap,
1356 AssignMod,
1357 AssignPlus,
1358 AssignPlusWrap,
1359 AssignTimes,
1360 AssignTimesWarp,
1361 BangEqual,
1362 BitAnd,
1363 BitOr,
1364 BitShiftLeft,
1365 BitShiftRight,
1366 BitXor,
1367 BoolAnd,
1368 BoolOr,
1369 Catch: ?&Node,
1370 Div,
1371 EqualEqual,
1372 ErrorUnion,
1373 GreaterOrEqual,
1374 GreaterThan,
1375 LessOrEqual,
1376 LessThan,
1377 MergeErrorSets,
1378 Mod,
1379 Mult,
1380 MultWrap,
1381 Period,
1382 Range,
1383 Sub,
1384 SubWrap,
1385 UnwrapMaybe,
1386 };
1387
1388 pub fn iterate(self: &InfixOp, index: usize) ?&Node {
1389 var i = index;
1390
1391 if (i < 1) return self.lhs;
1392 i -= 1;
1393
1394 switch (self.op) {
1395 Op.Catch => |maybe_payload| {
1396 if (maybe_payload) |payload| {
1397 if (i < 1) return payload;
1398 i -= 1;
1399 }
1400 },
1401
1402 Op.Add,
1403 Op.AddWrap,
1404 Op.ArrayCat,
1405 Op.ArrayMult,
1406 Op.Assign,
1407 Op.AssignBitAnd,
1408 Op.AssignBitOr,
1409 Op.AssignBitShiftLeft,
1410 Op.AssignBitShiftRight,
1411 Op.AssignBitXor,
1412 Op.AssignDiv,
1413 Op.AssignMinus,
1414 Op.AssignMinusWrap,
1415 Op.AssignMod,
1416 Op.AssignPlus,
1417 Op.AssignPlusWrap,
1418 Op.AssignTimes,
1419 Op.AssignTimesWarp,
1420 Op.BangEqual,
1421 Op.BitAnd,
1422 Op.BitOr,
1423 Op.BitShiftLeft,
1424 Op.BitShiftRight,
1425 Op.BitXor,
1426 Op.BoolAnd,
1427 Op.BoolOr,
1428 Op.Div,
1429 Op.EqualEqual,
1430 Op.ErrorUnion,
1431 Op.GreaterOrEqual,
1432 Op.GreaterThan,
1433 Op.LessOrEqual,
1434 Op.LessThan,
1435 Op.MergeErrorSets,
1436 Op.Mod,
1437 Op.Mult,
1438 Op.MultWrap,
1439 Op.Period,
1440 Op.Range,
1441 Op.Sub,
1442 Op.SubWrap,
1443 Op.UnwrapMaybe => {},
1444 }
1445
1446 if (i < 1) return self.rhs;
1447 i -= 1;
1448
1449 return null;
1450 }
1451
1452 pub fn firstToken(self: &InfixOp) TokenIndex {
1453 return self.lhs.firstToken();
1454 }
1455
1456 pub fn lastToken(self: &InfixOp) TokenIndex {
1457 return self.rhs.lastToken();
1458 }
1459 };
1460
1461 pub const PrefixOp = struct {
1462 base: Node,
1463 op_token: TokenIndex,
1464 op: Op,
1465 rhs: &Node,
1466
1467 const Op = union(enum) {
1468 AddrOf: AddrOfInfo,
1469 ArrayType: &Node,
1470 Await,
1471 BitNot,
1472 BoolNot,
1473 Cancel,
1474 Deref,
1475 MaybeType,
1476 Negation,
1477 NegationWrap,
1478 Resume,
1479 SliceType: AddrOfInfo,
1480 Try,
1481 UnwrapMaybe,
1482 };
1483
1484 const AddrOfInfo = struct {
1485 align_expr: ?&Node,
1486 bit_offset_start_token: ?TokenIndex,
1487 bit_offset_end_token: ?TokenIndex,
1488 const_token: ?TokenIndex,
1489 volatile_token: ?TokenIndex,
1490 };
1491
1492 pub fn iterate(self: &PrefixOp, index: usize) ?&Node {
1493 var i = index;
1494
1495 switch (self.op) {
1496 Op.SliceType => |addr_of_info| {
1497 if (addr_of_info.align_expr) |align_expr| {
1498 if (i < 1) return align_expr;
1499 i -= 1;
1500 }
1501 },
1502 Op.AddrOf => |addr_of_info| {
1503 if (addr_of_info.align_expr) |align_expr| {
1504 if (i < 1) return align_expr;
1505 i -= 1;
1506 }
1507 },
1508 Op.ArrayType => |size_expr| {
1509 if (i < 1) return size_expr;
1510 i -= 1;
1511 },
1512 Op.Await,
1513 Op.BitNot,
1514 Op.BoolNot,
1515 Op.Cancel,
1516 Op.Deref,
1517 Op.MaybeType,
1518 Op.Negation,
1519 Op.NegationWrap,
1520 Op.Try,
1521 Op.Resume,
1522 Op.UnwrapMaybe => {},
1523 }
1524
1525 if (i < 1) return self.rhs;
1526 i -= 1;
1527
1528 return null;
1529 }
1530
1531 pub fn firstToken(self: &PrefixOp) TokenIndex {
1532 return self.op_token;
1533 }
1534
1535 pub fn lastToken(self: &PrefixOp) TokenIndex {
1536 return self.rhs.lastToken();
1537 }
1538 };
1539
1540 pub const FieldInitializer = struct {
1541 base: Node,
1542 period_token: TokenIndex,
1543 name_token: TokenIndex,
1544 expr: &Node,
1545
1546 pub fn iterate(self: &FieldInitializer, index: usize) ?&Node {
1547 var i = index;
1548
1549 if (i < 1) return self.expr;
1550 i -= 1;
1551
1552 return null;
1553 }
1554
1555 pub fn firstToken(self: &FieldInitializer) TokenIndex {
1556 return self.period_token;
1557 }
1558
1559 pub fn lastToken(self: &FieldInitializer) TokenIndex {
1560 return self.expr.lastToken();
1561 }
1562 };
1563
1564 pub const SuffixOp = struct {
1565 base: Node,
1566 lhs: &Node,
1567 op: Op,
1568 rtoken: TokenIndex,
1569
1570 pub const Op = union(enum) {
1571 Call: Call,
1572 ArrayAccess: &Node,
1573 Slice: Slice,
1574 ArrayInitializer: InitList,
1575 StructInitializer: InitList,
1576
1577 pub const InitList = SegmentedList(&Node, 2);
1578
1579 pub const Call = struct {
1580 params: ParamList,
1581 async_attr: ?&AsyncAttribute,
1582
1583 pub const ParamList = SegmentedList(&Node, 2);
1584 };
1585
1586 pub const Slice = struct {
1587 start: &Node,
1588 end: ?&Node,
1589 };
1590 };
1591
1592 pub fn iterate(self: &SuffixOp, index: usize) ?&Node {
1593 var i = index;
1594
1595 if (i < 1) return self.lhs;
1596 i -= 1;
1597
1598 switch (self.op) {
1599 Op.Call => |call_info| {
1600 if (i < call_info.params.len) return *call_info.params.at(i);
1601 i -= call_info.params.len;
1602 },
1603 Op.ArrayAccess => |index_expr| {
1604 if (i < 1) return index_expr;
1605 i -= 1;
1606 },
1607 Op.Slice => |range| {
1608 if (i < 1) return range.start;
1609 i -= 1;
1610
1611 if (range.end) |end| {
1612 if (i < 1) return end;
1613 i -= 1;
1614 }
1615 },
1616 Op.ArrayInitializer => |exprs| {
1617 if (i < exprs.len) return *exprs.at(i);
1618 i -= exprs.len;
1619 },
1620 Op.StructInitializer => |fields| {
1621 if (i < fields.len) return *fields.at(i);
1622 i -= fields.len;
1623 },
1624 }
1625
1626 return null;
1627 }
1628
1629 pub fn firstToken(self: &SuffixOp) TokenIndex {
1630 return self.lhs.firstToken();
1631 }
1632
1633 pub fn lastToken(self: &SuffixOp) TokenIndex {
1634 return self.rtoken;
1635 }
1636 };
1637
1638 pub const GroupedExpression = struct {
1639 base: Node,
1640 lparen: TokenIndex,
1641 expr: &Node,
1642 rparen: TokenIndex,
1643
1644 pub fn iterate(self: &GroupedExpression, index: usize) ?&Node {
1645 var i = index;
1646
1647 if (i < 1) return self.expr;
1648 i -= 1;
1649
1650 return null;
1651 }
1652
1653 pub fn firstToken(self: &GroupedExpression) TokenIndex {
1654 return self.lparen;
1655 }
1656
1657 pub fn lastToken(self: &GroupedExpression) TokenIndex {
1658 return self.rparen;
1659 }
1660 };
1661
1662 pub const ControlFlowExpression = struct {
1663 base: Node,
1664 ltoken: TokenIndex,
1665 kind: Kind,
1666 rhs: ?&Node,
1667
1668 const Kind = union(enum) {
1669 Break: ?&Node,
1670 Continue: ?&Node,
1671 Return,
1672 };
1673
1674 pub fn iterate(self: &ControlFlowExpression, index: usize) ?&Node {
1675 var i = index;
1676
1677 switch (self.kind) {
1678 Kind.Break => |maybe_label| {
1679 if (maybe_label) |label| {
1680 if (i < 1) return label;
1681 i -= 1;
1682 }
1683 },
1684 Kind.Continue => |maybe_label| {
1685 if (maybe_label) |label| {
1686 if (i < 1) return label;
1687 i -= 1;
1688 }
1689 },
1690 Kind.Return => {},
1691 }
1692
1693 if (self.rhs) |rhs| {
1694 if (i < 1) return rhs;
1695 i -= 1;
1696 }
1697
1698 return null;
1699 }
1700
1701 pub fn firstToken(self: &ControlFlowExpression) TokenIndex {
1702 return self.ltoken;
1703 }
1704
1705 pub fn lastToken(self: &ControlFlowExpression) TokenIndex {
1706 if (self.rhs) |rhs| {
1707 return rhs.lastToken();
1708 }
1709
1710 switch (self.kind) {
1711 Kind.Break => |maybe_label| {
1712 if (maybe_label) |label| {
1713 return label.lastToken();
1714 }
1715 },
1716 Kind.Continue => |maybe_label| {
1717 if (maybe_label) |label| {
1718 return label.lastToken();
1719 }
1720 },
1721 Kind.Return => return self.ltoken,
1722 }
1723
1724 return self.ltoken;
1725 }
1726 };
1727
1728 pub const Suspend = struct {
1729 base: Node,
1730 label: ?TokenIndex,
1731 suspend_token: TokenIndex,
1732 payload: ?&Node,
1733 body: ?&Node,
1734
1735 pub fn iterate(self: &Suspend, index: usize) ?&Node {
1736 var i = index;
1737
1738 if (self.payload) |payload| {
1739 if (i < 1) return payload;
1740 i -= 1;
1741 }
1742
1743 if (self.body) |body| {
1744 if (i < 1) return body;
1745 i -= 1;
1746 }
1747
1748 return null;
1749 }
1750
1751 pub fn firstToken(self: &Suspend) TokenIndex {
1752 if (self.label) |label| return label;
1753 return self.suspend_token;
1754 }
1755
1756 pub fn lastToken(self: &Suspend) TokenIndex {
1757 if (self.body) |body| {
1758 return body.lastToken();
1759 }
1760
1761 if (self.payload) |payload| {
1762 return payload.lastToken();
1763 }
1764
1765 return self.suspend_token;
1766 }
1767 };
1768
1769 pub const IntegerLiteral = struct {
1770 base: Node,
1771 token: TokenIndex,
1772
1773 pub fn iterate(self: &IntegerLiteral, index: usize) ?&Node {
1774 return null;
1775 }
1776
1777 pub fn firstToken(self: &IntegerLiteral) TokenIndex {
1778 return self.token;
1779 }
1780
1781 pub fn lastToken(self: &IntegerLiteral) TokenIndex {
1782 return self.token;
1783 }
1784 };
1785
1786 pub const FloatLiteral = struct {
1787 base: Node,
1788 token: TokenIndex,
1789
1790 pub fn iterate(self: &FloatLiteral, index: usize) ?&Node {
1791 return null;
1792 }
1793
1794 pub fn firstToken(self: &FloatLiteral) TokenIndex {
1795 return self.token;
1796 }
1797
1798 pub fn lastToken(self: &FloatLiteral) TokenIndex {
1799 return self.token;
1800 }
1801 };
1802
1803 pub const BuiltinCall = struct {
1804 base: Node,
1805 builtin_token: TokenIndex,
1806 params: ParamList,
1807 rparen_token: TokenIndex,
1808
1809 pub const ParamList = SegmentedList(&Node, 2);
1810
1811 pub fn iterate(self: &BuiltinCall, index: usize) ?&Node {
1812 var i = index;
1813
1814 if (i < self.params.len) return *self.params.at(i);
1815 i -= self.params.len;
1816
1817 return null;
1818 }
1819
1820 pub fn firstToken(self: &BuiltinCall) TokenIndex {
1821 return self.builtin_token;
1822 }
1823
1824 pub fn lastToken(self: &BuiltinCall) TokenIndex {
1825 return self.rparen_token;
1826 }
1827 };
1828
1829 pub const StringLiteral = struct {
1830 base: Node,
1831 token: TokenIndex,
1832
1833 pub fn iterate(self: &StringLiteral, index: usize) ?&Node {
1834 return null;
1835 }
1836
1837 pub fn firstToken(self: &StringLiteral) TokenIndex {
1838 return self.token;
1839 }
1840
1841 pub fn lastToken(self: &StringLiteral) TokenIndex {
1842 return self.token;
1843 }
1844 };
1845
1846 pub const MultilineStringLiteral = struct {
1847 base: Node,
1848 lines: LineList,
1849
1850 pub const LineList = SegmentedList(TokenIndex, 4);
1851
1852 pub fn iterate(self: &MultilineStringLiteral, index: usize) ?&Node {
1853 return null;
1854 }
1855
1856 pub fn firstToken(self: &MultilineStringLiteral) TokenIndex {
1857 return *self.lines.at(0);
1858 }
1859
1860 pub fn lastToken(self: &MultilineStringLiteral) TokenIndex {
1861 return *self.lines.at(self.lines.len - 1);
1862 }
1863 };
1864
1865 pub const CharLiteral = struct {
1866 base: Node,
1867 token: TokenIndex,
1868
1869 pub fn iterate(self: &CharLiteral, index: usize) ?&Node {
1870 return null;
1871 }
1872
1873 pub fn firstToken(self: &CharLiteral) TokenIndex {
1874 return self.token;
1875 }
1876
1877 pub fn lastToken(self: &CharLiteral) TokenIndex {
1878 return self.token;
1879 }
1880 };
1881
1882 pub const BoolLiteral = struct {
1883 base: Node,
1884 token: TokenIndex,
1885
1886 pub fn iterate(self: &BoolLiteral, index: usize) ?&Node {
1887 return null;
1888 }
1889
1890 pub fn firstToken(self: &BoolLiteral) TokenIndex {
1891 return self.token;
1892 }
1893
1894 pub fn lastToken(self: &BoolLiteral) TokenIndex {
1895 return self.token;
1896 }
1897 };
1898
1899 pub const NullLiteral = struct {
1900 base: Node,
1901 token: TokenIndex,
1902
1903 pub fn iterate(self: &NullLiteral, index: usize) ?&Node {
1904 return null;
1905 }
1906
1907 pub fn firstToken(self: &NullLiteral) TokenIndex {
1908 return self.token;
1909 }
1910
1911 pub fn lastToken(self: &NullLiteral) TokenIndex {
1912 return self.token;
1913 }
1914 };
1915
1916 pub const UndefinedLiteral = struct {
1917 base: Node,
1918 token: TokenIndex,
1919
1920 pub fn iterate(self: &UndefinedLiteral, index: usize) ?&Node {
1921 return null;
1922 }
1923
1924 pub fn firstToken(self: &UndefinedLiteral) TokenIndex {
1925 return self.token;
1926 }
1927
1928 pub fn lastToken(self: &UndefinedLiteral) TokenIndex {
1929 return self.token;
1930 }
1931 };
1932
1933 pub const ThisLiteral = struct {
1934 base: Node,
1935 token: TokenIndex,
1936
1937 pub fn iterate(self: &ThisLiteral, index: usize) ?&Node {
1938 return null;
1939 }
1940
1941 pub fn firstToken(self: &ThisLiteral) TokenIndex {
1942 return self.token;
1943 }
1944
1945 pub fn lastToken(self: &ThisLiteral) TokenIndex {
1946 return self.token;
1947 }
1948 };
1949
1950 pub const AsmOutput = struct {
1951 base: Node,
1952 symbolic_name: &Node,
1953 constraint: &Node,
1954 kind: Kind,
1955
1956 const Kind = union(enum) {
1957 Variable: &Identifier,
1958 Return: &Node
1959 };
1960
1961 pub fn iterate(self: &AsmOutput, index: usize) ?&Node {
1962 var i = index;
1963
1964 if (i < 1) return self.symbolic_name;
1965 i -= 1;
1966
1967 if (i < 1) return self.constraint;
1968 i -= 1;
1969
1970 switch (self.kind) {
1971 Kind.Variable => |variable_name| {
1972 if (i < 1) return &variable_name.base;
1973 i -= 1;
1974 },
1975 Kind.Return => |return_type| {
1976 if (i < 1) return return_type;
1977 i -= 1;
1978 }
1979 }
1980
1981 return null;
1982 }
1983
1984 pub fn firstToken(self: &AsmOutput) TokenIndex {
1985 return self.symbolic_name.firstToken();
1986 }
1987
1988 pub fn lastToken(self: &AsmOutput) TokenIndex {
1989 return switch (self.kind) {
1990 Kind.Variable => |variable_name| variable_name.lastToken(),
1991 Kind.Return => |return_type| return_type.lastToken(),
1992 };
1993 }
1994 };
1995
1996 pub const AsmInput = struct {
1997 base: Node,
1998 symbolic_name: &Node,
1999 constraint: &Node,
2000 expr: &Node,
2001
2002 pub fn iterate(self: &AsmInput, index: usize) ?&Node {
2003 var i = index;
2004
2005 if (i < 1) return self.symbolic_name;
2006 i -= 1;
2007
2008 if (i < 1) return self.constraint;
2009 i -= 1;
2010
2011 if (i < 1) return self.expr;
2012 i -= 1;
2013
2014 return null;
2015 }
2016
2017 pub fn firstToken(self: &AsmInput) TokenIndex {
2018 return self.symbolic_name.firstToken();
2019 }
2020
2021 pub fn lastToken(self: &AsmInput) TokenIndex {
2022 return self.expr.lastToken();
2023 }
2024 };
2025
2026 pub const Asm = struct {
2027 base: Node,
2028 asm_token: TokenIndex,
2029 volatile_token: ?TokenIndex,
2030 template: &Node,
2031 outputs: OutputList,
2032 inputs: InputList,
2033 clobbers: ClobberList,
2034 rparen: TokenIndex,
2035
2036 const OutputList = SegmentedList(&AsmOutput, 2);
2037 const InputList = SegmentedList(&AsmInput, 2);
2038 const ClobberList = SegmentedList(&Node, 2);
2039
2040 pub fn iterate(self: &Asm, index: usize) ?&Node {
2041 var i = index;
2042
2043 if (i < self.outputs.len) return &(*self.outputs.at(index)).base;
2044 i -= self.outputs.len;
2045
2046 if (i < self.inputs.len) return &(*self.inputs.at(index)).base;
2047 i -= self.inputs.len;
2048
2049 if (i < self.clobbers.len) return *self.clobbers.at(index);
2050 i -= self.clobbers.len;
2051
2052 return null;
2053 }
2054
2055 pub fn firstToken(self: &Asm) TokenIndex {
2056 return self.asm_token;
2057 }
2058
2059 pub fn lastToken(self: &Asm) TokenIndex {
2060 return self.rparen;
2061 }
2062 };
2063
2064 pub const Unreachable = struct {
2065 base: Node,
2066 token: TokenIndex,
2067
2068 pub fn iterate(self: &Unreachable, index: usize) ?&Node {
2069 return null;
2070 }
2071
2072 pub fn firstToken(self: &Unreachable) TokenIndex {
2073 return self.token;
2074 }
2075
2076 pub fn lastToken(self: &Unreachable) TokenIndex {
2077 return self.token;
2078 }
2079 };
2080
2081 pub const ErrorType = struct {
2082 base: Node,
2083 token: TokenIndex,
2084
2085 pub fn iterate(self: &ErrorType, index: usize) ?&Node {
2086 return null;
2087 }
2088
2089 pub fn firstToken(self: &ErrorType) TokenIndex {
2090 return self.token;
2091 }
2092
2093 pub fn lastToken(self: &ErrorType) TokenIndex {
2094 return self.token;
2095 }
2096 };
2097
2098 pub const VarType = struct {
2099 base: Node,
2100 token: TokenIndex,
2101
2102 pub fn iterate(self: &VarType, index: usize) ?&Node {
2103 return null;
2104 }
2105
2106 pub fn firstToken(self: &VarType) TokenIndex {
2107 return self.token;
2108 }
2109
2110 pub fn lastToken(self: &VarType) TokenIndex {
2111 return self.token;
2112 }
2113 };
2114
2115 pub const LineComment = struct {
2116 base: Node,
2117 token: TokenIndex,
2118
2119 pub fn iterate(self: &LineComment, index: usize) ?&Node {
2120 return null;
2121 }
2122
2123 pub fn firstToken(self: &LineComment) TokenIndex {
2124 return self.token;
2125 }
2126
2127 pub fn lastToken(self: &LineComment) TokenIndex {
2128 return self.token;
2129 }
2130 };
2131
2132 pub const DocComment = struct {
2133 base: Node,
2134 lines: LineList,
2135
2136 pub const LineList = SegmentedList(TokenIndex, 4);
2137
2138 pub fn iterate(self: &DocComment, index: usize) ?&Node {
2139 return null;
2140 }
2141
2142 pub fn firstToken(self: &DocComment) TokenIndex {
2143 return *self.lines.at(0);
2144 }
2145
2146 pub fn lastToken(self: &DocComment) TokenIndex {
2147 return *self.lines.at(self.lines.len - 1);
2148 }
2149 };
2150
2151 pub const TestDecl = struct {
2152 base: Node,
2153 doc_comments: ?&DocComment,
2154 test_token: TokenIndex,
2155 name: &Node,
2156 body_node: &Node,
2157
2158 pub fn iterate(self: &TestDecl, index: usize) ?&Node {
2159 var i = index;
2160
2161 if (i < 1) return self.body_node;
2162 i -= 1;
2163
2164 return null;
2165 }
2166
2167 pub fn firstToken(self: &TestDecl) TokenIndex {
2168 return self.test_token;
2169 }
2170
2171 pub fn lastToken(self: &TestDecl) TokenIndex {
2172 return self.body_node.lastToken();
2173 }
2174 };
646};2175};
6472176
std/zig/bench.zig created+38
...@@ -0,0 +1,38 @@
1const std = @import("std");
2const mem = std.mem;
3const warn = std.debug.warn;
4const Tokenizer = std.zig.Tokenizer;
5const Parser = std.zig.Parser;
6const io = std.io;
7
8const source = @embedFile("../os/index.zig");
9var fixed_buffer_mem: [10 * 1024 * 1024]u8 = undefined;
10
11pub fn main() !void {
12 var i: usize = 0;
13 var timer = try std.os.time.Timer.start();
14 const start = timer.lap();
15 const iterations = 100;
16 var memory_used: usize = 0;
17 while (i < iterations) : (i += 1) {
18 memory_used += testOnce();
19 }
20 const end = timer.read();
21 memory_used /= iterations;
22 const elapsed_s = f64(end - start) / std.os.time.ns_per_s;
23 const bytes_per_sec = f64(source.len * iterations) / elapsed_s;
24 const mb_per_sec = bytes_per_sec / (1024 * 1024);
25
26 var stdout_file = try std.io.getStdOut();
27 const stdout = &std.io.FileOutStream.init(&stdout_file).stream;
28 try stdout.print("{.3} MB/s, {} KB used \n", mb_per_sec, memory_used / 1024);
29}
30
31fn testOnce() usize {
32 var fixed_buf_alloc = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
33 var allocator = &fixed_buf_alloc.allocator;
34 var tokenizer = Tokenizer.init(source);
35 var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
36 _ = parser.parse() catch @panic("parse failure");
37 return fixed_buf_alloc.end_index;
38}
std/zig/index.zig+5-3
...@@ -1,11 +1,13 @@...@@ -1,11 +1,13 @@
1const tokenizer = @import("tokenizer.zig");1const tokenizer = @import("tokenizer.zig");
2pub const Token = tokenizer.Token;2pub const Token = tokenizer.Token;
3pub const Tokenizer = tokenizer.Tokenizer;3pub const Tokenizer = tokenizer.Tokenizer;
4pub const Parser = @import("parser.zig").Parser;4pub const parse = @import("parse.zig").parse;
5pub const render = @import("render.zig").render;
5pub const ast = @import("ast.zig");6pub const ast = @import("ast.zig");
67
7test "std.zig tests" {8test "std.zig tests" {
8 _ = @import("tokenizer.zig");
9 _ = @import("parser.zig");
10 _ = @import("ast.zig");9 _ = @import("ast.zig");
10 _ = @import("parse.zig");
11 _ = @import("render.zig");
12 _ = @import("tokenizer.zig");
11}13}
std/zig/parse.zig created+3503
...@@ -0,0 +1,3503 @@
1const std = @import("../index.zig");
2const assert = std.debug.assert;
3const mem = std.mem;
4const ast = std.zig.ast;
5const Tokenizer = std.zig.Tokenizer;
6const Token = std.zig.Token;
7const TokenIndex = ast.TokenIndex;
8const Error = ast.Error;
9
10/// Returns an AST tree, allocated with the parser's allocator.
11/// Result should be freed with tree.deinit() when there are
12/// no more references to any AST nodes of the tree.
13pub fn parse(allocator: &mem.Allocator, source: []const u8) !ast.Tree {
14 var tree_arena = std.heap.ArenaAllocator.init(allocator);
15 errdefer tree_arena.deinit();
16
17 var stack = std.ArrayList(State).init(allocator);
18 defer stack.deinit();
19
20 const arena = &tree_arena.allocator;
21 const root_node = try createNode(arena, ast.Node.Root,
22 ast.Node.Root {
23 .base = undefined,
24 .decls = ast.Node.Root.DeclList.init(arena),
25 .doc_comments = null,
26 // initialized when we get the eof token
27 .eof_token = undefined,
28 }
29 );
30
31 var tree = ast.Tree {
32 .source = source,
33 .root_node = root_node,
34 .arena_allocator = tree_arena,
35 .tokens = ast.Tree.TokenList.init(arena),
36 .errors = ast.Tree.ErrorList.init(arena),
37 };
38
39 var tokenizer = Tokenizer.init(tree.source);
40 while (true) {
41 const token_ptr = try tree.tokens.addOne();
42 *token_ptr = tokenizer.next();
43 if (token_ptr.id == Token.Id.Eof)
44 break;
45 }
46 var tok_it = tree.tokens.iterator(0);
47
48 try stack.append(State.TopLevel);
49
50 while (true) {
51 // This gives us 1 free push that can't fail
52 const state = stack.pop();
53
54 switch (state) {
55 State.TopLevel => {
56 while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| {
57 try root_node.decls.push(&line_comment.base);
58 }
59
60 const comments = try eatDocComments(arena, &tok_it, &tree);
61
62 const token = nextToken(&tok_it, &tree);
63 const token_index = token.index;
64 const token_ptr = token.ptr;
65 switch (token_ptr.id) {
66 Token.Id.Keyword_test => {
67 stack.append(State.TopLevel) catch unreachable;
68
69 const block = try arena.construct(ast.Node.Block {
70 .base = ast.Node {
71 .id = ast.Node.Id.Block,
72 },
73 .label = null,
74 .lbrace = undefined,
75 .statements = ast.Node.Block.StatementList.init(arena),
76 .rbrace = undefined,
77 });
78 const test_node = try arena.construct(ast.Node.TestDecl {
79 .base = ast.Node {
80 .id = ast.Node.Id.TestDecl,
81 },
82 .doc_comments = comments,
83 .test_token = token_index,
84 .name = undefined,
85 .body_node = &block.base,
86 });
87 try root_node.decls.push(&test_node.base);
88 try stack.append(State { .Block = block });
89 try stack.append(State {
90 .ExpectTokenSave = ExpectTokenSave {
91 .id = Token.Id.LBrace,
92 .ptr = &block.rbrace,
93 }
94 });
95 try stack.append(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } });
96 continue;
97 },
98 Token.Id.Eof => {
99 root_node.eof_token = token_index;
100 root_node.doc_comments = comments;
101 return tree;
102 },
103 Token.Id.Keyword_pub => {
104 stack.append(State.TopLevel) catch unreachable;
105 try stack.append(State {
106 .TopLevelExtern = TopLevelDeclCtx {
107 .decls = &root_node.decls,
108 .visib_token = token_index,
109 .extern_export_inline_token = null,
110 .lib_name = null,
111 .comments = comments,
112 }
113 });
114 continue;
115 },
116 Token.Id.Keyword_comptime => {
117 const block = try createNode(arena, ast.Node.Block,
118 ast.Node.Block {
119 .base = undefined,
120 .label = null,
121 .lbrace = undefined,
122 .statements = ast.Node.Block.StatementList.init(arena),
123 .rbrace = undefined,
124 }
125 );
126 const node = try arena.construct(ast.Node.Comptime {
127 .base = ast.Node {
128 .id = ast.Node.Id.Comptime,
129 },
130 .comptime_token = token_index,
131 .expr = &block.base,
132 .doc_comments = comments,
133 });
134 try root_node.decls.push(&node.base);
135
136 stack.append(State.TopLevel) catch unreachable;
137 try stack.append(State { .Block = block });
138 try stack.append(State {
139 .ExpectTokenSave = ExpectTokenSave {
140 .id = Token.Id.LBrace,
141 .ptr = &block.rbrace,
142 }
143 });
144 continue;
145 },
146 else => {
147 putBackToken(&tok_it, &tree);
148 stack.append(State.TopLevel) catch unreachable;
149 try stack.append(State {
150 .TopLevelExtern = TopLevelDeclCtx {
151 .decls = &root_node.decls,
152 .visib_token = null,
153 .extern_export_inline_token = null,
154 .lib_name = null,
155 .comments = comments,
156 }
157 });
158 continue;
159 },
160 }
161 },
162 State.TopLevelExtern => |ctx| {
163 const token = nextToken(&tok_it, &tree);
164 const token_index = token.index;
165 const token_ptr = token.ptr;
166 switch (token_ptr.id) {
167 Token.Id.Keyword_export, Token.Id.Keyword_inline => {
168 stack.append(State {
169 .TopLevelDecl = TopLevelDeclCtx {
170 .decls = ctx.decls,
171 .visib_token = ctx.visib_token,
172 .extern_export_inline_token = AnnotatedToken {
173 .index = token_index,
174 .ptr = token_ptr,
175 },
176 .lib_name = null,
177 .comments = ctx.comments,
178 },
179 }) catch unreachable;
180 continue;
181 },
182 Token.Id.Keyword_extern => {
183 stack.append(State {
184 .TopLevelLibname = TopLevelDeclCtx {
185 .decls = ctx.decls,
186 .visib_token = ctx.visib_token,
187 .extern_export_inline_token = AnnotatedToken {
188 .index = token_index,
189 .ptr = token_ptr,
190 },
191 .lib_name = null,
192 .comments = ctx.comments,
193 },
194 }) catch unreachable;
195 continue;
196 },
197 else => {
198 putBackToken(&tok_it, &tree);
199 stack.append(State { .TopLevelDecl = ctx }) catch unreachable;
200 continue;
201 }
202 }
203 },
204 State.TopLevelLibname => |ctx| {
205 const lib_name = blk: {
206 const lib_name_token = nextToken(&tok_it, &tree);
207 const lib_name_token_index = lib_name_token.index;
208 const lib_name_token_ptr = lib_name_token.ptr;
209 break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index, &tree)) ?? {
210 putBackToken(&tok_it, &tree);
211 break :blk null;
212 };
213 };
214
215 stack.append(State {
216 .TopLevelDecl = TopLevelDeclCtx {
217 .decls = ctx.decls,
218 .visib_token = ctx.visib_token,
219 .extern_export_inline_token = ctx.extern_export_inline_token,
220 .lib_name = lib_name,
221 .comments = ctx.comments,
222 },
223 }) catch unreachable;
224 continue;
225 },
226 State.TopLevelDecl => |ctx| {
227 const token = nextToken(&tok_it, &tree);
228 const token_index = token.index;
229 const token_ptr = token.ptr;
230 switch (token_ptr.id) {
231 Token.Id.Keyword_use => {
232 if (ctx.extern_export_inline_token) |annotated_token| {
233 *(try tree.errors.addOne()) = Error {
234 .InvalidToken = Error.InvalidToken { .token = annotated_token.index },
235 };
236 return tree;
237 }
238
239 const node = try arena.construct(ast.Node.Use {
240 .base = ast.Node {.id = ast.Node.Id.Use },
241 .visib_token = ctx.visib_token,
242 .expr = undefined,
243 .semicolon_token = undefined,
244 .doc_comments = ctx.comments,
245 });
246 try ctx.decls.push(&node.base);
247
248 stack.append(State {
249 .ExpectTokenSave = ExpectTokenSave {
250 .id = Token.Id.Semicolon,
251 .ptr = &node.semicolon_token,
252 }
253 }) catch unreachable;
254 try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
255 continue;
256 },
257 Token.Id.Keyword_var, Token.Id.Keyword_const => {
258 if (ctx.extern_export_inline_token) |annotated_token| {
259 if (annotated_token.ptr.id == Token.Id.Keyword_inline) {
260 *(try tree.errors.addOne()) = Error {
261 .InvalidToken = Error.InvalidToken { .token = annotated_token.index },
262 };
263 return tree;
264 }
265 }
266
267 try stack.append(State {
268 .VarDecl = VarDeclCtx {
269 .comments = ctx.comments,
270 .visib_token = ctx.visib_token,
271 .lib_name = ctx.lib_name,
272 .comptime_token = null,
273 .extern_export_token = if (ctx.extern_export_inline_token) |at| at.index else null,
274 .mut_token = token_index,
275 .list = ctx.decls
276 }
277 });
278 continue;
279 },
280 Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc,
281 Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => {
282 const fn_proto = try arena.construct(ast.Node.FnProto {
283 .base = ast.Node {
284 .id = ast.Node.Id.FnProto,
285 },
286 .doc_comments = ctx.comments,
287 .visib_token = ctx.visib_token,
288 .name_token = null,
289 .fn_token = undefined,
290 .params = ast.Node.FnProto.ParamList.init(arena),
291 .return_type = undefined,
292 .var_args_token = null,
293 .extern_export_inline_token = if (ctx.extern_export_inline_token) |at| at.index else null,
294 .cc_token = null,
295 .async_attr = null,
296 .body_node = null,
297 .lib_name = ctx.lib_name,
298 .align_expr = null,
299 });
300 try ctx.decls.push(&fn_proto.base);
301 stack.append(State { .FnDef = fn_proto }) catch unreachable;
302 try stack.append(State { .FnProto = fn_proto });
303
304 switch (token_ptr.id) {
305 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
306 fn_proto.cc_token = token_index;
307 try stack.append(State {
308 .ExpectTokenSave = ExpectTokenSave {
309 .id = Token.Id.Keyword_fn,
310 .ptr = &fn_proto.fn_token,
311 }
312 });
313 continue;
314 },
315 Token.Id.Keyword_async => {
316 const async_node = try createNode(arena, ast.Node.AsyncAttribute,
317 ast.Node.AsyncAttribute {
318 .base = undefined,
319 .async_token = token_index,
320 .allocator_type = null,
321 .rangle_bracket = null,
322 }
323 );
324 fn_proto.async_attr = async_node;
325
326 try stack.append(State {
327 .ExpectTokenSave = ExpectTokenSave {
328 .id = Token.Id.Keyword_fn,
329 .ptr = &fn_proto.fn_token,
330 }
331 });
332 try stack.append(State { .AsyncAllocator = async_node });
333 continue;
334 },
335 Token.Id.Keyword_fn => {
336 fn_proto.fn_token = token_index;
337 continue;
338 },
339 else => unreachable,
340 }
341 },
342 else => {
343 *(try tree.errors.addOne()) = Error {
344 .ExpectedVarDeclOrFn = Error.ExpectedVarDeclOrFn { .token = token_index },
345 };
346 return tree;
347 },
348 }
349 },
350 State.TopLevelExternOrField => |ctx| {
351 if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |identifier| {
352 std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct);
353 const node = try arena.construct(ast.Node.StructField {
354 .base = ast.Node {
355 .id = ast.Node.Id.StructField,
356 },
357 .doc_comments = ctx.comments,
358 .visib_token = ctx.visib_token,
359 .name_token = identifier,
360 .type_expr = undefined,
361 });
362 const node_ptr = try ctx.container_decl.fields_and_decls.addOne();
363 *node_ptr = &node.base;
364
365 stack.append(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable;
366 try stack.append(State { .Expression = OptionalCtx { .Required = &node.type_expr } });
367 try stack.append(State { .ExpectToken = Token.Id.Colon });
368 continue;
369 }
370
371 stack.append(State{ .ContainerDecl = ctx.container_decl }) catch unreachable;
372 try stack.append(State {
373 .TopLevelExtern = TopLevelDeclCtx {
374 .decls = &ctx.container_decl.fields_and_decls,
375 .visib_token = ctx.visib_token,
376 .extern_export_inline_token = null,
377 .lib_name = null,
378 .comments = ctx.comments,
379 }
380 });
381 continue;
382 },
383
384 State.FieldInitValue => |ctx| {
385 const eq_tok = nextToken(&tok_it, &tree);
386 const eq_tok_index = eq_tok.index;
387 const eq_tok_ptr = eq_tok.ptr;
388 if (eq_tok_ptr.id != Token.Id.Equal) {
389 putBackToken(&tok_it, &tree);
390 continue;
391 }
392 stack.append(State { .Expression = ctx }) catch unreachable;
393 continue;
394 },
395
396 State.ContainerKind => |ctx| {
397 const token = nextToken(&tok_it, &tree);
398 const token_index = token.index;
399 const token_ptr = token.ptr;
400 const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl,
401 ast.Node.ContainerDecl {
402 .base = undefined,
403 .ltoken = ctx.ltoken,
404 .layout = ctx.layout,
405 .kind = switch (token_ptr.id) {
406 Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct,
407 Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union,
408 Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum,
409 else => {
410 *(try tree.errors.addOne()) = Error {
411 .ExpectedAggregateKw = Error.ExpectedAggregateKw { .token = token_index },
412 };
413 return tree;
414 },
415 },
416 .init_arg_expr = ast.Node.ContainerDecl.InitArg.None,
417 .fields_and_decls = ast.Node.ContainerDecl.DeclList.init(arena),
418 .rbrace_token = undefined,
419 }
420 );
421
422 stack.append(State { .ContainerDecl = node }) catch unreachable;
423 try stack.append(State { .ExpectToken = Token.Id.LBrace });
424 try stack.append(State { .ContainerInitArgStart = node });
425 continue;
426 },
427
428 State.ContainerInitArgStart => |container_decl| {
429 if (eatToken(&tok_it, &tree, Token.Id.LParen) == null) {
430 continue;
431 }
432
433 stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
434 try stack.append(State { .ContainerInitArg = container_decl });
435 continue;
436 },
437
438 State.ContainerInitArg => |container_decl| {
439 const init_arg_token = nextToken(&tok_it, &tree);
440 const init_arg_token_index = init_arg_token.index;
441 const init_arg_token_ptr = init_arg_token.ptr;
442 switch (init_arg_token_ptr.id) {
443 Token.Id.Keyword_enum => {
444 container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg {.Enum = null};
445 const lparen_tok = nextToken(&tok_it, &tree);
446 const lparen_tok_index = lparen_tok.index;
447 const lparen_tok_ptr = lparen_tok.ptr;
448 if (lparen_tok_ptr.id == Token.Id.LParen) {
449 try stack.append(State { .ExpectToken = Token.Id.RParen } );
450 try stack.append(State { .Expression = OptionalCtx {
451 .RequiredNull = &container_decl.init_arg_expr.Enum,
452 } });
453 } else {
454 putBackToken(&tok_it, &tree);
455 }
456 },
457 else => {
458 putBackToken(&tok_it, &tree);
459 container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined };
460 stack.append(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable;
461 },
462 }
463 continue;
464 },
465
466 State.ContainerDecl => |container_decl| {
467 while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| {
468 try container_decl.fields_and_decls.push(&line_comment.base);
469 }
470
471 const comments = try eatDocComments(arena, &tok_it, &tree);
472 const token = nextToken(&tok_it, &tree);
473 const token_index = token.index;
474 const token_ptr = token.ptr;
475 switch (token_ptr.id) {
476 Token.Id.Identifier => {
477 switch (container_decl.kind) {
478 ast.Node.ContainerDecl.Kind.Struct => {
479 const node = try arena.construct(ast.Node.StructField {
480 .base = ast.Node {
481 .id = ast.Node.Id.StructField,
482 },
483 .doc_comments = comments,
484 .visib_token = null,
485 .name_token = token_index,
486 .type_expr = undefined,
487 });
488 const node_ptr = try container_decl.fields_and_decls.addOne();
489 *node_ptr = &node.base;
490
491 try stack.append(State { .FieldListCommaOrEnd = container_decl });
492 try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } });
493 try stack.append(State { .ExpectToken = Token.Id.Colon });
494 continue;
495 },
496 ast.Node.ContainerDecl.Kind.Union => {
497 const node = try arena.construct(ast.Node.UnionTag {
498 .base = ast.Node {.id = ast.Node.Id.UnionTag },
499 .name_token = token_index,
500 .type_expr = null,
501 .value_expr = null,
502 .doc_comments = comments,
503 });
504 try container_decl.fields_and_decls.push(&node.base);
505
506 stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
507 try stack.append(State { .FieldInitValue = OptionalCtx { .RequiredNull = &node.value_expr } });
508 try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } });
509 try stack.append(State { .IfToken = Token.Id.Colon });
510 continue;
511 },
512 ast.Node.ContainerDecl.Kind.Enum => {
513 const node = try arena.construct(ast.Node.EnumTag {
514 .base = ast.Node { .id = ast.Node.Id.EnumTag },
515 .name_token = token_index,
516 .value = null,
517 .doc_comments = comments,
518 });
519 try container_decl.fields_and_decls.push(&node.base);
520
521 stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
522 try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &node.value } });
523 try stack.append(State { .IfToken = Token.Id.Equal });
524 continue;
525 },
526 }
527 },
528 Token.Id.Keyword_pub => {
529 switch (container_decl.kind) {
530 ast.Node.ContainerDecl.Kind.Struct => {
531 try stack.append(State {
532 .TopLevelExternOrField = TopLevelExternOrFieldCtx {
533 .visib_token = token_index,
534 .container_decl = container_decl,
535 .comments = comments,
536 }
537 });
538 continue;
539 },
540 else => {
541 stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
542 try stack.append(State {
543 .TopLevelExtern = TopLevelDeclCtx {
544 .decls = &container_decl.fields_and_decls,
545 .visib_token = token_index,
546 .extern_export_inline_token = null,
547 .lib_name = null,
548 .comments = comments,
549 }
550 });
551 continue;
552 }
553 }
554 },
555 Token.Id.Keyword_export => {
556 stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
557 try stack.append(State {
558 .TopLevelExtern = TopLevelDeclCtx {
559 .decls = &container_decl.fields_and_decls,
560 .visib_token = token_index,
561 .extern_export_inline_token = null,
562 .lib_name = null,
563 .comments = comments,
564 }
565 });
566 continue;
567 },
568 Token.Id.RBrace => {
569 if (comments != null) {
570 *(try tree.errors.addOne()) = Error {
571 .UnattachedDocComment = Error.UnattachedDocComment { .token = token_index },
572 };
573 return tree;
574 }
575 container_decl.rbrace_token = token_index;
576 continue;
577 },
578 else => {
579 putBackToken(&tok_it, &tree);
580 stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
581 try stack.append(State {
582 .TopLevelExtern = TopLevelDeclCtx {
583 .decls = &container_decl.fields_and_decls,
584 .visib_token = null,
585 .extern_export_inline_token = null,
586 .lib_name = null,
587 .comments = comments,
588 }
589 });
590 continue;
591 }
592 }
593 },
594
595
596 State.VarDecl => |ctx| {
597 const var_decl = try arena.construct(ast.Node.VarDecl {
598 .base = ast.Node {
599 .id = ast.Node.Id.VarDecl,
600 },
601 .doc_comments = ctx.comments,
602 .visib_token = ctx.visib_token,
603 .mut_token = ctx.mut_token,
604 .comptime_token = ctx.comptime_token,
605 .extern_export_token = ctx.extern_export_token,
606 .type_node = null,
607 .align_node = null,
608 .init_node = null,
609 .lib_name = ctx.lib_name,
610 // initialized later
611 .name_token = undefined,
612 .eq_token = undefined,
613 .semicolon_token = undefined,
614 });
615 try ctx.list.push(&var_decl.base);
616
617 try stack.append(State { .VarDeclAlign = var_decl });
618 try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} });
619 try stack.append(State { .IfToken = Token.Id.Colon });
620 try stack.append(State {
621 .ExpectTokenSave = ExpectTokenSave {
622 .id = Token.Id.Identifier,
623 .ptr = &var_decl.name_token,
624 }
625 });
626 continue;
627 },
628 State.VarDeclAlign => |var_decl| {
629 try stack.append(State { .VarDeclEq = var_decl });
630
631 const next_token = nextToken(&tok_it, &tree);
632 const next_token_index = next_token.index;
633 const next_token_ptr = next_token.ptr;
634 if (next_token_ptr.id == Token.Id.Keyword_align) {
635 try stack.append(State { .ExpectToken = Token.Id.RParen });
636 try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} });
637 try stack.append(State { .ExpectToken = Token.Id.LParen });
638 continue;
639 }
640
641 putBackToken(&tok_it, &tree);
642 continue;
643 },
644 State.VarDeclEq => |var_decl| {
645 const token = nextToken(&tok_it, &tree);
646 const token_index = token.index;
647 const token_ptr = token.ptr;
648 switch (token_ptr.id) {
649 Token.Id.Equal => {
650 var_decl.eq_token = token_index;
651 stack.append(State {
652 .ExpectTokenSave = ExpectTokenSave {
653 .id = Token.Id.Semicolon,
654 .ptr = &var_decl.semicolon_token,
655 },
656 }) catch unreachable;
657 try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } });
658 continue;
659 },
660 Token.Id.Semicolon => {
661 var_decl.semicolon_token = token_index;
662 continue;
663 },
664 else => {
665 *(try tree.errors.addOne()) = Error {
666 .ExpectedEqOrSemi = Error.ExpectedEqOrSemi { .token = token_index },
667 };
668 return tree;
669 }
670 }
671 },
672
673
674 State.FnDef => |fn_proto| {
675 const token = nextToken(&tok_it, &tree);
676 const token_index = token.index;
677 const token_ptr = token.ptr;
678 switch(token_ptr.id) {
679 Token.Id.LBrace => {
680 const block = try arena.construct(ast.Node.Block {
681 .base = ast.Node { .id = ast.Node.Id.Block },
682 .label = null,
683 .lbrace = token_index,
684 .statements = ast.Node.Block.StatementList.init(arena),
685 .rbrace = undefined,
686 });
687 fn_proto.body_node = &block.base;
688 stack.append(State { .Block = block }) catch unreachable;
689 continue;
690 },
691 Token.Id.Semicolon => continue,
692 else => {
693 *(try tree.errors.addOne()) = Error {
694 .ExpectedSemiOrLBrace = Error.ExpectedSemiOrLBrace { .token = token_index },
695 };
696 return tree;
697 },
698 }
699 },
700 State.FnProto => |fn_proto| {
701 stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable;
702 try stack.append(State { .ParamDecl = fn_proto });
703 try stack.append(State { .ExpectToken = Token.Id.LParen });
704
705 if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |name_token| {
706 fn_proto.name_token = name_token;
707 }
708 continue;
709 },
710 State.FnProtoAlign => |fn_proto| {
711 stack.append(State { .FnProtoReturnType = fn_proto }) catch unreachable;
712
713 if (eatToken(&tok_it, &tree, Token.Id.Keyword_align)) |align_token| {
714 try stack.append(State { .ExpectToken = Token.Id.RParen });
715 try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } });
716 try stack.append(State { .ExpectToken = Token.Id.LParen });
717 }
718 continue;
719 },
720 State.FnProtoReturnType => |fn_proto| {
721 const token = nextToken(&tok_it, &tree);
722 const token_index = token.index;
723 const token_ptr = token.ptr;
724 switch (token_ptr.id) {
725 Token.Id.Bang => {
726 fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined };
727 stack.append(State {
728 .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet },
729 }) catch unreachable;
730 continue;
731 },
732 else => {
733 // TODO: this is a special case. Remove this when #760 is fixed
734 if (token_ptr.id == Token.Id.Keyword_error) {
735 if ((??tok_it.peek()).id == Token.Id.LBrace) {
736 const error_type_node = try arena.construct(ast.Node.ErrorType {
737 .base = ast.Node { .id = ast.Node.Id.ErrorType },
738 .token = token_index,
739 });
740 fn_proto.return_type = ast.Node.FnProto.ReturnType {
741 .Explicit = &error_type_node.base,
742 };
743 continue;
744 }
745 }
746
747 putBackToken(&tok_it, &tree);
748 fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined };
749 stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable;
750 continue;
751 },
752 }
753 },
754
755
756 State.ParamDecl => |fn_proto| {
757 if (eatToken(&tok_it, &tree, Token.Id.RParen)) |_| {
758 continue;
759 }
760 const param_decl = try arena.construct(ast.Node.ParamDecl {
761 .base = ast.Node {.id = ast.Node.Id.ParamDecl },
762 .comptime_token = null,
763 .noalias_token = null,
764 .name_token = null,
765 .type_node = undefined,
766 .var_args_token = null,
767 });
768 try fn_proto.params.push(&param_decl.base);
769
770 stack.append(State {
771 .ParamDeclEnd = ParamDeclEndCtx {
772 .param_decl = param_decl,
773 .fn_proto = fn_proto,
774 }
775 }) catch unreachable;
776 try stack.append(State { .ParamDeclName = param_decl });
777 try stack.append(State { .ParamDeclAliasOrComptime = param_decl });
778 continue;
779 },
780 State.ParamDeclAliasOrComptime => |param_decl| {
781 if (eatToken(&tok_it, &tree, Token.Id.Keyword_comptime)) |comptime_token| {
782 param_decl.comptime_token = comptime_token;
783 } else if (eatToken(&tok_it, &tree, Token.Id.Keyword_noalias)) |noalias_token| {
784 param_decl.noalias_token = noalias_token;
785 }
786 continue;
787 },
788 State.ParamDeclName => |param_decl| {
789 // TODO: Here, we eat two tokens in one state. This means that we can't have
790 // comments between these two tokens.
791 if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |ident_token| {
792 if (eatToken(&tok_it, &tree, Token.Id.Colon)) |_| {
793 param_decl.name_token = ident_token;
794 } else {
795 putBackToken(&tok_it, &tree);
796 }
797 }
798 continue;
799 },
800 State.ParamDeclEnd => |ctx| {
801 if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| {
802 ctx.param_decl.var_args_token = ellipsis3;
803 stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
804 continue;
805 }
806
807 try stack.append(State { .ParamDeclComma = ctx.fn_proto });
808 try stack.append(State {
809 .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node }
810 });
811 continue;
812 },
813 State.ParamDeclComma => |fn_proto| {
814 switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RParen)) {
815 ExpectCommaOrEndResult.end_token => |t| {
816 if (t == null) {
817 stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
818 }
819 continue;
820 },
821 ExpectCommaOrEndResult.parse_error => |e| {
822 try tree.errors.push(e);
823 return tree;
824 },
825 }
826 },
827
828 State.MaybeLabeledExpression => |ctx| {
829 if (eatToken(&tok_it, &tree, Token.Id.Colon)) |_| {
830 stack.append(State {
831 .LabeledExpression = LabelCtx {
832 .label = ctx.label,
833 .opt_ctx = ctx.opt_ctx,
834 }
835 }) catch unreachable;
836 continue;
837 }
838
839 _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, ctx.label);
840 continue;
841 },
842 State.LabeledExpression => |ctx| {
843 const token = nextToken(&tok_it, &tree);
844 const token_index = token.index;
845 const token_ptr = token.ptr;
846 switch (token_ptr.id) {
847 Token.Id.LBrace => {
848 const block = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block,
849 ast.Node.Block {
850 .base = undefined,
851 .label = ctx.label,
852 .lbrace = token_index,
853 .statements = ast.Node.Block.StatementList.init(arena),
854 .rbrace = undefined,
855 }
856 );
857 stack.append(State { .Block = block }) catch unreachable;
858 continue;
859 },
860 Token.Id.Keyword_while => {
861 stack.append(State {
862 .While = LoopCtx {
863 .label = ctx.label,
864 .inline_token = null,
865 .loop_token = token_index,
866 .opt_ctx = ctx.opt_ctx.toRequired(),
867 }
868 }) catch unreachable;
869 continue;
870 },
871 Token.Id.Keyword_for => {
872 stack.append(State {
873 .For = LoopCtx {
874 .label = ctx.label,
875 .inline_token = null,
876 .loop_token = token_index,
877 .opt_ctx = ctx.opt_ctx.toRequired(),
878 }
879 }) catch unreachable;
880 continue;
881 },
882 Token.Id.Keyword_suspend => {
883 const node = try arena.construct(ast.Node.Suspend {
884 .base = ast.Node {
885 .id = ast.Node.Id.Suspend,
886 },
887 .label = ctx.label,
888 .suspend_token = token_index,
889 .payload = null,
890 .body = null,
891 });
892 ctx.opt_ctx.store(&node.base);
893 stack.append(State { .SuspendBody = node }) catch unreachable;
894 try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
895 continue;
896 },
897 Token.Id.Keyword_inline => {
898 stack.append(State {
899 .Inline = InlineCtx {
900 .label = ctx.label,
901 .inline_token = token_index,
902 .opt_ctx = ctx.opt_ctx.toRequired(),
903 }
904 }) catch unreachable;
905 continue;
906 },
907 else => {
908 if (ctx.opt_ctx != OptionalCtx.Optional) {
909 *(try tree.errors.addOne()) = Error {
910 .ExpectedLabelable = Error.ExpectedLabelable { .token = token_index },
911 };
912 return tree;
913 }
914
915 putBackToken(&tok_it, &tree);
916 continue;
917 },
918 }
919 },
920 State.Inline => |ctx| {
921 const token = nextToken(&tok_it, &tree);
922 const token_index = token.index;
923 const token_ptr = token.ptr;
924 switch (token_ptr.id) {
925 Token.Id.Keyword_while => {
926 stack.append(State {
927 .While = LoopCtx {
928 .inline_token = ctx.inline_token,
929 .label = ctx.label,
930 .loop_token = token_index,
931 .opt_ctx = ctx.opt_ctx.toRequired(),
932 }
933 }) catch unreachable;
934 continue;
935 },
936 Token.Id.Keyword_for => {
937 stack.append(State {
938 .For = LoopCtx {
939 .inline_token = ctx.inline_token,
940 .label = ctx.label,
941 .loop_token = token_index,
942 .opt_ctx = ctx.opt_ctx.toRequired(),
943 }
944 }) catch unreachable;
945 continue;
946 },
947 else => {
948 if (ctx.opt_ctx != OptionalCtx.Optional) {
949 *(try tree.errors.addOne()) = Error {
950 .ExpectedInlinable = Error.ExpectedInlinable { .token = token_index },
951 };
952 return tree;
953 }
954
955 putBackToken(&tok_it, &tree);
956 continue;
957 },
958 }
959 },
960 State.While => |ctx| {
961 const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.While,
962 ast.Node.While {
963 .base = undefined,
964 .label = ctx.label,
965 .inline_token = ctx.inline_token,
966 .while_token = ctx.loop_token,
967 .condition = undefined,
968 .payload = null,
969 .continue_expr = null,
970 .body = undefined,
971 .@"else" = null,
972 }
973 );
974 stack.append(State { .Else = &node.@"else" }) catch unreachable;
975 try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } });
976 try stack.append(State { .WhileContinueExpr = &node.continue_expr });
977 try stack.append(State { .IfToken = Token.Id.Colon });
978 try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
979 try stack.append(State { .ExpectToken = Token.Id.RParen });
980 try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } });
981 try stack.append(State { .ExpectToken = Token.Id.LParen });
982 continue;
983 },
984 State.WhileContinueExpr => |dest| {
985 stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
986 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } });
987 try stack.append(State { .ExpectToken = Token.Id.LParen });
988 continue;
989 },
990 State.For => |ctx| {
991 const node = try createToCtxNode(arena, ctx.opt_ctx, ast.Node.For,
992 ast.Node.For {
993 .base = undefined,
994 .label = ctx.label,
995 .inline_token = ctx.inline_token,
996 .for_token = ctx.loop_token,
997 .array_expr = undefined,
998 .payload = null,
999 .body = undefined,
1000 .@"else" = null,
1001 }
1002 );
1003 stack.append(State { .Else = &node.@"else" }) catch unreachable;
1004 try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } });
1005 try stack.append(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } });
1006 try stack.append(State { .ExpectToken = Token.Id.RParen });
1007 try stack.append(State { .Expression = OptionalCtx { .Required = &node.array_expr } });
1008 try stack.append(State { .ExpectToken = Token.Id.LParen });
1009 continue;
1010 },
1011 State.Else => |dest| {
1012 if (eatToken(&tok_it, &tree, Token.Id.Keyword_else)) |else_token| {
1013 const node = try createNode(arena, ast.Node.Else,
1014 ast.Node.Else {
1015 .base = undefined,
1016 .else_token = else_token,
1017 .payload = null,
1018 .body = undefined,
1019 }
1020 );
1021 *dest = node;
1022
1023 stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable;
1024 try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
1025 continue;
1026 } else {
1027 continue;
1028 }
1029 },
1030
1031
1032 State.Block => |block| {
1033 const token = nextToken(&tok_it, &tree);
1034 const token_index = token.index;
1035 const token_ptr = token.ptr;
1036 switch (token_ptr.id) {
1037 Token.Id.RBrace => {
1038 block.rbrace = token_index;
1039 continue;
1040 },
1041 else => {
1042 putBackToken(&tok_it, &tree);
1043 stack.append(State { .Block = block }) catch unreachable;
1044
1045 var any_comments = false;
1046 while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| {
1047 try block.statements.push(&line_comment.base);
1048 any_comments = true;
1049 }
1050 if (any_comments) continue;
1051
1052 try stack.append(State { .Statement = block });
1053 continue;
1054 },
1055 }
1056 },
1057 State.Statement => |block| {
1058 const token = nextToken(&tok_it, &tree);
1059 const token_index = token.index;
1060 const token_ptr = token.ptr;
1061 switch (token_ptr.id) {
1062 Token.Id.Keyword_comptime => {
1063 stack.append(State {
1064 .ComptimeStatement = ComptimeStatementCtx {
1065 .comptime_token = token_index,
1066 .block = block,
1067 }
1068 }) catch unreachable;
1069 continue;
1070 },
1071 Token.Id.Keyword_var, Token.Id.Keyword_const => {
1072 stack.append(State {
1073 .VarDecl = VarDeclCtx {
1074 .comments = null,
1075 .visib_token = null,
1076 .comptime_token = null,
1077 .extern_export_token = null,
1078 .lib_name = null,
1079 .mut_token = token_index,
1080 .list = &block.statements,
1081 }
1082 }) catch unreachable;
1083 continue;
1084 },
1085 Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
1086 const node = try arena.construct(ast.Node.Defer {
1087 .base = ast.Node {
1088 .id = ast.Node.Id.Defer,
1089 },
1090 .defer_token = token_index,
1091 .kind = switch (token_ptr.id) {
1092 Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
1093 Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
1094 else => unreachable,
1095 },
1096 .expr = undefined,
1097 });
1098 const node_ptr = try block.statements.addOne();
1099 *node_ptr = &node.base;
1100
1101 stack.append(State { .Semicolon = node_ptr }) catch unreachable;
1102 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } });
1103 continue;
1104 },
1105 Token.Id.LBrace => {
1106 const inner_block = try arena.construct(ast.Node.Block {
1107 .base = ast.Node { .id = ast.Node.Id.Block },
1108 .label = null,
1109 .lbrace = token_index,
1110 .statements = ast.Node.Block.StatementList.init(arena),
1111 .rbrace = undefined,
1112 });
1113 try block.statements.push(&inner_block.base);
1114
1115 stack.append(State { .Block = inner_block }) catch unreachable;
1116 continue;
1117 },
1118 else => {
1119 putBackToken(&tok_it, &tree);
1120 const statement = try block.statements.addOne();
1121 try stack.append(State { .Semicolon = statement });
1122 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } });
1123 continue;
1124 }
1125 }
1126 },
1127 State.ComptimeStatement => |ctx| {
1128 const token = nextToken(&tok_it, &tree);
1129 const token_index = token.index;
1130 const token_ptr = token.ptr;
1131 switch (token_ptr.id) {
1132 Token.Id.Keyword_var, Token.Id.Keyword_const => {
1133 stack.append(State {
1134 .VarDecl = VarDeclCtx {
1135 .comments = null,
1136 .visib_token = null,
1137 .comptime_token = ctx.comptime_token,
1138 .extern_export_token = null,
1139 .lib_name = null,
1140 .mut_token = token_index,
1141 .list = &ctx.block.statements,
1142 }
1143 }) catch unreachable;
1144 continue;
1145 },
1146 else => {
1147 putBackToken(&tok_it, &tree);
1148 putBackToken(&tok_it, &tree);
1149 const statement = try ctx.block.statements.addOne();
1150 try stack.append(State { .Semicolon = statement });
1151 try stack.append(State { .Expression = OptionalCtx { .Required = statement } });
1152 continue;
1153 }
1154 }
1155 },
1156 State.Semicolon => |node_ptr| {
1157 const node = *node_ptr;
1158 if (node.requireSemiColon()) {
1159 stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
1160 continue;
1161 }
1162 continue;
1163 },
1164
1165 State.AsmOutputItems => |items| {
1166 const lbracket = nextToken(&tok_it, &tree);
1167 const lbracket_index = lbracket.index;
1168 const lbracket_ptr = lbracket.ptr;
1169 if (lbracket_ptr.id != Token.Id.LBracket) {
1170 putBackToken(&tok_it, &tree);
1171 continue;
1172 }
1173
1174 const node = try createNode(arena, ast.Node.AsmOutput,
1175 ast.Node.AsmOutput {
1176 .base = undefined,
1177 .symbolic_name = undefined,
1178 .constraint = undefined,
1179 .kind = undefined,
1180 }
1181 );
1182 try items.push(node);
1183
1184 stack.append(State { .AsmOutputItems = items }) catch unreachable;
1185 try stack.append(State { .IfToken = Token.Id.Comma });
1186 try stack.append(State { .ExpectToken = Token.Id.RParen });
1187 try stack.append(State { .AsmOutputReturnOrType = node });
1188 try stack.append(State { .ExpectToken = Token.Id.LParen });
1189 try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } });
1190 try stack.append(State { .ExpectToken = Token.Id.RBracket });
1191 try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } });
1192 continue;
1193 },
1194 State.AsmOutputReturnOrType => |node| {
1195 const token = nextToken(&tok_it, &tree);
1196 const token_index = token.index;
1197 const token_ptr = token.ptr;
1198 switch (token_ptr.id) {
1199 Token.Id.Identifier => {
1200 node.kind = ast.Node.AsmOutput.Kind { .Variable = try createLiteral(arena, ast.Node.Identifier, token_index) };
1201 continue;
1202 },
1203 Token.Id.Arrow => {
1204 node.kind = ast.Node.AsmOutput.Kind { .Return = undefined };
1205 try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } });
1206 continue;
1207 },
1208 else => {
1209 *(try tree.errors.addOne()) = Error {
1210 .ExpectedAsmOutputReturnOrType = Error.ExpectedAsmOutputReturnOrType {
1211 .token = token_index,
1212 },
1213 };
1214 return tree;
1215 },
1216 }
1217 },
1218 State.AsmInputItems => |items| {
1219 const lbracket = nextToken(&tok_it, &tree);
1220 const lbracket_index = lbracket.index;
1221 const lbracket_ptr = lbracket.ptr;
1222 if (lbracket_ptr.id != Token.Id.LBracket) {
1223 putBackToken(&tok_it, &tree);
1224 continue;
1225 }
1226
1227 const node = try createNode(arena, ast.Node.AsmInput,
1228 ast.Node.AsmInput {
1229 .base = undefined,
1230 .symbolic_name = undefined,
1231 .constraint = undefined,
1232 .expr = undefined,
1233 }
1234 );
1235 try items.push(node);
1236
1237 stack.append(State { .AsmInputItems = items }) catch unreachable;
1238 try stack.append(State { .IfToken = Token.Id.Comma });
1239 try stack.append(State { .ExpectToken = Token.Id.RParen });
1240 try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
1241 try stack.append(State { .ExpectToken = Token.Id.LParen });
1242 try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } });
1243 try stack.append(State { .ExpectToken = Token.Id.RBracket });
1244 try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } });
1245 continue;
1246 },
1247 State.AsmClobberItems => |items| {
1248 stack.append(State { .AsmClobberItems = items }) catch unreachable;
1249 try stack.append(State { .IfToken = Token.Id.Comma });
1250 try stack.append(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } });
1251 continue;
1252 },
1253
1254
1255 State.ExprListItemOrEnd => |list_state| {
1256 if (eatToken(&tok_it, &tree, list_state.end)) |token_index| {
1257 *list_state.ptr = token_index;
1258 continue;
1259 }
1260
1261 stack.append(State { .ExprListCommaOrEnd = list_state }) catch unreachable;
1262 try stack.append(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } });
1263 continue;
1264 },
1265 State.ExprListCommaOrEnd => |list_state| {
1266 switch (expectCommaOrEnd(&tok_it, &tree, list_state.end)) {
1267 ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
1268 *list_state.ptr = end;
1269 continue;
1270 } else {
1271 stack.append(State { .ExprListItemOrEnd = list_state }) catch unreachable;
1272 continue;
1273 },
1274 ExpectCommaOrEndResult.parse_error => |e| {
1275 try tree.errors.push(e);
1276 return tree;
1277 },
1278 }
1279 },
1280 State.FieldInitListItemOrEnd => |list_state| {
1281 while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| {
1282 try list_state.list.push(&line_comment.base);
1283 }
1284
1285 if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| {
1286 *list_state.ptr = rbrace;
1287 continue;
1288 }
1289
1290 const node = try arena.construct(ast.Node.FieldInitializer {
1291 .base = ast.Node {
1292 .id = ast.Node.Id.FieldInitializer,
1293 },
1294 .period_token = undefined,
1295 .name_token = undefined,
1296 .expr = undefined,
1297 });
1298 try list_state.list.push(&node.base);
1299
1300 stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;
1301 try stack.append(State { .Expression = OptionalCtx{ .Required = &node.expr } });
1302 try stack.append(State { .ExpectToken = Token.Id.Equal });
1303 try stack.append(State {
1304 .ExpectTokenSave = ExpectTokenSave {
1305 .id = Token.Id.Identifier,
1306 .ptr = &node.name_token,
1307 }
1308 });
1309 try stack.append(State {
1310 .ExpectTokenSave = ExpectTokenSave {
1311 .id = Token.Id.Period,
1312 .ptr = &node.period_token,
1313 }
1314 });
1315 continue;
1316 },
1317 State.FieldInitListCommaOrEnd => |list_state| {
1318 switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) {
1319 ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
1320 *list_state.ptr = end;
1321 continue;
1322 } else {
1323 stack.append(State { .FieldInitListItemOrEnd = list_state }) catch unreachable;
1324 continue;
1325 },
1326 ExpectCommaOrEndResult.parse_error => |e| {
1327 try tree.errors.push(e);
1328 return tree;
1329 },
1330 }
1331 },
1332 State.FieldListCommaOrEnd => |container_decl| {
1333 switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) {
1334 ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
1335 container_decl.rbrace_token = end;
1336 continue;
1337 } else {
1338 try stack.append(State { .ContainerDecl = container_decl });
1339 continue;
1340 },
1341 ExpectCommaOrEndResult.parse_error => |e| {
1342 try tree.errors.push(e);
1343 return tree;
1344 },
1345 }
1346 },
1347 State.ErrorTagListItemOrEnd => |list_state| {
1348 while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| {
1349 try list_state.list.push(&line_comment.base);
1350 }
1351
1352 if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| {
1353 *list_state.ptr = rbrace;
1354 continue;
1355 }
1356
1357 const node_ptr = try list_state.list.addOne();
1358
1359 try stack.append(State { .ErrorTagListCommaOrEnd = list_state });
1360 try stack.append(State { .ErrorTag = node_ptr });
1361 continue;
1362 },
1363 State.ErrorTagListCommaOrEnd => |list_state| {
1364 switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) {
1365 ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
1366 *list_state.ptr = end;
1367 continue;
1368 } else {
1369 stack.append(State { .ErrorTagListItemOrEnd = list_state }) catch unreachable;
1370 continue;
1371 },
1372 ExpectCommaOrEndResult.parse_error => |e| {
1373 try tree.errors.push(e);
1374 return tree;
1375 },
1376 }
1377 },
1378 State.SwitchCaseOrEnd => |list_state| {
1379 while (try eatLineComment(arena, &tok_it, &tree)) |line_comment| {
1380 try list_state.list.push(&line_comment.base);
1381 }
1382
1383 if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| {
1384 *list_state.ptr = rbrace;
1385 continue;
1386 }
1387
1388 const comments = try eatDocComments(arena, &tok_it, &tree);
1389 const node = try arena.construct(ast.Node.SwitchCase {
1390 .base = ast.Node {
1391 .id = ast.Node.Id.SwitchCase,
1392 },
1393 .items = ast.Node.SwitchCase.ItemList.init(arena),
1394 .payload = null,
1395 .expr = undefined,
1396 });
1397 try list_state.list.push(&node.base);
1398 try stack.append(State { .SwitchCaseCommaOrEnd = list_state });
1399 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } });
1400 try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
1401 try stack.append(State { .SwitchCaseFirstItem = &node.items });
1402
1403 continue;
1404 },
1405
1406 State.SwitchCaseCommaOrEnd => |list_state| {
1407 switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RParen)) {
1408 ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
1409 *list_state.ptr = end;
1410 continue;
1411 } else {
1412 try stack.append(State { .SwitchCaseOrEnd = list_state });
1413 continue;
1414 },
1415 ExpectCommaOrEndResult.parse_error => |e| {
1416 try tree.errors.push(e);
1417 return tree;
1418 },
1419 }
1420 },
1421
1422 State.SwitchCaseFirstItem => |case_items| {
1423 const token = nextToken(&tok_it, &tree);
1424 const token_index = token.index;
1425 const token_ptr = token.ptr;
1426 if (token_ptr.id == Token.Id.Keyword_else) {
1427 const else_node = try arena.construct(ast.Node.SwitchElse {
1428 .base = ast.Node{ .id = ast.Node.Id.SwitchElse},
1429 .token = token_index,
1430 });
1431 try case_items.push(&else_node.base);
1432
1433 try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
1434 continue;
1435 } else {
1436 putBackToken(&tok_it, &tree);
1437 try stack.append(State { .SwitchCaseItem = case_items });
1438 continue;
1439 }
1440 },
1441 State.SwitchCaseItem => |case_items| {
1442 stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable;
1443 try stack.append(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } });
1444 },
1445 State.SwitchCaseItemCommaOrEnd => |case_items| {
1446 switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.EqualAngleBracketRight)) {
1447 ExpectCommaOrEndResult.end_token => |t| {
1448 if (t == null) {
1449 stack.append(State { .SwitchCaseItem = case_items }) catch unreachable;
1450 }
1451 continue;
1452 },
1453 ExpectCommaOrEndResult.parse_error => |e| {
1454 try tree.errors.push(e);
1455 return tree;
1456 },
1457 }
1458 continue;
1459 },
1460
1461
1462 State.SuspendBody => |suspend_node| {
1463 if (suspend_node.payload != null) {
1464 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } });
1465 }
1466 continue;
1467 },
1468 State.AsyncAllocator => |async_node| {
1469 if (eatToken(&tok_it, &tree, Token.Id.AngleBracketLeft) == null) {
1470 continue;
1471 }
1472
1473 async_node.rangle_bracket = TokenIndex(0);
1474 try stack.append(State {
1475 .ExpectTokenSave = ExpectTokenSave {
1476 .id = Token.Id.AngleBracketRight,
1477 .ptr = &??async_node.rangle_bracket,
1478 }
1479 });
1480 try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } });
1481 continue;
1482 },
1483 State.AsyncEnd => |ctx| {
1484 const node = ctx.ctx.get() ?? continue;
1485
1486 switch (node.id) {
1487 ast.Node.Id.FnProto => {
1488 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", node);
1489 fn_proto.async_attr = ctx.attribute;
1490 continue;
1491 },
1492 ast.Node.Id.SuffixOp => {
1493 const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node);
1494 if (suffix_op.op == @TagType(ast.Node.SuffixOp.Op).Call) {
1495 suffix_op.op.Call.async_attr = ctx.attribute;
1496 continue;
1497 }
1498
1499 *(try tree.errors.addOne()) = Error {
1500 .ExpectedCall = Error.ExpectedCall { .node = node },
1501 };
1502 return tree;
1503 },
1504 else => {
1505 *(try tree.errors.addOne()) = Error {
1506 .ExpectedCallOrFnProto = Error.ExpectedCallOrFnProto { .node = node },
1507 };
1508 return tree;
1509 }
1510 }
1511 },
1512
1513
1514 State.ExternType => |ctx| {
1515 if (eatToken(&tok_it, &tree, Token.Id.Keyword_fn)) |fn_token| {
1516 const fn_proto = try arena.construct(ast.Node.FnProto {
1517 .base = ast.Node {
1518 .id = ast.Node.Id.FnProto,
1519 },
1520 .doc_comments = ctx.comments,
1521 .visib_token = null,
1522 .name_token = null,
1523 .fn_token = fn_token,
1524 .params = ast.Node.FnProto.ParamList.init(arena),
1525 .return_type = undefined,
1526 .var_args_token = null,
1527 .extern_export_inline_token = ctx.extern_token,
1528 .cc_token = null,
1529 .async_attr = null,
1530 .body_node = null,
1531 .lib_name = null,
1532 .align_expr = null,
1533 });
1534 ctx.opt_ctx.store(&fn_proto.base);
1535 stack.append(State { .FnProto = fn_proto }) catch unreachable;
1536 continue;
1537 }
1538
1539 stack.append(State {
1540 .ContainerKind = ContainerKindCtx {
1541 .opt_ctx = ctx.opt_ctx,
1542 .ltoken = ctx.extern_token,
1543 .layout = ast.Node.ContainerDecl.Layout.Extern,
1544 },
1545 }) catch unreachable;
1546 continue;
1547 },
1548 State.SliceOrArrayAccess => |node| {
1549 const token = nextToken(&tok_it, &tree);
1550 const token_index = token.index;
1551 const token_ptr = token.ptr;
1552 switch (token_ptr.id) {
1553 Token.Id.Ellipsis2 => {
1554 const start = node.op.ArrayAccess;
1555 node.op = ast.Node.SuffixOp.Op {
1556 .Slice = ast.Node.SuffixOp.Op.Slice {
1557 .start = start,
1558 .end = null,
1559 }
1560 };
1561
1562 stack.append(State {
1563 .ExpectTokenSave = ExpectTokenSave {
1564 .id = Token.Id.RBracket,
1565 .ptr = &node.rtoken,
1566 }
1567 }) catch unreachable;
1568 try stack.append(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } });
1569 continue;
1570 },
1571 Token.Id.RBracket => {
1572 node.rtoken = token_index;
1573 continue;
1574 },
1575 else => {
1576 *(try tree.errors.addOne()) = Error {
1577 .ExpectedSliceOrRBracket = Error.ExpectedSliceOrRBracket { .token = token_index },
1578 };
1579 return tree;
1580 }
1581 }
1582 },
1583 State.SliceOrArrayType => |node| {
1584 if (eatToken(&tok_it, &tree, Token.Id.RBracket)) |_| {
1585 node.op = ast.Node.PrefixOp.Op {
1586 .SliceType = ast.Node.PrefixOp.AddrOfInfo {
1587 .align_expr = null,
1588 .bit_offset_start_token = null,
1589 .bit_offset_end_token = null,
1590 .const_token = null,
1591 .volatile_token = null,
1592 }
1593 };
1594 stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
1595 try stack.append(State { .AddrOfModifiers = &node.op.SliceType });
1596 continue;
1597 }
1598
1599 node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined };
1600 stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
1601 try stack.append(State { .ExpectToken = Token.Id.RBracket });
1602 try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } });
1603 continue;
1604 },
1605 State.AddrOfModifiers => |addr_of_info| {
1606 const token = nextToken(&tok_it, &tree);
1607 const token_index = token.index;
1608 const token_ptr = token.ptr;
1609 switch (token_ptr.id) {
1610 Token.Id.Keyword_align => {
1611 stack.append(state) catch unreachable;
1612 if (addr_of_info.align_expr != null) {
1613 *(try tree.errors.addOne()) = Error {
1614 .ExtraAlignQualifier = Error.ExtraAlignQualifier { .token = token_index },
1615 };
1616 return tree;
1617 }
1618 try stack.append(State { .ExpectToken = Token.Id.RParen });
1619 try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} });
1620 try stack.append(State { .ExpectToken = Token.Id.LParen });
1621 continue;
1622 },
1623 Token.Id.Keyword_const => {
1624 stack.append(state) catch unreachable;
1625 if (addr_of_info.const_token != null) {
1626 *(try tree.errors.addOne()) = Error {
1627 .ExtraConstQualifier = Error.ExtraConstQualifier { .token = token_index },
1628 };
1629 return tree;
1630 }
1631 addr_of_info.const_token = token_index;
1632 continue;
1633 },
1634 Token.Id.Keyword_volatile => {
1635 stack.append(state) catch unreachable;
1636 if (addr_of_info.volatile_token != null) {
1637 *(try tree.errors.addOne()) = Error {
1638 .ExtraVolatileQualifier = Error.ExtraVolatileQualifier { .token = token_index },
1639 };
1640 return tree;
1641 }
1642 addr_of_info.volatile_token = token_index;
1643 continue;
1644 },
1645 else => {
1646 putBackToken(&tok_it, &tree);
1647 continue;
1648 },
1649 }
1650 },
1651
1652
1653 State.Payload => |opt_ctx| {
1654 const token = nextToken(&tok_it, &tree);
1655 const token_index = token.index;
1656 const token_ptr = token.ptr;
1657 if (token_ptr.id != Token.Id.Pipe) {
1658 if (opt_ctx != OptionalCtx.Optional) {
1659 *(try tree.errors.addOne()) = Error {
1660 .ExpectedToken = Error.ExpectedToken {
1661 .token = token_index,
1662 .expected_id = Token.Id.Pipe,
1663 },
1664 };
1665 return tree;
1666 }
1667
1668 putBackToken(&tok_it, &tree);
1669 continue;
1670 }
1671
1672 const node = try createToCtxNode(arena, opt_ctx, ast.Node.Payload,
1673 ast.Node.Payload {
1674 .base = undefined,
1675 .lpipe = token_index,
1676 .error_symbol = undefined,
1677 .rpipe = undefined
1678 }
1679 );
1680
1681 stack.append(State {
1682 .ExpectTokenSave = ExpectTokenSave {
1683 .id = Token.Id.Pipe,
1684 .ptr = &node.rpipe,
1685 }
1686 }) catch unreachable;
1687 try stack.append(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } });
1688 continue;
1689 },
1690 State.PointerPayload => |opt_ctx| {
1691 const token = nextToken(&tok_it, &tree);
1692 const token_index = token.index;
1693 const token_ptr = token.ptr;
1694 if (token_ptr.id != Token.Id.Pipe) {
1695 if (opt_ctx != OptionalCtx.Optional) {
1696 *(try tree.errors.addOne()) = Error {
1697 .ExpectedToken = Error.ExpectedToken {
1698 .token = token_index,
1699 .expected_id = Token.Id.Pipe,
1700 },
1701 };
1702 return tree;
1703 }
1704
1705 putBackToken(&tok_it, &tree);
1706 continue;
1707 }
1708
1709 const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload,
1710 ast.Node.PointerPayload {
1711 .base = undefined,
1712 .lpipe = token_index,
1713 .ptr_token = null,
1714 .value_symbol = undefined,
1715 .rpipe = undefined
1716 }
1717 );
1718
1719 try stack.append(State {
1720 .ExpectTokenSave = ExpectTokenSave {
1721 .id = Token.Id.Pipe,
1722 .ptr = &node.rpipe,
1723 }
1724 });
1725 try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
1726 try stack.append(State {
1727 .OptionalTokenSave = OptionalTokenSave {
1728 .id = Token.Id.Asterisk,
1729 .ptr = &node.ptr_token,
1730 }
1731 });
1732 continue;
1733 },
1734 State.PointerIndexPayload => |opt_ctx| {
1735 const token = nextToken(&tok_it, &tree);
1736 const token_index = token.index;
1737 const token_ptr = token.ptr;
1738 if (token_ptr.id != Token.Id.Pipe) {
1739 if (opt_ctx != OptionalCtx.Optional) {
1740 *(try tree.errors.addOne()) = Error {
1741 .ExpectedToken = Error.ExpectedToken {
1742 .token = token_index,
1743 .expected_id = Token.Id.Pipe,
1744 },
1745 };
1746 return tree;
1747 }
1748
1749 putBackToken(&tok_it, &tree);
1750 continue;
1751 }
1752
1753 const node = try createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload,
1754 ast.Node.PointerIndexPayload {
1755 .base = undefined,
1756 .lpipe = token_index,
1757 .ptr_token = null,
1758 .value_symbol = undefined,
1759 .index_symbol = null,
1760 .rpipe = undefined
1761 }
1762 );
1763
1764 stack.append(State {
1765 .ExpectTokenSave = ExpectTokenSave {
1766 .id = Token.Id.Pipe,
1767 .ptr = &node.rpipe,
1768 }
1769 }) catch unreachable;
1770 try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } });
1771 try stack.append(State { .IfToken = Token.Id.Comma });
1772 try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
1773 try stack.append(State {
1774 .OptionalTokenSave = OptionalTokenSave {
1775 .id = Token.Id.Asterisk,
1776 .ptr = &node.ptr_token,
1777 }
1778 });
1779 continue;
1780 },
1781
1782
1783 State.Expression => |opt_ctx| {
1784 const token = nextToken(&tok_it, &tree);
1785 const token_index = token.index;
1786 const token_ptr = token.ptr;
1787 switch (token_ptr.id) {
1788 Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => {
1789 const node = try createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression,
1790 ast.Node.ControlFlowExpression {
1791 .base = undefined,
1792 .ltoken = token_index,
1793 .kind = undefined,
1794 .rhs = null,
1795 }
1796 );
1797
1798 stack.append(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable;
1799
1800 switch (token_ptr.id) {
1801 Token.Id.Keyword_break => {
1802 node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null };
1803 try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } });
1804 try stack.append(State { .IfToken = Token.Id.Colon });
1805 },
1806 Token.Id.Keyword_continue => {
1807 node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null };
1808 try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } });
1809 try stack.append(State { .IfToken = Token.Id.Colon });
1810 },
1811 Token.Id.Keyword_return => {
1812 node.kind = ast.Node.ControlFlowExpression.Kind.Return;
1813 },
1814 else => unreachable,
1815 }
1816 continue;
1817 },
1818 Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => {
1819 const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
1820 ast.Node.PrefixOp {
1821 .base = undefined,
1822 .op_token = token_index,
1823 .op = switch (token_ptr.id) {
1824 Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} },
1825 Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} },
1826 Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} },
1827 else => unreachable,
1828 },
1829 .rhs = undefined,
1830 }
1831 );
1832
1833 stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
1834 continue;
1835 },
1836 else => {
1837 if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr, token_index)) {
1838 putBackToken(&tok_it, &tree);
1839 stack.append(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable;
1840 }
1841 continue;
1842 }
1843 }
1844 },
1845 State.RangeExpressionBegin => |opt_ctx| {
1846 stack.append(State { .RangeExpressionEnd = opt_ctx }) catch unreachable;
1847 try stack.append(State { .Expression = opt_ctx });
1848 continue;
1849 },
1850 State.RangeExpressionEnd => |opt_ctx| {
1851 const lhs = opt_ctx.get() ?? continue;
1852
1853 if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| {
1854 const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
1855 ast.Node.InfixOp {
1856 .base = undefined,
1857 .lhs = lhs,
1858 .op_token = ellipsis3,
1859 .op = ast.Node.InfixOp.Op.Range,
1860 .rhs = undefined,
1861 }
1862 );
1863 stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
1864 continue;
1865 }
1866 },
1867 State.AssignmentExpressionBegin => |opt_ctx| {
1868 stack.append(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable;
1869 try stack.append(State { .Expression = opt_ctx });
1870 continue;
1871 },
1872
1873 State.AssignmentExpressionEnd => |opt_ctx| {
1874 const lhs = opt_ctx.get() ?? continue;
1875
1876 const token = nextToken(&tok_it, &tree);
1877 const token_index = token.index;
1878 const token_ptr = token.ptr;
1879 if (tokenIdToAssignment(token_ptr.id)) |ass_id| {
1880 const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
1881 ast.Node.InfixOp {
1882 .base = undefined,
1883 .lhs = lhs,
1884 .op_token = token_index,
1885 .op = ass_id,
1886 .rhs = undefined,
1887 }
1888 );
1889 stack.append(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
1890 try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } });
1891 continue;
1892 } else {
1893 putBackToken(&tok_it, &tree);
1894 continue;
1895 }
1896 },
1897
1898 State.UnwrapExpressionBegin => |opt_ctx| {
1899 stack.append(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable;
1900 try stack.append(State { .BoolOrExpressionBegin = opt_ctx });
1901 continue;
1902 },
1903
1904 State.UnwrapExpressionEnd => |opt_ctx| {
1905 const lhs = opt_ctx.get() ?? continue;
1906
1907 const token = nextToken(&tok_it, &tree);
1908 const token_index = token.index;
1909 const token_ptr = token.ptr;
1910 if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| {
1911 const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
1912 ast.Node.InfixOp {
1913 .base = undefined,
1914 .lhs = lhs,
1915 .op_token = token_index,
1916 .op = unwrap_id,
1917 .rhs = undefined,
1918 }
1919 );
1920
1921 stack.append(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
1922 try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } });
1923
1924 if (node.op == ast.Node.InfixOp.Op.Catch) {
1925 try stack.append(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } });
1926 }
1927 continue;
1928 } else {
1929 putBackToken(&tok_it, &tree);
1930 continue;
1931 }
1932 },
1933
1934 State.BoolOrExpressionBegin => |opt_ctx| {
1935 stack.append(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable;
1936 try stack.append(State { .BoolAndExpressionBegin = opt_ctx });
1937 continue;
1938 },
1939
1940 State.BoolOrExpressionEnd => |opt_ctx| {
1941 const lhs = opt_ctx.get() ?? continue;
1942
1943 if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| {
1944 const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
1945 ast.Node.InfixOp {
1946 .base = undefined,
1947 .lhs = lhs,
1948 .op_token = or_token,
1949 .op = ast.Node.InfixOp.Op.BoolOr,
1950 .rhs = undefined,
1951 }
1952 );
1953 stack.append(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
1954 try stack.append(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
1955 continue;
1956 }
1957 },
1958
1959 State.BoolAndExpressionBegin => |opt_ctx| {
1960 stack.append(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable;
1961 try stack.append(State { .ComparisonExpressionBegin = opt_ctx });
1962 continue;
1963 },
1964
1965 State.BoolAndExpressionEnd => |opt_ctx| {
1966 const lhs = opt_ctx.get() ?? continue;
1967
1968 if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| {
1969 const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
1970 ast.Node.InfixOp {
1971 .base = undefined,
1972 .lhs = lhs,
1973 .op_token = and_token,
1974 .op = ast.Node.InfixOp.Op.BoolAnd,
1975 .rhs = undefined,
1976 }
1977 );
1978 stack.append(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
1979 try stack.append(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } });
1980 continue;
1981 }
1982 },
1983
1984 State.ComparisonExpressionBegin => |opt_ctx| {
1985 stack.append(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable;
1986 try stack.append(State { .BinaryOrExpressionBegin = opt_ctx });
1987 continue;
1988 },
1989
1990 State.ComparisonExpressionEnd => |opt_ctx| {
1991 const lhs = opt_ctx.get() ?? continue;
1992
1993 const token = nextToken(&tok_it, &tree);
1994 const token_index = token.index;
1995 const token_ptr = token.ptr;
1996 if (tokenIdToComparison(token_ptr.id)) |comp_id| {
1997 const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
1998 ast.Node.InfixOp {
1999 .base = undefined,
2000 .lhs = lhs,
2001 .op_token = token_index,
2002 .op = comp_id,
2003 .rhs = undefined,
2004 }
2005 );
2006 stack.append(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2007 try stack.append(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2008 continue;
2009 } else {
2010 putBackToken(&tok_it, &tree);
2011 continue;
2012 }
2013 },
2014
2015 State.BinaryOrExpressionBegin => |opt_ctx| {
2016 stack.append(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable;
2017 try stack.append(State { .BinaryXorExpressionBegin = opt_ctx });
2018 continue;
2019 },
2020
2021 State.BinaryOrExpressionEnd => |opt_ctx| {
2022 const lhs = opt_ctx.get() ?? continue;
2023
2024 if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| {
2025 const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2026 ast.Node.InfixOp {
2027 .base = undefined,
2028 .lhs = lhs,
2029 .op_token = pipe,
2030 .op = ast.Node.InfixOp.Op.BitOr,
2031 .rhs = undefined,
2032 }
2033 );
2034 stack.append(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2035 try stack.append(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2036 continue;
2037 }
2038 },
2039
2040 State.BinaryXorExpressionBegin => |opt_ctx| {
2041 stack.append(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable;
2042 try stack.append(State { .BinaryAndExpressionBegin = opt_ctx });
2043 continue;
2044 },
2045
2046 State.BinaryXorExpressionEnd => |opt_ctx| {
2047 const lhs = opt_ctx.get() ?? continue;
2048
2049 if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| {
2050 const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2051 ast.Node.InfixOp {
2052 .base = undefined,
2053 .lhs = lhs,
2054 .op_token = caret,
2055 .op = ast.Node.InfixOp.Op.BitXor,
2056 .rhs = undefined,
2057 }
2058 );
2059 stack.append(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2060 try stack.append(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2061 continue;
2062 }
2063 },
2064
2065 State.BinaryAndExpressionBegin => |opt_ctx| {
2066 stack.append(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable;
2067 try stack.append(State { .BitShiftExpressionBegin = opt_ctx });
2068 continue;
2069 },
2070
2071 State.BinaryAndExpressionEnd => |opt_ctx| {
2072 const lhs = opt_ctx.get() ?? continue;
2073
2074 if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| {
2075 const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2076 ast.Node.InfixOp {
2077 .base = undefined,
2078 .lhs = lhs,
2079 .op_token = ampersand,
2080 .op = ast.Node.InfixOp.Op.BitAnd,
2081 .rhs = undefined,
2082 }
2083 );
2084 stack.append(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2085 try stack.append(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2086 continue;
2087 }
2088 },
2089
2090 State.BitShiftExpressionBegin => |opt_ctx| {
2091 stack.append(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable;
2092 try stack.append(State { .AdditionExpressionBegin = opt_ctx });
2093 continue;
2094 },
2095
2096 State.BitShiftExpressionEnd => |opt_ctx| {
2097 const lhs = opt_ctx.get() ?? continue;
2098
2099 const token = nextToken(&tok_it, &tree);
2100 const token_index = token.index;
2101 const token_ptr = token.ptr;
2102 if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| {
2103 const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2104 ast.Node.InfixOp {
2105 .base = undefined,
2106 .lhs = lhs,
2107 .op_token = token_index,
2108 .op = bitshift_id,
2109 .rhs = undefined,
2110 }
2111 );
2112 stack.append(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2113 try stack.append(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2114 continue;
2115 } else {
2116 putBackToken(&tok_it, &tree);
2117 continue;
2118 }
2119 },
2120
2121 State.AdditionExpressionBegin => |opt_ctx| {
2122 stack.append(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable;
2123 try stack.append(State { .MultiplyExpressionBegin = opt_ctx });
2124 continue;
2125 },
2126
2127 State.AdditionExpressionEnd => |opt_ctx| {
2128 const lhs = opt_ctx.get() ?? continue;
2129
2130 const token = nextToken(&tok_it, &tree);
2131 const token_index = token.index;
2132 const token_ptr = token.ptr;
2133 if (tokenIdToAddition(token_ptr.id)) |add_id| {
2134 const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2135 ast.Node.InfixOp {
2136 .base = undefined,
2137 .lhs = lhs,
2138 .op_token = token_index,
2139 .op = add_id,
2140 .rhs = undefined,
2141 }
2142 );
2143 stack.append(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2144 try stack.append(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2145 continue;
2146 } else {
2147 putBackToken(&tok_it, &tree);
2148 continue;
2149 }
2150 },
2151
2152 State.MultiplyExpressionBegin => |opt_ctx| {
2153 stack.append(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable;
2154 try stack.append(State { .CurlySuffixExpressionBegin = opt_ctx });
2155 continue;
2156 },
2157
2158 State.MultiplyExpressionEnd => |opt_ctx| {
2159 const lhs = opt_ctx.get() ?? continue;
2160
2161 const token = nextToken(&tok_it, &tree);
2162 const token_index = token.index;
2163 const token_ptr = token.ptr;
2164 if (tokenIdToMultiply(token_ptr.id)) |mult_id| {
2165 const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2166 ast.Node.InfixOp {
2167 .base = undefined,
2168 .lhs = lhs,
2169 .op_token = token_index,
2170 .op = mult_id,
2171 .rhs = undefined,
2172 }
2173 );
2174 stack.append(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2175 try stack.append(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2176 continue;
2177 } else {
2178 putBackToken(&tok_it, &tree);
2179 continue;
2180 }
2181 },
2182
2183 State.CurlySuffixExpressionBegin => |opt_ctx| {
2184 stack.append(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable;
2185 try stack.append(State { .IfToken = Token.Id.LBrace });
2186 try stack.append(State { .TypeExprBegin = opt_ctx });
2187 continue;
2188 },
2189
2190 State.CurlySuffixExpressionEnd => |opt_ctx| {
2191 const lhs = opt_ctx.get() ?? continue;
2192
2193 if ((??tok_it.peek()).id == Token.Id.Period) {
2194 const node = try arena.construct(ast.Node.SuffixOp {
2195 .base = ast.Node { .id = ast.Node.Id.SuffixOp },
2196 .lhs = lhs,
2197 .op = ast.Node.SuffixOp.Op {
2198 .StructInitializer = ast.Node.SuffixOp.Op.InitList.init(arena),
2199 },
2200 .rtoken = undefined,
2201 });
2202 opt_ctx.store(&node.base);
2203
2204 stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2205 try stack.append(State { .IfToken = Token.Id.LBrace });
2206 try stack.append(State {
2207 .FieldInitListItemOrEnd = ListSave(@typeOf(node.op.StructInitializer)) {
2208 .list = &node.op.StructInitializer,
2209 .ptr = &node.rtoken,
2210 }
2211 });
2212 continue;
2213 }
2214
2215 const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
2216 ast.Node.SuffixOp {
2217 .base = undefined,
2218 .lhs = lhs,
2219 .op = ast.Node.SuffixOp.Op {
2220 .ArrayInitializer = ast.Node.SuffixOp.Op.InitList.init(arena),
2221 },
2222 .rtoken = undefined,
2223 }
2224 );
2225 stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2226 try stack.append(State { .IfToken = Token.Id.LBrace });
2227 try stack.append(State {
2228 .ExprListItemOrEnd = ExprListCtx {
2229 .list = &node.op.ArrayInitializer,
2230 .end = Token.Id.RBrace,
2231 .ptr = &node.rtoken,
2232 }
2233 });
2234 continue;
2235 },
2236
2237 State.TypeExprBegin => |opt_ctx| {
2238 stack.append(State { .TypeExprEnd = opt_ctx }) catch unreachable;
2239 try stack.append(State { .PrefixOpExpression = opt_ctx });
2240 continue;
2241 },
2242
2243 State.TypeExprEnd => |opt_ctx| {
2244 const lhs = opt_ctx.get() ?? continue;
2245
2246 if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| {
2247 const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2248 ast.Node.InfixOp {
2249 .base = undefined,
2250 .lhs = lhs,
2251 .op_token = bang,
2252 .op = ast.Node.InfixOp.Op.ErrorUnion,
2253 .rhs = undefined,
2254 }
2255 );
2256 stack.append(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable;
2257 try stack.append(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } });
2258 continue;
2259 }
2260 },
2261
2262 State.PrefixOpExpression => |opt_ctx| {
2263 const token = nextToken(&tok_it, &tree);
2264 const token_index = token.index;
2265 const token_ptr = token.ptr;
2266 if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| {
2267 var node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
2268 ast.Node.PrefixOp {
2269 .base = undefined,
2270 .op_token = token_index,
2271 .op = prefix_id,
2272 .rhs = undefined,
2273 }
2274 );
2275
2276 // Treat '**' token as two derefs
2277 if (token_ptr.id == Token.Id.AsteriskAsterisk) {
2278 const child = try createNode(arena, ast.Node.PrefixOp,
2279 ast.Node.PrefixOp {
2280 .base = undefined,
2281 .op_token = token_index,
2282 .op = prefix_id,
2283 .rhs = undefined,
2284 }
2285 );
2286 node.rhs = &child.base;
2287 node = child;
2288 }
2289
2290 stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
2291 if (node.op == ast.Node.PrefixOp.Op.AddrOf) {
2292 try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
2293 }
2294 continue;
2295 } else {
2296 putBackToken(&tok_it, &tree);
2297 stack.append(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable;
2298 continue;
2299 }
2300 },
2301
2302 State.SuffixOpExpressionBegin => |opt_ctx| {
2303 if (eatToken(&tok_it, &tree, Token.Id.Keyword_async)) |async_token| {
2304 const async_node = try createNode(arena, ast.Node.AsyncAttribute,
2305 ast.Node.AsyncAttribute {
2306 .base = undefined,
2307 .async_token = async_token,
2308 .allocator_type = null,
2309 .rangle_bracket = null,
2310 }
2311 );
2312 stack.append(State {
2313 .AsyncEnd = AsyncEndCtx {
2314 .ctx = opt_ctx,
2315 .attribute = async_node,
2316 }
2317 }) catch unreachable;
2318 try stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() });
2319 try stack.append(State { .PrimaryExpression = opt_ctx.toRequired() });
2320 try stack.append(State { .AsyncAllocator = async_node });
2321 continue;
2322 }
2323
2324 stack.append(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable;
2325 try stack.append(State { .PrimaryExpression = opt_ctx });
2326 continue;
2327 },
2328
2329 State.SuffixOpExpressionEnd => |opt_ctx| {
2330 const lhs = opt_ctx.get() ?? continue;
2331
2332 const token = nextToken(&tok_it, &tree);
2333 const token_index = token.index;
2334 const token_ptr = token.ptr;
2335 switch (token_ptr.id) {
2336 Token.Id.LParen => {
2337 const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
2338 ast.Node.SuffixOp {
2339 .base = undefined,
2340 .lhs = lhs,
2341 .op = ast.Node.SuffixOp.Op {
2342 .Call = ast.Node.SuffixOp.Op.Call {
2343 .params = ast.Node.SuffixOp.Op.Call.ParamList.init(arena),
2344 .async_attr = null,
2345 }
2346 },
2347 .rtoken = undefined,
2348 }
2349 );
2350 stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2351 try stack.append(State {
2352 .ExprListItemOrEnd = ExprListCtx {
2353 .list = &node.op.Call.params,
2354 .end = Token.Id.RParen,
2355 .ptr = &node.rtoken,
2356 }
2357 });
2358 continue;
2359 },
2360 Token.Id.LBracket => {
2361 const node = try createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
2362 ast.Node.SuffixOp {
2363 .base = undefined,
2364 .lhs = lhs,
2365 .op = ast.Node.SuffixOp.Op {
2366 .ArrayAccess = undefined,
2367 },
2368 .rtoken = undefined
2369 }
2370 );
2371 stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2372 try stack.append(State { .SliceOrArrayAccess = node });
2373 try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }});
2374 continue;
2375 },
2376 Token.Id.Period => {
2377 const node = try createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2378 ast.Node.InfixOp {
2379 .base = undefined,
2380 .lhs = lhs,
2381 .op_token = token_index,
2382 .op = ast.Node.InfixOp.Op.Period,
2383 .rhs = undefined,
2384 }
2385 );
2386 stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2387 try stack.append(State { .Identifier = OptionalCtx { .Required = &node.rhs } });
2388 continue;
2389 },
2390 else => {
2391 putBackToken(&tok_it, &tree);
2392 continue;
2393 },
2394 }
2395 },
2396
2397 State.PrimaryExpression => |opt_ctx| {
2398 const token = nextToken(&tok_it, &tree);
2399 switch (token.ptr.id) {
2400 Token.Id.IntegerLiteral => {
2401 _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token.index);
2402 continue;
2403 },
2404 Token.Id.FloatLiteral => {
2405 _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token.index);
2406 continue;
2407 },
2408 Token.Id.CharLiteral => {
2409 _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token.index);
2410 continue;
2411 },
2412 Token.Id.Keyword_undefined => {
2413 _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token.index);
2414 continue;
2415 },
2416 Token.Id.Keyword_true, Token.Id.Keyword_false => {
2417 _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token.index);
2418 continue;
2419 },
2420 Token.Id.Keyword_null => {
2421 _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token.index);
2422 continue;
2423 },
2424 Token.Id.Keyword_this => {
2425 _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token.index);
2426 continue;
2427 },
2428 Token.Id.Keyword_var => {
2429 _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token.index);
2430 continue;
2431 },
2432 Token.Id.Keyword_unreachable => {
2433 _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token.index);
2434 continue;
2435 },
2436 Token.Id.Keyword_promise => {
2437 const node = try arena.construct(ast.Node.PromiseType {
2438 .base = ast.Node {
2439 .id = ast.Node.Id.PromiseType,
2440 },
2441 .promise_token = token.index,
2442 .result = null,
2443 });
2444 opt_ctx.store(&node.base);
2445 const next_token = nextToken(&tok_it, &tree);
2446 const next_token_index = next_token.index;
2447 const next_token_ptr = next_token.ptr;
2448 if (next_token_ptr.id != Token.Id.Arrow) {
2449 putBackToken(&tok_it, &tree);
2450 continue;
2451 }
2452 node.result = ast.Node.PromiseType.Result {
2453 .arrow_token = next_token_index,
2454 .return_type = undefined,
2455 };
2456 const return_type_ptr = &((??node.result).return_type);
2457 try stack.append(State { .Expression = OptionalCtx { .Required = return_type_ptr, } });
2458 continue;
2459 },
2460 Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
2461 opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, &tree)) ?? unreachable);
2462 continue;
2463 },
2464 Token.Id.LParen => {
2465 const node = try createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression,
2466 ast.Node.GroupedExpression {
2467 .base = undefined,
2468 .lparen = token.index,
2469 .expr = undefined,
2470 .rparen = undefined,
2471 }
2472 );
2473 stack.append(State {
2474 .ExpectTokenSave = ExpectTokenSave {
2475 .id = Token.Id.RParen,
2476 .ptr = &node.rparen,
2477 }
2478 }) catch unreachable;
2479 try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
2480 continue;
2481 },
2482 Token.Id.Builtin => {
2483 const node = try createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall,
2484 ast.Node.BuiltinCall {
2485 .base = undefined,
2486 .builtin_token = token.index,
2487 .params = ast.Node.BuiltinCall.ParamList.init(arena),
2488 .rparen_token = undefined,
2489 }
2490 );
2491 stack.append(State {
2492 .ExprListItemOrEnd = ExprListCtx {
2493 .list = &node.params,
2494 .end = Token.Id.RParen,
2495 .ptr = &node.rparen_token,
2496 }
2497 }) catch unreachable;
2498 try stack.append(State { .ExpectToken = Token.Id.LParen, });
2499 continue;
2500 },
2501 Token.Id.LBracket => {
2502 const node = try createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
2503 ast.Node.PrefixOp {
2504 .base = undefined,
2505 .op_token = token.index,
2506 .op = undefined,
2507 .rhs = undefined,
2508 }
2509 );
2510 stack.append(State { .SliceOrArrayType = node }) catch unreachable;
2511 continue;
2512 },
2513 Token.Id.Keyword_error => {
2514 stack.append(State {
2515 .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx {
2516 .error_token = token.index,
2517 .opt_ctx = opt_ctx
2518 }
2519 }) catch unreachable;
2520 continue;
2521 },
2522 Token.Id.Keyword_packed => {
2523 stack.append(State {
2524 .ContainerKind = ContainerKindCtx {
2525 .opt_ctx = opt_ctx,
2526 .ltoken = token.index,
2527 .layout = ast.Node.ContainerDecl.Layout.Packed,
2528 },
2529 }) catch unreachable;
2530 continue;
2531 },
2532 Token.Id.Keyword_extern => {
2533 stack.append(State {
2534 .ExternType = ExternTypeCtx {
2535 .opt_ctx = opt_ctx,
2536 .extern_token = token.index,
2537 .comments = null,
2538 },
2539 }) catch unreachable;
2540 continue;
2541 },
2542 Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
2543 putBackToken(&tok_it, &tree);
2544 stack.append(State {
2545 .ContainerKind = ContainerKindCtx {
2546 .opt_ctx = opt_ctx,
2547 .ltoken = token.index,
2548 .layout = ast.Node.ContainerDecl.Layout.Auto,
2549 },
2550 }) catch unreachable;
2551 continue;
2552 },
2553 Token.Id.Identifier => {
2554 stack.append(State {
2555 .MaybeLabeledExpression = MaybeLabeledExpressionCtx {
2556 .label = token.index,
2557 .opt_ctx = opt_ctx
2558 }
2559 }) catch unreachable;
2560 continue;
2561 },
2562 Token.Id.Keyword_fn => {
2563 const fn_proto = try arena.construct(ast.Node.FnProto {
2564 .base = ast.Node {
2565 .id = ast.Node.Id.FnProto,
2566 },
2567 .doc_comments = null,
2568 .visib_token = null,
2569 .name_token = null,
2570 .fn_token = token.index,
2571 .params = ast.Node.FnProto.ParamList.init(arena),
2572 .return_type = undefined,
2573 .var_args_token = null,
2574 .extern_export_inline_token = null,
2575 .cc_token = null,
2576 .async_attr = null,
2577 .body_node = null,
2578 .lib_name = null,
2579 .align_expr = null,
2580 });
2581 opt_ctx.store(&fn_proto.base);
2582 stack.append(State { .FnProto = fn_proto }) catch unreachable;
2583 continue;
2584 },
2585 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
2586 const fn_proto = try arena.construct(ast.Node.FnProto {
2587 .base = ast.Node {
2588 .id = ast.Node.Id.FnProto,
2589 },
2590 .doc_comments = null,
2591 .visib_token = null,
2592 .name_token = null,
2593 .fn_token = undefined,
2594 .params = ast.Node.FnProto.ParamList.init(arena),
2595 .return_type = undefined,
2596 .var_args_token = null,
2597 .extern_export_inline_token = null,
2598 .cc_token = token.index,
2599 .async_attr = null,
2600 .body_node = null,
2601 .lib_name = null,
2602 .align_expr = null,
2603 });
2604 opt_ctx.store(&fn_proto.base);
2605 stack.append(State { .FnProto = fn_proto }) catch unreachable;
2606 try stack.append(State {
2607 .ExpectTokenSave = ExpectTokenSave {
2608 .id = Token.Id.Keyword_fn,
2609 .ptr = &fn_proto.fn_token
2610 }
2611 });
2612 continue;
2613 },
2614 Token.Id.Keyword_asm => {
2615 const node = try createToCtxNode(arena, opt_ctx, ast.Node.Asm,
2616 ast.Node.Asm {
2617 .base = undefined,
2618 .asm_token = token.index,
2619 .volatile_token = null,
2620 .template = undefined,
2621 .outputs = ast.Node.Asm.OutputList.init(arena),
2622 .inputs = ast.Node.Asm.InputList.init(arena),
2623 .clobbers = ast.Node.Asm.ClobberList.init(arena),
2624 .rparen = undefined,
2625 }
2626 );
2627 stack.append(State {
2628 .ExpectTokenSave = ExpectTokenSave {
2629 .id = Token.Id.RParen,
2630 .ptr = &node.rparen,
2631 }
2632 }) catch unreachable;
2633 try stack.append(State { .AsmClobberItems = &node.clobbers });
2634 try stack.append(State { .IfToken = Token.Id.Colon });
2635 try stack.append(State { .AsmInputItems = &node.inputs });
2636 try stack.append(State { .IfToken = Token.Id.Colon });
2637 try stack.append(State { .AsmOutputItems = &node.outputs });
2638 try stack.append(State { .IfToken = Token.Id.Colon });
2639 try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.template } });
2640 try stack.append(State { .ExpectToken = Token.Id.LParen });
2641 try stack.append(State {
2642 .OptionalTokenSave = OptionalTokenSave {
2643 .id = Token.Id.Keyword_volatile,
2644 .ptr = &node.volatile_token,
2645 }
2646 });
2647 },
2648 Token.Id.Keyword_inline => {
2649 stack.append(State {
2650 .Inline = InlineCtx {
2651 .label = null,
2652 .inline_token = token.index,
2653 .opt_ctx = opt_ctx,
2654 }
2655 }) catch unreachable;
2656 continue;
2657 },
2658 else => {
2659 if (!try parseBlockExpr(&stack, arena, opt_ctx, token.ptr, token.index)) {
2660 putBackToken(&tok_it, &tree);
2661 if (opt_ctx != OptionalCtx.Optional) {
2662 *(try tree.errors.addOne()) = Error {
2663 .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token.index },
2664 };
2665 return tree;
2666 }
2667 }
2668 continue;
2669 }
2670 }
2671 },
2672
2673
2674 State.ErrorTypeOrSetDecl => |ctx| {
2675 if (eatToken(&tok_it, &tree, Token.Id.LBrace) == null) {
2676 _ = try createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token);
2677 continue;
2678 }
2679
2680 const node = try arena.construct(ast.Node.ErrorSetDecl {
2681 .base = ast.Node {
2682 .id = ast.Node.Id.ErrorSetDecl,
2683 },
2684 .error_token = ctx.error_token,
2685 .decls = ast.Node.ErrorSetDecl.DeclList.init(arena),
2686 .rbrace_token = undefined,
2687 });
2688 ctx.opt_ctx.store(&node.base);
2689
2690 stack.append(State {
2691 .ErrorTagListItemOrEnd = ListSave(@typeOf(node.decls)) {
2692 .list = &node.decls,
2693 .ptr = &node.rbrace_token,
2694 }
2695 }) catch unreachable;
2696 continue;
2697 },
2698 State.StringLiteral => |opt_ctx| {
2699 const token = nextToken(&tok_it, &tree);
2700 const token_index = token.index;
2701 const token_ptr = token.ptr;
2702 opt_ctx.store(
2703 (try parseStringLiteral(arena, &tok_it, token_ptr, token_index, &tree)) ?? {
2704 putBackToken(&tok_it, &tree);
2705 if (opt_ctx != OptionalCtx.Optional) {
2706 *(try tree.errors.addOne()) = Error {
2707 .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr { .token = token_index },
2708 };
2709 return tree;
2710 }
2711
2712 continue;
2713 }
2714 );
2715 },
2716
2717 State.Identifier => |opt_ctx| {
2718 if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |ident_token| {
2719 _ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token);
2720 continue;
2721 }
2722
2723 if (opt_ctx != OptionalCtx.Optional) {
2724 const token = nextToken(&tok_it, &tree);
2725 const token_index = token.index;
2726 const token_ptr = token.ptr;
2727 *(try tree.errors.addOne()) = Error {
2728 .ExpectedToken = Error.ExpectedToken {
2729 .token = token_index,
2730 .expected_id = Token.Id.Identifier,
2731 },
2732 };
2733 return tree;
2734 }
2735 },
2736
2737 State.ErrorTag => |node_ptr| {
2738 const comments = try eatDocComments(arena, &tok_it, &tree);
2739 const ident_token = nextToken(&tok_it, &tree);
2740 const ident_token_index = ident_token.index;
2741 const ident_token_ptr = ident_token.ptr;
2742 if (ident_token_ptr.id != Token.Id.Identifier) {
2743 *(try tree.errors.addOne()) = Error {
2744 .ExpectedToken = Error.ExpectedToken {
2745 .token = ident_token_index,
2746 .expected_id = Token.Id.Identifier,
2747 },
2748 };
2749 return tree;
2750 }
2751
2752 const node = try arena.construct(ast.Node.ErrorTag {
2753 .base = ast.Node {
2754 .id = ast.Node.Id.ErrorTag,
2755 },
2756 .doc_comments = comments,
2757 .name_token = ident_token_index,
2758 });
2759 *node_ptr = &node.base;
2760 continue;
2761 },
2762
2763 State.ExpectToken => |token_id| {
2764 const token = nextToken(&tok_it, &tree);
2765 const token_index = token.index;
2766 const token_ptr = token.ptr;
2767 if (token_ptr.id != token_id) {
2768 *(try tree.errors.addOne()) = Error {
2769 .ExpectedToken = Error.ExpectedToken {
2770 .token = token_index,
2771 .expected_id = token_id,
2772 },
2773 };
2774 return tree;
2775 }
2776 continue;
2777 },
2778 State.ExpectTokenSave => |expect_token_save| {
2779 const token = nextToken(&tok_it, &tree);
2780 const token_index = token.index;
2781 const token_ptr = token.ptr;
2782 if (token_ptr.id != expect_token_save.id) {
2783 *(try tree.errors.addOne()) = Error {
2784 .ExpectedToken = Error.ExpectedToken {
2785 .token = token_index,
2786 .expected_id = expect_token_save.id,
2787 },
2788 };
2789 return tree;
2790 }
2791 *expect_token_save.ptr = token_index;
2792 continue;
2793 },
2794 State.IfToken => |token_id| {
2795 if (eatToken(&tok_it, &tree, token_id)) |_| {
2796 continue;
2797 }
2798
2799 _ = stack.pop();
2800 continue;
2801 },
2802 State.IfTokenSave => |if_token_save| {
2803 if (eatToken(&tok_it, &tree, if_token_save.id)) |token_index| {
2804 *if_token_save.ptr = token_index;
2805 continue;
2806 }
2807
2808 _ = stack.pop();
2809 continue;
2810 },
2811 State.OptionalTokenSave => |optional_token_save| {
2812 if (eatToken(&tok_it, &tree, optional_token_save.id)) |token_index| {
2813 *optional_token_save.ptr = token_index;
2814 continue;
2815 }
2816
2817 continue;
2818 },
2819 }
2820 }
2821}
2822
2823const AnnotatedToken = struct {
2824 ptr: &Token,
2825 index: TokenIndex,
2826};
2827
2828const TopLevelDeclCtx = struct {
2829 decls: &ast.Node.Root.DeclList,
2830 visib_token: ?TokenIndex,
2831 extern_export_inline_token: ?AnnotatedToken,
2832 lib_name: ?&ast.Node,
2833 comments: ?&ast.Node.DocComment,
2834};
2835
2836const VarDeclCtx = struct {
2837 mut_token: TokenIndex,
2838 visib_token: ?TokenIndex,
2839 comptime_token: ?TokenIndex,
2840 extern_export_token: ?TokenIndex,
2841 lib_name: ?&ast.Node,
2842 list: &ast.Node.Root.DeclList,
2843 comments: ?&ast.Node.DocComment,
2844};
2845
2846const TopLevelExternOrFieldCtx = struct {
2847 visib_token: TokenIndex,
2848 container_decl: &ast.Node.ContainerDecl,
2849 comments: ?&ast.Node.DocComment,
2850};
2851
2852const ExternTypeCtx = struct {
2853 opt_ctx: OptionalCtx,
2854 extern_token: TokenIndex,
2855 comments: ?&ast.Node.DocComment,
2856};
2857
2858const ContainerKindCtx = struct {
2859 opt_ctx: OptionalCtx,
2860 ltoken: TokenIndex,
2861 layout: ast.Node.ContainerDecl.Layout,
2862};
2863
2864const ExpectTokenSave = struct {
2865 id: @TagType(Token.Id),
2866 ptr: &TokenIndex,
2867};
2868
2869const OptionalTokenSave = struct {
2870 id: @TagType(Token.Id),
2871 ptr: &?TokenIndex,
2872};
2873
2874const ExprListCtx = struct {
2875 list: &ast.Node.SuffixOp.Op.InitList,
2876 end: Token.Id,
2877 ptr: &TokenIndex,
2878};
2879
2880fn ListSave(comptime List: type) type {
2881 return struct {
2882 list: &List,
2883 ptr: &TokenIndex,
2884 };
2885}
2886
2887const MaybeLabeledExpressionCtx = struct {
2888 label: TokenIndex,
2889 opt_ctx: OptionalCtx,
2890};
2891
2892const LabelCtx = struct {
2893 label: ?TokenIndex,
2894 opt_ctx: OptionalCtx,
2895};
2896
2897const InlineCtx = struct {
2898 label: ?TokenIndex,
2899 inline_token: ?TokenIndex,
2900 opt_ctx: OptionalCtx,
2901};
2902
2903const LoopCtx = struct {
2904 label: ?TokenIndex,
2905 inline_token: ?TokenIndex,
2906 loop_token: TokenIndex,
2907 opt_ctx: OptionalCtx,
2908};
2909
2910const AsyncEndCtx = struct {
2911 ctx: OptionalCtx,
2912 attribute: &ast.Node.AsyncAttribute,
2913};
2914
2915const ErrorTypeOrSetDeclCtx = struct {
2916 opt_ctx: OptionalCtx,
2917 error_token: TokenIndex,
2918};
2919
2920const ParamDeclEndCtx = struct {
2921 fn_proto: &ast.Node.FnProto,
2922 param_decl: &ast.Node.ParamDecl,
2923};
2924
2925const ComptimeStatementCtx = struct {
2926 comptime_token: TokenIndex,
2927 block: &ast.Node.Block,
2928};
2929
2930const OptionalCtx = union(enum) {
2931 Optional: &?&ast.Node,
2932 RequiredNull: &?&ast.Node,
2933 Required: &&ast.Node,
2934
2935 pub fn store(self: &const OptionalCtx, value: &ast.Node) void {
2936 switch (*self) {
2937 OptionalCtx.Optional => |ptr| *ptr = value,
2938 OptionalCtx.RequiredNull => |ptr| *ptr = value,
2939 OptionalCtx.Required => |ptr| *ptr = value,
2940 }
2941 }
2942
2943 pub fn get(self: &const OptionalCtx) ?&ast.Node {
2944 switch (*self) {
2945 OptionalCtx.Optional => |ptr| return *ptr,
2946 OptionalCtx.RequiredNull => |ptr| return ??*ptr,
2947 OptionalCtx.Required => |ptr| return *ptr,
2948 }
2949 }
2950
2951 pub fn toRequired(self: &const OptionalCtx) OptionalCtx {
2952 switch (*self) {
2953 OptionalCtx.Optional => |ptr| {
2954 return OptionalCtx { .RequiredNull = ptr };
2955 },
2956 OptionalCtx.RequiredNull => |ptr| return *self,
2957 OptionalCtx.Required => |ptr| return *self,
2958 }
2959 }
2960};
2961
2962const AddCommentsCtx = struct {
2963 node_ptr: &&ast.Node,
2964 comments: ?&ast.Node.DocComment,
2965};
2966
2967const State = union(enum) {
2968 TopLevel,
2969 TopLevelExtern: TopLevelDeclCtx,
2970 TopLevelLibname: TopLevelDeclCtx,
2971 TopLevelDecl: TopLevelDeclCtx,
2972 TopLevelExternOrField: TopLevelExternOrFieldCtx,
2973
2974 ContainerKind: ContainerKindCtx,
2975 ContainerInitArgStart: &ast.Node.ContainerDecl,
2976 ContainerInitArg: &ast.Node.ContainerDecl,
2977 ContainerDecl: &ast.Node.ContainerDecl,
2978
2979 VarDecl: VarDeclCtx,
2980 VarDeclAlign: &ast.Node.VarDecl,
2981 VarDeclEq: &ast.Node.VarDecl,
2982
2983 FnDef: &ast.Node.FnProto,
2984 FnProto: &ast.Node.FnProto,
2985 FnProtoAlign: &ast.Node.FnProto,
2986 FnProtoReturnType: &ast.Node.FnProto,
2987
2988 ParamDecl: &ast.Node.FnProto,
2989 ParamDeclAliasOrComptime: &ast.Node.ParamDecl,
2990 ParamDeclName: &ast.Node.ParamDecl,
2991 ParamDeclEnd: ParamDeclEndCtx,
2992 ParamDeclComma: &ast.Node.FnProto,
2993
2994 MaybeLabeledExpression: MaybeLabeledExpressionCtx,
2995 LabeledExpression: LabelCtx,
2996 Inline: InlineCtx,
2997 While: LoopCtx,
2998 WhileContinueExpr: &?&ast.Node,
2999 For: LoopCtx,
3000 Else: &?&ast.Node.Else,
3001
3002 Block: &ast.Node.Block,
3003 Statement: &ast.Node.Block,
3004 ComptimeStatement: ComptimeStatementCtx,
3005 Semicolon: &&ast.Node,
3006
3007 AsmOutputItems: &ast.Node.Asm.OutputList,
3008 AsmOutputReturnOrType: &ast.Node.AsmOutput,
3009 AsmInputItems: &ast.Node.Asm.InputList,
3010 AsmClobberItems: &ast.Node.Asm.ClobberList,
3011
3012 ExprListItemOrEnd: ExprListCtx,
3013 ExprListCommaOrEnd: ExprListCtx,
3014 FieldInitListItemOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList),
3015 FieldInitListCommaOrEnd: ListSave(ast.Node.SuffixOp.Op.InitList),
3016 FieldListCommaOrEnd: &ast.Node.ContainerDecl,
3017 FieldInitValue: OptionalCtx,
3018 ErrorTagListItemOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList),
3019 ErrorTagListCommaOrEnd: ListSave(ast.Node.ErrorSetDecl.DeclList),
3020 SwitchCaseOrEnd: ListSave(ast.Node.Switch.CaseList),
3021 SwitchCaseCommaOrEnd: ListSave(ast.Node.Switch.CaseList),
3022 SwitchCaseFirstItem: &ast.Node.SwitchCase.ItemList,
3023 SwitchCaseItem: &ast.Node.SwitchCase.ItemList,
3024 SwitchCaseItemCommaOrEnd: &ast.Node.SwitchCase.ItemList,
3025
3026 SuspendBody: &ast.Node.Suspend,
3027 AsyncAllocator: &ast.Node.AsyncAttribute,
3028 AsyncEnd: AsyncEndCtx,
3029
3030 ExternType: ExternTypeCtx,
3031 SliceOrArrayAccess: &ast.Node.SuffixOp,
3032 SliceOrArrayType: &ast.Node.PrefixOp,
3033 AddrOfModifiers: &ast.Node.PrefixOp.AddrOfInfo,
3034
3035 Payload: OptionalCtx,
3036 PointerPayload: OptionalCtx,
3037 PointerIndexPayload: OptionalCtx,
3038
3039 Expression: OptionalCtx,
3040 RangeExpressionBegin: OptionalCtx,
3041 RangeExpressionEnd: OptionalCtx,
3042 AssignmentExpressionBegin: OptionalCtx,
3043 AssignmentExpressionEnd: OptionalCtx,
3044 UnwrapExpressionBegin: OptionalCtx,
3045 UnwrapExpressionEnd: OptionalCtx,
3046 BoolOrExpressionBegin: OptionalCtx,
3047 BoolOrExpressionEnd: OptionalCtx,
3048 BoolAndExpressionBegin: OptionalCtx,
3049 BoolAndExpressionEnd: OptionalCtx,
3050 ComparisonExpressionBegin: OptionalCtx,
3051 ComparisonExpressionEnd: OptionalCtx,
3052 BinaryOrExpressionBegin: OptionalCtx,
3053 BinaryOrExpressionEnd: OptionalCtx,
3054 BinaryXorExpressionBegin: OptionalCtx,
3055 BinaryXorExpressionEnd: OptionalCtx,
3056 BinaryAndExpressionBegin: OptionalCtx,
3057 BinaryAndExpressionEnd: OptionalCtx,
3058 BitShiftExpressionBegin: OptionalCtx,
3059 BitShiftExpressionEnd: OptionalCtx,
3060 AdditionExpressionBegin: OptionalCtx,
3061 AdditionExpressionEnd: OptionalCtx,
3062 MultiplyExpressionBegin: OptionalCtx,
3063 MultiplyExpressionEnd: OptionalCtx,
3064 CurlySuffixExpressionBegin: OptionalCtx,
3065 CurlySuffixExpressionEnd: OptionalCtx,
3066 TypeExprBegin: OptionalCtx,
3067 TypeExprEnd: OptionalCtx,
3068 PrefixOpExpression: OptionalCtx,
3069 SuffixOpExpressionBegin: OptionalCtx,
3070 SuffixOpExpressionEnd: OptionalCtx,
3071 PrimaryExpression: OptionalCtx,
3072
3073 ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx,
3074 StringLiteral: OptionalCtx,
3075 Identifier: OptionalCtx,
3076 ErrorTag: &&ast.Node,
3077
3078
3079 IfToken: @TagType(Token.Id),
3080 IfTokenSave: ExpectTokenSave,
3081 ExpectToken: @TagType(Token.Id),
3082 ExpectTokenSave: ExpectTokenSave,
3083 OptionalTokenSave: OptionalTokenSave,
3084};
3085
3086fn eatDocComments(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) !?&ast.Node.DocComment {
3087 var result: ?&ast.Node.DocComment = null;
3088 while (true) {
3089 if (eatToken(tok_it, tree, Token.Id.DocComment)) |line_comment| {
3090 const node = blk: {
3091 if (result) |comment_node| {
3092 break :blk comment_node;
3093 } else {
3094 const comment_node = try arena.construct(ast.Node.DocComment {
3095 .base = ast.Node {
3096 .id = ast.Node.Id.DocComment,
3097 },
3098 .lines = ast.Node.DocComment.LineList.init(arena),
3099 });
3100 result = comment_node;
3101 break :blk comment_node;
3102 }
3103 };
3104 try node.lines.push(line_comment);
3105 continue;
3106 }
3107 break;
3108 }
3109 return result;
3110}
3111
3112fn eatLineComment(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) !?&ast.Node.LineComment {
3113 const token = eatToken(tok_it, tree, Token.Id.LineComment) ?? return null;
3114 return try arena.construct(ast.Node.LineComment {
3115 .base = ast.Node {
3116 .id = ast.Node.Id.LineComment,
3117 },
3118 .token = token,
3119 });
3120}
3121
3122fn parseStringLiteral(arena: &mem.Allocator, tok_it: &ast.Tree.TokenList.Iterator,
3123 token_ptr: &const Token, token_index: TokenIndex, tree: &ast.Tree) !?&ast.Node
3124{
3125 switch (token_ptr.id) {
3126 Token.Id.StringLiteral => {
3127 return &(try createLiteral(arena, ast.Node.StringLiteral, token_index)).base;
3128 },
3129 Token.Id.MultilineStringLiteralLine => {
3130 const node = try arena.construct(ast.Node.MultilineStringLiteral {
3131 .base = ast.Node { .id = ast.Node.Id.MultilineStringLiteral },
3132 .lines = ast.Node.MultilineStringLiteral.LineList.init(arena),
3133 });
3134 try node.lines.push(token_index);
3135 while (true) {
3136 const multiline_str = nextToken(tok_it, tree);
3137 const multiline_str_index = multiline_str.index;
3138 const multiline_str_ptr = multiline_str.ptr;
3139 if (multiline_str_ptr.id != Token.Id.MultilineStringLiteralLine) {
3140 putBackToken(tok_it, tree);
3141 break;
3142 }
3143
3144 try node.lines.push(multiline_str_index);
3145 }
3146
3147 return &node.base;
3148 },
3149 // TODO: We shouldn't need a cast, but:
3150 // zig: /home/jc/Documents/zig/src/ir.cpp:7962: TypeTableEntry* ir_resolve_peer_types(IrAnalyze*, AstNode*, IrInstruction**, size_t): Assertion `err_set_type != nullptr' failed.
3151 else => return (?&ast.Node)(null),
3152 }
3153}
3154
3155fn parseBlockExpr(stack: &std.ArrayList(State), arena: &mem.Allocator, ctx: &const OptionalCtx,
3156 token_ptr: &const Token, token_index: TokenIndex) !bool {
3157 switch (token_ptr.id) {
3158 Token.Id.Keyword_suspend => {
3159 const node = try createToCtxNode(arena, ctx, ast.Node.Suspend,
3160 ast.Node.Suspend {
3161 .base = undefined,
3162 .label = null,
3163 .suspend_token = token_index,
3164 .payload = null,
3165 .body = null,
3166 }
3167 );
3168
3169 stack.append(State { .SuspendBody = node }) catch unreachable;
3170 try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
3171 return true;
3172 },
3173 Token.Id.Keyword_if => {
3174 const node = try createToCtxNode(arena, ctx, ast.Node.If,
3175 ast.Node.If {
3176 .base = undefined,
3177 .if_token = token_index,
3178 .condition = undefined,
3179 .payload = null,
3180 .body = undefined,
3181 .@"else" = null,
3182 }
3183 );
3184
3185 stack.append(State { .Else = &node.@"else" }) catch unreachable;
3186 try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } });
3187 try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
3188 try stack.append(State { .ExpectToken = Token.Id.RParen });
3189 try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } });
3190 try stack.append(State { .ExpectToken = Token.Id.LParen });
3191 return true;
3192 },
3193 Token.Id.Keyword_while => {
3194 stack.append(State {
3195 .While = LoopCtx {
3196 .label = null,
3197 .inline_token = null,
3198 .loop_token = token_index,
3199 .opt_ctx = *ctx,
3200 }
3201 }) catch unreachable;
3202 return true;
3203 },
3204 Token.Id.Keyword_for => {
3205 stack.append(State {
3206 .For = LoopCtx {
3207 .label = null,
3208 .inline_token = null,
3209 .loop_token = token_index,
3210 .opt_ctx = *ctx,
3211 }
3212 }) catch unreachable;
3213 return true;
3214 },
3215 Token.Id.Keyword_switch => {
3216 const node = try arena.construct(ast.Node.Switch {
3217 .base = ast.Node {
3218 .id = ast.Node.Id.Switch,
3219 },
3220 .switch_token = token_index,
3221 .expr = undefined,
3222 .cases = ast.Node.Switch.CaseList.init(arena),
3223 .rbrace = undefined,
3224 });
3225 ctx.store(&node.base);
3226
3227 stack.append(State {
3228 .SwitchCaseOrEnd = ListSave(@typeOf(node.cases)) {
3229 .list = &node.cases,
3230 .ptr = &node.rbrace,
3231 },
3232 }) catch unreachable;
3233 try stack.append(State { .ExpectToken = Token.Id.LBrace });
3234 try stack.append(State { .ExpectToken = Token.Id.RParen });
3235 try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
3236 try stack.append(State { .ExpectToken = Token.Id.LParen });
3237 return true;
3238 },
3239 Token.Id.Keyword_comptime => {
3240 const node = try createToCtxNode(arena, ctx, ast.Node.Comptime,
3241 ast.Node.Comptime {
3242 .base = undefined,
3243 .comptime_token = token_index,
3244 .expr = undefined,
3245 .doc_comments = null,
3246 }
3247 );
3248 try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
3249 return true;
3250 },
3251 Token.Id.LBrace => {
3252 const block = try arena.construct(ast.Node.Block {
3253 .base = ast.Node {.id = ast.Node.Id.Block },
3254 .label = null,
3255 .lbrace = token_index,
3256 .statements = ast.Node.Block.StatementList.init(arena),
3257 .rbrace = undefined,
3258 });
3259 ctx.store(&block.base);
3260 stack.append(State { .Block = block }) catch unreachable;
3261 return true;
3262 },
3263 else => {
3264 return false;
3265 }
3266 }
3267}
3268
3269const ExpectCommaOrEndResult = union(enum) {
3270 end_token: ?TokenIndex,
3271 parse_error: Error,
3272};
3273
3274fn expectCommaOrEnd(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, end: @TagType(Token.Id)) ExpectCommaOrEndResult {
3275 const token = nextToken(tok_it, tree);
3276 const token_index = token.index;
3277 const token_ptr = token.ptr;
3278 switch (token_ptr.id) {
3279 Token.Id.Comma => return ExpectCommaOrEndResult { .end_token = null},
3280 else => {
3281 if (end == token_ptr.id) {
3282 return ExpectCommaOrEndResult { .end_token = token_index };
3283 }
3284
3285 return ExpectCommaOrEndResult {
3286 .parse_error = Error {
3287 .ExpectedCommaOrEnd = Error.ExpectedCommaOrEnd {
3288 .token = token_index,
3289 .end_id = end,
3290 },
3291 },
3292 };
3293 },
3294 }
3295}
3296
3297fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op {
3298 // TODO: We have to cast all cases because of this:
3299 // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
3300 return switch (*id) {
3301 Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = {} },
3302 Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = {} },
3303 Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = {} },
3304 Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = {} },
3305 Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = {} },
3306 Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = {} },
3307 Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = {} },
3308 Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = {} },
3309 Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = {} },
3310 Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = {} },
3311 Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = {} },
3312 Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = {} },
3313 Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = {} },
3314 Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = {} },
3315 else => null,
3316 };
3317}
3318
3319fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
3320 return switch (id) {
3321 Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null },
3322 Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} },
3323 else => null,
3324 };
3325}
3326
3327fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
3328 return switch (id) {
3329 Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} },
3330 Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} },
3331 Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} },
3332 Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} },
3333 Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} },
3334 Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} },
3335 else => null,
3336 };
3337}
3338
3339fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
3340 return switch (id) {
3341 Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} },
3342 Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} },
3343 else => null,
3344 };
3345}
3346
3347fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
3348 return switch (id) {
3349 Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} },
3350 Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} },
3351 Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} },
3352 Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} },
3353 Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} },
3354 else => null,
3355 };
3356}
3357
3358fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
3359 return switch (id) {
3360 Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} },
3361 Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} },
3362 Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} },
3363 Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} },
3364 Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} },
3365 Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} },
3366 else => null,
3367 };
3368}
3369
3370fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op {
3371 return switch (id) {
3372 Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} },
3373 Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} },
3374 Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} },
3375 Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} },
3376 Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} },
3377 Token.Id.Ampersand => ast.Node.PrefixOp.Op {
3378 .AddrOf = ast.Node.PrefixOp.AddrOfInfo {
3379 .align_expr = null,
3380 .bit_offset_start_token = null,
3381 .bit_offset_end_token = null,
3382 .const_token = null,
3383 .volatile_token = null,
3384 },
3385 },
3386 Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} },
3387 Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} },
3388 Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} },
3389 Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } },
3390 else => null,
3391 };
3392}
3393
3394fn createNode(arena: &mem.Allocator, comptime T: type, init_to: &const T) !&T {
3395 const node = try arena.create(T);
3396 *node = *init_to;
3397 node.base = blk: {
3398 const id = ast.Node.typeToId(T);
3399 break :blk ast.Node {
3400 .id = id,
3401 };
3402 };
3403
3404 return node;
3405}
3406
3407fn createToCtxNode(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, init_to: &const T) !&T {
3408 const node = try createNode(arena, T, init_to);
3409 opt_ctx.store(&node.base);
3410
3411 return node;
3412}
3413
3414fn createLiteral(arena: &mem.Allocator, comptime T: type, token_index: TokenIndex) !&T {
3415 return createNode(arena, T,
3416 T {
3417 .base = undefined,
3418 .token = token_index,
3419 }
3420 );
3421}
3422
3423fn createToCtxLiteral(arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token_index: TokenIndex) !&T {
3424 const node = try createLiteral(arena, T, token_index);
3425 opt_ctx.store(&node.base);
3426
3427 return node;
3428}
3429
3430fn eatToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree, id: @TagType(Token.Id)) ?TokenIndex {
3431 const token = nextToken(tok_it, tree);
3432
3433 if (token.ptr.id == id)
3434 return token.index;
3435
3436 putBackToken(tok_it, tree);
3437 return null;
3438}
3439
3440fn nextToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) AnnotatedToken {
3441 const result = AnnotatedToken {
3442 .index = tok_it.index,
3443 .ptr = ??tok_it.next(),
3444 };
3445 // possibly skip a following same line token
3446 const token = tok_it.next() ?? return result;
3447 if (token.id != Token.Id.LineComment) {
3448 putBackToken(tok_it, tree);
3449 return result;
3450 }
3451 const loc = tree.tokenLocationPtr(result.ptr.end, token);
3452 if (loc.line != 0) {
3453 putBackToken(tok_it, tree);
3454 }
3455 return result;
3456}
3457
3458fn putBackToken(tok_it: &ast.Tree.TokenList.Iterator, tree: &ast.Tree) void {
3459 const prev_tok = ??tok_it.prev();
3460 if (prev_tok.id == Token.Id.LineComment) {
3461 const minus2_tok = tok_it.prev() ?? return;
3462 const loc = tree.tokenLocationPtr(minus2_tok.end, prev_tok);
3463 if (loc.line != 0) {
3464 _ = tok_it.next();
3465 }
3466 }
3467}
3468
3469const RenderAstFrame = struct {
3470 node: &ast.Node,
3471 indent: usize,
3472};
3473
3474pub fn renderAst(allocator: &mem.Allocator, tree: &const ast.Tree, stream: var) !void {
3475 var stack = std.ArrayList(State).init(allocator);
3476 defer stack.deinit();
3477
3478 try stack.append(RenderAstFrame {
3479 .node = &root_node.base,
3480 .indent = 0,
3481 });
3482
3483 while (stack.popOrNull()) |frame| {
3484 {
3485 var i: usize = 0;
3486 while (i < frame.indent) : (i += 1) {
3487 try stream.print(" ");
3488 }
3489 }
3490 try stream.print("{}\n", @tagName(frame.node.id));
3491 var child_i: usize = 0;
3492 while (frame.node.iterate(child_i)) |child| : (child_i += 1) {
3493 try stack.append(RenderAstFrame {
3494 .node = child,
3495 .indent = frame.indent + 2,
3496 });
3497 }
3498 }
3499}
3500
3501test "std.zig.parser" {
3502 _ = @import("parser_test.zig");
3503}
std/zig/parser.zig deleted-1733
...@@ -1,1733 +0,0 @@
1const std = @import("../index.zig");
2const assert = std.debug.assert;
3const ArrayList = std.ArrayList;
4const mem = std.mem;
5const ast = std.zig.ast;
6const Tokenizer = std.zig.Tokenizer;
7const Token = std.zig.Token;
8const builtin = @import("builtin");
9const io = std.io;
10
11// TODO when we make parse errors into error types instead of printing directly,
12// get rid of this
13const warn = std.debug.warn;
14
15pub const Parser = struct {
16 util_allocator: &mem.Allocator,
17 tokenizer: &Tokenizer,
18 put_back_tokens: [2]Token,
19 put_back_count: usize,
20 source_file_name: []const u8,
21 pending_line_comment_node: ?&ast.NodeLineComment,
22
23 pub const Tree = struct {
24 root_node: &ast.NodeRoot,
25 arena_allocator: std.heap.ArenaAllocator,
26
27 pub fn deinit(self: &Tree) void {
28 self.arena_allocator.deinit();
29 }
30 };
31
32 // This memory contents are used only during a function call. It's used to repurpose memory;
33 // we reuse the same bytes for the stack data structure used by parsing, tree rendering, and
34 // source rendering.
35 const utility_bytes_align = @alignOf( union { a: RenderAstFrame, b: State, c: RenderState } );
36 utility_bytes: []align(utility_bytes_align) u8,
37
38 /// allocator must outlive the returned Parser and all the parse trees you create with it.
39 pub fn init(tokenizer: &Tokenizer, allocator: &mem.Allocator, source_file_name: []const u8) Parser {
40 return Parser {
41 .util_allocator = allocator,
42 .tokenizer = tokenizer,
43 .put_back_tokens = undefined,
44 .put_back_count = 0,
45 .source_file_name = source_file_name,
46 .utility_bytes = []align(utility_bytes_align) u8{},
47 .pending_line_comment_node = null,
48 };
49 }
50
51 pub fn deinit(self: &Parser) void {
52 self.util_allocator.free(self.utility_bytes);
53 }
54
55 const TopLevelDeclCtx = struct {
56 visib_token: ?Token,
57 extern_token: ?Token,
58 };
59
60 const DestPtr = union(enum) {
61 Field: &&ast.Node,
62 NullableField: &?&ast.Node,
63 List: &ArrayList(&ast.Node),
64
65 pub fn store(self: &const DestPtr, value: &ast.Node) !void {
66 switch (*self) {
67 DestPtr.Field => |ptr| *ptr = value,
68 DestPtr.NullableField => |ptr| *ptr = value,
69 DestPtr.List => |list| try list.append(value),
70 }
71 }
72 };
73
74 const ExpectTokenSave = struct {
75 id: Token.Id,
76 ptr: &Token,
77 };
78
79 const State = union(enum) {
80 TopLevel,
81 TopLevelExtern: ?Token,
82 TopLevelDecl: TopLevelDeclCtx,
83 Expression: DestPtr,
84 ExpectOperand,
85 Operand: &ast.Node,
86 AfterOperand,
87 InfixOp: &ast.NodeInfixOp,
88 PrefixOp: &ast.NodePrefixOp,
89 SuffixOp: &ast.Node,
90 AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
91 TypeExpr: DestPtr,
92 VarDecl: &ast.NodeVarDecl,
93 VarDeclAlign: &ast.NodeVarDecl,
94 VarDeclEq: &ast.NodeVarDecl,
95 ExpectToken: @TagType(Token.Id),
96 ExpectTokenSave: ExpectTokenSave,
97 FnProto: &ast.NodeFnProto,
98 FnProtoAlign: &ast.NodeFnProto,
99 FnProtoReturnType: &ast.NodeFnProto,
100 ParamDecl: &ast.NodeFnProto,
101 ParamDeclComma,
102 FnDef: &ast.NodeFnProto,
103 Block: &ast.NodeBlock,
104 Statement: &ast.NodeBlock,
105 ExprListItemOrEnd: &ArrayList(&ast.Node),
106 ExprListCommaOrEnd: &ArrayList(&ast.Node),
107 };
108
109 /// Returns an AST tree, allocated with the parser's allocator.
110 /// Result should be freed with tree.deinit() when there are
111 /// no more references to any AST nodes of the tree.
112 pub fn parse(self: &Parser) !Tree {
113 var stack = self.initUtilityArrayList(State);
114 defer self.deinitUtilityArrayList(stack);
115
116 var arena_allocator = std.heap.ArenaAllocator.init(self.util_allocator);
117 errdefer arena_allocator.deinit();
118
119 const arena = &arena_allocator.allocator;
120 const root_node = try self.createRoot(arena);
121
122 try stack.append(State.TopLevel);
123
124 while (true) {
125 //{
126 // const token = self.getNextToken();
127 // warn("{} ", @tagName(token.id));
128 // self.putBackToken(token);
129 // var i: usize = stack.len;
130 // while (i != 0) {
131 // i -= 1;
132 // warn("{} ", @tagName(stack.items[i]));
133 // }
134 // warn("\n");
135 //}
136
137 // look for line comments
138 while (true) {
139 const token = self.getNextToken();
140 if (token.id == Token.Id.LineComment) {
141 const node = blk: {
142 if (self.pending_line_comment_node) |comment_node| {
143 break :blk comment_node;
144 } else {
145 const comment_node = try arena.create(ast.NodeLineComment);
146 *comment_node = ast.NodeLineComment {
147 .base = ast.Node {
148 .id = ast.Node.Id.LineComment,
149 .comment = null,
150 },
151 .lines = ArrayList(Token).init(arena),
152 };
153 self.pending_line_comment_node = comment_node;
154 break :blk comment_node;
155 }
156 };
157 try node.lines.append(token);
158 continue;
159 }
160 self.putBackToken(token);
161 break;
162 }
163
164 // This gives us 1 free append that can't fail
165 const state = stack.pop();
166
167 switch (state) {
168 State.TopLevel => {
169 const token = self.getNextToken();
170 switch (token.id) {
171 Token.Id.Keyword_pub, Token.Id.Keyword_export => {
172 stack.append(State { .TopLevelExtern = token }) catch unreachable;
173 continue;
174 },
175 Token.Id.Keyword_test => {
176 stack.append(State.TopLevel) catch unreachable;
177
178 const name_token = self.getNextToken();
179 if (name_token.id != Token.Id.StringLiteral)
180 return self.parseError(token, "expected {}, found {}", @tagName(Token.Id.StringLiteral), @tagName(name_token.id));
181
182 const lbrace = self.getNextToken();
183 if (lbrace.id != Token.Id.LBrace)
184 return self.parseError(token, "expected {}, found {}", @tagName(Token.Id.LBrace), @tagName(name_token.id));
185
186 const block = try self.createBlock(arena, token);
187 const test_decl = try self.createAttachTestDecl(arena, &root_node.decls, token, name_token, block);
188 try stack.append(State { .Block = block });
189 continue;
190 },
191 Token.Id.Eof => {
192 root_node.eof_token = token;
193 return Tree {.root_node = root_node, .arena_allocator = arena_allocator};
194 },
195 else => {
196 self.putBackToken(token);
197 stack.append(State { .TopLevelExtern = null }) catch unreachable;
198 continue;
199 },
200 }
201 },
202 State.TopLevelExtern => |visib_token| {
203 const token = self.getNextToken();
204 if (token.id == Token.Id.Keyword_extern) {
205 stack.append(State {
206 .TopLevelDecl = TopLevelDeclCtx {
207 .visib_token = visib_token,
208 .extern_token = token,
209 },
210 }) catch unreachable;
211 continue;
212 }
213 self.putBackToken(token);
214 stack.append(State {
215 .TopLevelDecl = TopLevelDeclCtx {
216 .visib_token = visib_token,
217 .extern_token = null,
218 },
219 }) catch unreachable;
220 continue;
221 },
222 State.TopLevelDecl => |ctx| {
223 const token = self.getNextToken();
224 switch (token.id) {
225 Token.Id.Keyword_var, Token.Id.Keyword_const => {
226 stack.append(State.TopLevel) catch unreachable;
227 // TODO shouldn't need these casts
228 const var_decl_node = try self.createAttachVarDecl(arena, &root_node.decls, ctx.visib_token,
229 token, (?Token)(null), ctx.extern_token);
230 try stack.append(State { .VarDecl = var_decl_node });
231 continue;
232 },
233 Token.Id.Keyword_fn => {
234 stack.append(State.TopLevel) catch unreachable;
235 // TODO shouldn't need these casts
236 const fn_proto = try self.createAttachFnProto(arena, &root_node.decls, token,
237 ctx.extern_token, (?Token)(null), ctx.visib_token, (?Token)(null));
238 try stack.append(State { .FnDef = fn_proto });
239 try stack.append(State { .FnProto = fn_proto });
240 continue;
241 },
242 Token.Id.StringLiteral => {
243 @panic("TODO extern with string literal");
244 },
245 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
246 stack.append(State.TopLevel) catch unreachable;
247 const fn_token = try self.eatToken(Token.Id.Keyword_fn);
248 // TODO shouldn't need this cast
249 const fn_proto = try self.createAttachFnProto(arena, &root_node.decls, fn_token,
250 ctx.extern_token, (?Token)(token), (?Token)(null), (?Token)(null));
251 try stack.append(State { .FnDef = fn_proto });
252 try stack.append(State { .FnProto = fn_proto });
253 continue;
254 },
255 else => return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id)),
256 }
257 },
258 State.VarDecl => |var_decl| {
259 var_decl.name_token = try self.eatToken(Token.Id.Identifier);
260 stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;
261
262 const next_token = self.getNextToken();
263 if (next_token.id == Token.Id.Colon) {
264 try stack.append(State { .TypeExpr = DestPtr {.NullableField = &var_decl.type_node} });
265 continue;
266 }
267
268 self.putBackToken(next_token);
269 continue;
270 },
271 State.VarDeclAlign => |var_decl| {
272 stack.append(State { .VarDeclEq = var_decl }) catch unreachable;
273
274 const next_token = self.getNextToken();
275 if (next_token.id == Token.Id.Keyword_align) {
276 _ = try self.eatToken(Token.Id.LParen);
277 try stack.append(State { .ExpectToken = Token.Id.RParen });
278 try stack.append(State { .Expression = DestPtr{.NullableField = &var_decl.align_node} });
279 continue;
280 }
281
282 self.putBackToken(next_token);
283 continue;
284 },
285 State.VarDeclEq => |var_decl| {
286 const token = self.getNextToken();
287 if (token.id == Token.Id.Equal) {
288 var_decl.eq_token = token;
289 stack.append(State {
290 .ExpectTokenSave = ExpectTokenSave {
291 .id = Token.Id.Semicolon,
292 .ptr = &var_decl.semicolon_token,
293 },
294 }) catch unreachable;
295 try stack.append(State {
296 .Expression = DestPtr {.NullableField = &var_decl.init_node},
297 });
298 continue;
299 }
300 if (token.id == Token.Id.Semicolon) {
301 var_decl.semicolon_token = token;
302 continue;
303 }
304 return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
305 },
306 State.ExpectToken => |token_id| {
307 _ = try self.eatToken(token_id);
308 continue;
309 },
310
311 State.ExpectTokenSave => |expect_token_save| {
312 *expect_token_save.ptr = try self.eatToken(expect_token_save.id);
313 continue;
314 },
315
316 State.Expression => |dest_ptr| {
317 // save the dest_ptr for later
318 stack.append(state) catch unreachable;
319 try stack.append(State.ExpectOperand);
320 continue;
321 },
322 State.ExpectOperand => {
323 // we'll either get an operand (like 1 or x),
324 // or a prefix operator (like ~ or return).
325 const token = self.getNextToken();
326 switch (token.id) {
327 Token.Id.Keyword_return => {
328 try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
329 ast.NodePrefixOp.PrefixOp.Return) });
330 try stack.append(State.ExpectOperand);
331 continue;
332 },
333 Token.Id.Keyword_try => {
334 try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
335 ast.NodePrefixOp.PrefixOp.Try) });
336 try stack.append(State.ExpectOperand);
337 continue;
338 },
339 Token.Id.Minus => {
340 try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
341 ast.NodePrefixOp.PrefixOp.Negation) });
342 try stack.append(State.ExpectOperand);
343 continue;
344 },
345 Token.Id.MinusPercent => {
346 try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
347 ast.NodePrefixOp.PrefixOp.NegationWrap) });
348 try stack.append(State.ExpectOperand);
349 continue;
350 },
351 Token.Id.Tilde => {
352 try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
353 ast.NodePrefixOp.PrefixOp.BitNot) });
354 try stack.append(State.ExpectOperand);
355 continue;
356 },
357 Token.Id.QuestionMarkQuestionMark => {
358 try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
359 ast.NodePrefixOp.PrefixOp.UnwrapMaybe) });
360 try stack.append(State.ExpectOperand);
361 continue;
362 },
363 Token.Id.Bang => {
364 try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
365 ast.NodePrefixOp.PrefixOp.BoolNot) });
366 try stack.append(State.ExpectOperand);
367 continue;
368 },
369 Token.Id.Asterisk => {
370 try stack.append(State { .PrefixOp = try self.createPrefixOp(arena, token,
371 ast.NodePrefixOp.PrefixOp.Deref) });
372 try stack.append(State.ExpectOperand);
373 continue;
374 },
375 Token.Id.Ampersand => {
376 const prefix_op = try self.createPrefixOp(arena, token, ast.NodePrefixOp.PrefixOp{
377 .AddrOf = ast.NodePrefixOp.AddrOfInfo {
378 .align_expr = null,
379 .bit_offset_start_token = null,
380 .bit_offset_end_token = null,
381 .const_token = null,
382 .volatile_token = null,
383 }
384 });
385 try stack.append(State { .PrefixOp = prefix_op });
386 try stack.append(State.ExpectOperand);
387 try stack.append(State { .AddrOfModifiers = &prefix_op.op.AddrOf });
388 continue;
389 },
390 Token.Id.Identifier => {
391 try stack.append(State {
392 .Operand = &(try self.createIdentifier(arena, token)).base
393 });
394 try stack.append(State.AfterOperand);
395 continue;
396 },
397 Token.Id.IntegerLiteral => {
398 try stack.append(State {
399 .Operand = &(try self.createIntegerLiteral(arena, token)).base
400 });
401 try stack.append(State.AfterOperand);
402 continue;
403 },
404 Token.Id.FloatLiteral => {
405 try stack.append(State {
406 .Operand = &(try self.createFloatLiteral(arena, token)).base
407 });
408 try stack.append(State.AfterOperand);
409 continue;
410 },
411 Token.Id.Keyword_undefined => {
412 try stack.append(State {
413 .Operand = &(try self.createUndefined(arena, token)).base
414 });
415 try stack.append(State.AfterOperand);
416 continue;
417 },
418 Token.Id.Builtin => {
419 const node = try arena.create(ast.NodeBuiltinCall);
420 *node = ast.NodeBuiltinCall {
421 .base = self.initNode(ast.Node.Id.BuiltinCall),
422 .builtin_token = token,
423 .params = ArrayList(&ast.Node).init(arena),
424 .rparen_token = undefined,
425 };
426 try stack.append(State {
427 .Operand = &node.base
428 });
429 try stack.append(State.AfterOperand);
430 try stack.append(State {.ExprListItemOrEnd = &node.params });
431 try stack.append(State {
432 .ExpectTokenSave = ExpectTokenSave {
433 .id = Token.Id.LParen,
434 .ptr = &node.rparen_token,
435 },
436 });
437 continue;
438 },
439 Token.Id.StringLiteral => {
440 const node = try arena.create(ast.NodeStringLiteral);
441 *node = ast.NodeStringLiteral {
442 .base = self.initNode(ast.Node.Id.StringLiteral),
443 .token = token,
444 };
445 try stack.append(State {
446 .Operand = &node.base
447 });
448 try stack.append(State.AfterOperand);
449 continue;
450 },
451
452 else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
453 }
454 },
455
456 State.AfterOperand => {
457 // we'll either get an infix operator (like != or ^),
458 // or a postfix operator (like () or {}),
459 // otherwise this expression is done (like on a ; or else).
460 var token = self.getNextToken();
461 if (tokenIdToInfixOp(token.id)) |infix_id| {
462 try stack.append(State {
463 .InfixOp = try self.createInfixOp(arena, token, infix_id)
464 });
465 try stack.append(State.ExpectOperand);
466 continue;
467
468 } else if (token.id == Token.Id.LParen) {
469 self.putBackToken(token);
470
471 const node = try arena.create(ast.NodeCall);
472 *node = ast.NodeCall {
473 .base = self.initNode(ast.Node.Id.Call),
474 .callee = undefined,
475 .params = ArrayList(&ast.Node).init(arena),
476 .rparen_token = undefined,
477 };
478 try stack.append(State { .SuffixOp = &node.base });
479 try stack.append(State.AfterOperand);
480 try stack.append(State {.ExprListItemOrEnd = &node.params });
481 try stack.append(State {
482 .ExpectTokenSave = ExpectTokenSave {
483 .id = Token.Id.LParen,
484 .ptr = &node.rparen_token,
485 },
486 });
487 continue;
488
489 // TODO: Parse postfix operator
490 } else {
491 // no postfix/infix operator after this operand.
492 self.putBackToken(token);
493
494 var expression = popSuffixOp(&stack);
495 while (true) {
496 switch (stack.pop()) {
497 State.Expression => |dest_ptr| {
498 // we're done
499 try dest_ptr.store(expression);
500 break;
501 },
502 State.InfixOp => |infix_op| {
503 infix_op.rhs = expression;
504 infix_op.lhs = popSuffixOp(&stack);
505 expression = &infix_op.base;
506 continue;
507 },
508 State.PrefixOp => |prefix_op| {
509 prefix_op.rhs = expression;
510 expression = &prefix_op.base;
511 continue;
512 },
513 else => unreachable,
514 }
515 }
516 continue;
517 }
518 },
519
520 State.ExprListItemOrEnd => |params| {
521 var token = self.getNextToken();
522 switch (token.id) {
523 Token.Id.RParen => continue,
524 else => {
525 self.putBackToken(token);
526 stack.append(State { .ExprListCommaOrEnd = params }) catch unreachable;
527 try stack.append(State { .Expression = DestPtr{.List = params} });
528 },
529 }
530 },
531
532 State.ExprListCommaOrEnd => |params| {
533 var token = self.getNextToken();
534 switch (token.id) {
535 Token.Id.Comma => {
536 stack.append(State { .ExprListItemOrEnd = params }) catch unreachable;
537 },
538 Token.Id.RParen => continue,
539 else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)),
540 }
541 },
542
543 State.AddrOfModifiers => |addr_of_info| {
544 var token = self.getNextToken();
545 switch (token.id) {
546 Token.Id.Keyword_align => {
547 stack.append(state) catch unreachable;
548 if (addr_of_info.align_expr != null) return self.parseError(token, "multiple align qualifiers");
549 _ = try self.eatToken(Token.Id.LParen);
550 try stack.append(State { .ExpectToken = Token.Id.RParen });
551 try stack.append(State { .Expression = DestPtr{.NullableField = &addr_of_info.align_expr} });
552 continue;
553 },
554 Token.Id.Keyword_const => {
555 stack.append(state) catch unreachable;
556 if (addr_of_info.const_token != null) return self.parseError(token, "duplicate qualifier: const");
557 addr_of_info.const_token = token;
558 continue;
559 },
560 Token.Id.Keyword_volatile => {
561 stack.append(state) catch unreachable;
562 if (addr_of_info.volatile_token != null) return self.parseError(token, "duplicate qualifier: volatile");
563 addr_of_info.volatile_token = token;
564 continue;
565 },
566 else => {
567 self.putBackToken(token);
568 continue;
569 },
570 }
571 },
572
573 State.TypeExpr => |dest_ptr| {
574 const token = self.getNextToken();
575 if (token.id == Token.Id.Keyword_var) {
576 @panic("TODO param with type var");
577 }
578 self.putBackToken(token);
579
580 stack.append(State { .Expression = dest_ptr }) catch unreachable;
581 continue;
582 },
583
584 State.FnProto => |fn_proto| {
585 stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable;
586 try stack.append(State { .ParamDecl = fn_proto });
587 try stack.append(State { .ExpectToken = Token.Id.LParen });
588
589 const next_token = self.getNextToken();
590 if (next_token.id == Token.Id.Identifier) {
591 fn_proto.name_token = next_token;
592 continue;
593 }
594 self.putBackToken(next_token);
595 continue;
596 },
597
598 State.FnProtoAlign => |fn_proto| {
599 const token = self.getNextToken();
600 if (token.id == Token.Id.Keyword_align) {
601 @panic("TODO fn proto align");
602 }
603 self.putBackToken(token);
604 stack.append(State {
605 .FnProtoReturnType = fn_proto,
606 }) catch unreachable;
607 continue;
608 },
609
610 State.FnProtoReturnType => |fn_proto| {
611 const token = self.getNextToken();
612 switch (token.id) {
613 Token.Id.Keyword_var => {
614 fn_proto.return_type = ast.NodeFnProto.ReturnType { .Infer = token };
615 },
616 Token.Id.Bang => {
617 fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined };
618 stack.append(State {
619 .TypeExpr = DestPtr {.Field = &fn_proto.return_type.InferErrorSet},
620 }) catch unreachable;
621 },
622 else => {
623 self.putBackToken(token);
624 fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined };
625 stack.append(State {
626 .TypeExpr = DestPtr {.Field = &fn_proto.return_type.Explicit},
627 }) catch unreachable;
628 },
629 }
630 if (token.id == Token.Id.Keyword_align) {
631 @panic("TODO fn proto align");
632 }
633 continue;
634 },
635
636 State.ParamDecl => |fn_proto| {
637 var token = self.getNextToken();
638 if (token.id == Token.Id.RParen) {
639 continue;
640 }
641 const param_decl = try self.createAttachParamDecl(arena, &fn_proto.params);
642 if (token.id == Token.Id.Keyword_comptime) {
643 param_decl.comptime_token = token;
644 token = self.getNextToken();
645 } else if (token.id == Token.Id.Keyword_noalias) {
646 param_decl.noalias_token = token;
647 token = self.getNextToken();
648 }
649 if (token.id == Token.Id.Identifier) {
650 const next_token = self.getNextToken();
651 if (next_token.id == Token.Id.Colon) {
652 param_decl.name_token = token;
653 token = self.getNextToken();
654 } else {
655 self.putBackToken(next_token);
656 }
657 }
658 if (token.id == Token.Id.Ellipsis3) {
659 param_decl.var_args_token = token;
660 stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
661 continue;
662 } else {
663 self.putBackToken(token);
664 }
665
666 stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
667 try stack.append(State.ParamDeclComma);
668 try stack.append(State {
669 .TypeExpr = DestPtr {.Field = &param_decl.type_node}
670 });
671 continue;
672 },
673
674 State.ParamDeclComma => {
675 const token = self.getNextToken();
676 switch (token.id) {
677 Token.Id.RParen => {
678 _ = stack.pop(); // pop off the ParamDecl
679 continue;
680 },
681 Token.Id.Comma => continue,
682 else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)),
683 }
684 },
685
686 State.FnDef => |fn_proto| {
687 const token = self.getNextToken();
688 switch(token.id) {
689 Token.Id.LBrace => {
690 const block = try self.createBlock(arena, token);
691 fn_proto.body_node = &block.base;
692 stack.append(State { .Block = block }) catch unreachable;
693 continue;
694 },
695 Token.Id.Semicolon => continue,
696 else => return self.parseError(token, "expected ';' or '{{', found {}", @tagName(token.id)),
697 }
698 },
699
700 State.Block => |block| {
701 const token = self.getNextToken();
702 switch (token.id) {
703 Token.Id.RBrace => {
704 block.end_token = token;
705 continue;
706 },
707 else => {
708 self.putBackToken(token);
709 stack.append(State { .Block = block }) catch unreachable;
710 try stack.append(State { .Statement = block });
711 continue;
712 },
713 }
714 },
715
716 State.Statement => |block| {
717 {
718 // Look for comptime var, comptime const
719 const comptime_token = self.getNextToken();
720 if (comptime_token.id == Token.Id.Keyword_comptime) {
721 const mut_token = self.getNextToken();
722 if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
723 // TODO shouldn't need these casts
724 const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
725 mut_token, (?Token)(comptime_token), (?Token)(null));
726 try stack.append(State { .VarDecl = var_decl });
727 continue;
728 }
729 self.putBackToken(mut_token);
730 }
731 self.putBackToken(comptime_token);
732 }
733 {
734 // Look for const, var
735 const mut_token = self.getNextToken();
736 if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
737 // TODO shouldn't need these casts
738 const var_decl = try self.createAttachVarDecl(arena, &block.statements, (?Token)(null),
739 mut_token, (?Token)(null), (?Token)(null));
740 try stack.append(State { .VarDecl = var_decl });
741 continue;
742 }
743 self.putBackToken(mut_token);
744 }
745
746 stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
747 try stack.append(State { .Expression = DestPtr{.List = &block.statements} });
748 continue;
749 },
750
751 // These are data, not control flow.
752 State.InfixOp => unreachable,
753 State.PrefixOp => unreachable,
754 State.SuffixOp => unreachable,
755 State.Operand => unreachable,
756 }
757 }
758 }
759
760 fn popSuffixOp(stack: &ArrayList(State)) &ast.Node {
761 var expression: &ast.Node = undefined;
762 var left_leaf_ptr: &&ast.Node = &expression;
763 while (true) {
764 switch (stack.pop()) {
765 State.SuffixOp => |suffix_op| {
766 switch (suffix_op.id) {
767 ast.Node.Id.Call => {
768 const call = @fieldParentPtr(ast.NodeCall, "base", suffix_op);
769 *left_leaf_ptr = &call.base;
770 left_leaf_ptr = &call.callee;
771 continue;
772 },
773 else => unreachable,
774 }
775 },
776 State.Operand => |operand| {
777 *left_leaf_ptr = operand;
778 break;
779 },
780 else => unreachable,
781 }
782 }
783
784 return expression;
785 }
786
787 fn tokenIdToInfixOp(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {
788 return switch (*id) {
789 Token.Id.Ampersand => ast.NodeInfixOp.InfixOp.BitAnd,
790 Token.Id.AmpersandEqual => ast.NodeInfixOp.InfixOp.AssignBitAnd,
791 Token.Id.AngleBracketAngleBracketLeft => ast.NodeInfixOp.InfixOp.BitShiftLeft,
792 Token.Id.AngleBracketAngleBracketLeftEqual => ast.NodeInfixOp.InfixOp.AssignBitShiftLeft,
793 Token.Id.AngleBracketAngleBracketRight => ast.NodeInfixOp.InfixOp.BitShiftRight,
794 Token.Id.AngleBracketAngleBracketRightEqual => ast.NodeInfixOp.InfixOp.AssignBitShiftRight,
795 Token.Id.AngleBracketLeft => ast.NodeInfixOp.InfixOp.LessThan,
796 Token.Id.AngleBracketLeftEqual => ast.NodeInfixOp.InfixOp.LessOrEqual,
797 Token.Id.AngleBracketRight => ast.NodeInfixOp.InfixOp.GreaterThan,
798 Token.Id.AngleBracketRightEqual => ast.NodeInfixOp.InfixOp.GreaterOrEqual,
799 Token.Id.Asterisk => ast.NodeInfixOp.InfixOp.Mult,
800 Token.Id.AsteriskAsterisk => ast.NodeInfixOp.InfixOp.ArrayMult,
801 Token.Id.AsteriskEqual => ast.NodeInfixOp.InfixOp.AssignTimes,
802 Token.Id.AsteriskPercent => ast.NodeInfixOp.InfixOp.MultWrap,
803 Token.Id.AsteriskPercentEqual => ast.NodeInfixOp.InfixOp.AssignTimesWarp,
804 Token.Id.Bang => ast.NodeInfixOp.InfixOp.ErrorUnion,
805 Token.Id.BangEqual => ast.NodeInfixOp.InfixOp.BangEqual,
806 Token.Id.Caret => ast.NodeInfixOp.InfixOp.BitXor,
807 Token.Id.CaretEqual => ast.NodeInfixOp.InfixOp.AssignBitXor,
808 Token.Id.Equal => ast.NodeInfixOp.InfixOp.Assign,
809 Token.Id.EqualEqual => ast.NodeInfixOp.InfixOp.EqualEqual,
810 Token.Id.Keyword_and => ast.NodeInfixOp.InfixOp.BoolAnd,
811 Token.Id.Keyword_or => ast.NodeInfixOp.InfixOp.BoolOr,
812 Token.Id.Minus => ast.NodeInfixOp.InfixOp.Sub,
813 Token.Id.MinusEqual => ast.NodeInfixOp.InfixOp.AssignMinus,
814 Token.Id.MinusPercent => ast.NodeInfixOp.InfixOp.SubWrap,
815 Token.Id.MinusPercentEqual => ast.NodeInfixOp.InfixOp.AssignMinusWrap,
816 Token.Id.Percent => ast.NodeInfixOp.InfixOp.Mod,
817 Token.Id.PercentEqual => ast.NodeInfixOp.InfixOp.AssignMod,
818 Token.Id.Period => ast.NodeInfixOp.InfixOp.Period,
819 Token.Id.Pipe => ast.NodeInfixOp.InfixOp.BitOr,
820 Token.Id.PipeEqual => ast.NodeInfixOp.InfixOp.AssignBitOr,
821 Token.Id.PipePipe => ast.NodeInfixOp.InfixOp.MergeErrorSets,
822 Token.Id.Plus => ast.NodeInfixOp.InfixOp.Add,
823 Token.Id.PlusEqual => ast.NodeInfixOp.InfixOp.AssignPlus,
824 Token.Id.PlusPercent => ast.NodeInfixOp.InfixOp.AddWrap,
825 Token.Id.PlusPercentEqual => ast.NodeInfixOp.InfixOp.AssignPlusWrap,
826 Token.Id.PlusPlus => ast.NodeInfixOp.InfixOp.ArrayCat,
827 Token.Id.QuestionMarkQuestionMark => ast.NodeInfixOp.InfixOp.UnwrapMaybe,
828 Token.Id.Slash => ast.NodeInfixOp.InfixOp.Div,
829 Token.Id.SlashEqual => ast.NodeInfixOp.InfixOp.AssignDiv,
830 else => null,
831 };
832 }
833
834 fn initNode(self: &Parser, id: ast.Node.Id) ast.Node {
835 if (self.pending_line_comment_node) |comment_node| {
836 self.pending_line_comment_node = null;
837 return ast.Node {.id = id, .comment = comment_node};
838 }
839 return ast.Node {.id = id, .comment = null };
840 }
841
842 fn createRoot(self: &Parser, arena: &mem.Allocator) !&ast.NodeRoot {
843 const node = try arena.create(ast.NodeRoot);
844
845 *node = ast.NodeRoot {
846 .base = self.initNode(ast.Node.Id.Root),
847 .decls = ArrayList(&ast.Node).init(arena),
848 // initialized when we get the eof token
849 .eof_token = undefined,
850 };
851 return node;
852 }
853
854 fn createVarDecl(self: &Parser, arena: &mem.Allocator, visib_token: &const ?Token, mut_token: &const Token,
855 comptime_token: &const ?Token, extern_token: &const ?Token) !&ast.NodeVarDecl
856 {
857 const node = try arena.create(ast.NodeVarDecl);
858
859 *node = ast.NodeVarDecl {
860 .base = self.initNode(ast.Node.Id.VarDecl),
861 .visib_token = *visib_token,
862 .mut_token = *mut_token,
863 .comptime_token = *comptime_token,
864 .extern_token = *extern_token,
865 .type_node = null,
866 .align_node = null,
867 .init_node = null,
868 .lib_name = null,
869 // initialized later
870 .name_token = undefined,
871 .eq_token = undefined,
872 .semicolon_token = undefined,
873 };
874 return node;
875 }
876
877 fn createTestDecl(self: &Parser, arena: &mem.Allocator, test_token: &const Token, name_token: &const Token,
878 block: &ast.NodeBlock) !&ast.NodeTestDecl
879 {
880 const node = try arena.create(ast.NodeTestDecl);
881
882 *node = ast.NodeTestDecl {
883 .base = self.initNode(ast.Node.Id.TestDecl),
884 .test_token = *test_token,
885 .name_token = *name_token,
886 .body_node = &block.base,
887 };
888 return node;
889 }
890
891 fn createFnProto(self: &Parser, arena: &mem.Allocator, fn_token: &const Token, extern_token: &const ?Token,
892 cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) !&ast.NodeFnProto
893 {
894 const node = try arena.create(ast.NodeFnProto);
895
896 *node = ast.NodeFnProto {
897 .base = self.initNode(ast.Node.Id.FnProto),
898 .visib_token = *visib_token,
899 .name_token = null,
900 .fn_token = *fn_token,
901 .params = ArrayList(&ast.Node).init(arena),
902 .return_type = undefined,
903 .var_args_token = null,
904 .extern_token = *extern_token,
905 .inline_token = *inline_token,
906 .cc_token = *cc_token,
907 .body_node = null,
908 .lib_name = null,
909 .align_expr = null,
910 };
911 return node;
912 }
913
914 fn createParamDecl(self: &Parser, arena: &mem.Allocator) !&ast.NodeParamDecl {
915 const node = try arena.create(ast.NodeParamDecl);
916
917 *node = ast.NodeParamDecl {
918 .base = self.initNode(ast.Node.Id.ParamDecl),
919 .comptime_token = null,
920 .noalias_token = null,
921 .name_token = null,
922 .type_node = undefined,
923 .var_args_token = null,
924 };
925 return node;
926 }
927
928 fn createBlock(self: &Parser, arena: &mem.Allocator, begin_token: &const Token) !&ast.NodeBlock {
929 const node = try arena.create(ast.NodeBlock);
930
931 *node = ast.NodeBlock {
932 .base = self.initNode(ast.Node.Id.Block),
933 .begin_token = *begin_token,
934 .end_token = undefined,
935 .statements = ArrayList(&ast.Node).init(arena),
936 };
937 return node;
938 }
939
940 fn createInfixOp(self: &Parser, arena: &mem.Allocator, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) !&ast.NodeInfixOp {
941 const node = try arena.create(ast.NodeInfixOp);
942
943 *node = ast.NodeInfixOp {
944 .base = self.initNode(ast.Node.Id.InfixOp),
945 .op_token = *op_token,
946 .lhs = undefined,
947 .op = *op,
948 .rhs = undefined,
949 };
950 return node;
951 }
952
953 fn createPrefixOp(self: &Parser, arena: &mem.Allocator, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) !&ast.NodePrefixOp {
954 const node = try arena.create(ast.NodePrefixOp);
955
956 *node = ast.NodePrefixOp {
957 .base = self.initNode(ast.Node.Id.PrefixOp),
958 .op_token = *op_token,
959 .op = *op,
960 .rhs = undefined,
961 };
962 return node;
963 }
964
965 fn createIdentifier(self: &Parser, arena: &mem.Allocator, name_token: &const Token) !&ast.NodeIdentifier {
966 const node = try arena.create(ast.NodeIdentifier);
967
968 *node = ast.NodeIdentifier {
969 .base = self.initNode(ast.Node.Id.Identifier),
970 .name_token = *name_token,
971 };
972 return node;
973 }
974
975 fn createIntegerLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeIntegerLiteral {
976 const node = try arena.create(ast.NodeIntegerLiteral);
977
978 *node = ast.NodeIntegerLiteral {
979 .base = self.initNode(ast.Node.Id.IntegerLiteral),
980 .token = *token,
981 };
982 return node;
983 }
984
985 fn createFloatLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeFloatLiteral {
986 const node = try arena.create(ast.NodeFloatLiteral);
987
988 *node = ast.NodeFloatLiteral {
989 .base = self.initNode(ast.Node.Id.FloatLiteral),
990 .token = *token,
991 };
992 return node;
993 }
994
995 fn createUndefined(self: &Parser, arena: &mem.Allocator, token: &const Token) !&ast.NodeUndefinedLiteral {
996 const node = try arena.create(ast.NodeUndefinedLiteral);
997
998 *node = ast.NodeUndefinedLiteral {
999 .base = self.initNode(ast.Node.Id.UndefinedLiteral),
1000 .token = *token,
1001 };
1002 return node;
1003 }
1004
1005 fn createAttachIdentifier(self: &Parser, arena: &mem.Allocator, dest_ptr: &const DestPtr, name_token: &const Token) !&ast.NodeIdentifier {
1006 const node = try self.createIdentifier(arena, name_token);
1007 try dest_ptr.store(&node.base);
1008 return node;
1009 }
1010
1011 fn createAttachParamDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node)) !&ast.NodeParamDecl {
1012 const node = try self.createParamDecl(arena);
1013 try list.append(&node.base);
1014 return node;
1015 }
1016
1017 fn createAttachFnProto(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node), fn_token: &const Token,
1018 extern_token: &const ?Token, cc_token: &const ?Token, visib_token: &const ?Token,
1019 inline_token: &const ?Token) !&ast.NodeFnProto
1020 {
1021 const node = try self.createFnProto(arena, fn_token, extern_token, cc_token, visib_token, inline_token);
1022 try list.append(&node.base);
1023 return node;
1024 }
1025
1026 fn createAttachVarDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node),
1027 visib_token: &const ?Token, mut_token: &const Token, comptime_token: &const ?Token,
1028 extern_token: &const ?Token) !&ast.NodeVarDecl
1029 {
1030 const node = try self.createVarDecl(arena, visib_token, mut_token, comptime_token, extern_token);
1031 try list.append(&node.base);
1032 return node;
1033 }
1034
1035 fn createAttachTestDecl(self: &Parser, arena: &mem.Allocator, list: &ArrayList(&ast.Node),
1036 test_token: &const Token, name_token: &const Token, block: &ast.NodeBlock) !&ast.NodeTestDecl
1037 {
1038 const node = try self.createTestDecl(arena, test_token, name_token, block);
1039 try list.append(&node.base);
1040 return node;
1041 }
1042
1043 fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) {
1044 const loc = self.tokenizer.getTokenLocation(token);
1045 warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, token.line + 1, token.column + 1, args);
1046 warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
1047 {
1048 var i: usize = 0;
1049 while (i < token.column) : (i += 1) {
1050 warn(" ");
1051 }
1052 }
1053 {
1054 const caret_count = token.end - token.start;
1055 var i: usize = 0;
1056 while (i < caret_count) : (i += 1) {
1057 warn("~");
1058 }
1059 }
1060 warn("\n");
1061 return error.ParseError;
1062 }
1063
1064 fn expectToken(self: &Parser, token: &const Token, id: @TagType(Token.Id)) !void {
1065 if (token.id != id) {
1066 return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id));
1067 }
1068 }
1069
1070 fn eatToken(self: &Parser, id: @TagType(Token.Id)) !Token {
1071 const token = self.getNextToken();
1072 try self.expectToken(token, id);
1073 return token;
1074 }
1075
1076 fn putBackToken(self: &Parser, token: &const Token) void {
1077 self.put_back_tokens[self.put_back_count] = *token;
1078 self.put_back_count += 1;
1079 }
1080
1081 fn getNextToken(self: &Parser) Token {
1082 if (self.put_back_count != 0) {
1083 const put_back_index = self.put_back_count - 1;
1084 const put_back_token = self.put_back_tokens[put_back_index];
1085 self.put_back_count = put_back_index;
1086 return put_back_token;
1087 } else {
1088 return self.tokenizer.next();
1089 }
1090 }
1091
1092 const RenderAstFrame = struct {
1093 node: &ast.Node,
1094 indent: usize,
1095 };
1096
1097 pub fn renderAst(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void {
1098 var stack = self.initUtilityArrayList(RenderAstFrame);
1099 defer self.deinitUtilityArrayList(stack);
1100
1101 try stack.append(RenderAstFrame {
1102 .node = &root_node.base,
1103 .indent = 0,
1104 });
1105
1106 while (stack.popOrNull()) |frame| {
1107 {
1108 var i: usize = 0;
1109 while (i < frame.indent) : (i += 1) {
1110 try stream.print(" ");
1111 }
1112 }
1113 try stream.print("{}\n", @tagName(frame.node.id));
1114 var child_i: usize = 0;
1115 while (frame.node.iterate(child_i)) |child| : (child_i += 1) {
1116 try stack.append(RenderAstFrame {
1117 .node = child,
1118 .indent = frame.indent + 2,
1119 });
1120 }
1121 }
1122 }
1123
1124 const RenderState = union(enum) {
1125 TopLevelDecl: &ast.Node,
1126 FnProtoRParen: &ast.NodeFnProto,
1127 ParamDecl: &ast.Node,
1128 Text: []const u8,
1129 Expression: &ast.Node,
1130 VarDecl: &ast.NodeVarDecl,
1131 Statement: &ast.Node,
1132 PrintIndent,
1133 Indent: usize,
1134 };
1135
1136 pub fn renderSource(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void {
1137 var stack = self.initUtilityArrayList(RenderState);
1138 defer self.deinitUtilityArrayList(stack);
1139
1140 {
1141 try stack.append(RenderState { .Text = "\n"});
1142
1143 var i = root_node.decls.len;
1144 while (i != 0) {
1145 i -= 1;
1146 const decl = root_node.decls.items[i];
1147 try stack.append(RenderState {.TopLevelDecl = decl});
1148 if (i != 0) {
1149 try stack.append(RenderState {
1150 .Text = blk: {
1151 const prev_node = root_node.decls.at(i - 1);
1152 const prev_line_index = prev_node.lastToken().line;
1153 const this_line_index = decl.firstToken().line;
1154 if (this_line_index - prev_line_index >= 2) {
1155 break :blk "\n\n";
1156 }
1157 break :blk "\n";
1158 },
1159 });
1160 }
1161 }
1162 }
1163
1164 const indent_delta = 4;
1165 var indent: usize = 0;
1166 while (stack.popOrNull()) |state| {
1167 switch (state) {
1168 RenderState.TopLevelDecl => |decl| {
1169 switch (decl.id) {
1170 ast.Node.Id.FnProto => {
1171 const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", decl);
1172 if (fn_proto.visib_token) |visib_token| {
1173 switch (visib_token.id) {
1174 Token.Id.Keyword_pub => try stream.print("pub "),
1175 Token.Id.Keyword_export => try stream.print("export "),
1176 else => unreachable,
1177 }
1178 }
1179 if (fn_proto.extern_token) |extern_token| {
1180 try stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
1181 }
1182 try stream.print("fn");
1183
1184 if (fn_proto.name_token) |name_token| {
1185 try stream.print(" {}", self.tokenizer.getTokenSlice(name_token));
1186 }
1187
1188 try stream.print("(");
1189
1190 if (fn_proto.body_node == null) {
1191 try stack.append(RenderState { .Text = ";" });
1192 }
1193
1194 try stack.append(RenderState { .FnProtoRParen = fn_proto});
1195 var i = fn_proto.params.len;
1196 while (i != 0) {
1197 i -= 1;
1198 const param_decl_node = fn_proto.params.items[i];
1199 try stack.append(RenderState { .ParamDecl = param_decl_node});
1200 if (i != 0) {
1201 try stack.append(RenderState { .Text = ", " });
1202 }
1203 }
1204 },
1205 ast.Node.Id.VarDecl => {
1206 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
1207 try stack.append(RenderState { .VarDecl = var_decl});
1208 },
1209 ast.Node.Id.TestDecl => {
1210 const test_decl = @fieldParentPtr(ast.NodeTestDecl, "base", decl);
1211 try stream.print("test {} ", self.tokenizer.getTokenSlice(test_decl.name_token));
1212 try stack.append(RenderState { .Expression = test_decl.body_node });
1213 },
1214 else => unreachable,
1215 }
1216 },
1217
1218 RenderState.VarDecl => |var_decl| {
1219 if (var_decl.visib_token) |visib_token| {
1220 try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
1221 }
1222 if (var_decl.extern_token) |extern_token| {
1223 try stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
1224 if (var_decl.lib_name != null) {
1225 @panic("TODO");
1226 }
1227 }
1228 if (var_decl.comptime_token) |comptime_token| {
1229 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
1230 }
1231 try stream.print("{} ", self.tokenizer.getTokenSlice(var_decl.mut_token));
1232 try stream.print("{}", self.tokenizer.getTokenSlice(var_decl.name_token));
1233
1234 try stack.append(RenderState { .Text = ";" });
1235 if (var_decl.init_node) |init_node| {
1236 try stack.append(RenderState { .Expression = init_node });
1237 try stack.append(RenderState { .Text = " = " });
1238 }
1239 if (var_decl.align_node) |align_node| {
1240 try stack.append(RenderState { .Text = ")" });
1241 try stack.append(RenderState { .Expression = align_node });
1242 try stack.append(RenderState { .Text = " align(" });
1243 }
1244 if (var_decl.type_node) |type_node| {
1245 try stream.print(": ");
1246 try stack.append(RenderState { .Expression = type_node });
1247 }
1248 },
1249
1250 RenderState.ParamDecl => |base| {
1251 const param_decl = @fieldParentPtr(ast.NodeParamDecl, "base", base);
1252 if (param_decl.comptime_token) |comptime_token| {
1253 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
1254 }
1255 if (param_decl.noalias_token) |noalias_token| {
1256 try stream.print("{} ", self.tokenizer.getTokenSlice(noalias_token));
1257 }
1258 if (param_decl.name_token) |name_token| {
1259 try stream.print("{}: ", self.tokenizer.getTokenSlice(name_token));
1260 }
1261 if (param_decl.var_args_token) |var_args_token| {
1262 try stream.print("{}", self.tokenizer.getTokenSlice(var_args_token));
1263 } else {
1264 try stack.append(RenderState { .Expression = param_decl.type_node});
1265 }
1266 },
1267 RenderState.Text => |bytes| {
1268 try stream.write(bytes);
1269 },
1270 RenderState.Expression => |base| switch (base.id) {
1271 ast.Node.Id.Identifier => {
1272 const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base);
1273 try stream.print("{}", self.tokenizer.getTokenSlice(identifier.name_token));
1274 },
1275 ast.Node.Id.Block => {
1276 const block = @fieldParentPtr(ast.NodeBlock, "base", base);
1277 if (block.statements.len == 0) {
1278 try stream.write("{}");
1279 } else {
1280 try stream.write("{");
1281 try stack.append(RenderState { .Text = "}"});
1282 try stack.append(RenderState.PrintIndent);
1283 try stack.append(RenderState { .Indent = indent});
1284 try stack.append(RenderState { .Text = "\n"});
1285 var i = block.statements.len;
1286 while (i != 0) {
1287 i -= 1;
1288 const statement_node = block.statements.items[i];
1289 try stack.append(RenderState { .Statement = statement_node});
1290 try stack.append(RenderState.PrintIndent);
1291 try stack.append(RenderState { .Indent = indent + indent_delta});
1292 try stack.append(RenderState {
1293 .Text = blk: {
1294 if (i != 0) {
1295 const prev_statement_node = block.statements.items[i - 1];
1296 const prev_line_index = prev_statement_node.lastToken().line;
1297 const this_line_index = statement_node.firstToken().line;
1298 if (this_line_index - prev_line_index >= 2) {
1299 break :blk "\n\n";
1300 }
1301 }
1302 break :blk "\n";
1303 },
1304 });
1305 }
1306 }
1307 },
1308 ast.Node.Id.InfixOp => {
1309 const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);
1310 try stack.append(RenderState { .Expression = prefix_op_node.rhs });
1311 const text = switch (prefix_op_node.op) {
1312 ast.NodeInfixOp.InfixOp.Add => " + ",
1313 ast.NodeInfixOp.InfixOp.AddWrap => " +% ",
1314 ast.NodeInfixOp.InfixOp.ArrayCat => " ++ ",
1315 ast.NodeInfixOp.InfixOp.ArrayMult => " ** ",
1316 ast.NodeInfixOp.InfixOp.Assign => " = ",
1317 ast.NodeInfixOp.InfixOp.AssignBitAnd => " &= ",
1318 ast.NodeInfixOp.InfixOp.AssignBitOr => " |= ",
1319 ast.NodeInfixOp.InfixOp.AssignBitShiftLeft => " <<= ",
1320 ast.NodeInfixOp.InfixOp.AssignBitShiftRight => " >>= ",
1321 ast.NodeInfixOp.InfixOp.AssignBitXor => " ^= ",
1322 ast.NodeInfixOp.InfixOp.AssignDiv => " /= ",
1323 ast.NodeInfixOp.InfixOp.AssignMinus => " -= ",
1324 ast.NodeInfixOp.InfixOp.AssignMinusWrap => " -%= ",
1325 ast.NodeInfixOp.InfixOp.AssignMod => " %= ",
1326 ast.NodeInfixOp.InfixOp.AssignPlus => " += ",
1327 ast.NodeInfixOp.InfixOp.AssignPlusWrap => " +%= ",
1328 ast.NodeInfixOp.InfixOp.AssignTimes => " *= ",
1329 ast.NodeInfixOp.InfixOp.AssignTimesWarp => " *%= ",
1330 ast.NodeInfixOp.InfixOp.BangEqual => " != ",
1331 ast.NodeInfixOp.InfixOp.BitAnd => " & ",
1332 ast.NodeInfixOp.InfixOp.BitOr => " | ",
1333 ast.NodeInfixOp.InfixOp.BitShiftLeft => " << ",
1334 ast.NodeInfixOp.InfixOp.BitShiftRight => " >> ",
1335 ast.NodeInfixOp.InfixOp.BitXor => " ^ ",
1336 ast.NodeInfixOp.InfixOp.BoolAnd => " and ",
1337 ast.NodeInfixOp.InfixOp.BoolOr => " or ",
1338 ast.NodeInfixOp.InfixOp.Div => " / ",
1339 ast.NodeInfixOp.InfixOp.EqualEqual => " == ",
1340 ast.NodeInfixOp.InfixOp.ErrorUnion => "!",
1341 ast.NodeInfixOp.InfixOp.GreaterOrEqual => " >= ",
1342 ast.NodeInfixOp.InfixOp.GreaterThan => " > ",
1343 ast.NodeInfixOp.InfixOp.LessOrEqual => " <= ",
1344 ast.NodeInfixOp.InfixOp.LessThan => " < ",
1345 ast.NodeInfixOp.InfixOp.MergeErrorSets => " || ",
1346 ast.NodeInfixOp.InfixOp.Mod => " % ",
1347 ast.NodeInfixOp.InfixOp.Mult => " * ",
1348 ast.NodeInfixOp.InfixOp.MultWrap => " *% ",
1349 ast.NodeInfixOp.InfixOp.Period => ".",
1350 ast.NodeInfixOp.InfixOp.Sub => " - ",
1351 ast.NodeInfixOp.InfixOp.SubWrap => " -% ",
1352 ast.NodeInfixOp.InfixOp.UnwrapMaybe => " ?? ",
1353 };
1354
1355 try stack.append(RenderState { .Text = text });
1356 try stack.append(RenderState { .Expression = prefix_op_node.lhs });
1357 },
1358 ast.Node.Id.PrefixOp => {
1359 const prefix_op_node = @fieldParentPtr(ast.NodePrefixOp, "base", base);
1360 try stack.append(RenderState { .Expression = prefix_op_node.rhs });
1361 switch (prefix_op_node.op) {
1362 ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| {
1363 try stream.write("&");
1364 if (addr_of_info.volatile_token != null) {
1365 try stack.append(RenderState { .Text = "volatile "});
1366 }
1367 if (addr_of_info.const_token != null) {
1368 try stack.append(RenderState { .Text = "const "});
1369 }
1370 if (addr_of_info.align_expr) |align_expr| {
1371 try stream.print("align(");
1372 try stack.append(RenderState { .Text = ") "});
1373 try stack.append(RenderState { .Expression = align_expr});
1374 }
1375 },
1376 ast.NodePrefixOp.PrefixOp.BitNot => try stream.write("~"),
1377 ast.NodePrefixOp.PrefixOp.BoolNot => try stream.write("!"),
1378 ast.NodePrefixOp.PrefixOp.Deref => try stream.write("*"),
1379 ast.NodePrefixOp.PrefixOp.Negation => try stream.write("-"),
1380 ast.NodePrefixOp.PrefixOp.NegationWrap => try stream.write("-%"),
1381 ast.NodePrefixOp.PrefixOp.Return => try stream.write("return "),
1382 ast.NodePrefixOp.PrefixOp.Try => try stream.write("try "),
1383 ast.NodePrefixOp.PrefixOp.UnwrapMaybe => try stream.write("??"),
1384 }
1385 },
1386 ast.Node.Id.IntegerLiteral => {
1387 const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base);
1388 try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token));
1389 },
1390 ast.Node.Id.FloatLiteral => {
1391 const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base);
1392 try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token));
1393 },
1394 ast.Node.Id.StringLiteral => {
1395 const string_literal = @fieldParentPtr(ast.NodeStringLiteral, "base", base);
1396 try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token));
1397 },
1398 ast.Node.Id.UndefinedLiteral => {
1399 const undefined_literal = @fieldParentPtr(ast.NodeUndefinedLiteral, "base", base);
1400 try stream.print("{}", self.tokenizer.getTokenSlice(undefined_literal.token));
1401 },
1402 ast.Node.Id.BuiltinCall => {
1403 const builtin_call = @fieldParentPtr(ast.NodeBuiltinCall, "base", base);
1404 try stream.print("{}(", self.tokenizer.getTokenSlice(builtin_call.builtin_token));
1405 try stack.append(RenderState { .Text = ")"});
1406 var i = builtin_call.params.len;
1407 while (i != 0) {
1408 i -= 1;
1409 const param_node = builtin_call.params.at(i);
1410 try stack.append(RenderState { .Expression = param_node});
1411 if (i != 0) {
1412 try stack.append(RenderState { .Text = ", " });
1413 }
1414 }
1415 },
1416 ast.Node.Id.Call => {
1417 const call = @fieldParentPtr(ast.NodeCall, "base", base);
1418 try stack.append(RenderState { .Text = ")"});
1419 var i = call.params.len;
1420 while (i != 0) {
1421 i -= 1;
1422 const param_node = call.params.at(i);
1423 try stack.append(RenderState { .Expression = param_node});
1424 if (i != 0) {
1425 try stack.append(RenderState { .Text = ", " });
1426 }
1427 }
1428 try stack.append(RenderState { .Text = "("});
1429 try stack.append(RenderState { .Expression = call.callee });
1430 },
1431 ast.Node.Id.FnProto => @panic("TODO fn proto in an expression"),
1432 ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
1433
1434 ast.Node.Id.Root,
1435 ast.Node.Id.VarDecl,
1436 ast.Node.Id.TestDecl,
1437 ast.Node.Id.ParamDecl => unreachable,
1438 },
1439 RenderState.FnProtoRParen => |fn_proto| {
1440 try stream.print(")");
1441 if (fn_proto.align_expr != null) {
1442 @panic("TODO");
1443 }
1444 try stream.print(" ");
1445 if (fn_proto.body_node) |body_node| {
1446 try stack.append(RenderState { .Expression = body_node});
1447 try stack.append(RenderState { .Text = " "});
1448 }
1449 switch (fn_proto.return_type) {
1450 ast.NodeFnProto.ReturnType.Explicit => |node| {
1451 try stack.append(RenderState { .Expression = node});
1452 },
1453 ast.NodeFnProto.ReturnType.Infer => {
1454 try stream.print("var");
1455 },
1456 ast.NodeFnProto.ReturnType.InferErrorSet => |node| {
1457 try stream.print("!");
1458 try stack.append(RenderState { .Expression = node});
1459 },
1460 }
1461 },
1462 RenderState.Statement => |base| {
1463 if (base.comment) |comment| {
1464 for (comment.lines.toSliceConst()) |line_token| {
1465 try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));
1466 try stream.writeByteNTimes(' ', indent);
1467 }
1468 }
1469 switch (base.id) {
1470 ast.Node.Id.VarDecl => {
1471 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
1472 try stack.append(RenderState { .VarDecl = var_decl});
1473 },
1474 else => {
1475 try stack.append(RenderState { .Text = ";"});
1476 try stack.append(RenderState { .Expression = base});
1477 },
1478 }
1479 },
1480 RenderState.Indent => |new_indent| indent = new_indent,
1481 RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent),
1482 }
1483 }
1484 }
1485
1486 fn initUtilityArrayList(self: &Parser, comptime T: type) ArrayList(T) {
1487 const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T);
1488 self.utility_bytes = self.util_allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count);
1489 const typed_slice = ([]T)(self.utility_bytes);
1490 return ArrayList(T) {
1491 .allocator = self.util_allocator,
1492 .items = typed_slice,
1493 .len = 0,
1494 };
1495 }
1496
1497 fn deinitUtilityArrayList(self: &Parser, list: var) void {
1498 self.utility_bytes = ([]align(utility_bytes_align) u8)(list.items);
1499 }
1500
1501};
1502
1503var fixed_buffer_mem: [100 * 1024]u8 = undefined;
1504
1505fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
1506 var tokenizer = Tokenizer.init(source);
1507 var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
1508 defer parser.deinit();
1509
1510 var tree = try parser.parse();
1511 defer tree.deinit();
1512
1513 var buffer = try std.Buffer.initSize(allocator, 0);
1514 errdefer buffer.deinit();
1515
1516 var buffer_out_stream = io.BufferOutStream.init(&buffer);
1517 try parser.renderSource(&buffer_out_stream.stream, tree.root_node);
1518 return buffer.toOwnedSlice();
1519}
1520
1521fn testCanonical(source: []const u8) !void {
1522 const needed_alloc_count = x: {
1523 // Try it once with unlimited memory, make sure it works
1524 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1525 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
1526 const result_source = try testParse(source, &failing_allocator.allocator);
1527 if (!mem.eql(u8, result_source, source)) {
1528 warn("\n====== expected this output: =========\n");
1529 warn("{}", source);
1530 warn("\n======== instead found this: =========\n");
1531 warn("{}", result_source);
1532 warn("\n======================================\n");
1533 return error.TestFailed;
1534 }
1535 failing_allocator.allocator.free(result_source);
1536 break :x failing_allocator.index;
1537 };
1538
1539 var fail_index: usize = 0;
1540 while (fail_index < needed_alloc_count) : (fail_index += 1) {
1541 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1542 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
1543 if (testParse(source, &failing_allocator.allocator)) |_| {
1544 return error.NondeterministicMemoryUsage;
1545 } else |err| switch (err) {
1546 error.OutOfMemory => {
1547 if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
1548 warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
1549 fail_index, needed_alloc_count,
1550 failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
1551 failing_allocator.index, failing_allocator.deallocations);
1552 return error.MemoryLeakDetected;
1553 }
1554 },
1555 error.ParseError => @panic("test failed"),
1556 }
1557 }
1558}
1559
1560test "zig fmt" {
1561 try testCanonical(
1562 \\const std = @import("std");
1563 \\
1564 \\pub fn main() !void {
1565 \\ // If this program is run without stdout attached, exit with an error.
1566 \\ // another comment
1567 \\ var stdout_file = try std.io.getStdOut;
1568 \\}
1569 \\
1570 );
1571
1572 try testCanonical(
1573 \\const std = @import("std");
1574 \\
1575 \\pub fn main() !void {
1576 \\ var stdout_file = try std.io.getStdOut;
1577 \\ var stdout_file = try std.io.getStdOut;
1578 \\
1579 \\ var stdout_file = try std.io.getStdOut;
1580 \\ var stdout_file = try std.io.getStdOut;
1581 \\}
1582 \\
1583 );
1584
1585 try testCanonical(
1586 \\pub fn main() !void {}
1587 \\pub fn main() var {}
1588 \\pub fn main() i32 {}
1589 \\
1590 );
1591
1592 try testCanonical(
1593 \\const std = @import("std");
1594 \\const std = @import();
1595 \\
1596 );
1597
1598 try testCanonical(
1599 \\extern fn puts(s: &const u8) c_int;
1600 \\
1601 );
1602
1603 try testCanonical(
1604 \\const a = b;
1605 \\pub const a = b;
1606 \\var a = b;
1607 \\pub var a = b;
1608 \\const a: i32 = b;
1609 \\pub const a: i32 = b;
1610 \\var a: i32 = b;
1611 \\pub var a: i32 = b;
1612 \\
1613 );
1614
1615 try testCanonical(
1616 \\extern var foo: c_int;
1617 \\
1618 );
1619
1620 try testCanonical(
1621 \\var foo: c_int align(1);
1622 \\
1623 );
1624
1625 try testCanonical(
1626 \\fn main(argc: c_int, argv: &&u8) c_int {
1627 \\ const a = b;
1628 \\}
1629 \\
1630 );
1631
1632 try testCanonical(
1633 \\fn foo(argc: c_int, argv: &&u8) c_int {
1634 \\ return 0;
1635 \\}
1636 \\
1637 );
1638
1639 try testCanonical(
1640 \\extern fn f1(s: &align(&u8) u8) c_int;
1641 \\
1642 );
1643
1644 try testCanonical(
1645 \\extern fn f1(s: &&align(1) &const &volatile u8) c_int;
1646 \\extern fn f2(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
1647 \\extern fn f3(s: &align(1) const volatile u8) c_int;
1648 \\
1649 );
1650
1651 try testCanonical(
1652 \\fn f1(a: bool, b: bool) bool {
1653 \\ a != b;
1654 \\ return a == b;
1655 \\}
1656 \\
1657 );
1658
1659 try testCanonical(
1660 \\test "test name" {
1661 \\ const a = 1;
1662 \\ var b = 1;
1663 \\}
1664 \\
1665 );
1666
1667 try testCanonical(
1668 \\test "infix operators" {
1669 \\ var i = undefined;
1670 \\ i = 2;
1671 \\ i *= 2;
1672 \\ i |= 2;
1673 \\ i ^= 2;
1674 \\ i <<= 2;
1675 \\ i >>= 2;
1676 \\ i &= 2;
1677 \\ i *= 2;
1678 \\ i *%= 2;
1679 \\ i -= 2;
1680 \\ i -%= 2;
1681 \\ i += 2;
1682 \\ i +%= 2;
1683 \\ i /= 2;
1684 \\ i %= 2;
1685 \\ _ = i == i;
1686 \\ _ = i != i;
1687 \\ _ = i != i;
1688 \\ _ = i.i;
1689 \\ _ = i || i;
1690 \\ _ = i!i;
1691 \\ _ = i ** i;
1692 \\ _ = i ++ i;
1693 \\ _ = i ?? i;
1694 \\ _ = i % i;
1695 \\ _ = i / i;
1696 \\ _ = i *% i;
1697 \\ _ = i * i;
1698 \\ _ = i -% i;
1699 \\ _ = i - i;
1700 \\ _ = i +% i;
1701 \\ _ = i + i;
1702 \\ _ = i << i;
1703 \\ _ = i >> i;
1704 \\ _ = i & i;
1705 \\ _ = i ^ i;
1706 \\ _ = i | i;
1707 \\ _ = i >= i;
1708 \\ _ = i <= i;
1709 \\ _ = i > i;
1710 \\ _ = i < i;
1711 \\ _ = i and i;
1712 \\ _ = i or i;
1713 \\}
1714 \\
1715 );
1716
1717 try testCanonical(
1718 \\test "prefix operators" {
1719 \\ --%~??!*&0;
1720 \\}
1721 \\
1722 );
1723
1724 try testCanonical(
1725 \\test "test calls" {
1726 \\ a();
1727 \\ a(1);
1728 \\ a(1, 2);
1729 \\ a(1, 2) + a(1, 2);
1730 \\}
1731 \\
1732 );
1733}
std/zig/parser_test.zig created+1183
...@@ -0,0 +1,1183 @@
1test "zig fmt: same-line comment after a statement" {
2 try testCanonical(
3 \\test "" {
4 \\ a = b;
5 \\ debug.assert(H.digest_size <= H.block_size); // HMAC makes this assumption
6 \\ a = b;
7 \\}
8 \\
9 );
10}
11
12test "zig fmt: same-line comment after var decl in struct" {
13 try testCanonical(
14 \\pub const vfs_cap_data = extern struct {
15 \\ const Data = struct {}; // when on disk.
16 \\};
17 \\
18 );
19}
20
21test "zig fmt: same-line comment after field decl" {
22 try testCanonical(
23 \\pub const dirent = extern struct {
24 \\ d_name: u8,
25 \\ d_name: u8, // comment 1
26 \\ d_name: u8,
27 \\ d_name: u8, // comment 2
28 \\ d_name: u8,
29 \\};
30 \\
31 );
32}
33
34test "zig fmt: same-line comment after switch prong" {
35 try testCanonical(
36 \\test "" {
37 \\ switch (err) {
38 \\ error.PathAlreadyExists => {}, // comment 2
39 \\ else => return err, // comment 1
40 \\ }
41 \\}
42 \\
43 );
44}
45
46test "zig fmt: same-line comment after non-block if expression" {
47 try testCanonical(
48 \\comptime {
49 \\ if (sr > n_uword_bits - 1) // d > r
50 \\ return 0;
51 \\}
52 \\
53 );
54}
55
56test "zig fmt: same-line comment on comptime expression" {
57 try testCanonical(
58 \\test "" {
59 \\ comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt
60 \\}
61 \\
62 );
63}
64
65test "zig fmt: switch with empty body" {
66 try testCanonical(
67 \\test "" {
68 \\ foo() catch |err| switch (err) {};
69 \\}
70 \\
71 );
72}
73
74test "zig fmt: float literal with exponent" {
75 try testCanonical(
76 \\pub const f64_true_min = 4.94065645841246544177e-324;
77 \\
78 );
79}
80
81test "zig fmt: line comments in struct initializer" {
82 try testCanonical(
83 \\fn foo() void {
84 \\ return Self{
85 \\ .a = b,
86 \\
87 \\ // Initialize these two fields to buffer_size so that
88 \\ // in `readFn` we treat the state as being able to read
89 \\ .start_index = buffer_size,
90 \\ .end_index = buffer_size,
91 \\
92 \\ // middle
93 \\
94 \\ .a = b,
95 \\
96 \\ // end
97 \\ };
98 \\}
99 \\
100 );
101}
102
103test "zig fmt: doc comments before struct field" {
104 try testCanonical(
105 \\pub const Allocator = struct {
106 \\ /// Allocate byte_count bytes and return them in a slice, with the
107 \\ /// slice's pointer aligned at least to alignment bytes.
108 \\ allocFn: fn() void,
109 \\};
110 \\
111 );
112}
113
114test "zig fmt: error set declaration" {
115 try testCanonical(
116 \\const E = error{
117 \\ A,
118 \\ B,
119 \\
120 \\ C,
121 \\};
122 \\
123 \\const Error = error{
124 \\ /// no more memory
125 \\ OutOfMemory,
126 \\};
127 \\
128 \\const Error = error{
129 \\ /// no more memory
130 \\ OutOfMemory,
131 \\
132 \\ /// another
133 \\ Another,
134 \\
135 \\ // end
136 \\};
137 \\
138 \\const Error = error{OutOfMemory};
139 \\const Error = error{};
140 \\
141 );
142}
143
144test "zig fmt: union(enum(u32)) with assigned enum values" {
145 try testCanonical(
146 \\const MultipleChoice = union(enum(u32)) {
147 \\ A = 20,
148 \\ B = 40,
149 \\ C = 60,
150 \\ D = 1000,
151 \\};
152 \\
153 );
154}
155
156test "zig fmt: labeled suspend" {
157 try testCanonical(
158 \\fn foo() void {
159 \\ s: suspend |p| {
160 \\ break :s;
161 \\ }
162 \\}
163 \\
164 );
165}
166
167test "zig fmt: comments before error set decl" {
168 try testCanonical(
169 \\const UnexpectedError = error{
170 \\ /// The Operating System returned an undocumented error code.
171 \\ Unexpected,
172 \\ // another
173 \\ Another,
174 \\
175 \\ // in between
176 \\
177 \\ // at end
178 \\};
179 \\
180 );
181}
182
183test "zig fmt: comments before switch prong" {
184 try testCanonical(
185 \\test "" {
186 \\ switch (err) {
187 \\ error.PathAlreadyExists => continue,
188 \\
189 \\ // comment 1
190 \\
191 \\ // comment 2
192 \\ else => return err,
193 \\ // at end
194 \\ }
195 \\}
196 \\
197 );
198}
199
200test "zig fmt: comments before var decl in struct" {
201 try testCanonical(
202 \\pub const vfs_cap_data = extern struct {
203 \\ // All of these are mandated as little endian
204 \\ // when on disk.
205 \\ const Data = struct {
206 \\ permitted: u32,
207 \\ inheritable: u32,
208 \\ };
209 \\
210 \\ // in between
211 \\
212 \\ /// All of these are mandated as little endian
213 \\ /// when on disk.
214 \\ const Data = struct {
215 \\ permitted: u32,
216 \\ inheritable: u32,
217 \\ };
218 \\
219 \\ // at end
220 \\};
221 \\
222 );
223}
224
225test "zig fmt: array literal with 1 item on 1 line" {
226 try testCanonical(
227 \\var s = []const u64{0} ** 25;
228 \\
229 );
230}
231
232test "zig fmt: comments before global variables" {
233 try testCanonical(
234 \\/// Foo copies keys and values before they go into the map, and
235 \\/// frees them when they get removed.
236 \\pub const Foo = struct {};
237 \\
238 );
239}
240
241test "zig fmt: comments in statements" {
242 try testCanonical(
243 \\test "std" {
244 \\ // statement comment
245 \\ _ = @import("foo/bar.zig");
246 \\
247 \\ // middle
248 \\ // middle2
249 \\
250 \\ // end
251 \\}
252 \\
253 );
254}
255
256test "zig fmt: comments before test decl" {
257 try testCanonical(
258 \\/// top level doc comment
259 \\test "hi" {}
260 \\
261 \\// top level normal comment
262 \\test "hi" {}
263 \\
264 \\// middle
265 \\
266 \\// end
267 \\
268 );
269}
270
271test "zig fmt: preserve spacing" {
272 try testCanonical(
273 \\const std = @import("std");
274 \\
275 \\pub fn main() !void {
276 \\ var stdout_file = try std.io.getStdOut;
277 \\ var stdout_file = try std.io.getStdOut;
278 \\
279 \\ var stdout_file = try std.io.getStdOut;
280 \\ var stdout_file = try std.io.getStdOut;
281 \\}
282 \\
283 );
284}
285
286test "zig fmt: return types" {
287 try testCanonical(
288 \\pub fn main() !void {}
289 \\pub fn main() var {}
290 \\pub fn main() i32 {}
291 \\
292 );
293}
294
295test "zig fmt: imports" {
296 try testCanonical(
297 \\const std = @import("std");
298 \\const std = @import();
299 \\
300 );
301}
302
303test "zig fmt: global declarations" {
304 try testCanonical(
305 \\const a = b;
306 \\pub const a = b;
307 \\var a = b;
308 \\pub var a = b;
309 \\const a: i32 = b;
310 \\pub const a: i32 = b;
311 \\var a: i32 = b;
312 \\pub var a: i32 = b;
313 \\extern const a: i32 = b;
314 \\pub extern const a: i32 = b;
315 \\extern var a: i32 = b;
316 \\pub extern var a: i32 = b;
317 \\extern "a" const a: i32 = b;
318 \\pub extern "a" const a: i32 = b;
319 \\extern "a" var a: i32 = b;
320 \\pub extern "a" var a: i32 = b;
321 \\
322 );
323}
324
325test "zig fmt: extern declaration" {
326 try testCanonical(
327 \\extern var foo: c_int;
328 \\
329 );
330}
331
332test "zig fmt: alignment" {
333 try testCanonical(
334 \\var foo: c_int align(1);
335 \\
336 );
337}
338
339test "zig fmt: C main" {
340 try testCanonical(
341 \\fn main(argc: c_int, argv: &&u8) c_int {
342 \\ const a = b;
343 \\}
344 \\
345 );
346}
347
348test "zig fmt: return" {
349 try testCanonical(
350 \\fn foo(argc: c_int, argv: &&u8) c_int {
351 \\ return 0;
352 \\}
353 \\
354 \\fn bar() void {
355 \\ return;
356 \\}
357 \\
358 );
359}
360
361test "zig fmt: pointer attributes" {
362 try testCanonical(
363 \\extern fn f1(s: &align(&u8) u8) c_int;
364 \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
365 \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
366 \\extern fn f4(s: &align(1) const volatile u8) c_int;
367 \\
368 );
369}
370
371test "zig fmt: slice attributes" {
372 try testCanonical(
373 \\extern fn f1(s: &align(&u8) u8) c_int;
374 \\extern fn f2(s: &&align(1) &const &volatile u8) c_int;
375 \\extern fn f3(s: &align(1) const &align(1) volatile &const volatile u8) c_int;
376 \\extern fn f4(s: &align(1) const volatile u8) c_int;
377 \\
378 );
379}
380
381test "zig fmt: test declaration" {
382 try testCanonical(
383 \\test "test name" {
384 \\ const a = 1;
385 \\ var b = 1;
386 \\}
387 \\
388 );
389}
390
391test "zig fmt: infix operators" {
392 try testCanonical(
393 \\test "infix operators" {
394 \\ var i = undefined;
395 \\ i = 2;
396 \\ i *= 2;
397 \\ i |= 2;
398 \\ i ^= 2;
399 \\ i <<= 2;
400 \\ i >>= 2;
401 \\ i &= 2;
402 \\ i *= 2;
403 \\ i *%= 2;
404 \\ i -= 2;
405 \\ i -%= 2;
406 \\ i += 2;
407 \\ i +%= 2;
408 \\ i /= 2;
409 \\ i %= 2;
410 \\ _ = i == i;
411 \\ _ = i != i;
412 \\ _ = i != i;
413 \\ _ = i.i;
414 \\ _ = i || i;
415 \\ _ = i!i;
416 \\ _ = i ** i;
417 \\ _ = i ++ i;
418 \\ _ = i ?? i;
419 \\ _ = i % i;
420 \\ _ = i / i;
421 \\ _ = i *% i;
422 \\ _ = i * i;
423 \\ _ = i -% i;
424 \\ _ = i - i;
425 \\ _ = i +% i;
426 \\ _ = i + i;
427 \\ _ = i << i;
428 \\ _ = i >> i;
429 \\ _ = i & i;
430 \\ _ = i ^ i;
431 \\ _ = i | i;
432 \\ _ = i >= i;
433 \\ _ = i <= i;
434 \\ _ = i > i;
435 \\ _ = i < i;
436 \\ _ = i and i;
437 \\ _ = i or i;
438 \\}
439 \\
440 );
441}
442
443test "zig fmt: precedence" {
444 try testCanonical(
445 \\test "precedence" {
446 \\ a!b();
447 \\ (a!b)();
448 \\ !a!b;
449 \\ !(a!b);
450 \\ !a{};
451 \\ !(a{});
452 \\ a + b{};
453 \\ (a + b){};
454 \\ a << b + c;
455 \\ (a << b) + c;
456 \\ a & b << c;
457 \\ (a & b) << c;
458 \\ a ^ b & c;
459 \\ (a ^ b) & c;
460 \\ a | b ^ c;
461 \\ (a | b) ^ c;
462 \\ a == b | c;
463 \\ (a == b) | c;
464 \\ a and b == c;
465 \\ (a and b) == c;
466 \\ a or b and c;
467 \\ (a or b) and c;
468 \\ (a or b) and c;
469 \\}
470 \\
471 );
472}
473
474test "zig fmt: prefix operators" {
475 try testCanonical(
476 \\test "prefix operators" {
477 \\ try return --%~??!*&0;
478 \\}
479 \\
480 );
481}
482
483test "zig fmt: call expression" {
484 try testCanonical(
485 \\test "test calls" {
486 \\ a();
487 \\ a(1);
488 \\ a(1, 2);
489 \\ a(1, 2) + a(1, 2);
490 \\}
491 \\
492 );
493}
494
495test "zig fmt: var args" {
496 try testCanonical(
497 \\fn print(args: ...) void {}
498 \\
499 );
500}
501
502test "zig fmt: var type" {
503 try testCanonical(
504 \\fn print(args: var) var {}
505 \\const Var = var;
506 \\const i: var = 0;
507 \\
508 );
509}
510
511test "zig fmt: functions" {
512 try testCanonical(
513 \\extern fn puts(s: &const u8) c_int;
514 \\extern "c" fn puts(s: &const u8) c_int;
515 \\export fn puts(s: &const u8) c_int;
516 \\inline fn puts(s: &const u8) c_int;
517 \\pub extern fn puts(s: &const u8) c_int;
518 \\pub extern "c" fn puts(s: &const u8) c_int;
519 \\pub export fn puts(s: &const u8) c_int;
520 \\pub inline fn puts(s: &const u8) c_int;
521 \\pub extern fn puts(s: &const u8) align(2 + 2) c_int;
522 \\pub extern "c" fn puts(s: &const u8) align(2 + 2) c_int;
523 \\pub export fn puts(s: &const u8) align(2 + 2) c_int;
524 \\pub inline fn puts(s: &const u8) align(2 + 2) c_int;
525 \\
526 );
527}
528
529test "zig fmt: multiline string" {
530 try testCanonical(
531 \\test "" {
532 \\ const s1 =
533 \\ \\one
534 \\ \\two)
535 \\ \\three
536 \\ ;
537 \\ const s2 =
538 \\ c\\one
539 \\ c\\two)
540 \\ c\\three
541 \\ ;
542 \\}
543 \\
544 );
545}
546
547test "zig fmt: values" {
548 try testCanonical(
549 \\test "values" {
550 \\ 1;
551 \\ 1.0;
552 \\ "string";
553 \\ c"cstring";
554 \\ 'c';
555 \\ true;
556 \\ false;
557 \\ null;
558 \\ undefined;
559 \\ error;
560 \\ this;
561 \\ unreachable;
562 \\}
563 \\
564 );
565}
566
567test "zig fmt: indexing" {
568 try testCanonical(
569 \\test "test index" {
570 \\ a[0];
571 \\ a[0 + 5];
572 \\ a[0..];
573 \\ a[0..5];
574 \\ a[a[0]];
575 \\ a[a[0..]];
576 \\ a[a[0..5]];
577 \\ a[a[0]..];
578 \\ a[a[0..5]..];
579 \\ a[a[0]..a[0]];
580 \\ a[a[0..5]..a[0]];
581 \\ a[a[0..5]..a[0..5]];
582 \\}
583 \\
584 );
585}
586
587test "zig fmt: struct declaration" {
588 try testCanonical(
589 \\const S = struct {
590 \\ const Self = this;
591 \\ f1: u8,
592 \\ pub f3: u8,
593 \\
594 \\ fn method(self: &Self) Self {
595 \\ return *self;
596 \\ }
597 \\
598 \\ f2: u8,
599 \\};
600 \\
601 \\const Ps = packed struct {
602 \\ a: u8,
603 \\ pub b: u8,
604 \\
605 \\ c: u8,
606 \\};
607 \\
608 \\const Es = extern struct {
609 \\ a: u8,
610 \\ pub b: u8,
611 \\
612 \\ c: u8,
613 \\};
614 \\
615 );
616}
617
618test "zig fmt: enum declaration" {
619 try testCanonical(
620 \\const E = enum {
621 \\ Ok,
622 \\ SomethingElse = 0,
623 \\};
624 \\
625 \\const E2 = enum(u8) {
626 \\ Ok,
627 \\ SomethingElse = 255,
628 \\ SomethingThird,
629 \\};
630 \\
631 \\const Ee = extern enum {
632 \\ Ok,
633 \\ SomethingElse,
634 \\ SomethingThird,
635 \\};
636 \\
637 \\const Ep = packed enum {
638 \\ Ok,
639 \\ SomethingElse,
640 \\ SomethingThird,
641 \\};
642 \\
643 );
644}
645
646test "zig fmt: union declaration" {
647 try testCanonical(
648 \\const U = union {
649 \\ Int: u8,
650 \\ Float: f32,
651 \\ None,
652 \\ Bool: bool,
653 \\};
654 \\
655 \\const Ue = union(enum) {
656 \\ Int: u8,
657 \\ Float: f32,
658 \\ None,
659 \\ Bool: bool,
660 \\};
661 \\
662 \\const E = enum {
663 \\ Int,
664 \\ Float,
665 \\ None,
666 \\ Bool,
667 \\};
668 \\
669 \\const Ue2 = union(E) {
670 \\ Int: u8,
671 \\ Float: f32,
672 \\ None,
673 \\ Bool: bool,
674 \\};
675 \\
676 \\const Eu = extern union {
677 \\ Int: u8,
678 \\ Float: f32,
679 \\ None,
680 \\ Bool: bool,
681 \\};
682 \\
683 );
684}
685
686test "zig fmt: arrays" {
687 try testCanonical(
688 \\test "test array" {
689 \\ const a: [2]u8 = [2]u8{
690 \\ 1,
691 \\ 2,
692 \\ };
693 \\ const a: [2]u8 = []u8{
694 \\ 1,
695 \\ 2,
696 \\ };
697 \\ const a: [0]u8 = []u8{};
698 \\}
699 \\
700 );
701}
702
703test "zig fmt: container initializers" {
704 try testCanonical(
705 \\const a0 = []u8{};
706 \\const a1 = []u8{1};
707 \\const a2 = []u8{
708 \\ 1,
709 \\ 2,
710 \\ 3,
711 \\ 4,
712 \\};
713 \\const s0 = S{};
714 \\const s1 = S{ .a = 1 };
715 \\const s2 = S{
716 \\ .a = 1,
717 \\ .b = 2,
718 \\};
719 \\
720 );
721}
722
723test "zig fmt: catch" {
724 try testCanonical(
725 \\test "catch" {
726 \\ const a: error!u8 = 0;
727 \\ _ = a catch return;
728 \\ _ = a catch |err| return;
729 \\}
730 \\
731 );
732}
733
734test "zig fmt: blocks" {
735 try testCanonical(
736 \\test "blocks" {
737 \\ {
738 \\ const a = 0;
739 \\ const b = 0;
740 \\ }
741 \\
742 \\ blk: {
743 \\ const a = 0;
744 \\ const b = 0;
745 \\ }
746 \\
747 \\ const r = blk: {
748 \\ const a = 0;
749 \\ const b = 0;
750 \\ };
751 \\}
752 \\
753 );
754}
755
756test "zig fmt: switch" {
757 try testCanonical(
758 \\test "switch" {
759 \\ switch (0) {
760 \\ 0 => {},
761 \\ 1 => unreachable,
762 \\ 2,
763 \\ 3 => {},
764 \\ 4 ... 7 => {},
765 \\ 1 + 4 * 3 + 22 => {},
766 \\ else => {
767 \\ const a = 1;
768 \\ const b = a;
769 \\ },
770 \\ }
771 \\
772 \\ const res = switch (0) {
773 \\ 0 => 0,
774 \\ 1 => 2,
775 \\ 1 => a = 4,
776 \\ else => 4,
777 \\ };
778 \\
779 \\ const Union = union(enum) {
780 \\ Int: i64,
781 \\ Float: f64,
782 \\ };
783 \\
784 \\ switch (u) {
785 \\ Union.Int => |int| {},
786 \\ Union.Float => |*float| unreachable,
787 \\ }
788 \\}
789 \\
790 );
791}
792
793test "zig fmt: while" {
794 try testCanonical(
795 \\test "while" {
796 \\ while (10 < 1) {
797 \\ unreachable;
798 \\ }
799 \\
800 \\ while (10 < 1)
801 \\ unreachable;
802 \\
803 \\ var i: usize = 0;
804 \\ while (i < 10) : (i += 1) {
805 \\ continue;
806 \\ }
807 \\
808 \\ i = 0;
809 \\ while (i < 10) : (i += 1)
810 \\ continue;
811 \\
812 \\ i = 0;
813 \\ var j: usize = 0;
814 \\ while (i < 10) : ({
815 \\ i += 1;
816 \\ j += 1;
817 \\ }) {
818 \\ continue;
819 \\ }
820 \\
821 \\ var a: ?u8 = 2;
822 \\ while (a) |v| : (a = null) {
823 \\ continue;
824 \\ }
825 \\
826 \\ while (a) |v| : (a = null)
827 \\ unreachable;
828 \\
829 \\ label: while (10 < 0) {
830 \\ unreachable;
831 \\ }
832 \\
833 \\ const res = while (0 < 10) {
834 \\ break 7;
835 \\ } else {
836 \\ unreachable;
837 \\ };
838 \\
839 \\ const res = while (0 < 10)
840 \\ break 7
841 \\ else
842 \\ unreachable;
843 \\
844 \\ var a: error!u8 = 0;
845 \\ while (a) |v| {
846 \\ a = error.Err;
847 \\ } else |err| {
848 \\ i = 1;
849 \\ }
850 \\
851 \\ comptime var k: usize = 0;
852 \\ inline while (i < 10) : (i += 1)
853 \\ j += 2;
854 \\}
855 \\
856 );
857}
858
859test "zig fmt: for" {
860 try testCanonical(
861 \\test "for" {
862 \\ for (a) |v| {
863 \\ continue;
864 \\ }
865 \\
866 \\ for (a) |v|
867 \\ continue;
868 \\
869 \\ for (a) |*v|
870 \\ continue;
871 \\
872 \\ for (a) |v, i| {
873 \\ continue;
874 \\ }
875 \\
876 \\ for (a) |v, i|
877 \\ continue;
878 \\
879 \\ const res = for (a) |v, i| {
880 \\ break v;
881 \\ } else {
882 \\ unreachable;
883 \\ };
884 \\
885 \\ var num: usize = 0;
886 \\ inline for (a) |v, i| {
887 \\ num += v;
888 \\ num += i;
889 \\ }
890 \\}
891 \\
892 );
893}
894
895test "zig fmt: if" {
896 try testCanonical(
897 \\test "if" {
898 \\ if (10 < 0) {
899 \\ unreachable;
900 \\ }
901 \\
902 \\ if (10 < 0) unreachable;
903 \\
904 \\ if (10 < 0) {
905 \\ unreachable;
906 \\ } else {
907 \\ const a = 20;
908 \\ }
909 \\
910 \\ if (10 < 0) {
911 \\ unreachable;
912 \\ } else if (5 < 0) {
913 \\ unreachable;
914 \\ } else {
915 \\ const a = 20;
916 \\ }
917 \\
918 \\ const is_world_broken = if (10 < 0) true else false;
919 \\ const some_number = 1 + if (10 < 0) 2 else 3;
920 \\
921 \\ const a: ?u8 = 10;
922 \\ const b: ?u8 = null;
923 \\ if (a) |v| {
924 \\ const some = v;
925 \\ } else if (b) |*v| {
926 \\ unreachable;
927 \\ } else {
928 \\ const some = 10;
929 \\ }
930 \\
931 \\ const non_null_a = if (a) |v| v else 0;
932 \\
933 \\ const a_err: error!u8 = 0;
934 \\ if (a_err) |v| {
935 \\ const p = v;
936 \\ } else |err| {
937 \\ unreachable;
938 \\ }
939 \\}
940 \\
941 );
942}
943
944test "zig fmt: defer" {
945 try testCanonical(
946 \\test "defer" {
947 \\ var i: usize = 0;
948 \\ defer i = 1;
949 \\ defer {
950 \\ i += 2;
951 \\ i *= i;
952 \\ }
953 \\
954 \\ errdefer i += 3;
955 \\ errdefer {
956 \\ i += 2;
957 \\ i /= i;
958 \\ }
959 \\}
960 \\
961 );
962}
963
964test "zig fmt: comptime" {
965 try testCanonical(
966 \\fn a() u8 {
967 \\ return 5;
968 \\}
969 \\
970 \\fn b(comptime i: u8) u8 {
971 \\ return i;
972 \\}
973 \\
974 \\const av = comptime a();
975 \\const av2 = comptime blk: {
976 \\ var res = a();
977 \\ res *= b(2);
978 \\ break :blk res;
979 \\};
980 \\
981 \\comptime {
982 \\ _ = a();
983 \\}
984 \\
985 \\test "comptime" {
986 \\ const av3 = comptime a();
987 \\ const av4 = comptime blk: {
988 \\ var res = a();
989 \\ res *= a();
990 \\ break :blk res;
991 \\ };
992 \\
993 \\ comptime var i = 0;
994 \\ comptime {
995 \\ i = a();
996 \\ i += b(i);
997 \\ }
998 \\}
999 \\
1000 );
1001}
1002
1003test "zig fmt: fn type" {
1004 try testCanonical(
1005 \\fn a(i: u8) u8 {
1006 \\ return i + 1;
1007 \\}
1008 \\
1009 \\const a: fn(u8) u8 = undefined;
1010 \\const b: extern fn(u8) u8 = undefined;
1011 \\const c: nakedcc fn(u8) u8 = undefined;
1012 \\const ap: fn(u8) u8 = a;
1013 \\
1014 );
1015}
1016
1017test "zig fmt: inline asm" {
1018 try testCanonical(
1019 \\pub fn syscall1(number: usize, arg1: usize) usize {
1020 \\ return asm volatile ("syscall"
1021 \\ : [ret] "={rax}" (-> usize)
1022 \\ : [number] "{rax}" (number),
1023 \\ [arg1] "{rdi}" (arg1)
1024 \\ : "rcx", "r11");
1025 \\}
1026 \\
1027 );
1028}
1029
1030test "zig fmt: coroutines" {
1031 try testCanonical(
1032 \\async fn simpleAsyncFn() void {
1033 \\ const a = async a.b();
1034 \\ x += 1;
1035 \\ suspend;
1036 \\ x += 1;
1037 \\ suspend |p| {}
1038 \\ const p: promise->void = async simpleAsyncFn() catch unreachable;
1039 \\ await p;
1040 \\}
1041 \\
1042 \\test "coroutine suspend, resume, cancel" {
1043 \\ const p: promise = try async<std.debug.global_allocator> testAsyncSeq();
1044 \\ resume p;
1045 \\ cancel p;
1046 \\}
1047 \\
1048 );
1049}
1050
1051test "zig fmt: Block after if" {
1052 try testCanonical(
1053 \\test "Block after if" {
1054 \\ if (true) {
1055 \\ const a = 0;
1056 \\ }
1057 \\
1058 \\ {
1059 \\ const a = 0;
1060 \\ }
1061 \\}
1062 \\
1063 );
1064}
1065
1066test "zig fmt: use" {
1067 try testCanonical(
1068 \\use @import("std");
1069 \\pub use @import("std");
1070 \\
1071 );
1072}
1073
1074test "zig fmt: string identifier" {
1075 try testCanonical(
1076 \\const @"a b" = @"c d".@"e f";
1077 \\fn @"g h"() void {}
1078 \\
1079 );
1080}
1081
1082test "zig fmt: error return" {
1083 try testCanonical(
1084 \\fn err() error {
1085 \\ call();
1086 \\ return error.InvalidArgs;
1087 \\}
1088 \\
1089 );
1090}
1091
1092const std = @import("std");
1093const mem = std.mem;
1094const warn = std.debug.warn;
1095const io = std.io;
1096
1097var fixed_buffer_mem: [100 * 1024]u8 = undefined;
1098
1099fn testParse(source: []const u8, allocator: &mem.Allocator) ![]u8 {
1100 var stderr_file = try io.getStdErr();
1101 var stderr = &io.FileOutStream.init(&stderr_file).stream;
1102
1103 var tree = try std.zig.parse(allocator, source);
1104 defer tree.deinit();
1105
1106 var error_it = tree.errors.iterator(0);
1107 while (error_it.next()) |parse_error| {
1108 const token = tree.tokens.at(parse_error.loc());
1109 const loc = tree.tokenLocation(0, parse_error.loc());
1110 try stderr.print("(memory buffer):{}:{}: error: ", loc.line + 1, loc.column + 1);
1111 try tree.renderError(parse_error, stderr);
1112 try stderr.print("\n{}\n", source[loc.line_start..loc.line_end]);
1113 {
1114 var i: usize = 0;
1115 while (i < loc.column) : (i += 1) {
1116 try stderr.write(" ");
1117 }
1118 }
1119 {
1120 const caret_count = token.end - token.start;
1121 var i: usize = 0;
1122 while (i < caret_count) : (i += 1) {
1123 try stderr.write("~");
1124 }
1125 }
1126 try stderr.write("\n");
1127 }
1128 if (tree.errors.len != 0) {
1129 return error.ParseError;
1130 }
1131
1132 var buffer = try std.Buffer.initSize(allocator, 0);
1133 errdefer buffer.deinit();
1134
1135 var buffer_out_stream = io.BufferOutStream.init(&buffer);
1136 try std.zig.render(allocator, &buffer_out_stream.stream, &tree);
1137 return buffer.toOwnedSlice();
1138}
1139
1140fn testTransform(source: []const u8, expected_source: []const u8) !void {
1141 const needed_alloc_count = x: {
1142 // Try it once with unlimited memory, make sure it works
1143 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1144 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
1145 const result_source = try testParse(source, &failing_allocator.allocator);
1146 if (!mem.eql(u8, result_source, expected_source)) {
1147 warn("\n====== expected this output: =========\n");
1148 warn("{}", expected_source);
1149 warn("\n======== instead found this: =========\n");
1150 warn("{}", result_source);
1151 warn("\n======================================\n");
1152 return error.TestFailed;
1153 }
1154 failing_allocator.allocator.free(result_source);
1155 break :x failing_allocator.index;
1156 };
1157
1158 var fail_index: usize = 0;
1159 while (fail_index < needed_alloc_count) : (fail_index += 1) {
1160 var fixed_allocator = std.heap.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
1161 var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
1162 if (testParse(source, &failing_allocator.allocator)) |_| {
1163 return error.NondeterministicMemoryUsage;
1164 } else |err| switch (err) {
1165 error.OutOfMemory => {
1166 if (failing_allocator.allocated_bytes != failing_allocator.freed_bytes) {
1167 warn("\nfail_index: {}/{}\nallocated bytes: {}\nfreed bytes: {}\nallocations: {}\ndeallocations: {}\n",
1168 fail_index, needed_alloc_count,
1169 failing_allocator.allocated_bytes, failing_allocator.freed_bytes,
1170 failing_allocator.index, failing_allocator.deallocations);
1171 return error.MemoryLeakDetected;
1172 }
1173 },
1174 error.ParseError => @panic("test failed"),
1175 else => @panic("test failed"),
1176 }
1177 }
1178}
1179
1180fn testCanonical(source: []const u8) !void {
1181 return testTransform(source, source);
1182}
1183
std/zig/render.zig created+1270
...@@ -0,0 +1,1270 @@
1const std = @import("../index.zig");
2const assert = std.debug.assert;
3const mem = std.mem;
4const ast = std.zig.ast;
5const Token = std.zig.Token;
6
7const RenderState = union(enum) {
8 TopLevelDecl: &ast.Node,
9 ParamDecl: &ast.Node,
10 Text: []const u8,
11 Expression: &ast.Node,
12 VarDecl: &ast.Node.VarDecl,
13 Statement: &ast.Node,
14 PrintIndent,
15 Indent: usize,
16 MaybeSemiColon: &ast.Node,
17 Token: ast.TokenIndex,
18 NonBreakToken: ast.TokenIndex,
19};
20
21const indent_delta = 4;
22
23pub fn render(allocator: &mem.Allocator, stream: var, tree: &ast.Tree) !void {
24 var stack = std.ArrayList(RenderState).init(allocator);
25 defer stack.deinit();
26
27 {
28 try stack.append(RenderState { .Text = "\n"});
29
30 var i = tree.root_node.decls.len;
31 while (i != 0) {
32 i -= 1;
33 const decl = *tree.root_node.decls.at(i);
34 try stack.append(RenderState {.TopLevelDecl = decl});
35 if (i != 0) {
36 try stack.append(RenderState {
37 .Text = blk: {
38 const prev_node = *tree.root_node.decls.at(i - 1);
39 const prev_node_last_token = tree.tokens.at(prev_node.lastToken());
40 const loc = tree.tokenLocation(prev_node_last_token.end, decl.firstToken());
41 if (loc.line >= 2) {
42 break :blk "\n\n";
43 }
44 break :blk "\n";
45 },
46 });
47 }
48 }
49 }
50
51 var indent: usize = 0;
52 while (stack.popOrNull()) |state| {
53 switch (state) {
54 RenderState.TopLevelDecl => |decl| {
55 switch (decl.id) {
56 ast.Node.Id.FnProto => {
57 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
58 try renderComments(tree, stream, fn_proto, indent);
59
60 if (fn_proto.body_node) |body_node| {
61 stack.append(RenderState { .Expression = body_node}) catch unreachable;
62 try stack.append(RenderState { .Text = " "});
63 } else {
64 stack.append(RenderState { .Text = ";" }) catch unreachable;
65 }
66
67 try stack.append(RenderState { .Expression = decl });
68 },
69 ast.Node.Id.Use => {
70 const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl);
71 if (use_decl.visib_token) |visib_token| {
72 try stream.print("{} ", tree.tokenSlice(visib_token));
73 }
74 try stream.print("use ");
75 try stack.append(RenderState { .Text = ";" });
76 try stack.append(RenderState { .Expression = use_decl.expr });
77 },
78 ast.Node.Id.VarDecl => {
79 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
80 try renderComments(tree, stream, var_decl, indent);
81 try stack.append(RenderState { .VarDecl = var_decl});
82 },
83 ast.Node.Id.TestDecl => {
84 const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
85 try renderComments(tree, stream, test_decl, indent);
86 try stream.print("test ");
87 try stack.append(RenderState { .Expression = test_decl.body_node });
88 try stack.append(RenderState { .Text = " " });
89 try stack.append(RenderState { .Expression = test_decl.name });
90 },
91 ast.Node.Id.StructField => {
92 const field = @fieldParentPtr(ast.Node.StructField, "base", decl);
93 try renderComments(tree, stream, field, indent);
94 if (field.visib_token) |visib_token| {
95 try stream.print("{} ", tree.tokenSlice(visib_token));
96 }
97 try stream.print("{}: ", tree.tokenSlice(field.name_token));
98 try stack.append(RenderState { .Token = field.lastToken() + 1 });
99 try stack.append(RenderState { .Expression = field.type_expr});
100 },
101 ast.Node.Id.UnionTag => {
102 const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl);
103 try renderComments(tree, stream, tag, indent);
104 try stream.print("{}", tree.tokenSlice(tag.name_token));
105
106 try stack.append(RenderState { .Text = "," });
107
108 if (tag.value_expr) |value_expr| {
109 try stack.append(RenderState { .Expression = value_expr });
110 try stack.append(RenderState { .Text = " = " });
111 }
112
113 if (tag.type_expr) |type_expr| {
114 try stream.print(": ");
115 try stack.append(RenderState { .Expression = type_expr});
116 }
117 },
118 ast.Node.Id.EnumTag => {
119 const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl);
120 try renderComments(tree, stream, tag, indent);
121 try stream.print("{}", tree.tokenSlice(tag.name_token));
122
123 try stack.append(RenderState { .Text = "," });
124 if (tag.value) |value| {
125 try stream.print(" = ");
126 try stack.append(RenderState { .Expression = value});
127 }
128 },
129 ast.Node.Id.ErrorTag => {
130 const tag = @fieldParentPtr(ast.Node.ErrorTag, "base", decl);
131 try renderComments(tree, stream, tag, indent);
132 try stream.print("{}", tree.tokenSlice(tag.name_token));
133 },
134 ast.Node.Id.Comptime => {
135 try stack.append(RenderState { .MaybeSemiColon = decl });
136 try stack.append(RenderState { .Expression = decl });
137 },
138 ast.Node.Id.LineComment => {
139 const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", decl);
140 try stream.write(tree.tokenSlice(line_comment_node.token));
141 },
142 else => unreachable,
143 }
144 },
145
146 RenderState.VarDecl => |var_decl| {
147 try stack.append(RenderState { .Token = var_decl.semicolon_token });
148 if (var_decl.init_node) |init_node| {
149 try stack.append(RenderState { .Expression = init_node });
150 const text = if (init_node.id == ast.Node.Id.MultilineStringLiteral) " =" else " = ";
151 try stack.append(RenderState { .Text = text });
152 }
153 if (var_decl.align_node) |align_node| {
154 try stack.append(RenderState { .Text = ")" });
155 try stack.append(RenderState { .Expression = align_node });
156 try stack.append(RenderState { .Text = " align(" });
157 }
158 if (var_decl.type_node) |type_node| {
159 try stack.append(RenderState { .Expression = type_node });
160 try stack.append(RenderState { .Text = ": " });
161 }
162 try stack.append(RenderState { .Text = tree.tokenSlice(var_decl.name_token) });
163 try stack.append(RenderState { .Text = " " });
164 try stack.append(RenderState { .Text = tree.tokenSlice(var_decl.mut_token) });
165
166 if (var_decl.comptime_token) |comptime_token| {
167 try stack.append(RenderState { .Text = " " });
168 try stack.append(RenderState { .Text = tree.tokenSlice(comptime_token) });
169 }
170
171 if (var_decl.extern_export_token) |extern_export_token| {
172 if (var_decl.lib_name != null) {
173 try stack.append(RenderState { .Text = " " });
174 try stack.append(RenderState { .Expression = ??var_decl.lib_name });
175 }
176 try stack.append(RenderState { .Text = " " });
177 try stack.append(RenderState { .Text = tree.tokenSlice(extern_export_token) });
178 }
179
180 if (var_decl.visib_token) |visib_token| {
181 try stack.append(RenderState { .Text = " " });
182 try stack.append(RenderState { .Text = tree.tokenSlice(visib_token) });
183 }
184 },
185
186 RenderState.ParamDecl => |base| {
187 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
188 if (param_decl.comptime_token) |comptime_token| {
189 try stream.print("{} ", tree.tokenSlice(comptime_token));
190 }
191 if (param_decl.noalias_token) |noalias_token| {
192 try stream.print("{} ", tree.tokenSlice(noalias_token));
193 }
194 if (param_decl.name_token) |name_token| {
195 try stream.print("{}: ", tree.tokenSlice(name_token));
196 }
197 if (param_decl.var_args_token) |var_args_token| {
198 try stream.print("{}", tree.tokenSlice(var_args_token));
199 } else {
200 try stack.append(RenderState { .Expression = param_decl.type_node});
201 }
202 },
203 RenderState.Text => |bytes| {
204 try stream.write(bytes);
205 },
206 RenderState.Expression => |base| switch (base.id) {
207 ast.Node.Id.Identifier => {
208 const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
209 try stream.print("{}", tree.tokenSlice(identifier.token));
210 },
211 ast.Node.Id.Block => {
212 const block = @fieldParentPtr(ast.Node.Block, "base", base);
213 if (block.label) |label| {
214 try stream.print("{}: ", tree.tokenSlice(label));
215 }
216
217 if (block.statements.len == 0) {
218 try stream.write("{}");
219 } else {
220 try stream.write("{");
221 try stack.append(RenderState { .Text = "}"});
222 try stack.append(RenderState.PrintIndent);
223 try stack.append(RenderState { .Indent = indent});
224 try stack.append(RenderState { .Text = "\n"});
225 var i = block.statements.len;
226 while (i != 0) {
227 i -= 1;
228 const statement_node = *block.statements.at(i);
229 try stack.append(RenderState { .Statement = statement_node});
230 try stack.append(RenderState.PrintIndent);
231 try stack.append(RenderState { .Indent = indent + indent_delta});
232 try stack.append(RenderState {
233 .Text = blk: {
234 if (i != 0) {
235 const prev_node = *block.statements.at(i - 1);
236 const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
237 const loc = tree.tokenLocation(prev_node_last_token_end, statement_node.firstToken());
238 if (loc.line >= 2) {
239 break :blk "\n\n";
240 }
241 }
242 break :blk "\n";
243 },
244 });
245 }
246 }
247 },
248 ast.Node.Id.Defer => {
249 const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);
250 try stream.print("{} ", tree.tokenSlice(defer_node.defer_token));
251 try stack.append(RenderState { .Expression = defer_node.expr });
252 },
253 ast.Node.Id.Comptime => {
254 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base);
255 try stream.print("{} ", tree.tokenSlice(comptime_node.comptime_token));
256 try stack.append(RenderState { .Expression = comptime_node.expr });
257 },
258 ast.Node.Id.AsyncAttribute => {
259 const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base);
260 try stream.print("{}", tree.tokenSlice(async_attr.async_token));
261
262 if (async_attr.allocator_type) |allocator_type| {
263 try stack.append(RenderState { .Text = ">" });
264 try stack.append(RenderState { .Expression = allocator_type });
265 try stack.append(RenderState { .Text = "<" });
266 }
267 },
268 ast.Node.Id.Suspend => {
269 const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
270 if (suspend_node.label) |label| {
271 try stream.print("{}: ", tree.tokenSlice(label));
272 }
273 try stream.print("{}", tree.tokenSlice(suspend_node.suspend_token));
274
275 if (suspend_node.body) |body| {
276 try stack.append(RenderState { .Expression = body });
277 try stack.append(RenderState { .Text = " " });
278 }
279
280 if (suspend_node.payload) |payload| {
281 try stack.append(RenderState { .Expression = payload });
282 try stack.append(RenderState { .Text = " " });
283 }
284 },
285 ast.Node.Id.InfixOp => {
286 const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base);
287 try stack.append(RenderState { .Expression = prefix_op_node.rhs });
288
289 if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) {
290 if (prefix_op_node.op.Catch) |payload| {
291 try stack.append(RenderState { .Text = " " });
292 try stack.append(RenderState { .Expression = payload });
293 }
294 try stack.append(RenderState { .Text = " catch " });
295 } else {
296 const text = switch (prefix_op_node.op) {
297 ast.Node.InfixOp.Op.Add => " + ",
298 ast.Node.InfixOp.Op.AddWrap => " +% ",
299 ast.Node.InfixOp.Op.ArrayCat => " ++ ",
300 ast.Node.InfixOp.Op.ArrayMult => " ** ",
301 ast.Node.InfixOp.Op.Assign => " = ",
302 ast.Node.InfixOp.Op.AssignBitAnd => " &= ",
303 ast.Node.InfixOp.Op.AssignBitOr => " |= ",
304 ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ",
305 ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ",
306 ast.Node.InfixOp.Op.AssignBitXor => " ^= ",
307 ast.Node.InfixOp.Op.AssignDiv => " /= ",
308 ast.Node.InfixOp.Op.AssignMinus => " -= ",
309 ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ",
310 ast.Node.InfixOp.Op.AssignMod => " %= ",
311 ast.Node.InfixOp.Op.AssignPlus => " += ",
312 ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ",
313 ast.Node.InfixOp.Op.AssignTimes => " *= ",
314 ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ",
315 ast.Node.InfixOp.Op.BangEqual => " != ",
316 ast.Node.InfixOp.Op.BitAnd => " & ",
317 ast.Node.InfixOp.Op.BitOr => " | ",
318 ast.Node.InfixOp.Op.BitShiftLeft => " << ",
319 ast.Node.InfixOp.Op.BitShiftRight => " >> ",
320 ast.Node.InfixOp.Op.BitXor => " ^ ",
321 ast.Node.InfixOp.Op.BoolAnd => " and ",
322 ast.Node.InfixOp.Op.BoolOr => " or ",
323 ast.Node.InfixOp.Op.Div => " / ",
324 ast.Node.InfixOp.Op.EqualEqual => " == ",
325 ast.Node.InfixOp.Op.ErrorUnion => "!",
326 ast.Node.InfixOp.Op.GreaterOrEqual => " >= ",
327 ast.Node.InfixOp.Op.GreaterThan => " > ",
328 ast.Node.InfixOp.Op.LessOrEqual => " <= ",
329 ast.Node.InfixOp.Op.LessThan => " < ",
330 ast.Node.InfixOp.Op.MergeErrorSets => " || ",
331 ast.Node.InfixOp.Op.Mod => " % ",
332 ast.Node.InfixOp.Op.Mult => " * ",
333 ast.Node.InfixOp.Op.MultWrap => " *% ",
334 ast.Node.InfixOp.Op.Period => ".",
335 ast.Node.InfixOp.Op.Sub => " - ",
336 ast.Node.InfixOp.Op.SubWrap => " -% ",
337 ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ",
338 ast.Node.InfixOp.Op.Range => " ... ",
339 ast.Node.InfixOp.Op.Catch => unreachable,
340 };
341
342 try stack.append(RenderState { .Text = text });
343 }
344 try stack.append(RenderState { .Expression = prefix_op_node.lhs });
345 },
346 ast.Node.Id.PrefixOp => {
347 const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base);
348 try stack.append(RenderState { .Expression = prefix_op_node.rhs });
349 switch (prefix_op_node.op) {
350 ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| {
351 try stream.write("&");
352 if (addr_of_info.volatile_token != null) {
353 try stack.append(RenderState { .Text = "volatile "});
354 }
355 if (addr_of_info.const_token != null) {
356 try stack.append(RenderState { .Text = "const "});
357 }
358 if (addr_of_info.align_expr) |align_expr| {
359 try stream.print("align(");
360 try stack.append(RenderState { .Text = ") "});
361 try stack.append(RenderState { .Expression = align_expr});
362 }
363 },
364 ast.Node.PrefixOp.Op.SliceType => |addr_of_info| {
365 try stream.write("[]");
366 if (addr_of_info.volatile_token != null) {
367 try stack.append(RenderState { .Text = "volatile "});
368 }
369 if (addr_of_info.const_token != null) {
370 try stack.append(RenderState { .Text = "const "});
371 }
372 if (addr_of_info.align_expr) |align_expr| {
373 try stream.print("align(");
374 try stack.append(RenderState { .Text = ") "});
375 try stack.append(RenderState { .Expression = align_expr});
376 }
377 },
378 ast.Node.PrefixOp.Op.ArrayType => |array_index| {
379 try stack.append(RenderState { .Text = "]"});
380 try stack.append(RenderState { .Expression = array_index});
381 try stack.append(RenderState { .Text = "["});
382 },
383 ast.Node.PrefixOp.Op.BitNot => try stream.write("~"),
384 ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"),
385 ast.Node.PrefixOp.Op.Deref => try stream.write("*"),
386 ast.Node.PrefixOp.Op.Negation => try stream.write("-"),
387 ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"),
388 ast.Node.PrefixOp.Op.Try => try stream.write("try "),
389 ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"),
390 ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"),
391 ast.Node.PrefixOp.Op.Await => try stream.write("await "),
392 ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "),
393 ast.Node.PrefixOp.Op.Resume => try stream.write("resume "),
394 }
395 },
396 ast.Node.Id.SuffixOp => {
397 const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base);
398
399 switch (suffix_op.op) {
400 @TagType(ast.Node.SuffixOp.Op).Call => |*call_info| {
401 try stack.append(RenderState { .Text = ")"});
402 var i = call_info.params.len;
403 while (i != 0) {
404 i -= 1;
405 const param_node = *call_info.params.at(i);
406 try stack.append(RenderState { .Expression = param_node});
407 if (i != 0) {
408 try stack.append(RenderState { .Text = ", " });
409 }
410 }
411 try stack.append(RenderState { .Text = "("});
412 try stack.append(RenderState { .Expression = suffix_op.lhs });
413
414 if (call_info.async_attr) |async_attr| {
415 try stack.append(RenderState { .Text = " "});
416 try stack.append(RenderState { .Expression = &async_attr.base });
417 }
418 },
419 ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| {
420 try stack.append(RenderState { .Text = "]"});
421 try stack.append(RenderState { .Expression = index_expr});
422 try stack.append(RenderState { .Text = "["});
423 try stack.append(RenderState { .Expression = suffix_op.lhs });
424 },
425 @TagType(ast.Node.SuffixOp.Op).Slice => |range| {
426 try stack.append(RenderState { .Text = "]"});
427 if (range.end) |end| {
428 try stack.append(RenderState { .Expression = end});
429 }
430 try stack.append(RenderState { .Text = ".."});
431 try stack.append(RenderState { .Expression = range.start});
432 try stack.append(RenderState { .Text = "["});
433 try stack.append(RenderState { .Expression = suffix_op.lhs });
434 },
435 ast.Node.SuffixOp.Op.StructInitializer => |*field_inits| {
436 if (field_inits.len == 0) {
437 try stack.append(RenderState { .Text = "{}" });
438 try stack.append(RenderState { .Expression = suffix_op.lhs });
439 continue;
440 }
441 if (field_inits.len == 1) {
442 const field_init = *field_inits.at(0);
443
444 try stack.append(RenderState { .Text = " }" });
445 try stack.append(RenderState { .Expression = field_init });
446 try stack.append(RenderState { .Text = "{ " });
447 try stack.append(RenderState { .Expression = suffix_op.lhs });
448 continue;
449 }
450 try stack.append(RenderState { .Text = "}"});
451 try stack.append(RenderState.PrintIndent);
452 try stack.append(RenderState { .Indent = indent });
453 try stack.append(RenderState { .Text = "\n" });
454 var i = field_inits.len;
455 while (i != 0) {
456 i -= 1;
457 const field_init = *field_inits.at(i);
458 if (field_init.id != ast.Node.Id.LineComment) {
459 try stack.append(RenderState { .Text = "," });
460 }
461 try stack.append(RenderState { .Expression = field_init });
462 try stack.append(RenderState.PrintIndent);
463 if (i != 0) {
464 try stack.append(RenderState { .Text = blk: {
465 const prev_node = *field_inits.at(i - 1);
466 const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
467 const loc = tree.tokenLocation(prev_node_last_token_end, field_init.firstToken());
468 if (loc.line >= 2) {
469 break :blk "\n\n";
470 }
471 break :blk "\n";
472 }});
473 }
474 }
475 try stack.append(RenderState { .Indent = indent + indent_delta });
476 try stack.append(RenderState { .Text = "{\n"});
477 try stack.append(RenderState { .Expression = suffix_op.lhs });
478 },
479 ast.Node.SuffixOp.Op.ArrayInitializer => |*exprs| {
480 if (exprs.len == 0) {
481 try stack.append(RenderState { .Text = "{}" });
482 try stack.append(RenderState { .Expression = suffix_op.lhs });
483 continue;
484 }
485 if (exprs.len == 1) {
486 const expr = *exprs.at(0);
487
488 try stack.append(RenderState { .Text = "}" });
489 try stack.append(RenderState { .Expression = expr });
490 try stack.append(RenderState { .Text = "{" });
491 try stack.append(RenderState { .Expression = suffix_op.lhs });
492 continue;
493 }
494
495 try stack.append(RenderState { .Text = "}"});
496 try stack.append(RenderState.PrintIndent);
497 try stack.append(RenderState { .Indent = indent });
498 var i = exprs.len;
499 while (i != 0) {
500 i -= 1;
501 const expr = *exprs.at(i);
502 try stack.append(RenderState { .Text = ",\n" });
503 try stack.append(RenderState { .Expression = expr });
504 try stack.append(RenderState.PrintIndent);
505 }
506 try stack.append(RenderState { .Indent = indent + indent_delta });
507 try stack.append(RenderState { .Text = "{\n"});
508 try stack.append(RenderState { .Expression = suffix_op.lhs });
509 },
510 }
511 },
512 ast.Node.Id.ControlFlowExpression => {
513 const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base);
514
515 if (flow_expr.rhs) |rhs| {
516 try stack.append(RenderState { .Expression = rhs });
517 try stack.append(RenderState { .Text = " " });
518 }
519
520 switch (flow_expr.kind) {
521 ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| {
522 try stream.print("break");
523 if (maybe_label) |label| {
524 try stream.print(" :");
525 try stack.append(RenderState { .Expression = label });
526 }
527 },
528 ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| {
529 try stream.print("continue");
530 if (maybe_label) |label| {
531 try stream.print(" :");
532 try stack.append(RenderState { .Expression = label });
533 }
534 },
535 ast.Node.ControlFlowExpression.Kind.Return => {
536 try stream.print("return");
537 },
538
539 }
540 },
541 ast.Node.Id.Payload => {
542 const payload = @fieldParentPtr(ast.Node.Payload, "base", base);
543 try stack.append(RenderState { .Text = "|"});
544 try stack.append(RenderState { .Expression = payload.error_symbol });
545 try stack.append(RenderState { .Text = "|"});
546 },
547 ast.Node.Id.PointerPayload => {
548 const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base);
549 try stack.append(RenderState { .Text = "|"});
550 try stack.append(RenderState { .Expression = payload.value_symbol });
551
552 if (payload.ptr_token) |ptr_token| {
553 try stack.append(RenderState { .Text = tree.tokenSlice(ptr_token) });
554 }
555
556 try stack.append(RenderState { .Text = "|"});
557 },
558 ast.Node.Id.PointerIndexPayload => {
559 const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base);
560 try stack.append(RenderState { .Text = "|"});
561
562 if (payload.index_symbol) |index_symbol| {
563 try stack.append(RenderState { .Expression = index_symbol });
564 try stack.append(RenderState { .Text = ", "});
565 }
566
567 try stack.append(RenderState { .Expression = payload.value_symbol });
568
569 if (payload.ptr_token) |ptr_token| {
570 try stack.append(RenderState { .Text = tree.tokenSlice(ptr_token) });
571 }
572
573 try stack.append(RenderState { .Text = "|"});
574 },
575 ast.Node.Id.GroupedExpression => {
576 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base);
577 try stack.append(RenderState { .Text = ")"});
578 try stack.append(RenderState { .Expression = grouped_expr.expr });
579 try stack.append(RenderState { .Text = "("});
580 },
581 ast.Node.Id.FieldInitializer => {
582 const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base);
583 try stream.print(".{} = ", tree.tokenSlice(field_init.name_token));
584 try stack.append(RenderState { .Expression = field_init.expr });
585 },
586 ast.Node.Id.IntegerLiteral => {
587 const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base);
588 try stream.print("{}", tree.tokenSlice(integer_literal.token));
589 },
590 ast.Node.Id.FloatLiteral => {
591 const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base);
592 try stream.print("{}", tree.tokenSlice(float_literal.token));
593 },
594 ast.Node.Id.StringLiteral => {
595 const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base);
596 try stream.print("{}", tree.tokenSlice(string_literal.token));
597 },
598 ast.Node.Id.CharLiteral => {
599 const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
600 try stream.print("{}", tree.tokenSlice(char_literal.token));
601 },
602 ast.Node.Id.BoolLiteral => {
603 const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
604 try stream.print("{}", tree.tokenSlice(bool_literal.token));
605 },
606 ast.Node.Id.NullLiteral => {
607 const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base);
608 try stream.print("{}", tree.tokenSlice(null_literal.token));
609 },
610 ast.Node.Id.ThisLiteral => {
611 const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base);
612 try stream.print("{}", tree.tokenSlice(this_literal.token));
613 },
614 ast.Node.Id.Unreachable => {
615 const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base);
616 try stream.print("{}", tree.tokenSlice(unreachable_node.token));
617 },
618 ast.Node.Id.ErrorType => {
619 const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base);
620 try stream.print("{}", tree.tokenSlice(error_type.token));
621 },
622 ast.Node.Id.VarType => {
623 const var_type = @fieldParentPtr(ast.Node.VarType, "base", base);
624 try stream.print("{}", tree.tokenSlice(var_type.token));
625 },
626 ast.Node.Id.ContainerDecl => {
627 const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
628
629 switch (container_decl.layout) {
630 ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "),
631 ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "),
632 ast.Node.ContainerDecl.Layout.Auto => { },
633 }
634
635 switch (container_decl.kind) {
636 ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"),
637 ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"),
638 ast.Node.ContainerDecl.Kind.Union => try stream.print("union"),
639 }
640
641 if (container_decl.fields_and_decls.len == 0) {
642 try stack.append(RenderState { .Text = "{}"});
643 } else {
644 try stack.append(RenderState { .Text = "}"});
645 try stack.append(RenderState.PrintIndent);
646 try stack.append(RenderState { .Indent = indent });
647 try stack.append(RenderState { .Text = "\n"});
648
649 var i = container_decl.fields_and_decls.len;
650 while (i != 0) {
651 i -= 1;
652 const node = *container_decl.fields_and_decls.at(i);
653 try stack.append(RenderState { .TopLevelDecl = node});
654 try stack.append(RenderState.PrintIndent);
655 try stack.append(RenderState {
656 .Text = blk: {
657 if (i != 0) {
658 const prev_node = *container_decl.fields_and_decls.at(i - 1);
659 const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
660 const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
661 if (loc.line >= 2) {
662 break :blk "\n\n";
663 }
664 }
665 break :blk "\n";
666 },
667 });
668 }
669 try stack.append(RenderState { .Indent = indent + indent_delta});
670 try stack.append(RenderState { .Text = "{"});
671 }
672
673 switch (container_decl.init_arg_expr) {
674 ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}),
675 ast.Node.ContainerDecl.InitArg.Enum => |enum_tag_type| {
676 if (enum_tag_type) |expr| {
677 try stack.append(RenderState { .Text = ")) "});
678 try stack.append(RenderState { .Expression = expr});
679 try stack.append(RenderState { .Text = "(enum("});
680 } else {
681 try stack.append(RenderState { .Text = "(enum) "});
682 }
683 },
684 ast.Node.ContainerDecl.InitArg.Type => |type_expr| {
685 try stack.append(RenderState { .Text = ") "});
686 try stack.append(RenderState { .Expression = type_expr});
687 try stack.append(RenderState { .Text = "("});
688 },
689 }
690 },
691 ast.Node.Id.ErrorSetDecl => {
692 const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base);
693
694 if (err_set_decl.decls.len == 0) {
695 try stream.write("error{}");
696 continue;
697 }
698
699 if (err_set_decl.decls.len == 1) blk: {
700 const node = *err_set_decl.decls.at(0);
701
702 // if there are any doc comments or same line comments
703 // don't try to put it all on one line
704 if (node.cast(ast.Node.ErrorTag)) |tag| {
705 if (tag.doc_comments != null) break :blk;
706 } else {
707 break :blk;
708 }
709
710
711 try stream.write("error{");
712 try stack.append(RenderState { .Text = "}" });
713 try stack.append(RenderState { .TopLevelDecl = node });
714 continue;
715 }
716
717 try stream.write("error{");
718
719 try stack.append(RenderState { .Text = "}"});
720 try stack.append(RenderState.PrintIndent);
721 try stack.append(RenderState { .Indent = indent });
722 try stack.append(RenderState { .Text = "\n"});
723
724 var i = err_set_decl.decls.len;
725 while (i != 0) {
726 i -= 1;
727 const node = *err_set_decl.decls.at(i);
728 if (node.id != ast.Node.Id.LineComment) {
729 try stack.append(RenderState { .Text = "," });
730 }
731 try stack.append(RenderState { .TopLevelDecl = node });
732 try stack.append(RenderState.PrintIndent);
733 try stack.append(RenderState {
734 .Text = blk: {
735 if (i != 0) {
736 const prev_node = *err_set_decl.decls.at(i - 1);
737 const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
738 const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
739 if (loc.line >= 2) {
740 break :blk "\n\n";
741 }
742 }
743 break :blk "\n";
744 },
745 });
746 }
747 try stack.append(RenderState { .Indent = indent + indent_delta});
748 },
749 ast.Node.Id.MultilineStringLiteral => {
750 const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
751 try stream.print("\n");
752
753 var i : usize = 0;
754 while (i < multiline_str_literal.lines.len) : (i += 1) {
755 const t = *multiline_str_literal.lines.at(i);
756 try stream.writeByteNTimes(' ', indent + indent_delta);
757 try stream.print("{}", tree.tokenSlice(t));
758 }
759 try stream.writeByteNTimes(' ', indent);
760 },
761 ast.Node.Id.UndefinedLiteral => {
762 const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base);
763 try stream.print("{}", tree.tokenSlice(undefined_literal.token));
764 },
765 ast.Node.Id.BuiltinCall => {
766 const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base);
767 try stream.print("{}(", tree.tokenSlice(builtin_call.builtin_token));
768 try stack.append(RenderState { .Text = ")"});
769 var i = builtin_call.params.len;
770 while (i != 0) {
771 i -= 1;
772 const param_node = *builtin_call.params.at(i);
773 try stack.append(RenderState { .Expression = param_node});
774 if (i != 0) {
775 try stack.append(RenderState { .Text = ", " });
776 }
777 }
778 },
779 ast.Node.Id.FnProto => {
780 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);
781
782 switch (fn_proto.return_type) {
783 ast.Node.FnProto.ReturnType.Explicit => |node| {
784 try stack.append(RenderState { .Expression = node});
785 },
786 ast.Node.FnProto.ReturnType.InferErrorSet => |node| {
787 try stack.append(RenderState { .Expression = node});
788 try stack.append(RenderState { .Text = "!"});
789 },
790 }
791
792 if (fn_proto.align_expr) |align_expr| {
793 try stack.append(RenderState { .Text = ") " });
794 try stack.append(RenderState { .Expression = align_expr});
795 try stack.append(RenderState { .Text = "align(" });
796 }
797
798 try stack.append(RenderState { .Text = ") " });
799 var i = fn_proto.params.len;
800 while (i != 0) {
801 i -= 1;
802 const param_decl_node = *fn_proto.params.at(i);
803 try stack.append(RenderState { .ParamDecl = param_decl_node});
804 if (i != 0) {
805 try stack.append(RenderState { .Text = ", " });
806 }
807 }
808
809 try stack.append(RenderState { .Text = "(" });
810 if (fn_proto.name_token) |name_token| {
811 try stack.append(RenderState { .Text = tree.tokenSlice(name_token) });
812 try stack.append(RenderState { .Text = " " });
813 }
814
815 try stack.append(RenderState { .Text = "fn" });
816
817 if (fn_proto.async_attr) |async_attr| {
818 try stack.append(RenderState { .Text = " " });
819 try stack.append(RenderState { .Expression = &async_attr.base });
820 }
821
822 if (fn_proto.cc_token) |cc_token| {
823 try stack.append(RenderState { .Text = " " });
824 try stack.append(RenderState { .Text = tree.tokenSlice(cc_token) });
825 }
826
827 if (fn_proto.lib_name) |lib_name| {
828 try stack.append(RenderState { .Text = " " });
829 try stack.append(RenderState { .Expression = lib_name });
830 }
831 if (fn_proto.extern_export_inline_token) |extern_export_inline_token| {
832 try stack.append(RenderState { .Text = " " });
833 try stack.append(RenderState { .Text = tree.tokenSlice(extern_export_inline_token) });
834 }
835
836 if (fn_proto.visib_token) |visib_token_index| {
837 const visib_token = tree.tokens.at(visib_token_index);
838 assert(visib_token.id == Token.Id.Keyword_pub or visib_token.id == Token.Id.Keyword_export);
839 try stack.append(RenderState { .Text = " " });
840 try stack.append(RenderState { .Text = tree.tokenSlice(visib_token_index) });
841 }
842 },
843 ast.Node.Id.PromiseType => {
844 const promise_type = @fieldParentPtr(ast.Node.PromiseType, "base", base);
845 try stream.write(tree.tokenSlice(promise_type.promise_token));
846 if (promise_type.result) |result| {
847 try stream.write(tree.tokenSlice(result.arrow_token));
848 try stack.append(RenderState { .Expression = result.return_type});
849 }
850 },
851 ast.Node.Id.LineComment => {
852 const line_comment_node = @fieldParentPtr(ast.Node.LineComment, "base", base);
853 try stream.write(tree.tokenSlice(line_comment_node.token));
854 },
855 ast.Node.Id.DocComment => unreachable, // doc comments are attached to nodes
856 ast.Node.Id.Switch => {
857 const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);
858
859 try stream.print("{} (", tree.tokenSlice(switch_node.switch_token));
860
861 if (switch_node.cases.len == 0) {
862 try stack.append(RenderState { .Text = ") {}"});
863 try stack.append(RenderState { .Expression = switch_node.expr });
864 continue;
865 }
866
867 try stack.append(RenderState { .Text = "}"});
868 try stack.append(RenderState.PrintIndent);
869 try stack.append(RenderState { .Indent = indent });
870 try stack.append(RenderState { .Text = "\n"});
871
872 var i = switch_node.cases.len;
873 while (i != 0) {
874 i -= 1;
875 const node = *switch_node.cases.at(i);
876 try stack.append(RenderState { .Expression = node});
877 try stack.append(RenderState.PrintIndent);
878 try stack.append(RenderState {
879 .Text = blk: {
880 if (i != 0) {
881 const prev_node = *switch_node.cases.at(i - 1);
882 const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
883 const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
884 if (loc.line >= 2) {
885 break :blk "\n\n";
886 }
887 }
888 break :blk "\n";
889 },
890 });
891 }
892 try stack.append(RenderState { .Indent = indent + indent_delta});
893 try stack.append(RenderState { .Text = ") {"});
894 try stack.append(RenderState { .Expression = switch_node.expr });
895 },
896 ast.Node.Id.SwitchCase => {
897 const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
898
899 try stack.append(RenderState { .Token = switch_case.lastToken() + 1 });
900 try stack.append(RenderState { .Expression = switch_case.expr });
901 if (switch_case.payload) |payload| {
902 try stack.append(RenderState { .Text = " " });
903 try stack.append(RenderState { .Expression = payload });
904 }
905 try stack.append(RenderState { .Text = " => "});
906
907 var i = switch_case.items.len;
908 while (i != 0) {
909 i -= 1;
910 try stack.append(RenderState { .Expression = *switch_case.items.at(i) });
911
912 if (i != 0) {
913 try stack.append(RenderState.PrintIndent);
914 try stack.append(RenderState { .Text = ",\n" });
915 }
916 }
917 },
918 ast.Node.Id.SwitchElse => {
919 const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base);
920 try stream.print("{}", tree.tokenSlice(switch_else.token));
921 },
922 ast.Node.Id.Else => {
923 const else_node = @fieldParentPtr(ast.Node.Else, "base", base);
924 try stream.print("{}", tree.tokenSlice(else_node.else_token));
925
926 switch (else_node.body.id) {
927 ast.Node.Id.Block, ast.Node.Id.If,
928 ast.Node.Id.For, ast.Node.Id.While,
929 ast.Node.Id.Switch => {
930 try stream.print(" ");
931 try stack.append(RenderState { .Expression = else_node.body });
932 },
933 else => {
934 try stack.append(RenderState { .Indent = indent });
935 try stack.append(RenderState { .Expression = else_node.body });
936 try stack.append(RenderState.PrintIndent);
937 try stack.append(RenderState { .Indent = indent + indent_delta });
938 try stack.append(RenderState { .Text = "\n" });
939 }
940 }
941
942 if (else_node.payload) |payload| {
943 try stack.append(RenderState { .Text = " " });
944 try stack.append(RenderState { .Expression = payload });
945 }
946 },
947 ast.Node.Id.While => {
948 const while_node = @fieldParentPtr(ast.Node.While, "base", base);
949 if (while_node.label) |label| {
950 try stream.print("{}: ", tree.tokenSlice(label));
951 }
952
953 if (while_node.inline_token) |inline_token| {
954 try stream.print("{} ", tree.tokenSlice(inline_token));
955 }
956
957 try stream.print("{} ", tree.tokenSlice(while_node.while_token));
958
959 if (while_node.@"else") |@"else"| {
960 try stack.append(RenderState { .Expression = &@"else".base });
961
962 if (while_node.body.id == ast.Node.Id.Block) {
963 try stack.append(RenderState { .Text = " " });
964 } else {
965 try stack.append(RenderState.PrintIndent);
966 try stack.append(RenderState { .Text = "\n" });
967 }
968 }
969
970 if (while_node.body.id == ast.Node.Id.Block) {
971 try stack.append(RenderState { .Expression = while_node.body });
972 try stack.append(RenderState { .Text = " " });
973 } else {
974 try stack.append(RenderState { .Indent = indent });
975 try stack.append(RenderState { .Expression = while_node.body });
976 try stack.append(RenderState.PrintIndent);
977 try stack.append(RenderState { .Indent = indent + indent_delta });
978 try stack.append(RenderState { .Text = "\n" });
979 }
980
981 if (while_node.continue_expr) |continue_expr| {
982 try stack.append(RenderState { .Text = ")" });
983 try stack.append(RenderState { .Expression = continue_expr });
984 try stack.append(RenderState { .Text = ": (" });
985 try stack.append(RenderState { .Text = " " });
986 }
987
988 if (while_node.payload) |payload| {
989 try stack.append(RenderState { .Expression = payload });
990 try stack.append(RenderState { .Text = " " });
991 }
992
993 try stack.append(RenderState { .Text = ")" });
994 try stack.append(RenderState { .Expression = while_node.condition });
995 try stack.append(RenderState { .Text = "(" });
996 },
997 ast.Node.Id.For => {
998 const for_node = @fieldParentPtr(ast.Node.For, "base", base);
999 if (for_node.label) |label| {
1000 try stream.print("{}: ", tree.tokenSlice(label));
1001 }
1002
1003 if (for_node.inline_token) |inline_token| {
1004 try stream.print("{} ", tree.tokenSlice(inline_token));
1005 }
1006
1007 try stream.print("{} ", tree.tokenSlice(for_node.for_token));
1008
1009 if (for_node.@"else") |@"else"| {
1010 try stack.append(RenderState { .Expression = &@"else".base });
1011
1012 if (for_node.body.id == ast.Node.Id.Block) {
1013 try stack.append(RenderState { .Text = " " });
1014 } else {
1015 try stack.append(RenderState.PrintIndent);
1016 try stack.append(RenderState { .Text = "\n" });
1017 }
1018 }
1019
1020 if (for_node.body.id == ast.Node.Id.Block) {
1021 try stack.append(RenderState { .Expression = for_node.body });
1022 try stack.append(RenderState { .Text = " " });
1023 } else {
1024 try stack.append(RenderState { .Indent = indent });
1025 try stack.append(RenderState { .Expression = for_node.body });
1026 try stack.append(RenderState.PrintIndent);
1027 try stack.append(RenderState { .Indent = indent + indent_delta });
1028 try stack.append(RenderState { .Text = "\n" });
1029 }
1030
1031 if (for_node.payload) |payload| {
1032 try stack.append(RenderState { .Expression = payload });
1033 try stack.append(RenderState { .Text = " " });
1034 }
1035
1036 try stack.append(RenderState { .Text = ")" });
1037 try stack.append(RenderState { .Expression = for_node.array_expr });
1038 try stack.append(RenderState { .Text = "(" });
1039 },
1040 ast.Node.Id.If => {
1041 const if_node = @fieldParentPtr(ast.Node.If, "base", base);
1042 try stream.print("{} ", tree.tokenSlice(if_node.if_token));
1043
1044 switch (if_node.body.id) {
1045 ast.Node.Id.Block, ast.Node.Id.If,
1046 ast.Node.Id.For, ast.Node.Id.While,
1047 ast.Node.Id.Switch => {
1048 if (if_node.@"else") |@"else"| {
1049 try stack.append(RenderState { .Expression = &@"else".base });
1050
1051 if (if_node.body.id == ast.Node.Id.Block) {
1052 try stack.append(RenderState { .Text = " " });
1053 } else {
1054 try stack.append(RenderState.PrintIndent);
1055 try stack.append(RenderState { .Text = "\n" });
1056 }
1057 }
1058 },
1059 else => {
1060 if (if_node.@"else") |@"else"| {
1061 try stack.append(RenderState { .Expression = @"else".body });
1062
1063 if (@"else".payload) |payload| {
1064 try stack.append(RenderState { .Text = " " });
1065 try stack.append(RenderState { .Expression = payload });
1066 }
1067
1068 try stack.append(RenderState { .Text = " " });
1069 try stack.append(RenderState { .Text = tree.tokenSlice(@"else".else_token) });
1070 try stack.append(RenderState { .Text = " " });
1071 }
1072 }
1073 }
1074
1075 try stack.append(RenderState { .Expression = if_node.body });
1076
1077 if (if_node.payload) |payload| {
1078 try stack.append(RenderState { .Text = " " });
1079 try stack.append(RenderState { .Expression = payload });
1080 }
1081
1082 try stack.append(RenderState { .NonBreakToken = if_node.condition.lastToken() + 1 });
1083 try stack.append(RenderState { .Expression = if_node.condition });
1084 try stack.append(RenderState { .Text = "(" });
1085 },
1086 ast.Node.Id.Asm => {
1087 const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base);
1088 try stream.print("{} ", tree.tokenSlice(asm_node.asm_token));
1089
1090 if (asm_node.volatile_token) |volatile_token| {
1091 try stream.print("{} ", tree.tokenSlice(volatile_token));
1092 }
1093
1094 try stack.append(RenderState { .Indent = indent });
1095 try stack.append(RenderState { .Text = ")" });
1096 {
1097 var i = asm_node.clobbers.len;
1098 while (i != 0) {
1099 i -= 1;
1100 try stack.append(RenderState { .Expression = *asm_node.clobbers.at(i) });
1101
1102 if (i != 0) {
1103 try stack.append(RenderState { .Text = ", " });
1104 }
1105 }
1106 }
1107 try stack.append(RenderState { .Text = ": " });
1108 try stack.append(RenderState.PrintIndent);
1109 try stack.append(RenderState { .Indent = indent + indent_delta });
1110 try stack.append(RenderState { .Text = "\n" });
1111 {
1112 var i = asm_node.inputs.len;
1113 while (i != 0) {
1114 i -= 1;
1115 const node = *asm_node.inputs.at(i);
1116 try stack.append(RenderState { .Expression = &node.base});
1117
1118 if (i != 0) {
1119 try stack.append(RenderState.PrintIndent);
1120 try stack.append(RenderState {
1121 .Text = blk: {
1122 const prev_node = *asm_node.inputs.at(i - 1);
1123 const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
1124 const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
1125 if (loc.line >= 2) {
1126 break :blk "\n\n";
1127 }
1128 break :blk "\n";
1129 },
1130 });
1131 try stack.append(RenderState { .Text = "," });
1132 }
1133 }
1134 }
1135 try stack.append(RenderState { .Indent = indent + indent_delta + 2});
1136 try stack.append(RenderState { .Text = ": "});
1137 try stack.append(RenderState.PrintIndent);
1138 try stack.append(RenderState { .Indent = indent + indent_delta});
1139 try stack.append(RenderState { .Text = "\n" });
1140 {
1141 var i = asm_node.outputs.len;
1142 while (i != 0) {
1143 i -= 1;
1144 const node = *asm_node.outputs.at(i);
1145 try stack.append(RenderState { .Expression = &node.base});
1146
1147 if (i != 0) {
1148 try stack.append(RenderState.PrintIndent);
1149 try stack.append(RenderState {
1150 .Text = blk: {
1151 const prev_node = *asm_node.outputs.at(i - 1);
1152 const prev_node_last_token_end = tree.tokens.at(prev_node.lastToken()).end;
1153 const loc = tree.tokenLocation(prev_node_last_token_end, node.firstToken());
1154 if (loc.line >= 2) {
1155 break :blk "\n\n";
1156 }
1157 break :blk "\n";
1158 },
1159 });
1160 try stack.append(RenderState { .Text = "," });
1161 }
1162 }
1163 }
1164 try stack.append(RenderState { .Indent = indent + indent_delta + 2});
1165 try stack.append(RenderState { .Text = ": "});
1166 try stack.append(RenderState.PrintIndent);
1167 try stack.append(RenderState { .Indent = indent + indent_delta});
1168 try stack.append(RenderState { .Text = "\n" });
1169 try stack.append(RenderState { .Expression = asm_node.template });
1170 try stack.append(RenderState { .Text = "(" });
1171 },
1172 ast.Node.Id.AsmInput => {
1173 const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base);
1174
1175 try stack.append(RenderState { .Text = ")"});
1176 try stack.append(RenderState { .Expression = asm_input.expr});
1177 try stack.append(RenderState { .Text = " ("});
1178 try stack.append(RenderState { .Expression = asm_input.constraint });
1179 try stack.append(RenderState { .Text = "] "});
1180 try stack.append(RenderState { .Expression = asm_input.symbolic_name });
1181 try stack.append(RenderState { .Text = "["});
1182 },
1183 ast.Node.Id.AsmOutput => {
1184 const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base);
1185
1186 try stack.append(RenderState { .Text = ")"});
1187 switch (asm_output.kind) {
1188 ast.Node.AsmOutput.Kind.Variable => |variable_name| {
1189 try stack.append(RenderState { .Expression = &variable_name.base});
1190 },
1191 ast.Node.AsmOutput.Kind.Return => |return_type| {
1192 try stack.append(RenderState { .Expression = return_type});
1193 try stack.append(RenderState { .Text = "-> "});
1194 },
1195 }
1196 try stack.append(RenderState { .Text = " ("});
1197 try stack.append(RenderState { .Expression = asm_output.constraint });
1198 try stack.append(RenderState { .Text = "] "});
1199 try stack.append(RenderState { .Expression = asm_output.symbolic_name });
1200 try stack.append(RenderState { .Text = "["});
1201 },
1202
1203 ast.Node.Id.StructField,
1204 ast.Node.Id.UnionTag,
1205 ast.Node.Id.EnumTag,
1206 ast.Node.Id.ErrorTag,
1207 ast.Node.Id.Root,
1208 ast.Node.Id.VarDecl,
1209 ast.Node.Id.Use,
1210 ast.Node.Id.TestDecl,
1211 ast.Node.Id.ParamDecl => unreachable,
1212 },
1213 RenderState.Statement => |base| {
1214 switch (base.id) {
1215 ast.Node.Id.VarDecl => {
1216 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
1217 try stack.append(RenderState { .VarDecl = var_decl});
1218 },
1219 else => {
1220 try stack.append(RenderState { .MaybeSemiColon = base });
1221 try stack.append(RenderState { .Expression = base });
1222 },
1223 }
1224 },
1225 RenderState.Indent => |new_indent| indent = new_indent,
1226 RenderState.PrintIndent => try stream.writeByteNTimes(' ', indent),
1227 RenderState.Token => |token_index| try renderToken(tree, stream, token_index, indent, true),
1228 RenderState.NonBreakToken => |token_index| try renderToken(tree, stream, token_index, indent, false),
1229 RenderState.MaybeSemiColon => |base| {
1230 if (base.requireSemiColon()) {
1231 const semicolon_index = base.lastToken() + 1;
1232 assert(tree.tokens.at(semicolon_index).id == Token.Id.Semicolon);
1233 try renderToken(tree, stream, semicolon_index, indent, true);
1234 }
1235 },
1236 }
1237 }
1238}
1239
1240fn renderToken(tree: &ast.Tree, stream: var, token_index: ast.TokenIndex, indent: usize, line_break: bool) !void {
1241 const token = tree.tokens.at(token_index);
1242 try stream.write(tree.tokenSlicePtr(token));
1243
1244 const next_token = tree.tokens.at(token_index + 1);
1245 if (next_token.id == Token.Id.LineComment) {
1246 const loc = tree.tokenLocationPtr(token.end, next_token);
1247 if (loc.line == 0) {
1248 try stream.print(" {}", tree.tokenSlicePtr(next_token));
1249 if (!line_break) {
1250 try stream.write("\n");
1251 try stream.writeByteNTimes(' ', indent + indent_delta);
1252 return;
1253 }
1254 }
1255 }
1256
1257 if (!line_break) {
1258 try stream.writeByte(' ');
1259 }
1260}
1261
1262fn renderComments(tree: &ast.Tree, stream: var, node: var, indent: usize) !void {
1263 const comment = node.doc_comments ?? return;
1264 var it = comment.lines.iterator(0);
1265 while (it.next()) |line_token_index| {
1266 try stream.print("{}\n", tree.tokenSlice(*line_token_index));
1267 try stream.writeByteNTimes(' ', indent);
1268 }
1269}
1270
std/zig/tokenizer.zig+301-119
...@@ -5,56 +5,61 @@ pub const Token = struct {...@@ -5,56 +5,61 @@ pub const Token = struct {
5 id: Id,5 id: Id,
6 start: usize,6 start: usize,
7 end: usize,7 end: usize,
8 line: usize,
9 column: usize,
108
11 const KeywordId = struct {9 const Keyword = struct {
12 bytes: []const u8,10 bytes: []const u8,
13 id: Id,11 id: Id,
14 };12 };
1513
16 const keywords = []KeywordId {14 const keywords = []Keyword {
17 KeywordId{.bytes="align", .id = Id.Keyword_align},15 Keyword{.bytes="align", .id = Id.Keyword_align},
18 KeywordId{.bytes="and", .id = Id.Keyword_and},16 Keyword{.bytes="and", .id = Id.Keyword_and},
19 KeywordId{.bytes="asm", .id = Id.Keyword_asm},17 Keyword{.bytes="asm", .id = Id.Keyword_asm},
20 KeywordId{.bytes="break", .id = Id.Keyword_break},18 Keyword{.bytes="async", .id = Id.Keyword_async},
21 KeywordId{.bytes="catch", .id = Id.Keyword_catch},19 Keyword{.bytes="await", .id = Id.Keyword_await},
22 KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},20 Keyword{.bytes="break", .id = Id.Keyword_break},
23 KeywordId{.bytes="const", .id = Id.Keyword_const},21 Keyword{.bytes="catch", .id = Id.Keyword_catch},
24 KeywordId{.bytes="continue", .id = Id.Keyword_continue},22 Keyword{.bytes="cancel", .id = Id.Keyword_cancel},
25 KeywordId{.bytes="defer", .id = Id.Keyword_defer},23 Keyword{.bytes="comptime", .id = Id.Keyword_comptime},
26 KeywordId{.bytes="else", .id = Id.Keyword_else},24 Keyword{.bytes="const", .id = Id.Keyword_const},
27 KeywordId{.bytes="enum", .id = Id.Keyword_enum},25 Keyword{.bytes="continue", .id = Id.Keyword_continue},
28 KeywordId{.bytes="error", .id = Id.Keyword_error},26 Keyword{.bytes="defer", .id = Id.Keyword_defer},
29 KeywordId{.bytes="export", .id = Id.Keyword_export},27 Keyword{.bytes="else", .id = Id.Keyword_else},
30 KeywordId{.bytes="extern", .id = Id.Keyword_extern},28 Keyword{.bytes="enum", .id = Id.Keyword_enum},
31 KeywordId{.bytes="false", .id = Id.Keyword_false},29 Keyword{.bytes="errdefer", .id = Id.Keyword_errdefer},
32 KeywordId{.bytes="fn", .id = Id.Keyword_fn},30 Keyword{.bytes="error", .id = Id.Keyword_error},
33 KeywordId{.bytes="for", .id = Id.Keyword_for},31 Keyword{.bytes="export", .id = Id.Keyword_export},
34 KeywordId{.bytes="if", .id = Id.Keyword_if},32 Keyword{.bytes="extern", .id = Id.Keyword_extern},
35 KeywordId{.bytes="inline", .id = Id.Keyword_inline},33 Keyword{.bytes="false", .id = Id.Keyword_false},
36 KeywordId{.bytes="nakedcc", .id = Id.Keyword_nakedcc},34 Keyword{.bytes="fn", .id = Id.Keyword_fn},
37 KeywordId{.bytes="noalias", .id = Id.Keyword_noalias},35 Keyword{.bytes="for", .id = Id.Keyword_for},
38 KeywordId{.bytes="null", .id = Id.Keyword_null},36 Keyword{.bytes="if", .id = Id.Keyword_if},
39 KeywordId{.bytes="or", .id = Id.Keyword_or},37 Keyword{.bytes="inline", .id = Id.Keyword_inline},
40 KeywordId{.bytes="packed", .id = Id.Keyword_packed},38 Keyword{.bytes="nakedcc", .id = Id.Keyword_nakedcc},
41 KeywordId{.bytes="pub", .id = Id.Keyword_pub},39 Keyword{.bytes="noalias", .id = Id.Keyword_noalias},
42 KeywordId{.bytes="return", .id = Id.Keyword_return},40 Keyword{.bytes="null", .id = Id.Keyword_null},
43 KeywordId{.bytes="section", .id = Id.Keyword_section},41 Keyword{.bytes="or", .id = Id.Keyword_or},
44 KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},42 Keyword{.bytes="packed", .id = Id.Keyword_packed},
45 KeywordId{.bytes="struct", .id = Id.Keyword_struct},43 Keyword{.bytes="promise", .id = Id.Keyword_promise},
46 KeywordId{.bytes="switch", .id = Id.Keyword_switch},44 Keyword{.bytes="pub", .id = Id.Keyword_pub},
47 KeywordId{.bytes="test", .id = Id.Keyword_test},45 Keyword{.bytes="resume", .id = Id.Keyword_resume},
48 KeywordId{.bytes="this", .id = Id.Keyword_this},46 Keyword{.bytes="return", .id = Id.Keyword_return},
49 KeywordId{.bytes="true", .id = Id.Keyword_true},47 Keyword{.bytes="section", .id = Id.Keyword_section},
50 KeywordId{.bytes="try", .id = Id.Keyword_try},48 Keyword{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
51 KeywordId{.bytes="undefined", .id = Id.Keyword_undefined},49 Keyword{.bytes="struct", .id = Id.Keyword_struct},
52 KeywordId{.bytes="union", .id = Id.Keyword_union},50 Keyword{.bytes="suspend", .id = Id.Keyword_suspend},
53 KeywordId{.bytes="unreachable", .id = Id.Keyword_unreachable},51 Keyword{.bytes="switch", .id = Id.Keyword_switch},
54 KeywordId{.bytes="use", .id = Id.Keyword_use},52 Keyword{.bytes="test", .id = Id.Keyword_test},
55 KeywordId{.bytes="var", .id = Id.Keyword_var},53 Keyword{.bytes="this", .id = Id.Keyword_this},
56 KeywordId{.bytes="volatile", .id = Id.Keyword_volatile},54 Keyword{.bytes="true", .id = Id.Keyword_true},
57 KeywordId{.bytes="while", .id = Id.Keyword_while},55 Keyword{.bytes="try", .id = Id.Keyword_try},
56 Keyword{.bytes="undefined", .id = Id.Keyword_undefined},
57 Keyword{.bytes="union", .id = Id.Keyword_union},
58 Keyword{.bytes="unreachable", .id = Id.Keyword_unreachable},
59 Keyword{.bytes="use", .id = Id.Keyword_use},
60 Keyword{.bytes="var", .id = Id.Keyword_var},
61 Keyword{.bytes="volatile", .id = Id.Keyword_volatile},
62 Keyword{.bytes="while", .id = Id.Keyword_while},
58 };63 };
5964
60 fn getKeyword(bytes: []const u8) ?Id {65 fn getKeyword(bytes: []const u8) ?Id {
...@@ -72,7 +77,8 @@ pub const Token = struct {...@@ -72,7 +77,8 @@ pub const Token = struct {
72 Invalid,77 Invalid,
73 Identifier,78 Identifier,
74 StringLiteral: StrLitKind,79 StringLiteral: StrLitKind,
75 StringIdentifier,80 MultilineStringLiteralLine: StrLitKind,
81 CharLiteral,
76 Eof,82 Eof,
77 Builtin,83 Builtin,
78 Bang,84 Bang,
...@@ -81,6 +87,7 @@ pub const Token = struct {...@@ -81,6 +87,7 @@ pub const Token = struct {
81 PipeEqual,87 PipeEqual,
82 Equal,88 Equal,
83 EqualEqual,89 EqualEqual,
90 EqualAngleBracketRight,
84 BangEqual,91 BangEqual,
85 LParen,92 LParen,
86 RParen,93 RParen,
...@@ -89,6 +96,8 @@ pub const Token = struct {...@@ -89,6 +96,8 @@ pub const Token = struct {
89 PercentEqual,96 PercentEqual,
90 LBrace,97 LBrace,
91 RBrace,98 RBrace,
99 LBracket,
100 RBracket,
92 Period,101 Period,
93 Ellipsis2,102 Ellipsis2,
94 Ellipsis3,103 Ellipsis3,
...@@ -129,10 +138,14 @@ pub const Token = struct {...@@ -129,10 +138,14 @@ pub const Token = struct {
129 IntegerLiteral,138 IntegerLiteral,
130 FloatLiteral,139 FloatLiteral,
131 LineComment,140 LineComment,
141 DocComment,
132 Keyword_align,142 Keyword_align,
133 Keyword_and,143 Keyword_and,
134 Keyword_asm,144 Keyword_asm,
145 Keyword_async,
146 Keyword_await,
135 Keyword_break,147 Keyword_break,
148 Keyword_cancel,
136 Keyword_catch,149 Keyword_catch,
137 Keyword_comptime,150 Keyword_comptime,
138 Keyword_const,151 Keyword_const,
...@@ -140,6 +153,7 @@ pub const Token = struct {...@@ -140,6 +153,7 @@ pub const Token = struct {
140 Keyword_defer,153 Keyword_defer,
141 Keyword_else,154 Keyword_else,
142 Keyword_enum,155 Keyword_enum,
156 Keyword_errdefer,
143 Keyword_error,157 Keyword_error,
144 Keyword_export,158 Keyword_export,
145 Keyword_extern,159 Keyword_extern,
...@@ -153,11 +167,14 @@ pub const Token = struct {...@@ -153,11 +167,14 @@ pub const Token = struct {
153 Keyword_null,167 Keyword_null,
154 Keyword_or,168 Keyword_or,
155 Keyword_packed,169 Keyword_packed,
170 Keyword_promise,
156 Keyword_pub,171 Keyword_pub,
172 Keyword_resume,
157 Keyword_return,173 Keyword_return,
158 Keyword_section,174 Keyword_section,
159 Keyword_stdcallcc,175 Keyword_stdcallcc,
160 Keyword_struct,176 Keyword_struct,
177 Keyword_suspend,
161 Keyword_switch,178 Keyword_switch,
162 Keyword_test,179 Keyword_test,
163 Keyword_this,180 Keyword_this,
...@@ -176,33 +193,8 @@ pub const Token = struct {...@@ -176,33 +193,8 @@ pub const Token = struct {
176pub const Tokenizer = struct {193pub const Tokenizer = struct {
177 buffer: []const u8,194 buffer: []const u8,
178 index: usize,195 index: usize,
179 line: usize,
180 column: usize,
181 pending_invalid_token: ?Token,196 pending_invalid_token: ?Token,
182197
183 pub const LineLocation = struct {
184 line_start: usize,
185 line_end: usize,
186 };
187
188 pub fn getTokenLocation(self: &Tokenizer, token: &const Token) LineLocation {
189 var loc = LineLocation {
190 .line_start = 0,
191 .line_end = self.buffer.len,
192 };
193 for (self.buffer) |c, i| {
194 if (i == token.start) {
195 loc.line_end = i;
196 while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {}
197 return loc;
198 }
199 if (c == '\n') {
200 loc.line_start = i + 1;
201 }
202 }
203 return loc;
204 }
205
206 /// For debugging purposes198 /// For debugging purposes
207 pub fn dump(self: &Tokenizer, token: &const Token) void {199 pub fn dump(self: &Tokenizer, token: &const Token) void {
208 std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]);200 std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]);
...@@ -212,8 +204,6 @@ pub const Tokenizer = struct {...@@ -212,8 +204,6 @@ pub const Tokenizer = struct {
212 return Tokenizer {204 return Tokenizer {
213 .buffer = buffer,205 .buffer = buffer,
214 .index = 0,206 .index = 0,
215 .line = 0,
216 .column = 0,
217 .pending_invalid_token = null,207 .pending_invalid_token = null,
218 };208 };
219 }209 }
...@@ -225,6 +215,12 @@ pub const Tokenizer = struct {...@@ -225,6 +215,12 @@ pub const Tokenizer = struct {
225 C,215 C,
226 StringLiteral,216 StringLiteral,
227 StringLiteralBackslash,217 StringLiteralBackslash,
218 MultilineStringLiteralLine,
219 MultilineStringLiteralLineBackslash,
220 CharLiteral,
221 CharLiteralBackslash,
222 CharLiteralEnd,
223 Backslash,
228 Equal,224 Equal,
229 Bang,225 Bang,
230 Pipe,226 Pipe,
...@@ -233,7 +229,10 @@ pub const Tokenizer = struct {...@@ -233,7 +229,10 @@ pub const Tokenizer = struct {
233 Asterisk,229 Asterisk,
234 AsteriskPercent,230 AsteriskPercent,
235 Slash,231 Slash,
232 LineCommentStart,
236 LineComment,233 LineComment,
234 DocCommentStart,
235 DocComment,
237 Zero,236 Zero,
238 IntegerLiteral,237 IntegerLiteral,
239 IntegerLiteralWithRadix,238 IntegerLiteralWithRadix,
...@@ -261,26 +260,22 @@ pub const Tokenizer = struct {...@@ -261,26 +260,22 @@ pub const Tokenizer = struct {
261 self.pending_invalid_token = null;260 self.pending_invalid_token = null;
262 return token;261 return token;
263 }262 }
263 const start_index = self.index;
264 var state = State.Start;264 var state = State.Start;
265 var result = Token {265 var result = Token {
266 .id = Token.Id.Eof,266 .id = Token.Id.Eof,
267 .start = self.index,267 .start = self.index,
268 .end = undefined,268 .end = undefined,
269 .line = self.line,
270 .column = self.column,
271 };269 };
272 while (self.index < self.buffer.len) {270 while (self.index < self.buffer.len) : (self.index += 1) {
273 const c = self.buffer[self.index];271 const c = self.buffer[self.index];
274 switch (state) {272 switch (state) {
275 State.Start => switch (c) {273 State.Start => switch (c) {
276 ' ' => {274 ' ' => {
277 result.start = self.index + 1;275 result.start = self.index + 1;
278 result.column += 1;
279 },276 },
280 '\n' => {277 '\n' => {
281 result.start = self.index + 1;278 result.start = self.index + 1;
282 result.line += 1;
283 result.column = 0;
284 },279 },
285 'c' => {280 'c' => {
286 state = State.C;281 state = State.C;
...@@ -290,6 +285,9 @@ pub const Tokenizer = struct {...@@ -290,6 +285,9 @@ pub const Tokenizer = struct {
290 state = State.StringLiteral;285 state = State.StringLiteral;
291 result.id = Token.Id { .StringLiteral = Token.StrLitKind.Normal };286 result.id = Token.Id { .StringLiteral = Token.StrLitKind.Normal };
292 },287 },
288 '\'' => {
289 state = State.CharLiteral;
290 },
293 'a'...'b', 'd'...'z', 'A'...'Z', '_' => {291 'a'...'b', 'd'...'z', 'A'...'Z', '_' => {
294 state = State.Identifier;292 state = State.Identifier;
295 result.id = Token.Id.Identifier;293 result.id = Token.Id.Identifier;
...@@ -316,6 +314,16 @@ pub const Tokenizer = struct {...@@ -316,6 +314,16 @@ pub const Tokenizer = struct {
316 self.index += 1;314 self.index += 1;
317 break;315 break;
318 },316 },
317 '[' => {
318 result.id = Token.Id.LBracket;
319 self.index += 1;
320 break;
321 },
322 ']' => {
323 result.id = Token.Id.RBracket;
324 self.index += 1;
325 break;
326 },
319 ';' => {327 ';' => {
320 result.id = Token.Id.Semicolon;328 result.id = Token.Id.Semicolon;
321 self.index += 1;329 self.index += 1;
...@@ -352,6 +360,10 @@ pub const Tokenizer = struct {...@@ -352,6 +360,10 @@ pub const Tokenizer = struct {
352 '^' => {360 '^' => {
353 state = State.Caret;361 state = State.Caret;
354 },362 },
363 '\\' => {
364 state = State.Backslash;
365 result.id = Token.Id { .MultilineStringLiteralLine = Token.StrLitKind.Normal };
366 },
355 '{' => {367 '{' => {
356 result.id = Token.Id.LBrace;368 result.id = Token.Id.LBrace;
357 self.index += 1;369 self.index += 1;
...@@ -396,7 +408,7 @@ pub const Tokenizer = struct {...@@ -396,7 +408,7 @@ pub const Tokenizer = struct {
396408
397 State.SawAtSign => switch (c) {409 State.SawAtSign => switch (c) {
398 '"' => {410 '"' => {
399 result.id = Token.Id.StringIdentifier;411 result.id = Token.Id.Identifier;
400 state = State.StringLiteral;412 state = State.StringLiteral;
401 },413 },
402 else => {414 else => {
...@@ -532,8 +544,17 @@ pub const Tokenizer = struct {...@@ -532,8 +544,17 @@ pub const Tokenizer = struct {
532 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},544 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
533 else => break,545 else => break,
534 },546 },
547 State.Backslash => switch (c) {
548 '\\' => {
549 state = State.MultilineStringLiteralLine;
550 },
551 else => break,
552 },
535 State.C => switch (c) {553 State.C => switch (c) {
536 '\\' => @panic("TODO"),554 '\\' => {
555 state = State.Backslash;
556 result.id = Token.Id { .MultilineStringLiteralLine = Token.StrLitKind.C };
557 },
537 '"' => {558 '"' => {
538 state = State.StringLiteral;559 state = State.StringLiteral;
539 result.id = Token.Id { .StringLiteral = Token.StrLitKind.C };560 result.id = Token.Id { .StringLiteral = Token.StrLitKind.C };
...@@ -562,6 +583,64 @@ pub const Tokenizer = struct {...@@ -562,6 +583,64 @@ pub const Tokenizer = struct {
562 },583 },
563 },584 },
564585
586 State.CharLiteral => switch (c) {
587 '\\' => {
588 state = State.CharLiteralBackslash;
589 },
590 '\'' => {
591 result.id = Token.Id.Invalid;
592 break;
593 },
594 else => {
595 if (c < 0x20 or c == 0x7f) {
596 result.id = Token.Id.Invalid;
597 break;
598 }
599
600 state = State.CharLiteralEnd;
601 }
602 },
603
604 State.CharLiteralBackslash => switch (c) {
605 '\n' => {
606 result.id = Token.Id.Invalid;
607 break;
608 },
609 else => {
610 state = State.CharLiteralEnd;
611 },
612 },
613
614 State.CharLiteralEnd => switch (c) {
615 '\'' => {
616 result.id = Token.Id.CharLiteral;
617 self.index += 1;
618 break;
619 },
620 else => {
621 result.id = Token.Id.Invalid;
622 break;
623 },
624 },
625
626 State.MultilineStringLiteralLine => switch (c) {
627 '\\' => {
628 state = State.MultilineStringLiteralLineBackslash;
629 },
630 '\n' => {
631 self.index += 1;
632 break;
633 },
634 else => self.checkLiteralCharacter(),
635 },
636
637 State.MultilineStringLiteralLineBackslash => switch (c) {
638 '\n' => break, // Look for this error later.
639 else => {
640 state = State.MultilineStringLiteralLine;
641 },
642 },
643
565 State.Bang => switch (c) {644 State.Bang => switch (c) {
566 '=' => {645 '=' => {
567 result.id = Token.Id.BangEqual;646 result.id = Token.Id.BangEqual;
...@@ -597,6 +676,11 @@ pub const Tokenizer = struct {...@@ -597,6 +676,11 @@ pub const Tokenizer = struct {
597 self.index += 1;676 self.index += 1;
598 break;677 break;
599 },678 },
679 '>' => {
680 result.id = Token.Id.EqualAngleBracketRight;
681 self.index += 1;
682 break;
683 },
600 else => {684 else => {
601 result.id = Token.Id.Equal;685 result.id = Token.Id.Equal;
602 break;686 break;
...@@ -713,8 +797,8 @@ pub const Tokenizer = struct {...@@ -713,8 +797,8 @@ pub const Tokenizer = struct {
713797
714 State.Slash => switch (c) {798 State.Slash => switch (c) {
715 '/' => {799 '/' => {
800 state = State.LineCommentStart;
716 result.id = Token.Id.LineComment;801 result.id = Token.Id.LineComment;
717 state = State.LineComment;
718 },802 },
719 '=' => {803 '=' => {
720 result.id = Token.Id.SlashEqual;804 result.id = Token.Id.SlashEqual;
...@@ -726,7 +810,31 @@ pub const Tokenizer = struct {...@@ -726,7 +810,31 @@ pub const Tokenizer = struct {
726 break;810 break;
727 },811 },
728 },812 },
729 State.LineComment => switch (c) {813 State.LineCommentStart => switch (c) {
814 '/' => {
815 state = State.DocCommentStart;
816 },
817 '\n' => break,
818 else => {
819 state = State.LineComment;
820 self.checkLiteralCharacter();
821 },
822 },
823 State.DocCommentStart => switch (c) {
824 '/' => {
825 state = State.LineComment;
826 },
827 '\n' => {
828 result.id = Token.Id.DocComment;
829 break;
830 },
831 else => {
832 state = State.DocComment;
833 result.id = Token.Id.DocComment;
834 self.checkLiteralCharacter();
835 },
836 },
837 State.LineComment, State.DocComment => switch (c) {
730 '\n' => break,838 '\n' => break,
731 else => self.checkLiteralCharacter(),839 else => self.checkLiteralCharacter(),
732 },840 },
...@@ -773,10 +881,10 @@ pub const Tokenizer = struct {...@@ -773,10 +881,10 @@ pub const Tokenizer = struct {
773 },881 },
774 },882 },
775 State.FloatFraction => switch (c) {883 State.FloatFraction => switch (c) {
776 'p', 'P' => {884 'p', 'P', 'e', 'E' => {
777 state = State.FloatExponentUnsigned;885 state = State.FloatExponentUnsigned;
778 },886 },
779 '0'...'9', 'a'...'f', 'A'...'F' => {},887 '0'...'9' => {},
780 else => break,888 else => break,
781 },889 },
782 State.FloatExponentUnsigned => switch (c) {890 State.FloatExponentUnsigned => switch (c) {
...@@ -794,14 +902,6 @@ pub const Tokenizer = struct {...@@ -794,14 +902,6 @@ pub const Tokenizer = struct {
794 else => break,902 else => break,
795 },903 },
796 }904 }
797
798 self.index += 1;
799 if (c == '\n') {
800 self.line += 1;
801 self.column = 0;
802 } else {
803 self.column += 1;
804 }
805 } else if (self.index == self.buffer.len) {905 } else if (self.index == self.buffer.len) {
806 switch (state) {906 switch (state) {
807 State.Start,907 State.Start,
...@@ -811,6 +911,7 @@ pub const Tokenizer = struct {...@@ -811,6 +911,7 @@ pub const Tokenizer = struct {
811 State.FloatFraction,911 State.FloatFraction,
812 State.FloatExponentNumber,912 State.FloatExponentNumber,
813 State.StringLiteral, // find this error later913 State.StringLiteral, // find this error later
914 State.MultilineStringLiteralLine,
814 State.Builtin => {},915 State.Builtin => {},
815916
816 State.Identifier => {917 State.Identifier => {
...@@ -818,13 +919,22 @@ pub const Tokenizer = struct {...@@ -818,13 +919,22 @@ pub const Tokenizer = struct {
818 result.id = id;919 result.id = id;
819 }920 }
820 },921 },
922 State.LineCommentStart,
821 State.LineComment => {923 State.LineComment => {
822 result.id = Token.Id.Eof;924 result.id = Token.Id.LineComment;
925 },
926 State.DocComment, State.DocCommentStart => {
927 result.id = Token.Id.DocComment;
823 },928 },
824929
825 State.NumberDot,930 State.NumberDot,
826 State.FloatExponentUnsigned,931 State.FloatExponentUnsigned,
827 State.SawAtSign,932 State.SawAtSign,
933 State.Backslash,
934 State.MultilineStringLiteralLineBackslash,
935 State.CharLiteral,
936 State.CharLiteralBackslash,
937 State.CharLiteralEnd,
828 State.StringLiteralBackslash => {938 State.StringLiteralBackslash => {
829 result.id = Token.Id.Invalid;939 result.id = Token.Id.Invalid;
830 },940 },
...@@ -894,6 +1004,7 @@ pub const Tokenizer = struct {...@@ -894,6 +1004,7 @@ pub const Tokenizer = struct {
894 },1004 },
895 }1005 }
896 }1006 }
1007
897 if (result.id == Token.Id.Eof) {1008 if (result.id == Token.Id.Eof) {
898 if (self.pending_invalid_token) |token| {1009 if (self.pending_invalid_token) |token| {
899 self.pending_invalid_token = null;1010 self.pending_invalid_token = null;
...@@ -905,10 +1016,6 @@ pub const Tokenizer = struct {...@@ -905,10 +1016,6 @@ pub const Tokenizer = struct {
905 return result;1016 return result;
906 }1017 }
9071018
908 pub fn getTokenSlice(self: &const Tokenizer, token: &const Token) []const u8 {
909 return self.buffer[token.start..token.end];
910 }
911
912 fn checkLiteralCharacter(self: &Tokenizer) void {1019 fn checkLiteralCharacter(self: &Tokenizer) void {
913 if (self.pending_invalid_token != null) return;1020 if (self.pending_invalid_token != null) return;
914 const invalid_length = self.getInvalidCharacterLength();1021 const invalid_length = self.getInvalidCharacterLength();
...@@ -917,8 +1024,6 @@ pub const Tokenizer = struct {...@@ -917,8 +1024,6 @@ pub const Tokenizer = struct {
917 .id = Token.Id.Invalid,1024 .id = Token.Id.Invalid,
918 .start = self.index,1025 .start = self.index,
919 .end = self.index + invalid_length,1026 .end = self.index + invalid_length,
920 .line = self.line,
921 .column = self.column,
922 };1027 };
923 }1028 }
9241029
...@@ -968,9 +1073,25 @@ test "tokenizer" {...@@ -968,9 +1073,25 @@ test "tokenizer" {
968 });1073 });
969}1074}
9701075
1076test "tokenizer - float literal" {
1077 testTokenize("a = 4.94065645841246544177e-324;\n", []Token.Id {
1078 Token.Id.Identifier,
1079 Token.Id.Equal,
1080 Token.Id.FloatLiteral,
1081 Token.Id.Semicolon,
1082 });
1083}
1084
1085test "tokenizer - chars" {
1086 testTokenize("'c'", []Token.Id {Token.Id.CharLiteral});
1087}
1088
971test "tokenizer - invalid token characters" {1089test "tokenizer - invalid token characters" {
972 testTokenize("#", []Token.Id{Token.Id.Invalid});1090 testTokenize("#", []Token.Id{Token.Id.Invalid});
973 testTokenize("`", []Token.Id{Token.Id.Invalid});1091 testTokenize("`", []Token.Id{Token.Id.Invalid});
1092 testTokenize("'c", []Token.Id {Token.Id.Invalid});
1093 testTokenize("'", []Token.Id {Token.Id.Invalid});
1094 testTokenize("''", []Token.Id {Token.Id.Invalid, Token.Id.Invalid});
974}1095}
9751096
976test "tokenizer - invalid literal/comment characters" {1097test "tokenizer - invalid literal/comment characters" {
...@@ -979,41 +1100,77 @@ test "tokenizer - invalid literal/comment characters" {...@@ -979,41 +1100,77 @@ test "tokenizer - invalid literal/comment characters" {
979 Token.Id.Invalid,1100 Token.Id.Invalid,
980 });1101 });
981 testTokenize("//\x00", []Token.Id {1102 testTokenize("//\x00", []Token.Id {
1103 Token.Id.LineComment,
982 Token.Id.Invalid,1104 Token.Id.Invalid,
983 });1105 });
984 testTokenize("//\x1f", []Token.Id {1106 testTokenize("//\x1f", []Token.Id {
1107 Token.Id.LineComment,
985 Token.Id.Invalid,1108 Token.Id.Invalid,
986 });1109 });
987 testTokenize("//\x7f", []Token.Id {1110 testTokenize("//\x7f", []Token.Id {
1111 Token.Id.LineComment,
988 Token.Id.Invalid,1112 Token.Id.Invalid,
989 });1113 });
990}1114}
9911115
992test "tokenizer - utf8" {1116test "tokenizer - utf8" {
993 testTokenize("//\xc2\x80", []Token.Id{});1117 testTokenize("//\xc2\x80", []Token.Id{Token.Id.LineComment});
994 testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{});1118 testTokenize("//\xf4\x8f\xbf\xbf", []Token.Id{Token.Id.LineComment});
995}1119}
9961120
997test "tokenizer - invalid utf8" {1121test "tokenizer - invalid utf8" {
998 testTokenize("//\x80", []Token.Id{Token.Id.Invalid});1122 testTokenize("//\x80", []Token.Id{
999 testTokenize("//\xbf", []Token.Id{Token.Id.Invalid});1123 Token.Id.LineComment,
1000 testTokenize("//\xf8", []Token.Id{Token.Id.Invalid});1124 Token.Id.Invalid,
1001 testTokenize("//\xff", []Token.Id{Token.Id.Invalid});1125 });
1002 testTokenize("//\xc2\xc0", []Token.Id{Token.Id.Invalid});1126 testTokenize("//\xbf", []Token.Id{
1003 testTokenize("//\xe0", []Token.Id{Token.Id.Invalid});1127 Token.Id.LineComment,
1004 testTokenize("//\xf0", []Token.Id{Token.Id.Invalid});1128 Token.Id.Invalid,
1005 testTokenize("//\xf0\x90\x80\xc0", []Token.Id{Token.Id.Invalid});1129 });
1130 testTokenize("//\xf8", []Token.Id{
1131 Token.Id.LineComment,
1132 Token.Id.Invalid,
1133 });
1134 testTokenize("//\xff", []Token.Id{
1135 Token.Id.LineComment,
1136 Token.Id.Invalid,
1137 });
1138 testTokenize("//\xc2\xc0", []Token.Id{
1139 Token.Id.LineComment,
1140 Token.Id.Invalid,
1141 });
1142 testTokenize("//\xe0", []Token.Id{
1143 Token.Id.LineComment,
1144 Token.Id.Invalid,
1145 });
1146 testTokenize("//\xf0", []Token.Id{
1147 Token.Id.LineComment,
1148 Token.Id.Invalid,
1149 });
1150 testTokenize("//\xf0\x90\x80\xc0", []Token.Id{
1151 Token.Id.LineComment,
1152 Token.Id.Invalid,
1153 });
1006}1154}
10071155
1008test "tokenizer - illegal unicode codepoints" {1156test "tokenizer - illegal unicode codepoints" {
1009 // unicode newline characters.U+0085, U+2028, U+20291157 // unicode newline characters.U+0085, U+2028, U+2029
1010 testTokenize("//\xc2\x84", []Token.Id{});1158 testTokenize("//\xc2\x84", []Token.Id{Token.Id.LineComment});
1011 testTokenize("//\xc2\x85", []Token.Id{Token.Id.Invalid});1159 testTokenize("//\xc2\x85", []Token.Id{
1012 testTokenize("//\xc2\x86", []Token.Id{});1160 Token.Id.LineComment,
1013 testTokenize("//\xe2\x80\xa7", []Token.Id{});1161 Token.Id.Invalid,
1014 testTokenize("//\xe2\x80\xa8", []Token.Id{Token.Id.Invalid});1162 });
1015 testTokenize("//\xe2\x80\xa9", []Token.Id{Token.Id.Invalid});1163 testTokenize("//\xc2\x86", []Token.Id{Token.Id.LineComment});
1016 testTokenize("//\xe2\x80\xaa", []Token.Id{});1164 testTokenize("//\xe2\x80\xa7", []Token.Id{Token.Id.LineComment});
1165 testTokenize("//\xe2\x80\xa8", []Token.Id{
1166 Token.Id.LineComment,
1167 Token.Id.Invalid,
1168 });
1169 testTokenize("//\xe2\x80\xa9", []Token.Id{
1170 Token.Id.LineComment,
1171 Token.Id.Invalid,
1172 });
1173 testTokenize("//\xe2\x80\xaa", []Token.Id{Token.Id.LineComment});
1017}1174}
10181175
1019test "tokenizer - string identifier and builtin fns" {1176test "tokenizer - string identifier and builtin fns" {
...@@ -1022,7 +1179,7 @@ test "tokenizer - string identifier and builtin fns" {...@@ -1022,7 +1179,7 @@ test "tokenizer - string identifier and builtin fns" {
1022 ,1179 ,
1023 []Token.Id{1180 []Token.Id{
1024 Token.Id.Keyword_const,1181 Token.Id.Keyword_const,
1025 Token.Id.StringIdentifier,1182 Token.Id.Identifier,
1026 Token.Id.Equal,1183 Token.Id.Equal,
1027 Token.Id.Builtin,1184 Token.Id.Builtin,
1028 Token.Id.LParen,1185 Token.Id.LParen,
...@@ -1040,11 +1197,36 @@ test "tokenizer - pipe and then invalid" {...@@ -1040,11 +1197,36 @@ test "tokenizer - pipe and then invalid" {
1040 });1197 });
1041}1198}
10421199
1200test "tokenizer - line comment and doc comment" {
1201 testTokenize("//", []Token.Id{Token.Id.LineComment});
1202 testTokenize("// a / b", []Token.Id{Token.Id.LineComment});
1203 testTokenize("// /", []Token.Id{Token.Id.LineComment});
1204 testTokenize("/// a", []Token.Id{Token.Id.DocComment});
1205 testTokenize("///", []Token.Id{Token.Id.DocComment});
1206 testTokenize("////", []Token.Id{Token.Id.LineComment});
1207}
1208
1209test "tokenizer - line comment followed by identifier" {
1210 testTokenize(
1211 \\ Unexpected,
1212 \\ // another
1213 \\ Another,
1214 , []Token.Id{
1215 Token.Id.Identifier,
1216 Token.Id.Comma,
1217 Token.Id.LineComment,
1218 Token.Id.Identifier,
1219 Token.Id.Comma,
1220 });
1221}
1222
1043fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {1223fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {
1044 var tokenizer = Tokenizer.init(source);1224 var tokenizer = Tokenizer.init(source);
1045 for (expected_tokens) |expected_token_id| {1225 for (expected_tokens) |expected_token_id| {
1046 const token = tokenizer.next();1226 const token = tokenizer.next();
1047 std.debug.assert(@TagType(Token.Id)(token.id) == @TagType(Token.Id)(expected_token_id));1227 if (@TagType(Token.Id)(token.id) != @TagType(Token.Id)(expected_token_id)) {
1228 std.debug.panic("expected {}, found {}\n", @tagName(@TagType(Token.Id)(expected_token_id)), @tagName(@TagType(Token.Id)(token.id)));
1229 }
1048 switch (expected_token_id) {1230 switch (expected_token_id) {
1049 Token.Id.StringLiteral => |expected_kind| {1231 Token.Id.StringLiteral => |expected_kind| {
1050 std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable });1232 std.debug.assert(expected_kind == switch (token.id) { Token.Id.StringLiteral => |kind| kind, else => unreachable });
test/behavior.zig+3
...@@ -12,6 +12,7 @@ comptime {...@@ -12,6 +12,7 @@ comptime {
12 _ = @import("cases/bugs/655.zig");12 _ = @import("cases/bugs/655.zig");
13 _ = @import("cases/bugs/656.zig");13 _ = @import("cases/bugs/656.zig");
14 _ = @import("cases/bugs/828.zig");14 _ = @import("cases/bugs/828.zig");
15 _ = @import("cases/bugs/920.zig");
15 _ = @import("cases/cast.zig");16 _ = @import("cases/cast.zig");
16 _ = @import("cases/const_slice_child.zig");17 _ = @import("cases/const_slice_child.zig");
17 _ = @import("cases/coroutines.zig");18 _ = @import("cases/coroutines.zig");
...@@ -35,6 +36,7 @@ comptime {...@@ -35,6 +36,7 @@ comptime {
35 _ = @import("cases/pub_enum/index.zig");36 _ = @import("cases/pub_enum/index.zig");
36 _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig");37 _ = @import("cases/ref_var_in_if_after_if_2nd_switch_prong.zig");
37 _ = @import("cases/reflection.zig");38 _ = @import("cases/reflection.zig");
39 _ = @import("cases/type_info.zig");
38 _ = @import("cases/sizeof_and_typeof.zig");40 _ = @import("cases/sizeof_and_typeof.zig");
39 _ = @import("cases/slice.zig");41 _ = @import("cases/slice.zig");
40 _ = @import("cases/struct.zig");42 _ = @import("cases/struct.zig");
...@@ -51,4 +53,5 @@ comptime {...@@ -51,4 +53,5 @@ comptime {
51 _ = @import("cases/var_args.zig");53 _ = @import("cases/var_args.zig");
52 _ = @import("cases/void.zig");54 _ = @import("cases/void.zig");
53 _ = @import("cases/while.zig");55 _ = @import("cases/while.zig");
56 _ = @import("cases/fn_in_struct_in_comptime.zig");
54}57}
test/cases/atomics.zig+47-3
...@@ -1,12 +1,24 @@...@@ -1,12 +1,24 @@
1const assert = @import("std").debug.assert;1const std = @import("std");
2const assert = std.debug.assert;
2const builtin = @import("builtin");3const builtin = @import("builtin");
3const AtomicRmwOp = builtin.AtomicRmwOp;4const AtomicRmwOp = builtin.AtomicRmwOp;
4const AtomicOrder = builtin.AtomicOrder;5const AtomicOrder = builtin.AtomicOrder;
56
6test "cmpxchg" {7test "cmpxchg" {
7 var x: i32 = 1234;8 var x: i32 = 1234;
8 while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {}9 if (@cmpxchgWeak(i32, &x, 99, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
10 assert(x1 == 1234);
11 } else {
12 @panic("cmpxchg should have failed");
13 }
14
15 while (@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
16 assert(x1 == 1234);
17 }
9 assert(x == 5678);18 assert(x == 5678);
19
20 assert(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null);
21 assert(x == 42);
10}22}
1123
12test "fence" {24test "fence" {
...@@ -15,13 +27,45 @@ test "fence" {...@@ -15,13 +27,45 @@ test "fence" {
15 x = 5678;27 x = 5678;
16}28}
1729
18test "atomicrmw" {30test "atomicrmw and atomicload" {
19 var data: u8 = 200;31 var data: u8 = 200;
20 testAtomicRmw(&data);32 testAtomicRmw(&data);
21 assert(data == 42);33 assert(data == 42);
34 testAtomicLoad(&data);
22}35}
2336
24fn testAtomicRmw(ptr: &u8) void {37fn testAtomicRmw(ptr: &u8) void {
25 const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst);38 const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst);
26 assert(prev_value == 200);39 assert(prev_value == 200);
40 comptime {
41 var x: i32 = 1234;
42 const y: i32 = 12345;
43 assert(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234);
44 assert(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345);
45 }
46}
47
48fn testAtomicLoad(ptr: &u8) void {
49 const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst);
50 assert(x == 42);
51}
52
53test "cmpxchg with ptr" {
54 var data1: i32 = 1234;
55 var data2: i32 = 5678;
56 var data3: i32 = 9101;
57 var x: &i32 = &data1;
58 if (@cmpxchgWeak(&i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
59 assert(x1 == &data1);
60 } else {
61 @panic("cmpxchg should have failed");
62 }
63
64 while (@cmpxchgWeak(&i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
65 assert(x1 == &data1);
66 }
67 assert(x == &data3);
68
69 assert(@cmpxchgStrong(&i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null);
70 assert(x == &data2);
27}71}
test/cases/bugs/920.zig created+60
...@@ -0,0 +1,60 @@
1const std = @import("std");
2const math = std.math;
3const Random = std.rand.Random;
4
5const ZigTable = struct {
6 r: f64,
7 x: [257]f64,
8 f: [257]f64,
9
10 pdf: fn(f64) f64,
11 is_symmetric: bool,
12 zero_case: fn(&Random, f64) f64,
13};
14
15fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64,
16 comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable {
17 var tables: ZigTable = undefined;
18
19 tables.is_symmetric = is_symmetric;
20 tables.r = r;
21 tables.pdf = f;
22 tables.zero_case = zero_case;
23
24 tables.x[0] = v / f(r);
25 tables.x[1] = r;
26
27 for (tables.x[2..256]) |*entry, i| {
28 const last = tables.x[2 + i - 1];
29 *entry = f_inv(v / last + f(last));
30 }
31 tables.x[256] = 0;
32
33 for (tables.f[0..]) |*entry, i| {
34 *entry = f(tables.x[i]);
35 }
36
37 return tables;
38}
39
40const norm_r = 3.6541528853610088;
41const norm_v = 0.00492867323399;
42
43fn norm_f(x: f64) f64 { return math.exp(-x * x / 2.0); }
44fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); }
45fn norm_zero_case(random: &Random, u: f64) f64 { return 0.0; }
46
47const NormalDist = blk: {
48 @setEvalBranchQuota(30000);
49 break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case);
50};
51
52test "bug 920 fixed" {
53 const NormalDist1 = blk: {
54 break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case);
55 };
56
57 for (NormalDist1.f) |_, i| {
58 std.debug.assert(NormalDist1.f[i] == NormalDist.f[i]);
59 }
60}
test/cases/coroutines.zig+54
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("std");1const std = @import("std");
2const builtin = @import("builtin");
2const assert = std.debug.assert;3const assert = std.debug.assert;
34
4var x: i32 = 1;5var x: i32 = 1;
...@@ -189,3 +190,56 @@ async fn failing() !void {...@@ -189,3 +190,56 @@ async fn failing() !void {
189 suspend;190 suspend;
190 return error.Fail;191 return error.Fail;
191}192}
193
194test "error return trace across suspend points - early return" {
195 const p = nonFailing();
196 resume p;
197 const p2 = try async<std.debug.global_allocator> printTrace(p);
198 cancel p2;
199}
200
201test "error return trace across suspend points - async return" {
202 const p = nonFailing();
203 const p2 = try async<std.debug.global_allocator> printTrace(p);
204 resume p;
205 cancel p2;
206}
207
208fn nonFailing() promise->error!void {
209 return async<std.debug.global_allocator> suspendThenFail() catch unreachable;
210}
211
212async fn suspendThenFail() error!void {
213 suspend;
214 return error.Fail;
215}
216
217async fn printTrace(p: promise->error!void) void {
218 (await p) catch |e| {
219 std.debug.assert(e == error.Fail);
220 if (@errorReturnTrace()) |trace| {
221 assert(trace.index == 1);
222 } else switch (builtin.mode) {
223 builtin.Mode.Debug, builtin.Mode.ReleaseSafe => @panic("expected return trace"),
224 builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {},
225 }
226 };
227}
228
229test "break from suspend" {
230 var buf: [500]u8 = undefined;
231 var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
232 var my_result: i32 = 1;
233 const p = try async<a> testBreakFromSuspend(&my_result);
234 cancel p;
235 std.debug.assert(my_result == 2);
236}
237
238async fn testBreakFromSuspend(my_result: &i32) void {
239 s: suspend |p| {
240 break :s;
241 }
242 *my_result += 1;
243 suspend;
244 *my_result += 1;
245}
test/cases/defer.zig+11
...@@ -41,3 +41,14 @@ fn testBreakContInDefer(x: usize) void {...@@ -41,3 +41,14 @@ fn testBreakContInDefer(x: usize) void {
41 assert(i == 5);41 assert(i == 5);
42 }42 }
43}43}
44
45test "defer and labeled break" {
46 var i = usize(0);
47
48 blk: {
49 defer i += 1;
50 break :blk;
51 }
52
53 assert(i == 1);
54}
test/cases/enum.zig+9
...@@ -392,3 +392,12 @@ test "enum with 1 field but explicit tag type should still have the tag type" {...@@ -392,3 +392,12 @@ test "enum with 1 field but explicit tag type should still have the tag type" {
392 const Enum = enum(u8) { B = 2 };392 const Enum = enum(u8) { B = 2 };
393 comptime @import("std").debug.assert(@sizeOf(Enum) == @sizeOf(u8));393 comptime @import("std").debug.assert(@sizeOf(Enum) == @sizeOf(u8));
394}394}
395
396test "empty extern enum with members" {
397 const E = extern enum {
398 A,
399 B,
400 C,
401 };
402 assert(@sizeOf(E) == @sizeOf(c_int));
403}
test/cases/error.zig+66
...@@ -175,3 +175,69 @@ fn baz_1() !i32 {...@@ -175,3 +175,69 @@ fn baz_1() !i32 {
175fn quux_1() !i32 {175fn quux_1() !i32 {
176 return error.C;176 return error.C;
177}177}
178
179
180test "error: fn returning empty error set can be passed as fn returning any error" {
181 entry();
182 comptime entry();
183}
184
185fn entry() void {
186 foo2(bar2);
187}
188
189fn foo2(f: fn()error!void) void {
190 const x = f();
191}
192
193fn bar2() (error{}!void) { }
194
195
196test "error: Zero sized error set returned with value payload crash" {
197 _ = foo3(0);
198 _ = comptime foo3(0);
199}
200
201const Error = error{};
202fn foo3(b: usize) Error!usize {
203 return b;
204}
205
206
207test "error: Infer error set from literals" {
208 _ = nullLiteral("n") catch |err| handleErrors(err);
209 _ = floatLiteral("n") catch |err| handleErrors(err);
210 _ = intLiteral("n") catch |err| handleErrors(err);
211 _ = comptime nullLiteral("n") catch |err| handleErrors(err);
212 _ = comptime floatLiteral("n") catch |err| handleErrors(err);
213 _ = comptime intLiteral("n") catch |err| handleErrors(err);
214}
215
216fn handleErrors(err: var) noreturn {
217 switch (err) {
218 error.T => {}
219 }
220
221 unreachable;
222}
223
224fn nullLiteral(str: []const u8) !?i64 {
225 if (str[0] == 'n')
226 return null;
227
228 return error.T;
229}
230
231fn floatLiteral(str: []const u8) !?f64 {
232 if (str[0] == 'n')
233 return 1.0;
234
235 return error.T;
236}
237
238fn intLiteral(str: []const u8) !?i64 {
239 if (str[0] == 'n')
240 return 1;
241
242 return error.T;
243}
test/cases/eval.zig+23
...@@ -513,3 +513,26 @@ test "array concat of slices gives slice" {...@@ -513,3 +513,26 @@ test "array concat of slices gives slice" {
513 assert(std.mem.eql(u8, c, "aoeuasdf"));513 assert(std.mem.eql(u8, c, "aoeuasdf"));
514 }514 }
515}515}
516
517test "comptime shlWithOverflow" {
518 const ct_shifted: u64 = comptime amt: {
519 var amt = u64(0);
520 _ = @shlWithOverflow(u64, ~u64(0), 16, &amt);
521 break :amt amt;
522 };
523
524 const rt_shifted: u64 = amt: {
525 var amt = u64(0);
526 _ = @shlWithOverflow(u64, ~u64(0), 16, &amt);
527 break :amt amt;
528 };
529
530 assert(ct_shifted == rt_shifted);
531}
532
533test "runtime 128 bit integer division" {
534 var a: u128 = 152313999999999991610955792383;
535 var b: u128 = 10000000000000000000;
536 var c = a / b;
537 assert(c == 15231399999);
538}
test/cases/fn.zig+17
...@@ -94,3 +94,20 @@ test "inline function call" {...@@ -94,3 +94,20 @@ test "inline function call" {
94}94}
9595
96fn add(a: i32, b: i32) i32 { return a + b; }96fn add(a: i32, b: i32) i32 { return a + b; }
97
98
99test "number literal as an argument" {
100 numberLiteralArg(3);
101 comptime numberLiteralArg(3);
102}
103
104fn numberLiteralArg(a: var) void {
105 assert(a == 3);
106}
107
108test "assign inline fn to const variable" {
109 const a = inlineFn;
110 a();
111}
112
113inline fn inlineFn() void { }
test/cases/fn_in_struct_in_comptime.zig created+17
...@@ -0,0 +1,17 @@
1const assert = @import("std").debug.assert;
2
3fn get_foo() fn(&u8)usize {
4 comptime {
5 return struct {
6 fn func(ptr: &u8) usize {
7 var u = @ptrToInt(ptr);
8 return u;
9 }
10 }.func;
11 }
12}
13
14test "define a function in an anonymous struct in comptime" {
15 const foo = get_foo();
16 assert(foo(@intToPtr(&u8, 12345)) == 12345);
17}
test/cases/math.zig+41
...@@ -349,6 +349,31 @@ test "big number shifting" {...@@ -349,6 +349,31 @@ test "big number shifting" {
349 }349 }
350}350}
351351
352test "big number multi-limb shift and mask" {
353 comptime {
354 var a = 0xefffffffa0000001eeeeeeefaaaaaaab;
355
356 assert(u32(a & 0xffffffff) == 0xaaaaaaab);
357 a >>= 32;
358 assert(u32(a & 0xffffffff) == 0xeeeeeeef);
359 a >>= 32;
360 assert(u32(a & 0xffffffff) == 0xa0000001);
361 a >>= 32;
362 assert(u32(a & 0xffffffff) == 0xefffffff);
363 a >>= 32;
364
365 assert(a == 0);
366 }
367}
368
369test "big number multi-limb partial shift right" {
370 comptime {
371 var a = 0x1ffffffffeeeeeeee;
372 a >>= 16;
373 assert(a == 0x1ffffffffeeee);
374 }
375}
376
352test "xor" {377test "xor" {
353 test_xor();378 test_xor();
354 comptime test_xor();379 comptime test_xor();
...@@ -402,3 +427,19 @@ test "comptime float rem int" {...@@ -402,3 +427,19 @@ test "comptime float rem int" {
402 assert(x == 1.0);427 assert(x == 1.0);
403 }428 }
404}429}
430
431test "@sqrt" {
432 testSqrt(f64, 12.0);
433 comptime testSqrt(f64, 12.0);
434 testSqrt(f32, 13.0);
435 comptime testSqrt(f32, 13.0);
436
437 const x = 14.0;
438 const y = x * x;
439 const z = @sqrt(@typeOf(y), y);
440 comptime assert(z == x);
441}
442
443fn testSqrt(comptime T: type, x: T) void {
444 assert(@sqrt(T, x * x) == x);
445}
test/cases/reflection.zig+24
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1const assert = @import("std").debug.assert;1const assert = @import("std").debug.assert;
2const mem = @import("std").mem;2const mem = @import("std").mem;
3const reflection = this;
34
4test "reflection: array, pointer, nullable, error union type child" {5test "reflection: array, pointer, nullable, error union type child" {
5 comptime {6 comptime {
...@@ -56,7 +57,30 @@ test "reflection: enum member types and names" {...@@ -56,7 +57,30 @@ test "reflection: enum member types and names" {
5657
57}58}
5859
60test "reflection: @field" {
61 var f = Foo {
62 .one = 42,
63 .two = true,
64 .three = void{},
65 };
66
67 assert(f.one == f.one);
68 assert(@field(f, "o" ++ "ne") == f.one);
69 assert(@field(f, "t" ++ "wo") == f.two);
70 assert(@field(f, "th" ++ "ree") == f.three);
71 assert(@field(Foo, "const" ++ "ant") == Foo.constant);
72 assert(@field(Bar, "O" ++ "ne") == Bar.One);
73 assert(@field(Bar, "T" ++ "wo") == Bar.Two);
74 assert(@field(Bar, "Th" ++ "ree") == Bar.Three);
75 assert(@field(Bar, "F" ++ "our") == Bar.Four);
76 assert(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2));
77 @field(f, "o" ++ "ne") = 4;
78 assert(f.one == 4);
79}
80
59const Foo = struct {81const Foo = struct {
82 const constant = 52;
83
60 one: i32,84 one: i32,
61 two: bool,85 two: bool,
62 three: void,86 three: void,
test/cases/type_info.zig created+200
...@@ -0,0 +1,200 @@
1const assert = @import("std").debug.assert;
2const mem = @import("std").mem;
3const TypeInfo = @import("builtin").TypeInfo;
4const TypeId = @import("builtin").TypeId;
5
6test "type info: tag type, void info" {
7 comptime {
8 assert(@TagType(TypeInfo) == TypeId);
9 const void_info = @typeInfo(void);
10 assert(TypeId(void_info) == TypeId.Void);
11 assert(void_info.Void == {});
12 }
13}
14
15test "type info: integer, floating point type info" {
16 comptime {
17 const u8_info = @typeInfo(u8);
18 assert(TypeId(u8_info) == TypeId.Int);
19 assert(!u8_info.Int.is_signed);
20 assert(u8_info.Int.bits == 8);
21
22 const f64_info = @typeInfo(f64);
23 assert(TypeId(f64_info) == TypeId.Float);
24 assert(f64_info.Float.bits == 64);
25 }
26}
27
28test "type info: pointer type info" {
29 comptime {
30 const u32_ptr_info = @typeInfo(&u32);
31 assert(TypeId(u32_ptr_info) == TypeId.Pointer);
32 assert(u32_ptr_info.Pointer.is_const == false);
33 assert(u32_ptr_info.Pointer.is_volatile == false);
34 assert(u32_ptr_info.Pointer.alignment == 4);
35 assert(u32_ptr_info.Pointer.child == u32);
36 }
37}
38
39test "type info: slice type info" {
40 comptime {
41 const u32_slice_info = @typeInfo([]u32);
42 assert(TypeId(u32_slice_info) == TypeId.Slice);
43 assert(u32_slice_info.Slice.is_const == false);
44 assert(u32_slice_info.Slice.is_volatile == false);
45 assert(u32_slice_info.Slice.alignment == 4);
46 assert(u32_slice_info.Slice.child == u32);
47 }
48}
49
50test "type info: array type info" {
51 comptime {
52 const arr_info = @typeInfo([42]bool);
53 assert(TypeId(arr_info) == TypeId.Array);
54 assert(arr_info.Array.len == 42);
55 assert(arr_info.Array.child == bool);
56 }
57}
58
59test "type info: nullable type info" {
60 comptime {
61 const null_info = @typeInfo(?void);
62 assert(TypeId(null_info) == TypeId.Nullable);
63 assert(null_info.Nullable.child == void);
64 }
65}
66
67test "type info: promise info" {
68 comptime {
69 const null_promise_info = @typeInfo(promise);
70 assert(TypeId(null_promise_info) == TypeId.Promise);
71 assert(null_promise_info.Promise.child == @typeOf(undefined));
72
73 const promise_info = @typeInfo(promise->usize);
74 assert(TypeId(promise_info) == TypeId.Promise);
75 assert(promise_info.Promise.child == usize);
76 }
77
78}
79
80test "type info: error set, error union info" {
81 comptime {
82 const TestErrorSet = error {
83 First,
84 Second,
85 Third,
86 };
87
88 const error_set_info = @typeInfo(TestErrorSet);
89 assert(TypeId(error_set_info) == TypeId.ErrorSet);
90 assert(error_set_info.ErrorSet.errors.len == 3);
91 assert(mem.eql(u8, error_set_info.ErrorSet.errors[0].name, "First"));
92 assert(error_set_info.ErrorSet.errors[2].value == usize(TestErrorSet.Third));
93
94 const error_union_info = @typeInfo(TestErrorSet!usize);
95 assert(TypeId(error_union_info) == TypeId.ErrorUnion);
96 assert(error_union_info.ErrorUnion.error_set == TestErrorSet);
97 assert(error_union_info.ErrorUnion.payload == usize);
98 }
99}
100
101test "type info: enum info" {
102 comptime {
103 const Os = @import("builtin").Os;
104
105 const os_info = @typeInfo(Os);
106 assert(TypeId(os_info) == TypeId.Enum);
107 assert(os_info.Enum.layout == TypeInfo.ContainerLayout.Auto);
108 assert(os_info.Enum.fields.len == 32);
109 assert(mem.eql(u8, os_info.Enum.fields[1].name, "ananas"));
110 assert(os_info.Enum.fields[10].value == 10);
111 assert(os_info.Enum.tag_type == u5);
112 assert(os_info.Enum.defs.len == 0);
113 }
114}
115
116test "type info: union info" {
117 comptime {
118 const typeinfo_info = @typeInfo(TypeInfo);
119 assert(TypeId(typeinfo_info) == TypeId.Union);
120 assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto);
121 assert(typeinfo_info.Union.tag_type == TypeId);
122 assert(typeinfo_info.Union.fields.len == 26);
123 assert(typeinfo_info.Union.fields[4].enum_field != null);
124 assert((??typeinfo_info.Union.fields[4].enum_field).value == 4);
125 assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int));
126 assert(typeinfo_info.Union.defs.len == 21);
127
128 const TestNoTagUnion = union {
129 Foo: void,
130 Bar: u32,
131 };
132
133 const notag_union_info = @typeInfo(TestNoTagUnion);
134 assert(TypeId(notag_union_info) == TypeId.Union);
135 assert(notag_union_info.Union.tag_type == @typeOf(undefined));
136 assert(notag_union_info.Union.layout == TypeInfo.ContainerLayout.Auto);
137 assert(notag_union_info.Union.fields.len == 2);
138 assert(notag_union_info.Union.fields[0].enum_field == null);
139 assert(notag_union_info.Union.fields[1].field_type == u32);
140
141 const TestExternUnion = extern union {
142 foo: &c_void,
143 };
144
145 const extern_union_info = @typeInfo(TestExternUnion);
146 assert(extern_union_info.Union.layout == TypeInfo.ContainerLayout.Extern);
147 assert(extern_union_info.Union.tag_type == @typeOf(undefined));
148 assert(extern_union_info.Union.fields[0].enum_field == null);
149 assert(extern_union_info.Union.fields[0].field_type == &c_void);
150 }
151}
152
153test "type info: struct info" {
154 comptime {
155 const struct_info = @typeInfo(TestStruct);
156 assert(TypeId(struct_info) == TypeId.Struct);
157 assert(struct_info.Struct.layout == TypeInfo.ContainerLayout.Packed);
158 assert(struct_info.Struct.fields.len == 3);
159 assert(struct_info.Struct.fields[1].offset == null);
160 assert(struct_info.Struct.fields[2].field_type == &TestStruct);
161 assert(struct_info.Struct.defs.len == 2);
162 assert(struct_info.Struct.defs[0].is_pub);
163 assert(!struct_info.Struct.defs[0].data.Fn.is_extern);
164 assert(struct_info.Struct.defs[0].data.Fn.lib_name == null);
165 assert(struct_info.Struct.defs[0].data.Fn.return_type == void);
166 assert(struct_info.Struct.defs[0].data.Fn.fn_type == fn(&const TestStruct)void);
167 }
168}
169
170const TestStruct = packed struct {
171 const Self = this;
172
173 fieldA: usize,
174 fieldB: void,
175 fieldC: &Self,
176
177 pub fn foo(self: &const Self) void {}
178};
179
180test "type info: function type info" {
181 comptime {
182 const fn_info = @typeInfo(@typeOf(foo));
183 assert(TypeId(fn_info) == TypeId.Fn);
184 assert(fn_info.Fn.calling_convention == TypeInfo.CallingConvention.Unspecified);
185 assert(fn_info.Fn.is_generic);
186 assert(fn_info.Fn.args.len == 2);
187 assert(fn_info.Fn.is_var_args);
188 assert(fn_info.Fn.return_type == @typeOf(undefined));
189 assert(fn_info.Fn.async_allocator_type == @typeOf(undefined));
190
191 const test_instance: TestStruct = undefined;
192 const bound_fn_info = @typeInfo(@typeOf(test_instance.foo));
193 assert(TypeId(bound_fn_info) == TypeId.BoundFn);
194 assert(bound_fn_info.BoundFn.args[0].arg_type == &const TestStruct);
195 }
196}
197
198fn foo(comptime a: usize, b: bool, args: ...) usize {
199 return 0;
200}
test/cases/union.zig+22
...@@ -45,6 +45,16 @@ test "basic unions" {...@@ -45,6 +45,16 @@ test "basic unions" {
45 assert(foo.float == 12.34);45 assert(foo.float == 12.34);
46}46}
4747
48test "comptime union field access" {
49 comptime {
50 var foo = Foo { .int = 0 };
51 assert(foo.int == 0);
52
53 foo = Foo { .float = 42.42 };
54 assert(foo.float == 42.42);
55 }
56}
57
48test "init union with runtime value" {58test "init union with runtime value" {
49 var foo: Foo = undefined;59 var foo: Foo = undefined;
5060
...@@ -262,3 +272,15 @@ const PartialInstWithPayload = union(enum) {...@@ -262,3 +272,15 @@ const PartialInstWithPayload = union(enum) {
262 Compiled: i32,272 Compiled: i32,
263};273};
264274
275
276test "access a member of tagged union with conflicting enum tag name" {
277 const Bar = union(enum) {
278 A: A,
279 B: B,
280
281 const A = u8;
282 const B = void;
283 };
284
285 comptime assert(Bar.A == u8);
286}
test/compile_errors.zig+86-9
...@@ -1,6 +1,55 @@...@@ -1,6 +1,55 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: &tests.CompileErrorContext) void {3pub fn addCases(cases: &tests.CompileErrorContext) void {
4 cases.add("invalid deref on switch target",
5 \\comptime {
6 \\ var tile = Tile.Empty;
7 \\ switch (*tile) {
8 \\ Tile.Empty => {},
9 \\ Tile.Filled => {},
10 \\ }
11 \\}
12 \\const Tile = enum {
13 \\ Empty,
14 \\ Filled,
15 \\};
16 ,
17 ".tmp_source.zig:3:13: error: invalid deref on switch target");
18
19 cases.add("invalid field access in comptime",
20 \\comptime { var x = doesnt_exist.whatever; }
21 ,
22 ".tmp_source.zig:1:20: error: use of undeclared identifier 'doesnt_exist'");
23
24 cases.add("suspend inside suspend block",
25 \\const std = @import("std");
26 \\
27 \\export fn entry() void {
28 \\ var buf: [500]u8 = undefined;
29 \\ var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
30 \\ const p = (async<a> foo()) catch unreachable;
31 \\ cancel p;
32 \\}
33 \\
34 \\async fn foo() void {
35 \\ suspend |p| {
36 \\ suspend |p1| {
37 \\ }
38 \\ }
39 \\}
40 ,
41 ".tmp_source.zig:12:9: error: cannot suspend inside suspend block",
42 ".tmp_source.zig:11:5: note: other suspend block here");
43
44 cases.add("assign inline fn to non-comptime var",
45 \\export fn entry() void {
46 \\ var a = b;
47 \\}
48 \\inline fn b() void { }
49 ,
50 ".tmp_source.zig:2:5: error: functions marked inline must be stored in const or comptime var",
51 ".tmp_source.zig:4:8: note: declared here");
52
4 cases.add("wrong type passed to @panic",53 cases.add("wrong type passed to @panic",
5 \\export fn entry() void {54 \\export fn entry() void {
6 \\ var e = error.Foo;55 \\ var e = error.Foo;
...@@ -1385,17 +1434,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1385,17 +1434,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1385 \\const AtomicOrder = @import("builtin").AtomicOrder;1434 \\const AtomicOrder = @import("builtin").AtomicOrder;
1386 \\export fn f() void {1435 \\export fn f() void {
1387 \\ var x: i32 = 1234;1436 \\ var x: i32 = 1234;
1388 \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {}1437 \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {}
1389 \\}1438 \\}
1390 , ".tmp_source.zig:4:72: error: failure atomic ordering must be no stricter than success");1439 , ".tmp_source.zig:4:81: error: failure atomic ordering must be no stricter than success");
13911440
1392 cases.add("atomic orderings of cmpxchg - success Monotonic or stricter",1441 cases.add("atomic orderings of cmpxchg - success Monotonic or stricter",
1393 \\const AtomicOrder = @import("builtin").AtomicOrder;1442 \\const AtomicOrder = @import("builtin").AtomicOrder;
1394 \\export fn f() void {1443 \\export fn f() void {
1395 \\ var x: i32 = 1234;1444 \\ var x: i32 = 1234;
1396 \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {}1445 \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {}
1397 \\}1446 \\}
1398 , ".tmp_source.zig:4:49: error: success atomic ordering must be Monotonic or stricter");1447 , ".tmp_source.zig:4:58: error: success atomic ordering must be Monotonic or stricter");
13991448
1400 cases.add("negation overflow in function evaluation",1449 cases.add("negation overflow in function evaluation",
1401 \\const y = neg(-128);1450 \\const y = neg(-128);
...@@ -1723,7 +1772,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1723,7 +1772,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1723 \\}1772 \\}
1724 \\1773 \\
1725 \\export fn entry() usize { return @sizeOf(@typeOf(bar)); }1774 \\export fn entry() usize { return @sizeOf(@typeOf(bar)); }
1726 , ".tmp_source.zig:10:16: error: parameter of type '(integer literal)' requires comptime");1775 , ".tmp_source.zig:10:16: error: compiler bug: integer and float literals in var args function must be casted");
17271776
1728 cases.add("assign too big number to u16",1777 cases.add("assign too big number to u16",
1729 \\export fn foo() void {1778 \\export fn foo() void {
...@@ -2451,11 +2500,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -2451,11 +2500,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
2451 \\const AtomicOrder = @import("builtin").AtomicOrder;2500 \\const AtomicOrder = @import("builtin").AtomicOrder;
2452 \\export fn entry() bool {2501 \\export fn entry() bool {
2453 \\ var x: i32 align(1) = 1234;2502 \\ var x: i32 align(1) = 1234;
2454 \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {}2503 \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {}
2455 \\ return x == 5678;2504 \\ return x == 5678;
2456 \\}2505 \\}
2457 ,2506 ,
2458 ".tmp_source.zig:4:23: error: expected pointer alignment of at least 4, found 1");2507 ".tmp_source.zig:4:32: error: expected type '&i32', found '&align(1) i32'");
24592508
2460 cases.add("wrong size to an array literal",2509 cases.add("wrong size to an array literal",
2461 \\comptime {2510 \\comptime {
...@@ -2525,10 +2574,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -2525,10 +2574,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
2525 cases.add("wrong types given to atomic order args in cmpxchg",2574 cases.add("wrong types given to atomic order args in cmpxchg",
2526 \\export fn entry() void {2575 \\export fn entry() void {
2527 \\ var x: i32 = 1234;2576 \\ var x: i32 = 1234;
2528 \\ while (!@cmpxchg(&x, 1234, 5678, u32(1234), u32(1234))) {}2577 \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, u32(1234), u32(1234))) {}
2529 \\}2578 \\}
2530 ,2579 ,
2531 ".tmp_source.zig:3:41: error: expected type 'AtomicOrder', found 'u32'");2580 ".tmp_source.zig:3:50: error: expected type 'AtomicOrder', found 'u32'");
25322581
2533 cases.add("wrong types given to @export",2582 cases.add("wrong types given to @export",
2534 \\extern fn entry() void { }2583 \\extern fn entry() void { }
...@@ -3160,4 +3209,32 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -3160,4 +3209,32 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
3160 \\}3209 \\}
3161 ,3210 ,
3162 ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset");3211 ".tmp_source.zig:5:42: error: zero-bit field 'val' in struct 'Empty' has no offset");
3212
3213 cases.add("invalid union field access in comptime",
3214 \\const Foo = union {
3215 \\ Bar: u8,
3216 \\ Baz: void,
3217 \\};
3218 \\comptime {
3219 \\ var foo = Foo {.Baz = {}};
3220 \\ const bar_val = foo.Bar;
3221 \\}
3222 ,
3223 ".tmp_source.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set");
3224
3225 cases.add("getting return type of generic function",
3226 \\fn generic(a: var) void {}
3227 \\comptime {
3228 \\ _ = @typeOf(generic).ReturnType;
3229 \\}
3230 ,
3231 ".tmp_source.zig:3:25: error: ReturnType has not been resolved because 'fn(var)var' is generic");
3232
3233 cases.add("getting @ArgType of generic function",
3234 \\fn generic(a: var) void {}
3235 \\comptime {
3236 \\ _ = @ArgType(@typeOf(generic), 0);
3237 \\}
3238 ,
3239 ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic");
3163}3240}
test/tests.zig+5-4
...@@ -152,7 +152,7 @@ pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []cons...@@ -152,7 +152,7 @@ pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []cons
152 const step = b.step(b.fmt("test-{}", name), desc);152 const step = b.step(b.fmt("test-{}", name), desc);
153 for (test_targets) |test_target| {153 for (test_targets) |test_target| {
154 const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch);154 const is_native = (test_target.os == builtin.os and test_target.arch == builtin.arch);
155 for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast}) |mode| {155 for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast, Mode.ReleaseSmall}) |mode| {
156 for ([]bool{false, true}) |link_libc| {156 for ([]bool{false, true}) |link_libc| {
157 if (link_libc and !is_native) {157 if (link_libc and !is_native) {
158 // don't assume we have a cross-compiling libc set up158 // don't assume we have a cross-compiling libc set up
...@@ -451,7 +451,7 @@ pub const CompareOutputContext = struct {...@@ -451,7 +451,7 @@ pub const CompareOutputContext = struct {
451 self.step.dependOn(&run_and_cmp_output.step);451 self.step.dependOn(&run_and_cmp_output.step);
452 },452 },
453 Special.None => {453 Special.None => {
454 for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast}) |mode| {454 for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast, Mode.ReleaseSmall}) |mode| {
455 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})",455 const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})",
456 "compare-output", case.name, @tagName(mode)) catch unreachable;456 "compare-output", case.name, @tagName(mode)) catch unreachable;
457 if (self.test_filter) |filter| {457 if (self.test_filter) |filter| {
...@@ -583,6 +583,7 @@ pub const CompileErrorContext = struct {...@@ -583,6 +583,7 @@ pub const CompileErrorContext = struct {
583 Mode.Debug => {},583 Mode.Debug => {},
584 Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,584 Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,
585 Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable,585 Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable,
586 Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable,
586 }587 }
587588
588 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);589 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
...@@ -704,7 +705,7 @@ pub const CompileErrorContext = struct {...@@ -704,7 +705,7 @@ pub const CompileErrorContext = struct {
704 pub fn addCase(self: &CompileErrorContext, case: &const TestCase) void {705 pub fn addCase(self: &CompileErrorContext, case: &const TestCase) void {
705 const b = self.b;706 const b = self.b;
706707
707 for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast}) |mode| {708 for ([]Mode{Mode.Debug, Mode.ReleaseFast}) |mode| {
708 const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {} ({})",709 const annotated_case_name = fmt.allocPrint(self.b.allocator, "compile-error {} ({})",
709 case.name, @tagName(mode)) catch unreachable;710 case.name, @tagName(mode)) catch unreachable;
710 if (self.test_filter) |filter| {711 if (self.test_filter) |filter| {
...@@ -772,7 +773,7 @@ pub const BuildExamplesContext = struct {...@@ -772,7 +773,7 @@ pub const BuildExamplesContext = struct {
772 pub fn addAllArgs(self: &BuildExamplesContext, root_src: []const u8, link_libc: bool) void {773 pub fn addAllArgs(self: &BuildExamplesContext, root_src: []const u8, link_libc: bool) void {
773 const b = self.b;774 const b = self.b;
774775
775 for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast}) |mode| {776 for ([]Mode{Mode.Debug, Mode.ReleaseSafe, Mode.ReleaseFast, Mode.ReleaseSmall}) |mode| {
776 const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})",777 const annotated_case_name = fmt.allocPrint(self.b.allocator, "build {} ({})",
777 root_src, @tagName(mode)) catch unreachable;778 root_src, @tagName(mode)) catch unreachable;
778 if (self.test_filter) |filter| {779 if (self.test_filter) |filter| {
test/translate_c.zig+43
...@@ -1,6 +1,27 @@...@@ -1,6 +1,27 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: &tests.TranslateCContext) void {3pub fn addCases(cases: &tests.TranslateCContext) void {
4 cases.add("double define struct",
5 \\typedef struct Bar Bar;
6 \\typedef struct Foo Foo;
7 \\
8 \\struct Foo {
9 \\ Foo *a;
10 \\};
11 \\
12 \\struct Bar {
13 \\ Foo *a;
14 \\};
15 ,
16 \\pub const struct_Foo = extern struct {
17 \\ a: ?&Foo,
18 \\};
19 \\pub const Foo = struct_Foo;
20 \\pub const struct_Bar = extern struct {
21 \\ a: ?&Foo,
22 \\};
23 );
24
4 cases.addAllowWarnings("simple data types",25 cases.addAllowWarnings("simple data types",
5 \\#include <stdint.h>26 \\#include <stdint.h>
6 \\int foo(char a, unsigned char b, signed char c);27 \\int foo(char a, unsigned char b, signed char c);
...@@ -53,6 +74,28 @@ pub fn addCases(cases: &tests.TranslateCContext) void {...@@ -53,6 +74,28 @@ pub fn addCases(cases: &tests.TranslateCContext) void {
53 \\pub const Foo = enum_Foo;74 \\pub const Foo = enum_Foo;
54 );75 );
5576
77 cases.add("enums",
78 \\enum Foo {
79 \\ FooA = 2,
80 \\ FooB = 5,
81 \\ Foo1,
82 \\};
83 ,
84 \\pub const enum_Foo = extern enum {
85 \\ A = 2,
86 \\ B = 5,
87 \\ @"1" = 6,
88 \\};
89 ,
90 \\pub const FooA = enum_Foo.A;
91 ,
92 \\pub const FooB = enum_Foo.B;
93 ,
94 \\pub const Foo1 = enum_Foo.@"1";
95 ,
96 \\pub const Foo = enum_Foo;
97 );
98
56 cases.add("restrict -> noalias",99 cases.add("restrict -> noalias",
57 \\void foo(void *restrict bar, void *restrict);100 \\void foo(void *restrict bar, void *restrict);
58 ,101 ,