authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-28 19:42:08-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-28 19:42:43-07:00
log281a7baaeac6b6b3c8c78124f5e484f7ee101cf0
treea8ca0725a42e8940197064a4e6485e2523b9926b
parent8f469c11275e60f5f1a8ae08fc7596ba366eda16
parent175adc0bd738c2e3a55bb71c6a53dcc920c203ba

Merge remote-tracking branch 'origin/master' into zir-memory-layout

Wanted to make sure those new test cases still pass. Also grab that CI fix so we can get those green check marks.

32 files changed, 1087 insertions(+), 502 deletions(-)

CMakeLists.txt+15-7
...@@ -89,6 +89,7 @@ set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries...@@ -89,6 +89,7 @@ set(ZIG_TARGET_MCPU "baseline" CACHE STRING "-mcpu parameter to output binaries
89set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary")89set(ZIG_EXECUTABLE "" CACHE STRING "(when cross compiling) path to already-built zig binary")
90set(ZIG_SINGLE_THREADED off CACHE BOOL "limit the zig compiler to use only 1 thread")90set(ZIG_SINGLE_THREADED off CACHE BOOL "limit the zig compiler to use only 1 thread")
91set(ZIG_OMIT_STAGE2 off CACHE BOOL "omit the stage2 backend from stage1")91set(ZIG_OMIT_STAGE2 off CACHE BOOL "omit the stage2 backend from stage1")
92set(ZIG_ENABLE_LOGGING off CACHE BOOL "enable logging")
9293
93if("${ZIG_TARGET_TRIPLE}" STREQUAL "native")94if("${ZIG_TARGET_TRIPLE}" STREQUAL "native")
94 set(ZIG_USE_LLVM_CONFIG ON CACHE BOOL "use llvm-config to find LLVM libraries")95 set(ZIG_USE_LLVM_CONFIG ON CACHE BOOL "use llvm-config to find LLVM libraries")
...@@ -607,6 +608,12 @@ else()...@@ -607,6 +608,12 @@ else()
607 set(ZIG_OMIT_STAGE2_BOOL "false")608 set(ZIG_OMIT_STAGE2_BOOL "false")
608endif()609endif()
609610
611if(ZIG_ENABLE_LOGGING)
612 set(ZIG_ENABLE_LOGGING_BOOL "true")
613else()
614 set(ZIG_ENABLE_LOGGING_BOOL "false")
615endif()
616
610configure_file (617configure_file (
611 "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in"618 "${CMAKE_SOURCE_DIR}/src/stage1/config.h.in"
612 "${ZIG_CONFIG_H_OUT}"619 "${ZIG_CONFIG_H_OUT}"
...@@ -735,12 +742,14 @@ if(MSVC OR MINGW)...@@ -735,12 +742,14 @@ if(MSVC OR MINGW)
735 target_link_libraries(zigstage1 LINK_PUBLIC version)742 target_link_libraries(zigstage1 LINK_PUBLIC version)
736endif()743endif()
737744
738add_executable(zig0 ${ZIG0_SOURCES})745if("${ZIG_EXECUTABLE}" STREQUAL "")
739set_target_properties(zig0 PROPERTIES746 add_executable(zig0 ${ZIG0_SOURCES})
740 COMPILE_FLAGS ${EXE_CFLAGS}747 set_target_properties(zig0 PROPERTIES
741 LINK_FLAGS ${EXE_LDFLAGS}748 COMPILE_FLAGS ${EXE_CFLAGS}
742)749 LINK_FLAGS ${EXE_LDFLAGS}
743target_link_libraries(zig0 zigstage1)750 )
751 target_link_libraries(zig0 zigstage1)
752endif()
744753
745if(MSVC)754if(MSVC)
746 set(ZIG1_OBJECT "${CMAKE_BINARY_DIR}/zig1.obj")755 set(ZIG1_OBJECT "${CMAKE_BINARY_DIR}/zig1.obj")
...@@ -789,7 +798,6 @@ if("${ZIG_EXECUTABLE}" STREQUAL "")...@@ -789,7 +798,6 @@ if("${ZIG_EXECUTABLE}" STREQUAL "")
789else()798else()
790 add_custom_command(799 add_custom_command(
791 OUTPUT "${ZIG1_OBJECT}"800 OUTPUT "${ZIG1_OBJECT}"
792 BYPRODUCTS "${ZIG1_OBJECT}"
793 COMMAND "${ZIG_EXECUTABLE}" "build-obj" ${BUILD_ZIG1_ARGS}801 COMMAND "${ZIG_EXECUTABLE}" "build-obj" ${BUILD_ZIG1_ARGS}
794 DEPENDS ${ZIG_STAGE2_SOURCES}802 DEPENDS ${ZIG_STAGE2_SOURCES}
795 COMMENT STATUS "Building self-hosted component ${ZIG1_OBJECT}"803 COMMENT STATUS "Building self-hosted component ${ZIG1_OBJECT}"
ci/azure/macos_arm64_script created+132
...@@ -0,0 +1,132 @@
1#!/bin/sh
2
3set -x
4set -e
5
6brew install s3cmd ninja gnu-tar
7
8ZIGDIR="$(pwd)"
9ARCH="aarch64"
10# {product}-{os}{sdk_version}-{arch}-{llvm_version}-{cmake_build_type}
11CACHE_HOST_BASENAME="llvm-macos10.15-x86_64-11.0.1-release"
12CACHE_ARM64_BASENAME="llvm-macos11.0-arm64-11.0.1-release"
13PREFIX_HOST="$HOME/$CACHE_HOST_BASENAME"
14PREFIX_ARM64="$HOME/$CACHE_ARM64_BASENAME"
15JOBS="-j2"
16
17rm -rf $PREFIX
18cd $HOME
19wget -nv "https://ziglang.org/deps/$CACHE_HOST_BASENAME.tar.xz"
20wget -nv "https://ziglang.org/deps/$CACHE_ARM64_BASENAME.tar.xz"
21
22gtar xf "$CACHE_HOST_BASENAME.tar.xz"
23gtar xf "$CACHE_ARM64_BASENAME.tar.xz"
24
25cd $ZIGDIR
26
27# Make the `zig version` number consistent.
28# This will affect the cmake command below.
29git config core.abbrev 9
30git fetch --unshallow || true
31git fetch --tags
32
33# Select xcode: latest version found on vmImage macOS-10.15 .
34DEVELOPER_DIR=/Applications/Xcode_12.4.app
35
36export ZIG_LOCAL_CACHE_DIR="$ZIGDIR/zig-cache"
37export ZIG_GLOBAL_CACHE_DIR="$ZIGDIR/zig-cache"
38
39# Build zig for host and use `Debug` type to make builds a little faster.
40
41cd $ZIGDIR
42mkdir build.host
43cd build.host
44cmake -G "Ninja" .. \
45 -DCMAKE_INSTALL_PREFIX="$(pwd)/release" \
46 -DCMAKE_PREFIX_PATH="$PREFIX_HOST" \
47 -DCMAKE_BUILD_TYPE="Debug" \
48 -DZIG_STATIC="OFF"
49
50# Build but do not install.
51ninja $JOBS
52
53ZIG_EXE="$ZIGDIR/build.host/zig"
54
55# Build zig for arm64 target.
56# - use `Release` type for published tarballs
57# - ad-hoc codesign with linker
58# - note: apple quarantine of downloads (eg. via safari) still apply
59
60cd $ZIGDIR
61mkdir build.arm64
62cd build.arm64
63cmake -G "Ninja" .. \
64 -DCMAKE_INSTALL_PREFIX="$(pwd)/release" \
65 -DCMAKE_PREFIX_PATH="$PREFIX_ARM64" \
66 -DCMAKE_BUILD_TYPE="Release" \
67 -DCMAKE_CROSSCOMPILING="True" \
68 -DCMAKE_SYSTEM_NAME="Darwin" \
69 -DCMAKE_C_FLAGS="-arch arm64" \
70 -DCMAKE_CXX_FLAGS="-arch arm64" \
71 -DCMAKE_EXE_LINKER_FLAGS="-lz -Xlinker -adhoc_codesign" \
72 -DZIG_USE_LLVM_CONFIG="OFF" \
73 -DZIG_EXECUTABLE="$ZIG_EXE" \
74 -DZIG_TARGET_TRIPLE="${ARCH}-macos" \
75 -DZIG_STATIC="OFF"
76
77ninja $JOBS install
78
79# Disable test because binary is foreign arch.
80#release/bin/zig build test
81
82if [ "${BUILD_REASON}" != "PullRequest" ]; then
83 mv ../LICENSE release/
84
85 # We do not run test suite but still need langref.
86 mkdir -p release/docs
87 $ZIG_EXE run ../doc/docgen.zig -- $ZIG_EXE ../doc/langref.html.in release/docs/langref.html
88
89 # Produce the experimental std lib documentation.
90 mkdir -p release/docs/std
91 $ZIG_EXE test ../lib/std/std.zig \
92 --override-lib-dir ../lib \
93 -femit-docs=release/docs/std \
94 -fno-emit-bin
95
96 # Remove the unnecessary bin dir in $prefix/bin/zig
97 mv release/bin/zig release/
98 rmdir release/bin
99
100 # Remove the unnecessary zig dir in $prefix/lib/zig/std/std.zig
101 mv release/lib/zig release/lib2
102 rmdir release/lib
103 mv release/lib2 release/lib
104
105 VERSION=$($ZIG_EXE version)
106 DIRNAME="zig-macos-$ARCH-$VERSION"
107 TARBALL="$DIRNAME.tar.xz"
108 gtar cJf "$TARBALL" release/ --owner=root --sort=name --transform="s,^release,${DIRNAME},"
109 ln "$TARBALL" "$BUILD_ARTIFACTSTAGINGDIRECTORY/."
110
111 mv "$DOWNLOADSECUREFILE_SECUREFILEPATH" "$HOME/.s3cfg"
112 s3cmd put -P --add-header="cache-control: public, max-age=31536000, immutable" "$TARBALL" s3://ziglang.org/builds/
113
114 SHASUM=$(shasum -a 256 $TARBALL | cut '-d ' -f1)
115 BYTESIZE=$(wc -c < $TARBALL)
116
117 JSONFILE="macos-$GITBRANCH.json"
118 touch $JSONFILE
119 echo "{\"tarball\": \"$TARBALL\"," >>$JSONFILE
120 echo "\"shasum\": \"$SHASUM\"," >>$JSONFILE
121 echo "\"size\": \"$BYTESIZE\"}" >>$JSONFILE
122
123 s3cmd put -P --add-header="Cache-Control: max-age=0, must-revalidate" "$JSONFILE" "s3://ziglang.org/builds/$JSONFILE"
124 s3cmd put -P "$JSONFILE" "s3://ziglang.org/builds/$ARCH-macos-$VERSION.json"
125
126 # `set -x` causes these variables to be mangled.
127 # See https://developercommunity.visualstudio.com/content/problem/375679/pipeline-variable-incorrectly-inserts-single-quote.html
128 set +x
129 echo "##vso[task.setvariable variable=tarball;isOutput=true]$TARBALL"
130 echo "##vso[task.setvariable variable=shasum;isOutput=true]$SHASUM"
131 echo "##vso[task.setvariable variable=bytesize;isOutput=true]$BYTESIZE"
132fi
ci/azure/pipelines.yml+14-1
...@@ -12,6 +12,19 @@ jobs:...@@ -12,6 +12,19 @@ jobs:
12 - script: ci/azure/macos_script12 - script: ci/azure/macos_script
13 name: main13 name: main
14 displayName: 'Build and test'14 displayName: 'Build and test'
15- job: BuildMacOS_arm64
16 pool:
17 vmImage: 'macOS-10.15'
18
19 timeoutInMinutes: 60
20
21 steps:
22 - task: DownloadSecureFile@1
23 inputs:
24 secureFile: s3cfg
25 - script: ci/azure/macos_arm64_script
26 name: main
27 displayName: 'Build and cross-compile'
15- job: BuildLinux28- job: BuildLinux
16 pool:29 pool:
17 vmImage: 'ubuntu-18.04'30 vmImage: 'ubuntu-18.04'
...@@ -31,7 +44,7 @@ jobs:...@@ -31,7 +44,7 @@ jobs:
31 timeoutInMinutes: 36044 timeoutInMinutes: 360
32 steps:45 steps:
33 - powershell: |46 - powershell: |
34 (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2021-01-05/msys2-base-x86_64-20210105.sfx.exe", "sfx.exe")47 (New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2021-02-28/msys2-base-x86_64-20210228.sfx.exe", "sfx.exe")
35 .\sfx.exe -y -o\48 .\sfx.exe -y -o\
36 del sfx.exe49 del sfx.exe
37 displayName: Download/Extract/Install MSYS250 displayName: Download/Extract/Install MSYS2
ci/azure/windows_msvc_install+1-1
...@@ -3,7 +3,7 @@...@@ -3,7 +3,7 @@
3set -x3set -x
4set -e4set -e
55
6pacman -Su --needed --noconfirm6pacman -Suy --needed --noconfirm
7pacman -S --needed --noconfirm wget p7zip python3-pip tar xz7pacman -S --needed --noconfirm wget p7zip python3-pip tar xz
88
9pip install s3cmd9pip install s3cmd
doc/langref.html.in+3-3
...@@ -9952,9 +9952,9 @@ export fn decode_base_64(...@@ -9952,9 +9952,9 @@ export fn decode_base_64(
9952) usize {9952) usize {
9953 const src = source_ptr[0..source_len];9953 const src = source_ptr[0..source_len];
9954 const dest = dest_ptr[0..dest_len];9954 const dest = dest_ptr[0..dest_len];
9955 const base64_decoder = base64.standard_decoder_unsafe;9955 const base64_decoder = base64.standard.Decoder;
9956 const decoded_size = base64_decoder.calcSize(src);9956 const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable;
9957 base64_decoder.decode(dest[0..decoded_size], src);9957 base64_decoder.decode(dest[0..decoded_size], src) catch unreachable;
9958 return decoded_size;9958 return decoded_size;
9959}9959}
9960 {#code_end#}9960 {#code_end#}
lib/std/array_hash_map.zig+13-13
...@@ -687,8 +687,9 @@ pub fn ArrayHashMapUnmanaged(...@@ -687,8 +687,9 @@ pub fn ArrayHashMapUnmanaged(
687687
688 /// Removes the last inserted `Entry` in the hash map and returns it.688 /// Removes the last inserted `Entry` in the hash map and returns it.
689 pub fn pop(self: *Self) Entry {689 pub fn pop(self: *Self) Entry {
690 const top = self.entries.pop();690 const top = self.entries.items[self.entries.items.len - 1];
691 _ = self.removeWithHash(top.key, top.hash, .index_only);691 _ = self.removeWithHash(top.key, top.hash, .index_only);
692 self.entries.items.len -= 1;
692 return top;693 return top;
693 }694 }
694695
...@@ -1258,19 +1259,18 @@ test "pop" {...@@ -1258,19 +1259,18 @@ test "pop" {
1258 var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);1259 var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
1259 defer map.deinit();1260 defer map.deinit();
12601261
1261 testing.expect((try map.fetchPut(1, 11)) == null);1262 // Insert just enough entries so that the map expands. Afterwards,
1262 testing.expect((try map.fetchPut(2, 22)) == null);1263 // pop all entries out of the map.
1263 testing.expect((try map.fetchPut(3, 33)) == null);
1264 testing.expect((try map.fetchPut(4, 44)) == null);
12651264
1266 const pop1 = map.pop();1265 var i: i32 = 0;
1267 testing.expect(pop1.key == 4 and pop1.value == 44);1266 while (i < 9) : (i += 1) {
1268 const pop2 = map.pop();1267 testing.expect((try map.fetchPut(i, i)) == null);
1269 testing.expect(pop2.key == 3 and pop2.value == 33);1268 }
1270 const pop3 = map.pop();1269
1271 testing.expect(pop3.key == 2 and pop3.value == 22);1270 while (i > 0) : (i -= 1) {
1272 const pop4 = map.pop();1271 const pop = map.pop();
1273 testing.expect(pop4.key == 1 and pop4.value == 11);1272 testing.expect(pop.key == i - 1 and pop.value == i - 1);
1273 }
1274}1274}
12751275
1276test "reIndex" {1276test "reIndex" {
lib/std/base64.zig+322-324
...@@ -8,454 +8,452 @@ const assert = std.debug.assert;...@@ -8,454 +8,452 @@ const assert = std.debug.assert;
8const testing = std.testing;8const testing = std.testing;
9const mem = std.mem;9const mem = std.mem;
1010
11pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";11pub const Error = error{
12pub const standard_pad_char = '=';12 InvalidCharacter,
13pub const standard_encoder = Base64Encoder.init(standard_alphabet_chars, standard_pad_char);13 InvalidPadding,
14 NoSpaceLeft,
15};
16
17/// Base64 codecs
18pub const Codecs = struct {
19 alphabet_chars: [64]u8,
20 pad_char: ?u8,
21 decoderWithIgnore: fn (ignore: []const u8) Base64DecoderWithIgnore,
22 Encoder: Base64Encoder,
23 Decoder: Base64Decoder,
24};
25
26pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".*;
27fn standardBase64DecoderWithIgnore(ignore: []const u8) Base64DecoderWithIgnore {
28 return Base64DecoderWithIgnore.init(standard_alphabet_chars, '=', ignore);
29}
30
31/// Standard Base64 codecs, with padding
32pub const standard = Codecs{
33 .alphabet_chars = standard_alphabet_chars,
34 .pad_char = '=',
35 .decoderWithIgnore = standardBase64DecoderWithIgnore,
36 .Encoder = Base64Encoder.init(standard_alphabet_chars, '='),
37 .Decoder = Base64Decoder.init(standard_alphabet_chars, '='),
38};
39
40/// Standard Base64 codecs, without padding
41pub const standard_no_pad = Codecs{
42 .alphabet_chars = standard_alphabet_chars,
43 .pad_char = null,
44 .decoderWithIgnore = standardBase64DecoderWithIgnore,
45 .Encoder = Base64Encoder.init(standard_alphabet_chars, null),
46 .Decoder = Base64Decoder.init(standard_alphabet_chars, null),
47};
48
49pub const url_safe_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;
50fn urlSafeBase64DecoderWithIgnore(ignore: []const u8) Base64DecoderWithIgnore {
51 return Base64DecoderWithIgnore.init(url_safe_alphabet_chars, null, ignore);
52}
53
54/// URL-safe Base64 codecs, with padding
55pub const url_safe = Codecs{
56 .alphabet_chars = url_safe_alphabet_chars,
57 .pad_char = '=',
58 .decoderWithIgnore = urlSafeBase64DecoderWithIgnore,
59 .Encoder = Base64Encoder.init(url_safe_alphabet_chars, '='),
60 .Decoder = Base64Decoder.init(url_safe_alphabet_chars, '='),
61};
62
63/// URL-safe Base64 codecs, without padding
64pub const url_safe_no_pad = Codecs{
65 .alphabet_chars = url_safe_alphabet_chars,
66 .pad_char = null,
67 .decoderWithIgnore = urlSafeBase64DecoderWithIgnore,
68 .Encoder = Base64Encoder.init(url_safe_alphabet_chars, null),
69 .Decoder = Base64Decoder.init(url_safe_alphabet_chars, null),
70};
71
72// Backwards compatibility
73
74/// Deprecated - Use `standard.pad_char`
75pub const standard_pad_char = standard.pad_char;
76/// Deprecated - Use `standard.Encoder`
77pub const standard_encoder = standard.Encoder;
78/// Deprecated - Use `standard.Decoder`
79pub const standard_decoder = standard.Decoder;
1480
15pub const Base64Encoder = struct {81pub const Base64Encoder = struct {
16 alphabet_chars: []const u8,82 alphabet_chars: [64]u8,
17 pad_char: u8,83 pad_char: ?u8,
1884
19 /// a bunch of assertions, then simply pass the data right through.85 /// A bunch of assertions, then simply pass the data right through.
20 pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64Encoder {86 pub fn init(alphabet_chars: [64]u8, pad_char: ?u8) Base64Encoder {
21 assert(alphabet_chars.len == 64);87 assert(alphabet_chars.len == 64);
22 var char_in_alphabet = [_]bool{false} ** 256;88 var char_in_alphabet = [_]bool{false} ** 256;
23 for (alphabet_chars) |c| {89 for (alphabet_chars) |c| {
24 assert(!char_in_alphabet[c]);90 assert(!char_in_alphabet[c]);
25 assert(c != pad_char);91 assert(pad_char == null or c != pad_char.?);
26 char_in_alphabet[c] = true;92 char_in_alphabet[c] = true;
27 }93 }
28
29 return Base64Encoder{94 return Base64Encoder{
30 .alphabet_chars = alphabet_chars,95 .alphabet_chars = alphabet_chars,
31 .pad_char = pad_char,96 .pad_char = pad_char,
32 };97 };
33 }98 }
3499
35 /// ceil(source_len * 4/3)100 /// Compute the encoded length
36 pub fn calcSize(source_len: usize) usize {101 pub fn calcSize(encoder: *const Base64Encoder, source_len: usize) usize {
37 return @divTrunc(source_len + 2, 3) * 4;102 if (encoder.pad_char != null) {
103 return @divTrunc(source_len + 2, 3) * 4;
104 } else {
105 const leftover = source_len % 3;
106 return @divTrunc(source_len, 3) * 4 + @divTrunc(leftover * 4 + 2, 3);
107 }
38 }108 }
39109
40 /// dest.len must be what you get from ::calcSize.110 /// dest.len must at least be what you get from ::calcSize.
41 pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) []const u8 {111 pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) []const u8 {
42 assert(dest.len >= Base64Encoder.calcSize(source.len));112 const out_len = encoder.calcSize(source.len);
43113 assert(dest.len >= out_len);
44 var i: usize = 0;114
45 var out_index: usize = 0;115 const nibbles = source.len / 3;
46 while (i + 2 < source.len) : (i += 3) {116 const leftover = source.len - 3 * nibbles;
47 dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];117
48 out_index += 1;118 var acc: u12 = 0;
49119 var acc_len: u4 = 0;
50 dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | ((source[i + 1] & 0xf0) >> 4)];120 var out_idx: usize = 0;
51 out_index += 1;121 for (source) |v| {
52122 acc = (acc << 8) + v;
53 dest[out_index] = encoder.alphabet_chars[((source[i + 1] & 0xf) << 2) | ((source[i + 2] & 0xc0) >> 6)];123 acc_len += 8;
54 out_index += 1;124 while (acc_len >= 6) {
55125 acc_len -= 6;
56 dest[out_index] = encoder.alphabet_chars[source[i + 2] & 0x3f];126 dest[out_idx] = encoder.alphabet_chars[@truncate(u6, (acc >> acc_len))];
57 out_index += 1;127 out_idx += 1;
128 }
58 }129 }
59130 if (acc_len > 0) {
60 if (i < source.len) {131 dest[out_idx] = encoder.alphabet_chars[@truncate(u6, (acc << 6 - acc_len))];
61 dest[out_index] = encoder.alphabet_chars[(source[i] >> 2) & 0x3f];132 out_idx += 1;
62 out_index += 1;133 }
63134 if (encoder.pad_char) |pad_char| {
64 if (i + 1 == source.len) {135 for (dest[out_idx..]) |*pad| {
65 dest[out_index] = encoder.alphabet_chars[(source[i] & 0x3) << 4];136 pad.* = pad_char;
66 out_index += 1;
67
68 dest[out_index] = encoder.pad_char;
69 out_index += 1;
70 } else {
71 dest[out_index] = encoder.alphabet_chars[((source[i] & 0x3) << 4) | ((source[i + 1] & 0xf0) >> 4)];
72 out_index += 1;
73
74 dest[out_index] = encoder.alphabet_chars[(source[i + 1] & 0xf) << 2];
75 out_index += 1;
76 }137 }
77
78 dest[out_index] = encoder.pad_char;
79 out_index += 1;
80 }138 }
81 return dest[0..out_index];139 return dest[0..out_len];
82 }140 }
83};141};
84142
85pub const standard_decoder = Base64Decoder.init(standard_alphabet_chars, standard_pad_char);
86
87pub const Base64Decoder = struct {143pub const Base64Decoder = struct {
144 const invalid_char: u8 = 0xff;
145
88 /// e.g. 'A' => 0.146 /// e.g. 'A' => 0.
89 /// undefined for any value not in the 64 alphabet chars.147 /// `invalid_char` for any value not in the 64 alphabet chars.
90 char_to_index: [256]u8,148 char_to_index: [256]u8,
149 pad_char: ?u8,
91150
92 /// true only for the 64 chars in the alphabet, not the pad char.151 pub fn init(alphabet_chars: [64]u8, pad_char: ?u8) Base64Decoder {
93 char_in_alphabet: [256]bool,
94 pad_char: u8,
95
96 pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64Decoder {
97 assert(alphabet_chars.len == 64);
98
99 var result = Base64Decoder{152 var result = Base64Decoder{
100 .char_to_index = undefined,153 .char_to_index = [_]u8{invalid_char} ** 256,
101 .char_in_alphabet = [_]bool{false} ** 256,
102 .pad_char = pad_char,154 .pad_char = pad_char,
103 };155 };
104156
157 var char_in_alphabet = [_]bool{false} ** 256;
105 for (alphabet_chars) |c, i| {158 for (alphabet_chars) |c, i| {
106 assert(!result.char_in_alphabet[c]);159 assert(!char_in_alphabet[c]);
107 assert(c != pad_char);160 assert(pad_char == null or c != pad_char.?);
108161
109 result.char_to_index[c] = @intCast(u8, i);162 result.char_to_index[c] = @intCast(u8, i);
110 result.char_in_alphabet[c] = true;163 char_in_alphabet[c] = true;
111 }164 }
165 return result;
166 }
112167
168 /// Return the maximum possible decoded size for a given input length - The actual length may be less if the input includes padding.
169 /// `InvalidPadding` is returned if the input length is not valid.
170 pub fn calcSizeUpperBound(decoder: *const Base64Decoder, source_len: usize) Error!usize {
171 var result = source_len / 4 * 3;
172 const leftover = source_len % 4;
173 if (decoder.pad_char != null) {
174 if (leftover % 4 != 0) return error.InvalidPadding;
175 } else {
176 if (leftover % 4 == 1) return error.InvalidPadding;
177 result += leftover * 3 / 4;
178 }
113 return result;179 return result;
114 }180 }
115181
116 /// If the encoded buffer is detected to be invalid, returns error.InvalidPadding.182 /// Return the exact decoded size for a slice.
117 pub fn calcSize(decoder: *const Base64Decoder, source: []const u8) !usize {183 /// `InvalidPadding` is returned if the input length is not valid.
118 if (source.len % 4 != 0) return error.InvalidPadding;184 pub fn calcSizeForSlice(decoder: *const Base64Decoder, source: []const u8) Error!usize {
119 return calcDecodedSizeExactUnsafe(source, decoder.pad_char);185 const source_len = source.len;
186 var result = try decoder.calcSizeUpperBound(source_len);
187 if (decoder.pad_char) |pad_char| {
188 if (source_len >= 1 and source[source_len - 1] == pad_char) result -= 1;
189 if (source_len >= 2 and source[source_len - 2] == pad_char) result -= 1;
190 }
191 return result;
120 }192 }
121193
122 /// dest.len must be what you get from ::calcSize.194 /// dest.len must be what you get from ::calcSize.
123 /// invalid characters result in error.InvalidCharacter.195 /// invalid characters result in error.InvalidCharacter.
124 /// invalid padding results in error.InvalidPadding.196 /// invalid padding results in error.InvalidPadding.
125 pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) !void {197 pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) Error!void {
126 assert(dest.len == (decoder.calcSize(source) catch unreachable));198 if (decoder.pad_char != null and source.len % 4 != 0) return error.InvalidPadding;
127 assert(source.len % 4 == 0);199 var acc: u12 = 0;
128200 var acc_len: u4 = 0;
129 var src_cursor: usize = 0;201 var dest_idx: usize = 0;
130 var dest_cursor: usize = 0;202 var leftover_idx: ?usize = null;
131203 for (source) |c, src_idx| {
132 while (src_cursor < source.len) : (src_cursor += 4) {204 const d = decoder.char_to_index[c];
133 if (!decoder.char_in_alphabet[source[src_cursor + 0]]) return error.InvalidCharacter;205 if (d == invalid_char) {
134 if (!decoder.char_in_alphabet[source[src_cursor + 1]]) return error.InvalidCharacter;206 if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter;
135 if (src_cursor < source.len - 4 or source[src_cursor + 3] != decoder.pad_char) {207 leftover_idx = src_idx;
136 // common case208 break;
137 if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;209 }
138 if (!decoder.char_in_alphabet[source[src_cursor + 3]]) return error.InvalidCharacter;210 acc = (acc << 6) + d;
139 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4;211 acc_len += 6;
140 dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | decoder.char_to_index[source[src_cursor + 2]] >> 2;212 if (acc_len >= 8) {
141 dest[dest_cursor + 2] = decoder.char_to_index[source[src_cursor + 2]] << 6 | decoder.char_to_index[source[src_cursor + 3]];213 acc_len -= 8;
142 dest_cursor += 3;214 dest[dest_idx] = @truncate(u8, acc >> acc_len);
143 } else if (source[src_cursor + 2] != decoder.pad_char) {215 dest_idx += 1;
144 // one pad char
145 if (!decoder.char_in_alphabet[source[src_cursor + 2]]) return error.InvalidCharacter;
146 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4;
147 dest[dest_cursor + 1] = decoder.char_to_index[source[src_cursor + 1]] << 4 | decoder.char_to_index[source[src_cursor + 2]] >> 2;
148 if (decoder.char_to_index[source[src_cursor + 2]] << 6 != 0) return error.InvalidPadding;
149 dest_cursor += 2;
150 } else {
151 // two pad chars
152 dest[dest_cursor + 0] = decoder.char_to_index[source[src_cursor + 0]] << 2 | decoder.char_to_index[source[src_cursor + 1]] >> 4;
153 if (decoder.char_to_index[source[src_cursor + 1]] << 4 != 0) return error.InvalidPadding;
154 dest_cursor += 1;
155 }216 }
156 }217 }
157218 if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) {
158 assert(src_cursor == source.len);219 return error.InvalidPadding;
159 assert(dest_cursor == dest.len);220 }
221 if (leftover_idx == null) return;
222 var leftover = source[leftover_idx.?..];
223 if (decoder.pad_char) |pad_char| {
224 const padding_len = acc_len / 2;
225 var padding_chars: usize = 0;
226 var i: usize = 0;
227 for (leftover) |c| {
228 if (c != pad_char) {
229 return if (c == Base64Decoder.invalid_char) error.InvalidCharacter else error.InvalidPadding;
230 }
231 padding_chars += 1;
232 }
233 if (padding_chars != padding_len) return error.InvalidPadding;
234 }
160 }235 }
161};236};
162237
163pub const Base64DecoderWithIgnore = struct {238pub const Base64DecoderWithIgnore = struct {
164 decoder: Base64Decoder,239 decoder: Base64Decoder,
165 char_is_ignored: [256]bool,240 char_is_ignored: [256]bool,
166 pub fn init(alphabet_chars: []const u8, pad_char: u8, ignore_chars: []const u8) Base64DecoderWithIgnore {241
242 pub fn init(alphabet_chars: [64]u8, pad_char: ?u8, ignore_chars: []const u8) Base64DecoderWithIgnore {
167 var result = Base64DecoderWithIgnore{243 var result = Base64DecoderWithIgnore{
168 .decoder = Base64Decoder.init(alphabet_chars, pad_char),244 .decoder = Base64Decoder.init(alphabet_chars, pad_char),
169 .char_is_ignored = [_]bool{false} ** 256,245 .char_is_ignored = [_]bool{false} ** 256,
170 };246 };
171
172 for (ignore_chars) |c| {247 for (ignore_chars) |c| {
173 assert(!result.decoder.char_in_alphabet[c]);248 assert(result.decoder.char_to_index[c] == Base64Decoder.invalid_char);
174 assert(!result.char_is_ignored[c]);249 assert(!result.char_is_ignored[c]);
175 assert(result.decoder.pad_char != c);250 assert(result.decoder.pad_char != c);
176 result.char_is_ignored[c] = true;251 result.char_is_ignored[c] = true;
177 }252 }
178
179 return result;253 return result;
180 }254 }
181255
182 /// If no characters end up being ignored or padding, this will be the exact decoded size.256 /// Return the maximum possible decoded size for a given input length - The actual length may be less if the input includes padding
183 pub fn calcSizeUpperBound(encoded_len: usize) usize {257 /// `InvalidPadding` is returned if the input length is not valid.
184 return @divTrunc(encoded_len, 4) * 3;258 pub fn calcSizeUpperBound(decoder_with_ignore: *const Base64DecoderWithIgnore, source_len: usize) Error!usize {
259 var result = source_len / 4 * 3;
260 if (decoder_with_ignore.decoder.pad_char == null) {
261 const leftover = source_len % 4;
262 result += leftover * 3 / 4;
263 }
264 return result;
185 }265 }
186266
187 /// Invalid characters that are not ignored result in error.InvalidCharacter.267 /// Invalid characters that are not ignored result in error.InvalidCharacter.
188 /// Invalid padding results in error.InvalidPadding.268 /// Invalid padding results in error.InvalidPadding.
189 /// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound.269 /// Decoding more data than can fit in dest results in error.NoSpaceLeft. See also ::calcSizeUpperBound.
190 /// Returns the number of bytes written to dest.270 /// Returns the number of bytes written to dest.
191 pub fn decode(decoder_with_ignore: *const Base64DecoderWithIgnore, dest: []u8, source: []const u8) !usize {271 pub fn decode(decoder_with_ignore: *const Base64DecoderWithIgnore, dest: []u8, source: []const u8) Error!usize {
192 const decoder = &decoder_with_ignore.decoder;272 const decoder = &decoder_with_ignore.decoder;
193273 var acc: u12 = 0;
194 var src_cursor: usize = 0;274 var acc_len: u4 = 0;
195 var dest_cursor: usize = 0;275 var dest_idx: usize = 0;
196276 var leftover_idx: ?usize = null;
197 while (true) {277 for (source) |c, src_idx| {
198 // get the next 4 chars, if available278 if (decoder_with_ignore.char_is_ignored[c]) continue;
199 var next_4_chars: [4]u8 = undefined;279 const d = decoder.char_to_index[c];
200 var available_chars: usize = 0;280 if (d == Base64Decoder.invalid_char) {
201 var pad_char_count: usize = 0;281 if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter;
202 while (available_chars < 4 and src_cursor < source.len) {282 leftover_idx = src_idx;
203 var c = source[src_cursor];283 break;
204 src_cursor += 1;
205
206 if (decoder.char_in_alphabet[c]) {
207 // normal char
208 next_4_chars[available_chars] = c;
209 available_chars += 1;
210 } else if (decoder_with_ignore.char_is_ignored[c]) {
211 // we're told to skip this one
212 continue;
213 } else if (c == decoder.pad_char) {
214 // the padding has begun. count the pad chars.
215 pad_char_count += 1;
216 while (src_cursor < source.len) {
217 c = source[src_cursor];
218 src_cursor += 1;
219 if (c == decoder.pad_char) {
220 pad_char_count += 1;
221 if (pad_char_count > 2) return error.InvalidCharacter;
222 } else if (decoder_with_ignore.char_is_ignored[c]) {
223 // we can even ignore chars during the padding
224 continue;
225 } else return error.InvalidCharacter;
226 }
227 break;
228 } else return error.InvalidCharacter;
229 }284 }
230285 acc = (acc << 6) + d;
231 switch (available_chars) {286 acc_len += 6;
232 4 => {287 if (acc_len >= 8) {
233 // common case288 if (dest_idx == dest.len) return error.NoSpaceLeft;
234 if (dest_cursor + 3 > dest.len) return error.OutputTooSmall;289 acc_len -= 8;
235 assert(pad_char_count == 0);290 dest[dest_idx] = @truncate(u8, acc >> acc_len);
236 dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4;291 dest_idx += 1;
237 dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 | decoder.char_to_index[next_4_chars[2]] >> 2;
238 dest[dest_cursor + 2] = decoder.char_to_index[next_4_chars[2]] << 6 | decoder.char_to_index[next_4_chars[3]];
239 dest_cursor += 3;
240 continue;
241 },
242 3 => {
243 if (dest_cursor + 2 > dest.len) return error.OutputTooSmall;
244 if (pad_char_count != 1) return error.InvalidPadding;
245 dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4;
246 dest[dest_cursor + 1] = decoder.char_to_index[next_4_chars[1]] << 4 | decoder.char_to_index[next_4_chars[2]] >> 2;
247 if (decoder.char_to_index[next_4_chars[2]] << 6 != 0) return error.InvalidPadding;
248 dest_cursor += 2;
249 break;
250 },
251 2 => {
252 if (dest_cursor + 1 > dest.len) return error.OutputTooSmall;
253 if (pad_char_count != 2) return error.InvalidPadding;
254 dest[dest_cursor + 0] = decoder.char_to_index[next_4_chars[0]] << 2 | decoder.char_to_index[next_4_chars[1]] >> 4;
255 if (decoder.char_to_index[next_4_chars[1]] << 4 != 0) return error.InvalidPadding;
256 dest_cursor += 1;
257 break;
258 },
259 1 => {
260 return error.InvalidPadding;
261 },
262 0 => {
263 if (pad_char_count != 0) return error.InvalidPadding;
264 break;
265 },
266 else => unreachable,
267 }292 }
268 }293 }
269294 if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) {
270 assert(src_cursor == source.len);295 return error.InvalidPadding;
271
272 return dest_cursor;
273 }
274};
275
276pub const standard_decoder_unsafe = Base64DecoderUnsafe.init(standard_alphabet_chars, standard_pad_char);
277
278pub const Base64DecoderUnsafe = struct {
279 /// e.g. 'A' => 0.
280 /// undefined for any value not in the 64 alphabet chars.
281 char_to_index: [256]u8,
282 pad_char: u8,
283
284 pub fn init(alphabet_chars: []const u8, pad_char: u8) Base64DecoderUnsafe {
285 assert(alphabet_chars.len == 64);
286 var result = Base64DecoderUnsafe{
287 .char_to_index = undefined,
288 .pad_char = pad_char,
289 };
290 for (alphabet_chars) |c, i| {
291 assert(c != pad_char);
292 result.char_to_index[c] = @intCast(u8, i);
293 }296 }
294 return result;297 const padding_len = acc_len / 2;
295 }298 if (leftover_idx == null) {
296299 if (decoder.pad_char != null and padding_len != 0) return error.InvalidPadding;
297 /// The source buffer must be valid.300 return dest_idx;
298 pub fn calcSize(decoder: *const Base64DecoderUnsafe, source: []const u8) usize {
299 return calcDecodedSizeExactUnsafe(source, decoder.pad_char);
300 }
301
302 /// dest.len must be what you get from ::calcDecodedSizeExactUnsafe.
303 /// invalid characters or padding will result in undefined values.
304 pub fn decode(decoder: *const Base64DecoderUnsafe, dest: []u8, source: []const u8) void {
305 assert(dest.len == decoder.calcSize(source));
306
307 var src_index: usize = 0;
308 var dest_index: usize = 0;
309 var in_buf_len: usize = source.len;
310
311 while (in_buf_len > 0 and source[in_buf_len - 1] == decoder.pad_char) {
312 in_buf_len -= 1;
313 }301 }
314302 var leftover = source[leftover_idx.?..];
315 while (in_buf_len > 4) {303 if (decoder.pad_char) |pad_char| {
316 dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | decoder.char_to_index[source[src_index + 1]] >> 4;304 var padding_chars: usize = 0;
317 dest_index += 1;305 var i: usize = 0;
318306 for (leftover) |c| {
319 dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | decoder.char_to_index[source[src_index + 2]] >> 2;307 if (decoder_with_ignore.char_is_ignored[c]) continue;
320 dest_index += 1;308 if (c != pad_char) {
321309 return if (c == Base64Decoder.invalid_char) error.InvalidCharacter else error.InvalidPadding;
322 dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | decoder.char_to_index[source[src_index + 3]];310 }
323 dest_index += 1;311 padding_chars += 1;
324312 }
325 src_index += 4;313 if (padding_chars != padding_len) return error.InvalidPadding;
326 in_buf_len -= 4;
327 }
328
329 if (in_buf_len > 1) {
330 dest[dest_index] = decoder.char_to_index[source[src_index + 0]] << 2 | decoder.char_to_index[source[src_index + 1]] >> 4;
331 dest_index += 1;
332 }
333 if (in_buf_len > 2) {
334 dest[dest_index] = decoder.char_to_index[source[src_index + 1]] << 4 | decoder.char_to_index[source[src_index + 2]] >> 2;
335 dest_index += 1;
336 }
337 if (in_buf_len > 3) {
338 dest[dest_index] = decoder.char_to_index[source[src_index + 2]] << 6 | decoder.char_to_index[source[src_index + 3]];
339 dest_index += 1;
340 }314 }
315 return dest_idx;
341 }316 }
342};317};
343318
344fn calcDecodedSizeExactUnsafe(source: []const u8, pad_char: u8) usize {
345 if (source.len == 0) return 0;
346 var result = @divExact(source.len, 4) * 3;
347 if (source[source.len - 1] == pad_char) {
348 result -= 1;
349 if (source[source.len - 2] == pad_char) {
350 result -= 1;
351 }
352 }
353 return result;
354}
355
356test "base64" {319test "base64" {
357 @setEvalBranchQuota(8000);320 @setEvalBranchQuota(8000);
358 testBase64() catch unreachable;321 testBase64() catch unreachable;
359 comptime (testBase64() catch unreachable);322 comptime testAllApis(standard, "comptime", "Y29tcHRpbWU=") catch unreachable;
323}
324
325test "base64 url_safe_no_pad" {
326 @setEvalBranchQuota(8000);
327 testBase64UrlSafeNoPad() catch unreachable;
328 comptime testAllApis(url_safe_no_pad, "comptime", "Y29tcHRpbWU") catch unreachable;
360}329}
361330
362fn testBase64() !void {331fn testBase64() !void {
363 try testAllApis("", "");332 const codecs = standard;
364 try testAllApis("f", "Zg==");333
365 try testAllApis("fo", "Zm8=");334 try testAllApis(codecs, "", "");
366 try testAllApis("foo", "Zm9v");335 try testAllApis(codecs, "f", "Zg==");
367 try testAllApis("foob", "Zm9vYg==");336 try testAllApis(codecs, "fo", "Zm8=");
368 try testAllApis("fooba", "Zm9vYmE=");337 try testAllApis(codecs, "foo", "Zm9v");
369 try testAllApis("foobar", "Zm9vYmFy");338 try testAllApis(codecs, "foob", "Zm9vYg==");
370339 try testAllApis(codecs, "fooba", "Zm9vYmE=");
371 try testDecodeIgnoreSpace("", " ");340 try testAllApis(codecs, "foobar", "Zm9vYmFy");
372 try testDecodeIgnoreSpace("f", "Z g= =");341
373 try testDecodeIgnoreSpace("fo", " Zm8=");342 try testDecodeIgnoreSpace(codecs, "", " ");
374 try testDecodeIgnoreSpace("foo", "Zm9v ");343 try testDecodeIgnoreSpace(codecs, "f", "Z g= =");
375 try testDecodeIgnoreSpace("foob", "Zm9vYg = = ");344 try testDecodeIgnoreSpace(codecs, "fo", " Zm8=");
376 try testDecodeIgnoreSpace("fooba", "Zm9v YmE=");345 try testDecodeIgnoreSpace(codecs, "foo", "Zm9v ");
377 try testDecodeIgnoreSpace("foobar", " Z m 9 v Y m F y ");346 try testDecodeIgnoreSpace(codecs, "foob", "Zm9vYg = = ");
347 try testDecodeIgnoreSpace(codecs, "fooba", "Zm9v YmE=");
348 try testDecodeIgnoreSpace(codecs, "foobar", " Z m 9 v Y m F y ");
349
350 // test getting some api errors
351 try testError(codecs, "A", error.InvalidPadding);
352 try testError(codecs, "AA", error.InvalidPadding);
353 try testError(codecs, "AAA", error.InvalidPadding);
354 try testError(codecs, "A..A", error.InvalidCharacter);
355 try testError(codecs, "AA=A", error.InvalidPadding);
356 try testError(codecs, "AA/=", error.InvalidPadding);
357 try testError(codecs, "A/==", error.InvalidPadding);
358 try testError(codecs, "A===", error.InvalidPadding);
359 try testError(codecs, "====", error.InvalidPadding);
360
361 try testNoSpaceLeftError(codecs, "AA==");
362 try testNoSpaceLeftError(codecs, "AAA=");
363 try testNoSpaceLeftError(codecs, "AAAA");
364 try testNoSpaceLeftError(codecs, "AAAAAA==");
365}
366
367fn testBase64UrlSafeNoPad() !void {
368 const codecs = url_safe_no_pad;
369
370 try testAllApis(codecs, "", "");
371 try testAllApis(codecs, "f", "Zg");
372 try testAllApis(codecs, "fo", "Zm8");
373 try testAllApis(codecs, "foo", "Zm9v");
374 try testAllApis(codecs, "foob", "Zm9vYg");
375 try testAllApis(codecs, "fooba", "Zm9vYmE");
376 try testAllApis(codecs, "foobar", "Zm9vYmFy");
377
378 try testDecodeIgnoreSpace(codecs, "", " ");
379 try testDecodeIgnoreSpace(codecs, "f", "Z g ");
380 try testDecodeIgnoreSpace(codecs, "fo", " Zm8");
381 try testDecodeIgnoreSpace(codecs, "foo", "Zm9v ");
382 try testDecodeIgnoreSpace(codecs, "foob", "Zm9vYg ");
383 try testDecodeIgnoreSpace(codecs, "fooba", "Zm9v YmE");
384 try testDecodeIgnoreSpace(codecs, "foobar", " Z m 9 v Y m F y ");
378385
379 // test getting some api errors386 // test getting some api errors
380 try testError("A", error.InvalidPadding);387 try testError(codecs, "A", error.InvalidPadding);
381 try testError("AA", error.InvalidPadding);388 try testError(codecs, "AAA=", error.InvalidCharacter);
382 try testError("AAA", error.InvalidPadding);389 try testError(codecs, "A..A", error.InvalidCharacter);
383 try testError("A..A", error.InvalidCharacter);390 try testError(codecs, "AA=A", error.InvalidCharacter);
384 try testError("AA=A", error.InvalidCharacter);391 try testError(codecs, "AA/=", error.InvalidCharacter);
385 try testError("AA/=", error.InvalidPadding);392 try testError(codecs, "A/==", error.InvalidCharacter);
386 try testError("A/==", error.InvalidPadding);393 try testError(codecs, "A===", error.InvalidCharacter);
387 try testError("A===", error.InvalidCharacter);394 try testError(codecs, "====", error.InvalidCharacter);
388 try testError("====", error.InvalidCharacter);395
389396 try testNoSpaceLeftError(codecs, "AA");
390 try testOutputTooSmallError("AA==");397 try testNoSpaceLeftError(codecs, "AAA");
391 try testOutputTooSmallError("AAA=");398 try testNoSpaceLeftError(codecs, "AAAA");
392 try testOutputTooSmallError("AAAA");399 try testNoSpaceLeftError(codecs, "AAAAAA");
393 try testOutputTooSmallError("AAAAAA==");
394}400}
395401
396fn testAllApis(expected_decoded: []const u8, expected_encoded: []const u8) !void {402fn testAllApis(codecs: Codecs, expected_decoded: []const u8, expected_encoded: []const u8) !void {
397 // Base64Encoder403 // Base64Encoder
398 {404 {
399 var buffer: [0x100]u8 = undefined;405 var buffer: [0x100]u8 = undefined;
400 const encoded = standard_encoder.encode(&buffer, expected_decoded);406 const encoded = codecs.Encoder.encode(&buffer, expected_decoded);
401 testing.expectEqualSlices(u8, expected_encoded, encoded);407 testing.expectEqualSlices(u8, expected_encoded, encoded);
402 }408 }
403409
404 // Base64Decoder410 // Base64Decoder
405 {411 {
406 var buffer: [0x100]u8 = undefined;412 var buffer: [0x100]u8 = undefined;
407 var decoded = buffer[0..try standard_decoder.calcSize(expected_encoded)];413 var decoded = buffer[0..try codecs.Decoder.calcSizeForSlice(expected_encoded)];
408 try standard_decoder.decode(decoded, expected_encoded);414 try codecs.Decoder.decode(decoded, expected_encoded);
409 testing.expectEqualSlices(u8, expected_decoded, decoded);415 testing.expectEqualSlices(u8, expected_decoded, decoded);
410 }416 }
411417
412 // Base64DecoderWithIgnore418 // Base64DecoderWithIgnore
413 {419 {
414 const standard_decoder_ignore_nothing = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, "");420 const decoder_ignore_nothing = codecs.decoderWithIgnore("");
415 var buffer: [0x100]u8 = undefined;421 var buffer: [0x100]u8 = undefined;
416 var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(expected_encoded.len)];422 var decoded = buffer[0..try decoder_ignore_nothing.calcSizeUpperBound(expected_encoded.len)];
417 var written = try standard_decoder_ignore_nothing.decode(decoded, expected_encoded);423 var written = try decoder_ignore_nothing.decode(decoded, expected_encoded);
418 testing.expect(written <= decoded.len);424 testing.expect(written <= decoded.len);
419 testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]);425 testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]);
420 }426 }
421
422 // Base64DecoderUnsafe
423 {
424 var buffer: [0x100]u8 = undefined;
425 var decoded = buffer[0..standard_decoder_unsafe.calcSize(expected_encoded)];
426 standard_decoder_unsafe.decode(decoded, expected_encoded);
427 testing.expectEqualSlices(u8, expected_decoded, decoded);
428 }
429}427}
430428
431fn testDecodeIgnoreSpace(expected_decoded: []const u8, encoded: []const u8) !void {429fn testDecodeIgnoreSpace(codecs: Codecs, expected_decoded: []const u8, encoded: []const u8) !void {
432 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");430 const decoder_ignore_space = codecs.decoderWithIgnore(" ");
433 var buffer: [0x100]u8 = undefined;431 var buffer: [0x100]u8 = undefined;
434 var decoded = buffer[0..Base64DecoderWithIgnore.calcSizeUpperBound(encoded.len)];432 var decoded = buffer[0..try decoder_ignore_space.calcSizeUpperBound(encoded.len)];
435 var written = try standard_decoder_ignore_space.decode(decoded, encoded);433 var written = try decoder_ignore_space.decode(decoded, encoded);
436 testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]);434 testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]);
437}435}
438436
439fn testError(encoded: []const u8, expected_err: anyerror) !void {437fn testError(codecs: Codecs, encoded: []const u8, expected_err: anyerror) !void {
440 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");438 const decoder_ignore_space = codecs.decoderWithIgnore(" ");
441 var buffer: [0x100]u8 = undefined;439 var buffer: [0x100]u8 = undefined;
442 if (standard_decoder.calcSize(encoded)) |decoded_size| {440 if (codecs.Decoder.calcSizeForSlice(encoded)) |decoded_size| {
443 var decoded = buffer[0..decoded_size];441 var decoded = buffer[0..decoded_size];
444 if (standard_decoder.decode(decoded, encoded)) |_| {442 if (codecs.Decoder.decode(decoded, encoded)) |_| {
445 return error.ExpectedError;443 return error.ExpectedError;
446 } else |err| if (err != expected_err) return err;444 } else |err| if (err != expected_err) return err;
447 } else |err| if (err != expected_err) return err;445 } else |err| if (err != expected_err) return err;
448446
449 if (standard_decoder_ignore_space.decode(buffer[0..], encoded)) |_| {447 if (decoder_ignore_space.decode(buffer[0..], encoded)) |_| {
450 return error.ExpectedError;448 return error.ExpectedError;
451 } else |err| if (err != expected_err) return err;449 } else |err| if (err != expected_err) return err;
452}450}
453451
454fn testOutputTooSmallError(encoded: []const u8) !void {452fn testNoSpaceLeftError(codecs: Codecs, encoded: []const u8) !void {
455 const standard_decoder_ignore_space = Base64DecoderWithIgnore.init(standard_alphabet_chars, standard_pad_char, " ");453 const decoder_ignore_space = codecs.decoderWithIgnore(" ");
456 var buffer: [0x100]u8 = undefined;454 var buffer: [0x100]u8 = undefined;
457 var decoded = buffer[0 .. calcDecodedSizeExactUnsafe(encoded, standard_pad_char) - 1];455 var decoded = buffer[0 .. (try codecs.Decoder.calcSizeForSlice(encoded)) - 1];
458 if (standard_decoder_ignore_space.decode(decoded, encoded)) |_| {456 if (decoder_ignore_space.decode(decoded, encoded)) |_| {
459 return error.ExpectedError;457 return error.ExpectedError;
460 } else |err| if (err != error.OutputTooSmall) return err;458 } else |err| if (err != error.NoSpaceLeft) return err;
461}459}
lib/std/build.zig+7-16
...@@ -51,7 +51,7 @@ pub const Builder = struct {...@@ -51,7 +51,7 @@ pub const Builder = struct {
51 default_step: *Step,51 default_step: *Step,
52 env_map: *BufMap,52 env_map: *BufMap,
53 top_level_steps: ArrayList(*TopLevelStep),53 top_level_steps: ArrayList(*TopLevelStep),
54 install_prefix: ?[]const u8,54 install_prefix: []const u8,
55 dest_dir: ?[]const u8,55 dest_dir: ?[]const u8,
56 lib_dir: []const u8,56 lib_dir: []const u8,
57 exe_dir: []const u8,57 exe_dir: []const u8,
...@@ -156,7 +156,7 @@ pub const Builder = struct {...@@ -156,7 +156,7 @@ pub const Builder = struct {
156 .default_step = undefined,156 .default_step = undefined,
157 .env_map = env_map,157 .env_map = env_map,
158 .search_prefixes = ArrayList([]const u8).init(allocator),158 .search_prefixes = ArrayList([]const u8).init(allocator),
159 .install_prefix = null,159 .install_prefix = undefined,
160 .lib_dir = undefined,160 .lib_dir = undefined,
161 .exe_dir = undefined,161 .exe_dir = undefined,
162 .h_dir = undefined,162 .h_dir = undefined,
...@@ -190,22 +190,13 @@ pub const Builder = struct {...@@ -190,22 +190,13 @@ pub const Builder = struct {
190 }190 }
191191
192 /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file.192 /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file.
193 pub fn setInstallPrefix(self: *Builder, optional_prefix: ?[]const u8) void {193 pub fn resolveInstallPrefix(self: *Builder, install_prefix: ?[]const u8) void {
194 self.install_prefix = optional_prefix;
195 }
196
197 /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file.
198 pub fn resolveInstallPrefix(self: *Builder) void {
199 if (self.dest_dir) |dest_dir| {194 if (self.dest_dir) |dest_dir| {
200 const install_prefix = self.install_prefix orelse "/usr";195 self.install_prefix = install_prefix orelse "/usr";
201 self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, install_prefix }) catch unreachable;196 self.install_path = fs.path.join(self.allocator, &[_][]const u8{ dest_dir, self.install_prefix }) catch unreachable;
202 } else {197 } else {
203 const install_prefix = self.install_prefix orelse blk: {198 self.install_prefix = install_prefix orelse self.cache_root;
204 const p = self.cache_root;199 self.install_path = self.install_prefix;
205 self.install_prefix = p;
206 break :blk p;
207 };
208 self.install_path = install_prefix;
209 }200 }
210 self.lib_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "lib" }) catch unreachable;201 self.lib_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "lib" }) catch unreachable;
211 self.exe_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "bin" }) catch unreachable;202 self.exe_dir = fs.path.join(self.allocator, &[_][]const u8{ self.install_path, "bin" }) catch unreachable;
lib/std/fmt.zig+7-2
...@@ -1250,9 +1250,9 @@ fn formatDuration(ns: u64, comptime fmt: []const u8, options: std.fmt.FormatOpti...@@ -1250,9 +1250,9 @@ fn formatDuration(ns: u64, comptime fmt: []const u8, options: std.fmt.FormatOpti
1250 const kunits = ns_remaining * 1000 / unit.ns;1250 const kunits = ns_remaining * 1000 / unit.ns;
1251 if (kunits >= 1000) {1251 if (kunits >= 1000) {
1252 try formatInt(kunits / 1000, 10, false, .{}, writer);1252 try formatInt(kunits / 1000, 10, false, .{}, writer);
1253 if (kunits > 1000) {1253 const frac = kunits % 1000;
1254 if (frac > 0) {
1254 // Write up to 3 decimal places1255 // Write up to 3 decimal places
1255 const frac = kunits % 1000;
1256 var buf = [_]u8{ '.', 0, 0, 0 };1256 var buf = [_]u8{ '.', 0, 0, 0 };
1257 _ = formatIntBuf(buf[1..], frac, 10, false, .{ .fill = '0', .width = 3 });1257 _ = formatIntBuf(buf[1..], frac, 10, false, .{ .fill = '0', .width = 3 });
1258 var end: usize = 4;1258 var end: usize = 4;
...@@ -1286,9 +1286,14 @@ test "fmtDuration" {...@@ -1286,9 +1286,14 @@ test "fmtDuration" {
1286 .{ .s = "1us", .d = std.time.ns_per_us },1286 .{ .s = "1us", .d = std.time.ns_per_us },
1287 .{ .s = "1.45us", .d = 1450 },1287 .{ .s = "1.45us", .d = 1450 },
1288 .{ .s = "1.5us", .d = 3 * std.time.ns_per_us / 2 },1288 .{ .s = "1.5us", .d = 3 * std.time.ns_per_us / 2 },
1289 .{ .s = "14.5us", .d = 14500 },
1290 .{ .s = "145us", .d = 145000 },
1289 .{ .s = "999.999us", .d = std.time.ns_per_ms - 1 },1291 .{ .s = "999.999us", .d = std.time.ns_per_ms - 1 },
1290 .{ .s = "1ms", .d = std.time.ns_per_ms + 1 },1292 .{ .s = "1ms", .d = std.time.ns_per_ms + 1 },
1291 .{ .s = "1.5ms", .d = 3 * std.time.ns_per_ms / 2 },1293 .{ .s = "1.5ms", .d = 3 * std.time.ns_per_ms / 2 },
1294 .{ .s = "1.11ms", .d = 1110000 },
1295 .{ .s = "1.111ms", .d = 1111000 },
1296 .{ .s = "1.111ms", .d = 1111100 },
1292 .{ .s = "999.999ms", .d = std.time.ns_per_s - 1 },1297 .{ .s = "999.999ms", .d = std.time.ns_per_s - 1 },
1293 .{ .s = "1s", .d = std.time.ns_per_s },1298 .{ .s = "1s", .d = std.time.ns_per_s },
1294 .{ .s = "59.999s", .d = std.time.ns_per_min - 1 },1299 .{ .s = "59.999s", .d = std.time.ns_per_min - 1 },
lib/std/fs.zig+5-5
...@@ -50,13 +50,13 @@ pub const MAX_PATH_BYTES = switch (builtin.os.tag) {...@@ -50,13 +50,13 @@ pub const MAX_PATH_BYTES = switch (builtin.os.tag) {
50 else => @compileError("Unsupported OS"),50 else => @compileError("Unsupported OS"),
51};51};
5252
53pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";53pub const base64_alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*;
5454
55/// Base64 encoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.55/// Base64 encoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.
56pub const base64_encoder = base64.Base64Encoder.init(base64_alphabet, base64.standard_pad_char);56pub const base64_encoder = base64.Base64Encoder.init(base64_alphabet, null);
5757
58/// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.58/// Base64 decoder, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.
59pub const base64_decoder = base64.Base64Decoder.init(base64_alphabet, base64.standard_pad_char);59pub const base64_decoder = base64.Base64Decoder.init(base64_alphabet, null);
6060
61/// Whether or not async file system syscalls need a dedicated thread because the operating61/// Whether or not async file system syscalls need a dedicated thread because the operating
62/// system does not support non-blocking I/O on the file system.62/// system does not support non-blocking I/O on the file system.
...@@ -77,7 +77,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:...@@ -77,7 +77,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
77 const dirname = path.dirname(new_path) orelse ".";77 const dirname = path.dirname(new_path) orelse ".";
7878
79 var rand_buf: [AtomicFile.RANDOM_BYTES]u8 = undefined;79 var rand_buf: [AtomicFile.RANDOM_BYTES]u8 = undefined;
80 const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len));80 const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64_encoder.calcSize(rand_buf.len));
81 defer allocator.free(tmp_path);81 defer allocator.free(tmp_path);
82 mem.copy(u8, tmp_path[0..], dirname);82 mem.copy(u8, tmp_path[0..], dirname);
83 tmp_path[dirname.len] = path.sep;83 tmp_path[dirname.len] = path.sep;
...@@ -142,7 +142,7 @@ pub const AtomicFile = struct {...@@ -142,7 +142,7 @@ pub const AtomicFile = struct {
142 const InitError = File.OpenError;142 const InitError = File.OpenError;
143143
144 const RANDOM_BYTES = 12;144 const RANDOM_BYTES = 12;
145 const TMP_PATH_LEN = base64.Base64Encoder.calcSize(RANDOM_BYTES);145 const TMP_PATH_LEN = base64_encoder.calcSize(RANDOM_BYTES);
146146
147 /// Note that the `Dir.atomicFile` API may be more handy than this lower-level function.147 /// Note that the `Dir.atomicFile` API may be more handy than this lower-level function.
148 pub fn init(148 pub fn init(
lib/std/hash/auto_hash.zig+1-1
...@@ -95,7 +95,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {...@@ -95,7 +95,7 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
95 .EnumLiteral,95 .EnumLiteral,
96 .Frame,96 .Frame,
97 .Float,97 .Float,
98 => @compileError("cannot hash this type"),98 => @compileError("unable to hash type " ++ @typeName(Key)),
9999
100 // Help the optimizer see that hashing an int is easy by inlining!100 // Help the optimizer see that hashing an int is easy by inlining!
101 // TODO Check if the situation is better after #561 is resolved.101 // TODO Check if the situation is better after #561 is resolved.
lib/std/mem.zig+19
...@@ -1373,6 +1373,20 @@ test "mem.tokenize (multibyte)" {...@@ -1373,6 +1373,20 @@ test "mem.tokenize (multibyte)" {
1373 testing.expect(it.next() == null);1373 testing.expect(it.next() == null);
1374}1374}
13751375
1376test "mem.tokenize (reset)" {
1377 var it = tokenize(" abc def ghi ", " ");
1378 testing.expect(eql(u8, it.next().?, "abc"));
1379 testing.expect(eql(u8, it.next().?, "def"));
1380 testing.expect(eql(u8, it.next().?, "ghi"));
1381
1382 it.reset();
1383
1384 testing.expect(eql(u8, it.next().?, "abc"));
1385 testing.expect(eql(u8, it.next().?, "def"));
1386 testing.expect(eql(u8, it.next().?, "ghi"));
1387 testing.expect(it.next() == null);
1388}
1389
1376/// Returns an iterator that iterates over the slices of `buffer` that1390/// Returns an iterator that iterates over the slices of `buffer` that
1377/// are separated by bytes in `delimiter`.1391/// are separated by bytes in `delimiter`.
1378/// split("abc|def||ghi", "|")1392/// split("abc|def||ghi", "|")
...@@ -1471,6 +1485,11 @@ pub const TokenIterator = struct {...@@ -1471,6 +1485,11 @@ pub const TokenIterator = struct {
1471 return self.buffer[index..];1485 return self.buffer[index..];
1472 }1486 }
14731487
1488 /// Resets the iterator to the initial token.
1489 pub fn reset(self: *TokenIterator) void {
1490 self.index = 0;
1491 }
1492
1474 fn isSplitByte(self: TokenIterator, byte: u8) bool {1493 fn isSplitByte(self: TokenIterator, byte: u8) bool {
1475 for (self.delimiter_bytes) |delimiter_byte| {1494 for (self.delimiter_bytes) |delimiter_byte| {
1476 if (byte == delimiter_byte) {1495 if (byte == delimiter_byte) {
lib/std/os.zig+1
...@@ -5610,6 +5610,7 @@ pub fn recvfrom(...@@ -5610,6 +5610,7 @@ pub fn recvfrom(
5610 EAGAIN => return error.WouldBlock,5610 EAGAIN => return error.WouldBlock,
5611 ENOMEM => return error.SystemResources,5611 ENOMEM => return error.SystemResources,
5612 ECONNREFUSED => return error.ConnectionRefused,5612 ECONNREFUSED => return error.ConnectionRefused,
5613 ECONNRESET => return error.ConnectionResetByPeer,
5613 else => |err| return unexpectedErrno(err),5614 else => |err| return unexpectedErrno(err),
5614 }5615 }
5615 }5616 }
lib/std/os/linux/mips.zig+37
...@@ -115,6 +115,9 @@ pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize,...@@ -115,6 +115,9 @@ pub fn syscall5(number: SYS, arg1: usize, arg2: usize, arg3: usize, arg4: usize,
115 );115 );
116}116}
117117
118// NOTE: The o32 calling convention requires the callee to reserve 16 bytes for
119// the first four arguments even though they're passed in $a0-$a3.
120
118pub fn syscall6(121pub fn syscall6(
119 number: SYS,122 number: SYS,
120 arg1: usize,123 arg1: usize,
...@@ -146,6 +149,40 @@ pub fn syscall6(...@@ -146,6 +149,40 @@ pub fn syscall6(
146 );149 );
147}150}
148151
152pub fn syscall7(
153 number: SYS,
154 arg1: usize,
155 arg2: usize,
156 arg3: usize,
157 arg4: usize,
158 arg5: usize,
159 arg6: usize,
160 arg7: usize,
161) usize {
162 return asm volatile (
163 \\ .set noat
164 \\ subu $sp, $sp, 32
165 \\ sw %[arg5], 16($sp)
166 \\ sw %[arg6], 20($sp)
167 \\ sw %[arg7], 24($sp)
168 \\ syscall
169 \\ addu $sp, $sp, 32
170 \\ blez $7, 1f
171 \\ subu $2, $0, $2
172 \\ 1:
173 : [ret] "={$2}" (-> usize)
174 : [number] "{$2}" (@enumToInt(number)),
175 [arg1] "{$4}" (arg1),
176 [arg2] "{$5}" (arg2),
177 [arg3] "{$6}" (arg3),
178 [arg4] "{$7}" (arg4),
179 [arg5] "r" (arg5),
180 [arg6] "r" (arg6),
181 [arg7] "r" (arg7)
182 : "memory", "cc", "$7"
183 );
184}
185
149/// This matches the libc clone function.186/// This matches the libc clone function.
150pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;187pub extern fn clone(func: fn (arg: usize) callconv(.C) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
151188
lib/std/os/uefi/tables/boot_services.zig+2-1
...@@ -78,7 +78,8 @@ pub const BootServices = extern struct {...@@ -78,7 +78,8 @@ pub const BootServices = extern struct {
78 /// Returns an array of handles that support a specified protocol.78 /// Returns an array of handles that support a specified protocol.
79 locateHandle: fn (LocateSearchType, ?*align(8) const Guid, ?*const c_void, *usize, [*]Handle) callconv(.C) Status,79 locateHandle: fn (LocateSearchType, ?*align(8) const Guid, ?*const c_void, *usize, [*]Handle) callconv(.C) Status,
8080
81 locateDevicePath: Status, // TODO81 /// Locates the handle to a device on the device path that supports the specified protocol
82 locateDevicePath: fn (*align(8) const Guid, **const DevicePathProtocol, *?Handle) callconv(.C) Status,
82 installConfigurationTable: Status, // TODO83 installConfigurationTable: Status, // TODO
8384
84 /// Loads an EFI image into memory.85 /// Loads an EFI image into memory.
lib/std/os/windows/user32.zig+1-1
...@@ -373,7 +373,7 @@ pub fn createWindowExA(dwExStyle: u32, lpClassName: [*:0]const u8, lpWindowName:...@@ -373,7 +373,7 @@ pub fn createWindowExA(dwExStyle: u32, lpClassName: [*:0]const u8, lpWindowName:
373}373}
374374
375pub extern "user32" fn CreateWindowExW(dwExStyle: DWORD, lpClassName: [*:0]const u16, lpWindowName: [*:0]const u16, dwStyle: DWORD, X: i32, Y: i32, nWidth: i32, nHeight: i32, hWindParent: ?HWND, hMenu: ?HMENU, hInstance: HINSTANCE, lpParam: ?LPVOID) callconv(WINAPI) ?HWND;375pub extern "user32" fn CreateWindowExW(dwExStyle: DWORD, lpClassName: [*:0]const u16, lpWindowName: [*:0]const u16, dwStyle: DWORD, X: i32, Y: i32, nWidth: i32, nHeight: i32, hWindParent: ?HWND, hMenu: ?HMENU, hInstance: HINSTANCE, lpParam: ?LPVOID) callconv(WINAPI) ?HWND;
376pub var pfnCreateWindowExW: @TypeOf(RegisterClassExW) = undefined;376pub var pfnCreateWindowExW: @TypeOf(CreateWindowExW) = undefined;
377pub fn createWindowExW(dwExStyle: u32, lpClassName: [*:0]const u16, lpWindowName: [*:0]const u16, dwStyle: u32, X: i32, Y: i32, nWidth: i32, nHeight: i32, hWindParent: ?HWND, hMenu: ?HMENU, hInstance: HINSTANCE, lpParam: ?*c_void) !HWND {377pub fn createWindowExW(dwExStyle: u32, lpClassName: [*:0]const u16, lpWindowName: [*:0]const u16, dwStyle: u32, X: i32, Y: i32, nWidth: i32, nHeight: i32, hWindParent: ?HWND, hMenu: ?HMENU, hInstance: HINSTANCE, lpParam: ?*c_void) !HWND {
378 const function = selectSymbol(CreateWindowExW, pfnCreateWindowExW, .win2k);378 const function = selectSymbol(CreateWindowExW, pfnCreateWindowExW, .win2k);
379 const window = function(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWindParent, hMenu, hInstance, lpParam);379 const window = function(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWindParent, hMenu, hInstance, lpParam);
lib/std/special/build_runner.zig+4-4
...@@ -60,6 +60,7 @@ pub fn main() !void {...@@ -60,6 +60,7 @@ pub fn main() !void {
60 const stderr_stream = io.getStdErr().writer();60 const stderr_stream = io.getStdErr().writer();
61 const stdout_stream = io.getStdOut().writer();61 const stdout_stream = io.getStdOut().writer();
6262
63 var install_prefix: ?[]const u8 = null;
63 while (nextArg(args, &arg_idx)) |arg| {64 while (nextArg(args, &arg_idx)) |arg| {
64 if (mem.startsWith(u8, arg, "-D")) {65 if (mem.startsWith(u8, arg, "-D")) {
65 const option_contents = arg[2..];66 const option_contents = arg[2..];
...@@ -82,7 +83,7 @@ pub fn main() !void {...@@ -82,7 +83,7 @@ pub fn main() !void {
82 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {83 } else if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
83 return usage(builder, false, stdout_stream);84 return usage(builder, false, stdout_stream);
84 } else if (mem.eql(u8, arg, "--prefix")) {85 } else if (mem.eql(u8, arg, "--prefix")) {
85 builder.install_prefix = nextArg(args, &arg_idx) orelse {86 install_prefix = nextArg(args, &arg_idx) orelse {
86 warn("Expected argument after --prefix\n\n", .{});87 warn("Expected argument after --prefix\n\n", .{});
87 return usageAndErr(builder, false, stderr_stream);88 return usageAndErr(builder, false, stderr_stream);
88 };89 };
...@@ -134,7 +135,7 @@ pub fn main() !void {...@@ -134,7 +135,7 @@ pub fn main() !void {
134 }135 }
135 }136 }
136137
137 builder.resolveInstallPrefix();138 builder.resolveInstallPrefix(install_prefix);
138 try runBuild(builder);139 try runBuild(builder);
139140
140 if (builder.validateUserInputDidItFail())141 if (builder.validateUserInputDidItFail())
...@@ -162,8 +163,7 @@ fn runBuild(builder: *Builder) anyerror!void {...@@ -162,8 +163,7 @@ fn runBuild(builder: *Builder) anyerror!void {
162fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void {163fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void {
163 // run the build script to collect the options164 // run the build script to collect the options
164 if (!already_ran_build) {165 if (!already_ran_build) {
165 builder.setInstallPrefix(null);166 builder.resolveInstallPrefix(null);
166 builder.resolveInstallPrefix();
167 try runBuild(builder);167 try runBuild(builder);
168 }168 }
169169
lib/std/testing.zig+1-1
...@@ -298,7 +298,7 @@ pub const TmpDir = struct {...@@ -298,7 +298,7 @@ pub const TmpDir = struct {
298 sub_path: [sub_path_len]u8,298 sub_path: [sub_path_len]u8,
299299
300 const random_bytes_count = 12;300 const random_bytes_count = 12;
301 const sub_path_len = std.base64.Base64Encoder.calcSize(random_bytes_count);301 const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);
302302
303 pub fn cleanup(self: *TmpDir) void {303 pub fn cleanup(self: *TmpDir) void {
304 self.dir.close();304 self.dir.close();
src/Compilation.zig+17-6
...@@ -3188,7 +3188,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -3188,7 +3188,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
3188 id_symlink_basename,3188 id_symlink_basename,
3189 &prev_digest_buf,3189 &prev_digest_buf,
3190 ) catch |err| blk: {3190 ) catch |err| blk: {
3191 log.debug("stage1 {s} new_digest={} error: {s}", .{ mod.root_pkg.root_src_path, digest, @errorName(err) });3191 log.debug("stage1 {s} new_digest={s} error: {s}", .{
3192 mod.root_pkg.root_src_path,
3193 std.fmt.fmtSliceHexLower(&digest),
3194 @errorName(err),
3195 });
3192 // Handle this as a cache miss.3196 // Handle this as a cache miss.
3193 break :blk prev_digest_buf[0..0];3197 break :blk prev_digest_buf[0..0];
3194 };3198 };
...@@ -3196,10 +3200,13 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -3196,10 +3200,13 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
3196 if (!mem.eql(u8, prev_digest[0..digest.len], &digest))3200 if (!mem.eql(u8, prev_digest[0..digest.len], &digest))
3197 break :hit;3201 break :hit;
31983202
3199 log.debug("stage1 {s} digest={} match - skipping invocation", .{ mod.root_pkg.root_src_path, digest });3203 log.debug("stage1 {s} digest={s} match - skipping invocation", .{
3204 mod.root_pkg.root_src_path,
3205 std.fmt.fmtSliceHexLower(&digest),
3206 });
3200 var flags_bytes: [1]u8 = undefined;3207 var flags_bytes: [1]u8 = undefined;
3201 _ = std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..]) catch {3208 _ = std.fmt.hexToBytes(&flags_bytes, prev_digest[digest.len..]) catch {
3202 log.warn("bad cache stage1 digest: '{s}'", .{prev_digest});3209 log.warn("bad cache stage1 digest: '{s}'", .{std.fmt.fmtSliceHexLower(prev_digest)});
3203 break :hit;3210 break :hit;
3204 };3211 };
32053212
...@@ -3219,7 +3226,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -3219,7 +3226,11 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
3219 mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]);3226 mod.stage1_flags = @bitCast(@TypeOf(mod.stage1_flags), flags_bytes[0]);
3220 return;3227 return;
3221 }3228 }
3222 log.debug("stage1 {s} prev_digest={} new_digest={}", .{ mod.root_pkg.root_src_path, prev_digest, digest });3229 log.debug("stage1 {s} prev_digest={s} new_digest={s}", .{
3230 mod.root_pkg.root_src_path,
3231 std.fmt.fmtSliceHexLower(prev_digest),
3232 std.fmt.fmtSliceHexLower(&digest),
3233 });
3223 man.unhit(prev_hash_state, input_file_count);3234 man.unhit(prev_hash_state, input_file_count);
3224 }3235 }
32253236
...@@ -3366,8 +3377,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node...@@ -3366,8 +3377,8 @@ fn updateStage1Module(comp: *Compilation, main_progress_node: *std.Progress.Node
3366 // Update the small file with the digest. If it fails we can continue; it only3377 // Update the small file with the digest. If it fails we can continue; it only
3367 // means that the next invocation will have an unnecessary cache miss.3378 // means that the next invocation will have an unnecessary cache miss.
3368 const stage1_flags_byte = @bitCast(u8, mod.stage1_flags);3379 const stage1_flags_byte = @bitCast(u8, mod.stage1_flags);
3369 log.debug("stage1 {s} final digest={} flags={x}", .{3380 log.debug("stage1 {s} final digest={s} flags={x}", .{
3370 mod.root_pkg.root_src_path, digest, stage1_flags_byte,3381 mod.root_pkg.root_src_path, std.fmt.fmtSliceHexLower(&digest), stage1_flags_byte,
3371 });3382 });
3372 var digest_plus_flags: [digest.len + 2]u8 = undefined;3383 var digest_plus_flags: [digest.len + 2]u8 = undefined;
3373 digest_plus_flags[0..digest.len].* = digest;3384 digest_plus_flags[0..digest.len].* = digest;
src/codegen/wasm.zig+41-3
...@@ -94,7 +94,7 @@ pub const Context = struct {...@@ -94,7 +94,7 @@ pub const Context = struct {
94 return switch (ty.tag()) {94 return switch (ty.tag()) {
95 .f32 => wasm.valtype(.f32),95 .f32 => wasm.valtype(.f32),
96 .f64 => wasm.valtype(.f64),96 .f64 => wasm.valtype(.f64),
97 .u32, .i32 => wasm.valtype(.i32),97 .u32, .i32, .bool => wasm.valtype(.i32),
98 .u64, .i64 => wasm.valtype(.i64),98 .u64, .i64 => wasm.valtype(.i64),
99 else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}),99 else => self.fail(src, "TODO - Wasm genValtype for type '{s}'", .{ty.tag()}),
100 };100 };
...@@ -207,6 +207,7 @@ pub const Context = struct {...@@ -207,6 +207,7 @@ pub const Context = struct {
207 .alloc => self.genAlloc(inst.castTag(.alloc).?),207 .alloc => self.genAlloc(inst.castTag(.alloc).?),
208 .arg => self.genArg(inst.castTag(.arg).?),208 .arg => self.genArg(inst.castTag(.arg).?),
209 .block => self.genBlock(inst.castTag(.block).?),209 .block => self.genBlock(inst.castTag(.block).?),
210 .breakpoint => self.genBreakpoint(inst.castTag(.breakpoint).?),
210 .br => self.genBr(inst.castTag(.br).?),211 .br => self.genBr(inst.castTag(.br).?),
211 .call => self.genCall(inst.castTag(.call).?),212 .call => self.genCall(inst.castTag(.call).?),
212 .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),213 .cmp_eq => self.genCmp(inst.castTag(.cmp_eq).?, .eq),
...@@ -220,9 +221,11 @@ pub const Context = struct {...@@ -220,9 +221,11 @@ pub const Context = struct {
220 .dbg_stmt => WValue.none,221 .dbg_stmt => WValue.none,
221 .load => self.genLoad(inst.castTag(.load).?),222 .load => self.genLoad(inst.castTag(.load).?),
222 .loop => self.genLoop(inst.castTag(.loop).?),223 .loop => self.genLoop(inst.castTag(.loop).?),
224 .not => self.genNot(inst.castTag(.not).?),
223 .ret => self.genRet(inst.castTag(.ret).?),225 .ret => self.genRet(inst.castTag(.ret).?),
224 .retvoid => WValue.none,226 .retvoid => WValue.none,
225 .store => self.genStore(inst.castTag(.store).?),227 .store => self.genStore(inst.castTag(.store).?),
228 .unreach => self.genUnreachable(inst.castTag(.unreach).?),
226 else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}),229 else => self.fail(inst.src, "TODO: Implement wasm inst: {s}", .{inst.tag}),
227 };230 };
228 }231 }
...@@ -328,7 +331,7 @@ pub const Context = struct {...@@ -328,7 +331,7 @@ pub const Context = struct {
328 try writer.writeByte(wasm.opcode(.i32_const));331 try writer.writeByte(wasm.opcode(.i32_const));
329 try leb.writeILEB128(writer, inst.val.toUnsignedInt());332 try leb.writeILEB128(writer, inst.val.toUnsignedInt());
330 },333 },
331 .i32 => {334 .i32, .bool => {
332 try writer.writeByte(wasm.opcode(.i32_const));335 try writer.writeByte(wasm.opcode(.i32_const));
333 try leb.writeILEB128(writer, inst.val.toSignedInt());336 try leb.writeILEB128(writer, inst.val.toSignedInt());
334 },337 },
...@@ -413,7 +416,14 @@ pub const Context = struct {...@@ -413,7 +416,14 @@ pub const Context = struct {
413416
414 // insert blocks at the position of `offset` so417 // insert blocks at the position of `offset` so
415 // the condition can jump to it418 // the condition can jump to it
416 const offset = condition.code_offset;419 const offset = switch (condition) {
420 .code_offset => |offset| offset,
421 else => blk: {
422 const offset = self.code.items.len;
423 try self.emitWValue(condition);
424 break :blk offset;
425 },
426 };
417 const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty);427 const block_ty = try self.genBlockType(condbr.base.src, condbr.base.ty);
418 try self.startBlock(.block, block_ty, offset);428 try self.startBlock(.block, block_ty, offset);
419429
...@@ -522,4 +532,32 @@ pub const Context = struct {...@@ -522,4 +532,32 @@ pub const Context = struct {
522532
523 return .none;533 return .none;
524 }534 }
535
536 fn genNot(self: *Context, not: *Inst.UnOp) InnerError!WValue {
537 const offset = self.code.items.len;
538
539 const operand = self.resolveInst(not.operand);
540 try self.emitWValue(operand);
541
542 // wasm does not have booleans nor the `not` instruction, therefore compare with 0
543 // to create the same logic
544 const writer = self.code.writer();
545 try writer.writeByte(wasm.opcode(.i32_const));
546 try leb.writeILEB128(writer, @as(i32, 0));
547
548 try writer.writeByte(wasm.opcode(.i32_eq));
549
550 return WValue{ .code_offset = offset };
551 }
552
553 fn genBreakpoint(self: *Context, breakpoint: *Inst.NoOp) InnerError!WValue {
554 // unsupported by wasm itself. Can be implemented once we support DWARF
555 // for wasm
556 return .none;
557 }
558
559 fn genUnreachable(self: *Context, unreach: *Inst.NoOp) InnerError!WValue {
560 try self.code.append(wasm.opcode(.@"unreachable"));
561 return .none;
562 }
525};563};
src/config.zig.in+1-1
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1pub const have_llvm = true;1pub const have_llvm = true;
2pub const version: [:0]const u8 = "@ZIG_VERSION@";2pub const version: [:0]const u8 = "@ZIG_VERSION@";
3pub const semver = try @import("std").SemanticVersion.parse(version);3pub const semver = try @import("std").SemanticVersion.parse(version);
4pub const enable_logging: bool = false;4pub const enable_logging: bool = @ZIG_ENABLE_LOGGING_BOOL@;
5pub const enable_tracy = false;5pub const enable_tracy = false;
6pub const is_stage1 = true;6pub const is_stage1 = true;
7pub const skip_non_native = false;7pub const skip_non_native = false;
src/introspect.zig+8
...@@ -61,6 +61,14 @@ pub fn findZigLibDirFromSelfExe(...@@ -61,6 +61,14 @@ pub fn findZigLibDirFromSelfExe(
6161
62/// Caller owns returned memory.62/// Caller owns returned memory.
63pub fn resolveGlobalCacheDir(allocator: *mem.Allocator) ![]u8 {63pub fn resolveGlobalCacheDir(allocator: *mem.Allocator) ![]u8 {
64 if (std.process.getEnvVarOwned(allocator, "ZIG_GLOBAL_CACHE_DIR")) |value| {
65 if (value.len > 0) {
66 return value;
67 } else {
68 allocator.free(value);
69 }
70 } else |_| {}
71
64 const appname = "zig";72 const appname = "zig";
6573
66 if (std.Target.current.os.tag != .windows) {74 if (std.Target.current.os.tag != .windows) {
src/link/MachO/Archive.zig+25-3
...@@ -20,6 +20,11 @@ name: []u8,...@@ -20,6 +20,11 @@ name: []u8,
2020
21objects: std.ArrayListUnmanaged(Object) = .{},21objects: std.ArrayListUnmanaged(Object) = .{},
2222
23/// Parsed table of contents.
24/// Each symbol name points to a list of all definition
25/// sites within the current static archive.
26toc: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(u32)) = .{},
27
23// Archive files start with the ARMAG identifying string. Then follows a28// Archive files start with the ARMAG identifying string. Then follows a
24// `struct ar_hdr', and as many bytes of member file data as its `ar_size'29// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
25// member indicates, for each member file.30// member indicates, for each member file.
...@@ -88,6 +93,11 @@ pub fn deinit(self: *Archive) void {...@@ -88,6 +93,11 @@ pub fn deinit(self: *Archive) void {
88 object.deinit();93 object.deinit();
89 }94 }
90 self.objects.deinit(self.allocator);95 self.objects.deinit(self.allocator);
96 for (self.toc.items()) |*entry| {
97 self.allocator.free(entry.key);
98 entry.value.deinit(self.allocator);
99 }
100 self.toc.deinit(self.allocator);
91 self.file.close();101 self.file.close();
92}102}
93103
...@@ -159,8 +169,20 @@ fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {...@@ -159,8 +169,20 @@ fn readTableOfContents(self: *Archive, reader: anytype) ![]u32 {
159 };169 };
160 const object_offset = try symtab_reader.readIntLittle(u32);170 const object_offset = try symtab_reader.readIntLittle(u32);
161171
162 // TODO Store the table of contents for later reuse.172 const sym_name = mem.spanZ(@ptrCast([*:0]const u8, strtab.ptr + n_strx));
173 const owned_name = try self.allocator.dupe(u8, sym_name);
174 const res = try self.toc.getOrPut(self.allocator, owned_name);
175 defer if (res.found_existing) self.allocator.free(owned_name);
176
177 if (!res.found_existing) {
178 res.entry.value = .{};
179 }
180
181 try res.entry.value.append(self.allocator, object_offset);
163182
183 // TODO This will go once we properly use archive's TOC to pick
184 // an object which defines a missing symbol rather than pasting in
185 // all of the objects always.
164 // Here, we assume that symbols are NOT sorted in any way, and186 // Here, we assume that symbols are NOT sorted in any way, and
165 // they point to objects in sequence.187 // they point to objects in sequence.
166 if (object_offsets.items[last] != object_offset) {188 if (object_offsets.items[last] != object_offset) {
...@@ -248,8 +270,8 @@ fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 {...@@ -248,8 +270,8 @@ fn getName(allocator: *Allocator, header: ar_hdr, reader: anytype) ![]u8 {
248 var n = try allocator.alloc(u8, len);270 var n = try allocator.alloc(u8, len);
249 defer allocator.free(n);271 defer allocator.free(n);
250 try reader.readNoEof(n);272 try reader.readNoEof(n);
251 const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0));273 const actual_len = mem.indexOfScalar(u8, n, @as(u8, 0)) orelse n.len;
252 name = try allocator.dupe(u8, n[0..actual_len.?]);274 name = try allocator.dupe(u8, n[0..actual_len]);
253 },275 },
254 }276 }
255 return name;277 return name;
src/link/MachO/Object.zig+1
...@@ -164,6 +164,7 @@ pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !voi...@@ -164,6 +164,7 @@ pub fn readLoadCommands(self: *Object, reader: anytype, offset: ReadOffset) !voi
164 },164 },
165 macho.LC_DATA_IN_CODE => {165 macho.LC_DATA_IN_CODE => {
166 self.data_in_code_cmd_index = i;166 self.data_in_code_cmd_index = i;
167 cmd.LinkeditData.dataoff += offset_mod;
167 },168 },
168 else => {169 else => {
169 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});170 log.debug("Unknown load command detected: 0x{x}.", .{cmd.cmd()});
src/link/MachO/Zld.zig+105-3
...@@ -60,13 +60,17 @@ stub_helper_section_index: ?u16 = null,...@@ -60,13 +60,17 @@ stub_helper_section_index: ?u16 = null,
60text_const_section_index: ?u16 = null,60text_const_section_index: ?u16 = null,
61cstring_section_index: ?u16 = null,61cstring_section_index: ?u16 = null,
6262
63// __DATA segment sections63// __DATA_CONST segment sections
64got_section_index: ?u16 = null,64got_section_index: ?u16 = null,
65mod_init_func_section_index: ?u16 = null,
66mod_term_func_section_index: ?u16 = null,
67data_const_section_index: ?u16 = null,
68
69// __DATA segment sections
65tlv_section_index: ?u16 = null,70tlv_section_index: ?u16 = null,
66tlv_data_section_index: ?u16 = null,71tlv_data_section_index: ?u16 = null,
67tlv_bss_section_index: ?u16 = null,72tlv_bss_section_index: ?u16 = null,
68la_symbol_ptr_section_index: ?u16 = null,73la_symbol_ptr_section_index: ?u16 = null,
69data_const_section_index: ?u16 = null,
70data_section_index: ?u16 = null,74data_section_index: ?u16 = null,
71bss_section_index: ?u16 = null,75bss_section_index: ?u16 = null,
7276
...@@ -448,6 +452,46 @@ fn updateMetadata(self: *Zld, object_id: u16) !void {...@@ -448,6 +452,46 @@ fn updateMetadata(self: *Zld, object_id: u16) !void {
448 .reserved3 = 0,452 .reserved3 = 0,
449 });453 });
450 },454 },
455 macho.S_MOD_INIT_FUNC_POINTERS => {
456 if (!mem.eql(u8, segname, "__DATA")) continue;
457 if (self.mod_init_func_section_index != null) continue;
458
459 self.mod_init_func_section_index = @intCast(u16, data_const_seg.sections.items.len);
460 try data_const_seg.addSection(self.allocator, .{
461 .sectname = makeStaticString("__mod_init_func"),
462 .segname = makeStaticString("__DATA_CONST"),
463 .addr = 0,
464 .size = 0,
465 .offset = 0,
466 .@"align" = 0,
467 .reloff = 0,
468 .nreloc = 0,
469 .flags = macho.S_MOD_INIT_FUNC_POINTERS,
470 .reserved1 = 0,
471 .reserved2 = 0,
472 .reserved3 = 0,
473 });
474 },
475 macho.S_MOD_TERM_FUNC_POINTERS => {
476 if (!mem.eql(u8, segname, "__DATA")) continue;
477 if (self.mod_term_func_section_index != null) continue;
478
479 self.mod_term_func_section_index = @intCast(u16, data_const_seg.sections.items.len);
480 try data_const_seg.addSection(self.allocator, .{
481 .sectname = makeStaticString("__mod_term_func"),
482 .segname = makeStaticString("__DATA_CONST"),
483 .addr = 0,
484 .size = 0,
485 .offset = 0,
486 .@"align" = 0,
487 .reloff = 0,
488 .nreloc = 0,
489 .flags = macho.S_MOD_TERM_FUNC_POINTERS,
490 .reserved1 = 0,
491 .reserved2 = 0,
492 .reserved3 = 0,
493 });
494 },
451 macho.S_ZEROFILL => {495 macho.S_ZEROFILL => {
452 if (!mem.eql(u8, segname, "__DATA")) continue;496 if (!mem.eql(u8, segname, "__DATA")) continue;
453 if (self.bss_section_index != null) continue;497 if (self.bss_section_index != null) continue;
...@@ -583,6 +627,18 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection {...@@ -583,6 +627,18 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection {
583 .sect = self.cstring_section_index.?,627 .sect = self.cstring_section_index.?,
584 };628 };
585 },629 },
630 macho.S_MOD_INIT_FUNC_POINTERS => {
631 break :blk .{
632 .seg = self.data_const_segment_cmd_index.?,
633 .sect = self.mod_init_func_section_index.?,
634 };
635 },
636 macho.S_MOD_TERM_FUNC_POINTERS => {
637 break :blk .{
638 .seg = self.data_const_segment_cmd_index.?,
639 .sect = self.mod_term_func_section_index.?,
640 };
641 },
586 macho.S_ZEROFILL => {642 macho.S_ZEROFILL => {
587 break :blk .{643 break :blk .{
588 .seg = self.data_segment_cmd_index.?,644 .seg = self.data_segment_cmd_index.?,
...@@ -684,6 +740,8 @@ fn sortSections(self: *Zld) !void {...@@ -684,6 +740,8 @@ fn sortSections(self: *Zld) !void {
684740
685 const indices = &[_]*?u16{741 const indices = &[_]*?u16{
686 &self.got_section_index,742 &self.got_section_index,
743 &self.mod_init_func_section_index,
744 &self.mod_term_func_section_index,
687 &self.data_const_section_index,745 &self.data_const_section_index,
688 };746 };
689 for (indices) |maybe_index| {747 for (indices) |maybe_index| {
...@@ -2471,6 +2529,42 @@ fn writeRebaseInfoTable(self: *Zld) !void {...@@ -2471,6 +2529,42 @@ fn writeRebaseInfoTable(self: *Zld) !void {
2471 }2529 }
2472 }2530 }
24732531
2532 if (self.mod_init_func_section_index) |idx| {
2533 // TODO audit and investigate this.
2534 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2535 const sect = seg.sections.items[idx];
2536 const npointers = sect.size * @sizeOf(u64);
2537 const base_offset = sect.addr - seg.inner.vmaddr;
2538 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
2539
2540 try pointers.ensureCapacity(pointers.items.len + npointers);
2541 var i: usize = 0;
2542 while (i < npointers) : (i += 1) {
2543 pointers.appendAssumeCapacity(.{
2544 .offset = base_offset + i * @sizeOf(u64),
2545 .segment_id = segment_id,
2546 });
2547 }
2548 }
2549
2550 if (self.mod_term_func_section_index) |idx| {
2551 // TODO audit and investigate this.
2552 const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment;
2553 const sect = seg.sections.items[idx];
2554 const npointers = sect.size * @sizeOf(u64);
2555 const base_offset = sect.addr - seg.inner.vmaddr;
2556 const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?);
2557
2558 try pointers.ensureCapacity(pointers.items.len + npointers);
2559 var i: usize = 0;
2560 while (i < npointers) : (i += 1) {
2561 pointers.appendAssumeCapacity(.{
2562 .offset = base_offset + i * @sizeOf(u64),
2563 .segment_id = segment_id,
2564 });
2565 }
2566 }
2567
2474 if (self.la_symbol_ptr_section_index) |idx| {2568 if (self.la_symbol_ptr_section_index) |idx| {
2475 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len);2569 try pointers.ensureCapacity(pointers.items.len + self.lazy_imports.items().len);
2476 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;2570 const seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
...@@ -2707,7 +2801,15 @@ fn writeDebugInfo(self: *Zld) !void {...@@ -2707,7 +2801,15 @@ fn writeDebugInfo(self: *Zld) !void {
2707 };2801 };
2708 defer debug_info.deinit(self.allocator);2802 defer debug_info.deinit(self.allocator);
27092803
2710 const compile_unit = try debug_info.inner.findCompileUnit(0x0); // We assume there is only one CU.2804 // We assume there is only one CU.
2805 const compile_unit = debug_info.inner.findCompileUnit(0x0) catch |err| switch (err) {
2806 error.MissingDebugInfo => {
2807 // TODO audit cases with missing debug info and audit our dwarf.zig module.
2808 log.debug("invalid or missing debug info in {s}; skipping", .{object.name});
2809 continue;
2810 },
2811 else => |e| return e,
2812 };
2711 const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name);2813 const name = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_name);
2712 const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir);2814 const comp_dir = try compile_unit.die.getAttrString(&debug_info.inner, dwarf.AT_comp_dir);
27132815
src/main.zig+1-1
...@@ -557,7 +557,7 @@ fn buildOutputType(...@@ -557,7 +557,7 @@ fn buildOutputType(
557 var test_filter: ?[]const u8 = null;557 var test_filter: ?[]const u8 = null;
558 var test_name_prefix: ?[]const u8 = null;558 var test_name_prefix: ?[]const u8 = null;
559 var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR");559 var override_local_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LOCAL_CACHE_DIR");
560 var override_global_cache_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_GLOBAL_CACHE_DIR");560 var override_global_cache_dir: ?[]const u8 = null;
561 var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR");561 var override_lib_dir: ?[]const u8 = try optionalStringEnvVar(arena, "ZIG_LIB_DIR");
562 var main_pkg_path: ?[]const u8 = null;562 var main_pkg_path: ?[]const u8 = null;
563 var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;563 var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no;
src/stage1/all_types.hpp-4
...@@ -2139,10 +2139,6 @@ struct CodeGen {...@@ -2139,10 +2139,6 @@ struct CodeGen {
2139 Buf llvm_ir_file_output_path;2139 Buf llvm_ir_file_output_path;
2140 Buf analysis_json_output_path;2140 Buf analysis_json_output_path;
2141 Buf docs_output_path;2141 Buf docs_output_path;
2142 Buf *cache_dir;
2143 Buf *c_artifact_dir;
2144 const char **libc_include_dir_list;
2145 size_t libc_include_dir_len;
21462142
2147 Buf *builtin_zig_path;2143 Buf *builtin_zig_path;
2148 Buf *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir.2144 Buf *zig_std_special_dir; // Cannot be overridden; derived from zig_lib_dir.
src/translate_c.zig+137-60
...@@ -270,7 +270,10 @@ pub const Context = struct {...@@ -270,7 +270,10 @@ pub const Context = struct {
270 global_scope: *Scope.Root,270 global_scope: *Scope.Root,
271 clang_context: *clang.ASTContext,271 clang_context: *clang.ASTContext,
272 mangle_count: u32 = 0,272 mangle_count: u32 = 0,
273 /// Table of record decls that have been demoted to opaques.
273 opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{},274 opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{},
275 /// Table of unnamed enums and records that are child types of typedefs.
276 unnamed_typedefs: std.AutoHashMapUnmanaged(usize, []const u8) = .{},
274277
275 /// This one is different than the root scope's name table. This contains278 /// This one is different than the root scope's name table. This contains
276 /// a list of names that we found by visiting all the top level decls without279 /// a list of names that we found by visiting all the top level decls without
...@@ -338,6 +341,7 @@ pub fn translate(...@@ -338,6 +341,7 @@ pub fn translate(
338 context.alias_list.deinit();341 context.alias_list.deinit();
339 context.global_names.deinit(gpa);342 context.global_names.deinit(gpa);
340 context.opaque_demotes.deinit(gpa);343 context.opaque_demotes.deinit(gpa);
344 context.unnamed_typedefs.deinit(gpa);
341 context.global_scope.deinit();345 context.global_scope.deinit();
342 }346 }
343347
...@@ -401,6 +405,51 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void {...@@ -401,6 +405,51 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void {
401 if (decl.castToNamedDecl()) |named_decl| {405 if (decl.castToNamedDecl()) |named_decl| {
402 const decl_name = try c.str(named_decl.getName_bytes_begin());406 const decl_name = try c.str(named_decl.getName_bytes_begin());
403 try c.global_names.put(c.gpa, decl_name, {});407 try c.global_names.put(c.gpa, decl_name, {});
408
409 // Check for typedefs with unnamed enum/record child types.
410 if (decl.getKind() == .Typedef) {
411 const typedef_decl = @ptrCast(*const clang.TypedefNameDecl, decl);
412 var child_ty = typedef_decl.getUnderlyingType().getTypePtr();
413 const addr: usize = while (true) switch (child_ty.getTypeClass()) {
414 .Enum => {
415 const enum_ty = @ptrCast(*const clang.EnumType, child_ty);
416 const enum_decl = enum_ty.getDecl();
417 // check if this decl is unnamed
418 if (@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin()[0] != 0) return;
419 break @ptrToInt(enum_decl.getCanonicalDecl());
420 },
421 .Record => {
422 const record_ty = @ptrCast(*const clang.RecordType, child_ty);
423 const record_decl = record_ty.getDecl();
424 // check if this decl is unnamed
425 if (@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin()[0] != 0) return;
426 break @ptrToInt(record_decl.getCanonicalDecl());
427 },
428 .Elaborated => {
429 const elaborated_ty = @ptrCast(*const clang.ElaboratedType, child_ty);
430 child_ty = elaborated_ty.getNamedType().getTypePtr();
431 },
432 .Decayed => {
433 const decayed_ty = @ptrCast(*const clang.DecayedType, child_ty);
434 child_ty = decayed_ty.getDecayedType().getTypePtr();
435 },
436 .Attributed => {
437 const attributed_ty = @ptrCast(*const clang.AttributedType, child_ty);
438 child_ty = attributed_ty.getEquivalentType().getTypePtr();
439 },
440 .MacroQualified => {
441 const macroqualified_ty = @ptrCast(*const clang.MacroQualifiedType, child_ty);
442 child_ty = macroqualified_ty.getModifiedType().getTypePtr();
443 },
444 else => return,
445 } else unreachable;
446 // TODO https://github.com/ziglang/zig/issues/3756
447 // TODO https://github.com/ziglang/zig/issues/1802
448 const name = if (isZigPrimitiveType(decl_name)) try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ decl_name, c.getMangle() }) else decl_name;
449 try c.unnamed_typedefs.putNoClobber(c.gpa, addr, name);
450 // Put this typedef in the decl_table to avoid redefinitions.
451 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(typedef_decl.getCanonicalDecl()), name);
452 }
404 }453 }
405}454}
406455
...@@ -752,17 +801,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD...@@ -752,17 +801,10 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
752 const toplevel = scope.id == .root;801 const toplevel = scope.id == .root;
753 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;802 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
754803
755 var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin());
756 var is_unnamed = false;
757 // Record declarations such as `struct {...} x` have no name but they're not
758 // anonymous hence here isAnonymousStructOrUnion is not needed
759 if (bare_name.len == 0) {
760 bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
761 is_unnamed = true;
762 }
763
764 var container_kind_name: []const u8 = undefined;
765 var is_union = false;804 var is_union = false;
805 var container_kind_name: []const u8 = undefined;
806 var bare_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, record_decl).getName_bytes_begin());
807
766 if (record_decl.isUnion()) {808 if (record_decl.isUnion()) {
767 container_kind_name = "union";809 container_kind_name = "union";
768 is_union = true;810 is_union = true;
...@@ -773,7 +815,20 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD...@@ -773,7 +815,20 @@ fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordD
773 return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name});815 return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name});
774 }816 }
775817
776 var name: []const u8 = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name });818 var is_unnamed = false;
819 var name = bare_name;
820 if (c.unnamed_typedefs.get(@ptrToInt(record_decl.getCanonicalDecl()))) |typedef_name| {
821 bare_name = typedef_name;
822 name = typedef_name;
823 } else {
824 // Record declarations such as `struct {...} x` have no name but they're not
825 // anonymous hence here isAnonymousStructOrUnion is not needed
826 if (bare_name.len == 0) {
827 bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
828 is_unnamed = true;
829 }
830 name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name });
831 }
777 if (!toplevel) name = try bs.makeMangledName(c, name);832 if (!toplevel) name = try bs.makeMangledName(c, name);
778 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name);833 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(record_decl.getCanonicalDecl()), name);
779834
...@@ -874,14 +929,19 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) E...@@ -874,14 +929,19 @@ fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) E
874 const toplevel = scope.id == .root;929 const toplevel = scope.id == .root;
875 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;930 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
876931
877 var bare_name = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin());
878 var is_unnamed = false;932 var is_unnamed = false;
879 if (bare_name.len == 0) {933 var bare_name: []const u8 = try c.str(@ptrCast(*const clang.NamedDecl, enum_decl).getName_bytes_begin());
880 bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});934 var name = bare_name;
881 is_unnamed = true;935 if (c.unnamed_typedefs.get(@ptrToInt(enum_decl.getCanonicalDecl()))) |typedef_name| {
936 bare_name = typedef_name;
937 name = typedef_name;
938 } else {
939 if (bare_name.len == 0) {
940 bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
941 is_unnamed = true;
942 }
943 name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
882 }944 }
883
884 var name: []const u8 = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
885 if (!toplevel) _ = try bs.makeMangledName(c, name);945 if (!toplevel) _ = try bs.makeMangledName(c, name);
886 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name);946 try c.decl_table.putNoClobber(c.gpa, @ptrToInt(enum_decl.getCanonicalDecl()), name);
887947
...@@ -1063,6 +1123,7 @@ fn transStmt(...@@ -1063,6 +1123,7 @@ fn transStmt(
1063 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt);1123 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt);
1064 return transExpr(c, scope, gen_sel.getResultExpr(), result_used);1124 return transExpr(c, scope, gen_sel.getResultExpr(), result_used);
1065 },1125 },
1126 // When adding new cases here, see comment for maybeBlockify()
1066 else => {1127 else => {
1067 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});1128 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});
1068 },1129 },
...@@ -2242,6 +2303,35 @@ fn transImplicitValueInitExpr(...@@ -2242,6 +2303,35 @@ fn transImplicitValueInitExpr(
2242 return transZeroInitExpr(c, scope, source_loc, ty);2303 return transZeroInitExpr(c, scope, source_loc, ty);
2243}2304}
22442305
2306/// If a statement can possibly translate to a Zig assignment (either directly because it's
2307/// an assignment in C or indirectly via result assignment to `_`) AND it's the sole statement
2308/// in the body of an if statement or loop, then we need to put the statement into its own block.
2309/// The `else` case here corresponds to statements that could result in an assignment. If a statement
2310/// class never needs a block, add its enum to the top prong.
2311fn maybeBlockify(c: *Context, scope: *Scope, stmt: *const clang.Stmt) TransError!Node {
2312 switch (stmt.getStmtClass()) {
2313 .BreakStmtClass,
2314 .CompoundStmtClass,
2315 .ContinueStmtClass,
2316 .DeclRefExprClass,
2317 .DeclStmtClass,
2318 .DoStmtClass,
2319 .ForStmtClass,
2320 .IfStmtClass,
2321 .ReturnStmtClass,
2322 .NullStmtClass,
2323 .WhileStmtClass,
2324 => return transStmt(c, scope, stmt, .unused),
2325 else => {
2326 var block_scope = try Scope.Block.init(c, scope, false);
2327 defer block_scope.deinit();
2328 const result = try transStmt(c, &block_scope.base, stmt, .unused);
2329 try block_scope.statements.append(result);
2330 return block_scope.complete(c);
2331 },
2332 }
2333}
2334
2245fn transIfStmt(2335fn transIfStmt(
2246 c: *Context,2336 c: *Context,
2247 scope: *Scope,2337 scope: *Scope,
...@@ -2259,9 +2349,10 @@ fn transIfStmt(...@@ -2259,9 +2349,10 @@ fn transIfStmt(
2259 const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond());2349 const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond());
2260 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);2350 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);
22612351
2262 const then_body = try transStmt(c, scope, stmt.getThen(), .unused);2352 const then_body = try maybeBlockify(c, scope, stmt.getThen());
2353
2263 const else_body = if (stmt.getElse()) |expr|2354 const else_body = if (stmt.getElse()) |expr|
2264 try transStmt(c, scope, expr, .unused)2355 try maybeBlockify(c, scope, expr)
2265 else2356 else
2266 null;2357 null;
2267 return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body });2358 return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body });
...@@ -2286,7 +2377,7 @@ fn transWhileLoop(...@@ -2286,7 +2377,7 @@ fn transWhileLoop(
2286 .parent = scope,2377 .parent = scope,
2287 .id = .loop,2378 .id = .loop,
2288 };2379 };
2289 const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused);2380 const body = try maybeBlockify(c, &loop_scope, stmt.getBody());
2290 return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null });2381 return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null });
2291}2382}
22922383
...@@ -2312,7 +2403,7 @@ fn transDoWhileLoop(...@@ -2312,7 +2403,7 @@ fn transDoWhileLoop(
2312 const if_not_break = switch (cond.tag()) {2403 const if_not_break = switch (cond.tag()) {
2313 .false_literal => return transStmt(c, scope, stmt.getBody(), .unused),2404 .false_literal => return transStmt(c, scope, stmt.getBody(), .unused),
2314 .true_literal => {2405 .true_literal => {
2315 const body_node = try transStmt(c, scope, stmt.getBody(), .unused);2406 const body_node = try maybeBlockify(c, scope, stmt.getBody());
2316 return Tag.while_true.create(c.arena, body_node);2407 return Tag.while_true.create(c.arena, body_node);
2317 },2408 },
2318 else => try Tag.if_not_break.create(c.arena, cond),2409 else => try Tag.if_not_break.create(c.arena, cond),
...@@ -2388,7 +2479,7 @@ fn transForLoop(...@@ -2388,7 +2479,7 @@ fn transForLoop(
2388 else2479 else
2389 null;2480 null;
23902481
2391 const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused);2482 const body = try maybeBlockify(c, &loop_scope, stmt.getBody());
2392 const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr });2483 const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr });
2393 if (block_scope) |*bs| {2484 if (block_scope) |*bs| {
2394 try bs.statements.append(while_node);2485 try bs.statements.append(while_node);
...@@ -3106,43 +3197,34 @@ fn transCreateCompoundAssign(...@@ -3106,43 +3197,34 @@ fn transCreateCompoundAssign(
3106 const requires_int_cast = blk: {3197 const requires_int_cast = blk: {
3107 const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt);3198 const are_integers = cIsInteger(lhs_qt) and cIsInteger(rhs_qt);
3108 const are_same_sign = cIsSignedInteger(lhs_qt) == cIsSignedInteger(rhs_qt);3199 const are_same_sign = cIsSignedInteger(lhs_qt) == cIsSignedInteger(rhs_qt);
3109 break :blk are_integers and !are_same_sign;3200 break :blk are_integers and !(are_same_sign and cIntTypeCmp(lhs_qt, rhs_qt) == .eq);
3110 };3201 };
3202
3111 if (used == .unused) {3203 if (used == .unused) {
3112 // common case3204 // common case
3113 // c: lhs += rhs3205 // c: lhs += rhs
3114 // zig: lhs += rhs3206 // zig: lhs += rhs
3207 const lhs_node = try transExpr(c, scope, lhs, .used);
3208 var rhs_node = try transExpr(c, scope, rhs, .used);
3209 if (is_ptr_op_signed) rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
3210
3115 if ((is_mod or is_div) and is_signed) {3211 if ((is_mod or is_div) and is_signed) {
3116 const lhs_node = try transExpr(c, scope, lhs, .used);3212 if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
3117 const rhs_node = try transExpr(c, scope, rhs, .used);3213 const operands = .{ .lhs = lhs_node, .rhs = rhs_node };
3118 const builtin = if (is_mod)3214 const builtin = if (is_mod)
3119 try Tag.rem.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node })3215 try Tag.rem.create(c.arena, operands)
3120 else3216 else
3121 try Tag.div_trunc.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node });3217 try Tag.div_trunc.create(c.arena, operands);
31223218
3123 return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used);3219 return transCreateNodeInfixOp(c, scope, .assign, lhs_node, builtin, .used);
3124 }3220 }
31253221
3126 const lhs_node = try transExpr(c, scope, lhs, .used);3222 if (is_shift) {
3127 var rhs_node = if (is_shift or requires_int_cast)3223 const cast_to_type = try qualTypeToLog2IntRef(c, scope, rhs_qt, loc);
3128 try transExprCoercing(c, scope, rhs, .used)
3129 else
3130 try transExpr(c, scope, rhs, .used);
3131
3132 if (is_ptr_op_signed) {
3133 rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
3134 }
3135
3136 if (is_shift or requires_int_cast) {
3137 // @intCast(rhs)
3138 const cast_to_type = if (is_shift)
3139 try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc)
3140 else
3141 try transQualType(c, scope, getExprQualType(c, lhs), loc);
3142
3143 rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });3224 rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });
3225 } else if (requires_int_cast) {
3226 rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
3144 }3227 }
3145
3146 return transCreateNodeInfixOp(c, scope, op, lhs_node, rhs_node, .used);3228 return transCreateNodeInfixOp(c, scope, op, lhs_node, rhs_node, .used);
3147 }3229 }
3148 // worst case3230 // worst case
...@@ -3164,29 +3246,24 @@ fn transCreateCompoundAssign(...@@ -3164,29 +3246,24 @@ fn transCreateCompoundAssign(
3164 const lhs_node = try Tag.identifier.create(c.arena, ref);3246 const lhs_node = try Tag.identifier.create(c.arena, ref);
3165 const ref_node = try Tag.deref.create(c.arena, lhs_node);3247 const ref_node = try Tag.deref.create(c.arena, lhs_node);
31663248
3249 var rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
3250 if (is_ptr_op_signed) rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
3167 if ((is_mod or is_div) and is_signed) {3251 if ((is_mod or is_div) and is_signed) {
3168 const rhs_node = try transExpr(c, &block_scope.base, rhs, .used);3252 if (requires_int_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
3253 const operands = .{ .lhs = ref_node, .rhs = rhs_node };
3169 const builtin = if (is_mod)3254 const builtin = if (is_mod)
3170 try Tag.rem.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node })3255 try Tag.rem.create(c.arena, operands)
3171 else3256 else
3172 try Tag.div_trunc.create(c.arena, .{ .lhs = ref_node, .rhs = rhs_node });3257 try Tag.div_trunc.create(c.arena, operands);
31733258
3174 const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, ref_node, builtin, .used);3259 const assign = try transCreateNodeInfixOp(c, &block_scope.base, .assign, ref_node, builtin, .used);
3175 try block_scope.statements.append(assign);3260 try block_scope.statements.append(assign);
3176 } else {3261 } else {
3177 var rhs_node = try transExpr(c, &block_scope.base, rhs, .used);3262 if (is_shift) {
31783263 const cast_to_type = try qualTypeToLog2IntRef(c, &block_scope.base, rhs_qt, loc);
3179 if (is_shift or requires_int_cast) {
3180 // @intCast(rhs)
3181 const cast_to_type = if (is_shift)
3182 try qualTypeToLog2IntRef(c, scope, getExprQualType(c, rhs), loc)
3183 else
3184 try transQualType(c, scope, getExprQualType(c, lhs), loc);
3185
3186 rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });3264 rhs_node = try Tag.int_cast.create(c.arena, .{ .lhs = cast_to_type, .rhs = rhs_node });
3187 }3265 } else if (requires_int_cast) {
3188 if (is_ptr_op_signed) {3266 rhs_node = try transCCast(c, &block_scope.base, loc, lhs_qt, rhs_qt, rhs_node);
3189 rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
3190 }3267 }
31913268
3192 const assign = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, rhs_node, .used);3269 const assign = try transCreateNodeInfixOp(c, &block_scope.base, op, ref_node, rhs_node, .used);
test/run_translated_c.zig+64
...@@ -1244,4 +1244,68 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -1244,4 +1244,68 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
1244 \\ return 0;1244 \\ return 0;
1245 \\}1245 \\}
1246 , "");1246 , "");
1247
1248 cases.add("convert single-statement bodies into blocks for if/else/for/while. issue #8159",
1249 \\#include <stdlib.h>
1250 \\int foo() { return 1; }
1251 \\int main(void) {
1252 \\ int i = 0;
1253 \\ if (i == 0) if (i == 0) if (i != 0) i = 1;
1254 \\ if (i != 0) i = 1; else if (i == 0) if (i == 0) i += 1;
1255 \\ for (; i < 10;) for (; i < 10;) i++;
1256 \\ while (i == 100) while (i == 100) foo();
1257 \\ if (0) do do "string"; while(1); while(1);
1258 \\ return 0;
1259 \\}
1260 , "");
1261
1262 cases.add("cast RHS of compound assignment if necessary, unused result",
1263 \\#include <stdlib.h>
1264 \\int main(void) {
1265 \\ signed short val = -1;
1266 \\ val += 1; if (val != 0) abort();
1267 \\ val -= 1; if (val != -1) abort();
1268 \\ val *= 2; if (val != -2) abort();
1269 \\ val /= 2; if (val != -1) abort();
1270 \\ val %= 2; if (val != -1) abort();
1271 \\ val <<= 1; if (val != -2) abort();
1272 \\ val >>= 1; if (val != -1) abort();
1273 \\ val += 100000000; // compile error if @truncate() not inserted
1274 \\ unsigned short uval = 1;
1275 \\ uval += 1; if (uval != 2) abort();
1276 \\ uval -= 1; if (uval != 1) abort();
1277 \\ uval *= 2; if (uval != 2) abort();
1278 \\ uval /= 2; if (uval != 1) abort();
1279 \\ uval %= 2; if (uval != 1) abort();
1280 \\ uval <<= 1; if (uval != 2) abort();
1281 \\ uval >>= 1; if (uval != 1) abort();
1282 \\ uval += 100000000; // compile error if @truncate() not inserted
1283 \\}
1284 , "");
1285
1286 cases.add("cast RHS of compound assignment if necessary, used result",
1287 \\#include <stdlib.h>
1288 \\int main(void) {
1289 \\ signed short foo;
1290 \\ signed short val = -1;
1291 \\ foo = (val += 1); if (foo != 0) abort();
1292 \\ foo = (val -= 1); if (foo != -1) abort();
1293 \\ foo = (val *= 2); if (foo != -2) abort();
1294 \\ foo = (val /= 2); if (foo != -1) abort();
1295 \\ foo = (val %= 2); if (foo != -1) abort();
1296 \\ foo = (val <<= 1); if (foo != -2) abort();
1297 \\ foo = (val >>= 1); if (foo != -1) abort();
1298 \\ foo = (val += 100000000); // compile error if @truncate() not inserted
1299 \\ unsigned short ufoo;
1300 \\ unsigned short uval = 1;
1301 \\ ufoo = (uval += 1); if (ufoo != 2) abort();
1302 \\ ufoo = (uval -= 1); if (ufoo != 1) abort();
1303 \\ ufoo = (uval *= 2); if (ufoo != 2) abort();
1304 \\ ufoo = (uval /= 2); if (ufoo != 1) abort();
1305 \\ ufoo = (uval %= 2); if (ufoo != 1) abort();
1306 \\ ufoo = (uval <<= 1); if (ufoo != 2) abort();
1307 \\ ufoo = (uval >>= 1); if (ufoo != 1) abort();
1308 \\ ufoo = (uval += 100000000); // compile error if @truncate() not inserted
1309 \\}
1310 , "");
1247}1311}
test/stage2/wasm.zig+35
...@@ -175,6 +175,41 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -175,6 +175,41 @@ pub fn addCases(ctx: *TestContext) !void {
175 \\ return i;175 \\ return i;
176 \\}176 \\}
177 , "31\n");177 , "31\n");
178
179 case.addCompareOutput(
180 \\export fn _start() void {
181 \\ assert(foo(true) != @as(i32, 30));
182 \\}
183 \\
184 \\fn assert(ok: bool) void {
185 \\ if (!ok) unreachable;
186 \\}
187 \\
188 \\fn foo(ok: bool) i32 {
189 \\ const x = if(ok) @as(i32, 20) else @as(i32, 10);
190 \\ return x;
191 \\}
192 , "");
193
194 case.addCompareOutput(
195 \\export fn _start() void {
196 \\ assert(foo(false) == @as(i32, 20));
197 \\ assert(foo(true) == @as(i32, 30));
198 \\}
199 \\
200 \\fn assert(ok: bool) void {
201 \\ if (!ok) unreachable;
202 \\}
203 \\
204 \\fn foo(ok: bool) i32 {
205 \\ const val: i32 = blk: {
206 \\ var x: i32 = 1;
207 \\ if (!ok) break :blk x + @as(i32, 9);
208 \\ break :blk x + @as(i32, 19);
209 \\ };
210 \\ return val + 10;
211 \\}
212 , "");
178 }213 }
179214
180 {215 {
test/standalone/mix_o_files/base64.zig+3-3
...@@ -3,9 +3,9 @@ const base64 = @import("std").base64;...@@ -3,9 +3,9 @@ const base64 = @import("std").base64;
3export fn decode_base_64(dest_ptr: [*]u8, dest_len: usize, source_ptr: [*]const u8, source_len: usize) usize {3export fn decode_base_64(dest_ptr: [*]u8, dest_len: usize, source_ptr: [*]const u8, source_len: usize) usize {
4 const src = source_ptr[0..source_len];4 const src = source_ptr[0..source_len];
5 const dest = dest_ptr[0..dest_len];5 const dest = dest_ptr[0..dest_len];
6 const base64_decoder = base64.standard_decoder_unsafe;6 const base64_decoder = base64.standard.Decoder;
7 const decoded_size = base64_decoder.calcSize(src);7 const decoded_size = base64_decoder.calcSizeForSlice(src) catch unreachable;
8 base64_decoder.decode(dest[0..decoded_size], src);8 base64_decoder.decode(dest[0..decoded_size], src) catch unreachable;
9 return decoded_size;9 return decoded_size;
10}10}
1111
test/translate_c.zig+64-38
...@@ -3,6 +3,28 @@ const std = @import("std");...@@ -3,6 +3,28 @@ const std = @import("std");
3const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
44
5pub fn addCases(cases: *tests.TranslateCContext) void {5pub fn addCases(cases: *tests.TranslateCContext) void {
6 cases.add("unnamed child types of typedef receive typedef's name",
7 \\typedef enum {
8 \\ FooA,
9 \\ FooB,
10 \\} Foo;
11 \\typedef struct {
12 \\ int a, b;
13 \\} Bar;
14 , &[_][]const u8{
15 \\pub const Foo = extern enum(c_int) {
16 \\ A,
17 \\ B,
18 \\ _,
19 \\};
20 \\pub const FooA = @enumToInt(Foo.A);
21 \\pub const FooB = @enumToInt(Foo.B);
22 \\pub const Bar = extern struct {
23 \\ a: c_int,
24 \\ b: c_int,
25 \\};
26 });
27
6 cases.add("if as while stmt has semicolon",28 cases.add("if as while stmt has semicolon",
7 \\void foo() {29 \\void foo() {
8 \\ while (1) if (1) {30 \\ while (1) if (1) {
...@@ -218,9 +240,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -218,9 +240,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
218 \\} Bar;240 \\} Bar;
219 , &[_][]const u8{241 , &[_][]const u8{
220 \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo242 \\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo
221 \\const struct_unnamed_1 = opaque {};243 \\pub const Foo = opaque {};
222 \\pub const Foo = struct_unnamed_1;244 \\pub const Bar = extern struct {
223 \\const struct_unnamed_2 = extern struct {
224 \\ bar: ?*Foo,245 \\ bar: ?*Foo,
225 \\};246 \\};
226 });247 });
...@@ -519,17 +540,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -519,17 +540,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
519 \\} outer;540 \\} outer;
520 \\void foo(outer *x) { x->y = x->x; }541 \\void foo(outer *x) { x->y = x->x; }
521 , &[_][]const u8{542 , &[_][]const u8{
522 \\const struct_unnamed_3 = extern struct {543 \\const struct_unnamed_2 = extern struct {
523 \\ y: c_int,544 \\ y: c_int,
524 \\};545 \\};
525 \\const union_unnamed_2 = extern union {546 \\const union_unnamed_1 = extern union {
526 \\ x: u8,547 \\ x: u8,
527 \\ unnamed_0: struct_unnamed_3,548 \\ unnamed_0: struct_unnamed_2,
528 \\};549 \\};
529 \\const struct_unnamed_1 = extern struct {550 \\pub const outer = extern struct {
530 \\ unnamed_0: union_unnamed_2,551 \\ unnamed_0: union_unnamed_1,
531 \\};552 \\};
532 \\pub const outer = struct_unnamed_1;
533 \\pub export fn foo(arg_x: [*c]outer) void {553 \\pub export fn foo(arg_x: [*c]outer) void {
534 \\ var x = arg_x;554 \\ var x = arg_x;
535 \\ x.*.unnamed_0.unnamed_0.y = @bitCast(c_int, @as(c_uint, x.*.unnamed_0.x));555 \\ x.*.unnamed_0.unnamed_0.y = @bitCast(c_int, @as(c_uint, x.*.unnamed_0.x));
...@@ -565,21 +585,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -565,21 +585,20 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
565 \\struct {int x,y;} s2 = {.y = 2, .x=1};585 \\struct {int x,y;} s2 = {.y = 2, .x=1};
566 \\foo s3 = { 123 };586 \\foo s3 = { 123 };
567 , &[_][]const u8{587 , &[_][]const u8{
568 \\const struct_unnamed_1 = extern struct {588 \\pub const foo = extern struct {
569 \\ x: c_int,589 \\ x: c_int,
570 \\};590 \\};
571 \\pub const foo = struct_unnamed_1;591 \\const struct_unnamed_1 = extern struct {
572 \\const struct_unnamed_2 = extern struct {
573 \\ x: f64,592 \\ x: f64,
574 \\ y: f64,593 \\ y: f64,
575 \\ z: f64,594 \\ z: f64,
576 \\};595 \\};
577 \\pub export var s0: struct_unnamed_2 = struct_unnamed_2{596 \\pub export var s0: struct_unnamed_1 = struct_unnamed_1{
578 \\ .x = 1.2,597 \\ .x = 1.2,
579 \\ .y = 1.3,598 \\ .y = 1.3,
580 \\ .z = 0,599 \\ .z = 0,
581 \\};600 \\};
582 \\const struct_unnamed_3 = extern struct {601 \\const struct_unnamed_2 = extern struct {
583 \\ sec: c_int,602 \\ sec: c_int,
584 \\ min: c_int,603 \\ min: c_int,
585 \\ hour: c_int,604 \\ hour: c_int,
...@@ -587,7 +606,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -587,7 +606,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
587 \\ mon: c_int,606 \\ mon: c_int,
588 \\ year: c_int,607 \\ year: c_int,
589 \\};608 \\};
590 \\pub export var s1: struct_unnamed_3 = struct_unnamed_3{609 \\pub export var s1: struct_unnamed_2 = struct_unnamed_2{
591 \\ .sec = @as(c_int, 30),610 \\ .sec = @as(c_int, 30),
592 \\ .min = @as(c_int, 15),611 \\ .min = @as(c_int, 15),
593 \\ .hour = @as(c_int, 17),612 \\ .hour = @as(c_int, 17),
...@@ -595,11 +614,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -595,11 +614,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
595 \\ .mon = @as(c_int, 12),614 \\ .mon = @as(c_int, 12),
596 \\ .year = @as(c_int, 2014),615 \\ .year = @as(c_int, 2014),
597 \\};616 \\};
598 \\const struct_unnamed_4 = extern struct {617 \\const struct_unnamed_3 = extern struct {
599 \\ x: c_int,618 \\ x: c_int,
600 \\ y: c_int,619 \\ y: c_int,
601 \\};620 \\};
602 \\pub export var s2: struct_unnamed_4 = struct_unnamed_4{621 \\pub export var s2: struct_unnamed_3 = struct_unnamed_3{
603 \\ .x = @as(c_int, 1),622 \\ .x = @as(c_int, 1),
604 \\ .y = @as(c_int, 2),623 \\ .y = @as(c_int, 2),
605 \\};624 \\};
...@@ -1639,37 +1658,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1639,37 +1658,36 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1639 \\ p,1658 \\ p,
1640 \\};1659 \\};
1641 , &[_][]const u8{1660 , &[_][]const u8{
1642 \\const enum_unnamed_1 = extern enum(c_int) {1661 \\pub const d = extern enum(c_int) {
1643 \\ a,1662 \\ a,
1644 \\ b,1663 \\ b,
1645 \\ c,1664 \\ c,
1646 \\ _,1665 \\ _,
1647 \\};1666 \\};
1648 \\pub const a = @enumToInt(enum_unnamed_1.a);1667 \\pub const a = @enumToInt(d.a);
1649 \\pub const b = @enumToInt(enum_unnamed_1.b);1668 \\pub const b = @enumToInt(d.b);
1650 \\pub const c = @enumToInt(enum_unnamed_1.c);1669 \\pub const c = @enumToInt(d.c);
1651 \\pub const d = enum_unnamed_1;1670 \\const enum_unnamed_1 = extern enum(c_int) {
1652 \\const enum_unnamed_2 = extern enum(c_int) {
1653 \\ e = 0,1671 \\ e = 0,
1654 \\ f = 4,1672 \\ f = 4,
1655 \\ g = 5,1673 \\ g = 5,
1656 \\ _,1674 \\ _,
1657 \\};1675 \\};
1658 \\pub const e = @enumToInt(enum_unnamed_2.e);1676 \\pub const e = @enumToInt(enum_unnamed_1.e);
1659 \\pub const f = @enumToInt(enum_unnamed_2.f);1677 \\pub const f = @enumToInt(enum_unnamed_1.f);
1660 \\pub const g = @enumToInt(enum_unnamed_2.g);1678 \\pub const g = @enumToInt(enum_unnamed_1.g);
1661 \\pub export var h: enum_unnamed_2 = @intToEnum(enum_unnamed_2, e);1679 \\pub export var h: enum_unnamed_1 = @intToEnum(enum_unnamed_1, e);
1662 \\const enum_unnamed_3 = extern enum(c_int) {1680 \\const enum_unnamed_2 = extern enum(c_int) {
1663 \\ i,1681 \\ i,
1664 \\ j,1682 \\ j,
1665 \\ k,1683 \\ k,
1666 \\ _,1684 \\ _,
1667 \\};1685 \\};
1668 \\pub const i = @enumToInt(enum_unnamed_3.i);1686 \\pub const i = @enumToInt(enum_unnamed_2.i);
1669 \\pub const j = @enumToInt(enum_unnamed_3.j);1687 \\pub const j = @enumToInt(enum_unnamed_2.j);
1670 \\pub const k = @enumToInt(enum_unnamed_3.k);1688 \\pub const k = @enumToInt(enum_unnamed_2.k);
1671 \\pub const struct_Baz = extern struct {1689 \\pub const struct_Baz = extern struct {
1672 \\ l: enum_unnamed_3,1690 \\ l: enum_unnamed_2,
1673 \\ m: d,1691 \\ m: d,
1674 \\};1692 \\};
1675 \\pub const enum_i = extern enum(c_int) {1693 \\pub const enum_i = extern enum(c_int) {
...@@ -1934,7 +1952,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1934,7 +1952,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1934 , &[_][]const u8{1952 , &[_][]const u8{
1935 \\pub export fn foo() c_int {1953 \\pub export fn foo() c_int {
1936 \\ var a: c_int = 5;1954 \\ var a: c_int = 5;
1937 \\ while (true) a = 2;1955 \\ while (true) {
1956 \\ a = 2;
1957 \\ }
1938 \\ while (true) {1958 \\ while (true) {
1939 \\ var a_1: c_int = 4;1959 \\ var a_1: c_int = 4;
1940 \\ a_1 = 9;1960 \\ a_1 = 9;
...@@ -1947,7 +1967,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1947,7 +1967,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1947 \\ var a_1: c_int = 2;1967 \\ var a_1: c_int = 2;
1948 \\ a_1 = 12;1968 \\ a_1 = 12;
1949 \\ }1969 \\ }
1950 \\ while (true) a = 7;1970 \\ while (true) {
1971 \\ a = 7;
1972 \\ }
1951 \\ return 0;1973 \\ return 0;
1952 \\}1974 \\}
1953 });1975 });
...@@ -2008,7 +2030,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2008,7 +2030,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2008 \\}2030 \\}
2009 , &[_][]const u8{2031 , &[_][]const u8{
2010 \\pub export fn bar() c_int {2032 \\pub export fn bar() c_int {
2011 \\ if ((if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6)) != 0) _ = @as(c_int, 2);2033 \\ if ((if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6)) != 0) {
2034 \\ _ = @as(c_int, 2);
2035 \\ }
2012 \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6);2036 \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6);
2013 \\}2037 \\}
2014 });2038 });
...@@ -2389,7 +2413,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2389,7 +2413,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2389 \\pub const yes = [*c]u8;2413 \\pub const yes = [*c]u8;
2390 \\pub export fn foo() void {2414 \\pub export fn foo() void {
2391 \\ var a: yes = undefined;2415 \\ var a: yes = undefined;
2392 \\ if (a != null) _ = @as(c_int, 2);2416 \\ if (a != null) {
2417 \\ _ = @as(c_int, 2);
2418 \\ }
2393 \\}2419 \\}
2394 });2420 });
23952421
...@@ -2740,7 +2766,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2740,7 +2766,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2740 \\ var a = arg_a;2766 \\ var a = arg_a;
2741 \\ var i: c_int = 0;2767 \\ var i: c_int = 0;
2742 \\ while (a > @bitCast(c_uint, @as(c_int, 0))) {2768 \\ while (a > @bitCast(c_uint, @as(c_int, 0))) {
2743 \\ a >>= @intCast(@import("std").math.Log2Int(c_int), 1);2769 \\ a >>= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
2744 \\ }2770 \\ }
2745 \\ return i;2771 \\ return i;
2746 \\}2772 \\}
...@@ -2760,7 +2786,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2760,7 +2786,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2760 \\ var a = arg_a;2786 \\ var a = arg_a;
2761 \\ var i: c_int = 0;2787 \\ var i: c_int = 0;
2762 \\ while (a > @bitCast(c_uint, @as(c_int, 0))) {2788 \\ while (a > @bitCast(c_uint, @as(c_int, 0))) {
2763 \\ a >>= @intCast(@import("std").math.Log2Int(c_int), 1);2789 \\ a >>= @intCast(@import("std").math.Log2Int(c_int), @as(c_int, 1));
2764 \\ }2790 \\ }
2765 \\ return i;2791 \\ return i;
2766 \\}2792 \\}