authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-26 10:55:29-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-26 10:55:29-04:00
logbbfff4614614726dd2ef2c05f851f91be5368f84
tree9cbfbb8b744c883150312bc9adf068778c60397e
parentce68dda4b60914e947088e7cf0c5626ea7cebc08
parent27cbb44993389ae042a03266743379c0f15a523e

Merge remote-tracking branch 'origin/master' into llvm7


89 files changed, 9577 insertions(+), 4971 deletions(-)

.travis.yml+13-9
...@@ -1,18 +1,22 @@...@@ -1,18 +1,22 @@
1sudo: required1sudo: required
2services:2services:
3 - docker3- docker
4os:4os:
5 - linux5- linux
6 - osx6- osx
7dist: trusty7dist: trusty
8osx_image: xcode8.38osx_image: xcode8.3
9language: cpp9language: cpp
10before_install:10before_install:
11 - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_before_install; fi11- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_before_install; fi
12 - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_before_install; fi12- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_before_install; fi
13install:13install:
14 - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_install; fi14- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_install; fi
15 - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_install; fi15- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_install; fi
16script:16script:
17 - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_script; fi17- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ci/travis_linux_script; fi
18 - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_script; fi18- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then ci/travis_osx_script; fi
19env:
20 global:
21 - secure: QmJ+eLOxj3Irl5SHxt6lQvrj7++1AIz8bYri6RScAQGHQPIztkmbpBjAkpFgYaWPkZ04ROtamFXdS7oHtJHSECesgPoqM/CHIychQkgpDq30+TsFyYbBpDGHY+N6r2WnQTvg+9EuAp6P365us6qFS0D5zQ3P40c56uMbazFu3J4W1HZP+pLWlLjEXaN88ePhHWqNZyvwGMkLpYl3ghcrE9H4vGZQ7jenRW4UmskLEkuhUPJbQiow3Td8arJiRmLVISzWqneqNraLUpGyUVr4F3Rbjzacfoo3r9ZZynhY0mFsEye82x6TMGgH2xsNGkd91zpQuckWUT+pQv/G6FXpnEnjIJSO2Z5WAxXrx6xB1k2HZ17/4NWLF3fJVhdQJm3mS6odeGzUjgGrl1A42evxU+7VbcofEJq1aMiLgU1jUT2pt+pefCwmKJYLpEsSzuyrVxgvskQz0QpC053TAYSNf2Jj6Qhg9YDWyOeemYmDgffTqErF7AYhc6NKH0s0XKkIiNFSxorkEsfG/Ck1o+15slHNmWZXlmXToxDqFkLDoPvfGKg7koU5YTGvci/F9ZKb1juhGLxZbwap/18zN40BqA+Ip2yDBJAKxsIiwSjSIguy6g/Z1I50s0xNGOr36urfRRQX5H+rqr/xCZ63B6WSe6qBcZboWAQMDn8HLS9Xiwc=
22 - secure: dnb7r5guUeMOX9e7XlPUSZzmga8VW3G9Q1aa7LxEKiTjSnWhu5KpPDe8o1X3Rj6nc5iXDqmBH/C/7eNXPDyXJJWPvpE2YRpGymyUkRaakul0QBKJEaMvwy2SuAfS69CWC+TSzfGRvtSYkdpBhhLvs0h5S819S5jYbCNSCmOKfFucaP5NsHNIZ/I19oIeTPTa0/UnVm7DLFZXZjvbS+czkdyH1DhbT85sLj+XqNTzLePImE68efrjaHnlSy/CzBVJzj55UgD5i9fxNCQWzGWim/SD5xZ0zKtLycSOf6wQN2lCo0lkjw9rDlYz69mM5L9ikfYL9oHDPZnh84oXKglQ5miOHCgqs/qs4439I05lIu8i/EfbFA55YG4NyO3rL9YVOOt5gwiwvJYhDcnkVVzSl0o5bsoZgQfYvPWaIQKNkl3C53zfDQjgqS54CeDzlZpFrQTDQ1RrH8oeVC1gfYAeMabMDadox5rfZmLIN5JTf/F8iD/QdxGcoUvkEENcQgfP9PnubExtexgHGsEmqbm6ORSZ1MkEh2m3fo0f8KE6TbN1UigmcQ8nTkWBHsSmfHnB8HwJQp8mwQmDamXA+Hl3e3w4LOdYkJVlNW1/TTyJJOOvjMQCjF8SJmPHuh+QpqKbSaT9XM/vBhxbIZEufH8kawJKCBBcCNspGMNjhXfNjM0=
CMakeLists.txt+26-1
...@@ -498,7 +498,28 @@ set(ZIG_STD_FILES...@@ -498,7 +498,28 @@ set(ZIG_STD_FILES
498 "math/tan.zig"498 "math/tan.zig"
499 "math/tanh.zig"499 "math/tanh.zig"
500 "math/trunc.zig"500 "math/trunc.zig"
501 "math/x86_64/sqrt.zig"501 "math/complex/abs.zig"
502 "math/complex/acosh.zig"
503 "math/complex/acos.zig"
504 "math/complex/arg.zig"
505 "math/complex/asinh.zig"
506 "math/complex/asin.zig"
507 "math/complex/atanh.zig"
508 "math/complex/atan.zig"
509 "math/complex/conj.zig"
510 "math/complex/cosh.zig"
511 "math/complex/cos.zig"
512 "math/complex/exp.zig"
513 "math/complex/index.zig"
514 "math/complex/ldexp.zig"
515 "math/complex/log.zig"
516 "math/complex/pow.zig"
517 "math/complex/proj.zig"
518 "math/complex/sinh.zig"
519 "math/complex/sin.zig"
520 "math/complex/sqrt.zig"
521 "math/complex/tanh.zig"
522 "math/complex/tan.zig"
502 "mem.zig"523 "mem.zig"
503 "net.zig"524 "net.zig"
504 "os/child_process.zig"525 "os/child_process.zig"
...@@ -509,13 +530,17 @@ set(ZIG_STD_FILES...@@ -509,13 +530,17 @@ set(ZIG_STD_FILES
509 "os/index.zig"530 "os/index.zig"
510 "os/linux/errno.zig"531 "os/linux/errno.zig"
511 "os/linux/index.zig"532 "os/linux/index.zig"
533 "os/linux/vdso.zig"
512 "os/linux/x86_64.zig"534 "os/linux/x86_64.zig"
513 "os/path.zig"535 "os/path.zig"
536 "os/time.zig"
537 "os/epoch.zig"
514 "os/windows/error.zig"538 "os/windows/error.zig"
515 "os/windows/index.zig"539 "os/windows/index.zig"
516 "os/windows/util.zig"540 "os/windows/util.zig"
517 "os/zen.zig"541 "os/zen.zig"
518 "rand/index.zig"542 "rand/index.zig"
543 "rand/ziggurat.zig"
519 "sort.zig"544 "sort.zig"
520 "special/bootstrap.zig"545 "special/bootstrap.zig"
521 "special/bootstrap_lib.zig"546 "special/bootstrap_lib.zig"
ci/travis_linux_script+1-1
...@@ -19,5 +19,5 @@ if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then...@@ -19,5 +19,5 @@ if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then
19 echo "secret_key = $AWS_SECRET_ACCESS_KEY" >> ~/.s3cfg19 echo "secret_key = $AWS_SECRET_ACCESS_KEY" >> ~/.s3cfg
20 s3cmd put -P $TRAVIS_BUILD_DIR/artifacts/* s3://ziglang.org/builds/20 s3cmd put -P $TRAVIS_BUILD_DIR/artifacts/* s3://ziglang.org/builds/
21 touch empty21 touch empty
22 s3cmd put -P empty s3://ziglang.org/builds/zig-linux-x86_64-$TRAVIS_BRANCH.tar.xz --add-header=x-amz-website-redirect-location:/builds/$(ls $TRAVIS_BUILD_DIR/artifacts)22 s3cmd put -P empty s3://ziglang.org/builds/zig-linux-x86_64-$TRAVIS_BRANCH.tar.xz --add-header="Cache-Control: max-age=0, must-revalidate" --add-header=x-amz-website-redirect-location:/builds/$(ls $TRAVIS_BUILD_DIR/artifacts)
23fi23fi
deps/lld/ELF/MarkLive.cpp+9
...@@ -301,6 +301,15 @@ template <class ELFT> void elf::markLive() {...@@ -301,6 +301,15 @@ template <class ELFT> void elf::markLive() {
301 // Follow the graph to mark all live sections.301 // Follow the graph to mark all live sections.
302 doGcSections<ELFT>();302 doGcSections<ELFT>();
303303
304 // If all references to a DSO happen to be weak, the DSO is removed from
305 // DT_NEEDED, which creates dangling shared symbols to non-existent DSO.
306 // We'll replace such symbols with undefined ones to fix it.
307 for (Symbol *Sym : Symtab->getSymbols())
308 if (auto *S = dyn_cast<SharedSymbol>(Sym))
309 if (S->isWeak() && !S->getFile<ELFT>().IsNeeded)
310 replaceSymbol<Undefined>(S, nullptr, S->getName(), STB_WEAK, S->StOther,
311 S->Type);
312
304 // Report garbage-collected sections.313 // Report garbage-collected sections.
305 if (Config->PrintGcSections)314 if (Config->PrintGcSections)
306 for (InputSectionBase *Sec : InputSections)315 for (InputSectionBase *Sec : InputSections)
doc/docgen.zig+19
...@@ -749,6 +749,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var...@@ -749,6 +749,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
749 try build_args.append("--release-fast");749 try build_args.append("--release-fast");
750 try out.print(" --release-fast");750 try out.print(" --release-fast");
751 },751 },
752 builtin.Mode.ReleaseSmall => {
753 try build_args.append("--release-small");
754 try out.print(" --release-small");
755 },
752 }756 }
753 for (code.link_objects) |link_object| {757 for (code.link_objects) |link_object| {
754 const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", link_object, obj_ext);758 const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", link_object, obj_ext);
...@@ -810,6 +814,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var...@@ -810,6 +814,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
810 try test_args.append("--release-fast");814 try test_args.append("--release-fast");
811 try out.print(" --release-fast");815 try out.print(" --release-fast");
812 },816 },
817 builtin.Mode.ReleaseSmall => {
818 try test_args.append("--release-small");
819 try out.print(" --release-small");
820 },
813 }821 }
814 if (code.target_windows) {822 if (code.target_windows) {
815 try test_args.appendSlice([][]const u8{823 try test_args.appendSlice([][]const u8{
...@@ -840,6 +848,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var...@@ -840,6 +848,10 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
840 try test_args.append("--release-fast");848 try test_args.append("--release-fast");
841 try out.print(" --release-fast");849 try out.print(" --release-fast");
842 },850 },
851 builtin.Mode.ReleaseSmall => {
852 try test_args.append("--release-small");
853 try out.print(" --release-small");
854 },
843 }855 }
844 const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size);856 const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size);
845 switch (result.term) {857 switch (result.term) {
...@@ -874,6 +886,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var...@@ -874,6 +886,7 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
874 builtin.Mode.Debug => {},886 builtin.Mode.Debug => {},
875 builtin.Mode.ReleaseSafe => try test_args.append("--release-safe"),887 builtin.Mode.ReleaseSafe => try test_args.append("--release-safe"),
876 builtin.Mode.ReleaseFast => try test_args.append("--release-fast"),888 builtin.Mode.ReleaseFast => try test_args.append("--release-fast"),
889 builtin.Mode.ReleaseSmall => try test_args.append("--release-small"),
877 }890 }
878891
879 const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size);892 const result = try os.ChildProcess.exec(allocator, test_args.toSliceConst(), null, null, max_doc_file_size);
...@@ -927,6 +940,12 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var...@@ -927,6 +940,12 @@ fn genHtml(allocator: &mem.Allocator, tokenizer: &Tokenizer, toc: &Toc, out: var
927 try out.print(" --release-fast");940 try out.print(" --release-fast");
928 }941 }
929 },942 },
943 builtin.Mode.ReleaseSmall => {
944 try build_args.append("--release-small");
945 if (!code.is_inline) {
946 try out.print(" --release-small");
947 }
948 },
930 }949 }
931950
932 if (maybe_error_match) |error_match| {951 if (maybe_error_match) |error_match| {
doc/langref.html.in+83-6
...@@ -3880,6 +3880,25 @@ pub fn main() void {...@@ -3880,6 +3880,25 @@ pub fn main() void {
3880 {#header_open|@ArgType#}3880 {#header_open|@ArgType#}
3881 <p>TODO</p>3881 <p>TODO</p>
3882 {#header_close#}3882 {#header_close#}
3883 {#header_open|@atomicLoad#}
3884 <pre><code class="zig">@atomicLoad(comptime T: type, ptr: &amp;const T, comptime ordering: builtin.AtomicOrder) -&gt; T</code></pre>
3885 <p>
3886 This builtin function atomically dereferences a pointer and returns the value.
3887 </p>
3888 <p>
3889 <code>T</code> must be a pointer type, a <code>bool</code>,
3890 or an integer whose bit count meets these requirements:
3891 </p>
3892 <ul>
3893 <li>At least 8</li>
3894 <li>At most the same as usize</li>
3895 <li>Power of 2</li>
3896 </ul>
3897 <p>
3898 TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe
3899 we can remove this restriction
3900 </p>
3901 {#header_close#}
3883 {#header_open|@atomicRmw#}3902 {#header_open|@atomicRmw#}
3884 <pre><code class="zig">@atomicRmw(comptime T: type, ptr: &amp;T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) -&gt; T</code></pre>3903 <pre><code class="zig">@atomicRmw(comptime T: type, ptr: &amp;T, comptime op: builtin.AtomicRmwOp, operand: T, comptime ordering: builtin.AtomicOrder) -&gt; T</code></pre>
3885 <p>3904 <p>
...@@ -4046,16 +4065,60 @@ comptime {...@@ -4046,16 +4065,60 @@ comptime {
4046 </p>4065 </p>
40474066
4048 {#header_close#}4067 {#header_close#}
4049 {#header_open|@cmpxchg#}4068 {#header_open|@cmpxchgStrong#}
4050 <pre><code class="zig">@cmpxchg(ptr: &T, cmp: T, new: T, success_order: AtomicOrder, fail_order: AtomicOrder) -&gt; bool</code></pre>4069 <pre><code class="zig">@cmpxchgStrong(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -&gt; ?T</code></pre>
4070 <p>
4071 This function performs a strong atomic compare exchange operation. It's the equivalent of this code,
4072 except atomic:
4073 </p>
4074 {#code_begin|syntax#}
4075fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T {
4076 const old_value = *ptr;
4077 if (old_value == expected_value) {
4078 *ptr = new_value;
4079 return null;
4080 } else {
4081 return old_value;
4082 }
4083}
4084 {#code_end#}
4085 <p>
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.
4088 </p>
4089 <p>
4090 <code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>.
4091 </p>
4092 <p><code>@typeOf(ptr).alignment</code> must be <code>&gt;= @sizeOf(T).</code></p>
4093 {#see_also|Compile Variables|cmpxchgWeak#}
4094 {#header_close#}
4095 {#header_open|@cmpxchgWeak#}
4096 <pre><code class="zig">@cmpxchgWeak(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -&gt; ?T</code></pre>
4097 <p>
4098 This function performs a weak atomic compare exchange operation. It's the equivalent of this code,
4099 except atomic:
4100 </p>
4101 {#code_begin|syntax#}
4102fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T {
4103 const old_value = *ptr;
4104 if (old_value == expected_value and usuallyTrueButSometimesFalse()) {
4105 *ptr = new_value;
4106 return null;
4107 } else {
4108 return old_value;
4109 }
4110}
4111 {#code_end#}
4051 <p>4112 <p>
4052 This function performs an atomic compare exchange operation.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#}.
4053 </p>4116 </p>
4054 <p>4117 <p>
4055 <code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>.4118 <code>AtomicOrder</code> can be found with <code>@import("builtin").AtomicOrder</code>.
4056 </p>4119 </p>
4057 <p><code>@typeOf(ptr).alignment</code> must be <code>&gt;= @sizeOf(T).</code></p>4120 <p><code>@typeOf(ptr).alignment</code> must be <code>&gt;= @sizeOf(T).</code></p>
4058 {#see_also|Compile Variables#}4121 {#see_also|Compile Variables|cmpxchgStrong#}
4059 {#header_close#}4122 {#header_close#}
4060 {#header_open|@compileError#}4123 {#header_open|@compileError#}
4061 <pre><code class="zig">@compileError(comptime msg: []u8)</code></pre>4124 <pre><code class="zig">@compileError(comptime msg: []u8)</code></pre>
...@@ -4349,6 +4412,10 @@ fn add(a: i32, b: i32) i32 { return a + b; }...@@ -4349,6 +4412,10 @@ fn add(a: i32, b: i32) i32 { return a + b; }
4349 It does not include functions, variables, or constants.4412 It does not include functions, variables, or constants.
4350 </p>4413 </p>
4351 {#header_close#}4414 {#header_close#}
4415 {#header_open|@field#}
4416 <pre><code class="zig">@field(lhs: var, comptime field_name: []const u8) -&gt; (field)</code></pre>
4417 <p>Preforms field access equivalent to <code>lhs.-&gtfield_name-&lt</code>.</p>
4418 {#header_close#}
4352 {#header_open|@memberType#}4419 {#header_open|@memberType#}
4353 <pre><code class="zig">@memberType(comptime T: type, comptime index: usize) -&gt; type</code></pre>4420 <pre><code class="zig">@memberType(comptime T: type, comptime index: usize) -&gt; type</code></pre>
4354 <p>Returns the field type of a struct or union.</p>4421 <p>Returns the field type of a struct or union.</p>
...@@ -4669,6 +4736,16 @@ pub const FloatMode = enum {...@@ -4669,6 +4736,16 @@ pub const FloatMode = enum {
4669 The result is a target-specific compile time constant.4736 The result is a target-specific compile time constant.
4670 </p>4737 </p>
4671 {#header_close#}4738 {#header_close#}
4739 {#header_open|@sqrt#}
4740 <pre><code class="zig">@sqrt(comptime T: type, value: T) -&gt; T</code></pre>
4741 <p>
4742 Performs the square root of a floating point number. Uses a dedicated hardware instruction
4743 when available. Currently only supports f32 and f64 at runtime. f128 at runtime is TODO.
4744 </p>
4745 <p>
4746 This is a low-level intrinsic. Most code can use <code>std.math.sqrt</code> instead.
4747 </p>
4748 {#header_close#}
4672 {#header_open|@subWithOverflow#}4749 {#header_open|@subWithOverflow#}
4673 <pre><code class="zig">@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -&gt; bool</code></pre>4750 <pre><code class="zig">@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -&gt; bool</code></pre>
4674 <p>4751 <p>
...@@ -5845,7 +5922,7 @@ Defer(body) = ("defer" | "deferror") body...@@ -5845,7 +5922,7 @@ Defer(body) = ("defer" | "deferror") body
58455922
5846IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))5923IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
58475924
5848SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))5925SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))
58495926
5850IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)5927IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)
58515928
...@@ -5991,7 +6068,7 @@ hljs.registerLanguage("zig", function(t) {...@@ -5991,7 +6068,7 @@ hljs.registerLanguage("zig", function(t) {
5991 a = t.IR + "\\s*\\(",6068 a = t.IR + "\\s*\\(",
5992 c = {6069 c = {
5993 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",6070 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",
5994 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",6071 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",
5995 literal: "true false null undefined"6072 literal: "true false null undefined"
5996 },6073 },
5997 n = [e, t.CLCM, t.CBCM, s, r];6074 n = [e, t.CLCM, t.CBCM, s, r];
src-self-hosted/arg.zig created+284
...@@ -0,0 +1,284 @@
1const std = @import("std");
2const debug = std.debug;
3const mem = std.mem;
4
5const Allocator = mem.Allocator;
6const ArrayList = std.ArrayList;
7const HashMap = std.HashMap;
8
9fn trimStart(slice: []const u8, ch: u8) []const u8 {
10 var i: usize = 0;
11 for (slice) |b| {
12 if (b != '-') break;
13 i += 1;
14 }
15
16 return slice[i..];
17}
18
19fn argInAllowedSet(maybe_set: ?[]const []const u8, arg: []const u8) bool {
20 if (maybe_set) |set| {
21 for (set) |possible| {
22 if (mem.eql(u8, arg, possible)) {
23 return true;
24 }
25 }
26 return false;
27 } else {
28 return true;
29 }
30}
31
32// Modifies the current argument index during iteration
33fn readFlagArguments(allocator: &Allocator, args: []const []const u8, required: usize,
34 allowed_set: ?[]const []const u8, index: &usize) !FlagArg {
35
36 switch (required) {
37 0 => return FlagArg { .None = undefined }, // TODO: Required to force non-tag but value?
38 1 => {
39 if (*index + 1 >= args.len) {
40 return error.MissingFlagArguments;
41 }
42
43 *index += 1;
44 const arg = args[*index];
45
46 if (!argInAllowedSet(allowed_set, arg)) {
47 return error.ArgumentNotInAllowedSet;
48 }
49
50 return FlagArg { .Single = arg };
51 },
52 else => |needed| {
53 var extra = ArrayList([]const u8).init(allocator);
54 errdefer extra.deinit();
55
56 var j: usize = 0;
57 while (j < needed) : (j += 1) {
58 if (*index + 1 >= args.len) {
59 return error.MissingFlagArguments;
60 }
61
62 *index += 1;
63 const arg = args[*index];
64
65 if (!argInAllowedSet(allowed_set, arg)) {
66 return error.ArgumentNotInAllowedSet;
67 }
68
69 try extra.append(arg);
70 }
71
72 return FlagArg { .Many = extra };
73 },
74 }
75}
76
77const HashMapFlags = HashMap([]const u8, FlagArg, std.hash.Fnv1a_32.hash, mem.eql_slice_u8);
78
79// A store for querying found flags and positional arguments.
80pub const Args = struct {
81 flags: HashMapFlags,
82 positionals: ArrayList([]const u8),
83
84 pub fn parse(allocator: &Allocator, comptime spec: []const Flag, args: []const []const u8) !Args {
85 var parsed = Args {
86 .flags = HashMapFlags.init(allocator),
87 .positionals = ArrayList([]const u8).init(allocator),
88 };
89
90 var i: usize = 0;
91 next: while (i < args.len) : (i += 1) {
92 const arg = args[i];
93
94 if (arg.len != 0 and arg[0] == '-') {
95 // TODO: hashmap, although the linear scan is okay for small argument sets as is
96 for (spec) |flag| {
97 if (mem.eql(u8, arg, flag.name)) {
98 const flag_name_trimmed = trimStart(flag.name, '-');
99 const flag_args = readFlagArguments(allocator, args, flag.required, flag.allowed_set, &i) catch |err| {
100 switch (err) {
101 error.ArgumentNotInAllowedSet => {
102 std.debug.warn("argument '{}' is invalid for flag '{}'\n", args[i], arg);
103 std.debug.warn("allowed options are ");
104 for (??flag.allowed_set) |possible| {
105 std.debug.warn("'{}' ", possible);
106 }
107 std.debug.warn("\n");
108 },
109 error.MissingFlagArguments => {
110 std.debug.warn("missing argument for flag: {}\n", arg);
111 },
112 else => {},
113 }
114
115 return err;
116 };
117
118 if (flag.mergable) {
119 var prev =
120 if (parsed.flags.get(flag_name_trimmed)) |entry|
121 entry.value.Many
122 else
123 ArrayList([]const u8).init(allocator);
124
125 // MergeN creation disallows 0 length flag entry (doesn't make sense)
126 switch (flag_args) {
127 FlagArg.None => unreachable,
128 FlagArg.Single => |inner| try prev.append(inner),
129 FlagArg.Many => |inner| try prev.appendSlice(inner.toSliceConst()),
130 }
131
132 _ = try parsed.flags.put(flag_name_trimmed, FlagArg { .Many = prev });
133 } else {
134 _ = try parsed.flags.put(flag_name_trimmed, flag_args);
135 }
136
137 continue :next;
138 }
139 }
140
141 // TODO: Better errors with context, global error state and return is sufficient.
142 std.debug.warn("could not match flag: {}\n", arg);
143 return error.UnknownFlag;
144 } else {
145 try parsed.positionals.append(arg);
146 }
147 }
148
149 return parsed;
150 }
151
152 pub fn deinit(self: &Args) void {
153 self.flags.deinit();
154 self.positionals.deinit();
155 }
156
157 // e.g. --help
158 pub fn present(self: &Args, name: []const u8) bool {
159 return self.flags.contains(name);
160 }
161
162 // e.g. --name value
163 pub fn single(self: &Args, name: []const u8) ?[]const u8 {
164 if (self.flags.get(name)) |entry| {
165 switch (entry.value) {
166 FlagArg.Single => |inner| { return inner; },
167 else => @panic("attempted to retrieve flag with wrong type"),
168 }
169 } else {
170 return null;
171 }
172 }
173
174 // e.g. --names value1 value2 value3
175 pub fn many(self: &Args, name: []const u8) ?[]const []const u8 {
176 if (self.flags.get(name)) |entry| {
177 switch (entry.value) {
178 FlagArg.Many => |inner| { return inner.toSliceConst(); },
179 else => @panic("attempted to retrieve flag with wrong type"),
180 }
181 } else {
182 return null;
183 }
184 }
185};
186
187// Arguments for a flag. e.g. arg1, arg2 in `--command arg1 arg2`.
188const FlagArg = union(enum) {
189 None,
190 Single: []const u8,
191 Many: ArrayList([]const u8),
192};
193
194// Specification for how a flag should be parsed.
195pub const Flag = struct {
196 name: []const u8,
197 required: usize,
198 mergable: bool,
199 allowed_set: ?[]const []const u8,
200
201 pub fn Bool(comptime name: []const u8) Flag {
202 return ArgN(name, 0);
203 }
204
205 pub fn Arg1(comptime name: []const u8) Flag {
206 return ArgN(name, 1);
207 }
208
209 pub fn ArgN(comptime name: []const u8, comptime n: usize) Flag {
210 return Flag {
211 .name = name,
212 .required = n,
213 .mergable = false,
214 .allowed_set = null,
215 };
216 }
217
218 pub fn ArgMergeN(comptime name: []const u8, comptime n: usize) Flag {
219 if (n == 0) {
220 @compileError("n must be greater than 0");
221 }
222
223 return Flag {
224 .name = name,
225 .required = n,
226 .mergable = true,
227 .allowed_set = null,
228 };
229 }
230
231 pub fn Option(comptime name: []const u8, comptime set: []const []const u8) Flag {
232 return Flag {
233 .name = name,
234 .required = 1,
235 .mergable = false,
236 .allowed_set = set,
237 };
238 }
239};
240
241test "parse arguments" {
242 const spec1 = comptime []const Flag {
243 Flag.Bool("--help"),
244 Flag.Bool("--init"),
245 Flag.Arg1("--build-file"),
246 Flag.Option("--color", []const []const u8 { "on", "off", "auto" }),
247 Flag.ArgN("--pkg-begin", 2),
248 Flag.ArgMergeN("--object", 1),
249 Flag.ArgN("--library", 1),
250 };
251
252 const cliargs = []const []const u8 {
253 "build",
254 "--help",
255 "pos1",
256 "--build-file", "build.zig",
257 "--object", "obj1",
258 "--object", "obj2",
259 "--library", "lib1",
260 "--library", "lib2",
261 "--color", "on",
262 "pos2",
263 };
264
265 var args = try Args.parse(std.debug.global_allocator, spec1, cliargs);
266
267 debug.assert(args.present("help"));
268 debug.assert(!args.present("help2"));
269 debug.assert(!args.present("init"));
270
271 debug.assert(mem.eql(u8, ??args.single("build-file"), "build.zig"));
272 debug.assert(mem.eql(u8, ??args.single("color"), "on"));
273
274 const objects = ??args.many("object");
275 debug.assert(mem.eql(u8, objects[0], "obj1"));
276 debug.assert(mem.eql(u8, objects[1], "obj2"));
277
278 debug.assert(mem.eql(u8, ??args.single("library"), "lib2"));
279
280 const pos = args.positionals.toSliceConst();
281 debug.assert(mem.eql(u8, pos[0], "build"));
282 debug.assert(mem.eql(u8, pos[1], "pos1"));
283 debug.assert(mem.eql(u8, pos[2], "pos2"));
284}
src-self-hosted/introspect.zig created+57
...@@ -0,0 +1,57 @@
1// Introspection and determination of system libraries needed by zig.
2
3const std = @import("std");
4const mem = std.mem;
5const os = std.os;
6
7const warn = std.debug.warn;
8
9/// Caller must free result
10pub fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {
11 const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");
12 errdefer allocator.free(test_zig_dir);
13
14 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
15 defer allocator.free(test_index_file);
16
17 var file = try os.File.openRead(allocator, test_index_file);
18 file.close();
19
20 return test_zig_dir;
21}
22
23/// Caller must free result
24pub fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {
25 const self_exe_path = try os.selfExeDirPath(allocator);
26 defer allocator.free(self_exe_path);
27
28 var cur_path: []const u8 = self_exe_path;
29 while (true) {
30 const test_dir = os.path.dirname(cur_path);
31
32 if (mem.eql(u8, test_dir, cur_path)) {
33 break;
34 }
35
36 return testZigInstallPrefix(allocator, test_dir) catch |err| {
37 cur_path = test_dir;
38 continue;
39 };
40 }
41
42 return error.FileNotFound;
43}
44
45pub fn resolveZigLibDir(allocator: &mem.Allocator) ![]u8 {
46 return findZigLibDir(allocator) catch |err| {
47 warn(
48 \\Unable to find zig lib directory: {}.
49 \\Reinstall Zig or use --zig-install-prefix.
50 \\
51 ,
52 @errorName(err)
53 );
54
55 return error.ZigLibDirNotFound;
56 };
57}
src-self-hosted/main.zig+881-700
...@@ -1,613 +1,165 @@...@@ -1,613 +1,165 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;
3const io = std.io;
4const os = std.os;
5const heap = std.heap;
6const warn = std.debug.warn;
7const assert = std.debug.assert;
8const target = @import("target.zig");
9const Target = target.Target;
10const Module = @import("module.zig").Module;
11const ErrColor = Module.ErrColor;
12const Emit = Module.Emit;
13const builtin = @import("builtin");2const builtin = @import("builtin");
14const ArrayList = std.ArrayList;
15const c = @import("c.zig");
163
17const default_zig_cache_name = "zig-cache";4const os = std.os;
5const io = std.io;
6const mem = std.mem;
7const Allocator = mem.Allocator;
8const ArrayList = std.ArrayList;
9const Buffer = std.Buffer;
1810
19const Cmd = enum {11const arg = @import("arg.zig");
20 None,12const c = @import("c.zig");
21 Build,13const introspect = @import("introspect.zig");
22 Test,14const Args = arg.Args;
23 Version,15const Flag = arg.Flag;
24 Zen,16const Module = @import("module.zig").Module;
25 TranslateC,17const Target = @import("target.zig").Target;
26 Targets,18
19var stderr: &io.OutStream(io.FileOutStream.Error) = undefined;
20var stdout: &io.OutStream(io.FileOutStream.Error) = undefined;
21
22const usage =
23 \\usage: zig [command] [options]
24 \\
25 \\Commands:
26 \\
27 \\ build Build project from build.zig
28 \\ build-exe [source] Create executable from source or object files
29 \\ build-lib [source] Create library from source or object files
30 \\ build-obj [source] Create object from source or assembly
31 \\ fmt [source] Parse file and render in canonical zig format
32 \\ run [source] Create executable and run immediately
33 \\ targets List available compilation targets
34 \\ test [source] Create and run a test build
35 \\ translate-c [source] Convert c code to zig code
36 \\ version Print version number and exit
37 \\ zen Print zen of zig and exit
38 \\
39 \\
40 ;
41
42const Command = struct {
43 name: []const u8,
44 exec: fn(&Allocator, []const []const u8) error!void,
27};45};
2846
29fn badArgs(comptime format: []const u8, args: ...) noreturn {
30 var stderr = io.getStdErr() catch std.os.exit(1);
31 var stderr_stream_adapter = io.FileOutStream.init(&stderr);
32 const stderr_stream = &stderr_stream_adapter.stream;
33 stderr_stream.print(format ++ "\n\n", args) catch std.os.exit(1);
34 printUsage(&stderr_stream_adapter.stream) catch std.os.exit(1);
35 std.os.exit(1);
36}
37
38pub fn main() !void {47pub fn main() !void {
39 const allocator = std.heap.c_allocator;48 var allocator = std.heap.c_allocator;
49
50 var stdout_file = try std.io.getStdOut();
51 var stdout_out_stream = std.io.FileOutStream.init(&stdout_file);
52 stdout = &stdout_out_stream.stream;
53
54 var stderr_file = try std.io.getStdErr();
55 var stderr_out_stream = std.io.FileOutStream.init(&stderr_file);
56 stderr = &stderr_out_stream.stream;
4057
41 const args = try os.argsAlloc(allocator);58 const args = try os.argsAlloc(allocator);
42 defer os.argsFree(allocator, args);59 defer os.argsFree(allocator, args);
4360
44 if (args.len >= 2 and mem.eql(u8, args[1], "build")) {61 if (args.len <= 1) {
45 return buildMain(allocator, args[2..]);62 try stderr.write(usage);
46 }63 os.exit(1);
47
48 if (args.len >= 2 and mem.eql(u8, args[1], "fmt")) {
49 return fmtMain(allocator, args[2..]);
50 }
51
52 var cmd = Cmd.None;
53 var build_kind: Module.Kind = undefined;
54 var build_mode: builtin.Mode = builtin.Mode.Debug;
55 var color = ErrColor.Auto;
56 var emit_file_type = Emit.Binary;
57
58 var strip = false;
59 var is_static = false;
60 var verbose_tokenize = false;
61 var verbose_ast_tree = false;
62 var verbose_ast_fmt = false;
63 var verbose_link = false;
64 var verbose_ir = false;
65 var verbose_llvm_ir = false;
66 var verbose_cimport = false;
67 var mwindows = false;
68 var mconsole = false;
69 var rdynamic = false;
70 var each_lib_rpath = false;
71 var timing_info = false;
72
73 var in_file_arg: ?[]u8 = null;
74 var out_file: ?[]u8 = null;
75 var out_file_h: ?[]u8 = null;
76 var out_name_arg: ?[]u8 = null;
77 var libc_lib_dir_arg: ?[]u8 = null;
78 var libc_static_lib_dir_arg: ?[]u8 = null;
79 var libc_include_dir_arg: ?[]u8 = null;
80 var msvc_lib_dir_arg: ?[]u8 = null;
81 var kernel32_lib_dir_arg: ?[]u8 = null;
82 var zig_install_prefix: ?[]u8 = null;
83 var dynamic_linker_arg: ?[]u8 = null;
84 var cache_dir_arg: ?[]const u8 = null;
85 var target_arch: ?[]u8 = null;
86 var target_os: ?[]u8 = null;
87 var target_environ: ?[]u8 = null;
88 var mmacosx_version_min: ?[]u8 = null;
89 var mios_version_min: ?[]u8 = null;
90 var linker_script_arg: ?[]u8 = null;
91 var test_name_prefix_arg: ?[]u8 = null;
92
93 var test_filters = ArrayList([]const u8).init(allocator);
94 defer test_filters.deinit();
95
96 var lib_dirs = ArrayList([]const u8).init(allocator);
97 defer lib_dirs.deinit();
98
99 var clang_argv = ArrayList([]const u8).init(allocator);
100 defer clang_argv.deinit();
101
102 var llvm_argv = ArrayList([]const u8).init(allocator);
103 defer llvm_argv.deinit();
104
105 var link_libs = ArrayList([]const u8).init(allocator);
106 defer link_libs.deinit();
107
108 var frameworks = ArrayList([]const u8).init(allocator);
109 defer frameworks.deinit();
110
111 var objects = ArrayList([]const u8).init(allocator);
112 defer objects.deinit();
113
114 var asm_files = ArrayList([]const u8).init(allocator);
115 defer asm_files.deinit();
116
117 var rpath_list = ArrayList([]const u8).init(allocator);
118 defer rpath_list.deinit();
119
120 var ver_major: u32 = 0;
121 var ver_minor: u32 = 0;
122 var ver_patch: u32 = 0;
123
124 var arg_i: usize = 1;
125 while (arg_i < args.len) : (arg_i += 1) {
126 const arg = args[arg_i];
127
128 if (arg.len != 0 and arg[0] == '-') {
129 if (mem.eql(u8, arg, "--release-fast")) {
130 build_mode = builtin.Mode.ReleaseFast;
131 } else if (mem.eql(u8, arg, "--release-safe")) {
132 build_mode = builtin.Mode.ReleaseSafe;
133 } else if (mem.eql(u8, arg, "--strip")) {
134 strip = true;
135 } else if (mem.eql(u8, arg, "--static")) {
136 is_static = true;
137 } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
138 verbose_tokenize = true;
139 } else if (mem.eql(u8, arg, "--verbose-ast-tree")) {
140 verbose_ast_tree = true;
141 } else if (mem.eql(u8, arg, "--verbose-ast-fmt")) {
142 verbose_ast_fmt = true;
143 } else if (mem.eql(u8, arg, "--verbose-link")) {
144 verbose_link = true;
145 } else if (mem.eql(u8, arg, "--verbose-ir")) {
146 verbose_ir = true;
147 } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
148 verbose_llvm_ir = true;
149 } else if (mem.eql(u8, arg, "--verbose-cimport")) {
150 verbose_cimport = true;
151 } else if (mem.eql(u8, arg, "-mwindows")) {
152 mwindows = true;
153 } else if (mem.eql(u8, arg, "-mconsole")) {
154 mconsole = true;
155 } else if (mem.eql(u8, arg, "-rdynamic")) {
156 rdynamic = true;
157 } else if (mem.eql(u8, arg, "--each-lib-rpath")) {
158 each_lib_rpath = true;
159 } else if (mem.eql(u8, arg, "--enable-timing-info")) {
160 timing_info = true;
161 } else if (mem.eql(u8, arg, "--test-cmd-bin")) {
162 @panic("TODO --test-cmd-bin");
163 } else if (arg[1] == 'L' and arg.len > 2) {
164 // alias for --library-path
165 try lib_dirs.append(arg[1..]);
166 } else if (mem.eql(u8, arg, "--pkg-begin")) {
167 @panic("TODO --pkg-begin");
168 } else if (mem.eql(u8, arg, "--pkg-end")) {
169 @panic("TODO --pkg-end");
170 } else if (arg_i + 1 >= args.len) {
171 badArgs("expected another argument after {}", arg);
172 } else {
173 arg_i += 1;
174 if (mem.eql(u8, arg, "--output")) {
175 out_file = args[arg_i];
176 } else if (mem.eql(u8, arg, "--output-h")) {
177 out_file_h = args[arg_i];
178 } else if (mem.eql(u8, arg, "--color")) {
179 if (mem.eql(u8, args[arg_i], "auto")) {
180 color = ErrColor.Auto;
181 } else if (mem.eql(u8, args[arg_i], "on")) {
182 color = ErrColor.On;
183 } else if (mem.eql(u8, args[arg_i], "off")) {
184 color = ErrColor.Off;
185 } else {
186 badArgs("--color options are 'auto', 'on', or 'off'");
187 }
188 } else if (mem.eql(u8, arg, "--emit")) {
189 if (mem.eql(u8, args[arg_i], "asm")) {
190 emit_file_type = Emit.Assembly;
191 } else if (mem.eql(u8, args[arg_i], "bin")) {
192 emit_file_type = Emit.Binary;
193 } else if (mem.eql(u8, args[arg_i], "llvm-ir")) {
194 emit_file_type = Emit.LlvmIr;
195 } else {
196 badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
197 }
198 } else if (mem.eql(u8, arg, "--name")) {
199 out_name_arg = args[arg_i];
200 } else if (mem.eql(u8, arg, "--libc-lib-dir")) {
201 libc_lib_dir_arg = args[arg_i];
202 } else if (mem.eql(u8, arg, "--libc-static-lib-dir")) {
203 libc_static_lib_dir_arg = args[arg_i];
204 } else if (mem.eql(u8, arg, "--libc-include-dir")) {
205 libc_include_dir_arg = args[arg_i];
206 } else if (mem.eql(u8, arg, "--msvc-lib-dir")) {
207 msvc_lib_dir_arg = args[arg_i];
208 } else if (mem.eql(u8, arg, "--kernel32-lib-dir")) {
209 kernel32_lib_dir_arg = args[arg_i];
210 } else if (mem.eql(u8, arg, "--zig-install-prefix")) {
211 zig_install_prefix = args[arg_i];
212 } else if (mem.eql(u8, arg, "--dynamic-linker")) {
213 dynamic_linker_arg = args[arg_i];
214 } else if (mem.eql(u8, arg, "-isystem")) {
215 try clang_argv.append("-isystem");
216 try clang_argv.append(args[arg_i]);
217 } else if (mem.eql(u8, arg, "-dirafter")) {
218 try clang_argv.append("-dirafter");
219 try clang_argv.append(args[arg_i]);
220 } else if (mem.eql(u8, arg, "-mllvm")) {
221 try clang_argv.append("-mllvm");
222 try clang_argv.append(args[arg_i]);
223
224 try llvm_argv.append(args[arg_i]);
225 } else if (mem.eql(u8, arg, "--library-path") or mem.eql(u8, arg, "-L")) {
226 try lib_dirs.append(args[arg_i]);
227 } else if (mem.eql(u8, arg, "--library")) {
228 try link_libs.append(args[arg_i]);
229 } else if (mem.eql(u8, arg, "--object")) {
230 try objects.append(args[arg_i]);
231 } else if (mem.eql(u8, arg, "--assembly")) {
232 try asm_files.append(args[arg_i]);
233 } else if (mem.eql(u8, arg, "--cache-dir")) {
234 cache_dir_arg = args[arg_i];
235 } else if (mem.eql(u8, arg, "--target-arch")) {
236 target_arch = args[arg_i];
237 } else if (mem.eql(u8, arg, "--target-os")) {
238 target_os = args[arg_i];
239 } else if (mem.eql(u8, arg, "--target-environ")) {
240 target_environ = args[arg_i];
241 } else if (mem.eql(u8, arg, "-mmacosx-version-min")) {
242 mmacosx_version_min = args[arg_i];
243 } else if (mem.eql(u8, arg, "-mios-version-min")) {
244 mios_version_min = args[arg_i];
245 } else if (mem.eql(u8, arg, "-framework")) {
246 try frameworks.append(args[arg_i]);
247 } else if (mem.eql(u8, arg, "--linker-script")) {
248 linker_script_arg = args[arg_i];
249 } else if (mem.eql(u8, arg, "-rpath")) {
250 try rpath_list.append(args[arg_i]);
251 } else if (mem.eql(u8, arg, "--test-filter")) {
252 try test_filters.append(args[arg_i]);
253 } else if (mem.eql(u8, arg, "--test-name-prefix")) {
254 test_name_prefix_arg = args[arg_i];
255 } else if (mem.eql(u8, arg, "--ver-major")) {
256 ver_major = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
257 } else if (mem.eql(u8, arg, "--ver-minor")) {
258 ver_minor = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
259 } else if (mem.eql(u8, arg, "--ver-patch")) {
260 ver_patch = try std.fmt.parseUnsigned(u32, args[arg_i], 10);
261 } else if (mem.eql(u8, arg, "--test-cmd")) {
262 @panic("TODO --test-cmd");
263 } else {
264 badArgs("invalid argument: {}", arg);
265 }
266 }
267 } else if (cmd == Cmd.None) {
268 if (mem.eql(u8, arg, "build-obj")) {
269 cmd = Cmd.Build;
270 build_kind = Module.Kind.Obj;
271 } else if (mem.eql(u8, arg, "build-exe")) {
272 cmd = Cmd.Build;
273 build_kind = Module.Kind.Exe;
274 } else if (mem.eql(u8, arg, "build-lib")) {
275 cmd = Cmd.Build;
276 build_kind = Module.Kind.Lib;
277 } else if (mem.eql(u8, arg, "version")) {
278 cmd = Cmd.Version;
279 } else if (mem.eql(u8, arg, "zen")) {
280 cmd = Cmd.Zen;
281 } else if (mem.eql(u8, arg, "translate-c")) {
282 cmd = Cmd.TranslateC;
283 } else if (mem.eql(u8, arg, "test")) {
284 cmd = Cmd.Test;
285 build_kind = Module.Kind.Exe;
286 } else {
287 badArgs("unrecognized command: {}", arg);
288 }
289 } else switch (cmd) {
290 Cmd.Build, Cmd.TranslateC, Cmd.Test => {
291 if (in_file_arg == null) {
292 in_file_arg = arg;
293 } else {
294 badArgs("unexpected extra parameter: {}", arg);
295 }
296 },
297 Cmd.Version, Cmd.Zen, Cmd.Targets => {
298 badArgs("unexpected extra parameter: {}", arg);
299 },
300 Cmd.None => unreachable,
301 }
302 }64 }
30365
304 target.initializeAll();66 const commands = []Command {
30567 Command { .name = "build", .exec = cmdBuild },
306 // TODO68 Command { .name = "build-exe", .exec = cmdBuildExe },
307// ZigTarget alloc_target;69 Command { .name = "build-lib", .exec = cmdBuildLib },
308// ZigTarget *target;70 Command { .name = "build-obj", .exec = cmdBuildObj },
309// if (!target_arch && !target_os && !target_environ) {71 Command { .name = "fmt", .exec = cmdFmt },
310// target = nullptr;72 Command { .name = "run", .exec = cmdRun },
311// } else {73 Command { .name = "targets", .exec = cmdTargets },
312// target = &alloc_target;74 Command { .name = "test", .exec = cmdTest },
313// get_unknown_target(target);75 Command { .name = "translate-c", .exec = cmdTranslateC },
314// if (target_arch) {76 Command { .name = "version", .exec = cmdVersion },
315// if (parse_target_arch(target_arch, &target->arch)) {77 Command { .name = "zen", .exec = cmdZen },
316// fprintf(stderr, "invalid --target-arch argument\n");78
317// return usage(arg0);79 // undocumented commands
318// }80 Command { .name = "help", .exec = cmdHelp },
319// }81 Command { .name = "internal", .exec = cmdInternal },
320// if (target_os) {82 };
321// if (parse_target_os(target_os, &target->os)) {83
322// fprintf(stderr, "invalid --target-os argument\n");84 for (commands) |command| {
323// return usage(arg0);85 if (mem.eql(u8, command.name, args[1])) {
324// }86 try command.exec(allocator, args[2..]);
325// }87 return;
326// if (target_environ) {88 }
327// if (parse_target_environ(target_environ, &target->env_type)) {
328// fprintf(stderr, "invalid --target-environ argument\n");
329// return usage(arg0);
330// }
331// }
332// }
333
334 switch (cmd) {
335 Cmd.None => badArgs("expected command"),
336 Cmd.Zen => return printZen(),
337 Cmd.Build, Cmd.Test, Cmd.TranslateC => {
338 if (cmd == Cmd.Build and in_file_arg == null and objects.len == 0 and asm_files.len == 0) {
339 badArgs("expected source file argument or at least one --object or --assembly argument");
340 } else if ((cmd == Cmd.TranslateC or cmd == Cmd.Test) and in_file_arg == null) {
341 badArgs("expected source file argument");
342 } else if (cmd == Cmd.Build and build_kind == Module.Kind.Obj and objects.len != 0) {
343 badArgs("When building an object file, --object arguments are invalid");
344 }
345
346 const root_name = switch (cmd) {
347 Cmd.Build, Cmd.TranslateC => x: {
348 if (out_name_arg) |out_name| {
349 break :x out_name;
350 } else if (in_file_arg) |in_file_path| {
351 const basename = os.path.basename(in_file_path);
352 var it = mem.split(basename, ".");
353 break :x it.next() ?? badArgs("file name cannot be empty");
354 } else {
355 badArgs("--name [name] not provided and unable to infer");
356 }
357 },
358 Cmd.Test => "test",
359 else => unreachable,
360 };
361
362 const zig_root_source_file = if (cmd == Cmd.TranslateC) null else in_file_arg;
363
364 const chosen_cache_dir = cache_dir_arg ?? default_zig_cache_name;
365 const full_cache_dir = try os.path.resolve(allocator, ".", chosen_cache_dir);
366 defer allocator.free(full_cache_dir);
367
368 const zig_lib_dir = try resolveZigLibDir(allocator, zig_install_prefix);
369 errdefer allocator.free(zig_lib_dir);
370
371 const module = try Module.create(allocator, root_name, zig_root_source_file,
372 Target.Native, build_kind, build_mode, zig_lib_dir, full_cache_dir);
373 defer module.destroy();
374
375 module.version_major = ver_major;
376 module.version_minor = ver_minor;
377 module.version_patch = ver_patch;
378
379 module.is_test = cmd == Cmd.Test;
380 if (linker_script_arg) |linker_script| {
381 module.linker_script = linker_script;
382 }
383 module.each_lib_rpath = each_lib_rpath;
384 module.clang_argv = clang_argv.toSliceConst();
385 module.llvm_argv = llvm_argv.toSliceConst();
386 module.strip = strip;
387 module.is_static = is_static;
388
389 if (libc_lib_dir_arg) |libc_lib_dir| {
390 module.libc_lib_dir = libc_lib_dir;
391 }
392 if (libc_static_lib_dir_arg) |libc_static_lib_dir| {
393 module.libc_static_lib_dir = libc_static_lib_dir;
394 }
395 if (libc_include_dir_arg) |libc_include_dir| {
396 module.libc_include_dir = libc_include_dir;
397 }
398 if (msvc_lib_dir_arg) |msvc_lib_dir| {
399 module.msvc_lib_dir = msvc_lib_dir;
400 }
401 if (kernel32_lib_dir_arg) |kernel32_lib_dir| {
402 module.kernel32_lib_dir = kernel32_lib_dir;
403 }
404 if (dynamic_linker_arg) |dynamic_linker| {
405 module.dynamic_linker = dynamic_linker;
406 }
407 module.verbose_tokenize = verbose_tokenize;
408 module.verbose_ast_tree = verbose_ast_tree;
409 module.verbose_ast_fmt = verbose_ast_fmt;
410 module.verbose_link = verbose_link;
411 module.verbose_ir = verbose_ir;
412 module.verbose_llvm_ir = verbose_llvm_ir;
413 module.verbose_cimport = verbose_cimport;
414
415 module.err_color = color;
416
417 module.lib_dirs = lib_dirs.toSliceConst();
418 module.darwin_frameworks = frameworks.toSliceConst();
419 module.rpath_list = rpath_list.toSliceConst();
420
421 for (link_libs.toSliceConst()) |name| {
422 _ = try module.addLinkLib(name, true);
423 }
424
425 module.windows_subsystem_windows = mwindows;
426 module.windows_subsystem_console = mconsole;
427 module.linker_rdynamic = rdynamic;
428
429 if (mmacosx_version_min != null and mios_version_min != null) {
430 badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");
431 }
432
433 if (mmacosx_version_min) |ver| {
434 module.darwin_version_min = Module.DarwinVersionMin { .MacOS = ver };
435 } else if (mios_version_min) |ver| {
436 module.darwin_version_min = Module.DarwinVersionMin { .Ios = ver };
437 }
438
439 module.test_filters = test_filters.toSliceConst();
440 module.test_name_prefix = test_name_prefix_arg;
441 module.out_h_path = out_file_h;
442
443 // TODO
444 //add_package(g, cur_pkg, g->root_package);
445
446 switch (cmd) {
447 Cmd.Build => {
448 module.emit_file_type = emit_file_type;
449
450 module.link_objects = objects.toSliceConst();
451 module.assembly_files = asm_files.toSliceConst();
452
453 try module.build();
454 try module.link(out_file);
455 },
456 Cmd.TranslateC => @panic("TODO translate-c"),
457 Cmd.Test => @panic("TODO test cmd"),
458 else => unreachable,
459 }
460 },
461 Cmd.Version => {
462 var stdout_file = try io.getStdErr();
463 try stdout_file.write(std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
464 try stdout_file.write("\n");
465 },
466 Cmd.Targets => @panic("TODO zig targets"),
467 }89 }
468}
46990
470fn printUsage(stream: var) !void {91 try stderr.print("unknown command: {}\n\n", args[1]);
471 try stream.write(92 try stderr.write(usage);
472 \\Usage: zig [command] [options]
473 \\
474 \\Commands:
475 \\ build build project from build.zig
476 \\ build-exe [source] create executable from source or object files
477 \\ build-lib [source] create library from source or object files
478 \\ build-obj [source] create object from source or assembly
479 \\ fmt [file] parse file and render in canonical zig format
480 \\ translate-c [source] convert c code to zig code
481 \\ targets list available compilation targets
482 \\ test [source] create and run a test build
483 \\ version print version number and exit
484 \\ zen print zen of zig and exit
485 \\Compile Options:
486 \\ --assembly [source] add assembly file to build
487 \\ --cache-dir [path] override the cache directory
488 \\ --color [auto|off|on] enable or disable colored error messages
489 \\ --emit [filetype] emit a specific file format as compilation output
490 \\ --enable-timing-info print timing diagnostics
491 \\ --libc-include-dir [path] directory where libc stdlib.h resides
492 \\ --name [name] override output name
493 \\ --output [file] override destination path
494 \\ --output-h [file] override generated header file path
495 \\ --pkg-begin [name] [path] make package available to import and push current pkg
496 \\ --pkg-end pop current pkg
497 \\ --release-fast build with optimizations on and safety off
498 \\ --release-safe build with optimizations on and safety on
499 \\ --static output will be statically linked
500 \\ --strip exclude debug symbols
501 \\ --target-arch [name] specify target architecture
502 \\ --target-environ [name] specify target environment
503 \\ --target-os [name] specify target operating system
504 \\ --verbose-tokenize enable compiler debug info: tokenization
505 \\ --verbose-ast-tree enable compiler debug info: parsing into an AST (treeview)
506 \\ --verbose-ast-fmt enable compiler debug info: parsing into an AST (render source)
507 \\ --verbose-cimport enable compiler debug info: C imports
508 \\ --verbose-ir enable compiler debug info: Zig IR
509 \\ --verbose-llvm-ir enable compiler debug info: LLVM IR
510 \\ --verbose-link enable compiler debug info: linking
511 \\ --zig-install-prefix [path] override directory where zig thinks it is installed
512 \\ -dirafter [dir] same as -isystem but do it last
513 \\ -isystem [dir] add additional search path for other .h files
514 \\ -mllvm [arg] additional arguments to forward to LLVM's option processing
515 \\Link Options:
516 \\ --ar-path [path] set the path to ar
517 \\ --dynamic-linker [path] set the path to ld.so
518 \\ --each-lib-rpath add rpath for each used dynamic library
519 \\ --libc-lib-dir [path] directory where libc crt1.o resides
520 \\ --libc-static-lib-dir [path] directory where libc crtbegin.o resides
521 \\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
522 \\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
523 \\ --library [lib] link against lib
524 \\ --library-path [dir] add a directory to the library search path
525 \\ --linker-script [path] use a custom linker script
526 \\ --object [obj] add object file to build
527 \\ -L[dir] alias for --library-path
528 \\ -rdynamic add all symbols to the dynamic symbol table
529 \\ -rpath [path] add directory to the runtime library search path
530 \\ -mconsole (windows) --subsystem console to the linker
531 \\ -mwindows (windows) --subsystem windows to the linker
532 \\ -framework [name] (darwin) link against framework
533 \\ -mios-version-min [ver] (darwin) set iOS deployment target
534 \\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target
535 \\ --ver-major [ver] dynamic library semver major version
536 \\ --ver-minor [ver] dynamic library semver minor version
537 \\ --ver-patch [ver] dynamic library semver patch version
538 \\Test Options:
539 \\ --test-filter [text] skip tests that do not match filter
540 \\ --test-name-prefix [text] add prefix to all tests
541 \\ --test-cmd [arg] specify test execution command one arg at a time
542 \\ --test-cmd-bin appends test binary path to test cmd args
543 \\
544 );
545}
546
547fn printZen() !void {
548 var stdout_file = try io.getStdErr();
549 try stdout_file.write(
550 \\
551 \\ * Communicate intent precisely.
552 \\ * Edge cases matter.
553 \\ * Favor reading code over writing code.
554 \\ * Only one obvious way to do things.
555 \\ * Runtime crashes are better than bugs.
556 \\ * Compile errors are better than runtime crashes.
557 \\ * Incremental improvements.
558 \\ * Avoid local maximums.
559 \\ * Reduce the amount one must remember.
560 \\ * Minimize energy spent on coding style.
561 \\ * Together we serve end users.
562 \\
563 \\
564 );
565}93}
56694
567fn buildMain(allocator: &mem.Allocator, argv: []const []const u8) !void {95// cmd:build ///////////////////////////////////////////////////////////////////////////////////////
568 var build_file: [] const u8 = "build.zig";96
569 var cache_dir: ?[] const u8 = null;97const usage_build =
570 var zig_install_prefix: ?[] const u8 = null;98 \\usage: zig build <options>
571 var asked_for_help = false;99 \\
572 var asked_for_init = false;100 \\General Options:
573101 \\ --help Print this help and exit
574 var args = ArrayList([] const u8).init(allocator);102 \\ --init Generate a build.zig template
575 defer args.deinit();103 \\ --build-file [file] Override path to build.zig
576104 \\ --cache-dir [path] Override path to cache directory
577 var zig_exe_path = try os.selfExePath(allocator);105 \\ --verbose Print commands before executing them
578 defer allocator.free(zig_exe_path);106 \\ --prefix [path] Override default install prefix
579107 \\
580 try args.append(""); // Placeholder for zig-cache/build108 \\Project-Specific Options:
581 try args.append(""); // Placeholder for zig_exe_path109 \\
582 try args.append(""); // Placeholder for build_file_dirname110 \\ Project-specific options become available when the build file is found.
583 try args.append(""); // Placeholder for full_cache_dir111 \\
112 \\Advanced Options:
113 \\ --build-file [file] Override path to build.zig
114 \\ --cache-dir [path] Override path to cache directory
115 \\ --verbose-tokenize Enable compiler debug output for tokenization
116 \\ --verbose-ast Enable compiler debug output for parsing into an AST
117 \\ --verbose-link Enable compiler debug output for linking
118 \\ --verbose-ir Enable compiler debug output for Zig IR
119 \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR
120 \\ --verbose-cimport Enable compiler debug output for C imports
121 \\
122 \\
123 ;
124
125const args_build_spec = []Flag {
126 Flag.Bool("--help"),
127 Flag.Bool("--init"),
128 Flag.Arg1("--build-file"),
129 Flag.Arg1("--cache-dir"),
130 Flag.Bool("--verbose"),
131 Flag.Arg1("--prefix"),
132
133 Flag.Arg1("--build-file"),
134 Flag.Arg1("--cache-dir"),
135 Flag.Bool("--verbose-tokenize"),
136 Flag.Bool("--verbose-ast"),
137 Flag.Bool("--verbose-link"),
138 Flag.Bool("--verbose-ir"),
139 Flag.Bool("--verbose-llvm-ir"),
140 Flag.Bool("--verbose-cimport"),
141};
584142
585 var i: usize = 0;143const missing_build_file =
586 while (i < argv.len) : (i += 1) {144 \\No 'build.zig' file found.
587 var arg = argv[i];145 \\
588 if (mem.eql(u8, arg, "--help")) {146 \\Initialize a 'build.zig' template file with `zig build --init`,
589 asked_for_help = true;147 \\or build an executable directly with `zig build-exe $FILENAME.zig`.
590 try args.append(argv[i]);148 \\
591 } else if (mem.eql(u8, arg, "--init")) {149 \\See: `zig build --help` or `zig help` for more options.
592 asked_for_init = true;150 \\
593 try args.append(argv[i]);151 ;
594 } else if (i + 1 < argv.len and mem.eql(u8, arg, "--build-file")) {152
595 build_file = argv[i + 1];153fn cmdBuild(allocator: &Allocator, args: []const []const u8) !void {
596 i += 1;154 var flags = try Args.parse(allocator, args_build_spec, args);
597 } else if (i + 1 < argv.len and mem.eql(u8, arg, "--cache-dir")) {155 defer flags.deinit();
598 cache_dir = argv[i + 1];156
599 i += 1;157 if (flags.present("help")) {
600 } else if (i + 1 < argv.len and mem.eql(u8, arg, "--zig-install-prefix")) {158 try stderr.write(usage_build);
601 try args.append(arg);159 os.exit(0);
602 i += 1;
603 zig_install_prefix = argv[i];
604 try args.append(argv[i]);
605 } else {
606 try args.append(arg);
607 }
608 }160 }
609161
610 const zig_lib_dir = try resolveZigLibDir(allocator, zig_install_prefix);162 const zig_lib_dir = try introspect.resolveZigLibDir(allocator);
611 defer allocator.free(zig_lib_dir);163 defer allocator.free(zig_lib_dir);
612164
613 const zig_std_dir = try os.path.join(allocator, zig_lib_dir, "std");165 const zig_std_dir = try os.path.join(allocator, zig_lib_dir, "std");
...@@ -619,113 +171,502 @@ fn buildMain(allocator: &mem.Allocator, argv: []const []const u8) !void {...@@ -619,113 +171,502 @@ fn buildMain(allocator: &mem.Allocator, argv: []const []const u8) !void {
619 const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");171 const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");
620 defer allocator.free(build_runner_path);172 defer allocator.free(build_runner_path);
621173
622 // g = codegen_create(build_runner_path, ...)174 const build_file = flags.single("build-file") ?? "build.zig";
623 // codegen_set_out_name(g, "build")
624
625 const build_file_abs = try os.path.resolve(allocator, ".", build_file);175 const build_file_abs = try os.path.resolve(allocator, ".", build_file);
626 defer allocator.free(build_file_abs);176 defer allocator.free(build_file_abs);
627177
628 const build_file_basename = os.path.basename(build_file_abs);178 const build_file_exists = os.File.access(allocator, build_file_abs, os.default_file_mode) catch false;
629 const build_file_dirname = os.path.dirname(build_file_abs);
630179
631 var full_cache_dir: []u8 = undefined;180 if (flags.present("init")) {
632 if (cache_dir == null) {181 if (build_file_exists) {
633 full_cache_dir = try os.path.join(allocator, build_file_dirname, "zig-cache");182 try stderr.print("build.zig already exists\n");
634 } else {183 os.exit(1);
635 full_cache_dir = try os.path.resolve(allocator, ".", ??cache_dir, full_cache_dir);184 }
636 }
637 defer allocator.free(full_cache_dir);
638185
639 const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build");186 // need a new scope for proper defer scope finalization on exit
640 defer allocator.free(path_to_build_exe);187 {
641 // codegen_set_cache_dir(g, full_cache_dir)188 const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig");
189 defer allocator.free(build_template_path);
642190
643 args.items[0] = path_to_build_exe;191 try os.copyFile(allocator, build_template_path, build_file_abs);
644 args.items[1] = zig_exe_path;192 try stderr.print("wrote build.zig template\n");
645 args.items[2] = build_file_dirname;193 }
646 args.items[3] = full_cache_dir;
647194
648 var build_file_exists: bool = undefined;195 os.exit(0);
649 if (os.File.openRead(allocator, build_file_abs)) |*file| {
650 file.close();
651 build_file_exists = true;
652 } else |_| {
653 build_file_exists = false;
654 }196 }
655197
656 if (!build_file_exists and asked_for_help) {198 if (!build_file_exists) {
657 // TODO(bnoordhuis) Print help message from std/special/build_runner.zig199 try stderr.write(missing_build_file);
658 return;200 os.exit(1);
659 }201 }
660202
661 if (!build_file_exists and asked_for_init) {203 // TODO: Invoke build.zig entrypoint directly?
662 const build_template_path = try os.path.join(allocator, special_dir, "build_file_template.zig");204 var zig_exe_path = try os.selfExePath(allocator);
663 defer allocator.free(build_template_path);205 defer allocator.free(zig_exe_path);
664
665 var srcfile = try os.File.openRead(allocator, build_template_path);
666 defer srcfile.close();
667206
668 var dstfile = try os.File.openWrite(allocator, build_file_abs);207 var build_args = ArrayList([]const u8).init(allocator);
669 defer dstfile.close();208 defer build_args.deinit();
670209
671 while (true) {210 const build_file_basename = os.path.basename(build_file_abs);
672 var buffer: [4096]u8 = undefined;211 const build_file_dirname = os.path.dirname(build_file_abs);
673 const n = try srcfile.read(buffer[0..]);
674 if (n == 0) break;
675 try dstfile.write(buffer[0..n]);
676 }
677212
678 return;213 var full_cache_dir: []u8 = undefined;
214 if (flags.single("cache-dir")) |cache_dir| {
215 full_cache_dir = try os.path.resolve(allocator, ".", cache_dir, full_cache_dir);
216 } else {
217 full_cache_dir = try os.path.join(allocator, build_file_dirname, "zig-cache");
679 }218 }
219 defer allocator.free(full_cache_dir);
680220
681 if (!build_file_exists) {221 const path_to_build_exe = try os.path.join(allocator, full_cache_dir, "build");
682 warn(222 defer allocator.free(path_to_build_exe);
683 \\No 'build.zig' file found.
684 \\Initialize a 'build.zig' template file with `zig build --init`,
685 \\or build an executable directly with `zig build-exe $FILENAME.zig`.
686 \\See: `zig build --help` or `zig help` for more options.
687 \\
688 );
689 os.exit(1);
690 }
691223
692 // codegen_build(g)224 try build_args.append(path_to_build_exe);
693 // codegen_link(g, path_to_build_exe)225 try build_args.append(zig_exe_path);
694 // codegen_destroy(g)226 try build_args.append(build_file_dirname);
227 try build_args.append(full_cache_dir);
695228
696 var proc = try os.ChildProcess.init(args.toSliceConst(), allocator);229 var proc = try os.ChildProcess.init(build_args.toSliceConst(), allocator);
697 defer proc.deinit();230 defer proc.deinit();
698231
699 var term = try proc.spawnAndWait();232 var term = try proc.spawnAndWait();
700 switch (term) {233 switch (term) {
701 os.ChildProcess.Term.Exited => |status| {234 os.ChildProcess.Term.Exited => |status| {
702 if (status != 0) {235 if (status != 0) {
703 warn("{} exited with status {}\n", args.at(0), status);236 try stderr.print("{} exited with status {}\n", build_args.at(0), status);
704 os.exit(1);237 os.exit(1);
705 }238 }
706 },239 },
707 os.ChildProcess.Term.Signal => |signal| {240 os.ChildProcess.Term.Signal => |signal| {
708 warn("{} killed by signal {}\n", args.at(0), signal);241 try stderr.print("{} killed by signal {}\n", build_args.at(0), signal);
709 os.exit(1);242 os.exit(1);
710 },243 },
711 os.ChildProcess.Term.Stopped => |signal| {244 os.ChildProcess.Term.Stopped => |signal| {
712 warn("{} stopped by signal {}\n", args.at(0), signal);245 try stderr.print("{} stopped by signal {}\n", build_args.at(0), signal);
713 os.exit(1);246 os.exit(1);
714 },247 },
715 os.ChildProcess.Term.Unknown => |status| {248 os.ChildProcess.Term.Unknown => |status| {
716 warn("{} encountered unknown failure {}\n", args.at(0), status);249 try stderr.print("{} encountered unknown failure {}\n", build_args.at(0), status);
250 os.exit(1);
251 },
252 }
253}
254
255// cmd:build-exe ///////////////////////////////////////////////////////////////////////////////////
256
257const usage_build_generic =
258 \\usage: zig build-exe <options> [file]
259 \\ zig build-lib <options> [file]
260 \\ zig build-obj <options> [file]
261 \\
262 \\General Options:
263 \\ --help Print this help and exit
264 \\ --color [auto|off|on] Enable or disable colored error messages
265 \\
266 \\Compile Options:
267 \\ --assembly [source] Add assembly file to build
268 \\ --cache-dir [path] Override the cache directory
269 \\ --emit [filetype] Emit a specific file format as compilation output
270 \\ --enable-timing-info Print timing diagnostics
271 \\ --libc-include-dir [path] Directory where libc stdlib.h resides
272 \\ --name [name] Override output name
273 \\ --output [file] Override destination path
274 \\ --output-h [file] Override generated header file path
275 \\ --pkg-begin [name] [path] Make package available to import and push current pkg
276 \\ --pkg-end Pop current pkg
277 \\ --release-fast Build with optimizations on and safety off
278 \\ --release-safe Build with optimizations on and safety on
279 \\ --static Output will be statically linked
280 \\ --strip Exclude debug symbols
281 \\ --target-arch [name] Specify target architecture
282 \\ --target-environ [name] Specify target environment
283 \\ --target-os [name] Specify target operating system
284 \\ --verbose-tokenize Turn on compiler debug output for tokenization
285 \\ --verbose-ast-tree Turn on compiler debug output for parsing into an AST (tree view)
286 \\ --verbose-ast-fmt Turn on compiler debug output for parsing into an AST (render source)
287 \\ --verbose-link Turn on compiler debug output for linking
288 \\ --verbose-ir Turn on compiler debug output for Zig IR
289 \\ --verbose-llvm-ir Turn on compiler debug output for LLVM IR
290 \\ --verbose-cimport Turn on compiler debug output for C imports
291 \\ -dirafter [dir] Same as -isystem but do it last
292 \\ -isystem [dir] Add additional search path for other .h files
293 \\ -mllvm [arg] Additional arguments to forward to LLVM's option processing
294 \\
295 \\Link Options:
296 \\ --ar-path [path] Set the path to ar
297 \\ --dynamic-linker [path] Set the path to ld.so
298 \\ --each-lib-rpath Add rpath for each used dynamic library
299 \\ --libc-lib-dir [path] Directory where libc crt1.o resides
300 \\ --libc-static-lib-dir [path] Directory where libc crtbegin.o resides
301 \\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
302 \\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
303 \\ --library [lib] Link against lib
304 \\ --forbid-library [lib] Make it an error to link against lib
305 \\ --library-path [dir] Add a directory to the library search path
306 \\ --linker-script [path] Use a custom linker script
307 \\ --object [obj] Add object file to build
308 \\ -rdynamic Add all symbols to the dynamic symbol table
309 \\ -rpath [path] Add directory to the runtime library search path
310 \\ -mconsole (windows) --subsystem console to the linker
311 \\ -mwindows (windows) --subsystem windows to the linker
312 \\ -framework [name] (darwin) link against framework
313 \\ -mios-version-min [ver] (darwin) set iOS deployment target
314 \\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target
315 \\ --ver-major [ver] Dynamic library semver major version
316 \\ --ver-minor [ver] Dynamic library semver minor version
317 \\ --ver-patch [ver] Dynamic library semver patch version
318 \\
319 \\
320 ;
321
322const args_build_generic = []Flag {
323 Flag.Bool("--help"),
324 Flag.Option("--color", []const []const u8 { "auto", "off", "on" }),
325
326 Flag.ArgMergeN("--assembly", 1),
327 Flag.Arg1("--cache-dir"),
328 Flag.Option("--emit", []const []const u8 { "asm", "bin", "llvm-ir" }),
329 Flag.Bool("--enable-timing-info"),
330 Flag.Arg1("--libc-include-dir"),
331 Flag.Arg1("--name"),
332 Flag.Arg1("--output"),
333 Flag.Arg1("--output-h"),
334 // NOTE: Parsed manually after initial check
335 Flag.ArgN("--pkg-begin", 2),
336 Flag.Bool("--pkg-end"),
337 Flag.Bool("--release-fast"),
338 Flag.Bool("--release-safe"),
339 Flag.Bool("--static"),
340 Flag.Bool("--strip"),
341 Flag.Arg1("--target-arch"),
342 Flag.Arg1("--target-environ"),
343 Flag.Arg1("--target-os"),
344 Flag.Bool("--verbose-tokenize"),
345 Flag.Bool("--verbose-ast-tree"),
346 Flag.Bool("--verbose-ast-fmt"),
347 Flag.Bool("--verbose-link"),
348 Flag.Bool("--verbose-ir"),
349 Flag.Bool("--verbose-llvm-ir"),
350 Flag.Bool("--verbose-cimport"),
351 Flag.Arg1("-dirafter"),
352 Flag.ArgMergeN("-isystem", 1),
353 Flag.Arg1("-mllvm"),
354
355 Flag.Arg1("--ar-path"),
356 Flag.Arg1("--dynamic-linker"),
357 Flag.Bool("--each-lib-rpath"),
358 Flag.Arg1("--libc-lib-dir"),
359 Flag.Arg1("--libc-static-lib-dir"),
360 Flag.Arg1("--msvc-lib-dir"),
361 Flag.Arg1("--kernel32-lib-dir"),
362 Flag.ArgMergeN("--library", 1),
363 Flag.ArgMergeN("--forbid-library", 1),
364 Flag.ArgMergeN("--library-path", 1),
365 Flag.Arg1("--linker-script"),
366 Flag.ArgMergeN("--object", 1),
367 // NOTE: Removed -L since it would need to be special-cased and we have an alias in library-path
368 Flag.Bool("-rdynamic"),
369 Flag.Arg1("-rpath"),
370 Flag.Bool("-mconsole"),
371 Flag.Bool("-mwindows"),
372 Flag.ArgMergeN("-framework", 1),
373 Flag.Arg1("-mios-version-min"),
374 Flag.Arg1("-mmacosx-version-min"),
375 Flag.Arg1("--ver-major"),
376 Flag.Arg1("--ver-minor"),
377 Flag.Arg1("--ver-patch"),
378};
379
380fn buildOutputType(allocator: &Allocator, args: []const []const u8, out_type: Module.Kind) !void {
381 var flags = try Args.parse(allocator, args_build_generic, args);
382 defer flags.deinit();
383
384 if (flags.present("help")) {
385 try stderr.write(usage_build_generic);
386 os.exit(0);
387 }
388
389 var build_mode = builtin.Mode.Debug;
390 if (flags.present("release-fast")) {
391 build_mode = builtin.Mode.ReleaseFast;
392 } else if (flags.present("release-safe")) {
393 build_mode = builtin.Mode.ReleaseSafe;
394 }
395
396 var color = Module.ErrColor.Auto;
397 if (flags.single("color")) |color_flag| {
398 if (mem.eql(u8, color_flag, "auto")) {
399 color = Module.ErrColor.Auto;
400 } else if (mem.eql(u8, color_flag, "on")) {
401 color = Module.ErrColor.On;
402 } else if (mem.eql(u8, color_flag, "off")) {
403 color = Module.ErrColor.Off;
404 } else {
405 unreachable;
406 }
407 }
408
409 var emit_type = Module.Emit.Binary;
410 if (flags.single("emit")) |emit_flag| {
411 if (mem.eql(u8, emit_flag, "asm")) {
412 emit_type = Module.Emit.Assembly;
413 } else if (mem.eql(u8, emit_flag, "bin")) {
414 emit_type = Module.Emit.Binary;
415 } else if (mem.eql(u8, emit_flag, "llvm-ir")) {
416 emit_type = Module.Emit.LlvmIr;
417 } else {
418 unreachable;
419 }
420 }
421
422 var cur_pkg = try Module.CliPkg.init(allocator, "", "", null); // TODO: Need a path, name?
423 defer cur_pkg.deinit();
424
425 var i: usize = 0;
426 while (i < args.len) : (i += 1) {
427 const arg_name = args[i];
428 if (mem.eql(u8, "--pkg-begin", arg_name)) {
429 // following two arguments guaranteed to exist due to arg parsing
430 i += 1;
431 const new_pkg_name = args[i];
432 i += 1;
433 const new_pkg_path = args[i];
434
435 var new_cur_pkg = try Module.CliPkg.init(allocator, new_pkg_name, new_pkg_path, cur_pkg);
436 try cur_pkg.children.append(new_cur_pkg);
437 cur_pkg = new_cur_pkg;
438 } else if (mem.eql(u8, "--pkg-end", arg_name)) {
439 if (cur_pkg.parent == null) {
440 try stderr.print("encountered --pkg-end with no matching --pkg-begin\n");
441 os.exit(1);
442 }
443 cur_pkg = ??cur_pkg.parent;
444 }
445 }
446
447 if (cur_pkg.parent != null) {
448 try stderr.print("unmatched --pkg-begin\n");
449 os.exit(1);
450 }
451
452 var in_file: ?[]const u8 = undefined;
453 switch (flags.positionals.len) {
454 0 => {
455 try stderr.write("--name [name] not provided and unable to infer\n");
717 os.exit(1);456 os.exit(1);
718 },457 },
458 1 => {
459 in_file = flags.positionals.at(0);
460 },
461 else => {
462 try stderr.write("only one zig input file is accepted during build\n");
463 os.exit(1);
464 },
465 }
466
467 const basename = os.path.basename(??in_file);
468 var it = mem.split(basename, ".");
469 const root_name = it.next() ?? {
470 try stderr.write("file name cannot be empty\n");
471 os.exit(1);
472 };
473
474 const asm_a= flags.many("assembly");
475 const obj_a = flags.many("object");
476 if (in_file == null and (obj_a == null or (??obj_a).len == 0) and (asm_a == null or (??asm_a).len == 0)) {
477 try stderr.write("Expected source file argument or at least one --object or --assembly argument\n");
478 os.exit(1);
479 }
480
481 if (out_type == Module.Kind.Obj and (obj_a != null and (??obj_a).len != 0)) {
482 try stderr.write("When building an object file, --object arguments are invalid\n");
483 os.exit(1);
484 }
485
486 const zig_root_source_file = in_file;
487
488 const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") ?? "zig-cache"[0..]) catch {
489 os.exit(1);
490 };
491 defer allocator.free(full_cache_dir);
492
493 const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
494 defer allocator.free(zig_lib_dir);
495
496 var module =
497 try Module.create(
498 allocator,
499 root_name,
500 zig_root_source_file,
501 Target.Native,
502 out_type,
503 build_mode,
504 zig_lib_dir,
505 full_cache_dir
506 );
507 defer module.destroy();
508
509 module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") ?? "0", 10);
510 module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") ?? "0", 10);
511 module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") ?? "0", 10);
512
513 module.is_test = false;
514
515 if (flags.single("linker-script")) |linker_script| {
516 module.linker_script = linker_script;
517 }
518
519 module.each_lib_rpath = flags.present("each-lib-rpath");
520
521 var clang_argv_buf = ArrayList([]const u8).init(allocator);
522 defer clang_argv_buf.deinit();
523 if (flags.many("mllvm")) |mllvm_flags| {
524 for (mllvm_flags) |mllvm| {
525 try clang_argv_buf.append("-mllvm");
526 try clang_argv_buf.append(mllvm);
527 }
528
529 module.llvm_argv = mllvm_flags;
530 module.clang_argv = clang_argv_buf.toSliceConst();
531 }
532
533 module.strip = flags.present("strip");
534 module.is_static = flags.present("static");
535
536 if (flags.single("libc-lib-dir")) |libc_lib_dir| {
537 module.libc_lib_dir = libc_lib_dir;
538 }
539 if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| {
540 module.libc_static_lib_dir = libc_static_lib_dir;
541 }
542 if (flags.single("libc-include-dir")) |libc_include_dir| {
543 module.libc_include_dir = libc_include_dir;
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;
719 }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);
613}
614
615fn cmdBuildExe(allocator: &Allocator, args: []const []const u8) !void {
616 try buildOutputType(allocator, args, Module.Kind.Exe);
720}617}
721618
722fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {619// cmd:build-lib ///////////////////////////////////////////////////////////////////////////////////
723 for (file_paths) |file_path| {620
621fn cmdBuildLib(allocator: &Allocator, args: []const []const u8) !void {
622 try buildOutputType(allocator, args, Module.Kind.Lib);
623}
624
625// cmd:build-obj ///////////////////////////////////////////////////////////////////////////////////
626
627fn cmdBuildObj(allocator: &Allocator, args: []const []const u8) !void {
628 try buildOutputType(allocator, args, Module.Kind.Obj);
629}
630
631// cmd:fmt /////////////////////////////////////////////////////////////////////////////////////////
632
633const usage_fmt =
634 \\usage: zig fmt [file]...
635 \\
636 \\ Formats the input files and modifies them in-place.
637 \\
638 \\Options:
639 \\ --help Print this help and exit
640 \\ --keep-backups Retain backup entries for every file
641 \\
642 \\
643 ;
644
645const args_fmt_spec = []Flag {
646 Flag.Bool("--help"),
647 Flag.Bool("--keep-backups"),
648};
649
650fn cmdFmt(allocator: &Allocator, args: []const []const u8) !void {
651 var flags = try Args.parse(allocator, args_fmt_spec, args);
652 defer flags.deinit();
653
654 if (flags.present("help")) {
655 try stderr.write(usage_fmt);
656 os.exit(0);
657 }
658
659 if (flags.positionals.len == 0) {
660 try stderr.write("expected at least one source file argument\n");
661 os.exit(1);
662 }
663
664 for (flags.positionals.toSliceConst()) |file_path| {
724 var file = try os.File.openRead(allocator, file_path);665 var file = try os.File.openRead(allocator, file_path);
725 defer file.close();666 defer file.close();
726667
727 const source_code = io.readFileAlloc(allocator, file_path) catch |err| {668 const source_code = io.readFileAlloc(allocator, file_path) catch |err| {
728 warn("unable to open '{}': {}", file_path, err);669 try stderr.print("unable to open '{}': {}", file_path, err);
729 continue;670 continue;
730 };671 };
731 defer allocator.free(source_code);672 defer allocator.free(source_code);
...@@ -734,72 +675,312 @@ fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {...@@ -734,72 +675,312 @@ fn fmtMain(allocator: &mem.Allocator, file_paths: []const []const u8) !void {
734 var parser = std.zig.Parser.init(&tokenizer, allocator, file_path);675 var parser = std.zig.Parser.init(&tokenizer, allocator, file_path);
735 defer parser.deinit();676 defer parser.deinit();
736677
737 var tree = try parser.parse();678 var tree = parser.parse() catch |err| {
679 try stderr.print("error parsing file '{}': {}\n", file_path, err);
680 continue;
681 };
738 defer tree.deinit();682 defer tree.deinit();
739683
740 const baf = try io.BufferedAtomicFile.create(allocator, file_path);684 var original_file_backup = try Buffer.init(allocator, file_path);
741 defer baf.destroy();685 defer original_file_backup.deinit();
686 try original_file_backup.append(".backup");
687
688 try os.rename(allocator, file_path, original_file_backup.toSliceConst());
742689
743 try parser.renderSource(baf.stream(), tree.root_node);690 try stderr.print("{}\n", file_path);
744 try baf.finish();691
692 // TODO: BufferedAtomicFile has some access problems.
693 var out_file = try os.File.openWrite(allocator, file_path);
694 defer out_file.close();
695
696 var out_file_stream = io.FileOutStream.init(&out_file);
697 try parser.renderSource(out_file_stream.stream, tree.root_node);
698
699 if (!flags.present("keep-backups")) {
700 try os.deleteFile(allocator, original_file_backup.toSliceConst());
701 }
745 }702 }
746}703}
747704
748/// Caller must free result705// cmd:targets /////////////////////////////////////////////////////////////////////////////////////
749fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) ![]u8 {706
750 if (zig_install_prefix_arg) |zig_install_prefix| {707fn cmdTargets(allocator: &Allocator, args: []const []const u8) !void {
751 return testZigInstallPrefix(allocator, zig_install_prefix) catch |err| {708 try stdout.write("Architectures:\n");
752 warn("No Zig installation found at prefix {}: {}\n", zig_install_prefix_arg, @errorName(err));709 {
753 return error.ZigInstallationNotFound;710 comptime var i: usize = 0;
754 };711 inline while (i < @memberCount(builtin.Arch)) : (i += 1) {
755 } else {712 comptime const arch_tag = @memberName(builtin.Arch, i);
756 return findZigLibDir(allocator) catch |err| {713 // NOTE: Cannot use empty string, see #918.
757 warn("Unable to find zig lib directory: {}.\nReinstall Zig or use --zig-install-prefix.\n",714 comptime const native_str =
758 @errorName(err));715 if (comptime mem.eql(u8, arch_tag, @tagName(builtin.arch))) " (native)\n" else "\n";
759 return error.ZigLibDirNotFound;716
760 };717 try stdout.print(" {}{}", arch_tag, native_str);
718 }
719 }
720 try stdout.write("\n");
721
722 try stdout.write("Operating Systems:\n");
723 {
724 comptime var i: usize = 0;
725 inline while (i < @memberCount(builtin.Os)) : (i += 1) {
726 comptime const os_tag = @memberName(builtin.Os, i);
727 // NOTE: Cannot use empty string, see #918.
728 comptime const native_str =
729 if (comptime mem.eql(u8, os_tag, @tagName(builtin.os))) " (native)\n" else "\n";
730
731 try stdout.print(" {}{}", os_tag, native_str);
732 }
733 }
734 try stdout.write("\n");
735
736 try stdout.write("Environments:\n");
737 {
738 comptime var i: usize = 0;
739 inline while (i < @memberCount(builtin.Environ)) : (i += 1) {
740 comptime const environ_tag = @memberName(builtin.Environ, i);
741 // NOTE: Cannot use empty string, see #918.
742 comptime const native_str =
743 if (comptime mem.eql(u8, environ_tag, @tagName(builtin.environ))) " (native)\n" else "\n";
744
745 try stdout.print(" {}{}", environ_tag, native_str);
746 }
761 }747 }
762}748}
763749
764/// Caller must free result750// cmd:version /////////////////////////////////////////////////////////////////////////////////////
765fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) ![]u8 {751
766 const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");752fn cmdVersion(allocator: &Allocator, args: []const []const u8) !void {
767 errdefer allocator.free(test_zig_dir);753 try stdout.print("{}\n", std.cstr.toSliceConst(c.ZIG_VERSION_STRING));
754}
768755
769 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");756// cmd:test ////////////////////////////////////////////////////////////////////////////////////////
770 defer allocator.free(test_index_file);
771757
772 var file = try os.File.openRead(allocator, test_index_file);758const usage_test =
773 file.close();759 \\usage: zig test [file]...
760 \\
761 \\Options:
762 \\ --help Print this help and exit
763 \\
764 \\
765 ;
766
767const args_test_spec = []Flag {
768 Flag.Bool("--help"),
769};
770
771
772fn cmdTest(allocator: &Allocator, args: []const []const u8) !void {
773 var flags = try Args.parse(allocator, args_build_spec, args);
774 defer flags.deinit();
775
776 if (flags.present("help")) {
777 try stderr.write(usage_test);
778 os.exit(0);
779 }
774780
775 return test_zig_dir;781 if (flags.positionals.len != 1) {
782 try stderr.write("expected exactly one zig source file\n");
783 os.exit(1);
784 }
785
786 // compile the test program into the cache and run
787
788 // NOTE: May be overlap with buildOutput, take the shared part out.
789 try stderr.print("testing file {}\n", flags.positionals.at(0));
776}790}
777791
778/// Caller must free result792// cmd:run /////////////////////////////////////////////////////////////////////////////////////////
779fn findZigLibDir(allocator: &mem.Allocator) ![]u8 {793
780 const self_exe_path = try os.selfExeDirPath(allocator);794// Run should be simple and not expose the full set of arguments provided by build-exe. If specific
781 defer allocator.free(self_exe_path);795// build requirements are need, the user should `build-exe` then `run` manually.
796const usage_run =
797 \\usage: zig run [file] -- <runtime args>
798 \\
799 \\Options:
800 \\ --help Print this help and exit
801 \\
802 \\
803 ;
804
805const args_run_spec = []Flag {
806 Flag.Bool("--help"),
807};
782808
783 var cur_path: []const u8 = self_exe_path;
784 while (true) {
785 const test_dir = os.path.dirname(cur_path);
786809
787 if (mem.eql(u8, test_dir, cur_path)) {810fn cmdRun(allocator: &Allocator, args: []const []const u8) !void {
811 var compile_args = args;
812 var runtime_args: []const []const u8 = []const []const u8 {};
813
814 for (args) |argv, i| {
815 if (mem.eql(u8, argv, "--")) {
816 compile_args = args[0..i];
817 runtime_args = args[i+1..];
788 break;818 break;
789 }819 }
820 }
821 var flags = try Args.parse(allocator, args_run_spec, compile_args);
822 defer flags.deinit();
790823
791 return testZigInstallPrefix(allocator, test_dir) catch |err| {824 if (flags.present("help")) {
792 cur_path = test_dir;825 try stderr.write(usage_run);
793 continue;826 os.exit(0);
794 };827 }
828
829 if (flags.positionals.len != 1) {
830 try stderr.write("expected exactly one zig source file\n");
831 os.exit(1);
832 }
833
834 try stderr.print("runtime args:\n");
835 for (runtime_args) |cargs| {
836 try stderr.print("{}\n", cargs);
837 }
838}
839
840// cmd:translate-c /////////////////////////////////////////////////////////////////////////////////
841
842const usage_translate_c =
843 \\usage: zig translate-c [file]
844 \\
845 \\Options:
846 \\ --help Print this help and exit
847 \\ --enable-timing-info Print timing diagnostics
848 \\ --output [path] Output file to write generated zig file (default: stdout)
849 \\
850 \\
851 ;
852
853const args_translate_c_spec = []Flag {
854 Flag.Bool("--help"),
855 Flag.Bool("--enable-timing-info"),
856 Flag.Arg1("--libc-include-dir"),
857 Flag.Arg1("--output"),
858};
859
860fn cmdTranslateC(allocator: &Allocator, args: []const []const u8) !void {
861 var flags = try Args.parse(allocator, args_translate_c_spec, args);
862 defer flags.deinit();
863
864 if (flags.present("help")) {
865 try stderr.write(usage_translate_c);
866 os.exit(0);
867 }
868
869 if (flags.positionals.len != 1) {
870 try stderr.write("expected exactly one c source file\n");
871 os.exit(1);
872 }
873
874 // set up codegen
875
876 const zig_root_source_file = null;
877
878 // NOTE: translate-c shouldn't require setting up the full codegen instance as it does in
879 // the C++ compiler.
880
881 // codegen_create(g);
882 // codegen_set_out_name(g, null);
883 // codegen_translate_c(g, flags.positional.at(0))
884
885 var output_stream = stdout;
886 if (flags.single("output")) |output_file| {
887 var file = try os.File.openWrite(allocator, output_file);
888 defer file.close();
889
890 var file_stream = io.FileOutStream.init(&file);
891 // TODO: Not being set correctly, still stdout
892 output_stream = &file_stream.stream;
795 }893 }
796894
797 // TODO look in hard coded installation path from configuration895 // ast_render(g, output_stream, g->root_import->root, 4);
798 //if (ZIG_INSTALL_PREFIX != nullptr) {896 try output_stream.write("pub const example = 10;\n");
799 // if (test_zig_install_prefix(buf_create_from_str(ZIG_INSTALL_PREFIX), out_path)) {897
800 // return 0;898 if (flags.present("enable-timing-info")) {
801 // }899 // codegen_print_timing_info(g, stdout);
802 //}900 try stderr.write("printing timing info for translate-c\n");
901 }
902}
903
904// cmd:help ////////////////////////////////////////////////////////////////////////////////////////
803905
804 return error.FileNotFound;906fn cmdHelp(allocator: &Allocator, args: []const []const u8) !void {
907 try stderr.write(usage);
908}
909
910// cmd:zen /////////////////////////////////////////////////////////////////////////////////////////
911
912const info_zen =
913 \\
914 \\ * Communicate intent precisely.
915 \\ * Edge cases matter.
916 \\ * Favor reading code over writing code.
917 \\ * Only one obvious way to do things.
918 \\ * Runtime crashes are better than bugs.
919 \\ * Compile errors are better than runtime crashes.
920 \\ * Incremental improvements.
921 \\ * Avoid local maximums.
922 \\ * Reduce the amount one must remember.
923 \\ * Minimize energy spent on coding style.
924 \\ * Together we serve end users.
925 \\
926 \\
927 ;
928
929fn cmdZen(allocator: &Allocator, args: []const []const u8) !void {
930 try stdout.write(info_zen);
931}
932
933// cmd:internal ////////////////////////////////////////////////////////////////////////////////////
934
935const usage_internal =
936 \\usage: zig internal [subcommand]
937 \\
938 \\Sub-Commands:
939 \\ build-info Print static compiler build-info
940 \\
941 \\
942 ;
943
944fn cmdInternal(allocator: &Allocator, args: []const []const u8) !void {
945 if (args.len == 0) {
946 try stderr.write(usage_internal);
947 os.exit(1);
948 }
949
950 const sub_commands = []Command {
951 Command { .name = "build-info", .exec = cmdInternalBuildInfo },
952 };
953
954 for (sub_commands) |sub_command| {
955 if (mem.eql(u8, sub_command.name, args[0])) {
956 try sub_command.exec(allocator, args[1..]);
957 return;
958 }
959 }
960
961 try stderr.print("unknown sub command: {}\n\n", args[0]);
962 try stderr.write(usage_internal);
963}
964
965fn cmdInternalBuildInfo(allocator: &Allocator, args: []const []const u8) !void {
966 try stdout.print(
967 \\ZIG_CMAKE_BINARY_DIR {}
968 \\ZIG_CXX_COMPILER {}
969 \\ZIG_LLVM_CONFIG_EXE {}
970 \\ZIG_LLD_INCLUDE_PATH {}
971 \\ZIG_LLD_LIBRARIES {}
972 \\ZIG_STD_FILES {}
973 \\ZIG_C_HEADER_FILES {}
974 \\ZIG_DIA_GUIDS_LIB {}
975 \\
976 ,
977 std.cstr.toSliceConst(c.ZIG_CMAKE_BINARY_DIR),
978 std.cstr.toSliceConst(c.ZIG_CXX_COMPILER),
979 std.cstr.toSliceConst(c.ZIG_LLVM_CONFIG_EXE),
980 std.cstr.toSliceConst(c.ZIG_LLD_INCLUDE_PATH),
981 std.cstr.toSliceConst(c.ZIG_LLD_LIBRARIES),
982 std.cstr.toSliceConst(c.ZIG_STD_FILES),
983 std.cstr.toSliceConst(c.ZIG_C_HEADER_FILES),
984 std.cstr.toSliceConst(c.ZIG_DIA_GUIDS_LIB),
985 );
805}986}
src-self-hosted/module.zig+23
...@@ -109,6 +109,29 @@ pub const Module = struct {...@@ -109,6 +109,29 @@ pub const Module = struct {
109 LlvmIr,109 LlvmIr,
110 };110 };
111111
112 pub const CliPkg = struct {
113 name: []const u8,
114 path: []const u8,
115 children: ArrayList(&CliPkg),
116 parent: ?&CliPkg,
117
118 pub fn init(allocator: &mem.Allocator, name: []const u8, path: []const u8, parent: ?&CliPkg) !&CliPkg {
119 var pkg = try allocator.create(CliPkg);
120 pkg.name = name;
121 pkg.path = path;
122 pkg.children = ArrayList(&CliPkg).init(allocator);
123 pkg.parent = parent;
124 return pkg;
125 }
126
127 pub fn deinit(self: &CliPkg) void {
128 for (self.children.toSliceConst()) |child| {
129 child.deinit();
130 }
131 self.children.deinit();
132 }
133 };
134
112 pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target,135 pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target,
113 kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module136 kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) !&Module
114 {137 {
src/all_types.hpp+49-3
...@@ -867,6 +867,7 @@ struct AstNodeAwaitExpr {...@@ -867,6 +867,7 @@ struct AstNodeAwaitExpr {
867};867};
868868
869struct AstNodeSuspend {869struct AstNodeSuspend {
870 Buf *name;
870 AstNode *block;871 AstNode *block;
871 AstNode *promise_symbol;872 AstNode *promise_symbol;
872};873};
...@@ -1291,6 +1292,7 @@ enum BuiltinFnId {...@@ -1291,6 +1292,7 @@ enum BuiltinFnId {
1291 BuiltinFnIdMemberCount,1292 BuiltinFnIdMemberCount,
1292 BuiltinFnIdMemberType,1293 BuiltinFnIdMemberType,
1293 BuiltinFnIdMemberName,1294 BuiltinFnIdMemberName,
1295 BuiltinFnIdField,
1294 BuiltinFnIdTypeof,1296 BuiltinFnIdTypeof,
1295 BuiltinFnIdAddWithOverflow,1297 BuiltinFnIdAddWithOverflow,
1296 BuiltinFnIdSubWithOverflow,1298 BuiltinFnIdSubWithOverflow,
...@@ -1310,13 +1312,15 @@ enum BuiltinFnId {...@@ -1310,13 +1312,15 @@ enum BuiltinFnId {
1310 BuiltinFnIdReturnAddress,1312 BuiltinFnIdReturnAddress,
1311 BuiltinFnIdFrameAddress,1313 BuiltinFnIdFrameAddress,
1312 BuiltinFnIdEmbedFile,1314 BuiltinFnIdEmbedFile,
1313 BuiltinFnIdCmpExchange,1315 BuiltinFnIdCmpxchgWeak,
1316 BuiltinFnIdCmpxchgStrong,
1314 BuiltinFnIdFence,1317 BuiltinFnIdFence,
1315 BuiltinFnIdDivExact,1318 BuiltinFnIdDivExact,
1316 BuiltinFnIdDivTrunc,1319 BuiltinFnIdDivTrunc,
1317 BuiltinFnIdDivFloor,1320 BuiltinFnIdDivFloor,
1318 BuiltinFnIdRem,1321 BuiltinFnIdRem,
1319 BuiltinFnIdMod,1322 BuiltinFnIdMod,
1323 BuiltinFnIdSqrt,
1320 BuiltinFnIdTruncate,1324 BuiltinFnIdTruncate,
1321 BuiltinFnIdIntType,1325 BuiltinFnIdIntType,
1322 BuiltinFnIdSetCold,1326 BuiltinFnIdSetCold,
...@@ -1346,6 +1350,7 @@ enum BuiltinFnId {...@@ -1346,6 +1350,7 @@ enum BuiltinFnId {
1346 BuiltinFnIdExport,1350 BuiltinFnIdExport,
1347 BuiltinFnIdErrorReturnTrace,1351 BuiltinFnIdErrorReturnTrace,
1348 BuiltinFnIdAtomicRmw,1352 BuiltinFnIdAtomicRmw,
1353 BuiltinFnIdAtomicLoad,
1349};1354};
13501355
1351struct BuiltinFnEntry {1356struct BuiltinFnEntry {
...@@ -1413,6 +1418,7 @@ enum ZigLLVMFnId {...@@ -1413,6 +1418,7 @@ enum ZigLLVMFnId {
1413 ZigLLVMFnIdOverflowArithmetic,1418 ZigLLVMFnIdOverflowArithmetic,
1414 ZigLLVMFnIdFloor,1419 ZigLLVMFnIdFloor,
1415 ZigLLVMFnIdCeil,1420 ZigLLVMFnIdCeil,
1421 ZigLLVMFnIdSqrt,
1416};1422};
14171423
1418enum AddSubMul {1424enum AddSubMul {
...@@ -1433,7 +1439,7 @@ struct ZigLLVMFnKey {...@@ -1433,7 +1439,7 @@ struct ZigLLVMFnKey {
1433 } clz;1439 } clz;
1434 struct {1440 struct {
1435 uint32_t bit_count;1441 uint32_t bit_count;
1436 } floor_ceil;1442 } floating;
1437 struct {1443 struct {
1438 AddSubMul add_sub_mul;1444 AddSubMul add_sub_mul;
1439 uint32_t bit_count;1445 uint32_t bit_count;
...@@ -1454,6 +1460,7 @@ enum BuildMode {...@@ -1454,6 +1460,7 @@ enum BuildMode {
1454 BuildModeDebug,1460 BuildModeDebug,
1455 BuildModeFastRelease,1461 BuildModeFastRelease,
1456 BuildModeSafeRelease,1462 BuildModeSafeRelease,
1463 BuildModeSmallRelease,
1457};1464};
14581465
1459enum EmitFileType {1466enum EmitFileType {
...@@ -1698,6 +1705,8 @@ struct CodeGen {...@@ -1698,6 +1705,8 @@ struct CodeGen {
1698 ZigList<ZigLLVMDIType **> error_di_types;1705 ZigList<ZigLLVMDIType **> error_di_types;
16991706
1700 ZigList<Buf *> forbidden_libs;1707 ZigList<Buf *> forbidden_libs;
1708
1709 bool no_rosegment_workaround;
1701};1710};
17021711
1703enum VarLinkage {1712enum VarLinkage {
...@@ -1748,6 +1757,7 @@ enum ScopeId {...@@ -1748,6 +1757,7 @@ enum ScopeId {
1748 ScopeIdVarDecl,1757 ScopeIdVarDecl,
1749 ScopeIdCImport,1758 ScopeIdCImport,
1750 ScopeIdLoop,1759 ScopeIdLoop,
1760 ScopeIdSuspend,
1751 ScopeIdFnDef,1761 ScopeIdFnDef,
1752 ScopeIdCompTime,1762 ScopeIdCompTime,
1753 ScopeIdCoroPrelude,1763 ScopeIdCoroPrelude,
...@@ -1843,6 +1853,17 @@ struct ScopeLoop {...@@ -1843,6 +1853,17 @@ struct ScopeLoop {
1843 ZigList<IrBasicBlock *> *incoming_blocks;1853 ZigList<IrBasicBlock *> *incoming_blocks;
1844};1854};
18451855
1856// This scope is created for a suspend block in order to have labeled
1857// suspend for breaking out of a suspend and for detecting if a suspend
1858// block is inside a suspend block.
1859struct ScopeSuspend {
1860 Scope base;
1861
1862 Buf *name;
1863 IrBasicBlock *resume_block;
1864 bool reported_err;
1865};
1866
1846// This scope is created for a comptime expression.1867// This scope is created for a comptime expression.
1847// NodeTypeCompTime, NodeTypeSwitchExpr1868// NodeTypeCompTime, NodeTypeSwitchExpr
1848struct ScopeCompTime {1869struct ScopeCompTime {
...@@ -2039,12 +2060,14 @@ enum IrInstructionId {...@@ -2039,12 +2060,14 @@ enum IrInstructionId {
2039 IrInstructionIdCoroPromise,2060 IrInstructionIdCoroPromise,
2040 IrInstructionIdCoroAllocHelper,2061 IrInstructionIdCoroAllocHelper,
2041 IrInstructionIdAtomicRmw,2062 IrInstructionIdAtomicRmw,
2063 IrInstructionIdAtomicLoad,
2042 IrInstructionIdPromiseResultType,2064 IrInstructionIdPromiseResultType,
2043 IrInstructionIdAwaitBookkeeping,2065 IrInstructionIdAwaitBookkeeping,
2044 IrInstructionIdSaveErrRetAddr,2066 IrInstructionIdSaveErrRetAddr,
2045 IrInstructionIdAddImplicitReturnType,2067 IrInstructionIdAddImplicitReturnType,
2046 IrInstructionIdMergeErrRetTraces,2068 IrInstructionIdMergeErrRetTraces,
2047 IrInstructionIdMarkErrRetTracePtr,2069 IrInstructionIdMarkErrRetTracePtr,
2070 IrInstructionIdSqrt,
2048};2071};
20492072
2050struct IrInstruction {2073struct IrInstruction {
...@@ -2201,7 +2224,8 @@ struct IrInstructionFieldPtr {...@@ -2201,7 +2224,8 @@ struct IrInstructionFieldPtr {
2201 IrInstruction base;2224 IrInstruction base;
22022225
2203 IrInstruction *container_ptr;2226 IrInstruction *container_ptr;
2204 Buf *field_name;2227 Buf *field_name_buffer;
2228 IrInstruction *field_name_expr;
2205 bool is_const;2229 bool is_const;
2206};2230};
22072231
...@@ -2520,6 +2544,7 @@ struct IrInstructionEmbedFile {...@@ -2520,6 +2544,7 @@ struct IrInstructionEmbedFile {
2520struct IrInstructionCmpxchg {2544struct IrInstructionCmpxchg {
2521 IrInstruction base;2545 IrInstruction base;
25222546
2547 IrInstruction *type_value;
2523 IrInstruction *ptr;2548 IrInstruction *ptr;
2524 IrInstruction *cmp_value;2549 IrInstruction *cmp_value;
2525 IrInstruction *new_value;2550 IrInstruction *new_value;
...@@ -2527,8 +2552,13 @@ struct IrInstructionCmpxchg {...@@ -2527,8 +2552,13 @@ struct IrInstructionCmpxchg {
2527 IrInstruction *failure_order_value;2552 IrInstruction *failure_order_value;
25282553
2529 // if this instruction gets to runtime then we know these values:2554 // if this instruction gets to runtime then we know these values:
2555 TypeTableEntry *type;
2530 AtomicOrder success_order;2556 AtomicOrder success_order;
2531 AtomicOrder failure_order;2557 AtomicOrder failure_order;
2558
2559 bool is_weak;
2560
2561 LLVMValueRef tmp_ptr;
2532};2562};
25332563
2534struct IrInstructionFence {2564struct IrInstructionFence {
...@@ -2998,6 +3028,15 @@ struct IrInstructionAtomicRmw {...@@ -2998,6 +3028,15 @@ struct IrInstructionAtomicRmw {
2998 AtomicOrder resolved_ordering;3028 AtomicOrder resolved_ordering;
2999};3029};
30003030
3031struct IrInstructionAtomicLoad {
3032 IrInstruction base;
3033
3034 IrInstruction *operand_type;
3035 IrInstruction *ptr;
3036 IrInstruction *ordering;
3037 AtomicOrder resolved_ordering;
3038};
3039
3001struct IrInstructionPromiseResultType {3040struct IrInstructionPromiseResultType {
3002 IrInstruction base;3041 IrInstruction base;
30033042
...@@ -3034,6 +3073,13 @@ struct IrInstructionMarkErrRetTracePtr {...@@ -3034,6 +3073,13 @@ struct IrInstructionMarkErrRetTracePtr {
3034 IrInstruction *err_ret_trace_ptr;3073 IrInstruction *err_ret_trace_ptr;
3035};3074};
30363075
3076struct IrInstructionSqrt {
3077 IrInstruction base;
3078
3079 IrInstruction *type;
3080 IrInstruction *op;
3081};
3082
3037static const size_t slice_ptr_index = 0;3083static const size_t slice_ptr_index = 0;
3038static const size_t slice_len_index = 1;3084static const size_t slice_len_index = 1;
30393085
src/analyze.cpp+40-17
...@@ -156,6 +156,14 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {...@@ -156,6 +156,14 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {
156 return scope;156 return scope;
157}157}
158158
159ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent) {
160 assert(node->type == NodeTypeSuspend);
161 ScopeSuspend *scope = allocate<ScopeSuspend>(1);
162 init_scope(&scope->base, ScopeIdSuspend, node, parent);
163 scope->name = node->data.suspend.name;
164 return scope;
165}
166
159ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) {167ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry) {
160 ScopeFnDef *scope = allocate<ScopeFnDef>(1);168 ScopeFnDef *scope = allocate<ScopeFnDef>(1);
161 init_scope(&scope->base, ScopeIdFnDef, node, parent);169 init_scope(&scope->base, ScopeIdFnDef, node, parent);
...@@ -3616,6 +3624,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) {...@@ -3616,6 +3624,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) {
3616 case ScopeIdVarDecl:3624 case ScopeIdVarDecl:
3617 case ScopeIdCImport:3625 case ScopeIdCImport:
3618 case ScopeIdLoop:3626 case ScopeIdLoop:
3627 case ScopeIdSuspend:
3619 case ScopeIdCompTime:3628 case ScopeIdCompTime:
3620 case ScopeIdCoroPrelude:3629 case ScopeIdCoroPrelude:
3621 scope = scope->parent;3630 scope = scope->parent;
...@@ -4297,7 +4306,8 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {...@@ -4297,7 +4306,8 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
4297static ZigWindowsSDK *get_windows_sdk(CodeGen *g) {4306static ZigWindowsSDK *get_windows_sdk(CodeGen *g) {
4298 if (g->win_sdk == nullptr) {4307 if (g->win_sdk == nullptr) {
4299 if (os_find_windows_sdk(&g->win_sdk)) {4308 if (os_find_windows_sdk(&g->win_sdk)) {
4300 zig_panic("Unable to determine Windows SDK path.");4309 fprintf(stderr, "unable to determine windows sdk path\n");
4310 exit(1);
4301 }4311 }
4302 }4312 }
4303 assert(g->win_sdk != nullptr);4313 assert(g->win_sdk != nullptr);
...@@ -4399,7 +4409,8 @@ void find_libc_include_path(CodeGen *g) {...@@ -4399,7 +4409,8 @@ void find_libc_include_path(CodeGen *g) {
4399 ZigWindowsSDK *sdk = get_windows_sdk(g);4409 ZigWindowsSDK *sdk = get_windows_sdk(g);
4400 g->libc_include_dir = buf_alloc();4410 g->libc_include_dir = buf_alloc();
4401 if (os_get_win32_ucrt_include_path(sdk, g->libc_include_dir)) {4411 if (os_get_win32_ucrt_include_path(sdk, g->libc_include_dir)) {
4402 zig_panic("Unable to determine libc include path.");4412 fprintf(stderr, "Unable to determine libc include path. --libc-include-dir");
4413 exit(1);
4403 }4414 }
4404 } else if (g->zig_target.os == OsLinux) {4415 } else if (g->zig_target.os == OsLinux) {
4405 g->libc_include_dir = get_linux_libc_include_path();4416 g->libc_include_dir = get_linux_libc_include_path();
...@@ -4421,24 +4432,33 @@ void find_libc_lib_path(CodeGen *g) {...@@ -4421,24 +4432,33 @@ void find_libc_lib_path(CodeGen *g) {
4421 if (g->zig_target.os == OsWindows) {4432 if (g->zig_target.os == OsWindows) {
4422 ZigWindowsSDK *sdk = get_windows_sdk(g);4433 ZigWindowsSDK *sdk = get_windows_sdk(g);
44234434
4424 Buf* vc_lib_dir = buf_alloc();4435 if (g->msvc_lib_dir == nullptr) {
4425 if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) {4436 Buf* vc_lib_dir = buf_alloc();
4426 zig_panic("Unable to determine vcruntime path.");4437 if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) {
4438 fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir");
4439 exit(1);
4440 }
4441 g->msvc_lib_dir = vc_lib_dir;
4427 }4442 }
44284443
4429 Buf* ucrt_lib_path = buf_alloc();4444 if (g->libc_lib_dir == nullptr) {
4430 if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) {4445 Buf* ucrt_lib_path = buf_alloc();
4431 zig_panic("Unable to determine ucrt path.");4446 if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) {
4447 fprintf(stderr, "Unable to determine ucrt path. --libc-lib-dir");
4448 exit(1);
4449 }
4450 g->libc_lib_dir = ucrt_lib_path;
4432 }4451 }
44334452
4434 Buf* kern_lib_path = buf_alloc();4453 if (g->kernel32_lib_dir == nullptr) {
4435 if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) {4454 Buf* kern_lib_path = buf_alloc();
4436 zig_panic("Unable to determine kernel32 path.");4455 if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) {
4456 fprintf(stderr, "Unable to determine kernel32 path. --kernel32-lib-dir");
4457 exit(1);
4458 }
4459 g->kernel32_lib_dir = kern_lib_path;
4437 }4460 }
44384461
4439 g->msvc_lib_dir = vc_lib_dir;
4440 g->libc_lib_dir = ucrt_lib_path;
4441 g->kernel32_lib_dir = kern_lib_path;
4442 } else if (g->zig_target.os == OsLinux) {4462 } else if (g->zig_target.os == OsLinux) {
4443 g->libc_lib_dir = get_linux_libc_lib_path("crt1.o");4463 g->libc_lib_dir = get_linux_libc_lib_path("crt1.o");
4444 } else {4464 } else {
...@@ -5801,9 +5821,11 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {...@@ -5801,9 +5821,11 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
5801 case ZigLLVMFnIdClz:5821 case ZigLLVMFnIdClz:
5802 return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;5822 return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;
5803 case ZigLLVMFnIdFloor:5823 case ZigLLVMFnIdFloor:
5804 return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1899859168;5824 return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168;
5805 case ZigLLVMFnIdCeil:5825 case ZigLLVMFnIdCeil:
5806 return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1953839089;5826 return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1953839089;
5827 case ZigLLVMFnIdSqrt:
5828 return (uint32_t)(x.data.floating.bit_count) * (uint32_t)2225366385;
5807 case ZigLLVMFnIdOverflowArithmetic:5829 case ZigLLVMFnIdOverflowArithmetic:
5808 return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +5830 return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
5809 ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +5831 ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +
...@@ -5822,7 +5844,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {...@@ -5822,7 +5844,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
5822 return a.data.clz.bit_count == b.data.clz.bit_count;5844 return a.data.clz.bit_count == b.data.clz.bit_count;
5823 case ZigLLVMFnIdFloor:5845 case ZigLLVMFnIdFloor:
5824 case ZigLLVMFnIdCeil:5846 case ZigLLVMFnIdCeil:
5825 return a.data.floor_ceil.bit_count == b.data.floor_ceil.bit_count;5847 case ZigLLVMFnIdSqrt:
5848 return a.data.floating.bit_count == b.data.floating.bit_count;
5826 case ZigLLVMFnIdOverflowArithmetic:5849 case ZigLLVMFnIdOverflowArithmetic:
5827 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&5850 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
5828 (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&5851 (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
src/analyze.hpp+1
...@@ -104,6 +104,7 @@ ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent);...@@ -104,6 +104,7 @@ ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent);
104Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var);104Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var);
105ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent);105ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent);
106ScopeLoop *create_loop_scope(AstNode *node, Scope *parent);106ScopeLoop *create_loop_scope(AstNode *node, Scope *parent);
107ScopeSuspend *create_suspend_scope(AstNode *node, Scope *parent);
107ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry);108ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry);
108ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import);109ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import);
109Scope *create_comptime_scope(AstNode *node, Scope *parent);110Scope *create_comptime_scope(AstNode *node, Scope *parent);
src/bigfloat.cpp+4
...@@ -181,3 +181,7 @@ bool bigfloat_has_fraction(const BigFloat *bigfloat) {...@@ -181,3 +181,7 @@ bool bigfloat_has_fraction(const BigFloat *bigfloat) {
181 f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored);181 f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored);
182 return !f128M_eq(&floored, &bigfloat->value);182 return !f128M_eq(&floored, &bigfloat->value);
183}183}
184
185void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) {
186 f128M_sqrt(&op->value, &dest->value);
187}
src/bigfloat.hpp+1
...@@ -42,6 +42,7 @@ void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2...@@ -42,6 +42,7 @@ void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2
42void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);42void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
43void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);43void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
44void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);44void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
45void bigfloat_sqrt(BigFloat *dest, const BigFloat *op);
45void bigfloat_append_buf(Buf *buf, const BigFloat *op);46void bigfloat_append_buf(Buf *buf, const BigFloat *op);
46Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);47Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);
4748
src/bigint.cpp+5
...@@ -86,6 +86,11 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count)...@@ -86,6 +86,11 @@ static void to_twos_complement(BigInt *dest, const BigInt *op, size_t bit_count)
86 size_t digits_to_copy = bit_count / 64;86 size_t digits_to_copy = bit_count / 64;
87 size_t leftover_bits = bit_count % 64;87 size_t leftover_bits = bit_count % 64;
88 dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1);88 dest->digit_count = digits_to_copy + ((leftover_bits == 0) ? 0 : 1);
89 if (dest->digit_count == 1 && leftover_bits == 0) {
90 dest->data.digit = op_digits[0];
91 if (dest->data.digit == 0) dest->digit_count = 0;
92 return;
93 }
89 dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);94 dest->data.digits = allocate_nonzero<uint64_t>(dest->digit_count);
90 for (size_t i = 0; i < digits_to_copy; i += 1) {95 for (size_t i = 0; i < digits_to_copy; i += 1) {
91 uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0;96 uint64_t digit = (i < op->digit_count) ? op_digits[i] : 0;
src/codegen.cpp+81-19
...@@ -471,7 +471,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {...@@ -471,7 +471,7 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
471 fn_table_entry->llvm_value, buf_ptr(&fn_export->name));471 fn_table_entry->llvm_value, buf_ptr(&fn_export->name));
472 }472 }
473 }473 }
474 fn_table_entry->llvm_name = LLVMGetValueName(fn_table_entry->llvm_value);474 fn_table_entry->llvm_name = strdup(LLVMGetValueName(fn_table_entry->llvm_value));
475475
476 switch (fn_table_entry->fn_inline) {476 switch (fn_table_entry->fn_inline) {
477 case FnInlineAlways:477 case FnInlineAlways:
...@@ -516,7 +516,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {...@@ -516,7 +516,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
516 }516 }
517517
518 if (fn_table_entry->body_node != nullptr) {518 if (fn_table_entry->body_node != nullptr) {
519 bool want_fn_safety = g->build_mode != BuildModeFastRelease && !fn_table_entry->def_scope->safety_off;519 bool want_fn_safety = g->build_mode != BuildModeFastRelease &&
520 g->build_mode != BuildModeSmallRelease &&
521 !fn_table_entry->def_scope->safety_off;
520 if (want_fn_safety) {522 if (want_fn_safety) {
521 if (g->libc_link_lib != nullptr) {523 if (g->libc_link_lib != nullptr) {
522 addLLVMFnAttr(fn_table_entry->llvm_value, "sspstrong");524 addLLVMFnAttr(fn_table_entry->llvm_value, "sspstrong");
...@@ -656,6 +658,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {...@@ -656,6 +658,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
656 }658 }
657 case ScopeIdDeferExpr:659 case ScopeIdDeferExpr:
658 case ScopeIdLoop:660 case ScopeIdLoop:
661 case ScopeIdSuspend:
659 case ScopeIdCompTime:662 case ScopeIdCompTime:
660 case ScopeIdCoroPrelude:663 case ScopeIdCoroPrelude:
661 return get_di_scope(g, scope->parent);664 return get_di_scope(g, scope->parent);
...@@ -721,12 +724,12 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,...@@ -721,12 +724,12 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
721 return fn_val;724 return fn_val;
722}725}
723726
724static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) {727static LLVMValueRef get_float_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) {
725 assert(type_entry->id == TypeTableEntryIdFloat);728 assert(type_entry->id == TypeTableEntryIdFloat);
726729
727 ZigLLVMFnKey key = {};730 ZigLLVMFnKey key = {};
728 key.id = fn_id;731 key.id = fn_id;
729 key.data.floor_ceil.bit_count = (uint32_t)type_entry->data.floating.bit_count;732 key.data.floating.bit_count = (uint32_t)type_entry->data.floating.bit_count;
730733
731 auto existing_entry = g->llvm_fn_table.maybe_get(key);734 auto existing_entry = g->llvm_fn_table.maybe_get(key);
732 if (existing_entry)735 if (existing_entry)
...@@ -737,6 +740,8 @@ static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, Zi...@@ -737,6 +740,8 @@ static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, Zi
737 name = "floor";740 name = "floor";
738 } else if (fn_id == ZigLLVMFnIdCeil) {741 } else if (fn_id == ZigLLVMFnIdCeil) {
739 name = "ceil";742 name = "ceil";
743 } else if (fn_id == ZigLLVMFnIdSqrt) {
744 name = "sqrt";
740 } else {745 } else {
741 zig_unreachable();746 zig_unreachable();
742 }747 }
...@@ -819,7 +824,7 @@ static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) {...@@ -819,7 +824,7 @@ static bool ir_want_fast_math(CodeGen *g, IrInstruction *instruction) {
819}824}
820825
821static bool ir_want_runtime_safety(CodeGen *g, IrInstruction *instruction) {826static bool ir_want_runtime_safety(CodeGen *g, IrInstruction *instruction) {
822 if (g->build_mode == BuildModeFastRelease)827 if (g->build_mode == BuildModeFastRelease || g->build_mode == BuildModeSmallRelease)
823 return false;828 return false;
824829
825 // TODO memoize830 // TODO memoize
...@@ -1867,7 +1872,7 @@ static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, TypeTableEntry *type...@@ -1867,7 +1872,7 @@ static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, TypeTableEntry *type
1867 if (type_entry->id == TypeTableEntryIdInt)1872 if (type_entry->id == TypeTableEntryIdInt)
1868 return val;1873 return val;
18691874
1870 LLVMValueRef floor_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdFloor);1875 LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloor);
1871 return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");1876 return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");
1872}1877}
18731878
...@@ -1875,7 +1880,7 @@ static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, TypeTableEntry *type_...@@ -1875,7 +1880,7 @@ static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, TypeTableEntry *type_
1875 if (type_entry->id == TypeTableEntryIdInt)1880 if (type_entry->id == TypeTableEntryIdInt)
1876 return val;1881 return val;
18771882
1878 LLVMValueRef ceil_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdCeil);1883 LLVMValueRef ceil_fn = get_float_fn(g, type_entry, ZigLLVMFnIdCeil);
1879 return LLVMBuildCall(g->builder, ceil_fn, &val, 1, "");1884 return LLVMBuildCall(g->builder, ceil_fn, &val, 1, "");
1880}1885}
18811886
...@@ -3186,10 +3191,12 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui...@@ -3186,10 +3191,12 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui
3186 fn_name = "cttz";3191 fn_name = "cttz";
3187 key.id = ZigLLVMFnIdCtz;3192 key.id = ZigLLVMFnIdCtz;
3188 key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count;3193 key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count;
3189 } else {3194 } else if (fn_id == BuiltinFnIdClz) {
3190 fn_name = "ctlz";3195 fn_name = "ctlz";
3191 key.id = ZigLLVMFnIdClz;3196 key.id = ZigLLVMFnIdClz;
3192 key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count;3197 key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count;
3198 } else {
3199 zig_unreachable();
3193 }3200 }
31943201
3195 auto existing_entry = g->llvm_fn_table.maybe_get(key);3202 auto existing_entry = g->llvm_fn_table.maybe_get(key);
...@@ -3491,9 +3498,30 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn...@@ -3491,9 +3498,30 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn
3491 LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order);3498 LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order);
34923499
3493 LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val,3500 LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val,
3494 success_order, failure_order);3501 success_order, failure_order, instruction->is_weak);
34953502
3496 return LLVMBuildExtractValue(g->builder, result_val, 1, "");3503 TypeTableEntry *maybe_type = instruction->base.value.type;
3504 assert(maybe_type->id == TypeTableEntryIdMaybe);
3505 TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
3506
3507 if (type_is_codegen_pointer(child_type)) {
3508 LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, "");
3509 LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, "");
3510 return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(child_type->type_ref), payload_val, "");
3511 }
3512
3513 assert(instruction->tmp_ptr != nullptr);
3514 assert(type_has_bits(instruction->type));
3515
3516 LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, "");
3517 LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, "");
3518 gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val);
3519
3520 LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, "");
3521 LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, "");
3522 LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, "");
3523 gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false);
3524 return instruction->tmp_ptr;
3497}3525}
34983526
3499static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) {3527static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) {
...@@ -4296,6 +4324,16 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable,...@@ -4296,6 +4324,16 @@ static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutable *executable,
4296 return LLVMBuildIntToPtr(g->builder, uncasted_result, operand_type->type_ref, "");4324 return LLVMBuildIntToPtr(g->builder, uncasted_result, operand_type->type_ref, "");
4297}4325}
42984326
4327static LLVMValueRef ir_render_atomic_load(CodeGen *g, IrExecutable *executable,
4328 IrInstructionAtomicLoad *instruction)
4329{
4330 LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->resolved_ordering);
4331 LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr);
4332 LLVMValueRef load_inst = gen_load(g, ptr, instruction->ptr->value.type, "");
4333 LLVMSetOrdering(load_inst, ordering);
4334 return load_inst;
4335}
4336
4299static LLVMValueRef ir_render_merge_err_ret_traces(CodeGen *g, IrExecutable *executable,4337static LLVMValueRef ir_render_merge_err_ret_traces(CodeGen *g, IrExecutable *executable,
4300 IrInstructionMergeErrRetTraces *instruction)4338 IrInstructionMergeErrRetTraces *instruction)
4301{4339{
...@@ -4317,6 +4355,13 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e...@@ -4317,6 +4355,13 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e
4317 return nullptr;4355 return nullptr;
4318}4356}
43194357
4358static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstructionSqrt *instruction) {
4359 LLVMValueRef op = ir_llvm_value(g, instruction->op);
4360 assert(instruction->base.value.type->id == TypeTableEntryIdFloat);
4361 LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdSqrt);
4362 return LLVMBuildCall(g->builder, fn_val, &op, 1, "");
4363}
4364
4320static void set_debug_location(CodeGen *g, IrInstruction *instruction) {4365static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
4321 AstNode *source_node = instruction->source_node;4366 AstNode *source_node = instruction->source_node;
4322 Scope *scope = instruction->scope;4367 Scope *scope = instruction->scope;
...@@ -4532,12 +4577,16 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -4532,12 +4577,16 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
4532 return ir_render_coro_alloc_helper(g, executable, (IrInstructionCoroAllocHelper *)instruction);4577 return ir_render_coro_alloc_helper(g, executable, (IrInstructionCoroAllocHelper *)instruction);
4533 case IrInstructionIdAtomicRmw:4578 case IrInstructionIdAtomicRmw:
4534 return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction);4579 return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction);
4580 case IrInstructionIdAtomicLoad:
4581 return ir_render_atomic_load(g, executable, (IrInstructionAtomicLoad *)instruction);
4535 case IrInstructionIdSaveErrRetAddr:4582 case IrInstructionIdSaveErrRetAddr:
4536 return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction);4583 return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction);
4537 case IrInstructionIdMergeErrRetTraces:4584 case IrInstructionIdMergeErrRetTraces:
4538 return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction);4585 return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction);
4539 case IrInstructionIdMarkErrRetTracePtr:4586 case IrInstructionIdMarkErrRetTracePtr:
4540 return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction);4587 return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction);
4588 case IrInstructionIdSqrt:
4589 return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction);
4541 }4590 }
4542 zig_unreachable();4591 zig_unreachable();
4543}4592}
...@@ -5476,6 +5525,9 @@ static void do_code_gen(CodeGen *g) {...@@ -5476,6 +5525,9 @@ static void do_code_gen(CodeGen *g) {
5476 } else if (instruction->id == IrInstructionIdErrWrapCode) {5525 } else if (instruction->id == IrInstructionIdErrWrapCode) {
5477 IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction;5526 IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction;
5478 slot = &err_wrap_code_instruction->tmp_ptr;5527 slot = &err_wrap_code_instruction->tmp_ptr;
5528 } else if (instruction->id == IrInstructionIdCmpxchg) {
5529 IrInstructionCmpxchg *cmpxchg_instruction = (IrInstructionCmpxchg *)instruction;
5530 slot = &cmpxchg_instruction->tmp_ptr;
5479 } else {5531 } else {
5480 zig_unreachable();5532 zig_unreachable();
5481 }5533 }
...@@ -5637,10 +5689,12 @@ static void do_code_gen(CodeGen *g) {...@@ -5637,10 +5689,12 @@ static void do_code_gen(CodeGen *g) {
5637 os_path_join(g->cache_dir, o_basename, output_path);5689 os_path_join(g->cache_dir, o_basename, output_path);
5638 ensure_cache_dir(g);5690 ensure_cache_dir(g);
56395691
5692 bool is_small = g->build_mode == BuildModeSmallRelease;
5693
5640 switch (g->emit_file_type) {5694 switch (g->emit_file_type) {
5641 case EmitFileTypeBinary:5695 case EmitFileTypeBinary:
5642 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),5696 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
5643 ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug))5697 ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug, is_small))
5644 {5698 {
5645 zig_panic("unable to write object file %s: %s", buf_ptr(output_path), err_msg);5699 zig_panic("unable to write object file %s: %s", buf_ptr(output_path), err_msg);
5646 }5700 }
...@@ -5650,7 +5704,7 @@ static void do_code_gen(CodeGen *g) {...@@ -5650,7 +5704,7 @@ static void do_code_gen(CodeGen *g) {
56505704
5651 case EmitFileTypeAssembly:5705 case EmitFileTypeAssembly:
5652 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),5706 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
5653 ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug))5707 ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug, is_small))
5654 {5708 {
5655 zig_panic("unable to write assembly file %s: %s", buf_ptr(output_path), err_msg);5709 zig_panic("unable to write assembly file %s: %s", buf_ptr(output_path), err_msg);
5656 }5710 }
...@@ -5659,7 +5713,7 @@ static void do_code_gen(CodeGen *g) {...@@ -5659,7 +5713,7 @@ static void do_code_gen(CodeGen *g) {
56595713
5660 case EmitFileTypeLLVMIr:5714 case EmitFileTypeLLVMIr:
5661 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),5715 if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
5662 ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug))5716 ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug, is_small))
5663 {5717 {
5664 zig_panic("unable to write llvm-ir file %s: %s", buf_ptr(output_path), err_msg);5718 zig_panic("unable to write llvm-ir file %s: %s", buf_ptr(output_path), err_msg);
5665 }5719 }
...@@ -5985,6 +6039,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -5985,6 +6039,7 @@ static void define_builtin_fns(CodeGen *g) {
5985 create_builtin_fn(g, BuiltinFnIdMemberCount, "memberCount", 1);6039 create_builtin_fn(g, BuiltinFnIdMemberCount, "memberCount", 1);
5986 create_builtin_fn(g, BuiltinFnIdMemberType, "memberType", 2);6040 create_builtin_fn(g, BuiltinFnIdMemberType, "memberType", 2);
5987 create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2);6041 create_builtin_fn(g, BuiltinFnIdMemberName, "memberName", 2);
6042 create_builtin_fn(g, BuiltinFnIdField, "field", 2);
5988 create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf6043 create_builtin_fn(g, BuiltinFnIdTypeof, "typeOf", 1); // TODO rename to TypeOf
5989 create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4);6044 create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4);
5990 create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4);6045 create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4);
...@@ -6001,7 +6056,8 @@ static void define_builtin_fns(CodeGen *g) {...@@ -6001,7 +6056,8 @@ static void define_builtin_fns(CodeGen *g) {
6001 create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1);6056 create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1);
6002 create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2);6057 create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2);
6003 create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);6058 create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);
6004 create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5);6059 create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6);
6060 create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6);
6005 create_builtin_fn(g, BuiltinFnIdFence, "fence", 1);6061 create_builtin_fn(g, BuiltinFnIdFence, "fence", 1);
6006 create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2);6062 create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2);
6007 create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);6063 create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1);
...@@ -6024,6 +6080,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -6024,6 +6080,7 @@ static void define_builtin_fns(CodeGen *g) {
6024 create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2);6080 create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2);
6025 create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);6081 create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
6026 create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);6082 create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
6083 create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2);
6027 create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);6084 create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
6028 create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);6085 create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
6029 create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);6086 create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
...@@ -6037,6 +6094,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -6037,6 +6094,7 @@ static void define_builtin_fns(CodeGen *g) {
6037 create_builtin_fn(g, BuiltinFnIdExport, "export", 3);6094 create_builtin_fn(g, BuiltinFnIdExport, "export", 3);
6038 create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);6095 create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);
6039 create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5);6096 create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5);
6097 create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3);
6040}6098}
60416099
6042static const char *bool_to_str(bool b) {6100static const char *bool_to_str(bool b) {
...@@ -6048,6 +6106,7 @@ static const char *build_mode_to_str(BuildMode build_mode) {...@@ -6048,6 +6106,7 @@ static const char *build_mode_to_str(BuildMode build_mode) {
6048 case BuildModeDebug: return "Mode.Debug";6106 case BuildModeDebug: return "Mode.Debug";
6049 case BuildModeSafeRelease: return "Mode.ReleaseSafe";6107 case BuildModeSafeRelease: return "Mode.ReleaseSafe";
6050 case BuildModeFastRelease: return "Mode.ReleaseFast";6108 case BuildModeFastRelease: return "Mode.ReleaseFast";
6109 case BuildModeSmallRelease: return "Mode.ReleaseSmall";
6051 }6110 }
6052 zig_unreachable();6111 zig_unreachable();
6053}6112}
...@@ -6188,6 +6247,7 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -6188,6 +6247,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
6188 " Debug,\n"6247 " Debug,\n"
6189 " ReleaseSafe,\n"6248 " ReleaseSafe,\n"
6190 " ReleaseFast,\n"6249 " ReleaseFast,\n"
6250 " ReleaseSmall,\n"
6191 "};\n\n");6251 "};\n\n");
6192 }6252 }
6193 {6253 {
...@@ -6238,7 +6298,8 @@ static void define_builtin_compile_vars(CodeGen *g) {...@@ -6238,7 +6298,8 @@ static void define_builtin_compile_vars(CodeGen *g) {
6238 int err;6298 int err;
6239 Buf *abs_full_path = buf_alloc();6299 Buf *abs_full_path = buf_alloc();
6240 if ((err = os_path_real(builtin_zig_path, abs_full_path))) {6300 if ((err = os_path_real(builtin_zig_path, abs_full_path))) {
6241 zig_panic("unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err));6301 fprintf(stderr, "unable to open '%s': %s", buf_ptr(builtin_zig_path), err_str(err));
6302 exit(1);
6242 }6303 }
62436304
6244 assert(g->root_package);6305 assert(g->root_package);
...@@ -6358,7 +6419,7 @@ static void init(CodeGen *g) {...@@ -6358,7 +6419,7 @@ static void init(CodeGen *g) {
6358 }6419 }
6359 }6420 }
63606421
6361 g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease;6422 g->have_err_ret_tracing = g->build_mode != BuildModeFastRelease && g->build_mode != BuildModeSmallRelease;
63626423
6363 define_builtin_fns(g);6424 define_builtin_fns(g);
6364 define_builtin_compile_vars(g);6425 define_builtin_compile_vars(g);
...@@ -6492,12 +6553,14 @@ static void gen_root_source(CodeGen *g) {...@@ -6492,12 +6553,14 @@ static void gen_root_source(CodeGen *g) {
6492 Buf *abs_full_path = buf_alloc();6553 Buf *abs_full_path = buf_alloc();
6493 int err;6554 int err;
6494 if ((err = os_path_real(rel_full_path, abs_full_path))) {6555 if ((err = os_path_real(rel_full_path, abs_full_path))) {
6495 zig_panic("unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));6556 fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));
6557 exit(1);
6496 }6558 }
64976559
6498 Buf *source_code = buf_alloc();6560 Buf *source_code = buf_alloc();
6499 if ((err = os_fetch_file_path(rel_full_path, source_code, true))) {6561 if ((err = os_fetch_file_path(rel_full_path, source_code, true))) {
6500 zig_panic("unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));6562 fprintf(stderr, "unable to open '%s': %s", buf_ptr(rel_full_path), err_str(err));
6563 exit(1);
6501 }6564 }
65026565
6503 g->root_import = add_source_file(g, g->root_package, abs_full_path, source_code);6566 g->root_import = add_source_file(g, g->root_package, abs_full_path, source_code);
...@@ -7050,4 +7113,3 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir,...@@ -7050,4 +7113,3 @@ PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir,
7050 }7113 }
7051 return pkg;7114 return pkg;
7052}7115}
7053
src/ir.cpp+499-137
...@@ -110,6 +110,8 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_...@@ -110,6 +110,8 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_
110 IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type);110 IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type);
111static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,111static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
112 VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr);112 VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr);
113static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op);
114static IrInstruction *ir_lval_wrap(IrBuilder *irb, Scope *scope, IrInstruction *value, LVal lval);
113115
114ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {116ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) {
115 assert(const_val->type->id == TypeTableEntryIdPointer);117 assert(const_val->type->id == TypeTableEntryIdPointer);
...@@ -709,6 +711,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) {...@@ -709,6 +711,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicRmw *) {
709 return IrInstructionIdAtomicRmw;711 return IrInstructionIdAtomicRmw;
710}712}
711713
714static constexpr IrInstructionId ir_instruction_id(IrInstructionAtomicLoad *) {
715 return IrInstructionIdAtomicLoad;
716}
717
712static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseResultType *) {718static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseResultType *) {
713 return IrInstructionIdPromiseResultType;719 return IrInstructionIdPromiseResultType;
714}720}
...@@ -733,6 +739,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP...@@ -733,6 +739,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP
733 return IrInstructionIdMarkErrRetTracePtr;739 return IrInstructionIdMarkErrRetTracePtr;
734}740}
735741
742static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) {
743 return IrInstructionIdSqrt;
744}
745
736template<typename T>746template<typename T>
737static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {747static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
738 T *special_instruction = allocate<T>(1);748 T *special_instruction = allocate<T>(1);
...@@ -1024,12 +1034,27 @@ static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_...@@ -1024,12 +1034,27 @@ static IrInstruction *ir_build_elem_ptr_from(IrBuilder *irb, IrInstruction *old_
1024 return new_instruction;1034 return new_instruction;
1025}1035}
10261036
1037static IrInstruction *ir_build_field_ptr_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node,
1038 IrInstruction *container_ptr, IrInstruction *field_name_expr)
1039{
1040 IrInstructionFieldPtr *instruction = ir_build_instruction<IrInstructionFieldPtr>(irb, scope, source_node);
1041 instruction->container_ptr = container_ptr;
1042 instruction->field_name_buffer = nullptr;
1043 instruction->field_name_expr = field_name_expr;
1044
1045 ir_ref_instruction(container_ptr, irb->current_basic_block);
1046 ir_ref_instruction(field_name_expr, irb->current_basic_block);
1047
1048 return &instruction->base;
1049}
1050
1027static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,1051static IrInstruction *ir_build_field_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node,
1028 IrInstruction *container_ptr, Buf *field_name)1052 IrInstruction *container_ptr, Buf *field_name)
1029{1053{
1030 IrInstructionFieldPtr *instruction = ir_build_instruction<IrInstructionFieldPtr>(irb, scope, source_node);1054 IrInstructionFieldPtr *instruction = ir_build_instruction<IrInstructionFieldPtr>(irb, scope, source_node);
1031 instruction->container_ptr = container_ptr;1055 instruction->container_ptr = container_ptr;
1032 instruction->field_name = field_name;1056 instruction->field_name_buffer = field_name;
1057 instruction->field_name_expr = nullptr;
10331058
1034 ir_ref_instruction(container_ptr, irb->current_basic_block);1059 ir_ref_instruction(container_ptr, irb->current_basic_block);
10351060
...@@ -1824,38 +1849,34 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode...@@ -1824,38 +1849,34 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode
1824 return &instruction->base;1849 return &instruction->base;
1825}1850}
18261851
1827static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr,1852static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value,
1828 IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value,1853 IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value,
1829 AtomicOrder success_order, AtomicOrder failure_order)1854 IrInstruction *success_order_value, IrInstruction *failure_order_value,
1855 bool is_weak,
1856 TypeTableEntry *type, AtomicOrder success_order, AtomicOrder failure_order)
1830{1857{
1831 IrInstructionCmpxchg *instruction = ir_build_instruction<IrInstructionCmpxchg>(irb, scope, source_node);1858 IrInstructionCmpxchg *instruction = ir_build_instruction<IrInstructionCmpxchg>(irb, scope, source_node);
1859 instruction->type_value = type_value;
1832 instruction->ptr = ptr;1860 instruction->ptr = ptr;
1833 instruction->cmp_value = cmp_value;1861 instruction->cmp_value = cmp_value;
1834 instruction->new_value = new_value;1862 instruction->new_value = new_value;
1835 instruction->success_order_value = success_order_value;1863 instruction->success_order_value = success_order_value;
1836 instruction->failure_order_value = failure_order_value;1864 instruction->failure_order_value = failure_order_value;
1865 instruction->is_weak = is_weak;
1866 instruction->type = type;
1837 instruction->success_order = success_order;1867 instruction->success_order = success_order;
1838 instruction->failure_order = failure_order;1868 instruction->failure_order = failure_order;
18391869
1870 if (type_value != nullptr) ir_ref_instruction(type_value, irb->current_basic_block);
1840 ir_ref_instruction(ptr, irb->current_basic_block);1871 ir_ref_instruction(ptr, irb->current_basic_block);
1841 ir_ref_instruction(cmp_value, irb->current_basic_block);1872 ir_ref_instruction(cmp_value, irb->current_basic_block);
1842 ir_ref_instruction(new_value, irb->current_basic_block);1873 ir_ref_instruction(new_value, irb->current_basic_block);
1843 ir_ref_instruction(success_order_value, irb->current_basic_block);1874 if (type_value != nullptr) ir_ref_instruction(success_order_value, irb->current_basic_block);
1844 ir_ref_instruction(failure_order_value, irb->current_basic_block);1875 if (type_value != nullptr) ir_ref_instruction(failure_order_value, irb->current_basic_block);
18451876
1846 return &instruction->base;1877 return &instruction->base;
1847}1878}
18481879
1849static IrInstruction *ir_build_cmpxchg_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr,
1850 IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value,
1851 AtomicOrder success_order, AtomicOrder failure_order)
1852{
1853 IrInstruction *new_instruction = ir_build_cmpxchg(irb, old_instruction->scope, old_instruction->source_node,
1854 ptr, cmp_value, new_value, success_order_value, failure_order_value, success_order, failure_order);
1855 ir_link_new_instruction(new_instruction, old_instruction);
1856 return new_instruction;
1857}
1858
1859static IrInstruction *ir_build_fence(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *order_value, AtomicOrder order) {1880static IrInstruction *ir_build_fence(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *order_value, AtomicOrder order) {
1860 IrInstructionFence *instruction = ir_build_instruction<IrInstructionFence>(irb, scope, source_node);1881 IrInstructionFence *instruction = ir_build_instruction<IrInstructionFence>(irb, scope, source_node);
1861 instruction->order_value = order_value;1882 instruction->order_value = order_value;
...@@ -2669,6 +2690,23 @@ static IrInstruction *ir_build_atomic_rmw(IrBuilder *irb, Scope *scope, AstNode...@@ -2669,6 +2690,23 @@ static IrInstruction *ir_build_atomic_rmw(IrBuilder *irb, Scope *scope, AstNode
2669 return &instruction->base;2690 return &instruction->base;
2670}2691}
26712692
2693static IrInstruction *ir_build_atomic_load(IrBuilder *irb, Scope *scope, AstNode *source_node,
2694 IrInstruction *operand_type, IrInstruction *ptr,
2695 IrInstruction *ordering, AtomicOrder resolved_ordering)
2696{
2697 IrInstructionAtomicLoad *instruction = ir_build_instruction<IrInstructionAtomicLoad>(irb, scope, source_node);
2698 instruction->operand_type = operand_type;
2699 instruction->ptr = ptr;
2700 instruction->ordering = ordering;
2701 instruction->resolved_ordering = resolved_ordering;
2702
2703 if (operand_type != nullptr) ir_ref_instruction(operand_type, irb->current_basic_block);
2704 ir_ref_instruction(ptr, irb->current_basic_block);
2705 if (ordering != nullptr) ir_ref_instruction(ordering, irb->current_basic_block);
2706
2707 return &instruction->base;
2708}
2709
2672static IrInstruction *ir_build_promise_result_type(IrBuilder *irb, Scope *scope, AstNode *source_node,2710static IrInstruction *ir_build_promise_result_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
2673 IrInstruction *promise_type)2711 IrInstruction *promise_type)
2674{2712{
...@@ -2731,6 +2769,17 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco...@@ -2731,6 +2769,17 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco
2731 return &instruction->base;2769 return &instruction->base;
2732}2770}
27332771
2772static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
2773 IrInstructionSqrt *instruction = ir_build_instruction<IrInstructionSqrt>(irb, scope, source_node);
2774 instruction->type = type;
2775 instruction->op = op;
2776
2777 if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
2778 ir_ref_instruction(op, irb->current_basic_block);
2779
2780 return &instruction->base;
2781}
2782
2734static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {2783static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
2735 results[ReturnKindUnconditional] = 0;2784 results[ReturnKindUnconditional] = 0;
2736 results[ReturnKindError] = 0;2785 results[ReturnKindError] = 0;
...@@ -2796,6 +2845,18 @@ static void ir_set_cursor_at_end_and_append_block(IrBuilder *irb, IrBasicBlock *...@@ -2796,6 +2845,18 @@ static void ir_set_cursor_at_end_and_append_block(IrBuilder *irb, IrBasicBlock *
2796 ir_set_cursor_at_end(irb, basic_block);2845 ir_set_cursor_at_end(irb, basic_block);
2797}2846}
27982847
2848static ScopeSuspend *get_scope_suspend(Scope *scope) {
2849 while (scope) {
2850 if (scope->id == ScopeIdSuspend)
2851 return (ScopeSuspend *)scope;
2852 if (scope->id == ScopeIdFnDef)
2853 return nullptr;
2854
2855 scope = scope->parent;
2856 }
2857 return nullptr;
2858}
2859
2799static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {2860static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {
2800 while (scope) {2861 while (scope) {
2801 if (scope->id == ScopeIdDeferExpr)2862 if (scope->id == ScopeIdDeferExpr)
...@@ -3086,6 +3147,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode...@@ -3086,6 +3147,9 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
3086 if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) {3147 if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) {
3087 return noreturn_return_value;3148 return noreturn_return_value;
3088 }3149 }
3150
3151 ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block);
3152 return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
3089 } else {3153 } else {
3090 incoming_blocks.append(irb->current_basic_block);3154 incoming_blocks.append(irb->current_basic_block);
3091 incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node)));3155 incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node)));
...@@ -3479,7 +3543,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode...@@ -3479,7 +3543,7 @@ static IrInstruction *ir_gen_array_access(IrBuilder *irb, Scope *scope, AstNode
3479 return ir_build_load_ptr(irb, scope, node, ptr_instruction);3543 return ir_build_load_ptr(irb, scope, node, ptr_instruction);
3480}3544}
34813545
3482static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {3546static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode *node) {
3483 assert(node->type == NodeTypeFieldAccessExpr);3547 assert(node->type == NodeTypeFieldAccessExpr);
34843548
3485 AstNode *container_ref_node = node->data.field_access_expr.struct_expr;3549 AstNode *container_ref_node = node->data.field_access_expr.struct_expr;
...@@ -3489,11 +3553,7 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode...@@ -3489,11 +3553,7 @@ static IrInstruction *ir_gen_field_access(IrBuilder *irb, Scope *scope, AstNode
3489 if (container_ref_instruction == irb->codegen->invalid_instruction)3553 if (container_ref_instruction == irb->codegen->invalid_instruction)
3490 return container_ref_instruction;3554 return container_ref_instruction;
34913555
3492 IrInstruction *ptr_instruction = ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name);3556 return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name);
3493 if (lval.is_ptr)
3494 return ptr_instruction;
3495
3496 return ir_build_load_ptr(irb, scope, node, ptr_instruction);
3497}3557}
34983558
3499static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *node, IrOverflowOp op) {3559static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *node, IrOverflowOp op) {
...@@ -3524,7 +3584,7 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *...@@ -3524,7 +3584,7 @@ static IrInstruction *ir_gen_overflow_op(IrBuilder *irb, Scope *scope, AstNode *
3524 return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr);3584 return ir_build_overflow_op(irb, scope, node, op, type_value, op1, op2, result_ptr, nullptr);
3525}3585}
35263586
3527static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) {3587static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
3528 assert(node->type == NodeTypeFnCallExpr);3588 assert(node->type == NodeTypeFnCallExpr);
35293589
3530 AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;3590 AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr;
...@@ -3556,7 +3616,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3556,7 +3616,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3556 IrInstruction *arg = ir_gen_node(irb, arg_node, scope);3616 IrInstruction *arg = ir_gen_node(irb, arg_node, scope);
3557 if (arg == irb->codegen->invalid_instruction)3617 if (arg == irb->codegen->invalid_instruction)
3558 return arg;3618 return arg;
3559 return ir_build_typeof(irb, scope, node, arg);3619
3620 IrInstruction *type_of = ir_build_typeof(irb, scope, node, arg);
3621 return ir_lval_wrap(irb, scope, type_of, lval);
3560 }3622 }
3561 case BuiltinFnIdSetCold:3623 case BuiltinFnIdSetCold:
3562 {3624 {
...@@ -3565,7 +3627,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3565,7 +3627,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3565 if (arg0_value == irb->codegen->invalid_instruction)3627 if (arg0_value == irb->codegen->invalid_instruction)
3566 return arg0_value;3628 return arg0_value;
35673629
3568 return ir_build_set_cold(irb, scope, node, arg0_value);3630 IrInstruction *set_cold = ir_build_set_cold(irb, scope, node, arg0_value);
3631 return ir_lval_wrap(irb, scope, set_cold, lval);
3569 }3632 }
3570 case BuiltinFnIdSetRuntimeSafety:3633 case BuiltinFnIdSetRuntimeSafety:
3571 {3634 {
...@@ -3574,7 +3637,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3574,7 +3637,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3574 if (arg0_value == irb->codegen->invalid_instruction)3637 if (arg0_value == irb->codegen->invalid_instruction)
3575 return arg0_value;3638 return arg0_value;
35763639
3577 return ir_build_set_runtime_safety(irb, scope, node, arg0_value);3640 IrInstruction *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value);
3641 return ir_lval_wrap(irb, scope, set_safety, lval);
3578 }3642 }
3579 case BuiltinFnIdSetFloatMode:3643 case BuiltinFnIdSetFloatMode:
3580 {3644 {
...@@ -3588,7 +3652,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3588,7 +3652,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3588 if (arg1_value == irb->codegen->invalid_instruction)3652 if (arg1_value == irb->codegen->invalid_instruction)
3589 return arg1_value;3653 return arg1_value;
35903654
3591 return ir_build_set_float_mode(irb, scope, node, arg0_value, arg1_value);3655 IrInstruction *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value, arg1_value);
3656 return ir_lval_wrap(irb, scope, set_float_mode, lval);
3592 }3657 }
3593 case BuiltinFnIdSizeof:3658 case BuiltinFnIdSizeof:
3594 {3659 {
...@@ -3597,7 +3662,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3597,7 +3662,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3597 if (arg0_value == irb->codegen->invalid_instruction)3662 if (arg0_value == irb->codegen->invalid_instruction)
3598 return arg0_value;3663 return arg0_value;
35993664
3600 return ir_build_size_of(irb, scope, node, arg0_value);3665 IrInstruction *size_of = ir_build_size_of(irb, scope, node, arg0_value);
3666 return ir_lval_wrap(irb, scope, size_of, lval);
3601 }3667 }
3602 case BuiltinFnIdCtz:3668 case BuiltinFnIdCtz:
3603 {3669 {
...@@ -3606,7 +3672,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3606,7 +3672,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3606 if (arg0_value == irb->codegen->invalid_instruction)3672 if (arg0_value == irb->codegen->invalid_instruction)
3607 return arg0_value;3673 return arg0_value;
36083674
3609 return ir_build_ctz(irb, scope, node, arg0_value);3675 IrInstruction *ctz = ir_build_ctz(irb, scope, node, arg0_value);
3676 return ir_lval_wrap(irb, scope, ctz, lval);
3610 }3677 }
3611 case BuiltinFnIdClz:3678 case BuiltinFnIdClz:
3612 {3679 {
...@@ -3615,7 +3682,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3615,7 +3682,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3615 if (arg0_value == irb->codegen->invalid_instruction)3682 if (arg0_value == irb->codegen->invalid_instruction)
3616 return arg0_value;3683 return arg0_value;
36173684
3618 return ir_build_clz(irb, scope, node, arg0_value);3685 IrInstruction *clz = ir_build_clz(irb, scope, node, arg0_value);
3686 return ir_lval_wrap(irb, scope, clz, lval);
3619 }3687 }
3620 case BuiltinFnIdImport:3688 case BuiltinFnIdImport:
3621 {3689 {
...@@ -3624,11 +3692,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3624,11 +3692,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3624 if (arg0_value == irb->codegen->invalid_instruction)3692 if (arg0_value == irb->codegen->invalid_instruction)
3625 return arg0_value;3693 return arg0_value;
36263694
3627 return ir_build_import(irb, scope, node, arg0_value);3695 IrInstruction *import = ir_build_import(irb, scope, node, arg0_value);
3696 return ir_lval_wrap(irb, scope, import, lval);
3628 }3697 }
3629 case BuiltinFnIdCImport:3698 case BuiltinFnIdCImport:
3630 {3699 {
3631 return ir_build_c_import(irb, scope, node);3700 IrInstruction *c_import = ir_build_c_import(irb, scope, node);
3701 return ir_lval_wrap(irb, scope, c_import, lval);
3632 }3702 }
3633 case BuiltinFnIdCInclude:3703 case BuiltinFnIdCInclude:
3634 {3704 {
...@@ -3642,7 +3712,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3642,7 +3712,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3642 return irb->codegen->invalid_instruction;3712 return irb->codegen->invalid_instruction;
3643 }3713 }
36443714
3645 return ir_build_c_include(irb, scope, node, arg0_value);3715 IrInstruction *c_include = ir_build_c_include(irb, scope, node, arg0_value);
3716 return ir_lval_wrap(irb, scope, c_include, lval);
3646 }3717 }
3647 case BuiltinFnIdCDefine:3718 case BuiltinFnIdCDefine:
3648 {3719 {
...@@ -3661,7 +3732,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3661,7 +3732,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3661 return irb->codegen->invalid_instruction;3732 return irb->codegen->invalid_instruction;
3662 }3733 }
36633734
3664 return ir_build_c_define(irb, scope, node, arg0_value, arg1_value);3735 IrInstruction *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value);
3736 return ir_lval_wrap(irb, scope, c_define, lval);
3665 }3737 }
3666 case BuiltinFnIdCUndef:3738 case BuiltinFnIdCUndef:
3667 {3739 {
...@@ -3675,7 +3747,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3675,7 +3747,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3675 return irb->codegen->invalid_instruction;3747 return irb->codegen->invalid_instruction;
3676 }3748 }
36773749
3678 return ir_build_c_undef(irb, scope, node, arg0_value);3750 IrInstruction *c_undef = ir_build_c_undef(irb, scope, node, arg0_value);
3751 return ir_lval_wrap(irb, scope, c_undef, lval);
3679 }3752 }
3680 case BuiltinFnIdMaxValue:3753 case BuiltinFnIdMaxValue:
3681 {3754 {
...@@ -3684,7 +3757,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3684,7 +3757,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3684 if (arg0_value == irb->codegen->invalid_instruction)3757 if (arg0_value == irb->codegen->invalid_instruction)
3685 return arg0_value;3758 return arg0_value;
36863759
3687 return ir_build_max_value(irb, scope, node, arg0_value);3760 IrInstruction *max_value = ir_build_max_value(irb, scope, node, arg0_value);
3761 return ir_lval_wrap(irb, scope, max_value, lval);
3688 }3762 }
3689 case BuiltinFnIdMinValue:3763 case BuiltinFnIdMinValue:
3690 {3764 {
...@@ -3693,7 +3767,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3693,7 +3767,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3693 if (arg0_value == irb->codegen->invalid_instruction)3767 if (arg0_value == irb->codegen->invalid_instruction)
3694 return arg0_value;3768 return arg0_value;
36953769
3696 return ir_build_min_value(irb, scope, node, arg0_value);3770 IrInstruction *min_value = ir_build_min_value(irb, scope, node, arg0_value);
3771 return ir_lval_wrap(irb, scope, min_value, lval);
3697 }3772 }
3698 case BuiltinFnIdCompileErr:3773 case BuiltinFnIdCompileErr:
3699 {3774 {
...@@ -3702,7 +3777,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3702,7 +3777,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3702 if (arg0_value == irb->codegen->invalid_instruction)3777 if (arg0_value == irb->codegen->invalid_instruction)
3703 return arg0_value;3778 return arg0_value;
37043779
3705 return ir_build_compile_err(irb, scope, node, arg0_value);3780 IrInstruction *compile_err = ir_build_compile_err(irb, scope, node, arg0_value);
3781 return ir_lval_wrap(irb, scope, compile_err, lval);
3706 }3782 }
3707 case BuiltinFnIdCompileLog:3783 case BuiltinFnIdCompileLog:
3708 {3784 {
...@@ -3715,7 +3791,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3715,7 +3791,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3715 return irb->codegen->invalid_instruction;3791 return irb->codegen->invalid_instruction;
3716 }3792 }
37173793
3718 return ir_build_compile_log(irb, scope, node, actual_param_count, args);3794 IrInstruction *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args);
3795 return ir_lval_wrap(irb, scope, compile_log, lval);
3719 }3796 }
3720 case BuiltinFnIdErrName:3797 case BuiltinFnIdErrName:
3721 {3798 {
...@@ -3724,7 +3801,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3724,7 +3801,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3724 if (arg0_value == irb->codegen->invalid_instruction)3801 if (arg0_value == irb->codegen->invalid_instruction)
3725 return arg0_value;3802 return arg0_value;
37263803
3727 return ir_build_err_name(irb, scope, node, arg0_value);3804 IrInstruction *err_name = ir_build_err_name(irb, scope, node, arg0_value);
3805 return ir_lval_wrap(irb, scope, err_name, lval);
3728 }3806 }
3729 case BuiltinFnIdEmbedFile:3807 case BuiltinFnIdEmbedFile:
3730 {3808 {
...@@ -3733,9 +3811,11 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3733,9 +3811,11 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3733 if (arg0_value == irb->codegen->invalid_instruction)3811 if (arg0_value == irb->codegen->invalid_instruction)
3734 return arg0_value;3812 return arg0_value;
37353813
3736 return ir_build_embed_file(irb, scope, node, arg0_value);3814 IrInstruction *embed_file = ir_build_embed_file(irb, scope, node, arg0_value);
3815 return ir_lval_wrap(irb, scope, embed_file, lval);
3737 }3816 }
3738 case BuiltinFnIdCmpExchange:3817 case BuiltinFnIdCmpxchgWeak:
3818 case BuiltinFnIdCmpxchgStrong:
3739 {3819 {
3740 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);3820 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
3741 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);3821 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
...@@ -3762,9 +3842,15 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3762,9 +3842,15 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3762 if (arg4_value == irb->codegen->invalid_instruction)3842 if (arg4_value == irb->codegen->invalid_instruction)
3763 return arg4_value;3843 return arg4_value;
37643844
3765 return ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value,3845 AstNode *arg5_node = node->data.fn_call_expr.params.at(5);
3766 arg2_value, arg3_value, arg4_value,3846 IrInstruction *arg5_value = ir_gen_node(irb, arg5_node, scope);
3767 AtomicOrderUnordered, AtomicOrderUnordered);3847 if (arg5_value == irb->codegen->invalid_instruction)
3848 return arg5_value;
3849
3850 IrInstruction *cmpxchg = ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value,
3851 arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak),
3852 nullptr, AtomicOrderUnordered, AtomicOrderUnordered);
3853 return ir_lval_wrap(irb, scope, cmpxchg, lval);
3768 }3854 }
3769 case BuiltinFnIdFence:3855 case BuiltinFnIdFence:
3770 {3856 {
...@@ -3773,7 +3859,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3773,7 +3859,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3773 if (arg0_value == irb->codegen->invalid_instruction)3859 if (arg0_value == irb->codegen->invalid_instruction)
3774 return arg0_value;3860 return arg0_value;
37753861
3776 return ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered);3862 IrInstruction *fence = ir_build_fence(irb, scope, node, arg0_value, AtomicOrderUnordered);
3863 return ir_lval_wrap(irb, scope, fence, lval);
3777 }3864 }
3778 case BuiltinFnIdDivExact:3865 case BuiltinFnIdDivExact:
3779 {3866 {
...@@ -3787,7 +3874,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3787,7 +3874,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3787 if (arg1_value == irb->codegen->invalid_instruction)3874 if (arg1_value == irb->codegen->invalid_instruction)
3788 return arg1_value;3875 return arg1_value;
37893876
3790 return ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true);3877 IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true);
3878 return ir_lval_wrap(irb, scope, bin_op, lval);
3791 }3879 }
3792 case BuiltinFnIdDivTrunc:3880 case BuiltinFnIdDivTrunc:
3793 {3881 {
...@@ -3801,7 +3889,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3801,7 +3889,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3801 if (arg1_value == irb->codegen->invalid_instruction)3889 if (arg1_value == irb->codegen->invalid_instruction)
3802 return arg1_value;3890 return arg1_value;
38033891
3804 return ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true);3892 IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true);
3893 return ir_lval_wrap(irb, scope, bin_op, lval);
3805 }3894 }
3806 case BuiltinFnIdDivFloor:3895 case BuiltinFnIdDivFloor:
3807 {3896 {
...@@ -3815,7 +3904,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3815,7 +3904,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3815 if (arg1_value == irb->codegen->invalid_instruction)3904 if (arg1_value == irb->codegen->invalid_instruction)
3816 return arg1_value;3905 return arg1_value;
38173906
3818 return ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true);3907 IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true);
3908 return ir_lval_wrap(irb, scope, bin_op, lval);
3819 }3909 }
3820 case BuiltinFnIdRem:3910 case BuiltinFnIdRem:
3821 {3911 {
...@@ -3829,7 +3919,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3829,7 +3919,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3829 if (arg1_value == irb->codegen->invalid_instruction)3919 if (arg1_value == irb->codegen->invalid_instruction)
3830 return arg1_value;3920 return arg1_value;
38313921
3832 return ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true);3922 IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true);
3923 return ir_lval_wrap(irb, scope, bin_op, lval);
3833 }3924 }
3834 case BuiltinFnIdMod:3925 case BuiltinFnIdMod:
3835 {3926 {
...@@ -3843,7 +3934,23 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3843,7 +3934,23 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3843 if (arg1_value == irb->codegen->invalid_instruction)3934 if (arg1_value == irb->codegen->invalid_instruction)
3844 return arg1_value;3935 return arg1_value;
38453936
3846 return ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true);3937 IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true);
3938 return ir_lval_wrap(irb, scope, bin_op, lval);
3939 }
3940 case BuiltinFnIdSqrt:
3941 {
3942 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
3943 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
3944 if (arg0_value == irb->codegen->invalid_instruction)
3945 return arg0_value;
3946
3947 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
3948 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
3949 if (arg1_value == irb->codegen->invalid_instruction)
3950 return arg1_value;
3951
3952 IrInstruction *ir_sqrt = ir_build_sqrt(irb, scope, node, arg0_value, arg1_value);
3953 return ir_lval_wrap(irb, scope, ir_sqrt, lval);
3847 }3954 }
3848 case BuiltinFnIdTruncate:3955 case BuiltinFnIdTruncate:
3849 {3956 {
...@@ -3857,7 +3964,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3857,7 +3964,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3857 if (arg1_value == irb->codegen->invalid_instruction)3964 if (arg1_value == irb->codegen->invalid_instruction)
3858 return arg1_value;3965 return arg1_value;
38593966
3860 return ir_build_truncate(irb, scope, node, arg0_value, arg1_value);3967 IrInstruction *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value);
3968 return ir_lval_wrap(irb, scope, truncate, lval);
3861 }3969 }
3862 case BuiltinFnIdIntType:3970 case BuiltinFnIdIntType:
3863 {3971 {
...@@ -3871,7 +3979,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3871,7 +3979,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3871 if (arg1_value == irb->codegen->invalid_instruction)3979 if (arg1_value == irb->codegen->invalid_instruction)
3872 return arg1_value;3980 return arg1_value;
38733981
3874 return ir_build_int_type(irb, scope, node, arg0_value, arg1_value);3982 IrInstruction *int_type = ir_build_int_type(irb, scope, node, arg0_value, arg1_value);
3983 return ir_lval_wrap(irb, scope, int_type, lval);
3875 }3984 }
3876 case BuiltinFnIdMemcpy:3985 case BuiltinFnIdMemcpy:
3877 {3986 {
...@@ -3890,7 +3999,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3890,7 +3999,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3890 if (arg2_value == irb->codegen->invalid_instruction)3999 if (arg2_value == irb->codegen->invalid_instruction)
3891 return arg2_value;4000 return arg2_value;
38924001
3893 return ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value);4002 IrInstruction *ir_memcpy = ir_build_memcpy(irb, scope, node, arg0_value, arg1_value, arg2_value);
4003 return ir_lval_wrap(irb, scope, ir_memcpy, lval);
3894 }4004 }
3895 case BuiltinFnIdMemset:4005 case BuiltinFnIdMemset:
3896 {4006 {
...@@ -3909,7 +4019,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3909,7 +4019,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3909 if (arg2_value == irb->codegen->invalid_instruction)4019 if (arg2_value == irb->codegen->invalid_instruction)
3910 return arg2_value;4020 return arg2_value;
39114021
3912 return ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value);4022 IrInstruction *ir_memset = ir_build_memset(irb, scope, node, arg0_value, arg1_value, arg2_value);
4023 return ir_lval_wrap(irb, scope, ir_memset, lval);
3913 }4024 }
3914 case BuiltinFnIdMemberCount:4025 case BuiltinFnIdMemberCount:
3915 {4026 {
...@@ -3918,7 +4029,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3918,7 +4029,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3918 if (arg0_value == irb->codegen->invalid_instruction)4029 if (arg0_value == irb->codegen->invalid_instruction)
3919 return arg0_value;4030 return arg0_value;
39204031
3921 return ir_build_member_count(irb, scope, node, arg0_value);4032 IrInstruction *member_count = ir_build_member_count(irb, scope, node, arg0_value);
4033 return ir_lval_wrap(irb, scope, member_count, lval);
3922 }4034 }
3923 case BuiltinFnIdMemberType:4035 case BuiltinFnIdMemberType:
3924 {4036 {
...@@ -3933,7 +4045,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3933,7 +4045,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3933 return arg1_value;4045 return arg1_value;
39344046
39354047
3936 return ir_build_member_type(irb, scope, node, arg0_value, arg1_value);4048 IrInstruction *member_type = ir_build_member_type(irb, scope, node, arg0_value, arg1_value);
4049 return ir_lval_wrap(irb, scope, member_type, lval);
3937 }4050 }
3938 case BuiltinFnIdMemberName:4051 case BuiltinFnIdMemberName:
3939 {4052 {
...@@ -3948,14 +4061,34 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3948,14 +4061,34 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3948 return arg1_value;4061 return arg1_value;
39494062
39504063
3951 return ir_build_member_name(irb, scope, node, arg0_value, arg1_value);4064 IrInstruction *member_name = ir_build_member_name(irb, scope, node, arg0_value, arg1_value);
4065 return ir_lval_wrap(irb, scope, member_name, lval);
4066 }
4067 case BuiltinFnIdField:
4068 {
4069 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4070 IrInstruction *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LVAL_PTR);
4071 if (arg0_value == irb->codegen->invalid_instruction)
4072 return arg0_value;
4073
4074 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4075 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
4076 if (arg1_value == irb->codegen->invalid_instruction)
4077 return arg1_value;
4078
4079 IrInstruction *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, arg0_value, arg1_value);
4080
4081 if (lval.is_ptr)
4082 return ptr_instruction;
4083
4084 return ir_build_load_ptr(irb, scope, node, ptr_instruction);
3952 }4085 }
3953 case BuiltinFnIdBreakpoint:4086 case BuiltinFnIdBreakpoint:
3954 return ir_build_breakpoint(irb, scope, node);4087 return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval);
3955 case BuiltinFnIdReturnAddress:4088 case BuiltinFnIdReturnAddress:
3956 return ir_build_return_address(irb, scope, node);4089 return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval);
3957 case BuiltinFnIdFrameAddress:4090 case BuiltinFnIdFrameAddress:
3958 return ir_build_frame_address(irb, scope, node);4091 return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval);
3959 case BuiltinFnIdAlignOf:4092 case BuiltinFnIdAlignOf:
3960 {4093 {
3961 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);4094 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
...@@ -3963,16 +4096,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3963,16 +4096,17 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3963 if (arg0_value == irb->codegen->invalid_instruction)4096 if (arg0_value == irb->codegen->invalid_instruction)
3964 return arg0_value;4097 return arg0_value;
39654098
3966 return ir_build_align_of(irb, scope, node, arg0_value);4099 IrInstruction *align_of = ir_build_align_of(irb, scope, node, arg0_value);
4100 return ir_lval_wrap(irb, scope, align_of, lval);
3967 }4101 }
3968 case BuiltinFnIdAddWithOverflow:4102 case BuiltinFnIdAddWithOverflow:
3969 return ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd);4103 return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval);
3970 case BuiltinFnIdSubWithOverflow:4104 case BuiltinFnIdSubWithOverflow:
3971 return ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub);4105 return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval);
3972 case BuiltinFnIdMulWithOverflow:4106 case BuiltinFnIdMulWithOverflow:
3973 return ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul);4107 return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval);
3974 case BuiltinFnIdShlWithOverflow:4108 case BuiltinFnIdShlWithOverflow:
3975 return ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl);4109 return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval);
3976 case BuiltinFnIdTypeName:4110 case BuiltinFnIdTypeName:
3977 {4111 {
3978 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);4112 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
...@@ -3980,7 +4114,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3980,7 +4114,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3980 if (arg0_value == irb->codegen->invalid_instruction)4114 if (arg0_value == irb->codegen->invalid_instruction)
3981 return arg0_value;4115 return arg0_value;
39824116
3983 return ir_build_type_name(irb, scope, node, arg0_value);4117 IrInstruction *type_name = ir_build_type_name(irb, scope, node, arg0_value);
4118 return ir_lval_wrap(irb, scope, type_name, lval);
3984 }4119 }
3985 case BuiltinFnIdCanImplicitCast:4120 case BuiltinFnIdCanImplicitCast:
3986 {4121 {
...@@ -3994,7 +4129,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3994,7 +4129,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
3994 if (arg1_value == irb->codegen->invalid_instruction)4129 if (arg1_value == irb->codegen->invalid_instruction)
3995 return arg1_value;4130 return arg1_value;
39964131
3997 return ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value);4132 IrInstruction *can_implicit_cast = ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value);
4133 return ir_lval_wrap(irb, scope, can_implicit_cast, lval);
3998 }4134 }
3999 case BuiltinFnIdPanic:4135 case BuiltinFnIdPanic:
4000 {4136 {
...@@ -4003,7 +4139,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4003,7 +4139,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4003 if (arg0_value == irb->codegen->invalid_instruction)4139 if (arg0_value == irb->codegen->invalid_instruction)
4004 return arg0_value;4140 return arg0_value;
40054141
4006 return ir_build_panic(irb, scope, node, arg0_value);4142 IrInstruction *panic = ir_build_panic(irb, scope, node, arg0_value);
4143 return ir_lval_wrap(irb, scope, panic, lval);
4007 }4144 }
4008 case BuiltinFnIdPtrCast:4145 case BuiltinFnIdPtrCast:
4009 {4146 {
...@@ -4017,7 +4154,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4017,7 +4154,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4017 if (arg1_value == irb->codegen->invalid_instruction)4154 if (arg1_value == irb->codegen->invalid_instruction)
4018 return arg1_value;4155 return arg1_value;
40194156
4020 return ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value);4157 IrInstruction *ptr_cast = ir_build_ptr_cast(irb, scope, node, arg0_value, arg1_value);
4158 return ir_lval_wrap(irb, scope, ptr_cast, lval);
4021 }4159 }
4022 case BuiltinFnIdBitCast:4160 case BuiltinFnIdBitCast:
4023 {4161 {
...@@ -4031,7 +4169,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4031,7 +4169,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4031 if (arg1_value == irb->codegen->invalid_instruction)4169 if (arg1_value == irb->codegen->invalid_instruction)
4032 return arg1_value;4170 return arg1_value;
40334171
4034 return ir_build_bit_cast(irb, scope, node, arg0_value, arg1_value);4172 IrInstruction *bit_cast = ir_build_bit_cast(irb, scope, node, arg0_value, arg1_value);
4173 return ir_lval_wrap(irb, scope, bit_cast, lval);
4035 }4174 }
4036 case BuiltinFnIdIntToPtr:4175 case BuiltinFnIdIntToPtr:
4037 {4176 {
...@@ -4045,7 +4184,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4045,7 +4184,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4045 if (arg1_value == irb->codegen->invalid_instruction)4184 if (arg1_value == irb->codegen->invalid_instruction)
4046 return arg1_value;4185 return arg1_value;
40474186
4048 return ir_build_int_to_ptr(irb, scope, node, arg0_value, arg1_value);4187 IrInstruction *int_to_ptr = ir_build_int_to_ptr(irb, scope, node, arg0_value, arg1_value);
4188 return ir_lval_wrap(irb, scope, int_to_ptr, lval);
4049 }4189 }
4050 case BuiltinFnIdPtrToInt:4190 case BuiltinFnIdPtrToInt:
4051 {4191 {
...@@ -4054,7 +4194,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4054,7 +4194,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4054 if (arg0_value == irb->codegen->invalid_instruction)4194 if (arg0_value == irb->codegen->invalid_instruction)
4055 return arg0_value;4195 return arg0_value;
40564196
4057 return ir_build_ptr_to_int(irb, scope, node, arg0_value);4197 IrInstruction *ptr_to_int = ir_build_ptr_to_int(irb, scope, node, arg0_value);
4198 return ir_lval_wrap(irb, scope, ptr_to_int, lval);
4058 }4199 }
4059 case BuiltinFnIdTagName:4200 case BuiltinFnIdTagName:
4060 {4201 {
...@@ -4064,7 +4205,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4064,7 +4205,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4064 return arg0_value;4205 return arg0_value;
40654206
4066 IrInstruction *actual_tag = ir_build_union_tag(irb, scope, node, arg0_value);4207 IrInstruction *actual_tag = ir_build_union_tag(irb, scope, node, arg0_value);
4067 return ir_build_tag_name(irb, scope, node, actual_tag);4208 IrInstruction *tag_name = ir_build_tag_name(irb, scope, node, actual_tag);
4209 return ir_lval_wrap(irb, scope, tag_name, lval);
4068 }4210 }
4069 case BuiltinFnIdTagType:4211 case BuiltinFnIdTagType:
4070 {4212 {
...@@ -4073,7 +4215,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4073,7 +4215,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4073 if (arg0_value == irb->codegen->invalid_instruction)4215 if (arg0_value == irb->codegen->invalid_instruction)
4074 return arg0_value;4216 return arg0_value;
40754217
4076 return ir_build_tag_type(irb, scope, node, arg0_value);4218 IrInstruction *tag_type = ir_build_tag_type(irb, scope, node, arg0_value);
4219 return ir_lval_wrap(irb, scope, tag_type, lval);
4077 }4220 }
4078 case BuiltinFnIdFieldParentPtr:4221 case BuiltinFnIdFieldParentPtr:
4079 {4222 {
...@@ -4092,7 +4235,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4092,7 +4235,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4092 if (arg2_value == irb->codegen->invalid_instruction)4235 if (arg2_value == irb->codegen->invalid_instruction)
4093 return arg2_value;4236 return arg2_value;
40944237
4095 return ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr);4238 IrInstruction *field_parent_ptr = ir_build_field_parent_ptr(irb, scope, node, arg0_value, arg1_value, arg2_value, nullptr);
4239 return ir_lval_wrap(irb, scope, field_parent_ptr, lval);
4096 }4240 }
4097 case BuiltinFnIdOffsetOf:4241 case BuiltinFnIdOffsetOf:
4098 {4242 {
...@@ -4106,7 +4250,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4106,7 +4250,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4106 if (arg1_value == irb->codegen->invalid_instruction)4250 if (arg1_value == irb->codegen->invalid_instruction)
4107 return arg1_value;4251 return arg1_value;
41084252
4109 return ir_build_offset_of(irb, scope, node, arg0_value, arg1_value);4253 IrInstruction *offset_of = ir_build_offset_of(irb, scope, node, arg0_value, arg1_value);
4254 return ir_lval_wrap(irb, scope, offset_of, lval);
4110 }4255 }
4111 case BuiltinFnIdInlineCall:4256 case BuiltinFnIdInlineCall:
4112 case BuiltinFnIdNoInlineCall:4257 case BuiltinFnIdNoInlineCall:
...@@ -4132,7 +4277,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4132,7 +4277,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4132 }4277 }
4133 FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever;4278 FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever;
41344279
4135 return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr);4280 IrInstruction *call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline, false, nullptr);
4281 return ir_lval_wrap(irb, scope, call, lval);
4136 }4282 }
4137 case BuiltinFnIdTypeId:4283 case BuiltinFnIdTypeId:
4138 {4284 {
...@@ -4141,7 +4287,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4141,7 +4287,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4141 if (arg0_value == irb->codegen->invalid_instruction)4287 if (arg0_value == irb->codegen->invalid_instruction)
4142 return arg0_value;4288 return arg0_value;
41434289
4144 return ir_build_type_id(irb, scope, node, arg0_value);4290 IrInstruction *type_id = ir_build_type_id(irb, scope, node, arg0_value);
4291 return ir_lval_wrap(irb, scope, type_id, lval);
4145 }4292 }
4146 case BuiltinFnIdShlExact:4293 case BuiltinFnIdShlExact:
4147 {4294 {
...@@ -4155,7 +4302,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4155,7 +4302,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4155 if (arg1_value == irb->codegen->invalid_instruction)4302 if (arg1_value == irb->codegen->invalid_instruction)
4156 return arg1_value;4303 return arg1_value;
41574304
4158 return ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true);4305 IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true);
4306 return ir_lval_wrap(irb, scope, bin_op, lval);
4159 }4307 }
4160 case BuiltinFnIdShrExact:4308 case BuiltinFnIdShrExact:
4161 {4309 {
...@@ -4169,7 +4317,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4169,7 +4317,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4169 if (arg1_value == irb->codegen->invalid_instruction)4317 if (arg1_value == irb->codegen->invalid_instruction)
4170 return arg1_value;4318 return arg1_value;
41714319
4172 return ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true);4320 IrInstruction *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true);
4321 return ir_lval_wrap(irb, scope, bin_op, lval);
4173 }4322 }
4174 case BuiltinFnIdSetEvalBranchQuota:4323 case BuiltinFnIdSetEvalBranchQuota:
4175 {4324 {
...@@ -4178,7 +4327,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4178,7 +4327,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4178 if (arg0_value == irb->codegen->invalid_instruction)4327 if (arg0_value == irb->codegen->invalid_instruction)
4179 return arg0_value;4328 return arg0_value;
41804329
4181 return ir_build_set_eval_branch_quota(irb, scope, node, arg0_value);4330 IrInstruction *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value);
4331 return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval);
4182 }4332 }
4183 case BuiltinFnIdAlignCast:4333 case BuiltinFnIdAlignCast:
4184 {4334 {
...@@ -4192,10 +4342,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4192,10 +4342,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4192 if (arg1_value == irb->codegen->invalid_instruction)4342 if (arg1_value == irb->codegen->invalid_instruction)
4193 return arg1_value;4343 return arg1_value;
41944344
4195 return ir_build_align_cast(irb, scope, node, arg0_value, arg1_value);4345 IrInstruction *align_cast = ir_build_align_cast(irb, scope, node, arg0_value, arg1_value);
4346 return ir_lval_wrap(irb, scope, align_cast, lval);
4196 }4347 }
4197 case BuiltinFnIdOpaqueType:4348 case BuiltinFnIdOpaqueType:
4198 return ir_build_opaque_type(irb, scope, node);4349 {
4350 IrInstruction *opaque_type = ir_build_opaque_type(irb, scope, node);
4351 return ir_lval_wrap(irb, scope, opaque_type, lval);
4352 }
4199 case BuiltinFnIdSetAlignStack:4353 case BuiltinFnIdSetAlignStack:
4200 {4354 {
4201 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);4355 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
...@@ -4203,7 +4357,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4203,7 +4357,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4203 if (arg0_value == irb->codegen->invalid_instruction)4357 if (arg0_value == irb->codegen->invalid_instruction)
4204 return arg0_value;4358 return arg0_value;
42054359
4206 return ir_build_set_align_stack(irb, scope, node, arg0_value);4360 IrInstruction *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value);
4361 return ir_lval_wrap(irb, scope, set_align_stack, lval);
4207 }4362 }
4208 case BuiltinFnIdArgType:4363 case BuiltinFnIdArgType:
4209 {4364 {
...@@ -4217,7 +4372,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4217,7 +4372,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4217 if (arg1_value == irb->codegen->invalid_instruction)4372 if (arg1_value == irb->codegen->invalid_instruction)
4218 return arg1_value;4373 return arg1_value;
42194374
4220 return ir_build_arg_type(irb, scope, node, arg0_value, arg1_value);4375 IrInstruction *arg_type = ir_build_arg_type(irb, scope, node, arg0_value, arg1_value);
4376 return ir_lval_wrap(irb, scope, arg_type, lval);
4221 }4377 }
4222 case BuiltinFnIdExport:4378 case BuiltinFnIdExport:
4223 {4379 {
...@@ -4236,11 +4392,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4236,11 +4392,13 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4236 if (arg2_value == irb->codegen->invalid_instruction)4392 if (arg2_value == irb->codegen->invalid_instruction)
4237 return arg2_value;4393 return arg2_value;
42384394
4239 return ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value);4395 IrInstruction *ir_export = ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value);
4396 return ir_lval_wrap(irb, scope, ir_export, lval);
4240 }4397 }
4241 case BuiltinFnIdErrorReturnTrace:4398 case BuiltinFnIdErrorReturnTrace:
4242 {4399 {
4243 return ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::Null);4400 IrInstruction *error_return_trace = ir_build_error_return_trace(irb, scope, node, IrInstructionErrorReturnTrace::Null);
4401 return ir_lval_wrap(irb, scope, error_return_trace, lval);
4244 }4402 }
4245 case BuiltinFnIdAtomicRmw:4403 case BuiltinFnIdAtomicRmw:
4246 {4404 {
...@@ -4274,15 +4432,36 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4274,15 +4432,36 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4274 // these 2 values don't mean anything since we passed non-null values for other args4432 // these 2 values don't mean anything since we passed non-null values for other args
4275 AtomicRmwOp_xchg, AtomicOrderMonotonic);4433 AtomicRmwOp_xchg, AtomicOrderMonotonic);
4276 }4434 }
4435 case BuiltinFnIdAtomicLoad:
4436 {
4437 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
4438 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
4439 if (arg0_value == irb->codegen->invalid_instruction)
4440 return arg0_value;
4441
4442 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
4443 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
4444 if (arg1_value == irb->codegen->invalid_instruction)
4445 return arg1_value;
4446
4447 AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
4448 IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
4449 if (arg2_value == irb->codegen->invalid_instruction)
4450 return arg2_value;
4451
4452 return ir_build_atomic_load(irb, scope, node, arg0_value, arg1_value, arg2_value,
4453 // this value does not mean anything since we passed non-null values for other arg
4454 AtomicOrderMonotonic);
4455 }
4277 }4456 }
4278 zig_unreachable();4457 zig_unreachable();
4279}4458}
42804459
4281static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node) {4460static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
4282 assert(node->type == NodeTypeFnCallExpr);4461 assert(node->type == NodeTypeFnCallExpr);
42834462
4284 if (node->data.fn_call_expr.is_builtin)4463 if (node->data.fn_call_expr.is_builtin)
4285 return ir_gen_builtin_fn_call(irb, scope, node);4464 return ir_gen_builtin_fn_call(irb, scope, node, lval);
42864465
4287 AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;4466 AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
4288 IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);4467 IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope);
...@@ -4308,7 +4487,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node...@@ -4308,7 +4487,8 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node
4308 }4487 }
4309 }4488 }
43104489
4311 return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator);4490 IrInstruction *fn_call = ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto, is_async, async_allocator);
4491 return ir_lval_wrap(irb, scope, fn_call, lval);
4312}4492}
43134493
4314static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) {4494static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
...@@ -5591,6 +5771,15 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop...@@ -5591,6 +5771,15 @@ static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scop
5591 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);5771 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
5592}5772}
55935773
5774static IrInstruction *ir_gen_break_from_suspend(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeSuspend *suspend_scope) {
5775 IrInstruction *is_comptime = ir_build_const_bool(irb, break_scope, node, false);
5776
5777 IrBasicBlock *dest_block = suspend_scope->resume_block;
5778 ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
5779
5780 return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
5781}
5782
5594static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {5783static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {
5595 assert(node->type == NodeTypeBreak);5784 assert(node->type == NodeTypeBreak);
55965785
...@@ -5630,6 +5819,13 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *...@@ -5630,6 +5819,13 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *
5630 assert(this_block_scope->end_block != nullptr);5819 assert(this_block_scope->end_block != nullptr);
5631 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);5820 return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
5632 }5821 }
5822 } else if (search_scope->id == ScopeIdSuspend) {
5823 ScopeSuspend *this_suspend_scope = (ScopeSuspend *)search_scope;
5824 if (node->data.break_expr.name != nullptr &&
5825 (this_suspend_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_suspend_scope->name)))
5826 {
5827 return ir_gen_break_from_suspend(irb, break_scope, node, this_suspend_scope);
5828 }
5633 }5829 }
5634 search_scope = search_scope->parent;5830 search_scope = search_scope->parent;
5635 }5831 }
...@@ -6216,14 +6412,26 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod...@@ -6216,14 +6412,26 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
6216 ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope);6412 ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(parent_scope);
6217 if (scope_defer_expr) {6413 if (scope_defer_expr) {
6218 if (!scope_defer_expr->reported_err) {6414 if (!scope_defer_expr->reported_err) {
6219 add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression"));6415 ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside defer expression"));
6416 add_error_note(irb->codegen, msg, scope_defer_expr->base.source_node, buf_sprintf("defer here"));
6220 scope_defer_expr->reported_err = true;6417 scope_defer_expr->reported_err = true;
6221 }6418 }
6222 return irb->codegen->invalid_instruction;6419 return irb->codegen->invalid_instruction;
6223 }6420 }
6421 ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope);
6422 if (existing_suspend_scope) {
6423 if (!existing_suspend_scope->reported_err) {
6424 ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside suspend block"));
6425 add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("other suspend block here"));
6426 existing_suspend_scope->reported_err = true;
6427 }
6428 return irb->codegen->invalid_instruction;
6429 }
62246430
6225 Scope *outer_scope = irb->exec->begin_scope;6431 Scope *outer_scope = irb->exec->begin_scope;
62266432
6433 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");
6434 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
62276435
6228 IrInstruction *suspend_code;6436 IrInstruction *suspend_code;
6229 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);6437 IrInstruction *const_bool_false = ir_build_const_bool(irb, parent_scope, node, false);
...@@ -6242,28 +6450,28 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod...@@ -6242,28 +6450,28 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
6242 } else {6450 } else {
6243 child_scope = parent_scope;6451 child_scope = parent_scope;
6244 }6452 }
6453 ScopeSuspend *suspend_scope = create_suspend_scope(node, child_scope);
6454 suspend_scope->resume_block = resume_block;
6455 child_scope = &suspend_scope->base;
6245 IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);6456 IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);
6246 ir_gen_node(irb, node->data.suspend.block, child_scope);6457 ir_gen_node(irb, node->data.suspend.block, child_scope);
6247 suspend_code = ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false);6458 suspend_code = ir_mark_gen(ir_build_coro_suspend(irb, parent_scope, node, save_token, const_bool_false));
6248 }6459 }
62496460
6250 IrBasicBlock *cleanup_block = ir_create_basic_block(irb, parent_scope, "SuspendCleanup");
6251 IrBasicBlock *resume_block = ir_create_basic_block(irb, parent_scope, "SuspendResume");
6252
6253 IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);6461 IrInstructionSwitchBrCase *cases = allocate<IrInstructionSwitchBrCase>(2);
6254 cases[0].value = ir_build_const_u8(irb, parent_scope, node, 0);6462 cases[0].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 0));
6255 cases[0].block = resume_block;6463 cases[0].block = resume_block;
6256 cases[1].value = ir_build_const_u8(irb, parent_scope, node, 1);6464 cases[1].value = ir_mark_gen(ir_build_const_u8(irb, parent_scope, node, 1));
6257 cases[1].block = cleanup_block;6465 cases[1].block = cleanup_block;
6258 ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,6466 ir_mark_gen(ir_build_switch_br(irb, parent_scope, node, suspend_code, irb->exec->coro_suspend_block,
6259 2, cases, const_bool_false);6467 2, cases, const_bool_false));
62606468
6261 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);6469 ir_set_cursor_at_end_and_append_block(irb, cleanup_block);
6262 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);6470 ir_gen_defers_for_block(irb, parent_scope, outer_scope, true);
6263 ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));6471 ir_mark_gen(ir_build_br(irb, parent_scope, node, irb->exec->coro_final_cleanup_block, const_bool_false));
62646472
6265 ir_set_cursor_at_end_and_append_block(irb, resume_block);6473 ir_set_cursor_at_end_and_append_block(irb, resume_block);
6266 return ir_build_const_void(irb, parent_scope, node);6474 return ir_mark_gen(ir_build_const_void(irb, parent_scope, node));
6267}6475}
62686476
6269static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,6477static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scope,
...@@ -6296,7 +6504,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop...@@ -6296,7 +6504,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
6296 case NodeTypeSymbol:6504 case NodeTypeSymbol:
6297 return ir_gen_symbol(irb, scope, node, lval);6505 return ir_gen_symbol(irb, scope, node, lval);
6298 case NodeTypeFnCallExpr:6506 case NodeTypeFnCallExpr:
6299 return ir_lval_wrap(irb, scope, ir_gen_fn_call(irb, scope, node), lval);6507 return ir_gen_fn_call(irb, scope, node, lval);
6300 case NodeTypeIfBoolExpr:6508 case NodeTypeIfBoolExpr:
6301 return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval);6509 return ir_lval_wrap(irb, scope, ir_gen_if_bool_expr(irb, scope, node), lval);
6302 case NodeTypePrefixOpExpr:6510 case NodeTypePrefixOpExpr:
...@@ -6316,7 +6524,15 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop...@@ -6316,7 +6524,15 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
6316 case NodeTypeReturnExpr:6524 case NodeTypeReturnExpr:
6317 return ir_gen_return(irb, scope, node, lval);6525 return ir_gen_return(irb, scope, node, lval);
6318 case NodeTypeFieldAccessExpr:6526 case NodeTypeFieldAccessExpr:
6319 return ir_gen_field_access(irb, scope, node, lval);6527 {
6528 IrInstruction *ptr_instruction = ir_gen_field_access(irb, scope, node);
6529 if (ptr_instruction == irb->codegen->invalid_instruction)
6530 return ptr_instruction;
6531 if (lval.is_ptr)
6532 return ptr_instruction;
6533
6534 return ir_build_load_ptr(irb, scope, node, ptr_instruction);
6535 }
6320 case NodeTypeThisLiteral:6536 case NodeTypeThisLiteral:
6321 return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval);6537 return ir_lval_wrap(irb, scope, ir_gen_this_literal(irb, scope, node), lval);
6322 case NodeTypeBoolLiteral:6538 case NodeTypeBoolLiteral:
...@@ -11395,7 +11611,19 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc...@@ -11395,7 +11611,19 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
11395 }11611 }
11396 break;11612 break;
11397 case VarClassRequiredAny:11613 case VarClassRequiredAny:
11398 // OK11614 if (casted_init_value->value.special == ConstValSpecialStatic &&
11615 casted_init_value->value.type->id == TypeTableEntryIdFn &&
11616 casted_init_value->value.data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways)
11617 {
11618 var_class_requires_const = true;
11619 if (!var->src_is_const && !is_comptime_var) {
11620 ErrorMsg *msg = ir_add_error_node(ira, source_node,
11621 buf_sprintf("functions marked inline must be stored in const or comptime var"));
11622 AstNode *proto_node = casted_init_value->value.data.x_ptr.data.fn.fn_entry->proto_node;
11623 add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here"));
11624 result_type = ira->codegen->builtin_types.entry_invalid;
11625 }
11626 }
11399 break;11627 break;
11400 }11628 }
11401 }11629 }
...@@ -13332,7 +13560,15 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru...@@ -13332,7 +13560,15 @@ static TypeTableEntry *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstru
13332 zig_unreachable();13560 zig_unreachable();
13333 }13561 }
1333413562
13335 Buf *field_name = field_ptr_instruction->field_name;13563 Buf *field_name = field_ptr_instruction->field_name_buffer;
13564 if (!field_name) {
13565 IrInstruction *field_name_expr = field_ptr_instruction->field_name_expr->other;
13566 field_name = ir_resolve_str(ira, field_name_expr);
13567 if (!field_name)
13568 return ira->codegen->builtin_types.entry_invalid;
13569 }
13570
13571
13336 AstNode *source_node = field_ptr_instruction->base.source_node;13572 AstNode *source_node = field_ptr_instruction->base.source_node;
1333713573
13338 if (type_is_invalid(container_type)) {13574 if (type_is_invalid(container_type)) {
...@@ -14549,7 +14785,10 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,...@@ -14549,7 +14785,10 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
14549 return out_val->type;14785 return out_val->type;
14550 }14786 }
1455114787
14552 assert(target_value_ptr->value.type->id == TypeTableEntryIdPointer);14788 if (target_value_ptr->value.type->id != TypeTableEntryIdPointer) {
14789 ir_add_error(ira, target_value_ptr, buf_sprintf("invalid deref on switch target"));
14790 return ira->codegen->builtin_types.entry_invalid;
14791 }
1455314792
14554 TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type;14793 TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type;
14555 ConstExprValue *pointee_val = nullptr;14794 ConstExprValue *pointee_val = nullptr;
...@@ -15325,6 +15564,8 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,...@@ -15325,6 +15564,8 @@ static TypeTableEntry *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira,
15325 }15564 }
1532615565
15327 ensure_complete_type(ira->codegen, container_type);15566 ensure_complete_type(ira->codegen, container_type);
15567 if (type_is_invalid(container_type))
15568 return ira->codegen->builtin_types.entry_invalid;
1532815569
15329 TypeStructField *field = find_struct_type_field(container_type, field_name);15570 TypeStructField *field = find_struct_type_field(container_type, field_name);
15330 if (field == nullptr) {15571 if (field == nullptr) {
...@@ -15647,10 +15888,20 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr...@@ -15647,10 +15888,20 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr
15647}15888}
1564815889
15649static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) {15890static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) {
15891 TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->other);
15892 if (type_is_invalid(operand_type))
15893 return ira->codegen->builtin_types.entry_invalid;
15894
15650 IrInstruction *ptr = instruction->ptr->other;15895 IrInstruction *ptr = instruction->ptr->other;
15651 if (type_is_invalid(ptr->value.type))15896 if (type_is_invalid(ptr->value.type))
15652 return ira->codegen->builtin_types.entry_invalid;15897 return ira->codegen->builtin_types.entry_invalid;
1565315898
15899 // TODO let this be volatile
15900 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false);
15901 IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr, ptr_type);
15902 if (type_is_invalid(casted_ptr->value.type))
15903 return ira->codegen->builtin_types.entry_invalid;
15904
15654 IrInstruction *cmp_value = instruction->cmp_value->other;15905 IrInstruction *cmp_value = instruction->cmp_value->other;
15655 if (type_is_invalid(cmp_value->value.type))15906 if (type_is_invalid(cmp_value->value.type))
15656 return ira->codegen->builtin_types.entry_invalid;15907 return ira->codegen->builtin_types.entry_invalid;
...@@ -15675,28 +15926,11 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct...@@ -15675,28 +15926,11 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct
15675 if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order))15926 if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order))
15676 return ira->codegen->builtin_types.entry_invalid;15927 return ira->codegen->builtin_types.entry_invalid;
1567715928
15678 if (ptr->value.type->id != TypeTableEntryIdPointer) {15929 IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, operand_type);
15679 ir_add_error(ira, instruction->ptr,
15680 buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&ptr->value.type->name)));
15681 return ira->codegen->builtin_types.entry_invalid;
15682 }
15683
15684 TypeTableEntry *child_type = ptr->value.type->data.pointer.child_type;
15685
15686 uint32_t align_bytes = ptr->value.type->data.pointer.alignment;
15687 uint64_t size_bytes = type_size(ira->codegen, child_type);
15688 if (align_bytes < size_bytes) {
15689 ir_add_error(ira, instruction->ptr,
15690 buf_sprintf("expected pointer alignment of at least %" ZIG_PRI_u64 ", found %" PRIu32,
15691 size_bytes, align_bytes));
15692 return ira->codegen->builtin_types.entry_invalid;
15693 }
15694
15695 IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, child_type);
15696 if (type_is_invalid(casted_cmp_value->value.type))15930 if (type_is_invalid(casted_cmp_value->value.type))
15697 return ira->codegen->builtin_types.entry_invalid;15931 return ira->codegen->builtin_types.entry_invalid;
1569815932
15699 IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, child_type);15933 IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, operand_type);
15700 if (type_is_invalid(casted_new_value->value.type))15934 if (type_is_invalid(casted_new_value->value.type))
15701 return ira->codegen->builtin_types.entry_invalid;15935 return ira->codegen->builtin_types.entry_invalid;
1570215936
...@@ -15721,9 +15955,17 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct...@@ -15721,9 +15955,17 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct
15721 return ira->codegen->builtin_types.entry_invalid;15955 return ira->codegen->builtin_types.entry_invalid;
15722 }15956 }
1572315957
15724 ir_build_cmpxchg_from(&ira->new_irb, &instruction->base, ptr, casted_cmp_value, casted_new_value,15958 if (instr_is_comptime(casted_ptr) && instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) {
15725 success_order_value, failure_order_value, success_order, failure_order);15959 zig_panic("TODO compile-time execution of cmpxchg");
15726 return ira->codegen->builtin_types.entry_bool;15960 }
15961
15962 IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node,
15963 nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak,
15964 operand_type, success_order, failure_order);
15965 result->value.type = get_maybe_type(ira->codegen, operand_type);
15966 ir_link_new_instruction(result, &instruction->base);
15967 ir_add_alloca(ira, result, result->value.type);
15968 return result->value.type;
15727}15969}
1572815970
15729static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) {15971static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) {
...@@ -17857,39 +18099,48 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira,...@@ -17857,39 +18099,48 @@ static TypeTableEntry *ir_analyze_instruction_coro_alloc_helper(IrAnalyze *ira,
17857 return result->value.type;18099 return result->value.type;
17858}18100}
1785918101
17860static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) {18102static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op) {
17861 TypeTableEntry *operand_type = ir_resolve_type(ira, instruction->operand_type->other);18103 TypeTableEntry *operand_type = ir_resolve_type(ira, op);
17862 if (type_is_invalid(operand_type)) {18104 if (type_is_invalid(operand_type))
17863 return ira->codegen->builtin_types.entry_invalid;18105 return ira->codegen->builtin_types.entry_invalid;
17864 }18106
17865 if (operand_type->id == TypeTableEntryIdInt) {18107 if (operand_type->id == TypeTableEntryIdInt) {
17866 if (operand_type->data.integral.bit_count < 8) {18108 if (operand_type->data.integral.bit_count < 8) {
17867 ir_add_error(ira, &instruction->base,18109 ir_add_error(ira, op,
17868 buf_sprintf("expected integer type 8 bits or larger, found %" PRIu32 "-bit integer type",18110 buf_sprintf("expected integer type 8 bits or larger, found %" PRIu32 "-bit integer type",
17869 operand_type->data.integral.bit_count));18111 operand_type->data.integral.bit_count));
17870 return ira->codegen->builtin_types.entry_invalid;18112 return ira->codegen->builtin_types.entry_invalid;
17871 }18113 }
17872 if (operand_type->data.integral.bit_count > ira->codegen->pointer_size_bytes * 8) {18114 if (operand_type->data.integral.bit_count > ira->codegen->pointer_size_bytes * 8) {
17873 ir_add_error(ira, &instruction->base,18115 ir_add_error(ira, op,
17874 buf_sprintf("expected integer type pointer size or smaller, found %" PRIu32 "-bit integer type",18116 buf_sprintf("expected integer type pointer size or smaller, found %" PRIu32 "-bit integer type",
17875 operand_type->data.integral.bit_count));18117 operand_type->data.integral.bit_count));
17876 return ira->codegen->builtin_types.entry_invalid;18118 return ira->codegen->builtin_types.entry_invalid;
17877 }18119 }
17878 if (!is_power_of_2(operand_type->data.integral.bit_count)) {18120 if (!is_power_of_2(operand_type->data.integral.bit_count)) {
17879 ir_add_error(ira, &instruction->base,18121 ir_add_error(ira, op,
17880 buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count));18122 buf_sprintf("%" PRIu32 "-bit integer type is not a power of 2", operand_type->data.integral.bit_count));
17881 return ira->codegen->builtin_types.entry_invalid;18123 return ira->codegen->builtin_types.entry_invalid;
17882 }18124 }
17883 } else if (get_codegen_ptr_type(operand_type) == nullptr) {18125 } else if (get_codegen_ptr_type(operand_type) == nullptr) {
17884 ir_add_error(ira, &instruction->base,18126 ir_add_error(ira, op,
17885 buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name)));18127 buf_sprintf("expected integer or pointer type, found '%s'", buf_ptr(&operand_type->name)));
17886 return ira->codegen->builtin_types.entry_invalid;18128 return ira->codegen->builtin_types.entry_invalid;
17887 }18129 }
1788818130
18131 return operand_type;
18132}
18133
18134static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstructionAtomicRmw *instruction) {
18135 TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->other);
18136 if (type_is_invalid(operand_type))
18137 return ira->codegen->builtin_types.entry_invalid;
18138
17889 IrInstruction *ptr_inst = instruction->ptr->other;18139 IrInstruction *ptr_inst = instruction->ptr->other;
17890 if (type_is_invalid(ptr_inst->value.type))18140 if (type_is_invalid(ptr_inst->value.type))
17891 return ira->codegen->builtin_types.entry_invalid;18141 return ira->codegen->builtin_types.entry_invalid;
1789218142
18143 // TODO let this be volatile
17893 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false);18144 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false);
17894 IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);18145 IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);
17895 if (type_is_invalid(casted_ptr->value.type))18146 if (type_is_invalid(casted_ptr->value.type))
...@@ -17933,6 +18184,49 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr...@@ -17933,6 +18184,49 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr
17933 return result->value.type;18184 return result->value.type;
17934}18185}
1793518186
18187static TypeTableEntry *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstructionAtomicLoad *instruction) {
18188 TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->other);
18189 if (type_is_invalid(operand_type))
18190 return ira->codegen->builtin_types.entry_invalid;
18191
18192 IrInstruction *ptr_inst = instruction->ptr->other;
18193 if (type_is_invalid(ptr_inst->value.type))
18194 return ira->codegen->builtin_types.entry_invalid;
18195
18196 TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, true);
18197 IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type);
18198 if (type_is_invalid(casted_ptr->value.type))
18199 return ira->codegen->builtin_types.entry_invalid;
18200
18201 AtomicOrder ordering;
18202 if (instruction->ordering == nullptr) {
18203 ordering = instruction->resolved_ordering;
18204 } else {
18205 if (!ir_resolve_atomic_order(ira, instruction->ordering->other, &ordering))
18206 return ira->codegen->builtin_types.entry_invalid;
18207 }
18208
18209 if (ordering == AtomicOrderRelease || ordering == AtomicOrderAcqRel) {
18210 assert(instruction->ordering != nullptr);
18211 ir_add_error(ira, instruction->ordering,
18212 buf_sprintf("@atomicLoad atomic ordering must not be Release or AcqRel"));
18213 return ira->codegen->builtin_types.entry_invalid;
18214 }
18215
18216 if (instr_is_comptime(casted_ptr)) {
18217 IrInstruction *result = ir_get_deref(ira, &instruction->base, casted_ptr);
18218 ir_link_new_instruction(result, &instruction->base);
18219 assert(result->value.type != nullptr);
18220 return result->value.type;
18221 }
18222
18223 IrInstruction *result = ir_build_atomic_load(&ira->new_irb, instruction->base.scope,
18224 instruction->base.source_node, nullptr, casted_ptr, nullptr, ordering);
18225 ir_link_new_instruction(result, &instruction->base);
18226 result->value.type = operand_type;
18227 return result->value.type;
18228}
18229
17936static TypeTableEntry *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) {18230static TypeTableEntry *ir_analyze_instruction_promise_result_type(IrAnalyze *ira, IrInstructionPromiseResultType *instruction) {
17937 TypeTableEntry *promise_type = ir_resolve_type(ira, instruction->promise_type->other);18231 TypeTableEntry *promise_type = ir_resolve_type(ira, instruction->promise_type->other);
17938 if (type_is_invalid(promise_type))18232 if (type_is_invalid(promise_type))
...@@ -18019,6 +18313,68 @@ static TypeTableEntry *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *...@@ -18019,6 +18313,68 @@ static TypeTableEntry *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *
18019 return result->value.type;18313 return result->value.type;
18020}18314}
1802118315
18316static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) {
18317 TypeTableEntry *float_type = ir_resolve_type(ira, instruction->type->other);
18318 if (type_is_invalid(float_type))
18319 return ira->codegen->builtin_types.entry_invalid;
18320
18321 IrInstruction *op = instruction->op->other;
18322 if (type_is_invalid(op->value.type))
18323 return ira->codegen->builtin_types.entry_invalid;
18324
18325 bool ok_type = float_type->id == TypeTableEntryIdNumLitFloat || float_type->id == TypeTableEntryIdFloat;
18326 if (!ok_type) {
18327 ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name)));
18328 return ira->codegen->builtin_types.entry_invalid;
18329 }
18330
18331 IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type);
18332 if (type_is_invalid(casted_op->value.type))
18333 return ira->codegen->builtin_types.entry_invalid;
18334
18335 if (instr_is_comptime(casted_op)) {
18336 ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad);
18337 if (!val)
18338 return ira->codegen->builtin_types.entry_invalid;
18339
18340 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
18341
18342 if (float_type->id == TypeTableEntryIdNumLitFloat) {
18343 bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat);
18344 } else if (float_type->id == TypeTableEntryIdFloat) {
18345 switch (float_type->data.floating.bit_count) {
18346 case 32:
18347 out_val->data.x_f32 = sqrtf(val->data.x_f32);
18348 break;
18349 case 64:
18350 out_val->data.x_f64 = sqrt(val->data.x_f64);
18351 break;
18352 case 128:
18353 f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128);
18354 break;
18355 default:
18356 zig_unreachable();
18357 }
18358 } else {
18359 zig_unreachable();
18360 }
18361
18362 return float_type;
18363 }
18364
18365 assert(float_type->id == TypeTableEntryIdFloat);
18366 if (float_type->data.floating.bit_count != 32 && float_type->data.floating.bit_count != 64) {
18367 ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name)));
18368 return ira->codegen->builtin_types.entry_invalid;
18369 }
18370
18371 IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope,
18372 instruction->base.source_node, nullptr, casted_op);
18373 ir_link_new_instruction(result, &instruction->base);
18374 result->value.type = float_type;
18375 return result->value.type;
18376}
18377
18022static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {18378static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
18023 switch (instruction->id) {18379 switch (instruction->id) {
18024 case IrInstructionIdInvalid:18380 case IrInstructionIdInvalid:
...@@ -18254,6 +18610,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -18254,6 +18610,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
18254 return ir_analyze_instruction_coro_alloc_helper(ira, (IrInstructionCoroAllocHelper *)instruction);18610 return ir_analyze_instruction_coro_alloc_helper(ira, (IrInstructionCoroAllocHelper *)instruction);
18255 case IrInstructionIdAtomicRmw:18611 case IrInstructionIdAtomicRmw:
18256 return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction);18612 return ir_analyze_instruction_atomic_rmw(ira, (IrInstructionAtomicRmw *)instruction);
18613 case IrInstructionIdAtomicLoad:
18614 return ir_analyze_instruction_atomic_load(ira, (IrInstructionAtomicLoad *)instruction);
18257 case IrInstructionIdPromiseResultType:18615 case IrInstructionIdPromiseResultType:
18258 return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction);18616 return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction);
18259 case IrInstructionIdAwaitBookkeeping:18617 case IrInstructionIdAwaitBookkeeping:
...@@ -18266,6 +18624,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -18266,6 +18624,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
18266 return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction);18624 return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction);
18267 case IrInstructionIdMarkErrRetTracePtr:18625 case IrInstructionIdMarkErrRetTracePtr:
18268 return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);18626 return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);
18627 case IrInstructionIdSqrt:
18628 return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction);
18269 }18629 }
18270 zig_unreachable();18630 zig_unreachable();
18271}18631}
...@@ -18395,6 +18755,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -18395,6 +18755,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
18395 case IrInstructionIdAddImplicitReturnType:18755 case IrInstructionIdAddImplicitReturnType:
18396 case IrInstructionIdMergeErrRetTraces:18756 case IrInstructionIdMergeErrRetTraces:
18397 case IrInstructionIdMarkErrRetTracePtr:18757 case IrInstructionIdMarkErrRetTracePtr:
18758 case IrInstructionIdAtomicRmw:
18398 return true;18759 return true;
1839918760
18400 case IrInstructionIdPhi:18761 case IrInstructionIdPhi:
...@@ -18475,9 +18836,10 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -18475,9 +18836,10 @@ bool ir_has_side_effects(IrInstruction *instruction) {
18475 case IrInstructionIdCoroSize:18836 case IrInstructionIdCoroSize:
18476 case IrInstructionIdCoroSuspend:18837 case IrInstructionIdCoroSuspend:
18477 case IrInstructionIdCoroFree:18838 case IrInstructionIdCoroFree:
18478 case IrInstructionIdAtomicRmw:
18479 case IrInstructionIdCoroPromise:18839 case IrInstructionIdCoroPromise:
18480 case IrInstructionIdPromiseResultType:18840 case IrInstructionIdPromiseResultType:
18841 case IrInstructionIdSqrt:
18842 case IrInstructionIdAtomicLoad:
18481 return false;18843 return false;
1848218844
18483 case IrInstructionIdAsm:18845 case IrInstructionIdAsm:
src/ir_print.cpp+48-3
...@@ -358,9 +358,18 @@ static void ir_print_ptr_type_child(IrPrint *irp, IrInstructionPtrTypeChild *ins...@@ -358,9 +358,18 @@ static void ir_print_ptr_type_child(IrPrint *irp, IrInstructionPtrTypeChild *ins
358}358}
359359
360static void ir_print_field_ptr(IrPrint *irp, IrInstructionFieldPtr *instruction) {360static void ir_print_field_ptr(IrPrint *irp, IrInstructionFieldPtr *instruction) {
361 fprintf(irp->f, "fieldptr ");361 if (instruction->field_name_buffer) {
362 ir_print_other_instruction(irp, instruction->container_ptr);362 fprintf(irp->f, "fieldptr ");
363 fprintf(irp->f, ".%s", buf_ptr(instruction->field_name));363 ir_print_other_instruction(irp, instruction->container_ptr);
364 fprintf(irp->f, ".%s", buf_ptr(instruction->field_name_buffer));
365 } else {
366 assert(instruction->field_name_expr);
367 fprintf(irp->f, "@field(");
368 ir_print_other_instruction(irp, instruction->container_ptr);
369 fprintf(irp->f, ", ");
370 ir_print_other_instruction(irp, instruction->field_name_expr);
371 fprintf(irp->f, ")");
372 }
364}373}
365374
366static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr *instruction) {375static void ir_print_struct_field_ptr(IrPrint *irp, IrInstructionStructFieldPtr *instruction) {
...@@ -1172,6 +1181,24 @@ static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instructio...@@ -1172,6 +1181,24 @@ static void ir_print_atomic_rmw(IrPrint *irp, IrInstructionAtomicRmw *instructio
1172 fprintf(irp->f, ")");1181 fprintf(irp->f, ")");
1173}1182}
11741183
1184static void ir_print_atomic_load(IrPrint *irp, IrInstructionAtomicLoad *instruction) {
1185 fprintf(irp->f, "@atomicLoad(");
1186 if (instruction->operand_type != nullptr) {
1187 ir_print_other_instruction(irp, instruction->operand_type);
1188 } else {
1189 fprintf(irp->f, "[TODO print]");
1190 }
1191 fprintf(irp->f, ",");
1192 ir_print_other_instruction(irp, instruction->ptr);
1193 fprintf(irp->f, ",");
1194 if (instruction->ordering != nullptr) {
1195 ir_print_other_instruction(irp, instruction->ordering);
1196 } else {
1197 fprintf(irp->f, "[TODO print]");
1198 }
1199 fprintf(irp->f, ")");
1200}
1201
1175static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeeping *instruction) {1202static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeeping *instruction) {
1176 fprintf(irp->f, "@awaitBookkeeping(");1203 fprintf(irp->f, "@awaitBookkeeping(");
1177 ir_print_other_instruction(irp, instruction->promise_result_type);1204 ir_print_other_instruction(irp, instruction->promise_result_type);
...@@ -1204,6 +1231,18 @@ static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRe...@@ -1204,6 +1231,18 @@ static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRe
1204 fprintf(irp->f, ")");1231 fprintf(irp->f, ")");
1205}1232}
12061233
1234static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) {
1235 fprintf(irp->f, "@sqrt(");
1236 if (instruction->type != nullptr) {
1237 ir_print_other_instruction(irp, instruction->type);
1238 } else {
1239 fprintf(irp->f, "null");
1240 }
1241 fprintf(irp->f, ",");
1242 ir_print_other_instruction(irp, instruction->op);
1243 fprintf(irp->f, ")");
1244}
1245
1207static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {1246static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1208 ir_print_prefix(irp, instruction);1247 ir_print_prefix(irp, instruction);
1209 switch (instruction->id) {1248 switch (instruction->id) {
...@@ -1590,6 +1629,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1590,6 +1629,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1590 case IrInstructionIdMarkErrRetTracePtr:1629 case IrInstructionIdMarkErrRetTracePtr:
1591 ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction);1630 ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction);
1592 break;1631 break;
1632 case IrInstructionIdSqrt:
1633 ir_print_sqrt(irp, (IrInstructionSqrt *)instruction);
1634 break;
1635 case IrInstructionIdAtomicLoad:
1636 ir_print_atomic_load(irp, (IrInstructionAtomicLoad *)instruction);
1637 break;
1593 }1638 }
1594 fprintf(irp->f, "\n");1639 fprintf(irp->f, "\n");
1595}1640}
src/link.cpp+3
...@@ -217,6 +217,9 @@ static void construct_linker_job_elf(LinkJob *lj) {...@@ -217,6 +217,9 @@ static void construct_linker_job_elf(LinkJob *lj) {
217 lj->args.append(g->linker_script);217 lj->args.append(g->linker_script);
218 }218 }
219219
220 if (g->no_rosegment_workaround) {
221 lj->args.append("--no-rosegment");
222 }
220 lj->args.append("--gc-sections");223 lj->args.append("--gc-sections");
221224
222 lj->args.append("-m");225 lj->args.append("-m");
src/main.cpp+16-25
...@@ -43,6 +43,7 @@ static int usage(const char *arg0) {...@@ -43,6 +43,7 @@ static int usage(const char *arg0) {
43 " --pkg-end pop current pkg\n"43 " --pkg-end pop current pkg\n"
44 " --release-fast build with optimizations on and safety off\n"44 " --release-fast build with optimizations on and safety off\n"
45 " --release-safe build with optimizations on and safety on\n"45 " --release-safe build with optimizations on and safety on\n"
46 " --release-small build with size optimizations on and safety off\n"
46 " --static output will be statically linked\n"47 " --static output will be statically linked\n"
47 " --strip exclude debug symbols\n"48 " --strip exclude debug symbols\n"
48 " --target-arch [name] specify target architecture\n"49 " --target-arch [name] specify target architecture\n"
...@@ -54,7 +55,6 @@ static int usage(const char *arg0) {...@@ -54,7 +55,6 @@ static int usage(const char *arg0) {
54 " --verbose-ir turn on compiler debug output for Zig IR\n"55 " --verbose-ir turn on compiler debug output for Zig IR\n"
55 " --verbose-llvm-ir turn on compiler debug output for LLVM IR\n"56 " --verbose-llvm-ir turn on compiler debug output for LLVM IR\n"
56 " --verbose-cimport turn on compiler debug output for C imports\n"57 " --verbose-cimport turn on compiler debug output for C imports\n"
57 " --zig-install-prefix [path] override directory where zig thinks it is installed\n"
58 " -dirafter [dir] same as -isystem but do it last\n"58 " -dirafter [dir] same as -isystem but do it last\n"
59 " -isystem [dir] add additional search path for other .h files\n"59 " -isystem [dir] add additional search path for other .h files\n"
60 " -mllvm [arg] additional arguments to forward to LLVM's option processing\n"60 " -mllvm [arg] additional arguments to forward to LLVM's option processing\n"
...@@ -74,6 +74,7 @@ static int usage(const char *arg0) {...@@ -74,6 +74,7 @@ static int usage(const char *arg0) {
74 " -L[dir] alias for --library-path\n"74 " -L[dir] alias for --library-path\n"
75 " -rdynamic add all symbols to the dynamic symbol table\n"75 " -rdynamic add all symbols to the dynamic symbol table\n"
76 " -rpath [path] add directory to the runtime library search path\n"76 " -rpath [path] add directory to the runtime library search path\n"
77 " --no-rosegment compromise security to workaround valgrind bug\n"
77 " -mconsole (windows) --subsystem console to the linker\n"78 " -mconsole (windows) --subsystem console to the linker\n"
78 " -mwindows (windows) --subsystem windows to the linker\n"79 " -mwindows (windows) --subsystem windows to the linker\n"
79 " -framework [name] (darwin) link against framework\n"80 " -framework [name] (darwin) link against framework\n"
...@@ -177,6 +178,7 @@ static int find_zig_lib_dir(Buf *out_path) {...@@ -177,6 +178,7 @@ static int find_zig_lib_dir(Buf *out_path) {
177 int err;178 int err;
178179
179 Buf self_exe_path = BUF_INIT;180 Buf self_exe_path = BUF_INIT;
181 buf_resize(&self_exe_path, 0);
180 if (!(err = os_self_exe_path(&self_exe_path))) {182 if (!(err = os_self_exe_path(&self_exe_path))) {
181 Buf *cur_path = &self_exe_path;183 Buf *cur_path = &self_exe_path;
182184
...@@ -199,23 +201,14 @@ static int find_zig_lib_dir(Buf *out_path) {...@@ -199,23 +201,14 @@ static int find_zig_lib_dir(Buf *out_path) {
199 return ErrorFileNotFound;201 return ErrorFileNotFound;
200}202}
201203
202static Buf *resolve_zig_lib_dir(const char *zig_install_prefix_arg) {204static Buf *resolve_zig_lib_dir(void) {
203 int err;205 int err;
204 Buf *result = buf_alloc();206 Buf *result = buf_alloc();
205 if (zig_install_prefix_arg == nullptr) {207 if ((err = find_zig_lib_dir(result))) {
206 if ((err = find_zig_lib_dir(result))) {208 fprintf(stderr, "Unable to find zig lib directory\n");
207 fprintf(stderr, "Unable to find zig lib directory. Reinstall Zig or use --zig-install-prefix.\n");209 exit(EXIT_FAILURE);
208 exit(EXIT_FAILURE);
209 }
210 return result;
211 }
212 Buf *zig_lib_dir_buf = buf_create_from_str(zig_install_prefix_arg);
213 if (test_zig_install_prefix(zig_lib_dir_buf, result)) {
214 return result;
215 }210 }
216211 return result;
217 fprintf(stderr, "No Zig installation found at prefix: %s\n", zig_install_prefix_arg);
218 exit(EXIT_FAILURE);
219}212}
220213
221enum Cmd {214enum Cmd {
...@@ -299,7 +292,6 @@ int main(int argc, char **argv) {...@@ -299,7 +292,6 @@ int main(int argc, char **argv) {
299 const char *libc_include_dir = nullptr;292 const char *libc_include_dir = nullptr;
300 const char *msvc_lib_dir = nullptr;293 const char *msvc_lib_dir = nullptr;
301 const char *kernel32_lib_dir = nullptr;294 const char *kernel32_lib_dir = nullptr;
302 const char *zig_install_prefix = nullptr;
303 const char *dynamic_linker = nullptr;295 const char *dynamic_linker = nullptr;
304 ZigList<const char *> clang_argv = {0};296 ZigList<const char *> clang_argv = {0};
305 ZigList<const char *> llvm_argv = {0};297 ZigList<const char *> llvm_argv = {0};
...@@ -333,6 +325,7 @@ int main(int argc, char **argv) {...@@ -333,6 +325,7 @@ int main(int argc, char **argv) {
333 ZigList<const char *> test_exec_args = {0};325 ZigList<const char *> test_exec_args = {0};
334 int comptime_args_end = 0;326 int comptime_args_end = 0;
335 int runtime_args_start = argc;327 int runtime_args_start = argc;
328 bool no_rosegment_workaround = false;
336329
337 if (argc >= 2 && strcmp(argv[1], "build") == 0) {330 if (argc >= 2 && strcmp(argv[1], "build") == 0) {
338 const char *zig_exe_path = arg0;331 const char *zig_exe_path = arg0;
...@@ -359,17 +352,12 @@ int main(int argc, char **argv) {...@@ -359,17 +352,12 @@ int main(int argc, char **argv) {
359 } else if (i + 1 < argc && strcmp(argv[i], "--cache-dir") == 0) {352 } else if (i + 1 < argc && strcmp(argv[i], "--cache-dir") == 0) {
360 cache_dir = argv[i + 1];353 cache_dir = argv[i + 1];
361 i += 1;354 i += 1;
362 } else if (i + 1 < argc && strcmp(argv[i], "--zig-install-prefix") == 0) {
363 args.append(argv[i]);
364 i += 1;
365 zig_install_prefix = argv[i];
366 args.append(zig_install_prefix);
367 } else {355 } else {
368 args.append(argv[i]);356 args.append(argv[i]);
369 }357 }
370 }358 }
371359
372 Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix);360 Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
373361
374 Buf *zig_std_dir = buf_alloc();362 Buf *zig_std_dir = buf_alloc();
375 os_path_join(zig_lib_dir_buf, buf_create_from_str("std"), zig_std_dir);363 os_path_join(zig_lib_dir_buf, buf_create_from_str("std"), zig_std_dir);
...@@ -497,6 +485,8 @@ int main(int argc, char **argv) {...@@ -497,6 +485,8 @@ int main(int argc, char **argv) {
497 build_mode = BuildModeFastRelease;485 build_mode = BuildModeFastRelease;
498 } else if (strcmp(arg, "--release-safe") == 0) {486 } else if (strcmp(arg, "--release-safe") == 0) {
499 build_mode = BuildModeSafeRelease;487 build_mode = BuildModeSafeRelease;
488 } else if (strcmp(arg, "--release-small") == 0) {
489 build_mode = BuildModeSmallRelease;
500 } else if (strcmp(arg, "--strip") == 0) {490 } else if (strcmp(arg, "--strip") == 0) {
501 strip = true;491 strip = true;
502 } else if (strcmp(arg, "--static") == 0) {492 } else if (strcmp(arg, "--static") == 0) {
...@@ -519,6 +509,8 @@ int main(int argc, char **argv) {...@@ -519,6 +509,8 @@ int main(int argc, char **argv) {
519 mconsole = true;509 mconsole = true;
520 } else if (strcmp(arg, "-rdynamic") == 0) {510 } else if (strcmp(arg, "-rdynamic") == 0) {
521 rdynamic = true;511 rdynamic = true;
512 } else if (strcmp(arg, "--no-rosegment") == 0) {
513 no_rosegment_workaround = true;
522 } else if (strcmp(arg, "--each-lib-rpath") == 0) {514 } else if (strcmp(arg, "--each-lib-rpath") == 0) {
523 each_lib_rpath = true;515 each_lib_rpath = true;
524 } else if (strcmp(arg, "--enable-timing-info") == 0) {516 } else if (strcmp(arg, "--enable-timing-info") == 0) {
...@@ -590,8 +582,6 @@ int main(int argc, char **argv) {...@@ -590,8 +582,6 @@ int main(int argc, char **argv) {
590 msvc_lib_dir = argv[i];582 msvc_lib_dir = argv[i];
591 } else if (strcmp(arg, "--kernel32-lib-dir") == 0) {583 } else if (strcmp(arg, "--kernel32-lib-dir") == 0) {
592 kernel32_lib_dir = argv[i];584 kernel32_lib_dir = argv[i];
593 } else if (strcmp(arg, "--zig-install-prefix") == 0) {
594 zig_install_prefix = argv[i];
595 } else if (strcmp(arg, "--dynamic-linker") == 0) {585 } else if (strcmp(arg, "--dynamic-linker") == 0) {
596 dynamic_linker = argv[i];586 dynamic_linker = argv[i];
597 } else if (strcmp(arg, "-isystem") == 0) {587 } else if (strcmp(arg, "-isystem") == 0) {
...@@ -803,7 +793,7 @@ int main(int argc, char **argv) {...@@ -803,7 +793,7 @@ int main(int argc, char **argv) {
803 full_cache_dir);793 full_cache_dir);
804 }794 }
805795
806 Buf *zig_lib_dir_buf = resolve_zig_lib_dir(zig_install_prefix);796 Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
807797
808 CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, zig_lib_dir_buf);798 CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, zig_lib_dir_buf);
809 codegen_set_out_name(g, buf_out_name);799 codegen_set_out_name(g, buf_out_name);
...@@ -858,6 +848,7 @@ int main(int argc, char **argv) {...@@ -858,6 +848,7 @@ int main(int argc, char **argv) {
858848
859 codegen_set_windows_subsystem(g, mwindows, mconsole);849 codegen_set_windows_subsystem(g, mwindows, mconsole);
860 codegen_set_rdynamic(g, rdynamic);850 codegen_set_rdynamic(g, rdynamic);
851 g->no_rosegment_workaround = no_rosegment_workaround;
861 if (mmacosx_version_min && mios_version_min) {852 if (mmacosx_version_min && mios_version_min) {
862 fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n");853 fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n");
863 return EXIT_FAILURE;854 return EXIT_FAILURE;
src/os.cpp+1
...@@ -1007,6 +1007,7 @@ int os_self_exe_path(Buf *out_path) {...@@ -1007,6 +1007,7 @@ int os_self_exe_path(Buf *out_path) {
1007 buf_resize(out_path, buf_len(out_path) * 2);1007 buf_resize(out_path, buf_len(out_path) * 2);
1008 continue;1008 continue;
1009 }1009 }
1010 buf_resize(out_path, amt);
1010 return 0;1011 return 0;
1011 }1012 }
1012#endif1013#endif
src/parser.cpp+23-2
...@@ -648,12 +648,30 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m...@@ -648,12 +648,30 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
648}648}
649649
650/*650/*
651SuspendExpression(body) = "suspend" "|" Symbol "|" body651SuspendExpression(body) = option(Symbol ":") "suspend" option(("|" Symbol "|" body))
652*/652*/
653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {653static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
654 size_t orig_token_index = *token_index;654 size_t orig_token_index = *token_index;
655655
656 Token *suspend_token = &pc->tokens->at(*token_index);656 Token *name_token = nullptr;
657 Token *token = &pc->tokens->at(*token_index);
658 if (token->id == TokenIdSymbol) {
659 *token_index += 1;
660 Token *colon_token = &pc->tokens->at(*token_index);
661 if (colon_token->id == TokenIdColon) {
662 *token_index += 1;
663 name_token = token;
664 token = &pc->tokens->at(*token_index);
665 } else if (mandatory) {
666 ast_expect_token(pc, colon_token, TokenIdColon);
667 zig_unreachable();
668 } else {
669 *token_index = orig_token_index;
670 return nullptr;
671 }
672 }
673
674 Token *suspend_token = token;
657 if (suspend_token->id == TokenIdKeywordSuspend) {675 if (suspend_token->id == TokenIdKeywordSuspend) {
658 *token_index += 1;676 *token_index += 1;
659 } else if (mandatory) {677 } else if (mandatory) {
...@@ -675,6 +693,9 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b...@@ -675,6 +693,9 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b
675 }693 }
676694
677 AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);695 AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
696 if (name_token != nullptr) {
697 node->data.suspend.name = token_buf(name_token);
698 }
678 node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);699 node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);
679 ast_eat_token(pc, token_index, TokenIdBinOr);700 ast_eat_token(pc, token_index, TokenIdBinOr);
680 node->data.suspend.block = ast_parse_block(pc, token_index, true);701 node->data.suspend.block = ast_parse_block(pc, token_index, true);
src/zig_llvm.cpp+7-5
...@@ -82,7 +82,7 @@ static const bool assertions_on = false;...@@ -82,7 +82,7 @@ static const bool assertions_on = false;
82#endif82#endif
8383
84bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,84bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
85 const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug)85 const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small)
86{86{
87 std::error_code EC;87 std::error_code EC;
88 raw_fd_ostream dest(filename, EC, sys::fs::F_None);88 raw_fd_ostream dest(filename, EC, sys::fs::F_None);
...@@ -101,7 +101,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM...@@ -101,7 +101,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
101 return true;101 return true;
102 }102 }
103 PMBuilder->OptLevel = target_machine->getOptLevel();103 PMBuilder->OptLevel = target_machine->getOptLevel();
104 PMBuilder->SizeLevel = 0;104 PMBuilder->SizeLevel = is_small ? 2 : 0;
105105
106 PMBuilder->DisableTailCalls = is_debug;106 PMBuilder->DisableTailCalls = is_debug;
107 PMBuilder->DisableUnitAtATime = is_debug;107 PMBuilder->DisableUnitAtATime = is_debug;
...@@ -780,10 +780,12 @@ static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) {...@@ -780,10 +780,12 @@ static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) {
780780
781LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,781LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
782 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,782 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
783 LLVMAtomicOrdering failure_ordering)783 LLVMAtomicOrdering failure_ordering, bool is_weak)
784{784{
785 return wrap(unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp), unwrap(new_val),785 AtomicCmpXchgInst *inst = unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp),
786 mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering)));786 unwrap(new_val), mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering));
787 inst->setWeak(is_weak);
788 return wrap(inst);
787}789}
788790
789LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,791LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
src/zig_llvm.h+2-2
...@@ -52,7 +52,7 @@ enum ZigLLVM_EmitOutputType {...@@ -52,7 +52,7 @@ enum ZigLLVM_EmitOutputType {
52};52};
5353
54ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,54ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
55 const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug);55 const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small);
5656
57ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref);57ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref);
5858
...@@ -72,7 +72,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr,...@@ -72,7 +72,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildMemSet(LLVMBuilderRef B, LLVMValueRef Ptr,
7272
73ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,73ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
74 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,74 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
75 LLVMAtomicOrdering failure_ordering);75 LLVMAtomicOrdering failure_ordering, bool is_weak);
7676
77ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,77ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS,
78 const char *name);78 const char *name);
std/build.zig+10-5
...@@ -426,15 +426,18 @@ pub const Builder = struct {...@@ -426,15 +426,18 @@ pub const Builder = struct {
426426
427 const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false;427 const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false;
428 const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false;428 const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false;
429 const release_small = self.option(bool, "release-small", "size optimizations on and safety off") ?? false;
429430
430 const mode = if (release_safe and !release_fast)431 const mode = if (release_safe and !release_fast and !release_small)
431 builtin.Mode.ReleaseSafe432 builtin.Mode.ReleaseSafe
432 else if (release_fast and !release_safe)433 else if (release_fast and !release_safe and !release_small)
433 builtin.Mode.ReleaseFast434 builtin.Mode.ReleaseFast
434 else if (!release_fast and !release_safe)435 else if (release_small and !release_fast and !release_safe)
436 builtin.Mode.ReleaseSmall
437 else if (!release_fast and !release_safe and !release_small)
435 builtin.Mode.Debug438 builtin.Mode.Debug
436 else x: {439 else x: {
437 warn("Both -Drelease-safe and -Drelease-fast specified");440 warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)");
438 self.markInvalidUserInput();441 self.markInvalidUserInput();
439 break :x builtin.Mode.Debug;442 break :x builtin.Mode.Debug;
440 };443 };
...@@ -1229,6 +1232,7 @@ pub const LibExeObjStep = struct {...@@ -1229,6 +1232,7 @@ pub const LibExeObjStep = struct {
1229 builtin.Mode.Debug => {},1232 builtin.Mode.Debug => {},
1230 builtin.Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,1233 builtin.Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,
1231 builtin.Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable,1234 builtin.Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable,
1235 builtin.Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable,
1232 }1236 }
12331237
1234 zig_args.append("--cache-dir") catch unreachable;1238 zig_args.append("--cache-dir") catch unreachable;
...@@ -1369,7 +1373,7 @@ pub const LibExeObjStep = struct {...@@ -1369,7 +1373,7 @@ pub const LibExeObjStep = struct {
1369 args.append("ssp-buffer-size=4") catch unreachable;1373 args.append("ssp-buffer-size=4") catch unreachable;
1370 }1374 }
1371 },1375 },
1372 builtin.Mode.ReleaseFast => {1376 builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => {
1373 args.append("-O2") catch unreachable;1377 args.append("-O2") catch unreachable;
1374 args.append("-fno-stack-protector") catch unreachable;1378 args.append("-fno-stack-protector") catch unreachable;
1375 },1379 },
...@@ -1706,6 +1710,7 @@ pub const TestStep = struct {...@@ -1706,6 +1710,7 @@ pub const TestStep = struct {
1706 builtin.Mode.Debug => {},1710 builtin.Mode.Debug => {},
1707 builtin.Mode.ReleaseSafe => try zig_args.append("--release-safe"),1711 builtin.Mode.ReleaseSafe => try zig_args.append("--release-safe"),
1708 builtin.Mode.ReleaseFast => try zig_args.append("--release-fast"),1712 builtin.Mode.ReleaseFast => try zig_args.append("--release-fast"),
1713 builtin.Mode.ReleaseSmall => try zig_args.append("--release-small"),
1709 }1714 }
17101715
1711 switch (self.target) {1716 switch (self.target) {
std/c/darwin.zig+18
...@@ -3,10 +3,28 @@ pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int;...@@ -3,10 +3,28 @@ pub extern "c" fn _NSGetExecutablePath(buf: &u8, bufsize: &u32) c_int;
33
4pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize;4pub extern "c" fn __getdirentries64(fd: c_int, buf_ptr: &u8, buf_len: usize, basep: &i64) usize;
55
6pub extern "c" fn mach_absolute_time() u64;
7pub extern "c" fn mach_timebase_info(tinfo: ?&mach_timebase_info_data) void;
8
6pub use @import("../os/darwin_errno.zig");9pub use @import("../os/darwin_errno.zig");
710
8pub const _errno = __error;11pub const _errno = __error;
912
13pub const timeval = extern struct {
14 tv_sec: isize,
15 tv_usec: isize,
16};
17
18pub const timezone = extern struct {
19 tz_minuteswest: i32,
20 tz_dsttime: i32,
21};
22
23pub const mach_timebase_info_data = struct {
24 numer: u32,
25 denom: u32,
26};
27
10/// Renamed to Stat to not conflict with the stat function.28/// Renamed to Stat to not conflict with the stat function.
11pub const Stat = extern struct {29pub const Stat = extern struct {
12 dev: i32,30 dev: i32,
std/c/index.zig+2
...@@ -28,6 +28,7 @@ pub extern "c" fn unlink(path: &const u8) c_int;...@@ -28,6 +28,7 @@ pub extern "c" fn unlink(path: &const u8) c_int;
28pub extern "c" fn getcwd(buf: &u8, size: usize) ?&u8;28pub extern "c" fn getcwd(buf: &u8, size: usize) ?&u8;
29pub extern "c" fn waitpid(pid: c_int, stat_loc: &c_int, options: c_int) c_int;29pub extern "c" fn waitpid(pid: c_int, stat_loc: &c_int, options: c_int) c_int;
30pub extern "c" fn fork() c_int;30pub extern "c" fn fork() c_int;
31pub extern "c" fn access(path: &const u8, mode: c_uint) c_int;
31pub extern "c" fn pipe(fds: &c_int) c_int;32pub extern "c" fn pipe(fds: &c_int) c_int;
32pub extern "c" fn mkdir(path: &const u8, mode: c_uint) c_int;33pub extern "c" fn mkdir(path: &const u8, mode: c_uint) c_int;
33pub extern "c" fn symlink(existing: &const u8, new: &const u8) c_int;34pub extern "c" fn symlink(existing: &const u8, new: &const u8) c_int;
...@@ -40,6 +41,7 @@ pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int;...@@ -40,6 +41,7 @@ pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int;
40pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize;41pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) isize;
41pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8;42pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) ?&u8;
42pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int;43pub extern "c" fn sigprocmask(how: c_int, noalias set: &const sigset_t, noalias oset: ?&sigset_t) c_int;
44pub extern "c" fn gettimeofday(tv: ?&timeval, tz: ?&timezone) c_int;
43pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int;45pub extern "c" fn sigaction(sig: c_int, noalias act: &const Sigaction, noalias oact: ?&Sigaction) c_int;
44pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?&timespec) c_int;46pub extern "c" fn nanosleep(rqtp: &const timespec, rmtp: ?&timespec) c_int;
45pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;47pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;
std/crypto/throughput_test.zig+11-16
...@@ -1,22 +1,17 @@...@@ -1,22 +1,17 @@
1// Modify the HashFunction variable to the one wanted to test.1// Modify the HashFunction variable to the one wanted to test.
2//2//
3// NOTE: The throughput measurement may be slightly lower than other measurements since we run
4// through our block alignment functions as well. Be aware when comparing against other tests.
5//
6// ```3// ```
7// zig build-exe --release-fast --library c throughput_test.zig4// zig build-exe --release-fast throughput_test.zig
8// ./throughput_test5// ./throughput_test
9// ```6// ```
10const HashFunction = @import("md5.zig").Md5;
11const BytesToHash = 1024 * Mb;
127
13const std = @import("std");8const std = @import("std");
9const time = std.os.time;
10const Timer = time.Timer;
11const HashFunction = @import("md5.zig").Md5;
1412
15const c = @cImport({13const MiB = 1024 * 1024;
16 @cInclude("time.h");14const BytesToHash = 1024 * MiB;
17});
18
19const Mb = 1024 * 1024;
2015
21pub fn main() !void {16pub fn main() !void {
22 var stdout_file = try std.io.getStdOut();17 var stdout_file = try std.io.getStdOut();
...@@ -29,15 +24,15 @@ pub fn main() !void {...@@ -29,15 +24,15 @@ pub fn main() !void {
29 var h = HashFunction.init();24 var h = HashFunction.init();
30 var offset: usize = 0;25 var offset: usize = 0;
3126
32 const start = c.clock();27 var timer = try Timer.start();
28 const start = timer.lap();
33 while (offset < BytesToHash) : (offset += block.len) {29 while (offset < BytesToHash) : (offset += block.len) {
34 h.update(block[0..]);30 h.update(block[0..]);
35 }31 }
36 const end = c.clock();32 const end = timer.read();
3733
38 const elapsed_s = f64((end - start) * c.CLOCKS_PER_SEC) / 1000000;34 const elapsed_s = f64(end - start) / time.ns_per_s;
39 const throughput = u64(BytesToHash / elapsed_s);35 const throughput = u64(BytesToHash / elapsed_s);
4036
41 try stdout.print("{}: ", @typeName(HashFunction));37 try stdout.print("{}: {} MiB/s\n", @typeName(HashFunction), throughput / (1 * MiB));
42 try stdout.print("{} Mb/s\n", throughput);
43}38}
std/debug/index.zig+20-5
...@@ -38,7 +38,7 @@ pub fn getSelfDebugInfo() !&ElfStackTrace {...@@ -38,7 +38,7 @@ pub fn getSelfDebugInfo() !&ElfStackTrace {
38 if (self_debug_info) |info| {38 if (self_debug_info) |info| {
39 return info;39 return info;
40 } else {40 } else {
41 const info = try openSelfDebugInfo(global_allocator);41 const info = try openSelfDebugInfo(getDebugInfoAllocator());
42 self_debug_info = info;42 self_debug_info = info;
43 return info;43 return info;
44 }44 }
...@@ -51,7 +51,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {...@@ -51,7 +51,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
51 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;51 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
52 return;52 return;
53 };53 };
54 writeCurrentStackTrace(stderr, global_allocator, debug_info, stderr_file.isTty(), start_addr) catch |err| {54 writeCurrentStackTrace(stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty(), start_addr) catch |err| {
55 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;55 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
56 return;56 return;
57 };57 };
...@@ -64,7 +64,7 @@ pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void {...@@ -64,7 +64,7 @@ pub fn dumpStackTrace(stack_trace: &const builtin.StackTrace) void {
64 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;64 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
65 return;65 return;
66 };66 };
67 writeStackTrace(stack_trace, stderr, global_allocator, debug_info, stderr_file.isTty()) catch |err| {67 writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, stderr_file.isTty()) catch |err| {
68 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;68 stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return;
69 return;69 return;
70 };70 };
...@@ -592,8 +592,8 @@ fn getString(st: &ElfStackTrace, offset: u64) ![]u8 {...@@ -592,8 +592,8 @@ fn getString(st: &ElfStackTrace, offset: u64) ![]u8 {
592}592}
593593
594fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 {594fn readAllocBytes(allocator: &mem.Allocator, in_stream: var, size: usize) ![]u8 {
595 const buf = try global_allocator.alloc(u8, size);595 const buf = try allocator.alloc(u8, size);
596 errdefer global_allocator.free(buf);596 errdefer allocator.free(buf);
597 if ((try in_stream.read(buf)) < size) return error.EndOfFile;597 if ((try in_stream.read(buf)) < size) return error.EndOfFile;
598 return buf;598 return buf;
599}599}
...@@ -1126,6 +1126,21 @@ fn readILeb128(in_stream: var) !i64 {...@@ -1126,6 +1126,21 @@ fn readILeb128(in_stream: var) !i64 {
1126 }1126 }
1127}1127}
11281128
1129/// This should only be used in temporary test programs.
1129pub const global_allocator = &global_fixed_allocator.allocator;1130pub const global_allocator = &global_fixed_allocator.allocator;
1130var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]);1131var global_fixed_allocator = std.heap.FixedBufferAllocator.init(global_allocator_mem[0..]);
1131var global_allocator_mem: [100 * 1024]u8 = undefined;1132var global_allocator_mem: [100 * 1024]u8 = undefined;
1133
1134
1135// TODO make thread safe
1136var debug_info_allocator: ?&mem.Allocator = null;
1137var debug_info_direct_allocator: std.heap.DirectAllocator = undefined;
1138var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;
1139fn getDebugInfoAllocator() &mem.Allocator {
1140 if (debug_info_allocator) |a| return a;
1141
1142 debug_info_direct_allocator = std.heap.DirectAllocator.init();
1143 debug_info_arena_allocator = std.heap.ArenaAllocator.init(&debug_info_direct_allocator.allocator);
1144 debug_info_allocator = &debug_info_arena_allocator.allocator;
1145 return &debug_info_arena_allocator.allocator;
1146}
std/elf.zig+611
...@@ -7,6 +7,246 @@ const mem = std.mem;...@@ -7,6 +7,246 @@ const mem = std.mem;
7const debug = std.debug;7const debug = std.debug;
8const InStream = std.stream.InStream;8const InStream = std.stream.InStream;
99
10pub const AT_NULL = 0;
11pub const AT_IGNORE = 1;
12pub const AT_EXECFD = 2;
13pub const AT_PHDR = 3;
14pub const AT_PHENT = 4;
15pub const AT_PHNUM = 5;
16pub const AT_PAGESZ = 6;
17pub const AT_BASE = 7;
18pub const AT_FLAGS = 8;
19pub const AT_ENTRY = 9;
20pub const AT_NOTELF = 10;
21pub const AT_UID = 11;
22pub const AT_EUID = 12;
23pub const AT_GID = 13;
24pub const AT_EGID = 14;
25pub const AT_CLKTCK = 17;
26pub const AT_PLATFORM = 15;
27pub const AT_HWCAP = 16;
28pub const AT_FPUCW = 18;
29pub const AT_DCACHEBSIZE = 19;
30pub const AT_ICACHEBSIZE = 20;
31pub const AT_UCACHEBSIZE = 21;
32pub const AT_IGNOREPPC = 22;
33pub const AT_SECURE = 23;
34pub const AT_BASE_PLATFORM = 24;
35pub const AT_RANDOM = 25;
36pub const AT_HWCAP2 = 26;
37pub const AT_EXECFN = 31;
38pub const AT_SYSINFO = 32;
39pub const AT_SYSINFO_EHDR = 33;
40pub const AT_L1I_CACHESHAPE = 34;
41pub const AT_L1D_CACHESHAPE = 35;
42pub const AT_L2_CACHESHAPE = 36;
43pub const AT_L3_CACHESHAPE = 37;
44pub const AT_L1I_CACHESIZE = 40;
45pub const AT_L1I_CACHEGEOMETRY = 41;
46pub const AT_L1D_CACHESIZE = 42;
47pub const AT_L1D_CACHEGEOMETRY = 43;
48pub const AT_L2_CACHESIZE = 44;
49pub const AT_L2_CACHEGEOMETRY = 45;
50pub const AT_L3_CACHESIZE = 46;
51pub const AT_L3_CACHEGEOMETRY = 47;
52
53pub const DT_NULL = 0;
54pub const DT_NEEDED = 1;
55pub const DT_PLTRELSZ = 2;
56pub const DT_PLTGOT = 3;
57pub const DT_HASH = 4;
58pub const DT_STRTAB = 5;
59pub const DT_SYMTAB = 6;
60pub const DT_RELA = 7;
61pub const DT_RELASZ = 8;
62pub const DT_RELAENT = 9;
63pub const DT_STRSZ = 10;
64pub const DT_SYMENT = 11;
65pub const DT_INIT = 12;
66pub const DT_FINI = 13;
67pub const DT_SONAME = 14;
68pub const DT_RPATH = 15;
69pub const DT_SYMBOLIC = 16;
70pub const DT_REL = 17;
71pub const DT_RELSZ = 18;
72pub const DT_RELENT = 19;
73pub const DT_PLTREL = 20;
74pub const DT_DEBUG = 21;
75pub const DT_TEXTREL = 22;
76pub const DT_JMPREL = 23;
77pub const DT_BIND_NOW = 24;
78pub const DT_INIT_ARRAY = 25;
79pub const DT_FINI_ARRAY = 26;
80pub const DT_INIT_ARRAYSZ = 27;
81pub const DT_FINI_ARRAYSZ = 28;
82pub const DT_RUNPATH = 29;
83pub const DT_FLAGS = 30;
84pub const DT_ENCODING = 32;
85pub const DT_PREINIT_ARRAY = 32;
86pub const DT_PREINIT_ARRAYSZ = 33;
87pub const DT_SYMTAB_SHNDX = 34;
88pub const DT_NUM = 35;
89pub const DT_LOOS = 0x6000000d;
90pub const DT_HIOS = 0x6ffff000;
91pub const DT_LOPROC = 0x70000000;
92pub const DT_HIPROC = 0x7fffffff;
93pub const DT_PROCNUM = DT_MIPS_NUM;
94
95pub const DT_VALRNGLO = 0x6ffffd00;
96pub const DT_GNU_PRELINKED = 0x6ffffdf5;
97pub const DT_GNU_CONFLICTSZ = 0x6ffffdf6;
98pub const DT_GNU_LIBLISTSZ = 0x6ffffdf7;
99pub const DT_CHECKSUM = 0x6ffffdf8;
100pub const DT_PLTPADSZ = 0x6ffffdf9;
101pub const DT_MOVEENT = 0x6ffffdfa;
102pub const DT_MOVESZ = 0x6ffffdfb;
103pub const DT_FEATURE_1 = 0x6ffffdfc;
104pub const DT_POSFLAG_1 = 0x6ffffdfd;
105
106pub const DT_SYMINSZ = 0x6ffffdfe;
107pub const DT_SYMINENT = 0x6ffffdff;
108pub const DT_VALRNGHI = 0x6ffffdff;
109pub const DT_VALNUM = 12;
110
111pub const DT_ADDRRNGLO = 0x6ffffe00;
112pub const DT_GNU_HASH = 0x6ffffef5;
113pub const DT_TLSDESC_PLT = 0x6ffffef6;
114pub const DT_TLSDESC_GOT = 0x6ffffef7;
115pub const DT_GNU_CONFLICT = 0x6ffffef8;
116pub const DT_GNU_LIBLIST = 0x6ffffef9;
117pub const DT_CONFIG = 0x6ffffefa;
118pub const DT_DEPAUDIT = 0x6ffffefb;
119pub const DT_AUDIT = 0x6ffffefc;
120pub const DT_PLTPAD = 0x6ffffefd;
121pub const DT_MOVETAB = 0x6ffffefe;
122pub const DT_SYMINFO = 0x6ffffeff;
123pub const DT_ADDRRNGHI = 0x6ffffeff;
124pub const DT_ADDRNUM = 11;
125
126
127pub const DT_VERSYM = 0x6ffffff0;
128
129pub const DT_RELACOUNT = 0x6ffffff9;
130pub const DT_RELCOUNT = 0x6ffffffa;
131
132
133pub const DT_FLAGS_1 = 0x6ffffffb;
134pub const DT_VERDEF = 0x6ffffffc;
135
136pub const DT_VERDEFNUM = 0x6ffffffd;
137pub const DT_VERNEED = 0x6ffffffe;
138
139pub const DT_VERNEEDNUM = 0x6fffffff;
140pub const DT_VERSIONTAGNUM = 16;
141
142
143
144pub const DT_AUXILIARY = 0x7ffffffd;
145pub const DT_FILTER = 0x7fffffff;
146pub const DT_EXTRANUM = 3;
147
148
149pub const DT_SPARC_REGISTER = 0x70000001;
150pub const DT_SPARC_NUM = 2;
151
152pub const DT_MIPS_RLD_VERSION = 0x70000001;
153pub const DT_MIPS_TIME_STAMP = 0x70000002;
154pub const DT_MIPS_ICHECKSUM = 0x70000003;
155pub const DT_MIPS_IVERSION = 0x70000004;
156pub const DT_MIPS_FLAGS = 0x70000005;
157pub const DT_MIPS_BASE_ADDRESS = 0x70000006;
158pub const DT_MIPS_MSYM = 0x70000007;
159pub const DT_MIPS_CONFLICT = 0x70000008;
160pub const DT_MIPS_LIBLIST = 0x70000009;
161pub const DT_MIPS_LOCAL_GOTNO = 0x7000000a;
162pub const DT_MIPS_CONFLICTNO = 0x7000000b;
163pub const DT_MIPS_LIBLISTNO = 0x70000010;
164pub const DT_MIPS_SYMTABNO = 0x70000011;
165pub const DT_MIPS_UNREFEXTNO = 0x70000012;
166pub const DT_MIPS_GOTSYM = 0x70000013;
167pub const DT_MIPS_HIPAGENO = 0x70000014;
168pub const DT_MIPS_RLD_MAP = 0x70000016;
169pub const DT_MIPS_DELTA_CLASS = 0x70000017;
170pub const DT_MIPS_DELTA_CLASS_NO = 0x70000018;
171
172pub const DT_MIPS_DELTA_INSTANCE = 0x70000019;
173pub const DT_MIPS_DELTA_INSTANCE_NO = 0x7000001a;
174
175pub const DT_MIPS_DELTA_RELOC = 0x7000001b;
176pub const DT_MIPS_DELTA_RELOC_NO = 0x7000001c;
177
178pub const DT_MIPS_DELTA_SYM = 0x7000001d;
179
180pub const DT_MIPS_DELTA_SYM_NO = 0x7000001e;
181
182pub const DT_MIPS_DELTA_CLASSSYM = 0x70000020;
183
184pub const DT_MIPS_DELTA_CLASSSYM_NO = 0x70000021;
185
186pub const DT_MIPS_CXX_FLAGS = 0x70000022;
187pub const DT_MIPS_PIXIE_INIT = 0x70000023;
188pub const DT_MIPS_SYMBOL_LIB = 0x70000024;
189pub const DT_MIPS_LOCALPAGE_GOTIDX = 0x70000025;
190pub const DT_MIPS_LOCAL_GOTIDX = 0x70000026;
191pub const DT_MIPS_HIDDEN_GOTIDX = 0x70000027;
192pub const DT_MIPS_PROTECTED_GOTIDX = 0x70000028;
193pub const DT_MIPS_OPTIONS = 0x70000029;
194pub const DT_MIPS_INTERFACE = 0x7000002a;
195pub const DT_MIPS_DYNSTR_ALIGN = 0x7000002b;
196pub const DT_MIPS_INTERFACE_SIZE = 0x7000002c;
197pub const DT_MIPS_RLD_TEXT_RESOLVE_ADDR = 0x7000002d;
198
199pub const DT_MIPS_PERF_SUFFIX = 0x7000002e;
200
201pub const DT_MIPS_COMPACT_SIZE = 0x7000002f;
202pub const DT_MIPS_GP_VALUE = 0x70000030;
203pub const DT_MIPS_AUX_DYNAMIC = 0x70000031;
204
205pub const DT_MIPS_PLTGOT = 0x70000032;
206
207pub const DT_MIPS_RWPLT = 0x70000034;
208pub const DT_MIPS_RLD_MAP_REL = 0x70000035;
209pub const DT_MIPS_NUM = 0x36;
210
211pub const DT_ALPHA_PLTRO = (DT_LOPROC + 0);
212pub const DT_ALPHA_NUM = 1;
213
214pub const DT_PPC_GOT = (DT_LOPROC + 0);
215pub const DT_PPC_OPT = (DT_LOPROC + 1);
216pub const DT_PPC_NUM = 2;
217
218pub const DT_PPC64_GLINK = (DT_LOPROC + 0);
219pub const DT_PPC64_OPD = (DT_LOPROC + 1);
220pub const DT_PPC64_OPDSZ = (DT_LOPROC + 2);
221pub const DT_PPC64_OPT = (DT_LOPROC + 3);
222pub const DT_PPC64_NUM = 4;
223
224pub const DT_IA_64_PLT_RESERVE = (DT_LOPROC + 0);
225pub const DT_IA_64_NUM = 1;
226
227pub const DT_NIOS2_GP = 0x70000002;
228
229pub const PT_NULL = 0;
230pub const PT_LOAD = 1;
231pub const PT_DYNAMIC = 2;
232pub const PT_INTERP = 3;
233pub const PT_NOTE = 4;
234pub const PT_SHLIB = 5;
235pub const PT_PHDR = 6;
236pub const PT_TLS = 7;
237pub const PT_NUM = 8;
238pub const PT_LOOS = 0x60000000;
239pub const PT_GNU_EH_FRAME = 0x6474e550;
240pub const PT_GNU_STACK = 0x6474e551;
241pub const PT_GNU_RELRO = 0x6474e552;
242pub const PT_LOSUNW = 0x6ffffffa;
243pub const PT_SUNWBSS = 0x6ffffffa;
244pub const PT_SUNWSTACK = 0x6ffffffb;
245pub const PT_HISUNW = 0x6fffffff;
246pub const PT_HIOS = 0x6fffffff;
247pub const PT_LOPROC = 0x70000000;
248pub const PT_HIPROC = 0x7fffffff;
249
10pub const SHT_NULL = 0;250pub const SHT_NULL = 0;
11pub const SHT_PROGBITS = 1;251pub const SHT_PROGBITS = 1;
12pub const SHT_SYMTAB = 2;252pub const SHT_SYMTAB = 2;
...@@ -31,6 +271,45 @@ pub const SHT_HIPROC = 0x7fffffff;...@@ -31,6 +271,45 @@ pub const SHT_HIPROC = 0x7fffffff;
31pub const SHT_LOUSER = 0x80000000;271pub const SHT_LOUSER = 0x80000000;
32pub const SHT_HIUSER = 0xffffffff;272pub const SHT_HIUSER = 0xffffffff;
33273
274pub const STB_LOCAL = 0;
275pub const STB_GLOBAL = 1;
276pub const STB_WEAK = 2;
277pub const STB_NUM = 3;
278pub const STB_LOOS = 10;
279pub const STB_GNU_UNIQUE = 10;
280pub const STB_HIOS = 12;
281pub const STB_LOPROC = 13;
282pub const STB_HIPROC = 15;
283
284pub const STB_MIPS_SPLIT_COMMON = 13;
285
286pub const STT_NOTYPE = 0;
287pub const STT_OBJECT = 1;
288pub const STT_FUNC = 2;
289pub const STT_SECTION = 3;
290pub const STT_FILE = 4;
291pub const STT_COMMON = 5;
292pub const STT_TLS = 6;
293pub const STT_NUM = 7;
294pub const STT_LOOS = 10;
295pub const STT_GNU_IFUNC = 10;
296pub const STT_HIOS = 12;
297pub const STT_LOPROC = 13;
298pub const STT_HIPROC = 15;
299
300pub const STT_SPARC_REGISTER = 13;
301
302pub const STT_PARISC_MILLICODE = 13;
303
304pub const STT_HP_OPAQUE = (STT_LOOS + 0x1);
305pub const STT_HP_STUB = (STT_LOOS + 0x2);
306
307pub const STT_ARM_TFUNC = STT_LOPROC;
308pub const STT_ARM_16BIT = STT_HIPROC;
309
310pub const VER_FLG_BASE = 0x1;
311pub const VER_FLG_WEAK = 0x2;
312
34pub const FileType = enum {313pub const FileType = enum {
35 Relocatable,314 Relocatable,
36 Executable,315 Executable,
...@@ -266,3 +545,335 @@ pub const Elf = struct {...@@ -266,3 +545,335 @@ pub const Elf = struct {
266 try elf.in_file.seekTo(elf_section.offset);545 try elf.in_file.seekTo(elf_section.offset);
267 }546 }
268};547};
548
549pub const EI_NIDENT = 16;
550pub const Elf32_Half = u16;
551pub const Elf64_Half = u16;
552pub const Elf32_Word = u32;
553pub const Elf32_Sword = i32;
554pub const Elf64_Word = u32;
555pub const Elf64_Sword = i32;
556pub const Elf32_Xword = u64;
557pub const Elf32_Sxword = i64;
558pub const Elf64_Xword = u64;
559pub const Elf64_Sxword = i64;
560pub const Elf32_Addr = u32;
561pub const Elf64_Addr = u64;
562pub const Elf32_Off = u32;
563pub const Elf64_Off = u64;
564pub const Elf32_Section = u16;
565pub const Elf64_Section = u16;
566pub const Elf32_Versym = Elf32_Half;
567pub const Elf64_Versym = Elf64_Half;
568pub const Elf32_Ehdr = extern struct {
569 e_ident: [EI_NIDENT]u8,
570 e_type: Elf32_Half,
571 e_machine: Elf32_Half,
572 e_version: Elf32_Word,
573 e_entry: Elf32_Addr,
574 e_phoff: Elf32_Off,
575 e_shoff: Elf32_Off,
576 e_flags: Elf32_Word,
577 e_ehsize: Elf32_Half,
578 e_phentsize: Elf32_Half,
579 e_phnum: Elf32_Half,
580 e_shentsize: Elf32_Half,
581 e_shnum: Elf32_Half,
582 e_shstrndx: Elf32_Half,
583};
584pub const Elf64_Ehdr = extern struct {
585 e_ident: [EI_NIDENT]u8,
586 e_type: Elf64_Half,
587 e_machine: Elf64_Half,
588 e_version: Elf64_Word,
589 e_entry: Elf64_Addr,
590 e_phoff: Elf64_Off,
591 e_shoff: Elf64_Off,
592 e_flags: Elf64_Word,
593 e_ehsize: Elf64_Half,
594 e_phentsize: Elf64_Half,
595 e_phnum: Elf64_Half,
596 e_shentsize: Elf64_Half,
597 e_shnum: Elf64_Half,
598 e_shstrndx: Elf64_Half,
599};
600pub const Elf32_Shdr = extern struct {
601 sh_name: Elf32_Word,
602 sh_type: Elf32_Word,
603 sh_flags: Elf32_Word,
604 sh_addr: Elf32_Addr,
605 sh_offset: Elf32_Off,
606 sh_size: Elf32_Word,
607 sh_link: Elf32_Word,
608 sh_info: Elf32_Word,
609 sh_addralign: Elf32_Word,
610 sh_entsize: Elf32_Word,
611};
612pub const Elf64_Shdr = extern struct {
613 sh_name: Elf64_Word,
614 sh_type: Elf64_Word,
615 sh_flags: Elf64_Xword,
616 sh_addr: Elf64_Addr,
617 sh_offset: Elf64_Off,
618 sh_size: Elf64_Xword,
619 sh_link: Elf64_Word,
620 sh_info: Elf64_Word,
621 sh_addralign: Elf64_Xword,
622 sh_entsize: Elf64_Xword,
623};
624pub const Elf32_Chdr = extern struct {
625 ch_type: Elf32_Word,
626 ch_size: Elf32_Word,
627 ch_addralign: Elf32_Word,
628};
629pub const Elf64_Chdr = extern struct {
630 ch_type: Elf64_Word,
631 ch_reserved: Elf64_Word,
632 ch_size: Elf64_Xword,
633 ch_addralign: Elf64_Xword,
634};
635pub const Elf32_Sym = extern struct {
636 st_name: Elf32_Word,
637 st_value: Elf32_Addr,
638 st_size: Elf32_Word,
639 st_info: u8,
640 st_other: u8,
641 st_shndx: Elf32_Section,
642};
643pub const Elf64_Sym = extern struct {
644 st_name: Elf64_Word,
645 st_info: u8,
646 st_other: u8,
647 st_shndx: Elf64_Section,
648 st_value: Elf64_Addr,
649 st_size: Elf64_Xword,
650};
651pub const Elf32_Syminfo = extern struct {
652 si_boundto: Elf32_Half,
653 si_flags: Elf32_Half,
654};
655pub const Elf64_Syminfo = extern struct {
656 si_boundto: Elf64_Half,
657 si_flags: Elf64_Half,
658};
659pub const Elf32_Rel = extern struct {
660 r_offset: Elf32_Addr,
661 r_info: Elf32_Word,
662};
663pub const Elf64_Rel = extern struct {
664 r_offset: Elf64_Addr,
665 r_info: Elf64_Xword,
666};
667pub const Elf32_Rela = extern struct {
668 r_offset: Elf32_Addr,
669 r_info: Elf32_Word,
670 r_addend: Elf32_Sword,
671};
672pub const Elf64_Rela = extern struct {
673 r_offset: Elf64_Addr,
674 r_info: Elf64_Xword,
675 r_addend: Elf64_Sxword,
676};
677pub const Elf32_Phdr = extern struct {
678 p_type: Elf32_Word,
679 p_offset: Elf32_Off,
680 p_vaddr: Elf32_Addr,
681 p_paddr: Elf32_Addr,
682 p_filesz: Elf32_Word,
683 p_memsz: Elf32_Word,
684 p_flags: Elf32_Word,
685 p_align: Elf32_Word,
686};
687pub const Elf64_Phdr = extern struct {
688 p_type: Elf64_Word,
689 p_flags: Elf64_Word,
690 p_offset: Elf64_Off,
691 p_vaddr: Elf64_Addr,
692 p_paddr: Elf64_Addr,
693 p_filesz: Elf64_Xword,
694 p_memsz: Elf64_Xword,
695 p_align: Elf64_Xword,
696};
697pub const Elf32_Dyn = extern struct {
698 d_tag: Elf32_Sword,
699 d_un: extern union {
700 d_val: Elf32_Word,
701 d_ptr: Elf32_Addr,
702 },
703};
704pub const Elf64_Dyn = extern struct {
705 d_tag: Elf64_Sxword,
706 d_un: extern union {
707 d_val: Elf64_Xword,
708 d_ptr: Elf64_Addr,
709 },
710};
711pub const Elf32_Verdef = extern struct {
712 vd_version: Elf32_Half,
713 vd_flags: Elf32_Half,
714 vd_ndx: Elf32_Half,
715 vd_cnt: Elf32_Half,
716 vd_hash: Elf32_Word,
717 vd_aux: Elf32_Word,
718 vd_next: Elf32_Word,
719};
720pub const Elf64_Verdef = extern struct {
721 vd_version: Elf64_Half,
722 vd_flags: Elf64_Half,
723 vd_ndx: Elf64_Half,
724 vd_cnt: Elf64_Half,
725 vd_hash: Elf64_Word,
726 vd_aux: Elf64_Word,
727 vd_next: Elf64_Word,
728};
729pub const Elf32_Verdaux = extern struct {
730 vda_name: Elf32_Word,
731 vda_next: Elf32_Word,
732};
733pub const Elf64_Verdaux = extern struct {
734 vda_name: Elf64_Word,
735 vda_next: Elf64_Word,
736};
737pub const Elf32_Verneed = extern struct {
738 vn_version: Elf32_Half,
739 vn_cnt: Elf32_Half,
740 vn_file: Elf32_Word,
741 vn_aux: Elf32_Word,
742 vn_next: Elf32_Word,
743};
744pub const Elf64_Verneed = extern struct {
745 vn_version: Elf64_Half,
746 vn_cnt: Elf64_Half,
747 vn_file: Elf64_Word,
748 vn_aux: Elf64_Word,
749 vn_next: Elf64_Word,
750};
751pub const Elf32_Vernaux = extern struct {
752 vna_hash: Elf32_Word,
753 vna_flags: Elf32_Half,
754 vna_other: Elf32_Half,
755 vna_name: Elf32_Word,
756 vna_next: Elf32_Word,
757};
758pub const Elf64_Vernaux = extern struct {
759 vna_hash: Elf64_Word,
760 vna_flags: Elf64_Half,
761 vna_other: Elf64_Half,
762 vna_name: Elf64_Word,
763 vna_next: Elf64_Word,
764};
765pub const Elf32_auxv_t = extern struct {
766 a_type: u32,
767 a_un: extern union {
768 a_val: u32,
769 },
770};
771pub const Elf64_auxv_t = extern struct {
772 a_type: u64,
773 a_un: extern union {
774 a_val: u64,
775 },
776};
777pub const Elf32_Nhdr = extern struct {
778 n_namesz: Elf32_Word,
779 n_descsz: Elf32_Word,
780 n_type: Elf32_Word,
781};
782pub const Elf64_Nhdr = extern struct {
783 n_namesz: Elf64_Word,
784 n_descsz: Elf64_Word,
785 n_type: Elf64_Word,
786};
787pub const Elf32_Move = extern struct {
788 m_value: Elf32_Xword,
789 m_info: Elf32_Word,
790 m_poffset: Elf32_Word,
791 m_repeat: Elf32_Half,
792 m_stride: Elf32_Half,
793};
794pub const Elf64_Move = extern struct {
795 m_value: Elf64_Xword,
796 m_info: Elf64_Xword,
797 m_poffset: Elf64_Xword,
798 m_repeat: Elf64_Half,
799 m_stride: Elf64_Half,
800};
801pub const Elf32_gptab = extern union {
802 gt_header: extern struct {
803 gt_current_g_value: Elf32_Word,
804 gt_unused: Elf32_Word,
805 },
806 gt_entry: extern struct {
807 gt_g_value: Elf32_Word,
808 gt_bytes: Elf32_Word,
809 },
810};
811pub const Elf32_RegInfo = extern struct {
812 ri_gprmask: Elf32_Word,
813 ri_cprmask: [4]Elf32_Word,
814 ri_gp_value: Elf32_Sword,
815};
816pub const Elf_Options = extern struct {
817 kind: u8,
818 size: u8,
819 @"section": Elf32_Section,
820 info: Elf32_Word,
821};
822pub const Elf_Options_Hw = extern struct {
823 hwp_flags1: Elf32_Word,
824 hwp_flags2: Elf32_Word,
825};
826pub const Elf32_Lib = extern struct {
827 l_name: Elf32_Word,
828 l_time_stamp: Elf32_Word,
829 l_checksum: Elf32_Word,
830 l_version: Elf32_Word,
831 l_flags: Elf32_Word,
832};
833pub const Elf64_Lib = extern struct {
834 l_name: Elf64_Word,
835 l_time_stamp: Elf64_Word,
836 l_checksum: Elf64_Word,
837 l_version: Elf64_Word,
838 l_flags: Elf64_Word,
839};
840pub const Elf32_Conflict = Elf32_Addr;
841pub const Elf_MIPS_ABIFlags_v0 = extern struct {
842 version: Elf32_Half,
843 isa_level: u8,
844 isa_rev: u8,
845 gpr_size: u8,
846 cpr1_size: u8,
847 cpr2_size: u8,
848 fp_abi: u8,
849 isa_ext: Elf32_Word,
850 ases: Elf32_Word,
851 flags1: Elf32_Word,
852 flags2: Elf32_Word,
853};
854
855pub const Ehdr = switch(@sizeOf(usize)) {
856 4 => Elf32_Ehdr,
857 8 => Elf64_Ehdr,
858 else => @compileError("expected pointer size of 32 or 64"),
859};
860pub const Phdr = switch(@sizeOf(usize)) {
861 4 => Elf32_Phdr,
862 8 => Elf64_Phdr,
863 else => @compileError("expected pointer size of 32 or 64"),
864};
865pub const Sym = switch(@sizeOf(usize)) {
866 4 => Elf32_Sym,
867 8 => Elf64_Sym,
868 else => @compileError("expected pointer size of 32 or 64"),
869};
870pub const Verdef = switch(@sizeOf(usize)) {
871 4 => Elf32_Verdef,
872 8 => Elf64_Verdef,
873 else => @compileError("expected pointer size of 32 or 64"),
874};
875pub const Verdaux = switch(@sizeOf(usize)) {
876 4 => Elf32_Verdaux,
877 8 => Elf64_Verdaux,
878 else => @compileError("expected pointer size of 32 or 64"),
879};
std/fmt/index.zig+15-14
...@@ -8,25 +8,25 @@ const errol3 = @import("errol/index.zig").errol3;...@@ -8,25 +8,25 @@ const errol3 = @import("errol/index.zig").errol3;
88
9const max_int_digits = 65;9const max_int_digits = 65;
1010
11const State = enum { // TODO put inside format function and make sure the name and debug info is correct
12 Start,
13 OpenBrace,
14 CloseBrace,
15 Integer,
16 IntegerWidth,
17 Float,
18 FloatWidth,
19 Character,
20 Buf,
21 BufWidth,
22};
23
24/// Renders fmt string with args, calling output with slices of bytes.11/// Renders fmt string with args, calling output with slices of bytes.
25/// If `output` returns an error, the error is returned from `format` and12/// If `output` returns an error, the error is returned from `format` and
26/// `output` is not called again.13/// `output` is not called again.
27pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void,14pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context), []const u8) Errors!void,
28 comptime fmt: []const u8, args: ...) Errors!void15 comptime fmt: []const u8, args: ...) Errors!void
29{16{
17 const State = enum {
18 Start,
19 OpenBrace,
20 CloseBrace,
21 Integer,
22 IntegerWidth,
23 Float,
24 FloatWidth,
25 Character,
26 Buf,
27 BufWidth,
28 };
29
30 comptime var start_index = 0;30 comptime var start_index = 0;
31 comptime var state = State.Start;31 comptime var state = State.Start;
32 comptime var next_arg = 0;32 comptime var next_arg = 0;
...@@ -86,7 +86,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),...@@ -86,7 +86,8 @@ pub fn format(context: var, comptime Errors: type, output: fn(@typeOf(context),
86 },86 },
87 's' => {87 's' => {
88 state = State.Buf;88 state = State.Buf;
89 },'.' => {89 },
90 '.' => {
90 state = State.Float;91 state = State.Float;
91 },92 },
92 else => @compileError("Unknown format character: " ++ []u8{c}),93 else => @compileError("Unknown format character: " ++ []u8{c}),
std/heap.zig+59-8
...@@ -79,19 +79,38 @@ pub const DirectAllocator = struct {...@@ -79,19 +79,38 @@ pub const DirectAllocator = struct {
7979
80 switch (builtin.os) {80 switch (builtin.os) {
81 Os.linux, Os.macosx, Os.ios => {81 Os.linux, Os.macosx, Os.ios => {
82 assert(alignment <= os.page_size);
83 const p = os.posix;82 const p = os.posix;
84 const addr = p.mmap(null, n, p.PROT_READ|p.PROT_WRITE,83 const alloc_size = if(alignment <= os.page_size) n else n + alignment;
85 p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0);84 const addr = p.mmap(null, alloc_size, p.PROT_READ|p.PROT_WRITE,
86 if (addr == p.MAP_FAILED) {85 p.MAP_PRIVATE|p.MAP_ANONYMOUS, -1, 0);
87 return error.OutOfMemory;86 if(addr == p.MAP_FAILED) return error.OutOfMemory;
88 }87
89 return @intToPtr(&u8, addr)[0..n];88 if(alloc_size == n) return @intToPtr(&u8, addr)[0..n];
89
90 var aligned_addr = addr & ~usize(alignment - 1);
91 aligned_addr += alignment;
92
93 //We can unmap the unused portions of our mmap, but we must only
94 // pass munmap bytes that exist outside our allocated pages or it
95 // will happily eat us too
96
97 //Since alignment > page_size, we are by definition on a page boundry
98 const unused_start = addr;
99 const unused_len = aligned_addr - 1 - unused_start;
100
101 var err = p.munmap(@intToPtr(&u8, unused_start), unused_len);
102 debug.assert(p.getErrno(err) == 0);
103
104 //It is impossible that there is an unoccupied page at the top of our
105 // mmap.
106
107 return @intToPtr(&u8, aligned_addr)[0..n];
90 },108 },
91 Os.windows => {109 Os.windows => {
92 const amt = n + alignment + @sizeOf(usize);110 const amt = n + alignment + @sizeOf(usize);
93 const heap_handle = self.heap_handle ?? blk: {111 const heap_handle = self.heap_handle ?? blk: {
94 const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) ?? return error.OutOfMemory;112 const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0)
113 ?? return error.OutOfMemory;
95 self.heap_handle = hh;114 self.heap_handle = hh;
96 break :blk hh;115 break :blk hh;
97 };116 };
...@@ -322,6 +341,7 @@ test "DirectAllocator" {...@@ -322,6 +341,7 @@ test "DirectAllocator" {
322341
323 const allocator = &direct_allocator.allocator;342 const allocator = &direct_allocator.allocator;
324 try testAllocator(allocator);343 try testAllocator(allocator);
344 try testAllocatorLargeAlignment(allocator);
325}345}
326346
327test "ArenaAllocator" {347test "ArenaAllocator" {
...@@ -332,6 +352,7 @@ test "ArenaAllocator" {...@@ -332,6 +352,7 @@ test "ArenaAllocator" {
332 defer arena_allocator.deinit();352 defer arena_allocator.deinit();
333353
334 try testAllocator(&arena_allocator.allocator);354 try testAllocator(&arena_allocator.allocator);
355 try testAllocatorLargeAlignment(&arena_allocator.allocator);
335}356}
336357
337var test_fixed_buffer_allocator_memory: [30000 * @sizeOf(usize)]u8 = undefined;358var test_fixed_buffer_allocator_memory: [30000 * @sizeOf(usize)]u8 = undefined;
...@@ -339,6 +360,7 @@ test "FixedBufferAllocator" {...@@ -339,6 +360,7 @@ test "FixedBufferAllocator" {
339 var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);360 var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
340361
341 try testAllocator(&fixed_buffer_allocator.allocator);362 try testAllocator(&fixed_buffer_allocator.allocator);
363 try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
342}364}
343365
344fn testAllocator(allocator: &mem.Allocator) !void {366fn testAllocator(allocator: &mem.Allocator) !void {
...@@ -360,3 +382,32 @@ fn testAllocator(allocator: &mem.Allocator) !void {...@@ -360,3 +382,32 @@ fn testAllocator(allocator: &mem.Allocator) !void {
360382
361 allocator.free(slice);383 allocator.free(slice);
362}384}
385
386fn testAllocatorLargeAlignment(allocator: &mem.Allocator) mem.Allocator.Error!void {
387 //Maybe a platform's page_size is actually the same as or
388 // very near usize?
389 if(os.page_size << 2 > @maxValue(usize)) return;
390
391 const USizeShift = @IntType(false, std.math.log2(usize.bit_count));
392 const large_align = u29(os.page_size << 2);
393
394 var align_mask: usize = undefined;
395 _ = @shlWithOverflow(usize, ~usize(0), USizeShift(@ctz(large_align)), &align_mask);
396
397 var slice = try allocator.allocFn(allocator, 500, large_align);
398 debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
399
400 slice = try allocator.reallocFn(allocator, slice, 100, large_align);
401 debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
402
403 slice = try allocator.reallocFn(allocator, slice, 5000, large_align);
404 debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
405
406 slice = try allocator.reallocFn(allocator, slice, 10, large_align);
407 debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
408
409 slice = try allocator.reallocFn(allocator, slice, 20000, large_align);
410 debug.assert(@ptrToInt(slice.ptr) & align_mask == @ptrToInt(slice.ptr));
411
412 allocator.free(slice);
413}
std/math/complex/abs.zig created+18
...@@ -0,0 +1,18 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn abs(z: var) @typeOf(z.re) {
8 const T = @typeOf(z.re);
9 return math.hypot(T, z.re, z.im);
10}
11
12const epsilon = 0.0001;
13
14test "complex.cabs" {
15 const a = Complex(f32).new(5, 3);
16 const c = abs(a);
17 debug.assert(math.approxEq(f32, c, 5.83095, epsilon));
18}
std/math/complex/acos.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn acos(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const q = cmath.asin(z);
10 return Complex(T).new(T(math.pi) / 2 - q.re, -q.im);
11}
12
13const epsilon = 0.0001;
14
15test "complex.cacos" {
16 const a = Complex(f32).new(5, 3);
17 const c = acos(a);
18
19 debug.assert(math.approxEq(f32, c.re, 0.546975, epsilon));
20 debug.assert(math.approxEq(f32, c.im, -2.452914, epsilon));
21}
std/math/complex/acosh.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn acosh(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const q = cmath.acos(z);
10 return Complex(T).new(-q.im, q.re);
11}
12
13const epsilon = 0.0001;
14
15test "complex.cacosh" {
16 const a = Complex(f32).new(5, 3);
17 const c = acosh(a);
18
19 debug.assert(math.approxEq(f32, c.re, 2.452914, epsilon));
20 debug.assert(math.approxEq(f32, c.im, 0.546975, epsilon));
21}
std/math/complex/arg.zig created+18
...@@ -0,0 +1,18 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn arg(z: var) @typeOf(z.re) {
8 const T = @typeOf(z.re);
9 return math.atan2(T, z.im, z.re);
10}
11
12const epsilon = 0.0001;
13
14test "complex.carg" {
15 const a = Complex(f32).new(5, 3);
16 const c = arg(a);
17 debug.assert(math.approxEq(f32, c, 0.540420, epsilon));
18}
std/math/complex/asin.zig created+27
...@@ -0,0 +1,27 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn asin(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const x = z.re;
10 const y = z.im;
11
12 const p = Complex(T).new(1.0 - (x - y) * (x + y), -2.0 * x * y);
13 const q = Complex(T).new(-y, x);
14 const r = cmath.log(q.add(cmath.sqrt(p)));
15
16 return Complex(T).new(r.im, -r.re);
17}
18
19const epsilon = 0.0001;
20
21test "complex.casin" {
22 const a = Complex(f32).new(5, 3);
23 const c = asin(a);
24
25 debug.assert(math.approxEq(f32, c.re, 1.023822, epsilon));
26 debug.assert(math.approxEq(f32, c.im, 2.452914, epsilon));
27}
std/math/complex/asinh.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn asinh(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const q = Complex(T).new(-z.im, z.re);
10 const r = cmath.asin(q);
11 return Complex(T).new(r.im, -r.re);
12}
13
14const epsilon = 0.0001;
15
16test "complex.casinh" {
17 const a = Complex(f32).new(5, 3);
18 const c = asinh(a);
19
20 debug.assert(math.approxEq(f32, c.re, 2.459831, epsilon));
21 debug.assert(math.approxEq(f32, c.im, 0.533999, epsilon));
22}
std/math/complex/atan.zig created+130
...@@ -0,0 +1,130 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn atan(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 return switch (T) {
10 f32 => atan32(z),
11 f64 => atan64(z),
12 else => @compileError("atan not implemented for " ++ @typeName(z)),
13 };
14}
15
16fn redupif32(x: f32) f32 {
17 const DP1 = 3.140625;
18 const DP2 = 9.67502593994140625e-4;
19 const DP3 = 1.509957990978376432e-7;
20
21 var t = x / math.pi;
22 if (t >= 0.0) {
23 t += 0.5;
24 } else {
25 t -= 0.5;
26 }
27
28 const u = f32(i32(t));
29 return ((x - u * DP1) - u * DP2) - t * DP3;
30}
31
32fn atan32(z: &const Complex(f32)) Complex(f32) {
33 const maxnum = 1.0e38;
34
35 const x = z.re;
36 const y = z.im;
37
38 if ((x == 0.0) and (y > 1.0)) {
39 // overflow
40 return Complex(f32).new(maxnum, maxnum);
41 }
42
43 const x2 = x * x;
44 var a = 1.0 - x2 - (y * y);
45 if (a == 0.0) {
46 // overflow
47 return Complex(f32).new(maxnum, maxnum);
48 }
49
50 var t = 0.5 * math.atan2(f32, 2.0 * x, a);
51 var w = redupif32(t);
52
53 t = y - 1.0;
54 a = x2 + t * t;
55 if (a == 0.0) {
56 // overflow
57 return Complex(f32).new(maxnum, maxnum);
58 }
59
60 t = y + 1.0;
61 a = (x2 + (t * t)) / a;
62 return Complex(f32).new(w, 0.25 * math.ln(a));
63}
64
65fn redupif64(x: f64) f64 {
66 const DP1 = 3.14159265160560607910;
67 const DP2 = 1.98418714791870343106e-9;
68 const DP3 = 1.14423774522196636802e-17;
69
70 var t = x / math.pi;
71 if (t >= 0.0) {
72 t += 0.5;
73 } else {
74 t -= 0.5;
75 }
76
77 const u = f64(i64(t));
78 return ((x - u * DP1) - u * DP2) - t * DP3;
79}
80
81fn atan64(z: &const Complex(f64)) Complex(f64) {
82 const maxnum = 1.0e308;
83
84 const x = z.re;
85 const y = z.im;
86
87 if ((x == 0.0) and (y > 1.0)) {
88 // overflow
89 return Complex(f64).new(maxnum, maxnum);
90 }
91
92 const x2 = x * x;
93 var a = 1.0 - x2 - (y * y);
94 if (a == 0.0) {
95 // overflow
96 return Complex(f64).new(maxnum, maxnum);
97 }
98
99 var t = 0.5 * math.atan2(f64, 2.0 * x, a);
100 var w = redupif64(t);
101
102 t = y - 1.0;
103 a = x2 + t * t;
104 if (a == 0.0) {
105 // overflow
106 return Complex(f64).new(maxnum, maxnum);
107 }
108
109 t = y + 1.0;
110 a = (x2 + (t * t)) / a;
111 return Complex(f64).new(w, 0.25 * math.ln(a));
112}
113
114const epsilon = 0.0001;
115
116test "complex.catan32" {
117 const a = Complex(f32).new(5, 3);
118 const c = atan(a);
119
120 debug.assert(math.approxEq(f32, c.re, 1.423679, epsilon));
121 debug.assert(math.approxEq(f32, c.im, 0.086569, epsilon));
122}
123
124test "complex.catan64" {
125 const a = Complex(f64).new(5, 3);
126 const c = atan(a);
127
128 debug.assert(math.approxEq(f64, c.re, 1.423679, epsilon));
129 debug.assert(math.approxEq(f64, c.im, 0.086569, epsilon));
130}
std/math/complex/atanh.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn atanh(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const q = Complex(T).new(-z.im, z.re);
10 const r = cmath.atan(q);
11 return Complex(T).new(r.im, -r.re);
12}
13
14const epsilon = 0.0001;
15
16test "complex.catanh" {
17 const a = Complex(f32).new(5, 3);
18 const c = atanh(a);
19
20 debug.assert(math.approxEq(f32, c.re, 0.146947, epsilon));
21 debug.assert(math.approxEq(f32, c.im, 1.480870, epsilon));
22}
std/math/complex/conj.zig created+17
...@@ -0,0 +1,17 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn conj(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 return Complex(T).new(z.re, -z.im);
10}
11
12test "complex.conj" {
13 const a = Complex(f32).new(5, 3);
14 const c = a.conjugate();
15
16 debug.assert(c.re == 5 and c.im == -3);
17}
std/math/complex/cos.zig created+21
...@@ -0,0 +1,21 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn cos(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const p = Complex(T).new(-z.im, z.re);
10 return cmath.cosh(p);
11}
12
13const epsilon = 0.0001;
14
15test "complex.ccos" {
16 const a = Complex(f32).new(5, 3);
17 const c = cos(a);
18
19 debug.assert(math.approxEq(f32, c.re, 2.855815, epsilon));
20 debug.assert(math.approxEq(f32, c.im, 9.606383, epsilon));
21}
std/math/complex/cosh.zig created+165
...@@ -0,0 +1,165 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
8
9pub fn cosh(z: var) Complex(@typeOf(z.re)) {
10 const T = @typeOf(z.re);
11 return switch (T) {
12 f32 => cosh32(z),
13 f64 => cosh64(z),
14 else => @compileError("cosh not implemented for " ++ @typeName(z)),
15 };
16}
17
18fn cosh32(z: &const Complex(f32)) Complex(f32) {
19 const x = z.re;
20 const y = z.im;
21
22 const hx = @bitCast(u32, x);
23 const ix = hx & 0x7fffffff;
24
25 const hy = @bitCast(u32, y);
26 const iy = hy & 0x7fffffff;
27
28 if (ix < 0x7f800000 and iy < 0x7f800000) {
29 if (iy == 0) {
30 return Complex(f32).new(math.cosh(x), y);
31 }
32 // small x: normal case
33 if (ix < 0x41100000) {
34 return Complex(f32).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y));
35 }
36
37 // |x|>= 9, so cosh(x) ~= exp(|x|)
38 if (ix < 0x42b17218) {
39 // x < 88.7: exp(|x|) won't overflow
40 const h = math.exp(math.fabs(x)) * 0.5;
41 return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));
42 }
43 // x < 192.7: scale to avoid overflow
44 else if (ix < 0x4340b1e7) {
45 const v = Complex(f32).new(math.fabs(x), y);
46 const r = ldexp_cexp(v, -1);
47 return Complex(f32).new(x, y * math.copysign(f32, 1, x));
48 }
49 // x >= 192.7: result always overflows
50 else {
51 const h = 0x1p127 * x;
52 return Complex(f32).new(h * h * math.cos(y), h * math.sin(y));
53 }
54 }
55
56 if (ix == 0 and iy >= 0x7f800000) {
57 return Complex(f32).new(y - y, math.copysign(f32, 0, x * (y - y)));
58 }
59
60 if (iy == 0 and ix >= 0x7f800000) {
61 if (hx & 0x7fffff == 0) {
62 return Complex(f32).new(x * x, math.copysign(f32, 0, x) * y);
63 }
64 return Complex(f32).new(x, math.copysign(f32, 0, (x + x) * y));
65 }
66
67 if (ix < 0x7f800000 and iy >= 0x7f800000) {
68 return Complex(f32).new(y - y, x * (y - y));
69 }
70
71 if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) {
72 if (iy >= 0x7f800000) {
73 return Complex(f32).new(x * x, x * (y - y));
74 }
75 return Complex(f32).new((x * x) * math.cos(y), x * math.sin(y));
76 }
77
78 return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y));
79}
80
81fn cosh64(z: &const Complex(f64)) Complex(f64) {
82 const x = z.re;
83 const y = z.im;
84
85 const fx = @bitCast(u64, x);
86 const hx = u32(fx >> 32);
87 const lx = @truncate(u32, fx);
88 const ix = hx & 0x7fffffff;
89
90 const fy = @bitCast(u64, y);
91 const hy = u32(fy >> 32);
92 const ly = @truncate(u32, fy);
93 const iy = hy & 0x7fffffff;
94
95 // nearly non-exceptional case where x, y are finite
96 if (ix < 0x7ff00000 and iy < 0x7ff00000) {
97 if (iy | ly == 0) {
98 return Complex(f64).new(math.cosh(x), x * y);
99 }
100 // small x: normal case
101 if (ix < 0x40360000) {
102 return Complex(f64).new(math.cosh(x) * math.cos(y), math.sinh(x) * math.sin(y));
103 }
104
105 // |x|>= 22, so cosh(x) ~= exp(|x|)
106 if (ix < 0x40862e42) {
107 // x < 710: exp(|x|) won't overflow
108 const h = math.exp(math.fabs(x)) * 0.5;
109 return Complex(f64).new(h * math.cos(y), math.copysign(f64, h, x) * math.sin(y));
110 }
111 // x < 1455: scale to avoid overflow
112 else if (ix < 0x4096bbaa) {
113 const v = Complex(f64).new(math.fabs(x), y);
114 const r = ldexp_cexp(v, -1);
115 return Complex(f64).new(x, y * math.copysign(f64, 1, x));
116 }
117 // x >= 1455: result always overflows
118 else {
119 const h = 0x1p1023;
120 return Complex(f64).new(h * h * math.cos(y), h * math.sin(y));
121 }
122 }
123
124 if (ix | lx == 0 and iy >= 0x7ff00000) {
125 return Complex(f64).new(y - y, math.copysign(f64, 0, x * (y - y)));
126 }
127
128 if (iy | ly == 0 and ix >= 0x7ff00000) {
129 if ((hx & 0xfffff) | lx == 0) {
130 return Complex(f64).new(x * x, math.copysign(f64, 0, x) * y);
131 }
132 return Complex(f64).new(x * x, math.copysign(f64, 0, (x + x) * y));
133 }
134
135 if (ix < 0x7ff00000 and iy >= 0x7ff00000) {
136 return Complex(f64).new(y - y, x * (y - y));
137 }
138
139 if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) {
140 if (iy >= 0x7ff00000) {
141 return Complex(f64).new(x * x, x * (y - y));
142 }
143 return Complex(f64).new(x * x * math.cos(y), x * math.sin(y));
144 }
145
146 return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y));
147}
148
149const epsilon = 0.0001;
150
151test "complex.ccosh32" {
152 const a = Complex(f32).new(5, 3);
153 const c = cosh(a);
154
155 debug.assert(math.approxEq(f32, c.re, -73.467300, epsilon));
156 debug.assert(math.approxEq(f32, c.im, 10.471557, epsilon));
157}
158
159test "complex.ccosh64" {
160 const a = Complex(f64).new(5, 3);
161 const c = cosh(a);
162
163 debug.assert(math.approxEq(f64, c.re, -73.467300, epsilon));
164 debug.assert(math.approxEq(f64, c.im, 10.471557, epsilon));
165}
std/math/complex/exp.zig created+140
...@@ -0,0 +1,140 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
8
9pub fn exp(z: var) Complex(@typeOf(z.re)) {
10 const T = @typeOf(z.re);
11
12 return switch (T) {
13 f32 => exp32(z),
14 f64 => exp64(z),
15 else => @compileError("exp not implemented for " ++ @typeName(z)),
16 };
17}
18
19fn exp32(z: &const Complex(f32)) Complex(f32) {
20 @setFloatMode(this, @import("builtin").FloatMode.Strict);
21
22 const exp_overflow = 0x42b17218; // max_exp * ln2 ~= 88.72283955
23 const cexp_overflow = 0x43400074; // (max_exp - min_denom_exp) * ln2
24
25 const x = z.re;
26 const y = z.im;
27
28 const hy = @bitCast(u32, y) & 0x7fffffff;
29 // cexp(x + i0) = exp(x) + i0
30 if (hy == 0) {
31 return Complex(f32).new(math.exp(x), y);
32 }
33
34 const hx = @bitCast(u32, x);
35 // cexp(0 + iy) = cos(y) + isin(y)
36 if ((hx & 0x7fffffff) == 0) {
37 return Complex(f32).new(math.cos(y), math.sin(y));
38 }
39
40 if (hy >= 0x7f800000) {
41 // cexp(finite|nan +- i inf|nan) = nan + i nan
42 if ((hx & 0x7fffffff) != 0x7f800000) {
43 return Complex(f32).new(y - y, y - y);
44 }
45 // cexp(-inf +- i inf|nan) = 0 + i0
46 else if (hx & 0x80000000 != 0) {
47 return Complex(f32).new(0, 0);
48 }
49 // cexp(+inf +- i inf|nan) = inf + i nan
50 else {
51 return Complex(f32).new(x, y - y);
52 }
53 }
54
55 // 88.7 <= x <= 192 so must scale
56 if (hx >= exp_overflow and hx <= cexp_overflow) {
57 return ldexp_cexp(z, 0);
58 }
59 // - x < exp_overflow => exp(x) won't overflow (common)
60 // - x > cexp_overflow, so exp(x) * s overflows for s > 0
61 // - x = +-inf
62 // - x = nan
63 else {
64 const exp_x = math.exp(x);
65 return Complex(f32).new(exp_x * math.cos(y), exp_x * math.sin(y));
66 }
67}
68
69fn exp64(z: &const Complex(f64)) Complex(f64) {
70 const exp_overflow = 0x40862e42; // high bits of max_exp * ln2 ~= 710
71 const cexp_overflow = 0x4096b8e4; // (max_exp - min_denorm_exp) * ln2
72
73 const x = z.re;
74 const y = z.im;
75
76 const fy = @bitCast(u64, y);
77 const hy = u32(fy >> 32) & 0x7fffffff;
78 const ly = @truncate(u32, fy);
79
80 // cexp(x + i0) = exp(x) + i0
81 if (hy | ly == 0) {
82 return Complex(f64).new(math.exp(x), y);
83 }
84
85 const fx = @bitCast(u64, x);
86 const hx = u32(fx >> 32);
87 const lx = @truncate(u32, fx);
88
89 // cexp(0 + iy) = cos(y) + isin(y)
90 if ((hx & 0x7fffffff) | lx == 0) {
91 return Complex(f64).new(math.cos(y), math.sin(y));
92 }
93
94 if (hy >= 0x7ff00000) {
95 // cexp(finite|nan +- i inf|nan) = nan + i nan
96 if (lx != 0 or (hx & 0x7fffffff) != 0x7ff00000) {
97 return Complex(f64).new(y - y, y - y);
98 }
99 // cexp(-inf +- i inf|nan) = 0 + i0
100 else if (hx & 0x80000000 != 0) {
101 return Complex(f64).new(0, 0);
102 }
103 // cexp(+inf +- i inf|nan) = inf + i nan
104 else {
105 return Complex(f64).new(x, y - y);
106 }
107 }
108
109 // 709.7 <= x <= 1454.3 so must scale
110 if (hx >= exp_overflow and hx <= cexp_overflow) {
111 const r = ldexp_cexp(z, 0);
112 return *r;
113 }
114 // - x < exp_overflow => exp(x) won't overflow (common)
115 // - x > cexp_overflow, so exp(x) * s overflows for s > 0
116 // - x = +-inf
117 // - x = nan
118 else {
119 const exp_x = math.exp(x);
120 return Complex(f64).new(exp_x * math.cos(y), exp_x * math.sin(y));
121 }
122}
123
124const epsilon = 0.0001;
125
126test "complex.cexp32" {
127 const a = Complex(f32).new(5, 3);
128 const c = exp(a);
129
130 debug.assert(math.approxEq(f32, c.re, -146.927917, epsilon));
131 debug.assert(math.approxEq(f32, c.im, 20.944065, epsilon));
132}
133
134test "complex.cexp64" {
135 const a = Complex(f32).new(5, 3);
136 const c = exp(a);
137
138 debug.assert(math.approxEq(f64, c.re, -146.927917, epsilon));
139 debug.assert(math.approxEq(f64, c.im, 20.944065, epsilon));
140}
std/math/complex/index.zig created+171
...@@ -0,0 +1,171 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4
5pub const abs = @import("abs.zig").abs;
6pub const acosh = @import("acosh.zig").acosh;
7pub const acos = @import("acos.zig").acos;
8pub const arg = @import("arg.zig").arg;
9pub const asinh = @import("asinh.zig").asinh;
10pub const asin = @import("asin.zig").asin;
11pub const atanh = @import("atanh.zig").atanh;
12pub const atan = @import("atan.zig").atan;
13pub const conj = @import("conj.zig").conj;
14pub const cosh = @import("cosh.zig").cosh;
15pub const cos = @import("cos.zig").cos;
16pub const exp = @import("exp.zig").exp;
17pub const log = @import("log.zig").log;
18pub const pow = @import("pow.zig").pow;
19pub const proj = @import("proj.zig").proj;
20pub const sinh = @import("sinh.zig").sinh;
21pub const sin = @import("sin.zig").sin;
22pub const sqrt = @import("sqrt.zig").sqrt;
23pub const tanh = @import("tanh.zig").tanh;
24pub const tan = @import("tan.zig").tan;
25
26pub fn Complex(comptime T: type) type {
27 return struct {
28 const Self = this;
29
30 re: T,
31 im: T,
32
33 pub fn new(re: T, im: T) Self {
34 return Self {
35 .re = re,
36 .im = im,
37 };
38 }
39
40 pub fn add(self: &const Self, other: &const Self) Self {
41 return Self {
42 .re = self.re + other.re,
43 .im = self.im + other.im,
44 };
45 }
46
47 pub fn sub(self: &const Self, other: &const Self) Self {
48 return Self {
49 .re = self.re - other.re,
50 .im = self.im - other.im,
51 };
52 }
53
54 pub fn mul(self: &const Self, other: &const Self) Self {
55 return Self {
56 .re = self.re * other.re - self.im * other.im,
57 .im = self.im * other.re + self.re * other.im,
58 };
59 }
60
61 pub fn div(self: &const Self, other: &const Self) Self {
62 const re_num = self.re * other.re + self.im * other.im;
63 const im_num = self.im * other.re - self.re * other.im;
64 const den = other.re * other.re + other.im * other.im;
65
66 return Self {
67 .re = re_num / den,
68 .im = im_num / den,
69 };
70 }
71
72 pub fn conjugate(self: &const Self) Self {
73 return Self {
74 .re = self.re,
75 .im = -self.im,
76 };
77 }
78
79 pub fn reciprocal(self: &const Self) Self {
80 const m = self.re * self.re + self.im * self.im;
81 return Self {
82 .re = self.re / m,
83 .im = -self.im / m,
84 };
85 }
86
87 pub fn magnitude(self: &const Self) T {
88 return math.sqrt(self.re * self.re + self.im * self.im);
89 }
90 };
91}
92
93const epsilon = 0.0001;
94
95test "complex.add" {
96 const a = Complex(f32).new(5, 3);
97 const b = Complex(f32).new(2, 7);
98 const c = a.add(b);
99
100 debug.assert(c.re == 7 and c.im == 10);
101}
102
103test "complex.sub" {
104 const a = Complex(f32).new(5, 3);
105 const b = Complex(f32).new(2, 7);
106 const c = a.sub(b);
107
108 debug.assert(c.re == 3 and c.im == -4);
109}
110
111test "complex.mul" {
112 const a = Complex(f32).new(5, 3);
113 const b = Complex(f32).new(2, 7);
114 const c = a.mul(b);
115
116 debug.assert(c.re == -11 and c.im == 41);
117}
118
119test "complex.div" {
120 const a = Complex(f32).new(5, 3);
121 const b = Complex(f32).new(2, 7);
122 const c = a.div(b);
123
124 debug.assert(math.approxEq(f32, c.re, f32(31)/53, epsilon) and
125 math.approxEq(f32, c.im, f32(-29)/53, epsilon));
126}
127
128test "complex.conjugate" {
129 const a = Complex(f32).new(5, 3);
130 const c = a.conjugate();
131
132 debug.assert(c.re == 5 and c.im == -3);
133}
134
135test "complex.reciprocal" {
136 const a = Complex(f32).new(5, 3);
137 const c = a.reciprocal();
138
139 debug.assert(math.approxEq(f32, c.re, f32(5)/34, epsilon) and
140 math.approxEq(f32, c.im, f32(-3)/34, epsilon));
141}
142
143test "complex.magnitude" {
144 const a = Complex(f32).new(5, 3);
145 const c = a.magnitude();
146
147 debug.assert(math.approxEq(f32, c, 5.83095, epsilon));
148}
149
150test "complex.cmath" {
151 _ = @import("abs.zig");
152 _ = @import("acosh.zig");
153 _ = @import("acos.zig");
154 _ = @import("arg.zig");
155 _ = @import("asinh.zig");
156 _ = @import("asin.zig");
157 _ = @import("atanh.zig");
158 _ = @import("atan.zig");
159 _ = @import("conj.zig");
160 _ = @import("cosh.zig");
161 _ = @import("cos.zig");
162 _ = @import("exp.zig");
163 _ = @import("log.zig");
164 _ = @import("pow.zig");
165 _ = @import("proj.zig");
166 _ = @import("sinh.zig");
167 _ = @import("sin.zig");
168 _ = @import("sqrt.zig");
169 _ = @import("tanh.zig");
170 _ = @import("tan.zig");
171}
std/math/complex/ldexp.zig created+75
...@@ -0,0 +1,75 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn ldexp_cexp(z: var, expt: i32) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9
10 return switch (T) {
11 f32 => ldexp_cexp32(z, expt),
12 f64 => ldexp_cexp64(z, expt),
13 else => unreachable,
14 };
15}
16
17fn frexp_exp32(x: f32, expt: &i32) f32 {
18 const k = 235; // reduction constant
19 const kln2 = 162.88958740; // k * ln2
20
21 const exp_x = math.exp(x - kln2);
22 const hx = @bitCast(u32, exp_x);
23 *expt = i32(hx >> 23) - (0x7f + 127) + k;
24 return @bitCast(f32, (hx & 0x7fffff) | ((0x7f + 127) << 23));
25}
26
27fn ldexp_cexp32(z: &const Complex(f32), expt: i32) Complex(f32) {
28 var ex_expt: i32 = undefined;
29 const exp_x = frexp_exp32(z.re, &ex_expt);
30 const exptf = expt + ex_expt;
31
32 const half_expt1 = @divTrunc(exptf, 2);
33 const scale1 = @bitCast(f32, (0x7f + half_expt1) << 23);
34
35 const half_expt2 = exptf - half_expt1;
36 const scale2 = @bitCast(f32, (0x7f + half_expt2) << 23);
37
38 return Complex(f32).new(
39 math.cos(z.im) * exp_x * scale1 * scale2,
40 math.sin(z.im) * exp_x * scale1 * scale2,
41 );
42}
43
44fn frexp_exp64(x: f64, expt: &i32) f64 {
45 const k = 1799; // reduction constant
46 const kln2 = 1246.97177782734161156; // k * ln2
47
48 const exp_x = math.exp(x - kln2);
49
50 const fx = @bitCast(u64, x);
51 const hx = u32(fx >> 32);
52 const lx = @truncate(u32, fx);
53
54 *expt = i32(hx >> 20) - (0x3ff + 1023) + k;
55
56 const high_word = (hx & 0xfffff) | ((0x3ff + 1023) << 20);
57 return @bitCast(f64, (u64(high_word) << 32) | lx);
58}
59
60fn ldexp_cexp64(z: &const Complex(f64), expt: i32) Complex(f64) {
61 var ex_expt: i32 = undefined;
62 const exp_x = frexp_exp64(z.re, &ex_expt);
63 const exptf = i64(expt + ex_expt);
64
65 const half_expt1 = @divTrunc(exptf, 2);
66 const scale1 = @bitCast(f64, (0x3ff + half_expt1) << 20);
67
68 const half_expt2 = exptf - half_expt1;
69 const scale2 = @bitCast(f64, (0x3ff + half_expt2) << 20);
70
71 return Complex(f64).new(
72 math.cos(z.im) * exp_x * scale1 * scale2,
73 math.sin(z.im) * exp_x * scale1 * scale2,
74 );
75}
std/math/complex/log.zig created+23
...@@ -0,0 +1,23 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn log(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const r = cmath.abs(z);
10 const phi = cmath.arg(z);
11
12 return Complex(T).new(math.ln(r), phi);
13}
14
15const epsilon = 0.0001;
16
17test "complex.clog" {
18 const a = Complex(f32).new(5, 3);
19 const c = log(a);
20
21 debug.assert(math.approxEq(f32, c.re, 1.763180, epsilon));
22 debug.assert(math.approxEq(f32, c.im, 0.540419, epsilon));
23}
std/math/complex/pow.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn pow(comptime T: type, z: &const T, c: &const T) T {
8 const p = cmath.log(z);
9 const q = c.mul(p);
10 return cmath.exp(q);
11}
12
13const epsilon = 0.0001;
14
15test "complex.cpow" {
16 const a = Complex(f32).new(5, 3);
17 const b = Complex(f32).new(2.3, -1.3);
18 const c = pow(Complex(f32), a, b);
19
20 debug.assert(math.approxEq(f32, c.re, 58.049110, epsilon));
21 debug.assert(math.approxEq(f32, c.im, -101.003433, epsilon));
22}
std/math/complex/proj.zig created+24
...@@ -0,0 +1,24 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn proj(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9
10 if (math.isInf(z.re) or math.isInf(z.im)) {
11 return Complex(T).new(math.inf(T), math.copysign(T, 0, z.re));
12 }
13
14 return Complex(T).new(z.re, z.im);
15}
16
17const epsilon = 0.0001;
18
19test "complex.cproj" {
20 const a = Complex(f32).new(5, 3);
21 const c = proj(a);
22
23 debug.assert(c.re == 5 and c.im == 3);
24}
std/math/complex/sin.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn sin(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const p = Complex(T).new(-z.im, z.re);
10 const q = cmath.sinh(p);
11 return Complex(T).new(q.im, -q.re);
12}
13
14const epsilon = 0.0001;
15
16test "complex.csin" {
17 const a = Complex(f32).new(5, 3);
18 const c = sin(a);
19
20 debug.assert(math.approxEq(f32, c.re, -9.654126, epsilon));
21 debug.assert(math.approxEq(f32, c.im, 2.841692, epsilon));
22}
std/math/complex/sinh.zig created+164
...@@ -0,0 +1,164 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
8
9pub fn sinh(z: var) Complex(@typeOf(z.re)) {
10 const T = @typeOf(z.re);
11 return switch (T) {
12 f32 => sinh32(z),
13 f64 => sinh64(z),
14 else => @compileError("tan not implemented for " ++ @typeName(z)),
15 };
16}
17
18fn sinh32(z: &const Complex(f32)) Complex(f32) {
19 const x = z.re;
20 const y = z.im;
21
22 const hx = @bitCast(u32, x);
23 const ix = hx & 0x7fffffff;
24
25 const hy = @bitCast(u32, y);
26 const iy = hy & 0x7fffffff;
27
28 if (ix < 0x7f800000 and iy < 0x7f800000) {
29 if (iy == 0) {
30 return Complex(f32).new(math.sinh(x), y);
31 }
32 // small x: normal case
33 if (ix < 0x41100000) {
34 return Complex(f32).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y));
35 }
36
37 // |x|>= 9, so cosh(x) ~= exp(|x|)
38 if (ix < 0x42b17218) {
39 // x < 88.7: exp(|x|) won't overflow
40 const h = math.exp(math.fabs(x)) * 0.5;
41 return Complex(f32).new(math.copysign(f32, h, x) * math.cos(y), h * math.sin(y));
42 }
43 // x < 192.7: scale to avoid overflow
44 else if (ix < 0x4340b1e7) {
45 const v = Complex(f32).new(math.fabs(x), y);
46 const r = ldexp_cexp(v, -1);
47 return Complex(f32).new(x * math.copysign(f32, 1, x), y);
48 }
49 // x >= 192.7: result always overflows
50 else {
51 const h = 0x1p127 * x;
52 return Complex(f32).new(h * math.cos(y), h * h * math.sin(y));
53 }
54 }
55
56 if (ix == 0 and iy >= 0x7f800000) {
57 return Complex(f32).new(math.copysign(f32, 0, x * (y - y)), y - y);
58 }
59
60 if (iy == 0 and ix >= 0x7f800000) {
61 if (hx & 0x7fffff == 0) {
62 return Complex(f32).new(x, y);
63 }
64 return Complex(f32).new(x, math.copysign(f32, 0, y));
65 }
66
67 if (ix < 0x7f800000 and iy >= 0x7f800000) {
68 return Complex(f32).new(y - y, x * (y - y));
69 }
70
71 if (ix >= 0x7f800000 and (hx & 0x7fffff) == 0) {
72 if (iy >= 0x7f800000) {
73 return Complex(f32).new(x * x, x * (y - y));
74 }
75 return Complex(f32).new(x * math.cos(y), math.inf_f32 * math.sin(y));
76 }
77
78 return Complex(f32).new((x * x) * (y - y), (x + x) * (y - y));
79}
80
81fn sinh64(z: &const Complex(f64)) Complex(f64) {
82 const x = z.re;
83 const y = z.im;
84
85 const fx = @bitCast(u64, x);
86 const hx = u32(fx >> 32);
87 const lx = @truncate(u32, fx);
88 const ix = hx & 0x7fffffff;
89
90 const fy = @bitCast(u64, y);
91 const hy = u32(fy >> 32);
92 const ly = @truncate(u32, fy);
93 const iy = hy & 0x7fffffff;
94
95 if (ix < 0x7ff00000 and iy < 0x7ff00000) {
96 if (iy | ly == 0) {
97 return Complex(f64).new(math.sinh(x), y);
98 }
99 // small x: normal case
100 if (ix < 0x40360000) {
101 return Complex(f64).new(math.sinh(x) * math.cos(y), math.cosh(x) * math.sin(y));
102 }
103
104 // |x|>= 22, so cosh(x) ~= exp(|x|)
105 if (ix < 0x40862e42) {
106 // x < 710: exp(|x|) won't overflow
107 const h = math.exp(math.fabs(x)) * 0.5;
108 return Complex(f64).new(math.copysign(f64, h, x) * math.cos(y), h * math.sin(y));
109 }
110 // x < 1455: scale to avoid overflow
111 else if (ix < 0x4096bbaa) {
112 const v = Complex(f64).new(math.fabs(x), y);
113 const r = ldexp_cexp(v, -1);
114 return Complex(f64).new(x * math.copysign(f64, 1, x), y);
115 }
116 // x >= 1455: result always overflows
117 else {
118 const h = 0x1p1023 * x;
119 return Complex(f64).new(h * math.cos(y), h * h * math.sin(y));
120 }
121 }
122
123 if (ix | lx == 0 and iy >= 0x7ff00000) {
124 return Complex(f64).new(math.copysign(f64, 0, x * (y - y)), y - y);
125 }
126
127 if (iy | ly == 0 and ix >= 0x7ff00000) {
128 if ((hx & 0xfffff) | lx == 0) {
129 return Complex(f64).new(x, y);
130 }
131 return Complex(f64).new(x, math.copysign(f64, 0, y));
132 }
133
134 if (ix < 0x7ff00000 and iy >= 0x7ff00000) {
135 return Complex(f64).new(y - y, x * (y - y));
136 }
137
138 if (ix >= 0x7ff00000 and (hx & 0xfffff) | lx == 0) {
139 if (iy >= 0x7ff00000) {
140 return Complex(f64).new(x * x, x * (y - y));
141 }
142 return Complex(f64).new(x * math.cos(y), math.inf_f64 * math.sin(y));
143 }
144
145 return Complex(f64).new((x * x) * (y - y), (x + x) * (y - y));
146}
147
148const epsilon = 0.0001;
149
150test "complex.csinh32" {
151 const a = Complex(f32).new(5, 3);
152 const c = sinh(a);
153
154 debug.assert(math.approxEq(f32, c.re, -73.460617, epsilon));
155 debug.assert(math.approxEq(f32, c.im, 10.472508, epsilon));
156}
157
158test "complex.csinh64" {
159 const a = Complex(f64).new(5, 3);
160 const c = sinh(a);
161
162 debug.assert(math.approxEq(f64, c.re, -73.460617, epsilon));
163 debug.assert(math.approxEq(f64, c.im, 10.472508, epsilon));
164}
std/math/complex/sqrt.zig created+133
...@@ -0,0 +1,133 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7// TODO when #733 is solved this can be @typeOf(z) instead of Complex(@typeOf(z.re))
8pub fn sqrt(z: var) Complex(@typeOf(z.re)) {
9 const T = @typeOf(z.re);
10
11 return switch (T) {
12 f32 => sqrt32(z),
13 f64 => sqrt64(z),
14 else => @compileError("sqrt not implemented for " ++ @typeName(z)),
15 };
16}
17
18fn sqrt32(z: &const Complex(f32)) Complex(f32) {
19 const x = z.re;
20 const y = z.im;
21
22 if (x == 0 and y == 0) {
23 return Complex(f32).new(0, y);
24 }
25 if (math.isInf(y)) {
26 return Complex(f32).new(math.inf(f32), y);
27 }
28 if (math.isNan(x)) {
29 // raise invalid if y is not nan
30 const t = (y - y) / (y - y);
31 return Complex(f32).new(x, t);
32 }
33 if (math.isInf(x)) {
34 // sqrt(inf + i nan) = inf + nan i
35 // sqrt(inf + iy) = inf + i0
36 // sqrt(-inf + i nan) = nan +- inf i
37 // sqrt(-inf + iy) = 0 + inf i
38 if (math.signbit(x)) {
39 return Complex(f32).new(math.fabs(x - y), math.copysign(f32, x, y));
40 } else {
41 return Complex(f32).new(x, math.copysign(f32, y - y, y));
42 }
43 }
44
45 // y = nan special case is handled fine below
46
47 // double-precision avoids overflow with correct rounding.
48 const dx = f64(x);
49 const dy = f64(y);
50
51 if (dx >= 0) {
52 const t = math.sqrt((dx + math.hypot(f64, dx, dy)) * 0.5);
53 return Complex(f32).new(f32(t), f32(dy / (2.0 * t)));
54 } else {
55 const t = math.sqrt((-dx + math.hypot(f64, dx, dy)) * 0.5);
56 return Complex(f32).new(f32(math.fabs(y) / (2.0 * t)), f32(math.copysign(f64, t, y)));
57 }
58}
59
60fn sqrt64(z: &const Complex(f64)) Complex(f64) {
61 // may encounter overflow for im,re >= DBL_MAX / (1 + sqrt(2))
62 const threshold = 0x1.a827999fcef32p+1022;
63
64 var x = z.re;
65 var y = z.im;
66
67 if (x == 0 and y == 0) {
68 return Complex(f64).new(0, y);
69 }
70 if (math.isInf(y)) {
71 return Complex(f64).new(math.inf(f64), y);
72 }
73 if (math.isNan(x)) {
74 // raise invalid if y is not nan
75 const t = (y - y) / (y - y);
76 return Complex(f64).new(x, t);
77 }
78 if (math.isInf(x)) {
79 // sqrt(inf + i nan) = inf + nan i
80 // sqrt(inf + iy) = inf + i0
81 // sqrt(-inf + i nan) = nan +- inf i
82 // sqrt(-inf + iy) = 0 + inf i
83 if (math.signbit(x)) {
84 return Complex(f64).new(math.fabs(x - y), math.copysign(f64, x, y));
85 } else {
86 return Complex(f64).new(x, math.copysign(f64, y - y, y));
87 }
88 }
89
90 // y = nan special case is handled fine below
91
92 // scale to avoid overflow
93 var scale = false;
94 if (math.fabs(x) >= threshold or math.fabs(y) >= threshold) {
95 x *= 0.25;
96 y *= 0.25;
97 scale = true;
98 }
99
100 var result: Complex(f64) = undefined;
101 if (x >= 0) {
102 const t = math.sqrt((x + math.hypot(f64, x, y)) * 0.5);
103 result = Complex(f64).new(t, y / (2.0 * t));
104 } else {
105 const t = math.sqrt((-x + math.hypot(f64, x, y)) * 0.5);
106 result = Complex(f64).new(math.fabs(y) / (2.0 * t), math.copysign(f64, t, y));
107 }
108
109 if (scale) {
110 result.re *= 2;
111 result.im *= 2;
112 }
113
114 return result;
115}
116
117const epsilon = 0.0001;
118
119test "complex.csqrt32" {
120 const a = Complex(f32).new(5, 3);
121 const c = sqrt(a);
122
123 debug.assert(math.approxEq(f32, c.re, 2.327117, epsilon));
124 debug.assert(math.approxEq(f32, c.im, 0.644574, epsilon));
125}
126
127test "complex.csqrt64" {
128 const a = Complex(f64).new(5, 3);
129 const c = sqrt(a);
130
131 debug.assert(math.approxEq(f64, c.re, 2.3271175190399496, epsilon));
132 debug.assert(math.approxEq(f64, c.im, 0.6445742373246469, epsilon));
133}
std/math/complex/tan.zig created+22
...@@ -0,0 +1,22 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn tan(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 const q = Complex(T).new(-z.im, z.re);
10 const r = cmath.tanh(q);
11 return Complex(T).new(r.im, -r.re);
12}
13
14const epsilon = 0.0001;
15
16test "complex.ctan" {
17 const a = Complex(f32).new(5, 3);
18 const c = tan(a);
19
20 debug.assert(math.approxEq(f32, c.re, -0.002708233, epsilon));
21 debug.assert(math.approxEq(f32, c.im, 1.004165, epsilon));
22}
std/math/complex/tanh.zig created+111
...@@ -0,0 +1,111 @@
1const std = @import("../../index.zig");
2const debug = std.debug;
3const math = std.math;
4const cmath = math.complex;
5const Complex = cmath.Complex;
6
7pub fn tanh(z: var) Complex(@typeOf(z.re)) {
8 const T = @typeOf(z.re);
9 return switch (T) {
10 f32 => tanh32(z),
11 f64 => tanh64(z),
12 else => @compileError("tan not implemented for " ++ @typeName(z)),
13 };
14}
15
16fn tanh32(z: &const Complex(f32)) Complex(f32) {
17 const x = z.re;
18 const y = z.im;
19
20 const hx = @bitCast(u32, x);
21 const ix = hx & 0x7fffffff;
22
23 if (ix >= 0x7f800000) {
24 if (ix & 0x7fffff != 0) {
25 const r = if (y == 0) y else x * y;
26 return Complex(f32).new(x, r);
27 }
28 const xx = @bitCast(f32, hx - 0x40000000);
29 const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y);
30 return Complex(f32).new(xx, math.copysign(f32, 0, r));
31 }
32
33 if (!math.isFinite(y)) {
34 const r = if (ix != 0) y - y else x;
35 return Complex(f32).new(r, y - y);
36 }
37
38 // x >= 11
39 if (ix >= 0x41300000) {
40 const exp_mx = math.exp(-math.fabs(x));
41 return Complex(f32).new(math.copysign(f32, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx);
42 }
43
44 // Kahan's algorithm
45 const t = math.tan(y);
46 const beta = 1.0 + t * t;
47 const s = math.sinh(x);
48 const rho = math.sqrt(1 + s * s);
49 const den = 1 + beta * s * s;
50
51 return Complex(f32).new((beta * rho * s) / den, t / den);
52}
53
54fn tanh64(z: &const Complex(f64)) Complex(f64) {
55 const x = z.re;
56 const y = z.im;
57
58 const fx = @bitCast(u64, x);
59 const hx = u32(fx >> 32);
60 const lx = @truncate(u32, fx);
61 const ix = hx & 0x7fffffff;
62
63 if (ix >= 0x7ff00000) {
64 if ((ix & 0x7fffff) | lx != 0) {
65 const r = if (y == 0) y else x * y;
66 return Complex(f64).new(x, r);
67 }
68
69 const xx = @bitCast(f64, (u64(hx - 0x40000000) << 32) | lx);
70 const r = if (math.isInf(y)) y else math.sin(y) * math.cos(y);
71 return Complex(f64).new(xx, math.copysign(f64, 0, r));
72 }
73
74 if (!math.isFinite(y)) {
75 const r = if (ix != 0) y - y else x;
76 return Complex(f64).new(r, y - y);
77 }
78
79 // x >= 22
80 if (ix >= 0x40360000) {
81 const exp_mx = math.exp(-math.fabs(x));
82 return Complex(f64).new(math.copysign(f64, 1, x), 4 * math.sin(y) * math.cos(y) * exp_mx * exp_mx);
83 }
84
85 // Kahan's algorithm
86 const t = math.tan(y);
87 const beta = 1.0 + t * t;
88 const s = math.sinh(x);
89 const rho = math.sqrt(1 + s * s);
90 const den = 1 + beta * s * s;
91
92 return Complex(f64).new((beta * rho * s) / den, t / den);
93}
94
95const epsilon = 0.0001;
96
97test "complex.ctanh32" {
98 const a = Complex(f32).new(5, 3);
99 const c = tanh(a);
100
101 debug.assert(math.approxEq(f32, c.re, 0.999913, epsilon));
102 debug.assert(math.approxEq(f32, c.im, -0.000025, epsilon));
103}
104
105test "complex.ctanh64" {
106 const a = Complex(f64).new(5, 3);
107 const c = tanh(a);
108
109 debug.assert(math.approxEq(f64, c.re, 0.999913, epsilon));
110 debug.assert(math.approxEq(f64, c.im, -0.000025, epsilon));
111}
std/math/exp.zig+5
...@@ -6,6 +6,7 @@...@@ -6,6 +6,7 @@
6const std = @import("../index.zig");6const std = @import("../index.zig");
7const math = std.math;7const math = std.math;
8const assert = std.debug.assert;8const assert = std.debug.assert;
9const builtin = @import("builtin");
910
10pub fn exp(x: var) @typeOf(x) {11pub fn exp(x: var) @typeOf(x) {
11 const T = @typeOf(x);12 const T = @typeOf(x);
...@@ -17,6 +18,8 @@ pub fn exp(x: var) @typeOf(x) {...@@ -17,6 +18,8 @@ pub fn exp(x: var) @typeOf(x) {
17}18}
1819
19fn exp32(x_: f32) f32 {20fn exp32(x_: f32) f32 {
21 @setFloatMode(this, builtin.FloatMode.Strict);
22
20 const half = []f32 { 0.5, -0.5 };23 const half = []f32 { 0.5, -0.5 };
21 const ln2hi = 6.9314575195e-1;24 const ln2hi = 6.9314575195e-1;
22 const ln2lo = 1.4286067653e-6;25 const ln2lo = 1.4286067653e-6;
...@@ -94,6 +97,8 @@ fn exp32(x_: f32) f32 {...@@ -94,6 +97,8 @@ fn exp32(x_: f32) f32 {
94}97}
9598
96fn exp64(x_: f64) f64 {99fn exp64(x_: f64) f64 {
100 @setFloatMode(this, builtin.FloatMode.Strict);
101
97 const half = []const f64 { 0.5, -0.5 };102 const half = []const f64 { 0.5, -0.5 };
98 const ln2hi: f64 = 6.93147180369123816490e-01;103 const ln2hi: f64 = 6.93147180369123816490e-01;
99 const ln2lo: f64 = 1.90821492927058770002e-10;104 const ln2lo: f64 = 1.90821492927058770002e-10;
std/math/index.zig+5
...@@ -129,6 +129,9 @@ pub const cos = @import("cos.zig").cos;...@@ -129,6 +129,9 @@ pub const cos = @import("cos.zig").cos;
129pub const sin = @import("sin.zig").sin;129pub const sin = @import("sin.zig").sin;
130pub const tan = @import("tan.zig").tan;130pub const tan = @import("tan.zig").tan;
131131
132pub const complex = @import("complex/index.zig");
133pub const Complex = complex.Complex;
134
132test "math" {135test "math" {
133 _ = @import("nan.zig");136 _ = @import("nan.zig");
134 _ = @import("isnan.zig");137 _ = @import("isnan.zig");
...@@ -172,6 +175,8 @@ test "math" {...@@ -172,6 +175,8 @@ test "math" {
172 _ = @import("sin.zig");175 _ = @import("sin.zig");
173 _ = @import("cos.zig");176 _ = @import("cos.zig");
174 _ = @import("tan.zig");177 _ = @import("tan.zig");
178
179 _ = @import("complex/index.zig");
175}180}
176181
177182
std/math/ln.zig+2
...@@ -89,6 +89,8 @@ pub fn ln_32(x_: f32) f32 {...@@ -89,6 +89,8 @@ pub fn ln_32(x_: f32) f32 {
89}89}
9090
91pub fn ln_64(x_: f64) f64 {91pub fn ln_64(x_: f64) f64 {
92 @setFloatMode(this, @import("builtin").FloatMode.Strict);
93
92 const ln2_hi: f64 = 6.93147180369123816490e-01;94 const ln2_hi: f64 = 6.93147180369123816490e-01;
93 const ln2_lo: f64 = 1.90821492927058770002e-10;95 const ln2_lo: f64 = 1.90821492927058770002e-10;
94 const Lg1: f64 = 6.666666666666735130e-01;96 const Lg1: f64 = 6.666666666666735130e-01;
std/math/sqrt.zig+33-262
...@@ -14,26 +14,8 @@ const TypeId = builtin.TypeId;...@@ -14,26 +14,8 @@ const TypeId = builtin.TypeId;
14pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) {14pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) {
15 const T = @typeOf(x);15 const T = @typeOf(x);
16 switch (@typeId(T)) {16 switch (@typeId(T)) {
17 TypeId.FloatLiteral => {17 TypeId.FloatLiteral => return T(@sqrt(f64, x)), // TODO upgrade to f128
18 return T(sqrt64(x));18 TypeId.Float => return @sqrt(T, x),
19 },
20 TypeId.Float => {
21 switch (T) {
22 f32 => {
23 switch (builtin.arch) {
24 builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt32(x),
25 else => return sqrt32(x),
26 }
27 },
28 f64 => {
29 switch (builtin.arch) {
30 builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt64(x),
31 else => return sqrt64(x),
32 }
33 },
34 else => @compileError("sqrt not implemented for " ++ @typeName(T)),
35 }
36 },
37 TypeId.IntLiteral => comptime {19 TypeId.IntLiteral => comptime {
38 if (x > @maxValue(u128)) {20 if (x > @maxValue(u128)) {
39 @compileError("sqrt not implemented for comptime_int greater than 128 bits");21 @compileError("sqrt not implemented for comptime_int greater than 128 bits");
...@@ -43,269 +25,58 @@ pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typ...@@ -43,269 +25,58 @@ pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typ
43 }25 }
44 return T(sqrt_int(u128, x));26 return T(sqrt_int(u128, x));
45 },27 },
46 TypeId.Int => {28 TypeId.Int => return sqrt_int(T, x),
47 return sqrt_int(T, x);
48 },
49 else => @compileError("sqrt not implemented for " ++ @typeName(T)),29 else => @compileError("sqrt not implemented for " ++ @typeName(T)),
50 }30 }
51}31}
5232
53fn sqrt32(x: f32) f32 {
54 const tiny: f32 = 1.0e-30;
55 const sign: i32 = @bitCast(i32, u32(0x80000000));
56 var ix: i32 = @bitCast(i32, x);
57
58 if ((ix & 0x7F800000) == 0x7F800000) {
59 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
60 }
61
62 // zero
63 if (ix <= 0) {
64 if (ix & ~sign == 0) {
65 return x; // sqrt (+-0) = +-0
66 }
67 if (ix < 0) {
68 return math.snan(f32);
69 }
70 }
71
72 // normalize
73 var m = ix >> 23;
74 if (m == 0) {
75 // subnormal
76 var i: i32 = 0;
77 while (ix & 0x00800000 == 0) : (i += 1) {
78 ix <<= 1;
79 }
80 m -= i - 1;
81 }
82
83 m -= 127; // unbias exponent
84 ix = (ix & 0x007FFFFF) | 0x00800000;
85
86 if (m & 1 != 0) { // odd m, double x to even
87 ix += ix;
88 }
89
90 m >>= 1; // m = [m / 2]
91
92 // sqrt(x) bit by bit
93 ix += ix;
94 var q: i32 = 0; // q = sqrt(x)
95 var s: i32 = 0;
96 var r: i32 = 0x01000000; // r = moving bit right -> left
97
98 while (r != 0) {
99 const t = s + r;
100 if (t <= ix) {
101 s = t + r;
102 ix -= t;
103 q += r;
104 }
105 ix += ix;
106 r >>= 1;
107 }
108
109 // floating add to find rounding direction
110 if (ix != 0) {
111 var z = 1.0 - tiny; // inexact
112 if (z >= 1.0) {
113 z = 1.0 + tiny;
114 if (z > 1.0) {
115 q += 2;
116 } else {
117 if (q & 1 != 0) {
118 q += 1;
119 }
120 }
121 }
122 }
123
124 ix = (q >> 1) + 0x3f000000;
125 ix += m << 23;
126 return @bitCast(f32, ix);
127}
128
129// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
130// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
131// potentially some edge cases remaining that are not handled in the same way.
132fn sqrt64(x: f64) f64 {
133 const tiny: f64 = 1.0e-300;
134 const sign: u32 = 0x80000000;
135 const u = @bitCast(u64, x);
136
137 var ix0 = u32(u >> 32);
138 var ix1 = u32(u & 0xFFFFFFFF);
139
140 // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
141 if (ix0 & 0x7FF00000 == 0x7FF00000) {
142 return x * x + x;
143 }
144
145 // sqrt(+-0) = +-0
146 if (x == 0.0) {
147 return x;
148 }
149 // sqrt(-ve) = snan
150 if (ix0 & sign != 0) {
151 return math.snan(f64);
152 }
153
154 // normalize x
155 var m = i32(ix0 >> 20);
156 if (m == 0) {
157 // subnormal
158 while (ix0 == 0) {
159 m -= 21;
160 ix0 |= ix1 >> 11;
161 ix1 <<= 21;
162 }
163
164 // subnormal
165 var i: u32 = 0;
166 while (ix0 & 0x00100000 == 0) : (i += 1) {
167 ix0 <<= 1;
168 }
169 m -= i32(i) - 1;
170 ix0 |= ix1 >> u5(32 - i);
171 ix1 <<= u5(i);
172 }
173
174 // unbias exponent
175 m -= 1023;
176 ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
177 if (m & 1 != 0) {
178 ix0 += ix0 + (ix1 >> 31);
179 ix1 = ix1 +% ix1;
180 }
181 m >>= 1;
182
183 // sqrt(x) bit by bit
184 ix0 += ix0 + (ix1 >> 31);
185 ix1 = ix1 +% ix1;
186
187 var q: u32 = 0;
188 var q1: u32 = 0;
189 var s0: u32 = 0;
190 var s1: u32 = 0;
191 var r: u32 = 0x00200000;
192 var t: u32 = undefined;
193 var t1: u32 = undefined;
194
195 while (r != 0) {
196 t = s0 +% r;
197 if (t <= ix0) {
198 s0 = t + r;
199 ix0 -= t;
200 q += r;
201 }
202 ix0 = ix0 +% ix0 +% (ix1 >> 31);
203 ix1 = ix1 +% ix1;
204 r >>= 1;
205 }
206
207 r = sign;
208 while (r != 0) {
209 t = s1 +% r;
210 t = s0;
211 if (t < ix0 or (t == ix0 and t1 <= ix1)) {
212 s1 = t1 +% r;
213 if (t1 & sign == sign and s1 & sign == 0) {
214 s0 += 1;
215 }
216 ix0 -= t;
217 if (ix1 < t1) {
218 ix0 -= 1;
219 }
220 ix1 = ix1 -% t1;
221 q1 += r;
222 }
223 ix0 = ix0 +% ix0 +% (ix1 >> 31);
224 ix1 = ix1 +% ix1;
225 r >>= 1;
226 }
227
228 // rounding direction
229 if (ix0 | ix1 != 0) {
230 var z = 1.0 - tiny; // raise inexact
231 if (z >= 1.0) {
232 z = 1.0 + tiny;
233 if (q1 == 0xFFFFFFFF) {
234 q1 = 0;
235 q += 1;
236 } else if (z > 1.0) {
237 if (q1 == 0xFFFFFFFE) {
238 q += 1;
239 }
240 q1 += 2;
241 } else {
242 q1 += q1 & 1;
243 }
244 }
245 }
246
247 ix0 = (q >> 1) + 0x3FE00000;
248 ix1 = q1 >> 1;
249 if (q & 1 != 0) {
250 ix1 |= 0x80000000;
251 }
252
253 // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
254 // behaviour at least.
255 var iix0 = i32(ix0);
256 iix0 = iix0 +% (m << 20);
257
258 const uz = (u64(iix0) << 32) | ix1;
259 return @bitCast(f64, uz);
260}
261
262test "math.sqrt" {33test "math.sqrt" {
263 assert(sqrt(f32(0.0)) == sqrt32(0.0));34 assert(sqrt(f32(0.0)) == @sqrt(f32, 0.0));
264 assert(sqrt(f64(0.0)) == sqrt64(0.0));35 assert(sqrt(f64(0.0)) == @sqrt(f64, 0.0));
265}36}
26637
267test "math.sqrt32" {38test "math.sqrt32" {
268 const epsilon = 0.000001;39 const epsilon = 0.000001;
26940
270 assert(sqrt32(0.0) == 0.0);41 assert(@sqrt(f32, 0.0) == 0.0);
271 assert(math.approxEq(f32, sqrt32(2.0), 1.414214, epsilon));42 assert(math.approxEq(f32, @sqrt(f32, 2.0), 1.414214, epsilon));
272 assert(math.approxEq(f32, sqrt32(3.6), 1.897367, epsilon));43 assert(math.approxEq(f32, @sqrt(f32, 3.6), 1.897367, epsilon));
273 assert(sqrt32(4.0) == 2.0);44 assert(@sqrt(f32, 4.0) == 2.0);
274 assert(math.approxEq(f32, sqrt32(7.539840), 2.745877, epsilon));45 assert(math.approxEq(f32, @sqrt(f32, 7.539840), 2.745877, epsilon));
275 assert(math.approxEq(f32, sqrt32(19.230934), 4.385309, epsilon));46 assert(math.approxEq(f32, @sqrt(f32, 19.230934), 4.385309, epsilon));
276 assert(sqrt32(64.0) == 8.0);47 assert(@sqrt(f32, 64.0) == 8.0);
277 assert(math.approxEq(f32, sqrt32(64.1), 8.006248, epsilon));48 assert(math.approxEq(f32, @sqrt(f32, 64.1), 8.006248, epsilon));
278 assert(math.approxEq(f32, sqrt32(8942.230469), 94.563370, epsilon));49 assert(math.approxEq(f32, @sqrt(f32, 8942.230469), 94.563370, epsilon));
279}50}
28051
281test "math.sqrt64" {52test "math.sqrt64" {
282 const epsilon = 0.000001;53 const epsilon = 0.000001;
28354
284 assert(sqrt64(0.0) == 0.0);55 assert(@sqrt(f64, 0.0) == 0.0);
285 assert(math.approxEq(f64, sqrt64(2.0), 1.414214, epsilon));56 assert(math.approxEq(f64, @sqrt(f64, 2.0), 1.414214, epsilon));
286 assert(math.approxEq(f64, sqrt64(3.6), 1.897367, epsilon));57 assert(math.approxEq(f64, @sqrt(f64, 3.6), 1.897367, epsilon));
287 assert(sqrt64(4.0) == 2.0);58 assert(@sqrt(f64, 4.0) == 2.0);
288 assert(math.approxEq(f64, sqrt64(7.539840), 2.745877, epsilon));59 assert(math.approxEq(f64, @sqrt(f64, 7.539840), 2.745877, epsilon));
289 assert(math.approxEq(f64, sqrt64(19.230934), 4.385309, epsilon));60 assert(math.approxEq(f64, @sqrt(f64, 19.230934), 4.385309, epsilon));
290 assert(sqrt64(64.0) == 8.0);61 assert(@sqrt(f64, 64.0) == 8.0);
291 assert(math.approxEq(f64, sqrt64(64.1), 8.006248, epsilon));62 assert(math.approxEq(f64, @sqrt(f64, 64.1), 8.006248, epsilon));
292 assert(math.approxEq(f64, sqrt64(8942.230469), 94.563367, epsilon));63 assert(math.approxEq(f64, @sqrt(f64, 8942.230469), 94.563367, epsilon));
293}64}
29465
295test "math.sqrt32.special" {66test "math.sqrt32.special" {
296 assert(math.isPositiveInf(sqrt32(math.inf(f32))));67 assert(math.isPositiveInf(@sqrt(f32, math.inf(f32))));
297 assert(sqrt32(0.0) == 0.0);68 assert(@sqrt(f32, 0.0) == 0.0);
298 assert(sqrt32(-0.0) == -0.0);69 assert(@sqrt(f32, -0.0) == -0.0);
299 assert(math.isNan(sqrt32(-1.0)));70 assert(math.isNan(@sqrt(f32, -1.0)));
300 assert(math.isNan(sqrt32(math.nan(f32))));71 assert(math.isNan(@sqrt(f32, math.nan(f32))));
301}72}
30273
303test "math.sqrt64.special" {74test "math.sqrt64.special" {
304 assert(math.isPositiveInf(sqrt64(math.inf(f64))));75 assert(math.isPositiveInf(@sqrt(f64, math.inf(f64))));
305 assert(sqrt64(0.0) == 0.0);76 assert(@sqrt(f64, 0.0) == 0.0);
306 assert(sqrt64(-0.0) == -0.0);77 assert(@sqrt(f64, -0.0) == -0.0);
307 assert(math.isNan(sqrt64(-1.0)));78 assert(math.isNan(@sqrt(f64, -1.0)));
308 assert(math.isNan(sqrt64(math.nan(f64))));79 assert(math.isNan(@sqrt(f64, math.nan(f64))));
309}80}
31081
311fn sqrt_int(comptime T: type, value: T) @IntType(false, T.bit_count / 2) {82fn sqrt_int(comptime T: type, value: T) @IntType(false, T.bit_count / 2) {
std/math/x86_64/sqrt.zig deleted-15
...@@ -1,15 +0,0 @@
1pub fn sqrt32(x: f32) f32 {
2 return asm (
3 \\sqrtss %%xmm0, %%xmm0
4 : [ret] "={xmm0}" (-> f32)
5 : [x] "{xmm0}" (x)
6 );
7}
8
9pub fn sqrt64(x: f64) f64 {
10 return asm (
11 \\sqrtsd %%xmm0, %%xmm0
12 : [ret] "={xmm0}" (-> f64)
13 : [x] "{xmm0}" (x)
14 );
15}
std/mem.zig+86-2
...@@ -11,6 +11,8 @@ pub const Allocator = struct {...@@ -11,6 +11,8 @@ pub const Allocator = struct {
11 /// Allocate byte_count bytes and return them in a slice, with the11 /// Allocate byte_count bytes and return them in a slice, with the
12 /// slice's pointer aligned at least to alignment bytes.12 /// slice's pointer aligned at least to alignment bytes.
13 /// The returned newly allocated memory is undefined.13 /// The returned newly allocated memory is undefined.
14 /// `alignment` is guaranteed to be >= 1
15 /// `alignment` is guaranteed to be a power of 2
14 allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8,16 allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) Error![]u8,
1517
16 /// If `new_byte_count > old_mem.len`:18 /// If `new_byte_count > old_mem.len`:
...@@ -18,10 +20,12 @@ pub const Allocator = struct {...@@ -18,10 +20,12 @@ pub const Allocator = struct {
18 /// * alignment >= alignment of old_mem.ptr20 /// * alignment >= alignment of old_mem.ptr
19 ///21 ///
20 /// If `new_byte_count <= old_mem.len`:22 /// If `new_byte_count <= old_mem.len`:
21 /// * this function must return successfully. 23 /// * this function must return successfully.
22 /// * alignment <= alignment of old_mem.ptr24 /// * alignment <= alignment of old_mem.ptr
23 ///25 ///
24 /// The returned newly allocated memory is undefined.26 /// The returned newly allocated memory is undefined.
27 /// `alignment` is guaranteed to be >= 1
28 /// `alignment` is guaranteed to be a power of 2
25 reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8,29 reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) Error![]u8,
2630
27 /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`31 /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
...@@ -170,6 +174,20 @@ pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T {...@@ -170,6 +174,20 @@ pub fn dupe(allocator: &Allocator, comptime T: type, m: []const T) ![]T {
170 return new_buf;174 return new_buf;
171}175}
172176
177/// Remove values from the beginning of a slice.
178pub fn trimLeft(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
179 var begin: usize = 0;
180 while (begin < slice.len and indexOfScalar(T, values_to_strip, slice[begin]) != null) : (begin += 1) {}
181 return slice[begin..];
182}
183
184/// Remove values from the end of a slice.
185pub fn trimRight(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
186 var end: usize = slice.len;
187 while (end > 0 and indexOfScalar(T, values_to_strip, slice[end - 1]) != null) : (end -= 1) {}
188 return slice[0..end];
189}
190
173/// Remove values from the beginning and end of a slice.191/// Remove values from the beginning and end of a slice.
174pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {192pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
175 var begin: usize = 0;193 var begin: usize = 0;
...@@ -180,6 +198,8 @@ pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []co...@@ -180,6 +198,8 @@ pub fn trim(comptime T: type, slice: []const T, values_to_strip: []const T) []co
180}198}
181199
182test "mem.trim" {200test "mem.trim" {
201 assert(eql(u8, trimLeft(u8, " foo\n ", " \n"), "foo\n "));
202 assert(eql(u8, trimRight(u8, " foo\n ", " \n"), " foo"));
183 assert(eql(u8, trim(u8, " foo\n ", " \n"), "foo"));203 assert(eql(u8, trim(u8, " foo\n ", " \n"), "foo"));
184 assert(eql(u8, trim(u8, "foo", " \n"), "foo"));204 assert(eql(u8, trim(u8, "foo", " \n"), "foo"));
185}205}
...@@ -189,6 +209,17 @@ pub fn indexOfScalar(comptime T: type, slice: []const T, value: T) ?usize {...@@ -189,6 +209,17 @@ pub fn indexOfScalar(comptime T: type, slice: []const T, value: T) ?usize {
189 return indexOfScalarPos(T, slice, 0, value);209 return indexOfScalarPos(T, slice, 0, value);
190}210}
191211
212/// Linear search for the last index of a scalar value inside a slice.
213pub fn lastIndexOfScalar(comptime T: type, slice: []const T, value: T) ?usize {
214 var i: usize = slice.len;
215 while (i != 0) {
216 i -= 1;
217 if (slice[i] == value)
218 return i;
219 }
220 return null;
221}
222
192pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize {223pub fn indexOfScalarPos(comptime T: type, slice: []const T, start_index: usize, value: T) ?usize {
193 var i: usize = start_index;224 var i: usize = start_index;
194 while (i < slice.len) : (i += 1) {225 while (i < slice.len) : (i += 1) {
...@@ -202,6 +233,18 @@ pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize...@@ -202,6 +233,18 @@ pub fn indexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize
202 return indexOfAnyPos(T, slice, 0, values);233 return indexOfAnyPos(T, slice, 0, values);
203}234}
204235
236pub fn lastIndexOfAny(comptime T: type, slice: []const T, values: []const T) ?usize {
237 var i: usize = slice.len;
238 while (i != 0) {
239 i -= 1;
240 for (values) |value| {
241 if (slice[i] == value)
242 return i;
243 }
244 }
245 return null;
246}
247
205pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize {248pub fn indexOfAnyPos(comptime T: type, slice: []const T, start_index: usize, values: []const T) ?usize {
206 var i: usize = start_index;249 var i: usize = start_index;
207 while (i < slice.len) : (i += 1) {250 while (i < slice.len) : (i += 1) {
...@@ -217,6 +260,22 @@ pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize...@@ -217,6 +260,22 @@ pub fn indexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize
217 return indexOfPos(T, haystack, 0, needle);260 return indexOfPos(T, haystack, 0, needle);
218}261}
219262
263/// Find the index in a slice of a sub-slice, searching from the end backwards.
264/// To start looking at a different index, slice the haystack first.
265/// TODO is there even a better algorithm for this?
266pub fn lastIndexOf(comptime T: type, haystack: []const T, needle: []const T) ?usize {
267 if (needle.len > haystack.len)
268 return null;
269
270 var i: usize = haystack.len - needle.len;
271 while (true) : (i -= 1) {
272 if (mem.eql(T, haystack[i..i+needle.len], needle))
273 return i;
274 if (i == 0)
275 return null;
276 }
277}
278
220// TODO boyer-moore algorithm279// TODO boyer-moore algorithm
221pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize {280pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, needle: []const T) ?usize {
222 if (needle.len > haystack.len)281 if (needle.len > haystack.len)
...@@ -233,9 +292,19 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee...@@ -233,9 +292,19 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee
233292
234test "mem.indexOf" {293test "mem.indexOf" {
235 assert(??indexOf(u8, "one two three four", "four") == 14);294 assert(??indexOf(u8, "one two three four", "four") == 14);
295 assert(??lastIndexOf(u8, "one two three two four", "two") == 14);
236 assert(indexOf(u8, "one two three four", "gour") == null);296 assert(indexOf(u8, "one two three four", "gour") == null);
297 assert(lastIndexOf(u8, "one two three four", "gour") == null);
237 assert(??indexOf(u8, "foo", "foo") == 0);298 assert(??indexOf(u8, "foo", "foo") == 0);
299 assert(??lastIndexOf(u8, "foo", "foo") == 0);
238 assert(indexOf(u8, "foo", "fool") == null);300 assert(indexOf(u8, "foo", "fool") == null);
301 assert(lastIndexOf(u8, "foo", "lfoo") == null);
302 assert(lastIndexOf(u8, "foo", "fool") == null);
303
304 assert(??indexOf(u8, "foo foo", "foo") == 0);
305 assert(??lastIndexOf(u8, "foo foo", "foo") == 4);
306 assert(??lastIndexOfAny(u8, "boo, cat", "abo") == 6);
307 assert(??lastIndexOfScalar(u8, "boo", 'o') == 2);
239}308}
240309
241/// Reads an integer from memory with size equal to bytes.len.310/// Reads an integer from memory with size equal to bytes.len.
...@@ -355,9 +424,24 @@ pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool...@@ -355,9 +424,24 @@ pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) bool
355 return if (needle.len > haystack.len) false else eql(T, haystack[0 .. needle.len], needle);424 return if (needle.len > haystack.len) false else eql(T, haystack[0 .. needle.len], needle);
356}425}
357426
427test "mem.startsWith" {
428 assert(startsWith(u8, "Bob", "Bo"));
429 assert(!startsWith(u8, "Needle in haystack", "haystack"));
430}
431
432pub fn endsWith(comptime T: type, haystack: []const T, needle: []const T) bool {
433 return if (needle.len > haystack.len) false else eql(T, haystack[haystack.len - needle.len ..], needle);
434}
435
436
437test "mem.endsWith" {
438 assert(endsWith(u8, "Needle in haystack", "haystack"));
439 assert(!endsWith(u8, "Bob", "Bo"));
440}
441
358pub const SplitIterator = struct {442pub const SplitIterator = struct {
359 buffer: []const u8,443 buffer: []const u8,
360 split_bytes: []const u8, 444 split_bytes: []const u8,
361 index: usize,445 index: usize,
362446
363 pub fn next(self: &SplitIterator) ?[]const u8 {447 pub fn next(self: &SplitIterator) ?[]const u8 {
std/os/darwin.zig+21
...@@ -41,6 +41,11 @@ pub const SA_64REGSET = 0x0200; /// signal handler with SA_SIGINFO args with 64...@@ -41,6 +41,11 @@ pub const SA_64REGSET = 0x0200; /// signal handler with SA_SIGINFO args with 64
41pub const O_LARGEFILE = 0x0000;41pub const O_LARGEFILE = 0x0000;
42pub const O_PATH = 0x0000;42pub const O_PATH = 0x0000;
4343
44pub const F_OK = 0;
45pub const X_OK = 1;
46pub const W_OK = 2;
47pub const R_OK = 4;
48
44pub const O_RDONLY = 0x0000; /// open for reading only49pub const O_RDONLY = 0x0000; /// open for reading only
45pub const O_WRONLY = 0x0001; /// open for writing only50pub const O_WRONLY = 0x0001; /// open for writing only
46pub const O_RDWR = 0x0002; /// open for reading and writing51pub const O_RDWR = 0x0002; /// open for reading and writing
...@@ -209,6 +214,10 @@ pub fn fork() usize {...@@ -209,6 +214,10 @@ pub fn fork() usize {
209 return errnoWrap(c.fork());214 return errnoWrap(c.fork());
210}215}
211216
217pub fn access(path: &const u8, mode: u32) usize {
218 return errnoWrap(c.access(path, mode));
219}
220
212pub fn pipe(fds: &[2]i32) usize {221pub fn pipe(fds: &[2]i32) usize {
213 comptime assert(i32.bit_count == c_int.bit_count);222 comptime assert(i32.bit_count == c_int.bit_count);
214 return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));223 return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));
...@@ -251,6 +260,10 @@ pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) u...@@ -251,6 +260,10 @@ pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) u
251 return errnoWrap(c.readlink(path, buf_ptr, buf_len));260 return errnoWrap(c.readlink(path, buf_ptr, buf_len));
252}261}
253262
263pub fn gettimeofday(tv: ?&timeval, tz: ?&timezone) usize {
264 return errnoWrap(c.gettimeofday(tv, tz));
265}
266
254pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize {267pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize {
255 return errnoWrap(c.nanosleep(req, rem));268 return errnoWrap(c.nanosleep(req, rem));
256}269}
...@@ -321,3 +334,11 @@ pub fn sigaddset(set: &sigset_t, signo: u5) void {...@@ -321,3 +334,11 @@ pub fn sigaddset(set: &sigset_t, signo: u5) void {
321fn errnoWrap(value: isize) usize {334fn errnoWrap(value: isize) usize {
322 return @bitCast(usize, if (value == -1) -isize(*c._errno()) else value);335 return @bitCast(usize, if (value == -1) -isize(*c._errno()) else value);
323}336}
337
338
339pub const timezone = c.timezone;
340pub const timeval = c.timeval;
341pub const mach_timebase_info_data = c.mach_timebase_info_data;
342
343pub const mach_absolute_time = c.mach_absolute_time;
344pub const mach_timebase_info = c.mach_timebase_info;
\ No newline at end of file
std/os/epoch.zig created+26
...@@ -0,0 +1,26 @@
1/// Epoch reference times in terms of their difference from
2/// posix epoch in seconds.
3pub const posix = 0; //Jan 01, 1970 AD
4pub const dos = 315532800; //Jan 01, 1980 AD
5pub const ios = 978307200; //Jan 01, 2001 AD
6pub const openvms = -3506716800; //Nov 17, 1858 AD
7pub const zos = -2208988800; //Jan 01, 1900 AD
8pub const windows = -11644473600; //Jan 01, 1601 AD
9pub const amiga = 252460800; //Jan 01, 1978 AD
10pub const pickos = -63244800; //Dec 31, 1967 AD
11pub const gps = 315964800; //Jan 06, 1980 AD
12pub const clr = -62135769600; //Jan 01, 0001 AD
13
14pub const unix = posix;
15pub const android = posix;
16pub const os2 = dos;
17pub const bios = dos;
18pub const vfat = dos;
19pub const ntfs = windows;
20pub const ntp = zos;
21pub const jbase = pickos;
22pub const aros = amiga;
23pub const morphos = amiga;
24pub const brew = gps;
25pub const atsc = gps;
26pub const go = clr;
\ No newline at end of file
std/os/file.zig+44-1
...@@ -85,6 +85,47 @@ pub const File = struct {...@@ -85,6 +85,47 @@ pub const File = struct {
85 };85 };
86 }86 }
8787
88 pub fn access(allocator: &mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool {
89 const path_with_null = try std.cstr.addNullByte(allocator, path);
90 defer allocator.free(path_with_null);
91
92 if (is_posix) {
93 // mode is ignored and is always F_OK for now
94 const result = posix.access(path_with_null.ptr, posix.F_OK);
95 const err = posix.getErrno(result);
96 if (err > 0) {
97 return switch (err) {
98 posix.EACCES => error.PermissionDenied,
99 posix.EROFS => error.PermissionDenied,
100 posix.ELOOP => error.PermissionDenied,
101 posix.ETXTBSY => error.PermissionDenied,
102 posix.ENOTDIR => error.NotFound,
103 posix.ENOENT => error.NotFound,
104
105 posix.ENAMETOOLONG => error.NameTooLong,
106 posix.EINVAL => error.BadMode,
107 posix.EFAULT => error.BadPathName,
108 posix.EIO => error.Io,
109 posix.ENOMEM => error.SystemResources,
110 else => os.unexpectedErrorPosix(err),
111 };
112 }
113 return true;
114 } else if (is_windows) {
115 if (os.windows.PathFileExists(path_with_null.ptr) == os.windows.TRUE) {
116 return true;
117 }
118
119 const err = windows.GetLastError();
120 return switch (err) {
121 windows.ERROR.FILE_NOT_FOUND => error.NotFound,
122 windows.ERROR.ACCESS_DENIED => error.PermissionDenied,
123 else => os.unexpectedErrorWindows(err),
124 };
125 } else {
126 @compileError("TODO implement access for this OS");
127 }
128 }
88129
89 /// Upon success, the stream is in an uninitialized state. To continue using it,130 /// Upon success, the stream is in an uninitialized state. To continue using it,
90 /// you must use the open() function.131 /// you must use the open() function.
...@@ -245,7 +286,9 @@ pub const File = struct {...@@ -245,7 +286,9 @@ pub const File = struct {
245 };286 };
246 }287 }
247288
248 return stat.mode;289 // TODO: we should be able to cast u16 to ModeError!u32, making this
290 // explicit cast not necessary
291 return os.FileMode(stat.mode);
249 } else if (is_windows) {292 } else if (is_windows) {
250 return {};293 return {};
251 } else {294 } else {
std/os/index.zig+132-46
...@@ -9,11 +9,10 @@ test "std.os" {...@@ -9,11 +9,10 @@ test "std.os" {
9 _ = @import("darwin.zig");9 _ = @import("darwin.zig");
10 _ = @import("darwin_errno.zig");10 _ = @import("darwin_errno.zig");
11 _ = @import("get_user_id.zig");11 _ = @import("get_user_id.zig");
12 _ = @import("linux/errno.zig");
13 _ = @import("linux/index.zig");12 _ = @import("linux/index.zig");
14 _ = @import("linux/x86_64.zig");
15 _ = @import("path.zig");13 _ = @import("path.zig");
16 _ = @import("test.zig");14 _ = @import("test.zig");
15 _ = @import("time.zig");
17 _ = @import("windows/index.zig");16 _ = @import("windows/index.zig");
18}17}
1918
...@@ -32,6 +31,7 @@ pub const net = @import("net.zig");...@@ -32,6 +31,7 @@ pub const net = @import("net.zig");
32pub const ChildProcess = @import("child_process.zig").ChildProcess;31pub const ChildProcess = @import("child_process.zig").ChildProcess;
33pub const path = @import("path.zig");32pub const path = @import("path.zig");
34pub const File = @import("file.zig").File;33pub const File = @import("file.zig").File;
34pub const time = @import("time.zig");
3535
36pub const FileMode = switch (builtin.os) {36pub const FileMode = switch (builtin.os) {
37 Os.windows => void,37 Os.windows => void,
...@@ -478,6 +478,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {...@@ -478,6 +478,7 @@ fn posixExecveErrnoToErr(err: usize) PosixExecveError {
478 };478 };
479}479}
480480
481pub var linux_aux_raw = []usize{0} ** 38;
481pub var posix_environ_raw: []&u8 = undefined;482pub var posix_environ_raw: []&u8 = undefined;
482483
483/// Caller must free result when done.484/// Caller must free result when done.
...@@ -1379,50 +1380,6 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {...@@ -1379,50 +1380,6 @@ pub fn readLink(allocator: &Allocator, pathname: []const u8) ![]u8 {
1379 }1380 }
1380}1381}
13811382
1382pub fn sleep(seconds: usize, nanoseconds: usize) void {
1383 switch(builtin.os) {
1384 Os.linux, Os.macosx, Os.ios => {
1385 posixSleep(u63(seconds), u63(nanoseconds));
1386 },
1387 Os.windows => {
1388 const milliseconds = seconds * 1000 + nanoseconds / 1000000;
1389 windows.Sleep(windows.DWORD(milliseconds));
1390 },
1391 else => @compileError("Unsupported OS"),
1392 }
1393}
1394
1395const u63 = @IntType(false, 63);
1396pub fn posixSleep(seconds: u63, nanoseconds: u63) void {
1397 var req = posix.timespec {
1398 .tv_sec = seconds,
1399 .tv_nsec = nanoseconds,
1400 };
1401 var rem: posix.timespec = undefined;
1402 while (true) {
1403 const ret_val = posix.nanosleep(&req, &rem);
1404 const err = posix.getErrno(ret_val);
1405 if (err == 0) return;
1406 switch (err) {
1407 posix.EFAULT => unreachable,
1408 posix.EINVAL => {
1409 // Sometimes Darwin returns EINVAL for no reason.
1410 // We treat it as a spurious wakeup.
1411 return;
1412 },
1413 posix.EINTR => {
1414 req = rem;
1415 continue;
1416 },
1417 else => return,
1418 }
1419 }
1420}
1421
1422test "os.sleep" {
1423 sleep(0, 1);
1424}
1425
1426pub fn posix_setuid(uid: u32) !void {1383pub fn posix_setuid(uid: u32) !void {
1427 const err = posix.getErrno(posix.setuid(uid));1384 const err = posix.getErrno(posix.setuid(uid));
1428 if (err == 0) return;1385 if (err == 0) return;
...@@ -2384,3 +2341,132 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void {...@@ -2384,3 +2341,132 @@ pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void {
2384 posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.2341 posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
2385 }2342 }
2386}2343}
2344
2345pub const Thread = struct {
2346 pid: i32,
2347 allocator: ?&mem.Allocator,
2348 stack: []u8,
2349
2350 pub fn wait(self: &const Thread) void {
2351 while (true) {
2352 const pid_value = @atomicLoad(i32, &self.pid, builtin.AtomicOrder.SeqCst);
2353 if (pid_value == 0) break;
2354 const rc = linux.futex_wait(@ptrToInt(&self.pid), linux.FUTEX_WAIT, pid_value, null);
2355 switch (linux.getErrno(rc)) {
2356 0 => continue,
2357 posix.EINTR => continue,
2358 posix.EAGAIN => continue,
2359 else => unreachable,
2360 }
2361 }
2362 if (self.allocator) |a| {
2363 a.free(self.stack);
2364 }
2365 }
2366};
2367
2368pub const SpawnThreadError = error {
2369 /// A system-imposed limit on the number of threads was encountered.
2370 /// There are a number of limits that may trigger this error:
2371 /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)),
2372 /// which limits the number of processes and threads for a real
2373 /// user ID, was reached;
2374 /// * the kernel's system-wide limit on the number of processes and
2375 /// threads, /proc/sys/kernel/threads-max, was reached (see
2376 /// proc(5));
2377 /// * the maximum number of PIDs, /proc/sys/kernel/pid_max, was
2378 /// reached (see proc(5)); or
2379 /// * the PID limit (pids.max) imposed by the cgroup "process num‐
2380 /// ber" (PIDs) controller was reached.
2381 ThreadQuotaExceeded,
2382
2383 /// The kernel cannot allocate sufficient memory to allocate a task structure
2384 /// for the child, or to copy those parts of the caller's context that need to
2385 /// be copied.
2386 SystemResources,
2387
2388 Unexpected,
2389};
2390
2391pub const SpawnThreadAllocatorError = SpawnThreadError || error{OutOfMemory};
2392
2393/// caller must call wait on the returned thread
2394/// fn startFn(@typeOf(context)) T
2395/// where T is u8, noreturn, void, or !void
2396pub fn spawnThreadAllocator(allocator: &mem.Allocator, context: var, comptime startFn: var) SpawnThreadAllocatorError!&Thread {
2397 // TODO compile-time call graph analysis to determine stack upper bound
2398 // https://github.com/zig-lang/zig/issues/157
2399 const default_stack_size = 8 * 1024 * 1024;
2400 const stack_bytes = try allocator.alloc(u8, default_stack_size);
2401 const thread = try spawnThread(stack_bytes, context, startFn);
2402 thread.allocator = allocator;
2403 return thread;
2404}
2405
2406/// stack must be big enough to store one Thread and one @typeOf(context), each with default alignment, at the end
2407/// fn startFn(@typeOf(context)) T
2408/// where T is u8, noreturn, void, or !void
2409/// caller must call wait on the returned thread
2410pub fn spawnThread(stack: []u8, context: var, comptime startFn: var) SpawnThreadError!&Thread {
2411 const Context = @typeOf(context);
2412 comptime assert(@ArgType(@typeOf(startFn), 0) == Context);
2413
2414 var stack_end: usize = @ptrToInt(stack.ptr) + stack.len;
2415 var arg: usize = undefined;
2416 if (@sizeOf(Context) != 0) {
2417 stack_end -= @sizeOf(Context);
2418 stack_end -= stack_end % @alignOf(Context);
2419 assert(stack_end >= @ptrToInt(stack.ptr));
2420 const context_ptr = @alignCast(@alignOf(Context), @intToPtr(&Context, stack_end));
2421 *context_ptr = context;
2422 arg = stack_end;
2423 }
2424
2425 stack_end -= @sizeOf(Thread);
2426 stack_end -= stack_end % @alignOf(Thread);
2427 assert(stack_end >= @ptrToInt(stack.ptr));
2428 const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(&Thread, stack_end));
2429 thread_ptr.stack = stack;
2430 thread_ptr.allocator = null;
2431
2432 const threadMain = struct {
2433 extern fn threadMain(ctx_addr: usize) u8 {
2434 if (@sizeOf(Context) == 0) {
2435 return startFn({});
2436 } else {
2437 return startFn(*@intToPtr(&const Context, ctx_addr));
2438 }
2439 }
2440 }.threadMain;
2441
2442 const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND
2443 | posix.CLONE_THREAD | posix.CLONE_SYSVSEM // | posix.CLONE_SETTLS
2444 | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
2445 const newtls: usize = 0;
2446 const rc = posix.clone(threadMain, stack_end, flags, arg, &thread_ptr.pid, newtls, &thread_ptr.pid);
2447 const err = posix.getErrno(rc);
2448 switch (err) {
2449 0 => return thread_ptr,
2450 posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded,
2451 posix.EINVAL => unreachable,
2452 posix.ENOMEM => return SpawnThreadError.SystemResources,
2453 posix.ENOSPC => unreachable,
2454 posix.EPERM => unreachable,
2455 posix.EUSERS => unreachable,
2456 else => return unexpectedErrorPosix(err),
2457 }
2458}
2459
2460pub fn posixWait(pid: i32) i32 {
2461 var status: i32 = undefined;
2462 while (true) {
2463 const err = posix.getErrno(posix.waitpid(pid, &status, 0));
2464 switch (err) {
2465 0 => return status,
2466 posix.EINTR => continue,
2467 posix.ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
2468 posix.EINVAL => unreachable, // The options argument was invalid
2469 else => unreachable,
2470 }
2471 }
2472}
std/os/linux/index.zig+80-3
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1const std = @import("../../index.zig");1const std = @import("../../index.zig");
2const assert = std.debug.assert;2const assert = std.debug.assert;
3const builtin = @import("builtin");3const builtin = @import("builtin");
4const vdso = @import("vdso.zig");
4pub use switch (builtin.arch) {5pub use switch (builtin.arch) {
5 builtin.Arch.x86_64 => @import("x86_64.zig"),6 builtin.Arch.x86_64 => @import("x86_64.zig"),
6 builtin.Arch.i386 => @import("i386.zig"),7 builtin.Arch.i386 => @import("i386.zig"),
...@@ -14,6 +15,22 @@ pub const STDIN_FILENO = 0;...@@ -14,6 +15,22 @@ pub const STDIN_FILENO = 0;
14pub const STDOUT_FILENO = 1;15pub const STDOUT_FILENO = 1;
15pub const STDERR_FILENO = 2;16pub const STDERR_FILENO = 2;
1617
18pub const FUTEX_WAIT = 0;
19pub const FUTEX_WAKE = 1;
20pub const FUTEX_FD = 2;
21pub const FUTEX_REQUEUE = 3;
22pub const FUTEX_CMP_REQUEUE = 4;
23pub const FUTEX_WAKE_OP = 5;
24pub const FUTEX_LOCK_PI = 6;
25pub const FUTEX_UNLOCK_PI = 7;
26pub const FUTEX_TRYLOCK_PI = 8;
27pub const FUTEX_WAIT_BITSET = 9;
28
29pub const FUTEX_PRIVATE_FLAG = 128;
30
31pub const FUTEX_CLOCK_REALTIME = 256;
32
33
17pub const PROT_NONE = 0;34pub const PROT_NONE = 0;
18pub const PROT_READ = 1;35pub const PROT_READ = 1;
19pub const PROT_WRITE = 2;36pub const PROT_WRITE = 2;
...@@ -38,6 +55,11 @@ pub const MAP_STACK = 0x20000;...@@ -38,6 +55,11 @@ pub const MAP_STACK = 0x20000;
38pub const MAP_HUGETLB = 0x40000;55pub const MAP_HUGETLB = 0x40000;
39pub const MAP_FILE = 0;56pub const MAP_FILE = 0;
4057
58pub const F_OK = 0;
59pub const X_OK = 1;
60pub const W_OK = 2;
61pub const R_OK = 4;
62
41pub const WNOHANG = 1;63pub const WNOHANG = 1;
42pub const WUNTRACED = 2;64pub const WUNTRACED = 2;
43pub const WSTOPPED = 2;65pub const WSTOPPED = 2;
...@@ -647,6 +669,10 @@ pub fn fork() usize {...@@ -647,6 +669,10 @@ pub fn fork() usize {
647 return syscall0(SYS_fork);669 return syscall0(SYS_fork);
648}670}
649671
672pub fn futex_wait(uaddr: usize, futex_op: u32, val: i32, timeout: ?&timespec) usize {
673 return syscall4(SYS_futex, uaddr, futex_op, @bitCast(u32, val), @ptrToInt(timeout));
674}
675
650pub fn getcwd(buf: &u8, size: usize) usize {676pub fn getcwd(buf: &u8, size: usize) usize {
651 return syscall2(SYS_getcwd, @ptrToInt(buf), size);677 return syscall2(SYS_getcwd, @ptrToInt(buf), size);
652}678}
...@@ -705,6 +731,10 @@ pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize {...@@ -705,6 +731,10 @@ pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize {
705 return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset);731 return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset);
706}732}
707733
734pub fn access(path: &const u8, mode: u32) usize {
735 return syscall2(SYS_access, @ptrToInt(path), mode);
736}
737
708pub fn pipe(fd: &[2]i32) usize {738pub fn pipe(fd: &[2]i32) usize {
709 return pipe2(fd, 0);739 return pipe2(fd, 0);
710}740}
...@@ -737,6 +767,16 @@ pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) usize {...@@ -737,6 +767,16 @@ pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) usize {
737 return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode);767 return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode);
738}768}
739769
770/// See also `clone` (from the arch-specific include)
771pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: &i32, child_tid: &i32, newtls: usize) usize {
772 return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls);
773}
774
775/// See also `clone` (from the arch-specific include)
776pub fn clone2(flags: usize, child_stack_ptr: usize) usize {
777 return syscall2(SYS_clone, flags, child_stack_ptr);
778}
779
740pub fn close(fd: i32) usize {780pub fn close(fd: i32) usize {
741 return syscall1(SYS_close, usize(fd));781 return syscall1(SYS_close, usize(fd));
742}782}
...@@ -766,6 +806,45 @@ pub fn waitpid(pid: i32, status: &i32, options: i32) usize {...@@ -766,6 +806,45 @@ pub fn waitpid(pid: i32, status: &i32, options: i32) usize {
766 return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);806 return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);
767}807}
768808
809pub fn clock_gettime(clk_id: i32, tp: &timespec) usize {
810 if (VDSO_CGT_SYM.len != 0) {
811 const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered);
812 if (@ptrToInt(f) != 0) {
813 const rc = f(clk_id, tp);
814 switch (rc) {
815 0, @bitCast(usize, isize(-EINVAL)) => return rc,
816 else => {},
817 }
818 }
819 }
820 return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
821}
822var vdso_clock_gettime = init_vdso_clock_gettime;
823extern fn init_vdso_clock_gettime(clk: i32, ts: &timespec) usize {
824 const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM);
825 var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr);
826 _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f,
827 builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic);
828 if (@ptrToInt(f) == 0) return @bitCast(usize, isize(-ENOSYS));
829 return f(clk, ts);
830}
831
832pub fn clock_getres(clk_id: i32, tp: &timespec) usize {
833 return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
834}
835
836pub fn clock_settime(clk_id: i32, tp: &const timespec) usize {
837 return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
838}
839
840pub fn gettimeofday(tv: &timeval, tz: &timezone) usize {
841 return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz));
842}
843
844pub fn settimeofday(tv: &const timeval, tz: &const timezone) usize {
845 return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz));
846}
847
769pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize {848pub fn nanosleep(req: &const timespec, rem: ?&timespec) usize {
770 return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));849 return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));
771}850}
...@@ -1250,9 +1329,7 @@ pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize {...@@ -1250,9 +1329,7 @@ pub fn capset(hdrp: &cap_user_header_t, datap: &const cap_user_data_t) usize {
1250 return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));1329 return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap));
1251}1330}
12521331
1253test "import linux test" {1332test "import" {
1254 // TODO lazy analysis should prevent this test from being compiled on windows, but
1255 // it is still compiled on windows
1256 if (builtin.os == builtin.Os.linux) {1333 if (builtin.os == builtin.Os.linux) {
1257 _ = @import("test.zig");1334 _ = @import("test.zig");
1258 }1335 }
std/os/linux/test.zig+1
...@@ -1,4 +1,5 @@...@@ -1,4 +1,5 @@
1const std = @import("../../index.zig");1const std = @import("../../index.zig");
2const builtin = @import("builtin");
2const linux = std.os.linux;3const linux = std.os.linux;
3const assert = std.debug.assert;4const assert = std.debug.assert;
45
std/os/linux/vdso.zig created+89
...@@ -0,0 +1,89 @@
1const std = @import("../../index.zig");
2const elf = std.elf;
3const linux = std.os.linux;
4const cstr = std.cstr;
5const mem = std.mem;
6
7pub fn lookup(vername: []const u8, name: []const u8) usize {
8 const vdso_addr = std.os.linux_aux_raw[std.elf.AT_SYSINFO_EHDR];
9 if (vdso_addr == 0) return 0;
10
11 const eh = @intToPtr(&elf.Ehdr, vdso_addr);
12 var ph_addr: usize = vdso_addr + eh.e_phoff;
13 const ph = @intToPtr(&elf.Phdr, ph_addr);
14
15 var maybe_dynv: ?&usize = null;
16 var base: usize = @maxValue(usize);
17 {
18 var i: usize = 0;
19 while (i < eh.e_phnum) : ({i += 1; ph_addr += eh.e_phentsize;}) {
20 const this_ph = @intToPtr(&elf.Phdr, ph_addr);
21 switch (this_ph.p_type) {
22 elf.PT_LOAD => base = vdso_addr + this_ph.p_offset - this_ph.p_vaddr,
23 elf.PT_DYNAMIC => maybe_dynv = @intToPtr(&usize, vdso_addr + this_ph.p_offset),
24 else => {},
25 }
26 }
27 }
28 const dynv = maybe_dynv ?? return 0;
29 if (base == @maxValue(usize)) return 0;
30
31 var maybe_strings: ?&u8 = null;
32 var maybe_syms: ?&elf.Sym = null;
33 var maybe_hashtab: ?&linux.Elf_Symndx = null;
34 var maybe_versym: ?&u16 = null;
35 var maybe_verdef: ?&elf.Verdef = null;
36
37 {
38 var i: usize = 0;
39 while (dynv[i] != 0) : (i += 2) {
40 const p = base + dynv[i + 1];
41 switch (dynv[i]) {
42 elf.DT_STRTAB => maybe_strings = @intToPtr(&u8, p),
43 elf.DT_SYMTAB => maybe_syms = @intToPtr(&elf.Sym, p),
44 elf.DT_HASH => maybe_hashtab = @intToPtr(&linux.Elf_Symndx, p),
45 elf.DT_VERSYM => maybe_versym = @intToPtr(&u16, p),
46 elf.DT_VERDEF => maybe_verdef = @intToPtr(&elf.Verdef, p),
47 else => {},
48 }
49 }
50 }
51
52 const strings = maybe_strings ?? return 0;
53 const syms = maybe_syms ?? return 0;
54 const hashtab = maybe_hashtab ?? return 0;
55 if (maybe_verdef == null) maybe_versym = null;
56
57
58 const OK_TYPES = (1<<elf.STT_NOTYPE | 1<<elf.STT_OBJECT | 1<<elf.STT_FUNC | 1<<elf.STT_COMMON);
59 const OK_BINDS = (1<<elf.STB_GLOBAL | 1<<elf.STB_WEAK | 1<<elf.STB_GNU_UNIQUE);
60
61 var i: usize = 0;
62 while (i < hashtab[1]) : (i += 1) {
63 if (0==(u32(1)<<u5(syms[i].st_info&0xf) & OK_TYPES)) continue;
64 if (0==(u32(1)<<u5(syms[i].st_info>>4) & OK_BINDS)) continue;
65 if (0==syms[i].st_shndx) continue;
66 if (!mem.eql(u8, name, cstr.toSliceConst(&strings[syms[i].st_name]))) continue;
67 if (maybe_versym) |versym| {
68 if (!checkver(??maybe_verdef, versym[i], vername, strings))
69 continue;
70 }
71 return base + syms[i].st_value;
72 }
73
74 return 0;
75}
76
77fn checkver(def_arg: &elf.Verdef, vsym_arg: i32, vername: []const u8, strings: &u8) bool {
78 var def = def_arg;
79 const vsym = @bitCast(u32, vsym_arg) & 0x7fff;
80 while (true) {
81 if (0==(def.vd_flags & elf.VER_FLG_BASE) and (def.vd_ndx & 0x7fff) == vsym)
82 break;
83 if (def.vd_next == 0)
84 return false;
85 def = @intToPtr(&elf.Verdef, @ptrToInt(def) + def.vd_next);
86 }
87 const aux = @intToPtr(&elf.Verdaux, @ptrToInt(def ) + def.vd_aux);
88 return mem.eql(u8, vername, cstr.toSliceConst(&strings[aux.vda_name]));
89}
std/os/linux/x86_64.zig+21
...@@ -371,6 +371,13 @@ pub const F_GETOWN_EX = 16;...@@ -371,6 +371,13 @@ pub const F_GETOWN_EX = 16;
371371
372pub const F_GETOWNER_UIDS = 17;372pub const F_GETOWNER_UIDS = 17;
373373
374
375pub const VDSO_USEFUL = true;
376pub const VDSO_CGT_SYM = "__vdso_clock_gettime";
377pub const VDSO_CGT_VER = "LINUX_2.6";
378pub const VDSO_GETCPU_SYM = "__vdso_getcpu";
379pub const VDSO_GETCPU_VER = "LINUX_2.6";
380
374pub fn syscall0(number: usize) usize {381pub fn syscall0(number: usize) usize {
375 return asm volatile ("syscall"382 return asm volatile ("syscall"
376 : [ret] "={rax}" (-> usize)383 : [ret] "={rax}" (-> usize)
...@@ -443,6 +450,9 @@ pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usiz...@@ -443,6 +450,9 @@ pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usiz
443 : "rcx", "r11");450 : "rcx", "r11");
444}451}
445452
453/// This matches the libc clone function.
454pub extern fn clone(func: extern fn(arg: usize) u8, stack: usize, flags: usize, arg: usize, ptid: &i32, tls: usize, ctid: &i32) usize;
455
446pub nakedcc fn restore_rt() void {456pub nakedcc fn restore_rt() void {
447 return asm volatile ("syscall"457 return asm volatile ("syscall"
448 :458 :
...@@ -489,6 +499,16 @@ pub const timespec = extern struct {...@@ -489,6 +499,16 @@ pub const timespec = extern struct {
489 tv_nsec: isize,499 tv_nsec: isize,
490};500};
491501
502pub const timeval = extern struct {
503 tv_sec: isize,
504 tv_usec: isize,
505};
506
507pub const timezone = extern struct {
508 tz_minuteswest: i32,
509 tz_dsttime: i32,
510};
511
492pub const dirent = extern struct {512pub const dirent = extern struct {
493 d_ino: usize,513 d_ino: usize,
494 d_off: usize,514 d_off: usize,
...@@ -496,3 +516,4 @@ pub const dirent = extern struct {...@@ -496,3 +516,4 @@ pub const dirent = extern struct {
496 d_name: u8, // field address is the address of first byte of name516 d_name: u8, // field address is the address of first byte of name
497};517};
498518
519pub const Elf_Symndx = u32;
std/os/test.zig+56
...@@ -6,6 +6,8 @@ const io = std.io;...@@ -6,6 +6,8 @@ const io = std.io;
6const a = std.debug.global_allocator;6const a = std.debug.global_allocator;
77
8const builtin = @import("builtin");8const builtin = @import("builtin");
9const AtomicRmwOp = builtin.AtomicRmwOp;
10const AtomicOrder = builtin.AtomicOrder;
911
10test "makePath, put some files in it, deleteTree" {12test "makePath, put some files in it, deleteTree" {
11 if (builtin.os == builtin.Os.windows) {13 if (builtin.os == builtin.Os.windows) {
...@@ -23,3 +25,57 @@ test "makePath, put some files in it, deleteTree" {...@@ -23,3 +25,57 @@ test "makePath, put some files in it, deleteTree" {
23 assert(err == error.PathNotFound);25 assert(err == error.PathNotFound);
24 }26 }
25}27}
28
29test "access file" {
30 if (builtin.os == builtin.Os.windows) {
31 return;
32 }
33
34 try os.makePath(a, "os_test_tmp");
35 if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
36 unreachable;
37 } else |err| {
38 assert(err == error.NotFound);
39 }
40
41 try io.writeFile(a, "os_test_tmp/file.txt", "");
42 assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true);
43 try os.deleteTree(a, "os_test_tmp");
44}
45
46test "spawn threads" {
47 if (builtin.os != builtin.Os.linux) {
48 // TODO implement threads on macos and windows
49 return;
50 }
51
52 var direct_allocator = std.heap.DirectAllocator.init();
53 defer direct_allocator.deinit();
54
55 var shared_ctx: i32 = 1;
56
57 const thread1 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, {}, start1);
58 const thread4 = try std.os.spawnThreadAllocator(&direct_allocator.allocator, &shared_ctx, start2);
59
60 var stack1: [1024]u8 = undefined;
61 var stack2: [1024]u8 = undefined;
62
63 const thread2 = try std.os.spawnThread(stack1[0..], &shared_ctx, start2);
64 const thread3 = try std.os.spawnThread(stack2[0..], &shared_ctx, start2);
65
66 thread1.wait();
67 thread2.wait();
68 thread3.wait();
69 thread4.wait();
70
71 assert(shared_ctx == 4);
72}
73
74fn start1(ctx: void) u8 {
75 return 0;
76}
77
78fn start2(ctx: &i32) u8 {
79 _ = @atomicRmw(i32, ctx, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
80 return 0;
81}
std/os/time.zig created+288
...@@ -0,0 +1,288 @@
1const std = @import("../index.zig");
2const builtin = @import("builtin");
3const Os = builtin.Os;
4const debug = std.debug;
5
6const windows = std.os.windows;
7const linux = std.os.linux;
8const darwin = std.os.darwin;
9const posix = std.os.posix;
10
11pub const epoch = @import("epoch.zig");
12
13/// Sleep for the specified duration
14pub fn sleep(seconds: usize, nanoseconds: usize) void {
15 switch (builtin.os) {
16 Os.linux, Os.macosx, Os.ios => {
17 posixSleep(u63(seconds), u63(nanoseconds));
18 },
19 Os.windows => {
20 const ns_per_ms = ns_per_s / ms_per_s;
21 const milliseconds = seconds * ms_per_s + nanoseconds / ns_per_ms;
22 windows.Sleep(windows.DWORD(milliseconds));
23 },
24 else => @compileError("Unsupported OS"),
25 }
26}
27
28const u63 = @IntType(false, 63);
29pub fn posixSleep(seconds: u63, nanoseconds: u63) void {
30 var req = posix.timespec {
31 .tv_sec = seconds,
32 .tv_nsec = nanoseconds,
33 };
34 var rem: posix.timespec = undefined;
35 while (true) {
36 const ret_val = posix.nanosleep(&req, &rem);
37 const err = posix.getErrno(ret_val);
38 if (err == 0) return;
39 switch (err) {
40 posix.EFAULT => unreachable,
41 posix.EINVAL => {
42 // Sometimes Darwin returns EINVAL for no reason.
43 // We treat it as a spurious wakeup.
44 return;
45 },
46 posix.EINTR => {
47 req = rem;
48 continue;
49 },
50 else => return,
51 }
52 }
53}
54
55/// Get the posix timestamp, UTC, in seconds
56pub fn timestamp() u64 {
57 return @divFloor(milliTimestamp(), ms_per_s);
58}
59
60/// Get the posix timestamp, UTC, in milliseconds
61pub const milliTimestamp = switch (builtin.os) {
62 Os.windows => milliTimestampWindows,
63 Os.linux => milliTimestampPosix,
64 Os.macosx, Os.ios => milliTimestampDarwin,
65 else => @compileError("Unsupported OS"),
66};
67
68fn milliTimestampWindows() u64 {
69 //FileTime has a granularity of 100 nanoseconds
70 // and uses the NTFS/Windows epoch
71 var ft: i64 = undefined;
72 windows.GetSystemTimeAsFileTime(&ft);
73 const hns_per_ms = (ns_per_s / 100) / ms_per_s;
74 const epoch_adj = epoch.windows * ms_per_s;
75 return u64(@divFloor(ft, hns_per_ms) + epoch_adj);
76}
77
78fn milliTimestampDarwin() u64 {
79 //Sources suggest MacOS 10.12 has support for
80 // posix clock_gettime.
81 var tv: darwin.timeval = undefined;
82 var err = darwin.gettimeofday(&tv, null);
83 debug.assert(err == 0);
84 const sec_ms = u64(tv.tv_sec) * ms_per_s;
85 const usec_ms = @divFloor(u64(tv.tv_usec), us_per_s / ms_per_s);
86 return u64(sec_ms) + u64(usec_ms);
87}
88
89fn milliTimestampPosix() u64 {
90 //From what I can tell there's no reason clock_gettime
91 // should ever fail for us with CLOCK_REALTIME,
92 // seccomp aside.
93 var ts: posix.timespec = undefined;
94 const err = posix.clock_gettime(posix.CLOCK_REALTIME, &ts);
95 debug.assert(err == 0);
96 const sec_ms = u64(ts.tv_sec) * ms_per_s;
97 const nsec_ms = @divFloor(u64(ts.tv_nsec), ns_per_s / ms_per_s);
98 return sec_ms + nsec_ms;
99}
100
101/// Divisions of a second
102pub const ns_per_s = 1000000000;
103pub const us_per_s = 1000000;
104pub const ms_per_s = 1000;
105pub const cs_per_s = 100;
106
107/// Common time divisions
108pub const s_per_min = 60;
109pub const s_per_hour = s_per_min * 60;
110pub const s_per_day = s_per_hour * 24;
111pub const s_per_week = s_per_day * 7;
112
113
114/// A monotonic high-performance timer.
115/// Timer.start() must be called to initialize the struct, which captures
116/// the counter frequency on windows and darwin, records the resolution,
117/// and gives the user an oportunity to check for the existnece of
118/// monotonic clocks without forcing them to check for error on each read.
119/// .resolution is in nanoseconds on all platforms but .start_time's meaning
120/// depends on the OS. On Windows and Darwin it is a hardware counter
121/// value that requires calculation to convert to a meaninful unit.
122pub const Timer = struct {
123
124 //if we used resolution's value when performing the
125 // performance counter calc on windows/darwin, it would
126 // be less precise
127 frequency: switch (builtin.os) {
128 Os.windows => u64,
129 Os.macosx, Os.ios => darwin.mach_timebase_info_data,
130 else => void,
131 },
132 resolution: u64,
133 start_time: u64,
134
135
136 //At some point we may change our minds on RAW, but for now we're
137 // sticking with posix standard MONOTONIC. For more information, see:
138 // https://github.com/zig-lang/zig/pull/933
139 //
140 //const monotonic_clock_id = switch(builtin.os) {
141 // Os.linux => linux.CLOCK_MONOTONIC_RAW,
142 // else => posix.CLOCK_MONOTONIC,
143 //};
144 const monotonic_clock_id = posix.CLOCK_MONOTONIC;
145
146
147 /// Initialize the timer structure.
148 //This gives us an oportunity to grab the counter frequency in windows.
149 //On Windows: QueryPerformanceCounter will succeed on anything >= XP/2000.
150 //On Posix: CLOCK_MONOTONIC will only fail if the monotonic counter is not
151 // supported, or if the timespec pointer is out of bounds, which should be
152 // impossible here barring cosmic rays or other such occurances of
153 // incredibly bad luck.
154 //On Darwin: This cannot fail, as far as I am able to tell.
155 const TimerError = error{TimerUnsupported, Unexpected};
156 pub fn start() TimerError!Timer {
157 var self: Timer = undefined;
158
159 switch (builtin.os) {
160 Os.windows => {
161 var freq: i64 = undefined;
162 var err = windows.QueryPerformanceFrequency(&freq);
163 if (err == windows.FALSE) return error.TimerUnsupported;
164 self.frequency = u64(freq);
165 self.resolution = @divFloor(ns_per_s, self.frequency);
166
167 var start_time: i64 = undefined;
168 err = windows.QueryPerformanceCounter(&start_time);
169 debug.assert(err != windows.FALSE);
170 self.start_time = u64(start_time);
171 },
172 Os.linux => {
173 //On Linux, seccomp can do arbitrary things to our ability to call
174 // syscalls, including return any errno value it wants and
175 // inconsistently throwing errors. Since we can't account for
176 // abuses of seccomp in a reasonable way, we'll assume that if
177 // seccomp is going to block us it will at least do so consistently
178 var ts: posix.timespec = undefined;
179 var result = posix.clock_getres(monotonic_clock_id, &ts);
180 var errno = posix.getErrno(result);
181 switch (errno) {
182 0 => {},
183 posix.EINVAL => return error.TimerUnsupported,
184 else => return std.os.unexpectedErrorPosix(errno),
185 }
186 self.resolution = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
187
188 result = posix.clock_gettime(monotonic_clock_id, &ts);
189 errno = posix.getErrno(result);
190 if (errno != 0) return std.os.unexpectedErrorPosix(errno);
191 self.start_time = u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
192 },
193 Os.macosx, Os.ios => {
194 darwin.mach_timebase_info(&self.frequency);
195 self.resolution = @divFloor(self.frequency.numer, self.frequency.denom);
196 self.start_time = darwin.mach_absolute_time();
197 },
198 else => @compileError("Unsupported OS"),
199 }
200 return self;
201 }
202
203 /// Reads the timer value since start or the last reset in nanoseconds
204 pub fn read(self: &Timer) u64 {
205 var clock = clockNative() - self.start_time;
206 return switch (builtin.os) {
207 Os.windows => @divFloor(clock * ns_per_s, self.frequency),
208 Os.linux => clock,
209 Os.macosx, Os.ios => @divFloor(clock * self.frequency.numer, self.frequency.denom),
210 else => @compileError("Unsupported OS"),
211 };
212 }
213
214 /// Resets the timer value to 0/now.
215 pub fn reset(self: &Timer) void
216 {
217 self.start_time = clockNative();
218 }
219
220 /// Returns the current value of the timer in nanoseconds, then resets it
221 pub fn lap(self: &Timer) u64 {
222 var now = clockNative();
223 var lap_time = self.read();
224 self.start_time = now;
225 return lap_time;
226 }
227
228
229 const clockNative = switch (builtin.os) {
230 Os.windows => clockWindows,
231 Os.linux => clockLinux,
232 Os.macosx, Os.ios => clockDarwin,
233 else => @compileError("Unsupported OS"),
234 };
235
236 fn clockWindows() u64 {
237 var result: i64 = undefined;
238 var err = windows.QueryPerformanceCounter(&result);
239 debug.assert(err != windows.FALSE);
240 return u64(result);
241 }
242
243 fn clockDarwin() u64 {
244 return darwin.mach_absolute_time();
245 }
246
247 fn clockLinux() u64 {
248 var ts: posix.timespec = undefined;
249 var result = posix.clock_gettime(monotonic_clock_id, &ts);
250 debug.assert(posix.getErrno(result) == 0);
251 return u64(ts.tv_sec) * u64(ns_per_s) + u64(ts.tv_nsec);
252 }
253};
254
255
256
257
258
259test "os.time.sleep" {
260 sleep(0, 1);
261}
262
263test "os.time.timestamp" {
264 const ns_per_ms = (ns_per_s / ms_per_s);
265 const margin = 50;
266
267 const time_0 = milliTimestamp();
268 sleep(0, ns_per_ms);
269 const time_1 = milliTimestamp();
270 const interval = time_1 - time_0;
271 debug.assert(interval > 0 and interval < margin);
272}
273
274test "os.time.Timer" {
275 const ns_per_ms = (ns_per_s / ms_per_s);
276 const margin = ns_per_ms * 50;
277
278 var timer = try Timer.start();
279 sleep(0, 10 * ns_per_ms);
280 const time_0 = timer.read();
281 debug.assert(time_0 > 0 and time_0 < margin);
282
283 const time_1 = timer.lap();
284 debug.assert(time_1 > time_0);
285
286 timer.reset();
287 debug.assert(timer.read() < time_1);
288}
std/os/windows/index.zig+13
...@@ -61,6 +61,8 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz...@@ -61,6 +61,8 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(hFile: HANDLE, lpsz
6161
62pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;62pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
6363
64pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?&FILETIME) void;
65
64pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;66pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
65pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;67pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
66pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void, dwBytes: SIZE_T) ?&c_void;68pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: &c_void, dwBytes: SIZE_T) ?&c_void;
...@@ -77,6 +79,12 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem...@@ -77,6 +79,12 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
7779
78pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR,80pub extern "kernel32" stdcallcc fn MoveFileExA(lpExistingFileName: LPCSTR, lpNewFileName: LPCSTR,
79 dwFlags: DWORD) BOOL;81 dwFlags: DWORD) BOOL;
82
83pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: &LARGE_INTEGER) BOOL;
84
85pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: &LARGE_INTEGER) BOOL;
86
87pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL;
8088
81pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void,89pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: &c_void,
82 in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,90 in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD,
...@@ -137,6 +145,7 @@ pub const UNICODE = false;...@@ -137,6 +145,7 @@ pub const UNICODE = false;
137pub const WCHAR = u16;145pub const WCHAR = u16;
138pub const WORD = u16;146pub const WORD = u16;
139pub const LARGE_INTEGER = i64;147pub const LARGE_INTEGER = i64;
148pub const FILETIME = i64;
140149
141pub const TRUE = 1;150pub const TRUE = 1;
142pub const FALSE = 0;151pub const FALSE = 0;
...@@ -308,3 +317,7 @@ pub const FILE_END = 2;...@@ -308,3 +317,7 @@ pub const FILE_END = 2;
308pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;317pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
309pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;318pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
310pub const HEAP_NO_SERIALIZE = 0x00000001;319pub const HEAP_NO_SERIALIZE = 0x00000001;
320
321test "import" {
322 _ = @import("util.zig");
323}
std/rand/index.zig+19-5
...@@ -19,6 +19,7 @@ const builtin = @import("builtin");...@@ -19,6 +19,7 @@ const builtin = @import("builtin");
19const assert = std.debug.assert;19const assert = std.debug.assert;
20const mem = std.mem;20const mem = std.mem;
21const math = std.math;21const math = std.math;
22const ziggurat = @import("ziggurat.zig");
2223
23// When you need fast unbiased random numbers24// When you need fast unbiased random numbers
24pub const DefaultPrng = Xoroshiro128;25pub const DefaultPrng = Xoroshiro128;
...@@ -109,15 +110,28 @@ pub const Random = struct {...@@ -109,15 +110,28 @@ pub const Random = struct {
109 }110 }
110 }111 }
111112
112 /// Return a floating point value normally distributed in the range [0, 1].113 /// Return a floating point value normally distributed with mean = 0, stddev = 1.
114 ///
115 /// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean.
113 pub fn floatNorm(r: &Random, comptime T: type) T {116 pub fn floatNorm(r: &Random, comptime T: type) T {
114 // TODO(tiehuis): See https://www.doornik.com/research/ziggurat.pdf117 const value = ziggurat.next_f64(r, ziggurat.NormDist);
115 @compileError("floatNorm is unimplemented");118 switch (T) {
119 f32 => return f32(value),
120 f64 => return value,
121 else => @compileError("unknown floating point type"),
122 }
116 }123 }
117124
118 /// Return a exponentially distributed float between (0, @maxValue(f64))125 /// Return an exponentially distributed float with a rate parameter of 1.
126 ///
127 /// To use a different rate parameter, use: floatExp(...) / desiredRate.
119 pub fn floatExp(r: &Random, comptime T: type) T {128 pub fn floatExp(r: &Random, comptime T: type) T {
120 @compileError("floatExp is unimplemented");129 const value = ziggurat.next_f64(r, ziggurat.ExpDist);
130 switch (T) {
131 f32 => return f32(value),
132 f64 => return value,
133 else => @compileError("unknown floating point type"),
134 }
121 }135 }
122136
123 /// Shuffle a slice into a random order.137 /// Shuffle a slice into a random order.
std/rand/ziggurat.zig created+146
...@@ -0,0 +1,146 @@
1// Implements ZIGNOR [1].
2//
3// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to Generate Normal Random Samples*]
4// (https://www.doornik.com/research/ziggurat.pdf). Nuffield College, Oxford.
5//
6// rust/rand used as a reference;
7//
8// NOTE: This seems interesting but reference code is a bit hard to grok:
9// https://sbarral.github.io/etf.
10
11const std = @import("../index.zig");
12const math = std.math;
13const Random = std.rand.Random;
14
15pub fn next_f64(random: &Random, comptime tables: &const ZigTable) f64 {
16 while (true) {
17 // We manually construct a float from parts as we can avoid an extra random lookup here by
18 // using the unused exponent for the lookup table entry.
19 const bits = random.scalar(u64);
20 const i = usize(bits & 0xff);
21
22 const u = blk: {
23 if (tables.is_symmetric) {
24 // Generate a value in the range [2, 4) and scale into [-1, 1)
25 const repr = ((0x3ff + 1) << 52) | (bits >> 12);
26 break :blk @bitCast(f64, repr) - 3.0;
27 } else {
28 // Generate a value in the range [1, 2) and scale into (0, 1)
29 const repr = (0x3ff << 52) | (bits >> 12);
30 break :blk @bitCast(f64, repr) - (1.0 - math.f64_epsilon / 2.0);
31 }
32 };
33
34 const x = u * tables.x[i];
35 const test_x = if (tables.is_symmetric) math.fabs(x) else x;
36
37 // equivalent to |u| < tables.x[i+1] / tables.x[i] (or u < tables.x[i+1] / tables.x[i])
38 if (test_x < tables.x[i + 1]) {
39 return x;
40 }
41
42 if (i == 0) {
43 return tables.zero_case(random, u);
44 }
45
46 // equivalent to f1 + DRanU() * (f0 - f1) < 1
47 if (tables.f[i + 1] + (tables.f[i] - tables.f[i + 1]) * random.float(f64) < tables.pdf(x)) {
48 return x;
49 }
50 }
51}
52
53pub const ZigTable = struct {
54 r: f64,
55 x: [257]f64,
56 f: [257]f64,
57
58 // probability density function used as a fallback
59 pdf: fn(f64) f64,
60 // whether the distribution is symmetric
61 is_symmetric: bool,
62 // fallback calculation in the case we are in the 0 block
63 zero_case: fn(&Random, f64) f64,
64};
65
66// zigNorInit
67fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64,
68 comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable {
69 var tables: ZigTable = undefined;
70
71 tables.is_symmetric = is_symmetric;
72 tables.r = r;
73 tables.pdf = f;
74 tables.zero_case = zero_case;
75
76 tables.x[0] = v / f(r);
77 tables.x[1] = r;
78
79 for (tables.x[2..256]) |*entry, i| {
80 const last = tables.x[2 + i - 1];
81 *entry = f_inv(v / last + f(last));
82 }
83 tables.x[256] = 0;
84
85 for (tables.f[0..]) |*entry, i| {
86 *entry = f(tables.x[i]);
87 }
88
89 return tables;
90}
91
92// N(0, 1)
93pub const NormDist = blk: {
94 @setEvalBranchQuota(30000);
95 break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case);
96};
97
98const norm_r = 3.6541528853610088;
99const norm_v = 0.00492867323399;
100
101fn norm_f(x: f64) f64 { return math.exp(-x * x / 2.0); }
102fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); }
103fn norm_zero_case(random: &Random, u: f64) f64 {
104 var x: f64 = 1;
105 var y: f64 = 0;
106
107 while (-2.0 * y < x * x) {
108 x = math.ln(random.float(f64)) / norm_r;
109 y = math.ln(random.float(f64));
110 }
111
112 if (u < 0) {
113 return x - norm_r;
114 } else {
115 return norm_r - x;
116 }
117}
118
119test "ziggurant normal dist sanity" {
120 var prng = std.rand.DefaultPrng.init(0);
121 var i: usize = 0;
122 while (i < 1000) : (i += 1) {
123 _ = prng.random.floatNorm(f64);
124 }
125}
126
127// Exp(1)
128pub const ExpDist = blk: {
129 @setEvalBranchQuota(30000);
130 break :blk ZigTableGen(false, exp_r, exp_v, exp_f, exp_f_inv, exp_zero_case);
131};
132
133const exp_r = 7.69711747013104972;
134const exp_v = 0.0039496598225815571993;
135
136fn exp_f(x: f64) f64 { return math.exp(-x); }
137fn exp_f_inv(y: f64) f64 { return -math.ln(y); }
138fn exp_zero_case(random: &Random, _: f64) f64 { return exp_r - math.ln(random.float(f64)); }
139
140test "ziggurant exp dist sanity" {
141 var prng = std.rand.DefaultPrng.init(0);
142 var i: usize = 0;
143 while (i < 1000) : (i += 1) {
144 _ = prng.random.floatExp(f64);
145 }
146}
std/special/bootstrap.zig+19-8
...@@ -48,22 +48,33 @@ extern fn WinMainCRTStartup() noreturn {...@@ -48,22 +48,33 @@ extern fn WinMainCRTStartup() noreturn {
48fn posixCallMainAndExit() noreturn {48fn posixCallMainAndExit() noreturn {
49 const argc = *argc_ptr;49 const argc = *argc_ptr;
50 const argv = @ptrCast(&&u8, &argc_ptr[1]);50 const argv = @ptrCast(&&u8, &argc_ptr[1]);
51 const envp = @ptrCast(&?&u8, &argv[argc + 1]);51 const envp_nullable = @ptrCast(&?&u8, &argv[argc + 1]);
52 var envp_count: usize = 0;
53 while (envp_nullable[envp_count]) |_| : (envp_count += 1) {}
54 const envp = @ptrCast(&&u8, envp_nullable)[0..envp_count];
55 if (builtin.os == builtin.Os.linux) {
56 const auxv = &@ptrCast(&usize, envp.ptr)[envp_count + 1];
57 var i: usize = 0;
58 while (auxv[i] != 0) : (i += 2) {
59 if (auxv[i] < std.os.linux_aux_raw.len) std.os.linux_aux_raw[auxv[i]] = auxv[i+1];
60 }
61 std.debug.assert(std.os.linux_aux_raw[std.elf.AT_PAGESZ] == std.os.page_size);
62 }
63
52 std.os.posix.exit(callMainWithArgs(argc, argv, envp));64 std.os.posix.exit(callMainWithArgs(argc, argv, envp));
53}65}
5466
55fn callMainWithArgs(argc: usize, argv: &&u8, envp: &?&u8) u8 {67fn callMainWithArgs(argc: usize, argv: &&u8, envp: []&u8) u8 {
56 std.os.ArgIteratorPosix.raw = argv[0..argc];68 std.os.ArgIteratorPosix.raw = argv[0..argc];
5769 std.os.posix_environ_raw = envp;
58 var env_count: usize = 0;
59 while (envp[env_count] != null) : (env_count += 1) {}
60 std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count];
61
62 return callMain();70 return callMain();
63}71}
6472
65extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) i32 {73extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) i32 {
66 return callMainWithArgs(usize(c_argc), c_argv, c_envp);74 var env_count: usize = 0;
75 while (c_envp[env_count] != null) : (env_count += 1) {}
76 const envp = @ptrCast(&&u8, c_envp)[0..env_count];
77 return callMainWithArgs(usize(c_argc), c_argv, envp);
67}78}
6879
69fn callMain() u8 {80fn callMain() u8 {
std/special/builtin.zig+247-1
...@@ -54,14 +54,51 @@ export fn memmove(dest: ?&u8, src: ?&const u8, n: usize) ?&u8 {...@@ -54,14 +54,51 @@ export fn memmove(dest: ?&u8, src: ?&const u8, n: usize) ?&u8 {
54}54}
5555
56comptime {56comptime {
57 if (builtin.mode != builtin.Mode.ReleaseFast and builtin.os != builtin.Os.windows) {57 if (builtin.mode != builtin.Mode.ReleaseFast and
58 builtin.mode != builtin.Mode.ReleaseSmall and
59 builtin.os != builtin.Os.windows) {
58 @export("__stack_chk_fail", __stack_chk_fail, builtin.GlobalLinkage.Strong);60 @export("__stack_chk_fail", __stack_chk_fail, builtin.GlobalLinkage.Strong);
59 }61 }
62 if (builtin.os == builtin.Os.linux and builtin.arch == builtin.Arch.x86_64) {
63 @export("clone", clone, builtin.GlobalLinkage.Strong);
64 }
60}65}
61extern fn __stack_chk_fail() noreturn {66extern fn __stack_chk_fail() noreturn {
62 @panic("stack smashing detected");67 @panic("stack smashing detected");
63}68}
6469
70// TODO we should be able to put this directly in std/linux/x86_64.zig but
71// it causes a segfault in release mode. this is a workaround of calling it
72// across .o file boundaries. fix comptime @ptrCast of nakedcc functions.
73nakedcc fn clone() void {
74 asm volatile (
75 \\ xor %%eax,%%eax
76 \\ mov $56,%%al
77 \\ mov %%rdi,%%r11
78 \\ mov %%rdx,%%rdi
79 \\ mov %%r8,%%rdx
80 \\ mov %%r9,%%r8
81 \\ mov 8(%%rsp),%%r10
82 \\ mov %%r11,%%r9
83 \\ and $-16,%%rsi
84 \\ sub $8,%%rsi
85 \\ mov %%rcx,(%%rsi)
86 \\ syscall
87 \\ test %%eax,%%eax
88 \\ jnz 1f
89 \\ xor %%ebp,%%ebp
90 \\ pop %%rdi
91 \\ call *%%r9
92 \\ mov %%eax,%%edi
93 \\ xor %%eax,%%eax
94 \\ mov $60,%%al
95 \\ syscall
96 \\ hlt
97 \\1: ret
98 \\
99 );
100}
101
65const math = @import("../math/index.zig");102const math = @import("../math/index.zig");
66103
67export fn fmodf(x: f32, y: f32) f32 { return generic_fmod(f32, x, y); }104export fn fmodf(x: f32, y: f32) f32 { return generic_fmod(f32, x, y); }
...@@ -159,3 +196,212 @@ fn isNan(comptime T: type, bits: T) bool {...@@ -159,3 +196,212 @@ fn isNan(comptime T: type, bits: T) bool {
159 unreachable;196 unreachable;
160 }197 }
161}198}
199
200// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
201// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
202// potentially some edge cases remaining that are not handled in the same way.
203export fn sqrt(x: f64) f64 {
204 const tiny: f64 = 1.0e-300;
205 const sign: u32 = 0x80000000;
206 const u = @bitCast(u64, x);
207
208 var ix0 = u32(u >> 32);
209 var ix1 = u32(u & 0xFFFFFFFF);
210
211 // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
212 if (ix0 & 0x7FF00000 == 0x7FF00000) {
213 return x * x + x;
214 }
215
216 // sqrt(+-0) = +-0
217 if (x == 0.0) {
218 return x;
219 }
220 // sqrt(-ve) = snan
221 if (ix0 & sign != 0) {
222 return math.snan(f64);
223 }
224
225 // normalize x
226 var m = i32(ix0 >> 20);
227 if (m == 0) {
228 // subnormal
229 while (ix0 == 0) {
230 m -= 21;
231 ix0 |= ix1 >> 11;
232 ix1 <<= 21;
233 }
234
235 // subnormal
236 var i: u32 = 0;
237 while (ix0 & 0x00100000 == 0) : (i += 1) {
238 ix0 <<= 1;
239 }
240 m -= i32(i) - 1;
241 ix0 |= ix1 >> u5(32 - i);
242 ix1 <<= u5(i);
243 }
244
245 // unbias exponent
246 m -= 1023;
247 ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
248 if (m & 1 != 0) {
249 ix0 += ix0 + (ix1 >> 31);
250 ix1 = ix1 +% ix1;
251 }
252 m >>= 1;
253
254 // sqrt(x) bit by bit
255 ix0 += ix0 + (ix1 >> 31);
256 ix1 = ix1 +% ix1;
257
258 var q: u32 = 0;
259 var q1: u32 = 0;
260 var s0: u32 = 0;
261 var s1: u32 = 0;
262 var r: u32 = 0x00200000;
263 var t: u32 = undefined;
264 var t1: u32 = undefined;
265
266 while (r != 0) {
267 t = s0 +% r;
268 if (t <= ix0) {
269 s0 = t + r;
270 ix0 -= t;
271 q += r;
272 }
273 ix0 = ix0 +% ix0 +% (ix1 >> 31);
274 ix1 = ix1 +% ix1;
275 r >>= 1;
276 }
277
278 r = sign;
279 while (r != 0) {
280 t = s1 +% r;
281 t = s0;
282 if (t < ix0 or (t == ix0 and t1 <= ix1)) {
283 s1 = t1 +% r;
284 if (t1 & sign == sign and s1 & sign == 0) {
285 s0 += 1;
286 }
287 ix0 -= t;
288 if (ix1 < t1) {
289 ix0 -= 1;
290 }
291 ix1 = ix1 -% t1;
292 q1 += r;
293 }
294 ix0 = ix0 +% ix0 +% (ix1 >> 31);
295 ix1 = ix1 +% ix1;
296 r >>= 1;
297 }
298
299 // rounding direction
300 if (ix0 | ix1 != 0) {
301 var z = 1.0 - tiny; // raise inexact
302 if (z >= 1.0) {
303 z = 1.0 + tiny;
304 if (q1 == 0xFFFFFFFF) {
305 q1 = 0;
306 q += 1;
307 } else if (z > 1.0) {
308 if (q1 == 0xFFFFFFFE) {
309 q += 1;
310 }
311 q1 += 2;
312 } else {
313 q1 += q1 & 1;
314 }
315 }
316 }
317
318 ix0 = (q >> 1) + 0x3FE00000;
319 ix1 = q1 >> 1;
320 if (q & 1 != 0) {
321 ix1 |= 0x80000000;
322 }
323
324 // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
325 // behaviour at least.
326 var iix0 = i32(ix0);
327 iix0 = iix0 +% (m << 20);
328
329 const uz = (u64(iix0) << 32) | ix1;
330 return @bitCast(f64, uz);
331}
332
333export fn sqrtf(x: f32) f32 {
334 const tiny: f32 = 1.0e-30;
335 const sign: i32 = @bitCast(i32, u32(0x80000000));
336 var ix: i32 = @bitCast(i32, x);
337
338 if ((ix & 0x7F800000) == 0x7F800000) {
339 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
340 }
341
342 // zero
343 if (ix <= 0) {
344 if (ix & ~sign == 0) {
345 return x; // sqrt (+-0) = +-0
346 }
347 if (ix < 0) {
348 return math.snan(f32);
349 }
350 }
351
352 // normalize
353 var m = ix >> 23;
354 if (m == 0) {
355 // subnormal
356 var i: i32 = 0;
357 while (ix & 0x00800000 == 0) : (i += 1) {
358 ix <<= 1;
359 }
360 m -= i - 1;
361 }
362
363 m -= 127; // unbias exponent
364 ix = (ix & 0x007FFFFF) | 0x00800000;
365
366 if (m & 1 != 0) { // odd m, double x to even
367 ix += ix;
368 }
369
370 m >>= 1; // m = [m / 2]
371
372 // sqrt(x) bit by bit
373 ix += ix;
374 var q: i32 = 0; // q = sqrt(x)
375 var s: i32 = 0;
376 var r: i32 = 0x01000000; // r = moving bit right -> left
377
378 while (r != 0) {
379 const t = s + r;
380 if (t <= ix) {
381 s = t + r;
382 ix -= t;
383 q += r;
384 }
385 ix += ix;
386 r >>= 1;
387 }
388
389 // floating add to find rounding direction
390 if (ix != 0) {
391 var z = 1.0 - tiny; // inexact
392 if (z >= 1.0) {
393 z = 1.0 + tiny;
394 if (z > 1.0) {
395 q += 2;
396 } else {
397 if (q & 1 != 0) {
398 q += 1;
399 }
400 }
401 }
402 }
403
404 ix = (q >> 1) + 0x3f000000;
405 ix += m << 23;
406 return @bitCast(f32, ix);
407}
std/zig/ast.zig+1315-1364
...@@ -6,7 +6,6 @@ const mem = std.mem;...@@ -6,7 +6,6 @@ const mem = std.mem;
66
7pub const Node = struct {7pub const Node = struct {
8 id: Id,8 id: Id,
9 comment: ?&NodeLineComment,
109
11 pub const Id = enum {10 pub const Id = enum {
12 // Top level11 // Top level
...@@ -74,1734 +73,1686 @@ pub const Node = struct {...@@ -74,1734 +73,1686 @@ pub const Node = struct {
74 FieldInitializer,73 FieldInitializer,
75 };74 };
7675
77 const IdTypePair = struct {
78 id: Id,
79 Type: type,
80 };
81
82 // TODO: When @field exists, we could generate this by iterating over all members of `Id`,
83 // and making an array of `IdTypePair { .id = @field(Id, @memberName(Id, i)), .Type = @field(ast, "Node" ++ @memberName(Id, i)) }`
84 const idTypeTable = []IdTypePair {
85 IdTypePair { .id = Id.Root, .Type = NodeRoot },
86 IdTypePair { .id = Id.Use, .Type = NodeUse },
87 IdTypePair { .id = Id.TestDecl, .Type = NodeTestDecl },
88
89 IdTypePair { .id = Id.VarDecl, .Type = NodeVarDecl },
90 IdTypePair { .id = Id.Defer, .Type = NodeDefer },
91
92 IdTypePair { .id = Id.InfixOp, .Type = NodeInfixOp },
93 IdTypePair { .id = Id.PrefixOp, .Type = NodePrefixOp },
94 IdTypePair { .id = Id.SuffixOp, .Type = NodeSuffixOp },
95
96 IdTypePair { .id = Id.Switch, .Type = NodeSwitch },
97 IdTypePair { .id = Id.While, .Type = NodeWhile },
98 IdTypePair { .id = Id.For, .Type = NodeFor },
99 IdTypePair { .id = Id.If, .Type = NodeIf },
100 IdTypePair { .id = Id.ControlFlowExpression, .Type = NodeControlFlowExpression },
101 IdTypePair { .id = Id.Suspend, .Type = NodeSuspend },
102
103 IdTypePair { .id = Id.VarType, .Type = NodeVarType },
104 IdTypePair { .id = Id.ErrorType, .Type = NodeErrorType },
105 IdTypePair { .id = Id.FnProto, .Type = NodeFnProto },
106
107 IdTypePair { .id = Id.IntegerLiteral, .Type = NodeIntegerLiteral },
108 IdTypePair { .id = Id.FloatLiteral, .Type = NodeFloatLiteral },
109 IdTypePair { .id = Id.StringLiteral, .Type = NodeStringLiteral },
110 IdTypePair { .id = Id.MultilineStringLiteral, .Type = NodeMultilineStringLiteral },
111 IdTypePair { .id = Id.CharLiteral, .Type = NodeCharLiteral },
112 IdTypePair { .id = Id.BoolLiteral, .Type = NodeBoolLiteral },
113 IdTypePair { .id = Id.NullLiteral, .Type = NodeNullLiteral },
114 IdTypePair { .id = Id.UndefinedLiteral, .Type = NodeUndefinedLiteral },
115 IdTypePair { .id = Id.ThisLiteral, .Type = NodeThisLiteral },
116 IdTypePair { .id = Id.Unreachable, .Type = NodeUnreachable },
117 IdTypePair { .id = Id.Identifier, .Type = NodeIdentifier },
118 IdTypePair { .id = Id.GroupedExpression, .Type = NodeGroupedExpression },
119 IdTypePair { .id = Id.BuiltinCall, .Type = NodeBuiltinCall },
120 IdTypePair { .id = Id.ErrorSetDecl, .Type = NodeErrorSetDecl },
121 IdTypePair { .id = Id.ContainerDecl, .Type = NodeContainerDecl },
122 IdTypePair { .id = Id.Asm, .Type = NodeAsm },
123 IdTypePair { .id = Id.Comptime, .Type = NodeComptime },
124 IdTypePair { .id = Id.Block, .Type = NodeBlock },
125
126 IdTypePair { .id = Id.LineComment, .Type = NodeLineComment },
127 IdTypePair { .id = Id.SwitchCase, .Type = NodeSwitchCase },
128 IdTypePair { .id = Id.SwitchElse, .Type = NodeSwitchElse },
129 IdTypePair { .id = Id.Else, .Type = NodeElse },
130 IdTypePair { .id = Id.Payload, .Type = NodePayload },
131 IdTypePair { .id = Id.PointerPayload, .Type = NodePointerPayload },
132 IdTypePair { .id = Id.PointerIndexPayload, .Type = NodePointerIndexPayload },
133 IdTypePair { .id = Id.StructField, .Type = NodeStructField },
134 IdTypePair { .id = Id.UnionTag, .Type = NodeUnionTag },
135 IdTypePair { .id = Id.EnumTag, .Type = NodeEnumTag },
136 IdTypePair { .id = Id.AsmInput, .Type = NodeAsmInput },
137 IdTypePair { .id = Id.AsmOutput, .Type = NodeAsmOutput },
138 IdTypePair { .id = Id.AsyncAttribute, .Type = NodeAsyncAttribute },
139 IdTypePair { .id = Id.ParamDecl, .Type = NodeParamDecl },
140 IdTypePair { .id = Id.FieldInitializer, .Type = NodeFieldInitializer },
141 };
142
143 pub fn IdToType(comptime id: Id) type {
144 inline for (idTypeTable) |id_type_pair| {
145 if (id == id_type_pair.id)
146 return id_type_pair.Type;
147 }
148
149 unreachable;
150 }
151
152 pub fn typeToId(comptime T: type) Id {
153 inline for (idTypeTable) |id_type_pair| {
154 if (T == id_type_pair.Type)
155 return id_type_pair.id;
156 }
157
158 unreachable;
159 }
160
161 pub fn iterate(base: &Node, index: usize) ?&Node {76 pub fn iterate(base: &Node, index: usize) ?&Node {
162 inline for (idTypeTable) |id_type_pair| {77 comptime var i = 0;
163 if (base.id == id_type_pair.id)78 inline while (i < @memberCount(Id)) : (i += 1) {
164 return @fieldParentPtr(id_type_pair.Type, "base", base).iterate(index);79 if (base.id == @field(Id, @memberName(Id, i))) {
80 const T = @field(Node, @memberName(Id, i));
81 return @fieldParentPtr(T, "base", base).iterate(index);
82 }
165 }83 }
166
167 unreachable;84 unreachable;
168 }85 }
16986
170 pub fn firstToken(base: &Node) Token {87 pub fn firstToken(base: &Node) Token {
171 inline for (idTypeTable) |id_type_pair| {88 comptime var i = 0;
172 if (base.id == id_type_pair.id)89 inline while (i < @memberCount(Id)) : (i += 1) {
173 return @fieldParentPtr(id_type_pair.Type, "base", base).firstToken();90 if (base.id == @field(Id, @memberName(Id, i))) {
91 const T = @field(Node, @memberName(Id, i));
92 return @fieldParentPtr(T, "base", base).firstToken();
93 }
174 }94 }
175
176 unreachable;95 unreachable;
177 }96 }
17897
179 pub fn lastToken(base: &Node) Token {98 pub fn lastToken(base: &Node) Token {
180 inline for (idTypeTable) |id_type_pair| {99 comptime var i = 0;
181 if (base.id == id_type_pair.id)100 inline while (i < @memberCount(Id)) : (i += 1) {
182 return @fieldParentPtr(id_type_pair.Type, "base", base).lastToken();101 if (base.id == @field(Id, @memberName(Id, i))) {
102 const T = @field(Node, @memberName(Id, i));
103 return @fieldParentPtr(T, "base", base).lastToken();
104 }
183 }105 }
184
185 unreachable;106 unreachable;
186 }107 }
187};
188108
189pub const NodeRoot = struct {109 pub fn typeToId(comptime T: type) Id {
190 base: Node,110 comptime var i = 0;
191 decls: ArrayList(&Node),111 inline while (i < @memberCount(Id)) : (i += 1) {
192 eof_token: Token,112 if (T == @field(Node, @memberName(Id, i))) {
193113 return @field(Id, @memberName(Id, i));
194 pub fn iterate(self: &NodeRoot, index: usize) ?&Node {114 }
195 if (index < self.decls.len) {
196 return self.decls.items[self.decls.len - index - 1];
197 }115 }
198 return null;116 unreachable;
199 }
200
201 pub fn firstToken(self: &NodeRoot) Token {
202 return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken();
203 }117 }
204118
205 pub fn lastToken(self: &NodeRoot) Token {119 pub const Root = struct {
206 return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken();120 base: Node,
207 }121 decls: ArrayList(&Node),
208};122 eof_token: Token,
209123
210pub const NodeVarDecl = struct {124 pub fn iterate(self: &Root, index: usize) ?&Node {
211 base: Node,125 if (index < self.decls.len) {
212 visib_token: ?Token,126 return self.decls.items[self.decls.len - index - 1];
213 name_token: Token,127 }
214 eq_token: Token,128 return null;
215 mut_token: Token,
216 comptime_token: ?Token,
217 extern_export_token: ?Token,
218 lib_name: ?&Node,
219 type_node: ?&Node,
220 align_node: ?&Node,
221 init_node: ?&Node,
222 semicolon_token: Token,
223
224 pub fn iterate(self: &NodeVarDecl, index: usize) ?&Node {
225 var i = index;
226
227 if (self.type_node) |type_node| {
228 if (i < 1) return type_node;
229 i -= 1;
230 }129 }
231130
232 if (self.align_node) |align_node| {131 pub fn firstToken(self: &Root) Token {
233 if (i < 1) return align_node;132 return if (self.decls.len == 0) self.eof_token else self.decls.at(0).firstToken();
234 i -= 1;
235 }133 }
236134
237 if (self.init_node) |init_node| {135 pub fn lastToken(self: &Root) Token {
238 if (i < 1) return init_node;136 return if (self.decls.len == 0) self.eof_token else self.decls.at(self.decls.len - 1).lastToken();
239 i -= 1;
240 }137 }
138 };
241139
242 return null;140 pub const VarDecl = struct {
243 }141 base: Node,
244142 comments: ?&LineComment,
245 pub fn firstToken(self: &NodeVarDecl) Token {143 visib_token: ?Token,
246 if (self.visib_token) |visib_token| return visib_token;144 name_token: Token,
247 if (self.comptime_token) |comptime_token| return comptime_token;145 eq_token: Token,
248 if (self.extern_export_token) |extern_export_token| return extern_export_token;146 mut_token: Token,
249 assert(self.lib_name == null);147 comptime_token: ?Token,
250 return self.mut_token;148 extern_export_token: ?Token,
251 }149 lib_name: ?&Node,
252150 type_node: ?&Node,
253 pub fn lastToken(self: &NodeVarDecl) Token {151 align_node: ?&Node,
254 return self.semicolon_token;152 init_node: ?&Node,
255 }153 semicolon_token: Token,
256};154
257155 pub fn iterate(self: &VarDecl, index: usize) ?&Node {
258pub const NodeUse = struct {156 var i = index;
259 base: Node,157
260 visib_token: ?Token,158 if (self.type_node) |type_node| {
261 expr: &Node,159 if (i < 1) return type_node;
262 semicolon_token: Token,160 i -= 1;
263161 }
264 pub fn iterate(self: &NodeUse, index: usize) ?&Node {
265 var i = index;
266162
267 if (i < 1) return self.expr;163 if (self.align_node) |align_node| {
268 i -= 1;164 if (i < 1) return align_node;
165 i -= 1;
166 }
269167
270 return null;168 if (self.init_node) |init_node| {
271 }169 if (i < 1) return init_node;
170 i -= 1;
171 }
272172
273 pub fn firstToken(self: &NodeUse) Token {173 return null;
274 if (self.visib_token) |visib_token| return visib_token;174 }
275 return self.expr.firstToken();
276 }
277175
278 pub fn lastToken(self: &NodeUse) Token {176 pub fn firstToken(self: &VarDecl) Token {
279 return self.semicolon_token;177 if (self.visib_token) |visib_token| return visib_token;
280 }178 if (self.comptime_token) |comptime_token| return comptime_token;
281};179 if (self.extern_export_token) |extern_export_token| return extern_export_token;
180 assert(self.lib_name == null);
181 return self.mut_token;
182 }
282183
283pub const NodeErrorSetDecl = struct {184 pub fn lastToken(self: &VarDecl) Token {
284 base: Node,185 return self.semicolon_token;
285 error_token: Token,186 }
286 decls: ArrayList(&NodeIdentifier),187 };
287 rbrace_token: Token,
288188
289 pub fn iterate(self: &NodeErrorSetDecl, index: usize) ?&Node {189 pub const Use = struct {
290 var i = index;190 base: Node,
191 visib_token: ?Token,
192 expr: &Node,
193 semicolon_token: Token,
291194
292 if (i < self.decls.len) return &self.decls.at(i).base;195 pub fn iterate(self: &Use, index: usize) ?&Node {
293 i -= self.decls.len;196 var i = index;
294197
295 return null;198 if (i < 1) return self.expr;
296 }199 i -= 1;
297200
298 pub fn firstToken(self: &NodeErrorSetDecl) Token {201 return null;
299 return self.error_token;202 }
300 }
301203
302 pub fn lastToken(self: &NodeErrorSetDecl) Token {204 pub fn firstToken(self: &Use) Token {
303 return self.rbrace_token;205 if (self.visib_token) |visib_token| return visib_token;
304 }206 return self.expr.firstToken();
305};207 }
306208
307pub const NodeContainerDecl = struct {209 pub fn lastToken(self: &Use) Token {
308 base: Node,210 return self.semicolon_token;
309 ltoken: Token,211 }
310 layout: Layout,
311 kind: Kind,
312 init_arg_expr: InitArg,
313 fields_and_decls: ArrayList(&Node),
314 rbrace_token: Token,
315
316 const Layout = enum {
317 Auto,
318 Extern,
319 Packed,
320 };212 };
321213
322 const Kind = enum {214 pub const ErrorSetDecl = struct {
323 Struct,215 base: Node,
324 Enum,216 error_token: Token,
325 Union,217 decls: ArrayList(&Node),
326 };218 rbrace_token: Token,
327219
328 const InitArg = union(enum) {220 pub fn iterate(self: &ErrorSetDecl, index: usize) ?&Node {
329 None,221 var i = index;
330 Enum,
331 Type: &Node,
332 };
333222
334 pub fn iterate(self: &NodeContainerDecl, index: usize) ?&Node {223 if (i < self.decls.len) return self.decls.at(i);
335 var i = index;224 i -= self.decls.len;
336225
337 switch (self.init_arg_expr) {226 return null;
338 InitArg.Type => |t| {
339 if (i < 1) return t;
340 i -= 1;
341 },
342 InitArg.None,
343 InitArg.Enum => { }
344 }227 }
345228
346 if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i);229 pub fn firstToken(self: &ErrorSetDecl) Token {
347 i -= self.fields_and_decls.len;230 return self.error_token;
231 }
348232
349 return null;233 pub fn lastToken(self: &ErrorSetDecl) Token {
350 }234 return self.rbrace_token;
235 }
236 };
351237
352 pub fn firstToken(self: &NodeContainerDecl) Token {238 pub const ContainerDecl = struct {
353 return self.ltoken;239 base: Node,
354 }240 ltoken: Token,
241 layout: Layout,
242 kind: Kind,
243 init_arg_expr: InitArg,
244 fields_and_decls: ArrayList(&Node),
245 rbrace_token: Token,
246
247 const Layout = enum {
248 Auto,
249 Extern,
250 Packed,
251 };
355252
356 pub fn lastToken(self: &NodeContainerDecl) Token {253 const Kind = enum {
357 return self.rbrace_token;254 Struct,
358 }255 Enum,
359};256 Union,
257 };
258
259 const InitArg = union(enum) {
260 None,
261 Enum,
262 Type: &Node,
263 };
360264
361pub const NodeStructField = struct {265 pub fn iterate(self: &ContainerDecl, index: usize) ?&Node {
362 base: Node,266 var i = index;
363 visib_token: ?Token,
364 name_token: Token,
365 type_expr: &Node,
366267
367 pub fn iterate(self: &NodeStructField, index: usize) ?&Node {268 switch (self.init_arg_expr) {
368 var i = index;269 InitArg.Type => |t| {
270 if (i < 1) return t;
271 i -= 1;
272 },
273 InitArg.None,
274 InitArg.Enum => { }
275 }
369276
370 if (i < 1) return self.type_expr;277 if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i);
371 i -= 1;278 i -= self.fields_and_decls.len;
372279
373 return null;280 return null;
374 }281 }
375282
376 pub fn firstToken(self: &NodeStructField) Token {283 pub fn firstToken(self: &ContainerDecl) Token {
377 if (self.visib_token) |visib_token| return visib_token;284 return self.ltoken;
378 return self.name_token;285 }
379 }
380286
381 pub fn lastToken(self: &NodeStructField) Token {287 pub fn lastToken(self: &ContainerDecl) Token {
382 return self.type_expr.lastToken();288 return self.rbrace_token;
383 }289 }
384};290 };
385291
386pub const NodeUnionTag = struct {292 pub const StructField = struct {
387 base: Node,293 base: Node,
388 name_token: Token,294 visib_token: ?Token,
389 type_expr: ?&Node,295 name_token: Token,
296 type_expr: &Node,
390297
391 pub fn iterate(self: &NodeUnionTag, index: usize) ?&Node {298 pub fn iterate(self: &StructField, index: usize) ?&Node {
392 var i = index;299 var i = index;
393300
394 if (self.type_expr) |type_expr| {301 if (i < 1) return self.type_expr;
395 if (i < 1) return type_expr;
396 i -= 1;302 i -= 1;
397 }
398303
399 return null;304 return null;
400 }305 }
401306
402 pub fn firstToken(self: &NodeUnionTag) Token {307 pub fn firstToken(self: &StructField) Token {
403 return self.name_token;308 if (self.visib_token) |visib_token| return visib_token;
404 }309 return self.name_token;
310 }
405311
406 pub fn lastToken(self: &NodeUnionTag) Token {312 pub fn lastToken(self: &StructField) Token {
407 if (self.type_expr) |type_expr| {313 return self.type_expr.lastToken();
408 return type_expr.lastToken();
409 }314 }
315 };
410316
411 return self.name_token;317 pub const UnionTag = struct {
412 }318 base: Node,
413};319 name_token: Token,
320 type_expr: ?&Node,
414321
415pub const NodeEnumTag = struct {322 pub fn iterate(self: &UnionTag, index: usize) ?&Node {
416 base: Node,323 var i = index;
417 name_token: Token,
418 value: ?&Node,
419324
420 pub fn iterate(self: &NodeEnumTag, index: usize) ?&Node {325 if (self.type_expr) |type_expr| {
421 var i = index;326 if (i < 1) return type_expr;
327 i -= 1;
328 }
422329
423 if (self.value) |value| {330 return null;
424 if (i < 1) return value;
425 i -= 1;
426 }331 }
427332
428 return null;333 pub fn firstToken(self: &UnionTag) Token {
429 }334 return self.name_token;
335 }
430336
431 pub fn firstToken(self: &NodeEnumTag) Token {337 pub fn lastToken(self: &UnionTag) Token {
432 return self.name_token;338 if (self.type_expr) |type_expr| {
433 }339 return type_expr.lastToken();
340 }
434341
435 pub fn lastToken(self: &NodeEnumTag) Token {342 return self.name_token;
436 if (self.value) |value| {
437 return value.lastToken();
438 }343 }
344 };
439345
440 return self.name_token;346 pub const EnumTag = struct {
441 }347 base: Node,
442};348 name_token: Token,
443349 value: ?&Node,
444pub const NodeIdentifier = struct {
445 base: Node,
446 token: Token,
447350
448 pub fn iterate(self: &NodeIdentifier, index: usize) ?&Node {351 pub fn iterate(self: &EnumTag, index: usize) ?&Node {
449 return null;352 var i = index;
450 }
451353
452 pub fn firstToken(self: &NodeIdentifier) Token {354 if (self.value) |value| {
453 return self.token;355 if (i < 1) return value;
454 }356 i -= 1;
357 }
455358
456 pub fn lastToken(self: &NodeIdentifier) Token {359 return null;
457 return self.token;360 }
458 }
459};
460361
461pub const NodeAsyncAttribute = struct {362 pub fn firstToken(self: &EnumTag) Token {
462 base: Node,363 return self.name_token;
463 async_token: Token,364 }
464 allocator_type: ?&Node,
465 rangle_bracket: ?Token,
466365
467 pub fn iterate(self: &NodeAsyncAttribute, index: usize) ?&Node {366 pub fn lastToken(self: &EnumTag) Token {
468 var i = index;367 if (self.value) |value| {
368 return value.lastToken();
369 }
469370
470 if (self.allocator_type) |allocator_type| {371 return self.name_token;
471 if (i < 1) return allocator_type;
472 i -= 1;
473 }372 }
373 };
474374
475 return null;375 pub const Identifier = struct {
476 }376 base: Node,
477377 token: Token,
478 pub fn firstToken(self: &NodeAsyncAttribute) Token {
479 return self.async_token;
480 }
481378
482 pub fn lastToken(self: &NodeAsyncAttribute) Token {379 pub fn iterate(self: &Identifier, index: usize) ?&Node {
483 if (self.rangle_bracket) |rangle_bracket| {380 return null;
484 return rangle_bracket;
485 }381 }
486382
487 return self.async_token;383 pub fn firstToken(self: &Identifier) Token {
488 }384 return self.token;
489};385 }
490386
491pub const NodeFnProto = struct {387 pub fn lastToken(self: &Identifier) Token {
492 base: Node,388 return self.token;
493 visib_token: ?Token,389 }
494 fn_token: Token,
495 name_token: ?Token,
496 params: ArrayList(&Node),
497 return_type: ReturnType,
498 var_args_token: ?Token,
499 extern_export_inline_token: ?Token,
500 cc_token: ?Token,
501 async_attr: ?&NodeAsyncAttribute,
502 body_node: ?&Node,
503 lib_name: ?&Node, // populated if this is an extern declaration
504 align_expr: ?&Node, // populated if align(A) is present
505
506 pub const ReturnType = union(enum) {
507 Explicit: &Node,
508 InferErrorSet: &Node,
509 };390 };
510391
511 pub fn iterate(self: &NodeFnProto, index: usize) ?&Node {392 pub const AsyncAttribute = struct {
512 var i = index;393 base: Node,
394 async_token: Token,
395 allocator_type: ?&Node,
396 rangle_bracket: ?Token,
513397
514 if (self.body_node) |body_node| {398 pub fn iterate(self: &AsyncAttribute, index: usize) ?&Node {
515 if (i < 1) return body_node;399 var i = index;
516 i -= 1;
517 }
518400
519 switch (self.return_type) {401 if (self.allocator_type) |allocator_type| {
520 // TODO allow this and next prong to share bodies since the types are the same402 if (i < 1) return allocator_type;
521 ReturnType.Explicit => |node| {
522 if (i < 1) return node;
523 i -= 1;403 i -= 1;
524 },404 }
525 ReturnType.InferErrorSet => |node| {405
526 if (i < 1) return node;406 return null;
527 i -= 1;
528 },
529 }407 }
530408
531 if (self.align_expr) |align_expr| {409 pub fn firstToken(self: &AsyncAttribute) Token {
532 if (i < 1) return align_expr;410 return self.async_token;
533 i -= 1;
534 }411 }
535412
536 if (i < self.params.len) return self.params.items[self.params.len - i - 1];413 pub fn lastToken(self: &AsyncAttribute) Token {
537 i -= self.params.len;414 if (self.rangle_bracket) |rangle_bracket| {
415 return rangle_bracket;
416 }
538417
539 if (self.lib_name) |lib_name| {418 return self.async_token;
540 if (i < 1) return lib_name;
541 i -= 1;
542 }419 }
420 };
543421
544 return null;422 pub const FnProto = struct {
545 }423 base: Node,
424 comments: ?&LineComment,
425 visib_token: ?Token,
426 fn_token: Token,
427 name_token: ?Token,
428 params: ArrayList(&Node),
429 return_type: ReturnType,
430 var_args_token: ?Token,
431 extern_export_inline_token: ?Token,
432 cc_token: ?Token,
433 async_attr: ?&AsyncAttribute,
434 body_node: ?&Node,
435 lib_name: ?&Node, // populated if this is an extern declaration
436 align_expr: ?&Node, // populated if align(A) is present
437
438 pub const ReturnType = union(enum) {
439 Explicit: &Node,
440 InferErrorSet: &Node,
441 };
546442
547 pub fn firstToken(self: &NodeFnProto) Token {443 pub fn iterate(self: &FnProto, index: usize) ?&Node {
548 if (self.visib_token) |visib_token| return visib_token;444 var i = index;
549 if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
550 assert(self.lib_name == null);
551 if (self.cc_token) |cc_token| return cc_token;
552 return self.fn_token;
553 }
554445
555 pub fn lastToken(self: &NodeFnProto) Token {446 if (self.body_node) |body_node| {
556 if (self.body_node) |body_node| return body_node.lastToken();447 if (i < 1) return body_node;
557 switch (self.return_type) {448 i -= 1;
558 // TODO allow this and next prong to share bodies since the types are the same449 }
559 ReturnType.Explicit => |node| return node.lastToken(),
560 ReturnType.InferErrorSet => |node| return node.lastToken(),
561 }
562 }
563};
564450
565pub const NodeParamDecl = struct {451 switch (self.return_type) {
566 base: Node,452 // TODO allow this and next prong to share bodies since the types are the same
567 comptime_token: ?Token,453 ReturnType.Explicit => |node| {
568 noalias_token: ?Token,454 if (i < 1) return node;
569 name_token: ?Token,455 i -= 1;
570 type_node: &Node,456 },
571 var_args_token: ?Token,457 ReturnType.InferErrorSet => |node| {
458 if (i < 1) return node;
459 i -= 1;
460 },
461 }
572462
573 pub fn iterate(self: &NodeParamDecl, index: usize) ?&Node {463 if (self.align_expr) |align_expr| {
574 var i = index;464 if (i < 1) return align_expr;
465 i -= 1;
466 }
575467
576 if (i < 1) return self.type_node;468 if (i < self.params.len) return self.params.items[self.params.len - i - 1];
577 i -= 1;469 i -= self.params.len;
578470
579 return null;471 if (self.lib_name) |lib_name| {
580 }472 if (i < 1) return lib_name;
473 i -= 1;
474 }
581475
582 pub fn firstToken(self: &NodeParamDecl) Token {476 return null;
583 if (self.comptime_token) |comptime_token| return comptime_token;477 }
584 if (self.noalias_token) |noalias_token| return noalias_token;
585 if (self.name_token) |name_token| return name_token;
586 return self.type_node.firstToken();
587 }
588478
589 pub fn lastToken(self: &NodeParamDecl) Token {479 pub fn firstToken(self: &FnProto) Token {
590 if (self.var_args_token) |var_args_token| return var_args_token;480 if (self.visib_token) |visib_token| return visib_token;
591 return self.type_node.lastToken();481 if (self.extern_export_inline_token) |extern_export_inline_token| return extern_export_inline_token;
592 }482 assert(self.lib_name == null);
593};483 if (self.cc_token) |cc_token| return cc_token;
484 return self.fn_token;
485 }
594486
595pub const NodeBlock = struct {487 pub fn lastToken(self: &FnProto) Token {
596 base: Node,488 if (self.body_node) |body_node| return body_node.lastToken();
597 label: ?Token,489 switch (self.return_type) {
598 lbrace: Token,490 // TODO allow this and next prong to share bodies since the types are the same
599 statements: ArrayList(&Node),491 ReturnType.Explicit => |node| return node.lastToken(),
600 rbrace: Token,492 ReturnType.InferErrorSet => |node| return node.lastToken(),
493 }
494 }
495 };
601496
602 pub fn iterate(self: &NodeBlock, index: usize) ?&Node {497 pub const ParamDecl = struct {
603 var i = index;498 base: Node,
499 comptime_token: ?Token,
500 noalias_token: ?Token,
501 name_token: ?Token,
502 type_node: &Node,
503 var_args_token: ?Token,
604504
605 if (i < self.statements.len) return self.statements.items[i];505 pub fn iterate(self: &ParamDecl, index: usize) ?&Node {
606 i -= self.statements.len;506 var i = index;
607507
608 return null;508 if (i < 1) return self.type_node;
609 }509 i -= 1;
610510
611 pub fn firstToken(self: &NodeBlock) Token {511 return null;
612 if (self.label) |label| {
613 return label;
614 }512 }
615513
616 return self.lbrace;514 pub fn firstToken(self: &ParamDecl) Token {
617 }515 if (self.comptime_token) |comptime_token| return comptime_token;
516 if (self.noalias_token) |noalias_token| return noalias_token;
517 if (self.name_token) |name_token| return name_token;
518 return self.type_node.firstToken();
519 }
618520
619 pub fn lastToken(self: &NodeBlock) Token {521 pub fn lastToken(self: &ParamDecl) Token {
620 return self.rbrace;522 if (self.var_args_token) |var_args_token| return var_args_token;
621 }523 return self.type_node.lastToken();
622};524 }
525 };
623526
624pub const NodeDefer = struct {527 pub const Block = struct {
625 base: Node,528 base: Node,
626 defer_token: Token,529 label: ?Token,
627 kind: Kind,530 lbrace: Token,
628 expr: &Node,531 statements: ArrayList(&Node),
532 rbrace: Token,
629533
630 const Kind = enum {534 pub fn iterate(self: &Block, index: usize) ?&Node {
631 Error,535 var i = index;
632 Unconditional,
633 };
634536
635 pub fn iterate(self: &NodeDefer, index: usize) ?&Node {537 if (i < self.statements.len) return self.statements.items[i];
636 var i = index;538 i -= self.statements.len;
637539
638 if (i < 1) return self.expr;540 return null;
639 i -= 1;541 }
640542
641 return null;543 pub fn firstToken(self: &Block) Token {
642 }544 if (self.label) |label| {
545 return label;
546 }
643547
644 pub fn firstToken(self: &NodeDefer) Token {548 return self.lbrace;
645 return self.defer_token;549 }
646 }
647550
648 pub fn lastToken(self: &NodeDefer) Token {551 pub fn lastToken(self: &Block) Token {
649 return self.expr.lastToken();552 return self.rbrace;
650 }553 }
651};554 };
652555
653pub const NodeComptime = struct {556 pub const Defer = struct {
654 base: Node,557 base: Node,
655 comptime_token: Token,558 defer_token: Token,
656 expr: &Node,559 kind: Kind,
560 expr: &Node,
657561
658 pub fn iterate(self: &NodeComptime, index: usize) ?&Node {562 const Kind = enum {
659 var i = index;563 Error,
564 Unconditional,
565 };
660566
661 if (i < 1) return self.expr;567 pub fn iterate(self: &Defer, index: usize) ?&Node {
662 i -= 1;568 var i = index;
663569
664 return null;570 if (i < 1) return self.expr;
665 }571 i -= 1;
666572
667 pub fn firstToken(self: &NodeComptime) Token {573 return null;
668 return self.comptime_token;574 }
669 }
670575
671 pub fn lastToken(self: &NodeComptime) Token {576 pub fn firstToken(self: &Defer) Token {
672 return self.expr.lastToken();577 return self.defer_token;
673 }578 }
674};
675579
676pub const NodePayload = struct {580 pub fn lastToken(self: &Defer) Token {
677 base: Node,581 return self.expr.lastToken();
678 lpipe: Token,582 }
679 error_symbol: &NodeIdentifier,583 };
680 rpipe: Token,
681584
682 pub fn iterate(self: &NodePayload, index: usize) ?&Node {585 pub const Comptime = struct {
683 var i = index;586 base: Node,
587 comptime_token: Token,
588 expr: &Node,
684589
685 if (i < 1) return &self.error_symbol.base;590 pub fn iterate(self: &Comptime, index: usize) ?&Node {
686 i -= 1;591 var i = index;
687592
688 return null;593 if (i < 1) return self.expr;
689 }594 i -= 1;
690595
691 pub fn firstToken(self: &NodePayload) Token {596 return null;
692 return self.lpipe;597 }
693 }
694598
695 pub fn lastToken(self: &NodePayload) Token {599 pub fn firstToken(self: &Comptime) Token {
696 return self.rpipe;600 return self.comptime_token;
697 }601 }
698};
699602
700pub const NodePointerPayload = struct {603 pub fn lastToken(self: &Comptime) Token {
701 base: Node,604 return self.expr.lastToken();
702 lpipe: Token,605 }
703 is_ptr: bool,606 };
704 value_symbol: &NodeIdentifier,
705 rpipe: Token,
706607
707 pub fn iterate(self: &NodePointerPayload, index: usize) ?&Node {608 pub const Payload = struct {
708 var i = index;609 base: Node,
610 lpipe: Token,
611 error_symbol: &Node,
612 rpipe: Token,
709613
710 if (i < 1) return &self.value_symbol.base;614 pub fn iterate(self: &Payload, index: usize) ?&Node {
711 i -= 1;615 var i = index;
712616
713 return null;617 if (i < 1) return self.error_symbol;
714 }618 i -= 1;
715619
716 pub fn firstToken(self: &NodePointerPayload) Token {620 return null;
717 return self.lpipe;621 }
718 }
719622
720 pub fn lastToken(self: &NodePointerPayload) Token {623 pub fn firstToken(self: &Payload) Token {
721 return self.rpipe;624 return self.lpipe;
722 }625 }
723};
724626
725pub const NodePointerIndexPayload = struct {627 pub fn lastToken(self: &Payload) Token {
726 base: Node,628 return self.rpipe;
727 lpipe: Token,629 }
728 is_ptr: bool,630 };
729 value_symbol: &NodeIdentifier,
730 index_symbol: ?&NodeIdentifier,
731 rpipe: Token,
732631
733 pub fn iterate(self: &NodePointerIndexPayload, index: usize) ?&Node {632 pub const PointerPayload = struct {
734 var i = index;633 base: Node,
634 lpipe: Token,
635 ptr_token: ?Token,
636 value_symbol: &Node,
637 rpipe: Token,
735638
736 if (i < 1) return &self.value_symbol.base;639 pub fn iterate(self: &PointerPayload, index: usize) ?&Node {
737 i -= 1;640 var i = index;
738641
739 if (self.index_symbol) |index_symbol| {642 if (i < 1) return self.value_symbol;
740 if (i < 1) return &index_symbol.base;
741 i -= 1;643 i -= 1;
742 }
743644
744 return null;645 return null;
745 }646 }
746647
747 pub fn firstToken(self: &NodePointerIndexPayload) Token {648 pub fn firstToken(self: &PointerPayload) Token {
748 return self.lpipe;649 return self.lpipe;
749 }650 }
750651
751 pub fn lastToken(self: &NodePointerIndexPayload) Token {652 pub fn lastToken(self: &PointerPayload) Token {
752 return self.rpipe;653 return self.rpipe;
753 }654 }
754};655 };
755656
756pub const NodeElse = struct {657 pub const PointerIndexPayload = struct {
757 base: Node,658 base: Node,
758 else_token: Token,659 lpipe: Token,
759 payload: ?&NodePayload,660 ptr_token: ?Token,
760 body: &Node,661 value_symbol: &Node,
662 index_symbol: ?&Node,
663 rpipe: Token,
761664
762 pub fn iterate(self: &NodeElse, index: usize) ?&Node {665 pub fn iterate(self: &PointerIndexPayload, index: usize) ?&Node {
763 var i = index;666 var i = index;
764667
765 if (self.payload) |payload| {668 if (i < 1) return self.value_symbol;
766 if (i < 1) return &payload.base;
767 i -= 1;669 i -= 1;
768 }
769670
770 if (i < 1) return self.body;671 if (self.index_symbol) |index_symbol| {
771 i -= 1;672 if (i < 1) return index_symbol;
772673 i -= 1;
773 return null;674 }
774 }
775675
776 pub fn firstToken(self: &NodeElse) Token {676 return null;
777 return self.else_token;677 }
778 }
779678
780 pub fn lastToken(self: &NodeElse) Token {679 pub fn firstToken(self: &PointerIndexPayload) Token {
781 return self.body.lastToken();680 return self.lpipe;
782 }681 }
783};
784682
785pub const NodeSwitch = struct {683 pub fn lastToken(self: &PointerIndexPayload) Token {
786 base: Node,684 return self.rpipe;
787 switch_token: Token,685 }
788 expr: &Node,686 };
789 cases: ArrayList(&NodeSwitchCase),
790 rbrace: Token,
791687
792 pub fn iterate(self: &NodeSwitch, index: usize) ?&Node {688 pub const Else = struct {
793 var i = index;689 base: Node,
690 else_token: Token,
691 payload: ?&Node,
692 body: &Node,
794693
795 if (i < 1) return self.expr;694 pub fn iterate(self: &Else, index: usize) ?&Node {
796 i -= 1;695 var i = index;
797696
798 if (i < self.cases.len) return &self.cases.at(i).base;697 if (self.payload) |payload| {
799 i -= self.cases.len;698 if (i < 1) return payload;
699 i -= 1;
700 }
800701
801 return null;702 if (i < 1) return self.body;
802 }703 i -= 1;
803704
804 pub fn firstToken(self: &NodeSwitch) Token {705 return null;
805 return self.switch_token;706 }
806 }
807707
808 pub fn lastToken(self: &NodeSwitch) Token {708 pub fn firstToken(self: &Else) Token {
809 return self.rbrace;709 return self.else_token;
810 }710 }
811};
812711
813pub const NodeSwitchCase = struct {712 pub fn lastToken(self: &Else) Token {
814 base: Node,713 return self.body.lastToken();
815 items: ArrayList(&Node),714 }
816 payload: ?&NodePointerPayload,715 };
817 expr: &Node,
818716
819 pub fn iterate(self: &NodeSwitchCase, index: usize) ?&Node {717 pub const Switch = struct {
820 var i = index;718 base: Node,
719 switch_token: Token,
720 expr: &Node,
721 cases: ArrayList(&SwitchCase),
722 rbrace: Token,
821723
822 if (i < self.items.len) return self.items.at(i);724 pub fn iterate(self: &Switch, index: usize) ?&Node {
823 i -= self.items.len;725 var i = index;
824726
825 if (self.payload) |payload| {727 if (i < 1) return self.expr;
826 if (i < 1) return &payload.base;
827 i -= 1;728 i -= 1;
828 }
829729
830 if (i < 1) return self.expr;730 if (i < self.cases.len) return &self.cases.at(i).base;
831 i -= 1;731 i -= self.cases.len;
832732
833 return null;733 return null;
834 }734 }
835735
836 pub fn firstToken(self: &NodeSwitchCase) Token {736 pub fn firstToken(self: &Switch) Token {
837 return self.items.at(0).firstToken();737 return self.switch_token;
838 }738 }
839739
840 pub fn lastToken(self: &NodeSwitchCase) Token {740 pub fn lastToken(self: &Switch) Token {
841 return self.expr.lastToken();741 return self.rbrace;
842 }742 }
843};743 };
844744
845pub const NodeSwitchElse = struct {745 pub const SwitchCase = struct {
846 base: Node,746 base: Node,
847 token: Token,747 items: ArrayList(&Node),
748 payload: ?&Node,
749 expr: &Node,
848750
849 pub fn iterate(self: &NodeSwitchElse, index: usize) ?&Node {751 pub fn iterate(self: &SwitchCase, index: usize) ?&Node {
850 return null;752 var i = index;
851 }
852753
853 pub fn firstToken(self: &NodeSwitchElse) Token {754 if (i < self.items.len) return self.items.at(i);
854 return self.token;755 i -= self.items.len;
855 }
856756
857 pub fn lastToken(self: &NodeSwitchElse) Token {757 if (self.payload) |payload| {
858 return self.token;758 if (i < 1) return payload;
859 }759 i -= 1;
860};760 }
861761
862pub const NodeWhile = struct {762 if (i < 1) return self.expr;
863 base: Node,
864 label: ?Token,
865 inline_token: ?Token,
866 while_token: Token,
867 condition: &Node,
868 payload: ?&NodePointerPayload,
869 continue_expr: ?&Node,
870 body: &Node,
871 @"else": ?&NodeElse,
872
873 pub fn iterate(self: &NodeWhile, index: usize) ?&Node {
874 var i = index;
875
876 if (i < 1) return self.condition;
877 i -= 1;
878
879 if (self.payload) |payload| {
880 if (i < 1) return &payload.base;
881 i -= 1;763 i -= 1;
882 }
883764
884 if (self.continue_expr) |continue_expr| {765 return null;
885 if (i < 1) return continue_expr;
886 i -= 1;
887 }766 }
888767
889 if (i < 1) return self.body;768 pub fn firstToken(self: &SwitchCase) Token {
890 i -= 1;769 return self.items.at(0).firstToken();
770 }
891771
892 if (self.@"else") |@"else"| {772 pub fn lastToken(self: &SwitchCase) Token {
893 if (i < 1) return &@"else".base;773 return self.expr.lastToken();
894 i -= 1;
895 }774 }
775 };
896776
897 return null;777 pub const SwitchElse = struct {
898 }778 base: Node,
779 token: Token,
899780
900 pub fn firstToken(self: &NodeWhile) Token {781 pub fn iterate(self: &SwitchElse, index: usize) ?&Node {
901 if (self.label) |label| {782 return null;
902 return label;
903 }783 }
904784
905 if (self.inline_token) |inline_token| {785 pub fn firstToken(self: &SwitchElse) Token {
906 return inline_token;786 return self.token;
907 }787 }
908788
909 return self.while_token;789 pub fn lastToken(self: &SwitchElse) Token {
910 }790 return self.token;
911
912 pub fn lastToken(self: &NodeWhile) Token {
913 if (self.@"else") |@"else"| {
914 return @"else".body.lastToken();
915 }791 }
792 };
916793
917 return self.body.lastToken();794 pub const While = struct {
918 }795 base: Node,
919};796 label: ?Token,
920797 inline_token: ?Token,
921pub const NodeFor = struct {798 while_token: Token,
922 base: Node,799 condition: &Node,
923 label: ?Token,800 payload: ?&Node,
924 inline_token: ?Token,801 continue_expr: ?&Node,
925 for_token: Token,802 body: &Node,
926 array_expr: &Node,803 @"else": ?&Else,
927 payload: ?&NodePointerIndexPayload,804
928 body: &Node,805 pub fn iterate(self: &While, index: usize) ?&Node {
929 @"else": ?&NodeElse,806 var i = index;
807
808 if (i < 1) return self.condition;
809 i -= 1;
930810
931 pub fn iterate(self: &NodeFor, index: usize) ?&Node {811 if (self.payload) |payload| {
932 var i = index;812 if (i < 1) return payload;
813 i -= 1;
814 }
933815
934 if (i < 1) return self.array_expr;816 if (self.continue_expr) |continue_expr| {
935 i -= 1;817 if (i < 1) return continue_expr;
818 i -= 1;
819 }
936820
937 if (self.payload) |payload| {821 if (i < 1) return self.body;
938 if (i < 1) return &payload.base;
939 i -= 1;822 i -= 1;
823
824 if (self.@"else") |@"else"| {
825 if (i < 1) return &@"else".base;
826 i -= 1;
827 }
828
829 return null;
940 }830 }
941831
942 if (i < 1) return self.body;832 pub fn firstToken(self: &While) Token {
943 i -= 1;833 if (self.label) |label| {
834 return label;
835 }
944836
945 if (self.@"else") |@"else"| {837 if (self.inline_token) |inline_token| {
946 if (i < 1) return &@"else".base;838 return inline_token;
947 i -= 1;839 }
840
841 return self.while_token;
948 }842 }
949843
950 return null;844 pub fn lastToken(self: &While) Token {
951 }845 if (self.@"else") |@"else"| {
846 return @"else".body.lastToken();
847 }
952848
953 pub fn firstToken(self: &NodeFor) Token {849 return self.body.lastToken();
954 if (self.label) |label| {
955 return label;
956 }850 }
851 };
852
853 pub const For = struct {
854 base: Node,
855 label: ?Token,
856 inline_token: ?Token,
857 for_token: Token,
858 array_expr: &Node,
859 payload: ?&Node,
860 body: &Node,
861 @"else": ?&Else,
862
863 pub fn iterate(self: &For, index: usize) ?&Node {
864 var i = index;
865
866 if (i < 1) return self.array_expr;
867 i -= 1;
868
869 if (self.payload) |payload| {
870 if (i < 1) return payload;
871 i -= 1;
872 }
873
874 if (i < 1) return self.body;
875 i -= 1;
876
877 if (self.@"else") |@"else"| {
878 if (i < 1) return &@"else".base;
879 i -= 1;
880 }
957881
958 if (self.inline_token) |inline_token| {882 return null;
959 return inline_token;
960 }883 }
961884
962 return self.for_token;885 pub fn firstToken(self: &For) Token {
963 }886 if (self.label) |label| {
887 return label;
888 }
889
890 if (self.inline_token) |inline_token| {
891 return inline_token;
892 }
964893
965 pub fn lastToken(self: &NodeFor) Token {894 return self.for_token;
966 if (self.@"else") |@"else"| {
967 return @"else".body.lastToken();
968 }895 }
969896
970 return self.body.lastToken();897 pub fn lastToken(self: &For) Token {
971 }898 if (self.@"else") |@"else"| {
972};899 return @"else".body.lastToken();
900 }
973901
974pub const NodeIf = struct {902 return self.body.lastToken();
975 base: Node,903 }
976 if_token: Token,904 };
977 condition: &Node,
978 payload: ?&NodePointerPayload,
979 body: &Node,
980 @"else": ?&NodeElse,
981905
982 pub fn iterate(self: &NodeIf, index: usize) ?&Node {906 pub const If = struct {
983 var i = index;907 base: Node,
908 if_token: Token,
909 condition: &Node,
910 payload: ?&Node,
911 body: &Node,
912 @"else": ?&Else,
984913
985 if (i < 1) return self.condition;914 pub fn iterate(self: &If, index: usize) ?&Node {
986 i -= 1;915 var i = index;
987916
988 if (self.payload) |payload| {917 if (i < 1) return self.condition;
989 if (i < 1) return &payload.base;
990 i -= 1;918 i -= 1;
991 }
992919
993 if (i < 1) return self.body;920 if (self.payload) |payload| {
994 i -= 1;921 if (i < 1) return payload;
922 i -= 1;
923 }
995924
996 if (self.@"else") |@"else"| {925 if (i < 1) return self.body;
997 if (i < 1) return &@"else".base;
998 i -= 1;926 i -= 1;
999 }
1000927
1001 return null;928 if (self.@"else") |@"else"| {
1002 }929 if (i < 1) return &@"else".base;
930 i -= 1;
931 }
1003932
1004 pub fn firstToken(self: &NodeIf) Token {933 return null;
1005 return self.if_token;934 }
1006 }
1007935
1008 pub fn lastToken(self: &NodeIf) Token {936 pub fn firstToken(self: &If) Token {
1009 if (self.@"else") |@"else"| {937 return self.if_token;
1010 return @"else".body.lastToken();
1011 }938 }
1012939
1013 return self.body.lastToken();940 pub fn lastToken(self: &If) Token {
1014 }941 if (self.@"else") |@"else"| {
1015};942 return @"else".body.lastToken();
943 }
1016944
1017pub const NodeInfixOp = struct {945 return self.body.lastToken();
1018 base: Node,946 }
1019 op_token: Token,
1020 lhs: &Node,
1021 op: InfixOp,
1022 rhs: &Node,
1023
1024 const InfixOp = union(enum) {
1025 Add,
1026 AddWrap,
1027 ArrayCat,
1028 ArrayMult,
1029 Assign,
1030 AssignBitAnd,
1031 AssignBitOr,
1032 AssignBitShiftLeft,
1033 AssignBitShiftRight,
1034 AssignBitXor,
1035 AssignDiv,
1036 AssignMinus,
1037 AssignMinusWrap,
1038 AssignMod,
1039 AssignPlus,
1040 AssignPlusWrap,
1041 AssignTimes,
1042 AssignTimesWarp,
1043 BangEqual,
1044 BitAnd,
1045 BitOr,
1046 BitShiftLeft,
1047 BitShiftRight,
1048 BitXor,
1049 BoolAnd,
1050 BoolOr,
1051 Catch: ?&NodePayload,
1052 Div,
1053 EqualEqual,
1054 ErrorUnion,
1055 GreaterOrEqual,
1056 GreaterThan,
1057 LessOrEqual,
1058 LessThan,
1059 MergeErrorSets,
1060 Mod,
1061 Mult,
1062 MultWrap,
1063 Period,
1064 Range,
1065 Sub,
1066 SubWrap,
1067 UnwrapMaybe,
1068 };947 };
1069948
1070 pub fn iterate(self: &NodeInfixOp, index: usize) ?&Node {949 pub const InfixOp = struct {
1071 var i = index;950 base: Node,
951 op_token: Token,
952 lhs: &Node,
953 op: Op,
954 rhs: &Node,
955
956 pub const Op = union(enum) {
957 Add,
958 AddWrap,
959 ArrayCat,
960 ArrayMult,
961 Assign,
962 AssignBitAnd,
963 AssignBitOr,
964 AssignBitShiftLeft,
965 AssignBitShiftRight,
966 AssignBitXor,
967 AssignDiv,
968 AssignMinus,
969 AssignMinusWrap,
970 AssignMod,
971 AssignPlus,
972 AssignPlusWrap,
973 AssignTimes,
974 AssignTimesWarp,
975 BangEqual,
976 BitAnd,
977 BitOr,
978 BitShiftLeft,
979 BitShiftRight,
980 BitXor,
981 BoolAnd,
982 BoolOr,
983 Catch: ?&Node,
984 Div,
985 EqualEqual,
986 ErrorUnion,
987 GreaterOrEqual,
988 GreaterThan,
989 LessOrEqual,
990 LessThan,
991 MergeErrorSets,
992 Mod,
993 Mult,
994 MultWrap,
995 Period,
996 Range,
997 Sub,
998 SubWrap,
999 UnwrapMaybe,
1000 };
10721001
1073 if (i < 1) return self.lhs;1002 pub fn iterate(self: &InfixOp, index: usize) ?&Node {
1074 i -= 1;1003 var i = index;
10751004
1076 switch (self.op) {1005 if (i < 1) return self.lhs;
1077 InfixOp.Catch => |maybe_payload| {1006 i -= 1;
1078 if (maybe_payload) |payload| {
1079 if (i < 1) return &payload.base;
1080 i -= 1;
1081 }
1082 },
1083
1084 InfixOp.Add,
1085 InfixOp.AddWrap,
1086 InfixOp.ArrayCat,
1087 InfixOp.ArrayMult,
1088 InfixOp.Assign,
1089 InfixOp.AssignBitAnd,
1090 InfixOp.AssignBitOr,
1091 InfixOp.AssignBitShiftLeft,
1092 InfixOp.AssignBitShiftRight,
1093 InfixOp.AssignBitXor,
1094 InfixOp.AssignDiv,
1095 InfixOp.AssignMinus,
1096 InfixOp.AssignMinusWrap,
1097 InfixOp.AssignMod,
1098 InfixOp.AssignPlus,
1099 InfixOp.AssignPlusWrap,
1100 InfixOp.AssignTimes,
1101 InfixOp.AssignTimesWarp,
1102 InfixOp.BangEqual,
1103 InfixOp.BitAnd,
1104 InfixOp.BitOr,
1105 InfixOp.BitShiftLeft,
1106 InfixOp.BitShiftRight,
1107 InfixOp.BitXor,
1108 InfixOp.BoolAnd,
1109 InfixOp.BoolOr,
1110 InfixOp.Div,
1111 InfixOp.EqualEqual,
1112 InfixOp.ErrorUnion,
1113 InfixOp.GreaterOrEqual,
1114 InfixOp.GreaterThan,
1115 InfixOp.LessOrEqual,
1116 InfixOp.LessThan,
1117 InfixOp.MergeErrorSets,
1118 InfixOp.Mod,
1119 InfixOp.Mult,
1120 InfixOp.MultWrap,
1121 InfixOp.Period,
1122 InfixOp.Range,
1123 InfixOp.Sub,
1124 InfixOp.SubWrap,
1125 InfixOp.UnwrapMaybe => {},
1126 }
1127
1128 if (i < 1) return self.rhs;
1129 i -= 1;
1130
1131 return null;
1132 }
11331007
1134 pub fn firstToken(self: &NodeInfixOp) Token {1008 switch (self.op) {
1135 return self.lhs.firstToken();1009 Op.Catch => |maybe_payload| {
1136 }1010 if (maybe_payload) |payload| {
1011 if (i < 1) return payload;
1012 i -= 1;
1013 }
1014 },
1015
1016 Op.Add,
1017 Op.AddWrap,
1018 Op.ArrayCat,
1019 Op.ArrayMult,
1020 Op.Assign,
1021 Op.AssignBitAnd,
1022 Op.AssignBitOr,
1023 Op.AssignBitShiftLeft,
1024 Op.AssignBitShiftRight,
1025 Op.AssignBitXor,
1026 Op.AssignDiv,
1027 Op.AssignMinus,
1028 Op.AssignMinusWrap,
1029 Op.AssignMod,
1030 Op.AssignPlus,
1031 Op.AssignPlusWrap,
1032 Op.AssignTimes,
1033 Op.AssignTimesWarp,
1034 Op.BangEqual,
1035 Op.BitAnd,
1036 Op.BitOr,
1037 Op.BitShiftLeft,
1038 Op.BitShiftRight,
1039 Op.BitXor,
1040 Op.BoolAnd,
1041 Op.BoolOr,
1042 Op.Div,
1043 Op.EqualEqual,
1044 Op.ErrorUnion,
1045 Op.GreaterOrEqual,
1046 Op.GreaterThan,
1047 Op.LessOrEqual,
1048 Op.LessThan,
1049 Op.MergeErrorSets,
1050 Op.Mod,
1051 Op.Mult,
1052 Op.MultWrap,
1053 Op.Period,
1054 Op.Range,
1055 Op.Sub,
1056 Op.SubWrap,
1057 Op.UnwrapMaybe => {},
1058 }
11371059
1138 pub fn lastToken(self: &NodeInfixOp) Token {1060 if (i < 1) return self.rhs;
1139 return self.rhs.lastToken();1061 i -= 1;
1140 }
1141};
11421062
1143pub const NodePrefixOp = struct {1063 return null;
1144 base: Node,1064 }
1145 op_token: Token,
1146 op: PrefixOp,
1147 rhs: &Node,
1148
1149 const PrefixOp = union(enum) {
1150 AddrOf: AddrOfInfo,
1151 ArrayType: &Node,
1152 Await,
1153 BitNot,
1154 BoolNot,
1155 Cancel,
1156 Deref,
1157 MaybeType,
1158 Negation,
1159 NegationWrap,
1160 Resume,
1161 SliceType: AddrOfInfo,
1162 Try,
1163 UnwrapMaybe,
1164 };
11651065
1166 const AddrOfInfo = struct {1066 pub fn firstToken(self: &InfixOp) Token {
1167 align_expr: ?&Node,1067 return self.lhs.firstToken();
1168 bit_offset_start_token: ?Token,1068 }
1169 bit_offset_end_token: ?Token,1069
1170 const_token: ?Token,1070 pub fn lastToken(self: &InfixOp) Token {
1171 volatile_token: ?Token,1071 return self.rhs.lastToken();
1072 }
1172 };1073 };
11731074
1174 pub fn iterate(self: &NodePrefixOp, index: usize) ?&Node {1075 pub const PrefixOp = struct {
1175 var i = index;1076 base: Node,
1077 op_token: Token,
1078 op: Op,
1079 rhs: &Node,
1080
1081 const Op = union(enum) {
1082 AddrOf: AddrOfInfo,
1083 ArrayType: &Node,
1084 Await,
1085 BitNot,
1086 BoolNot,
1087 Cancel,
1088 Deref,
1089 MaybeType,
1090 Negation,
1091 NegationWrap,
1092 Resume,
1093 SliceType: AddrOfInfo,
1094 Try,
1095 UnwrapMaybe,
1096 };
1097
1098 const AddrOfInfo = struct {
1099 align_expr: ?&Node,
1100 bit_offset_start_token: ?Token,
1101 bit_offset_end_token: ?Token,
1102 const_token: ?Token,
1103 volatile_token: ?Token,
1104 };
11761105
1177 switch (self.op) {1106 pub fn iterate(self: &PrefixOp, index: usize) ?&Node {
1178 PrefixOp.SliceType => |addr_of_info| {1107 var i = index;
1179 if (addr_of_info.align_expr) |align_expr| {1108
1180 if (i < 1) return align_expr;1109 switch (self.op) {
1110 Op.SliceType => |addr_of_info| {
1111 if (addr_of_info.align_expr) |align_expr| {
1112 if (i < 1) return align_expr;
1113 i -= 1;
1114 }
1115 },
1116 Op.AddrOf => |addr_of_info| {
1117 if (addr_of_info.align_expr) |align_expr| {
1118 if (i < 1) return align_expr;
1119 i -= 1;
1120 }
1121 },
1122 Op.ArrayType => |size_expr| {
1123 if (i < 1) return size_expr;
1181 i -= 1;1124 i -= 1;
1182 }1125 },
1183 },1126 Op.Await,
1184 PrefixOp.AddrOf => |addr_of_info| {1127 Op.BitNot,
1185 if (addr_of_info.align_expr) |align_expr| {1128 Op.BoolNot,
1186 if (i < 1) return align_expr;1129 Op.Cancel,
1187 i -= 1;1130 Op.Deref,
1188 }1131 Op.MaybeType,
1189 },1132 Op.Negation,
1190 PrefixOp.ArrayType => |size_expr| {1133 Op.NegationWrap,
1191 if (i < 1) return size_expr;1134 Op.Try,
1192 i -= 1;1135 Op.Resume,
1193 },1136 Op.UnwrapMaybe => {},
1194 PrefixOp.Await,1137 }
1195 PrefixOp.BitNot,
1196 PrefixOp.BoolNot,
1197 PrefixOp.Cancel,
1198 PrefixOp.Deref,
1199 PrefixOp.MaybeType,
1200 PrefixOp.Negation,
1201 PrefixOp.NegationWrap,
1202 PrefixOp.Try,
1203 PrefixOp.Resume,
1204 PrefixOp.UnwrapMaybe => {},
1205 }
1206
1207 if (i < 1) return self.rhs;
1208 i -= 1;
1209
1210 return null;
1211 }
12121138
1213 pub fn firstToken(self: &NodePrefixOp) Token {1139 if (i < 1) return self.rhs;
1214 return self.op_token;1140 i -= 1;
1215 }
12161141
1217 pub fn lastToken(self: &NodePrefixOp) Token {1142 return null;
1218 return self.rhs.lastToken();1143 }
1219 }
1220};
12211144
1222pub const NodeFieldInitializer = struct {1145 pub fn firstToken(self: &PrefixOp) Token {
1223 base: Node,1146 return self.op_token;
1224 period_token: Token,1147 }
1225 name_token: Token,
1226 expr: &Node,
12271148
1228 pub fn iterate(self: &NodeFieldInitializer, index: usize) ?&Node {1149 pub fn lastToken(self: &PrefixOp) Token {
1229 var i = index;1150 return self.rhs.lastToken();
1151 }
1152 };
12301153
1231 if (i < 1) return self.expr;1154 pub const FieldInitializer = struct {
1232 i -= 1;1155 base: Node,
1156 period_token: Token,
1157 name_token: Token,
1158 expr: &Node,
12331159
1234 return null;1160 pub fn iterate(self: &FieldInitializer, index: usize) ?&Node {
1235 }1161 var i = index;
12361162
1237 pub fn firstToken(self: &NodeFieldInitializer) Token {1163 if (i < 1) return self.expr;
1238 return self.period_token;1164 i -= 1;
1239 }
12401165
1241 pub fn lastToken(self: &NodeFieldInitializer) Token {1166 return null;
1242 return self.expr.lastToken();1167 }
1243 }
1244};
12451168
1246pub const NodeSuffixOp = struct {1169 pub fn firstToken(self: &FieldInitializer) Token {
1247 base: Node,1170 return self.period_token;
1248 lhs: &Node,1171 }
1249 op: SuffixOp,
1250 rtoken: Token,
1251
1252 const SuffixOp = union(enum) {
1253 Call: CallInfo,
1254 ArrayAccess: &Node,
1255 Slice: SliceRange,
1256 ArrayInitializer: ArrayList(&Node),
1257 StructInitializer: ArrayList(&NodeFieldInitializer),
1258 };
12591172
1260 const CallInfo = struct {1173 pub fn lastToken(self: &FieldInitializer) Token {
1261 params: ArrayList(&Node),1174 return self.expr.lastToken();
1262 async_attr: ?&NodeAsyncAttribute,1175 }
1263 };1176 };
12641177
1265 const SliceRange = struct {1178 pub const SuffixOp = struct {
1266 start: &Node,1179 base: Node,
1267 end: ?&Node,1180 lhs: &Node,
1268 };1181 op: Op,
1182 rtoken: Token,
1183
1184 const Op = union(enum) {
1185 Call: CallInfo,
1186 ArrayAccess: &Node,
1187 Slice: SliceRange,
1188 ArrayInitializer: ArrayList(&Node),
1189 StructInitializer: ArrayList(&FieldInitializer),
1190 };
1191
1192 const CallInfo = struct {
1193 params: ArrayList(&Node),
1194 async_attr: ?&AsyncAttribute,
1195 };
12691196
1270 pub fn iterate(self: &NodeSuffixOp, index: usize) ?&Node {1197 const SliceRange = struct {
1271 var i = index;1198 start: &Node,
1199 end: ?&Node,
1200 };
12721201
1273 if (i < 1) return self.lhs;1202 pub fn iterate(self: &SuffixOp, index: usize) ?&Node {
1274 i -= 1;1203 var i = index;
12751204
1276 switch (self.op) {1205 if (i < 1) return self.lhs;
1277 SuffixOp.Call => |call_info| {1206 i -= 1;
1278 if (i < call_info.params.len) return call_info.params.at(i);
1279 i -= call_info.params.len;
1280 },
1281 SuffixOp.ArrayAccess => |index_expr| {
1282 if (i < 1) return index_expr;
1283 i -= 1;
1284 },
1285 SuffixOp.Slice => |range| {
1286 if (i < 1) return range.start;
1287 i -= 1;
12881207
1289 if (range.end) |end| {1208 switch (self.op) {
1290 if (i < 1) return end;1209 Op.Call => |call_info| {
1210 if (i < call_info.params.len) return call_info.params.at(i);
1211 i -= call_info.params.len;
1212 },
1213 Op.ArrayAccess => |index_expr| {
1214 if (i < 1) return index_expr;
1215 i -= 1;
1216 },
1217 Op.Slice => |range| {
1218 if (i < 1) return range.start;
1291 i -= 1;1219 i -= 1;
1292 }
1293 },
1294 SuffixOp.ArrayInitializer => |exprs| {
1295 if (i < exprs.len) return exprs.at(i);
1296 i -= exprs.len;
1297 },
1298 SuffixOp.StructInitializer => |fields| {
1299 if (i < fields.len) return &fields.at(i).base;
1300 i -= fields.len;
1301 },
1302 }
1303
1304 return null;
1305 }
13061220
1307 pub fn firstToken(self: &NodeSuffixOp) Token {1221 if (range.end) |end| {
1308 return self.lhs.firstToken();1222 if (i < 1) return end;
1309 }1223 i -= 1;
1224 }
1225 },
1226 Op.ArrayInitializer => |exprs| {
1227 if (i < exprs.len) return exprs.at(i);
1228 i -= exprs.len;
1229 },
1230 Op.StructInitializer => |fields| {
1231 if (i < fields.len) return &fields.at(i).base;
1232 i -= fields.len;
1233 },
1234 }
13101235
1311 pub fn lastToken(self: &NodeSuffixOp) Token {1236 return null;
1312 return self.rtoken;1237 }
1313 }
1314};
13151238
1316pub const NodeGroupedExpression = struct {1239 pub fn firstToken(self: &SuffixOp) Token {
1317 base: Node,1240 return self.lhs.firstToken();
1318 lparen: Token,1241 }
1319 expr: &Node,
1320 rparen: Token,
13211242
1322 pub fn iterate(self: &NodeGroupedExpression, index: usize) ?&Node {1243 pub fn lastToken(self: &SuffixOp) Token {
1323 var i = index;1244 return self.rtoken;
1245 }
1246 };
13241247
1325 if (i < 1) return self.expr;1248 pub const GroupedExpression = struct {
1326 i -= 1;1249 base: Node,
1250 lparen: Token,
1251 expr: &Node,
1252 rparen: Token,
13271253
1328 return null;1254 pub fn iterate(self: &GroupedExpression, index: usize) ?&Node {
1329 }1255 var i = index;
13301256
1331 pub fn firstToken(self: &NodeGroupedExpression) Token {1257 if (i < 1) return self.expr;
1332 return self.lparen;1258 i -= 1;
1333 }
13341259
1335 pub fn lastToken(self: &NodeGroupedExpression) Token {1260 return null;
1336 return self.rparen;1261 }
1337 }
1338};
13391262
1340pub const NodeControlFlowExpression = struct {1263 pub fn firstToken(self: &GroupedExpression) Token {
1341 base: Node,1264 return self.lparen;
1342 ltoken: Token,1265 }
1343 kind: Kind,
1344 rhs: ?&Node,
13451266
1346 const Kind = union(enum) {1267 pub fn lastToken(self: &GroupedExpression) Token {
1347 Break: ?Token,1268 return self.rparen;
1348 Continue: ?Token,1269 }
1349 Return,
1350 };1270 };
13511271
1352 pub fn iterate(self: &NodeControlFlowExpression, index: usize) ?&Node {1272 pub const ControlFlowExpression = struct {
1353 var i = index;1273 base: Node,
1274 ltoken: Token,
1275 kind: Kind,
1276 rhs: ?&Node,
13541277
1355 if (self.rhs) |rhs| {1278 const Kind = union(enum) {
1356 if (i < 1) return rhs;1279 Break: ?&Node,
1357 i -= 1;1280 Continue: ?&Node,
1358 }1281 Return,
1282 };
13591283
1360 return null;1284 pub fn iterate(self: &ControlFlowExpression, index: usize) ?&Node {
1361 }1285 var i = index;
1286
1287 switch (self.kind) {
1288 Kind.Break => |maybe_label| {
1289 if (maybe_label) |label| {
1290 if (i < 1) return label;
1291 i -= 1;
1292 }
1293 },
1294 Kind.Continue => |maybe_label| {
1295 if (maybe_label) |label| {
1296 if (i < 1) return label;
1297 i -= 1;
1298 }
1299 },
1300 Kind.Return => {},
1301 }
13621302
1363 pub fn firstToken(self: &NodeControlFlowExpression) Token {1303 if (self.rhs) |rhs| {
1364 return self.ltoken;1304 if (i < 1) return rhs;
1365 }1305 i -= 1;
1306 }
13661307
1367 pub fn lastToken(self: &NodeControlFlowExpression) Token {1308 return null;
1368 if (self.rhs) |rhs| {
1369 return rhs.lastToken();
1370 }1309 }
13711310
1372 switch (self.kind) {1311 pub fn firstToken(self: &ControlFlowExpression) Token {
1373 Kind.Break => |maybe_blk_token| {1312 return self.ltoken;
1374 if (maybe_blk_token) |blk_token| {
1375 return blk_token;
1376 }
1377 },
1378 Kind.Continue => |maybe_blk_token| {
1379 if (maybe_blk_token) |blk_token| {
1380 return blk_token;
1381 }
1382 },
1383 Kind.Return => return self.ltoken,
1384 }1313 }
13851314
1386 return self.ltoken;1315 pub fn lastToken(self: &ControlFlowExpression) Token {
1387 }1316 if (self.rhs) |rhs| {
1388};1317 return rhs.lastToken();
13891318 }
1390pub const NodeSuspend = struct {
1391 base: Node,
1392 suspend_token: Token,
1393 payload: ?&NodePayload,
1394 body: ?&Node,
13951319
1396 pub fn iterate(self: &NodeSuspend, index: usize) ?&Node {1320 switch (self.kind) {
1397 var i = index;1321 Kind.Break => |maybe_label| {
1322 if (maybe_label) |label| {
1323 return label.lastToken();
1324 }
1325 },
1326 Kind.Continue => |maybe_label| {
1327 if (maybe_label) |label| {
1328 return label.lastToken();
1329 }
1330 },
1331 Kind.Return => return self.ltoken,
1332 }
13981333
1399 if (self.payload) |payload| {1334 return self.ltoken;
1400 if (i < 1) return &payload.base;
1401 i -= 1;
1402 }1335 }
1336 };
14031337
1404 if (self.body) |body| {1338 pub const Suspend = struct {
1405 if (i < 1) return body;1339 base: Node,
1406 i -= 1;1340 suspend_token: Token,
1407 }1341 payload: ?&Node,
1342 body: ?&Node,
14081343
1409 return null;1344 pub fn iterate(self: &Suspend, index: usize) ?&Node {
1410 }1345 var i = index;
14111346
1412 pub fn firstToken(self: &NodeSuspend) Token {1347 if (self.payload) |payload| {
1413 return self.suspend_token;1348 if (i < 1) return payload;
1414 }1349 i -= 1;
1350 }
14151351
1416 pub fn lastToken(self: &NodeSuspend) Token {1352 if (self.body) |body| {
1417 if (self.body) |body| {1353 if (i < 1) return body;
1418 return body.lastToken();1354 i -= 1;
1355 }
1356
1357 return null;
1419 }1358 }
14201359
1421 if (self.payload) |payload| {1360 pub fn firstToken(self: &Suspend) Token {
1422 return payload.lastToken();1361 return self.suspend_token;
1423 }1362 }
14241363
1425 return self.suspend_token;1364 pub fn lastToken(self: &Suspend) Token {
1426 }1365 if (self.body) |body| {
1427};1366 return body.lastToken();
1367 }
14281368
1429pub const NodeIntegerLiteral = struct {1369 if (self.payload) |payload| {
1430 base: Node,1370 return payload.lastToken();
1431 token: Token,1371 }
14321372
1433 pub fn iterate(self: &NodeIntegerLiteral, index: usize) ?&Node {1373 return self.suspend_token;
1434 return null;1374 }
1435 }1375 };
14361376
1437 pub fn firstToken(self: &NodeIntegerLiteral) Token {1377 pub const IntegerLiteral = struct {
1438 return self.token;1378 base: Node,
1439 }1379 token: Token,
14401380
1441 pub fn lastToken(self: &NodeIntegerLiteral) Token {1381 pub fn iterate(self: &IntegerLiteral, index: usize) ?&Node {
1442 return self.token;1382 return null;
1443 }1383 }
1444};
14451384
1446pub const NodeFloatLiteral = struct {1385 pub fn firstToken(self: &IntegerLiteral) Token {
1447 base: Node,1386 return self.token;
1448 token: Token,1387 }
14491388
1450 pub fn iterate(self: &NodeFloatLiteral, index: usize) ?&Node {1389 pub fn lastToken(self: &IntegerLiteral) Token {
1451 return null;1390 return self.token;
1452 }1391 }
1392 };
14531393
1454 pub fn firstToken(self: &NodeFloatLiteral) Token {1394 pub const FloatLiteral = struct {
1455 return self.token;1395 base: Node,
1456 }1396 token: Token,
14571397
1458 pub fn lastToken(self: &NodeFloatLiteral) Token {1398 pub fn iterate(self: &FloatLiteral, index: usize) ?&Node {
1459 return self.token;1399 return null;
1460 }1400 }
1461};
14621401
1463pub const NodeBuiltinCall = struct {1402 pub fn firstToken(self: &FloatLiteral) Token {
1464 base: Node,1403 return self.token;
1465 builtin_token: Token,1404 }
1466 params: ArrayList(&Node),
1467 rparen_token: Token,
14681405
1469 pub fn iterate(self: &NodeBuiltinCall, index: usize) ?&Node {1406 pub fn lastToken(self: &FloatLiteral) Token {
1470 var i = index;1407 return self.token;
1408 }
1409 };
14711410
1472 if (i < self.params.len) return self.params.at(i);1411 pub const BuiltinCall = struct {
1473 i -= self.params.len;1412 base: Node,
1413 builtin_token: Token,
1414 params: ArrayList(&Node),
1415 rparen_token: Token,
14741416
1475 return null;1417 pub fn iterate(self: &BuiltinCall, index: usize) ?&Node {
1476 }1418 var i = index;
14771419
1478 pub fn firstToken(self: &NodeBuiltinCall) Token {1420 if (i < self.params.len) return self.params.at(i);
1479 return self.builtin_token;1421 i -= self.params.len;
1480 }
14811422
1482 pub fn lastToken(self: &NodeBuiltinCall) Token {1423 return null;
1483 return self.rparen_token;1424 }
1484 }
1485};
14861425
1487pub const NodeStringLiteral = struct {1426 pub fn firstToken(self: &BuiltinCall) Token {
1488 base: Node,1427 return self.builtin_token;
1489 token: Token,1428 }
14901429
1491 pub fn iterate(self: &NodeStringLiteral, index: usize) ?&Node {1430 pub fn lastToken(self: &BuiltinCall) Token {
1492 return null;1431 return self.rparen_token;
1493 }1432 }
1433 };
14941434
1495 pub fn firstToken(self: &NodeStringLiteral) Token {1435 pub const StringLiteral = struct {
1496 return self.token;1436 base: Node,
1497 }1437 token: Token,
14981438
1499 pub fn lastToken(self: &NodeStringLiteral) Token {1439 pub fn iterate(self: &StringLiteral, index: usize) ?&Node {
1500 return self.token;1440 return null;
1501 }1441 }
1502};
15031442
1504pub const NodeMultilineStringLiteral = struct {1443 pub fn firstToken(self: &StringLiteral) Token {
1505 base: Node,1444 return self.token;
1506 tokens: ArrayList(Token),1445 }
15071446
1508 pub fn iterate(self: &NodeMultilineStringLiteral, index: usize) ?&Node {1447 pub fn lastToken(self: &StringLiteral) Token {
1509 return null;1448 return self.token;
1510 }1449 }
1450 };
15111451
1512 pub fn firstToken(self: &NodeMultilineStringLiteral) Token {1452 pub const MultilineStringLiteral = struct {
1513 return self.tokens.at(0);1453 base: Node,
1514 }1454 tokens: ArrayList(Token),
15151455
1516 pub fn lastToken(self: &NodeMultilineStringLiteral) Token {1456 pub fn iterate(self: &MultilineStringLiteral, index: usize) ?&Node {
1517 return self.tokens.at(self.tokens.len - 1);1457 return null;
1518 }1458 }
1519};
15201459
1521pub const NodeCharLiteral = struct {1460 pub fn firstToken(self: &MultilineStringLiteral) Token {
1522 base: Node,1461 return self.tokens.at(0);
1523 token: Token,1462 }
15241463
1525 pub fn iterate(self: &NodeCharLiteral, index: usize) ?&Node {1464 pub fn lastToken(self: &MultilineStringLiteral) Token {
1526 return null;1465 return self.tokens.at(self.tokens.len - 1);
1527 }1466 }
1467 };
15281468
1529 pub fn firstToken(self: &NodeCharLiteral) Token {1469 pub const CharLiteral = struct {
1530 return self.token;1470 base: Node,
1531 }1471 token: Token,
15321472
1533 pub fn lastToken(self: &NodeCharLiteral) Token {1473 pub fn iterate(self: &CharLiteral, index: usize) ?&Node {
1534 return self.token;1474 return null;
1535 }1475 }
1536};
15371476
1538pub const NodeBoolLiteral = struct {1477 pub fn firstToken(self: &CharLiteral) Token {
1539 base: Node,1478 return self.token;
1540 token: Token,1479 }
15411480
1542 pub fn iterate(self: &NodeBoolLiteral, index: usize) ?&Node {1481 pub fn lastToken(self: &CharLiteral) Token {
1543 return null;1482 return self.token;
1544 }1483 }
1484 };
15451485
1546 pub fn firstToken(self: &NodeBoolLiteral) Token {1486 pub const BoolLiteral = struct {
1547 return self.token;1487 base: Node,
1548 }1488 token: Token,
15491489
1550 pub fn lastToken(self: &NodeBoolLiteral) Token {1490 pub fn iterate(self: &BoolLiteral, index: usize) ?&Node {
1551 return self.token;1491 return null;
1552 }1492 }
1553};
15541493
1555pub const NodeNullLiteral = struct {1494 pub fn firstToken(self: &BoolLiteral) Token {
1556 base: Node,1495 return self.token;
1557 token: Token,1496 }
15581497
1559 pub fn iterate(self: &NodeNullLiteral, index: usize) ?&Node {1498 pub fn lastToken(self: &BoolLiteral) Token {
1560 return null;1499 return self.token;
1561 }1500 }
1501 };
15621502
1563 pub fn firstToken(self: &NodeNullLiteral) Token {1503 pub const NullLiteral = struct {
1564 return self.token;1504 base: Node,
1565 }1505 token: Token,
15661506
1567 pub fn lastToken(self: &NodeNullLiteral) Token {1507 pub fn iterate(self: &NullLiteral, index: usize) ?&Node {
1568 return self.token;1508 return null;
1569 }1509 }
1570};
15711510
1572pub const NodeUndefinedLiteral = struct {1511 pub fn firstToken(self: &NullLiteral) Token {
1573 base: Node,1512 return self.token;
1574 token: Token,1513 }
15751514
1576 pub fn iterate(self: &NodeUndefinedLiteral, index: usize) ?&Node {1515 pub fn lastToken(self: &NullLiteral) Token {
1577 return null;1516 return self.token;
1578 }1517 }
1518 };
15791519
1580 pub fn firstToken(self: &NodeUndefinedLiteral) Token {1520 pub const UndefinedLiteral = struct {
1581 return self.token;1521 base: Node,
1582 }1522 token: Token,
15831523
1584 pub fn lastToken(self: &NodeUndefinedLiteral) Token {1524 pub fn iterate(self: &UndefinedLiteral, index: usize) ?&Node {
1585 return self.token;1525 return null;
1586 }1526 }
1587};
15881527
1589pub const NodeThisLiteral = struct {1528 pub fn firstToken(self: &UndefinedLiteral) Token {
1590 base: Node,1529 return self.token;
1591 token: Token,1530 }
15921531
1593 pub fn iterate(self: &NodeThisLiteral, index: usize) ?&Node {1532 pub fn lastToken(self: &UndefinedLiteral) Token {
1594 return null;1533 return self.token;
1595 }1534 }
1535 };
15961536
1597 pub fn firstToken(self: &NodeThisLiteral) Token {1537 pub const ThisLiteral = struct {
1598 return self.token;1538 base: Node,
1599 }1539 token: Token,
16001540
1601 pub fn lastToken(self: &NodeThisLiteral) Token {1541 pub fn iterate(self: &ThisLiteral, index: usize) ?&Node {
1602 return self.token;1542 return null;
1603 }1543 }
1604};
16051544
1606pub const NodeAsmOutput = struct {1545 pub fn firstToken(self: &ThisLiteral) Token {
1607 base: Node,1546 return self.token;
1608 symbolic_name: &NodeIdentifier,1547 }
1609 constraint: &Node,
1610 kind: Kind,
16111548
1612 const Kind = union(enum) {1549 pub fn lastToken(self: &ThisLiteral) Token {
1613 Variable: &NodeIdentifier,1550 return self.token;
1614 Return: &Node1551 }
1615 };1552 };
16161553
1617 pub fn iterate(self: &NodeAsmOutput, index: usize) ?&Node {1554 pub const AsmOutput = struct {
1618 var i = index;1555 base: Node,
1556 symbolic_name: &Node,
1557 constraint: &Node,
1558 kind: Kind,
16191559
1620 if (i < 1) return &self.symbolic_name.base;1560 const Kind = union(enum) {
1621 i -= 1;1561 Variable: &Identifier,
1562 Return: &Node
1563 };
16221564
1623 if (i < 1) return self.constraint;1565 pub fn iterate(self: &AsmOutput, index: usize) ?&Node {
1624 i -= 1;1566 var i = index;
16251567
1626 switch (self.kind) {1568 if (i < 1) return self.symbolic_name;
1627 Kind.Variable => |variable_name| {1569 i -= 1;
1628 if (i < 1) return &variable_name.base;1570
1629 i -= 1;1571 if (i < 1) return self.constraint;
1630 },1572 i -= 1;
1631 Kind.Return => |return_type| {1573
1632 if (i < 1) return return_type;1574 switch (self.kind) {
1633 i -= 1;1575 Kind.Variable => |variable_name| {
1576 if (i < 1) return &variable_name.base;
1577 i -= 1;
1578 },
1579 Kind.Return => |return_type| {
1580 if (i < 1) return return_type;
1581 i -= 1;
1582 }
1634 }1583 }
1635 }
16361584
1637 return null;1585 return null;
1638 }1586 }
16391587
1640 pub fn firstToken(self: &NodeAsmOutput) Token {1588 pub fn firstToken(self: &AsmOutput) Token {
1641 return self.symbolic_name.firstToken();1589 return self.symbolic_name.firstToken();
1642 }1590 }
16431591
1644 pub fn lastToken(self: &NodeAsmOutput) Token {1592 pub fn lastToken(self: &AsmOutput) Token {
1645 return switch (self.kind) {1593 return switch (self.kind) {
1646 Kind.Variable => |variable_name| variable_name.lastToken(),1594 Kind.Variable => |variable_name| variable_name.lastToken(),
1647 Kind.Return => |return_type| return_type.lastToken(),1595 Kind.Return => |return_type| return_type.lastToken(),
1648 };1596 };
1649 }1597 }
1650};1598 };
16511599
1652pub const NodeAsmInput = struct {1600 pub const AsmInput = struct {
1653 base: Node,1601 base: Node,
1654 symbolic_name: &NodeIdentifier,1602 symbolic_name: &Node,
1655 constraint: &Node,1603 constraint: &Node,
1656 expr: &Node,1604 expr: &Node,
16571605
1658 pub fn iterate(self: &NodeAsmInput, index: usize) ?&Node {1606 pub fn iterate(self: &AsmInput, index: usize) ?&Node {
1659 var i = index;1607 var i = index;
16601608
1661 if (i < 1) return &self.symbolic_name.base;1609 if (i < 1) return self.symbolic_name;
1662 i -= 1;1610 i -= 1;
16631611
1664 if (i < 1) return self.constraint;1612 if (i < 1) return self.constraint;
1665 i -= 1;1613 i -= 1;
16661614
1667 if (i < 1) return self.expr;1615 if (i < 1) return self.expr;
1668 i -= 1;1616 i -= 1;
16691617
1670 return null;1618 return null;
1671 }1619 }
16721620
1673 pub fn firstToken(self: &NodeAsmInput) Token {1621 pub fn firstToken(self: &AsmInput) Token {
1674 return self.symbolic_name.firstToken();1622 return self.symbolic_name.firstToken();
1675 }1623 }
16761624
1677 pub fn lastToken(self: &NodeAsmInput) Token {1625 pub fn lastToken(self: &AsmInput) Token {
1678 return self.expr.lastToken();1626 return self.expr.lastToken();
1679 }1627 }
1680};1628 };
16811629
1682pub const NodeAsm = struct {1630 pub const Asm = struct {
1683 base: Node,1631 base: Node,
1684 asm_token: Token,1632 asm_token: Token,
1685 is_volatile: bool,1633 volatile_token: ?Token,
1686 template: &Node,1634 template: &Node,
1687 //tokens: ArrayList(AsmToken),1635 //tokens: ArrayList(AsmToken),
1688 outputs: ArrayList(&NodeAsmOutput),1636 outputs: ArrayList(&AsmOutput),
1689 inputs: ArrayList(&NodeAsmInput),1637 inputs: ArrayList(&AsmInput),
1690 cloppers: ArrayList(&Node),1638 cloppers: ArrayList(&Node),
1691 rparen: Token,1639 rparen: Token,
16921640
1693 pub fn iterate(self: &NodeAsm, index: usize) ?&Node {1641 pub fn iterate(self: &Asm, index: usize) ?&Node {
1694 var i = index;1642 var i = index;
16951643
1696 if (i < self.outputs.len) return &self.outputs.at(index).base;1644 if (i < self.outputs.len) return &self.outputs.at(index).base;
1697 i -= self.outputs.len;1645 i -= self.outputs.len;
16981646
1699 if (i < self.inputs.len) return &self.inputs.at(index).base;1647 if (i < self.inputs.len) return &self.inputs.at(index).base;
1700 i -= self.inputs.len;1648 i -= self.inputs.len;
17011649
1702 if (i < self.cloppers.len) return self.cloppers.at(index);1650 if (i < self.cloppers.len) return self.cloppers.at(index);
1703 i -= self.cloppers.len;1651 i -= self.cloppers.len;
17041652
1705 return null;1653 return null;
1706 }1654 }
17071655
1708 pub fn firstToken(self: &NodeAsm) Token {1656 pub fn firstToken(self: &Asm) Token {
1709 return self.asm_token;1657 return self.asm_token;
1710 }1658 }
17111659
1712 pub fn lastToken(self: &NodeAsm) Token {1660 pub fn lastToken(self: &Asm) Token {
1713 return self.rparen;1661 return self.rparen;
1714 }1662 }
1715};1663 };
17161664
1717pub const NodeUnreachable = struct {1665 pub const Unreachable = struct {
1718 base: Node,1666 base: Node,
1719 token: Token,1667 token: Token,
17201668
1721 pub fn iterate(self: &NodeUnreachable, index: usize) ?&Node {1669 pub fn iterate(self: &Unreachable, index: usize) ?&Node {
1722 return null;1670 return null;
1723 }1671 }
17241672
1725 pub fn firstToken(self: &NodeUnreachable) Token {1673 pub fn firstToken(self: &Unreachable) Token {
1726 return self.token;1674 return self.token;
1727 }1675 }
17281676
1729 pub fn lastToken(self: &NodeUnreachable) Token {1677 pub fn lastToken(self: &Unreachable) Token {
1730 return self.token;1678 return self.token;
1731 }1679 }
1732};1680 };
17331681
1734pub const NodeErrorType = struct {1682 pub const ErrorType = struct {
1735 base: Node,1683 base: Node,
1736 token: Token,1684 token: Token,
17371685
1738 pub fn iterate(self: &NodeErrorType, index: usize) ?&Node {1686 pub fn iterate(self: &ErrorType, index: usize) ?&Node {
1739 return null;1687 return null;
1740 }1688 }
17411689
1742 pub fn firstToken(self: &NodeErrorType) Token {1690 pub fn firstToken(self: &ErrorType) Token {
1743 return self.token;1691 return self.token;
1744 }1692 }
17451693
1746 pub fn lastToken(self: &NodeErrorType) Token {1694 pub fn lastToken(self: &ErrorType) Token {
1747 return self.token;1695 return self.token;
1748 }1696 }
1749};1697 };
17501698
1751pub const NodeVarType = struct {1699 pub const VarType = struct {
1752 base: Node,1700 base: Node,
1753 token: Token,1701 token: Token,
17541702
1755 pub fn iterate(self: &NodeVarType, index: usize) ?&Node {1703 pub fn iterate(self: &VarType, index: usize) ?&Node {
1756 return null;1704 return null;
1757 }1705 }
17581706
1759 pub fn firstToken(self: &NodeVarType) Token {1707 pub fn firstToken(self: &VarType) Token {
1760 return self.token;1708 return self.token;
1761 }1709 }
17621710
1763 pub fn lastToken(self: &NodeVarType) Token {1711 pub fn lastToken(self: &VarType) Token {
1764 return self.token;1712 return self.token;
1765 }1713 }
1766};1714 };
17671715
1768pub const NodeLineComment = struct {1716 pub const LineComment = struct {
1769 base: Node,1717 base: Node,
1770 lines: ArrayList(Token),1718 lines: ArrayList(Token),
17711719
1772 pub fn iterate(self: &NodeLineComment, index: usize) ?&Node {1720 pub fn iterate(self: &LineComment, index: usize) ?&Node {
1773 return null;1721 return null;
1774 }1722 }
17751723
1776 pub fn firstToken(self: &NodeLineComment) Token {1724 pub fn firstToken(self: &LineComment) Token {
1777 return self.lines.at(0);1725 return self.lines.at(0);
1778 }1726 }
17791727
1780 pub fn lastToken(self: &NodeLineComment) Token {1728 pub fn lastToken(self: &LineComment) Token {
1781 return self.lines.at(self.lines.len - 1);1729 return self.lines.at(self.lines.len - 1);
1782 }1730 }
1783};1731 };
17841732
1785pub const NodeTestDecl = struct {1733 pub const TestDecl = struct {
1786 base: Node,1734 base: Node,
1787 test_token: Token,1735 comments: ?&LineComment,
1788 name: &Node,1736 test_token: Token,
1789 body_node: &Node,1737 name: &Node,
1738 body_node: &Node,
17901739
1791 pub fn iterate(self: &NodeTestDecl, index: usize) ?&Node {1740 pub fn iterate(self: &TestDecl, index: usize) ?&Node {
1792 var i = index;1741 var i = index;
17931742
1794 if (i < 1) return self.body_node;1743 if (i < 1) return self.body_node;
1795 i -= 1;1744 i -= 1;
17961745
1797 return null;1746 return null;
1798 }1747 }
17991748
1800 pub fn firstToken(self: &NodeTestDecl) Token {1749 pub fn firstToken(self: &TestDecl) Token {
1801 return self.test_token;1750 return self.test_token;
1802 }1751 }
18031752
1804 pub fn lastToken(self: &NodeTestDecl) Token {1753 pub fn lastToken(self: &TestDecl) Token {
1805 return self.body_node.lastToken();1754 return self.body_node.lastToken();
1806 }1755 }
1756 };
1807};1757};
1758
std/zig/parser.zig+2360-2275
...@@ -18,10 +18,9 @@ pub const Parser = struct {...@@ -18,10 +18,9 @@ pub const Parser = struct {
18 put_back_tokens: [2]Token,18 put_back_tokens: [2]Token,
19 put_back_count: usize,19 put_back_count: usize,
20 source_file_name: []const u8,20 source_file_name: []const u8,
21 pending_line_comment_node: ?&ast.NodeLineComment,
2221
23 pub const Tree = struct {22 pub const Tree = struct {
24 root_node: &ast.NodeRoot,23 root_node: &ast.Node.Root,
25 arena_allocator: std.heap.ArenaAllocator,24 arena_allocator: std.heap.ArenaAllocator,
2625
27 pub fn deinit(self: &Tree) void {26 pub fn deinit(self: &Tree) void {
...@@ -44,7 +43,6 @@ pub const Parser = struct {...@@ -44,7 +43,6 @@ pub const Parser = struct {
44 .put_back_count = 0,43 .put_back_count = 0,
45 .source_file_name = source_file_name,44 .source_file_name = source_file_name,
46 .utility_bytes = []align(utility_bytes_align) u8{},45 .utility_bytes = []align(utility_bytes_align) u8{},
47 .pending_line_comment_node = null,
48 };46 };
49 }47 }
5048
...@@ -59,29 +57,31 @@ pub const Parser = struct {...@@ -59,29 +57,31 @@ pub const Parser = struct {
59 lib_name: ?&ast.Node,57 lib_name: ?&ast.Node,
60 };58 };
6159
62 const ContainerExternCtx = struct {60 const VarDeclCtx = struct {
63 dest_ptr: DestPtr,61 mut_token: Token,
64 ltoken: Token,62 visib_token: ?Token,
65 layout: ast.NodeContainerDecl.Layout,63 comptime_token: ?Token,
64 extern_export_token: ?Token,
65 lib_name: ?&ast.Node,
66 list: &ArrayList(&ast.Node),
67 comments: ?&ast.Node.LineComment,
66 };68 };
6769
68 const DestPtr = union(enum) {70 const TopLevelExternOrFieldCtx = struct {
69 Field: &&ast.Node,71 visib_token: Token,
70 NullableField: &?&ast.Node,72 container_decl: &ast.Node.ContainerDecl,
73 };
7174
72 pub fn store(self: &const DestPtr, value: &ast.Node) void {75 const ExternTypeCtx = struct {
73 switch (*self) {76 opt_ctx: OptionalCtx,
74 DestPtr.Field => |ptr| *ptr = value,77 extern_token: Token,
75 DestPtr.NullableField => |ptr| *ptr = value,78 comments: ?&ast.Node.LineComment,
76 }79 };
77 }
7880
79 pub fn get(self: &const DestPtr) &ast.Node {81 const ContainerKindCtx = struct {
80 switch (*self) {82 opt_ctx: OptionalCtx,
81 DestPtr.Field => |ptr| return *ptr,83 ltoken: Token,
82 DestPtr.NullableField => |ptr| return ??*ptr,84 layout: ast.Node.ContainerDecl.Layout,
83 }
84 }
85 };85 };
8686
87 const ExpectTokenSave = struct {87 const ExpectTokenSave = struct {
...@@ -89,13 +89,9 @@ pub const Parser = struct {...@@ -89,13 +89,9 @@ pub const Parser = struct {
89 ptr: &Token,89 ptr: &Token,
90 };90 };
9191
92 const RevertState = struct {92 const OptionalTokenSave = struct {
93 parser: Parser,93 id: Token.Id,
94 tokenizer: Tokenizer,94 ptr: &?Token,
95
96 // We expect, that if something is optional, then there is a field,
97 // that needs to be set to null, when we revert.
98 ptr: &?&ast.Node,
99 };95 };
10096
101 const ExprListCtx = struct {97 const ExprListCtx = struct {
...@@ -104,11 +100,6 @@ pub const Parser = struct {...@@ -104,11 +100,6 @@ pub const Parser = struct {
104 ptr: &Token,100 ptr: &Token,
105 };101 };
106102
107 const ElseCtx = struct {
108 payload: ?DestPtr,
109 body: DestPtr,
110 };
111
112 fn ListSave(comptime T: type) type {103 fn ListSave(comptime T: type) type {
113 return struct {104 return struct {
114 list: &ArrayList(T),105 list: &ArrayList(T),
...@@ -116,27 +107,79 @@ pub const Parser = struct {...@@ -116,27 +107,79 @@ pub const Parser = struct {
116 };107 };
117 }108 }
118109
110 const MaybeLabeledExpressionCtx = struct {
111 label: Token,
112 opt_ctx: OptionalCtx,
113 };
114
119 const LabelCtx = struct {115 const LabelCtx = struct {
120 label: ?Token,116 label: ?Token,
121 dest_ptr: DestPtr,117 opt_ctx: OptionalCtx,
122 };118 };
123119
124 const InlineCtx = struct {120 const InlineCtx = struct {
125 label: ?Token,121 label: ?Token,
126 inline_token: ?Token,122 inline_token: ?Token,
127 dest_ptr: DestPtr,123 opt_ctx: OptionalCtx,
128 };124 };
129125
130 const LoopCtx = struct {126 const LoopCtx = struct {
131 label: ?Token,127 label: ?Token,
132 inline_token: ?Token,128 inline_token: ?Token,
133 loop_token: Token,129 loop_token: Token,
134 dest_ptr: DestPtr,130 opt_ctx: OptionalCtx,
135 };131 };
136132
137 const AsyncEndCtx = struct {133 const AsyncEndCtx = struct {
138 dest_ptr: DestPtr,134 ctx: OptionalCtx,
139 attribute: &ast.NodeAsyncAttribute,135 attribute: &ast.Node.AsyncAttribute,
136 };
137
138 const ErrorTypeOrSetDeclCtx = struct {
139 opt_ctx: OptionalCtx,
140 error_token: Token,
141 };
142
143 const ParamDeclEndCtx = struct {
144 fn_proto: &ast.Node.FnProto,
145 param_decl: &ast.Node.ParamDecl,
146 };
147
148 const ComptimeStatementCtx = struct {
149 comptime_token: Token,
150 block: &ast.Node.Block,
151 };
152
153 const OptionalCtx = union(enum) {
154 Optional: &?&ast.Node,
155 RequiredNull: &?&ast.Node,
156 Required: &&ast.Node,
157
158 pub fn store(self: &const OptionalCtx, value: &ast.Node) void {
159 switch (*self) {
160 OptionalCtx.Optional => |ptr| *ptr = value,
161 OptionalCtx.RequiredNull => |ptr| *ptr = value,
162 OptionalCtx.Required => |ptr| *ptr = value,
163 }
164 }
165
166 pub fn get(self: &const OptionalCtx) ?&ast.Node {
167 switch (*self) {
168 OptionalCtx.Optional => |ptr| return *ptr,
169 OptionalCtx.RequiredNull => |ptr| return ??*ptr,
170 OptionalCtx.Required => |ptr| return *ptr,
171 }
172 }
173
174 pub fn toRequired(self: &const OptionalCtx) OptionalCtx {
175 switch (*self) {
176 OptionalCtx.Optional => |ptr| {
177 return OptionalCtx { .RequiredNull = ptr };
178 },
179 OptionalCtx.RequiredNull => |ptr| return *self,
180 OptionalCtx.Required => |ptr| return *self,
181 }
182 }
140 };183 };
141184
142 const State = union(enum) {185 const State = union(enum) {
...@@ -144,92 +187,116 @@ pub const Parser = struct {...@@ -144,92 +187,116 @@ pub const Parser = struct {
144 TopLevelExtern: TopLevelDeclCtx,187 TopLevelExtern: TopLevelDeclCtx,
145 TopLevelLibname: TopLevelDeclCtx,188 TopLevelLibname: TopLevelDeclCtx,
146 TopLevelDecl: TopLevelDeclCtx,189 TopLevelDecl: TopLevelDeclCtx,
147 ContainerExtern: ContainerExternCtx,190 TopLevelExternOrField: TopLevelExternOrFieldCtx,
148 ContainerDecl: &ast.NodeContainerDecl,191
149 SliceOrArrayAccess: &ast.NodeSuffixOp,192 ContainerKind: ContainerKindCtx,
150 AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,193 ContainerInitArgStart: &ast.Node.ContainerDecl,
151 VarDecl: &ast.NodeVarDecl,194 ContainerInitArg: &ast.Node.ContainerDecl,
152 VarDeclAlign: &ast.NodeVarDecl,195 ContainerDecl: &ast.Node.ContainerDecl,
153 VarDeclEq: &ast.NodeVarDecl,196
154 IfToken: @TagType(Token.Id),197 VarDecl: VarDeclCtx,
155 IfTokenSave: ExpectTokenSave,198 VarDeclAlign: &ast.Node.VarDecl,
156 ExpectToken: @TagType(Token.Id),199 VarDeclEq: &ast.Node.VarDecl,
157 ExpectTokenSave: ExpectTokenSave,200
158 FnProto: &ast.NodeFnProto,201 FnDef: &ast.Node.FnProto,
159 FnProtoAlign: &ast.NodeFnProto,202 FnProto: &ast.Node.FnProto,
160 FnProtoReturnType: &ast.NodeFnProto,203 FnProtoAlign: &ast.Node.FnProto,
161 ParamDecl: &ast.NodeFnProto,204 FnProtoReturnType: &ast.Node.FnProto,
162 ParamDeclComma,205
163 FnDef: &ast.NodeFnProto,206 ParamDecl: &ast.Node.FnProto,
207 ParamDeclAliasOrComptime: &ast.Node.ParamDecl,
208 ParamDeclName: &ast.Node.ParamDecl,
209 ParamDeclEnd: ParamDeclEndCtx,
210 ParamDeclComma: &ast.Node.FnProto,
211
212 MaybeLabeledExpression: MaybeLabeledExpressionCtx,
164 LabeledExpression: LabelCtx,213 LabeledExpression: LabelCtx,
165 Inline: InlineCtx,214 Inline: InlineCtx,
166 While: LoopCtx,215 While: LoopCtx,
167 For: LoopCtx,
168 Block: &ast.NodeBlock,
169 Else: &?&ast.NodeElse,
170 WhileContinueExpr: &?&ast.Node,216 WhileContinueExpr: &?&ast.Node,
171 Statement: &ast.NodeBlock,217 For: LoopCtx,
172 Semicolon: &const &const ast.Node,218 Else: &?&ast.Node.Else,
173 AsmOutputItems: &ArrayList(&ast.NodeAsmOutput),219
174 AsmInputItems: &ArrayList(&ast.NodeAsmInput),220 Block: &ast.Node.Block,
221 Statement: &ast.Node.Block,
222 ComptimeStatement: ComptimeStatementCtx,
223 Semicolon: &&ast.Node,
224
225 AsmOutputItems: &ArrayList(&ast.Node.AsmOutput),
226 AsmOutputReturnOrType: &ast.Node.AsmOutput,
227 AsmInputItems: &ArrayList(&ast.Node.AsmInput),
175 AsmClopperItems: &ArrayList(&ast.Node),228 AsmClopperItems: &ArrayList(&ast.Node),
229
176 ExprListItemOrEnd: ExprListCtx,230 ExprListItemOrEnd: ExprListCtx,
177 ExprListCommaOrEnd: ExprListCtx,231 ExprListCommaOrEnd: ExprListCtx,
178 FieldInitListItemOrEnd: ListSave(&ast.NodeFieldInitializer),232 FieldInitListItemOrEnd: ListSave(&ast.Node.FieldInitializer),
179 FieldInitListCommaOrEnd: ListSave(&ast.NodeFieldInitializer),233 FieldInitListCommaOrEnd: ListSave(&ast.Node.FieldInitializer),
180 FieldListCommaOrEnd: &ast.NodeContainerDecl,234 FieldListCommaOrEnd: &ast.Node.ContainerDecl,
181 IdentifierListItemOrEnd: ListSave(&ast.NodeIdentifier),235 IdentifierListItemOrEnd: ListSave(&ast.Node),
182 IdentifierListCommaOrEnd: ListSave(&ast.NodeIdentifier),236 IdentifierListCommaOrEnd: ListSave(&ast.Node),
183 SwitchCaseOrEnd: ListSave(&ast.NodeSwitchCase),237 SwitchCaseOrEnd: ListSave(&ast.Node.SwitchCase),
184 SuspendBody: &ast.NodeSuspend,238 SwitchCaseCommaOrEnd: ListSave(&ast.Node.SwitchCase),
185 AsyncEnd: AsyncEndCtx,239 SwitchCaseFirstItem: &ArrayList(&ast.Node),
186 Payload: &?&ast.NodePayload,
187 PointerPayload: &?&ast.NodePointerPayload,
188 PointerIndexPayload: &?&ast.NodePointerIndexPayload,
189 SwitchCaseCommaOrEnd: ListSave(&ast.NodeSwitchCase),
190 SwitchCaseItem: &ArrayList(&ast.Node),240 SwitchCaseItem: &ArrayList(&ast.Node),
191 SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),241 SwitchCaseItemCommaOrEnd: &ArrayList(&ast.Node),
192242
193 /// A state that can be appended before any other State. If an error occures,243 SuspendBody: &ast.Node.Suspend,
194 /// the parser will first try looking for the closest optional state. If an244 AsyncAllocator: &ast.Node.AsyncAttribute,
195 /// optional state is found, the parser will revert to the state it was in245 AsyncEnd: AsyncEndCtx,
196 /// when the optional was added. This will polute the arena allocator with246
197 /// "leaked" nodes. TODO: Figure out if it's nessesary to handle leaked nodes.247 ExternType: ExternTypeCtx,
198 Optional: RevertState,248 SliceOrArrayAccess: &ast.Node.SuffixOp,
199249 SliceOrArrayType: &ast.Node.PrefixOp,
200 Expression: DestPtr,250 AddrOfModifiers: &ast.Node.PrefixOp.AddrOfInfo,
201 RangeExpressionBegin: DestPtr,251
202 RangeExpressionEnd: DestPtr,252 Payload: OptionalCtx,
203 AssignmentExpressionBegin: DestPtr,253 PointerPayload: OptionalCtx,
204 AssignmentExpressionEnd: DestPtr,254 PointerIndexPayload: OptionalCtx,
205 UnwrapExpressionBegin: DestPtr,255
206 UnwrapExpressionEnd: DestPtr,256 Expression: OptionalCtx,
207 BoolOrExpressionBegin: DestPtr,257 RangeExpressionBegin: OptionalCtx,
208 BoolOrExpressionEnd: DestPtr,258 RangeExpressionEnd: OptionalCtx,
209 BoolAndExpressionBegin: DestPtr,259 AssignmentExpressionBegin: OptionalCtx,
210 BoolAndExpressionEnd: DestPtr,260 AssignmentExpressionEnd: OptionalCtx,
211 ComparisonExpressionBegin: DestPtr,261 UnwrapExpressionBegin: OptionalCtx,
212 ComparisonExpressionEnd: DestPtr,262 UnwrapExpressionEnd: OptionalCtx,
213 BinaryOrExpressionBegin: DestPtr,263 BoolOrExpressionBegin: OptionalCtx,
214 BinaryOrExpressionEnd: DestPtr,264 BoolOrExpressionEnd: OptionalCtx,
215 BinaryXorExpressionBegin: DestPtr,265 BoolAndExpressionBegin: OptionalCtx,
216 BinaryXorExpressionEnd: DestPtr,266 BoolAndExpressionEnd: OptionalCtx,
217 BinaryAndExpressionBegin: DestPtr,267 ComparisonExpressionBegin: OptionalCtx,
218 BinaryAndExpressionEnd: DestPtr,268 ComparisonExpressionEnd: OptionalCtx,
219 BitShiftExpressionBegin: DestPtr,269 BinaryOrExpressionBegin: OptionalCtx,
220 BitShiftExpressionEnd: DestPtr,270 BinaryOrExpressionEnd: OptionalCtx,
221 AdditionExpressionBegin: DestPtr,271 BinaryXorExpressionBegin: OptionalCtx,
222 AdditionExpressionEnd: DestPtr,272 BinaryXorExpressionEnd: OptionalCtx,
223 MultiplyExpressionBegin: DestPtr,273 BinaryAndExpressionBegin: OptionalCtx,
224 MultiplyExpressionEnd: DestPtr,274 BinaryAndExpressionEnd: OptionalCtx,
225 CurlySuffixExpressionBegin: DestPtr,275 BitShiftExpressionBegin: OptionalCtx,
226 CurlySuffixExpressionEnd: DestPtr,276 BitShiftExpressionEnd: OptionalCtx,
227 TypeExprBegin: DestPtr,277 AdditionExpressionBegin: OptionalCtx,
228 TypeExprEnd: DestPtr,278 AdditionExpressionEnd: OptionalCtx,
229 PrefixOpExpression: DestPtr,279 MultiplyExpressionBegin: OptionalCtx,
230 SuffixOpExpressionBegin: DestPtr,280 MultiplyExpressionEnd: OptionalCtx,
231 SuffixOpExpressionEnd: DestPtr,281 CurlySuffixExpressionBegin: OptionalCtx,
232 PrimaryExpression: DestPtr,282 CurlySuffixExpressionEnd: OptionalCtx,
283 TypeExprBegin: OptionalCtx,
284 TypeExprEnd: OptionalCtx,
285 PrefixOpExpression: OptionalCtx,
286 SuffixOpExpressionBegin: OptionalCtx,
287 SuffixOpExpressionEnd: OptionalCtx,
288 PrimaryExpression: OptionalCtx,
289
290 ErrorTypeOrSetDecl: ErrorTypeOrSetDeclCtx,
291 StringLiteral: OptionalCtx,
292 Identifier: OptionalCtx,
293
294
295 IfToken: @TagType(Token.Id),
296 IfTokenSave: ExpectTokenSave,
297 ExpectToken: @TagType(Token.Id),
298 ExpectTokenSave: ExpectTokenSave,
299 OptionalTokenSave: OptionalTokenSave,
233 };300 };
234301
235 /// Returns an AST tree, allocated with the parser's allocator.302 /// Returns an AST tree, allocated with the parser's allocator.
...@@ -243,8 +310,8 @@ pub const Parser = struct {...@@ -243,8 +310,8 @@ pub const Parser = struct {
243 errdefer arena_allocator.deinit();310 errdefer arena_allocator.deinit();
244311
245 const arena = &arena_allocator.allocator;312 const arena = &arena_allocator.allocator;
246 const root_node = try self.createNode(arena, ast.NodeRoot,313 const root_node = try self.createNode(arena, ast.Node.Root,
247 ast.NodeRoot {314 ast.Node.Root {
248 .base = undefined,315 .base = undefined,
249 .decls = ArrayList(&ast.Node).init(arena),316 .decls = ArrayList(&ast.Node).init(arena),
250 // initialized when we get the eof token317 // initialized when we get the eof token
...@@ -267,66 +334,43 @@ pub const Parser = struct {...@@ -267,66 +334,43 @@ pub const Parser = struct {
267 // warn("\n");334 // warn("\n");
268 //}335 //}
269336
270 // look for line comments
271 while (true) {
272 if (self.eatToken(Token.Id.LineComment)) |line_comment| {
273 const node = blk: {
274 if (self.pending_line_comment_node) |comment_node| {
275 break :blk comment_node;
276 } else {
277 const comment_node = try arena.create(ast.NodeLineComment);
278 *comment_node = ast.NodeLineComment {
279 .base = ast.Node {
280 .id = ast.Node.Id.LineComment,
281 .comment = null,
282 },
283 .lines = ArrayList(Token).init(arena),
284 };
285 self.pending_line_comment_node = comment_node;
286 break :blk comment_node;
287 }
288 };
289 try node.lines.append(line_comment);
290 continue;
291 }
292 break;
293 }
294
295 // This gives us 1 free append that can't fail337 // This gives us 1 free append that can't fail
296 const state = stack.pop();338 const state = stack.pop();
297339
298 switch (state) {340 switch (state) {
299 State.TopLevel => {341 State.TopLevel => {
342 const comments = try self.eatComments(arena);
300 const token = self.getNextToken();343 const token = self.getNextToken();
301 switch (token.id) {344 switch (token.id) {
302 Token.Id.Keyword_test => {345 Token.Id.Keyword_test => {
303 stack.append(State.TopLevel) catch unreachable;346 stack.append(State.TopLevel) catch unreachable;
304347
305 const name_token = self.getNextToken();348 const block = try self.createNode(arena, ast.Node.Block,
306 const name = (try self.parseStringLiteral(arena, name_token)) ?? {349 ast.Node.Block {
307 try self.parseError(&stack, name_token, "expected string literal, found {}", @tagName(name_token.id));
308 continue;
309 };
310 const lbrace = (try self.expectToken(&stack, Token.Id.LBrace)) ?? continue;
311
312 const block = try self.createNode(arena, ast.NodeBlock,
313 ast.NodeBlock {
314 .base = undefined,350 .base = undefined,
315 .label = null,351 .label = null,
316 .lbrace = lbrace,352 .lbrace = undefined,
317 .statements = ArrayList(&ast.Node).init(arena),353 .statements = ArrayList(&ast.Node).init(arena),
318 .rbrace = undefined,354 .rbrace = undefined,
319 }355 }
320 );356 );
321 _ = try self.createAttachNode(arena, &root_node.decls, ast.NodeTestDecl,357 const test_node = try self.createAttachNode(arena, &root_node.decls, ast.Node.TestDecl,
322 ast.NodeTestDecl {358 ast.Node.TestDecl {
323 .base = undefined,359 .base = undefined,
360 .comments = comments,
324 .test_token = token,361 .test_token = token,
325 .name = name,362 .name = undefined,
326 .body_node = &block.base,363 .body_node = &block.base,
327 }364 }
328 );365 );
329 stack.append(State { .Block = block }) catch unreachable;366 stack.append(State { .Block = block }) catch unreachable;
367 try stack.append(State {
368 .ExpectTokenSave = ExpectTokenSave {
369 .id = Token.Id.LBrace,
370 .ptr = &block.rbrace,
371 }
372 });
373 try stack.append(State { .StringLiteral = OptionalCtx { .Required = &test_node.name } });
330 continue;374 continue;
331 },375 },
332 Token.Id.Eof => {376 Token.Id.Eof => {
...@@ -346,15 +390,30 @@ pub const Parser = struct {...@@ -346,15 +390,30 @@ pub const Parser = struct {
346 continue;390 continue;
347 },391 },
348 Token.Id.Keyword_comptime => {392 Token.Id.Keyword_comptime => {
349 const node = try self.createAttachNode(arena, &root_node.decls, ast.NodeComptime,393 const block = try self.createNode(arena, ast.Node.Block,
350 ast.NodeComptime {394 ast.Node.Block {
395 .base = undefined,
396 .label = null,
397 .lbrace = undefined,
398 .statements = ArrayList(&ast.Node).init(arena),
399 .rbrace = undefined,
400 }
401 );
402 const node = try self.createAttachNode(arena, &root_node.decls, ast.Node.Comptime,
403 ast.Node.Comptime {
351 .base = undefined,404 .base = undefined,
352 .comptime_token = token,405 .comptime_token = token,
353 .expr = undefined,406 .expr = &block.base,
354 }407 }
355 );408 );
356 stack.append(State.TopLevel) catch unreachable;409 stack.append(State.TopLevel) catch unreachable;
357 try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });410 try stack.append(State { .Block = block });
411 try stack.append(State {
412 .ExpectTokenSave = ExpectTokenSave {
413 .id = Token.Id.LBrace,
414 .ptr = &block.rbrace,
415 }
416 });
358 continue;417 continue;
359 },418 },
360 else => {419 else => {
...@@ -404,7 +463,6 @@ pub const Parser = struct {...@@ -404,7 +463,6 @@ pub const Parser = struct {
404 }463 }
405 }464 }
406 },465 },
407
408 State.TopLevelLibname => |ctx| {466 State.TopLevelLibname => |ctx| {
409 const lib_name = blk: {467 const lib_name = blk: {
410 const lib_name_token = self.getNextToken();468 const lib_name_token = self.getNextToken();
...@@ -422,19 +480,19 @@ pub const Parser = struct {...@@ -422,19 +480,19 @@ pub const Parser = struct {
422 .lib_name = lib_name,480 .lib_name = lib_name,
423 },481 },
424 }) catch unreachable;482 }) catch unreachable;
483 continue;
425 },484 },
426
427 State.TopLevelDecl => |ctx| {485 State.TopLevelDecl => |ctx| {
486 const comments = try self.eatComments(arena);
428 const token = self.getNextToken();487 const token = self.getNextToken();
429 switch (token.id) {488 switch (token.id) {
430 Token.Id.Keyword_use => {489 Token.Id.Keyword_use => {
431 if (ctx.extern_export_inline_token != null) {490 if (ctx.extern_export_inline_token != null) {
432 try self.parseError(&stack, token, "Invalid token {}", @tagName((??ctx.extern_export_inline_token).id));491 return self.parseError(token, "Invalid token {}", @tagName((??ctx.extern_export_inline_token).id));
433 continue;
434 }492 }
435493
436 const node = try self.createAttachNode(arena, ctx.decls, ast.NodeUse,494 const node = try self.createAttachNode(arena, ctx.decls, ast.Node.Use,
437 ast.NodeUse {495 ast.Node.Use {
438 .base = undefined,496 .base = undefined,
439 .visib_token = ctx.visib_token,497 .visib_token = ctx.visib_token,
440 .expr = undefined,498 .expr = undefined,
...@@ -447,63 +505,35 @@ pub const Parser = struct {...@@ -447,63 +505,35 @@ pub const Parser = struct {
447 .ptr = &node.semicolon_token,505 .ptr = &node.semicolon_token,
448 }506 }
449 }) catch unreachable;507 }) catch unreachable;
450 try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });508 try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
451 continue;509 continue;
452 },510 },
453 Token.Id.Keyword_var, Token.Id.Keyword_const => {511 Token.Id.Keyword_var, Token.Id.Keyword_const => {
454 if (ctx.extern_export_inline_token) |extern_export_inline_token| {512 if (ctx.extern_export_inline_token) |extern_export_inline_token| {
455 if (extern_export_inline_token.id == Token.Id.Keyword_inline) {513 if (extern_export_inline_token.id == Token.Id.Keyword_inline) {
456 try self.parseError(&stack, token, "Invalid token {}", @tagName(extern_export_inline_token.id));514 return self.parseError(token, "Invalid token {}", @tagName(extern_export_inline_token.id));
457 continue;
458 }515 }
459 }516 }
460517
461 const var_decl_node = try self.createAttachNode(arena, ctx.decls, ast.NodeVarDecl,518 stack.append(State {
462 ast.NodeVarDecl {519 .VarDecl = VarDeclCtx {
463 .base = undefined,520 .comments = comments,
464 .visib_token = ctx.visib_token,521 .visib_token = ctx.visib_token,
465 .mut_token = token,522 .lib_name = ctx.lib_name,
466 .comptime_token = null,523 .comptime_token = null,
467 .extern_export_token = ctx.extern_export_inline_token,524 .extern_export_token = ctx.extern_export_inline_token,
468 .type_node = null,525 .mut_token = token,
469 .align_node = null,526 .list = ctx.decls
470 .init_node = null,
471 .lib_name = ctx.lib_name,
472 // initialized later
473 .name_token = undefined,
474 .eq_token = undefined,
475 .semicolon_token = undefined,
476 }
477 );
478 stack.append(State { .VarDecl = var_decl_node }) catch unreachable;
479 continue;
480 },
481 Token.Id.Keyword_fn => {
482 const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.NodeFnProto,
483 ast.NodeFnProto {
484 .base = undefined,
485 .visib_token = ctx.visib_token,
486 .name_token = null,
487 .fn_token = token,
488 .params = ArrayList(&ast.Node).init(arena),
489 .return_type = undefined,
490 .var_args_token = null,
491 .extern_export_inline_token = ctx.extern_export_inline_token,
492 .cc_token = null,
493 .async_attr = null,
494 .body_node = null,
495 .lib_name = ctx.lib_name,
496 .align_expr = null,
497 }527 }
498 );528 }) catch unreachable;
499 stack.append(State { .FnDef = fn_proto }) catch unreachable;
500 try stack.append(State { .FnProto = fn_proto });
501 continue;529 continue;
502 },530 },
503 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {531 Token.Id.Keyword_fn, Token.Id.Keyword_nakedcc,
504 const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.NodeFnProto,532 Token.Id.Keyword_stdcallcc, Token.Id.Keyword_async => {
505 ast.NodeFnProto {533 const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.Node.FnProto,
534 ast.Node.FnProto {
506 .base = undefined,535 .base = undefined,
536 .comments = comments,
507 .visib_token = ctx.visib_token,537 .visib_token = ctx.visib_token,
508 .name_token = null,538 .name_token = null,
509 .fn_token = undefined,539 .fn_token = undefined,
...@@ -511,7 +541,7 @@ pub const Parser = struct {...@@ -511,7 +541,7 @@ pub const Parser = struct {
511 .return_type = undefined,541 .return_type = undefined,
512 .var_args_token = null,542 .var_args_token = null,
513 .extern_export_inline_token = ctx.extern_export_inline_token,543 .extern_export_inline_token = ctx.extern_export_inline_token,
514 .cc_token = token,544 .cc_token = null,
515 .async_attr = null,545 .async_attr = null,
516 .body_node = null,546 .body_node = null,
517 .lib_name = ctx.lib_name,547 .lib_name = ctx.lib_name,
...@@ -520,144 +550,101 @@ pub const Parser = struct {...@@ -520,144 +550,101 @@ pub const Parser = struct {
520 );550 );
521 stack.append(State { .FnDef = fn_proto }) catch unreachable;551 stack.append(State { .FnDef = fn_proto }) catch unreachable;
522 try stack.append(State { .FnProto = fn_proto });552 try stack.append(State { .FnProto = fn_proto });
523 try stack.append(State {
524 .ExpectTokenSave = ExpectTokenSave {
525 .id = Token.Id.Keyword_fn,
526 .ptr = &fn_proto.fn_token,
527 }
528 });
529 continue;
530 },
531 Token.Id.Keyword_async => {
532 const async_node = try self.createNode(arena, ast.NodeAsyncAttribute,
533 ast.NodeAsyncAttribute {
534 .base = undefined,
535 .async_token = token,
536 .allocator_type = null,
537 .rangle_bracket = null,
538 }
539 );
540553
541 const fn_proto = try self.createAttachNode(arena, ctx.decls, ast.NodeFnProto,554 switch (token.id) {
542 ast.NodeFnProto {555 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
543 .base = undefined,556 fn_proto.cc_token = token;
544 .visib_token = ctx.visib_token,557 try stack.append(State {
545 .name_token = null,558 .ExpectTokenSave = ExpectTokenSave {
546 .fn_token = undefined,559 .id = Token.Id.Keyword_fn,
547 .params = ArrayList(&ast.Node).init(arena),560 .ptr = &fn_proto.fn_token,
548 .return_type = undefined,561 }
549 .var_args_token = null,562 });
550 .extern_export_inline_token = ctx.extern_export_inline_token,563 continue;
551 .cc_token = null,564 },
552 .async_attr = async_node,565 Token.Id.Keyword_async => {
553 .body_node = null,566 const async_node = try self.createNode(arena, ast.Node.AsyncAttribute,
554 .lib_name = ctx.lib_name,567 ast.Node.AsyncAttribute {
555 .align_expr = null,568 .base = undefined,
556 }569 .async_token = token,
557 );570 .allocator_type = null,
558 stack.append(State { .FnDef = fn_proto }) catch unreachable;571 .rangle_bracket = null,
559 try stack.append(State { .FnProto = fn_proto });572 }
560 try stack.append(State {573 );
561 .ExpectTokenSave = ExpectTokenSave {574 fn_proto.async_attr = async_node;
562 .id = Token.Id.Keyword_fn,
563 .ptr = &fn_proto.fn_token,
564 }
565 });
566575
567 const langle_bracket = self.getNextToken();576 try stack.append(State {
568 if (langle_bracket.id != Token.Id.AngleBracketLeft) {577 .ExpectTokenSave = ExpectTokenSave {
569 self.putBackToken(langle_bracket);578 .id = Token.Id.Keyword_fn,
570 continue;579 .ptr = &fn_proto.fn_token,
580 }
581 });
582 try stack.append(State { .AsyncAllocator = async_node });
583 continue;
584 },
585 Token.Id.Keyword_fn => {
586 fn_proto.fn_token = token;
587 continue;
588 },
589 else => unreachable,
571 }590 }
572
573 async_node.rangle_bracket = Token(undefined);
574 try stack.append(State {
575 .ExpectTokenSave = ExpectTokenSave {
576 .id = Token.Id.AngleBracketRight,
577 .ptr = &??async_node.rangle_bracket,
578 }
579 });
580 try stack.append(State { .TypeExprBegin = DestPtr { .NullableField = &async_node.allocator_type } });
581 continue;
582 },591 },
583 else => {592 else => {
584 try self.parseError(&stack, token, "expected variable declaration or function, found {}", @tagName(token.id));593 return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id));
585 continue;
586 },594 },
587 }595 }
588 },596 },
589 State.VarDecl => |var_decl| {597 State.TopLevelExternOrField => |ctx| {
590 stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;598 if (self.eatToken(Token.Id.Identifier)) |identifier| {
591 try stack.append(State { .TypeExprBegin = DestPtr {.NullableField = &var_decl.type_node} });599 std.debug.assert(ctx.container_decl.kind == ast.Node.ContainerDecl.Kind.Struct);
592 try stack.append(State { .IfToken = Token.Id.Colon });600 const node = try self.createAttachNode(arena, &ctx.container_decl.fields_and_decls, ast.Node.StructField,
593 try stack.append(State {601 ast.Node.StructField {
594 .ExpectTokenSave = ExpectTokenSave {602 .base = undefined,
595 .id = Token.Id.Identifier,603 .visib_token = ctx.visib_token,
596 .ptr = &var_decl.name_token,604 .name_token = identifier,
597 }605 .type_expr = undefined,
598 });606 }
599 continue;607 );
600 },
601 State.VarDeclAlign => |var_decl| {
602 stack.append(State { .VarDeclEq = var_decl }) catch unreachable;
603608
604 const next_token = self.getNextToken();609 stack.append(State { .FieldListCommaOrEnd = ctx.container_decl }) catch unreachable;
605 if (next_token.id == Token.Id.Keyword_align) {610 try stack.append(State { .Expression = OptionalCtx { .Required = &node.type_expr } });
606 try stack.append(State { .ExpectToken = Token.Id.RParen });611 try stack.append(State { .ExpectToken = Token.Id.Colon });
607 try stack.append(State { .Expression = DestPtr{.NullableField = &var_decl.align_node} });
608 try stack.append(State { .ExpectToken = Token.Id.LParen });
609 continue;612 continue;
610 }613 }
611614
612 self.putBackToken(next_token);615 stack.append(State{ .ContainerDecl = ctx.container_decl }) catch unreachable;
613 continue;616 try stack.append(State {
614 },617 .TopLevelExtern = TopLevelDeclCtx {
615 State.VarDeclEq => |var_decl| {618 .decls = &ctx.container_decl.fields_and_decls,
616 const token = self.getNextToken();619 .visib_token = ctx.visib_token,
617 switch (token.id) {620 .extern_export_inline_token = null,
618 Token.Id.Equal => {621 .lib_name = null,
619 var_decl.eq_token = token;
620 stack.append(State {
621 .ExpectTokenSave = ExpectTokenSave {
622 .id = Token.Id.Semicolon,
623 .ptr = &var_decl.semicolon_token,
624 },
625 }) catch unreachable;
626 try stack.append(State { .Expression = DestPtr {.NullableField = &var_decl.init_node} });
627 continue;
628 },
629 Token.Id.Semicolon => {
630 var_decl.semicolon_token = token;
631 continue;
632 },
633 else => {
634 try self.parseError(&stack, token, "expected '=' or ';', found {}", @tagName(token.id));
635 continue;
636 }622 }
637 }623 });
624 continue;
638 },625 },
639626
640 State.ContainerExtern => |ctx| {627
628 State.ContainerKind => |ctx| {
641 const token = self.getNextToken();629 const token = self.getNextToken();
642 const node = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeContainerDecl,630 const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ContainerDecl,
643 ast.NodeContainerDecl {631 ast.Node.ContainerDecl {
644 .base = undefined,632 .base = undefined,
645 .ltoken = ctx.ltoken,633 .ltoken = ctx.ltoken,
646 .layout = ctx.layout,634 .layout = ctx.layout,
647 .kind = switch (token.id) {635 .kind = switch (token.id) {
648 Token.Id.Keyword_struct => ast.NodeContainerDecl.Kind.Struct,636 Token.Id.Keyword_struct => ast.Node.ContainerDecl.Kind.Struct,
649 Token.Id.Keyword_union => ast.NodeContainerDecl.Kind.Union,637 Token.Id.Keyword_union => ast.Node.ContainerDecl.Kind.Union,
650 Token.Id.Keyword_enum => ast.NodeContainerDecl.Kind.Enum,638 Token.Id.Keyword_enum => ast.Node.ContainerDecl.Kind.Enum,
651 else => {639 else => {
652 try self.parseError(&stack, token, "expected {}, {} or {}, found {}",640 return self.parseError(token, "expected {}, {} or {}, found {}",
653 @tagName(Token.Id.Keyword_struct),641 @tagName(Token.Id.Keyword_struct),
654 @tagName(Token.Id.Keyword_union),642 @tagName(Token.Id.Keyword_union),
655 @tagName(Token.Id.Keyword_enum),643 @tagName(Token.Id.Keyword_enum),
656 @tagName(token.id));644 @tagName(token.id));
657 continue;
658 },645 },
659 },646 },
660 .init_arg_expr = undefined,647 .init_arg_expr = ast.Node.ContainerDecl.InitArg.None,
661 .fields_and_decls = ArrayList(&ast.Node).init(arena),648 .fields_and_decls = ArrayList(&ast.Node).init(arena),
662 .rbrace_token = undefined,649 .rbrace_token = undefined,
663 }650 }
...@@ -665,43 +652,42 @@ pub const Parser = struct {...@@ -665,43 +652,42 @@ pub const Parser = struct {
665652
666 stack.append(State { .ContainerDecl = node }) catch unreachable;653 stack.append(State { .ContainerDecl = node }) catch unreachable;
667 try stack.append(State { .ExpectToken = Token.Id.LBrace });654 try stack.append(State { .ExpectToken = Token.Id.LBrace });
655 try stack.append(State { .ContainerInitArgStart = node });
656 continue;
657 },
668658
669 const lparen = self.getNextToken();659 State.ContainerInitArgStart => |container_decl| {
670 if (lparen.id != Token.Id.LParen) {660 if (self.eatToken(Token.Id.LParen) == null) {
671 self.putBackToken(lparen);
672 node.init_arg_expr = ast.NodeContainerDecl.InitArg.None;
673 continue;661 continue;
674 }662 }
675663
676 try stack.append(State { .ExpectToken = Token.Id.RParen });664 stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
665 try stack.append(State { .ContainerInitArg = container_decl });
666 continue;
667 },
677668
669 State.ContainerInitArg => |container_decl| {
678 const init_arg_token = self.getNextToken();670 const init_arg_token = self.getNextToken();
679 switch (init_arg_token.id) {671 switch (init_arg_token.id) {
680 Token.Id.Keyword_enum => {672 Token.Id.Keyword_enum => {
681 node.init_arg_expr = ast.NodeContainerDecl.InitArg.Enum;673 container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg.Enum;
682 },674 },
683 else => {675 else => {
684 self.putBackToken(init_arg_token);676 self.putBackToken(init_arg_token);
685 node.init_arg_expr = ast.NodeContainerDecl.InitArg { .Type = undefined };677 container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg { .Type = undefined };
686 try stack.append(State {678 stack.append(State { .Expression = OptionalCtx { .Required = &container_decl.init_arg_expr.Type } }) catch unreachable;
687 .Expression = DestPtr {
688 .Field = &node.init_arg_expr.Type
689 }
690 });
691 },679 },
692 }680 }
693 continue;681 continue;
694 },682 },
695
696 State.ContainerDecl => |container_decl| {683 State.ContainerDecl => |container_decl| {
697 const token = self.getNextToken();684 const token = self.getNextToken();
698
699 switch (token.id) {685 switch (token.id) {
700 Token.Id.Identifier => {686 Token.Id.Identifier => {
701 switch (container_decl.kind) {687 switch (container_decl.kind) {
702 ast.NodeContainerDecl.Kind.Struct => {688 ast.Node.ContainerDecl.Kind.Struct => {
703 const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeStructField,689 const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.StructField,
704 ast.NodeStructField {690 ast.Node.StructField {
705 .base = undefined,691 .base = undefined,
706 .visib_token = null,692 .visib_token = null,
707 .name_token = token,693 .name_token = token,
...@@ -710,13 +696,13 @@ pub const Parser = struct {...@@ -710,13 +696,13 @@ pub const Parser = struct {
710 );696 );
711697
712 stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;698 stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
713 try stack.append(State { .Expression = DestPtr { .Field = &node.type_expr } });699 try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.type_expr } });
714 try stack.append(State { .ExpectToken = Token.Id.Colon });700 try stack.append(State { .ExpectToken = Token.Id.Colon });
715 continue;701 continue;
716 },702 },
717 ast.NodeContainerDecl.Kind.Union => {703 ast.Node.ContainerDecl.Kind.Union => {
718 const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeUnionTag,704 const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.UnionTag,
719 ast.NodeUnionTag {705 ast.Node.UnionTag {
720 .base = undefined,706 .base = undefined,
721 .name_token = token,707 .name_token = token,
722 .type_expr = null,708 .type_expr = null,
...@@ -724,19 +710,13 @@ pub const Parser = struct {...@@ -724,19 +710,13 @@ pub const Parser = struct {
724 );710 );
725711
726 stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;712 stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
727713 try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &node.type_expr } });
728 const next = self.getNextToken();714 try stack.append(State { .IfToken = Token.Id.Colon });
729 if (next.id != Token.Id.Colon) {
730 self.putBackToken(next);
731 continue;
732 }
733
734 try stack.append(State { .Expression = DestPtr { .NullableField = &node.type_expr } });
735 continue;715 continue;
736 },716 },
737 ast.NodeContainerDecl.Kind.Enum => {717 ast.Node.ContainerDecl.Kind.Enum => {
738 const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeEnumTag,718 const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.Node.EnumTag,
739 ast.NodeEnumTag {719 ast.Node.EnumTag {
740 .base = undefined,720 .base = undefined,
741 .name_token = token,721 .name_token = token,
742 .value = null,722 .value = null,
...@@ -744,52 +724,36 @@ pub const Parser = struct {...@@ -744,52 +724,36 @@ pub const Parser = struct {
744 );724 );
745725
746 stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;726 stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;
747727 try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &node.value } });
748 const next = self.getNextToken();728 try stack.append(State { .IfToken = Token.Id.Equal });
749 if (next.id != Token.Id.Equal) {
750 self.putBackToken(next);
751 continue;
752 }
753
754 try stack.append(State { .Expression = DestPtr { .NullableField = &node.value } });
755 continue;729 continue;
756 },730 },
757 }731 }
758 },732 },
759 Token.Id.Keyword_pub => {733 Token.Id.Keyword_pub => {
760 if (self.eatToken(Token.Id.Identifier)) |identifier| {734 switch (container_decl.kind) {
761 switch (container_decl.kind) {735 ast.Node.ContainerDecl.Kind.Struct => {
762 ast.NodeContainerDecl.Kind.Struct => {736 try stack.append(State {
763 const node = try self.createAttachNode(arena, &container_decl.fields_and_decls, ast.NodeStructField,737 .TopLevelExternOrField = TopLevelExternOrFieldCtx {
764 ast.NodeStructField {738 .visib_token = token,
765 .base = undefined,739 .container_decl = container_decl,
766 .visib_token = token,740 }
767 .name_token = identifier,741 });
768 .type_expr = undefined,742 continue;
769 }743 },
770 );744 else => {
771745 stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
772 stack.append(State { .FieldListCommaOrEnd = container_decl }) catch unreachable;746 try stack.append(State {
773 try stack.append(State { .Expression = DestPtr { .Field = &node.type_expr } });747 .TopLevelExtern = TopLevelDeclCtx {
774 try stack.append(State { .ExpectToken = Token.Id.Colon });748 .decls = &container_decl.fields_and_decls,
775 continue;749 .visib_token = token,
776 },750 .extern_export_inline_token = null,
777 else => {751 .lib_name = null,
778 self.putBackToken(identifier);752 }
779 }753 });
780 }754 continue;
781 }
782
783 stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
784 try stack.append(State {
785 .TopLevelExtern = TopLevelDeclCtx {
786 .decls = &container_decl.fields_and_decls,
787 .visib_token = token,
788 .extern_export_inline_token = null,
789 .lib_name = null,
790 }755 }
791 });756 }
792 continue;
793 },757 },
794 Token.Id.Keyword_export => {758 Token.Id.Keyword_export => {
795 stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;759 stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
...@@ -823,1054 +787,845 @@ pub const Parser = struct {...@@ -823,1054 +787,845 @@ pub const Parser = struct {
823 }787 }
824 },788 },
825789
826 State.ExpectToken => |token_id| {
827 _ = (try self.expectToken(&stack, token_id)) ?? continue;
828 continue;
829 },
830790
831 State.ExpectTokenSave => |expect_token_save| {791 State.VarDecl => |ctx| {
832 *expect_token_save.ptr = (try self.expectToken(&stack, expect_token_save.id)) ?? continue;792 const var_decl = try self.createAttachNode(arena, ctx.list, ast.Node.VarDecl,
833 continue;793 ast.Node.VarDecl {
834 },794 .base = undefined,
795 .comments = ctx.comments,
796 .visib_token = ctx.visib_token,
797 .mut_token = ctx.mut_token,
798 .comptime_token = ctx.comptime_token,
799 .extern_export_token = ctx.extern_export_token,
800 .type_node = null,
801 .align_node = null,
802 .init_node = null,
803 .lib_name = ctx.lib_name,
804 // initialized later
805 .name_token = undefined,
806 .eq_token = undefined,
807 .semicolon_token = undefined,
808 }
809 );
835810
836 State.IfToken => |token_id| {811 stack.append(State { .VarDeclAlign = var_decl }) catch unreachable;
837 const token = self.getNextToken();812 try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &var_decl.type_node} });
838 if (@TagType(Token.Id)(token.id) != token_id) {813 try stack.append(State { .IfToken = Token.Id.Colon });
839 self.putBackToken(token);814 try stack.append(State {
840 _ = stack.pop();815 .ExpectTokenSave = ExpectTokenSave {
841 continue;816 .id = Token.Id.Identifier,
842 }817 .ptr = &var_decl.name_token,
818 }
819 });
843 continue;820 continue;
844 },821 },
822 State.VarDeclAlign => |var_decl| {
823 stack.append(State { .VarDeclEq = var_decl }) catch unreachable;
845824
846 State.IfTokenSave => |if_token_save| {825 const next_token = self.getNextToken();
847 const token = self.getNextToken();826 if (next_token.id == Token.Id.Keyword_align) {
848 if (@TagType(Token.Id)(token.id) != if_token_save.id) {827 try stack.append(State { .ExpectToken = Token.Id.RParen });
849 self.putBackToken(token);828 try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.align_node} });
850 _ = stack.pop();829 try stack.append(State { .ExpectToken = Token.Id.LParen });
851 continue;830 continue;
852 }831 }
853832
854 *if_token_save.ptr = token;833 self.putBackToken(next_token);
855 continue;834 continue;
856 },835 },
857836 State.VarDeclEq => |var_decl| {
858 State.Optional => { },
859
860 State.Expression => |dest_ptr| {
861 const token = self.getNextToken();837 const token = self.getNextToken();
862 switch (token.id) {838 switch (token.id) {
863 Token.Id.Keyword_return => {839 Token.Id.Equal => {
864 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeControlFlowExpression,840 var_decl.eq_token = token;
865 ast.NodeControlFlowExpression {
866 .base = undefined,
867 .ltoken = token,
868 .kind = ast.NodeControlFlowExpression.Kind.Return,
869 .rhs = undefined,
870 }
871 );
872
873 // TODO: Find another way to do optional expressions
874 stack.append(State {
875 .Optional = RevertState {
876 .parser = *self,
877 .tokenizer = *self.tokenizer,
878 .ptr = &node.rhs,
879 }
880 }) catch unreachable;
881 try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } });
882 continue;
883 },
884 Token.Id.Keyword_break, Token.Id.Keyword_continue => {
885 const label = blk: {
886 const colon = self.getNextToken();
887 if (colon.id != Token.Id.Colon) {
888 self.putBackToken(colon);
889 break :blk null;
890 }
891
892 break :blk (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;
893 };
894
895 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeControlFlowExpression,
896 ast.NodeControlFlowExpression {
897 .base = undefined,
898 .ltoken = token,
899 .kind = switch (token.id) {
900 Token.Id.Keyword_break => ast.NodeControlFlowExpression.Kind { .Break = label },
901 Token.Id.Keyword_continue => ast.NodeControlFlowExpression.Kind { .Continue = label },
902 else => unreachable,
903 },
904 .rhs = undefined,
905 }
906 );
907
908 // TODO: Find another way to do optional expressions
909 stack.append(State {841 stack.append(State {
910 .Optional = RevertState {842 .ExpectTokenSave = ExpectTokenSave {
911 .parser = *self,843 .id = Token.Id.Semicolon,
912 .tokenizer = *self.tokenizer,844 .ptr = &var_decl.semicolon_token,
913 .ptr = &node.rhs,845 },
914 }
915 }) catch unreachable;846 }) catch unreachable;
916 try stack.append(State { .Expression = DestPtr { .NullableField = &node.rhs } });847 try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &var_decl.init_node } });
917 continue;848 continue;
918 },849 },
919 Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => {850 Token.Id.Semicolon => {
920 const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,851 var_decl.semicolon_token = token;
921 ast.NodePrefixOp {
922 .base = undefined,
923 .op_token = token,
924 .op = switch (token.id) {
925 Token.Id.Keyword_try => ast.NodePrefixOp.PrefixOp { .Try = void{} },
926 Token.Id.Keyword_cancel => ast.NodePrefixOp.PrefixOp { .Cancel = void{} },
927 Token.Id.Keyword_resume => ast.NodePrefixOp.PrefixOp { .Resume = void{} },
928 else => unreachable,
929 },
930 .rhs = undefined,
931 }
932 );
933
934 stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
935 continue;852 continue;
936 },853 },
937 else => {854 else => {
938 if (!try self.parseBlockExpr(&stack, arena, dest_ptr, token)) {855 return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
939 self.putBackToken(token);
940 stack.append(State { .UnwrapExpressionBegin = dest_ptr }) catch unreachable;
941 }
942 continue;
943 }856 }
944 }857 }
945 },858 },
946859
947 State.RangeExpressionBegin => |dest_ptr| {
948 stack.append(State { .RangeExpressionEnd = dest_ptr }) catch unreachable;
949 try stack.append(State { .Expression = dest_ptr });
950 continue;
951 },
952
953 State.RangeExpressionEnd => |dest_ptr| {
954 if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| {
955 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
956 ast.NodeInfixOp {
957 .base = undefined,
958 .lhs = dest_ptr.get(),
959 .op_token = ellipsis3,
960 .op = ast.NodeInfixOp.InfixOp.Range,
961 .rhs = undefined,
962 }
963 );
964 stack.append(State { .Expression = DestPtr { .Field = &node.rhs } }) catch unreachable;
965 }
966
967 continue;
968 },
969
970 State.AssignmentExpressionBegin => |dest_ptr| {
971 stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
972 try stack.append(State { .Expression = dest_ptr });
973 continue;
974 },
975
976 State.AssignmentExpressionEnd => |dest_ptr| {
977 const token = self.getNextToken();
978 if (tokenIdToAssignment(token.id)) |ass_id| {
979 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
980 ast.NodeInfixOp {
981 .base = undefined,
982 .lhs = dest_ptr.get(),
983 .op_token = token,
984 .op = ass_id,
985 .rhs = undefined,
986 }
987 );
988 stack.append(State { .AssignmentExpressionEnd = dest_ptr }) catch unreachable;
989 try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
990 continue;
991 } else {
992 self.putBackToken(token);
993 continue;
994 }
995 },
996
997 State.UnwrapExpressionBegin => |dest_ptr| {
998 stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;
999 try stack.append(State { .BoolOrExpressionBegin = dest_ptr });
1000 continue;
1001 },
1002860
1003 State.UnwrapExpressionEnd => |dest_ptr| {861 State.FnDef => |fn_proto| {
1004 const token = self.getNextToken();862 const token = self.getNextToken();
1005 switch (token.id) {863 switch(token.id) {
1006 Token.Id.Keyword_catch, Token.Id.QuestionMarkQuestionMark => {864 Token.Id.LBrace => {
1007 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,865 const block = try self.createNode(arena, ast.Node.Block,
1008 ast.NodeInfixOp {866 ast.Node.Block {
1009 .base = undefined,867 .base = undefined,
1010 .lhs = dest_ptr.get(),868 .label = null,
1011 .op_token = token,869 .lbrace = token,
1012 .op = switch (token.id) {870 .statements = ArrayList(&ast.Node).init(arena),
1013 Token.Id.Keyword_catch => ast.NodeInfixOp.InfixOp { .Catch = null },871 .rbrace = undefined,
1014 Token.Id.QuestionMarkQuestionMark => ast.NodeInfixOp.InfixOp { .UnwrapMaybe = void{} },
1015 else => unreachable,
1016 },
1017 .rhs = undefined,
1018 }872 }
1019 );873 );
1020874 fn_proto.body_node = &block.base;
1021 stack.append(State { .UnwrapExpressionEnd = dest_ptr }) catch unreachable;875 stack.append(State { .Block = block }) catch unreachable;
1022 try stack.append(State { .Expression = DestPtr { .Field = &node.rhs } });
1023
1024 if (node.op == ast.NodeInfixOp.InfixOp.Catch) {
1025 try stack.append(State { .Payload = &node.op.Catch });
1026 }
1027 continue;876 continue;
1028 },877 },
878 Token.Id.Semicolon => continue,
1029 else => {879 else => {
1030 self.putBackToken(token);880 return self.parseError(token, "expected ';' or '{{', found {}", @tagName(token.id));
1031 continue;
1032 },881 },
1033 }882 }
1034 },883 },
884 State.FnProto => |fn_proto| {
885 stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable;
886 try stack.append(State { .ParamDecl = fn_proto });
887 try stack.append(State { .ExpectToken = Token.Id.LParen });
1035888
1036 State.BoolOrExpressionBegin => |dest_ptr| {889 if (self.eatToken(Token.Id.Identifier)) |name_token| {
1037 stack.append(State { .BoolOrExpressionEnd = dest_ptr }) catch unreachable;890 fn_proto.name_token = name_token;
1038 try stack.append(State { .BoolAndExpressionBegin = dest_ptr });891 }
1039 continue;892 continue;
1040 },893 },
894 State.FnProtoAlign => |fn_proto| {
895 stack.append(State { .FnProtoReturnType = fn_proto }) catch unreachable;
1041896
1042 State.BoolOrExpressionEnd => |dest_ptr| {897 if (self.eatToken(Token.Id.Keyword_align)) |align_token| {
898 try stack.append(State { .ExpectToken = Token.Id.RParen });
899 try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &fn_proto.align_expr } });
900 try stack.append(State { .ExpectToken = Token.Id.LParen });
901 }
902 continue;
903 },
904 State.FnProtoReturnType => |fn_proto| {
1043 const token = self.getNextToken();905 const token = self.getNextToken();
1044 switch (token.id) {906 switch (token.id) {
1045 Token.Id.Keyword_or => {907 Token.Id.Bang => {
1046 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,908 fn_proto.return_type = ast.Node.FnProto.ReturnType { .InferErrorSet = undefined };
1047 ast.NodeInfixOp {909 stack.append(State {
1048 .base = undefined,910 .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.InferErrorSet },
1049 .lhs = dest_ptr.get(),911 }) catch unreachable;
1050 .op_token = token,
1051 .op = ast.NodeInfixOp.InfixOp.BoolOr,
1052 .rhs = undefined,
1053 }
1054 );
1055 stack.append(State { .BoolOrExpressionEnd = dest_ptr }) catch unreachable;
1056 try stack.append(State { .BoolAndExpressionBegin = DestPtr { .Field = &node.rhs } });
1057 continue;912 continue;
1058 },913 },
1059 else => {914 else => {
915 // TODO: this is a special case. Remove this when #760 is fixed
916 if (token.id == Token.Id.Keyword_error) {
917 if (self.isPeekToken(Token.Id.LBrace)) {
918 fn_proto.return_type = ast.Node.FnProto.ReturnType {
919 .Explicit = &(try self.createLiteral(arena, ast.Node.ErrorType, token)).base
920 };
921 continue;
922 }
923 }
924
1060 self.putBackToken(token);925 self.putBackToken(token);
926 fn_proto.return_type = ast.Node.FnProto.ReturnType { .Explicit = undefined };
927 stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &fn_proto.return_type.Explicit }, }) catch unreachable;
1061 continue;928 continue;
1062 },929 },
1063 }930 }
1064 },931 },
1065932
1066 State.BoolAndExpressionBegin => |dest_ptr| {
1067 stack.append(State { .BoolAndExpressionEnd = dest_ptr }) catch unreachable;
1068 try stack.append(State { .ComparisonExpressionBegin = dest_ptr });
1069 continue;
1070 },
1071933
1072 State.BoolAndExpressionEnd => |dest_ptr| {934 State.ParamDecl => |fn_proto| {
1073 const token = self.getNextToken();935 if (self.eatToken(Token.Id.RParen)) |_| {
1074 switch (token.id) {936 continue;
1075 Token.Id.Keyword_and => {937 }
1076 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,938 const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.Node.ParamDecl,
1077 ast.NodeInfixOp {939 ast.Node.ParamDecl {
1078 .base = undefined,940 .base = undefined,
1079 .lhs = dest_ptr.get(),941 .comptime_token = null,
1080 .op_token = token,942 .noalias_token = null,
1081 .op = ast.NodeInfixOp.InfixOp.BoolAnd,943 .name_token = null,
1082 .rhs = undefined,944 .type_node = undefined,
1083 }945 .var_args_token = null,
1084 );
1085 stack.append(State { .BoolAndExpressionEnd = dest_ptr }) catch unreachable;
1086 try stack.append(State { .ComparisonExpressionBegin = DestPtr { .Field = &node.rhs } });
1087 continue;
1088 },
1089 else => {
1090 self.putBackToken(token);
1091 continue;
1092 },946 },
947 );
948
949 stack.append(State {
950 .ParamDeclEnd = ParamDeclEndCtx {
951 .param_decl = param_decl,
952 .fn_proto = fn_proto,
953 }
954 }) catch unreachable;
955 try stack.append(State { .ParamDeclName = param_decl });
956 try stack.append(State { .ParamDeclAliasOrComptime = param_decl });
957 continue;
958 },
959 State.ParamDeclAliasOrComptime => |param_decl| {
960 if (self.eatToken(Token.Id.Keyword_comptime)) |comptime_token| {
961 param_decl.comptime_token = comptime_token;
962 } else if (self.eatToken(Token.Id.Keyword_noalias)) |noalias_token| {
963 param_decl.noalias_token = noalias_token;
964 }
965 continue;
966 },
967 State.ParamDeclName => |param_decl| {
968 // TODO: Here, we eat two tokens in one state. This means that we can't have
969 // comments between these two tokens.
970 if (self.eatToken(Token.Id.Identifier)) |ident_token| {
971 if (self.eatToken(Token.Id.Colon)) |_| {
972 param_decl.name_token = ident_token;
973 } else {
974 self.putBackToken(ident_token);
975 }
1093 }976 }
977 continue;
1094 },978 },
979 State.ParamDeclEnd => |ctx| {
980 if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| {
981 ctx.param_decl.var_args_token = ellipsis3;
982 stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
983 continue;
984 }
1095985
1096 State.ComparisonExpressionBegin => |dest_ptr| {986 try stack.append(State { .ParamDeclComma = ctx.fn_proto });
1097 stack.append(State { .ComparisonExpressionEnd = dest_ptr }) catch unreachable;987 try stack.append(State {
1098 try stack.append(State { .BinaryOrExpressionBegin = dest_ptr });988 .TypeExprBegin = OptionalCtx { .Required = &ctx.param_decl.type_node }
989 });
990 continue;
991 },
992 State.ParamDeclComma => |fn_proto| {
993 if ((try self.expectCommaOrEnd(Token.Id.RParen)) == null) {
994 stack.append(State { .ParamDecl = fn_proto }) catch unreachable;
995 }
1099 continue;996 continue;
1100 },997 },
1101998
1102 State.ComparisonExpressionEnd => |dest_ptr| {999 State.MaybeLabeledExpression => |ctx| {
1103 const token = self.getNextToken();1000 if (self.eatToken(Token.Id.Colon)) |_| {
1104 if (tokenIdToComparison(token.id)) |comp_id| {1001 stack.append(State {
1105 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,1002 .LabeledExpression = LabelCtx {
1106 ast.NodeInfixOp {1003 .label = ctx.label,
1107 .base = undefined,1004 .opt_ctx = ctx.opt_ctx,
1108 .lhs = dest_ptr.get(),
1109 .op_token = token,
1110 .op = comp_id,
1111 .rhs = undefined,
1112 }1005 }
1113 );1006 }) catch unreachable;
1114 stack.append(State { .ComparisonExpressionEnd = dest_ptr }) catch unreachable;
1115 try stack.append(State { .BinaryOrExpressionBegin = DestPtr { .Field = &node.rhs } });
1116 continue;
1117 } else {
1118 self.putBackToken(token);
1119 continue;1007 continue;
1120 }1008 }
1121 },
11221009
1123 State.BinaryOrExpressionBegin => |dest_ptr| {1010 _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.Identifier, ctx.label);
1124 stack.append(State { .BinaryOrExpressionEnd = dest_ptr }) catch unreachable;
1125 try stack.append(State { .BinaryXorExpressionBegin = dest_ptr });
1126 continue;1011 continue;
1127 },1012 },
11281013 State.LabeledExpression => |ctx| {
1129 State.BinaryOrExpressionEnd => |dest_ptr| {
1130 const token = self.getNextToken();1014 const token = self.getNextToken();
1131 switch (token.id) {1015 switch (token.id) {
1132 Token.Id.Pipe => {1016 Token.Id.LBrace => {
1133 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,1017 const block = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.Block,
1134 ast.NodeInfixOp {1018 ast.Node.Block {
1135 .base = undefined,1019 .base = undefined,
1136 .lhs = dest_ptr.get(),1020 .label = ctx.label,
1137 .op_token = token,1021 .lbrace = token,
1138 .op = ast.NodeInfixOp.InfixOp.BitOr,1022 .statements = ArrayList(&ast.Node).init(arena),
1139 .rhs = undefined,1023 .rbrace = undefined,
1140 }1024 }
1141 );1025 );
1142 stack.append(State { .BinaryOrExpressionEnd = dest_ptr }) catch unreachable;1026 stack.append(State { .Block = block }) catch unreachable;
1143 try stack.append(State { .BinaryXorExpressionBegin = DestPtr { .Field = &node.rhs } });
1144 continue;1027 continue;
1145 },1028 },
1146 else => {1029 Token.Id.Keyword_while => {
1147 self.putBackToken(token);1030 stack.append(State {
1031 .While = LoopCtx {
1032 .label = ctx.label,
1033 .inline_token = null,
1034 .loop_token = token,
1035 .opt_ctx = ctx.opt_ctx.toRequired(),
1036 }
1037 }) catch unreachable;
1148 continue;1038 continue;
1149 },1039 },
1150 }1040 Token.Id.Keyword_for => {
1151 },1041 stack.append(State {
11521042 .For = LoopCtx {
1153 State.BinaryXorExpressionBegin => |dest_ptr| {1043 .label = ctx.label,
1154 stack.append(State { .BinaryXorExpressionEnd = dest_ptr }) catch unreachable;1044 .inline_token = null,
1155 try stack.append(State { .BinaryAndExpressionBegin = dest_ptr });1045 .loop_token = token,
1156 continue;1046 .opt_ctx = ctx.opt_ctx.toRequired(),
1157 },
1158
1159 State.BinaryXorExpressionEnd => |dest_ptr| {
1160 const token = self.getNextToken();
1161 switch (token.id) {
1162 Token.Id.Caret => {
1163 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
1164 ast.NodeInfixOp {
1165 .base = undefined,
1166 .lhs = dest_ptr.get(),
1167 .op_token = token,
1168 .op = ast.NodeInfixOp.InfixOp.BitXor,
1169 .rhs = undefined,
1170 }1047 }
1171 );1048 }) catch unreachable;
1172 stack.append(State { .BinaryXorExpressionEnd = dest_ptr }) catch unreachable;1049 continue;
1173 try stack.append(State { .BinaryAndExpressionBegin = DestPtr { .Field = &node.rhs } });1050 },
1051 Token.Id.Keyword_inline => {
1052 stack.append(State {
1053 .Inline = InlineCtx {
1054 .label = ctx.label,
1055 .inline_token = token,
1056 .opt_ctx = ctx.opt_ctx.toRequired(),
1057 }
1058 }) catch unreachable;
1174 continue;1059 continue;
1175 },1060 },
1176 else => {1061 else => {
1062 if (ctx.opt_ctx != OptionalCtx.Optional) {
1063 return self.parseError(token, "expected 'while', 'for', 'inline' or '{{', found {}", @tagName(token.id));
1064 }
1065
1177 self.putBackToken(token);1066 self.putBackToken(token);
1178 continue;1067 continue;
1179 },1068 },
1180 }1069 }
1181 },1070 },
11821071 State.Inline => |ctx| {
1183 State.BinaryAndExpressionBegin => |dest_ptr| {
1184 stack.append(State { .BinaryAndExpressionEnd = dest_ptr }) catch unreachable;
1185 try stack.append(State { .BitShiftExpressionBegin = dest_ptr });
1186 continue;
1187 },
1188
1189 State.BinaryAndExpressionEnd => |dest_ptr| {
1190 const token = self.getNextToken();1072 const token = self.getNextToken();
1191 switch (token.id) {1073 switch (token.id) {
1192 Token.Id.Ampersand => {1074 Token.Id.Keyword_while => {
1193 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,1075 stack.append(State {
1194 ast.NodeInfixOp {1076 .While = LoopCtx {
1195 .base = undefined,1077 .inline_token = ctx.inline_token,
1196 .lhs = dest_ptr.get(),1078 .label = ctx.label,
1197 .op_token = token,1079 .loop_token = token,
1198 .op = ast.NodeInfixOp.InfixOp.BitAnd,1080 .opt_ctx = ctx.opt_ctx.toRequired(),
1199 .rhs = undefined,
1200 }1081 }
1201 );1082 }) catch unreachable;
1202 stack.append(State { .BinaryAndExpressionEnd = dest_ptr }) catch unreachable;1083 continue;
1203 try stack.append(State { .BitShiftExpressionBegin = DestPtr { .Field = &node.rhs } });1084 },
1085 Token.Id.Keyword_for => {
1086 stack.append(State {
1087 .For = LoopCtx {
1088 .inline_token = ctx.inline_token,
1089 .label = ctx.label,
1090 .loop_token = token,
1091 .opt_ctx = ctx.opt_ctx.toRequired(),
1092 }
1093 }) catch unreachable;
1204 continue;1094 continue;
1205 },1095 },
1206 else => {1096 else => {
1097 if (ctx.opt_ctx != OptionalCtx.Optional) {
1098 return self.parseError(token, "expected 'while' or 'for', found {}", @tagName(token.id));
1099 }
1100
1207 self.putBackToken(token);1101 self.putBackToken(token);
1208 continue;1102 continue;
1209 },1103 },
1210 }1104 }
1211 },1105 },
12121106 State.While => |ctx| {
1213 State.BitShiftExpressionBegin => |dest_ptr| {1107 const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.While,
1214 stack.append(State { .BitShiftExpressionEnd = dest_ptr }) catch unreachable;1108 ast.Node.While {
1215 try stack.append(State { .AdditionExpressionBegin = dest_ptr });1109 .base = undefined,
1110 .label = ctx.label,
1111 .inline_token = ctx.inline_token,
1112 .while_token = ctx.loop_token,
1113 .condition = undefined,
1114 .payload = null,
1115 .continue_expr = null,
1116 .body = undefined,
1117 .@"else" = null,
1118 }
1119 );
1120 stack.append(State { .Else = &node.@"else" }) catch unreachable;
1121 try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } });
1122 try stack.append(State { .WhileContinueExpr = &node.continue_expr });
1123 try stack.append(State { .IfToken = Token.Id.Colon });
1124 try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
1125 try stack.append(State { .ExpectToken = Token.Id.RParen });
1126 try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } });
1127 try stack.append(State { .ExpectToken = Token.Id.LParen });
1216 continue;1128 continue;
1217 },1129 },
12181130 State.WhileContinueExpr => |dest| {
1219 State.BitShiftExpressionEnd => |dest_ptr| {1131 stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
1220 const token = self.getNextToken();1132 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = dest } });
1221 if (tokenIdToBitShift(token.id)) |bitshift_id| {1133 try stack.append(State { .ExpectToken = Token.Id.LParen });
1222 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
1223 ast.NodeInfixOp {
1224 .base = undefined,
1225 .lhs = dest_ptr.get(),
1226 .op_token = token,
1227 .op = bitshift_id,
1228 .rhs = undefined,
1229 }
1230 );
1231 stack.append(State { .BitShiftExpressionEnd = dest_ptr }) catch unreachable;
1232 try stack.append(State { .AdditionExpressionBegin = DestPtr { .Field = &node.rhs } });
1233 continue;
1234 } else {
1235 self.putBackToken(token);
1236 continue;
1237 }
1238 },
1239
1240 State.AdditionExpressionBegin => |dest_ptr| {
1241 stack.append(State { .AdditionExpressionEnd = dest_ptr }) catch unreachable;
1242 try stack.append(State { .MultiplyExpressionBegin = dest_ptr });
1243 continue;1134 continue;
1244 },1135 },
12451136 State.For => |ctx| {
1246 State.AdditionExpressionEnd => |dest_ptr| {1137 const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.For,
1247 const token = self.getNextToken();1138 ast.Node.For {
1248 if (tokenIdToAddition(token.id)) |add_id| {1139 .base = undefined,
1249 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,1140 .label = ctx.label,
1250 ast.NodeInfixOp {1141 .inline_token = ctx.inline_token,
1251 .base = undefined,1142 .for_token = ctx.loop_token,
1252 .lhs = dest_ptr.get(),1143 .array_expr = undefined,
1253 .op_token = token,1144 .payload = null,
1254 .op = add_id,1145 .body = undefined,
1255 .rhs = undefined,1146 .@"else" = null,
1256 }1147 }
1257 );1148 );
1258 stack.append(State { .AdditionExpressionEnd = dest_ptr }) catch unreachable;1149 stack.append(State { .Else = &node.@"else" }) catch unreachable;
1259 try stack.append(State { .MultiplyExpressionBegin = DestPtr { .Field = &node.rhs } });1150 try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } });
1260 continue;1151 try stack.append(State { .PointerIndexPayload = OptionalCtx { .Optional = &node.payload } });
1261 } else {1152 try stack.append(State { .ExpectToken = Token.Id.RParen });
1262 self.putBackToken(token);1153 try stack.append(State { .Expression = OptionalCtx { .Required = &node.array_expr } });
1263 continue;1154 try stack.append(State { .ExpectToken = Token.Id.LParen });
1264 }
1265 },
1266
1267 State.MultiplyExpressionBegin => |dest_ptr| {
1268 stack.append(State { .MultiplyExpressionEnd = dest_ptr }) catch unreachable;
1269 try stack.append(State { .CurlySuffixExpressionBegin = dest_ptr });
1270 continue;1155 continue;
1271 },1156 },
12721157 State.Else => |dest| {
1273 State.MultiplyExpressionEnd => |dest_ptr| {1158 if (self.eatToken(Token.Id.Keyword_else)) |else_token| {
1274 const token = self.getNextToken();1159 const node = try self.createNode(arena, ast.Node.Else,
1275 if (tokenIdToMultiply(token.id)) |mult_id| {1160 ast.Node.Else {
1276 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,
1277 ast.NodeInfixOp {
1278 .base = undefined,1161 .base = undefined,
1279 .lhs = dest_ptr.get(),1162 .else_token = else_token,
1280 .op_token = token,1163 .payload = null,
1281 .op = mult_id,1164 .body = undefined,
1282 .rhs = undefined,
1283 }1165 }
1284 );1166 );
1285 stack.append(State { .MultiplyExpressionEnd = dest_ptr }) catch unreachable;1167 *dest = node;
1286 try stack.append(State { .CurlySuffixExpressionBegin = DestPtr { .Field = &node.rhs } });
1287 continue;
1288 } else {
1289 self.putBackToken(token);
1290 continue;
1291 }
1292 },
1293
1294 State.CurlySuffixExpressionBegin => |dest_ptr| {
1295 stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
1296 try stack.append(State { .TypeExprBegin = dest_ptr });
1297 continue;
1298 },
1299
1300 State.CurlySuffixExpressionEnd => |dest_ptr| {
1301 if (self.eatToken(Token.Id.LBrace) == null) {
1302 continue;
1303 }
13041168
1305 if (self.isPeekToken(Token.Id.Period)) {1169 stack.append(State { .Expression = OptionalCtx { .Required = &node.body } }) catch unreachable;
1306 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,1170 try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
1307 ast.NodeSuffixOp {
1308 .base = undefined,
1309 .lhs = dest_ptr.get(),
1310 .op = ast.NodeSuffixOp.SuffixOp {
1311 .StructInitializer = ArrayList(&ast.NodeFieldInitializer).init(arena),
1312 },
1313 .rtoken = undefined,
1314 }
1315 );
1316 stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
1317 try stack.append(State {
1318 .FieldInitListItemOrEnd = ListSave(&ast.NodeFieldInitializer) {
1319 .list = &node.op.StructInitializer,
1320 .ptr = &node.rtoken,
1321 }
1322 });
1323 continue;1171 continue;
1324 } else {1172 } else {
1325 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
1326 ast.NodeSuffixOp {
1327 .base = undefined,
1328 .lhs = dest_ptr.get(),
1329 .op = ast.NodeSuffixOp.SuffixOp {
1330 .ArrayInitializer = ArrayList(&ast.Node).init(arena),
1331 },
1332 .rtoken = undefined,
1333 }
1334 );
1335 stack.append(State { .CurlySuffixExpressionEnd = dest_ptr }) catch unreachable;
1336 try stack.append(State {
1337 .ExprListItemOrEnd = ExprListCtx {
1338 .list = &node.op.ArrayInitializer,
1339 .end = Token.Id.RBrace,
1340 .ptr = &node.rtoken,
1341 }
1342 });
1343 continue;1173 continue;
1344 }1174 }
1345 },1175 },
13461176
1347 State.TypeExprBegin => |dest_ptr| {
1348 stack.append(State { .TypeExprEnd = dest_ptr }) catch unreachable;
1349 try stack.append(State { .PrefixOpExpression = dest_ptr });
1350 continue;
1351 },
13521177
1353 State.TypeExprEnd => |dest_ptr| {1178 State.Block => |block| {
1354 const token = self.getNextToken();1179 const token = self.getNextToken();
1355 switch (token.id) {1180 switch (token.id) {
1356 Token.Id.Bang => {1181 Token.Id.RBrace => {
1357 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,1182 block.rbrace = token;
1358 ast.NodeInfixOp {
1359 .base = undefined,
1360 .lhs = dest_ptr.get(),
1361 .op_token = token,
1362 .op = ast.NodeInfixOp.InfixOp.ErrorUnion,
1363 .rhs = undefined,
1364 }
1365 );
1366 stack.append(State { .TypeExprEnd = dest_ptr }) catch unreachable;
1367 try stack.append(State { .PrefixOpExpression = DestPtr { .Field = &node.rhs } });
1368 continue;1183 continue;
1369 },1184 },
1370 else => {1185 else => {
1371 self.putBackToken(token);1186 self.putBackToken(token);
1187 stack.append(State { .Block = block }) catch unreachable;
1188 try stack.append(State { .Statement = block });
1372 continue;1189 continue;
1373 },1190 },
1374 }1191 }
1375 },1192 },
13761193 State.Statement => |block| {
1377 State.PrefixOpExpression => |dest_ptr| {1194 const comments = try self.eatComments(arena);
1378 const token = self.getNextToken();
1379 if (tokenIdToPrefixOp(token.id)) |prefix_id| {
1380 var node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,
1381 ast.NodePrefixOp {
1382 .base = undefined,
1383 .op_token = token,
1384 .op = prefix_id,
1385 .rhs = undefined,
1386 }
1387 );
1388
1389 if (token.id == Token.Id.AsteriskAsterisk) {
1390 const child = try self.createNode(arena, ast.NodePrefixOp,
1391 ast.NodePrefixOp {
1392 .base = undefined,
1393 .op_token = token,
1394 .op = prefix_id,
1395 .rhs = undefined,
1396 }
1397 );
1398 node.rhs = &child.base;
1399 node = child;
1400 }
1401
1402 stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
1403 if (node.op == ast.NodePrefixOp.PrefixOp.AddrOf) {
1404 try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
1405 }
1406 continue;
1407 } else {
1408 self.putBackToken(token);
1409 stack.append(State { .SuffixOpExpressionBegin = dest_ptr }) catch unreachable;
1410 continue;
1411 }
1412 },
1413
1414 State.SuffixOpExpressionBegin => |dest_ptr| {
1415 const token = self.getNextToken();1195 const token = self.getNextToken();
1416 switch (token.id) {1196 switch (token.id) {
1417 Token.Id.Keyword_async => {1197 Token.Id.Keyword_comptime => {
1418 const async_node = try self.createNode(arena, ast.NodeAsyncAttribute,
1419 ast.NodeAsyncAttribute {
1420 .base = undefined,
1421 .async_token = token,
1422 .allocator_type = null,
1423 .rangle_bracket = null,
1424 }
1425 );
1426 stack.append(State {1198 stack.append(State {
1427 .AsyncEnd = AsyncEndCtx {1199 .ComptimeStatement = ComptimeStatementCtx {
1428 .dest_ptr = dest_ptr,1200 .comptime_token = token,
1429 .attribute = async_node,1201 .block = block,
1430 }1202 }
1431 }) catch unreachable;1203 }) catch unreachable;
1432 try stack.append(State { .SuffixOpExpressionEnd = dest_ptr });
1433 try stack.append(State { .PrimaryExpression = dest_ptr });
1434
1435 const langle_bracket = self.getNextToken();
1436 if (langle_bracket.id != Token.Id.AngleBracketLeft) {
1437 self.putBackToken(langle_bracket);
1438 continue;
1439 }
1440
1441 async_node.rangle_bracket = Token(undefined);
1442 try stack.append(State {
1443 .ExpectTokenSave = ExpectTokenSave {
1444 .id = Token.Id.AngleBracketRight,
1445 .ptr = &??async_node.rangle_bracket,
1446 }
1447 });
1448 try stack.append(State { .TypeExprBegin = DestPtr { .NullableField = &async_node.allocator_type } });
1449 continue;1204 continue;
1450 },1205 },
1451 else => {1206 Token.Id.Keyword_var, Token.Id.Keyword_const => {
1452 self.putBackToken(token);1207 stack.append(State {
1453 stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;1208 .VarDecl = VarDeclCtx {
1454 try stack.append(State { .PrimaryExpression = dest_ptr });1209 .comments = comments,
1210 .visib_token = null,
1211 .comptime_token = null,
1212 .extern_export_token = null,
1213 .lib_name = null,
1214 .mut_token = token,
1215 .list = &block.statements,
1216 }
1217 }) catch unreachable;
1455 continue;1218 continue;
1456 }1219 },
1457 }1220 Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
1458 },1221 const node = try self.createAttachNode(arena, &block.statements, ast.Node.Defer,
14591222 ast.Node.Defer {
1460 State.SuffixOpExpressionEnd => |dest_ptr| {
1461 const token = self.getNextToken();
1462 switch (token.id) {
1463 Token.Id.LParen => {
1464 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,
1465 ast.NodeSuffixOp {
1466 .base = undefined,1223 .base = undefined,
1467 .lhs = dest_ptr.get(),1224 .defer_token = token,
1468 .op = ast.NodeSuffixOp.SuffixOp {1225 .kind = switch (token.id) {
1469 .Call = ast.NodeSuffixOp.CallInfo {1226 Token.Id.Keyword_defer => ast.Node.Defer.Kind.Unconditional,
1470 .params = ArrayList(&ast.Node).init(arena),1227 Token.Id.Keyword_errdefer => ast.Node.Defer.Kind.Error,
1471 .async_attr = null,1228 else => unreachable,
1472 }
1473 },1229 },
1474 .rtoken = undefined,1230 .expr = undefined,
1475 }1231 }
1476 );1232 );
1477 stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;1233 stack.append(State { .Semicolon = &&node.base }) catch unreachable;
1478 try stack.append(State {1234 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = &node.expr } });
1479 .ExprListItemOrEnd = ExprListCtx {
1480 .list = &node.op.Call.params,
1481 .end = Token.Id.RParen,
1482 .ptr = &node.rtoken,
1483 }
1484 });
1485 continue;1235 continue;
1486 },1236 },
1487 Token.Id.LBracket => {1237 Token.Id.LBrace => {
1488 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuffixOp,1238 const inner_block = try self.createAttachNode(arena, &block.statements, ast.Node.Block,
1489 ast.NodeSuffixOp {1239 ast.Node.Block {
1490 .base = undefined,1240 .base = undefined,
1491 .lhs = dest_ptr.get(),1241 .label = null,
1492 .op = ast.NodeSuffixOp.SuffixOp {1242 .lbrace = token,
1493 .ArrayAccess = undefined,1243 .statements = ArrayList(&ast.Node).init(arena),
1494 },1244 .rbrace = undefined,
1495 .rtoken = undefined
1496 }1245 }
1497 );1246 );
1498 stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;1247 stack.append(State { .Block = inner_block }) catch unreachable;
1499 try stack.append(State { .SliceOrArrayAccess = node });
1500 try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayAccess }});
1501 continue;1248 continue;
1502 },1249 },
1503 Token.Id.Period => {1250 else => {
1504 const identifier = try self.createLiteral(arena, ast.NodeIdentifier, Token(undefined));1251 self.putBackToken(token);
1505 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeInfixOp,1252 const statememt = try block.statements.addOne();
1506 ast.NodeInfixOp {1253 stack.append(State { .Semicolon = statememt }) catch unreachable;
1507 .base = undefined,1254 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx{ .Required = statememt } });
1508 .lhs = dest_ptr.get(),1255 continue;
1509 .op_token = token,1256 }
1510 .op = ast.NodeInfixOp.InfixOp.Period,1257 }
1511 .rhs = &identifier.base,1258 },
1512 }1259 State.ComptimeStatement => |ctx| {
1513 );1260 const comments = try self.eatComments(arena);
1514 stack.append(State { .SuffixOpExpressionEnd = dest_ptr }) catch unreachable;1261 const token = self.getNextToken();
1515 try stack.append(State {1262 switch (token.id) {
1516 .ExpectTokenSave = ExpectTokenSave {1263 Token.Id.Keyword_var, Token.Id.Keyword_const => {
1517 .id = Token.Id.Identifier,1264 stack.append(State {
1518 .ptr = &identifier.token1265 .VarDecl = VarDeclCtx {
1266 .comments = comments,
1267 .visib_token = null,
1268 .comptime_token = ctx.comptime_token,
1269 .extern_export_token = null,
1270 .lib_name = null,
1271 .mut_token = token,
1272 .list = &ctx.block.statements,
1519 }1273 }
1520 });1274 }) catch unreachable;
1521 continue;1275 continue;
1522 },1276 },
1523 else => {1277 else => {
1524 self.putBackToken(token);1278 self.putBackToken(token);
1279 self.putBackToken(ctx.comptime_token);
1280 const statememt = try ctx.block.statements.addOne();
1281 stack.append(State { .Semicolon = statememt }) catch unreachable;
1282 try stack.append(State { .Expression = OptionalCtx { .Required = statememt } });
1525 continue;1283 continue;
1526 },1284 }
1285 }
1286 },
1287 State.Semicolon => |node_ptr| {
1288 const node = *node_ptr;
1289 if (requireSemiColon(node)) {
1290 stack.append(State { .ExpectToken = Token.Id.Semicolon }) catch unreachable;
1291 continue;
1527 }1292 }
1293 continue;
1528 },1294 },
15291295
1530 State.PrimaryExpression => |dest_ptr| {1296
1297 State.AsmOutputItems => |items| {
1298 const lbracket = self.getNextToken();
1299 if (lbracket.id != Token.Id.LBracket) {
1300 self.putBackToken(lbracket);
1301 continue;
1302 }
1303
1304 const node = try self.createNode(arena, ast.Node.AsmOutput,
1305 ast.Node.AsmOutput {
1306 .base = undefined,
1307 .symbolic_name = undefined,
1308 .constraint = undefined,
1309 .kind = undefined,
1310 }
1311 );
1312 try items.append(node);
1313
1314 stack.append(State { .AsmOutputItems = items }) catch unreachable;
1315 try stack.append(State { .IfToken = Token.Id.Comma });
1316 try stack.append(State { .ExpectToken = Token.Id.RParen });
1317 try stack.append(State { .AsmOutputReturnOrType = node });
1318 try stack.append(State { .ExpectToken = Token.Id.LParen });
1319 try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } });
1320 try stack.append(State { .ExpectToken = Token.Id.RBracket });
1321 try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } });
1322 continue;
1323 },
1324 State.AsmOutputReturnOrType => |node| {
1531 const token = self.getNextToken();1325 const token = self.getNextToken();
1532 switch (token.id) {1326 switch (token.id) {
1533 Token.Id.IntegerLiteral => {1327 Token.Id.Identifier => {
1534 dest_ptr.store(&(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base);1328 node.kind = ast.Node.AsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.Node.Identifier, token) };
1535 continue;
1536 },
1537 Token.Id.FloatLiteral => {
1538 dest_ptr.store(&(try self.createLiteral(arena, ast.NodeFloatLiteral, token)).base);
1539 continue;
1540 },
1541 Token.Id.CharLiteral => {
1542 dest_ptr.store(&(try self.createLiteral(arena, ast.NodeCharLiteral, token)).base);
1543 continue;
1544 },
1545 Token.Id.Keyword_undefined => {
1546 dest_ptr.store(&(try self.createLiteral(arena, ast.NodeUndefinedLiteral, token)).base);
1547 continue;
1548 },
1549 Token.Id.Keyword_true, Token.Id.Keyword_false => {
1550 dest_ptr.store(&(try self.createLiteral(arena, ast.NodeBoolLiteral, token)).base);
1551 continue;
1552 },
1553 Token.Id.Keyword_null => {
1554 dest_ptr.store(&(try self.createLiteral(arena, ast.NodeNullLiteral, token)).base);
1555 continue;
1556 },
1557 Token.Id.Keyword_this => {
1558 dest_ptr.store(&(try self.createLiteral(arena, ast.NodeThisLiteral, token)).base);
1559 continue;
1560 },
1561 Token.Id.Keyword_var => {
1562 dest_ptr.store(&(try self.createLiteral(arena, ast.NodeVarType, token)).base);
1563 continue;
1564 },
1565 Token.Id.Keyword_unreachable => {
1566 dest_ptr.store(&(try self.createLiteral(arena, ast.NodeUnreachable, token)).base);
1567 continue;1329 continue;
1568 },1330 },
1569 Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {1331 Token.Id.Arrow => {
1570 dest_ptr.store((try self.parseStringLiteral(arena, token)) ?? unreachable);1332 node.kind = ast.Node.AsmOutput.Kind { .Return = undefined };
1571 },1333 try stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.kind.Return } });
1572 Token.Id.LParen => {
1573 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeGroupedExpression,
1574 ast.NodeGroupedExpression {
1575 .base = undefined,
1576 .lparen = token,
1577 .expr = undefined,
1578 .rparen = undefined,
1579 }
1580 );
1581 stack.append(State {
1582 .ExpectTokenSave = ExpectTokenSave {
1583 .id = Token.Id.RParen,
1584 .ptr = &node.rparen,
1585 }
1586 }) catch unreachable;
1587 try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
1588 continue;1334 continue;
1589 },1335 },
1590 Token.Id.Builtin => {1336 else => {
1591 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeBuiltinCall,1337 return self.parseError(token, "expected '->' or {}, found {}",
1592 ast.NodeBuiltinCall {1338 @tagName(Token.Id.Identifier),
1593 .base = undefined,1339 @tagName(token.id));
1594 .builtin_token = token,
1595 .params = ArrayList(&ast.Node).init(arena),
1596 .rparen_token = undefined,
1597 }
1598 );
1599 stack.append(State {
1600 .ExprListItemOrEnd = ExprListCtx {
1601 .list = &node.params,
1602 .end = Token.Id.RParen,
1603 .ptr = &node.rparen_token,
1604 }
1605 }) catch unreachable;
1606 try stack.append(State { .ExpectToken = Token.Id.LParen, });
1607 continue;
1608 },1340 },
1609 Token.Id.LBracket => {1341 }
1610 const rbracket_token = self.getNextToken();1342 },
1611 if (rbracket_token.id == Token.Id.RBracket) {1343 State.AsmInputItems => |items| {
1612 const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,1344 const lbracket = self.getNextToken();
1613 ast.NodePrefixOp {1345 if (lbracket.id != Token.Id.LBracket) {
1614 .base = undefined,1346 self.putBackToken(lbracket);
1615 .op_token = token,1347 continue;
1616 .op = ast.NodePrefixOp.PrefixOp{1348 }
1617 .SliceType = ast.NodePrefixOp.AddrOfInfo {
1618 .align_expr = null,
1619 .bit_offset_start_token = null,
1620 .bit_offset_end_token = null,
1621 .const_token = null,
1622 .volatile_token = null,
1623 }
1624 },
1625 .rhs = undefined,
1626 }
1627 );
1628 dest_ptr.store(&node.base);
1629 stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;
1630 try stack.append(State { .AddrOfModifiers = &node.op.SliceType });
1631 continue;
1632 }
16331349
1634 self.putBackToken(rbracket_token);1350 const node = try self.createNode(arena, ast.Node.AsmInput,
1351 ast.Node.AsmInput {
1352 .base = undefined,
1353 .symbolic_name = undefined,
1354 .constraint = undefined,
1355 .expr = undefined,
1356 }
1357 );
1358 try items.append(node);
16351359
1636 const node = try self.createToDestNode(arena, dest_ptr, ast.NodePrefixOp,1360 stack.append(State { .AsmInputItems = items }) catch unreachable;
1637 ast.NodePrefixOp {1361 try stack.append(State { .IfToken = Token.Id.Comma });
1638 .base = undefined,1362 try stack.append(State { .ExpectToken = Token.Id.RParen });
1639 .op_token = token,1363 try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
1640 .op = ast.NodePrefixOp.PrefixOp{1364 try stack.append(State { .ExpectToken = Token.Id.LParen });
1641 .ArrayType = undefined,1365 try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.constraint } });
1642 },1366 try stack.append(State { .ExpectToken = Token.Id.RBracket });
1643 .rhs = undefined,1367 try stack.append(State { .Identifier = OptionalCtx { .Required = &node.symbolic_name } });
1644 }1368 continue;
1645 );1369 },
1646 stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.rhs } }) catch unreachable;1370 State.AsmClopperItems => |items| {
1647 try stack.append(State { .ExpectToken = Token.Id.RBracket });1371 stack.append(State { .AsmClopperItems = items }) catch unreachable;
1648 try stack.append(State { .Expression = DestPtr { .Field = &node.op.ArrayType } });1372 try stack.append(State { .IfToken = Token.Id.Comma });
1373 try stack.append(State { .StringLiteral = OptionalCtx { .Required = try items.addOne() } });
1374 continue;
1375 },
16491376
1650 },
1651 Token.Id.Keyword_error => {
1652 if (self.eatToken(Token.Id.LBrace) == null) {
1653 dest_ptr.store(&(try self.createLiteral(arena, ast.NodeErrorType, token)).base);
1654 continue;
1655 }
16561377
1657 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeErrorSetDecl,1378 State.ExprListItemOrEnd => |list_state| {
1658 ast.NodeErrorSetDecl {1379 if (self.eatToken(list_state.end)) |token| {
1659 .base = undefined,1380 *list_state.ptr = token;
1660 .error_token = token,1381 continue;
1661 .decls = ArrayList(&ast.NodeIdentifier).init(arena),1382 }
1662 .rbrace_token = undefined,
1663 }
1664 );
16651383
1666 stack.append(State {1384 stack.append(State { .ExprListCommaOrEnd = list_state }) catch unreachable;
1667 .IdentifierListItemOrEnd = ListSave(&ast.NodeIdentifier) {1385 try stack.append(State { .Expression = OptionalCtx { .Required = try list_state.list.addOne() } });
1668 .list = &node.decls,1386 continue;
1669 .ptr = &node.rbrace_token,1387 },
1670 }1388 State.ExprListCommaOrEnd => |list_state| {
1671 }) catch unreachable;1389 if (try self.expectCommaOrEnd(list_state.end)) |end| {
1672 continue;1390 *list_state.ptr = end;
1673 },1391 continue;
1674 Token.Id.Keyword_packed => {1392 } else {
1675 stack.append(State {1393 stack.append(State { .ExprListItemOrEnd = list_state }) catch unreachable;
1676 .ContainerExtern = ContainerExternCtx {1394 continue;
1677 .dest_ptr = dest_ptr,1395 }
1678 .ltoken = token,1396 },
1679 .layout = ast.NodeContainerDecl.Layout.Packed,1397 State.FieldInitListItemOrEnd => |list_state| {
1680 },1398 if (self.eatToken(Token.Id.RBrace)) |rbrace| {
1681 }) catch unreachable;1399 *list_state.ptr = rbrace;
1682 },1400 continue;
1683 Token.Id.Keyword_extern => {1401 }
1684 const next = self.getNextToken();
1685 if (next.id == Token.Id.Keyword_fn) {
1686 const fn_proto = try self.createToDestNode(arena, dest_ptr, ast.NodeFnProto,
1687 ast.NodeFnProto {
1688 .base = undefined,
1689 .visib_token = null,
1690 .name_token = null,
1691 .fn_token = next,
1692 .params = ArrayList(&ast.Node).init(arena),
1693 .return_type = undefined,
1694 .var_args_token = null,
1695 .extern_export_inline_token = token,
1696 .cc_token = null,
1697 .async_attr = null,
1698 .body_node = null,
1699 .lib_name = null,
1700 .align_expr = null,
1701 }
1702 );
1703 stack.append(State { .FnProto = fn_proto }) catch unreachable;
1704 continue;
1705 }
17061402
1707 self.putBackToken(next);1403 const node = try self.createNode(arena, ast.Node.FieldInitializer,
1708 stack.append(State {1404 ast.Node.FieldInitializer {
1709 .ContainerExtern = ContainerExternCtx {1405 .base = undefined,
1710 .dest_ptr = dest_ptr,1406 .period_token = undefined,
1711 .ltoken = token,1407 .name_token = undefined,
1712 .layout = ast.NodeContainerDecl.Layout.Extern,1408 .expr = undefined,
1713 },1409 }
1714 }) catch unreachable;1410 );
1715 },1411 try list_state.list.append(node);
1716 Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {1412
1717 self.putBackToken(token);1413 stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;
1718 stack.append(State {1414 try stack.append(State { .Expression = OptionalCtx{ .Required = &node.expr } });
1719 .ContainerExtern = ContainerExternCtx {1415 try stack.append(State { .ExpectToken = Token.Id.Equal });
1720 .dest_ptr = dest_ptr,1416 try stack.append(State {
1721 .ltoken = token,1417 .ExpectTokenSave = ExpectTokenSave {
1722 .layout = ast.NodeContainerDecl.Layout.Auto,1418 .id = Token.Id.Identifier,
1723 },1419 .ptr = &node.name_token,
1724 }) catch unreachable;1420 }
1725 },1421 });
1726 Token.Id.Identifier => {1422 try stack.append(State {
1727 const next = self.getNextToken();1423 .ExpectTokenSave = ExpectTokenSave {
1728 if (next.id != Token.Id.Colon) {1424 .id = Token.Id.Period,
1729 self.putBackToken(next);1425 .ptr = &node.period_token,
1730 dest_ptr.store(&(try self.createLiteral(arena, ast.NodeIdentifier, token)).base);1426 }
1731 continue;1427 });
1428 continue;
1429 },
1430 State.FieldInitListCommaOrEnd => |list_state| {
1431 if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
1432 *list_state.ptr = end;
1433 continue;
1434 } else {
1435 stack.append(State { .FieldInitListItemOrEnd = list_state }) catch unreachable;
1436 continue;
1437 }
1438 },
1439 State.FieldListCommaOrEnd => |container_decl| {
1440 if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
1441 container_decl.rbrace_token = end;
1442 continue;
1443 } else {
1444 stack.append(State { .ContainerDecl = container_decl }) catch unreachable;
1445 continue;
1446 }
1447 },
1448 State.IdentifierListItemOrEnd => |list_state| {
1449 if (self.eatToken(Token.Id.RBrace)) |rbrace| {
1450 *list_state.ptr = rbrace;
1451 continue;
1452 }
1453
1454 stack.append(State { .IdentifierListCommaOrEnd = list_state }) catch unreachable;
1455 try stack.append(State { .Identifier = OptionalCtx { .Required = try list_state.list.addOne() } });
1456 continue;
1457 },
1458 State.IdentifierListCommaOrEnd => |list_state| {
1459 if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
1460 *list_state.ptr = end;
1461 continue;
1462 } else {
1463 stack.append(State { .IdentifierListItemOrEnd = list_state }) catch unreachable;
1464 continue;
1465 }
1466 },
1467 State.SwitchCaseOrEnd => |list_state| {
1468 if (self.eatToken(Token.Id.RBrace)) |rbrace| {
1469 *list_state.ptr = rbrace;
1470 continue;
1471 }
1472
1473 const node = try self.createNode(arena, ast.Node.SwitchCase,
1474 ast.Node.SwitchCase {
1475 .base = undefined,
1476 .items = ArrayList(&ast.Node).init(arena),
1477 .payload = null,
1478 .expr = undefined,
1479 }
1480 );
1481 try list_state.list.append(node);
1482 stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable;
1483 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .Required = &node.expr } });
1484 try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
1485 try stack.append(State { .SwitchCaseFirstItem = &node.items });
1486 continue;
1487 },
1488 State.SwitchCaseCommaOrEnd => |list_state| {
1489 if (try self.expectCommaOrEnd(Token.Id.RBrace)) |end| {
1490 *list_state.ptr = end;
1491 continue;
1492 } else {
1493 stack.append(State { .SwitchCaseOrEnd = list_state }) catch unreachable;
1494 continue;
1495 }
1496 },
1497 State.SwitchCaseFirstItem => |case_items| {
1498 const token = self.getNextToken();
1499 if (token.id == Token.Id.Keyword_else) {
1500 const else_node = try self.createAttachNode(arena, case_items, ast.Node.SwitchElse,
1501 ast.Node.SwitchElse {
1502 .base = undefined,
1503 .token = token,
1732 }1504 }
1505 );
1506 try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight });
1507 continue;
1508 } else {
1509 self.putBackToken(token);
1510 try stack.append(State { .SwitchCaseItem = case_items });
1511 continue;
1512 }
1513 },
1514 State.SwitchCaseItem => |case_items| {
1515 stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable;
1516 try stack.append(State { .RangeExpressionBegin = OptionalCtx { .Required = try case_items.addOne() } });
1517 },
1518 State.SwitchCaseItemCommaOrEnd => |case_items| {
1519 if ((try self.expectCommaOrEnd(Token.Id.EqualAngleBracketRight)) == null) {
1520 stack.append(State { .SwitchCaseItem = case_items }) catch unreachable;
1521 }
1522 continue;
1523 },
17331524
1734 stack.append(State {
1735 .LabeledExpression = LabelCtx {
1736 .label = token,
1737 .dest_ptr = dest_ptr
1738 }
1739 }) catch unreachable;
1740 continue;
1741 },
1742 Token.Id.Keyword_fn => {
1743 const fn_proto = try self.createToDestNode(arena, dest_ptr, ast.NodeFnProto,
1744 ast.NodeFnProto {
1745 .base = undefined,
1746 .visib_token = null,
1747 .name_token = null,
1748 .fn_token = token,
1749 .params = ArrayList(&ast.Node).init(arena),
1750 .return_type = undefined,
1751 .var_args_token = null,
1752 .extern_export_inline_token = null,
1753 .cc_token = null,
1754 .async_attr = null,
1755 .body_node = null,
1756 .lib_name = null,
1757 .align_expr = null,
1758 }
1759 );
1760 stack.append(State { .FnProto = fn_proto }) catch unreachable;
1761 continue;
1762 },
1763 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
1764 const fn_token = (try self.expectToken(&stack, Token.Id.Keyword_fn)) ?? continue;
1765 const fn_proto = try self.createToDestNode(arena, dest_ptr, ast.NodeFnProto,
1766 ast.NodeFnProto {
1767 .base = undefined,
1768 .visib_token = null,
1769 .name_token = null,
1770 .fn_token = fn_token,
1771 .params = ArrayList(&ast.Node).init(arena),
1772 .return_type = undefined,
1773 .var_args_token = null,
1774 .extern_export_inline_token = null,
1775 .cc_token = token,
1776 .async_attr = null,
1777 .body_node = null,
1778 .lib_name = null,
1779 .align_expr = null,
1780 }
1781 );
1782 stack.append(State { .FnProto = fn_proto }) catch unreachable;
1783 continue;
1784 },
1785 Token.Id.Keyword_asm => {
1786 const is_volatile = blk: {
1787 const volatile_token = self.getNextToken();
1788 if (volatile_token.id != Token.Id.Keyword_volatile) {
1789 self.putBackToken(volatile_token);
1790 break :blk false;
1791 }
1792 break :blk true;
1793 };
1794 _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue;
17951525
1796 const template_token = self.getNextToken();1526 State.SuspendBody => |suspend_node| {
1797 const template = (try self.parseStringLiteral(arena, template_token)) ?? {1527 if (suspend_node.payload != null) {
1798 try self.parseError(&stack, template_token, "expected string literal, found {}", @tagName(template_token.id));1528 try stack.append(State { .AssignmentExpressionBegin = OptionalCtx { .RequiredNull = &suspend_node.body } });
1799 continue;1529 }
1800 };1530 continue;
1801 // TODO parse template1531 },
1532 State.AsyncAllocator => |async_node| {
1533 if (self.eatToken(Token.Id.AngleBracketLeft) == null) {
1534 continue;
1535 }
18021536
1803 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeAsm,1537 async_node.rangle_bracket = Token(undefined);
1804 ast.NodeAsm {1538 try stack.append(State {
1805 .base = undefined,1539 .ExpectTokenSave = ExpectTokenSave {
1806 .asm_token = token,1540 .id = Token.Id.AngleBracketRight,
1807 .is_volatile = is_volatile,1541 .ptr = &??async_node.rangle_bracket,
1808 .template = template,1542 }
1809 //.tokens = ArrayList(ast.NodeAsm.AsmToken).init(arena),1543 });
1810 .outputs = ArrayList(&ast.NodeAsmOutput).init(arena),1544 try stack.append(State { .TypeExprBegin = OptionalCtx { .RequiredNull = &async_node.allocator_type } });
1811 .inputs = ArrayList(&ast.NodeAsmInput).init(arena),1545 continue;
1812 .cloppers = ArrayList(&ast.Node).init(arena),1546 },
1813 .rparen = undefined,1547 State.AsyncEnd => |ctx| {
1814 }1548 const node = ctx.ctx.get() ?? continue;
1815 );1549
1816 stack.append(State {1550 switch (node.id) {
1817 .ExpectTokenSave = ExpectTokenSave {1551 ast.Node.Id.FnProto => {
1818 .id = Token.Id.RParen,1552 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", node);
1819 .ptr = &node.rparen,1553 fn_proto.async_attr = ctx.attribute;
1820 }
1821 }) catch unreachable;
1822 try stack.append(State { .AsmClopperItems = &node.cloppers });
1823 try stack.append(State { .IfToken = Token.Id.Colon });
1824 try stack.append(State { .AsmInputItems = &node.inputs });
1825 try stack.append(State { .IfToken = Token.Id.Colon });
1826 try stack.append(State { .AsmOutputItems = &node.outputs });
1827 try stack.append(State { .IfToken = Token.Id.Colon });
1828 },
1829 Token.Id.Keyword_inline => {
1830 stack.append(State {
1831 .Inline = InlineCtx {
1832 .label = null,
1833 .inline_token = token,
1834 .dest_ptr = dest_ptr,
1835 }
1836 }) catch unreachable;
1837 continue;1554 continue;
1838 },1555 },
1839 else => {1556 ast.Node.Id.SuffixOp => {
1840 if (!try self.parseBlockExpr(&stack, arena, dest_ptr, token)) {1557 const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", node);
1841 try self.parseError(&stack, token, "expected primary expression, found {}", @tagName(token.id));1558 if (suffix_op.op == ast.Node.SuffixOp.Op.Call) {
1559 suffix_op.op.Call.async_attr = ctx.attribute;
1560 continue;
1842 }1561 }
1843 continue;1562
1563 return self.parseError(node.firstToken(), "expected {}, found {}.",
1564 @tagName(ast.Node.SuffixOp.Op.Call),
1565 @tagName(suffix_op.op));
1566 },
1567 else => {
1568 return self.parseError(node.firstToken(), "expected {} or {}, found {}.",
1569 @tagName(ast.Node.SuffixOp.Op.Call),
1570 @tagName(ast.Node.Id.FnProto),
1571 @tagName(node.id));
1844 }1572 }
1845 }1573 }
1846 },1574 },
18471575
1576
1577 State.ExternType => |ctx| {
1578 if (self.eatToken(Token.Id.Keyword_fn)) |fn_token| {
1579 const fn_proto = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.FnProto,
1580 ast.Node.FnProto {
1581 .base = undefined,
1582 .comments = ctx.comments,
1583 .visib_token = null,
1584 .name_token = null,
1585 .fn_token = fn_token,
1586 .params = ArrayList(&ast.Node).init(arena),
1587 .return_type = undefined,
1588 .var_args_token = null,
1589 .extern_export_inline_token = ctx.extern_token,
1590 .cc_token = null,
1591 .async_attr = null,
1592 .body_node = null,
1593 .lib_name = null,
1594 .align_expr = null,
1595 }
1596 );
1597 stack.append(State { .FnProto = fn_proto }) catch unreachable;
1598 continue;
1599 }
1600
1601 stack.append(State {
1602 .ContainerKind = ContainerKindCtx {
1603 .opt_ctx = ctx.opt_ctx,
1604 .ltoken = ctx.extern_token,
1605 .layout = ast.Node.ContainerDecl.Layout.Extern,
1606 },
1607 }) catch unreachable;
1608 continue;
1609 },
1848 State.SliceOrArrayAccess => |node| {1610 State.SliceOrArrayAccess => |node| {
1849 var token = self.getNextToken();1611 var token = self.getNextToken();
1850
1851 switch (token.id) {1612 switch (token.id) {
1852 Token.Id.Ellipsis2 => {1613 Token.Id.Ellipsis2 => {
1853 const start = node.op.ArrayAccess;1614 const start = node.op.ArrayAccess;
1854 node.op = ast.NodeSuffixOp.SuffixOp {1615 node.op = ast.Node.SuffixOp.Op {
1855 .Slice = ast.NodeSuffixOp.SliceRange {1616 .Slice = ast.Node.SuffixOp.SliceRange {
1856 .start = start,1617 .start = start,
1857 .end = undefined,1618 .end = null,
1858 }1619 }
1859 };1620 };
18601621
1861 const rbracket_token = self.getNextToken();1622 stack.append(State {
1862 if (rbracket_token.id != Token.Id.RBracket) {1623 .ExpectTokenSave = ExpectTokenSave {
1863 self.putBackToken(rbracket_token);1624 .id = Token.Id.RBracket,
1864 stack.append(State {1625 .ptr = &node.rtoken,
1865 .ExpectTokenSave = ExpectTokenSave {1626 }
1866 .id = Token.Id.RBracket,1627 }) catch unreachable;
1867 .ptr = &node.rtoken,1628 try stack.append(State { .Expression = OptionalCtx { .Optional = &node.op.Slice.end } });
1868 }
1869 }) catch unreachable;
1870 try stack.append(State { .Expression = DestPtr { .NullableField = &node.op.Slice.end } });
1871 } else {
1872 node.rtoken = rbracket_token;
1873 }
1874 continue;1629 continue;
1875 },1630 },
1876 Token.Id.RBracket => {1631 Token.Id.RBracket => {
...@@ -1878,832 +1633,1137 @@ pub const Parser = struct {...@@ -1878,832 +1633,1137 @@ pub const Parser = struct {
1878 continue;1633 continue;
1879 },1634 },
1880 else => {1635 else => {
1881 try self.parseError(&stack, token, "expected ']' or '..', found {}", @tagName(token.id));1636 return self.parseError(token, "expected ']' or '..', found {}", @tagName(token.id));
1882 continue;
1883 }1637 }
1884 }1638 }
1885 },1639 },
18861640 State.SliceOrArrayType => |node| {
18871641 if (self.eatToken(Token.Id.RBracket)) |_| {
1888 State.AsmOutputItems => |items| {1642 node.op = ast.Node.PrefixOp.Op {
1889 const lbracket = self.getNextToken();1643 .SliceType = ast.Node.PrefixOp.AddrOfInfo {
1890 if (lbracket.id != Token.Id.LBracket) {1644 .align_expr = null,
1891 self.putBackToken(lbracket);1645 .bit_offset_start_token = null,
1646 .bit_offset_end_token = null,
1647 .const_token = null,
1648 .volatile_token = null,
1649 }
1650 };
1651 stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
1652 try stack.append(State { .AddrOfModifiers = &node.op.SliceType });
1892 continue;1653 continue;
1893 }1654 }
18941655
1895 stack.append(State { .AsmOutputItems = items }) catch unreachable;1656 node.op = ast.Node.PrefixOp.Op { .ArrayType = undefined };
1896 try stack.append(State { .IfToken = Token.Id.Comma });1657 stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
18971658 try stack.append(State { .ExpectToken = Token.Id.RBracket });
1898 const symbolic_name = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;1659 try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayType } });
1899 _ = (try self.expectToken(&stack, Token.Id.RBracket)) ?? continue;1660 continue;
19001661 },
1901 const constraint_token = self.getNextToken();1662 State.AddrOfModifiers => |addr_of_info| {
1902 const constraint = (try self.parseStringLiteral(arena, constraint_token)) ?? {1663 var token = self.getNextToken();
1903 try self.parseError(&stack, constraint_token, "expected string literal, found {}", @tagName(constraint_token.id));1664 switch (token.id) {
1904 continue;1665 Token.Id.Keyword_align => {
1905 };1666 stack.append(state) catch unreachable;
19061667 if (addr_of_info.align_expr != null) {
1907 _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue;1668 return self.parseError(token, "multiple align qualifiers");
1908 try stack.append(State { .ExpectToken = Token.Id.RParen });1669 }
19091670 try stack.append(State { .ExpectToken = Token.Id.RParen });
1910 const node = try self.createNode(arena, ast.NodeAsmOutput,1671 try stack.append(State { .Expression = OptionalCtx { .RequiredNull = &addr_of_info.align_expr} });
1911 ast.NodeAsmOutput {1672 try stack.append(State { .ExpectToken = Token.Id.LParen });
1912 .base = undefined,1673 continue;
1913 .symbolic_name = try self.createLiteral(arena, ast.NodeIdentifier, symbolic_name),
1914 .constraint = constraint,
1915 .kind = undefined,
1916 }
1917 );
1918 try items.append(node);
1919
1920 const symbol_or_arrow = self.getNextToken();
1921 switch (symbol_or_arrow.id) {
1922 Token.Id.Identifier => {
1923 node.kind = ast.NodeAsmOutput.Kind { .Variable = try self.createLiteral(arena, ast.NodeIdentifier, symbol_or_arrow) };
1924 },1674 },
1925 Token.Id.Arrow => {1675 Token.Id.Keyword_const => {
1926 node.kind = ast.NodeAsmOutput.Kind { .Return = undefined };1676 stack.append(state) catch unreachable;
1927 try stack.append(State { .TypeExprBegin = DestPtr { .Field = &node.kind.Return } });1677 if (addr_of_info.const_token != null) {
1678 return self.parseError(token, "duplicate qualifier: const");
1679 }
1680 addr_of_info.const_token = token;
1681 continue;
1682 },
1683 Token.Id.Keyword_volatile => {
1684 stack.append(state) catch unreachable;
1685 if (addr_of_info.volatile_token != null) {
1686 return self.parseError(token, "duplicate qualifier: volatile");
1687 }
1688 addr_of_info.volatile_token = token;
1689 continue;
1928 },1690 },
1929 else => {1691 else => {
1930 try self.parseError(&stack, symbol_or_arrow, "expected '->' or {}, found {}",1692 self.putBackToken(token);
1931 @tagName(Token.Id.Identifier),
1932 @tagName(symbol_or_arrow.id));
1933 continue;1693 continue;
1934 },1694 },
1935 }1695 }
1936 },1696 },
19371697
1938 State.AsmInputItems => |items| {
1939 const lbracket = self.getNextToken();
1940 if (lbracket.id != Token.Id.LBracket) {
1941 self.putBackToken(lbracket);
1942 continue;
1943 }
1944
1945 stack.append(State { .AsmInputItems = items }) catch unreachable;
1946 try stack.append(State { .IfToken = Token.Id.Comma });
19471698
1948 const symbolic_name = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;1699 State.Payload => |opt_ctx| {
1949 _ = (try self.expectToken(&stack, Token.Id.RBracket)) ?? continue;1700 const token = self.getNextToken();
1701 if (token.id != Token.Id.Pipe) {
1702 if (opt_ctx != OptionalCtx.Optional) {
1703 return self.parseError(token, "expected {}, found {}.",
1704 @tagName(Token.Id.Pipe),
1705 @tagName(token.id));
1706 }
19501707
1951 const constraint_token = self.getNextToken();1708 self.putBackToken(token);
1952 const constraint = (try self.parseStringLiteral(arena, constraint_token)) ?? {
1953 try self.parseError(&stack, constraint_token, "expected string literal, found {}", @tagName(constraint_token.id));
1954 continue;1709 continue;
1955 };1710 }
1956
1957 _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue;
1958 try stack.append(State { .ExpectToken = Token.Id.RParen });
19591711
1960 const node = try self.createNode(arena, ast.NodeAsmInput,1712 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.Payload,
1961 ast.NodeAsmInput {1713 ast.Node.Payload {
1962 .base = undefined,1714 .base = undefined,
1963 .symbolic_name = try self.createLiteral(arena, ast.NodeIdentifier, symbolic_name),1715 .lpipe = token,
1964 .constraint = constraint,1716 .error_symbol = undefined,
1965 .expr = undefined,1717 .rpipe = undefined
1966 }1718 }
1967 );1719 );
1968 try items.append(node);
1969 try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });
1970 },
1971
1972 State.AsmClopperItems => |items| {
1973 const string_token = self.getNextToken();
1974 const string = (try self.parseStringLiteral(arena, string_token)) ?? {
1975 self.putBackToken(string_token);
1976 continue;
1977 };
1978 try items.append(string);
19791720
1980 stack.append(State { .AsmClopperItems = items }) catch unreachable;1721 stack.append(State {
1981 try stack.append(State { .IfToken = Token.Id.Comma });1722 .ExpectTokenSave = ExpectTokenSave {
1723 .id = Token.Id.Pipe,
1724 .ptr = &node.rpipe,
1725 }
1726 }) catch unreachable;
1727 try stack.append(State { .Identifier = OptionalCtx { .Required = &node.error_symbol } });
1728 continue;
1982 },1729 },
1730 State.PointerPayload => |opt_ctx| {
1731 const token = self.getNextToken();
1732 if (token.id != Token.Id.Pipe) {
1733 if (opt_ctx != OptionalCtx.Optional) {
1734 return self.parseError(token, "expected {}, found {}.",
1735 @tagName(Token.Id.Pipe),
1736 @tagName(token.id));
1737 }
19831738
1984 State.ExprListItemOrEnd => |list_state| {1739 self.putBackToken(token);
1985 var token = self.getNextToken();
1986
1987 const IdTag = @TagType(Token.Id);
1988 if (IdTag(list_state.end) == token.id) {
1989 *list_state.ptr = token;
1990 continue;1740 continue;
1991 }1741 }
19921742
1993 self.putBackToken(token);1743 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PointerPayload,
1994 stack.append(State { .ExprListCommaOrEnd = list_state }) catch unreachable;1744 ast.Node.PointerPayload {
1995 try stack.append(State { .Expression = DestPtr{ .Field = try list_state.list.addOne() } });1745 .base = undefined,
1746 .lpipe = token,
1747 .ptr_token = null,
1748 .value_symbol = undefined,
1749 .rpipe = undefined
1750 }
1751 );
1752
1753 stack.append(State {
1754 .ExpectTokenSave = ExpectTokenSave {
1755 .id = Token.Id.Pipe,
1756 .ptr = &node.rpipe,
1757 }
1758 }) catch unreachable;
1759 try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
1760 try stack.append(State {
1761 .OptionalTokenSave = OptionalTokenSave {
1762 .id = Token.Id.Asterisk,
1763 .ptr = &node.ptr_token,
1764 }
1765 });
1766 continue;
1996 },1767 },
1768 State.PointerIndexPayload => |opt_ctx| {
1769 const token = self.getNextToken();
1770 if (token.id != Token.Id.Pipe) {
1771 if (opt_ctx != OptionalCtx.Optional) {
1772 return self.parseError(token, "expected {}, found {}.",
1773 @tagName(Token.Id.Pipe),
1774 @tagName(token.id));
1775 }
19971776
1998 State.FieldInitListItemOrEnd => |list_state| {1777 self.putBackToken(token);
1999 if (self.eatToken(Token.Id.RBrace)) |rbrace| {
2000 *list_state.ptr = rbrace;
2001 continue;1778 continue;
2002 }1779 }
20031780
2004 const node = try self.createNode(arena, ast.NodeFieldInitializer,1781 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PointerIndexPayload,
2005 ast.NodeFieldInitializer {1782 ast.Node.PointerIndexPayload {
2006 .base = undefined,1783 .base = undefined,
2007 .period_token = undefined,1784 .lpipe = token,
2008 .name_token = undefined,1785 .ptr_token = null,
2009 .expr = undefined,1786 .value_symbol = undefined,
1787 .index_symbol = null,
1788 .rpipe = undefined
2010 }1789 }
2011 );1790 );
2012 try list_state.list.append(node);
20131791
2014 stack.append(State { .FieldInitListCommaOrEnd = list_state }) catch unreachable;1792 stack.append(State {
2015 try stack.append(State { .Expression = DestPtr{.Field = &node.expr} });
2016 try stack.append(State { .ExpectToken = Token.Id.Equal });
2017 try stack.append(State {
2018 .ExpectTokenSave = ExpectTokenSave {1793 .ExpectTokenSave = ExpectTokenSave {
2019 .id = Token.Id.Identifier,1794 .id = Token.Id.Pipe,
2020 .ptr = &node.name_token,1795 .ptr = &node.rpipe,
2021 }1796 }
2022 });1797 }) catch unreachable;
1798 try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.index_symbol } });
1799 try stack.append(State { .IfToken = Token.Id.Comma });
1800 try stack.append(State { .Identifier = OptionalCtx { .Required = &node.value_symbol } });
2023 try stack.append(State {1801 try stack.append(State {
2024 .ExpectTokenSave = ExpectTokenSave {1802 .OptionalTokenSave = OptionalTokenSave {
2025 .id = Token.Id.Period,1803 .id = Token.Id.Asterisk,
2026 .ptr = &node.period_token,1804 .ptr = &node.ptr_token,
2027 }1805 }
2028 });1806 });
1807 continue;
2029 },1808 },
20301809
2031 State.IdentifierListItemOrEnd => |list_state| {
2032 if (self.eatToken(Token.Id.RBrace)) |rbrace| {
2033 *list_state.ptr = rbrace;
2034 continue;
2035 }
20361810
2037 const node = try self.createLiteral(arena, ast.NodeIdentifier, Token(undefined));1811 State.Expression => |opt_ctx| {
2038 try list_state.list.append(node);1812 const token = self.getNextToken();
1813 switch (token.id) {
1814 Token.Id.Keyword_return, Token.Id.Keyword_break, Token.Id.Keyword_continue => {
1815 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.ControlFlowExpression,
1816 ast.Node.ControlFlowExpression {
1817 .base = undefined,
1818 .ltoken = token,
1819 .kind = undefined,
1820 .rhs = null,
1821 }
1822 );
20391823
2040 stack.append(State { .IdentifierListCommaOrEnd = list_state }) catch unreachable;1824 stack.append(State { .Expression = OptionalCtx { .Optional = &node.rhs } }) catch unreachable;
2041 try stack.append(State {1825
2042 .ExpectTokenSave = ExpectTokenSave {1826 switch (token.id) {
2043 .id = Token.Id.Identifier,1827 Token.Id.Keyword_break => {
2044 .ptr = &node.token,1828 node.kind = ast.Node.ControlFlowExpression.Kind { .Break = null };
1829 try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Break } });
1830 try stack.append(State { .IfToken = Token.Id.Colon });
1831 },
1832 Token.Id.Keyword_continue => {
1833 node.kind = ast.Node.ControlFlowExpression.Kind { .Continue = null };
1834 try stack.append(State { .Identifier = OptionalCtx { .RequiredNull = &node.kind.Continue } });
1835 try stack.append(State { .IfToken = Token.Id.Colon });
1836 },
1837 Token.Id.Keyword_return => {
1838 node.kind = ast.Node.ControlFlowExpression.Kind.Return;
1839 },
1840 else => unreachable,
1841 }
1842 continue;
1843 },
1844 Token.Id.Keyword_try, Token.Id.Keyword_cancel, Token.Id.Keyword_resume => {
1845 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
1846 ast.Node.PrefixOp {
1847 .base = undefined,
1848 .op_token = token,
1849 .op = switch (token.id) {
1850 Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{} },
1851 Token.Id.Keyword_cancel => ast.Node.PrefixOp.Op { .Cancel = void{} },
1852 Token.Id.Keyword_resume => ast.Node.PrefixOp.Op { .Resume = void{} },
1853 else => unreachable,
1854 },
1855 .rhs = undefined,
1856 }
1857 );
1858
1859 stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
1860 continue;
1861 },
1862 else => {
1863 if (!try self.parseBlockExpr(&stack, arena, opt_ctx, token)) {
1864 self.putBackToken(token);
1865 stack.append(State { .UnwrapExpressionBegin = opt_ctx }) catch unreachable;
1866 }
1867 continue;
2045 }1868 }
2046 });1869 }
2047 },1870 },
1871 State.RangeExpressionBegin => |opt_ctx| {
1872 stack.append(State { .RangeExpressionEnd = opt_ctx }) catch unreachable;
1873 try stack.append(State { .Expression = opt_ctx });
1874 continue;
1875 },
1876 State.RangeExpressionEnd => |opt_ctx| {
1877 const lhs = opt_ctx.get() ?? continue;
20481878
2049 State.SwitchCaseOrEnd => |list_state| {1879 if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| {
2050 if (self.eatToken(Token.Id.RBrace)) |rbrace| {1880 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2051 *list_state.ptr = rbrace;1881 ast.Node.InfixOp {
1882 .base = undefined,
1883 .lhs = lhs,
1884 .op_token = ellipsis3,
1885 .op = ast.Node.InfixOp.Op.Range,
1886 .rhs = undefined,
1887 }
1888 );
1889 stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
2052 continue;1890 continue;
2053 }1891 }
1892 },
1893 State.AssignmentExpressionBegin => |opt_ctx| {
1894 stack.append(State { .AssignmentExpressionEnd = opt_ctx }) catch unreachable;
1895 try stack.append(State { .Expression = opt_ctx });
1896 continue;
1897 },
20541898
2055 const node = try self.createNode(arena, ast.NodeSwitchCase,1899 State.AssignmentExpressionEnd => |opt_ctx| {
2056 ast.NodeSwitchCase {1900 const lhs = opt_ctx.get() ?? continue;
2057 .base = undefined,
2058 .items = ArrayList(&ast.Node).init(arena),
2059 .payload = null,
2060 .expr = undefined,
2061 }
2062 );
2063 try list_state.list.append(node);
2064 stack.append(State { .SwitchCaseCommaOrEnd = list_state }) catch unreachable;
2065 try stack.append(State { .AssignmentExpressionBegin = DestPtr{ .Field = &node.expr } });
2066 try stack.append(State { .PointerPayload = &node.payload });
20671901
2068 const maybe_else = self.getNextToken();1902 const token = self.getNextToken();
2069 if (maybe_else.id == Token.Id.Keyword_else) {1903 if (tokenIdToAssignment(token.id)) |ass_id| {
2070 const else_node = try self.createAttachNode(arena, &node.items, ast.NodeSwitchElse,1904 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2071 ast.NodeSwitchElse {1905 ast.Node.InfixOp {
2072 .base = undefined,1906 .base = undefined,
2073 .token = maybe_else,1907 .lhs = lhs,
1908 .op_token = token,
1909 .op = ass_id,
1910 .rhs = undefined,
2074 }1911 }
2075 );1912 );
2076 try stack.append(State { .ExpectToken = Token.Id.EqualAngleBracketRight });1913 stack.append(State { .AssignmentExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
1914 try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } });
1915 continue;
1916 } else {
1917 self.putBackToken(token);
1918 continue;
1919 }
1920 },
1921
1922 State.UnwrapExpressionBegin => |opt_ctx| {
1923 stack.append(State { .UnwrapExpressionEnd = opt_ctx }) catch unreachable;
1924 try stack.append(State { .BoolOrExpressionBegin = opt_ctx });
1925 continue;
1926 },
1927
1928 State.UnwrapExpressionEnd => |opt_ctx| {
1929 const lhs = opt_ctx.get() ?? continue;
1930
1931 const token = self.getNextToken();
1932 if (tokenIdToUnwrapExpr(token.id)) |unwrap_id| {
1933 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
1934 ast.Node.InfixOp {
1935 .base = undefined,
1936 .lhs = lhs,
1937 .op_token = token,
1938 .op = unwrap_id,
1939 .rhs = undefined,
1940 }
1941 );
1942
1943 stack.append(State { .UnwrapExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
1944 try stack.append(State { .Expression = OptionalCtx { .Required = &node.rhs } });
1945
1946 if (node.op == ast.Node.InfixOp.Op.Catch) {
1947 try stack.append(State { .Payload = OptionalCtx { .Optional = &node.op.Catch } });
1948 }
2077 continue;1949 continue;
2078 } else {1950 } else {
2079 self.putBackToken(maybe_else);1951 self.putBackToken(token);
2080 try stack.append(State { .SwitchCaseItem = &node.items });
2081 continue;1952 continue;
2082 }1953 }
2083 },1954 },
20841955
2085 State.SwitchCaseItem => |case_items| {1956 State.BoolOrExpressionBegin => |opt_ctx| {
2086 stack.append(State { .SwitchCaseItemCommaOrEnd = case_items }) catch unreachable;1957 stack.append(State { .BoolOrExpressionEnd = opt_ctx }) catch unreachable;
2087 try stack.append(State { .RangeExpressionBegin = DestPtr{ .Field = try case_items.addOne() } });1958 try stack.append(State { .BoolAndExpressionBegin = opt_ctx });
1959 continue;
2088 },1960 },
20891961
2090 State.ExprListCommaOrEnd => |list_state| {1962 State.BoolOrExpressionEnd => |opt_ctx| {
2091 try self.commaOrEnd(&stack, list_state.end, list_state.ptr, State { .ExprListItemOrEnd = list_state });1963 const lhs = opt_ctx.get() ?? continue;
2092 continue;1964
1965 if (self.eatToken(Token.Id.Keyword_or)) |or_token| {
1966 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
1967 ast.Node.InfixOp {
1968 .base = undefined,
1969 .lhs = lhs,
1970 .op_token = or_token,
1971 .op = ast.Node.InfixOp.Op.BoolOr,
1972 .rhs = undefined,
1973 }
1974 );
1975 stack.append(State { .BoolOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
1976 try stack.append(State { .BoolAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
1977 continue;
1978 }
2093 },1979 },
20941980
2095 State.FieldInitListCommaOrEnd => |list_state| {1981 State.BoolAndExpressionBegin => |opt_ctx| {
2096 try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .FieldInitListItemOrEnd = list_state });1982 stack.append(State { .BoolAndExpressionEnd = opt_ctx }) catch unreachable;
1983 try stack.append(State { .ComparisonExpressionBegin = opt_ctx });
2097 continue;1984 continue;
2098 },1985 },
20991986
2100 State.FieldListCommaOrEnd => |container_decl| {1987 State.BoolAndExpressionEnd => |opt_ctx| {
2101 try self.commaOrEnd(&stack, Token.Id.RBrace, &container_decl.rbrace_token,1988 const lhs = opt_ctx.get() ?? continue;
2102 State { .ContainerDecl = container_decl });1989
2103 continue;1990 if (self.eatToken(Token.Id.Keyword_and)) |and_token| {
1991 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
1992 ast.Node.InfixOp {
1993 .base = undefined,
1994 .lhs = lhs,
1995 .op_token = and_token,
1996 .op = ast.Node.InfixOp.Op.BoolAnd,
1997 .rhs = undefined,
1998 }
1999 );
2000 stack.append(State { .BoolAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2001 try stack.append(State { .ComparisonExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2002 continue;
2003 }
2104 },2004 },
21052005
2106 State.IdentifierListCommaOrEnd => |list_state| {2006 State.ComparisonExpressionBegin => |opt_ctx| {
2107 try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .IdentifierListItemOrEnd = list_state });2007 stack.append(State { .ComparisonExpressionEnd = opt_ctx }) catch unreachable;
2008 try stack.append(State { .BinaryOrExpressionBegin = opt_ctx });
2108 continue;2009 continue;
2109 },2010 },
21102011
2111 State.SwitchCaseCommaOrEnd => |list_state| {2012 State.ComparisonExpressionEnd => |opt_ctx| {
2112 try self.commaOrEnd(&stack, Token.Id.RBrace, list_state.ptr, State { .SwitchCaseOrEnd = list_state });2013 const lhs = opt_ctx.get() ?? continue;
2113 continue;2014
2015 const token = self.getNextToken();
2016 if (tokenIdToComparison(token.id)) |comp_id| {
2017 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2018 ast.Node.InfixOp {
2019 .base = undefined,
2020 .lhs = lhs,
2021 .op_token = token,
2022 .op = comp_id,
2023 .rhs = undefined,
2024 }
2025 );
2026 stack.append(State { .ComparisonExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2027 try stack.append(State { .BinaryOrExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2028 continue;
2029 } else {
2030 self.putBackToken(token);
2031 continue;
2032 }
2114 },2033 },
21152034
2116 State.SwitchCaseItemCommaOrEnd => |case_items| {2035 State.BinaryOrExpressionBegin => |opt_ctx| {
2117 try self.commaOrEnd(&stack, Token.Id.EqualAngleBracketRight, null, State { .SwitchCaseItem = case_items });2036 stack.append(State { .BinaryOrExpressionEnd = opt_ctx }) catch unreachable;
2037 try stack.append(State { .BinaryXorExpressionBegin = opt_ctx });
2118 continue;2038 continue;
2119 },2039 },
21202040
2121 State.Else => |dest| {2041 State.BinaryOrExpressionEnd => |opt_ctx| {
2122 const else_token = self.getNextToken();2042 const lhs = opt_ctx.get() ?? continue;
2123 if (else_token.id != Token.Id.Keyword_else) {2043
2124 self.putBackToken(else_token);2044 if (self.eatToken(Token.Id.Pipe)) |pipe| {
2045 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2046 ast.Node.InfixOp {
2047 .base = undefined,
2048 .lhs = lhs,
2049 .op_token = pipe,
2050 .op = ast.Node.InfixOp.Op.BitOr,
2051 .rhs = undefined,
2052 }
2053 );
2054 stack.append(State { .BinaryOrExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2055 try stack.append(State { .BinaryXorExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2125 continue;2056 continue;
2126 }2057 }
2058 },
21272059
2128 const node = try self.createNode(arena, ast.NodeElse,2060 State.BinaryXorExpressionBegin => |opt_ctx| {
2129 ast.NodeElse {2061 stack.append(State { .BinaryXorExpressionEnd = opt_ctx }) catch unreachable;
2130 .base = undefined,2062 try stack.append(State { .BinaryAndExpressionBegin = opt_ctx });
2131 .else_token = else_token,2063 continue;
2132 .payload = null,
2133 .body = undefined,
2134 }
2135 );
2136 *dest = node;
2137
2138 stack.append(State { .Expression = DestPtr { .Field = &node.body } }) catch unreachable;
2139 try stack.append(State { .Payload = &node.payload });
2140 },2064 },
21412065
2142 State.WhileContinueExpr => |dest| {2066 State.BinaryXorExpressionEnd => |opt_ctx| {
2143 const colon = self.getNextToken();2067 const lhs = opt_ctx.get() ?? continue;
2144 if (colon.id != Token.Id.Colon) {2068
2145 self.putBackToken(colon);2069 if (self.eatToken(Token.Id.Caret)) |caret| {
2070 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2071 ast.Node.InfixOp {
2072 .base = undefined,
2073 .lhs = lhs,
2074 .op_token = caret,
2075 .op = ast.Node.InfixOp.Op.BitXor,
2076 .rhs = undefined,
2077 }
2078 );
2079 stack.append(State { .BinaryXorExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2080 try stack.append(State { .BinaryAndExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2146 continue;2081 continue;
2147 }2082 }
2148
2149 _ = (try self.expectToken(&stack, Token.Id.LParen)) ?? continue;
2150 stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;
2151 try stack.append(State { .AssignmentExpressionBegin = DestPtr { .NullableField = dest } });
2152 },2083 },
21532084
2154 State.SuspendBody => |suspend_node| {2085 State.BinaryAndExpressionBegin => |opt_ctx| {
2155 if (suspend_node.payload != null) {2086 stack.append(State { .BinaryAndExpressionEnd = opt_ctx }) catch unreachable;
2156 try stack.append(State { .AssignmentExpressionBegin = DestPtr { .NullableField = &suspend_node.body } });2087 try stack.append(State { .BitShiftExpressionBegin = opt_ctx });
2157 }
2158 continue;2088 continue;
2159 },2089 },
21602090
2161 State.AsyncEnd => |ctx| {2091 State.BinaryAndExpressionEnd => |opt_ctx| {
2162 const node = ctx.dest_ptr.get();2092 const lhs = opt_ctx.get() ?? continue;
21632093
2164 switch (node.id) {2094 if (self.eatToken(Token.Id.Ampersand)) |ampersand| {
2165 ast.Node.Id.FnProto => {2095 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2166 const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", node);2096 ast.Node.InfixOp {
2167 fn_proto.async_attr = ctx.attribute;2097 .base = undefined,
2168 },2098 .lhs = lhs,
2169 ast.Node.Id.SuffixOp => {2099 .op_token = ampersand,
2170 const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", node);2100 .op = ast.Node.InfixOp.Op.BitAnd,
2171 if (suffix_op.op == ast.NodeSuffixOp.SuffixOp.Call) {2101 .rhs = undefined,
2172 suffix_op.op.Call.async_attr = ctx.attribute;
2173 continue;
2174 }2102 }
21752103 );
2176 try self.parseError(&stack, node.firstToken(), "expected call or fn proto, found {}.",2104 stack.append(State { .BinaryAndExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2177 @tagName(suffix_op.op));2105 try stack.append(State { .BitShiftExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2178 continue;2106 continue;
2179 },
2180 else => {
2181 try self.parseError(&stack, node.firstToken(), "expected call or fn proto, found {}.",
2182 @tagName(node.id));
2183 continue;
2184 }
2185 }2107 }
2186 },2108 },
21872109
2188 State.Payload => |dest| {2110 State.BitShiftExpressionBegin => |opt_ctx| {
2189 const lpipe = self.getNextToken();2111 stack.append(State { .BitShiftExpressionEnd = opt_ctx }) catch unreachable;
2190 if (lpipe.id != Token.Id.Pipe) {2112 try stack.append(State { .AdditionExpressionBegin = opt_ctx });
2191 self.putBackToken(lpipe);2113 continue;
2114 },
2115
2116 State.BitShiftExpressionEnd => |opt_ctx| {
2117 const lhs = opt_ctx.get() ?? continue;
2118
2119 const token = self.getNextToken();
2120 if (tokenIdToBitShift(token.id)) |bitshift_id| {
2121 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2122 ast.Node.InfixOp {
2123 .base = undefined,
2124 .lhs = lhs,
2125 .op_token = token,
2126 .op = bitshift_id,
2127 .rhs = undefined,
2128 }
2129 );
2130 stack.append(State { .BitShiftExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2131 try stack.append(State { .AdditionExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2132 continue;
2133 } else {
2134 self.putBackToken(token);
2192 continue;2135 continue;
2193 }2136 }
2137 },
21942138
2195 const error_symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;2139 State.AdditionExpressionBegin => |opt_ctx| {
2196 const rpipe = (try self.expectToken(&stack, Token.Id.Pipe)) ?? continue;2140 stack.append(State { .AdditionExpressionEnd = opt_ctx }) catch unreachable;
2197 *dest = try self.createNode(arena, ast.NodePayload,2141 try stack.append(State { .MultiplyExpressionBegin = opt_ctx });
2198 ast.NodePayload {2142 continue;
2199 .base = undefined,
2200 .lpipe = lpipe,
2201 .error_symbol = try self.createLiteral(arena, ast.NodeIdentifier, error_symbol),
2202 .rpipe = rpipe
2203 }
2204 );
2205 },2143 },
22062144
2207 State.PointerPayload => |dest| {2145 State.AdditionExpressionEnd => |opt_ctx| {
2208 const lpipe = self.getNextToken();2146 const lhs = opt_ctx.get() ?? continue;
2209 if (lpipe.id != Token.Id.Pipe) {2147
2210 self.putBackToken(lpipe);2148 const token = self.getNextToken();
2149 if (tokenIdToAddition(token.id)) |add_id| {
2150 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2151 ast.Node.InfixOp {
2152 .base = undefined,
2153 .lhs = lhs,
2154 .op_token = token,
2155 .op = add_id,
2156 .rhs = undefined,
2157 }
2158 );
2159 stack.append(State { .AdditionExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2160 try stack.append(State { .MultiplyExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2161 continue;
2162 } else {
2163 self.putBackToken(token);
2211 continue;2164 continue;
2212 }2165 }
2166 },
22132167
2214 const is_ptr = blk: {2168 State.MultiplyExpressionBegin => |opt_ctx| {
2215 const asterik = self.getNextToken();2169 stack.append(State { .MultiplyExpressionEnd = opt_ctx }) catch unreachable;
2216 if (asterik.id == Token.Id.Asterisk) {2170 try stack.append(State { .CurlySuffixExpressionBegin = opt_ctx });
2217 break :blk true;2171 continue;
2218 } else {
2219 self.putBackToken(asterik);
2220 break :blk false;
2221 }
2222 };
2223
2224 const value_symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;
2225 const rpipe = (try self.expectToken(&stack, Token.Id.Pipe)) ?? continue;
2226 *dest = try self.createNode(arena, ast.NodePointerPayload,
2227 ast.NodePointerPayload {
2228 .base = undefined,
2229 .lpipe = lpipe,
2230 .is_ptr = is_ptr,
2231 .value_symbol = try self.createLiteral(arena, ast.NodeIdentifier, value_symbol),
2232 .rpipe = rpipe
2233 }
2234 );
2235 },2172 },
22362173
2237 State.PointerIndexPayload => |dest| {2174 State.MultiplyExpressionEnd => |opt_ctx| {
2238 const lpipe = self.getNextToken();2175 const lhs = opt_ctx.get() ?? continue;
2239 if (lpipe.id != Token.Id.Pipe) {2176
2240 self.putBackToken(lpipe);2177 const token = self.getNextToken();
2178 if (tokenIdToMultiply(token.id)) |mult_id| {
2179 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2180 ast.Node.InfixOp {
2181 .base = undefined,
2182 .lhs = lhs,
2183 .op_token = token,
2184 .op = mult_id,
2185 .rhs = undefined,
2186 }
2187 );
2188 stack.append(State { .MultiplyExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2189 try stack.append(State { .CurlySuffixExpressionBegin = OptionalCtx { .Required = &node.rhs } });
2190 continue;
2191 } else {
2192 self.putBackToken(token);
2241 continue;2193 continue;
2242 }2194 }
2195 },
22432196
2244 const is_ptr = blk: {2197 State.CurlySuffixExpressionBegin => |opt_ctx| {
2245 const asterik = self.getNextToken();2198 stack.append(State { .CurlySuffixExpressionEnd = opt_ctx }) catch unreachable;
2246 if (asterik.id == Token.Id.Asterisk) {2199 try stack.append(State { .IfToken = Token.Id.LBrace });
2247 break :blk true;2200 try stack.append(State { .TypeExprBegin = opt_ctx });
2248 } else {2201 continue;
2249 self.putBackToken(asterik);2202 },
2250 break :blk false;
2251 }
2252 };
22532203
2254 const value_symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;2204 State.CurlySuffixExpressionEnd => |opt_ctx| {
2255 const index_symbol = blk: {2205 const lhs = opt_ctx.get() ?? continue;
2256 const comma = self.getNextToken();
2257 if (comma.id != Token.Id.Comma) {
2258 self.putBackToken(comma);
2259 break :blk null;
2260 }
22612206
2262 const symbol = (try self.expectToken(&stack, Token.Id.Identifier)) ?? continue;2207 if (self.isPeekToken(Token.Id.Period)) {
2263 break :blk try self.createLiteral(arena, ast.NodeIdentifier, symbol);2208 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
2264 };2209 ast.Node.SuffixOp {
2210 .base = undefined,
2211 .lhs = lhs,
2212 .op = ast.Node.SuffixOp.Op {
2213 .StructInitializer = ArrayList(&ast.Node.FieldInitializer).init(arena),
2214 },
2215 .rtoken = undefined,
2216 }
2217 );
2218 stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2219 try stack.append(State { .IfToken = Token.Id.LBrace });
2220 try stack.append(State {
2221 .FieldInitListItemOrEnd = ListSave(&ast.Node.FieldInitializer) {
2222 .list = &node.op.StructInitializer,
2223 .ptr = &node.rtoken,
2224 }
2225 });
2226 continue;
2227 }
22652228
2266 const rpipe = (try self.expectToken(&stack, Token.Id.Pipe)) ?? continue;2229 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
2267 *dest = try self.createNode(arena, ast.NodePointerIndexPayload,2230 ast.Node.SuffixOp {
2268 ast.NodePointerIndexPayload {
2269 .base = undefined,2231 .base = undefined,
2270 .lpipe = lpipe,2232 .lhs = lhs,
2271 .is_ptr = is_ptr,2233 .op = ast.Node.SuffixOp.Op {
2272 .value_symbol = try self.createLiteral(arena, ast.NodeIdentifier, value_symbol),2234 .ArrayInitializer = ArrayList(&ast.Node).init(arena),
2273 .index_symbol = index_symbol,2235 },
2274 .rpipe = rpipe2236 .rtoken = undefined,
2275 }2237 }
2276 );2238 );
2239 stack.append(State { .CurlySuffixExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2240 try stack.append(State { .IfToken = Token.Id.LBrace });
2241 try stack.append(State {
2242 .ExprListItemOrEnd = ExprListCtx {
2243 .list = &node.op.ArrayInitializer,
2244 .end = Token.Id.RBrace,
2245 .ptr = &node.rtoken,
2246 }
2247 });
2248 continue;
2277 },2249 },
22782250
2279 State.AddrOfModifiers => |addr_of_info| {2251 State.TypeExprBegin => |opt_ctx| {
2280 var token = self.getNextToken();2252 stack.append(State { .TypeExprEnd = opt_ctx }) catch unreachable;
2281 switch (token.id) {2253 try stack.append(State { .PrefixOpExpression = opt_ctx });
2282 Token.Id.Keyword_align => {2254 continue;
2283 stack.append(state) catch unreachable;
2284 if (addr_of_info.align_expr != null) {
2285 try self.parseError(&stack, token, "multiple align qualifiers");
2286 continue;
2287 }
2288 try stack.append(State { .ExpectToken = Token.Id.RParen });
2289 try stack.append(State { .Expression = DestPtr{.NullableField = &addr_of_info.align_expr} });
2290 try stack.append(State { .ExpectToken = Token.Id.LParen });
2291 continue;
2292 },
2293 Token.Id.Keyword_const => {
2294 stack.append(state) catch unreachable;
2295 if (addr_of_info.const_token != null) {
2296 try self.parseError(&stack, token, "duplicate qualifier: const");
2297 continue;
2298 }
2299 addr_of_info.const_token = token;
2300 continue;
2301 },
2302 Token.Id.Keyword_volatile => {
2303 stack.append(state) catch unreachable;
2304 if (addr_of_info.volatile_token != null) {
2305 try self.parseError(&stack, token, "duplicate qualifier: volatile");
2306 continue;
2307 }
2308 addr_of_info.volatile_token = token;
2309 continue;
2310 },
2311 else => {
2312 self.putBackToken(token);
2313 continue;
2314 },
2315 }
2316 },2255 },
23172256
2318 State.FnProto => |fn_proto| {2257 State.TypeExprEnd => |opt_ctx| {
2319 stack.append(State { .FnProtoAlign = fn_proto }) catch unreachable;2258 const lhs = opt_ctx.get() ?? continue;
2320 try stack.append(State { .ParamDecl = fn_proto });
2321 try stack.append(State { .ExpectToken = Token.Id.LParen });
23222259
2323 const next_token = self.getNextToken();2260 if (self.eatToken(Token.Id.Bang)) |bang| {
2324 if (next_token.id == Token.Id.Identifier) {2261 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
2325 fn_proto.name_token = next_token;2262 ast.Node.InfixOp {
2263 .base = undefined,
2264 .lhs = lhs,
2265 .op_token = bang,
2266 .op = ast.Node.InfixOp.Op.ErrorUnion,
2267 .rhs = undefined,
2268 }
2269 );
2270 stack.append(State { .TypeExprEnd = opt_ctx.toRequired() }) catch unreachable;
2271 try stack.append(State { .PrefixOpExpression = OptionalCtx { .Required = &node.rhs } });
2326 continue;2272 continue;
2327 }2273 }
2328 self.putBackToken(next_token);
2329 continue;
2330 },
2331
2332 State.FnProtoAlign => |fn_proto| {
2333 if (self.eatToken(Token.Id.Keyword_align)) |align_token| {
2334 @panic("TODO fn proto align");
2335 }
2336 stack.append(State {
2337 .FnProtoReturnType = fn_proto,
2338 }) catch unreachable;
2339 continue;
2340 },2274 },
23412275
2342 State.FnProtoReturnType => |fn_proto| {2276 State.PrefixOpExpression => |opt_ctx| {
2343 const token = self.getNextToken();2277 const token = self.getNextToken();
2344 switch (token.id) {2278 if (tokenIdToPrefixOp(token.id)) |prefix_id| {
2345 Token.Id.Bang => {2279 var node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
2346 fn_proto.return_type = ast.NodeFnProto.ReturnType { .InferErrorSet = undefined };2280 ast.Node.PrefixOp {
2347 stack.append(State {2281 .base = undefined,
2348 .TypeExprBegin = DestPtr {.Field = &fn_proto.return_type.InferErrorSet},2282 .op_token = token,
2349 }) catch unreachable;2283 .op = prefix_id,
2350 continue;2284 .rhs = undefined,
2351 },
2352 Token.Id.Keyword_align => {
2353 @panic("TODO fn proto align");
2354 continue;
2355 },
2356 else => {
2357 // TODO: this is a special case. Remove this when #760 is fixed
2358 if (token.id == Token.Id.Keyword_error) {
2359 if (self.isPeekToken(Token.Id.LBrace)) {
2360 fn_proto.return_type = ast.NodeFnProto.ReturnType {
2361 .Explicit = &(try self.createLiteral(arena, ast.NodeErrorType, token)).base
2362 };
2363 continue;
2364 }
2365 }2285 }
2286 );
23662287
2367 self.putBackToken(token);2288 // Treat '**' token as two derefs
2368 fn_proto.return_type = ast.NodeFnProto.ReturnType { .Explicit = undefined };2289 if (token.id == Token.Id.AsteriskAsterisk) {
2369 stack.append(State {2290 const child = try self.createNode(arena, ast.Node.PrefixOp,
2370 .TypeExprBegin = DestPtr {.Field = &fn_proto.return_type.Explicit},2291 ast.Node.PrefixOp {
2371 }) catch unreachable;2292 .base = undefined,
2372 continue;2293 .op_token = token,
2373 },2294 .op = prefix_id,
2374 }2295 .rhs = undefined,
2375 },2296 }
2297 );
2298 node.rhs = &child.base;
2299 node = child;
2300 }
23762301
2377 State.ParamDecl => |fn_proto| {2302 stack.append(State { .TypeExprBegin = OptionalCtx { .Required = &node.rhs } }) catch unreachable;
2378 if (self.eatToken(Token.Id.RParen)) |_| {2303 if (node.op == ast.Node.PrefixOp.Op.AddrOf) {
2379 continue;2304 try stack.append(State { .AddrOfModifiers = &node.op.AddrOf });
2380 }
2381 const param_decl = try self.createAttachNode(arena, &fn_proto.params, ast.NodeParamDecl,
2382 ast.NodeParamDecl {
2383 .base = undefined,
2384 .comptime_token = null,
2385 .noalias_token = null,
2386 .name_token = null,
2387 .type_node = undefined,
2388 .var_args_token = null,
2389 },
2390 );
2391 if (self.eatToken(Token.Id.Keyword_comptime)) |comptime_token| {
2392 param_decl.comptime_token = comptime_token;
2393 } else if (self.eatToken(Token.Id.Keyword_noalias)) |noalias_token| {
2394 param_decl.noalias_token = noalias_token;
2395 }
2396 if (self.eatToken(Token.Id.Identifier)) |identifier| {
2397 if (self.eatToken(Token.Id.Colon)) |_| {
2398 param_decl.name_token = identifier;
2399 } else {
2400 self.putBackToken(identifier);
2401 }2305 }
2306 continue;
2307 } else {
2308 self.putBackToken(token);
2309 stack.append(State { .SuffixOpExpressionBegin = opt_ctx }) catch unreachable;
2310 continue;
2402 }2311 }
2403 if (self.eatToken(Token.Id.Ellipsis3)) |ellipsis3| {2312 },
2404 param_decl.var_args_token = ellipsis3;2313
2405 stack.append(State { .ExpectToken = Token.Id.RParen }) catch unreachable;2314 State.SuffixOpExpressionBegin => |opt_ctx| {
2315 if (self.eatToken(Token.Id.Keyword_async)) |async_token| {
2316 const async_node = try self.createNode(arena, ast.Node.AsyncAttribute,
2317 ast.Node.AsyncAttribute {
2318 .base = undefined,
2319 .async_token = async_token,
2320 .allocator_type = null,
2321 .rangle_bracket = null,
2322 }
2323 );
2324 stack.append(State {
2325 .AsyncEnd = AsyncEndCtx {
2326 .ctx = opt_ctx,
2327 .attribute = async_node,
2328 }
2329 }) catch unreachable;
2330 try stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() });
2331 try stack.append(State { .PrimaryExpression = opt_ctx.toRequired() });
2332 try stack.append(State { .AsyncAllocator = async_node });
2406 continue;2333 continue;
2407 }2334 }
24082335
2409 stack.append(State { .ParamDecl = fn_proto }) catch unreachable;2336 stack.append(State { .SuffixOpExpressionEnd = opt_ctx }) catch unreachable;
2410 try stack.append(State.ParamDeclComma);2337 try stack.append(State { .PrimaryExpression = opt_ctx });
2411 try stack.append(State {
2412 .TypeExprBegin = DestPtr {.Field = &param_decl.type_node}
2413 });
2414 continue;2338 continue;
2415 },2339 },
24162340
2417 State.ParamDeclComma => {2341 State.SuffixOpExpressionEnd => |opt_ctx| {
2342 const lhs = opt_ctx.get() ?? continue;
2343
2418 const token = self.getNextToken();2344 const token = self.getNextToken();
2419 switch (token.id) {2345 switch (token.id) {
2420 Token.Id.RParen => {2346 Token.Id.LParen => {
2421 _ = stack.pop(); // pop off the ParamDecl2347 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
2348 ast.Node.SuffixOp {
2349 .base = undefined,
2350 .lhs = lhs,
2351 .op = ast.Node.SuffixOp.Op {
2352 .Call = ast.Node.SuffixOp.CallInfo {
2353 .params = ArrayList(&ast.Node).init(arena),
2354 .async_attr = null,
2355 }
2356 },
2357 .rtoken = undefined,
2358 }
2359 );
2360 stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2361 try stack.append(State {
2362 .ExprListItemOrEnd = ExprListCtx {
2363 .list = &node.op.Call.params,
2364 .end = Token.Id.RParen,
2365 .ptr = &node.rtoken,
2366 }
2367 });
2422 continue;2368 continue;
2423 },2369 },
2424 Token.Id.Comma => continue,2370 Token.Id.LBracket => {
2425 else => {2371 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.SuffixOp,
2426 try self.parseError(&stack, token, "expected ',' or ')', found {}", @tagName(token.id));2372 ast.Node.SuffixOp {
2373 .base = undefined,
2374 .lhs = lhs,
2375 .op = ast.Node.SuffixOp.Op {
2376 .ArrayAccess = undefined,
2377 },
2378 .rtoken = undefined
2379 }
2380 );
2381 stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2382 try stack.append(State { .SliceOrArrayAccess = node });
2383 try stack.append(State { .Expression = OptionalCtx { .Required = &node.op.ArrayAccess }});
2427 continue;2384 continue;
2428 },2385 },
2429 }2386 Token.Id.Period => {
2430 },2387 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.InfixOp,
24312388 ast.Node.InfixOp {
2432 State.FnDef => |fn_proto| {
2433 const token = self.getNextToken();
2434 switch(token.id) {
2435 Token.Id.LBrace => {
2436 const block = try self.createNode(arena, ast.NodeBlock,
2437 ast.NodeBlock {
2438 .base = undefined,2389 .base = undefined,
2439 .label = null,2390 .lhs = lhs,
2440 .lbrace = token,2391 .op_token = token,
2441 .statements = ArrayList(&ast.Node).init(arena),2392 .op = ast.Node.InfixOp.Op.Period,
2442 .rbrace = undefined,2393 .rhs = undefined,
2443 }2394 }
2444 );2395 );
2445 fn_proto.body_node = &block.base;2396 stack.append(State { .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
2446 stack.append(State { .Block = block }) catch unreachable;2397 try stack.append(State { .Identifier = OptionalCtx { .Required = &node.rhs } });
2447 continue;2398 continue;
2448 },2399 },
2449 Token.Id.Semicolon => continue,
2450 else => {2400 else => {
2451 try self.parseError(&stack, token, "expected ';' or '{{', found {}", @tagName(token.id));2401 self.putBackToken(token);
2452 continue;2402 continue;
2453 },2403 },
2454 }2404 }
2455 },2405 },
24562406
2457 State.LabeledExpression => |ctx| {2407 State.PrimaryExpression => |opt_ctx| {
2458 const token = self.getNextToken();2408 const token = self.getNextToken();
2459 switch (token.id) {2409 switch (token.id) {
2460 Token.Id.LBrace => {2410 Token.Id.IntegerLiteral => {
2461 const block = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeBlock,2411 _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token);
2462 ast.NodeBlock {2412 continue;
2413 },
2414 Token.Id.FloatLiteral => {
2415 _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.FloatLiteral, token);
2416 continue;
2417 },
2418 Token.Id.CharLiteral => {
2419 _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.CharLiteral, token);
2420 continue;
2421 },
2422 Token.Id.Keyword_undefined => {
2423 _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.UndefinedLiteral, token);
2424 continue;
2425 },
2426 Token.Id.Keyword_true, Token.Id.Keyword_false => {
2427 _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.BoolLiteral, token);
2428 continue;
2429 },
2430 Token.Id.Keyword_null => {
2431 _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.NullLiteral, token);
2432 continue;
2433 },
2434 Token.Id.Keyword_this => {
2435 _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.ThisLiteral, token);
2436 continue;
2437 },
2438 Token.Id.Keyword_var => {
2439 _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.VarType, token);
2440 continue;
2441 },
2442 Token.Id.Keyword_unreachable => {
2443 _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Unreachable, token);
2444 continue;
2445 },
2446 Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
2447 opt_ctx.store((try self.parseStringLiteral(arena, token)) ?? unreachable);
2448 continue;
2449 },
2450 Token.Id.LParen => {
2451 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.GroupedExpression,
2452 ast.Node.GroupedExpression {
2463 .base = undefined,2453 .base = undefined,
2464 .label = ctx.label,2454 .lparen = token,
2465 .lbrace = token,2455 .expr = undefined,
2466 .statements = ArrayList(&ast.Node).init(arena),2456 .rparen = undefined,
2467 .rbrace = undefined,
2468 }2457 }
2469 );2458 );
2470 stack.append(State { .Block = block }) catch unreachable;2459 stack.append(State {
2460 .ExpectTokenSave = ExpectTokenSave {
2461 .id = Token.Id.RParen,
2462 .ptr = &node.rparen,
2463 }
2464 }) catch unreachable;
2465 try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
2471 continue;2466 continue;
2472 },2467 },
2473 Token.Id.Keyword_while => {2468 Token.Id.Builtin => {
2469 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.BuiltinCall,
2470 ast.Node.BuiltinCall {
2471 .base = undefined,
2472 .builtin_token = token,
2473 .params = ArrayList(&ast.Node).init(arena),
2474 .rparen_token = undefined,
2475 }
2476 );
2474 stack.append(State {2477 stack.append(State {
2475 .While = LoopCtx {2478 .ExprListItemOrEnd = ExprListCtx {
2476 .label = ctx.label,2479 .list = &node.params,
2477 .inline_token = null,2480 .end = Token.Id.RParen,
2478 .loop_token = token,2481 .ptr = &node.rparen_token,
2479 .dest_ptr = ctx.dest_ptr,
2480 }2482 }
2481 }) catch unreachable;2483 }) catch unreachable;
2484 try stack.append(State { .ExpectToken = Token.Id.LParen, });
2482 continue;2485 continue;
2483 },2486 },
2484 Token.Id.Keyword_for => {2487 Token.Id.LBracket => {
2488 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.PrefixOp,
2489 ast.Node.PrefixOp {
2490 .base = undefined,
2491 .op_token = token,
2492 .op = undefined,
2493 .rhs = undefined,
2494 }
2495 );
2496 stack.append(State { .SliceOrArrayType = node }) catch unreachable;
2497 continue;
2498 },
2499 Token.Id.Keyword_error => {
2485 stack.append(State {2500 stack.append(State {
2486 .For = LoopCtx {2501 .ErrorTypeOrSetDecl = ErrorTypeOrSetDeclCtx {
2487 .label = ctx.label,2502 .error_token = token,
2488 .inline_token = null,2503 .opt_ctx = opt_ctx
2489 .loop_token = token,
2490 .dest_ptr = ctx.dest_ptr,
2491 }2504 }
2492 }) catch unreachable;2505 }) catch unreachable;
2493 continue;2506 continue;
2494 },2507 },
2495 Token.Id.Keyword_inline => {2508 Token.Id.Keyword_packed => {
2496 stack.append(State {2509 stack.append(State {
2497 .Inline = InlineCtx {2510 .ContainerKind = ContainerKindCtx {
2498 .label = ctx.label,2511 .opt_ctx = opt_ctx,
2499 .inline_token = token,2512 .ltoken = token,
2500 .dest_ptr = ctx.dest_ptr,2513 .layout = ast.Node.ContainerDecl.Layout.Packed,
2514 },
2515 }) catch unreachable;
2516 continue;
2517 },
2518 Token.Id.Keyword_extern => {
2519 stack.append(State {
2520 .ExternType = ExternTypeCtx {
2521 .opt_ctx = opt_ctx,
2522 .extern_token = token,
2523 .comments = null,
2524 },
2525 }) catch unreachable;
2526 continue;
2527 },
2528 Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
2529 self.putBackToken(token);
2530 stack.append(State {
2531 .ContainerKind = ContainerKindCtx {
2532 .opt_ctx = opt_ctx,
2533 .ltoken = token,
2534 .layout = ast.Node.ContainerDecl.Layout.Auto,
2535 },
2536 }) catch unreachable;
2537 continue;
2538 },
2539 Token.Id.Identifier => {
2540 stack.append(State {
2541 .MaybeLabeledExpression = MaybeLabeledExpressionCtx {
2542 .label = token,
2543 .opt_ctx = opt_ctx
2501 }2544 }
2502 }) catch unreachable;2545 }) catch unreachable;
2503 continue;2546 continue;
2504 },2547 },
2505 else => {2548 Token.Id.Keyword_fn => {
2506 try self.parseError(&stack, token, "expected 'while', 'for', 'inline' or '{{', found {}", @tagName(token.id));2549 const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto,
2550 ast.Node.FnProto {
2551 .base = undefined,
2552 .comments = null,
2553 .visib_token = null,
2554 .name_token = null,
2555 .fn_token = token,
2556 .params = ArrayList(&ast.Node).init(arena),
2557 .return_type = undefined,
2558 .var_args_token = null,
2559 .extern_export_inline_token = null,
2560 .cc_token = null,
2561 .async_attr = null,
2562 .body_node = null,
2563 .lib_name = null,
2564 .align_expr = null,
2565 }
2566 );
2567 stack.append(State { .FnProto = fn_proto }) catch unreachable;
2507 continue;2568 continue;
2508 },2569 },
2509 }2570 Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
2510 },2571 const fn_proto = try self.createToCtxNode(arena, opt_ctx, ast.Node.FnProto,
25112572 ast.Node.FnProto {
2512 State.Inline => |ctx| {2573 .base = undefined,
2513 const token = self.getNextToken();2574 .comments = null,
2514 switch (token.id) {2575 .visib_token = null,
2515 Token.Id.Keyword_while => {2576 .name_token = null,
2577 .fn_token = undefined,
2578 .params = ArrayList(&ast.Node).init(arena),
2579 .return_type = undefined,
2580 .var_args_token = null,
2581 .extern_export_inline_token = null,
2582 .cc_token = token,
2583 .async_attr = null,
2584 .body_node = null,
2585 .lib_name = null,
2586 .align_expr = null,
2587 }
2588 );
2589 stack.append(State { .FnProto = fn_proto }) catch unreachable;
2590 try stack.append(State {
2591 .ExpectTokenSave = ExpectTokenSave {
2592 .id = Token.Id.Keyword_fn,
2593 .ptr = &fn_proto.fn_token
2594 }
2595 });
2596 continue;
2597 },
2598 Token.Id.Keyword_asm => {
2599 const node = try self.createToCtxNode(arena, opt_ctx, ast.Node.Asm,
2600 ast.Node.Asm {
2601 .base = undefined,
2602 .asm_token = token,
2603 .volatile_token = null,
2604 .template = undefined,
2605 //.tokens = ArrayList(ast.Node.Asm.AsmToken).init(arena),
2606 .outputs = ArrayList(&ast.Node.AsmOutput).init(arena),
2607 .inputs = ArrayList(&ast.Node.AsmInput).init(arena),
2608 .cloppers = ArrayList(&ast.Node).init(arena),
2609 .rparen = undefined,
2610 }
2611 );
2516 stack.append(State {2612 stack.append(State {
2517 .While = LoopCtx {2613 .ExpectTokenSave = ExpectTokenSave {
2518 .inline_token = ctx.inline_token,2614 .id = Token.Id.RParen,
2519 .label = ctx.label,2615 .ptr = &node.rparen,
2520 .loop_token = token,2616 }
2521 .dest_ptr = ctx.dest_ptr,2617 }) catch unreachable;
2618 try stack.append(State { .AsmClopperItems = &node.cloppers });
2619 try stack.append(State { .IfToken = Token.Id.Colon });
2620 try stack.append(State { .AsmInputItems = &node.inputs });
2621 try stack.append(State { .IfToken = Token.Id.Colon });
2622 try stack.append(State { .AsmOutputItems = &node.outputs });
2623 try stack.append(State { .IfToken = Token.Id.Colon });
2624 try stack.append(State { .StringLiteral = OptionalCtx { .Required = &node.template } });
2625 try stack.append(State { .ExpectToken = Token.Id.LParen });
2626 try stack.append(State {
2627 .OptionalTokenSave = OptionalTokenSave {
2628 .id = Token.Id.Keyword_volatile,
2629 .ptr = &node.volatile_token,
2522 }2630 }
2523 }) catch unreachable;2631 });
2524 continue;
2525 },2632 },
2526 Token.Id.Keyword_for => {2633 Token.Id.Keyword_inline => {
2527 stack.append(State {2634 stack.append(State {
2528 .For = LoopCtx {2635 .Inline = InlineCtx {
2529 .inline_token = ctx.inline_token,2636 .label = null,
2530 .label = ctx.label,2637 .inline_token = token,
2531 .loop_token = token,2638 .opt_ctx = opt_ctx,
2532 .dest_ptr = ctx.dest_ptr,
2533 }2639 }
2534 }) catch unreachable;2640 }) catch unreachable;
2535 continue;2641 continue;
2536 },2642 },
2537 else => {2643 else => {
2538 try self.parseError(&stack, token, "expected 'while' or 'for', found {}", @tagName(token.id));2644 if (!try self.parseBlockExpr(&stack, arena, opt_ctx, token)) {
2645 self.putBackToken(token);
2646 if (opt_ctx != OptionalCtx.Optional) {
2647 return self.parseError(token, "expected primary expression, found {}", @tagName(token.id));
2648 }
2649 }
2539 continue;2650 continue;
2540 },2651 }
2541 }2652 }
2542 },2653 },
25432654
2544 State.While => |ctx| {2655
2545 const node = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeWhile,2656 State.ErrorTypeOrSetDecl => |ctx| {
2546 ast.NodeWhile {2657 if (self.eatToken(Token.Id.LBrace) == null) {
2658 _ = try self.createToCtxLiteral(arena, ctx.opt_ctx, ast.Node.ErrorType, ctx.error_token);
2659 continue;
2660 }
2661
2662 const node = try self.createToCtxNode(arena, ctx.opt_ctx, ast.Node.ErrorSetDecl,
2663 ast.Node.ErrorSetDecl {
2547 .base = undefined,2664 .base = undefined,
2548 .label = ctx.label,2665 .error_token = ctx.error_token,
2549 .inline_token = ctx.inline_token,2666 .decls = ArrayList(&ast.Node).init(arena),
2550 .while_token = ctx.loop_token,2667 .rbrace_token = undefined,
2551 .condition = undefined,
2552 .payload = null,
2553 .continue_expr = null,
2554 .body = undefined,
2555 .@"else" = null,
2556 }2668 }
2557 );2669 );
2558 stack.append(State { .Else = &node.@"else" }) catch unreachable;
2559 try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
2560 try stack.append(State { .WhileContinueExpr = &node.continue_expr });
2561 try stack.append(State { .PointerPayload = &node.payload });
2562 try stack.append(State { .ExpectToken = Token.Id.RParen });
2563 try stack.append(State { .Expression = DestPtr { .Field = &node.condition } });
2564 try stack.append(State { .ExpectToken = Token.Id.LParen });
2565 },
25662670
2567 State.For => |ctx| {2671 stack.append(State {
2568 const node = try self.createToDestNode(arena, ctx.dest_ptr, ast.NodeFor,2672 .IdentifierListItemOrEnd = ListSave(&ast.Node) {
2569 ast.NodeFor {2673 .list = &node.decls,
2570 .base = undefined,2674 .ptr = &node.rbrace_token,
2571 .label = ctx.label,
2572 .inline_token = ctx.inline_token,
2573 .for_token = ctx.loop_token,
2574 .array_expr = undefined,
2575 .payload = null,
2576 .body = undefined,
2577 .@"else" = null,
2578 }2675 }
2579 );2676 }) catch unreachable;
2580 stack.append(State { .Else = &node.@"else" }) catch unreachable;2677 continue;
2581 try stack.append(State { .Expression = DestPtr { .Field = &node.body } });
2582 try stack.append(State { .PointerIndexPayload = &node.payload });
2583 try stack.append(State { .ExpectToken = Token.Id.RParen });
2584 try stack.append(State { .Expression = DestPtr { .Field = &node.array_expr } });
2585 try stack.append(State { .ExpectToken = Token.Id.LParen });
2586 },2678 },
25872679 State.StringLiteral => |opt_ctx| {
2588 State.Block => |block| {
2589 const token = self.getNextToken();2680 const token = self.getNextToken();
2590 switch (token.id) {2681 opt_ctx.store(
2591 Token.Id.RBrace => {2682 (try self.parseStringLiteral(arena, token)) ?? {
2592 block.rbrace = token;
2593 continue;
2594 },
2595 else => {
2596 self.putBackToken(token);2683 self.putBackToken(token);
2597 stack.append(State { .Block = block }) catch unreachable;2684 if (opt_ctx != OptionalCtx.Optional) {
2598 try stack.append(State { .Statement = block });2685 return self.parseError(token, "expected primary expression, found {}", @tagName(token.id));
2686 }
2687
2599 continue;2688 continue;
2600 },2689 }
2690 );
2691 },
2692 State.Identifier => |opt_ctx| {
2693 if (self.eatToken(Token.Id.Identifier)) |ident_token| {
2694 _ = try self.createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token);
2695 continue;
2696 }
2697
2698 if (opt_ctx != OptionalCtx.Optional) {
2699 const token = self.getNextToken();
2700 return self.parseError(token, "expected identifier, found {}", @tagName(token.id));
2601 }2701 }
2602 },2702 },
26032703
2604 State.Statement => |block| {2704
2605 const next = self.getNextToken();2705 State.ExpectToken => |token_id| {
2606 switch (next.id) {2706 _ = try self.expectToken(token_id);
2607 Token.Id.Keyword_comptime => {2707 continue;
2608 const mut_token = self.getNextToken();2708 },
2609 if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {2709 State.ExpectTokenSave => |expect_token_save| {
2610 const var_decl = try self.createAttachNode(arena, &block.statements, ast.NodeVarDecl,2710 *expect_token_save.ptr = try self.expectToken(expect_token_save.id);
2611 ast.NodeVarDecl {2711 continue;
2612 .base = undefined,2712 },
2613 .visib_token = null,2713 State.IfToken => |token_id| {
2614 .mut_token = mut_token,2714 if (self.eatToken(token_id)) |_| {
2615 .comptime_token = next,2715 continue;
2616 .extern_export_token = null,
2617 .type_node = null,
2618 .align_node = null,
2619 .init_node = null,
2620 .lib_name = null,
2621 // initialized later
2622 .name_token = undefined,
2623 .eq_token = undefined,
2624 .semicolon_token = undefined,
2625 }
2626 );
2627 stack.append(State { .VarDecl = var_decl }) catch unreachable;
2628 continue;
2629 } else {
2630 self.putBackToken(mut_token);
2631 self.putBackToken(next);
2632 const statememt = try block.statements.addOne();
2633 stack.append(State { .Semicolon = statememt }) catch unreachable;
2634 try stack.append(State { .Expression = DestPtr{.Field = statememt } });
2635 }
2636 },
2637 Token.Id.Keyword_var, Token.Id.Keyword_const => {
2638 const var_decl = try self.createAttachNode(arena, &block.statements, ast.NodeVarDecl,
2639 ast.NodeVarDecl {
2640 .base = undefined,
2641 .visib_token = null,
2642 .mut_token = next,
2643 .comptime_token = null,
2644 .extern_export_token = null,
2645 .type_node = null,
2646 .align_node = null,
2647 .init_node = null,
2648 .lib_name = null,
2649 // initialized later
2650 .name_token = undefined,
2651 .eq_token = undefined,
2652 .semicolon_token = undefined,
2653 }
2654 );
2655 stack.append(State { .VarDecl = var_decl }) catch unreachable;
2656 continue;
2657 },
2658 Token.Id.Keyword_defer, Token.Id.Keyword_errdefer => {
2659 const node = try self.createAttachNode(arena, &block.statements, ast.NodeDefer,
2660 ast.NodeDefer {
2661 .base = undefined,
2662 .defer_token = next,
2663 .kind = switch (next.id) {
2664 Token.Id.Keyword_defer => ast.NodeDefer.Kind.Unconditional,
2665 Token.Id.Keyword_errdefer => ast.NodeDefer.Kind.Error,
2666 else => unreachable,
2667 },
2668 .expr = undefined,
2669 }
2670 );
2671 stack.append(State { .Semicolon = &node.base }) catch unreachable;
2672 try stack.append(State { .AssignmentExpressionBegin = DestPtr{.Field = &node.expr } });
2673 continue;
2674 },
2675 Token.Id.LBrace => {
2676 const inner_block = try self.createAttachNode(arena, &block.statements, ast.NodeBlock,
2677 ast.NodeBlock {
2678 .base = undefined,
2679 .label = null,
2680 .lbrace = next,
2681 .statements = ArrayList(&ast.Node).init(arena),
2682 .rbrace = undefined,
2683 }
2684 );
2685 stack.append(State { .Block = inner_block }) catch unreachable;
2686 continue;
2687 },
2688 else => {
2689 self.putBackToken(next);
2690 const statememt = try block.statements.addOne();
2691 stack.append(State { .Semicolon = statememt }) catch unreachable;
2692 try stack.append(State { .AssignmentExpressionBegin = DestPtr{.Field = statememt } });
2693 continue;
2694 }
2695 }2716 }
26962717
2718 _ = stack.pop();
2719 continue;
2697 },2720 },
2721 State.IfTokenSave => |if_token_save| {
2722 if (self.eatToken(if_token_save.id)) |token| {
2723 *if_token_save.ptr = token;
2724 continue;
2725 }
26982726
2699 State.Semicolon => |node_ptr| {2727 _ = stack.pop();
2700 const node = *node_ptr;2728 continue;
2701 if (requireSemiColon(node)) {2729 },
2702 _ = (try self.expectToken(&stack, Token.Id.Semicolon)) ?? continue;2730 State.OptionalTokenSave => |optional_token_save| {
2731 if (self.eatToken(optional_token_save.id)) |token| {
2732 *optional_token_save.ptr = token;
2733 continue;
2703 }2734 }
2704 }2735
2736 continue;
2737 },
2738 }
2739 }
2740 }
2741
2742 fn eatComments(self: &Parser, arena: &mem.Allocator) !?&ast.Node.LineComment {
2743 var result: ?&ast.Node.LineComment = null;
2744 while (true) {
2745 if (self.eatToken(Token.Id.LineComment)) |line_comment| {
2746 const node = blk: {
2747 if (result) |comment_node| {
2748 break :blk comment_node;
2749 } else {
2750 const comment_node = try arena.create(ast.Node.LineComment);
2751 *comment_node = ast.Node.LineComment {
2752 .base = ast.Node {
2753 .id = ast.Node.Id.LineComment,
2754 },
2755 .lines = ArrayList(Token).init(arena),
2756 };
2757 result = comment_node;
2758 break :blk comment_node;
2759 }
2760 };
2761 try node.lines.append(line_comment);
2762 continue;
2705 }2763 }
2764 break;
2706 }2765 }
2766 return result;
2707 }2767 }
27082768
2709 fn requireSemiColon(node: &const ast.Node) bool {2769 fn requireSemiColon(node: &const ast.Node) bool {
...@@ -2726,7 +2786,7 @@ pub const Parser = struct {...@@ -2726,7 +2786,7 @@ pub const Parser = struct {
2726 ast.Node.Id.LineComment,2786 ast.Node.Id.LineComment,
2727 ast.Node.Id.TestDecl => return false,2787 ast.Node.Id.TestDecl => return false,
2728 ast.Node.Id.While => {2788 ast.Node.Id.While => {
2729 const while_node = @fieldParentPtr(ast.NodeWhile, "base", n);2789 const while_node = @fieldParentPtr(ast.Node.While, "base", n);
2730 if (while_node.@"else") |@"else"| {2790 if (while_node.@"else") |@"else"| {
2731 n = @"else".base;2791 n = @"else".base;
2732 continue;2792 continue;
...@@ -2735,7 +2795,7 @@ pub const Parser = struct {...@@ -2735,7 +2795,7 @@ pub const Parser = struct {
2735 return while_node.body.id != ast.Node.Id.Block;2795 return while_node.body.id != ast.Node.Id.Block;
2736 },2796 },
2737 ast.Node.Id.For => {2797 ast.Node.Id.For => {
2738 const for_node = @fieldParentPtr(ast.NodeFor, "base", n);2798 const for_node = @fieldParentPtr(ast.Node.For, "base", n);
2739 if (for_node.@"else") |@"else"| {2799 if (for_node.@"else") |@"else"| {
2740 n = @"else".base;2800 n = @"else".base;
2741 continue;2801 continue;
...@@ -2744,7 +2804,7 @@ pub const Parser = struct {...@@ -2744,7 +2804,7 @@ pub const Parser = struct {
2744 return for_node.body.id != ast.Node.Id.Block;2804 return for_node.body.id != ast.Node.Id.Block;
2745 },2805 },
2746 ast.Node.Id.If => {2806 ast.Node.Id.If => {
2747 const if_node = @fieldParentPtr(ast.NodeIf, "base", n);2807 const if_node = @fieldParentPtr(ast.Node.If, "base", n);
2748 if (if_node.@"else") |@"else"| {2808 if (if_node.@"else") |@"else"| {
2749 n = @"else".base;2809 n = @"else".base;
2750 continue;2810 continue;
...@@ -2753,20 +2813,20 @@ pub const Parser = struct {...@@ -2753,20 +2813,20 @@ pub const Parser = struct {
2753 return if_node.body.id != ast.Node.Id.Block;2813 return if_node.body.id != ast.Node.Id.Block;
2754 },2814 },
2755 ast.Node.Id.Else => {2815 ast.Node.Id.Else => {
2756 const else_node = @fieldParentPtr(ast.NodeElse, "base", n);2816 const else_node = @fieldParentPtr(ast.Node.Else, "base", n);
2757 n = else_node.body;2817 n = else_node.body;
2758 continue;2818 continue;
2759 },2819 },
2760 ast.Node.Id.Defer => {2820 ast.Node.Id.Defer => {
2761 const defer_node = @fieldParentPtr(ast.NodeDefer, "base", n);2821 const defer_node = @fieldParentPtr(ast.Node.Defer, "base", n);
2762 return defer_node.expr.id != ast.Node.Id.Block;2822 return defer_node.expr.id != ast.Node.Id.Block;
2763 },2823 },
2764 ast.Node.Id.Comptime => {2824 ast.Node.Id.Comptime => {
2765 const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", n);2825 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", n);
2766 return comptime_node.expr.id != ast.Node.Id.Block;2826 return comptime_node.expr.id != ast.Node.Id.Block;
2767 },2827 },
2768 ast.Node.Id.Suspend => {2828 ast.Node.Id.Suspend => {
2769 const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", n);2829 const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", n);
2770 if (suspend_node.body) |body| {2830 if (suspend_node.body) |body| {
2771 return body.id != ast.Node.Id.Block;2831 return body.id != ast.Node.Id.Block;
2772 }2832 }
...@@ -2781,11 +2841,11 @@ pub const Parser = struct {...@@ -2781,11 +2841,11 @@ pub const Parser = struct {
2781 fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node {2841 fn parseStringLiteral(self: &Parser, arena: &mem.Allocator, token: &const Token) !?&ast.Node {
2782 switch (token.id) {2842 switch (token.id) {
2783 Token.Id.StringLiteral => {2843 Token.Id.StringLiteral => {
2784 return &(try self.createLiteral(arena, ast.NodeStringLiteral, token)).base;2844 return &(try self.createLiteral(arena, ast.Node.StringLiteral, token)).base;
2785 },2845 },
2786 Token.Id.MultilineStringLiteralLine => {2846 Token.Id.MultilineStringLiteralLine => {
2787 const node = try self.createNode(arena, ast.NodeMultilineStringLiteral,2847 const node = try self.createNode(arena, ast.Node.MultilineStringLiteral,
2788 ast.NodeMultilineStringLiteral {2848 ast.Node.MultilineStringLiteral {
2789 .base = undefined,2849 .base = undefined,
2790 .tokens = ArrayList(Token).init(arena),2850 .tokens = ArrayList(Token).init(arena),
2791 }2851 }
...@@ -2809,11 +2869,11 @@ pub const Parser = struct {...@@ -2809,11 +2869,11 @@ pub const Parser = struct {
2809 }2869 }
2810 }2870 }
28112871
2812 fn parseBlockExpr(self: &Parser, stack: &ArrayList(State), arena: &mem.Allocator, dest_ptr: &const DestPtr, token: &const Token) !bool {2872 fn parseBlockExpr(self: &Parser, stack: &ArrayList(State), arena: &mem.Allocator, ctx: &const OptionalCtx, token: &const Token) !bool {
2813 switch (token.id) {2873 switch (token.id) {
2814 Token.Id.Keyword_suspend => {2874 Token.Id.Keyword_suspend => {
2815 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSuspend,2875 const node = try self.createToCtxNode(arena, ctx, ast.Node.Suspend,
2816 ast.NodeSuspend {2876 ast.Node.Suspend {
2817 .base = undefined,2877 .base = undefined,
2818 .suspend_token = *token,2878 .suspend_token = *token,
2819 .payload = null,2879 .payload = null,
...@@ -2822,12 +2882,12 @@ pub const Parser = struct {...@@ -2822,12 +2882,12 @@ pub const Parser = struct {
2822 );2882 );
28232883
2824 stack.append(State { .SuspendBody = node }) catch unreachable;2884 stack.append(State { .SuspendBody = node }) catch unreachable;
2825 try stack.append(State { .Payload = &node.payload });2885 try stack.append(State { .Payload = OptionalCtx { .Optional = &node.payload } });
2826 return true;2886 return true;
2827 },2887 },
2828 Token.Id.Keyword_if => {2888 Token.Id.Keyword_if => {
2829 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeIf,2889 const node = try self.createToCtxNode(arena, ctx, ast.Node.If,
2830 ast.NodeIf {2890 ast.Node.If {
2831 .base = undefined,2891 .base = undefined,
2832 .if_token = *token,2892 .if_token = *token,
2833 .condition = undefined,2893 .condition = undefined,
...@@ -2838,10 +2898,10 @@ pub const Parser = struct {...@@ -2838,10 +2898,10 @@ pub const Parser = struct {
2838 );2898 );
28392899
2840 stack.append(State { .Else = &node.@"else" }) catch unreachable;2900 stack.append(State { .Else = &node.@"else" }) catch unreachable;
2841 try stack.append(State { .Expression = DestPtr { .Field = &node.body } });2901 try stack.append(State { .Expression = OptionalCtx { .Required = &node.body } });
2842 try stack.append(State { .PointerPayload = &node.payload });2902 try stack.append(State { .PointerPayload = OptionalCtx { .Optional = &node.payload } });
2843 try stack.append(State { .ExpectToken = Token.Id.RParen });2903 try stack.append(State { .ExpectToken = Token.Id.RParen });
2844 try stack.append(State { .Expression = DestPtr { .Field = &node.condition } });2904 try stack.append(State { .Expression = OptionalCtx { .Required = &node.condition } });
2845 try stack.append(State { .ExpectToken = Token.Id.LParen });2905 try stack.append(State { .ExpectToken = Token.Id.LParen });
2846 return true;2906 return true;
2847 },2907 },
...@@ -2851,7 +2911,7 @@ pub const Parser = struct {...@@ -2851,7 +2911,7 @@ pub const Parser = struct {
2851 .label = null,2911 .label = null,
2852 .inline_token = null,2912 .inline_token = null,
2853 .loop_token = *token,2913 .loop_token = *token,
2854 .dest_ptr = *dest_ptr,2914 .opt_ctx = *ctx,
2855 }2915 }
2856 }) catch unreachable;2916 }) catch unreachable;
2857 return true;2917 return true;
...@@ -2862,48 +2922,48 @@ pub const Parser = struct {...@@ -2862,48 +2922,48 @@ pub const Parser = struct {
2862 .label = null,2922 .label = null,
2863 .inline_token = null,2923 .inline_token = null,
2864 .loop_token = *token,2924 .loop_token = *token,
2865 .dest_ptr = *dest_ptr,2925 .opt_ctx = *ctx,
2866 }2926 }
2867 }) catch unreachable;2927 }) catch unreachable;
2868 return true;2928 return true;
2869 },2929 },
2870 Token.Id.Keyword_switch => {2930 Token.Id.Keyword_switch => {
2871 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeSwitch,2931 const node = try self.createToCtxNode(arena, ctx, ast.Node.Switch,
2872 ast.NodeSwitch {2932 ast.Node.Switch {
2873 .base = undefined,2933 .base = undefined,
2874 .switch_token = *token,2934 .switch_token = *token,
2875 .expr = undefined,2935 .expr = undefined,
2876 .cases = ArrayList(&ast.NodeSwitchCase).init(arena),2936 .cases = ArrayList(&ast.Node.SwitchCase).init(arena),
2877 .rbrace = undefined,2937 .rbrace = undefined,
2878 }2938 }
2879 );2939 );
28802940
2881 stack.append(State {2941 stack.append(State {
2882 .SwitchCaseOrEnd = ListSave(&ast.NodeSwitchCase) {2942 .SwitchCaseOrEnd = ListSave(&ast.Node.SwitchCase) {
2883 .list = &node.cases,2943 .list = &node.cases,
2884 .ptr = &node.rbrace,2944 .ptr = &node.rbrace,
2885 },2945 },
2886 }) catch unreachable;2946 }) catch unreachable;
2887 try stack.append(State { .ExpectToken = Token.Id.LBrace });2947 try stack.append(State { .ExpectToken = Token.Id.LBrace });
2888 try stack.append(State { .ExpectToken = Token.Id.RParen });2948 try stack.append(State { .ExpectToken = Token.Id.RParen });
2889 try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });2949 try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
2890 try stack.append(State { .ExpectToken = Token.Id.LParen });2950 try stack.append(State { .ExpectToken = Token.Id.LParen });
2891 return true;2951 return true;
2892 },2952 },
2893 Token.Id.Keyword_comptime => {2953 Token.Id.Keyword_comptime => {
2894 const node = try self.createToDestNode(arena, dest_ptr, ast.NodeComptime,2954 const node = try self.createToCtxNode(arena, ctx, ast.Node.Comptime,
2895 ast.NodeComptime {2955 ast.Node.Comptime {
2896 .base = undefined,2956 .base = undefined,
2897 .comptime_token = *token,2957 .comptime_token = *token,
2898 .expr = undefined,2958 .expr = undefined,
2899 }2959 }
2900 );2960 );
2901 try stack.append(State { .Expression = DestPtr { .Field = &node.expr } });2961 try stack.append(State { .Expression = OptionalCtx { .Required = &node.expr } });
2902 return true;2962 return true;
2903 },2963 },
2904 Token.Id.LBrace => {2964 Token.Id.LBrace => {
2905 const block = try self.createToDestNode(arena, dest_ptr, ast.NodeBlock,2965 const block = try self.createToCtxNode(arena, ctx, ast.Node.Block,
2906 ast.NodeBlock {2966 ast.Node.Block {
2907 .base = undefined,2967 .base = undefined,
2908 .label = null,2968 .label = null,
2909 .lbrace = *token,2969 .lbrace = *token,
...@@ -2920,100 +2980,102 @@ pub const Parser = struct {...@@ -2920,100 +2980,102 @@ pub const Parser = struct {
2920 }2980 }
2921 }2981 }
29222982
2923 fn commaOrEnd(self: &Parser, stack: &ArrayList(State), end: &const Token.Id, maybe_ptr: ?&Token, state_after_comma: &const State) !void {2983 fn expectCommaOrEnd(self: &Parser, end: @TagType(Token.Id)) !?Token {
2924 var token = self.getNextToken();2984 var token = self.getNextToken();
2925 switch (token.id) {2985 switch (token.id) {
2926 Token.Id.Comma => {2986 Token.Id.Comma => return null,
2927 stack.append(state_after_comma) catch unreachable;
2928 },
2929 else => {2987 else => {
2930 const IdTag = @TagType(Token.Id);2988 if (end == token.id) {
2931 if (IdTag(*end) == token.id) {2989 return token;
2932 if (maybe_ptr) |ptr| {
2933 *ptr = token;
2934 }
2935 return;
2936 }2990 }
29372991
2938 try self.parseError(stack, token, "expected ',' or {}, found {}", @tagName(*end), @tagName(token.id));2992 return self.parseError(token, "expected ',' or {}, found {}", @tagName(end), @tagName(token.id));
2939 },2993 },
2940 }2994 }
2941 }2995 }
29422996
2943 fn tokenIdToAssignment(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {2997 fn tokenIdToAssignment(id: &const Token.Id) ?ast.Node.InfixOp.Op {
2944 // TODO: We have to cast all cases because of this:2998 // TODO: We have to cast all cases because of this:
2945 // error: expected type '?InfixOp', found '?@TagType(InfixOp)'2999 // error: expected type '?InfixOp', found '?@TagType(InfixOp)'
2946 return switch (*id) {3000 return switch (*id) {
2947 Token.Id.AmpersandEqual => ast.NodeInfixOp.InfixOp { .AssignBitAnd = void{} },3001 Token.Id.AmpersandEqual => ast.Node.InfixOp.Op { .AssignBitAnd = void{} },
2948 Token.Id.AngleBracketAngleBracketLeftEqual => ast.NodeInfixOp.InfixOp { .AssignBitShiftLeft = void{} },3002 Token.Id.AngleBracketAngleBracketLeftEqual => ast.Node.InfixOp.Op { .AssignBitShiftLeft = void{} },
2949 Token.Id.AngleBracketAngleBracketRightEqual => ast.NodeInfixOp.InfixOp { .AssignBitShiftRight = void{} },3003 Token.Id.AngleBracketAngleBracketRightEqual => ast.Node.InfixOp.Op { .AssignBitShiftRight = void{} },
2950 Token.Id.AsteriskEqual => ast.NodeInfixOp.InfixOp { .AssignTimes = void{} },3004 Token.Id.AsteriskEqual => ast.Node.InfixOp.Op { .AssignTimes = void{} },
2951 Token.Id.AsteriskPercentEqual => ast.NodeInfixOp.InfixOp { .AssignTimesWarp = void{} },3005 Token.Id.AsteriskPercentEqual => ast.Node.InfixOp.Op { .AssignTimesWarp = void{} },
2952 Token.Id.CaretEqual => ast.NodeInfixOp.InfixOp { .AssignBitXor = void{} },3006 Token.Id.CaretEqual => ast.Node.InfixOp.Op { .AssignBitXor = void{} },
2953 Token.Id.Equal => ast.NodeInfixOp.InfixOp { .Assign = void{} },3007 Token.Id.Equal => ast.Node.InfixOp.Op { .Assign = void{} },
2954 Token.Id.MinusEqual => ast.NodeInfixOp.InfixOp { .AssignMinus = void{} },3008 Token.Id.MinusEqual => ast.Node.InfixOp.Op { .AssignMinus = void{} },
2955 Token.Id.MinusPercentEqual => ast.NodeInfixOp.InfixOp { .AssignMinusWrap = void{} },3009 Token.Id.MinusPercentEqual => ast.Node.InfixOp.Op { .AssignMinusWrap = void{} },
2956 Token.Id.PercentEqual => ast.NodeInfixOp.InfixOp { .AssignMod = void{} },3010 Token.Id.PercentEqual => ast.Node.InfixOp.Op { .AssignMod = void{} },
2957 Token.Id.PipeEqual => ast.NodeInfixOp.InfixOp { .AssignBitOr = void{} },3011 Token.Id.PipeEqual => ast.Node.InfixOp.Op { .AssignBitOr = void{} },
2958 Token.Id.PlusEqual => ast.NodeInfixOp.InfixOp { .AssignPlus = void{} },3012 Token.Id.PlusEqual => ast.Node.InfixOp.Op { .AssignPlus = void{} },
2959 Token.Id.PlusPercentEqual => ast.NodeInfixOp.InfixOp { .AssignPlusWrap = void{} },3013 Token.Id.PlusPercentEqual => ast.Node.InfixOp.Op { .AssignPlusWrap = void{} },
2960 Token.Id.SlashEqual => ast.NodeInfixOp.InfixOp { .AssignDiv = void{} },3014 Token.Id.SlashEqual => ast.Node.InfixOp.Op { .AssignDiv = void{} },
2961 else => null,3015 else => null,
2962 };3016 };
2963 }3017 }
29643018
2965 fn tokenIdToComparison(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {3019 fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
2966 return switch (*id) {3020 return switch (id) {
2967 Token.Id.BangEqual => ast.NodeInfixOp.InfixOp { .BangEqual = void{} },3021 Token.Id.Keyword_catch => ast.Node.InfixOp.Op { .Catch = null },
2968 Token.Id.EqualEqual => ast.NodeInfixOp.InfixOp { .EqualEqual = void{} },3022 Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op { .UnwrapMaybe = void{} },
2969 Token.Id.AngleBracketLeft => ast.NodeInfixOp.InfixOp { .LessThan = void{} },
2970 Token.Id.AngleBracketLeftEqual => ast.NodeInfixOp.InfixOp { .LessOrEqual = void{} },
2971 Token.Id.AngleBracketRight => ast.NodeInfixOp.InfixOp { .GreaterThan = void{} },
2972 Token.Id.AngleBracketRightEqual => ast.NodeInfixOp.InfixOp { .GreaterOrEqual = void{} },
2973 else => null,3023 else => null,
2974 };3024 };
2975 }3025 }
29763026
2977 fn tokenIdToBitShift(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {3027 fn tokenIdToComparison(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
2978 return switch (*id) {3028 return switch (id) {
2979 Token.Id.AngleBracketAngleBracketLeft => ast.NodeInfixOp.InfixOp { .BitShiftLeft = void{} },3029 Token.Id.BangEqual => ast.Node.InfixOp.Op { .BangEqual = void{} },
2980 Token.Id.AngleBracketAngleBracketRight => ast.NodeInfixOp.InfixOp { .BitShiftRight = void{} },3030 Token.Id.EqualEqual => ast.Node.InfixOp.Op { .EqualEqual = void{} },
3031 Token.Id.AngleBracketLeft => ast.Node.InfixOp.Op { .LessThan = void{} },
3032 Token.Id.AngleBracketLeftEqual => ast.Node.InfixOp.Op { .LessOrEqual = void{} },
3033 Token.Id.AngleBracketRight => ast.Node.InfixOp.Op { .GreaterThan = void{} },
3034 Token.Id.AngleBracketRightEqual => ast.Node.InfixOp.Op { .GreaterOrEqual = void{} },
2981 else => null,3035 else => null,
2982 };3036 };
2983 }3037 }
29843038
2985 fn tokenIdToAddition(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {3039 fn tokenIdToBitShift(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
2986 return switch (*id) {3040 return switch (id) {
2987 Token.Id.Minus => ast.NodeInfixOp.InfixOp { .Sub = void{} },3041 Token.Id.AngleBracketAngleBracketLeft => ast.Node.InfixOp.Op { .BitShiftLeft = void{} },
2988 Token.Id.MinusPercent => ast.NodeInfixOp.InfixOp { .SubWrap = void{} },3042 Token.Id.AngleBracketAngleBracketRight => ast.Node.InfixOp.Op { .BitShiftRight = void{} },
2989 Token.Id.Plus => ast.NodeInfixOp.InfixOp { .Add = void{} },
2990 Token.Id.PlusPercent => ast.NodeInfixOp.InfixOp { .AddWrap = void{} },
2991 Token.Id.PlusPlus => ast.NodeInfixOp.InfixOp { .ArrayCat = void{} },
2992 else => null,3043 else => null,
2993 };3044 };
2994 }3045 }
29953046
2996 fn tokenIdToMultiply(id: &const Token.Id) ?ast.NodeInfixOp.InfixOp {3047 fn tokenIdToAddition(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
2997 return switch (*id) {3048 return switch (id) {
2998 Token.Id.Slash => ast.NodeInfixOp.InfixOp { .Div = void{} },3049 Token.Id.Minus => ast.Node.InfixOp.Op { .Sub = void{} },
2999 Token.Id.Asterisk => ast.NodeInfixOp.InfixOp { .Mult = void{} },3050 Token.Id.MinusPercent => ast.Node.InfixOp.Op { .SubWrap = void{} },
3000 Token.Id.AsteriskAsterisk => ast.NodeInfixOp.InfixOp { .ArrayMult = void{} },3051 Token.Id.Plus => ast.Node.InfixOp.Op { .Add = void{} },
3001 Token.Id.AsteriskPercent => ast.NodeInfixOp.InfixOp { .MultWrap = void{} },3052 Token.Id.PlusPercent => ast.Node.InfixOp.Op { .AddWrap = void{} },
3002 Token.Id.Percent => ast.NodeInfixOp.InfixOp { .Mod = void{} },3053 Token.Id.PlusPlus => ast.Node.InfixOp.Op { .ArrayCat = void{} },
3003 Token.Id.PipePipe => ast.NodeInfixOp.InfixOp { .MergeErrorSets = void{} },
3004 else => null,3054 else => null,
3005 };3055 };
3006 }3056 }
30073057
3008 fn tokenIdToPrefixOp(id: &const Token.Id) ?ast.NodePrefixOp.PrefixOp {3058 fn tokenIdToMultiply(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
3009 return switch (*id) {3059 return switch (id) {
3010 Token.Id.Bang => ast.NodePrefixOp.PrefixOp { .BoolNot = void{} },3060 Token.Id.Slash => ast.Node.InfixOp.Op { .Div = void{} },
3011 Token.Id.Tilde => ast.NodePrefixOp.PrefixOp { .BitNot = void{} },3061 Token.Id.Asterisk => ast.Node.InfixOp.Op { .Mult = void{} },
3012 Token.Id.Minus => ast.NodePrefixOp.PrefixOp { .Negation = void{} },3062 Token.Id.AsteriskAsterisk => ast.Node.InfixOp.Op { .ArrayMult = void{} },
3013 Token.Id.MinusPercent => ast.NodePrefixOp.PrefixOp { .NegationWrap = void{} },3063 Token.Id.AsteriskPercent => ast.Node.InfixOp.Op { .MultWrap = void{} },
3014 Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.NodePrefixOp.PrefixOp { .Deref = void{} },3064 Token.Id.Percent => ast.Node.InfixOp.Op { .Mod = void{} },
3015 Token.Id.Ampersand => ast.NodePrefixOp.PrefixOp {3065 Token.Id.PipePipe => ast.Node.InfixOp.Op { .MergeErrorSets = void{} },
3016 .AddrOf = ast.NodePrefixOp.AddrOfInfo {3066 else => null,
3067 };
3068 }
3069
3070 fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op {
3071 return switch (id) {
3072 Token.Id.Bang => ast.Node.PrefixOp.Op { .BoolNot = void{} },
3073 Token.Id.Tilde => ast.Node.PrefixOp.Op { .BitNot = void{} },
3074 Token.Id.Minus => ast.Node.PrefixOp.Op { .Negation = void{} },
3075 Token.Id.MinusPercent => ast.Node.PrefixOp.Op { .NegationWrap = void{} },
3076 Token.Id.Asterisk, Token.Id.AsteriskAsterisk => ast.Node.PrefixOp.Op { .Deref = void{} },
3077 Token.Id.Ampersand => ast.Node.PrefixOp.Op {
3078 .AddrOf = ast.Node.PrefixOp.AddrOfInfo {
3017 .align_expr = null,3079 .align_expr = null,
3018 .bit_offset_start_token = null,3080 .bit_offset_start_token = null,
3019 .bit_offset_end_token = null,3081 .bit_offset_end_token = null,
...@@ -3021,10 +3083,10 @@ pub const Parser = struct {...@@ -3021,10 +3083,10 @@ pub const Parser = struct {
3021 .volatile_token = null,3083 .volatile_token = null,
3022 },3084 },
3023 },3085 },
3024 Token.Id.QuestionMark => ast.NodePrefixOp.PrefixOp { .MaybeType = void{} },3086 Token.Id.QuestionMark => ast.Node.PrefixOp.Op { .MaybeType = void{} },
3025 Token.Id.QuestionMarkQuestionMark => ast.NodePrefixOp.PrefixOp { .UnwrapMaybe = void{} },3087 Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op { .UnwrapMaybe = void{} },
3026 Token.Id.Keyword_await => ast.NodePrefixOp.PrefixOp { .Await = void{} },3088 Token.Id.Keyword_await => ast.Node.PrefixOp.Op { .Await = void{} },
3027 Token.Id.Keyword_try => ast.NodePrefixOp.PrefixOp { .Try = void{ } },3089 Token.Id.Keyword_try => ast.Node.PrefixOp.Op { .Try = void{ } },
3028 else => null,3090 else => null,
3029 };3091 };
3030 }3092 }
...@@ -3034,11 +3096,7 @@ pub const Parser = struct {...@@ -3034,11 +3096,7 @@ pub const Parser = struct {
3034 *node = *init_to;3096 *node = *init_to;
3035 node.base = blk: {3097 node.base = blk: {
3036 const id = ast.Node.typeToId(T);3098 const id = ast.Node.typeToId(T);
3037 if (self.pending_line_comment_node) |comment_node| {3099 break :blk ast.Node {.id = id};
3038 self.pending_line_comment_node = null;
3039 break :blk ast.Node {.id = id, .comment = comment_node};
3040 }
3041 break :blk ast.Node {.id = id, .comment = null };
3042 };3100 };
30433101
3044 return node;3102 return node;
...@@ -3051,9 +3109,9 @@ pub const Parser = struct {...@@ -3051,9 +3109,9 @@ pub const Parser = struct {
3051 return node;3109 return node;
3052 }3110 }
30533111
3054 fn createToDestNode(self: &Parser, arena: &mem.Allocator, dest_ptr: &const DestPtr, comptime T: type, init_to: &const T) !&T {3112 fn createToCtxNode(self: &Parser, arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, init_to: &const T) !&T {
3055 const node = try self.createNode(arena, T, init_to);3113 const node = try self.createNode(arena, T, init_to);
3056 dest_ptr.store(&node.base);3114 opt_ctx.store(&node.base);
30573115
3058 return node;3116 return node;
3059 }3117 }
...@@ -3067,51 +3125,38 @@ pub const Parser = struct {...@@ -3067,51 +3125,38 @@ pub const Parser = struct {
3067 );3125 );
3068 }3126 }
30693127
3070 fn parseError(self: &Parser, stack: &ArrayList(State), token: &const Token, comptime fmt: []const u8, args: ...) !void {3128 fn createToCtxLiteral(self: &Parser, arena: &mem.Allocator, opt_ctx: &const OptionalCtx, comptime T: type, token: &const Token) !&T {
3071 // Before reporting an error. We pop the stack to see if our state was optional3129 const node = try self.createLiteral(arena, T, token);
3072 self.revertIfOptional(stack) catch {3130 opt_ctx.store(&node.base);
3073 const loc = self.tokenizer.getTokenLocation(0, token);3131
3074 warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);3132 return node;
3075 warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
3076 {
3077 var i: usize = 0;
3078 while (i < loc.column) : (i += 1) {
3079 warn(" ");
3080 }
3081 }
3082 {
3083 const caret_count = token.end - token.start;
3084 var i: usize = 0;
3085 while (i < caret_count) : (i += 1) {
3086 warn("~");
3087 }
3088 }
3089 warn("\n");
3090 return error.ParseError;
3091 };
3092 }3133 }
30933134
3094 fn revertIfOptional(self: &Parser, stack: &ArrayList(State)) !void {3135 fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) (error{ParseError}) {
3095 while (stack.popOrNull()) |state| {3136 const loc = self.tokenizer.getTokenLocation(0, token);
3096 switch (state) {3137 warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
3097 State.Optional => |revert| {3138 warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
3098 *self = revert.parser;3139 {
3099 *self.tokenizer = revert.tokenizer;3140 var i: usize = 0;
3100 *revert.ptr = null;3141 while (i < loc.column) : (i += 1) {
3101 return;3142 warn(" ");
3102 },
3103 else => { }
3104 }3143 }
3105 }3144 }
31063145 {
3107 return error.NoOptionalStateFound;3146 const caret_count = token.end - token.start;
3147 var i: usize = 0;
3148 while (i < caret_count) : (i += 1) {
3149 warn("~");
3150 }
3151 }
3152 warn("\n");
3153 return error.ParseError;
3108 }3154 }
31093155
3110 fn expectToken(self: &Parser, stack: &ArrayList(State), id: @TagType(Token.Id)) !?Token {3156 fn expectToken(self: &Parser, id: @TagType(Token.Id)) !Token {
3111 const token = self.getNextToken();3157 const token = self.getNextToken();
3112 if (token.id != id) {3158 if (token.id != id) {
3113 try self.parseError(stack, token, "expected {}, found {}", @tagName(id), @tagName(token.id));3159 return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id));
3114 return null;
3115 }3160 }
3116 return token;3161 return token;
3117 }3162 }
...@@ -3150,7 +3195,7 @@ pub const Parser = struct {...@@ -3150,7 +3195,7 @@ pub const Parser = struct {
3150 indent: usize,3195 indent: usize,
3151 };3196 };
31523197
3153 pub fn renderAst(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void {3198 pub fn renderAst(self: &Parser, stream: var, root_node: &ast.Node.Root) !void {
3154 var stack = self.initUtilityArrayList(RenderAstFrame);3199 var stack = self.initUtilityArrayList(RenderAstFrame);
3155 defer self.deinitUtilityArrayList(stack);3200 defer self.deinitUtilityArrayList(stack);
31563201
...@@ -3179,18 +3224,17 @@ pub const Parser = struct {...@@ -3179,18 +3224,17 @@ pub const Parser = struct {
31793224
3180 const RenderState = union(enum) {3225 const RenderState = union(enum) {
3181 TopLevelDecl: &ast.Node,3226 TopLevelDecl: &ast.Node,
3182 FnProtoRParen: &ast.NodeFnProto,
3183 ParamDecl: &ast.Node,3227 ParamDecl: &ast.Node,
3184 Text: []const u8,3228 Text: []const u8,
3185 Expression: &ast.Node,3229 Expression: &ast.Node,
3186 VarDecl: &ast.NodeVarDecl,3230 VarDecl: &ast.Node.VarDecl,
3187 Statement: &ast.Node,3231 Statement: &ast.Node,
3188 FieldInitializer: &ast.NodeFieldInitializer,3232 FieldInitializer: &ast.Node.FieldInitializer,
3189 PrintIndent,3233 PrintIndent,
3190 Indent: usize,3234 Indent: usize,
3191 };3235 };
31923236
3193 pub fn renderSource(self: &Parser, stream: var, root_node: &ast.NodeRoot) !void {3237 pub fn renderSource(self: &Parser, stream: var, root_node: &ast.Node.Root) !void {
3194 var stack = self.initUtilityArrayList(RenderState);3238 var stack = self.initUtilityArrayList(RenderState);
3195 defer self.deinitUtilityArrayList(stack);3239 defer self.deinitUtilityArrayList(stack);
31963240
...@@ -3224,7 +3268,8 @@ pub const Parser = struct {...@@ -3224,7 +3268,8 @@ pub const Parser = struct {
3224 RenderState.TopLevelDecl => |decl| {3268 RenderState.TopLevelDecl => |decl| {
3225 switch (decl.id) {3269 switch (decl.id) {
3226 ast.Node.Id.FnProto => {3270 ast.Node.Id.FnProto => {
3227 const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", decl);3271 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
3272 try self.renderComments(stream, fn_proto, indent);
32283273
3229 if (fn_proto.body_node) |body_node| {3274 if (fn_proto.body_node) |body_node| {
3230 stack.append(RenderState { .Expression = body_node}) catch unreachable;3275 stack.append(RenderState { .Expression = body_node}) catch unreachable;
...@@ -3236,7 +3281,7 @@ pub const Parser = struct {...@@ -3236,7 +3281,7 @@ pub const Parser = struct {
3236 try stack.append(RenderState { .Expression = decl });3281 try stack.append(RenderState { .Expression = decl });
3237 },3282 },
3238 ast.Node.Id.Use => {3283 ast.Node.Id.Use => {
3239 const use_decl = @fieldParentPtr(ast.NodeUse, "base", decl);3284 const use_decl = @fieldParentPtr(ast.Node.Use, "base", decl);
3240 if (use_decl.visib_token) |visib_token| {3285 if (use_decl.visib_token) |visib_token| {
3241 try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));3286 try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
3242 }3287 }
...@@ -3245,18 +3290,19 @@ pub const Parser = struct {...@@ -3245,18 +3290,19 @@ pub const Parser = struct {
3245 try stack.append(RenderState { .Expression = use_decl.expr });3290 try stack.append(RenderState { .Expression = use_decl.expr });
3246 },3291 },
3247 ast.Node.Id.VarDecl => {3292 ast.Node.Id.VarDecl => {
3248 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);3293 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", decl);
3249 try stack.append(RenderState { .VarDecl = var_decl});3294 try stack.append(RenderState { .VarDecl = var_decl});
3250 },3295 },
3251 ast.Node.Id.TestDecl => {3296 ast.Node.Id.TestDecl => {
3252 const test_decl = @fieldParentPtr(ast.NodeTestDecl, "base", decl);3297 const test_decl = @fieldParentPtr(ast.Node.TestDecl, "base", decl);
3298 try self.renderComments(stream, test_decl, indent);
3253 try stream.print("test ");3299 try stream.print("test ");
3254 try stack.append(RenderState { .Expression = test_decl.body_node });3300 try stack.append(RenderState { .Expression = test_decl.body_node });
3255 try stack.append(RenderState { .Text = " " });3301 try stack.append(RenderState { .Text = " " });
3256 try stack.append(RenderState { .Expression = test_decl.name });3302 try stack.append(RenderState { .Expression = test_decl.name });
3257 },3303 },
3258 ast.Node.Id.StructField => {3304 ast.Node.Id.StructField => {
3259 const field = @fieldParentPtr(ast.NodeStructField, "base", decl);3305 const field = @fieldParentPtr(ast.Node.StructField, "base", decl);
3260 if (field.visib_token) |visib_token| {3306 if (field.visib_token) |visib_token| {
3261 try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));3307 try stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
3262 }3308 }
...@@ -3264,7 +3310,7 @@ pub const Parser = struct {...@@ -3264,7 +3310,7 @@ pub const Parser = struct {
3264 try stack.append(RenderState { .Expression = field.type_expr});3310 try stack.append(RenderState { .Expression = field.type_expr});
3265 },3311 },
3266 ast.Node.Id.UnionTag => {3312 ast.Node.Id.UnionTag => {
3267 const tag = @fieldParentPtr(ast.NodeUnionTag, "base", decl);3313 const tag = @fieldParentPtr(ast.Node.UnionTag, "base", decl);
3268 try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));3314 try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
32693315
3270 if (tag.type_expr) |type_expr| {3316 if (tag.type_expr) |type_expr| {
...@@ -3273,7 +3319,7 @@ pub const Parser = struct {...@@ -3273,7 +3319,7 @@ pub const Parser = struct {
3273 }3319 }
3274 },3320 },
3275 ast.Node.Id.EnumTag => {3321 ast.Node.Id.EnumTag => {
3276 const tag = @fieldParentPtr(ast.NodeEnumTag, "base", decl);3322 const tag = @fieldParentPtr(ast.Node.EnumTag, "base", decl);
3277 try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));3323 try stream.print("{}", self.tokenizer.getTokenSlice(tag.name_token));
32783324
3279 if (tag.value) |value| {3325 if (tag.value) |value| {
...@@ -3292,6 +3338,7 @@ pub const Parser = struct {...@@ -3292,6 +3338,7 @@ pub const Parser = struct {
3292 },3338 },
32933339
3294 RenderState.FieldInitializer => |field_init| {3340 RenderState.FieldInitializer => |field_init| {
3341 //TODO try self.renderComments(stream, field_init, indent);
3295 try stream.print(".{}", self.tokenizer.getTokenSlice(field_init.name_token));3342 try stream.print(".{}", self.tokenizer.getTokenSlice(field_init.name_token));
3296 try stream.print(" = ");3343 try stream.print(" = ");
3297 try stack.append(RenderState { .Expression = field_init.expr });3344 try stack.append(RenderState { .Expression = field_init.expr });
...@@ -3337,7 +3384,8 @@ pub const Parser = struct {...@@ -3337,7 +3384,8 @@ pub const Parser = struct {
3337 },3384 },
33383385
3339 RenderState.ParamDecl => |base| {3386 RenderState.ParamDecl => |base| {
3340 const param_decl = @fieldParentPtr(ast.NodeParamDecl, "base", base);3387 const param_decl = @fieldParentPtr(ast.Node.ParamDecl, "base", base);
3388 // TODO try self.renderComments(stream, param_decl, indent);
3341 if (param_decl.comptime_token) |comptime_token| {3389 if (param_decl.comptime_token) |comptime_token| {
3342 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));3390 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
3343 }3391 }
...@@ -3358,11 +3406,11 @@ pub const Parser = struct {...@@ -3358,11 +3406,11 @@ pub const Parser = struct {
3358 },3406 },
3359 RenderState.Expression => |base| switch (base.id) {3407 RenderState.Expression => |base| switch (base.id) {
3360 ast.Node.Id.Identifier => {3408 ast.Node.Id.Identifier => {
3361 const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base);3409 const identifier = @fieldParentPtr(ast.Node.Identifier, "base", base);
3362 try stream.print("{}", self.tokenizer.getTokenSlice(identifier.token));3410 try stream.print("{}", self.tokenizer.getTokenSlice(identifier.token));
3363 },3411 },
3364 ast.Node.Id.Block => {3412 ast.Node.Id.Block => {
3365 const block = @fieldParentPtr(ast.NodeBlock, "base", base);3413 const block = @fieldParentPtr(ast.Node.Block, "base", base);
3366 if (block.label) |label| {3414 if (block.label) |label| {
3367 try stream.print("{}: ", self.tokenizer.getTokenSlice(label));3415 try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
3368 }3416 }
...@@ -3398,17 +3446,17 @@ pub const Parser = struct {...@@ -3398,17 +3446,17 @@ pub const Parser = struct {
3398 }3446 }
3399 },3447 },
3400 ast.Node.Id.Defer => {3448 ast.Node.Id.Defer => {
3401 const defer_node = @fieldParentPtr(ast.NodeDefer, "base", base);3449 const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);
3402 try stream.print("{} ", self.tokenizer.getTokenSlice(defer_node.defer_token));3450 try stream.print("{} ", self.tokenizer.getTokenSlice(defer_node.defer_token));
3403 try stack.append(RenderState { .Expression = defer_node.expr });3451 try stack.append(RenderState { .Expression = defer_node.expr });
3404 },3452 },
3405 ast.Node.Id.Comptime => {3453 ast.Node.Id.Comptime => {
3406 const comptime_node = @fieldParentPtr(ast.NodeComptime, "base", base);3454 const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", base);
3407 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_node.comptime_token));3455 try stream.print("{} ", self.tokenizer.getTokenSlice(comptime_node.comptime_token));
3408 try stack.append(RenderState { .Expression = comptime_node.expr });3456 try stack.append(RenderState { .Expression = comptime_node.expr });
3409 },3457 },
3410 ast.Node.Id.AsyncAttribute => {3458 ast.Node.Id.AsyncAttribute => {
3411 const async_attr = @fieldParentPtr(ast.NodeAsyncAttribute, "base", base);3459 const async_attr = @fieldParentPtr(ast.Node.AsyncAttribute, "base", base);
3412 try stream.print("{}", self.tokenizer.getTokenSlice(async_attr.async_token));3460 try stream.print("{}", self.tokenizer.getTokenSlice(async_attr.async_token));
34133461
3414 if (async_attr.allocator_type) |allocator_type| {3462 if (async_attr.allocator_type) |allocator_type| {
...@@ -3418,7 +3466,7 @@ pub const Parser = struct {...@@ -3418,7 +3466,7 @@ pub const Parser = struct {
3418 }3466 }
3419 },3467 },
3420 ast.Node.Id.Suspend => {3468 ast.Node.Id.Suspend => {
3421 const suspend_node = @fieldParentPtr(ast.NodeSuspend, "base", base);3469 const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
3422 try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token));3470 try stream.print("{}", self.tokenizer.getTokenSlice(suspend_node.suspend_token));
34233471
3424 if (suspend_node.body) |body| {3472 if (suspend_node.body) |body| {
...@@ -3427,65 +3475,65 @@ pub const Parser = struct {...@@ -3427,65 +3475,65 @@ pub const Parser = struct {
3427 }3475 }
34283476
3429 if (suspend_node.payload) |payload| {3477 if (suspend_node.payload) |payload| {
3430 try stack.append(RenderState { .Expression = &payload.base });3478 try stack.append(RenderState { .Expression = payload });
3431 try stack.append(RenderState { .Text = " " });3479 try stack.append(RenderState { .Text = " " });
3432 }3480 }
3433 },3481 },
3434 ast.Node.Id.InfixOp => {3482 ast.Node.Id.InfixOp => {
3435 const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);3483 const prefix_op_node = @fieldParentPtr(ast.Node.InfixOp, "base", base);
3436 try stack.append(RenderState { .Expression = prefix_op_node.rhs });3484 try stack.append(RenderState { .Expression = prefix_op_node.rhs });
34373485
3438 if (prefix_op_node.op == ast.NodeInfixOp.InfixOp.Catch) {3486 if (prefix_op_node.op == ast.Node.InfixOp.Op.Catch) {
3439 if (prefix_op_node.op.Catch) |payload| {3487 if (prefix_op_node.op.Catch) |payload| {
3440 try stack.append(RenderState { .Text = " " });3488 try stack.append(RenderState { .Text = " " });
3441 try stack.append(RenderState { .Expression = &payload.base });3489 try stack.append(RenderState { .Expression = payload });
3442 }3490 }
3443 try stack.append(RenderState { .Text = " catch " });3491 try stack.append(RenderState { .Text = " catch " });
3444 } else {3492 } else {
3445 const text = switch (prefix_op_node.op) {3493 const text = switch (prefix_op_node.op) {
3446 ast.NodeInfixOp.InfixOp.Add => " + ",3494 ast.Node.InfixOp.Op.Add => " + ",
3447 ast.NodeInfixOp.InfixOp.AddWrap => " +% ",3495 ast.Node.InfixOp.Op.AddWrap => " +% ",
3448 ast.NodeInfixOp.InfixOp.ArrayCat => " ++ ",3496 ast.Node.InfixOp.Op.ArrayCat => " ++ ",
3449 ast.NodeInfixOp.InfixOp.ArrayMult => " ** ",3497 ast.Node.InfixOp.Op.ArrayMult => " ** ",
3450 ast.NodeInfixOp.InfixOp.Assign => " = ",3498 ast.Node.InfixOp.Op.Assign => " = ",
3451 ast.NodeInfixOp.InfixOp.AssignBitAnd => " &= ",3499 ast.Node.InfixOp.Op.AssignBitAnd => " &= ",
3452 ast.NodeInfixOp.InfixOp.AssignBitOr => " |= ",3500 ast.Node.InfixOp.Op.AssignBitOr => " |= ",
3453 ast.NodeInfixOp.InfixOp.AssignBitShiftLeft => " <<= ",3501 ast.Node.InfixOp.Op.AssignBitShiftLeft => " <<= ",
3454 ast.NodeInfixOp.InfixOp.AssignBitShiftRight => " >>= ",3502 ast.Node.InfixOp.Op.AssignBitShiftRight => " >>= ",
3455 ast.NodeInfixOp.InfixOp.AssignBitXor => " ^= ",3503 ast.Node.InfixOp.Op.AssignBitXor => " ^= ",
3456 ast.NodeInfixOp.InfixOp.AssignDiv => " /= ",3504 ast.Node.InfixOp.Op.AssignDiv => " /= ",
3457 ast.NodeInfixOp.InfixOp.AssignMinus => " -= ",3505 ast.Node.InfixOp.Op.AssignMinus => " -= ",
3458 ast.NodeInfixOp.InfixOp.AssignMinusWrap => " -%= ",3506 ast.Node.InfixOp.Op.AssignMinusWrap => " -%= ",
3459 ast.NodeInfixOp.InfixOp.AssignMod => " %= ",3507 ast.Node.InfixOp.Op.AssignMod => " %= ",
3460 ast.NodeInfixOp.InfixOp.AssignPlus => " += ",3508 ast.Node.InfixOp.Op.AssignPlus => " += ",
3461 ast.NodeInfixOp.InfixOp.AssignPlusWrap => " +%= ",3509 ast.Node.InfixOp.Op.AssignPlusWrap => " +%= ",
3462 ast.NodeInfixOp.InfixOp.AssignTimes => " *= ",3510 ast.Node.InfixOp.Op.AssignTimes => " *= ",
3463 ast.NodeInfixOp.InfixOp.AssignTimesWarp => " *%= ",3511 ast.Node.InfixOp.Op.AssignTimesWarp => " *%= ",
3464 ast.NodeInfixOp.InfixOp.BangEqual => " != ",3512 ast.Node.InfixOp.Op.BangEqual => " != ",
3465 ast.NodeInfixOp.InfixOp.BitAnd => " & ",3513 ast.Node.InfixOp.Op.BitAnd => " & ",
3466 ast.NodeInfixOp.InfixOp.BitOr => " | ",3514 ast.Node.InfixOp.Op.BitOr => " | ",
3467 ast.NodeInfixOp.InfixOp.BitShiftLeft => " << ",3515 ast.Node.InfixOp.Op.BitShiftLeft => " << ",
3468 ast.NodeInfixOp.InfixOp.BitShiftRight => " >> ",3516 ast.Node.InfixOp.Op.BitShiftRight => " >> ",
3469 ast.NodeInfixOp.InfixOp.BitXor => " ^ ",3517 ast.Node.InfixOp.Op.BitXor => " ^ ",
3470 ast.NodeInfixOp.InfixOp.BoolAnd => " and ",3518 ast.Node.InfixOp.Op.BoolAnd => " and ",
3471 ast.NodeInfixOp.InfixOp.BoolOr => " or ",3519 ast.Node.InfixOp.Op.BoolOr => " or ",
3472 ast.NodeInfixOp.InfixOp.Div => " / ",3520 ast.Node.InfixOp.Op.Div => " / ",
3473 ast.NodeInfixOp.InfixOp.EqualEqual => " == ",3521 ast.Node.InfixOp.Op.EqualEqual => " == ",
3474 ast.NodeInfixOp.InfixOp.ErrorUnion => "!",3522 ast.Node.InfixOp.Op.ErrorUnion => "!",
3475 ast.NodeInfixOp.InfixOp.GreaterOrEqual => " >= ",3523 ast.Node.InfixOp.Op.GreaterOrEqual => " >= ",
3476 ast.NodeInfixOp.InfixOp.GreaterThan => " > ",3524 ast.Node.InfixOp.Op.GreaterThan => " > ",
3477 ast.NodeInfixOp.InfixOp.LessOrEqual => " <= ",3525 ast.Node.InfixOp.Op.LessOrEqual => " <= ",
3478 ast.NodeInfixOp.InfixOp.LessThan => " < ",3526 ast.Node.InfixOp.Op.LessThan => " < ",
3479 ast.NodeInfixOp.InfixOp.MergeErrorSets => " || ",3527 ast.Node.InfixOp.Op.MergeErrorSets => " || ",
3480 ast.NodeInfixOp.InfixOp.Mod => " % ",3528 ast.Node.InfixOp.Op.Mod => " % ",
3481 ast.NodeInfixOp.InfixOp.Mult => " * ",3529 ast.Node.InfixOp.Op.Mult => " * ",
3482 ast.NodeInfixOp.InfixOp.MultWrap => " *% ",3530 ast.Node.InfixOp.Op.MultWrap => " *% ",
3483 ast.NodeInfixOp.InfixOp.Period => ".",3531 ast.Node.InfixOp.Op.Period => ".",
3484 ast.NodeInfixOp.InfixOp.Sub => " - ",3532 ast.Node.InfixOp.Op.Sub => " - ",
3485 ast.NodeInfixOp.InfixOp.SubWrap => " -% ",3533 ast.Node.InfixOp.Op.SubWrap => " -% ",
3486 ast.NodeInfixOp.InfixOp.UnwrapMaybe => " ?? ",3534 ast.Node.InfixOp.Op.UnwrapMaybe => " ?? ",
3487 ast.NodeInfixOp.InfixOp.Range => " ... ",3535 ast.Node.InfixOp.Op.Range => " ... ",
3488 ast.NodeInfixOp.InfixOp.Catch => unreachable,3536 ast.Node.InfixOp.Op.Catch => unreachable,
3489 };3537 };
34903538
3491 try stack.append(RenderState { .Text = text });3539 try stack.append(RenderState { .Text = text });
...@@ -3493,10 +3541,10 @@ pub const Parser = struct {...@@ -3493,10 +3541,10 @@ pub const Parser = struct {
3493 try stack.append(RenderState { .Expression = prefix_op_node.lhs });3541 try stack.append(RenderState { .Expression = prefix_op_node.lhs });
3494 },3542 },
3495 ast.Node.Id.PrefixOp => {3543 ast.Node.Id.PrefixOp => {
3496 const prefix_op_node = @fieldParentPtr(ast.NodePrefixOp, "base", base);3544 const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base);
3497 try stack.append(RenderState { .Expression = prefix_op_node.rhs });3545 try stack.append(RenderState { .Expression = prefix_op_node.rhs });
3498 switch (prefix_op_node.op) {3546 switch (prefix_op_node.op) {
3499 ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| {3547 ast.Node.PrefixOp.Op.AddrOf => |addr_of_info| {
3500 try stream.write("&");3548 try stream.write("&");
3501 if (addr_of_info.volatile_token != null) {3549 if (addr_of_info.volatile_token != null) {
3502 try stack.append(RenderState { .Text = "volatile "});3550 try stack.append(RenderState { .Text = "volatile "});
...@@ -3510,7 +3558,7 @@ pub const Parser = struct {...@@ -3510,7 +3558,7 @@ pub const Parser = struct {
3510 try stack.append(RenderState { .Expression = align_expr});3558 try stack.append(RenderState { .Expression = align_expr});
3511 }3559 }
3512 },3560 },
3513 ast.NodePrefixOp.PrefixOp.SliceType => |addr_of_info| {3561 ast.Node.PrefixOp.Op.SliceType => |addr_of_info| {
3514 try stream.write("[]");3562 try stream.write("[]");
3515 if (addr_of_info.volatile_token != null) {3563 if (addr_of_info.volatile_token != null) {
3516 try stack.append(RenderState { .Text = "volatile "});3564 try stack.append(RenderState { .Text = "volatile "});
...@@ -3524,29 +3572,29 @@ pub const Parser = struct {...@@ -3524,29 +3572,29 @@ pub const Parser = struct {
3524 try stack.append(RenderState { .Expression = align_expr});3572 try stack.append(RenderState { .Expression = align_expr});
3525 }3573 }
3526 },3574 },
3527 ast.NodePrefixOp.PrefixOp.ArrayType => |array_index| {3575 ast.Node.PrefixOp.Op.ArrayType => |array_index| {
3528 try stack.append(RenderState { .Text = "]"});3576 try stack.append(RenderState { .Text = "]"});
3529 try stack.append(RenderState { .Expression = array_index});3577 try stack.append(RenderState { .Expression = array_index});
3530 try stack.append(RenderState { .Text = "["});3578 try stack.append(RenderState { .Text = "["});
3531 },3579 },
3532 ast.NodePrefixOp.PrefixOp.BitNot => try stream.write("~"),3580 ast.Node.PrefixOp.Op.BitNot => try stream.write("~"),
3533 ast.NodePrefixOp.PrefixOp.BoolNot => try stream.write("!"),3581 ast.Node.PrefixOp.Op.BoolNot => try stream.write("!"),
3534 ast.NodePrefixOp.PrefixOp.Deref => try stream.write("*"),3582 ast.Node.PrefixOp.Op.Deref => try stream.write("*"),
3535 ast.NodePrefixOp.PrefixOp.Negation => try stream.write("-"),3583 ast.Node.PrefixOp.Op.Negation => try stream.write("-"),
3536 ast.NodePrefixOp.PrefixOp.NegationWrap => try stream.write("-%"),3584 ast.Node.PrefixOp.Op.NegationWrap => try stream.write("-%"),
3537 ast.NodePrefixOp.PrefixOp.Try => try stream.write("try "),3585 ast.Node.PrefixOp.Op.Try => try stream.write("try "),
3538 ast.NodePrefixOp.PrefixOp.UnwrapMaybe => try stream.write("??"),3586 ast.Node.PrefixOp.Op.UnwrapMaybe => try stream.write("??"),
3539 ast.NodePrefixOp.PrefixOp.MaybeType => try stream.write("?"),3587 ast.Node.PrefixOp.Op.MaybeType => try stream.write("?"),
3540 ast.NodePrefixOp.PrefixOp.Await => try stream.write("await "),3588 ast.Node.PrefixOp.Op.Await => try stream.write("await "),
3541 ast.NodePrefixOp.PrefixOp.Cancel => try stream.write("cancel "),3589 ast.Node.PrefixOp.Op.Cancel => try stream.write("cancel "),
3542 ast.NodePrefixOp.PrefixOp.Resume => try stream.write("resume "),3590 ast.Node.PrefixOp.Op.Resume => try stream.write("resume "),
3543 }3591 }
3544 },3592 },
3545 ast.Node.Id.SuffixOp => {3593 ast.Node.Id.SuffixOp => {
3546 const suffix_op = @fieldParentPtr(ast.NodeSuffixOp, "base", base);3594 const suffix_op = @fieldParentPtr(ast.Node.SuffixOp, "base", base);
35473595
3548 switch (suffix_op.op) {3596 switch (suffix_op.op) {
3549 ast.NodeSuffixOp.SuffixOp.Call => |call_info| {3597 ast.Node.SuffixOp.Op.Call => |call_info| {
3550 try stack.append(RenderState { .Text = ")"});3598 try stack.append(RenderState { .Text = ")"});
3551 var i = call_info.params.len;3599 var i = call_info.params.len;
3552 while (i != 0) {3600 while (i != 0) {
...@@ -3565,13 +3613,13 @@ pub const Parser = struct {...@@ -3565,13 +3613,13 @@ pub const Parser = struct {
3565 try stack.append(RenderState { .Expression = &async_attr.base });3613 try stack.append(RenderState { .Expression = &async_attr.base });
3566 }3614 }
3567 },3615 },
3568 ast.NodeSuffixOp.SuffixOp.ArrayAccess => |index_expr| {3616 ast.Node.SuffixOp.Op.ArrayAccess => |index_expr| {
3569 try stack.append(RenderState { .Text = "]"});3617 try stack.append(RenderState { .Text = "]"});
3570 try stack.append(RenderState { .Expression = index_expr});3618 try stack.append(RenderState { .Expression = index_expr});
3571 try stack.append(RenderState { .Text = "["});3619 try stack.append(RenderState { .Text = "["});
3572 try stack.append(RenderState { .Expression = suffix_op.lhs });3620 try stack.append(RenderState { .Expression = suffix_op.lhs });
3573 },3621 },
3574 ast.NodeSuffixOp.SuffixOp.Slice => |range| {3622 ast.Node.SuffixOp.Op.Slice => |range| {
3575 try stack.append(RenderState { .Text = "]"});3623 try stack.append(RenderState { .Text = "]"});
3576 if (range.end) |end| {3624 if (range.end) |end| {
3577 try stack.append(RenderState { .Expression = end});3625 try stack.append(RenderState { .Expression = end});
...@@ -3581,162 +3629,177 @@ pub const Parser = struct {...@@ -3581,162 +3629,177 @@ pub const Parser = struct {
3581 try stack.append(RenderState { .Text = "["});3629 try stack.append(RenderState { .Text = "["});
3582 try stack.append(RenderState { .Expression = suffix_op.lhs });3630 try stack.append(RenderState { .Expression = suffix_op.lhs });
3583 },3631 },
3584 ast.NodeSuffixOp.SuffixOp.StructInitializer => |field_inits| {3632 ast.Node.SuffixOp.Op.StructInitializer => |field_inits| {
3585 try stack.append(RenderState { .Text = " }"});3633 if (field_inits.len == 0) {
3634 try stack.append(RenderState { .Text = "{}" });
3635 try stack.append(RenderState { .Expression = suffix_op.lhs });
3636 continue;
3637 }
3638 try stack.append(RenderState { .Text = "}"});
3639 try stack.append(RenderState.PrintIndent);
3640 try stack.append(RenderState { .Indent = indent });
3586 var i = field_inits.len;3641 var i = field_inits.len;
3587 while (i != 0) {3642 while (i != 0) {
3588 i -= 1;3643 i -= 1;
3589 const field_init = field_inits.at(i);3644 const field_init = field_inits.at(i);
3645 try stack.append(RenderState { .Text = ",\n" });
3590 try stack.append(RenderState { .FieldInitializer = field_init });3646 try stack.append(RenderState { .FieldInitializer = field_init });
3591 try stack.append(RenderState { .Text = " " });3647 try stack.append(RenderState.PrintIndent);
3592 if (i != 0) {
3593 try stack.append(RenderState { .Text = "," });
3594 }
3595 }3648 }
3596 try stack.append(RenderState { .Text = "{"});3649 try stack.append(RenderState { .Indent = indent + indent_delta });
3650 try stack.append(RenderState { .Text = " {\n"});
3597 try stack.append(RenderState { .Expression = suffix_op.lhs });3651 try stack.append(RenderState { .Expression = suffix_op.lhs });
3598 },3652 },
3599 ast.NodeSuffixOp.SuffixOp.ArrayInitializer => |exprs| {3653 ast.Node.SuffixOp.Op.ArrayInitializer => |exprs| {
3600 try stack.append(RenderState { .Text = " }"});3654 if (exprs.len == 0) {
3655 try stack.append(RenderState { .Text = "{}" });
3656 try stack.append(RenderState { .Expression = suffix_op.lhs });
3657 continue;
3658 }
3659 try stack.append(RenderState { .Text = "}"});
3660 try stack.append(RenderState.PrintIndent);
3661 try stack.append(RenderState { .Indent = indent });
3601 var i = exprs.len;3662 var i = exprs.len;
3602 while (i != 0) {3663 while (i != 0) {
3603 i -= 1;3664 i -= 1;
3604 const expr = exprs.at(i);3665 const expr = exprs.at(i);
3666 try stack.append(RenderState { .Text = ",\n" });
3605 try stack.append(RenderState { .Expression = expr });3667 try stack.append(RenderState { .Expression = expr });
3606 try stack.append(RenderState { .Text = " " });3668 try stack.append(RenderState.PrintIndent);
3607 if (i != 0) {
3608 try stack.append(RenderState { .Text = "," });
3609 }
3610 }3669 }
3611 try stack.append(RenderState { .Text = "{"});3670 try stack.append(RenderState { .Indent = indent + indent_delta });
3671 try stack.append(RenderState { .Text = " {\n"});
3612 try stack.append(RenderState { .Expression = suffix_op.lhs });3672 try stack.append(RenderState { .Expression = suffix_op.lhs });
3613 },3673 },
3614 }3674 }
3615 },3675 },
3616 ast.Node.Id.ControlFlowExpression => {3676 ast.Node.Id.ControlFlowExpression => {
3617 const flow_expr = @fieldParentPtr(ast.NodeControlFlowExpression, "base", base);3677 const flow_expr = @fieldParentPtr(ast.Node.ControlFlowExpression, "base", base);
3678
3679 if (flow_expr.rhs) |rhs| {
3680 try stack.append(RenderState { .Expression = rhs });
3681 try stack.append(RenderState { .Text = " " });
3682 }
3683
3618 switch (flow_expr.kind) {3684 switch (flow_expr.kind) {
3619 ast.NodeControlFlowExpression.Kind.Break => |maybe_blk_token| {3685 ast.Node.ControlFlowExpression.Kind.Break => |maybe_label| {
3620 try stream.print("break");3686 try stream.print("break");
3621 if (maybe_blk_token) |blk_token| {3687 if (maybe_label) |label| {
3622 try stream.print(" :{}", self.tokenizer.getTokenSlice(blk_token));3688 try stream.print(" :");
3689 try stack.append(RenderState { .Expression = label });
3623 }3690 }
3624 },3691 },
3625 ast.NodeControlFlowExpression.Kind.Continue => |maybe_blk_token| {3692 ast.Node.ControlFlowExpression.Kind.Continue => |maybe_label| {
3626 try stream.print("continue");3693 try stream.print("continue");
3627 if (maybe_blk_token) |blk_token| {3694 if (maybe_label) |label| {
3628 try stream.print(" :{}", self.tokenizer.getTokenSlice(blk_token));3695 try stream.print(" :");
3696 try stack.append(RenderState { .Expression = label });
3629 }3697 }
3630 },3698 },
3631 ast.NodeControlFlowExpression.Kind.Return => {3699 ast.Node.ControlFlowExpression.Kind.Return => {
3632 try stream.print("return");3700 try stream.print("return");
3633 },3701 },
36343702
3635 }3703 }
3636
3637 if (flow_expr.rhs) |rhs| {
3638 try stream.print(" ");
3639 try stack.append(RenderState { .Expression = rhs });
3640 }
3641 },3704 },
3642 ast.Node.Id.Payload => {3705 ast.Node.Id.Payload => {
3643 const payload = @fieldParentPtr(ast.NodePayload, "base", base);3706 const payload = @fieldParentPtr(ast.Node.Payload, "base", base);
3644 try stack.append(RenderState { .Text = "|"});3707 try stack.append(RenderState { .Text = "|"});
3645 try stack.append(RenderState { .Expression = &payload.error_symbol.base });3708 try stack.append(RenderState { .Expression = payload.error_symbol });
3646 try stack.append(RenderState { .Text = "|"});3709 try stack.append(RenderState { .Text = "|"});
3647 },3710 },
3648 ast.Node.Id.PointerPayload => {3711 ast.Node.Id.PointerPayload => {
3649 const payload = @fieldParentPtr(ast.NodePointerPayload, "base", base);3712 const payload = @fieldParentPtr(ast.Node.PointerPayload, "base", base);
3650 try stack.append(RenderState { .Text = "|"});3713 try stack.append(RenderState { .Text = "|"});
3651 try stack.append(RenderState { .Expression = &payload.value_symbol.base });3714 try stack.append(RenderState { .Expression = payload.value_symbol });
36523715
3653 if (payload.is_ptr) {3716 if (payload.ptr_token) |ptr_token| {
3654 try stack.append(RenderState { .Text = "*"});3717 try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(ptr_token) });
3655 }3718 }
36563719
3657 try stack.append(RenderState { .Text = "|"});3720 try stack.append(RenderState { .Text = "|"});
3658 },3721 },
3659 ast.Node.Id.PointerIndexPayload => {3722 ast.Node.Id.PointerIndexPayload => {
3660 const payload = @fieldParentPtr(ast.NodePointerIndexPayload, "base", base);3723 const payload = @fieldParentPtr(ast.Node.PointerIndexPayload, "base", base);
3661 try stack.append(RenderState { .Text = "|"});3724 try stack.append(RenderState { .Text = "|"});
36623725
3663 if (payload.index_symbol) |index_symbol| {3726 if (payload.index_symbol) |index_symbol| {
3664 try stack.append(RenderState { .Expression = &index_symbol.base });3727 try stack.append(RenderState { .Expression = index_symbol });
3665 try stack.append(RenderState { .Text = ", "});3728 try stack.append(RenderState { .Text = ", "});
3666 }3729 }
36673730
3668 try stack.append(RenderState { .Expression = &payload.value_symbol.base });3731 try stack.append(RenderState { .Expression = payload.value_symbol });
36693732
3670 if (payload.is_ptr) {3733 if (payload.ptr_token) |ptr_token| {
3671 try stack.append(RenderState { .Text = "*"});3734 try stack.append(RenderState { .Text = self.tokenizer.getTokenSlice(ptr_token) });
3672 }3735 }
36733736
3674 try stack.append(RenderState { .Text = "|"});3737 try stack.append(RenderState { .Text = "|"});
3675 },3738 },
3676 ast.Node.Id.GroupedExpression => {3739 ast.Node.Id.GroupedExpression => {
3677 const grouped_expr = @fieldParentPtr(ast.NodeGroupedExpression, "base", base);3740 const grouped_expr = @fieldParentPtr(ast.Node.GroupedExpression, "base", base);
3678 try stack.append(RenderState { .Text = ")"});3741 try stack.append(RenderState { .Text = ")"});
3679 try stack.append(RenderState { .Expression = grouped_expr.expr });3742 try stack.append(RenderState { .Expression = grouped_expr.expr });
3680 try stack.append(RenderState { .Text = "("});3743 try stack.append(RenderState { .Text = "("});
3681 },3744 },
3682 ast.Node.Id.FieldInitializer => {3745 ast.Node.Id.FieldInitializer => {
3683 const field_init = @fieldParentPtr(ast.NodeFieldInitializer, "base", base);3746 const field_init = @fieldParentPtr(ast.Node.FieldInitializer, "base", base);
3684 try stream.print(".{} = ", self.tokenizer.getTokenSlice(field_init.name_token));3747 try stream.print(".{} = ", self.tokenizer.getTokenSlice(field_init.name_token));
3685 try stack.append(RenderState { .Expression = field_init.expr });3748 try stack.append(RenderState { .Expression = field_init.expr });
3686 },3749 },
3687 ast.Node.Id.IntegerLiteral => {3750 ast.Node.Id.IntegerLiteral => {
3688 const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base);3751 const integer_literal = @fieldParentPtr(ast.Node.IntegerLiteral, "base", base);
3689 try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token));3752 try stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token));
3690 },3753 },
3691 ast.Node.Id.FloatLiteral => {3754 ast.Node.Id.FloatLiteral => {
3692 const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base);3755 const float_literal = @fieldParentPtr(ast.Node.FloatLiteral, "base", base);
3693 try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token));3756 try stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token));
3694 },3757 },
3695 ast.Node.Id.StringLiteral => {3758 ast.Node.Id.StringLiteral => {
3696 const string_literal = @fieldParentPtr(ast.NodeStringLiteral, "base", base);3759 const string_literal = @fieldParentPtr(ast.Node.StringLiteral, "base", base);
3697 try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token));3760 try stream.print("{}", self.tokenizer.getTokenSlice(string_literal.token));
3698 },3761 },
3699 ast.Node.Id.CharLiteral => {3762 ast.Node.Id.CharLiteral => {
3700 const char_literal = @fieldParentPtr(ast.NodeCharLiteral, "base", base);3763 const char_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
3701 try stream.print("{}", self.tokenizer.getTokenSlice(char_literal.token));3764 try stream.print("{}", self.tokenizer.getTokenSlice(char_literal.token));
3702 },3765 },
3703 ast.Node.Id.BoolLiteral => {3766 ast.Node.Id.BoolLiteral => {
3704 const bool_literal = @fieldParentPtr(ast.NodeCharLiteral, "base", base);3767 const bool_literal = @fieldParentPtr(ast.Node.CharLiteral, "base", base);
3705 try stream.print("{}", self.tokenizer.getTokenSlice(bool_literal.token));3768 try stream.print("{}", self.tokenizer.getTokenSlice(bool_literal.token));
3706 },3769 },
3707 ast.Node.Id.NullLiteral => {3770 ast.Node.Id.NullLiteral => {
3708 const null_literal = @fieldParentPtr(ast.NodeNullLiteral, "base", base);3771 const null_literal = @fieldParentPtr(ast.Node.NullLiteral, "base", base);
3709 try stream.print("{}", self.tokenizer.getTokenSlice(null_literal.token));3772 try stream.print("{}", self.tokenizer.getTokenSlice(null_literal.token));
3710 },3773 },
3711 ast.Node.Id.ThisLiteral => {3774 ast.Node.Id.ThisLiteral => {
3712 const this_literal = @fieldParentPtr(ast.NodeThisLiteral, "base", base);3775 const this_literal = @fieldParentPtr(ast.Node.ThisLiteral, "base", base);
3713 try stream.print("{}", self.tokenizer.getTokenSlice(this_literal.token));3776 try stream.print("{}", self.tokenizer.getTokenSlice(this_literal.token));
3714 },3777 },
3715 ast.Node.Id.Unreachable => {3778 ast.Node.Id.Unreachable => {
3716 const unreachable_node = @fieldParentPtr(ast.NodeUnreachable, "base", base);3779 const unreachable_node = @fieldParentPtr(ast.Node.Unreachable, "base", base);
3717 try stream.print("{}", self.tokenizer.getTokenSlice(unreachable_node.token));3780 try stream.print("{}", self.tokenizer.getTokenSlice(unreachable_node.token));
3718 },3781 },
3719 ast.Node.Id.ErrorType => {3782 ast.Node.Id.ErrorType => {
3720 const error_type = @fieldParentPtr(ast.NodeErrorType, "base", base);3783 const error_type = @fieldParentPtr(ast.Node.ErrorType, "base", base);
3721 try stream.print("{}", self.tokenizer.getTokenSlice(error_type.token));3784 try stream.print("{}", self.tokenizer.getTokenSlice(error_type.token));
3722 },3785 },
3723 ast.Node.Id.VarType => {3786 ast.Node.Id.VarType => {
3724 const var_type = @fieldParentPtr(ast.NodeVarType, "base", base);3787 const var_type = @fieldParentPtr(ast.Node.VarType, "base", base);
3725 try stream.print("{}", self.tokenizer.getTokenSlice(var_type.token));3788 try stream.print("{}", self.tokenizer.getTokenSlice(var_type.token));
3726 },3789 },
3727 ast.Node.Id.ContainerDecl => {3790 ast.Node.Id.ContainerDecl => {
3728 const container_decl = @fieldParentPtr(ast.NodeContainerDecl, "base", base);3791 const container_decl = @fieldParentPtr(ast.Node.ContainerDecl, "base", base);
37293792
3730 switch (container_decl.layout) {3793 switch (container_decl.layout) {
3731 ast.NodeContainerDecl.Layout.Packed => try stream.print("packed "),3794 ast.Node.ContainerDecl.Layout.Packed => try stream.print("packed "),
3732 ast.NodeContainerDecl.Layout.Extern => try stream.print("extern "),3795 ast.Node.ContainerDecl.Layout.Extern => try stream.print("extern "),
3733 ast.NodeContainerDecl.Layout.Auto => { },3796 ast.Node.ContainerDecl.Layout.Auto => { },
3734 }3797 }
37353798
3736 switch (container_decl.kind) {3799 switch (container_decl.kind) {
3737 ast.NodeContainerDecl.Kind.Struct => try stream.print("struct"),3800 ast.Node.ContainerDecl.Kind.Struct => try stream.print("struct"),
3738 ast.NodeContainerDecl.Kind.Enum => try stream.print("enum"),3801 ast.Node.ContainerDecl.Kind.Enum => try stream.print("enum"),
3739 ast.NodeContainerDecl.Kind.Union => try stream.print("union"),3802 ast.Node.ContainerDecl.Kind.Union => try stream.print("union"),
3740 }3803 }
37413804
3742 try stack.append(RenderState { .Text = "}"});3805 try stack.append(RenderState { .Text = "}"});
...@@ -3749,6 +3812,14 @@ pub const Parser = struct {...@@ -3749,6 +3812,14 @@ pub const Parser = struct {
3749 while (i != 0) {3812 while (i != 0) {
3750 i -= 1;3813 i -= 1;
3751 const node = fields_and_decls[i];3814 const node = fields_and_decls[i];
3815 switch (node.id) {
3816 ast.Node.Id.StructField,
3817 ast.Node.Id.UnionTag,
3818 ast.Node.Id.EnumTag => {
3819 try stack.append(RenderState { .Text = "," });
3820 },
3821 else => { }
3822 }
3752 try stack.append(RenderState { .TopLevelDecl = node});3823 try stack.append(RenderState { .TopLevelDecl = node});
3753 try stack.append(RenderState.PrintIndent);3824 try stack.append(RenderState.PrintIndent);
3754 try stack.append(RenderState {3825 try stack.append(RenderState {
...@@ -3763,26 +3834,14 @@ pub const Parser = struct {...@@ -3763,26 +3834,14 @@ pub const Parser = struct {
3763 break :blk "\n";3834 break :blk "\n";
3764 },3835 },
3765 });3836 });
3766
3767 if (i != 0) {
3768 const prev_node = fields_and_decls[i - 1];
3769 switch (prev_node.id) {
3770 ast.Node.Id.StructField,
3771 ast.Node.Id.UnionTag,
3772 ast.Node.Id.EnumTag => {
3773 try stack.append(RenderState { .Text = "," });
3774 },
3775 else => { }
3776 }
3777 }
3778 }3837 }
3779 try stack.append(RenderState { .Indent = indent + indent_delta});3838 try stack.append(RenderState { .Indent = indent + indent_delta});
3780 try stack.append(RenderState { .Text = "{"});3839 try stack.append(RenderState { .Text = "{"});
37813840
3782 switch (container_decl.init_arg_expr) {3841 switch (container_decl.init_arg_expr) {
3783 ast.NodeContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}),3842 ast.Node.ContainerDecl.InitArg.None => try stack.append(RenderState { .Text = " "}),
3784 ast.NodeContainerDecl.InitArg.Enum => try stack.append(RenderState { .Text = "(enum) "}),3843 ast.Node.ContainerDecl.InitArg.Enum => try stack.append(RenderState { .Text = "(enum) "}),
3785 ast.NodeContainerDecl.InitArg.Type => |type_expr| {3844 ast.Node.ContainerDecl.InitArg.Type => |type_expr| {
3786 try stack.append(RenderState { .Text = ") "});3845 try stack.append(RenderState { .Text = ") "});
3787 try stack.append(RenderState { .Expression = type_expr});3846 try stack.append(RenderState { .Expression = type_expr});
3788 try stack.append(RenderState { .Text = "("});3847 try stack.append(RenderState { .Text = "("});
...@@ -3790,7 +3849,7 @@ pub const Parser = struct {...@@ -3790,7 +3849,7 @@ pub const Parser = struct {
3790 }3849 }
3791 },3850 },
3792 ast.Node.Id.ErrorSetDecl => {3851 ast.Node.Id.ErrorSetDecl => {
3793 const err_set_decl = @fieldParentPtr(ast.NodeErrorSetDecl, "base", base);3852 const err_set_decl = @fieldParentPtr(ast.Node.ErrorSetDecl, "base", base);
3794 try stream.print("error ");3853 try stream.print("error ");
37953854
3796 try stack.append(RenderState { .Text = "}"});3855 try stack.append(RenderState { .Text = "}"});
...@@ -3803,7 +3862,8 @@ pub const Parser = struct {...@@ -3803,7 +3862,8 @@ pub const Parser = struct {
3803 while (i != 0) {3862 while (i != 0) {
3804 i -= 1;3863 i -= 1;
3805 const node = decls[i];3864 const node = decls[i];
3806 try stack.append(RenderState { .Expression = &node.base});3865 try stack.append(RenderState { .Text = "," });
3866 try stack.append(RenderState { .Expression = node });
3807 try stack.append(RenderState.PrintIndent);3867 try stack.append(RenderState.PrintIndent);
3808 try stack.append(RenderState {3868 try stack.append(RenderState {
3809 .Text = blk: {3869 .Text = blk: {
...@@ -3817,16 +3877,12 @@ pub const Parser = struct {...@@ -3817,16 +3877,12 @@ pub const Parser = struct {
3817 break :blk "\n";3877 break :blk "\n";
3818 },3878 },
3819 });3879 });
3820
3821 if (i != 0) {
3822 try stack.append(RenderState { .Text = "," });
3823 }
3824 }3880 }
3825 try stack.append(RenderState { .Indent = indent + indent_delta});3881 try stack.append(RenderState { .Indent = indent + indent_delta});
3826 try stack.append(RenderState { .Text = "{"});3882 try stack.append(RenderState { .Text = "{"});
3827 },3883 },
3828 ast.Node.Id.MultilineStringLiteral => {3884 ast.Node.Id.MultilineStringLiteral => {
3829 const multiline_str_literal = @fieldParentPtr(ast.NodeMultilineStringLiteral, "base", base);3885 const multiline_str_literal = @fieldParentPtr(ast.Node.MultilineStringLiteral, "base", base);
3830 try stream.print("\n");3886 try stream.print("\n");
38313887
3832 var i : usize = 0;3888 var i : usize = 0;
...@@ -3838,11 +3894,11 @@ pub const Parser = struct {...@@ -3838,11 +3894,11 @@ pub const Parser = struct {
3838 try stream.writeByteNTimes(' ', indent + indent_delta);3894 try stream.writeByteNTimes(' ', indent + indent_delta);
3839 },3895 },
3840 ast.Node.Id.UndefinedLiteral => {3896 ast.Node.Id.UndefinedLiteral => {
3841 const undefined_literal = @fieldParentPtr(ast.NodeUndefinedLiteral, "base", base);3897 const undefined_literal = @fieldParentPtr(ast.Node.UndefinedLiteral, "base", base);
3842 try stream.print("{}", self.tokenizer.getTokenSlice(undefined_literal.token));3898 try stream.print("{}", self.tokenizer.getTokenSlice(undefined_literal.token));
3843 },3899 },
3844 ast.Node.Id.BuiltinCall => {3900 ast.Node.Id.BuiltinCall => {
3845 const builtin_call = @fieldParentPtr(ast.NodeBuiltinCall, "base", base);3901 const builtin_call = @fieldParentPtr(ast.Node.BuiltinCall, "base", base);
3846 try stream.print("{}(", self.tokenizer.getTokenSlice(builtin_call.builtin_token));3902 try stream.print("{}(", self.tokenizer.getTokenSlice(builtin_call.builtin_token));
3847 try stack.append(RenderState { .Text = ")"});3903 try stack.append(RenderState { .Text = ")"});
3848 var i = builtin_call.params.len;3904 var i = builtin_call.params.len;
...@@ -3856,20 +3912,22 @@ pub const Parser = struct {...@@ -3856,20 +3912,22 @@ pub const Parser = struct {
3856 }3912 }
3857 },3913 },
3858 ast.Node.Id.FnProto => {3914 ast.Node.Id.FnProto => {
3859 const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", base);3915 const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", base);
38603916
3861 switch (fn_proto.return_type) {3917 switch (fn_proto.return_type) {
3862 ast.NodeFnProto.ReturnType.Explicit => |node| {3918 ast.Node.FnProto.ReturnType.Explicit => |node| {
3863 try stack.append(RenderState { .Expression = node});3919 try stack.append(RenderState { .Expression = node});
3864 },3920 },
3865 ast.NodeFnProto.ReturnType.InferErrorSet => |node| {3921 ast.Node.FnProto.ReturnType.InferErrorSet => |node| {
3866 try stack.append(RenderState { .Expression = node});3922 try stack.append(RenderState { .Expression = node});
3867 try stack.append(RenderState { .Text = "!"});3923 try stack.append(RenderState { .Text = "!"});
3868 },3924 },
3869 }3925 }
38703926
3871 if (fn_proto.align_expr != null) {3927 if (fn_proto.align_expr) |align_expr| {
3872 @panic("TODO");3928 try stack.append(RenderState { .Text = ") " });
3929 try stack.append(RenderState { .Expression = align_expr});
3930 try stack.append(RenderState { .Text = "align(" });
3873 }3931 }
38743932
3875 try stack.append(RenderState { .Text = ") " });3933 try stack.append(RenderState { .Text = ") " });
...@@ -3918,7 +3976,7 @@ pub const Parser = struct {...@@ -3918,7 +3976,7 @@ pub const Parser = struct {
3918 },3976 },
3919 ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),3977 ast.Node.Id.LineComment => @panic("TODO render line comment in an expression"),
3920 ast.Node.Id.Switch => {3978 ast.Node.Id.Switch => {
3921 const switch_node = @fieldParentPtr(ast.NodeSwitch, "base", base);3979 const switch_node = @fieldParentPtr(ast.Node.Switch, "base", base);
3922 try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token));3980 try stream.print("{} (", self.tokenizer.getTokenSlice(switch_node.switch_token));
39233981
3924 try stack.append(RenderState { .Text = "}"});3982 try stack.append(RenderState { .Text = "}"});
...@@ -3931,6 +3989,7 @@ pub const Parser = struct {...@@ -3931,6 +3989,7 @@ pub const Parser = struct {
3931 while (i != 0) {3989 while (i != 0) {
3932 i -= 1;3990 i -= 1;
3933 const node = cases[i];3991 const node = cases[i];
3992 try stack.append(RenderState { .Text = ","});
3934 try stack.append(RenderState { .Expression = &node.base});3993 try stack.append(RenderState { .Expression = &node.base});
3935 try stack.append(RenderState.PrintIndent);3994 try stack.append(RenderState.PrintIndent);
3936 try stack.append(RenderState {3995 try stack.append(RenderState {
...@@ -3945,22 +4004,18 @@ pub const Parser = struct {...@@ -3945,22 +4004,18 @@ pub const Parser = struct {
3945 break :blk "\n";4004 break :blk "\n";
3946 },4005 },
3947 });4006 });
3948
3949 if (i != 0) {
3950 try stack.append(RenderState { .Text = "," });
3951 }
3952 }4007 }
3953 try stack.append(RenderState { .Indent = indent + indent_delta});4008 try stack.append(RenderState { .Indent = indent + indent_delta});
3954 try stack.append(RenderState { .Text = ") {"});4009 try stack.append(RenderState { .Text = ") {"});
3955 try stack.append(RenderState { .Expression = switch_node.expr });4010 try stack.append(RenderState { .Expression = switch_node.expr });
3956 },4011 },
3957 ast.Node.Id.SwitchCase => {4012 ast.Node.Id.SwitchCase => {
3958 const switch_case = @fieldParentPtr(ast.NodeSwitchCase, "base", base);4013 const switch_case = @fieldParentPtr(ast.Node.SwitchCase, "base", base);
39594014
3960 try stack.append(RenderState { .Expression = switch_case.expr });4015 try stack.append(RenderState { .Expression = switch_case.expr });
3961 if (switch_case.payload) |payload| {4016 if (switch_case.payload) |payload| {
3962 try stack.append(RenderState { .Text = " " });4017 try stack.append(RenderState { .Text = " " });
3963 try stack.append(RenderState { .Expression = &payload.base });4018 try stack.append(RenderState { .Expression = payload });
3964 }4019 }
3965 try stack.append(RenderState { .Text = " => "});4020 try stack.append(RenderState { .Text = " => "});
39664021
...@@ -3971,16 +4026,17 @@ pub const Parser = struct {...@@ -3971,16 +4026,17 @@ pub const Parser = struct {
3971 try stack.append(RenderState { .Expression = items[i] });4026 try stack.append(RenderState { .Expression = items[i] });
39724027
3973 if (i != 0) {4028 if (i != 0) {
3974 try stack.append(RenderState { .Text = ", " });4029 try stack.append(RenderState.PrintIndent);
4030 try stack.append(RenderState { .Text = ",\n" });
3975 }4031 }
3976 }4032 }
3977 },4033 },
3978 ast.Node.Id.SwitchElse => {4034 ast.Node.Id.SwitchElse => {
3979 const switch_else = @fieldParentPtr(ast.NodeSwitchElse, "base", base);4035 const switch_else = @fieldParentPtr(ast.Node.SwitchElse, "base", base);
3980 try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token));4036 try stream.print("{}", self.tokenizer.getTokenSlice(switch_else.token));
3981 },4037 },
3982 ast.Node.Id.Else => {4038 ast.Node.Id.Else => {
3983 const else_node = @fieldParentPtr(ast.NodeElse, "base", base);4039 const else_node = @fieldParentPtr(ast.Node.Else, "base", base);
3984 try stream.print("{}", self.tokenizer.getTokenSlice(else_node.else_token));4040 try stream.print("{}", self.tokenizer.getTokenSlice(else_node.else_token));
39854041
3986 switch (else_node.body.id) {4042 switch (else_node.body.id) {
...@@ -4001,11 +4057,11 @@ pub const Parser = struct {...@@ -4001,11 +4057,11 @@ pub const Parser = struct {
40014057
4002 if (else_node.payload) |payload| {4058 if (else_node.payload) |payload| {
4003 try stack.append(RenderState { .Text = " " });4059 try stack.append(RenderState { .Text = " " });
4004 try stack.append(RenderState { .Expression = &payload.base });4060 try stack.append(RenderState { .Expression = payload });
4005 }4061 }
4006 },4062 },
4007 ast.Node.Id.While => {4063 ast.Node.Id.While => {
4008 const while_node = @fieldParentPtr(ast.NodeWhile, "base", base);4064 const while_node = @fieldParentPtr(ast.Node.While, "base", base);
4009 if (while_node.label) |label| {4065 if (while_node.label) |label| {
4010 try stream.print("{}: ", self.tokenizer.getTokenSlice(label));4066 try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
4011 }4067 }
...@@ -4046,7 +4102,7 @@ pub const Parser = struct {...@@ -4046,7 +4102,7 @@ pub const Parser = struct {
4046 }4102 }
40474103
4048 if (while_node.payload) |payload| {4104 if (while_node.payload) |payload| {
4049 try stack.append(RenderState { .Expression = &payload.base });4105 try stack.append(RenderState { .Expression = payload });
4050 try stack.append(RenderState { .Text = " " });4106 try stack.append(RenderState { .Text = " " });
4051 }4107 }
40524108
...@@ -4055,7 +4111,7 @@ pub const Parser = struct {...@@ -4055,7 +4111,7 @@ pub const Parser = struct {
4055 try stack.append(RenderState { .Text = "(" });4111 try stack.append(RenderState { .Text = "(" });
4056 },4112 },
4057 ast.Node.Id.For => {4113 ast.Node.Id.For => {
4058 const for_node = @fieldParentPtr(ast.NodeFor, "base", base);4114 const for_node = @fieldParentPtr(ast.Node.For, "base", base);
4059 if (for_node.label) |label| {4115 if (for_node.label) |label| {
4060 try stream.print("{}: ", self.tokenizer.getTokenSlice(label));4116 try stream.print("{}: ", self.tokenizer.getTokenSlice(label));
4061 }4117 }
...@@ -4089,7 +4145,7 @@ pub const Parser = struct {...@@ -4089,7 +4145,7 @@ pub const Parser = struct {
4089 }4145 }
40904146
4091 if (for_node.payload) |payload| {4147 if (for_node.payload) |payload| {
4092 try stack.append(RenderState { .Expression = &payload.base });4148 try stack.append(RenderState { .Expression = payload });
4093 try stack.append(RenderState { .Text = " " });4149 try stack.append(RenderState { .Text = " " });
4094 }4150 }
40954151
...@@ -4098,7 +4154,7 @@ pub const Parser = struct {...@@ -4098,7 +4154,7 @@ pub const Parser = struct {
4098 try stack.append(RenderState { .Text = "(" });4154 try stack.append(RenderState { .Text = "(" });
4099 },4155 },
4100 ast.Node.Id.If => {4156 ast.Node.Id.If => {
4101 const if_node = @fieldParentPtr(ast.NodeIf, "base", base);4157 const if_node = @fieldParentPtr(ast.Node.If, "base", base);
4102 try stream.print("{} ", self.tokenizer.getTokenSlice(if_node.if_token));4158 try stream.print("{} ", self.tokenizer.getTokenSlice(if_node.if_token));
41034159
4104 switch (if_node.body.id) {4160 switch (if_node.body.id) {
...@@ -4122,7 +4178,7 @@ pub const Parser = struct {...@@ -4122,7 +4178,7 @@ pub const Parser = struct {
41224178
4123 if (@"else".payload) |payload| {4179 if (@"else".payload) |payload| {
4124 try stack.append(RenderState { .Text = " " });4180 try stack.append(RenderState { .Text = " " });
4125 try stack.append(RenderState { .Expression = &payload.base });4181 try stack.append(RenderState { .Expression = payload });
4126 }4182 }
41274183
4128 try stack.append(RenderState { .Text = " " });4184 try stack.append(RenderState { .Text = " " });
...@@ -4136,7 +4192,7 @@ pub const Parser = struct {...@@ -4136,7 +4192,7 @@ pub const Parser = struct {
4136 try stack.append(RenderState { .Text = " " });4192 try stack.append(RenderState { .Text = " " });
41374193
4138 if (if_node.payload) |payload| {4194 if (if_node.payload) |payload| {
4139 try stack.append(RenderState { .Expression = &payload.base });4195 try stack.append(RenderState { .Expression = payload });
4140 try stack.append(RenderState { .Text = " " });4196 try stack.append(RenderState { .Text = " " });
4141 }4197 }
41424198
...@@ -4145,11 +4201,11 @@ pub const Parser = struct {...@@ -4145,11 +4201,11 @@ pub const Parser = struct {
4145 try stack.append(RenderState { .Text = "(" });4201 try stack.append(RenderState { .Text = "(" });
4146 },4202 },
4147 ast.Node.Id.Asm => {4203 ast.Node.Id.Asm => {
4148 const asm_node = @fieldParentPtr(ast.NodeAsm, "base", base);4204 const asm_node = @fieldParentPtr(ast.Node.Asm, "base", base);
4149 try stream.print("{} ", self.tokenizer.getTokenSlice(asm_node.asm_token));4205 try stream.print("{} ", self.tokenizer.getTokenSlice(asm_node.asm_token));
41504206
4151 if (asm_node.is_volatile) {4207 if (asm_node.volatile_token) |volatile_token| {
4152 try stream.write("volatile ");4208 try stream.print("{} ", self.tokenizer.getTokenSlice(volatile_token));
4153 }4209 }
41544210
4155 try stack.append(RenderState { .Indent = indent });4211 try stack.append(RenderState { .Indent = indent });
...@@ -4232,25 +4288,25 @@ pub const Parser = struct {...@@ -4232,25 +4288,25 @@ pub const Parser = struct {
4232 try stack.append(RenderState { .Text = "(" });4288 try stack.append(RenderState { .Text = "(" });
4233 },4289 },
4234 ast.Node.Id.AsmInput => {4290 ast.Node.Id.AsmInput => {
4235 const asm_input = @fieldParentPtr(ast.NodeAsmInput, "base", base);4291 const asm_input = @fieldParentPtr(ast.Node.AsmInput, "base", base);
42364292
4237 try stack.append(RenderState { .Text = ")"});4293 try stack.append(RenderState { .Text = ")"});
4238 try stack.append(RenderState { .Expression = asm_input.expr});4294 try stack.append(RenderState { .Expression = asm_input.expr});
4239 try stack.append(RenderState { .Text = " ("});4295 try stack.append(RenderState { .Text = " ("});
4240 try stack.append(RenderState { .Expression = asm_input.constraint });4296 try stack.append(RenderState { .Expression = asm_input.constraint });
4241 try stack.append(RenderState { .Text = "] "});4297 try stack.append(RenderState { .Text = "] "});
4242 try stack.append(RenderState { .Expression = &asm_input.symbolic_name.base});4298 try stack.append(RenderState { .Expression = asm_input.symbolic_name });
4243 try stack.append(RenderState { .Text = "["});4299 try stack.append(RenderState { .Text = "["});
4244 },4300 },
4245 ast.Node.Id.AsmOutput => {4301 ast.Node.Id.AsmOutput => {
4246 const asm_output = @fieldParentPtr(ast.NodeAsmOutput, "base", base);4302 const asm_output = @fieldParentPtr(ast.Node.AsmOutput, "base", base);
42474303
4248 try stack.append(RenderState { .Text = ")"});4304 try stack.append(RenderState { .Text = ")"});
4249 switch (asm_output.kind) {4305 switch (asm_output.kind) {
4250 ast.NodeAsmOutput.Kind.Variable => |variable_name| {4306 ast.Node.AsmOutput.Kind.Variable => |variable_name| {
4251 try stack.append(RenderState { .Expression = &variable_name.base});4307 try stack.append(RenderState { .Expression = &variable_name.base});
4252 },4308 },
4253 ast.NodeAsmOutput.Kind.Return => |return_type| {4309 ast.Node.AsmOutput.Kind.Return => |return_type| {
4254 try stack.append(RenderState { .Expression = return_type});4310 try stack.append(RenderState { .Expression = return_type});
4255 try stack.append(RenderState { .Text = "-> "});4311 try stack.append(RenderState { .Text = "-> "});
4256 },4312 },
...@@ -4258,7 +4314,7 @@ pub const Parser = struct {...@@ -4258,7 +4314,7 @@ pub const Parser = struct {
4258 try stack.append(RenderState { .Text = " ("});4314 try stack.append(RenderState { .Text = " ("});
4259 try stack.append(RenderState { .Expression = asm_output.constraint });4315 try stack.append(RenderState { .Expression = asm_output.constraint });
4260 try stack.append(RenderState { .Text = "] "});4316 try stack.append(RenderState { .Text = "] "});
4261 try stack.append(RenderState { .Expression = &asm_output.symbolic_name.base});4317 try stack.append(RenderState { .Expression = asm_output.symbolic_name });
4262 try stack.append(RenderState { .Text = "["});4318 try stack.append(RenderState { .Text = "["});
4263 },4319 },
42644320
...@@ -4271,36 +4327,11 @@ pub const Parser = struct {...@@ -4271,36 +4327,11 @@ pub const Parser = struct {
4271 ast.Node.Id.TestDecl,4327 ast.Node.Id.TestDecl,
4272 ast.Node.Id.ParamDecl => unreachable,4328 ast.Node.Id.ParamDecl => unreachable,
4273 },4329 },
4274 RenderState.FnProtoRParen => |fn_proto| {
4275 try stream.print(")");
4276 if (fn_proto.align_expr != null) {
4277 @panic("TODO");
4278 }
4279 try stream.print(" ");
4280 if (fn_proto.body_node) |body_node| {
4281 try stack.append(RenderState { .Expression = body_node});
4282 try stack.append(RenderState { .Text = " "});
4283 }
4284 switch (fn_proto.return_type) {
4285 ast.NodeFnProto.ReturnType.Explicit => |node| {
4286 try stack.append(RenderState { .Expression = node});
4287 },
4288 ast.NodeFnProto.ReturnType.InferErrorSet => |node| {
4289 try stream.print("!");
4290 try stack.append(RenderState { .Expression = node});
4291 },
4292 }
4293 },
4294 RenderState.Statement => |base| {4330 RenderState.Statement => |base| {
4295 if (base.comment) |comment| {
4296 for (comment.lines.toSliceConst()) |line_token| {
4297 try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));
4298 try stream.writeByteNTimes(' ', indent);
4299 }
4300 }
4301 switch (base.id) {4331 switch (base.id) {
4302 ast.Node.Id.VarDecl => {4332 ast.Node.Id.VarDecl => {
4303 const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);4333 const var_decl = @fieldParentPtr(ast.Node.VarDecl, "base", base);
4334 try self.renderComments(stream, var_decl, indent);
4304 try stack.append(RenderState { .VarDecl = var_decl});4335 try stack.append(RenderState { .VarDecl = var_decl});
4305 },4336 },
4306 else => {4337 else => {
...@@ -4317,6 +4348,14 @@ pub const Parser = struct {...@@ -4317,6 +4348,14 @@ pub const Parser = struct {
4317 }4348 }
4318 }4349 }
43194350
4351 fn renderComments(self: &Parser, stream: var, node: var, indent: usize) !void {
4352 const comment = node.comments ?? return;
4353 for (comment.lines.toSliceConst()) |line_token| {
4354 try stream.print("{}\n", self.tokenizer.getTokenSlice(line_token));
4355 try stream.writeByteNTimes(' ', indent);
4356 }
4357 }
4358
4320 fn initUtilityArrayList(self: &Parser, comptime T: type) ArrayList(T) {4359 fn initUtilityArrayList(self: &Parser, comptime T: type) ArrayList(T) {
4321 const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T);4360 const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T);
4322 self.utility_bytes = self.util_allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count);4361 self.utility_bytes = self.util_allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count);
...@@ -4391,6 +4430,14 @@ fn testCanonical(source: []const u8) !void {...@@ -4391,6 +4430,14 @@ fn testCanonical(source: []const u8) !void {
4391 }4430 }
4392}4431}
43934432
4433test "zig fmt: preserve top level comments" {
4434 try testCanonical(
4435 \\// top level comment
4436 \\test "hi" {}
4437 \\
4438 );
4439}
4440
4394test "zig fmt: get stdout or fail" {4441test "zig fmt: get stdout or fail" {
4395 try testCanonical(4442 try testCanonical(
4396 \\const std = @import("std");4443 \\const std = @import("std");
...@@ -4583,10 +4630,10 @@ test "zig fmt: precedence" {...@@ -4583,10 +4630,10 @@ test "zig fmt: precedence" {
4583 \\ (a!b)();4630 \\ (a!b)();
4584 \\ !a!b;4631 \\ !a!b;
4585 \\ !(a!b);4632 \\ !(a!b);
4586 \\ !a{ };4633 \\ !a{};
4587 \\ !(a{ });4634 \\ !(a{});
4588 \\ a + b{ };4635 \\ a + b{};
4589 \\ (a + b){ };4636 \\ (a + b){};
4590 \\ a << b + c;4637 \\ a << b + c;
4591 \\ (a << b) + c;4638 \\ (a << b) + c;
4592 \\ a & b << c;4639 \\ a & b << c;
...@@ -4644,12 +4691,20 @@ test "zig fmt: var type" {...@@ -4644,12 +4691,20 @@ test "zig fmt: var type" {
4644 );4691 );
4645}4692}
46464693
4647test "zig fmt: extern function" {4694test "zig fmt: functions" {
4648 try testCanonical(4695 try testCanonical(
4649 \\extern fn puts(s: &const u8) c_int;4696 \\extern fn puts(s: &const u8) c_int;
4650 \\extern "c" fn puts(s: &const u8) c_int;4697 \\extern "c" fn puts(s: &const u8) c_int;
4651 \\export fn puts(s: &const u8) c_int;4698 \\export fn puts(s: &const u8) c_int;
4652 \\inline fn puts(s: &const u8) c_int;4699 \\inline fn puts(s: &const u8) c_int;
4700 \\pub extern fn puts(s: &const u8) c_int;
4701 \\pub extern "c" fn puts(s: &const u8) c_int;
4702 \\pub export fn puts(s: &const u8) c_int;
4703 \\pub inline fn puts(s: &const u8) c_int;
4704 \\pub extern fn puts(s: &const u8) align(2 + 2) c_int;
4705 \\pub extern "c" fn puts(s: &const u8) align(2 + 2) c_int;
4706 \\pub export fn puts(s: &const u8) align(2 + 2) c_int;
4707 \\pub inline fn puts(s: &const u8) align(2 + 2) c_int;
4653 \\4708 \\
4654 );4709 );
4655}4710}
...@@ -4715,21 +4770,21 @@ test "zig fmt: struct declaration" {...@@ -4715,21 +4770,21 @@ test "zig fmt: struct declaration" {
4715 \\ return *self;4770 \\ return *self;
4716 \\ }4771 \\ }
4717 \\4772 \\
4718 \\ f2: u84773 \\ f2: u8,
4719 \\};4774 \\};
4720 \\4775 \\
4721 \\const Ps = packed struct {4776 \\const Ps = packed struct {
4722 \\ a: u8,4777 \\ a: u8,
4723 \\ pub b: u8,4778 \\ pub b: u8,
4724 \\4779 \\
4725 \\ c: u84780 \\ c: u8,
4726 \\};4781 \\};
4727 \\4782 \\
4728 \\const Es = extern struct {4783 \\const Es = extern struct {
4729 \\ a: u8,4784 \\ a: u8,
4730 \\ pub b: u8,4785 \\ pub b: u8,
4731 \\4786 \\
4732 \\ c: u84787 \\ c: u8,
4733 \\};4788 \\};
4734 \\4789 \\
4735 );4790 );
...@@ -4739,25 +4794,25 @@ test "zig fmt: enum declaration" {...@@ -4739,25 +4794,25 @@ test "zig fmt: enum declaration" {
4739 try testCanonical(4794 try testCanonical(
4740 \\const E = enum {4795 \\const E = enum {
4741 \\ Ok,4796 \\ Ok,
4742 \\ SomethingElse = 04797 \\ SomethingElse = 0,
4743 \\};4798 \\};
4744 \\4799 \\
4745 \\const E2 = enum(u8) {4800 \\const E2 = enum(u8) {
4746 \\ Ok,4801 \\ Ok,
4747 \\ SomethingElse = 255,4802 \\ SomethingElse = 255,
4748 \\ SomethingThird4803 \\ SomethingThird,
4749 \\};4804 \\};
4750 \\4805 \\
4751 \\const Ee = extern enum {4806 \\const Ee = extern enum {
4752 \\ Ok,4807 \\ Ok,
4753 \\ SomethingElse,4808 \\ SomethingElse,
4754 \\ SomethingThird4809 \\ SomethingThird,
4755 \\};4810 \\};
4756 \\4811 \\
4757 \\const Ep = packed enum {4812 \\const Ep = packed enum {
4758 \\ Ok,4813 \\ Ok,
4759 \\ SomethingElse,4814 \\ SomethingElse,
4760 \\ SomethingThird4815 \\ SomethingThird,
4761 \\};4816 \\};
4762 \\4817 \\
4763 );4818 );
...@@ -4769,35 +4824,35 @@ test "zig fmt: union declaration" {...@@ -4769,35 +4824,35 @@ test "zig fmt: union declaration" {
4769 \\ Int: u8,4824 \\ Int: u8,
4770 \\ Float: f32,4825 \\ Float: f32,
4771 \\ None,4826 \\ None,
4772 \\ Bool: bool4827 \\ Bool: bool,
4773 \\};4828 \\};
4774 \\4829 \\
4775 \\const Ue = union(enum) {4830 \\const Ue = union(enum) {
4776 \\ Int: u8,4831 \\ Int: u8,
4777 \\ Float: f32,4832 \\ Float: f32,
4778 \\ None,4833 \\ None,
4779 \\ Bool: bool4834 \\ Bool: bool,
4780 \\};4835 \\};
4781 \\4836 \\
4782 \\const E = enum {4837 \\const E = enum {
4783 \\ Int,4838 \\ Int,
4784 \\ Float,4839 \\ Float,
4785 \\ None,4840 \\ None,
4786 \\ Bool4841 \\ Bool,
4787 \\};4842 \\};
4788 \\4843 \\
4789 \\const Ue2 = union(E) {4844 \\const Ue2 = union(E) {
4790 \\ Int: u8,4845 \\ Int: u8,
4791 \\ Float: f32,4846 \\ Float: f32,
4792 \\ None,4847 \\ None,
4793 \\ Bool: bool4848 \\ Bool: bool,
4794 \\};4849 \\};
4795 \\4850 \\
4796 \\const Eu = extern union {4851 \\const Eu = extern union {
4797 \\ Int: u8,4852 \\ Int: u8,
4798 \\ Float: f32,4853 \\ Float: f32,
4799 \\ None,4854 \\ None,
4800 \\ Bool: bool4855 \\ Bool: bool,
4801 \\};4856 \\};
4802 \\4857 \\
4803 );4858 );
...@@ -4809,7 +4864,7 @@ test "zig fmt: error set declaration" {...@@ -4809,7 +4864,7 @@ test "zig fmt: error set declaration" {
4809 \\ A,4864 \\ A,
4810 \\ B,4865 \\ B,
4811 \\4866 \\
4812 \\ C4867 \\ C,
4813 \\};4868 \\};
4814 \\4869 \\
4815 );4870 );
...@@ -4818,9 +4873,15 @@ test "zig fmt: error set declaration" {...@@ -4818,9 +4873,15 @@ test "zig fmt: error set declaration" {
4818test "zig fmt: arrays" {4873test "zig fmt: arrays" {
4819 try testCanonical(4874 try testCanonical(
4820 \\test "test array" {4875 \\test "test array" {
4821 \\ const a: [2]u8 = [2]u8{ 1, 2 };4876 \\ const a: [2]u8 = [2]u8 {
4822 \\ const a: [2]u8 = []u8{ 1, 2 };4877 \\ 1,
4823 \\ const a: [0]u8 = []u8{ };4878 \\ 2,
4879 \\ };
4880 \\ const a: [2]u8 = []u8 {
4881 \\ 1,
4882 \\ 2,
4883 \\ };
4884 \\ const a: [0]u8 = []u8{};
4824 \\}4885 \\}
4825 \\4886 \\
4826 );4887 );
...@@ -4828,10 +4889,18 @@ test "zig fmt: arrays" {...@@ -4828,10 +4889,18 @@ test "zig fmt: arrays" {
48284889
4829test "zig fmt: container initializers" {4890test "zig fmt: container initializers" {
4830 try testCanonical(4891 try testCanonical(
4831 \\const a1 = []u8{ };4892 \\const a1 = []u8{};
4832 \\const a2 = []u8{ 1, 2, 3, 4 };4893 \\const a2 = []u8 {
4833 \\const s1 = S{ };4894 \\ 1,
4834 \\const s2 = S{ .a = 1, .b = 2 };4895 \\ 2,
4896 \\ 3,
4897 \\ 4,
4898 \\};
4899 \\const s1 = S{};
4900 \\const s2 = S {
4901 \\ .a = 1,
4902 \\ .b = 2,
4903 \\};
4835 \\4904 \\
4836 );4905 );
4837}4906}
...@@ -4875,31 +4944,34 @@ test "zig fmt: switch" {...@@ -4875,31 +4944,34 @@ test "zig fmt: switch" {
4875 \\ switch (0) {4944 \\ switch (0) {
4876 \\ 0 => {},4945 \\ 0 => {},
4877 \\ 1 => unreachable,4946 \\ 1 => unreachable,
4878 \\ 2, 3 => {},4947 \\ 2,
4948 \\ 3 => {},
4879 \\ 4 ... 7 => {},4949 \\ 4 ... 7 => {},
4880 \\ 1 + 4 * 3 + 22 => {},4950 \\ 1 + 4 * 3 + 22 => {},
4881 \\ else => {4951 \\ else => {
4882 \\ const a = 1;4952 \\ const a = 1;
4883 \\ const b = a;4953 \\ const b = a;
4884 \\ }4954 \\ },
4885 \\ }4955 \\ }
4886 \\4956 \\
4887 \\ const res = switch (0) {4957 \\ const res = switch (0) {
4888 \\ 0 => 0,4958 \\ 0 => 0,
4889 \\ 1 => 2,4959 \\ 1 => 2,
4890 \\ 1 => a = 4,4960 \\ 1 => a = 4,
4891 \\ else => 44961 \\ else => 4,
4892 \\ };4962 \\ };
4893 \\4963 \\
4894 \\ const Union = union(enum) {4964 \\ const Union = union(enum) {
4895 \\ Int: i64,4965 \\ Int: i64,
4896 \\ Float: f644966 \\ Float: f64,
4897 \\ };4967 \\ };
4898 \\4968 \\
4899 \\ const u = Union{ .Int = 0 };4969 \\ const u = Union {
4970 \\ .Int = 0,
4971 \\ };
4900 \\ switch (u) {4972 \\ switch (u) {
4901 \\ Union.Int => |int| {},4973 \\ Union.Int => |int| {},
4902 \\ Union.Float => |*float| unreachable4974 \\ Union.Float => |*float| unreachable,
4903 \\ }4975 \\ }
4904 \\}4976 \\}
4905 \\4977 \\
...@@ -4975,7 +5047,11 @@ test "zig fmt: while" {...@@ -4975,7 +5047,11 @@ test "zig fmt: while" {
4975test "zig fmt: for" {5047test "zig fmt: for" {
4976 try testCanonical(5048 try testCanonical(
4977 \\test "for" {5049 \\test "for" {
4978 \\ const a = []u8{ 1, 2, 3 };5050 \\ const a = []u8 {
5051 \\ 1,
5052 \\ 2,
5053 \\ 3,
5054 \\ };
4979 \\ for (a) |v| {5055 \\ for (a) |v| {
4980 \\ continue;5056 \\ continue;
4981 \\ }5057 \\ }
...@@ -5205,3 +5281,12 @@ test "zig fmt: error return" {...@@ -5205,3 +5281,12 @@ test "zig fmt: error return" {
5205 \\5281 \\
5206 );5282 );
5207}5283}
5284
5285test "zig fmt: struct literals with fields on each line" {
5286 try testCanonical(
5287 \\var self = BufSet {
5288 \\ .hash_map = BufSetHashMap.init(a),
5289 \\};
5290 \\
5291 );
5292}
test/behavior.zig+1
...@@ -12,6 +12,7 @@ comptime {...@@ -12,6 +12,7 @@ comptime {
12 _ = @import("cases/bugs/655.zig");12 _ = @import("cases/bugs/655.zig");
13 _ = @import("cases/bugs/656.zig");13 _ = @import("cases/bugs/656.zig");
14 _ = @import("cases/bugs/828.zig");14 _ = @import("cases/bugs/828.zig");
15 _ = @import("cases/bugs/920.zig");
15 _ = @import("cases/cast.zig");16 _ = @import("cases/cast.zig");
16 _ = @import("cases/const_slice_child.zig");17 _ = @import("cases/const_slice_child.zig");
17 _ = @import("cases/coroutines.zig");18 _ = @import("cases/coroutines.zig");
test/cases/atomics.zig+47-3
...@@ -1,12 +1,24 @@...@@ -1,12 +1,24 @@
1const assert = @import("std").debug.assert;1const std = @import("std");
2const assert = std.debug.assert;
2const builtin = @import("builtin");3const builtin = @import("builtin");
3const AtomicRmwOp = builtin.AtomicRmwOp;4const AtomicRmwOp = builtin.AtomicRmwOp;
4const AtomicOrder = builtin.AtomicOrder;5const AtomicOrder = builtin.AtomicOrder;
56
6test "cmpxchg" {7test "cmpxchg" {
7 var x: i32 = 1234;8 var x: i32 = 1234;
8 while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {}9 if (@cmpxchgWeak(i32, &x, 99, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
10 assert(x1 == 1234);
11 } else {
12 @panic("cmpxchg should have failed");
13 }
14
15 while (@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
16 assert(x1 == 1234);
17 }
9 assert(x == 5678);18 assert(x == 5678);
19
20 assert(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null);
21 assert(x == 42);
10}22}
1123
12test "fence" {24test "fence" {
...@@ -15,13 +27,45 @@ test "fence" {...@@ -15,13 +27,45 @@ test "fence" {
15 x = 5678;27 x = 5678;
16}28}
1729
18test "atomicrmw" {30test "atomicrmw and atomicload" {
19 var data: u8 = 200;31 var data: u8 = 200;
20 testAtomicRmw(&data);32 testAtomicRmw(&data);
21 assert(data == 42);33 assert(data == 42);
34 testAtomicLoad(&data);
22}35}
2336
24fn testAtomicRmw(ptr: &u8) void {37fn testAtomicRmw(ptr: &u8) void {
25 const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst);38 const prev_value = @atomicRmw(u8, ptr, AtomicRmwOp.Xchg, 42, AtomicOrder.SeqCst);
26 assert(prev_value == 200);39 assert(prev_value == 200);
40 comptime {
41 var x: i32 = 1234;
42 const y: i32 = 12345;
43 assert(@atomicLoad(i32, &x, AtomicOrder.SeqCst) == 1234);
44 assert(@atomicLoad(i32, &y, AtomicOrder.SeqCst) == 12345);
45 }
46}
47
48fn testAtomicLoad(ptr: &u8) void {
49 const x = @atomicLoad(u8, ptr, AtomicOrder.SeqCst);
50 assert(x == 42);
51}
52
53test "cmpxchg with ptr" {
54 var data1: i32 = 1234;
55 var data2: i32 = 5678;
56 var data3: i32 = 9101;
57 var x: &i32 = &data1;
58 if (@cmpxchgWeak(&i32, &x, &data2, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
59 assert(x1 == &data1);
60 } else {
61 @panic("cmpxchg should have failed");
62 }
63
64 while (@cmpxchgWeak(&i32, &x, &data1, &data3, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| {
65 assert(x1 == &data1);
66 }
67 assert(x == &data3);
68
69 assert(@cmpxchgStrong(&i32, &x, &data3, &data2, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null);
70 assert(x == &data2);
27}71}
test/cases/bugs/920.zig created+60
...@@ -0,0 +1,60 @@
1const std = @import("std");
2const math = std.math;
3const Random = std.rand.Random;
4
5const ZigTable = struct {
6 r: f64,
7 x: [257]f64,
8 f: [257]f64,
9
10 pdf: fn(f64) f64,
11 is_symmetric: bool,
12 zero_case: fn(&Random, f64) f64,
13};
14
15fn ZigTableGen(comptime is_symmetric: bool, comptime r: f64, comptime v: f64, comptime f: fn(f64) f64,
16 comptime f_inv: fn(f64) f64, comptime zero_case: fn(&Random, f64) f64) ZigTable {
17 var tables: ZigTable = undefined;
18
19 tables.is_symmetric = is_symmetric;
20 tables.r = r;
21 tables.pdf = f;
22 tables.zero_case = zero_case;
23
24 tables.x[0] = v / f(r);
25 tables.x[1] = r;
26
27 for (tables.x[2..256]) |*entry, i| {
28 const last = tables.x[2 + i - 1];
29 *entry = f_inv(v / last + f(last));
30 }
31 tables.x[256] = 0;
32
33 for (tables.f[0..]) |*entry, i| {
34 *entry = f(tables.x[i]);
35 }
36
37 return tables;
38}
39
40const norm_r = 3.6541528853610088;
41const norm_v = 0.00492867323399;
42
43fn norm_f(x: f64) f64 { return math.exp(-x * x / 2.0); }
44fn norm_f_inv(y: f64) f64 { return math.sqrt(-2.0 * math.ln(y)); }
45fn norm_zero_case(random: &Random, u: f64) f64 { return 0.0; }
46
47const NormalDist = blk: {
48 @setEvalBranchQuota(30000);
49 break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case);
50};
51
52test "bug 920 fixed" {
53 const NormalDist1 = blk: {
54 break :blk ZigTableGen(true, norm_r, norm_v, norm_f, norm_f_inv, norm_zero_case);
55 };
56
57 for (NormalDist1.f) |_, i| {
58 std.debug.assert(NormalDist1.f[i] == NormalDist.f[i]);
59 }
60}
test/cases/coroutines.zig+18
...@@ -224,3 +224,21 @@ async fn printTrace(p: promise->error!void) void {...@@ -224,3 +224,21 @@ async fn printTrace(p: promise->error!void) void {
224 }224 }
225 };225 };
226}226}
227
228test "break from suspend" {
229 var buf: [500]u8 = undefined;
230 var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
231 var my_result: i32 = 1;
232 const p = try async<a> testBreakFromSuspend(&my_result);
233 cancel p;
234 std.debug.assert(my_result == 2);
235}
236
237async fn testBreakFromSuspend(my_result: &i32) void {
238 s: suspend |p| {
239 break :s;
240 }
241 *my_result += 1;
242 suspend;
243 *my_result += 1;
244}
test/cases/defer.zig+11
...@@ -41,3 +41,14 @@ fn testBreakContInDefer(x: usize) void {...@@ -41,3 +41,14 @@ fn testBreakContInDefer(x: usize) void {
41 assert(i == 5);41 assert(i == 5);
42 }42 }
43}43}
44
45test "defer and labeled break" {
46 var i = usize(0);
47
48 blk: {
49 defer i += 1;
50 break :blk;
51 }
52
53 assert(i == 1);
54}
test/cases/eval.zig+16
...@@ -513,3 +513,19 @@ test "array concat of slices gives slice" {...@@ -513,3 +513,19 @@ test "array concat of slices gives slice" {
513 assert(std.mem.eql(u8, c, "aoeuasdf"));513 assert(std.mem.eql(u8, c, "aoeuasdf"));
514 }514 }
515}515}
516
517test "comptime shlWithOverflow" {
518 const ct_shifted: u64 = comptime amt: {
519 var amt = u64(0);
520 _ = @shlWithOverflow(u64, ~u64(0), 16, &amt);
521 break :amt amt;
522 };
523
524 const rt_shifted: u64 = amt: {
525 var amt = u64(0);
526 _ = @shlWithOverflow(u64, ~u64(0), 16, &amt);
527 break :amt amt;
528 };
529
530 assert(ct_shifted == rt_shifted);
531}
test/cases/fn.zig+7
...@@ -104,3 +104,10 @@ test "number literal as an argument" {...@@ -104,3 +104,10 @@ test "number literal as an argument" {
104fn numberLiteralArg(a: var) void {104fn numberLiteralArg(a: var) void {
105 assert(a == 3);105 assert(a == 3);
106}106}
107
108test "assign inline fn to const variable" {
109 const a = inlineFn;
110 a();
111}
112
113inline fn inlineFn() void { }
test/cases/math.zig+16
...@@ -402,3 +402,19 @@ test "comptime float rem int" {...@@ -402,3 +402,19 @@ test "comptime float rem int" {
402 assert(x == 1.0);402 assert(x == 1.0);
403 }403 }
404}404}
405
406test "@sqrt" {
407 testSqrt(f64, 12.0);
408 comptime testSqrt(f64, 12.0);
409 testSqrt(f32, 13.0);
410 comptime testSqrt(f32, 13.0);
411
412 const x = 14.0;
413 const y = x * x;
414 const z = @sqrt(@typeOf(y), y);
415 comptime assert(z == x);
416}
417
418fn testSqrt(comptime T: type, x: T) void {
419 assert(@sqrt(T, x * x) == x);
420}
test/cases/reflection.zig+24
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1const assert = @import("std").debug.assert;1const assert = @import("std").debug.assert;
2const mem = @import("std").mem;2const mem = @import("std").mem;
3const reflection = this;
34
4test "reflection: array, pointer, nullable, error union type child" {5test "reflection: array, pointer, nullable, error union type child" {
5 comptime {6 comptime {
...@@ -56,7 +57,30 @@ test "reflection: enum member types and names" {...@@ -56,7 +57,30 @@ test "reflection: enum member types and names" {
5657
57}58}
5859
60test "reflection: @field" {
61 var f = Foo {
62 .one = 42,
63 .two = true,
64 .three = void{},
65 };
66
67 assert(f.one == f.one);
68 assert(@field(f, "o" ++ "ne") == f.one);
69 assert(@field(f, "t" ++ "wo") == f.two);
70 assert(@field(f, "th" ++ "ree") == f.three);
71 assert(@field(Foo, "const" ++ "ant") == Foo.constant);
72 assert(@field(Bar, "O" ++ "ne") == Bar.One);
73 assert(@field(Bar, "T" ++ "wo") == Bar.Two);
74 assert(@field(Bar, "Th" ++ "ree") == Bar.Three);
75 assert(@field(Bar, "F" ++ "our") == Bar.Four);
76 assert(@field(reflection, "dum" ++ "my")(true, 1, 2) == dummy(true, 1, 2));
77 @field(f, "o" ++ "ne") = 4;
78 assert(f.one == 4);
79}
80
59const Foo = struct {81const Foo = struct {
82 const constant = 52;
83
60 one: i32,84 one: i32,
61 two: bool,85 two: bool,
62 three: void,86 three: void,
test/compile_errors.zig+57-8
...@@ -1,6 +1,55 @@...@@ -1,6 +1,55 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: &tests.CompileErrorContext) void {3pub fn addCases(cases: &tests.CompileErrorContext) void {
4 cases.add("invalid deref on switch target",
5 \\comptime {
6 \\ var tile = Tile.Empty;
7 \\ switch (*tile) {
8 \\ Tile.Empty => {},
9 \\ Tile.Filled => {},
10 \\ }
11 \\}
12 \\const Tile = enum {
13 \\ Empty,
14 \\ Filled,
15 \\};
16 ,
17 ".tmp_source.zig:3:13: error: invalid deref on switch target");
18
19 cases.add("invalid field access in comptime",
20 \\comptime { var x = doesnt_exist.whatever; }
21 ,
22 ".tmp_source.zig:1:20: error: use of undeclared identifier 'doesnt_exist'");
23
24 cases.add("suspend inside suspend block",
25 \\const std = @import("std");
26 \\
27 \\export fn entry() void {
28 \\ var buf: [500]u8 = undefined;
29 \\ var a = &std.heap.FixedBufferAllocator.init(buf[0..]).allocator;
30 \\ const p = (async<a> foo()) catch unreachable;
31 \\ cancel p;
32 \\}
33 \\
34 \\async fn foo() void {
35 \\ suspend |p| {
36 \\ suspend |p1| {
37 \\ }
38 \\ }
39 \\}
40 ,
41 ".tmp_source.zig:12:9: error: cannot suspend inside suspend block",
42 ".tmp_source.zig:11:5: note: other suspend block here");
43
44 cases.add("assign inline fn to non-comptime var",
45 \\export fn entry() void {
46 \\ var a = b;
47 \\}
48 \\inline fn b() void { }
49 ,
50 ".tmp_source.zig:2:5: error: functions marked inline must be stored in const or comptime var",
51 ".tmp_source.zig:4:8: note: declared here");
52
4 cases.add("wrong type passed to @panic",53 cases.add("wrong type passed to @panic",
5 \\export fn entry() void {54 \\export fn entry() void {
6 \\ var e = error.Foo;55 \\ var e = error.Foo;
...@@ -1385,17 +1434,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -1385,17 +1434,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
1385 \\const AtomicOrder = @import("builtin").AtomicOrder;1434 \\const AtomicOrder = @import("builtin").AtomicOrder;
1386 \\export fn f() void {1435 \\export fn f() void {
1387 \\ var x: i32 = 1234;1436 \\ var x: i32 = 1234;
1388 \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {}1437 \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {}
1389 \\}1438 \\}
1390 , ".tmp_source.zig:4:72: error: failure atomic ordering must be no stricter than success");1439 , ".tmp_source.zig:4:81: error: failure atomic ordering must be no stricter than success");
13911440
1392 cases.add("atomic orderings of cmpxchg - success Monotonic or stricter",1441 cases.add("atomic orderings of cmpxchg - success Monotonic or stricter",
1393 \\const AtomicOrder = @import("builtin").AtomicOrder;1442 \\const AtomicOrder = @import("builtin").AtomicOrder;
1394 \\export fn f() void {1443 \\export fn f() void {
1395 \\ var x: i32 = 1234;1444 \\ var x: i32 = 1234;
1396 \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {}1445 \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {}
1397 \\}1446 \\}
1398 , ".tmp_source.zig:4:49: error: success atomic ordering must be Monotonic or stricter");1447 , ".tmp_source.zig:4:58: error: success atomic ordering must be Monotonic or stricter");
13991448
1400 cases.add("negation overflow in function evaluation",1449 cases.add("negation overflow in function evaluation",
1401 \\const y = neg(-128);1450 \\const y = neg(-128);
...@@ -2451,11 +2500,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -2451,11 +2500,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
2451 \\const AtomicOrder = @import("builtin").AtomicOrder;2500 \\const AtomicOrder = @import("builtin").AtomicOrder;
2452 \\export fn entry() bool {2501 \\export fn entry() bool {
2453 \\ var x: i32 align(1) = 1234;2502 \\ var x: i32 align(1) = 1234;
2454 \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {}2503 \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {}
2455 \\ return x == 5678;2504 \\ return x == 5678;
2456 \\}2505 \\}
2457 ,2506 ,
2458 ".tmp_source.zig:4:23: error: expected pointer alignment of at least 4, found 1");2507 ".tmp_source.zig:4:32: error: expected type '&i32', found '&align(1) i32'");
24592508
2460 cases.add("wrong size to an array literal",2509 cases.add("wrong size to an array literal",
2461 \\comptime {2510 \\comptime {
...@@ -2525,10 +2574,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {...@@ -2525,10 +2574,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
2525 cases.add("wrong types given to atomic order args in cmpxchg",2574 cases.add("wrong types given to atomic order args in cmpxchg",
2526 \\export fn entry() void {2575 \\export fn entry() void {
2527 \\ var x: i32 = 1234;2576 \\ var x: i32 = 1234;
2528 \\ while (!@cmpxchg(&x, 1234, 5678, u32(1234), u32(1234))) {}2577 \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, u32(1234), u32(1234))) {}
2529 \\}2578 \\}
2530 ,2579 ,
2531 ".tmp_source.zig:3:41: error: expected type 'AtomicOrder', found 'u32'");2580 ".tmp_source.zig:3:50: error: expected type 'AtomicOrder', found 'u32'");
25322581
2533 cases.add("wrong types given to @export",2582 cases.add("wrong types given to @export",
2534 \\extern fn entry() void { }2583 \\extern fn entry() void { }
test/tests.zig+1
...@@ -583,6 +583,7 @@ pub const CompileErrorContext = struct {...@@ -583,6 +583,7 @@ pub const CompileErrorContext = struct {
583 Mode.Debug => {},583 Mode.Debug => {},
584 Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,584 Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,
585 Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable,585 Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable,
586 Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable,
586 }587 }
587588
588 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);589 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);