| author | |
| committer | |
| log | 8c1872543c8cf76215cc4bf3ced4637bb1065a4e |
| tree | 72dfebb643ab61579e3fb8dd58cd4610ffe876fa |
| parent | 7186e92c86982950d0aa7c0c2deef9ef96bc1264 |
| parent | 6e821078f625a03eb8b7794c983da0f7793366ab |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
Sync with zig-lang/zig master143 files changed, 23753 insertions(+), 5164 deletions(-)
.travis.yml+13-9| ... | ... | @@ -1,18 +1,22 @@ |
| 1 | 1 | sudo: required |
| 2 | 2 | services: |
| 3 | - docker | |
| 3 | - docker | |
| 4 | 4 | os: |
| 5 | - linux | |
| 6 | - osx | |
| 5 | - linux | |
| 6 | - osx | |
| 7 | 7 | dist: trusty |
| 8 | 8 | osx_image: xcode8.3 |
| 9 | 9 | language: cpp |
| 10 | 10 | before_install: |
| 11 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_before_install; fi | |
| 12 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_before_install; fi | |
| 11 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_before_install; fi | |
| 12 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_before_install; fi | |
| 13 | 13 | install: |
| 14 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_install; fi | |
| 15 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_install; fi | |
| 14 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_install; fi | |
| 15 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_install; fi | |
| 16 | 16 | script: |
| 17 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_script; fi | |
| 18 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_script; fi | |
| 17 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_script; fi | |
| 18 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_script; fi | |
| 19 | env: | |
| 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 | 5 | "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) |
| 6 | 6 | endif() |
| 7 | 7 | |
| 8 | if(NOT CMAKE_INSTALL_PREFIX) | |
| 9 | set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}" CACHE STRING | |
| 10 | "Directory to install zig to" FORCE) | |
| 11 | endif() | |
| 12 | ||
| 8 | 13 | project(zig C CXX) |
| 9 | 14 | set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) |
| 10 | 15 | |
| ... | ... | @@ -410,6 +415,9 @@ set(ZIG_CPP_SOURCES |
| 410 | 415 | |
| 411 | 416 | set(ZIG_STD_FILES |
| 412 | 417 | "array_list.zig" |
| 418 | "atomic/index.zig" | |
| 419 | "atomic/queue.zig" | |
| 420 | "atomic/stack.zig" | |
| 413 | 421 | "base64.zig" |
| 414 | 422 | "buf_map.zig" |
| 415 | 423 | "buf_set.zig" |
| ... | ... | @@ -419,33 +427,34 @@ set(ZIG_STD_FILES |
| 419 | 427 | "c/index.zig" |
| 420 | 428 | "c/linux.zig" |
| 421 | 429 | "c/windows.zig" |
| 430 | "crypto/blake2.zig" | |
| 431 | "crypto/hmac.zig" | |
| 422 | 432 | "crypto/index.zig" |
| 423 | 433 | "crypto/md5.zig" |
| 424 | 434 | "crypto/sha1.zig" |
| 425 | 435 | "crypto/sha2.zig" |
| 426 | 436 | "crypto/sha3.zig" |
| 427 | "crypto/blake2.zig" | |
| 428 | "crypto/hmac.zig" | |
| 429 | 437 | "cstr.zig" |
| 430 | 438 | "debug/failing_allocator.zig" |
| 431 | 439 | "debug/index.zig" |
| 432 | 440 | "dwarf.zig" |
| 433 | 441 | "elf.zig" |
| 434 | 442 | "empty.zig" |
| 435 | "endian.zig" | |
| 443 | "event.zig" | |
| 436 | 444 | "fmt/errol/enum3.zig" |
| 437 | 445 | "fmt/errol/index.zig" |
| 438 | 446 | "fmt/errol/lookup.zig" |
| 439 | 447 | "fmt/index.zig" |
| 440 | "hash_map.zig" | |
| 441 | "hash/index.zig" | |
| 442 | 448 | "hash/adler.zig" |
| 443 | 449 | "hash/crc.zig" |
| 444 | 450 | "hash/fnv.zig" |
| 451 | "hash/index.zig" | |
| 445 | 452 | "hash/siphash.zig" |
| 453 | "hash_map.zig" | |
| 446 | 454 | "heap.zig" |
| 447 | 455 | "index.zig" |
| 448 | 456 | "io.zig" |
| 457 | "json.zig" | |
| 449 | 458 | "linked_list.zig" |
| 450 | 459 | "macho.zig" |
| 451 | 460 | "math/acos.zig" |
| ... | ... | @@ -457,6 +466,28 @@ set(ZIG_STD_FILES |
| 457 | 466 | "math/atanh.zig" |
| 458 | 467 | "math/cbrt.zig" |
| 459 | 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 | 491 | "math/copysign.zig" |
| 461 | 492 | "math/cos.zig" |
| 462 | 493 | "math/cosh.zig" |
| ... | ... | @@ -493,25 +524,28 @@ set(ZIG_STD_FILES |
| 493 | 524 | "math/tan.zig" |
| 494 | 525 | "math/tanh.zig" |
| 495 | 526 | "math/trunc.zig" |
| 496 | "math/x86_64/sqrt.zig" | |
| 497 | 527 | "mem.zig" |
| 498 | 528 | "net.zig" |
| 499 | 529 | "os/child_process.zig" |
| 500 | 530 | "os/darwin.zig" |
| 501 | 531 | "os/darwin_errno.zig" |
| 532 | "os/epoch.zig" | |
| 502 | 533 | "os/file.zig" |
| 503 | 534 | "os/get_user_id.zig" |
| 504 | 535 | "os/index.zig" |
| 505 | 536 | "os/linux/errno.zig" |
| 506 | "os/linux/i386.zig" | |
| 507 | 537 | "os/linux/index.zig" |
| 538 | "os/linux/vdso.zig" | |
| 508 | 539 | "os/linux/x86_64.zig" |
| 509 | 540 | "os/path.zig" |
| 541 | "os/time.zig" | |
| 510 | 542 | "os/windows/error.zig" |
| 511 | 543 | "os/windows/index.zig" |
| 512 | 544 | "os/windows/util.zig" |
| 513 | 545 | "os/zen.zig" |
| 514 | 546 | "rand/index.zig" |
| 547 | "rand/ziggurat.zig" | |
| 548 | "segmented_list.zig" | |
| 515 | 549 | "sort.zig" |
| 516 | 550 | "special/bootstrap.zig" |
| 517 | 551 | "special/bootstrap_lib.zig" |
| ... | ... | @@ -542,7 +576,8 @@ set(ZIG_STD_FILES |
| 542 | 576 | "unicode.zig" |
| 543 | 577 | "zig/ast.zig" |
| 544 | 578 | "zig/index.zig" |
| 545 | "zig/parser.zig" | |
| 579 | "zig/parse.zig" | |
| 580 | "zig/render.zig" | |
| 546 | 581 | "zig/tokenizer.zig" |
| 547 | 582 | ) |
| 548 | 583 |
README.md+4-4| ... | ... | @@ -141,10 +141,10 @@ libc. Create demo games using Zig. |
| 141 | 141 | ``` |
| 142 | 142 | mkdir build |
| 143 | 143 | cd build |
| 144 | cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) | |
| 144 | cmake .. | |
| 145 | 145 | make |
| 146 | 146 | make install |
| 147 | ./zig build --build-file ../build.zig test | |
| 147 | bin/zig build --build-file ../build.zig test | |
| 148 | 148 | ``` |
| 149 | 149 | |
| 150 | 150 | ##### MacOS |
| ... | ... | @@ -154,9 +154,9 @@ brew install cmake llvm@6 |
| 154 | 154 | brew outdated llvm@6 || brew upgrade llvm@6 |
| 155 | 155 | mkdir build |
| 156 | 156 | cd build |
| 157 | cmake .. -DCMAKE_PREFIX_PATH=/usr/local/opt/llvm@6/ -DCMAKE_INSTALL_PREFIX=$(pwd) | |
| 157 | cmake .. -DCMAKE_PREFIX_PATH=/usr/local/opt/llvm@6/ | |
| 158 | 158 | make install |
| 159 | ./zig build --build-file ../build.zig test | |
| 159 | bin/zig build --build-file ../build.zig test | |
| 160 | 160 | ``` |
| 161 | 161 | |
| 162 | 162 | ##### Windows |
ci/travis_linux_script+1-1| ... | ... | @@ -19,5 +19,5 @@ if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then |
| 19 | 19 | echo "secret_key = $AWS_SECRET_ACCESS_KEY" >> ~/.s3cfg |
| 20 | 20 | s3cmd put -P $TRAVIS_BUILD_DIR/artifacts/* s3://ziglang.org/builds/ |
| 21 | 21 | 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) | |
| 23 | 23 | fi |
deps/lld/ELF/MarkLive.cpp+9| ... | ... | @@ -301,6 +301,15 @@ template <class ELFT> void elf::markLive() { |
| 301 | 301 | // Follow the graph to mark all live sections. |
| 302 | 302 | doGcSections<ELFT>(); |
| 303 | 303 | |
| 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 | 313 | // Report garbage-collected sections. |
| 305 | 314 | if (Config->PrintGcSections) |
| 306 | 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 | 749 | try build_args.append("--release-fast"); |
| 750 | 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 | 757 | for (code.link_objects) |link_object| { |
| 754 | 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 | 814 | try test_args.append("--release-fast"); |
| 811 | 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 | 822 | if (code.target_windows) { |
| 815 | 823 | try test_args.appendSlice([][]const u8{ |
| ... | ... | @@ -840,6 +848,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var |
| 840 | 848 | try test_args.append("--release-fast"); |
| 841 | 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 | 856 | const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size); |
| 845 | 857 | switch (result.term) { |
| ... | ... | @@ -874,6 +886,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var |
| 874 | 886 | builtin.Mode.Debug => {}, |
| 875 | 887 | builtin.Mode.ReleaseSafe => try test_args.append("--release-safe"), |
| 876 | 888 | builtin.Mode.ReleaseFast => try test_args.append("--release-fast"), |
| 889 | builtin.Mode.ReleaseSmall => try test_args.append("--release-small"), | |
| 877 | 890 | } |
| 878 | 891 | |
| 879 | 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 | 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 | } |
| 931 | 950 | |
| 932 | 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 | 1947 | export fn entry(foo: Foo) void { } |
| 1948 | 1948 | {#code_end#} |
| 1949 | 1949 | {#header_close#} |
| 1950 | <p>TODO packed enum</p> | |
| 1951 | {#see_also|@memberName|@memberCount|@tagName#} | |
| 1950 | {#header_open|packed enum#} | |
| 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#} | |
| 1955 | const std = @import("std"); | |
| 1956 | ||
| 1957 | test "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 | 1968 | {#header_close#} |
| 1953 | 1969 | {#header_open|union#} |
| 1954 | 1970 | {#code_begin|test|union#} |
| ... | ... | @@ -2017,7 +2033,27 @@ test "union variant switch" { |
| 2017 | 2033 | assert(mem.eql(u8, what_is_it, "this is a number")); |
| 2018 | 2034 | } |
| 2019 | 2035 | |
| 2020 | // TODO union methods | |
| 2036 | // Unions can have methods just like structs and enums: | |
| 2037 | ||
| 2038 | const 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 | ||
| 2050 | test "union method" { | |
| 2051 | var v1 = Variant { .Int = 1 }; | |
| 2052 | var v2 = Variant { .Bool = false }; | |
| 2053 | ||
| 2054 | assert(v1.truthy()); | |
| 2055 | assert(!v2.truthy()); | |
| 2056 | } | |
| 2021 | 2057 | |
| 2022 | 2058 | |
| 2023 | 2059 | const Small = union { |
| ... | ... | @@ -3844,6 +3880,25 @@ pub fn main() void { |
| 3844 | 3880 | {#header_open|@ArgType#} |
| 3845 | 3881 | <p>TODO</p> |
| 3846 | 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 | 3902 | {#header_open|@atomicRmw#} |
| 3848 | 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 | 3904 | <p> |
| ... | ... | @@ -4010,16 +4065,60 @@ comptime { |
| 4010 | 4065 | </p> |
| 4011 | 4066 | |
| 4012 | 4067 | {#header_close#} |
| 4013 | {#header_open|@cmpxchg#} | |
| 4014 | <pre><code class="zig">@cmpxchg(ptr: &T, cmp: T, new: T, success_order: AtomicOrder, fail_order: AtomicOrder) -&gt; bool</code></pre> | |
| 4068 | {#header_open|@cmpxchgStrong#} | |
| 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#} | |
| 4075 | fn 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 | 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 | 4088 | </p> |
| 4018 | 4089 | <p> |
| 4019 | 4090 | <code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>. |
| 4020 | 4091 | </p> |
| 4021 | 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#} | |
| 4102 | fn 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 | 4122 | {#header_close#} |
| 4024 | 4123 | {#header_open|@compileError#} |
| 4025 | 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 | 4412 | It does not include functions, variables, or constants. |
| 4314 | 4413 | </p> |
| 4315 | 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 | 4419 | {#header_open|@memberType#} |
| 4317 | 4420 | <pre><code class="zig">@memberType(comptime T: type, comptime index: usize) -&gt; type</code></pre> |
| 4318 | 4421 | <p>Returns the field type of a struct or union.</p> |
| ... | ... | @@ -4633,6 +4736,16 @@ pub const FloatMode = enum { |
| 4633 | 4736 | The result is a target-specific compile time constant. |
| 4634 | 4737 | </p> |
| 4635 | 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 | 4749 | {#header_open|@subWithOverflow#} |
| 4637 | 4750 | <pre><code class="zig">@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -&gt; bool</code></pre> |
| 4638 | 4751 | <p> |
| ... | ... | @@ -4696,6 +4809,182 @@ pub const TypeId = enum { |
| 4696 | 4809 | BoundFn, |
| 4697 | 4810 | ArgTuple, |
| 4698 | 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#} | |
| 4821 | pub 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 | 4989 | {#code_end#} |
| 4701 | 4990 | {#header_close#} |
| ... | ... | @@ -5113,7 +5402,6 @@ pub const Os = enum { |
| 5113 | 5402 | rtems, |
| 5114 | 5403 | nacl, |
| 5115 | 5404 | cnk, |
| 5116 | bitrig, | |
| 5117 | 5405 | aix, |
| 5118 | 5406 | cuda, |
| 5119 | 5407 | nvcl, |
| ... | ... | @@ -5124,10 +5412,12 @@ pub const Os = enum { |
| 5124 | 5412 | watchos, |
| 5125 | 5413 | mesa3d, |
| 5126 | 5414 | contiki, |
| 5415 | amdpal, | |
| 5127 | 5416 | zen, |
| 5128 | 5417 | }; |
| 5129 | 5418 | |
| 5130 | 5419 | pub const Arch = enum { |
| 5420 | armv8_3a, | |
| 5131 | 5421 | armv8_2a, |
| 5132 | 5422 | armv8_1a, |
| 5133 | 5423 | armv8, |
| ... | ... | @@ -5147,9 +5437,29 @@ pub const Arch = enum { |
| 5147 | 5437 | armv5, |
| 5148 | 5438 | armv5te, |
| 5149 | 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 | 5460 | aarch64, |
| 5152 | 5461 | aarch64_be, |
| 5462 | arc, | |
| 5153 | 5463 | avr, |
| 5154 | 5464 | bpfel, |
| 5155 | 5465 | bpfeb, |
| ... | ... | @@ -5202,6 +5512,7 @@ pub const Arch = enum { |
| 5202 | 5512 | pub const Environ = enum { |
| 5203 | 5513 | unknown, |
| 5204 | 5514 | gnu, |
| 5515 | gnuabin32, | |
| 5205 | 5516 | gnuabi64, |
| 5206 | 5517 | gnueabi, |
| 5207 | 5518 | gnueabihf, |
| ... | ... | @@ -5219,6 +5530,7 @@ pub const Environ = enum { |
| 5219 | 5530 | amdopencl, |
| 5220 | 5531 | coreclr, |
| 5221 | 5532 | opencl, |
| 5533 | simulator, | |
| 5222 | 5534 | }; |
| 5223 | 5535 | |
| 5224 | 5536 | pub const ObjectFormat = enum { |
| ... | ... | @@ -5245,10 +5557,23 @@ pub const AtomicOrder = enum { |
| 5245 | 5557 | SeqCst, |
| 5246 | 5558 | }; |
| 5247 | 5559 | |
| 5560 | pub const AtomicRmwOp = enum { | |
| 5561 | Xchg, | |
| 5562 | Add, | |
| 5563 | Sub, | |
| 5564 | And, | |
| 5565 | Nand, | |
| 5566 | Or, | |
| 5567 | Xor, | |
| 5568 | Max, | |
| 5569 | Min, | |
| 5570 | }; | |
| 5571 | ||
| 5248 | 5572 | pub const Mode = enum { |
| 5249 | 5573 | Debug, |
| 5250 | 5574 | ReleaseSafe, |
| 5251 | 5575 | ReleaseFast, |
| 5576 | ReleaseSmall, | |
| 5252 | 5577 | }; |
| 5253 | 5578 | |
| 5254 | 5579 | pub const TypeId = enum { |
| ... | ... | @@ -5267,7 +5592,7 @@ pub const TypeId = enum { |
| 5267 | 5592 | NullLiteral, |
| 5268 | 5593 | Nullable, |
| 5269 | 5594 | ErrorUnion, |
| 5270 | Error, | |
| 5595 | ErrorSet, | |
| 5271 | 5596 | Enum, |
| 5272 | 5597 | Union, |
| 5273 | 5598 | Fn, |
| ... | ... | @@ -5276,6 +5601,176 @@ pub const TypeId = enum { |
| 5276 | 5601 | BoundFn, |
| 5277 | 5602 | ArgTuple, |
| 5278 | 5603 | Opaque, |
| 5604 | Promise, | |
| 5605 | }; | |
| 5606 | ||
| 5607 | pub 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 | }; |
| 5280 | 5775 | |
| 5281 | 5776 | pub const FloatMode = enum { |
| ... | ... | @@ -5289,7 +5784,7 @@ pub const Endian = enum { |
| 5289 | 5784 | }; |
| 5290 | 5785 | |
| 5291 | 5786 | pub const endian = Endian.Little; |
| 5292 | pub const is_test = false; | |
| 5787 | pub const is_test = true; | |
| 5293 | 5788 | pub const os = Os.linux; |
| 5294 | 5789 | pub const arch = Arch.x86_64; |
| 5295 | 5790 | pub const environ = Environ.gnu; |
| ... | ... | @@ -5297,6 +5792,7 @@ pub const object_format = ObjectFormat.elf; |
| 5297 | 5792 | pub const mode = Mode.Debug; |
| 5298 | 5793 | pub const link_libc = false; |
| 5299 | 5794 | pub const have_error_return_tracing = true; |
| 5795 | pub const __zig_test_fn_slice = {}; // overwritten later | |
| 5300 | 5796 | {#code_end#} |
| 5301 | 5797 | {#see_also|Build Mode#} |
| 5302 | 5798 | {#header_close#} |
| ... | ... | @@ -5809,7 +6305,7 @@ Defer(body) = ("defer" | "deferror") body |
| 5809 | 6305 | |
| 5810 | 6306 | IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body)) |
| 5811 | 6307 | |
| 5812 | SuspendExpression(body) = "suspend" option(("|" Symbol "|" body)) | |
| 6308 | SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body)) | |
| 5813 | 6309 | |
| 5814 | 6310 | IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body) |
| 5815 | 6311 | |
| ... | ... | @@ -5955,7 +6451,7 @@ hljs.registerLanguage("zig", function(t) { |
| 5955 | 6451 | a = t.IR + "\\s*\\(", |
| 5956 | 6452 | c = { |
| 5957 | 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 | 6455 | literal: "true false null undefined" |
| 5960 | 6456 | }, |
| 5961 | 6457 | n = [e, t.CLCM, t.CBCM, s, r]; |
src-self-hosted/arg.zig created+284| ... | ... | @@ -0,0 +1,284 @@ |
| 1 | const std = @import("std"); | |
| 2 | const debug = std.debug; | |
| 3 | const mem = std.mem; | |
| 4 | ||
| 5 | const Allocator = mem.Allocator; | |
| 6 | const ArrayList = std.ArrayList; | |
| 7 | const HashMap = std.HashMap; | |
| 8 | ||
| 9 | fn 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 | ||
| 19 | fn 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 | |
| 33 | fn 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 | ||
| 77 | const 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. | |
| 80 | pub 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`. | |
| 188 | const 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. | |
| 195 | pub 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 | ||
| 241 | test "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 | ||
| 3 | const std = @import("std"); | |
| 4 | const mem = std.mem; | |
| 5 | const os = std.os; | |
| 6 | ||
| 7 | const warn = std.debug.warn; | |
| 8 | ||
| 9 | /// Caller must free result | |
| 10 | pub 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 | |
| 24 | pub 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 | ||
| 45 | pub 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 | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; | |
| 3 | const io = std.io; | |
| 4 | const os = std.os; | |
| 5 | const heap = std.heap; | |
| 6 | const warn = std.debug.warn; | |
| 7 | const assert = std.debug.assert; | |
| 8 | const target = @import("target.zig"); | |
| 9 | const Target = target.Target; | |
| 10 | const Module = @import("module.zig").Module; | |
| 11 | const ErrColor = Module.ErrColor; | |
| 12 | const Emit = Module.Emit; | |
| 13 | 2 | const builtin = @import("builtin"); |
| 14 | const ArrayList = std.ArrayList; | |
| 15 | const c = @import("c.zig"); | |
| 16 | 3 | |
| 17 | const default_zig_cache_name = "zig-cache"; | |
| 4 | const os = std.os; | |
| 5 | const io = std.io; | |
| 6 | const mem = std.mem; | |
| 7 | const Allocator = mem.Allocator; | |
| 8 | const ArrayList = std.ArrayList; | |
| 9 | const Buffer = std.Buffer; | |
| 18 | 10 | |
| 19 | const Cmd = enum { | |
| 20 | None, | |
| 21 | Build, | |
| 22 | Test, | |
| 23 | Version, | |
| 24 | Zen, | |
| 25 | TranslateC, | |
| 26 | Targets, | |
| 11 | const arg = @import("arg.zig"); | |
| 12 | const c = @import("c.zig"); | |
| 13 | const introspect = @import("introspect.zig"); | |
| 14 | const Args = arg.Args; | |
| 15 | const Flag = arg.Flag; | |
| 16 | const Module = @import("module.zig").Module; | |
| 17 | const Target = @import("target.zig").Target; | |
| 18 | ||
| 19 | var stderr: &io.OutStream(io.FileOutStream.Error) = undefined; | |
| 20 | var stdout: &io.OutStream(io.FileOutStream.Error) = undefined; | |
| 21 | ||
| 22 | const 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 | ||
| 42 | const Command = struct { | |
| 43 | name: []const u8, | |
| 44 | exec: fn(&Allocator, []const []const u8) error!void, | |
| 27 | 45 | }; |
| 28 | 46 | |
| 29 | fn 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 | ||
| 38 | 47 | pub 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; | |
| 40 | 57 | |
| 41 | 58 | const args = try os.argsAlloc(allocator); |
| 42 | 59 | defer os.argsFree(allocator, args); |
| 43 | 60 | |
| 44 | if (args.len >= 2 and mem.eql(u8, args[1], "build")) { | |
| 45 | return buildMain(allocator, args[2..]); | |
| 46 | } | |
| 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 | } | |
| 61 | if (args.len <= 1) { | |
| 62 | try stderr.write(usage); | |
| 63 | os.exit(1); | |
| 302 | 64 | } |
| 303 | 65 | |
| 304 | target.initializeAll(); | |
| 305 | ||
| 306 | // TODO | |
| 307 | // ZigTarget alloc_target; | |
| 308 | // ZigTarget *target; | |
| 309 | // if (!target_arch && !target_os && !target_environ) { | |
| 310 | // target = nullptr; | |
| 311 | // } else { | |
| 312 | // target = &alloc_target; | |
| 313 | // get_unknown_target(target); | |
| 314 | // if (target_arch) { | |
| 315 | // if (parse_target_arch(target_arch, &target->arch)) { | |
| 316 | // fprintf(stderr, "invalid --target-arch argument\n"); | |
| 317 | // return usage(arg0); | |
| 318 | // } | |
| 319 | // } | |
| 320 | // if (target_os) { | |
| 321 | // if (parse_target_os(target_os, &target->os)) { | |
| 322 | // fprintf(stderr, "invalid --target-os argument\n"); | |
| 323 | // return usage(arg0); | |
| 324 | // } | |
| 325 | // } | |
| 326 | // if (target_environ) { | |
| 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"), | |
| 66 | const commands = []Command { | |
| 67 | Command { .name = "build", .exec = cmdBuild }, | |
| 68 | Command { .name = "build-exe", .exec = cmdBuildExe }, | |
| 69 | Command { .name = "build-lib", .exec = cmdBuildLib }, | |
| 70 | Command { .name = "build-obj", .exec = cmdBuildObj }, | |
| 71 | Command { .name = "fmt", .exec = cmdFmt }, | |
| 72 | Command { .name = "run", .exec = cmdRun }, | |
| 73 | Command { .name = "targets", .exec = cmdTargets }, | |
| 74 | Command { .name = "test", .exec = cmdTest }, | |
| 75 | Command { .name = "translate-c", .exec = cmdTranslateC }, | |
| 76 | Command { .name = "version", .exec = cmdVersion }, | |
| 77 | Command { .name = "zen", .exec = cmdZen }, | |
| 78 | ||
| 79 | // undocumented commands | |
| 80 | Command { .name = "help", .exec = cmdHelp }, | |
| 81 | Command { .name = "internal", .exec = cmdInternal }, | |
| 82 | }; | |
| 83 | ||
| 84 | for (commands) |command| { | |
| 85 | if (mem.eql(u8, command.name, args[1])) { | |
| 86 | try command.exec(allocator, args[2..]); | |
| 87 | return; | |
| 88 | } | |
| 467 | 89 | } |
| 468 | } | |
| 469 | 90 | |
| 470 | fn printUsage(stream: var) !void { | |
| 471 | try stream.write( | |
| 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 | ||
| 547 | fn 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 | ); | |
| 91 | try stderr.print("unknown command: {}\n\n", args[1]); | |
| 92 | try stderr.write(usage); | |
| 565 | 93 | } |
| 566 | 94 | |
| 567 | fn buildMain(allocator: &mem.Allocator, argv: []const []const u8) !void { | |
| 568 | var build_file: [] const u8 = "build.zig"; | |
| 569 | var cache_dir: ?[] const u8 = null; | |
| 570 | var zig_install_prefix: ?[] const u8 = null; | |
| 571 | var asked_for_help = false; | |
| 572 | var asked_for_init = false; | |
| 573 | ||
| 574 | var args = ArrayList([] const u8).init(allocator); | |
| 575 | defer args.deinit(); | |
| 576 | ||
| 577 | var zig_exe_path = try os.selfExePath(allocator); | |
| 578 | defer allocator.free(zig_exe_path); | |
| 579 | ||
| 580 | try args.append(""); // Placeholder for zig-cache/build | |
| 581 | try args.append(""); // Placeholder for zig_exe_path | |
| 582 | try args.append(""); // Placeholder for build_file_dirname | |
| 583 | try args.append(""); // Placeholder for full_cache_dir | |
| 95 | // cmd:build /////////////////////////////////////////////////////////////////////////////////////// | |
| 96 | ||
| 97 | const usage_build = | |
| 98 | \\usage: zig build <options> | |
| 99 | \\ | |
| 100 | \\General Options: | |
| 101 | \\ --help Print this help and exit | |
| 102 | \\ --init Generate a build.zig template | |
| 103 | \\ --build-file [file] Override path to build.zig | |
| 104 | \\ --cache-dir [path] Override path to cache directory | |
| 105 | \\ --verbose Print commands before executing them | |
| 106 | \\ --prefix [path] Override default install prefix | |
| 107 | \\ | |
| 108 | \\Project-Specific Options: | |
| 109 | \\ | |
| 110 | \\ Project-specific options become available when the build file is found. | |
| 111 | \\ | |
| 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 | ||
| 125 | const 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 | }; | |
| 584 | 142 | |
| 585 | var i: usize = 0; | |
| 586 | while (i < argv.len) : (i += 1) { | |
| 587 | var arg = argv[i]; | |
| 588 | if (mem.eql(u8, arg, "--help")) { | |
| 589 | asked_for_help = true; | |
| 590 | try args.append(argv[i]); | |
| 591 | } else if (mem.eql(u8, arg, "--init")) { | |
| 592 | asked_for_init = true; | |
| 593 | try args.append(argv[i]); | |
| 594 | } else if (i + 1 < argv.len and mem.eql(u8, arg, "--build-file")) { | |
| 595 | build_file = argv[i + 1]; | |
| 596 | i += 1; | |
| 597 | } else if (i + 1 < argv.len and mem.eql(u8, arg, "--cache-dir")) { | |
| 598 | cache_dir = argv[i + 1]; | |
| 599 | i += 1; | |
| 600 | } else if (i + 1 < argv.len and mem.eql(u8, arg, "--zig-install-prefix")) { | |
| 601 | try args.append(arg); | |
| 602 | i += 1; | |
| 603 | zig_install_prefix = argv[i]; | |
| 604 | try args.append(argv[i]); | |
| 605 | } else { | |
| 606 | try args.append(arg); | |
| 607 | } | |
| 143 | const missing_build_file = | |
| 144 | \\No 'build.zig' file found. | |
| 145 | \\ | |
| 146 | \\Initialize a 'build.zig' template file with `zig build --init`, | |
| 147 | \\or build an executable directly with `zig build-exe $FILENAME.zig`. | |
| 148 | \\ | |
| 149 | \\See: `zig build --help` or `zig help` for more options. | |
| 150 | \\ | |
| 151 | ; | |
| 152 | ||
| 153 | fn cmdBuild(allocator: &Allocator, args: []const []const u8) !void { | |
| 154 | var flags = try Args.parse(allocator, args_build_spec, args); | |
| 155 | defer flags.deinit(); | |
| 156 | ||
| 157 | if (flags.present("help")) { | |
| 158 | try stderr.write(usage_build); | |
| 159 | os.exit(0); | |
| 608 | 160 | } |
| 609 | 161 | |
| 610 | const zig_lib_dir = try resolveZigLibDir(allocator, zig_install_prefix); | |
| 162 | const zig_lib_dir = try introspect.resolveZigLibDir(allocator); | |
| 611 | 163 | defer allocator.free(zig_lib_dir); |
| 612 | 164 | |
| 613 | 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 | 171 | const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig"); |
| 620 | 172 | defer allocator.free(build_runner_path); |
| 621 | 173 | |
| 622 | // g = codegen_create(build_runner_path, ...) | |
| 623 | // codegen_set_out_name(g, "build") | |
| 624 | ||
| 174 | const build_file = flags.single("build-file") ?? "build.zig"; | |
| 625 | 175 | const build_file_abs = try os.path.resolve(allocator, ".", build_file); |
| 626 | 176 | defer allocator.free(build_file_abs); |
| 627 | 177 | |
| 628 | const build_file_basename = os.path.basename(build_file_abs); | |
| 629 | const build_file_dirname = os.path.dirname(build_file_abs); | |
| 178 | const build_file_exists = os.File.access(allocator, build_file_abs, os.default_file_mode) catch false; | |
| 630 | 179 | |
| 631 | var full_cache_dir: []u8 = undefined; | |
| 632 | if (cache_dir == null) { | |
| 633 | full_cache_dir = try os.path.join(allocator, build_file_dirname, "zig-cache"); | |
| 634 | } else { | |
| 635 | full_cache_dir = try os.path.resolve(allocator, ".", ??cache_dir, full_cache_dir); | |
| 636 | } | |
| 637 | defer allocator.free(full_cache_dir); | |
| 180 | if (flags.present("init")) { | |
| 181 | if (build_file_exists) { | |
| 182 | try stderr.print("build.zig already exists\n"); | |
| 183 | os.exit(1); | |
| 184 | } | |
| 638 | 185 | |
| 639 | const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build"); | |
| 640 | defer allocator.free(path_to_build_exe); | |
| 641 | // codegen_set_cache_dir(g, full_cache_dir) | |
| 186 | // need a new scope for proper defer scope finalization on exit | |
| 187 | { | |
| 188 | const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig"); | |
| 189 | defer allocator.free(build_template_path); | |
| 642 | 190 | |
| 643 | args.items[0] = path_to_build_exe; | |
| 644 | args.items[1] = zig_exe_path; | |
| 645 | args.items[2] = build_file_dirname; | |
| 646 | args.items[3] = full_cache_dir; | |
| 191 | try os.copyFile(allocator, build_template_path, build_file_abs); | |
| 192 | try stderr.print("wrote build.zig template\n"); | |
| 193 | } | |
| 647 | 194 | |
| 648 | var build_file_exists: bool = undefined; | |
| 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; | |
| 195 | os.exit(0); | |
| 654 | 196 | } |
| 655 | 197 | |
| 656 | if (!build_file_exists and asked_for_help) { | |
| 657 | // TODO(bnoordhuis) Print help message from std/special/build_runner.zig | |
| 658 | return; | |
| 198 | if (!build_file_exists) { | |
| 199 | try stderr.write(missing_build_file); | |
| 200 | os.exit(1); | |
| 659 | 201 | } |
| 660 | 202 | |
| 661 | if (!build_file_exists and asked_for_init) { | |
| 662 | const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig"); | |
| 663 | defer allocator.free(build_template_path); | |
| 664 | ||
| 665 | var srcfile = try os.File.openRead(allocator, build_template_path); | |
| 666 | defer srcfile.close(); | |
| 203 | // TODO: Invoke build.zig entrypoint directly? | |
| 204 | var zig_exe_path = try os.selfExePath(allocator); | |
| 205 | defer allocator.free(zig_exe_path); | |
| 667 | 206 | |
| 668 | var dstfile = try os.File.openWrite(allocator, build_file_abs); | |
| 669 | defer dstfile.close(); | |
| 207 | var build_args = ArrayList([]const u8).init(allocator); | |
| 208 | defer build_args.deinit(); | |
| 670 | 209 | |
| 671 | while (true) { | |
| 672 | var buffer: [4096]u8 = undefined; | |
| 673 | const n = try srcfile.read(buffer[0..]); | |
| 674 | if (n == 0) break; | |
| 675 | try dstfile.write(buffer[0..n]); | |
| 676 | } | |
| 210 | const build_file_basename = os.path.basename(build_file_abs); | |
| 211 | const build_file_dirname = os.path.dirname(build_file_abs); | |
| 677 | 212 | |
| 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); | |
| 680 | 220 | |
| 681 | if (!build_file_exists) { | |
| 682 | warn( | |
| 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 | } | |
| 221 | const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build"); | |
| 222 | defer allocator.free(path_to_build_exe); | |
| 691 | 223 | |
| 692 | // codegen_build(g) | |
| 693 | // codegen_link(g, path_to_build_exe) | |
| 694 | // codegen_destroy(g) | |
| 224 | try build_args.append(path_to_build_exe); | |
| 225 | try build_args.append(zig_exe_path); | |
| 226 | try build_args.append(build_file_dirname); | |
| 227 | try build_args.append(full_cache_dir); | |
| 695 | 228 | |
| 696 | var proc = try os.ChildProcess.init(args.toSliceConst(), allocator); | |
| 229 | var proc = try os.ChildProcess.init(build_args.toSliceConst(), allocator); | |
| 697 | 230 | defer proc.deinit(); |
| 698 | 231 | |
| 699 | 232 | var term = try proc.spawnAndWait(); |
| 700 | 233 | switch (term) { |
| 701 | 234 | os.ChildProcess.Term.Exited => |status| { |
| 702 | 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 | 237 | os.exit(1); |
| 705 | 238 | } |
| 706 | 239 | }, |
| 707 | 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 | 242 | os.exit(1); |
| 710 | 243 | }, |
| 711 | 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 | 246 | os.exit(1); |
| 714 | 247 | }, |
| 715 | 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 | ||
| 257 | const 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 | ||
| 322 | const 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 | ||
| 380 | fn 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 | 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 | } |
| 721 | 614 | |
| 722 | fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void { | |
| 723 | for (file_paths) |file_path| { | |
| 615 | fn cmdBuildExe(allocator: &Allocator, args: []const []const u8) !void { | |
| 616 | try buildOutputType(allocator, args, Module.Kind.Exe); | |
| 617 | } | |
| 618 | ||
| 619 | // cmd:build-lib /////////////////////////////////////////////////////////////////////////////////// | |
| 620 | ||
| 621 | fn cmdBuildLib(allocator: &Allocator, args: []const []const u8) !void { | |
| 622 | try buildOutputType(allocator, args, Module.Kind.Lib); | |
| 623 | } | |
| 624 | ||
| 625 | // cmd:build-obj /////////////////////////////////////////////////////////////////////////////////// | |
| 626 | ||
| 627 | fn cmdBuildObj(allocator: &Allocator, args: []const []const u8) !void { | |
| 628 | try buildOutputType(allocator, args, Module.Kind.Obj); | |
| 629 | } | |
| 630 | ||
| 631 | // cmd:fmt ///////////////////////////////////////////////////////////////////////////////////////// | |
| 632 | ||
| 633 | const 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 | ||
| 645 | const args_fmt_spec = []Flag { | |
| 646 | Flag.Bool("--help"), | |
| 647 | Flag.Bool("--keep-backups"), | |
| 648 | }; | |
| 649 | ||
| 650 | fn 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 | 665 | var file = try os.File.openRead(allocator, file_path); |
| 725 | 666 | defer file.close(); |
| 726 | 667 | |
| 727 | 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 | 670 | continue; |
| 730 | 671 | }; |
| 731 | 672 | defer allocator.free(source_code); |
| 732 | 673 | |
| 733 | var tokenizer = std.zig.Tokenizer.init(source_code); | |
| 734 | var parser = std.zig.Parser.init(&tokenizer, allocator, file_path); | |
| 735 | defer parser.deinit(); | |
| 736 | ||
| 737 | var tree = try parser.parse(); | |
| 674 | var tree = std.zig.parse(allocator, source_code) catch |err| { | |
| 675 | try stderr.print("error parsing file '{}': {}\n", file_path, err); | |
| 676 | continue; | |
| 677 | }; | |
| 738 | 678 | defer tree.deinit(); |
| 739 | 679 | |
| 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 | 709 | const baf = try io.BufferedAtomicFile.create(allocator, file_path); |
| 741 | 710 | defer baf.destroy(); |
| 742 | 711 | |
| 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 | } |
| 746 | 716 | |
| 747 | /// Caller must free result | |
| 748 | fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) ![]u8 { | |
| 749 | if (zig_install_prefix_arg) |zig_install_prefix| { | |
| 750 | return testZigInstallPrefix(allocator, zig_install_prefix) catch |err| { | |
| 751 | warn("No Zig installation found at prefix {}: {}\n", zig_install_prefix_arg, @errorName(err)); | |
| 752 | return error.ZigInstallationNotFound; | |
| 753 | }; | |
| 754 | } else { | |
| 755 | return findZigLibDir(allocator) catch |err| { | |
| 756 | warn("Unable to find zig lib directory: {}.\nReinstall Zig or use --zig-install-prefix.\n", | |
| 757 | @errorName(err)); | |
| 758 | return error.ZigLibDirNotFound; | |
| 759 | }; | |
| 717 | // cmd:targets ///////////////////////////////////////////////////////////////////////////////////// | |
| 718 | ||
| 719 | fn cmdTargets(allocator: &Allocator, args: []const []const u8) !void { | |
| 720 | try stdout.write("Architectures:\n"); | |
| 721 | { | |
| 722 | comptime var i: usize = 0; | |
| 723 | inline while (i < @memberCount(builtin.Arch)) : (i += 1) { | |
| 724 | comptime const arch_tag = @memberName(builtin.Arch, i); | |
| 725 | // NOTE: Cannot use empty string, see #918. | |
| 726 | comptime const native_str = | |
| 727 | if (comptime mem.eql(u8, arch_tag, @tagName(builtin.arch))) " (native)\n" else "\n"; | |
| 728 | ||
| 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 | } |
| 762 | 761 | |
| 763 | /// Caller must free result | |
| 764 | fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 { | |
| 765 | const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig"); | |
| 766 | errdefer allocator.free(test_zig_dir); | |
| 762 | // cmd:version ///////////////////////////////////////////////////////////////////////////////////// | |
| 763 | ||
| 764 | fn cmdVersion(allocator: &Allocator, args: []const []const u8) !void { | |
| 765 | try stdout.print("{}\n", std.cstr.toSliceConst(c.ZIG_VERSION_STRING)); | |
| 766 | } | |
| 767 | ||
| 768 | // cmd:test //////////////////////////////////////////////////////////////////////////////////////// | |
| 769 | ||
| 770 | const usage_test = | |
| 771 | \\usage: zig test [file]... | |
| 772 | \\ | |
| 773 | \\Options: | |
| 774 | \\ --help Print this help and exit | |
| 775 | \\ | |
| 776 | \\ | |
| 777 | ; | |
| 778 | ||
| 779 | const args_test_spec = []Flag { | |
| 780 | Flag.Bool("--help"), | |
| 781 | }; | |
| 782 | ||
| 783 | ||
| 784 | fn cmdTest(allocator: &Allocator, args: []const []const u8) !void { | |
| 785 | var flags = try Args.parse(allocator, args_build_spec, args); | |
| 786 | defer flags.deinit(); | |
| 767 | 787 | |
| 768 | const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig"); | |
| 769 | defer allocator.free(test_index_file); | |
| 788 | if (flags.present("help")) { | |
| 789 | try stderr.write(usage_test); | |
| 790 | os.exit(0); | |
| 791 | } | |
| 770 | 792 | |
| 771 | var file = try os.File.openRead(allocator, test_index_file); | |
| 772 | file.close(); | |
| 793 | if (flags.positionals.len != 1) { | |
| 794 | try stderr.write("expected exactly one zig source file\n"); | |
| 795 | os.exit(1); | |
| 796 | } | |
| 773 | 797 | |
| 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 | } |
| 776 | 803 | |
| 777 | /// Caller must free result | |
| 778 | fn findZigLibDir(allocator: &mem.Allocator) ![]u8 { | |
| 779 | const self_exe_path = try os.selfExeDirPath(allocator); | |
| 780 | defer allocator.free(self_exe_path); | |
| 804 | // cmd:run ///////////////////////////////////////////////////////////////////////////////////////// | |
| 805 | ||
| 806 | // Run should be simple and not expose the full set of arguments provided by build-exe. If specific | |
| 807 | // build requirements are need, the user should `build-exe` then `run` manually. | |
| 808 | const usage_run = | |
| 809 | \\usage: zig run [file] -- <runtime args> | |
| 810 | \\ | |
| 811 | \\Options: | |
| 812 | \\ --help Print this help and exit | |
| 813 | \\ | |
| 814 | \\ | |
| 815 | ; | |
| 816 | ||
| 817 | const args_run_spec = []Flag { | |
| 818 | Flag.Bool("--help"), | |
| 819 | }; | |
| 820 | ||
| 781 | 821 | |
| 782 | var cur_path: []const u8 = self_exe_path; | |
| 783 | while (true) { | |
| 784 | const test_dir = os.path.dirname(cur_path); | |
| 822 | fn cmdRun(allocator: &Allocator, args: []const []const u8) !void { | |
| 823 | var compile_args = args; | |
| 824 | var runtime_args: []const []const u8 = []const []const u8 {}; | |
| 785 | 825 | |
| 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 | 830 | break; |
| 788 | 831 | } |
| 832 | } | |
| 833 | var flags = try Args.parse(allocator, args_run_spec, compile_args); | |
| 834 | defer flags.deinit(); | |
| 789 | 835 | |
| 790 | return testZigInstallPrefix(allocator, test_dir) catch |err| { | |
| 791 | cur_path = test_dir; | |
| 792 | continue; | |
| 793 | }; | |
| 836 | if (flags.present("help")) { | |
| 837 | try stderr.write(usage_run); | |
| 838 | os.exit(0); | |
| 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 | ||
| 854 | const 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 | ||
| 865 | const 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 | ||
| 872 | fn 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 | ||
| 918 | fn cmdHelp(allocator: &Allocator, args: []const []const u8) !void { | |
| 919 | try stderr.write(usage); | |
| 920 | } | |
| 921 | ||
| 922 | // cmd:zen ///////////////////////////////////////////////////////////////////////////////////////// | |
| 923 | ||
| 924 | const 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 | ||
| 941 | fn cmdZen(allocator: &Allocator, args: []const []const u8) !void { | |
| 942 | try stdout.write(info_zen); | |
| 943 | } | |
| 944 | ||
| 945 | // cmd:internal //////////////////////////////////////////////////////////////////////////////////// | |
| 946 | ||
| 947 | const usage_internal = | |
| 948 | \\usage: zig internal [subcommand] | |
| 949 | \\ | |
| 950 | \\Sub-Commands: | |
| 951 | \\ build-info Print static compiler build-info | |
| 952 | \\ | |
| 953 | \\ | |
| 954 | ; | |
| 955 | ||
| 956 | fn 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 | }; | |
| 795 | 965 | |
| 796 | // TODO look in hard coded installation path from configuration | |
| 797 | //if (ZIG_INSTALL_PREFIX != nullptr) { | |
| 798 | // if (test_zig_install_prefix(buf_create_from_str(ZIG_INSTALL_PREFIX), out_path)) { | |
| 799 | // return 0; | |
| 800 | // } | |
| 801 | //} | |
| 966 | for (sub_commands) |sub_command| { | |
| 967 | if (mem.eql(u8, sub_command.name, args[0])) { | |
| 968 | try sub_command.exec(allocator, args[1..]); | |
| 969 | return; | |
| 970 | } | |
| 971 | } | |
| 802 | 972 | |
| 803 | return error.FileNotFound; | |
| 973 | try stderr.print("unknown sub command: {}\n\n", args[0]); | |
| 974 | try stderr.write(usage_internal); | |
| 975 | } | |
| 976 | ||
| 977 | fn 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 | 8 | const builtin = @import("builtin"); |
| 9 | 9 | const Target = @import("target.zig").Target; |
| 10 | 10 | const warn = std.debug.warn; |
| 11 | const Tokenizer = std.zig.Tokenizer; | |
| 12 | 11 | const Token = std.zig.Token; |
| 13 | const Parser = std.zig.Parser; | |
| 14 | 12 | const ArrayList = std.ArrayList; |
| 15 | 13 | |
| 16 | 14 | pub const Module = struct { |
| ... | ... | @@ -109,6 +107,29 @@ pub const Module = struct { |
| 109 | 107 | LlvmIr, |
| 110 | 108 | }; |
| 111 | 109 | |
| 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 | 133 | pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target, |
| 113 | 134 | 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 | 244 | |
| 224 | 245 | warn("{}", source_code); |
| 225 | 246 | |
| 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 | 247 | warn("====parse:====\n"); |
| 239 | 248 | |
| 240 | var tokenizer = Tokenizer.init(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(); | |
| 249 | var tree = try std.zig.parse(self.allocator, source_code); | |
| 245 | 250 | defer tree.deinit(); |
| 246 | 251 | |
| 247 | 252 | var stderr_file = try std.io.getStdErr(); |
| 248 | 253 | var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file); |
| 249 | 254 | const out_stream = &stderr_file_out_stream.stream; |
| 250 | try parser.renderAst(out_stream, tree.root_node); | |
| 251 | 255 | |
| 252 | 256 | warn("====fmt:====\n"); |
| 253 | try parser.renderSource(out_stream, tree.root_node); | |
| 257 | try std.zig.render(self.allocator, out_stream, &tree); | |
| 254 | 258 | |
| 255 | 259 | warn("====ir:====\n"); |
| 256 | 260 | warn("TODO\n\n"); |
src/all_types.hpp+90-15| ... | ... | @@ -359,7 +359,6 @@ enum NodeType { |
| 359 | 359 | NodeTypeRoot, |
| 360 | 360 | NodeTypeFnProto, |
| 361 | 361 | NodeTypeFnDef, |
| 362 | NodeTypeFnDecl, | |
| 363 | 362 | NodeTypeParamDecl, |
| 364 | 363 | NodeTypeBlock, |
| 365 | 364 | NodeTypeGroupedExpr, |
| ... | ... | @@ -453,10 +452,6 @@ struct AstNodeFnDef { |
| 453 | 452 | AstNode *body; |
| 454 | 453 | }; |
| 455 | 454 | |
| 456 | struct AstNodeFnDecl { | |
| 457 | AstNode *fn_proto; | |
| 458 | }; | |
| 459 | ||
| 460 | 455 | struct AstNodeParamDecl { |
| 461 | 456 | Buf *name; |
| 462 | 457 | AstNode *type; |
| ... | ... | @@ -713,10 +708,6 @@ struct AstNodeSwitchRange { |
| 713 | 708 | AstNode *end; |
| 714 | 709 | }; |
| 715 | 710 | |
| 716 | struct AstNodeLabel { | |
| 717 | Buf *name; | |
| 718 | }; | |
| 719 | ||
| 720 | 711 | struct AstNodeCompTime { |
| 721 | 712 | AstNode *expr; |
| 722 | 713 | }; |
| ... | ... | @@ -876,6 +867,7 @@ struct AstNodeAwaitExpr { |
| 876 | 867 | }; |
| 877 | 868 | |
| 878 | 869 | struct AstNodeSuspend { |
| 870 | Buf *name; | |
| 879 | 871 | AstNode *block; |
| 880 | 872 | AstNode *promise_symbol; |
| 881 | 873 | }; |
| ... | ... | @@ -892,7 +884,6 @@ struct AstNode { |
| 892 | 884 | union { |
| 893 | 885 | AstNodeRoot root; |
| 894 | 886 | AstNodeFnDef fn_def; |
| 895 | AstNodeFnDecl fn_decl; | |
| 896 | 887 | AstNodeFnProto fn_proto; |
| 897 | 888 | AstNodeParamDecl param_decl; |
| 898 | 889 | AstNodeBlock block; |
| ... | ... | @@ -917,7 +908,6 @@ struct AstNode { |
| 917 | 908 | AstNodeSwitchExpr switch_expr; |
| 918 | 909 | AstNodeSwitchProng switch_prong; |
| 919 | 910 | AstNodeSwitchRange switch_range; |
| 920 | AstNodeLabel label; | |
| 921 | 911 | AstNodeCompTime comptime_expr; |
| 922 | 912 | AstNodeAsmExpr asm_expr; |
| 923 | 913 | AstNodeFieldAccessExpr field_access_expr; |
| ... | ... | @@ -1302,6 +1292,8 @@ enum BuiltinFnId { |
| 1302 | 1292 | BuiltinFnIdMemberCount, |
| 1303 | 1293 | BuiltinFnIdMemberType, |
| 1304 | 1294 | BuiltinFnIdMemberName, |
| 1295 | BuiltinFnIdField, | |
| 1296 | BuiltinFnIdTypeInfo, | |
| 1305 | 1297 | BuiltinFnIdTypeof, |
| 1306 | 1298 | BuiltinFnIdAddWithOverflow, |
| 1307 | 1299 | BuiltinFnIdSubWithOverflow, |
| ... | ... | @@ -1321,13 +1313,15 @@ enum BuiltinFnId { |
| 1321 | 1313 | BuiltinFnIdReturnAddress, |
| 1322 | 1314 | BuiltinFnIdFrameAddress, |
| 1323 | 1315 | BuiltinFnIdEmbedFile, |
| 1324 | BuiltinFnIdCmpExchange, | |
| 1316 | BuiltinFnIdCmpxchgWeak, | |
| 1317 | BuiltinFnIdCmpxchgStrong, | |
| 1325 | 1318 | BuiltinFnIdFence, |
| 1326 | 1319 | BuiltinFnIdDivExact, |
| 1327 | 1320 | BuiltinFnIdDivTrunc, |
| 1328 | 1321 | BuiltinFnIdDivFloor, |
| 1329 | 1322 | BuiltinFnIdRem, |
| 1330 | 1323 | BuiltinFnIdMod, |
| 1324 | BuiltinFnIdSqrt, | |
| 1331 | 1325 | BuiltinFnIdTruncate, |
| 1332 | 1326 | BuiltinFnIdIntType, |
| 1333 | 1327 | BuiltinFnIdSetCold, |
| ... | ... | @@ -1357,6 +1351,7 @@ enum BuiltinFnId { |
| 1357 | 1351 | BuiltinFnIdExport, |
| 1358 | 1352 | BuiltinFnIdErrorReturnTrace, |
| 1359 | 1353 | BuiltinFnIdAtomicRmw, |
| 1354 | BuiltinFnIdAtomicLoad, | |
| 1360 | 1355 | }; |
| 1361 | 1356 | |
| 1362 | 1357 | struct BuiltinFnEntry { |
| ... | ... | @@ -1424,6 +1419,7 @@ enum ZigLLVMFnId { |
| 1424 | 1419 | ZigLLVMFnIdOverflowArithmetic, |
| 1425 | 1420 | ZigLLVMFnIdFloor, |
| 1426 | 1421 | ZigLLVMFnIdCeil, |
| 1422 | ZigLLVMFnIdSqrt, | |
| 1427 | 1423 | }; |
| 1428 | 1424 | |
| 1429 | 1425 | enum AddSubMul { |
| ... | ... | @@ -1444,7 +1440,7 @@ struct ZigLLVMFnKey { |
| 1444 | 1440 | } clz; |
| 1445 | 1441 | struct { |
| 1446 | 1442 | uint32_t bit_count; |
| 1447 | } floor_ceil; | |
| 1443 | } floating; | |
| 1448 | 1444 | struct { |
| 1449 | 1445 | AddSubMul add_sub_mul; |
| 1450 | 1446 | uint32_t bit_count; |
| ... | ... | @@ -1465,6 +1461,7 @@ enum BuildMode { |
| 1465 | 1461 | BuildModeDebug, |
| 1466 | 1462 | BuildModeFastRelease, |
| 1467 | 1463 | BuildModeSafeRelease, |
| 1464 | BuildModeSmallRelease, | |
| 1468 | 1465 | }; |
| 1469 | 1466 | |
| 1470 | 1467 | enum EmitFileType { |
| ... | ... | @@ -1510,6 +1507,7 @@ struct CodeGen { |
| 1510 | 1507 | HashMap<Buf *, AstNode *, buf_hash, buf_eql_buf> exported_symbol_names; |
| 1511 | 1508 | HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> external_prototypes; |
| 1512 | 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; | |
| 1513 | 1511 | |
| 1514 | 1512 | |
| 1515 | 1513 | ZigList<ImportTableEntry *> import_queue; |
| ... | ... | @@ -1656,6 +1654,8 @@ struct CodeGen { |
| 1656 | 1654 | LLVMValueRef coro_save_fn_val; |
| 1657 | 1655 | LLVMValueRef coro_promise_fn_val; |
| 1658 | 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 | 1659 | bool error_during_imports; |
| 1660 | 1660 | |
| 1661 | 1661 | const char **clang_argv; |
| ... | ... | @@ -1709,6 +1709,8 @@ struct CodeGen { |
| 1709 | 1709 | ZigList<ZigLLVMDIType **> error_di_types; |
| 1710 | 1710 | |
| 1711 | 1711 | ZigList<Buf *> forbidden_libs; |
| 1712 | ||
| 1713 | bool no_rosegment_workaround; | |
| 1712 | 1714 | }; |
| 1713 | 1715 | |
| 1714 | 1716 | enum VarLinkage { |
| ... | ... | @@ -1759,6 +1761,7 @@ enum ScopeId { |
| 1759 | 1761 | ScopeIdVarDecl, |
| 1760 | 1762 | ScopeIdCImport, |
| 1761 | 1763 | ScopeIdLoop, |
| 1764 | ScopeIdSuspend, | |
| 1762 | 1765 | ScopeIdFnDef, |
| 1763 | 1766 | ScopeIdCompTime, |
| 1764 | 1767 | ScopeIdCoroPrelude, |
| ... | ... | @@ -1854,6 +1857,17 @@ struct ScopeLoop { |
| 1854 | 1857 | ZigList<IrBasicBlock *> *incoming_blocks; |
| 1855 | 1858 | }; |
| 1856 | 1859 | |
| 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. | |
| 1863 | struct ScopeSuspend { | |
| 1864 | Scope base; | |
| 1865 | ||
| 1866 | Buf *name; | |
| 1867 | IrBasicBlock *resume_block; | |
| 1868 | bool reported_err; | |
| 1869 | }; | |
| 1870 | ||
| 1857 | 1871 | // This scope is created for a comptime expression. |
| 1858 | 1872 | // NodeTypeCompTime, NodeTypeSwitchExpr |
| 1859 | 1873 | struct ScopeCompTime { |
| ... | ... | @@ -2025,6 +2039,7 @@ enum IrInstructionId { |
| 2025 | 2039 | IrInstructionIdTagType, |
| 2026 | 2040 | IrInstructionIdFieldParentPtr, |
| 2027 | 2041 | IrInstructionIdOffsetOf, |
| 2042 | IrInstructionIdTypeInfo, | |
| 2028 | 2043 | IrInstructionIdTypeId, |
| 2029 | 2044 | IrInstructionIdSetEvalBranchQuota, |
| 2030 | 2045 | IrInstructionIdPtrTypeOf, |
| ... | ... | @@ -2050,10 +2065,14 @@ enum IrInstructionId { |
| 2050 | 2065 | IrInstructionIdCoroPromise, |
| 2051 | 2066 | IrInstructionIdCoroAllocHelper, |
| 2052 | 2067 | IrInstructionIdAtomicRmw, |
| 2068 | IrInstructionIdAtomicLoad, | |
| 2053 | 2069 | IrInstructionIdPromiseResultType, |
| 2054 | 2070 | IrInstructionIdAwaitBookkeeping, |
| 2055 | 2071 | IrInstructionIdSaveErrRetAddr, |
| 2056 | 2072 | IrInstructionIdAddImplicitReturnType, |
| 2073 | IrInstructionIdMergeErrRetTraces, | |
| 2074 | IrInstructionIdMarkErrRetTracePtr, | |
| 2075 | IrInstructionIdSqrt, | |
| 2057 | 2076 | }; |
| 2058 | 2077 | |
| 2059 | 2078 | struct IrInstruction { |
| ... | ... | @@ -2210,7 +2229,8 @@ struct IrInstructionFieldPtr { |
| 2210 | 2229 | IrInstruction base; |
| 2211 | 2230 | |
| 2212 | 2231 | IrInstruction *container_ptr; |
| 2213 | Buf *field_name; | |
| 2232 | Buf *field_name_buffer; | |
| 2233 | IrInstruction *field_name_expr; | |
| 2214 | 2234 | bool is_const; |
| 2215 | 2235 | }; |
| 2216 | 2236 | |
| ... | ... | @@ -2529,6 +2549,7 @@ struct IrInstructionEmbedFile { |
| 2529 | 2549 | struct IrInstructionCmpxchg { |
| 2530 | 2550 | IrInstruction base; |
| 2531 | 2551 | |
| 2552 | IrInstruction *type_value; | |
| 2532 | 2553 | IrInstruction *ptr; |
| 2533 | 2554 | IrInstruction *cmp_value; |
| 2534 | 2555 | IrInstruction *new_value; |
| ... | ... | @@ -2536,8 +2557,13 @@ struct IrInstructionCmpxchg { |
| 2536 | 2557 | IrInstruction *failure_order_value; |
| 2537 | 2558 | |
| 2538 | 2559 | // if this instruction gets to runtime then we know these values: |
| 2560 | TypeTableEntry *type; | |
| 2539 | 2561 | AtomicOrder success_order; |
| 2540 | 2562 | AtomicOrder failure_order; |
| 2563 | ||
| 2564 | bool is_weak; | |
| 2565 | ||
| 2566 | LLVMValueRef tmp_ptr; | |
| 2541 | 2567 | }; |
| 2542 | 2568 | |
| 2543 | 2569 | struct IrInstructionFence { |
| ... | ... | @@ -2835,6 +2861,12 @@ struct IrInstructionOffsetOf { |
| 2835 | 2861 | IrInstruction *field_name; |
| 2836 | 2862 | }; |
| 2837 | 2863 | |
| 2864 | struct IrInstructionTypeInfo { | |
| 2865 | IrInstruction base; | |
| 2866 | ||
| 2867 | IrInstruction *type_value; | |
| 2868 | }; | |
| 2869 | ||
| 2838 | 2870 | struct IrInstructionTypeId { |
| 2839 | 2871 | IrInstruction base; |
| 2840 | 2872 | |
| ... | ... | @@ -2892,6 +2924,11 @@ struct IrInstructionExport { |
| 2892 | 2924 | |
| 2893 | 2925 | struct IrInstructionErrorReturnTrace { |
| 2894 | 2926 | IrInstruction base; |
| 2927 | ||
| 2928 | enum Nullable { | |
| 2929 | Null, | |
| 2930 | NonNull, | |
| 2931 | } nullable; | |
| 2895 | 2932 | }; |
| 2896 | 2933 | |
| 2897 | 2934 | struct IrInstructionErrorUnion { |
| ... | ... | @@ -3002,6 +3039,15 @@ struct IrInstructionAtomicRmw { |
| 3002 | 3039 | AtomicOrder resolved_ordering; |
| 3003 | 3040 | }; |
| 3004 | 3041 | |
| 3042 | struct IrInstructionAtomicLoad { | |
| 3043 | IrInstruction base; | |
| 3044 | ||
| 3045 | IrInstruction *operand_type; | |
| 3046 | IrInstruction *ptr; | |
| 3047 | IrInstruction *ordering; | |
| 3048 | AtomicOrder resolved_ordering; | |
| 3049 | }; | |
| 3050 | ||
| 3005 | 3051 | struct IrInstructionPromiseResultType { |
| 3006 | 3052 | IrInstruction base; |
| 3007 | 3053 | |
| ... | ... | @@ -3024,6 +3070,27 @@ struct IrInstructionAddImplicitReturnType { |
| 3024 | 3070 | IrInstruction *value; |
| 3025 | 3071 | }; |
| 3026 | 3072 | |
| 3073 | struct 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 | ||
| 3081 | struct IrInstructionMarkErrRetTracePtr { | |
| 3082 | IrInstruction base; | |
| 3083 | ||
| 3084 | IrInstruction *err_ret_trace_ptr; | |
| 3085 | }; | |
| 3086 | ||
| 3087 | struct IrInstructionSqrt { | |
| 3088 | IrInstruction base; | |
| 3089 | ||
| 3090 | IrInstruction *type; | |
| 3091 | IrInstruction *op; | |
| 3092 | }; | |
| 3093 | ||
| 3027 | 3094 | static const size_t slice_ptr_index = 0; |
| 3028 | 3095 | static const size_t slice_len_index = 1; |
| 3029 | 3096 | |
| ... | ... | @@ -3033,10 +3100,18 @@ static const size_t maybe_null_index = 1; |
| 3033 | 3100 | static const size_t err_union_err_index = 0; |
| 3034 | 3101 | static const size_t err_union_payload_index = 1; |
| 3035 | 3102 | |
| 3103 | // TODO call graph analysis to find out what this number needs to be for every function | |
| 3104 | static 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 | 3110 | #define ASYNC_ALLOC_FIELD_NAME "allocFn" |
| 3037 | 3111 | #define ASYNC_FREE_FIELD_NAME "freeFn" |
| 3038 | 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 | 3115 | #define RESULT_PTR_FIELD_NAME "result_ptr" |
| 3041 | 3116 | |
| 3042 | 3117 |
src/analyze.cpp+75-26| ... | ... | @@ -156,6 +156,14 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) { |
| 156 | 156 | return scope; |
| 157 | 157 | } |
| 158 | 158 | |
| 159 | ScopeSuspend *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 | ||
| 159 | 167 | ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) { |
| 160 | 168 | ScopeFnDef *scope = allocate<ScopeFnDef>(1); |
| 161 | 169 | init_scope(&scope->base, ScopeIdFnDef, node, parent); |
| ... | ... | @@ -468,10 +476,30 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) |
| 468 | 476 | |
| 469 | 477 | TypeTableEntry *awaiter_handle_type = get_maybe_type(g, g->builtin_types.entry_promise); |
| 470 | 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}; | |
| 472 | TypeTableEntry *field_types[] = {awaiter_handle_type, return_type, result_ptr_type}; | |
| 479 | ||
| 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 | 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); | |
| 475 | 503 | |
| 476 | 504 | return_type->promise_frame_parent = entry; |
| 477 | 505 | return entry; |
| ... | ... | @@ -1230,7 +1258,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou |
| 1230 | 1258 | } |
| 1231 | 1259 | |
| 1232 | 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 | 1262 | fn_type_id->next_param_index = 0; |
| 1235 | 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 | 2325 | HashMap<BigInt, AstNode *, bigint_hash, bigint_eql> occupied_tag_values = {}; |
| 2298 | 2326 | occupied_tag_values.init(field_count); |
| 2299 | 2327 | |
| 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 | } | |
| 2301 | 2334 | |
| 2335 | // TODO: Are extern enums allowed to have an init_arg_expr? | |
| 2302 | 2336 | if (decl_node->data.container_decl.init_arg_expr != nullptr) { |
| 2303 | 2337 | TypeTableEntry *wanted_tag_int_type = analyze_type_expr(g, scope, decl_node->data.container_decl.init_arg_expr); |
| 2304 | 2338 | if (type_is_invalid(wanted_tag_int_type)) { |
| ... | ... | @@ -3216,7 +3250,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { |
| 3216 | 3250 | break; |
| 3217 | 3251 | case NodeTypeContainerDecl: |
| 3218 | 3252 | case NodeTypeParamDecl: |
| 3219 | case NodeTypeFnDecl: | |
| 3220 | 3253 | case NodeTypeReturnExpr: |
| 3221 | 3254 | case NodeTypeDefer: |
| 3222 | 3255 | case NodeTypeBlock: |
| ... | ... | @@ -3597,6 +3630,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) { |
| 3597 | 3630 | case ScopeIdVarDecl: |
| 3598 | 3631 | case ScopeIdCImport: |
| 3599 | 3632 | case ScopeIdLoop: |
| 3633 | case ScopeIdSuspend: | |
| 3600 | 3634 | case ScopeIdCompTime: |
| 3601 | 3635 | case ScopeIdCoroPrelude: |
| 3602 | 3636 | scope = scope->parent; |
| ... | ... | @@ -4278,7 +4312,8 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { |
| 4278 | 4312 | static ZigWindowsSDK *get_windows_sdk(CodeGen *g) { |
| 4279 | 4313 | if (g->win_sdk == nullptr) { |
| 4280 | 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 | 4319 | assert(g->win_sdk != nullptr); |
| ... | ... | @@ -4380,7 +4415,8 @@ void find_libc_include_path(CodeGen *g) { |
| 4380 | 4415 | ZigWindowsSDK *sdk = get_windows_sdk(g); |
| 4381 | 4416 | g->libc_include_dir = buf_alloc(); |
| 4382 | 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 | 4421 | } else if (g->zig_target.os == OsLinux) { |
| 4386 | 4422 | g->libc_include_dir = get_linux_libc_include_path(); |
| ... | ... | @@ -4402,24 +4438,33 @@ void find_libc_lib_path(CodeGen *g) { |
| 4402 | 4438 | if (g->zig_target.os == OsWindows) { |
| 4403 | 4439 | ZigWindowsSDK *sdk = get_windows_sdk(g); |
| 4404 | 4440 | |
| 4405 | Buf* vc_lib_dir = buf_alloc(); | |
| 4406 | if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) { | |
| 4407 | zig_panic("Unable to determine vcruntime path."); | |
| 4441 | if (g->msvc_lib_dir == nullptr) { | |
| 4442 | Buf* vc_lib_dir = buf_alloc(); | |
| 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 | } |
| 4409 | 4449 | |
| 4410 | Buf* ucrt_lib_path = buf_alloc(); | |
| 4411 | if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) { | |
| 4412 | zig_panic("Unable to determine ucrt path."); | |
| 4450 | if (g->libc_lib_dir == nullptr) { | |
| 4451 | Buf* ucrt_lib_path = buf_alloc(); | |
| 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 | } |
| 4414 | 4458 | |
| 4415 | Buf* kern_lib_path = buf_alloc(); | |
| 4416 | if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) { | |
| 4417 | zig_panic("Unable to determine kernel32 path."); | |
| 4459 | if (g->kernel32_lib_dir == nullptr) { | |
| 4460 | Buf* kern_lib_path = buf_alloc(); | |
| 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 | } |
| 4419 | 4467 | |
| 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 | 4468 | } else if (g->zig_target.os == OsLinux) { |
| 4424 | 4469 | g->libc_lib_dir = get_linux_libc_lib_path("crt1.o"); |
| 4425 | 4470 | } else { |
| ... | ... | @@ -5782,9 +5827,11 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) { |
| 5782 | 5827 | case ZigLLVMFnIdClz: |
| 5783 | 5828 | return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817; |
| 5784 | 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 | 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 | 5835 | case ZigLLVMFnIdOverflowArithmetic: |
| 5789 | 5836 | return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) + |
| 5790 | 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 | 5850 | return a.data.clz.bit_count == b.data.clz.bit_count; |
| 5804 | 5851 | case ZigLLVMFnIdFloor: |
| 5805 | 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 | 5855 | case ZigLLVMFnIdOverflowArithmetic: |
| 5808 | 5856 | return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) && |
| 5809 | 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 | 5931 | return array_length(all_type_ids); |
| 5884 | 5932 | } |
| 5885 | 5933 | |
| 5886 | size_t type_id_index(TypeTableEntryId id) { | |
| 5887 | switch (id) { | |
| 5934 | size_t type_id_index(TypeTableEntry *entry) { | |
| 5935 | switch (entry->id) { | |
| 5888 | 5936 | case TypeTableEntryIdInvalid: |
| 5889 | 5937 | zig_unreachable(); |
| 5890 | 5938 | case TypeTableEntryIdMetaType: |
| ... | ... | @@ -5904,6 +5952,8 @@ size_t type_id_index(TypeTableEntryId id) { |
| 5904 | 5952 | case TypeTableEntryIdArray: |
| 5905 | 5953 | return 7; |
| 5906 | 5954 | case TypeTableEntryIdStruct: |
| 5955 | if (entry->data.structure.is_slice) | |
| 5956 | return 25; | |
| 5907 | 5957 | return 8; |
| 5908 | 5958 | case TypeTableEntryIdNumLitFloat: |
| 5909 | 5959 | return 9; |
| ... | ... | @@ -6089,4 +6139,3 @@ bool type_can_fail(TypeTableEntry *type_entry) { |
| 6089 | 6139 | bool fn_type_can_fail(FnTypeId *fn_type_id) { |
| 6090 | 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 | 104 | Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var); |
| 105 | 105 | ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent); |
| 106 | 106 | ScopeLoop *create_loop_scope(AstNode *node, Scope *parent); |
| 107 | ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent); | |
| 107 | 108 | ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry); |
| 108 | 109 | ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import); |
| 109 | 110 | Scope *create_comptime_scope(AstNode *node, Scope *parent); |
| ... | ... | @@ -173,7 +174,7 @@ void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value); |
| 173 | 174 | const char *type_id_name(TypeTableEntryId id); |
| 174 | 175 | TypeTableEntryId type_id_at_index(size_t index); |
| 175 | 176 | size_t type_id_len(); |
| 176 | size_t type_id_index(TypeTableEntryId id); | |
| 177 | size_t type_id_index(TypeTableEntry *entry); | |
| 177 | 178 | TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id); |
| 178 | 179 | bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry); |
| 179 | 180 | LinkLib *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 | 148 | return "Root"; |
| 149 | 149 | case NodeTypeFnDef: |
| 150 | 150 | return "FnDef"; |
| 151 | case NodeTypeFnDecl: | |
| 152 | return "FnDecl"; | |
| 153 | 151 | case NodeTypeFnProto: |
| 154 | 152 | return "FnProto"; |
| 155 | 153 | case NodeTypeParamDecl: |
| ... | ... | @@ -730,7 +728,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 730 | 728 | render_node_grouped(ar, field_node->data.struct_field.type); |
| 731 | 729 | } |
| 732 | 730 | if (field_node->data.struct_field.value != nullptr) { |
| 733 | fprintf(ar->f, "= "); | |
| 731 | fprintf(ar->f, " = "); | |
| 734 | 732 | render_node_grouped(ar, field_node->data.struct_field.value); |
| 735 | 733 | } |
| 736 | 734 | fprintf(ar->f, ",\n"); |
| ... | ... | @@ -1098,7 +1096,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { |
| 1098 | 1096 | } |
| 1099 | 1097 | break; |
| 1100 | 1098 | } |
| 1101 | case NodeTypeFnDecl: | |
| 1102 | 1099 | case NodeTypeParamDecl: |
| 1103 | 1100 | case NodeTypeTestDecl: |
| 1104 | 1101 | case NodeTypeStructField: |
src/bigfloat.cpp+4| ... | ... | @@ -181,3 +181,7 @@ bool bigfloat_has_fraction(const BigFloat *bigfloat) { |
| 181 | 181 | f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored); |
| 182 | 182 | return !f128M_eq(&floored, &bigfloat->value); |
| 183 | 183 | } |
| 184 | ||
| 185 | void 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 | 42 | void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); |
| 43 | 43 | void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); |
| 44 | 44 | void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2); |
| 45 | void bigfloat_sqrt(BigFloat *dest, const BigFloat *op); | |
| 45 | 46 | void bigfloat_append_buf(Buf *buf, const BigFloat *op); |
| 46 | 47 | Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2); |
| 47 | 48 |
src/bigint.cpp+9-6| ... | ... | @@ -86,6 +86,11 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count) |
| 86 | 86 | size_t digits_to_copy = bit_count / 64; |
| 87 | 87 | size_t leftover_bits = bit_count % 64; |
| 88 | 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 | 94 | dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count); |
| 90 | 95 | for (size_t i = 0; i < digits_to_copy; i += 1) { |
| 91 | 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 | 1259 | bigint_normalize(dest); |
| 1255 | 1260 | return; |
| 1256 | 1261 | } |
| 1257 | // TODO this code path is untested | |
| 1258 | uint64_t first_digit = dest->data.digit; | |
| 1262 | ||
| 1259 | 1263 | dest->digit_count = max(op1->digit_count, op2->digit_count); |
| 1260 | 1264 | dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count); |
| 1261 | dest->data.digits[0] = first_digit; | |
| 1262 | size_t i = 1; | |
| 1265 | ||
| 1266 | size_t i = 0; | |
| 1263 | 1267 | for (; i < op1->digit_count && i < op2->digit_count; i += 1) { |
| 1264 | 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 | 1411 | return; |
| 1408 | 1412 | } |
| 1409 | 1413 | |
| 1410 | // TODO this code path is untested | |
| 1411 | 1414 | size_t digit_shift_count = shift_amt / 64; |
| 1412 | 1415 | size_t leftover_shift_count = shift_amt % 64; |
| 1413 | 1416 | |
| ... | ... | @@ -1422,7 +1425,7 @@ void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) { |
| 1422 | 1425 | uint64_t digit = op1_digits[op_digit_index]; |
| 1423 | 1426 | size_t dest_digit_index = op_digit_index - digit_shift_count; |
| 1424 | 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); | |
| 1426 | 1429 | |
| 1427 | 1430 | if (dest_digit_index == 0) { break; } |
| 1428 | 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 | 88 | g->exported_symbol_names.init(8); |
| 89 | 89 | g->external_prototypes.init(8); |
| 90 | 90 | g->string_literals_table.init(16); |
| 91 | g->type_info_cache.init(32); | |
| 91 | 92 | g->is_test_build = false; |
| 92 | 93 | g->want_h_file = (out_type == OutTypeObj || out_type == OutTypeLib); |
| 93 | 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 | 409 | if (!g->have_err_ret_tracing) { |
| 409 | 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 | 415 | TypeTableEntry *fn_type = fn_table_entry->type_entry; |
| 412 | 416 | if (!fn_type_can_fail(&fn_type->data.fn.fn_type_id)) { |
| 413 | 417 | return UINT32_MAX; |
| ... | ... | @@ -464,7 +468,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 464 | 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)); | |
| 468 | 472 | |
| 469 | 473 | switch (fn_table_entry->fn_inline) { |
| 470 | 474 | case FnInlineAlways: |
| ... | ... | @@ -509,7 +513,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { |
| 509 | 513 | } |
| 510 | 514 | |
| 511 | 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 | 519 | if (want_fn_safety) { |
| 514 | 520 | if (g->libc_link_lib != nullptr) { |
| 515 | 521 | addLLVMFnAttr(fn_table_entry->llvm_value, "sspstrong"); |
| ... | ... | @@ -649,6 +655,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { |
| 649 | 655 | } |
| 650 | 656 | case ScopeIdDeferExpr: |
| 651 | 657 | case ScopeIdLoop: |
| 658 | case ScopeIdSuspend: | |
| 652 | 659 | case ScopeIdCompTime: |
| 653 | 660 | case ScopeIdCoroPrelude: |
| 654 | 661 | return get_di_scope(g, scope->parent); |
| ... | ... | @@ -714,12 +721,12 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry, |
| 714 | 721 | return fn_val; |
| 715 | 722 | } |
| 716 | 723 | |
| 717 | static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) { | |
| 724 | static LLVMValueRef get_float_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) { | |
| 718 | 725 | assert(type_entry->id == TypeTableEntryIdFloat); |
| 719 | 726 | |
| 720 | 727 | ZigLLVMFnKey key = {}; |
| 721 | 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; | |
| 723 | 730 | |
| 724 | 731 | auto existing_entry = g->llvm_fn_table.maybe_get(key); |
| 725 | 732 | if (existing_entry) |
| ... | ... | @@ -730,6 +737,8 @@ static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, Zi |
| 730 | 737 | name = "floor"; |
| 731 | 738 | } else if (fn_id == ZigLLVMFnIdCeil) { |
| 732 | 739 | name = "ceil"; |
| 740 | } else if (fn_id == ZigLLVMFnIdSqrt) { | |
| 741 | name = "sqrt"; | |
| 733 | 742 | } else { |
| 734 | 743 | zig_unreachable(); |
| 735 | 744 | } |
| ... | ... | @@ -812,7 +821,7 @@ static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) { |
| 812 | 821 | } |
| 813 | 822 | |
| 814 | 823 | static 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 | 825 | return false; |
| 817 | 826 | |
| 818 | 827 | // TODO memoize |
| ... | ... | @@ -1114,22 +1123,19 @@ static LLVMValueRef get_return_address_fn_val(CodeGen *g) { |
| 1114 | 1123 | return g->return_address_fn_val; |
| 1115 | 1124 | } |
| 1116 | 1125 | |
| 1117 | static LLVMValueRef get_return_err_fn(CodeGen *g) { | |
| 1118 | if (g->return_err_fn != nullptr) | |
| 1119 | return g->return_err_fn; | |
| 1120 | ||
| 1121 | assert(g->err_tag_type != nullptr); | |
| 1126 | static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) { | |
| 1127 | if (g->add_error_return_trace_addr_fn_val != nullptr) | |
| 1128 | return g->add_error_return_trace_addr_fn_val; | |
| 1122 | 1129 | |
| 1123 | 1130 | LLVMTypeRef arg_types[] = { |
| 1124 | // error return trace pointer | |
| 1125 | 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); | |
| 1128 | 1135 | |
| 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 | 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 address | |
| 1132 | addLLVMFnAttr(fn_val, "cold"); | |
| 1138 | addLLVMFnAttr(fn_val, "alwaysinline"); | |
| 1133 | 1139 | LLVMSetLinkage(fn_val, LLVMInternalLinkage); |
| 1134 | 1140 | LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); |
| 1135 | 1141 | addLLVMFnAttr(fn_val, "nounwind"); |
| ... | ... | @@ -1151,6 +1157,8 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { |
| 1151 | 1157 | // stack_trace.instruction_addresses[stack_trace.index % stack_trace.instruction_addresses.len] = return_address; |
| 1152 | 1158 | |
| 1153 | 1159 | LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0); |
| 1160 | LLVMValueRef address_value = LLVMGetParam(fn_val, 1); | |
| 1161 | ||
| 1154 | 1162 | size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index; |
| 1155 | 1163 | LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, ""); |
| 1156 | 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 | 1180 | LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, ""); |
| 1173 | 1181 | LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, ptr_value, address_indices, 1, ""); |
| 1174 | 1182 | |
| 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 | 1183 | gen_store_untyped(g, address_value, address_slot, 0, false); |
| 1181 | 1184 | |
| 1182 | 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 | 1187 | gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false); |
| 1185 | 1188 | |
| 1186 | 1189 | // return; |
| ... | ... | @@ -1189,6 +1192,187 @@ static LLVMValueRef get_return_err_fn(CodeGen *g) { |
| 1189 | 1192 | LLVMPositionBuilderAtEnd(g->builder, prev_block); |
| 1190 | 1193 | LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); |
| 1191 | 1194 | |
| 1195 | g->add_error_return_trace_addr_fn_val = fn_val; | |
| 1196 | return fn_val; | |
| 1197 | } | |
| 1198 | ||
| 1199 | static 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 | ||
| 1327 | static 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 | 1376 | g->return_err_fn = fn_val; |
| 1193 | 1377 | return fn_val; |
| 1194 | 1378 | } |
| ... | ... | @@ -1641,7 +1825,6 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut |
| 1641 | 1825 | }; |
| 1642 | 1826 | LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1, |
| 1643 | 1827 | get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, ""); |
| 1644 | LLVMSetTailCall(call_instruction, true); | |
| 1645 | 1828 | return call_instruction; |
| 1646 | 1829 | } |
| 1647 | 1830 | |
| ... | ... | @@ -1723,7 +1906,7 @@ static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, TypeTableEntry *type |
| 1723 | 1906 | if (type_entry->id == TypeTableEntryIdInt) |
| 1724 | 1907 | return val; |
| 1725 | 1908 | |
| 1726 | LLVMValueRef floor_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdFloor); | |
| 1909 | LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloor); | |
| 1727 | 1910 | return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); |
| 1728 | 1911 | } |
| 1729 | 1912 | |
| ... | ... | @@ -1731,7 +1914,7 @@ static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, TypeTableEntry *type_ |
| 1731 | 1914 | if (type_entry->id == TypeTableEntryIdInt) |
| 1732 | 1915 | return val; |
| 1733 | 1916 | |
| 1734 | LLVMValueRef ceil_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdCeil); | |
| 1917 | LLVMValueRef ceil_fn = get_float_fn(g, type_entry, ZigLLVMFnIdCeil); | |
| 1735 | 1918 | return LLVMBuildCall(g->builder, ceil_fn, &val, 1, ""); |
| 1736 | 1919 | } |
| 1737 | 1920 | |
| ... | ... | @@ -3070,10 +3253,12 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui |
| 3070 | 3253 | fn_name = "cttz"; |
| 3071 | 3254 | key.id = ZigLLVMFnIdCtz; |
| 3072 | 3255 | key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count; |
| 3073 | } else { | |
| 3256 | } else if (fn_id == BuiltinFnIdClz) { | |
| 3074 | 3257 | fn_name = "ctlz"; |
| 3075 | 3258 | key.id = ZigLLVMFnIdClz; |
| 3076 | 3259 | key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count; |
| 3260 | } else { | |
| 3261 | zig_unreachable(); | |
| 3077 | 3262 | } |
| 3078 | 3263 | |
| 3079 | 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 | 3560 | LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order); |
| 3376 | 3561 | |
| 3377 | 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); | |
| 3379 | 3581 | |
| 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 | } |
| 3382 | 3588 | |
| 3383 | 3589 | static 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 | 4410 | return LLVMBuildIntToPtr(g->builder, uncasted_result, operand_type->type_ref, ""); |
| 4205 | 4411 | } |
| 4206 | 4412 | |
| 4413 | static 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 | ||
| 4423 | static 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 | ||
| 4436 | static 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 | ||
| 4444 | static 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 | ||
| 4207 | 4451 | static void set_debug_location(CodeGen *g, IrInstruction *instruction) { |
| 4208 | 4452 | AstNode *source_node = instruction->source_node; |
| 4209 | 4453 | Scope *scope = instruction->scope; |
| ... | ... | @@ -4259,6 +4503,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 4259 | 4503 | case IrInstructionIdDeclRef: |
| 4260 | 4504 | case IrInstructionIdSwitchVar: |
| 4261 | 4505 | case IrInstructionIdOffsetOf: |
| 4506 | case IrInstructionIdTypeInfo: | |
| 4262 | 4507 | case IrInstructionIdTypeId: |
| 4263 | 4508 | case IrInstructionIdSetEvalBranchQuota: |
| 4264 | 4509 | case IrInstructionIdPtrTypeOf: |
| ... | ... | @@ -4419,8 +4664,16 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, |
| 4419 | 4664 | return ir_render_coro_alloc_helper(g, executable, (IrInstructionCoroAllocHelper *)instruction); |
| 4420 | 4665 | case IrInstructionIdAtomicRmw: |
| 4421 | 4666 | return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction); |
| 4667 | case IrInstructionIdAtomicLoad: | |
| 4668 | return ir_render_atomic_load(g, executable, (IrInstructionAtomicLoad *)instruction); | |
| 4422 | 4669 | case IrInstructionIdSaveErrRetAddr: |
| 4423 | 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 | 4678 | zig_unreachable(); |
| 4426 | 4679 | } |
| ... | ... | @@ -5310,38 +5563,14 @@ static void do_code_gen(CodeGen *g) { |
| 5310 | 5563 | g->cur_err_ret_trace_val_arg = nullptr; |
| 5311 | 5564 | } |
| 5312 | 5565 | |
| 5566 | // error return tracing setup | |
| 5313 | 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 && | |
| 5315 | (is_async || !have_err_ret_trace_arg); | |
| 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; | |
| 5569 | LLVMValueRef err_ret_array_val = nullptr; | |
| 5316 | 5570 | if (have_err_ret_trace_stack) { |
| 5317 | // TODO call graph analysis to find out what this number needs to be for every function | |
| 5318 | static const size_t stack_trace_ptr_count = 30; | |
| 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)); | |
| 5571 | TypeTableEntry *array_type = get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count); | |
| 5572 | err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses", get_abi_alignment(g, array_type)); | |
| 5324 | 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 | 5574 | } else { |
| 5346 | 5575 | g->cur_err_ret_trace_val_stack = nullptr; |
| 5347 | 5576 | } |
| ... | ... | @@ -5383,6 +5612,9 @@ static void do_code_gen(CodeGen *g) { |
| 5383 | 5612 | } else if (instruction->id == IrInstructionIdErrWrapCode) { |
| 5384 | 5613 | IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction; |
| 5385 | 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 | 5618 | } else { |
| 5387 | 5619 | zig_unreachable(); |
| 5388 | 5620 | } |
| ... | ... | @@ -5436,6 +5668,31 @@ static void do_code_gen(CodeGen *g) { |
| 5436 | 5668 | } |
| 5437 | 5669 | } |
| 5438 | 5670 | |
| 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 | 5696 | FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; |
| 5440 | 5697 | |
| 5441 | 5698 | // create debug variable declarations for parameters |
| ... | ... | @@ -5519,10 +5776,12 @@ static void do_code_gen(CodeGen *g) { |
| 5519 | 5776 | os_path_join(g->cache_dir, o_basename, output_path); |
| 5520 | 5777 | ensure_cache_dir(g); |
| 5521 | 5778 | |
| 5779 | bool is_small = g->build_mode == BuildModeSmallRelease; | |
| 5780 | ||
| 5522 | 5781 | switch (g->emit_file_type) { |
| 5523 | 5782 | case EmitFileTypeBinary: |
| 5524 | 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 | 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 | 5791 | |
| 5533 | 5792 | case EmitFileTypeAssembly: |
| 5534 | 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 | 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 | 5800 | |
| 5542 | 5801 | case EmitFileTypeLLVMIr: |
| 5543 | 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 | 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 | 6126 | create_builtin_fn(g, BuiltinFnIdMemberCount, "memberCount", 1); |
| 5868 | 6127 | create_builtin_fn(g, BuiltinFnIdMemberType, "memberType", 2); |
| 5869 | 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 | 6131 | create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf |
| 5871 | 6132 | create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); |
| 5872 | 6133 | create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); |
| ... | ... | @@ -5883,7 +6144,8 @@ static void define_builtin_fns(CodeGen *g) { |
| 5883 | 6144 | create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1); |
| 5884 | 6145 | create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2); |
| 5885 | 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 | 6149 | create_builtin_fn(g, BuiltinFnIdFence, "fence", 1); |
| 5888 | 6150 | create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2); |
| 5889 | 6151 | create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); |
| ... | ... | @@ -5906,6 +6168,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 5906 | 6168 | create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2); |
| 5907 | 6169 | create_builtin_fn(g, BuiltinFnIdRem, "rem", 2); |
| 5908 | 6170 | create_builtin_fn(g, BuiltinFnIdMod, "mod", 2); |
| 6171 | create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2); | |
| 5909 | 6172 | create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX); |
| 5910 | 6173 | create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX); |
| 5911 | 6174 | create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1); |
| ... | ... | @@ -5919,6 +6182,7 @@ static void define_builtin_fns(CodeGen *g) { |
| 5919 | 6182 | create_builtin_fn(g, BuiltinFnIdExport, "export", 3); |
| 5920 | 6183 | create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0); |
| 5921 | 6184 | create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5); |
| 6185 | create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3); | |
| 5922 | 6186 | } |
| 5923 | 6187 | |
| 5924 | 6188 | static const char *bool_to_str(bool b) { |
| ... | ... | @@ -5930,6 +6194,7 @@ static const char *build_mode_to_str(BuildMode build_mode) { |
| 5930 | 6194 | case BuildModeDebug: return "Mode.Debug"; |
| 5931 | 6195 | case BuildModeSafeRelease: return "Mode.ReleaseSafe"; |
| 5932 | 6196 | case BuildModeFastRelease: return "Mode.ReleaseFast"; |
| 6197 | case BuildModeSmallRelease: return "Mode.ReleaseSmall"; | |
| 5933 | 6198 | } |
| 5934 | 6199 | zig_unreachable(); |
| 5935 | 6200 | } |
| ... | ... | @@ -5943,6 +6208,8 @@ static void define_builtin_compile_vars(CodeGen *g) { |
| 5943 | 6208 | os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); |
| 5944 | 6209 | Buf *contents = buf_alloc(); |
| 5945 | 6210 | |
| 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 | 6213 | buf_append_str(contents, |
| 5947 | 6214 | "pub const StackTrace = struct {\n" |
| 5948 | 6215 | " index: usize,\n" |
| ... | ... | @@ -6068,6 +6335,7 @@ static void define_builtin_compile_vars(CodeGen *g) { |
| 6068 | 6335 | " Debug,\n" |
| 6069 | 6336 | " ReleaseSafe,\n" |
| 6070 | 6337 | " ReleaseFast,\n" |
| 6338 | " ReleaseSmall,\n" | |
| 6071 | 6339 | "};\n\n"); |
| 6072 | 6340 | } |
| 6073 | 6341 | { |
| ... | ... | @@ -6077,8 +6345,196 @@ static void define_builtin_compile_vars(CodeGen *g) { |
| 6077 | 6345 | const TypeTableEntryId id = type_id_at_index(i); |
| 6078 | 6346 | buf_appendf(contents, " %s,\n", type_id_name(id)); |
| 6079 | 6347 | } |
| 6348 | buf_appendf(contents, " Slice,\n"); | |
| 6080 | 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 | 6539 | buf_appendf(contents, |
| 6084 | 6540 | "pub const FloatMode = enum {\n" |
| ... | ... | @@ -6118,7 +6574,8 @@ static void define_builtin_compile_vars(CodeGen *g) { |
| 6118 | 6574 | int err; |
| 6119 | 6575 | Buf *abs_full_path = buf_alloc(); |
| 6120 | 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 | } |
| 6123 | 6580 | |
| 6124 | 6581 | assert(g->root_package); |
| ... | ... | @@ -6238,7 +6695,7 @@ static void init(CodeGen *g) { |
| 6238 | 6695 | } |
| 6239 | 6696 | } |
| 6240 | 6697 | |
| 6241 | g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease; | |
| 6698 | g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease && g->build_mode != BuildModeSmallRelease; | |
| 6242 | 6699 | |
| 6243 | 6700 | define_builtin_fns(g); |
| 6244 | 6701 | define_builtin_compile_vars(g); |
| ... | ... | @@ -6372,12 +6829,14 @@ static void gen_root_source(CodeGen *g) { |
| 6372 | 6829 | Buf *abs_full_path = buf_alloc(); |
| 6373 | 6830 | int err; |
| 6374 | 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 | } |
| 6377 | 6835 | |
| 6378 | 6836 | Buf *source_code = buf_alloc(); |
| 6379 | 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 | } |
| 6382 | 6841 | |
| 6383 | 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 | 7389 | } |
| 6931 | 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 | 110 | IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type); |
| 111 | 111 | static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, |
| 112 | 112 | VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr); |
| 113 | static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); | |
| 114 | static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval); | |
| 113 | 115 | |
| 114 | 116 | ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { |
| 115 | 117 | assert(const_val->type->id == TypeTableEntryIdPointer); |
| ... | ... | @@ -143,6 +145,8 @@ static bool ir_should_inline(IrExecutable *exec, Scope *scope) { |
| 143 | 145 | while (scope != nullptr) { |
| 144 | 146 | if (scope->id == ScopeIdCompTime) |
| 145 | 147 | return true; |
| 148 | if (scope->id == ScopeIdFnDef) | |
| 149 | break; | |
| 146 | 150 | scope = scope->parent; |
| 147 | 151 | } |
| 148 | 152 | return false; |
| ... | ... | @@ -613,6 +617,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionOffsetOf *) { |
| 613 | 617 | return IrInstructionIdOffsetOf; |
| 614 | 618 | } |
| 615 | 619 | |
| 620 | static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeInfo *) { | |
| 621 | return IrInstructionIdTypeInfo; | |
| 622 | } | |
| 623 | ||
| 616 | 624 | static constexpr IrInstructionId ir_instruction_id(IrInstructionTypeId *) { |
| 617 | 625 | return IrInstructionIdTypeId; |
| 618 | 626 | } |
| ... | ... | @@ -709,6 +717,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) { |
| 709 | 717 | return IrInstructionIdAtomicRmw; |
| 710 | 718 | } |
| 711 | 719 | |
| 720 | static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) { | |
| 721 | return IrInstructionIdAtomicLoad; | |
| 722 | } | |
| 723 | ||
| 712 | 724 | static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseResultType *) { |
| 713 | 725 | return IrInstructionIdPromiseResultType; |
| 714 | 726 | } |
| ... | ... | @@ -725,6 +737,18 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitRetur |
| 725 | 737 | return IrInstructionIdAddImplicitReturnType; |
| 726 | 738 | } |
| 727 | 739 | |
| 740 | static constexpr IrInstructionId ir_instruction_id(IrInstructionMergeErrRetTraces *) { | |
| 741 | return IrInstructionIdMergeErrRetTraces; | |
| 742 | } | |
| 743 | ||
| 744 | static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTracePtr *) { | |
| 745 | return IrInstructionIdMarkErrRetTracePtr; | |
| 746 | } | |
| 747 | ||
| 748 | static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) { | |
| 749 | return IrInstructionIdSqrt; | |
| 750 | } | |
| 751 | ||
| 728 | 752 | template<typename T> |
| 729 | 753 | static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { |
| 730 | 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 | 980 | return &const_instruction->base; |
| 957 | 981 | } |
| 958 | 982 | |
| 959 | static 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 | ||
| 978 | 983 | static IrInstruction *ir_build_bin_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrBinOp op_id, |
| 979 | 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 | 1040 | return new_instruction; |
| 1036 | 1041 | } |
| 1037 | 1042 | |
| 1043 | static 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 | ||
| 1038 | 1057 | static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 1039 | 1058 | IrInstruction *container_ptr, Buf *field_name) |
| 1040 | 1059 | { |
| 1041 | 1060 | IrInstructionFieldPtr *instruction = ir_build_instruction<IrInstructionFieldPtr>(irb, scope, source_node); |
| 1042 | 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; | |
| 1044 | 1064 | |
| 1045 | 1065 | ir_ref_instruction(container_ptr, irb->current_basic_block); |
| 1046 | 1066 | |
| ... | ... | @@ -1835,38 +1855,34 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode |
| 1835 | 1855 | return &instruction->base; |
| 1836 | 1856 | } |
| 1837 | 1857 | |
| 1838 | static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, | |
| 1839 | IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value, | |
| 1840 | AtomicOrder success_order, AtomicOrder failure_order) | |
| 1858 | static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, | |
| 1859 | IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, | |
| 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 | 1864 | IrInstructionCmpxchg *instruction = ir_build_instruction<IrInstructionCmpxchg>(irb, scope, source_node); |
| 1865 | instruction->type_value = type_value; | |
| 1843 | 1866 | instruction->ptr = ptr; |
| 1844 | 1867 | instruction->cmp_value = cmp_value; |
| 1845 | 1868 | instruction->new_value = new_value; |
| 1846 | 1869 | instruction->success_order_value = success_order_value; |
| 1847 | 1870 | instruction->failure_order_value = failure_order_value; |
| 1871 | instruction->is_weak = is_weak; | |
| 1872 | instruction->type = type; | |
| 1848 | 1873 | instruction->success_order = success_order; |
| 1849 | 1874 | instruction->failure_order = failure_order; |
| 1850 | 1875 | |
| 1876 | if (type_value != nullptr) ir_ref_instruction(type_value, irb->current_basic_block); | |
| 1851 | 1877 | ir_ref_instruction(ptr, irb->current_basic_block); |
| 1852 | 1878 | ir_ref_instruction(cmp_value, irb->current_basic_block); |
| 1853 | 1879 | ir_ref_instruction(new_value, irb->current_basic_block); |
| 1854 | ir_ref_instruction(success_order_value, irb->current_basic_block); | |
| 1855 | ir_ref_instruction(failure_order_value, irb->current_basic_block); | |
| 1880 | if (type_value != nullptr) ir_ref_instruction(success_order_value, irb->current_basic_block); | |
| 1881 | if (type_value != nullptr) ir_ref_instruction(failure_order_value, irb->current_basic_block); | |
| 1856 | 1882 | |
| 1857 | 1883 | return &instruction->base; |
| 1858 | 1884 | } |
| 1859 | 1885 | |
| 1860 | static 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 | ||
| 1870 | 1886 | static IrInstruction *ir_build_fence(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *order_value, AtomicOrder order) { |
| 1871 | 1887 | IrInstructionFence *instruction = ir_build_instruction<IrInstructionFence>(irb, scope, source_node); |
| 1872 | 1888 | instruction->order_value = order_value; |
| ... | ... | @@ -2430,6 +2446,16 @@ static IrInstruction *ir_build_offset_of(IrBuilder *irb, Scope *scope, AstNode * |
| 2430 | 2446 | return &instruction->base; |
| 2431 | 2447 | } |
| 2432 | 2448 | |
| 2449 | static 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 | ||
| 2433 | 2459 | static IrInstruction *ir_build_type_id(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 2434 | 2460 | IrInstruction *type_value) |
| 2435 | 2461 | { |
| ... | ... | @@ -2495,8 +2521,9 @@ static IrInstruction *ir_build_arg_type(IrBuilder *irb, Scope *scope, AstNode *s |
| 2495 | 2521 | return &instruction->base; |
| 2496 | 2522 | } |
| 2497 | 2523 | |
| 2498 | static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node) { | |
| 2524 | static IrInstruction *ir_build_error_return_trace(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstructionErrorReturnTrace::Nullable nullable) { | |
| 2499 | 2525 | IrInstructionErrorReturnTrace *instruction = ir_build_instruction<IrInstructionErrorReturnTrace>(irb, scope, source_node); |
| 2526 | instruction->nullable = nullable; | |
| 2500 | 2527 | |
| 2501 | 2528 | return &instruction->base; |
| 2502 | 2529 | } |
| ... | ... | @@ -2679,6 +2706,23 @@ static IrInstruction *ir_build_atomic_rmw(IrBuilder *irb, Scope *scope, AstNode |
| 2679 | 2706 | return &instruction->base; |
| 2680 | 2707 | } |
| 2681 | 2708 | |
| 2709 | static 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 | ||
| 2682 | 2726 | static IrInstruction *ir_build_promise_result_type(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 2683 | 2727 | IrInstruction *promise_type) |
| 2684 | 2728 | { |
| ... | ... | @@ -2717,6 +2761,41 @@ static IrInstruction *ir_build_add_implicit_return_type(IrBuilder *irb, Scope *s |
| 2717 | 2761 | return &instruction->base; |
| 2718 | 2762 | } |
| 2719 | 2763 | |
| 2764 | static 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 | ||
| 2779 | static 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 | ||
| 2788 | static 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 | ||
| 2720 | 2799 | static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { |
| 2721 | 2800 | results[ReturnKindUnconditional] = 0; |
| 2722 | 2801 | results[ReturnKindError] = 0; |
| ... | ... | @@ -2741,9 +2820,10 @@ static IrInstruction *ir_mark_gen(IrInstruction *instruction) { |
| 2741 | 2820 | |
| 2742 | 2821 | static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, bool gen_error_defers) { |
| 2743 | 2822 | Scope *scope = inner_scope; |
| 2823 | bool is_noreturn = false; | |
| 2744 | 2824 | while (scope != outer_scope) { |
| 2745 | 2825 | if (!scope) |
| 2746 | return false; | |
| 2826 | return is_noreturn; | |
| 2747 | 2827 | |
| 2748 | 2828 | if (scope->id == ScopeIdDefer) { |
| 2749 | 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 | 2836 | Scope *defer_expr_scope = defer_node->data.defer.expr_scope; |
| 2757 | 2837 | IrInstruction *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); |
| 2758 | 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 | } |
| 2762 | 2846 | |
| 2763 | 2847 | } |
| 2764 | 2848 | scope = scope->parent; |
| 2765 | 2849 | } |
| 2766 | return true; | |
| 2850 | return is_noreturn; | |
| 2767 | 2851 | } |
| 2768 | 2852 | |
| 2769 | 2853 | static 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 | 2861 | ir_set_cursor_at_end(irb, basic_block); |
| 2778 | 2862 | } |
| 2779 | 2863 | |
| 2864 | static 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 | ||
| 2780 | 2876 | static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) { |
| 2781 | 2877 | while (scope) { |
| 2782 | 2878 | if (scope->id == ScopeIdDeferExpr) |
| ... | ... | @@ -2822,34 +2918,6 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode |
| 2822 | 2918 | // the above blocks are rendered by ir_gen after the rest of codegen |
| 2823 | 2919 | } |
| 2824 | 2920 | |
| 2825 | static 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 | ||
| 2836 | static 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 | ||
| 2853 | 2921 | static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { |
| 2854 | 2922 | assert(node->type == NodeTypeReturnExpr); |
| 2855 | 2923 | |
| ... | ... | @@ -2895,8 +2963,9 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, |
| 2895 | 2963 | |
| 2896 | 2964 | IrInstruction *is_err = ir_build_test_err(irb, scope, node, return_value); |
| 2897 | 2965 | |
| 2966 | bool should_inline = ir_should_inline(irb->exec, scope); | |
| 2898 | 2967 | IrInstruction *is_comptime; |
| 2899 | if (ir_should_inline(irb->exec, scope)) { | |
| 2968 | if (should_inline) { | |
| 2900 | 2969 | is_comptime = ir_build_const_bool(irb, scope, node, true); |
| 2901 | 2970 | } else { |
| 2902 | 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 | 2978 | if (have_err_defers) { |
| 2910 | 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 | 2984 | ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); |
| 2914 | 2985 | |
| 2915 | 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 | 3009 | IrBasicBlock *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn"); |
| 2939 | 3010 | IrBasicBlock *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue"); |
| 2940 | 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 | 3014 | is_comptime = ir_build_const_bool(irb, scope, node, true); |
| 2943 | 3015 | } else { |
| 2944 | 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 | 3018 | ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime)); |
| 2947 | 3019 | |
| 2948 | 3020 | ir_set_cursor_at_end_and_append_block(irb, return_block); |
| 2949 | 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); | |
| 2951 | ir_gen_save_err_ret_addr(irb, scope, node); | |
| 2952 | ir_gen_async_return(irb, scope, node, err_val, false); | |
| 3021 | if (!ir_gen_defers_for_block(irb, scope, outer_scope, true)) { | |
| 3022 | IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr); | |
| 3023 | if (irb->codegen->have_err_ret_tracing && !should_inline) { | |
| 3024 | ir_build_save_err_ret_addr(irb, scope, node); | |
| 3025 | } | |
| 3026 | ir_gen_async_return(irb, scope, node, err_val, false); | |
| 3027 | } | |
| 2953 | 3028 | |
| 2954 | 3029 | ir_set_cursor_at_end_and_append_block(irb, continue_block); |
| 2955 | 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 | 3163 | if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) { |
| 3089 | 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 | 3169 | } else { |
| 3092 | 3170 | incoming_blocks.append(irb->current_basic_block); |
| 3093 | 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 | 3559 | return ir_build_load_ptr(irb, scope, node, ptr_instruction); |
| 3482 | 3560 | } |
| 3483 | 3561 | |
| 3484 | static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { | |
| 3562 | static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node) { | |
| 3485 | 3563 | assert(node->type == NodeTypeFieldAccessExpr); |
| 3486 | 3564 | |
| 3487 | 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 | 3569 | if (container_ref_instruction == irb->codegen->invalid_instruction) |
| 3492 | 3570 | return container_ref_instruction; |
| 3493 | 3571 | |
| 3494 | IrInstruction *ptr_instruction = 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); | |
| 3572 | return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name); | |
| 3499 | 3573 | } |
| 3500 | 3574 | |
| 3501 | 3575 | static 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 | 3600 | return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr); |
| 3527 | 3601 | } |
| 3528 | 3602 | |
| 3529 | static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) { | |
| 3603 | static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { | |
| 3530 | 3604 | assert(node->type == NodeTypeFnCallExpr); |
| 3531 | 3605 | |
| 3532 | 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 | 3632 | IrInstruction *arg = ir_gen_node(irb, arg_node, scope); |
| 3559 | 3633 | if (arg == irb->codegen->invalid_instruction) |
| 3560 | 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 | 3639 | case BuiltinFnIdSetCold: |
| 3564 | 3640 | { |
| ... | ... | @@ -3567,7 +3643,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3567 | 3643 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3568 | 3644 | return arg0_value; |
| 3569 | 3645 | |
| 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 | 3649 | case BuiltinFnIdSetRuntimeSafety: |
| 3573 | 3650 | { |
| ... | ... | @@ -3576,7 +3653,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3576 | 3653 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3577 | 3654 | return arg0_value; |
| 3578 | 3655 | |
| 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 | 3659 | case BuiltinFnIdSetFloatMode: |
| 3582 | 3660 | { |
| ... | ... | @@ -3590,7 +3668,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3590 | 3668 | if (arg1_value == irb->codegen->invalid_instruction) |
| 3591 | 3669 | return arg1_value; |
| 3592 | 3670 | |
| 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 | 3674 | case BuiltinFnIdSizeof: |
| 3596 | 3675 | { |
| ... | ... | @@ -3599,7 +3678,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3599 | 3678 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3600 | 3679 | return arg0_value; |
| 3601 | 3680 | |
| 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 | 3684 | case BuiltinFnIdCtz: |
| 3605 | 3685 | { |
| ... | ... | @@ -3608,7 +3688,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3608 | 3688 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3609 | 3689 | return arg0_value; |
| 3610 | 3690 | |
| 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 | 3694 | case BuiltinFnIdClz: |
| 3614 | 3695 | { |
| ... | ... | @@ -3617,7 +3698,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3617 | 3698 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3618 | 3699 | return arg0_value; |
| 3619 | 3700 | |
| 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 | 3704 | case BuiltinFnIdImport: |
| 3623 | 3705 | { |
| ... | ... | @@ -3626,11 +3708,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3626 | 3708 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3627 | 3709 | return arg0_value; |
| 3628 | 3710 | |
| 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 | 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 | 3719 | case BuiltinFnIdCInclude: |
| 3636 | 3720 | { |
| ... | ... | @@ -3644,7 +3728,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3644 | 3728 | return irb->codegen->invalid_instruction; |
| 3645 | 3729 | } |
| 3646 | 3730 | |
| 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 | 3734 | case BuiltinFnIdCDefine: |
| 3650 | 3735 | { |
| ... | ... | @@ -3663,7 +3748,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3663 | 3748 | return irb->codegen->invalid_instruction; |
| 3664 | 3749 | } |
| 3665 | 3750 | |
| 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 | 3754 | case BuiltinFnIdCUndef: |
| 3669 | 3755 | { |
| ... | ... | @@ -3677,7 +3763,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3677 | 3763 | return irb->codegen->invalid_instruction; |
| 3678 | 3764 | } |
| 3679 | 3765 | |
| 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 | 3769 | case BuiltinFnIdMaxValue: |
| 3683 | 3770 | { |
| ... | ... | @@ -3686,7 +3773,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3686 | 3773 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3687 | 3774 | return arg0_value; |
| 3688 | 3775 | |
| 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 | 3779 | case BuiltinFnIdMinValue: |
| 3692 | 3780 | { |
| ... | ... | @@ -3695,7 +3783,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3695 | 3783 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3696 | 3784 | return arg0_value; |
| 3697 | 3785 | |
| 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 | 3789 | case BuiltinFnIdCompileErr: |
| 3701 | 3790 | { |
| ... | ... | @@ -3704,7 +3793,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3704 | 3793 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3705 | 3794 | return arg0_value; |
| 3706 | 3795 | |
| 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 | 3799 | case BuiltinFnIdCompileLog: |
| 3710 | 3800 | { |
| ... | ... | @@ -3717,7 +3807,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3717 | 3807 | return irb->codegen->invalid_instruction; |
| 3718 | 3808 | } |
| 3719 | 3809 | |
| 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 | 3813 | case BuiltinFnIdErrName: |
| 3723 | 3814 | { |
| ... | ... | @@ -3726,7 +3817,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3726 | 3817 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3727 | 3818 | return arg0_value; |
| 3728 | 3819 | |
| 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 | 3823 | case BuiltinFnIdEmbedFile: |
| 3732 | 3824 | { |
| ... | ... | @@ -3735,9 +3827,11 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3735 | 3827 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3736 | 3828 | return arg0_value; |
| 3737 | 3829 | |
| 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 | 3836 | AstNode *arg0_node = node->data.fn_call_expr.params.at(0); |
| 3743 | 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 | 3858 | if (arg4_value == irb->codegen->invalid_instruction) |
| 3765 | 3859 | return arg4_value; |
| 3766 | 3860 | |
| 3767 | return ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, | |
| 3768 | arg2_value, arg3_value, arg4_value, | |
| 3769 | AtomicOrderUnordered, AtomicOrderUnordered); | |
| 3861 | AstNode *arg5_node = node->data.fn_call_expr.params.at(5); | |
| 3862 | IrInstruction *arg5_value = ir_gen_node(irb, arg5_node, scope); | |
| 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 | 3871 | case BuiltinFnIdFence: |
| 3772 | 3872 | { |
| ... | ... | @@ -3775,7 +3875,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3775 | 3875 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3776 | 3876 | return arg0_value; |
| 3777 | 3877 | |
| 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 | 3881 | case BuiltinFnIdDivExact: |
| 3781 | 3882 | { |
| ... | ... | @@ -3789,7 +3890,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3789 | 3890 | if (arg1_value == irb->codegen->invalid_instruction) |
| 3790 | 3891 | return arg1_value; |
| 3791 | 3892 | |
| 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 | 3896 | case BuiltinFnIdDivTrunc: |
| 3795 | 3897 | { |
| ... | ... | @@ -3803,7 +3905,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3803 | 3905 | if (arg1_value == irb->codegen->invalid_instruction) |
| 3804 | 3906 | return arg1_value; |
| 3805 | 3907 | |
| 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 | 3911 | case BuiltinFnIdDivFloor: |
| 3809 | 3912 | { |
| ... | ... | @@ -3817,7 +3920,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3817 | 3920 | if (arg1_value == irb->codegen->invalid_instruction) |
| 3818 | 3921 | return arg1_value; |
| 3819 | 3922 | |
| 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 | 3926 | case BuiltinFnIdRem: |
| 3823 | 3927 | { |
| ... | ... | @@ -3831,7 +3935,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3831 | 3935 | if (arg1_value == irb->codegen->invalid_instruction) |
| 3832 | 3936 | return arg1_value; |
| 3833 | 3937 | |
| 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 | 3941 | case BuiltinFnIdMod: |
| 3837 | 3942 | { |
| ... | ... | @@ -3845,7 +3950,23 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3845 | 3950 | if (arg1_value == irb->codegen->invalid_instruction) |
| 3846 | 3951 | return arg1_value; |
| 3847 | 3952 | |
| 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 | 3971 | case BuiltinFnIdTruncate: |
| 3851 | 3972 | { |
| ... | ... | @@ -3859,7 +3980,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3859 | 3980 | if (arg1_value == irb->codegen->invalid_instruction) |
| 3860 | 3981 | return arg1_value; |
| 3861 | 3982 | |
| 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 | 3986 | case BuiltinFnIdIntType: |
| 3865 | 3987 | { |
| ... | ... | @@ -3873,7 +3995,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3873 | 3995 | if (arg1_value == irb->codegen->invalid_instruction) |
| 3874 | 3996 | return arg1_value; |
| 3875 | 3997 | |
| 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 | 4001 | case BuiltinFnIdMemcpy: |
| 3879 | 4002 | { |
| ... | ... | @@ -3892,7 +4015,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3892 | 4015 | if (arg2_value == irb->codegen->invalid_instruction) |
| 3893 | 4016 | return arg2_value; |
| 3894 | 4017 | |
| 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 | 4021 | case BuiltinFnIdMemset: |
| 3898 | 4022 | { |
| ... | ... | @@ -3911,7 +4035,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3911 | 4035 | if (arg2_value == irb->codegen->invalid_instruction) |
| 3912 | 4036 | return arg2_value; |
| 3913 | 4037 | |
| 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 | 4041 | case BuiltinFnIdMemberCount: |
| 3917 | 4042 | { |
| ... | ... | @@ -3920,7 +4045,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3920 | 4045 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3921 | 4046 | return arg0_value; |
| 3922 | 4047 | |
| 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 | 4051 | case BuiltinFnIdMemberType: |
| 3926 | 4052 | { |
| ... | ... | @@ -3935,7 +4061,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3935 | 4061 | return arg1_value; |
| 3936 | 4062 | |
| 3937 | 4063 | |
| 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 | 4067 | case BuiltinFnIdMemberName: |
| 3941 | 4068 | { |
| ... | ... | @@ -3950,14 +4077,44 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3950 | 4077 | return arg1_value; |
| 3951 | 4078 | |
| 3952 | 4079 | |
| 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 | 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 | 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 | 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 | 4118 | case BuiltinFnIdAlignOf: |
| 3962 | 4119 | { |
| 3963 | 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 | 4122 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3966 | 4123 | return arg0_value; |
| 3967 | 4124 | |
| 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 | 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 | 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 | 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 | 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 | 4136 | case BuiltinFnIdTypeName: |
| 3979 | 4137 | { |
| 3980 | 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 | 4140 | if (arg0_value == irb->codegen->invalid_instruction) |
| 3983 | 4141 | return arg0_value; |
| 3984 | 4142 | |
| 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 | 4146 | case BuiltinFnIdCanImplicitCast: |
| 3988 | 4147 | { |
| ... | ... | @@ -3996,7 +4155,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 3996 | 4155 | if (arg1_value == irb->codegen->invalid_instruction) |
| 3997 | 4156 | return arg1_value; |
| 3998 | 4157 | |
| 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 | 4161 | case BuiltinFnIdPanic: |
| 4002 | 4162 | { |
| ... | ... | @@ -4005,7 +4165,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4005 | 4165 | if (arg0_value == irb->codegen->invalid_instruction) |
| 4006 | 4166 | return arg0_value; |
| 4007 | 4167 | |
| 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 | 4171 | case BuiltinFnIdPtrCast: |
| 4011 | 4172 | { |
| ... | ... | @@ -4019,7 +4180,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4019 | 4180 | if (arg1_value == irb->codegen->invalid_instruction) |
| 4020 | 4181 | return arg1_value; |
| 4021 | 4182 | |
| 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 | 4186 | case BuiltinFnIdBitCast: |
| 4025 | 4187 | { |
| ... | ... | @@ -4033,7 +4195,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4033 | 4195 | if (arg1_value == irb->codegen->invalid_instruction) |
| 4034 | 4196 | return arg1_value; |
| 4035 | 4197 | |
| 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 | 4201 | case BuiltinFnIdIntToPtr: |
| 4039 | 4202 | { |
| ... | ... | @@ -4047,7 +4210,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4047 | 4210 | if (arg1_value == irb->codegen->invalid_instruction) |
| 4048 | 4211 | return arg1_value; |
| 4049 | 4212 | |
| 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 | 4216 | case BuiltinFnIdPtrToInt: |
| 4053 | 4217 | { |
| ... | ... | @@ -4056,7 +4220,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4056 | 4220 | if (arg0_value == irb->codegen->invalid_instruction) |
| 4057 | 4221 | return arg0_value; |
| 4058 | 4222 | |
| 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 | 4226 | case BuiltinFnIdTagName: |
| 4062 | 4227 | { |
| ... | ... | @@ -4066,7 +4231,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4066 | 4231 | return arg0_value; |
| 4067 | 4232 | |
| 4068 | 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 | 4237 | case BuiltinFnIdTagType: |
| 4072 | 4238 | { |
| ... | ... | @@ -4075,7 +4241,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4075 | 4241 | if (arg0_value == irb->codegen->invalid_instruction) |
| 4076 | 4242 | return arg0_value; |
| 4077 | 4243 | |
| 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 | 4247 | case BuiltinFnIdFieldParentPtr: |
| 4081 | 4248 | { |
| ... | ... | @@ -4094,7 +4261,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4094 | 4261 | if (arg2_value == irb->codegen->invalid_instruction) |
| 4095 | 4262 | return arg2_value; |
| 4096 | 4263 | |
| 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 | 4267 | case BuiltinFnIdOffsetOf: |
| 4100 | 4268 | { |
| ... | ... | @@ -4108,7 +4276,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4108 | 4276 | if (arg1_value == irb->codegen->invalid_instruction) |
| 4109 | 4277 | return arg1_value; |
| 4110 | 4278 | |
| 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 | 4282 | case BuiltinFnIdInlineCall: |
| 4114 | 4283 | case BuiltinFnIdNoInlineCall: |
| ... | ... | @@ -4134,7 +4303,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4134 | 4303 | } |
| 4135 | 4304 | FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever; |
| 4136 | 4305 | |
| 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 | 4309 | case BuiltinFnIdTypeId: |
| 4140 | 4310 | { |
| ... | ... | @@ -4143,7 +4313,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4143 | 4313 | if (arg0_value == irb->codegen->invalid_instruction) |
| 4144 | 4314 | return arg0_value; |
| 4145 | 4315 | |
| 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 | 4319 | case BuiltinFnIdShlExact: |
| 4149 | 4320 | { |
| ... | ... | @@ -4157,7 +4328,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4157 | 4328 | if (arg1_value == irb->codegen->invalid_instruction) |
| 4158 | 4329 | return arg1_value; |
| 4159 | 4330 | |
| 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 | 4334 | case BuiltinFnIdShrExact: |
| 4163 | 4335 | { |
| ... | ... | @@ -4171,7 +4343,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4171 | 4343 | if (arg1_value == irb->codegen->invalid_instruction) |
| 4172 | 4344 | return arg1_value; |
| 4173 | 4345 | |
| 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 | 4349 | case BuiltinFnIdSetEvalBranchQuota: |
| 4177 | 4350 | { |
| ... | ... | @@ -4180,7 +4353,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4180 | 4353 | if (arg0_value == irb->codegen->invalid_instruction) |
| 4181 | 4354 | return arg0_value; |
| 4182 | 4355 | |
| 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 | 4359 | case BuiltinFnIdAlignCast: |
| 4186 | 4360 | { |
| ... | ... | @@ -4194,10 +4368,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4194 | 4368 | if (arg1_value == irb->codegen->invalid_instruction) |
| 4195 | 4369 | return arg1_value; |
| 4196 | 4370 | |
| 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 | 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 | 4379 | case BuiltinFnIdSetAlignStack: |
| 4202 | 4380 | { |
| 4203 | 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 | 4383 | if (arg0_value == irb->codegen->invalid_instruction) |
| 4206 | 4384 | return arg0_value; |
| 4207 | 4385 | |
| 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 | 4389 | case BuiltinFnIdArgType: |
| 4211 | 4390 | { |
| ... | ... | @@ -4219,7 +4398,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4219 | 4398 | if (arg1_value == irb->codegen->invalid_instruction) |
| 4220 | 4399 | return arg1_value; |
| 4221 | 4400 | |
| 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 | 4404 | case BuiltinFnIdExport: |
| 4225 | 4405 | { |
| ... | ... | @@ -4238,11 +4418,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4238 | 4418 | if (arg2_value == irb->codegen->invalid_instruction) |
| 4239 | 4419 | return arg2_value; |
| 4240 | 4420 | |
| 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 | 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 | 4429 | case BuiltinFnIdAtomicRmw: |
| 4248 | 4430 | { |
| ... | ... | @@ -4276,15 +4458,36 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo |
| 4276 | 4458 | // these 2 values don't mean anything since we passed non-null values for other args |
| 4277 | 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 | 4483 | zig_unreachable(); |
| 4281 | 4484 | } |
| 4282 | 4485 | |
| 4283 | static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) { | |
| 4486 | static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { | |
| 4284 | 4487 | assert(node->type == NodeTypeFnCallExpr); |
| 4285 | 4488 | |
| 4286 | 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); | |
| 4288 | 4491 | |
| 4289 | 4492 | AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; |
| 4290 | 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 | 4513 | } |
| 4311 | 4514 | } |
| 4312 | 4515 | |
| 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 | } |
| 4315 | 4519 | |
| 4316 | 4520 | static 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 | 5797 | return ir_build_br(irb, break_scope, node, dest_block, is_comptime); |
| 5594 | 5798 | } |
| 5595 | 5799 | |
| 5800 | static 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 | ||
| 5596 | 5809 | static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) { |
| 5597 | 5810 | assert(node->type == NodeTypeBreak); |
| 5598 | 5811 | |
| ... | ... | @@ -5632,6 +5845,13 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode * |
| 5632 | 5845 | assert(this_block_scope->end_block != nullptr); |
| 5633 | 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 | 5856 | search_scope = search_scope->parent; |
| 5637 | 5857 | } |
| ... | ... | @@ -5703,7 +5923,7 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast |
| 5703 | 5923 | |
| 5704 | 5924 | IrBasicBlock *dest_block = loop_scope->continue_block; |
| 5705 | 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 | } |
| 5708 | 5928 | |
| 5709 | 5929 | static 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 | 6192 | buf_init_from_buf(&err_set_type->name, type_name); |
| 5973 | 6193 | err_set_type->is_copyable = true; |
| 5974 | 6194 | err_set_type->data.error_set.err_count = err_count; |
| 5975 | ||
| 5976 | if (err_count == 0) { | |
| 5977 | err_set_type->zero_bits = true; | |
| 5978 | err_set_type->di_type = irb->codegen->builtin_types.entry_void->di_type; | |
| 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 | } | |
| 6195 | err_set_type->type_ref = irb->codegen->builtin_types.entry_global_error_set->type_ref; | |
| 6196 | err_set_type->di_type = irb->codegen->builtin_types.entry_global_error_set->di_type; | |
| 6197 | irb->codegen->error_di_types.append(&err_set_type->di_type); | |
| 6198 | err_set_type->data.error_set.errors = allocate<ErrorTableEntry *>(err_count); | |
| 5985 | 6199 | |
| 5986 | 6200 | ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count); |
| 5987 | 6201 | |
| ... | ... | @@ -6125,6 +6339,13 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast |
| 6125 | 6339 | Buf *result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); |
| 6126 | 6340 | IrInstruction *result_ptr_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_ptr_field_name); |
| 6127 | 6341 | |
| 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 | 6349 | Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME); |
| 6129 | 6350 | IrInstruction *awaiter_field_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, |
| 6130 | 6351 | awaiter_handle_field_name); |
| ... | ... | @@ -6148,10 +6369,16 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *parent_scope, Ast |
| 6148 | 6369 | IrInstruction *is_non_null = ir_build_test_nonnull(irb, parent_scope, node, maybe_await_handle); |
| 6149 | 6370 | IrBasicBlock *yes_suspend_block = ir_create_basic_block(irb, parent_scope, "YesSuspend"); |
| 6150 | 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 | 6373 | ir_build_cond_br(irb, parent_scope, node, is_non_null, no_suspend_block, yes_suspend_block, const_bool_false); |
| 6153 | 6374 | |
| 6154 | 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 | 6382 | Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); |
| 6156 | 6383 | IrInstruction *promise_result_ptr = ir_build_field_ptr(irb, parent_scope, node, coro_promise_ptr, result_field_name); |
| 6157 | 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 | 6400 | |
| 6174 | 6401 | ir_set_cursor_at_end_and_append_block(irb, cleanup_block); |
| 6175 | 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)); | |
| 6177 | 6404 | |
| 6178 | 6405 | ir_set_cursor_at_end_and_append_block(irb, resume_block); |
| 6179 | 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 | 6432 | ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope); |
| 6206 | 6433 | if (scope_defer_expr) { |
| 6207 | 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 | 6437 | scope_defer_expr->reported_err = true; |
| 6210 | 6438 | } |
| 6211 | 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 | } | |
| 6213 | 6450 | |
| 6214 | 6451 | Scope *outer_scope = irb->exec->begin_scope; |
| 6215 | 6452 | |
| 6453 | IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup"); | |
| 6454 | IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume"); | |
| 6216 | 6455 | |
| 6217 | 6456 | IrInstruction *suspend_code; |
| 6218 | 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 | 6470 | } else { |
| 6232 | 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 | 6476 | IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle); |
| 6235 | 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 | } |
| 6238 | 6480 | |
| 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 | 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 | 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 | 6485 | cases[1].block = cleanup_block; |
| 6247 | ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, | |
| 6248 | 2, cases, const_bool_false); | |
| 6486 | ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block, | |
| 6487 | 2, cases, const_bool_false)); | |
| 6249 | 6488 | |
| 6250 | 6489 | ir_set_cursor_at_end_and_append_block(irb, cleanup_block); |
| 6251 | 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)); | |
| 6253 | 6492 | |
| 6254 | 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 | } |
| 6257 | 6496 | |
| 6258 | 6497 | static 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 | 6507 | case NodeTypeSwitchRange: |
| 6269 | 6508 | case NodeTypeStructField: |
| 6270 | 6509 | case NodeTypeFnDef: |
| 6271 | case NodeTypeFnDecl: | |
| 6272 | 6510 | case NodeTypeTestDecl: |
| 6273 | 6511 | zig_unreachable(); |
| 6274 | 6512 | case NodeTypeBlock: |
| ... | ... | @@ -6286,7 +6524,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop |
| 6286 | 6524 | case NodeTypeSymbol: |
| 6287 | 6525 | return ir_gen_symbol(irb, scope, node, lval); |
| 6288 | 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 | 6528 | case NodeTypeIfBoolExpr: |
| 6291 | 6529 | return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval); |
| 6292 | 6530 | case NodeTypePrefixOpExpr: |
| ... | ... | @@ -6306,7 +6544,15 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop |
| 6306 | 6544 | case NodeTypeReturnExpr: |
| 6307 | 6545 | return ir_gen_return(irb, scope, node, lval); |
| 6308 | 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 | 6556 | case NodeTypeThisLiteral: |
| 6311 | 6557 | return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval); |
| 6312 | 6558 | case NodeTypeBoolLiteral: |
| ... | ... | @@ -6407,6 +6653,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec |
| 6407 | 6653 | IrInstruction *coro_id; |
| 6408 | 6654 | IrInstruction *u8_ptr_type; |
| 6409 | 6655 | IrInstruction *const_bool_false; |
| 6656 | IrInstruction *coro_promise_ptr; | |
| 6657 | IrInstruction *err_ret_trace_ptr; | |
| 6410 | 6658 | TypeTableEntry *return_type; |
| 6411 | 6659 | Buf *result_ptr_field_name; |
| 6412 | 6660 | VariableTableEntry *coro_size_var; |
| ... | ... | @@ -6417,9 +6665,12 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec |
| 6417 | 6665 | VariableTableEntry *promise_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); |
| 6418 | 6666 | |
| 6419 | 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); | |
| 6421 | ir_build_var_decl(irb, coro_scope, node, promise_var, nullptr, nullptr, promise_init); | |
| 6422 | IrInstruction *coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var, false, false); | |
| 6668 | IrInstruction *undef = ir_build_const_undefined(irb, coro_scope, node); | |
| 6669 | TypeTableEntry *coro_frame_type = get_promise_frame_type(irb->codegen, return_type); | |
| 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); | |
| 6423 | 6674 | |
| 6424 | 6675 | VariableTableEntry *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false); |
| 6425 | 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 | 6703 | ir_set_cursor_at_end_and_append_block(irb, alloc_err_block); |
| 6453 | 6704 | // we can return undefined here, because the caller passes a pointer to the error struct field |
| 6454 | 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 | 6706 | ir_build_return(irb, coro_scope, node, undef); |
| 6457 | 6707 | |
| 6458 | 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 | 6710 | irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr); |
| 6461 | 6711 | |
| 6462 | 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 | 6714 | awaiter_handle_field_name); |
| 6715 | ir_build_store_ptr(irb, scope, node, irb->exec->coro_awaiter_field_ptr, null_value); | |
| 6465 | 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 | 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); | |
| 6469 | ir_build_store_ptr(irb, coro_scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr); | |
| 6719 | irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name); | |
| 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 | } | |
| 6470 | 6742 | |
| 6471 | 6743 | |
| 6472 | 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 | 6789 | IrInstruction *size_of_ret_val = ir_build_size_of(irb, scope, node, return_type_inst); |
| 6518 | 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 | 6798 | ir_build_br(irb, scope, node, check_free_block, const_bool_false); |
| 6521 | 6799 | |
| 6522 | 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 | 8137 | *errors = reallocate(*errors, old_errors_count, *errors_count); |
| 7860 | 8138 | } |
| 7861 | 8139 | |
| 7862 | static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, IrInstruction **instructions, size_t instruction_count) { | |
| 8140 | static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, TypeTableEntry *expected_type, IrInstruction **instructions, size_t instruction_count) { | |
| 7863 | 8141 | assert(instruction_count >= 1); |
| 7864 | 8142 | IrInstruction *prev_inst = instructions[0]; |
| 7865 | 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 | 8184 | continue; |
| 7907 | 8185 | } |
| 7908 | 8186 | |
| 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 | 8187 | if (prev_type->id == TypeTableEntryIdErrorSet) { |
| 7920 | 8188 | assert(err_set_type != nullptr); |
| 7921 | 8189 | if (cur_type->id == TypeTableEntryIdErrorSet) { |
| ... | ... | @@ -8175,6 +8443,16 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod |
| 8175 | 8443 | } |
| 8176 | 8444 | } |
| 8177 | 8445 | |
| 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 | 8456 | if (types_match_const_cast_only(ira, prev_type, cur_type, source_node).id == ConstCastResultIdOk) { |
| 8179 | 8457 | continue; |
| 8180 | 8458 | } |
| ... | ... | @@ -8358,6 +8636,10 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod |
| 8358 | 8636 | } else if (err_set_type != nullptr) { |
| 8359 | 8637 | if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) { |
| 8360 | 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 | 8643 | } else { |
| 8362 | 8644 | if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || |
| 8363 | 8645 | prev_inst->value.type->id == TypeTableEntryIdNumLitFloat) |
| ... | ... | @@ -8369,8 +8651,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod |
| 8369 | 8651 | ir_add_error_node(ira, source_node, |
| 8370 | 8652 | buf_sprintf("unable to make error union out of null literal")); |
| 8371 | 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 | 8654 | } else { |
| 8375 | 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 | 10673 | } |
| 10394 | 10674 | |
| 10395 | 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 | 10677 | if (type_is_invalid(resolved_type)) |
| 10398 | 10678 | return resolved_type; |
| 10399 | 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 | 11063 | IrInstruction *op1 = bin_op_instruction->op1->other; |
| 10784 | 11064 | IrInstruction *op2 = bin_op_instruction->op2->other; |
| 10785 | 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 | 11067 | if (type_is_invalid(resolved_type)) |
| 10788 | 11068 | return resolved_type; |
| 10789 | 11069 | IrBinOp op_id = bin_op_instruction->op_id; |
| ... | ... | @@ -11353,7 +11633,19 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc |
| 11353 | 11633 | } |
| 11354 | 11634 | break; |
| 11355 | 11635 | case VarClassRequiredAny: |
| 11356 | // OK | |
| 11636 | 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 | 11649 | break; |
| 11358 | 11650 | } |
| 11359 | 11651 | } |
| ... | ... | @@ -11579,18 +11871,25 @@ static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) { |
| 11579 | 11871 | static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, |
| 11580 | 11872 | IrInstructionErrorReturnTrace *instruction) |
| 11581 | 11873 | { |
| 11582 | TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen); | |
| 11583 | TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); | |
| 11584 | if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) { | |
| 11585 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); | |
| 11586 | out_val->data.x_maybe = nullptr; | |
| 11874 | if (instruction->nullable == IrInstructionErrorReturnTrace::Null) { | |
| 11875 | TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen); | |
| 11876 | TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type); | |
| 11877 | if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) { | |
| 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 | 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 | } |
| 11595 | 11894 | |
| 11596 | 11895 | static 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 | 12054 | } |
| 11756 | 12055 | } |
| 11757 | 12056 | |
| 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; | |
| 11759 | 12059 | |
| 11760 | 12060 | ConstExprValue *arg_val; |
| 11761 | 12061 | |
| ... | ... | @@ -11780,6 +12080,12 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod |
| 11780 | 12080 | var->shadowable = !comptime_arg; |
| 11781 | 12081 | |
| 11782 | 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 | } |
| 11784 | 12090 | |
| 11785 | 12091 | if (!comptime_arg) { |
| ... | ... | @@ -12726,7 +13032,7 @@ static TypeTableEntry *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstructionP |
| 12726 | 13032 | return first_value->value.type; |
| 12727 | 13033 | } |
| 12728 | 13034 | |
| 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 | 13036 | new_incoming_values.items, new_incoming_values.length); |
| 12731 | 13037 | if (type_is_invalid(resolved_type)) |
| 12732 | 13038 | return resolved_type; |
| ... | ... | @@ -13072,6 +13378,7 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira, |
| 13072 | 13378 | { |
| 13073 | 13379 | if (!is_slice(bare_struct_type)) { |
| 13074 | 13380 | ScopeDecls *container_scope = get_container_scope(bare_struct_type); |
| 13381 | assert(container_scope != nullptr); | |
| 13075 | 13382 | auto entry = container_scope->decl_table.maybe_get(field_name); |
| 13076 | 13383 | Tld *tld = entry ? entry->value : nullptr; |
| 13077 | 13384 | if (tld && tld->id == TldIdFn) { |
| ... | ... | @@ -13105,7 +13412,6 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira, |
| 13105 | 13412 | return ira->codegen->invalid_instruction; |
| 13106 | 13413 | } |
| 13107 | 13414 | |
| 13108 | ||
| 13109 | 13415 | static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, |
| 13110 | 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 | 13473 | } else if (bare_type->id == TypeTableEntryIdUnion) { |
| 13168 | 13474 | TypeUnionField *field = find_union_type_field(bare_type, field_name); |
| 13169 | 13475 | if (field) { |
| 13170 | IrInstruction *result = ir_build_union_field_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, container_ptr, field); | |
| 13171 | result->value.type = get_pointer_to_type_extra(ira->codegen, field->type_entry, is_const, is_volatile, | |
| 13172 | get_abi_alignment(ira->codegen, field->type_entry), 0, 0); | |
| 13173 | return result; | |
| 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 | } | |
| 13476 | if (instr_is_comptime(container_ptr)) { | |
| 13477 | ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); | |
| 13478 | if (!ptr_val) | |
| 13479 | return ira->codegen->invalid_instruction; | |
| 13182 | 13480 | |
| 13183 | static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) { | |
| 13184 | LinkLib *link_lib = add_link_lib(ira->codegen, lib_name); | |
| 13481 | if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { | |
| 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 | ||
| 13534 | static 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 | 13536 | for (size_t i = 0; i < link_lib->symbols.length; i += 1) { |
| 13186 | 13537 | Buf *existing_symbol_name = link_lib->symbols.at(i); |
| 13187 | 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 | 13626 | zig_unreachable(); |
| 13276 | 13627 | } |
| 13277 | 13628 | |
| 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 | 13638 | AstNode *source_node = field_ptr_instruction->base.source_node; |
| 13280 | 13639 | |
| 13281 | 13640 | if (type_is_invalid(container_type)) { |
| ... | ... | @@ -13377,7 +13736,16 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru |
| 13377 | 13736 | create_const_enum(child_type, &field->value), child_type, |
| 13378 | 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 | 13749 | (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr || |
| 13382 | 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 | 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 | 13765 | ir_add_error(ira, &field_ptr_instruction->base, |
| 13406 | 13766 | buf_sprintf("container '%s' has no member called '%s'", |
| 13407 | 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 | 13930 | } |
| 13571 | 13931 | } else if (child_type->id == TypeTableEntryIdFn) { |
| 13572 | 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 | 13942 | bool ptr_is_const = true; |
| 13574 | 13943 | bool ptr_is_volatile = false; |
| 13575 | 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 | 14709 | } |
| 14341 | 14710 | |
| 14342 | 14711 | if (value->value.type->id != TypeTableEntryIdUnion) { |
| 14343 | ir_add_error(ira, source_instr, | |
| 14712 | ir_add_error(ira, value, | |
| 14344 | 14713 | buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value.type->name))); |
| 14345 | 14714 | return ira->codegen->invalid_instruction; |
| 14346 | 14715 | } |
| ... | ... | @@ -14492,7 +14861,10 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, |
| 14492 | 14861 | return out_val->type; |
| 14493 | 14862 | } |
| 14494 | 14863 | |
| 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 | } | |
| 14496 | 14868 | |
| 14497 | 14869 | TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type; |
| 14498 | 14870 | ConstExprValue *pointee_val = nullptr; |
| ... | ... | @@ -15268,6 +15640,8 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, |
| 15268 | 15640 | } |
| 15269 | 15641 | |
| 15270 | 15642 | ensure_complete_type(ira->codegen, container_type); |
| 15643 | if (type_is_invalid(container_type)) | |
| 15644 | return ira->codegen->builtin_types.entry_invalid; | |
| 15271 | 15645 | |
| 15272 | 15646 | TypeStructField *field = find_struct_type_field(container_type, field_name); |
| 15273 | 15647 | if (field == nullptr) { |
| ... | ... | @@ -15374,6 +15748,910 @@ static TypeTableEntry *ir_analyze_instruction_offset_of(IrAnalyze *ira, |
| 15374 | 15748 | return ira->codegen->builtin_types.entry_num_lit_int; |
| 15375 | 15749 | } |
| 15376 | 15750 | |
| 15751 | static 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 | ||
| 15762 | static 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 | ||
| 15801 | static 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 | ||
| 16049 | static 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 | ||
| 16628 | static 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 | ||
| 15377 | 16655 | static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira, |
| 15378 | 16656 | IrInstructionTypeId *instruction) |
| 15379 | 16657 | { |
| ... | ... | @@ -15387,7 +16665,7 @@ static TypeTableEntry *ir_analyze_instruction_type_id(IrAnalyze *ira, |
| 15387 | 16665 | TypeTableEntry *result_type = var_value->data.x_type; |
| 15388 | 16666 | |
| 15389 | 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 | 16669 | return result_type; |
| 15392 | 16670 | } |
| 15393 | 16671 | |
| ... | ... | @@ -15590,10 +16868,20 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr |
| 15590 | 16868 | } |
| 15591 | 16869 | |
| 15592 | 16870 | static 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 | 16875 | IrInstruction *ptr = instruction->ptr->other; |
| 15594 | 16876 | if (type_is_invalid(ptr->value.type)) |
| 15595 | 16877 | return ira->codegen->builtin_types.entry_invalid; |
| 15596 | 16878 | |
| 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 | 16885 | IrInstruction *cmp_value = instruction->cmp_value->other; |
| 15598 | 16886 | if (type_is_invalid(cmp_value->value.type)) |
| 15599 | 16887 | return ira->codegen->builtin_types.entry_invalid; |
| ... | ... | @@ -15618,28 +16906,11 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct |
| 15618 | 16906 | if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order)) |
| 15619 | 16907 | return ira->codegen->builtin_types.entry_invalid; |
| 15620 | 16908 | |
| 15621 | if (ptr->value.type->id != TypeTableEntryIdPointer) { | |
| 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); | |
| 16909 | IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, operand_type); | |
| 15639 | 16910 | if (type_is_invalid(casted_cmp_value->value.type)) |
| 15640 | 16911 | return ira->codegen->builtin_types.entry_invalid; |
| 15641 | 16912 | |
| 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 | 16914 | if (type_is_invalid(casted_new_value->value.type)) |
| 15644 | 16915 | return ira->codegen->builtin_types.entry_invalid; |
| 15645 | 16916 | |
| ... | ... | @@ -15664,9 +16935,17 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct |
| 15664 | 16935 | return ira->codegen->builtin_types.entry_invalid; |
| 15665 | 16936 | } |
| 15666 | 16937 | |
| 15667 | ir_build_cmpxchg_from(&ira->new_irb, &instruction->base, ptr, casted_cmp_value, casted_new_value, | |
| 15668 | success_order_value, failure_order_value, success_order, failure_order); | |
| 15669 | return ira->codegen->builtin_types.entry_bool; | |
| 16938 | if (instr_is_comptime(casted_ptr) && instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) { | |
| 16939 | zig_panic("TODO compile-time execution of cmpxchg"); | |
| 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 | } |
| 15671 | 16950 | |
| 15672 | 16951 | static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) { |
| ... | ... | @@ -16702,6 +17981,11 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc |
| 16702 | 17981 | return ira->codegen->builtin_types.entry_invalid; |
| 16703 | 17982 | |
| 16704 | 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 | 17989 | IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->other; |
| 16706 | 17990 | fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value); |
| 16707 | 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 | 18844 | |
| 17561 | 18845 | ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); |
| 17562 | 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 | 18857 | return ira->codegen->builtin_types.entry_type; |
| 17564 | 18858 | } |
| 17565 | 18859 | |
| ... | ... | @@ -17795,39 +19089,48 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira, |
| 17795 | 19089 | return result->value.type; |
| 17796 | 19090 | } |
| 17797 | 19091 | |
| 17798 | static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) { | |
| 17799 | TypeTableEntry *operand_type = ir_resolve_type(ira, instruction->operand_type->other); | |
| 17800 | if (type_is_invalid(operand_type)) { | |
| 19092 | static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op) { | |
| 19093 | TypeTableEntry *operand_type = ir_resolve_type(ira, op); | |
| 19094 | if (type_is_invalid(operand_type)) | |
| 17801 | 19095 | return ira->codegen->builtin_types.entry_invalid; |
| 17802 | } | |
| 19096 | ||
| 17803 | 19097 | if (operand_type->id == TypeTableEntryIdInt) { |
| 17804 | 19098 | if (operand_type->data.integral.bit_count < 8) { |
| 17805 | ir_add_error(ira, &instruction->base, | |
| 19099 | ir_add_error(ira, op, | |
| 17806 | 19100 | buf_sprintf("expected integer type 8 bits or larger, found %" PRIu32 "-bit integer type", |
| 17807 | 19101 | operand_type->data.integral.bit_count)); |
| 17808 | 19102 | return ira->codegen->builtin_types.entry_invalid; |
| 17809 | 19103 | } |
| 17810 | 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 | 19106 | buf_sprintf("expected integer type pointer size or smaller, found %" PRIu32 "-bit integer type", |
| 17813 | 19107 | operand_type->data.integral.bit_count)); |
| 17814 | 19108 | return ira->codegen->builtin_types.entry_invalid; |
| 17815 | 19109 | } |
| 17816 | 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 | 19112 | buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count)); |
| 17819 | 19113 | return ira->codegen->builtin_types.entry_invalid; |
| 17820 | 19114 | } |
| 17821 | 19115 | } else if (get_codegen_ptr_type(operand_type) == nullptr) { |
| 17822 | ir_add_error(ira, &instruction->base, | |
| 19116 | ir_add_error(ira, op, | |
| 17823 | 19117 | buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name))); |
| 17824 | 19118 | return ira->codegen->builtin_types.entry_invalid; |
| 17825 | 19119 | } |
| 17826 | 19120 | |
| 19121 | return operand_type; | |
| 19122 | } | |
| 19123 | ||
| 19124 | static 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 | 19129 | IrInstruction *ptr_inst = instruction->ptr->other; |
| 17828 | 19130 | if (type_is_invalid(ptr_inst->value.type)) |
| 17829 | 19131 | return ira->codegen->builtin_types.entry_invalid; |
| 17830 | 19132 | |
| 19133 | // TODO let this be volatile | |
| 17831 | 19134 | TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); |
| 17832 | 19135 | IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); |
| 17833 | 19136 | if (type_is_invalid(casted_ptr->value.type)) |
| ... | ... | @@ -17856,6 +19159,11 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr |
| 17856 | 19159 | } else { |
| 17857 | 19160 | if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering)) |
| 17858 | 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 | } |
| 17860 | 19168 | |
| 17861 | 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 | 19179 | return result->value.type; |
| 17872 | 19180 | } |
| 17873 | 19181 | |
| 19182 | static 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 | ||
| 17874 | 19225 | static TypeTableEntry *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) { |
| 17875 | 19226 | TypeTableEntry *promise_type = ir_resolve_type(ira, instruction->promise_type->other); |
| 17876 | 19227 | if (type_is_invalid(promise_type)) |
| ... | ... | @@ -17904,6 +19255,39 @@ static TypeTableEntry *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira, |
| 17904 | 19255 | return out_val->type; |
| 17905 | 19256 | } |
| 17906 | 19257 | |
| 19258 | static 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 | ||
| 17907 | 19291 | static TypeTableEntry *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) { |
| 17908 | 19292 | IrInstruction *result = ir_build_save_err_ret_addr(&ira->new_irb, instruction->base.scope, |
| 17909 | 19293 | instruction->base.source_node); |
| ... | ... | @@ -17912,6 +19296,80 @@ static TypeTableEntry *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, |
| 17912 | 19296 | return result->value.type; |
| 17913 | 19297 | } |
| 17914 | 19298 | |
| 19299 | static 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 | ||
| 19311 | static 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 | ||
| 17915 | 19373 | static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) { |
| 17916 | 19374 | switch (instruction->id) { |
| 17917 | 19375 | case IrInstructionIdInvalid: |
| ... | ... | @@ -18095,6 +19553,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 18095 | 19553 | return ir_analyze_instruction_field_parent_ptr(ira, (IrInstructionFieldParentPtr *)instruction); |
| 18096 | 19554 | case IrInstructionIdOffsetOf: |
| 18097 | 19555 | return ir_analyze_instruction_offset_of(ira, (IrInstructionOffsetOf *)instruction); |
| 19556 | case IrInstructionIdTypeInfo: | |
| 19557 | return ir_analyze_instruction_type_info(ira, (IrInstructionTypeInfo *) instruction); | |
| 18098 | 19558 | case IrInstructionIdTypeId: |
| 18099 | 19559 | return ir_analyze_instruction_type_id(ira, (IrInstructionTypeId *)instruction); |
| 18100 | 19560 | case IrInstructionIdSetEvalBranchQuota: |
| ... | ... | @@ -18147,6 +19607,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 18147 | 19607 | return ir_analyze_instruction_coro_alloc_helper(ira, (IrInstructionCoroAllocHelper *)instruction); |
| 18148 | 19608 | case IrInstructionIdAtomicRmw: |
| 18149 | 19609 | return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction); |
| 19610 | case IrInstructionIdAtomicLoad: | |
| 19611 | return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction); | |
| 18150 | 19612 | case IrInstructionIdPromiseResultType: |
| 18151 | 19613 | return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction); |
| 18152 | 19614 | case IrInstructionIdAwaitBookkeeping: |
| ... | ... | @@ -18155,6 +19617,12 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi |
| 18155 | 19617 | return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction); |
| 18156 | 19618 | case IrInstructionIdAddImplicitReturnType: |
| 18157 | 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 | 19627 | zig_unreachable(); |
| 18160 | 19628 | } |
| ... | ... | @@ -18231,7 +19699,7 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl |
| 18231 | 19699 | } else if (ira->src_implicit_return_type_list.length == 0) { |
| 18232 | 19700 | return codegen->builtin_types.entry_unreachable; |
| 18233 | 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 | 19703 | ira->src_implicit_return_type_list.length); |
| 18236 | 19704 | } |
| 18237 | 19705 | } |
| ... | ... | @@ -18282,6 +19750,9 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 18282 | 19750 | case IrInstructionIdAwaitBookkeeping: |
| 18283 | 19751 | case IrInstructionIdSaveErrRetAddr: |
| 18284 | 19752 | case IrInstructionIdAddImplicitReturnType: |
| 19753 | case IrInstructionIdMergeErrRetTraces: | |
| 19754 | case IrInstructionIdMarkErrRetTracePtr: | |
| 19755 | case IrInstructionIdAtomicRmw: | |
| 18285 | 19756 | return true; |
| 18286 | 19757 | |
| 18287 | 19758 | case IrInstructionIdPhi: |
| ... | ... | @@ -18350,6 +19821,7 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 18350 | 19821 | case IrInstructionIdTagName: |
| 18351 | 19822 | case IrInstructionIdFieldParentPtr: |
| 18352 | 19823 | case IrInstructionIdOffsetOf: |
| 19824 | case IrInstructionIdTypeInfo: | |
| 18353 | 19825 | case IrInstructionIdTypeId: |
| 18354 | 19826 | case IrInstructionIdAlignCast: |
| 18355 | 19827 | case IrInstructionIdOpaqueType: |
| ... | ... | @@ -18362,9 +19834,10 @@ bool ir_has_side_effects(IrInstruction *instruction) { |
| 18362 | 19834 | case IrInstructionIdCoroSize: |
| 18363 | 19835 | case IrInstructionIdCoroSuspend: |
| 18364 | 19836 | case IrInstructionIdCoroFree: |
| 18365 | case IrInstructionIdAtomicRmw: | |
| 18366 | 19837 | case IrInstructionIdCoroPromise: |
| 18367 | 19838 | case IrInstructionIdPromiseResultType: |
| 19839 | case IrInstructionIdSqrt: | |
| 19840 | case IrInstructionIdAtomicLoad: | |
| 18368 | 19841 | return false; |
| 18369 | 19842 | |
| 18370 | 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 | 358 | } |
| 359 | 359 | |
| 360 | 360 | static void ir_print_field_ptr(IrPrint *irp, IrInstructionFieldPtr *instruction) { |
| 361 | fprintf(irp->f, "fieldptr "); | |
| 362 | ir_print_other_instruction(irp, instruction->container_ptr); | |
| 363 | fprintf(irp->f, ".%s", buf_ptr(instruction->field_name)); | |
| 361 | if (instruction->field_name_buffer) { | |
| 362 | fprintf(irp->f, "fieldptr "); | |
| 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 | } |
| 365 | 374 | |
| 366 | 375 | static 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 | 966 | fprintf(irp->f, ")"); |
| 958 | 967 | } |
| 959 | 968 | |
| 969 | static 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 | ||
| 960 | 975 | static void ir_print_type_id(IrPrint *irp, IrInstructionTypeId *instruction) { |
| 961 | 976 | fprintf(irp->f, "@typeId("); |
| 962 | 977 | ir_print_other_instruction(irp, instruction->type_value); |
| ... | ... | @@ -1024,7 +1039,16 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) { |
| 1024 | 1039 | } |
| 1025 | 1040 | |
| 1026 | 1041 | static 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 | } |
| 1029 | 1053 | |
| 1030 | 1054 | static void ir_print_error_union(IrPrint *irp, IrInstructionErrorUnion *instruction) { |
| ... | ... | @@ -1163,6 +1187,24 @@ static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instructio |
| 1163 | 1187 | fprintf(irp->f, ")"); |
| 1164 | 1188 | } |
| 1165 | 1189 | |
| 1190 | static 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 | ||
| 1166 | 1208 | static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeeping *instruction) { |
| 1167 | 1209 | fprintf(irp->f, "@awaitBookkeeping("); |
| 1168 | 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 | 1221 | fprintf(irp->f, ")"); |
| 1180 | 1222 | } |
| 1181 | 1223 | |
| 1224 | static 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 | ||
| 1234 | static 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 | ||
| 1240 | static 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 | ||
| 1182 | 1252 | static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1183 | 1253 | ir_print_prefix(irp, instruction); |
| 1184 | 1254 | switch (instruction->id) { |
| ... | ... | @@ -1472,6 +1542,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1472 | 1542 | case IrInstructionIdOffsetOf: |
| 1473 | 1543 | ir_print_offset_of(irp, (IrInstructionOffsetOf *)instruction); |
| 1474 | 1544 | break; |
| 1545 | case IrInstructionIdTypeInfo: | |
| 1546 | ir_print_type_info(irp, (IrInstructionTypeInfo *)instruction); | |
| 1547 | break; | |
| 1475 | 1548 | case IrInstructionIdTypeId: |
| 1476 | 1549 | ir_print_type_id(irp, (IrInstructionTypeId *)instruction); |
| 1477 | 1550 | break; |
| ... | ... | @@ -1559,6 +1632,18 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { |
| 1559 | 1632 | case IrInstructionIdAddImplicitReturnType: |
| 1560 | 1633 | ir_print_add_implicit_return_type(irp, (IrInstructionAddImplicitReturnType *)instruction); |
| 1561 | 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 | 1648 | fprintf(irp->f, "\n"); |
| 1564 | 1649 | } |
src/link.cpp+3| ... | ... | @@ -217,6 +217,9 @@ static void construct_linker_job_elf(LinkJob *lj) { |
| 217 | 217 | lj->args.append(g->linker_script); |
| 218 | 218 | } |
| 219 | 219 | |
| 220 | if (g->no_rosegment_workaround) { | |
| 221 | lj->args.append("--no-rosegment"); | |
| 222 | } | |
| 220 | 223 | lj->args.append("--gc-sections"); |
| 221 | 224 | |
| 222 | 225 | lj->args.append("-m"); |
src/main.cpp+16-25| ... | ... | @@ -43,6 +43,7 @@ static int usage(const char *arg0) { |
| 43 | 43 | " --pkg-end pop current pkg\n" |
| 44 | 44 | " --release-fast build with optimizations on and safety off\n" |
| 45 | 45 | " --release-safe build with optimizations on and safety on\n" |
| 46 | " --release-small build with size optimizations on and safety off\n" | |
| 46 | 47 | " --static output will be statically linked\n" |
| 47 | 48 | " --strip exclude debug symbols\n" |
| 48 | 49 | " --target-arch [name] specify target architecture\n" |
| ... | ... | @@ -54,7 +55,6 @@ static int usage(const char *arg0) { |
| 54 | 55 | " --verbose-ir turn on compiler debug output for Zig IR\n" |
| 55 | 56 | " --verbose-llvm-ir turn on compiler debug output for LLVM IR\n" |
| 56 | 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 | 58 | " -dirafter [dir] same as -isystem but do it last\n" |
| 59 | 59 | " -isystem [dir] add additional search path for other .h files\n" |
| 60 | 60 | " -mllvm [arg] additional arguments to forward to LLVM's option processing\n" |
| ... | ... | @@ -74,6 +74,7 @@ static int usage(const char *arg0) { |
| 74 | 74 | " -L[dir] alias for --library-path\n" |
| 75 | 75 | " -rdynamic add all symbols to the dynamic symbol table\n" |
| 76 | 76 | " -rpath [path] add directory to the runtime library search path\n" |
| 77 | " --no-rosegment compromise security to workaround valgrind bug\n" | |
| 77 | 78 | " -mconsole (windows) --subsystem console to the linker\n" |
| 78 | 79 | " -mwindows (windows) --subsystem windows to the linker\n" |
| 79 | 80 | " -framework [name] (darwin) link against framework\n" |
| ... | ... | @@ -177,6 +178,7 @@ static int find_zig_lib_dir(Buf *out_path) { |
| 177 | 178 | int err; |
| 178 | 179 | |
| 179 | 180 | Buf self_exe_path = BUF_INIT; |
| 181 | buf_resize(&self_exe_path, 0); | |
| 180 | 182 | if (!(err = os_self_exe_path(&self_exe_path))) { |
| 181 | 183 | Buf *cur_path = &self_exe_path; |
| 182 | 184 | |
| ... | ... | @@ -199,23 +201,14 @@ static int find_zig_lib_dir(Buf *out_path) { |
| 199 | 201 | return ErrorFileNotFound; |
| 200 | 202 | } |
| 201 | 203 | |
| 202 | static Buf *resolve_zig_lib_dir(const char *zig_install_prefix_arg) { | |
| 204 | static Buf *resolve_zig_lib_dir(void) { | |
| 203 | 205 | int err; |
| 204 | 206 | Buf *result = buf_alloc(); |
| 205 | if (zig_install_prefix_arg == nullptr) { | |
| 206 | if ((err = find_zig_lib_dir(result))) { | |
| 207 | fprintf(stderr, "Unable to find zig lib directory. Reinstall Zig or use --zig-install-prefix.\n"); | |
| 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; | |
| 207 | if ((err = find_zig_lib_dir(result))) { | |
| 208 | fprintf(stderr, "Unable to find zig lib directory\n"); | |
| 209 | exit(EXIT_FAILURE); | |
| 215 | 210 | } |
| 216 | ||
| 217 | fprintf(stderr, "No Zig installation found at prefix: %s\n", zig_install_prefix_arg); | |
| 218 | exit(EXIT_FAILURE); | |
| 211 | return result; | |
| 219 | 212 | } |
| 220 | 213 | |
| 221 | 214 | enum Cmd { |
| ... | ... | @@ -299,7 +292,6 @@ int main(int argc, char **argv) { |
| 299 | 292 | const char *libc_include_dir = nullptr; |
| 300 | 293 | const char *msvc_lib_dir = nullptr; |
| 301 | 294 | const char *kernel32_lib_dir = nullptr; |
| 302 | const char *zig_install_prefix = nullptr; | |
| 303 | 295 | const char *dynamic_linker = nullptr; |
| 304 | 296 | ZigList<const char *> clang_argv = {0}; |
| 305 | 297 | ZigList<const char *> llvm_argv = {0}; |
| ... | ... | @@ -333,6 +325,7 @@ int main(int argc, char **argv) { |
| 333 | 325 | ZigList<const char *> test_exec_args = {0}; |
| 334 | 326 | int comptime_args_end = 0; |
| 335 | 327 | int runtime_args_start = argc; |
| 328 | bool no_rosegment_workaround = false; | |
| 336 | 329 | |
| 337 | 330 | if (argc >= 2 && strcmp(argv[1], "build") == 0) { |
| 338 | 331 | const char *zig_exe_path = arg0; |
| ... | ... | @@ -359,17 +352,12 @@ int main(int argc, char **argv) { |
| 359 | 352 | } else if (i + 1 < argc && strcmp(argv[i], "--cache-dir") == 0) { |
| 360 | 353 | cache_dir = argv[i + 1]; |
| 361 | 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 | 355 | } else { |
| 368 | 356 | args.append(argv[i]); |
| 369 | 357 | } |
| 370 | 358 | } |
| 371 | 359 | |
| 372 | Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix); | |
| 360 | Buf *zig_lib_dir_buf = resolve_zig_lib_dir(); | |
| 373 | 361 | |
| 374 | 362 | Buf *zig_std_dir = buf_alloc(); |
| 375 | 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 | 485 | build_mode = BuildModeFastRelease; |
| 498 | 486 | } else if (strcmp(arg, "--release-safe") == 0) { |
| 499 | 487 | build_mode = BuildModeSafeRelease; |
| 488 | } else if (strcmp(arg, "--release-small") == 0) { | |
| 489 | build_mode = BuildModeSmallRelease; | |
| 500 | 490 | } else if (strcmp(arg, "--strip") == 0) { |
| 501 | 491 | strip = true; |
| 502 | 492 | } else if (strcmp(arg, "--static") == 0) { |
| ... | ... | @@ -519,6 +509,8 @@ int main(int argc, char **argv) { |
| 519 | 509 | mconsole = true; |
| 520 | 510 | } else if (strcmp(arg, "-rdynamic") == 0) { |
| 521 | 511 | rdynamic = true; |
| 512 | } else if (strcmp(arg, "--no-rosegment") == 0) { | |
| 513 | no_rosegment_workaround = true; | |
| 522 | 514 | } else if (strcmp(arg, "--each-lib-rpath") == 0) { |
| 523 | 515 | each_lib_rpath = true; |
| 524 | 516 | } else if (strcmp(arg, "--enable-timing-info") == 0) { |
| ... | ... | @@ -590,8 +582,6 @@ int main(int argc, char **argv) { |
| 590 | 582 | msvc_lib_dir = argv[i]; |
| 591 | 583 | } else if (strcmp(arg, "--kernel32-lib-dir") == 0) { |
| 592 | 584 | kernel32_lib_dir = argv[i]; |
| 593 | } else if (strcmp(arg, "--zig-install-prefix") == 0) { | |
| 594 | zig_install_prefix = argv[i]; | |
| 595 | 585 | } else if (strcmp(arg, "--dynamic-linker") == 0) { |
| 596 | 586 | dynamic_linker = argv[i]; |
| 597 | 587 | } else if (strcmp(arg, "-isystem") == 0) { |
| ... | ... | @@ -803,7 +793,7 @@ int main(int argc, char **argv) { |
| 803 | 793 | full_cache_dir); |
| 804 | 794 | } |
| 805 | 795 | |
| 806 | Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix); | |
| 796 | Buf *zig_lib_dir_buf = resolve_zig_lib_dir(); | |
| 807 | 797 | |
| 808 | 798 | CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, zig_lib_dir_buf); |
| 809 | 799 | codegen_set_out_name(g, buf_out_name); |
| ... | ... | @@ -858,6 +848,7 @@ int main(int argc, char **argv) { |
| 858 | 848 | |
| 859 | 849 | codegen_set_windows_subsystem(g, mwindows, mconsole); |
| 860 | 850 | codegen_set_rdynamic(g, rdynamic); |
| 851 | g->no_rosegment_workaround = no_rosegment_workaround; | |
| 861 | 852 | if (mmacosx_version_min && mios_version_min) { |
| 862 | 853 | fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n"); |
| 863 | 854 | return EXIT_FAILURE; |
src/os.cpp+1| ... | ... | @@ -1007,6 +1007,7 @@ int os_self_exe_path(Buf *out_path) { |
| 1007 | 1007 | buf_resize(out_path, buf_len(out_path) * 2); |
| 1008 | 1008 | continue; |
| 1009 | 1009 | } |
| 1010 | buf_resize(out_path, amt); | |
| 1010 | 1011 | return 0; |
| 1011 | 1012 | } |
| 1012 | 1013 | #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 | 648 | } |
| 649 | 649 | |
| 650 | 650 | /* |
| 651 | SuspendExpression(body) = "suspend" "|" Symbol "|" body | |
| 651 | SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body)) | |
| 652 | 652 | */ |
| 653 | 653 | static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) { |
| 654 | 654 | size_t orig_token_index = *token_index; |
| 655 | 655 | |
| 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 | 675 | if (suspend_token->id == TokenIdKeywordSuspend) { |
| 658 | 676 | *token_index += 1; |
| 659 | 677 | } else if (mandatory) { |
| ... | ... | @@ -675,6 +693,9 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b |
| 675 | 693 | } |
| 676 | 694 | |
| 677 | 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 | 699 | node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index); |
| 679 | 700 | ast_eat_token(pc, token_index, TokenIdBinOr); |
| 680 | 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 | 2944 | visit_field(&node->data.fn_def.fn_proto, visit, context); |
| 2924 | 2945 | visit_field(&node->data.fn_def.body, visit, context); |
| 2925 | 2946 | break; |
| 2926 | case NodeTypeFnDecl: | |
| 2927 | visit_field(&node->data.fn_decl.fn_proto, visit, context); | |
| 2928 | break; | |
| 2929 | 2947 | case NodeTypeParamDecl: |
| 2930 | 2948 | visit_field(&node->data.param_decl.type, visit, context); |
| 2931 | 2949 | break; |
src/translate_c.cpp+45-71| ... | ... | @@ -3667,6 +3667,7 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_ |
| 3667 | 3667 | if (existing_entry) { |
| 3668 | 3668 | return existing_entry->value; |
| 3669 | 3669 | } |
| 3670 | ||
| 3670 | 3671 | QualType child_qt = typedef_decl->getUnderlyingType(); |
| 3671 | 3672 | Buf *type_name = buf_create_from_str(decl_name(typedef_decl)); |
| 3672 | 3673 | |
| ... | ... | @@ -3700,16 +3701,19 @@ static AstNode *resolve_typedef_decl(Context *c, const TypedefNameDecl *typedef_ |
| 3700 | 3701 | // use the name of this typedef |
| 3701 | 3702 | // TODO |
| 3702 | 3703 | |
| 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 | 3708 | AstNode *type_node = trans_qual_type(c, child_qt, typedef_decl->getLocation()); |
| 3704 | 3709 | if (type_node == nullptr) { |
| 3705 | 3710 | emit_warning(c, typedef_decl->getLocation(), "typedef %s - unresolved child type", buf_ptr(type_name)); |
| 3706 | 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 | 3713 | return nullptr; |
| 3708 | 3714 | } |
| 3709 | 3715 | add_global_var(c, type_name, type_node); |
| 3710 | 3716 | |
| 3711 | AstNode *symbol_node = trans_create_node_symbol(c, type_name); | |
| 3712 | c->decl_table.put(typedef_decl->getCanonicalDecl(), symbol_node); | |
| 3713 | 3717 | return symbol_node; |
| 3714 | 3718 | } |
| 3715 | 3719 | |
| ... | ... | @@ -3744,6 +3748,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { |
| 3744 | 3748 | return demote_enum_to_opaque(c, enum_decl, full_type_name, bare_name); |
| 3745 | 3749 | } |
| 3746 | 3750 | |
| 3751 | ||
| 3747 | 3752 | bool pure_enum = true; |
| 3748 | 3753 | uint32_t field_count = 0; |
| 3749 | 3754 | for (auto it = enum_def->enumerator_begin(), |
| ... | ... | @@ -3755,84 +3760,53 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { |
| 3755 | 3760 | pure_enum = false; |
| 3756 | 3761 | } |
| 3757 | 3762 | } |
| 3758 | ||
| 3759 | 3763 | AstNode *tag_int_type = trans_qual_type(c, enum_decl->getIntegerType(), enum_decl->getLocation()); |
| 3760 | 3764 | assert(tag_int_type); |
| 3761 | 3765 | |
| 3762 | if (pure_enum) { | |
| 3763 | AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl); | |
| 3764 | enum_node->data.container_decl.kind = ContainerKindEnum; | |
| 3765 | enum_node->data.container_decl.layout = ContainerLayoutExtern; | |
| 3766 | // TODO only emit this tag type if the enum tag type is not the default. | |
| 3767 | // I don't know what the default is, need to figure out how clang is deciding. | |
| 3768 | // it appears to at least be different across gcc/msvc | |
| 3769 | if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) && | |
| 3770 | !c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int)) | |
| 3771 | { | |
| 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 | } | |
| 3766 | AstNode *enum_node = trans_create_node(c, NodeTypeContainerDecl); | |
| 3767 | enum_node->data.container_decl.kind = ContainerKindEnum; | |
| 3768 | enum_node->data.container_decl.layout = ContainerLayoutExtern; | |
| 3769 | // 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. | |
| 3771 | // it appears to at least be different across gcc/msvc | |
| 3772 | if (!c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::UInt) && | |
| 3773 | !c_is_builtin_type(c, enum_decl->getIntegerType(), BuiltinType::Int)) | |
| 3774 | { | |
| 3775 | enum_node->data.container_decl.init_arg_expr = tag_int_type; | |
| 3818 | 3776 | } |
| 3819 | ||
| 3820 | // TODO after issue #305 is solved, make this be an enum with tag_int_type | |
| 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 | |
| 3777 | enum_node->data.container_decl.fields.resize(field_count); | |
| 3778 | uint32_t i = 0; | |
| 3826 | 3779 | for (auto it = enum_def->enumerator_begin(), |
| 3827 | 3780 | it_end = enum_def->enumerator_end(); |
| 3828 | it != it_end; ++it) | |
| 3781 | it != it_end; ++it, i += 1) | |
| 3829 | 3782 | { |
| 3830 | 3783 | const EnumConstantDecl *enum_const = *it; |
| 3831 | 3784 | |
| 3832 | 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()); | |
| 3834 | AstNode *var_node = add_global_var(c, enum_val_name, int_node); | |
| 3835 | var_node->data.variable_declaration.type = tag_int_type; | |
| 3786 | Buf *field_name; | |
| 3787 | if (bare_name != nullptr && buf_starts_with_buf(enum_val_name, bare_name)) { | |
| 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 | } |
| 3837 | 3811 | |
| 3838 | 3812 | if (is_anonymous) { |
| ... | ... | @@ -3843,7 +3817,7 @@ static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl) { |
| 3843 | 3817 | add_global_weak_alias(c, bare_name, full_type_name); |
| 3844 | 3818 | add_global_var(c, full_type_name, enum_node); |
| 3845 | 3819 | c->decl_table.put(enum_decl->getCanonicalDecl(), symbol_node); |
| 3846 | return symbol_node; | |
| 3820 | return enum_node; | |
| 3847 | 3821 | } |
| 3848 | 3822 | } |
| 3849 | 3823 |
src/zig_llvm.cpp+7-5| ... | ... | @@ -81,7 +81,7 @@ static const bool assertions_on = false; |
| 81 | 81 | #endif |
| 82 | 82 | |
| 83 | 83 | bool 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 | 86 | std::error_code EC; |
| 87 | 87 | raw_fd_ostream dest(filename, EC, sys::fs::F_None); |
| ... | ... | @@ -100,7 +100,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM |
| 100 | 100 | return true; |
| 101 | 101 | } |
| 102 | 102 | PMBuilder->OptLevel = target_machine->getOptLevel(); |
| 103 | PMBuilder->SizeLevel = 0; | |
| 103 | PMBuilder->SizeLevel = is_small ? 2 : 0; | |
| 104 | 104 | |
| 105 | 105 | PMBuilder->DisableTailCalls = is_debug; |
| 106 | 106 | PMBuilder->DisableUnitAtATime = is_debug; |
| ... | ... | @@ -765,10 +765,12 @@ static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) { |
| 765 | 765 | |
| 766 | 766 | LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp, |
| 767 | 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), | |
| 771 | mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering))); | |
| 770 | AtomicCmpXchgInst *inst = unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp), | |
| 771 | unwrap(new_val), mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering)); | |
| 772 | inst->setWeak(is_weak); | |
| 773 | return wrap(inst); | |
| 772 | 774 | } |
| 773 | 775 | |
| 774 | 776 | LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, |
src/zig_llvm.h+2-2| ... | ... | @@ -52,7 +52,7 @@ enum ZigLLVM_EmitOutputType { |
| 52 | 52 | }; |
| 53 | 53 | |
| 54 | 54 | ZIG_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); | |
| 56 | 56 | |
| 57 | 57 | ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref); |
| 58 | 58 | |
| ... | ... | @@ -66,7 +66,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LL |
| 66 | 66 | |
| 67 | 67 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp, |
| 68 | 68 | LLVMValueRef new_val, LLVMAtomicOrdering success_ordering, |
| 69 | LLVMAtomicOrdering failure_ordering); | |
| 69 | LLVMAtomicOrdering failure_ordering, bool is_weak); | |
| 70 | 70 | |
| 71 | 71 | ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, |
| 72 | 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 | 28 | }; |
| 29 | 29 | } |
| 30 | 30 | |
| 31 | pub fn deinit(l: &Self) void { | |
| 31 | pub fn deinit(l: &const Self) void { | |
| 32 | 32 | l.allocator.free(l.items); |
| 33 | 33 | } |
| 34 | 34 | |
| 35 | pub fn toSlice(l: &Self) []align(A) T { | |
| 35 | pub fn toSlice(l: &const Self) []align(A) T { | |
| 36 | 36 | return l.items[0..l.len]; |
| 37 | 37 | } |
| 38 | 38 | |
| ... | ... | @@ -44,6 +44,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ |
| 44 | 44 | return l.toSliceConst()[n]; |
| 45 | 45 | } |
| 46 | 46 | |
| 47 | pub fn count(self: &const Self) usize { | |
| 48 | return self.len; | |
| 49 | } | |
| 50 | ||
| 47 | 51 | /// ArrayList takes ownership of the passed in slice. The slice must have been |
| 48 | 52 | /// allocated with `allocator`. |
| 49 | 53 | /// Deinitialize with `deinit` or use `toOwnedSlice`. |
| ... | ... | @@ -128,6 +132,27 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type{ |
| 128 | 132 | return null; |
| 129 | 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 | } |
| 133 | 158 | |
| ... | ... | @@ -143,6 +168,14 @@ test "basic ArrayList test" { |
| 143 | 168 | assert(list.items[i] == i32(i + 1)); |
| 144 | 169 | }} |
| 145 | 170 | |
| 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 | 179 | assert(list.pop() == 10); |
| 147 | 180 | assert(list.len == 9); |
| 148 | 181 | |
| ... | ... | @@ -157,6 +190,35 @@ test "basic ArrayList test" { |
| 157 | 190 | assert(list.len == 9); |
| 158 | 191 | } |
| 159 | 192 | |
| 193 | test "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 | ||
| 160 | 222 | test "insert ArrayList test" { |
| 161 | 223 | var list = ArrayList(i32).init(debug.global_allocator); |
| 162 | 224 | defer list.deinit(); |
std/atomic/index.zig created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | pub const Stack = @import("stack.zig").Stack; | |
| 2 | pub const Queue = @import("queue.zig").Queue; | |
| 3 | ||
| 4 | test "std.atomic" { | |
| 5 | _ = @import("stack.zig").Stack; | |
| 6 | _ = @import("queue.zig").Queue; | |
| 7 | } |
std/atomic/queue.zig created+126| ... | ... | @@ -0,0 +1,126 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const AtomicOrder = builtin.AtomicOrder; | |
| 3 | const AtomicRmwOp = builtin.AtomicRmwOp; | |
| 4 | ||
| 5 | /// Many reader, many writer, non-allocating, thread-safe, lock-free | |
| 6 | pub 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 | ||
| 43 | const std = @import("std"); | |
| 44 | const 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 | |
| 58 | const puts_per_thread = 500; | |
| 59 | const put_thread_count = 3; | |
| 60 | ||
| 61 | test "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 | ||
| 99 | fn 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 | ||
| 113 | fn 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 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const AtomicOrder = builtin.AtomicOrder; | |
| 3 | ||
| 4 | /// Many reader, many writer, non-allocating, thread-safe, lock-free | |
| 5 | pub 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 | ||
| 50 | const std = @import("std"); | |
| 51 | const 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 | |
| 64 | const puts_per_thread = 500; | |
| 65 | const put_thread_count = 3; | |
| 66 | ||
| 67 | test "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 | ||
| 104 | fn 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 | ||
| 118 | fn 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 | const HashMap = @import("hash_map.zig").HashMap; | |
| 2 | const mem = @import("mem.zig"); | |
| 1 | const std = @import("index.zig"); | |
| 2 | const HashMap = std.HashMap; | |
| 3 | const mem = std.mem; | |
| 3 | 4 | const Allocator = mem.Allocator; |
| 5 | const assert = std.debug.assert; | |
| 4 | 6 | |
| 5 | 7 | /// BufMap copies keys and values before they go into the map, and |
| 6 | 8 | /// frees them when they get removed. |
| ... | ... | @@ -16,10 +18,10 @@ pub const BufMap = struct { |
| 16 | 18 | return self; |
| 17 | 19 | } |
| 18 | 20 | |
| 19 | pub fn deinit(self: &BufMap) void { | |
| 21 | pub fn deinit(self: &const BufMap) void { | |
| 20 | 22 | var it = self.hash_map.iterator(); |
| 21 | 23 | while (true) { |
| 22 | const entry = it.next() ?? break; | |
| 24 | const entry = it.next() ?? break; | |
| 23 | 25 | self.free(entry.key); |
| 24 | 26 | self.free(entry.value); |
| 25 | 27 | } |
| ... | ... | @@ -28,21 +30,15 @@ pub const BufMap = struct { |
| 28 | 30 | } |
| 29 | 31 | |
| 30 | 32 | pub fn set(self: &BufMap, key: []const u8, value: []const u8) !void { |
| 31 | if (self.hash_map.get(key)) |entry| { | |
| 32 | const value_copy = try self.copy(value); | |
| 33 | errdefer self.free(value_copy); | |
| 34 | _ = try self.hash_map.put(key, value_copy); | |
| 35 | self.free(entry.value); | |
| 36 | } else { | |
| 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 | } | |
| 33 | self.delete(key); | |
| 34 | const key_copy = try self.copy(key); | |
| 35 | errdefer self.free(key_copy); | |
| 36 | const value_copy = try self.copy(value); | |
| 37 | errdefer self.free(value_copy); | |
| 38 | _ = try self.hash_map.put(key_copy, value_copy); | |
| 43 | 39 | } |
| 44 | 40 | |
| 45 | pub fn get(self: &BufMap, key: []const u8) ?[]const u8 { | |
| 41 | pub fn get(self: &const BufMap, key: []const u8) ?[]const u8 { | |
| 46 | 42 | const entry = self.hash_map.get(key) ?? return null; |
| 47 | 43 | return entry.value; |
| 48 | 44 | } |
| ... | ... | @@ -54,20 +50,41 @@ pub const BufMap = struct { |
| 54 | 50 | } |
| 55 | 51 | |
| 56 | 52 | pub fn count(self: &const BufMap) usize { |
| 57 | return self.hash_map.size; | |
| 53 | return self.hash_map.count(); | |
| 58 | 54 | } |
| 59 | 55 | |
| 60 | 56 | pub fn iterator(self: &const BufMap) BufMapHashMap.Iterator { |
| 61 | 57 | return self.hash_map.iterator(); |
| 62 | 58 | } |
| 63 | 59 | |
| 64 | fn free(self: &BufMap, value: []const u8) void { | |
| 60 | fn free(self: &const BufMap, value: []const u8) void { | |
| 65 | 61 | self.hash_map.allocator.free(value); |
| 66 | 62 | } |
| 67 | 63 | |
| 68 | fn copy(self: &BufMap, value: []const u8) ![]const u8 { | |
| 69 | const result = try self.hash_map.allocator.alloc(u8, value.len); | |
| 70 | mem.copy(u8, result, value); | |
| 71 | return result; | |
| 64 | fn copy(self: &const BufMap, value: []const u8) ![]const u8 { | |
| 65 | return mem.dupe(self.hash_map.allocator, u8, value); | |
| 72 | 66 | } |
| 73 | 67 | }; |
| 68 | ||
| 69 | test "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 | const std = @import("index.zig"); | |
| 1 | 2 | const HashMap = @import("hash_map.zig").HashMap; |
| 2 | 3 | const mem = @import("mem.zig"); |
| 3 | 4 | const Allocator = mem.Allocator; |
| 5 | const assert = std.debug.assert; | |
| 4 | 6 | |
| 5 | 7 | pub const BufSet = struct { |
| 6 | 8 | hash_map: BufSetHashMap, |
| ... | ... | @@ -14,10 +16,10 @@ pub const BufSet = struct { |
| 14 | 16 | return self; |
| 15 | 17 | } |
| 16 | 18 | |
| 17 | pub fn deinit(self: &BufSet) void { | |
| 19 | pub fn deinit(self: &const BufSet) void { | |
| 18 | 20 | var it = self.hash_map.iterator(); |
| 19 | 21 | while (true) { |
| 20 | const entry = it.next() ?? break; | |
| 22 | const entry = it.next() ?? break; | |
| 21 | 23 | self.free(entry.key); |
| 22 | 24 | } |
| 23 | 25 | |
| ... | ... | @@ -38,7 +40,7 @@ pub const BufSet = struct { |
| 38 | 40 | } |
| 39 | 41 | |
| 40 | 42 | pub fn count(self: &const BufSet) usize { |
| 41 | return self.hash_map.size; | |
| 43 | return self.hash_map.count(); | |
| 42 | 44 | } |
| 43 | 45 | |
| 44 | 46 | pub fn iterator(self: &const BufSet) BufSetHashMap.Iterator { |
| ... | ... | @@ -49,14 +51,30 @@ pub const BufSet = struct { |
| 49 | 51 | return self.hash_map.allocator; |
| 50 | 52 | } |
| 51 | 53 | |
| 52 | fn free(self: &BufSet, value: []const u8) void { | |
| 54 | fn free(self: &const BufSet, value: []const u8) void { | |
| 53 | 55 | self.hash_map.allocator.free(value); |
| 54 | 56 | } |
| 55 | 57 | |
| 56 | fn copy(self: &BufSet, value: []const u8) ![]const u8 { | |
| 58 | fn copy(self: &const BufSet, value: []const u8) ![]const u8 { | |
| 57 | 59 | const result = try self.hash_map.allocator.alloc(u8, value.len); |
| 58 | 60 | mem.copy(u8, result, value); |
| 59 | 61 | return result; |
| 60 | 62 | } |
| 61 | 63 | }; |
| 62 | 64 | |
| 65 | test "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 | 66 | self.list.deinit(); |
| 67 | 67 | } |
| 68 | 68 | |
| 69 | pub fn toSlice(self: &Buffer) []u8 { | |
| 69 | pub fn toSlice(self: &const Buffer) []u8 { | |
| 70 | 70 | return self.list.toSlice()[0..self.len()]; |
| 71 | 71 | } |
| 72 | 72 | |
| ... | ... | @@ -99,26 +99,10 @@ pub const Buffer = struct { |
| 99 | 99 | mem.copy(u8, self.list.toSlice()[old_len..], m); |
| 100 | 100 | } |
| 101 | 101 | |
| 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 | 102 | pub fn appendByte(self: &Buffer, byte: u8) !void { |
| 109 | return self.appendByteNTimes(byte, 1); | |
| 110 | } | |
| 111 | ||
| 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 | } | |
| 103 | const old_len = self.len(); | |
| 104 | try self.resize(old_len + 1); | |
| 105 | self.list.toSlice()[old_len] = byte; | |
| 122 | 106 | } |
| 123 | 107 | |
| 124 | 108 | pub fn eql(self: &const Buffer, m: []const u8) bool { |
| ... | ... | @@ -154,7 +138,7 @@ test "simple Buffer" { |
| 154 | 138 | var buf = try Buffer.init(debug.global_allocator, ""); |
| 155 | 139 | assert(buf.len() == 0); |
| 156 | 140 | try buf.append("hello"); |
| 157 | try buf.appendByte(' '); | |
| 141 | try buf.append(" "); | |
| 158 | 142 | try buf.append("world"); |
| 159 | 143 | assert(buf.eql("hello world")); |
| 160 | 144 | assert(mem.eql(u8, cstr.toSliceConst(buf.toSliceConst().ptr), buf.toSliceConst())); |
| ... | ... | @@ -166,5 +150,5 @@ test "simple Buffer" { |
| 166 | 150 | assert(buf.endsWith("orld")); |
| 167 | 151 | |
| 168 | 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 | 426 | |
| 427 | 427 | const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false; |
| 428 | 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; | |
| 429 | 430 | |
| 430 | const mode = if (release_safe and !release_fast) | |
| 431 | const mode = if (release_safe and !release_fast and !release_small) | |
| 431 | 432 | builtin.Mode.ReleaseSafe |
| 432 | else if (release_fast and !release_safe) | |
| 433 | else if (release_fast and !release_safe and !release_small) | |
| 433 | 434 | 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 | 438 | builtin.Mode.Debug |
| 436 | 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 | 441 | self.markInvalidUserInput(); |
| 439 | 442 | break :x builtin.Mode.Debug; |
| 440 | 443 | }; |
| ... | ... | @@ -1229,6 +1232,7 @@ pub const LibExeObjStep = struct { |
| 1229 | 1232 | builtin.Mode.Debug => {}, |
| 1230 | 1233 | builtin.Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable, |
| 1231 | 1234 | builtin.Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable, |
| 1235 | builtin.Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable, | |
| 1232 | 1236 | } |
| 1233 | 1237 | |
| 1234 | 1238 | zig_args.append("--cache-dir") catch unreachable; |
| ... | ... | @@ -1369,7 +1373,7 @@ pub const LibExeObjStep = struct { |
| 1369 | 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 | 1377 | args.append("-O2") catch unreachable; |
| 1374 | 1378 | args.append("-fno-stack-protector") catch unreachable; |
| 1375 | 1379 | }, |
| ... | ... | @@ -1706,6 +1710,7 @@ pub const TestStep = struct { |
| 1706 | 1710 | builtin.Mode.Debug => {}, |
| 1707 | 1711 | builtin.Mode.ReleaseSafe => try zig_args.append("--release-safe"), |
| 1708 | 1712 | builtin.Mode.ReleaseFast => try zig_args.append("--release-fast"), |
| 1713 | builtin.Mode.ReleaseSmall => try zig_args.append("--release-small"), | |
| 1709 | 1714 | } |
| 1710 | 1715 | |
| 1711 | 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 | 3 | |
| 4 | 4 | pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize; |
| 5 | 5 | |
| 6 | pub extern "c" fn mach_absolute_time() u64; | |
| 7 | pub extern "c" fn mach_timebase_info(tinfo: ?&mach_timebase_info_data) void; | |
| 8 | ||
| 6 | 9 | pub use @import("../os/darwin_errno.zig"); |
| 7 | 10 | |
| 8 | 11 | pub const _errno = __error; |
| 9 | 12 | |
| 13 | pub const timeval = extern struct { | |
| 14 | tv_sec: isize, | |
| 15 | tv_usec: isize, | |
| 16 | }; | |
| 17 | ||
| 18 | pub const timezone = extern struct { | |
| 19 | tz_minuteswest: i32, | |
| 20 | tz_dsttime: i32, | |
| 21 | }; | |
| 22 | ||
| 23 | pub const mach_timebase_info_data = struct { | |
| 24 | numer: u32, | |
| 25 | denom: u32, | |
| 26 | }; | |
| 27 | ||
| 10 | 28 | /// Renamed to Stat to not conflict with the stat function. |
| 11 | 29 | pub const Stat = extern struct { |
| 12 | 30 | dev: i32, |
| ... | ... | @@ -55,3 +73,16 @@ pub const dirent = extern struct { |
| 55 | 73 | d_type: u8, |
| 56 | 74 | d_name: u8, // field address is address of first byte of name |
| 57 | 75 | }; |
| 76 | ||
| 77 | pub const sockaddr = extern struct { | |
| 78 | sa_len: u8, | |
| 79 | sa_family: sa_family_t, | |
| 80 | sa_data: [14]u8, | |
| 81 | }; | |
| 82 | ||
| 83 | pub const sa_family_t = u8; | |
| 84 | ||
| 85 | pub 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 | 28 | pub extern "c" fn getcwd(buf: &u8, size: usize) ?&u8; |
| 29 | 29 | pub extern "c" fn waitpid(pid: c_int, stat_loc: &c_int, options: c_int) c_int; |
| 30 | 30 | pub extern "c" fn fork() c_int; |
| 31 | pub extern "c" fn access(path: &const u8, mode: c_uint) c_int; | |
| 31 | 32 | pub extern "c" fn pipe(fds: &c_int) c_int; |
| 32 | 33 | pub extern "c" fn mkdir(path: &const u8, mode: c_uint) c_int; |
| 33 | 34 | pub 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 | 41 | pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize; |
| 41 | 42 | pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8; |
| 42 | 43 | pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int; |
| 44 | pub extern "c" fn gettimeofday(tv: ?&timeval, tz: ?&timezone) c_int; | |
| 43 | 45 | pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int; |
| 44 | 46 | pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?&timespec) c_int; |
| 45 | 47 | pub 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 | 53 | pub extern "c" fn realloc(&c_void, usize) ?&c_void; |
| 52 | 54 | pub extern "c" fn free(&c_void) void; |
| 53 | 55 | pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) c_int; |
| 56 | ||
| 57 | pub 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; | |
| 60 | pub extern "pthread" fn pthread_attr_init(attr: &pthread_attr_t) c_int; | |
| 61 | pub extern "pthread" fn pthread_attr_setstack(attr: &pthread_attr_t, stackaddr: &c_void, stacksize: usize) c_int; | |
| 62 | pub extern "pthread" fn pthread_attr_destroy(attr: &pthread_attr_t) c_int; | |
| 63 | pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?&?&c_void) c_int; | |
| 64 | ||
| 65 | pub const pthread_t = &@OpaqueType(); |
std/c/linux.zig+5| ... | ... | @@ -3,3 +3,8 @@ pub use @import("../os/linux/errno.zig"); |
| 3 | 3 | pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) c_int; |
| 4 | 4 | extern "c" fn __errno_location() &c_int; |
| 5 | 5 | pub const _errno = __errno_location; |
| 6 | ||
| 7 | pub 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 | 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.zig | |
| 4 | // zig build-exe --release-fast throughput_test.zig | |
| 8 | 5 | // ./throughput_test |
| 9 | 6 | // ``` |
| 10 | const HashFunction = @import("md5.zig").Md5; | |
| 11 | const BytesToHash = 1024 * Mb; | |
| 12 | 7 | |
| 13 | 8 | const std = @import("std"); |
| 9 | const time = std.os.time; | |
| 10 | const Timer = time.Timer; | |
| 11 | const HashFunction = @import("md5.zig").Md5; | |
| 14 | 12 | |
| 15 | const c = @cImport({ | |
| 16 | @cInclude("time.h"); | |
| 17 | }); | |
| 18 | ||
| 19 | const Mb = 1024 * 1024; | |
| 13 | const MiB = 1024 * 1024; | |
| 14 | const BytesToHash = 1024 * MiB; | |
| 20 | 15 | |
| 21 | 16 | pub fn main() !void { |
| 22 | 17 | var stdout_file = try std.io.getStdOut(); |
| ... | ... | @@ -29,15 +24,15 @@ pub fn main() !void { |
| 29 | 24 | var h = HashFunction.init(); |
| 30 | 25 | var offset: usize = 0; |
| 31 | 26 | |
| 32 | const start = c.clock(); | |
| 27 | var timer = try Timer.start(); | |
| 28 | const start = timer.lap(); | |
| 33 | 29 | while (offset < BytesToHash) : (offset += block.len) { |
| 34 | 30 | h.update(block[0..]); |
| 35 | 31 | } |
| 36 | const end = c.clock(); | |
| 32 | const end = timer.read(); | |
| 37 | 33 | |
| 38 | const elapsed_s = f64((end - start) * c.CLOCKS_PER_SEC) / 1000000; | |
| 34 | const elapsed_s = f64(end - start) / time.ns_per_s; | |
| 39 | 35 | const throughput = u64(BytesToHash / elapsed_s); |
| 40 | 36 | |
| 41 | try stdout.print("{}: ", @typeName(HashFunction)); | |
| 42 | try stdout.print("{} Mb/s\n", throughput); | |
| 37 | try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB)); | |
| 43 | 38 | } |
std/debug/index.zig+20-5| ... | ... | @@ -38,7 +38,7 @@ pub fn getSelfDebugInfo() !&ElfStackTrace { |
| 38 | 38 | if (self_debug_info) |info| { |
| 39 | 39 | return info; |
| 40 | 40 | } else { |
| 41 | const info = try openSelfDebugInfo(global_allocator); | |
| 41 | const info = try openSelfDebugInfo(getDebugInfoAllocator()); | |
| 42 | 42 | self_debug_info = info; |
| 43 | 43 | return info; |
| 44 | 44 | } |
| ... | ... | @@ -51,7 +51,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void { |
| 51 | 51 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; |
| 52 | 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 | 55 | stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; |
| 56 | 56 | return; |
| 57 | 57 | }; |
| ... | ... | @@ -64,7 +64,7 @@ pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void { |
| 64 | 64 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; |
| 65 | 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 | 68 | stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; |
| 69 | 69 | return; |
| 70 | 70 | }; |
| ... | ... | @@ -592,8 +592,8 @@ fn getString(st: &ElfStackTrace, offset: u64) ![]u8 { |
| 592 | 592 | } |
| 593 | 593 | |
| 594 | 594 | fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 { |
| 595 | const buf = try global_allocator.alloc(u8, size); | |
| 596 | errdefer global_allocator.free(buf); | |
| 595 | const buf = try allocator.alloc(u8, size); | |
| 596 | errdefer allocator.free(buf); | |
| 597 | 597 | if ((try in_stream.read(buf)) < size) return error.EndOfFile; |
| 598 | 598 | return buf; |
| 599 | 599 | } |
| ... | ... | @@ -1126,6 +1126,21 @@ fn readILeb128(in_stream: var) !i64 { |
| 1126 | 1126 | } |
| 1127 | 1127 | } |
| 1128 | 1128 | |
| 1129 | /// This should only be used in temporary test programs. | |
| 1129 | 1130 | pub const global_allocator = &global_fixed_allocator.allocator; |
| 1130 | 1131 | var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]); |
| 1131 | 1132 | var global_allocator_mem: [100 * 1024]u8 = undefined; |
| 1133 | ||
| 1134 | ||
| 1135 | // TODO make thread safe | |
| 1136 | var debug_info_allocator: ?&mem.Allocator = null; | |
| 1137 | var debug_info_direct_allocator: std.heap.DirectAllocator = undefined; | |
| 1138 | var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined; | |
| 1139 | fn 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 | 7 | const debug = std.debug; |
| 8 | 8 | const InStream = std.stream.InStream; |
| 9 | 9 | |
| 10 | pub const AT_NULL = 0; | |
| 11 | pub const AT_IGNORE = 1; | |
| 12 | pub const AT_EXECFD = 2; | |
| 13 | pub const AT_PHDR = 3; | |
| 14 | pub const AT_PHENT = 4; | |
| 15 | pub const AT_PHNUM = 5; | |
| 16 | pub const AT_PAGESZ = 6; | |
| 17 | pub const AT_BASE = 7; | |
| 18 | pub const AT_FLAGS = 8; | |
| 19 | pub const AT_ENTRY = 9; | |
| 20 | pub const AT_NOTELF = 10; | |
| 21 | pub const AT_UID = 11; | |
| 22 | pub const AT_EUID = 12; | |
| 23 | pub const AT_GID = 13; | |
| 24 | pub const AT_EGID = 14; | |
| 25 | pub const AT_CLKTCK = 17; | |
| 26 | pub const AT_PLATFORM = 15; | |
| 27 | pub const AT_HWCAP = 16; | |
| 28 | pub const AT_FPUCW = 18; | |
| 29 | pub const AT_DCACHEBSIZE = 19; | |
| 30 | pub const AT_ICACHEBSIZE = 20; | |
| 31 | pub const AT_UCACHEBSIZE = 21; | |
| 32 | pub const AT_IGNOREPPC = 22; | |
| 33 | pub const AT_SECURE = 23; | |
| 34 | pub const AT_BASE_PLATFORM = 24; | |
| 35 | pub const AT_RANDOM = 25; | |
| 36 | pub const AT_HWCAP2 = 26; | |
| 37 | pub const AT_EXECFN = 31; | |
| 38 | pub const AT_SYSINFO = 32; | |
| 39 | pub const AT_SYSINFO_EHDR = 33; | |
| 40 | pub const AT_L1I_CACHESHAPE = 34; | |
| 41 | pub const AT_L1D_CACHESHAPE = 35; | |
| 42 | pub const AT_L2_CACHESHAPE = 36; | |
| 43 | pub const AT_L3_CACHESHAPE = 37; | |
| 44 | pub const AT_L1I_CACHESIZE = 40; | |
| 45 | pub const AT_L1I_CACHEGEOMETRY = 41; | |
| 46 | pub const AT_L1D_CACHESIZE = 42; | |
| 47 | pub const AT_L1D_CACHEGEOMETRY = 43; | |
| 48 | pub const AT_L2_CACHESIZE = 44; | |
| 49 | pub const AT_L2_CACHEGEOMETRY = 45; | |
| 50 | pub const AT_L3_CACHESIZE = 46; | |
| 51 | pub const AT_L3_CACHEGEOMETRY = 47; | |
| 52 | ||
| 53 | pub const DT_NULL = 0; | |
| 54 | pub const DT_NEEDED = 1; | |
| 55 | pub const DT_PLTRELSZ = 2; | |
| 56 | pub const DT_PLTGOT = 3; | |
| 57 | pub const DT_HASH = 4; | |
| 58 | pub const DT_STRTAB = 5; | |
| 59 | pub const DT_SYMTAB = 6; | |
| 60 | pub const DT_RELA = 7; | |
| 61 | pub const DT_RELASZ = 8; | |
| 62 | pub const DT_RELAENT = 9; | |
| 63 | pub const DT_STRSZ = 10; | |
| 64 | pub const DT_SYMENT = 11; | |
| 65 | pub const DT_INIT = 12; | |
| 66 | pub const DT_FINI = 13; | |
| 67 | pub const DT_SONAME = 14; | |
| 68 | pub const DT_RPATH = 15; | |
| 69 | pub const DT_SYMBOLIC = 16; | |
| 70 | pub const DT_REL = 17; | |
| 71 | pub const DT_RELSZ = 18; | |
| 72 | pub const DT_RELENT = 19; | |
| 73 | pub const DT_PLTREL = 20; | |
| 74 | pub const DT_DEBUG = 21; | |
| 75 | pub const DT_TEXTREL = 22; | |
| 76 | pub const DT_JMPREL = 23; | |
| 77 | pub const DT_BIND_NOW = 24; | |
| 78 | pub const DT_INIT_ARRAY = 25; | |
| 79 | pub const DT_FINI_ARRAY = 26; | |
| 80 | pub const DT_INIT_ARRAYSZ = 27; | |
| 81 | pub const DT_FINI_ARRAYSZ = 28; | |
| 82 | pub const DT_RUNPATH = 29; | |
| 83 | pub const DT_FLAGS = 30; | |
| 84 | pub const DT_ENCODING = 32; | |
| 85 | pub const DT_PREINIT_ARRAY = 32; | |
| 86 | pub const DT_PREINIT_ARRAYSZ = 33; | |
| 87 | pub const DT_SYMTAB_SHNDX = 34; | |
| 88 | pub const DT_NUM = 35; | |
| 89 | pub const DT_LOOS = 0x6000000d; | |
| 90 | pub const DT_HIOS = 0x6ffff000; | |
| 91 | pub const DT_LOPROC = 0x70000000; | |
| 92 | pub const DT_HIPROC = 0x7fffffff; | |
| 93 | pub const DT_PROCNUM = DT_MIPS_NUM; | |
| 94 | ||
| 95 | pub const DT_VALRNGLO = 0x6ffffd00; | |
| 96 | pub const DT_GNU_PRELINKED = 0x6ffffdf5; | |
| 97 | pub const DT_GNU_CONFLICTSZ = 0x6ffffdf6; | |
| 98 | pub const DT_GNU_LIBLISTSZ = 0x6ffffdf7; | |
| 99 | pub const DT_CHECKSUM = 0x6ffffdf8; | |
| 100 | pub const DT_PLTPADSZ = 0x6ffffdf9; | |
| 101 | pub const DT_MOVEENT = 0x6ffffdfa; | |
| 102 | pub const DT_MOVESZ = 0x6ffffdfb; | |
| 103 | pub const DT_FEATURE_1 = 0x6ffffdfc; | |
| 104 | pub const DT_POSFLAG_1 = 0x6ffffdfd; | |
| 105 | ||
| 106 | pub const DT_SYMINSZ = 0x6ffffdfe; | |
| 107 | pub const DT_SYMINENT = 0x6ffffdff; | |
| 108 | pub const DT_VALRNGHI = 0x6ffffdff; | |
| 109 | pub const DT_VALNUM = 12; | |
| 110 | ||
| 111 | pub const DT_ADDRRNGLO = 0x6ffffe00; | |
| 112 | pub const DT_GNU_HASH = 0x6ffffef5; | |
| 113 | pub const DT_TLSDESC_PLT = 0x6ffffef6; | |
| 114 | pub const DT_TLSDESC_GOT = 0x6ffffef7; | |
| 115 | pub const DT_GNU_CONFLICT = 0x6ffffef8; | |
| 116 | pub const DT_GNU_LIBLIST = 0x6ffffef9; | |
| 117 | pub const DT_CONFIG = 0x6ffffefa; | |
| 118 | pub const DT_DEPAUDIT = 0x6ffffefb; | |
| 119 | pub const DT_AUDIT = 0x6ffffefc; | |
| 120 | pub const DT_PLTPAD = 0x6ffffefd; | |
| 121 | pub const DT_MOVETAB = 0x6ffffefe; | |
| 122 | pub const DT_SYMINFO = 0x6ffffeff; | |
| 123 | pub const DT_ADDRRNGHI = 0x6ffffeff; | |
| 124 | pub const DT_ADDRNUM = 11; | |
| 125 | ||
| 126 | ||
| 127 | pub const DT_VERSYM = 0x6ffffff0; | |
| 128 | ||
| 129 | pub const DT_RELACOUNT = 0x6ffffff9; | |
| 130 | pub const DT_RELCOUNT = 0x6ffffffa; | |
| 131 | ||
| 132 | ||
| 133 | pub const DT_FLAGS_1 = 0x6ffffffb; | |
| 134 | pub const DT_VERDEF = 0x6ffffffc; | |
| 135 | ||
| 136 | pub const DT_VERDEFNUM = 0x6ffffffd; | |
| 137 | pub const DT_VERNEED = 0x6ffffffe; | |
| 138 | ||
| 139 | pub const DT_VERNEEDNUM = 0x6fffffff; | |
| 140 | pub const DT_VERSIONTAGNUM = 16; | |
| 141 | ||
| 142 | ||
| 143 | ||
| 144 | pub const DT_AUXILIARY = 0x7ffffffd; | |
| 145 | pub const DT_FILTER = 0x7fffffff; | |
| 146 | pub const DT_EXTRANUM = 3; | |
| 147 | ||
| 148 | ||
| 149 | pub const DT_SPARC_REGISTER = 0x70000001; | |
| 150 | pub const DT_SPARC_NUM = 2; | |
| 151 | ||
| 152 | pub const DT_MIPS_RLD_VERSION = 0x70000001; | |
| 153 | pub const DT_MIPS_TIME_STAMP = 0x70000002; | |
| 154 | pub const DT_MIPS_ICHECKSUM = 0x70000003; | |
| 155 | pub const DT_MIPS_IVERSION = 0x70000004; | |
| 156 | pub const DT_MIPS_FLAGS = 0x70000005; | |
| 157 | pub const DT_MIPS_BASE_ADDRESS = 0x70000006; | |
| 158 | pub const DT_MIPS_MSYM = 0x70000007; | |
| 159 | pub const DT_MIPS_CONFLICT = 0x70000008; | |
| 160 | pub const DT_MIPS_LIBLIST = 0x70000009; | |
| 161 | pub const DT_MIPS_LOCAL_GOTNO = 0x7000000a; | |
| 162 | pub const DT_MIPS_CONFLICTNO = 0x7000000b; | |
| 163 | pub const DT_MIPS_LIBLISTNO = 0x70000010; | |
| 164 | pub const DT_MIPS_SYMTABNO = 0x70000011; | |
| 165 | pub const DT_MIPS_UNREFEXTNO = 0x70000012; | |
| 166 | pub const DT_MIPS_GOTSYM = 0x70000013; | |
| 167 | pub const DT_MIPS_HIPAGENO = 0x70000014; | |
| 168 | pub const DT_MIPS_RLD_MAP = 0x70000016; | |
| 169 | pub const DT_MIPS_DELTA_CLASS = 0x70000017; | |
| 170 | pub const DT_MIPS_DELTA_CLASS_NO = 0x70000018; | |
| 171 | ||
| 172 | pub const DT_MIPS_DELTA_INSTANCE = 0x70000019; | |
| 173 | pub const DT_MIPS_DELTA_INSTANCE_NO = 0x7000001a; | |
| 174 | ||
| 175 | pub const DT_MIPS_DELTA_RELOC = 0x7000001b; | |
| 176 | pub const DT_MIPS_DELTA_RELOC_NO = 0x7000001c; | |
| 177 | ||
| 178 | pub const DT_MIPS_DELTA_SYM = 0x7000001d; | |
| 179 | ||
| 180 | pub const DT_MIPS_DELTA_SYM_NO = 0x7000001e; | |
| 181 | ||
| 182 | pub const DT_MIPS_DELTA_CLASSSYM = 0x70000020; | |
| 183 | ||
| 184 | pub const DT_MIPS_DELTA_CLASSSYM_NO = 0x70000021; | |
| 185 | ||
| 186 | pub const DT_MIPS_CXX_FLAGS = 0x70000022; | |
| 187 | pub const DT_MIPS_PIXIE_INIT = 0x70000023; | |
| 188 | pub const DT_MIPS_SYMBOL_LIB = 0x70000024; | |
| 189 | pub const DT_MIPS_LOCALPAGE_GOTIDX = 0x70000025; | |
| 190 | pub const DT_MIPS_LOCAL_GOTIDX = 0x70000026; | |
| 191 | pub const DT_MIPS_HIDDEN_GOTIDX = 0x70000027; | |
| 192 | pub const DT_MIPS_PROTECTED_GOTIDX = 0x70000028; | |
| 193 | pub const DT_MIPS_OPTIONS = 0x70000029; | |
| 194 | pub const DT_MIPS_INTERFACE = 0x7000002a; | |
| 195 | pub const DT_MIPS_DYNSTR_ALIGN = 0x7000002b; | |
| 196 | pub const DT_MIPS_INTERFACE_SIZE = 0x7000002c; | |
| 197 | pub const DT_MIPS_RLD_TEXT_RESOLVE_ADDR = 0x7000002d; | |
| 198 | ||
| 199 | pub const DT_MIPS_PERF_SUFFIX = 0x7000002e; | |
| 200 | ||
| 201 | pub const DT_MIPS_COMPACT_SIZE = 0x7000002f; | |
| 202 | pub const DT_MIPS_GP_VALUE = 0x70000030; | |
| 203 | pub const DT_MIPS_AUX_DYNAMIC = 0x70000031; | |
| 204 | ||
| 205 | pub const DT_MIPS_PLTGOT = 0x70000032; | |
| 206 | ||
| 207 | pub const DT_MIPS_RWPLT = 0x70000034; | |
| 208 | pub const DT_MIPS_RLD_MAP_REL = 0x70000035; | |
| 209 | pub const DT_MIPS_NUM = 0x36; | |
| 210 | ||
| 211 | pub const DT_ALPHA_PLTRO = (DT_LOPROC + 0); | |
| 212 | pub const DT_ALPHA_NUM = 1; | |
| 213 | ||
| 214 | pub const DT_PPC_GOT = (DT_LOPROC + 0); | |
| 215 | pub const DT_PPC_OPT = (DT_LOPROC + 1); | |
| 216 | pub const DT_PPC_NUM = 2; | |
| 217 | ||
| 218 | pub const DT_PPC64_GLINK = (DT_LOPROC + 0); | |
| 219 | pub const DT_PPC64_OPD = (DT_LOPROC + 1); | |
| 220 | pub const DT_PPC64_OPDSZ = (DT_LOPROC + 2); | |
| 221 | pub const DT_PPC64_OPT = (DT_LOPROC + 3); | |
| 222 | pub const DT_PPC64_NUM = 4; | |
| 223 | ||
| 224 | pub const DT_IA_64_PLT_RESERVE = (DT_LOPROC + 0); | |
| 225 | pub const DT_IA_64_NUM = 1; | |
| 226 | ||
| 227 | pub const DT_NIOS2_GP = 0x70000002; | |
| 228 | ||
| 229 | pub const PT_NULL = 0; | |
| 230 | pub const PT_LOAD = 1; | |
| 231 | pub const PT_DYNAMIC = 2; | |
| 232 | pub const PT_INTERP = 3; | |
| 233 | pub const PT_NOTE = 4; | |
| 234 | pub const PT_SHLIB = 5; | |
| 235 | pub const PT_PHDR = 6; | |
| 236 | pub const PT_TLS = 7; | |
| 237 | pub const PT_NUM = 8; | |
| 238 | pub const PT_LOOS = 0x60000000; | |
| 239 | pub const PT_GNU_EH_FRAME = 0x6474e550; | |
| 240 | pub const PT_GNU_STACK = 0x6474e551; | |
| 241 | pub const PT_GNU_RELRO = 0x6474e552; | |
| 242 | pub const PT_LOSUNW = 0x6ffffffa; | |
| 243 | pub const PT_SUNWBSS = 0x6ffffffa; | |
| 244 | pub const PT_SUNWSTACK = 0x6ffffffb; | |
| 245 | pub const PT_HISUNW = 0x6fffffff; | |
| 246 | pub const PT_HIOS = 0x6fffffff; | |
| 247 | pub const PT_LOPROC = 0x70000000; | |
| 248 | pub const PT_HIPROC = 0x7fffffff; | |
| 249 | ||
| 10 | 250 | pub const SHT_NULL = 0; |
| 11 | 251 | pub const SHT_PROGBITS = 1; |
| 12 | 252 | pub const SHT_SYMTAB = 2; |
| ... | ... | @@ -31,6 +271,45 @@ pub const SHT_HIPROC = 0x7fffffff; |
| 31 | 271 | pub const SHT_LOUSER = 0x80000000; |
| 32 | 272 | pub const SHT_HIUSER = 0xffffffff; |
| 33 | 273 | |
| 274 | pub const STB_LOCAL = 0; | |
| 275 | pub const STB_GLOBAL = 1; | |
| 276 | pub const STB_WEAK = 2; | |
| 277 | pub const STB_NUM = 3; | |
| 278 | pub const STB_LOOS = 10; | |
| 279 | pub const STB_GNU_UNIQUE = 10; | |
| 280 | pub const STB_HIOS = 12; | |
| 281 | pub const STB_LOPROC = 13; | |
| 282 | pub const STB_HIPROC = 15; | |
| 283 | ||
| 284 | pub const STB_MIPS_SPLIT_COMMON = 13; | |
| 285 | ||
| 286 | pub const STT_NOTYPE = 0; | |
| 287 | pub const STT_OBJECT = 1; | |
| 288 | pub const STT_FUNC = 2; | |
| 289 | pub const STT_SECTION = 3; | |
| 290 | pub const STT_FILE = 4; | |
| 291 | pub const STT_COMMON = 5; | |
| 292 | pub const STT_TLS = 6; | |
| 293 | pub const STT_NUM = 7; | |
| 294 | pub const STT_LOOS = 10; | |
| 295 | pub const STT_GNU_IFUNC = 10; | |
| 296 | pub const STT_HIOS = 12; | |
| 297 | pub const STT_LOPROC = 13; | |
| 298 | pub const STT_HIPROC = 15; | |
| 299 | ||
| 300 | pub const STT_SPARC_REGISTER = 13; | |
| 301 | ||
| 302 | pub const STT_PARISC_MILLICODE = 13; | |
| 303 | ||
| 304 | pub const STT_HP_OPAQUE = (STT_LOOS + 0x1); | |
| 305 | pub const STT_HP_STUB = (STT_LOOS + 0x2); | |
| 306 | ||
| 307 | pub const STT_ARM_TFUNC = STT_LOPROC; | |
| 308 | pub const STT_ARM_16BIT = STT_HIPROC; | |
| 309 | ||
| 310 | pub const VER_FLG_BASE = 0x1; | |
| 311 | pub const VER_FLG_WEAK = 0x2; | |
| 312 | ||
| 34 | 313 | pub const FileType = enum { |
| 35 | 314 | Relocatable, |
| 36 | 315 | Executable, |
| ... | ... | @@ -266,3 +545,335 @@ pub const Elf = struct { |
| 266 | 545 | try elf.in_file.seekTo(elf_section.offset); |
| 267 | 546 | } |
| 268 | 547 | }; |
| 548 | ||
| 549 | pub const EI_NIDENT = 16; | |
| 550 | pub const Elf32_Half = u16; | |
| 551 | pub const Elf64_Half = u16; | |
| 552 | pub const Elf32_Word = u32; | |
| 553 | pub const Elf32_Sword = i32; | |
| 554 | pub const Elf64_Word = u32; | |
| 555 | pub const Elf64_Sword = i32; | |
| 556 | pub const Elf32_Xword = u64; | |
| 557 | pub const Elf32_Sxword = i64; | |
| 558 | pub const Elf64_Xword = u64; | |
| 559 | pub const Elf64_Sxword = i64; | |
| 560 | pub const Elf32_Addr = u32; | |
| 561 | pub const Elf64_Addr = u64; | |
| 562 | pub const Elf32_Off = u32; | |
| 563 | pub const Elf64_Off = u64; | |
| 564 | pub const Elf32_Section = u16; | |
| 565 | pub const Elf64_Section = u16; | |
| 566 | pub const Elf32_Versym = Elf32_Half; | |
| 567 | pub const Elf64_Versym = Elf64_Half; | |
| 568 | pub 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 | }; | |
| 584 | pub 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 | }; | |
| 600 | pub 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 | }; | |
| 612 | pub 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 | }; | |
| 624 | pub const Elf32_Chdr = extern struct { | |
| 625 | ch_type: Elf32_Word, | |
| 626 | ch_size: Elf32_Word, | |
| 627 | ch_addralign: Elf32_Word, | |
| 628 | }; | |
| 629 | pub 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 | }; | |
| 635 | pub 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 | }; | |
| 643 | pub 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 | }; | |
| 651 | pub const Elf32_Syminfo = extern struct { | |
| 652 | si_boundto: Elf32_Half, | |
| 653 | si_flags: Elf32_Half, | |
| 654 | }; | |
| 655 | pub const Elf64_Syminfo = extern struct { | |
| 656 | si_boundto: Elf64_Half, | |
| 657 | si_flags: Elf64_Half, | |
| 658 | }; | |
| 659 | pub const Elf32_Rel = extern struct { | |
| 660 | r_offset: Elf32_Addr, | |
| 661 | r_info: Elf32_Word, | |
| 662 | }; | |
| 663 | pub const Elf64_Rel = extern struct { | |
| 664 | r_offset: Elf64_Addr, | |
| 665 | r_info: Elf64_Xword, | |
| 666 | }; | |
| 667 | pub const Elf32_Rela = extern struct { | |
| 668 | r_offset: Elf32_Addr, | |
| 669 | r_info: Elf32_Word, | |
| 670 | r_addend: Elf32_Sword, | |
| 671 | }; | |
| 672 | pub const Elf64_Rela = extern struct { | |
| 673 | r_offset: Elf64_Addr, | |
| 674 | r_info: Elf64_Xword, | |
| 675 | r_addend: Elf64_Sxword, | |
| 676 | }; | |
| 677 | pub 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 | }; | |
| 687 | pub 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 | }; | |
| 697 | pub 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 | }; | |
| 704 | pub 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 | }; | |
| 711 | pub 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 | }; | |
| 720 | pub 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 | }; | |
| 729 | pub const Elf32_Verdaux = extern struct { | |
| 730 | vda_name: Elf32_Word, | |
| 731 | vda_next: Elf32_Word, | |
| 732 | }; | |
| 733 | pub const Elf64_Verdaux = extern struct { | |
| 734 | vda_name: Elf64_Word, | |
| 735 | vda_next: Elf64_Word, | |
| 736 | }; | |
| 737 | pub 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 | }; | |
| 744 | pub 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 | }; | |
| 751 | pub 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 | }; | |
| 758 | pub 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 | }; | |
| 765 | pub const Elf32_auxv_t = extern struct { | |
| 766 | a_type: u32, | |
| 767 | a_un: extern union { | |
| 768 | a_val: u32, | |
| 769 | }, | |
| 770 | }; | |
| 771 | pub const Elf64_auxv_t = extern struct { | |
| 772 | a_type: u64, | |
| 773 | a_un: extern union { | |
| 774 | a_val: u64, | |
| 775 | }, | |
| 776 | }; | |
| 777 | pub const Elf32_Nhdr = extern struct { | |
| 778 | n_namesz: Elf32_Word, | |
| 779 | n_descsz: Elf32_Word, | |
| 780 | n_type: Elf32_Word, | |
| 781 | }; | |
| 782 | pub const Elf64_Nhdr = extern struct { | |
| 783 | n_namesz: Elf64_Word, | |
| 784 | n_descsz: Elf64_Word, | |
| 785 | n_type: Elf64_Word, | |
| 786 | }; | |
| 787 | pub 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 | }; | |
| 794 | pub 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 | }; | |
| 801 | pub 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 | }; | |
| 811 | pub const Elf32_RegInfo = extern struct { | |
| 812 | ri_gprmask: Elf32_Word, | |
| 813 | ri_cprmask: [4]Elf32_Word, | |
| 814 | ri_gp_value: Elf32_Sword, | |
| 815 | }; | |
| 816 | pub const Elf_Options = extern struct { | |
| 817 | kind: u8, | |
| 818 | size: u8, | |
| 819 | @"section": Elf32_Section, | |
| 820 | info: Elf32_Word, | |
| 821 | }; | |
| 822 | pub const Elf_Options_Hw = extern struct { | |
| 823 | hwp_flags1: Elf32_Word, | |
| 824 | hwp_flags2: Elf32_Word, | |
| 825 | }; | |
| 826 | pub 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 | }; | |
| 833 | pub 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 | }; | |
| 840 | pub const Elf32_Conflict = Elf32_Addr; | |
| 841 | pub 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 | ||
| 855 | pub const Ehdr = switch(@sizeOf(usize)) { | |
| 856 | 4 => Elf32_Ehdr, | |
| 857 | 8 => Elf64_Ehdr, | |
| 858 | else => @compileError("expected pointer size of 32 or 64"), | |
| 859 | }; | |
| 860 | pub const Phdr = switch(@sizeOf(usize)) { | |
| 861 | 4 => Elf32_Phdr, | |
| 862 | 8 => Elf64_Phdr, | |
| 863 | else => @compileError("expected pointer size of 32 or 64"), | |
| 864 | }; | |
| 865 | pub const Sym = switch(@sizeOf(usize)) { | |
| 866 | 4 => Elf32_Sym, | |
| 867 | 8 => Elf64_Sym, | |
| 868 | else => @compileError("expected pointer size of 32 or 64"), | |
| 869 | }; | |
| 870 | pub const Verdef = switch(@sizeOf(usize)) { | |
| 871 | 4 => Elf32_Verdef, | |
| 872 | 8 => Elf64_Verdef, | |
| 873 | else => @compileError("expected pointer size of 32 or 64"), | |
| 874 | }; | |
| 875 | pub 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 @@ |
| 1 | const mem = @import("mem.zig"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | ||
| 4 | pub fn swapIfLe(comptime T: type, x: T) T { | |
| 5 | return swapIf(builtin.Endian.Little, T, x); | |
| 6 | } | |
| 7 | ||
| 8 | pub fn swapIfBe(comptime T: type, x: T) T { | |
| 9 | return swapIf(builtin.Endian.Big, T, x); | |
| 10 | } | |
| 11 | ||
| 12 | pub fn swapIf(endian: builtin.Endian, comptime T: type, x: T) T { | |
| 13 | return if (builtin.endian == endian) swap(T, x) else x; | |
| 14 | } | |
| 15 | ||
| 16 | pub 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 | ||
| 22 | test "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 @@ |
| 1 | const std = @import("index.zig"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const assert = std.debug.assert; | |
| 4 | const event = this; | |
| 5 | const mem = std.mem; | |
| 6 | const posix = std.os.posix; | |
| 7 | ||
| 8 | pub 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 | ||
| 106 | pub 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 | ||
| 159 | pub 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 | ||
| 172 | test "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 | ||
| 224 | async 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 | 12 | exp: i32, |
| 13 | 13 | }; |
| 14 | 14 | |
| 15 | pub 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. | |
| 24 | pub 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 | 81 | /// Corrected Errol3 double to ASCII conversion. |
| 16 | 82 | pub fn errol3(value: f64, buffer: []u8) FloatDecimal { |
| 17 | 83 | const bits = @bitCast(u64, value); |
| 18 | 84 | const i = tableLowerBound(bits); |
| 19 | 85 | if (i < enum3.len and enum3[i] == bits) { |
| 20 | 86 | const data = enum3_data[i]; |
| 21 | const digits = buffer[0..data.str.len]; | |
| 87 | const digits = buffer[1..data.str.len + 1]; | |
| 22 | 88 | mem.copy(u8, digits, data.str); |
| 23 | 89 | return FloatDecimal { |
| 24 | 90 | .digits = digits, |
| ... | ... | @@ -98,7 +164,11 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { |
| 98 | 164 | } |
| 99 | 165 | |
| 100 | 166 | // 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 | 172 | while (true) { |
| 103 | 173 | var hdig = u8(math.floor(high.val)); |
| 104 | 174 | if ((high.val == f64(hdig)) and (high.off < 0)) |
| ... | ... | @@ -128,7 +198,7 @@ fn errol3u(val: f64, buffer: []u8) FloatDecimal { |
| 128 | 198 | buf_index += 1; |
| 129 | 199 | |
| 130 | 200 | return FloatDecimal { |
| 131 | .digits = buffer[0..buf_index], | |
| 201 | .digits = buffer[1..buf_index], | |
| 132 | 202 | .exp = exp, |
| 133 | 203 | }; |
| 134 | 204 | } |
| ... | ... | @@ -189,6 +259,9 @@ fn gethi(in: f64) f64 { |
| 189 | 259 | /// Normalize the number by factoring in the error. |
| 190 | 260 | /// @hp: The float pair. |
| 191 | 261 | fn 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 | 265 | const val = hp.val; |
| 193 | 266 | |
| 194 | 267 | hp.val += hp.off; |
std/fmt/index.zig+448-122| ... | ... | @@ -4,29 +4,31 @@ const debug = std.debug; |
| 4 | 4 | const assert = debug.assert; |
| 5 | 5 | const mem = std.mem; |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | const errol3 = @import("errol/index.zig").errol3; | |
| 7 | const errol = @import("errol/index.zig"); | |
| 8 | 8 | |
| 9 | 9 | const max_int_digits = 65; |
| 10 | 10 | |
| 11 | const 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 | 11 | /// Renders fmt string with args, calling output with slices of bytes. |
| 25 | 12 | /// If `output` returns an error, the error is returned from `format` and |
| 26 | 13 | /// `output` is not called again. |
| 27 | 14 | pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void, |
| 28 | 15 | 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 | 32 | comptime var start_index = 0; |
| 31 | 33 | comptime var state = State.Start; |
| 32 | 34 | comptime var next_arg = 0; |
| ... | ... | @@ -86,7 +88,11 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), |
| 86 | 88 | }, |
| 87 | 89 | 's' => { |
| 88 | 90 | state = State.Buf; |
| 89 | },'.' => { | |
| 91 | }, | |
| 92 | 'e' => { | |
| 93 | state = State.FloatScientific; | |
| 94 | }, | |
| 95 | '.' => { | |
| 90 | 96 | state = State.Float; |
| 91 | 97 | }, |
| 92 | 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 | 138 | '0' ... '9' => {}, |
| 133 | 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 | 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 | 168 | next_arg += 1; |
| 139 | 169 | state = State.Start; |
| 140 | 170 | start_index = i + 1; |
| ... | ... | @@ -198,7 +228,7 @@ pub fn formatValue(value: var, context: var, comptime Errors: type, output: fn(@ |
| 198 | 228 | return formatInt(value, 10, false, 0, context, Errors, output); |
| 199 | 229 | }, |
| 200 | 230 | builtin.TypeId.Float => { |
| 201 | return formatFloat(value, context, Errors, output); | |
| 231 | return formatFloatScientific(value, null, context, Errors, output); | |
| 202 | 232 | }, |
| 203 | 233 | builtin.TypeId.Void => { |
| 204 | 234 | return output(context, "void"); |
| ... | ... | @@ -256,81 +286,237 @@ pub fn formatBuf(buf: []const u8, width: usize, |
| 256 | 286 | } |
| 257 | 287 | } |
| 258 | 288 | |
| 259 | pub 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. | |
| 292 | pub fn formatFloatScientific(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { | |
| 260 | 293 | var x = f64(value); |
| 261 | 294 | |
| 262 | 295 | // Errol doesn't handle these special cases. |
| 263 | if (math.isNan(x)) { | |
| 264 | return output(context, "NaN"); | |
| 265 | } | |
| 266 | 296 | if (math.signbit(x)) { |
| 267 | 297 | try output(context, "-"); |
| 268 | 298 | x = -x; |
| 269 | 299 | } |
| 300 | ||
| 301 | if (math.isNan(x)) { | |
| 302 | return output(context, "nan"); | |
| 303 | } | |
| 270 | 304 | if (math.isPositiveInf(x)) { |
| 271 | return output(context, "Infinity"); | |
| 305 | return output(context, "inf"); | |
| 272 | 306 | } |
| 273 | 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 | } |
| 276 | 325 | |
| 277 | 326 | var buffer: [32]u8 = undefined; |
| 278 | const float_decimal = errol3(x, buffer[0..]); | |
| 279 | try output(context, float_decimal.digits[0..1]); | |
| 280 | try output(context, "."); | |
| 281 | if (float_decimal.digits.len > 1) { | |
| 282 | const num_digits = if (@typeOf(value) == f32) | |
| 283 | math.min(usize(9), float_decimal.digits.len) | |
| 284 | else | |
| 285 | float_decimal.digits.len; | |
| 286 | try output(context, float_decimal.digits[1 .. num_digits]); | |
| 327 | var float_decimal = errol.errol3(x, buffer[0..]); | |
| 328 | ||
| 329 | if (maybe_precision) |precision| { | |
| 330 | errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); | |
| 331 | ||
| 332 | try output(context, float_decimal.digits[0..1]); | |
| 333 | ||
| 334 | // {e0} case prints no `.` | |
| 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 | 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 | } |
| 290 | 363 | |
| 291 | if (float_decimal.exp != 1) { | |
| 292 | try output(context, "e"); | |
| 293 | try formatInt(float_decimal.exp - 1, 10, false, 0, context, Errors, output); | |
| 364 | try output(context, "e"); | |
| 365 | const exp = float_decimal.exp - 1; | |
| 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 | } |
| 296 | 381 | |
| 297 | pub 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). | |
| 384 | pub fn formatFloatDecimal(value: var, maybe_precision: ?usize, context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8)Errors!void) Errors!void { | |
| 298 | 385 | var x = f64(value); |
| 299 | 386 | |
| 300 | 387 | // Errol doesn't handle these special cases. |
| 301 | if (math.isNan(x)) { | |
| 302 | return output(context, "NaN"); | |
| 303 | } | |
| 304 | 388 | if (math.signbit(x)) { |
| 305 | 389 | try output(context, "-"); |
| 306 | 390 | x = -x; |
| 307 | 391 | } |
| 392 | ||
| 393 | if (math.isNan(x)) { | |
| 394 | return output(context, "nan"); | |
| 395 | } | |
| 308 | 396 | if (math.isPositiveInf(x)) { |
| 309 | return output(context, "Infinity"); | |
| 397 | return output(context, "inf"); | |
| 310 | 398 | } |
| 311 | 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 | } |
| 314 | 418 | |
| 419 | // non-special case, use errol3 | |
| 315 | 420 | var buffer: [32]u8 = undefined; |
| 316 | const float_decimal = errol3(x, buffer[0..]); | |
| 317 | ||
| 318 | const num_left_digits = if (float_decimal.exp > 0) usize(float_decimal.exp) else 1; | |
| 319 | ||
| 320 | try output(context, float_decimal.digits[0 .. num_left_digits]); | |
| 321 | try output(context, "."); | |
| 322 | if (float_decimal.digits.len > 1) { | |
| 323 | const num_valid_digtis = if (@typeOf(value) == f32) math.min(usize(7), float_decimal.digits.len) | |
| 324 | else | |
| 325 | float_decimal.digits.len; | |
| 326 | ||
| 327 | const num_right_digits = if (precision != 0) | |
| 328 | math.min(precision, (num_valid_digtis-num_left_digits)) | |
| 329 | else | |
| 330 | num_valid_digtis - num_left_digits; | |
| 331 | try output(context, float_decimal.digits[num_left_digits .. (num_left_digits + num_right_digits)]); | |
| 421 | var float_decimal = errol.errol3(x, buffer[0..]); | |
| 422 | ||
| 423 | if (maybe_precision) |precision| { | |
| 424 | errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); | |
| 425 | ||
| 426 | // exp < 0 means the leading is always 0 as errol result is normalized. | |
| 427 | var num_digits_whole = if (float_decimal.exp > 0) usize(float_decimal.exp) else 0; | |
| 428 | ||
| 429 | // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. | |
| 430 | var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); | |
| 431 | ||
| 432 | if (num_digits_whole > 0) { | |
| 433 | // We may have to zero pad, for instance 1e4 requires zero padding. | |
| 434 | try output(context, float_decimal.digits[0 .. num_digits_whole_no_pad]); | |
| 435 | ||
| 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 | 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 | } |
| 336 | 522 | |
| ... | ... | @@ -465,7 +651,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned |
| 465 | 651 | return x; |
| 466 | 652 | } |
| 467 | 653 | |
| 468 | fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { | |
| 654 | pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { | |
| 469 | 655 | const value = switch (c) { |
| 470 | 656 | '0' ... '9' => c - '0', |
| 471 | 657 | 'A' ... 'Z' => c - 'A' + 10, |
| ... | ... | @@ -593,70 +779,210 @@ test "fmt.format" { |
| 593 | 779 | const result = try bufPrint(buf1[0..], "pointer: {}\n", &value); |
| 594 | 780 | assert(mem.startsWith(u8, result, "pointer: Struct@")); |
| 595 | 781 | } |
| 596 | ||
| 597 | // TODO get these tests passing in release modes | |
| 598 | // https://github.com/zig-lang/zig/issues/564 | |
| 599 | if (builtin.mode == builtin.Mode.Debug) { | |
| 600 | { | |
| 601 | var buf1: [32]u8 = undefined; | |
| 602 | const value: f32 = 12.34; | |
| 603 | const result = try bufPrint(buf1[0..], "f32: {}\n", value); | |
| 604 | assert(mem.eql(u8, result, "f32: 1.23400001e1\n")); | |
| 605 | } | |
| 606 | { | |
| 607 | var buf1: [32]u8 = undefined; | |
| 608 | const value: f64 = -12.34e10; | |
| 609 | const result = try bufPrint(buf1[0..], "f64: {}\n", value); | |
| 610 | assert(mem.eql(u8, result, "f64: -1.234e11\n")); | |
| 611 | } | |
| 612 | { | |
| 613 | var buf1: [32]u8 = undefined; | |
| 614 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); | |
| 615 | assert(mem.eql(u8, result, "f64: NaN\n")); | |
| 616 | } | |
| 617 | { | |
| 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 | { | |
| 782 | { | |
| 783 | var buf1: [32]u8 = undefined; | |
| 784 | const value: f32 = 1.34; | |
| 785 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); | |
| 786 | assert(mem.eql(u8, result, "f32: 1.34000003e+00\n")); | |
| 787 | } | |
| 788 | { | |
| 789 | var buf1: [32]u8 = undefined; | |
| 790 | const value: f32 = 12.34; | |
| 791 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); | |
| 792 | assert(mem.eql(u8, result, "f32: 1.23400001e+01\n")); | |
| 793 | } | |
| 794 | { | |
| 795 | var buf1: [32]u8 = undefined; | |
| 796 | const value: f64 = -12.34e10; | |
| 797 | const result = try bufPrint(buf1[0..], "f64: {e}\n", value); | |
| 798 | assert(mem.eql(u8, result, "f64: -1.234e+11\n")); | |
| 799 | } | |
| 800 | { | |
| 801 | // This fails on release due to a minor rounding difference. | |
| 802 | // --release-fast outputs 9.999960000000001e-40 vs. the expected. | |
| 803 | if (builtin.mode == builtin.Mode.Debug) { | |
| 654 | 804 | var buf1: [32]u8 = undefined; |
| 655 | const value: f64 = 91.12345678901235; | |
| 656 | const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); | |
| 657 | assert(mem.eql(u8, result, "f64: 91.1234567890\n")); | |
| 805 | const value: f64 = 9.999960e-40; | |
| 806 | const result = try bufPrint(buf1[0..], "f64: {e}\n", value); | |
| 807 | assert(mem.eql(u8, result, "f64: 9.99996e-40\n")); | |
| 658 | 808 | } |
| 659 | ||
| 809 | } | |
| 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 | } |
| 662 | 988 |
std/hash_map.zig+62-6| ... | ... | @@ -54,6 +54,14 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 54 | 54 | } |
| 55 | 55 | 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 | }; |
| 58 | 66 | |
| 59 | 67 | pub fn init(allocator: &Allocator) Self { |
| ... | ... | @@ -66,7 +74,7 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 66 | 74 | }; |
| 67 | 75 | } |
| 68 | 76 | |
| 69 | pub fn deinit(hm: &Self) void { | |
| 77 | pub fn deinit(hm: &const Self) void { | |
| 70 | 78 | hm.allocator.free(hm.entries); |
| 71 | 79 | } |
| 72 | 80 | |
| ... | ... | @@ -79,6 +87,10 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 79 | 87 | hm.incrementModificationCount(); |
| 80 | 88 | } |
| 81 | 89 | |
| 90 | pub fn count(hm: &const Self) usize { | |
| 91 | return hm.size; | |
| 92 | } | |
| 93 | ||
| 82 | 94 | /// Returns the value that was already there. |
| 83 | 95 | pub fn put(hm: &Self, key: K, value: &const V) !?V { |
| 84 | 96 | if (hm.entries.len == 0) { |
| ... | ... | @@ -102,18 +114,19 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 102 | 114 | return hm.internalPut(key, value); |
| 103 | 115 | } |
| 104 | 116 | |
| 105 | pub fn get(hm: &Self, key: K) ?&Entry { | |
| 117 | pub fn get(hm: &const Self, key: K) ?&Entry { | |
| 106 | 118 | if (hm.entries.len == 0) { |
| 107 | 119 | return null; |
| 108 | 120 | } |
| 109 | 121 | return hm.internalGet(key); |
| 110 | 122 | } |
| 111 | 123 | |
| 112 | pub fn contains(hm: &Self, key: K) bool { | |
| 124 | pub fn contains(hm: &const Self, key: K) bool { | |
| 113 | 125 | return hm.get(key) != null; |
| 114 | 126 | } |
| 115 | 127 | |
| 116 | 128 | pub fn remove(hm: &Self, key: K) ?&Entry { |
| 129 | if (hm.entries.len == 0) return null; | |
| 117 | 130 | hm.incrementModificationCount(); |
| 118 | 131 | const start_index = hm.keyToIndex(key); |
| 119 | 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 | 230 | unreachable; // put into a full map |
| 218 | 231 | } |
| 219 | 232 | |
| 220 | fn internalGet(hm: &Self, key: K) ?&Entry { | |
| 233 | fn internalGet(hm: &const Self, key: K) ?&Entry { | |
| 221 | 234 | const start_index = hm.keyToIndex(key); |
| 222 | 235 | {var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) { |
| 223 | 236 | const index = (start_index + roll_over) % hm.entries.len; |
| ... | ... | @@ -229,14 +242,17 @@ pub fn HashMap(comptime K: type, comptime V: type, |
| 229 | 242 | return null; |
| 230 | 243 | } |
| 231 | 244 | |
| 232 | fn keyToIndex(hm: &Self, key: K) usize { | |
| 245 | fn keyToIndex(hm: &const Self, key: K) usize { | |
| 233 | 246 | return usize(hash(key)) % hm.entries.len; |
| 234 | 247 | } |
| 235 | 248 | }; |
| 236 | 249 | } |
| 237 | 250 | |
| 238 | 251 | test "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 | 256 | defer map.deinit(); |
| 241 | 257 | |
| 242 | 258 | assert((map.put(1, 11) catch unreachable) == null); |
| ... | ... | @@ -248,12 +264,52 @@ test "basic hash map usage" { |
| 248 | 264 | assert(??(map.put(5, 66) catch unreachable) == 55); |
| 249 | 265 | assert(??(map.put(5, 55) catch unreachable) == 66); |
| 250 | 266 | |
| 267 | assert(map.contains(2)); | |
| 251 | 268 | assert((??map.get(2)).value == 22); |
| 252 | 269 | _ = map.remove(2); |
| 253 | 270 | assert(map.remove(2) == null); |
| 254 | 271 | assert(map.get(2) == null); |
| 255 | 272 | } |
| 256 | 273 | |
| 274 | test "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 | ||
| 257 | 313 | fn hash_i32(x: i32) u32 { |
| 258 | 314 | return @bitCast(u32, x); |
| 259 | 315 | } |
std/heap.zig+117-18| ... | ... | @@ -47,13 +47,6 @@ pub const DirectAllocator = struct { |
| 47 | 47 | |
| 48 | 48 | const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void; |
| 49 | 49 | |
| 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 | 50 | pub fn init() DirectAllocator { |
| 58 | 51 | return DirectAllocator { |
| 59 | 52 | .allocator = Allocator { |
| ... | ... | @@ -79,19 +72,38 @@ pub const DirectAllocator = struct { |
| 79 | 72 | |
| 80 | 73 | switch (builtin.os) { |
| 81 | 74 | Os.linux, Os.macosx, Os.ios => { |
| 82 | assert(alignment <= os.page_size); | |
| 83 | 75 | const p = os.posix; |
| 84 | const addr = p.mmap(null, n, p.PROT_READ|p.PROT_WRITE, | |
| 85 | p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0); | |
| 86 | if (addr == p.MAP_FAILED) { | |
| 87 | return error.OutOfMemory; | |
| 88 | } | |
| 89 | return @intToPtr(&u8, addr)[0..n]; | |
| 76 | const alloc_size = if(alignment <= os.page_size) n else n + alignment; | |
| 77 | const addr = p.mmap(null, alloc_size, p.PROT_READ|p.PROT_WRITE, | |
| 78 | p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0); | |
| 79 | if(addr == p.MAP_FAILED) return error.OutOfMemory; | |
| 80 | ||
| 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 | 102 | Os.windows => { |
| 92 | 103 | const amt = n + alignment + @sizeOf(usize); |
| 93 | 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 | 107 | self.heap_handle = hh; |
| 96 | 108 | break :blk hh; |
| 97 | 109 | }; |
| ... | ... | @@ -120,7 +132,7 @@ pub const DirectAllocator = struct { |
| 120 | 132 | const rem = @rem(new_addr_end, os.page_size); |
| 121 | 133 | const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem); |
| 122 | 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 | 137 | return old_mem[0..new_size]; |
| 126 | 138 | } |
| ... | ... | @@ -158,7 +170,7 @@ pub const DirectAllocator = struct { |
| 158 | 170 | |
| 159 | 171 | switch (builtin.os) { |
| 160 | 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 | 175 | Os.windows => { |
| 164 | 176 | const record_addr = @ptrToInt(bytes.ptr) + bytes.len; |
| ... | ... | @@ -279,7 +291,7 @@ pub const FixedBufferAllocator = struct { |
| 279 | 291 | |
| 280 | 292 | fn alloc(allocator: &Allocator, n: usize, alignment: u29) ![]u8 { |
| 281 | 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 | 295 | const rem = @rem(addr, alignment); |
| 284 | 296 | const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); |
| 285 | 297 | const adjusted_index = self.end_index + march_forward_bytes; |
| ... | ... | @@ -306,6 +318,54 @@ pub const FixedBufferAllocator = struct { |
| 306 | 318 | fn free(allocator: &Allocator, bytes: []u8) void { } |
| 307 | 319 | }; |
| 308 | 320 | |
| 321 | /// lock free | |
| 322 | pub 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 | ||
| 309 | 369 | |
| 310 | 370 | |
| 311 | 371 | test "c_allocator" { |
| ... | ... | @@ -322,6 +382,7 @@ test "DirectAllocator" { |
| 322 | 382 | |
| 323 | 383 | const allocator = &direct_allocator.allocator; |
| 324 | 384 | try testAllocator(allocator); |
| 385 | try testAllocatorLargeAlignment(allocator); | |
| 325 | 386 | } |
| 326 | 387 | |
| 327 | 388 | test "ArenaAllocator" { |
| ... | ... | @@ -332,6 +393,7 @@ test "ArenaAllocator" { |
| 332 | 393 | defer arena_allocator.deinit(); |
| 333 | 394 | |
| 334 | 395 | try testAllocator(&arena_allocator.allocator); |
| 396 | try testAllocatorLargeAlignment(&arena_allocator.allocator); | |
| 335 | 397 | } |
| 336 | 398 | |
| 337 | 399 | var test_fixed_buffer_allocator_memory: [30000 * @sizeOf(usize)]u8 = undefined; |
| ... | ... | @@ -339,6 +401,14 @@ test "FixedBufferAllocator" { |
| 339 | 401 | var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]); |
| 340 | 402 | |
| 341 | 403 | try testAllocator(&fixed_buffer_allocator.allocator); |
| 404 | try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator); | |
| 405 | } | |
| 406 | ||
| 407 | test "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 | } |
| 343 | 413 | |
| 344 | 414 | fn testAllocator(allocator: &mem.Allocator) !void { |
| ... | ... | @@ -360,3 +430,32 @@ fn testAllocator(allocator: &mem.Allocator) !void { |
| 360 | 430 | |
| 361 | 431 | allocator.free(slice); |
| 362 | 432 | } |
| 433 | ||
| 434 | fn 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 | pub const HashMap = @import("hash_map.zig").HashMap; |
| 8 | 8 | pub const LinkedList = @import("linked_list.zig").LinkedList; |
| 9 | 9 | pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList; |
| 10 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; | |
| 10 | 11 | |
| 12 | pub const atomic = @import("atomic/index.zig"); | |
| 11 | 13 | pub const base64 = @import("base64.zig"); |
| 12 | 14 | pub const build = @import("build.zig"); |
| 13 | 15 | pub const c = @import("c/index.zig"); |
| ... | ... | @@ -17,11 +19,12 @@ pub const debug = @import("debug/index.zig"); |
| 17 | 19 | pub const dwarf = @import("dwarf.zig"); |
| 18 | 20 | pub const elf = @import("elf.zig"); |
| 19 | 21 | pub const empty_import = @import("empty.zig"); |
| 20 | pub const endian = @import("endian.zig"); | |
| 22 | pub const event = @import("event.zig"); | |
| 21 | 23 | pub const fmt = @import("fmt/index.zig"); |
| 22 | 24 | pub const hash = @import("hash/index.zig"); |
| 23 | 25 | pub const heap = @import("heap.zig"); |
| 24 | 26 | pub const io = @import("io.zig"); |
| 27 | pub const json = @import("json.zig"); | |
| 25 | 28 | pub const macho = @import("macho.zig"); |
| 26 | 29 | pub const math = @import("math/index.zig"); |
| 27 | 30 | pub const mem = @import("mem.zig"); |
| ... | ... | @@ -34,12 +37,14 @@ pub const zig = @import("zig/index.zig"); |
| 34 | 37 | |
| 35 | 38 | test "std" { |
| 36 | 39 | // run tests from these |
| 40 | _ = @import("atomic/index.zig"); | |
| 37 | 41 | _ = @import("array_list.zig"); |
| 38 | 42 | _ = @import("buf_map.zig"); |
| 39 | 43 | _ = @import("buf_set.zig"); |
| 40 | 44 | _ = @import("buffer.zig"); |
| 41 | 45 | _ = @import("hash_map.zig"); |
| 42 | 46 | _ = @import("linked_list.zig"); |
| 47 | _ = @import("segmented_list.zig"); | |
| 43 | 48 | |
| 44 | 49 | _ = @import("base64.zig"); |
| 45 | 50 | _ = @import("build.zig"); |
| ... | ... | @@ -50,15 +55,16 @@ test "std" { |
| 50 | 55 | _ = @import("dwarf.zig"); |
| 51 | 56 | _ = @import("elf.zig"); |
| 52 | 57 | _ = @import("empty.zig"); |
| 53 | _ = @import("endian.zig"); | |
| 58 | _ = @import("event.zig"); | |
| 54 | 59 | _ = @import("fmt/index.zig"); |
| 55 | 60 | _ = @import("hash/index.zig"); |
| 56 | 61 | _ = @import("io.zig"); |
| 62 | _ = @import("json.zig"); | |
| 57 | 63 | _ = @import("macho.zig"); |
| 58 | 64 | _ = @import("math/index.zig"); |
| 59 | 65 | _ = @import("mem.zig"); |
| 60 | _ = @import("heap.zig"); | |
| 61 | 66 | _ = @import("net.zig"); |
| 67 | _ = @import("heap.zig"); | |
| 62 | 68 | _ = @import("os/index.zig"); |
| 63 | 69 | _ = @import("rand/index.zig"); |
| 64 | 70 | _ = @import("sort.zig"); |
std/io.zig+5| ... | ... | @@ -486,6 +486,11 @@ pub fn readLine(buf: []u8) !usize { |
| 486 | 486 | while (true) { |
| 487 | 487 | const byte = stream.readByte() catch return error.EndOfFile; |
| 488 | 488 | switch (byte) { |
| 489 | '\r' => { | |
| 490 | // trash the following \n | |
| 491 | _ = stream.readByte() catch return error.EndOfFile; | |
| 492 | return index; | |
| 493 | }, | |
| 489 | 494 | '\n' => return index, |
| 490 | 495 | else => { |
| 491 | 496 | if (index == buf.len) return error.InputTooLong; |
std/io_test.zig+17-1| ... | ... | @@ -1,6 +1,5 @@ |
| 1 | 1 | const std = @import("index.zig"); |
| 2 | 2 | const io = std.io; |
| 3 | const allocator = std.debug.global_allocator; | |
| 4 | 3 | const DefaultPrng = std.rand.DefaultPrng; |
| 5 | 4 | const assert = std.debug.assert; |
| 6 | 5 | const mem = std.mem; |
| ... | ... | @@ -8,6 +7,9 @@ const os = std.os; |
| 8 | 7 | const builtin = @import("builtin"); |
| 9 | 8 | |
| 10 | 9 | test "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 | 13 | var data: [1024]u8 = undefined; |
| 12 | 14 | var prng = DefaultPrng.init(1234); |
| 13 | 15 | prng.random.bytes(data[0..]); |
| ... | ... | @@ -44,3 +46,17 @@ test "write a file, read it, then delete it" { |
| 44 | 46 | } |
| 45 | 47 | try os.deleteFile(allocator, tmp_file_name); |
| 46 | 48 | } |
| 49 | ||
| 50 | test "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 | ||
| 5 | const std = @import("index.zig"); | |
| 6 | const mem = std.mem; | |
| 7 | ||
| 8 | const u1 = @IntType(false, 1); | |
| 9 | const 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. | |
| 14 | pub 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. | |
| 89 | const 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. | |
| 857 | pub 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 | ||
| 872 | const Allocator = std.mem.Allocator; | |
| 873 | const ArenaAllocator = std.heap.ArenaAllocator; | |
| 874 | const ArrayList = std.ArrayList; | |
| 875 | const HashMap = std.HashMap; | |
| 876 | ||
| 877 | pub const ValueTree = struct { | |
| 878 | arena: ArenaAllocator, | |
| 879 | root: Value, | |
| 880 | ||
| 881 | pub fn deinit(self: &ValueTree) void { | |
| 882 | self.arena.deinit(); | |
| 883 | } | |
| 884 | }; | |
| 885 | ||
| 886 | pub const ObjectMap = HashMap([]const u8, Value, mem.hash_slice_u8, mem.eql_slice_u8); | |
| 887 | ||
| 888 | pub 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. | |
| 1015 | const 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 | ||
| 1263 | const debug = std.debug; | |
| 1264 | ||
| 1265 | test "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 | ||
| 6 | const std = @import("index.zig"); | |
| 7 | ||
| 8 | fn ok(comptime s: []const u8) void { | |
| 9 | std.debug.assert(std.json.validate(s)); | |
| 10 | } | |
| 11 | ||
| 12 | fn err(comptime s: []const u8) void { | |
| 13 | std.debug.assert(!std.json.validate(s)); | |
| 14 | } | |
| 15 | ||
| 16 | fn any(comptime s: []const u8) void { | |
| 17 | std.debug.assert(true); | |
| 18 | } | |
| 19 | ||
| 20 | //////////////////////////////////////////////////////////////////////////////////////////////////// | |
| 21 | ||
| 22 | test "y_array_arraysWithSpaces" { | |
| 23 | ok( | |
| 24 | \\[[] ] | |
| 25 | ); | |
| 26 | } | |
| 27 | ||
| 28 | test "y_array_empty" { | |
| 29 | ok( | |
| 30 | \\[] | |
| 31 | ); | |
| 32 | } | |
| 33 | ||
| 34 | test "y_array_empty-string" { | |
| 35 | ok( | |
| 36 | \\[""] | |
| 37 | ); | |
| 38 | } | |
| 39 | ||
| 40 | test "y_array_ending_with_newline" { | |
| 41 | ok( | |
| 42 | \\["a"] | |
| 43 | ); | |
| 44 | } | |
| 45 | ||
| 46 | test "y_array_false" { | |
| 47 | ok( | |
| 48 | \\[false] | |
| 49 | ); | |
| 50 | } | |
| 51 | ||
| 52 | test "y_array_heterogeneous" { | |
| 53 | ok( | |
| 54 | \\[null, 1, "1", {}] | |
| 55 | ); | |
| 56 | } | |
| 57 | ||
| 58 | test "y_array_null" { | |
| 59 | ok( | |
| 60 | \\[null] | |
| 61 | ); | |
| 62 | } | |
| 63 | ||
| 64 | test "y_array_with_1_and_newline" { | |
| 65 | ok( | |
| 66 | \\[1 | |
| 67 | \\] | |
| 68 | ); | |
| 69 | } | |
| 70 | ||
| 71 | test "y_array_with_leading_space" { | |
| 72 | ok( | |
| 73 | \\ [1] | |
| 74 | ); | |
| 75 | } | |
| 76 | ||
| 77 | test "y_array_with_several_null" { | |
| 78 | ok( | |
| 79 | \\[1,null,null,null,2] | |
| 80 | ); | |
| 81 | } | |
| 82 | ||
| 83 | test "y_array_with_trailing_space" { | |
| 84 | ok( | |
| 85 | "[2] " | |
| 86 | ); | |
| 87 | } | |
| 88 | ||
| 89 | test "y_number_0e+1" { | |
| 90 | ok( | |
| 91 | \\[0e+1] | |
| 92 | ); | |
| 93 | } | |
| 94 | ||
| 95 | test "y_number_0e1" { | |
| 96 | ok( | |
| 97 | \\[0e1] | |
| 98 | ); | |
| 99 | } | |
| 100 | ||
| 101 | test "y_number_after_space" { | |
| 102 | ok( | |
| 103 | \\[ 4] | |
| 104 | ); | |
| 105 | } | |
| 106 | ||
| 107 | test "y_number_double_close_to_zero" { | |
| 108 | ok( | |
| 109 | \\[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001] | |
| 110 | ); | |
| 111 | } | |
| 112 | ||
| 113 | test "y_number_int_with_exp" { | |
| 114 | ok( | |
| 115 | \\[20e1] | |
| 116 | ); | |
| 117 | } | |
| 118 | ||
| 119 | test "y_number" { | |
| 120 | ok( | |
| 121 | \\[123e65] | |
| 122 | ); | |
| 123 | } | |
| 124 | ||
| 125 | test "y_number_minus_zero" { | |
| 126 | ok( | |
| 127 | \\[-0] | |
| 128 | ); | |
| 129 | } | |
| 130 | ||
| 131 | test "y_number_negative_int" { | |
| 132 | ok( | |
| 133 | \\[-123] | |
| 134 | ); | |
| 135 | } | |
| 136 | ||
| 137 | test "y_number_negative_one" { | |
| 138 | ok( | |
| 139 | \\[-1] | |
| 140 | ); | |
| 141 | } | |
| 142 | ||
| 143 | test "y_number_negative_zero" { | |
| 144 | ok( | |
| 145 | \\[-0] | |
| 146 | ); | |
| 147 | } | |
| 148 | ||
| 149 | test "y_number_real_capital_e" { | |
| 150 | ok( | |
| 151 | \\[1E22] | |
| 152 | ); | |
| 153 | } | |
| 154 | ||
| 155 | test "y_number_real_capital_e_neg_exp" { | |
| 156 | ok( | |
| 157 | \\[1E-2] | |
| 158 | ); | |
| 159 | } | |
| 160 | ||
| 161 | test "y_number_real_capital_e_pos_exp" { | |
| 162 | ok( | |
| 163 | \\[1E+2] | |
| 164 | ); | |
| 165 | } | |
| 166 | ||
| 167 | test "y_number_real_exponent" { | |
| 168 | ok( | |
| 169 | \\[123e45] | |
| 170 | ); | |
| 171 | } | |
| 172 | ||
| 173 | test "y_number_real_fraction_exponent" { | |
| 174 | ok( | |
| 175 | \\[123.456e78] | |
| 176 | ); | |
| 177 | } | |
| 178 | ||
| 179 | test "y_number_real_neg_exp" { | |
| 180 | ok( | |
| 181 | \\[1e-2] | |
| 182 | ); | |
| 183 | } | |
| 184 | ||
| 185 | test "y_number_real_pos_exponent" { | |
| 186 | ok( | |
| 187 | \\[1e+2] | |
| 188 | ); | |
| 189 | } | |
| 190 | ||
| 191 | test "y_number_simple_int" { | |
| 192 | ok( | |
| 193 | \\[123] | |
| 194 | ); | |
| 195 | } | |
| 196 | ||
| 197 | test "y_number_simple_real" { | |
| 198 | ok( | |
| 199 | \\[123.456789] | |
| 200 | ); | |
| 201 | } | |
| 202 | ||
| 203 | test "y_object_basic" { | |
| 204 | ok( | |
| 205 | \\{"asd":"sdf"} | |
| 206 | ); | |
| 207 | } | |
| 208 | ||
| 209 | test "y_object_duplicated_key_and_value" { | |
| 210 | ok( | |
| 211 | \\{"a":"b","a":"b"} | |
| 212 | ); | |
| 213 | } | |
| 214 | ||
| 215 | test "y_object_duplicated_key" { | |
| 216 | ok( | |
| 217 | \\{"a":"b","a":"c"} | |
| 218 | ); | |
| 219 | } | |
| 220 | ||
| 221 | test "y_object_empty" { | |
| 222 | ok( | |
| 223 | \\{} | |
| 224 | ); | |
| 225 | } | |
| 226 | ||
| 227 | test "y_object_empty_key" { | |
| 228 | ok( | |
| 229 | \\{"":0} | |
| 230 | ); | |
| 231 | } | |
| 232 | ||
| 233 | test "y_object_escaped_null_in_key" { | |
| 234 | ok( | |
| 235 | \\{"foo\u0000bar": 42} | |
| 236 | ); | |
| 237 | } | |
| 238 | ||
| 239 | test "y_object_extreme_numbers" { | |
| 240 | ok( | |
| 241 | \\{ "min": -1.0e+28, "max": 1.0e+28 } | |
| 242 | ); | |
| 243 | } | |
| 244 | ||
| 245 | test "y_object" { | |
| 246 | ok( | |
| 247 | \\{"asd":"sdf", "dfg":"fgh"} | |
| 248 | ); | |
| 249 | } | |
| 250 | ||
| 251 | test "y_object_long_strings" { | |
| 252 | ok( | |
| 253 | \\{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"} | |
| 254 | ); | |
| 255 | } | |
| 256 | ||
| 257 | test "y_object_simple" { | |
| 258 | ok( | |
| 259 | \\{"a":[]} | |
| 260 | ); | |
| 261 | } | |
| 262 | ||
| 263 | test "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 | ||
| 269 | test "y_object_with_newlines" { | |
| 270 | ok( | |
| 271 | \\{ | |
| 272 | \\"a": "b" | |
| 273 | \\} | |
| 274 | ); | |
| 275 | } | |
| 276 | ||
| 277 | test "y_string_1_2_3_bytes_UTF-8_sequences" { | |
| 278 | ok( | |
| 279 | \\["\u0060\u012a\u12AB"] | |
| 280 | ); | |
| 281 | } | |
| 282 | ||
| 283 | test "y_string_accepted_surrogate_pair" { | |
| 284 | ok( | |
| 285 | \\["\uD801\udc37"] | |
| 286 | ); | |
| 287 | } | |
| 288 | ||
| 289 | test "y_string_accepted_surrogate_pairs" { | |
| 290 | ok( | |
| 291 | \\["\ud83d\ude39\ud83d\udc8d"] | |
| 292 | ); | |
| 293 | } | |
| 294 | ||
| 295 | test "y_string_allowed_escapes" { | |
| 296 | ok( | |
| 297 | \\["\"\\\/\b\f\n\r\t"] | |
| 298 | ); | |
| 299 | } | |
| 300 | ||
| 301 | test "y_string_backslash_and_u_escaped_zero" { | |
| 302 | ok( | |
| 303 | \\["\\u0000"] | |
| 304 | ); | |
| 305 | } | |
| 306 | ||
| 307 | test "y_string_backslash_doublequotes" { | |
| 308 | ok( | |
| 309 | \\["\""] | |
| 310 | ); | |
| 311 | } | |
| 312 | ||
| 313 | test "y_string_comments" { | |
| 314 | ok( | |
| 315 | \\["a/*b*/c/*d//e"] | |
| 316 | ); | |
| 317 | } | |
| 318 | ||
| 319 | test "y_string_double_escape_a" { | |
| 320 | ok( | |
| 321 | \\["\\a"] | |
| 322 | ); | |
| 323 | } | |
| 324 | ||
| 325 | test "y_string_double_escape_n" { | |
| 326 | ok( | |
| 327 | \\["\\n"] | |
| 328 | ); | |
| 329 | } | |
| 330 | ||
| 331 | test "y_string_escaped_control_character" { | |
| 332 | ok( | |
| 333 | \\["\u0012"] | |
| 334 | ); | |
| 335 | } | |
| 336 | ||
| 337 | test "y_string_escaped_noncharacter" { | |
| 338 | ok( | |
| 339 | \\["\uFFFF"] | |
| 340 | ); | |
| 341 | } | |
| 342 | ||
| 343 | test "y_string_in_array" { | |
| 344 | ok( | |
| 345 | \\["asd"] | |
| 346 | ); | |
| 347 | } | |
| 348 | ||
| 349 | test "y_string_in_array_with_leading_space" { | |
| 350 | ok( | |
| 351 | \\[ "asd"] | |
| 352 | ); | |
| 353 | } | |
| 354 | ||
| 355 | test "y_string_last_surrogates_1_and_2" { | |
| 356 | ok( | |
| 357 | \\["\uDBFF\uDFFF"] | |
| 358 | ); | |
| 359 | } | |
| 360 | ||
| 361 | test "y_string_nbsp_uescaped" { | |
| 362 | ok( | |
| 363 | \\["new\u00A0line"] | |
| 364 | ); | |
| 365 | } | |
| 366 | ||
| 367 | test "y_string_nonCharacterInUTF-8_U+10FFFF" { | |
| 368 | ok( | |
| 369 | \\[""] | |
| 370 | ); | |
| 371 | } | |
| 372 | ||
| 373 | test "y_string_nonCharacterInUTF-8_U+FFFF" { | |
| 374 | ok( | |
| 375 | \\[""] | |
| 376 | ); | |
| 377 | } | |
| 378 | ||
| 379 | test "y_string_null_escape" { | |
| 380 | ok( | |
| 381 | \\["\u0000"] | |
| 382 | ); | |
| 383 | } | |
| 384 | ||
| 385 | test "y_string_one-byte-utf-8" { | |
| 386 | ok( | |
| 387 | \\["\u002c"] | |
| 388 | ); | |
| 389 | } | |
| 390 | ||
| 391 | test "y_string_pi" { | |
| 392 | ok( | |
| 393 | \\["π"] | |
| 394 | ); | |
| 395 | } | |
| 396 | ||
| 397 | test "y_string_reservedCharacterInUTF-8_U+1BFFF" { | |
| 398 | ok( | |
| 399 | \\[""] | |
| 400 | ); | |
| 401 | } | |
| 402 | ||
| 403 | test "y_string_simple_ascii" { | |
| 404 | ok( | |
| 405 | \\["asd "] | |
| 406 | ); | |
| 407 | } | |
| 408 | ||
| 409 | test "y_string_space" { | |
| 410 | ok( | |
| 411 | \\" " | |
| 412 | ); | |
| 413 | } | |
| 414 | ||
| 415 | test "y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF" { | |
| 416 | ok( | |
| 417 | \\["\uD834\uDd1e"] | |
| 418 | ); | |
| 419 | } | |
| 420 | ||
| 421 | test "y_string_three-byte-utf-8" { | |
| 422 | ok( | |
| 423 | \\["\u0821"] | |
| 424 | ); | |
| 425 | } | |
| 426 | ||
| 427 | test "y_string_two-byte-utf-8" { | |
| 428 | ok( | |
| 429 | \\["\u0123"] | |
| 430 | ); | |
| 431 | } | |
| 432 | ||
| 433 | test "y_string_u+2028_line_sep" { | |
| 434 | ok( | |
| 435 | \\[" "] | |
| 436 | ); | |
| 437 | } | |
| 438 | ||
| 439 | test "y_string_u+2029_par_sep" { | |
| 440 | ok( | |
| 441 | \\[" "] | |
| 442 | ); | |
| 443 | } | |
| 444 | ||
| 445 | test "y_string_uescaped_newline" { | |
| 446 | ok( | |
| 447 | \\["new\u000Aline"] | |
| 448 | ); | |
| 449 | } | |
| 450 | ||
| 451 | test "y_string_uEscape" { | |
| 452 | ok( | |
| 453 | \\["\u0061\u30af\u30EA\u30b9"] | |
| 454 | ); | |
| 455 | } | |
| 456 | ||
| 457 | test "y_string_unescaped_char_delete" { | |
| 458 | ok( | |
| 459 | \\[""] | |
| 460 | ); | |
| 461 | } | |
| 462 | ||
| 463 | test "y_string_unicode_2" { | |
| 464 | ok( | |
| 465 | \\["⍂㈴⍂"] | |
| 466 | ); | |
| 467 | } | |
| 468 | ||
| 469 | test "y_string_unicodeEscapedBackslash" { | |
| 470 | ok( | |
| 471 | \\["\u005C"] | |
| 472 | ); | |
| 473 | } | |
| 474 | ||
| 475 | test "y_string_unicode_escaped_double_quote" { | |
| 476 | ok( | |
| 477 | \\["\u0022"] | |
| 478 | ); | |
| 479 | } | |
| 480 | ||
| 481 | test "y_string_unicode" { | |
| 482 | ok( | |
| 483 | \\["\uA66D"] | |
| 484 | ); | |
| 485 | } | |
| 486 | ||
| 487 | test "y_string_unicode_U+10FFFE_nonchar" { | |
| 488 | ok( | |
| 489 | \\["\uDBFF\uDFFE"] | |
| 490 | ); | |
| 491 | } | |
| 492 | ||
| 493 | test "y_string_unicode_U+1FFFE_nonchar" { | |
| 494 | ok( | |
| 495 | \\["\uD83F\uDFFE"] | |
| 496 | ); | |
| 497 | } | |
| 498 | ||
| 499 | test "y_string_unicode_U+200B_ZERO_WIDTH_SPACE" { | |
| 500 | ok( | |
| 501 | \\["\u200B"] | |
| 502 | ); | |
| 503 | } | |
| 504 | ||
| 505 | test "y_string_unicode_U+2064_invisible_plus" { | |
| 506 | ok( | |
| 507 | \\["\u2064"] | |
| 508 | ); | |
| 509 | } | |
| 510 | ||
| 511 | test "y_string_unicode_U+FDD0_nonchar" { | |
| 512 | ok( | |
| 513 | \\["\uFDD0"] | |
| 514 | ); | |
| 515 | } | |
| 516 | ||
| 517 | test "y_string_unicode_U+FFFE_nonchar" { | |
| 518 | ok( | |
| 519 | \\["\uFFFE"] | |
| 520 | ); | |
| 521 | } | |
| 522 | ||
| 523 | test "y_string_utf8" { | |
| 524 | ok( | |
| 525 | \\["€𝄞"] | |
| 526 | ); | |
| 527 | } | |
| 528 | ||
| 529 | test "y_string_with_del_character" { | |
| 530 | ok( | |
| 531 | \\["aa"] | |
| 532 | ); | |
| 533 | } | |
| 534 | ||
| 535 | test "y_structure_lonely_false" { | |
| 536 | ok( | |
| 537 | \\false | |
| 538 | ); | |
| 539 | } | |
| 540 | ||
| 541 | test "y_structure_lonely_int" { | |
| 542 | ok( | |
| 543 | \\42 | |
| 544 | ); | |
| 545 | } | |
| 546 | ||
| 547 | test "y_structure_lonely_negative_real" { | |
| 548 | ok( | |
| 549 | \\-0.1 | |
| 550 | ); | |
| 551 | } | |
| 552 | ||
| 553 | test "y_structure_lonely_null" { | |
| 554 | ok( | |
| 555 | \\null | |
| 556 | ); | |
| 557 | } | |
| 558 | ||
| 559 | test "y_structure_lonely_string" { | |
| 560 | ok( | |
| 561 | \\"asd" | |
| 562 | ); | |
| 563 | } | |
| 564 | ||
| 565 | test "y_structure_lonely_true" { | |
| 566 | ok( | |
| 567 | \\true | |
| 568 | ); | |
| 569 | } | |
| 570 | ||
| 571 | test "y_structure_string_empty" { | |
| 572 | ok( | |
| 573 | \\"" | |
| 574 | ); | |
| 575 | } | |
| 576 | ||
| 577 | test "y_structure_trailing_newline" { | |
| 578 | ok( | |
| 579 | \\["a"] | |
| 580 | ); | |
| 581 | } | |
| 582 | ||
| 583 | test "y_structure_true_in_array" { | |
| 584 | ok( | |
| 585 | \\[true] | |
| 586 | ); | |
| 587 | } | |
| 588 | ||
| 589 | test "y_structure_whitespace_array" { | |
| 590 | ok( | |
| 591 | " [] " | |
| 592 | ); | |
| 593 | } | |
| 594 | ||
| 595 | //////////////////////////////////////////////////////////////////////////////////////////////////// | |
| 596 | ||
| 597 | test "n_array_1_true_without_comma" { | |
| 598 | err( | |
| 599 | \\[1 true] | |
| 600 | ); | |
| 601 | } | |
| 602 | ||
| 603 | test "n_array_a_invalid_utf8" { | |
| 604 | err( | |
| 605 | \\[aå] | |
| 606 | ); | |
| 607 | } | |
| 608 | ||
| 609 | test "n_array_colon_instead_of_comma" { | |
| 610 | err( | |
| 611 | \\["": 1] | |
| 612 | ); | |
| 613 | } | |
| 614 | ||
| 615 | test "n_array_comma_after_close" { | |
| 616 | //err( | |
| 617 | // \\[""], | |
| 618 | //); | |
| 619 | } | |
| 620 | ||
| 621 | test "n_array_comma_and_number" { | |
| 622 | err( | |
| 623 | \\[,1] | |
| 624 | ); | |
| 625 | } | |
| 626 | ||
| 627 | test "n_array_double_comma" { | |
| 628 | err( | |
| 629 | \\[1,,2] | |
| 630 | ); | |
| 631 | } | |
| 632 | ||
| 633 | test "n_array_double_extra_comma" { | |
| 634 | err( | |
| 635 | \\["x",,] | |
| 636 | ); | |
| 637 | } | |
| 638 | ||
| 639 | test "n_array_extra_close" { | |
| 640 | err( | |
| 641 | \\["x"]] | |
| 642 | ); | |
| 643 | } | |
| 644 | ||
| 645 | test "n_array_extra_comma" { | |
| 646 | //err( | |
| 647 | // \\["",] | |
| 648 | //); | |
| 649 | } | |
| 650 | ||
| 651 | test "n_array_incomplete_invalid_value" { | |
| 652 | err( | |
| 653 | \\[x | |
| 654 | ); | |
| 655 | } | |
| 656 | ||
| 657 | test "n_array_incomplete" { | |
| 658 | err( | |
| 659 | \\["x" | |
| 660 | ); | |
| 661 | } | |
| 662 | ||
| 663 | test "n_array_inner_array_no_comma" { | |
| 664 | err( | |
| 665 | \\[3[4]] | |
| 666 | ); | |
| 667 | } | |
| 668 | ||
| 669 | test "n_array_invalid_utf8" { | |
| 670 | err( | |
| 671 | \\[ÿ] | |
| 672 | ); | |
| 673 | } | |
| 674 | ||
| 675 | test "n_array_items_separated_by_semicolon" { | |
| 676 | err( | |
| 677 | \\[1:2] | |
| 678 | ); | |
| 679 | } | |
| 680 | ||
| 681 | test "n_array_just_comma" { | |
| 682 | err( | |
| 683 | \\[,] | |
| 684 | ); | |
| 685 | } | |
| 686 | ||
| 687 | test "n_array_just_minus" { | |
| 688 | err( | |
| 689 | \\[-] | |
| 690 | ); | |
| 691 | } | |
| 692 | ||
| 693 | test "n_array_missing_value" { | |
| 694 | err( | |
| 695 | \\[ , ""] | |
| 696 | ); | |
| 697 | } | |
| 698 | ||
| 699 | test "n_array_newlines_unclosed" { | |
| 700 | err( | |
| 701 | \\["a", | |
| 702 | \\4 | |
| 703 | \\,1, | |
| 704 | ); | |
| 705 | } | |
| 706 | ||
| 707 | ||
| 708 | test "n_array_number_and_comma" { | |
| 709 | err( | |
| 710 | \\[1,] | |
| 711 | ); | |
| 712 | } | |
| 713 | ||
| 714 | test "n_array_number_and_several_commas" { | |
| 715 | err( | |
| 716 | \\[1,,] | |
| 717 | ); | |
| 718 | } | |
| 719 | ||
| 720 | test "n_array_spaces_vertical_tab_formfeed" { | |
| 721 | err( | |
| 722 | \\["a"\f] | |
| 723 | ); | |
| 724 | } | |
| 725 | ||
| 726 | test "n_array_star_inside" { | |
| 727 | err( | |
| 728 | \\[*] | |
| 729 | ); | |
| 730 | } | |
| 731 | ||
| 732 | test "n_array_unclosed" { | |
| 733 | err( | |
| 734 | \\["" | |
| 735 | ); | |
| 736 | } | |
| 737 | ||
| 738 | test "n_array_unclosed_trailing_comma" { | |
| 739 | err( | |
| 740 | \\[1, | |
| 741 | ); | |
| 742 | } | |
| 743 | ||
| 744 | test "n_array_unclosed_with_new_lines" { | |
| 745 | err( | |
| 746 | \\[1, | |
| 747 | \\1 | |
| 748 | \\,1 | |
| 749 | ); | |
| 750 | } | |
| 751 | ||
| 752 | test "n_array_unclosed_with_object_inside" { | |
| 753 | err( | |
| 754 | \\[{} | |
| 755 | ); | |
| 756 | } | |
| 757 | ||
| 758 | test "n_incomplete_false" { | |
| 759 | err( | |
| 760 | \\[fals] | |
| 761 | ); | |
| 762 | } | |
| 763 | ||
| 764 | test "n_incomplete_null" { | |
| 765 | err( | |
| 766 | \\[nul] | |
| 767 | ); | |
| 768 | } | |
| 769 | ||
| 770 | test "n_incomplete_true" { | |
| 771 | err( | |
| 772 | \\[tru] | |
| 773 | ); | |
| 774 | } | |
| 775 | ||
| 776 | test "n_multidigit_number_then_00" { | |
| 777 | err( | |
| 778 | \\123 | |
| 779 | ); | |
| 780 | } | |
| 781 | ||
| 782 | test "n_number_0.1.2" { | |
| 783 | err( | |
| 784 | \\[0.1.2] | |
| 785 | ); | |
| 786 | } | |
| 787 | ||
| 788 | test "n_number_-01" { | |
| 789 | err( | |
| 790 | \\[-01] | |
| 791 | ); | |
| 792 | } | |
| 793 | ||
| 794 | test "n_number_0.3e" { | |
| 795 | err( | |
| 796 | \\[0.3e] | |
| 797 | ); | |
| 798 | } | |
| 799 | ||
| 800 | test "n_number_0.3e+" { | |
| 801 | err( | |
| 802 | \\[0.3e+] | |
| 803 | ); | |
| 804 | } | |
| 805 | ||
| 806 | test "n_number_0_capital_E" { | |
| 807 | err( | |
| 808 | \\[0E] | |
| 809 | ); | |
| 810 | } | |
| 811 | ||
| 812 | test "n_number_0_capital_E+" { | |
| 813 | err( | |
| 814 | \\[0E+] | |
| 815 | ); | |
| 816 | } | |
| 817 | ||
| 818 | test "n_number_0.e1" { | |
| 819 | err( | |
| 820 | \\[0.e1] | |
| 821 | ); | |
| 822 | } | |
| 823 | ||
| 824 | test "n_number_0e" { | |
| 825 | err( | |
| 826 | \\[0e] | |
| 827 | ); | |
| 828 | } | |
| 829 | ||
| 830 | test "n_number_0e+" { | |
| 831 | err( | |
| 832 | \\[0e+] | |
| 833 | ); | |
| 834 | } | |
| 835 | ||
| 836 | test "n_number_1_000" { | |
| 837 | err( | |
| 838 | \\[1 000.0] | |
| 839 | ); | |
| 840 | } | |
| 841 | ||
| 842 | test "n_number_1.0e-" { | |
| 843 | err( | |
| 844 | \\[1.0e-] | |
| 845 | ); | |
| 846 | } | |
| 847 | ||
| 848 | test "n_number_1.0e" { | |
| 849 | err( | |
| 850 | \\[1.0e] | |
| 851 | ); | |
| 852 | } | |
| 853 | ||
| 854 | test "n_number_1.0e+" { | |
| 855 | err( | |
| 856 | \\[1.0e+] | |
| 857 | ); | |
| 858 | } | |
| 859 | ||
| 860 | test "n_number_-1.0." { | |
| 861 | err( | |
| 862 | \\[-1.0.] | |
| 863 | ); | |
| 864 | } | |
| 865 | ||
| 866 | test "n_number_1eE2" { | |
| 867 | err( | |
| 868 | \\[1eE2] | |
| 869 | ); | |
| 870 | } | |
| 871 | ||
| 872 | test "n_number_.-1" { | |
| 873 | err( | |
| 874 | \\[.-1] | |
| 875 | ); | |
| 876 | } | |
| 877 | ||
| 878 | test "n_number_+1" { | |
| 879 | err( | |
| 880 | \\[+1] | |
| 881 | ); | |
| 882 | } | |
| 883 | ||
| 884 | test "n_number_.2e-3" { | |
| 885 | err( | |
| 886 | \\[.2e-3] | |
| 887 | ); | |
| 888 | } | |
| 889 | ||
| 890 | test "n_number_2.e-3" { | |
| 891 | err( | |
| 892 | \\[2.e-3] | |
| 893 | ); | |
| 894 | } | |
| 895 | ||
| 896 | test "n_number_2.e+3" { | |
| 897 | err( | |
| 898 | \\[2.e+3] | |
| 899 | ); | |
| 900 | } | |
| 901 | ||
| 902 | test "n_number_2.e3" { | |
| 903 | err( | |
| 904 | \\[2.e3] | |
| 905 | ); | |
| 906 | } | |
| 907 | ||
| 908 | test "n_number_-2." { | |
| 909 | err( | |
| 910 | \\[-2.] | |
| 911 | ); | |
| 912 | } | |
| 913 | ||
| 914 | test "n_number_9.e+" { | |
| 915 | err( | |
| 916 | \\[9.e+] | |
| 917 | ); | |
| 918 | } | |
| 919 | ||
| 920 | test "n_number_expression" { | |
| 921 | err( | |
| 922 | \\[1+2] | |
| 923 | ); | |
| 924 | } | |
| 925 | ||
| 926 | test "n_number_hex_1_digit" { | |
| 927 | err( | |
| 928 | \\[0x1] | |
| 929 | ); | |
| 930 | } | |
| 931 | ||
| 932 | test "n_number_hex_2_digits" { | |
| 933 | err( | |
| 934 | \\[0x42] | |
| 935 | ); | |
| 936 | } | |
| 937 | ||
| 938 | test "n_number_infinity" { | |
| 939 | err( | |
| 940 | \\[Infinity] | |
| 941 | ); | |
| 942 | } | |
| 943 | ||
| 944 | test "n_number_+Inf" { | |
| 945 | err( | |
| 946 | \\[+Inf] | |
| 947 | ); | |
| 948 | } | |
| 949 | ||
| 950 | test "n_number_Inf" { | |
| 951 | err( | |
| 952 | \\[Inf] | |
| 953 | ); | |
| 954 | } | |
| 955 | ||
| 956 | test "n_number_invalid+-" { | |
| 957 | err( | |
| 958 | \\[0e+-1] | |
| 959 | ); | |
| 960 | } | |
| 961 | ||
| 962 | test "n_number_invalid-negative-real" { | |
| 963 | err( | |
| 964 | \\[-123.123foo] | |
| 965 | ); | |
| 966 | } | |
| 967 | ||
| 968 | test "n_number_invalid-utf-8-in-bigger-int" { | |
| 969 | err( | |
| 970 | \\[123å] | |
| 971 | ); | |
| 972 | } | |
| 973 | ||
| 974 | test "n_number_invalid-utf-8-in-exponent" { | |
| 975 | err( | |
| 976 | \\[1e1å] | |
| 977 | ); | |
| 978 | } | |
| 979 | ||
| 980 | test "n_number_invalid-utf-8-in-int" { | |
| 981 | err( | |
| 982 | \\[0å] | |
| 983 | ); | |
| 984 | } | |
| 985 | ||
| 986 | ||
| 987 | test "n_number_++" { | |
| 988 | err( | |
| 989 | \\[++1234] | |
| 990 | ); | |
| 991 | } | |
| 992 | ||
| 993 | test "n_number_minus_infinity" { | |
| 994 | err( | |
| 995 | \\[-Infinity] | |
| 996 | ); | |
| 997 | } | |
| 998 | ||
| 999 | test "n_number_minus_sign_with_trailing_garbage" { | |
| 1000 | err( | |
| 1001 | \\[-foo] | |
| 1002 | ); | |
| 1003 | } | |
| 1004 | ||
| 1005 | test "n_number_minus_space_1" { | |
| 1006 | err( | |
| 1007 | \\[- 1] | |
| 1008 | ); | |
| 1009 | } | |
| 1010 | ||
| 1011 | test "n_number_-NaN" { | |
| 1012 | err( | |
| 1013 | \\[-NaN] | |
| 1014 | ); | |
| 1015 | } | |
| 1016 | ||
| 1017 | test "n_number_NaN" { | |
| 1018 | err( | |
| 1019 | \\[NaN] | |
| 1020 | ); | |
| 1021 | } | |
| 1022 | ||
| 1023 | test "n_number_neg_int_starting_with_zero" { | |
| 1024 | err( | |
| 1025 | \\[-012] | |
| 1026 | ); | |
| 1027 | } | |
| 1028 | ||
| 1029 | test "n_number_neg_real_without_int_part" { | |
| 1030 | err( | |
| 1031 | \\[-.123] | |
| 1032 | ); | |
| 1033 | } | |
| 1034 | ||
| 1035 | test "n_number_neg_with_garbage_at_end" { | |
| 1036 | err( | |
| 1037 | \\[-1x] | |
| 1038 | ); | |
| 1039 | } | |
| 1040 | ||
| 1041 | test "n_number_real_garbage_after_e" { | |
| 1042 | err( | |
| 1043 | \\[1ea] | |
| 1044 | ); | |
| 1045 | } | |
| 1046 | ||
| 1047 | test "n_number_real_with_invalid_utf8_after_e" { | |
| 1048 | err( | |
| 1049 | \\[1eå] | |
| 1050 | ); | |
| 1051 | } | |
| 1052 | ||
| 1053 | test "n_number_real_without_fractional_part" { | |
| 1054 | err( | |
| 1055 | \\[1.] | |
| 1056 | ); | |
| 1057 | } | |
| 1058 | ||
| 1059 | test "n_number_starting_with_dot" { | |
| 1060 | err( | |
| 1061 | \\[.123] | |
| 1062 | ); | |
| 1063 | } | |
| 1064 | ||
| 1065 | test "n_number_U+FF11_fullwidth_digit_one" { | |
| 1066 | err( | |
| 1067 | \\[ï¼] | |
| 1068 | ); | |
| 1069 | } | |
| 1070 | ||
| 1071 | test "n_number_with_alpha_char" { | |
| 1072 | err( | |
| 1073 | \\[1.8011670033376514H-308] | |
| 1074 | ); | |
| 1075 | } | |
| 1076 | ||
| 1077 | test "n_number_with_alpha" { | |
| 1078 | err( | |
| 1079 | \\[1.2a-3] | |
| 1080 | ); | |
| 1081 | } | |
| 1082 | ||
| 1083 | test "n_number_with_leading_zero" { | |
| 1084 | err( | |
| 1085 | \\[012] | |
| 1086 | ); | |
| 1087 | } | |
| 1088 | ||
| 1089 | test "n_object_bad_value" { | |
| 1090 | err( | |
| 1091 | \\["x", truth] | |
| 1092 | ); | |
| 1093 | } | |
| 1094 | ||
| 1095 | test "n_object_bracket_key" { | |
| 1096 | err( | |
| 1097 | \\{[: "x"} | |
| 1098 | ); | |
| 1099 | } | |
| 1100 | ||
| 1101 | test "n_object_comma_instead_of_colon" { | |
| 1102 | err( | |
| 1103 | \\{"x", null} | |
| 1104 | ); | |
| 1105 | } | |
| 1106 | ||
| 1107 | test "n_object_double_colon" { | |
| 1108 | err( | |
| 1109 | \\{"x"::"b"} | |
| 1110 | ); | |
| 1111 | } | |
| 1112 | ||
| 1113 | test "n_object_emoji" { | |
| 1114 | err( | |
| 1115 | \\{ð¨ð} | |
| 1116 | ); | |
| 1117 | } | |
| 1118 | ||
| 1119 | test "n_object_garbage_at_end" { | |
| 1120 | err( | |
| 1121 | \\{"a":"a" 123} | |
| 1122 | ); | |
| 1123 | } | |
| 1124 | ||
| 1125 | test "n_object_key_with_single_quotes" { | |
| 1126 | err( | |
| 1127 | \\{key: 'value'} | |
| 1128 | ); | |
| 1129 | } | |
| 1130 | ||
| 1131 | test "n_object_lone_continuation_byte_in_key_and_trailing_comma" { | |
| 1132 | err( | |
| 1133 | \\{"¹":"0",} | |
| 1134 | ); | |
| 1135 | } | |
| 1136 | ||
| 1137 | test "n_object_missing_colon" { | |
| 1138 | err( | |
| 1139 | \\{"a" b} | |
| 1140 | ); | |
| 1141 | } | |
| 1142 | ||
| 1143 | test "n_object_missing_key" { | |
| 1144 | err( | |
| 1145 | \\{:"b"} | |
| 1146 | ); | |
| 1147 | } | |
| 1148 | ||
| 1149 | test "n_object_missing_semicolon" { | |
| 1150 | err( | |
| 1151 | \\{"a" "b"} | |
| 1152 | ); | |
| 1153 | } | |
| 1154 | ||
| 1155 | test "n_object_missing_value" { | |
| 1156 | err( | |
| 1157 | \\{"a": | |
| 1158 | ); | |
| 1159 | } | |
| 1160 | ||
| 1161 | test "n_object_no-colon" { | |
| 1162 | err( | |
| 1163 | \\{"a" | |
| 1164 | ); | |
| 1165 | } | |
| 1166 | ||
| 1167 | test "n_object_non_string_key_but_huge_number_instead" { | |
| 1168 | err( | |
| 1169 | \\{9999E9999:1} | |
| 1170 | ); | |
| 1171 | } | |
| 1172 | ||
| 1173 | test "n_object_non_string_key" { | |
| 1174 | err( | |
| 1175 | \\{1:1} | |
| 1176 | ); | |
| 1177 | } | |
| 1178 | ||
| 1179 | test "n_object_repeated_null_null" { | |
| 1180 | err( | |
| 1181 | \\{null:null,null:null} | |
| 1182 | ); | |
| 1183 | } | |
| 1184 | ||
| 1185 | test "n_object_several_trailing_commas" { | |
| 1186 | err( | |
| 1187 | \\{"id":0,,,,,} | |
| 1188 | ); | |
| 1189 | } | |
| 1190 | ||
| 1191 | test "n_object_single_quote" { | |
| 1192 | err( | |
| 1193 | \\{'a':0} | |
| 1194 | ); | |
| 1195 | } | |
| 1196 | ||
| 1197 | test "n_object_trailing_comma" { | |
| 1198 | err( | |
| 1199 | \\{"id":0,} | |
| 1200 | ); | |
| 1201 | } | |
| 1202 | ||
| 1203 | test "n_object_trailing_comment" { | |
| 1204 | err( | |
| 1205 | \\{"a":"b"}/**/ | |
| 1206 | ); | |
| 1207 | } | |
| 1208 | ||
| 1209 | test "n_object_trailing_comment_open" { | |
| 1210 | err( | |
| 1211 | \\{"a":"b"}/**// | |
| 1212 | ); | |
| 1213 | } | |
| 1214 | ||
| 1215 | test "n_object_trailing_comment_slash_open_incomplete" { | |
| 1216 | err( | |
| 1217 | \\{"a":"b"}/ | |
| 1218 | ); | |
| 1219 | } | |
| 1220 | ||
| 1221 | test "n_object_trailing_comment_slash_open" { | |
| 1222 | err( | |
| 1223 | \\{"a":"b"}// | |
| 1224 | ); | |
| 1225 | } | |
| 1226 | ||
| 1227 | test "n_object_two_commas_in_a_row" { | |
| 1228 | err( | |
| 1229 | \\{"a":"b",,"c":"d"} | |
| 1230 | ); | |
| 1231 | } | |
| 1232 | ||
| 1233 | test "n_object_unquoted_key" { | |
| 1234 | err( | |
| 1235 | \\{a: "b"} | |
| 1236 | ); | |
| 1237 | } | |
| 1238 | ||
| 1239 | test "n_object_unterminated-value" { | |
| 1240 | err( | |
| 1241 | \\{"a":"a | |
| 1242 | ); | |
| 1243 | } | |
| 1244 | ||
| 1245 | test "n_object_with_single_string" { | |
| 1246 | err( | |
| 1247 | \\{ "foo" : "bar", "a" } | |
| 1248 | ); | |
| 1249 | } | |
| 1250 | ||
| 1251 | test "n_object_with_trailing_garbage" { | |
| 1252 | err( | |
| 1253 | \\{"a":"b"}# | |
| 1254 | ); | |
| 1255 | } | |
| 1256 | ||
| 1257 | test "n_single_space" { | |
| 1258 | err( | |
| 1259 | " " | |
| 1260 | ); | |
| 1261 | } | |
| 1262 | ||
| 1263 | test "n_string_1_surrogate_then_escape" { | |
| 1264 | err( | |
| 1265 | \\["\uD800\"] | |
| 1266 | ); | |
| 1267 | } | |
| 1268 | ||
| 1269 | test "n_string_1_surrogate_then_escape_u1" { | |
| 1270 | err( | |
| 1271 | \\["\uD800\u1"] | |
| 1272 | ); | |
| 1273 | } | |
| 1274 | ||
| 1275 | test "n_string_1_surrogate_then_escape_u1x" { | |
| 1276 | err( | |
| 1277 | \\["\uD800\u1x"] | |
| 1278 | ); | |
| 1279 | } | |
| 1280 | ||
| 1281 | test "n_string_1_surrogate_then_escape_u" { | |
| 1282 | err( | |
| 1283 | \\["\uD800\u"] | |
| 1284 | ); | |
| 1285 | } | |
| 1286 | ||
| 1287 | test "n_string_accentuated_char_no_quotes" { | |
| 1288 | err( | |
| 1289 | \\[é] | |
| 1290 | ); | |
| 1291 | } | |
| 1292 | ||
| 1293 | test "n_string_backslash_00" { | |
| 1294 | err( | |
| 1295 | \\["\ "] | |
| 1296 | ); | |
| 1297 | } | |
| 1298 | ||
| 1299 | test "n_string_escaped_backslash_bad" { | |
| 1300 | err( | |
| 1301 | \\["\\\"] | |
| 1302 | ); | |
| 1303 | } | |
| 1304 | ||
| 1305 | test "n_string_escaped_ctrl_char_tab" { | |
| 1306 | err( | |
| 1307 | \\["\	"] | |
| 1308 | ); | |
| 1309 | } | |
| 1310 | ||
| 1311 | test "n_string_escaped_emoji" { | |
| 1312 | err( | |
| 1313 | \\["\ð"] | |
| 1314 | ); | |
| 1315 | } | |
| 1316 | ||
| 1317 | test "n_string_escape_x" { | |
| 1318 | err( | |
| 1319 | \\["\x00"] | |
| 1320 | ); | |
| 1321 | } | |
| 1322 | ||
| 1323 | test "n_string_incomplete_escaped_character" { | |
| 1324 | err( | |
| 1325 | \\["\u00A"] | |
| 1326 | ); | |
| 1327 | } | |
| 1328 | ||
| 1329 | test "n_string_incomplete_escape" { | |
| 1330 | err( | |
| 1331 | \\["\"] | |
| 1332 | ); | |
| 1333 | } | |
| 1334 | ||
| 1335 | test "n_string_incomplete_surrogate_escape_invalid" { | |
| 1336 | err( | |
| 1337 | \\["\uD800\uD800\x"] | |
| 1338 | ); | |
| 1339 | } | |
| 1340 | ||
| 1341 | test "n_string_incomplete_surrogate" { | |
| 1342 | err( | |
| 1343 | \\["\uD834\uDd"] | |
| 1344 | ); | |
| 1345 | } | |
| 1346 | ||
| 1347 | test "n_string_invalid_backslash_esc" { | |
| 1348 | err( | |
| 1349 | \\["\a"] | |
| 1350 | ); | |
| 1351 | } | |
| 1352 | ||
| 1353 | test "n_string_invalid_unicode_escape" { | |
| 1354 | err( | |
| 1355 | \\["\uqqqq"] | |
| 1356 | ); | |
| 1357 | } | |
| 1358 | ||
| 1359 | test "n_string_invalid_utf8_after_escape" { | |
| 1360 | err( | |
| 1361 | \\["\å"] | |
| 1362 | ); | |
| 1363 | } | |
| 1364 | ||
| 1365 | test "n_string_invalid-utf-8-in-escape" { | |
| 1366 | err( | |
| 1367 | \\["\uå"] | |
| 1368 | ); | |
| 1369 | } | |
| 1370 | ||
| 1371 | test "n_string_leading_uescaped_thinspace" { | |
| 1372 | err( | |
| 1373 | \\[\u0020"asd"] | |
| 1374 | ); | |
| 1375 | } | |
| 1376 | ||
| 1377 | test "n_string_no_quotes_with_bad_escape" { | |
| 1378 | err( | |
| 1379 | \\[\n] | |
| 1380 | ); | |
| 1381 | } | |
| 1382 | ||
| 1383 | test "n_string_single_doublequote" { | |
| 1384 | err( | |
| 1385 | \\" | |
| 1386 | ); | |
| 1387 | } | |
| 1388 | ||
| 1389 | test "n_string_single_quote" { | |
| 1390 | err( | |
| 1391 | \\['single quote'] | |
| 1392 | ); | |
| 1393 | } | |
| 1394 | ||
| 1395 | test "n_string_single_string_no_double_quotes" { | |
| 1396 | err( | |
| 1397 | \\abc | |
| 1398 | ); | |
| 1399 | } | |
| 1400 | ||
| 1401 | test "n_string_start_escape_unclosed" { | |
| 1402 | err( | |
| 1403 | \\["\ | |
| 1404 | ); | |
| 1405 | } | |
| 1406 | ||
| 1407 | test "n_string_unescaped_crtl_char" { | |
| 1408 | err( | |
| 1409 | \\["a a"] | |
| 1410 | ); | |
| 1411 | } | |
| 1412 | ||
| 1413 | test "n_string_unescaped_newline" { | |
| 1414 | err( | |
| 1415 | \\["new | |
| 1416 | \\line"] | |
| 1417 | ); | |
| 1418 | } | |
| 1419 | ||
| 1420 | test "n_string_unescaped_tab" { | |
| 1421 | err( | |
| 1422 | \\["	"] | |
| 1423 | ); | |
| 1424 | } | |
| 1425 | ||
| 1426 | test "n_string_unicode_CapitalU" { | |
| 1427 | err( | |
| 1428 | \\"\UA66D" | |
| 1429 | ); | |
| 1430 | } | |
| 1431 | ||
| 1432 | test "n_string_with_trailing_garbage" { | |
| 1433 | err( | |
| 1434 | \\""x | |
| 1435 | ); | |
| 1436 | } | |
| 1437 | ||
| 1438 | test "n_structure_100000_opening_arrays" { | |
| 1439 | err( | |
| 1440 | "[" ** 100000 | |
| 1441 | ); | |
| 1442 | } | |
| 1443 | ||
| 1444 | test "n_structure_angle_bracket_." { | |
| 1445 | err( | |
| 1446 | \\<.> | |
| 1447 | ); | |
| 1448 | } | |
| 1449 | ||
| 1450 | test "n_structure_angle_bracket_null" { | |
| 1451 | err( | |
| 1452 | \\[<null>] | |
| 1453 | ); | |
| 1454 | } | |
| 1455 | ||
| 1456 | test "n_structure_array_trailing_garbage" { | |
| 1457 | err( | |
| 1458 | \\[1]x | |
| 1459 | ); | |
| 1460 | } | |
| 1461 | ||
| 1462 | test "n_structure_array_with_extra_array_close" { | |
| 1463 | err( | |
| 1464 | \\[1]] | |
| 1465 | ); | |
| 1466 | } | |
| 1467 | ||
| 1468 | test "n_structure_array_with_unclosed_string" { | |
| 1469 | err( | |
| 1470 | \\["asd] | |
| 1471 | ); | |
| 1472 | } | |
| 1473 | ||
| 1474 | test "n_structure_ascii-unicode-identifier" { | |
| 1475 | err( | |
| 1476 | \\aå | |
| 1477 | ); | |
| 1478 | } | |
| 1479 | ||
| 1480 | test "n_structure_capitalized_True" { | |
| 1481 | err( | |
| 1482 | \\[True] | |
| 1483 | ); | |
| 1484 | } | |
| 1485 | ||
| 1486 | test "n_structure_close_unopened_array" { | |
| 1487 | err( | |
| 1488 | \\1] | |
| 1489 | ); | |
| 1490 | } | |
| 1491 | ||
| 1492 | test "n_structure_comma_instead_of_closing_brace" { | |
| 1493 | err( | |
| 1494 | \\{"x": true, | |
| 1495 | ); | |
| 1496 | } | |
| 1497 | ||
| 1498 | test "n_structure_double_array" { | |
| 1499 | err( | |
| 1500 | \\[][] | |
| 1501 | ); | |
| 1502 | } | |
| 1503 | ||
| 1504 | test "n_structure_end_array" { | |
| 1505 | err( | |
| 1506 | \\] | |
| 1507 | ); | |
| 1508 | } | |
| 1509 | ||
| 1510 | test "n_structure_incomplete_UTF8_BOM" { | |
| 1511 | err( | |
| 1512 | \\ï»{} | |
| 1513 | ); | |
| 1514 | } | |
| 1515 | ||
| 1516 | test "n_structure_lone-invalid-utf-8" { | |
| 1517 | err( | |
| 1518 | \\å | |
| 1519 | ); | |
| 1520 | } | |
| 1521 | ||
| 1522 | test "n_structure_lone-open-bracket" { | |
| 1523 | err( | |
| 1524 | \\[ | |
| 1525 | ); | |
| 1526 | } | |
| 1527 | ||
| 1528 | test "n_structure_no_data" { | |
| 1529 | err( | |
| 1530 | \\ | |
| 1531 | ); | |
| 1532 | } | |
| 1533 | ||
| 1534 | test "n_structure_null-byte-outside-string" { | |
| 1535 | err( | |
| 1536 | \\[ ] | |
| 1537 | ); | |
| 1538 | } | |
| 1539 | ||
| 1540 | test "n_structure_number_with_trailing_garbage" { | |
| 1541 | err( | |
| 1542 | \\2@ | |
| 1543 | ); | |
| 1544 | } | |
| 1545 | ||
| 1546 | test "n_structure_object_followed_by_closing_object" { | |
| 1547 | err( | |
| 1548 | \\{}} | |
| 1549 | ); | |
| 1550 | } | |
| 1551 | ||
| 1552 | test "n_structure_object_unclosed_no_value" { | |
| 1553 | err( | |
| 1554 | \\{"": | |
| 1555 | ); | |
| 1556 | } | |
| 1557 | ||
| 1558 | test "n_structure_object_with_comment" { | |
| 1559 | err( | |
| 1560 | \\{"a":/*comment*/"b"} | |
| 1561 | ); | |
| 1562 | } | |
| 1563 | ||
| 1564 | test "n_structure_object_with_trailing_garbage" { | |
| 1565 | err( | |
| 1566 | \\{"a": true} "x" | |
| 1567 | ); | |
| 1568 | } | |
| 1569 | ||
| 1570 | test "n_structure_open_array_apostrophe" { | |
| 1571 | err( | |
| 1572 | \\[' | |
| 1573 | ); | |
| 1574 | } | |
| 1575 | ||
| 1576 | test "n_structure_open_array_comma" { | |
| 1577 | err( | |
| 1578 | \\[, | |
| 1579 | ); | |
| 1580 | } | |
| 1581 | ||
| 1582 | test "n_structure_open_array_object" { | |
| 1583 | err( | |
| 1584 | "[{\"\":" ** 50000 | |
| 1585 | ); | |
| 1586 | } | |
| 1587 | ||
| 1588 | test "n_structure_open_array_open_object" { | |
| 1589 | err( | |
| 1590 | \\[{ | |
| 1591 | ); | |
| 1592 | } | |
| 1593 | ||
| 1594 | test "n_structure_open_array_open_string" { | |
| 1595 | err( | |
| 1596 | \\["a | |
| 1597 | ); | |
| 1598 | } | |
| 1599 | ||
| 1600 | test "n_structure_open_array_string" { | |
| 1601 | err( | |
| 1602 | \\["a" | |
| 1603 | ); | |
| 1604 | } | |
| 1605 | ||
| 1606 | test "n_structure_open_object_close_array" { | |
| 1607 | err( | |
| 1608 | \\{] | |
| 1609 | ); | |
| 1610 | } | |
| 1611 | ||
| 1612 | test "n_structure_open_object_comma" { | |
| 1613 | err( | |
| 1614 | \\{, | |
| 1615 | ); | |
| 1616 | } | |
| 1617 | ||
| 1618 | test "n_structure_open_object" { | |
| 1619 | err( | |
| 1620 | \\{ | |
| 1621 | ); | |
| 1622 | } | |
| 1623 | ||
| 1624 | test "n_structure_open_object_open_array" { | |
| 1625 | err( | |
| 1626 | \\{[ | |
| 1627 | ); | |
| 1628 | } | |
| 1629 | ||
| 1630 | test "n_structure_open_object_open_string" { | |
| 1631 | err( | |
| 1632 | \\{"a | |
| 1633 | ); | |
| 1634 | } | |
| 1635 | ||
| 1636 | test "n_structure_open_object_string_with_apostrophes" { | |
| 1637 | err( | |
| 1638 | \\{'a' | |
| 1639 | ); | |
| 1640 | } | |
| 1641 | ||
| 1642 | test "n_structure_open_open" { | |
| 1643 | err( | |
| 1644 | \\["\{["\{["\{["\{ | |
| 1645 | ); | |
| 1646 | } | |
| 1647 | ||
| 1648 | test "n_structure_single_eacute" { | |
| 1649 | err( | |
| 1650 | \\é | |
| 1651 | ); | |
| 1652 | } | |
| 1653 | ||
| 1654 | test "n_structure_single_star" { | |
| 1655 | err( | |
| 1656 | \\* | |
| 1657 | ); | |
| 1658 | } | |
| 1659 | ||
| 1660 | test "n_structure_trailing_#" { | |
| 1661 | err( | |
| 1662 | \\{"a":"b"}#{} | |
| 1663 | ); | |
| 1664 | } | |
| 1665 | ||
| 1666 | test "n_structure_U+2060_word_joined" { | |
| 1667 | err( | |
| 1668 | \\[â ] | |
| 1669 | ); | |
| 1670 | } | |
| 1671 | ||
| 1672 | test "n_structure_uescaped_LF_before_string" { | |
| 1673 | err( | |
| 1674 | \\[\u000A""] | |
| 1675 | ); | |
| 1676 | } | |
| 1677 | ||
| 1678 | test "n_structure_unclosed_array" { | |
| 1679 | err( | |
| 1680 | \\[1 | |
| 1681 | ); | |
| 1682 | } | |
| 1683 | ||
| 1684 | test "n_structure_unclosed_array_partial_null" { | |
| 1685 | err( | |
| 1686 | \\[ false, nul | |
| 1687 | ); | |
| 1688 | } | |
| 1689 | ||
| 1690 | test "n_structure_unclosed_array_unfinished_false" { | |
| 1691 | err( | |
| 1692 | \\[ true, fals | |
| 1693 | ); | |
| 1694 | } | |
| 1695 | ||
| 1696 | test "n_structure_unclosed_array_unfinished_true" { | |
| 1697 | err( | |
| 1698 | \\[ false, tru | |
| 1699 | ); | |
| 1700 | } | |
| 1701 | ||
| 1702 | test "n_structure_unclosed_object" { | |
| 1703 | err( | |
| 1704 | \\{"asd":"asd" | |
| 1705 | ); | |
| 1706 | } | |
| 1707 | ||
| 1708 | test "n_structure_unicode-identifier" { | |
| 1709 | err( | |
| 1710 | \\Ã¥ | |
| 1711 | ); | |
| 1712 | } | |
| 1713 | ||
| 1714 | test "n_structure_UTF8_BOM_no_data" { | |
| 1715 | err( | |
| 1716 | \\ | |
| 1717 | ); | |
| 1718 | } | |
| 1719 | ||
| 1720 | test "n_structure_whitespace_formfeed" { | |
| 1721 | err( | |
| 1722 | \\[] | |
| 1723 | ); | |
| 1724 | } | |
| 1725 | ||
| 1726 | test "n_structure_whitespace_U+2060_word_joiner" { | |
| 1727 | err( | |
| 1728 | \\[â ] | |
| 1729 | ); | |
| 1730 | } | |
| 1731 | ||
| 1732 | //////////////////////////////////////////////////////////////////////////////////////////////////// | |
| 1733 | ||
| 1734 | test "i_number_double_huge_neg_exp" { | |
| 1735 | any( | |
| 1736 | \\[123.456e-789] | |
| 1737 | ); | |
| 1738 | } | |
| 1739 | ||
| 1740 | test "i_number_huge_exp" { | |
| 1741 | any( | |
| 1742 | \\[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006] | |
| 1743 | ); | |
| 1744 | } | |
| 1745 | ||
| 1746 | test "i_number_neg_int_huge_exp" { | |
| 1747 | any( | |
| 1748 | \\[-1e+9999] | |
| 1749 | ); | |
| 1750 | } | |
| 1751 | ||
| 1752 | test "i_number_pos_double_huge_exp" { | |
| 1753 | any( | |
| 1754 | \\[1.5e+9999] | |
| 1755 | ); | |
| 1756 | } | |
| 1757 | ||
| 1758 | test "i_number_real_neg_overflow" { | |
| 1759 | any( | |
| 1760 | \\[-123123e100000] | |
| 1761 | ); | |
| 1762 | } | |
| 1763 | ||
| 1764 | test "i_number_real_pos_overflow" { | |
| 1765 | any( | |
| 1766 | \\[123123e100000] | |
| 1767 | ); | |
| 1768 | } | |
| 1769 | ||
| 1770 | test "i_number_real_underflow" { | |
| 1771 | any( | |
| 1772 | \\[123e-10000000] | |
| 1773 | ); | |
| 1774 | } | |
| 1775 | ||
| 1776 | test "i_number_too_big_neg_int" { | |
| 1777 | any( | |
| 1778 | \\[-123123123123123123123123123123] | |
| 1779 | ); | |
| 1780 | } | |
| 1781 | ||
| 1782 | test "i_number_too_big_pos_int" { | |
| 1783 | any( | |
| 1784 | \\[100000000000000000000] | |
| 1785 | ); | |
| 1786 | } | |
| 1787 | ||
| 1788 | test "i_number_very_big_negative_int" { | |
| 1789 | any( | |
| 1790 | \\[-237462374673276894279832749832423479823246327846] | |
| 1791 | ); | |
| 1792 | } | |
| 1793 | ||
| 1794 | test "i_object_key_lone_2nd_surrogate" { | |
| 1795 | any( | |
| 1796 | \\{"\uDFAA":0} | |
| 1797 | ); | |
| 1798 | } | |
| 1799 | ||
| 1800 | test "i_string_1st_surrogate_but_2nd_missing" { | |
| 1801 | any( | |
| 1802 | \\["\uDADA"] | |
| 1803 | ); | |
| 1804 | } | |
| 1805 | ||
| 1806 | test "i_string_1st_valid_surrogate_2nd_invalid" { | |
| 1807 | any( | |
| 1808 | \\["\uD888\u1234"] | |
| 1809 | ); | |
| 1810 | } | |
| 1811 | ||
| 1812 | test "i_string_incomplete_surrogate_and_escape_valid" { | |
| 1813 | any( | |
| 1814 | \\["\uD800\n"] | |
| 1815 | ); | |
| 1816 | } | |
| 1817 | ||
| 1818 | test "i_string_incomplete_surrogate_pair" { | |
| 1819 | any( | |
| 1820 | \\["\uDd1ea"] | |
| 1821 | ); | |
| 1822 | } | |
| 1823 | ||
| 1824 | test "i_string_incomplete_surrogates_escape_valid" { | |
| 1825 | any( | |
| 1826 | \\["\uD800\uD800\n"] | |
| 1827 | ); | |
| 1828 | } | |
| 1829 | ||
| 1830 | test "i_string_invalid_lonely_surrogate" { | |
| 1831 | any( | |
| 1832 | \\["\ud800"] | |
| 1833 | ); | |
| 1834 | } | |
| 1835 | ||
| 1836 | test "i_string_invalid_surrogate" { | |
| 1837 | any( | |
| 1838 | \\["\ud800abc"] | |
| 1839 | ); | |
| 1840 | } | |
| 1841 | ||
| 1842 | test "i_string_invalid_utf-8" { | |
| 1843 | any( | |
| 1844 | \\["ÿ"] | |
| 1845 | ); | |
| 1846 | } | |
| 1847 | ||
| 1848 | test "i_string_inverted_surrogates_U+1D11E" { | |
| 1849 | any( | |
| 1850 | \\["\uDd1e\uD834"] | |
| 1851 | ); | |
| 1852 | } | |
| 1853 | ||
| 1854 | test "i_string_iso_latin_1" { | |
| 1855 | any( | |
| 1856 | \\["é"] | |
| 1857 | ); | |
| 1858 | } | |
| 1859 | ||
| 1860 | test "i_string_lone_second_surrogate" { | |
| 1861 | any( | |
| 1862 | \\["\uDFAA"] | |
| 1863 | ); | |
| 1864 | } | |
| 1865 | ||
| 1866 | test "i_string_lone_utf8_continuation_byte" { | |
| 1867 | any( | |
| 1868 | \\[""] | |
| 1869 | ); | |
| 1870 | } | |
| 1871 | ||
| 1872 | test "i_string_not_in_unicode_range" { | |
| 1873 | any( | |
| 1874 | \\["ô¿¿¿"] | |
| 1875 | ); | |
| 1876 | } | |
| 1877 | ||
| 1878 | test "i_string_overlong_sequence_2_bytes" { | |
| 1879 | any( | |
| 1880 | \\["À¯"] | |
| 1881 | ); | |
| 1882 | } | |
| 1883 | ||
| 1884 | test "i_string_overlong_sequence_6_bytes" { | |
| 1885 | any( | |
| 1886 | \\["ü¿¿¿¿"] | |
| 1887 | ); | |
| 1888 | } | |
| 1889 | ||
| 1890 | test "i_string_overlong_sequence_6_bytes_null" { | |
| 1891 | any( | |
| 1892 | \\["ü"] | |
| 1893 | ); | |
| 1894 | } | |
| 1895 | ||
| 1896 | test "i_string_truncated-utf-8" { | |
| 1897 | any( | |
| 1898 | \\["àÿ"] | |
| 1899 | ); | |
| 1900 | } | |
| 1901 | ||
| 1902 | test "i_string_utf16BE_no_BOM" { | |
| 1903 | any( | |
| 1904 | \\ [ " é |