diff --git a/.gitattributes b/.gitattributes index 8bf5843425..6cf47bc9ad 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,9 +3,9 @@ langref.html.in text eol=lf deps/SoftFloat-3e/*.txt text eol=crlf -deps/* linguist-vendored -lib/include/* linguist-vendored -lib/libc/* linguist-vendored -lib/libcxx/* linguist-vendored -lib/libcxxabi/* linguist-vendored -lib/libunwind/* linguist-vendored +deps/** linguist-vendored +lib/include/** linguist-vendored +lib/libc/** linguist-vendored +lib/libcxx/** linguist-vendored +lib/libcxxabi/** linguist-vendored +lib/libunwind/** linguist-vendored diff --git a/build.zig b/build.zig index f2f104d84d..d924719d92 100644 --- a/build.zig +++ b/build.zig @@ -267,6 +267,7 @@ pub fn build(b: *Builder) !void { test_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/std.zig", "std", "Run the standard library tests", modes, false, skip_non_native, skip_libc, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); test_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/special/compiler_rt.zig", "compiler-rt", "Run the compiler_rt tests", modes, true, skip_non_native, true, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); + test_step.dependOn(tests.addPkgTests(b, test_filter, "lib/std/special/c.zig", "minilibc", "Run the mini libc tests", modes, true, skip_non_native, true, is_wine_enabled, is_qemu_enabled, is_wasmtime_enabled, glibc_multi_dir)); test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes)); diff --git a/ci/drone/linux_script b/ci/drone/linux_script index fe7cc6a5d0..0994604f80 100755 --- a/ci/drone/linux_script +++ b/ci/drone/linux_script @@ -23,13 +23,11 @@ cmake .. -DCMAKE_BUILD_TYPE=Release "-DCMAKE_INSTALL_PREFIX=$DISTDIR" -DZIG_STAT samu install # run-translated-c tests are skipped due to: https://github.com/ziglang/zig/issues/8537 -# stage2 tests are skipped due to: https://github.com/ziglang/zig/issues/8545 ./zig build test \ -Dskip-release \ -Dskip-non-native \ -Dskip-compile-errors \ - -Dskip-run-translated-c \ - -Dskip-stage2-tests + -Dskip-run-translated-c if [ -z "$DRONE_PULL_REQUEST" ]; then mv ../LICENSE "$DISTDIR/" diff --git a/doc/langref.html.in b/doc/langref.html.in index 155fcbf468..80080ca146 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6509,7 +6509,7 @@ test "suspend with no resume" { fn func() void { x += 1; - suspend; + suspend {} // This line is never reached because the suspend has no matching resume. x += 1; } @@ -6574,7 +6574,7 @@ fn testResumeFromSuspend(my_result: *i32) void { resume @frame(); } my_result.* += 1; - suspend; + suspend {} my_result.* += 1; } {#code_end#} @@ -6613,7 +6613,7 @@ fn amain() void { } fn func() void { - suspend; + suspend {} } {#code_end#}

@@ -6915,7 +6915,7 @@ test "async fn pointer in a struct field" { fn func(y: *i32) void { defer y.* += 2; y.* += 1; - suspend; + suspend {} } {#code_end#} {#header_close#} @@ -7498,13 +7498,13 @@ test "main" { {#header_close#} {#header_open|@export#} -

{#syntax#}@export(target: anytype, comptime options: std.builtin.ExportOptions) void{#endsyntax#}
+
{#syntax#}@export(identifier, comptime options: std.builtin.ExportOptions) void{#endsyntax#}

Creates a symbol in the output object file.

This function can be called from a {#link|comptime#} block to conditionally export symbols. - When {#syntax#}target{#endsyntax#} is a function with the C calling convention and + When {#syntax#}identifier{#endsyntax#} is a function with the C calling convention and {#syntax#}options.linkage{#endsyntax#} is {#syntax#}Strong{#endsyntax#}, this is equivalent to the {#syntax#}export{#endsyntax#} keyword used on a function:

@@ -7531,6 +7531,14 @@ export fn @"A function name that is a complete sentence."() void {} {#see_also|Exporting a C Library#} {#header_close#} + {#header_open|@extern#} +
{#syntax#}@extern(T: type, comptime options: std.builtin.ExternOptions) *T{#endsyntax#}
+

+ Creates a reference to an external symbol in the output object file. +

+ {#see_also|@export#} + {#header_close#} + {#header_open|@fence#}
{#syntax#}@fence(order: AtomicOrder){#endsyntax#}

@@ -7640,7 +7648,7 @@ test "heap allocated frame" { } fn func() void { - suspend; + suspend {} } {#code_end#} {#header_close#} diff --git a/lib/std/atomic/bool.zig b/lib/std/atomic/bool.zig index c968b862b9..0cffb99d38 100644 --- a/lib/std/atomic/bool.zig +++ b/lib/std/atomic/bool.zig @@ -28,7 +28,7 @@ pub const Bool = extern struct { return @atomicRmw(bool, &self.unprotected_value, .Xchg, operand, ordering); } - pub fn load(self: *Self, comptime ordering: std.builtin.AtomicOrder) bool { + pub fn load(self: *const Self, comptime ordering: std.builtin.AtomicOrder) bool { switch (ordering) { .Unordered, .Monotonic, .Acquire, .SeqCst => {}, else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a load operation"), diff --git a/lib/std/atomic/int.zig b/lib/std/atomic/int.zig index 1a3bead2df..2d1c5f80e9 100644 --- a/lib/std/atomic/int.zig +++ b/lib/std/atomic/int.zig @@ -31,7 +31,7 @@ pub fn Int(comptime T: type) type { return @atomicRmw(T, &self.unprotected_value, op, operand, ordering); } - pub fn load(self: *Self, comptime ordering: builtin.AtomicOrder) T { + pub fn load(self: *const Self, comptime ordering: builtin.AtomicOrder) T { switch (ordering) { .Unordered, .Monotonic, .Acquire, .SeqCst => {}, else => @compileError("Invalid ordering '" ++ @tagName(ordering) ++ "' for a load operation"), @@ -59,7 +59,7 @@ pub fn Int(comptime T: type) type { return self.rmw(.Sub, 1, .SeqCst); } - pub fn get(self: *Self) T { + pub fn get(self: *const Self) T { return self.load(.SeqCst); } diff --git a/lib/std/build.zig b/lib/std/build.zig index 22c22c1961..ff2a16d2b9 100644 --- a/lib/std/build.zig +++ b/lib/std/build.zig @@ -1386,6 +1386,8 @@ pub const LibExeObjStep = struct { /// safely garbage-collected during the linking phase. link_function_sections: bool = false, + linker_allow_shlib_undefined: ?bool = null, + /// Uses system Wine installation to run cross compiled Windows build artifacts. enable_wine: bool = false, @@ -2338,6 +2340,9 @@ pub const LibExeObjStep = struct { if (self.link_function_sections) { try zig_args.append("-ffunction-sections"); } + if (self.linker_allow_shlib_undefined) |x| { + try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined"); + } if (self.single_threaded) { try zig_args.append("--single-threaded"); } diff --git a/lib/std/crypto.zig b/lib/std/crypto.zig index 457b9130d9..e4ec50f5b7 100644 --- a/lib/std/crypto.zig +++ b/lib/std/crypto.zig @@ -154,7 +154,7 @@ pub const random = &@import("crypto/tlcsprng.zig").interface; const std = @import("std.zig"); -pub const Error = @import("crypto/error.zig").Error; +pub const errors = @import("crypto/errors.zig"); test "crypto" { const please_windows_dont_oom = std.Target.current.os.tag == .windows; diff --git a/lib/std/crypto/25519/curve25519.zig b/lib/std/crypto/25519/curve25519.zig index fd9d426bf8..65d709bcb3 100644 --- a/lib/std/crypto/25519/curve25519.zig +++ b/lib/std/crypto/25519/curve25519.zig @@ -4,7 +4,11 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const std = @import("std"); -const Error = std.crypto.Error; +const crypto = std.crypto; + +const IdentityElementError = crypto.errors.IdentityElementError; +const NonCanonicalError = crypto.errors.NonCanonicalError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; /// Group operations over Curve25519. pub const Curve25519 = struct { @@ -29,12 +33,12 @@ pub const Curve25519 = struct { pub const basePoint = Curve25519{ .x = Fe.curve25519BasePoint }; /// Check that the encoding of a Curve25519 point is canonical. - pub fn rejectNonCanonical(s: [32]u8) Error!void { + pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void { return Fe.rejectNonCanonical(s, false); } /// Reject the neutral element. - pub fn rejectIdentity(p: Curve25519) Error!void { + pub fn rejectIdentity(p: Curve25519) IdentityElementError!void { if (p.x.isZero()) { return error.IdentityElement; } @@ -45,7 +49,7 @@ pub const Curve25519 = struct { return p.dbl().dbl().dbl(); } - fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) Error!Curve25519 { + fn ladder(p: Curve25519, s: [32]u8, comptime bits: usize) IdentityElementError!Curve25519 { var x1 = p.x; var x2 = Fe.one; var z2 = Fe.zero; @@ -86,7 +90,7 @@ pub const Curve25519 = struct { /// way to use Curve25519 for a DH operation. /// Return error.IdentityElement if the resulting point is /// the identity element. - pub fn clampedMul(p: Curve25519, s: [32]u8) Error!Curve25519 { + pub fn clampedMul(p: Curve25519, s: [32]u8) IdentityElementError!Curve25519 { var t: [32]u8 = s; scalar.clamp(&t); return try ladder(p, t, 255); @@ -96,16 +100,16 @@ pub const Curve25519 = struct { /// Return error.IdentityElement if the resulting point is /// the identity element or error.WeakPublicKey if the public /// key is a low-order point. - pub fn mul(p: Curve25519, s: [32]u8) Error!Curve25519 { + pub fn mul(p: Curve25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Curve25519 { const cofactor = [_]u8{8} ++ [_]u8{0} ** 31; _ = ladder(p, cofactor, 4) catch return error.WeakPublicKey; return try ladder(p, s, 256); } /// Compute the Curve25519 equivalent to an Edwards25519 point. - pub fn fromEdwards25519(p: std.crypto.ecc.Edwards25519) Error!Curve25519 { + pub fn fromEdwards25519(p: crypto.ecc.Edwards25519) IdentityElementError!Curve25519 { try p.clearCofactor().rejectIdentity(); - const one = std.crypto.ecc.Edwards25519.Fe.one; + const one = crypto.ecc.Edwards25519.Fe.one; const x = one.add(p.y).mul(one.sub(p.y).invert()); // xMont=(1+yEd)/(1-yEd) return Curve25519{ .x = x }; } diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig index e385e34f12..b48cc24b4b 100644 --- a/lib/std/crypto/25519/ed25519.zig +++ b/lib/std/crypto/25519/ed25519.zig @@ -8,8 +8,15 @@ const crypto = std.crypto; const debug = std.debug; const fmt = std.fmt; const mem = std.mem; + const Sha512 = crypto.hash.sha2.Sha512; -const Error = crypto.Error; + +const EncodingError = crypto.errors.EncodingError; +const IdentityElementError = crypto.errors.IdentityElementError; +const NonCanonicalError = crypto.errors.NonCanonicalError; +const SignatureVerificationError = crypto.errors.SignatureVerificationError; +const KeyMismatchError = crypto.errors.KeyMismatchError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; /// Ed25519 (EdDSA) signatures. pub const Ed25519 = struct { @@ -41,7 +48,7 @@ pub const Ed25519 = struct { /// /// For this reason, an EdDSA secret key is commonly called a seed, /// from which the actual secret is derived. - pub fn create(seed: ?[seed_length]u8) Error!KeyPair { + pub fn create(seed: ?[seed_length]u8) IdentityElementError!KeyPair { const ss = seed orelse ss: { var random_seed: [seed_length]u8 = undefined; crypto.random.bytes(&random_seed); @@ -51,7 +58,7 @@ pub const Ed25519 = struct { var h = Sha512.init(.{}); h.update(&ss); h.final(&az); - const p = try Curve.basePoint.clampedMul(az[0..32].*); + const p = Curve.basePoint.clampedMul(az[0..32].*) catch return error.IdentityElement; var sk: [secret_length]u8 = undefined; mem.copy(u8, &sk, &ss); const pk = p.toBytes(); @@ -72,7 +79,7 @@ pub const Ed25519 = struct { /// Sign a message using a key pair, and optional random noise. /// Having noise creates non-standard, non-deterministic signatures, /// but has been proven to increase resilience against fault attacks. - pub fn sign(msg: []const u8, key_pair: KeyPair, noise: ?[noise_length]u8) Error![signature_length]u8 { + pub fn sign(msg: []const u8, key_pair: KeyPair, noise: ?[noise_length]u8) (IdentityElementError || WeakPublicKeyError || KeyMismatchError)![signature_length]u8 { const seed = key_pair.secret_key[0..seed_length]; const public_key = key_pair.secret_key[seed_length..]; if (!mem.eql(u8, public_key, &key_pair.public_key)) { @@ -113,7 +120,7 @@ pub const Ed25519 = struct { /// Verify an Ed25519 signature given a message and a public key. /// Returns error.SignatureVerificationFailed is the signature verification failed. - pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) Error!void { + pub fn verify(sig: [signature_length]u8, msg: []const u8, public_key: [public_length]u8) (SignatureVerificationError || WeakPublicKeyError || EncodingError || NonCanonicalError || IdentityElementError)!void { const r = sig[0..32]; const s = sig[32..64]; try Curve.scalar.rejectNonCanonical(s.*); @@ -122,6 +129,7 @@ pub const Ed25519 = struct { try a.rejectIdentity(); try Curve.rejectNonCanonical(r.*); const expected_r = try Curve.fromBytes(r.*); + try expected_r.rejectIdentity(); var h = Sha512.init(.{}); h.update(r); @@ -131,8 +139,7 @@ pub const Ed25519 = struct { h.final(&hram64); const hram = Curve.scalar.reduce64(hram64); - const ah = try a.neg().mulPublic(hram); - const sb_ah = (try Curve.basePoint.mulPublic(s.*)).add(ah); + const sb_ah = try Curve.basePoint.mulDoubleBasePublic(s.*, a.neg(), hram); if (expected_r.sub(sb_ah).clearCofactor().rejectIdentity()) |_| { return error.SignatureVerificationFailed; } else |_| {} @@ -146,7 +153,7 @@ pub const Ed25519 = struct { }; /// Verify several signatures in a single operation, much faster than verifying signatures one-by-one - pub fn verifyBatch(comptime count: usize, signature_batch: [count]BatchElement) Error!void { + pub fn verifyBatch(comptime count: usize, signature_batch: [count]BatchElement) (SignatureVerificationError || IdentityElementError || WeakPublicKeyError || EncodingError || NonCanonicalError)!void { var r_batch: [count][32]u8 = undefined; var s_batch: [count][32]u8 = undefined; var a_batch: [count]Curve = undefined; @@ -161,6 +168,7 @@ pub const Ed25519 = struct { try a.rejectIdentity(); try Curve.rejectNonCanonical(r.*); const expected_r = try Curve.fromBytes(r.*); + try expected_r.rejectIdentity(); expected_r_batch[i] = expected_r; r_batch[i] = r.*; s_batch[i] = s.*; @@ -180,7 +188,7 @@ pub const Ed25519 = struct { var z_batch: [count]Curve.scalar.CompressedScalar = undefined; for (z_batch) |*z| { - std.crypto.random.bytes(z[0..16]); + crypto.random.bytes(z[0..16]); mem.set(u8, z[16..], 0); } @@ -233,8 +241,8 @@ test "ed25519 batch verification" { const key_pair = try Ed25519.KeyPair.create(null); var msg1: [32]u8 = undefined; var msg2: [32]u8 = undefined; - std.crypto.random.bytes(&msg1); - std.crypto.random.bytes(&msg2); + crypto.random.bytes(&msg1); + crypto.random.bytes(&msg2); const sig1 = try Ed25519.sign(&msg1, key_pair, null); const sig2 = try Ed25519.sign(&msg2, key_pair, null); var signature_batch = [_]Ed25519.BatchElement{ @@ -317,13 +325,13 @@ test "ed25519 test vectors" { .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41", .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43", .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f", - .expected = error.SignatureVerificationFailed, // 8 - non-canonical R + .expected = error.IdentityElement, // 8 - non-canonical R }, Vec{ .msg_hex = "9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41", .public_key_hex = "f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43", .sig_hex = "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908", - .expected = null, // 9 - non-canonical R + .expected = error.IdentityElement, // 9 - non-canonical R }, Vec{ .msg_hex = "e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b", diff --git a/lib/std/crypto/25519/edwards25519.zig b/lib/std/crypto/25519/edwards25519.zig index a7f385c365..13dacc77f9 100644 --- a/lib/std/crypto/25519/edwards25519.zig +++ b/lib/std/crypto/25519/edwards25519.zig @@ -4,10 +4,16 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const std = @import("std"); +const crypto = std.crypto; const debug = std.debug; const fmt = std.fmt; const mem = std.mem; -const Error = std.crypto.Error; + +const EncodingError = crypto.errors.EncodingError; +const IdentityElementError = crypto.errors.IdentityElementError; +const NonCanonicalError = crypto.errors.NonCanonicalError; +const NotSquareError = crypto.errors.NotSquareError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; /// Group operations over Edwards25519. pub const Edwards25519 = struct { @@ -26,7 +32,7 @@ pub const Edwards25519 = struct { is_base: bool = false, /// Decode an Edwards25519 point from its compressed (Y+sign) coordinates. - pub fn fromBytes(s: [encoded_length]u8) Error!Edwards25519 { + pub fn fromBytes(s: [encoded_length]u8) EncodingError!Edwards25519 { const z = Fe.one; const y = Fe.fromBytes(s); var u = y.sq(); @@ -56,7 +62,7 @@ pub const Edwards25519 = struct { } /// Check that the encoding of a point is canonical. - pub fn rejectNonCanonical(s: [32]u8) Error!void { + pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void { return Fe.rejectNonCanonical(s, true); } @@ -81,7 +87,7 @@ pub const Edwards25519 = struct { const identityElement = Edwards25519{ .x = Fe.zero, .y = Fe.one, .z = Fe.one, .t = Fe.zero }; /// Reject the neutral element. - pub fn rejectIdentity(p: Edwards25519) Error!void { + pub fn rejectIdentity(p: Edwards25519) IdentityElementError!void { if (p.x.isZero()) { return error.IdentityElement; } @@ -177,7 +183,7 @@ pub const Edwards25519 = struct { // Based on real-world benchmarks, we only use this for multi-scalar multiplication. // NAF could be useful to half the size of precomputation tables, but we intentionally // avoid these to keep the standard library lightweight. - fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) Error!Edwards25519 { + fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) IdentityElementError!Edwards25519 { std.debug.assert(vartime); const e = nonAdjacentForm(s); var q = Edwards25519.identityElement; @@ -197,7 +203,7 @@ pub const Edwards25519 = struct { } // Scalar multiplication with a 4-bit window and the first 15 multiples. - fn pcMul16(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) Error!Edwards25519 { + fn pcMul16(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) IdentityElementError!Edwards25519 { var q = Edwards25519.identityElement; var pos: usize = 252; while (true) : (pos -= 4) { @@ -232,10 +238,15 @@ pub const Edwards25519 = struct { break :pc precompute(Edwards25519.basePoint, 15); }; + const basePointPc8 = comptime pc: { + @setEvalBranchQuota(10000); + break :pc precompute(Edwards25519.basePoint, 8); + }; + /// Multiply an Edwards25519 point by a scalar without clamping it. - /// Return error.WeakPublicKey if the resulting point is - /// the identity element. - pub fn mul(p: Edwards25519, s: [32]u8) Error!Edwards25519 { + /// Return error.WeakPublicKey if the base generates a small-order group, + /// and error.IdentityElement if the result is the identity element. + pub fn mul(p: Edwards25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { const pc = if (p.is_base) basePointPc else pc: { const xpc = precompute(p, 15); xpc[4].rejectIdentity() catch return error.WeakPublicKey; @@ -246,7 +257,7 @@ pub const Edwards25519 = struct { /// Multiply an Edwards25519 point by a *PUBLIC* scalar *IN VARIABLE TIME* /// This can be used for signature verification. - pub fn mulPublic(p: Edwards25519, s: [32]u8) Error!Edwards25519 { + pub fn mulPublic(p: Edwards25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { if (p.is_base) { return pcMul16(basePointPc, s, true); } else { @@ -256,14 +267,50 @@ pub const Edwards25519 = struct { } } + /// Double-base multiplication of public parameters - Compute (p1*s1)+(p2*s2) *IN VARIABLE TIME* + /// This can be used for signature verification. + pub fn mulDoubleBasePublic(p1: Edwards25519, s1: [32]u8, p2: Edwards25519, s2: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { + const pc1 = if (p1.is_base) basePointPc8 else pc: { + const xpc = precompute(p1, 8); + xpc[4].rejectIdentity() catch return error.WeakPublicKey; + break :pc xpc; + }; + const pc2 = if (p2.is_base) basePointPc8 else pc: { + const xpc = precompute(p2, 8); + xpc[4].rejectIdentity() catch return error.WeakPublicKey; + break :pc xpc; + }; + const e1 = nonAdjacentForm(s1); + const e2 = nonAdjacentForm(s2); + var q = Edwards25519.identityElement; + var pos: usize = 2 * 32 - 1; + while (true) : (pos -= 1) { + const slot1 = e1[pos]; + if (slot1 > 0) { + q = q.add(pc1[@intCast(usize, slot1)]); + } else if (slot1 < 0) { + q = q.sub(pc1[@intCast(usize, -slot1)]); + } + const slot2 = e2[pos]; + if (slot2 > 0) { + q = q.add(pc2[@intCast(usize, slot2)]); + } else if (slot2 < 0) { + q = q.sub(pc2[@intCast(usize, -slot2)]); + } + if (pos == 0) break; + q = q.dbl().dbl().dbl().dbl(); + } + try q.rejectIdentity(); + return q; + } + /// Multiscalar multiplication *IN VARIABLE TIME* for public data /// Computes ps0*ss0 + ps1*ss1 + ps2*ss2... faster than doing many of these operations individually - pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) Error!Edwards25519 { + pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { var pcs: [count][9]Edwards25519 = undefined; for (ps) |p, i| { if (p.is_base) { - @setEvalBranchQuota(10000); - pcs[i] = comptime precompute(Edwards25519.basePoint, 8); + pcs[i] = basePointPc8; } else { pcs[i] = precompute(p, 8); pcs[i][4].rejectIdentity() catch return error.WeakPublicKey; @@ -297,14 +344,14 @@ pub const Edwards25519 = struct { /// This is strongly recommended for DH operations. /// Return error.WeakPublicKey if the resulting point is /// the identity element. - pub fn clampedMul(p: Edwards25519, s: [32]u8) Error!Edwards25519 { + pub fn clampedMul(p: Edwards25519, s: [32]u8) (IdentityElementError || WeakPublicKeyError)!Edwards25519 { var t: [32]u8 = s; scalar.clamp(&t); return mul(p, t); } // montgomery -- recover y = sqrt(x^3 + A*x^2 + x) - fn xmontToYmont(x: Fe) Error!Fe { + fn xmontToYmont(x: Fe) NotSquareError!Fe { var x2 = x.sq(); const x3 = x.mul(x2); x2 = x2.mul32(Fe.edwards25519a_32); @@ -367,7 +414,7 @@ pub const Edwards25519 = struct { fn stringToPoints(comptime n: usize, ctx: []const u8, s: []const u8) [n]Edwards25519 { debug.assert(n <= 2); - const H = std.crypto.hash.sha2.Sha512; + const H = crypto.hash.sha2.Sha512; const h_l: usize = 48; var xctx = ctx; var hctx: [H.digest_length]u8 = undefined; @@ -485,8 +532,8 @@ test "edwards25519 packing/unpacking" { test "edwards25519 point addition/substraction" { var s1: [32]u8 = undefined; var s2: [32]u8 = undefined; - std.crypto.random.bytes(&s1); - std.crypto.random.bytes(&s2); + crypto.random.bytes(&s1); + crypto.random.bytes(&s2); const p = try Edwards25519.basePoint.clampedMul(s1); const q = try Edwards25519.basePoint.clampedMul(s2); const r = p.add(q).add(q).sub(q).sub(q); diff --git a/lib/std/crypto/25519/field.zig b/lib/std/crypto/25519/field.zig index b570e2d06b..5ac184080c 100644 --- a/lib/std/crypto/25519/field.zig +++ b/lib/std/crypto/25519/field.zig @@ -4,9 +4,12 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const std = @import("std"); +const crypto = std.crypto; const readIntLittle = std.mem.readIntLittle; const writeIntLittle = std.mem.writeIntLittle; -const Error = std.crypto.Error; + +const NonCanonicalError = crypto.errors.NonCanonicalError; +const NotSquareError = crypto.errors.NotSquareError; pub const Fe = struct { limbs: [5]u64, @@ -113,7 +116,7 @@ pub const Fe = struct { } /// Reject non-canonical encodings of an element, possibly ignoring the top bit - pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) Error!void { + pub fn rejectNonCanonical(s: [32]u8, comptime ignore_extra_bit: bool) NonCanonicalError!void { var c: u16 = (s[31] & 0x7f) ^ 0x7f; comptime var i = 30; inline while (i > 0) : (i -= 1) { @@ -413,7 +416,7 @@ pub const Fe = struct { } /// Compute the square root of `x2`, returning `error.NotSquare` if `x2` was not a square - pub fn sqrt(x2: Fe) Error!Fe { + pub fn sqrt(x2: Fe) NotSquareError!Fe { var x2_copy = x2; const x = x2.uncheckedSqrt(); const check = x.sq().sub(x2_copy); diff --git a/lib/std/crypto/25519/ristretto255.zig b/lib/std/crypto/25519/ristretto255.zig index 4644b7622e..50f1580a80 100644 --- a/lib/std/crypto/25519/ristretto255.zig +++ b/lib/std/crypto/25519/ristretto255.zig @@ -5,7 +5,11 @@ // and substantial portions of the software. const std = @import("std"); const fmt = std.fmt; -const Error = std.crypto.Error; + +const EncodingError = std.crypto.errors.EncodingError; +const IdentityElementError = std.crypto.errors.IdentityElementError; +const NonCanonicalError = std.crypto.errors.NonCanonicalError; +const WeakPublicKeyError = std.crypto.errors.WeakPublicKeyError; /// Group operations over Edwards25519. pub const Ristretto255 = struct { @@ -35,7 +39,7 @@ pub const Ristretto255 = struct { return .{ .ratio_is_square = @boolToInt(has_m_root) | @boolToInt(has_p_root), .root = x.abs() }; } - fn rejectNonCanonical(s: [encoded_length]u8) Error!void { + fn rejectNonCanonical(s: [encoded_length]u8) NonCanonicalError!void { if ((s[0] & 1) != 0) { return error.NonCanonical; } @@ -43,7 +47,7 @@ pub const Ristretto255 = struct { } /// Reject the neutral element. - pub fn rejectIdentity(p: Ristretto255) callconv(.Inline) Error!void { + pub fn rejectIdentity(p: Ristretto255) callconv(.Inline) IdentityElementError!void { return p.p.rejectIdentity(); } @@ -51,7 +55,7 @@ pub const Ristretto255 = struct { pub const basePoint = Ristretto255{ .p = Curve.basePoint }; /// Decode a Ristretto255 representative. - pub fn fromBytes(s: [encoded_length]u8) Error!Ristretto255 { + pub fn fromBytes(s: [encoded_length]u8) (NonCanonicalError || EncodingError)!Ristretto255 { try rejectNonCanonical(s); const s_ = Fe.fromBytes(s); const ss = s_.sq(); // s^2 @@ -154,7 +158,7 @@ pub const Ristretto255 = struct { /// Multiply a Ristretto255 element with a scalar. /// Return error.WeakPublicKey if the resulting element is /// the identity element. - pub fn mul(p: Ristretto255, s: [encoded_length]u8) callconv(.Inline) Error!Ristretto255 { + pub fn mul(p: Ristretto255, s: [encoded_length]u8) callconv(.Inline) (IdentityElementError || WeakPublicKeyError)!Ristretto255 { return Ristretto255{ .p = try p.p.mul(s) }; } diff --git a/lib/std/crypto/25519/scalar.zig b/lib/std/crypto/25519/scalar.zig index a4bf5aafcf..21578486e8 100644 --- a/lib/std/crypto/25519/scalar.zig +++ b/lib/std/crypto/25519/scalar.zig @@ -5,7 +5,8 @@ // and substantial portions of the software. const std = @import("std"); const mem = std.mem; -const Error = std.crypto.Error; + +const NonCanonicalError = std.crypto.errors.NonCanonicalError; /// 2^252 + 27742317777372353535851937790883648493 pub const field_size = [32]u8{ @@ -19,7 +20,7 @@ pub const CompressedScalar = [32]u8; pub const zero = [_]u8{0} ** 32; /// Reject a scalar whose encoding is not canonical. -pub fn rejectNonCanonical(s: [32]u8) Error!void { +pub fn rejectNonCanonical(s: [32]u8) NonCanonicalError!void { var c: u8 = 0; var n: u8 = 1; var i: usize = 31; diff --git a/lib/std/crypto/25519/x25519.zig b/lib/std/crypto/25519/x25519.zig index 2d53124056..07b1dc7a86 100644 --- a/lib/std/crypto/25519/x25519.zig +++ b/lib/std/crypto/25519/x25519.zig @@ -9,7 +9,10 @@ const mem = std.mem; const fmt = std.fmt; const Sha512 = crypto.hash.sha2.Sha512; -const Error = crypto.Error; + +const EncodingError = crypto.errors.EncodingError; +const IdentityElementError = crypto.errors.IdentityElementError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; /// X25519 DH function. pub const X25519 = struct { @@ -32,7 +35,7 @@ pub const X25519 = struct { secret_key: [secret_length]u8, /// Create a new key pair using an optional seed. - pub fn create(seed: ?[seed_length]u8) Error!KeyPair { + pub fn create(seed: ?[seed_length]u8) IdentityElementError!KeyPair { const sk = seed orelse sk: { var random_seed: [seed_length]u8 = undefined; crypto.random.bytes(&random_seed); @@ -45,7 +48,7 @@ pub const X25519 = struct { } /// Create a key pair from an Ed25519 key pair - pub fn fromEd25519(ed25519_key_pair: crypto.sign.Ed25519.KeyPair) Error!KeyPair { + pub fn fromEd25519(ed25519_key_pair: crypto.sign.Ed25519.KeyPair) (IdentityElementError || EncodingError)!KeyPair { const seed = ed25519_key_pair.secret_key[0..32]; var az: [Sha512.digest_length]u8 = undefined; Sha512.hash(seed, &az, .{}); @@ -60,13 +63,13 @@ pub const X25519 = struct { }; /// Compute the public key for a given private key. - pub fn recoverPublicKey(secret_key: [secret_length]u8) Error![public_length]u8 { + pub fn recoverPublicKey(secret_key: [secret_length]u8) IdentityElementError![public_length]u8 { const q = try Curve.basePoint.clampedMul(secret_key); return q.toBytes(); } /// Compute the X25519 equivalent to an Ed25519 public eky. - pub fn publicKeyFromEd25519(ed25519_public_key: [crypto.sign.Ed25519.public_length]u8) Error![public_length]u8 { + pub fn publicKeyFromEd25519(ed25519_public_key: [crypto.sign.Ed25519.public_length]u8) (IdentityElementError || EncodingError)![public_length]u8 { const pk_ed = try crypto.ecc.Edwards25519.fromBytes(ed25519_public_key); const pk = try Curve.fromEdwards25519(pk_ed); return pk.toBytes(); @@ -75,7 +78,7 @@ pub const X25519 = struct { /// Compute the scalar product of a public key and a secret scalar. /// Note that the output should not be used as a shared secret without /// hashing it first. - pub fn scalarmult(secret_key: [secret_length]u8, public_key: [public_length]u8) Error![shared_length]u8 { + pub fn scalarmult(secret_key: [secret_length]u8, public_key: [public_length]u8) IdentityElementError![shared_length]u8 { const q = try Curve.fromBytes(public_key).clampedMul(secret_key); return q.toBytes(); } diff --git a/lib/std/crypto/aegis.zig b/lib/std/crypto/aegis.zig index 3969d59e10..59dcf04dac 100644 --- a/lib/std/crypto/aegis.zig +++ b/lib/std/crypto/aegis.zig @@ -8,7 +8,7 @@ const std = @import("std"); const mem = std.mem; const assert = std.debug.assert; const AesBlock = std.crypto.core.aes.Block; -const Error = std.crypto.Error; +const AuthenticationError = std.crypto.errors.AuthenticationError; const State128L = struct { blocks: [8]AesBlock, @@ -137,7 +137,7 @@ pub const Aegis128L = struct { /// ad: Associated Data /// npub: public nonce /// k: private key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); var state = State128L.init(key, npub); var src: [32]u8 align(16) = undefined; @@ -299,7 +299,7 @@ pub const Aegis256 = struct { /// ad: Associated Data /// npub: public nonce /// k: private key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); var state = State256.init(key, npub); var src: [16]u8 align(16) = undefined; diff --git a/lib/std/crypto/aes_gcm.zig b/lib/std/crypto/aes_gcm.zig index bcb1b4c5fa..70746af073 100644 --- a/lib/std/crypto/aes_gcm.zig +++ b/lib/std/crypto/aes_gcm.zig @@ -12,7 +12,7 @@ const debug = std.debug; const Ghash = std.crypto.onetimeauth.Ghash; const mem = std.mem; const modes = crypto.core.modes; -const Error = crypto.Error; +const AuthenticationError = crypto.errors.AuthenticationError; pub const Aes128Gcm = AesGcm(crypto.core.aes.Aes128); pub const Aes256Gcm = AesGcm(crypto.core.aes.Aes256); @@ -60,7 +60,7 @@ fn AesGcm(comptime Aes: anytype) type { } } - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); const aes = Aes.initEnc(key); diff --git a/lib/std/crypto/aes_ocb.zig b/lib/std/crypto/aes_ocb.zig index 9eb0561d9f..658b3b97ce 100644 --- a/lib/std/crypto/aes_ocb.zig +++ b/lib/std/crypto/aes_ocb.zig @@ -10,7 +10,7 @@ const aes = crypto.core.aes; const assert = std.debug.assert; const math = std.math; const mem = std.mem; -const Error = crypto.Error; +const AuthenticationError = crypto.errors.AuthenticationError; pub const Aes128Ocb = AesOcb(aes.Aes128); pub const Aes256Ocb = AesOcb(aes.Aes256); @@ -179,7 +179,7 @@ fn AesOcb(comptime Aes: anytype) type { /// ad: Associated Data /// npub: public nonce /// k: secret key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); const aes_enc_ctx = Aes.initEnc(key); diff --git a/lib/std/crypto/bcrypt.zig b/lib/std/crypto/bcrypt.zig index d00108b9c4..51fb144b2f 100644 --- a/lib/std/crypto/bcrypt.zig +++ b/lib/std/crypto/bcrypt.zig @@ -12,7 +12,8 @@ const mem = std.mem; const debug = std.debug; const testing = std.testing; const utils = crypto.utils; -const Error = crypto.Error; +const EncodingError = crypto.errors.EncodingError; +const PasswordVerificationError = crypto.errors.PasswordVerificationError; const salt_length: usize = 16; const salt_str_length: usize = 22; @@ -179,7 +180,7 @@ const Codec = struct { debug.assert(j == b64.len); } - fn decode(bin: []u8, b64: []const u8) Error!void { + fn decode(bin: []u8, b64: []const u8) EncodingError!void { var i: usize = 0; var j: usize = 0; while (j < bin.len) { @@ -204,7 +205,7 @@ const Codec = struct { } }; -fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) Error![hash_length]u8 { +fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) ![hash_length]u8 { var state = State{}; var password_buf: [73]u8 = undefined; const trimmed_len = math.min(password.len, password_buf.len - 1); @@ -252,14 +253,14 @@ fn strHashInternal(password: []const u8, rounds_log: u6, salt: [salt_length]u8) /// IMPORTANT: by design, bcrypt silently truncates passwords to 72 bytes. /// If this is an issue for your application, hash the password first using a function such as SHA-512, /// and then use the resulting hash as the password parameter for bcrypt. -pub fn strHash(password: []const u8, rounds_log: u6) Error![hash_length]u8 { +pub fn strHash(password: []const u8, rounds_log: u6) ![hash_length]u8 { var salt: [salt_length]u8 = undefined; crypto.random.bytes(&salt); return strHashInternal(password, rounds_log, salt); } /// Verify that a previously computed hash is valid for a given password. -pub fn strVerify(h: [hash_length]u8, password: []const u8) Error!void { +pub fn strVerify(h: [hash_length]u8, password: []const u8) (EncodingError || PasswordVerificationError)!void { if (!mem.eql(u8, "$2", h[0..2])) return error.InvalidEncoding; if (h[3] != '$' or h[6] != '$') return error.InvalidEncoding; const rounds_log_str = h[4..][0..2]; diff --git a/lib/std/crypto/chacha20.zig b/lib/std/crypto/chacha20.zig index e1fe3e232d..ea9eafd356 100644 --- a/lib/std/crypto/chacha20.zig +++ b/lib/std/crypto/chacha20.zig @@ -13,7 +13,7 @@ const testing = std.testing; const maxInt = math.maxInt; const Vector = std.meta.Vector; const Poly1305 = std.crypto.onetimeauth.Poly1305; -const Error = std.crypto.Error; +const AuthenticationError = std.crypto.errors.AuthenticationError; /// IETF-variant of the ChaCha20 stream cipher, as designed for TLS. pub const ChaCha20IETF = ChaChaIETF(20); @@ -521,7 +521,7 @@ fn ChaChaPoly1305(comptime rounds_nb: usize) type { /// npub: public nonce /// k: private key /// NOTE: the check of the authentication tag is currently not done in constant time - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); var polyKey = [_]u8{0} ** 32; @@ -583,7 +583,7 @@ fn XChaChaPoly1305(comptime rounds_nb: usize) type { /// ad: Associated Data /// npub: public nonce /// k: private key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { const extended = extend(k, npub, rounds_nb); return ChaChaPoly1305(rounds_nb).decrypt(m, c, tag, ad, extended.nonce, extended.key); } diff --git a/lib/std/crypto/error.zig b/lib/std/crypto/error.zig deleted file mode 100644 index 4cb12bb8f7..0000000000 --- a/lib/std/crypto/error.zig +++ /dev/null @@ -1,34 +0,0 @@ -pub const Error = error{ - /// MAC verification failed - The tag doesn't verify for the given ciphertext and secret key - AuthenticationFailed, - - /// The requested output length is too long for the chosen algorithm - OutputTooLong, - - /// Finite field operation returned the identity element - IdentityElement, - - /// Encoded input cannot be decoded - InvalidEncoding, - - /// The signature does't verify for the given message and public key - SignatureVerificationFailed, - - /// Both a public and secret key have been provided, but they are incompatible - KeyMismatch, - - /// Encoded input is not in canonical form - NonCanonical, - - /// Square root has no solutions - NotSquare, - - /// Verification string doesn't match the provided password and parameters - PasswordVerificationFailed, - - /// Parameters would be insecure to use - WeakParameters, - - /// Public key would be insecure to use - WeakPublicKey, -}; diff --git a/lib/std/crypto/errors.zig b/lib/std/crypto/errors.zig new file mode 100644 index 0000000000..4d79055919 --- /dev/null +++ b/lib/std/crypto/errors.zig @@ -0,0 +1,35 @@ +/// MAC verification failed - The tag doesn't verify for the given ciphertext and secret key +pub const AuthenticationError = error{AuthenticationFailed}; + +/// The requested output length is too long for the chosen algorithm +pub const OutputTooLongError = error{OutputTooLong}; + +/// Finite field operation returned the identity element +pub const IdentityElementError = error{IdentityElement}; + +/// Encoded input cannot be decoded +pub const EncodingError = error{InvalidEncoding}; + +/// The signature does't verify for the given message and public key +pub const SignatureVerificationError = error{SignatureVerificationFailed}; + +/// Both a public and secret key have been provided, but they are incompatible +pub const KeyMismatchError = error{KeyMismatch}; + +/// Encoded input is not in canonical form +pub const NonCanonicalError = error{NonCanonical}; + +/// Square root has no solutions +pub const NotSquareError = error{NotSquare}; + +/// Verification string doesn't match the provided password and parameters +pub const PasswordVerificationError = error{PasswordVerificationFailed}; + +/// Parameters would be insecure to use +pub const WeakParametersError = error{WeakParameters}; + +/// Public key would be insecure to use +pub const WeakPublicKeyError = error{WeakPublicKey}; + +/// Any error related to cryptography operations +pub const Error = AuthenticationError || OutputTooLongError || IdentityElementError || EncodingError || SignatureVerificationError || KeyMismatchError || NonCanonicalError || NotSquareError || PasswordVerificationError || WeakParametersError || WeakPublicKeyError; diff --git a/lib/std/crypto/gimli.zig b/lib/std/crypto/gimli.zig index 111e0c5274..fb67c25343 100644 --- a/lib/std/crypto/gimli.zig +++ b/lib/std/crypto/gimli.zig @@ -20,7 +20,7 @@ const assert = std.debug.assert; const testing = std.testing; const htest = @import("test.zig"); const Vector = std.meta.Vector; -const Error = std.crypto.Error; +const AuthenticationError = std.crypto.errors.AuthenticationError; pub const State = struct { pub const BLOCKBYTES = 48; @@ -393,7 +393,7 @@ pub const Aead = struct { /// npub: public nonce /// k: private key /// NOTE: the check of the authentication tag is currently not done in constant time - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { assert(c.len == m.len); var state = Aead.init(ad, npub, k); diff --git a/lib/std/crypto/isap.zig b/lib/std/crypto/isap.zig index 5219742d85..6deb4977bb 100644 --- a/lib/std/crypto/isap.zig +++ b/lib/std/crypto/isap.zig @@ -3,7 +3,7 @@ const debug = std.debug; const mem = std.mem; const math = std.math; const testing = std.testing; -const Error = std.crypto.Error; +const AuthenticationError = std.crypto.errors.AuthenticationError; /// ISAPv2 is an authenticated encryption system hardened against side channels and fault attacks. /// https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/round-2/spec-doc-rnd2/isap-spec-round2.pdf @@ -218,7 +218,7 @@ pub const IsapA128A = struct { tag.* = mac(c, ad, npub, key); } - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, key: [key_length]u8) AuthenticationError!void { var computed_tag = mac(c, ad, npub, key); var acc: u8 = 0; for (computed_tag) |_, j| { diff --git a/lib/std/crypto/pbkdf2.zig b/lib/std/crypto/pbkdf2.zig index 575fb83006..f93a5235af 100644 --- a/lib/std/crypto/pbkdf2.zig +++ b/lib/std/crypto/pbkdf2.zig @@ -7,7 +7,8 @@ const std = @import("std"); const mem = std.mem; const maxInt = std.math.maxInt; -const Error = std.crypto.Error; +const OutputTooLongError = std.crypto.errors.OutputTooLongError; +const WeakParametersError = std.crypto.errors.WeakParametersError; // RFC 2898 Section 5.2 // @@ -55,7 +56,7 @@ const Error = std.crypto.Error; /// the dk. It is common to tune this parameter to achieve approximately 100ms. /// /// Prf: Pseudo-random function to use. A common choice is `std.crypto.auth.hmac.HmacSha256`. -pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) Error!void { +pub fn pbkdf2(dk: []u8, password: []const u8, salt: []const u8, rounds: u32, comptime Prf: type) (WeakParametersError || OutputTooLongError)!void { if (rounds < 1) return error.WeakParameters; const dk_len = dk.len; diff --git a/lib/std/crypto/salsa20.zig b/lib/std/crypto/salsa20.zig index 006767c93f..2a06944adc 100644 --- a/lib/std/crypto/salsa20.zig +++ b/lib/std/crypto/salsa20.zig @@ -15,7 +15,10 @@ const Vector = std.meta.Vector; const Poly1305 = crypto.onetimeauth.Poly1305; const Blake2b = crypto.hash.blake2.Blake2b; const X25519 = crypto.dh.X25519; -const Error = crypto.Error; + +const AuthenticationError = crypto.errors.AuthenticationError; +const IdentityElementError = crypto.errors.IdentityElementError; +const WeakPublicKeyError = crypto.errors.WeakPublicKeyError; const Salsa20VecImpl = struct { const Lane = Vector(4, u32); @@ -399,7 +402,7 @@ pub const XSalsa20Poly1305 = struct { /// ad: Associated Data /// npub: public nonce /// k: private key - pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn decrypt(m: []u8, c: []const u8, tag: [tag_length]u8, ad: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { debug.assert(c.len == m.len); const extended = extend(k, npub); var block0 = [_]u8{0} ** 64; @@ -447,7 +450,7 @@ pub const SecretBox = struct { /// Verify and decrypt `c` using a nonce `npub` and a key `k`. /// `m` must be exactly `tag_length` smaller than `c`, as `c` includes an authentication tag in addition to the encrypted message. - pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, k: [key_length]u8) Error!void { + pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, k: [key_length]u8) AuthenticationError!void { if (c.len < tag_length) { return error.AuthenticationFailed; } @@ -482,20 +485,20 @@ pub const Box = struct { pub const KeyPair = X25519.KeyPair; /// Compute a secret suitable for `secretbox` given a recipent's public key and a sender's secret key. - pub fn createSharedSecret(public_key: [public_length]u8, secret_key: [secret_length]u8) Error![shared_length]u8 { + pub fn createSharedSecret(public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError)![shared_length]u8 { const p = try X25519.scalarmult(secret_key, public_key); const zero = [_]u8{0} ** 16; return Salsa20Impl.hsalsa20(zero, p); } /// Encrypt and authenticate a message using a recipient's public key `public_key` and a sender's `secret_key`. - pub fn seal(c: []u8, m: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) Error!void { + pub fn seal(c: []u8, m: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError)!void { const shared_key = try createSharedSecret(public_key, secret_key); return SecretBox.seal(c, m, npub, shared_key); } /// Verify and decrypt a message using a recipient's secret key `public_key` and a sender's `public_key`. - pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) Error!void { + pub fn open(m: []u8, c: []const u8, npub: [nonce_length]u8, public_key: [public_length]u8, secret_key: [secret_length]u8) (IdentityElementError || WeakPublicKeyError || AuthenticationError)!void { const shared_key = try createSharedSecret(public_key, secret_key); return SecretBox.open(m, c, npub, shared_key); } @@ -528,7 +531,7 @@ pub const SealedBox = struct { /// Encrypt a message `m` for a recipient whose public key is `public_key`. /// `c` must be `seal_length` bytes larger than `m`, so that the required metadata can be added. - pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) Error!void { + pub fn seal(c: []u8, m: []const u8, public_key: [public_length]u8) (WeakPublicKeyError || IdentityElementError)!void { debug.assert(c.len == m.len + seal_length); var ekp = try KeyPair.create(null); const nonce = createNonce(ekp.public_key, public_key); @@ -539,7 +542,7 @@ pub const SealedBox = struct { /// Decrypt a message using a key pair. /// `m` must be exactly `seal_length` bytes smaller than `c`, as `c` also includes metadata. - pub fn open(m: []u8, c: []const u8, keypair: KeyPair) Error!void { + pub fn open(m: []u8, c: []const u8, keypair: KeyPair) (IdentityElementError || WeakPublicKeyError || AuthenticationError)!void { if (c.len < seal_length) { return error.AuthenticationFailed; } diff --git a/lib/std/event/rwlock.zig b/lib/std/event/rwlock.zig index d981956c77..5cdb0a92df 100644 --- a/lib/std/event/rwlock.zig +++ b/lib/std/event/rwlock.zig @@ -264,7 +264,7 @@ var shared_test_data = [1]i32{0} ** 10; var shared_test_index: usize = 0; var shared_count: usize = 0; fn writeRunner(lock: *RwLock) callconv(.Async) void { - suspend; // resumed by onNextTick + suspend {} // resumed by onNextTick var i: usize = 0; while (i < shared_test_data.len) : (i += 1) { @@ -281,7 +281,7 @@ fn writeRunner(lock: *RwLock) callconv(.Async) void { } } fn readRunner(lock: *RwLock) callconv(.Async) void { - suspend; // resumed by onNextTick + suspend {} // resumed by onNextTick std.time.sleep(1); var i: usize = 0; diff --git a/lib/std/math.zig b/lib/std/math.zig index cb6f3cec1f..f6dfc6ec78 100644 --- a/lib/std/math.zig +++ b/lib/std/math.zig @@ -1349,15 +1349,6 @@ pub fn boolMask(comptime MaskInt: type, value: bool) callconv(.Inline) MaskInt { return @bitCast(i1, @as(u1, @boolToInt(value))); } - // At comptime, -% is disallowed on unsigned values. - // So we need to jump through some hoops in that case. - // This is a workaround for #7951 - if (@typeInfo(@TypeOf(.{value})).Struct.fields[0].is_comptime) { - // Since it's comptime, we don't need this to generate nice code. - // We can just do a branch here. - return if (value) ~@as(MaskInt, 0) else 0; - } - return -%@intCast(MaskInt, @boolToInt(value)); } diff --git a/lib/std/math/sqrt.zig b/lib/std/math/sqrt.zig index c9e999e1d1..2cca9bf836 100644 --- a/lib/std/math/sqrt.zig +++ b/lib/std/math/sqrt.zig @@ -38,7 +38,13 @@ pub fn sqrt(x: anytype) Sqrt(@TypeOf(x)) { } } -fn sqrt_int(comptime T: type, value: T) std.meta.Int(.unsigned, @typeInfo(T).Int.bits / 2) { +fn sqrt_int(comptime T: type, value: T) Sqrt(T) { + switch (T) { + u0 => return 0, + u1 => return value, + else => {}, + } + var op = value; var res: T = 0; var one: T = 1 << (@typeInfo(T).Int.bits - 2); @@ -57,11 +63,13 @@ fn sqrt_int(comptime T: type, value: T) std.meta.Int(.unsigned, @typeInfo(T).Int one >>= 2; } - const ResultType = std.meta.Int(.unsigned, @typeInfo(T).Int.bits / 2); + const ResultType = Sqrt(T); return @intCast(ResultType, res); } test "math.sqrt_int" { + expect(sqrt_int(u0, 0) == 0); + expect(sqrt_int(u1, 1) == 1); expect(sqrt_int(u32, 3) == 1); expect(sqrt_int(u32, 4) == 2); expect(sqrt_int(u32, 5) == 2); @@ -73,7 +81,13 @@ test "math.sqrt_int" { /// Returns the return type `sqrt` will return given an operand of type `T`. pub fn Sqrt(comptime T: type) type { return switch (@typeInfo(T)) { - .Int => |int| std.meta.Int(.unsigned, int.bits / 2), + .Int => |int| { + return switch (int.bits) { + 0 => u0, + 1 => u1, + else => std.meta.Int(.unsigned, int.bits / 2), + }; + }, else => T, }; } diff --git a/lib/std/meta.zig b/lib/std/meta.zig index a67e04431a..83ef3f83e0 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -884,7 +884,7 @@ pub fn Vector(comptime len: u32, comptime child: type) type { /// Given a type and value, cast the value to the type as c would. /// This is for translate-c and is not intended for general use. pub fn cast(comptime DestType: type, target: anytype) DestType { - // this function should behave like transCCast in translate-c, except it's for macros + // this function should behave like transCCast in translate-c, except it's for macros and enums const SourceType = @TypeOf(target); switch (@typeInfo(DestType)) { .Pointer => { @@ -921,9 +921,10 @@ pub fn cast(comptime DestType: type, target: anytype) DestType { } } }, - .Enum => { + .Enum => |enum_type| { if (@typeInfo(SourceType) == .Int or @typeInfo(SourceType) == .ComptimeInt) { - return @intToEnum(DestType, target); + const intermediate = cast(enum_type.tag_type, target); + return @intToEnum(DestType, intermediate); } }, .Int => { @@ -1011,6 +1012,17 @@ test "std.meta.cast" { testing.expectEqual(@intToPtr(*u8, 2), cast(*u8, @intToPtr(*volatile u8, 2))); testing.expectEqual(@intToPtr(?*c_void, 2), cast(?*c_void, @intToPtr(*u8, 2))); + + const C_ENUM = extern enum(c_int) { + A = 0, + B, + C, + _, + }; + testing.expectEqual(cast(C_ENUM, @as(i64, -1)), @intToEnum(C_ENUM, -1)); + testing.expectEqual(cast(C_ENUM, @as(i8, 1)), .B); + testing.expectEqual(cast(C_ENUM, @as(u64, 1)), .B); + testing.expectEqual(cast(C_ENUM, @as(u64, 42)), @intToEnum(C_ENUM, 42)); } /// Given a value returns its size as C's sizeof operator would. diff --git a/lib/std/os/bits/linux/powerpc.zig b/lib/std/os/bits/linux/powerpc.zig index 67be4146c4..2f86fb892a 100644 --- a/lib/std/os/bits/linux/powerpc.zig +++ b/lib/std/os/bits/linux/powerpc.zig @@ -557,18 +557,10 @@ pub const kernel_stat = extern struct { size: off_t, blksize: blksize_t, blocks: blkcnt_t, - __atim32: timespec32, - __mtim32: timespec32, - __ctim32: timespec32, - __unused: [2]u32, atim: timespec, mtim: timespec, ctim: timespec, - - const timespec32 = extern struct { - tv_sec: i32, - tv_nsec: i32, - }; + __unused: [2]u32, pub fn atime(self: @This()) timespec { return self.atim; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 555a67cd55..d34e7d16af 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -53,6 +53,7 @@ pub fn getauxval(index: usize) usize { // Some architectures (and some syscalls) require 64bit parameters to be passed // in a even-aligned register pair. const require_aligned_register_pair = + std.Target.current.cpu.arch.isPPC() or std.Target.current.cpu.arch.isMIPS() or std.Target.current.cpu.arch.isARM() or std.Target.current.cpu.arch.isThumb(); @@ -633,7 +634,7 @@ pub fn tkill(tid: pid_t, sig: i32) usize { } pub fn tgkill(tgid: pid_t, tid: pid_t, sig: i32) usize { - return syscall2(.tgkill, @bitCast(usize, @as(isize, tgid)), @bitCast(usize, @as(isize, tid)), @bitCast(usize, @as(isize, sig))); + return syscall3(.tgkill, @bitCast(usize, @as(isize, tgid)), @bitCast(usize, @as(isize, tid)), @bitCast(usize, @as(isize, sig))); } pub fn link(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) usize { @@ -1386,6 +1387,53 @@ pub fn madvise(address: [*]u8, len: usize, advice: u32) usize { return syscall3(.madvise, @ptrToInt(address), len, advice); } +pub fn pidfd_open(pid: pid_t, flags: u32) usize { + return syscall2(.pidfd_open, @bitCast(usize, @as(isize, pid)), flags); +} + +pub fn pidfd_getfd(pidfd: fd_t, targetfd: fd_t, flags: u32) usize { + return syscall3( + .pidfd_getfd, + @bitCast(usize, @as(isize, pidfd)), + @bitCast(usize, @as(isize, targetfd)), + flags, + ); +} + +pub fn pidfd_send_signal(pidfd: fd_t, sig: i32, info: ?*siginfo_t, flags: u32) usize { + return syscall4( + .pidfd_send_signal, + @bitCast(usize, @as(isize, pidfd)), + @bitCast(usize, @as(isize, sig)), + @ptrToInt(info), + flags, + ); +} + +pub fn process_vm_readv(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize { + return syscall6( + .process_vm_readv, + @bitCast(usize, @as(isize, pid)), + @ptrToInt(local), + local_count, + @ptrToInt(remote), + remote_count, + flags, + ); +} + +pub fn process_vm_writev(pid: pid_t, local: [*]const iovec, local_count: usize, remote: [*]const iovec, remote_count: usize, flags: usize) usize { + return syscall6( + .process_vm_writev, + @bitCast(usize, @as(isize, pid)), + @ptrToInt(local), + local_count, + @ptrToInt(remote), + remote_count, + flags, + ); +} + test { if (std.Target.current.os.tag == .linux) { _ = @import("linux/test.zig"); diff --git a/lib/std/os/linux/bpf/btf.zig b/lib/std/os/linux/bpf/btf.zig index b28f65945a..9ba3a0f942 100644 --- a/lib/std/os/linux/bpf/btf.zig +++ b/lib/std/os/linux/bpf/btf.zig @@ -6,7 +6,7 @@ const magic = 0xeb9f; const version = 1; -pub const ext = @import("ext.zig"); +pub const ext = @import("btf_ext.zig"); /// All offsets are in bytes relative to the end of this header pub const Header = packed struct { diff --git a/lib/std/os/windows/user32.zig b/lib/std/os/windows/user32.zig index c03ba939ba..4694be6ece 100644 --- a/lib/std/os/windows/user32.zig +++ b/lib/std/os/windows/user32.zig @@ -663,7 +663,7 @@ pub fn messageBoxA(hWnd: ?HWND, lpText: [*:0]const u8, lpCaption: [*:0]const u8, pub extern "user32" fn MessageBoxW(hWnd: ?HWND, lpText: [*:0]const u16, lpCaption: ?[*:0]const u16, uType: UINT) callconv(WINAPI) i32; pub var pfnMessageBoxW: @TypeOf(MessageBoxW) = undefined; pub fn messageBoxW(hWnd: ?HWND, lpText: [*:0]const u16, lpCaption: [*:0]const u16, uType: u32) !i32 { - const function = selectSymbol(pfnMessageBoxW, MessageBoxW, .win2k); + const function = selectSymbol(MessageBoxW, pfnMessageBoxW, .win2k); const value = function(hWnd, lpText, lpCaption, uType); if (value != 0) return value; switch (GetLastError()) { diff --git a/lib/std/special/c.zig b/lib/std/special/c.zig index fac9d4b827..8627e976d7 100644 --- a/lib/std/special/c.zig +++ b/lib/std/special/c.zig @@ -88,7 +88,7 @@ test "strncpy" { var s1: [9:0]u8 = undefined; s1[0] = 0; - _ = strncpy(&s1, "foobarbaz", 9); + _ = strncpy(&s1, "foobarbaz", @sizeOf(@TypeOf(s1))); std.testing.expectEqualSlices(u8, "foobarbaz", std.mem.spanZ(&s1)); } @@ -242,7 +242,7 @@ export fn memcmp(vl: ?[*]const u8, vr: ?[*]const u8, n: usize) callconv(.C) isiz return 0; } -test "test_memcmp" { +test "memcmp" { const base_arr = &[_]u8{ 1, 1, 1 }; const arr1 = &[_]u8{ 1, 1, 1 }; const arr2 = &[_]u8{ 1, 0, 1 }; @@ -266,7 +266,7 @@ export fn bcmp(vl: [*]allowzero const u8, vr: [*]allowzero const u8, n: usize) c return 0; } -test "test_bcmp" { +test "bcmp" { const base_arr = &[_]u8{ 1, 1, 1 }; const arr1 = &[_]u8{ 1, 1, 1 }; const arr2 = &[_]u8{ 1, 0, 1 }; @@ -862,6 +862,85 @@ fn generic_fmod(comptime T: type, x: T, y: T) T { return @bitCast(T, ux); } +test "fmod, fmodf" { + inline for ([_]type{ f32, f64 }) |T| { + const nan_val = math.nan(T); + const inf_val = math.inf(T); + + std.testing.expect(isNan(generic_fmod(T, nan_val, 1.0))); + std.testing.expect(isNan(generic_fmod(T, 1.0, nan_val))); + std.testing.expect(isNan(generic_fmod(T, inf_val, 1.0))); + std.testing.expect(isNan(generic_fmod(T, 0.0, 0.0))); + std.testing.expect(isNan(generic_fmod(T, 1.0, 0.0))); + + std.testing.expectEqual(@as(T, 0.0), generic_fmod(T, 0.0, 2.0)); + std.testing.expectEqual(@as(T, -0.0), generic_fmod(T, -0.0, 2.0)); + + std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, 10.0)); + std.testing.expectEqual(@as(T, -2.0), generic_fmod(T, -32.0, -10.0)); + std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, 10.0)); + std.testing.expectEqual(@as(T, 2.0), generic_fmod(T, 32.0, -10.0)); + } +} + +fn generic_fmin(comptime T: type, x: T, y: T) T { + if (isNan(x)) + return y; + if (isNan(y)) + return x; + return if (x < y) x else y; +} + +export fn fminf(x: f32, y: f32) callconv(.C) f32 { + return generic_fmin(f32, x, y); +} + +export fn fmin(x: f64, y: f64) callconv(.C) f64 { + return generic_fmin(f64, x, y); +} + +test "fmin, fminf" { + inline for ([_]type{ f32, f64 }) |T| { + const nan_val = math.nan(T); + + std.testing.expect(isNan(generic_fmin(T, nan_val, nan_val))); + std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, nan_val, 1.0)); + std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, nan_val)); + + std.testing.expectEqual(@as(T, 1.0), generic_fmin(T, 1.0, 10.0)); + std.testing.expectEqual(@as(T, -1.0), generic_fmin(T, 1.0, -1.0)); + } +} + +fn generic_fmax(comptime T: type, x: T, y: T) T { + if (isNan(x)) + return y; + if (isNan(y)) + return x; + return if (x < y) y else x; +} + +export fn fmaxf(x: f32, y: f32) callconv(.C) f32 { + return generic_fmax(f32, x, y); +} + +export fn fmax(x: f64, y: f64) callconv(.C) f64 { + return generic_fmax(f64, x, y); +} + +test "fmax, fmaxf" { + inline for ([_]type{ f32, f64 }) |T| { + const nan_val = math.nan(T); + + std.testing.expect(isNan(generic_fmax(T, nan_val, nan_val))); + std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, nan_val, 1.0)); + std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, nan_val)); + + std.testing.expectEqual(@as(T, 10.0), generic_fmax(T, 1.0, 10.0)); + std.testing.expectEqual(@as(T, 1.0), generic_fmax(T, 1.0, -1.0)); + } +} + // NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound // behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are // potentially some edge cases remaining that are not handled in the same way. @@ -996,25 +1075,32 @@ export fn sqrt(x: f64) f64 { } test "sqrt" { - const epsilon = 0.000001; + const V = [_]f64{ + 0.0, + 4.089288054930154, + 7.538757127071935, + 8.97780793672623, + 5.304443821913729, + 5.682408965311888, + 0.5846878579110049, + 3.650338664297043, + 0.3178091951800732, + 7.1505232436382835, + 3.6589165881946464, + }; - std.testing.expect(sqrt(0.0) == 0.0); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(2.0), 1.414214, epsilon)); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(3.6), 1.897367, epsilon)); - std.testing.expect(sqrt(4.0) == 2.0); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(7.539840), 2.745877, epsilon)); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(19.230934), 4.385309, epsilon)); - std.testing.expect(sqrt(64.0) == 8.0); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(64.1), 8.006248, epsilon)); - std.testing.expect(std.math.approxEqAbs(f64, sqrt(8942.230469), 94.563367, epsilon)); + // Note that @sqrt will either generate the sqrt opcode (if supported by the + // target ISA) or a call to `sqrtf` otherwise. + for (V) |val| + std.testing.expectEqual(@sqrt(val), sqrt(val)); } test "sqrt special" { std.testing.expect(std.math.isPositiveInf(sqrt(std.math.inf(f64)))); std.testing.expect(sqrt(0.0) == 0.0); std.testing.expect(sqrt(-0.0) == -0.0); - std.testing.expect(std.math.isNan(sqrt(-1.0))); - std.testing.expect(std.math.isNan(sqrt(std.math.nan(f64)))); + std.testing.expect(isNan(sqrt(-1.0))); + std.testing.expect(isNan(sqrt(std.math.nan(f64)))); } export fn sqrtf(x: f32) f32 { @@ -1094,23 +1180,30 @@ export fn sqrtf(x: f32) f32 { } test "sqrtf" { - const epsilon = 0.000001; + const V = [_]f32{ + 0.0, + 4.089288054930154, + 7.538757127071935, + 8.97780793672623, + 5.304443821913729, + 5.682408965311888, + 0.5846878579110049, + 3.650338664297043, + 0.3178091951800732, + 7.1505232436382835, + 3.6589165881946464, + }; - std.testing.expect(sqrtf(0.0) == 0.0); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(2.0), 1.414214, epsilon)); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(3.6), 1.897367, epsilon)); - std.testing.expect(sqrtf(4.0) == 2.0); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(7.539840), 2.745877, epsilon)); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(19.230934), 4.385309, epsilon)); - std.testing.expect(sqrtf(64.0) == 8.0); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(64.1), 8.006248, epsilon)); - std.testing.expect(std.math.approxEqAbs(f32, sqrtf(8942.230469), 94.563370, epsilon)); + // Note that @sqrt will either generate the sqrt opcode (if supported by the + // target ISA) or a call to `sqrtf` otherwise. + for (V) |val| + std.testing.expectEqual(@sqrt(val), sqrtf(val)); } test "sqrtf special" { std.testing.expect(std.math.isPositiveInf(sqrtf(std.math.inf(f32)))); std.testing.expect(sqrtf(0.0) == 0.0); std.testing.expect(sqrtf(-0.0) == -0.0); - std.testing.expect(std.math.isNan(sqrtf(-1.0))); - std.testing.expect(std.math.isNan(sqrtf(std.math.nan(f32)))); + std.testing.expect(isNan(sqrtf(-1.0))); + std.testing.expect(isNan(sqrtf(std.math.nan(f32)))); } diff --git a/lib/std/special/compiler_rt.zig b/lib/std/special/compiler_rt.zig index 90db855fde..a13097765e 100644 --- a/lib/std/special/compiler_rt.zig +++ b/lib/std/special/compiler_rt.zig @@ -116,9 +116,11 @@ comptime { @export(@import("compiler_rt/extendXfYf2.zig").__extenddftf2, .{ .name = "__extenddftf2", .linkage = linkage }); @export(@import("compiler_rt/extendXfYf2.zig").__extendsftf2, .{ .name = "__extendsftf2", .linkage = linkage }); @export(@import("compiler_rt/extendXfYf2.zig").__extendhfsf2, .{ .name = "__extendhfsf2", .linkage = linkage }); + @export(@import("compiler_rt/extendXfYf2.zig").__extendhftf2, .{ .name = "__extendhftf2", .linkage = linkage }); @export(@import("compiler_rt/truncXfYf2.zig").__truncsfhf2, .{ .name = "__truncsfhf2", .linkage = linkage }); @export(@import("compiler_rt/truncXfYf2.zig").__truncdfhf2, .{ .name = "__truncdfhf2", .linkage = linkage }); + @export(@import("compiler_rt/truncXfYf2.zig").__trunctfhf2, .{ .name = "__trunctfhf2", .linkage = linkage }); @export(@import("compiler_rt/truncXfYf2.zig").__trunctfdf2, .{ .name = "__trunctfdf2", .linkage = linkage }); @export(@import("compiler_rt/truncXfYf2.zig").__trunctfsf2, .{ .name = "__trunctfsf2", .linkage = linkage }); @@ -299,7 +301,7 @@ comptime { @export(@import("compiler_rt/sparc.zig")._Qp_qtod, .{ .name = "_Qp_qtod", .linkage = linkage }); } - if (arch == .powerpc or arch.isPPC64()) { + if ((arch == .powerpc or arch.isPPC64()) and !is_test) { @export(@import("compiler_rt/addXf3.zig").__addtf3, .{ .name = "__addkf3", .linkage = linkage }); @export(@import("compiler_rt/addXf3.zig").__subtf3, .{ .name = "__subkf3", .linkage = linkage }); @export(@import("compiler_rt/mulXf3.zig").__multf3, .{ .name = "__mulkf3", .linkage = linkage }); diff --git a/lib/std/special/compiler_rt/extendXfYf2.zig b/lib/std/special/compiler_rt/extendXfYf2.zig index c5b93fa51e..59be8441fa 100644 --- a/lib/std/special/compiler_rt/extendXfYf2.zig +++ b/lib/std/special/compiler_rt/extendXfYf2.zig @@ -23,6 +23,10 @@ pub fn __extendhfsf2(a: u16) callconv(.C) f32 { return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f32, f16, a }); } +pub fn __extendhftf2(a: u16) callconv(.C) f128 { + return @call(.{ .modifier = .always_inline }, extendXfYf2, .{ f128, f16, a }); +} + pub fn __aeabi_h2f(arg: u16) callconv(.AAPCS) f32 { @setRuntimeSafety(false); return @call(.{ .modifier = .always_inline }, __extendhfsf2, .{arg}); diff --git a/lib/std/special/compiler_rt/extendXfYf2_test.zig b/lib/std/special/compiler_rt/extendXfYf2_test.zig index 6a3f69d8e9..f05d33eac3 100644 --- a/lib/std/special/compiler_rt/extendXfYf2_test.zig +++ b/lib/std/special/compiler_rt/extendXfYf2_test.zig @@ -4,9 +4,10 @@ // The MIT license requires this copyright notice to be included in all copies // and substantial portions of the software. const builtin = @import("builtin"); -const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; const __extendhfsf2 = @import("extendXfYf2.zig").__extendhfsf2; +const __extendhftf2 = @import("extendXfYf2.zig").__extendhftf2; const __extendsftf2 = @import("extendXfYf2.zig").__extendsftf2; +const __extenddftf2 = @import("extendXfYf2.zig").__extenddftf2; fn test__extenddftf2(a: f64, expectedHi: u64, expectedLo: u64) void { const x = __extenddftf2(a); @@ -161,3 +162,49 @@ fn makeNaN32(rand: u32) f32 { fn makeInf32() f32 { return @bitCast(f32, @as(u32, 0x7f800000)); } + +fn test__extendhftf2(a: u16, expectedHi: u64, expectedLo: u64) void { + const x = __extendhftf2(a); + + const rep = @bitCast(u128, x); + const hi = @intCast(u64, rep >> 64); + const lo = @truncate(u64, rep); + + if (hi == expectedHi and lo == expectedLo) + return; + + // test other possible NaN representation(signal NaN) + if (expectedHi == 0x7fff800000000000 and expectedLo == 0x0) { + if ((hi & 0x7fff000000000000) == 0x7fff000000000000 and + ((hi & 0xffffffffffff) > 0 or lo > 0)) + { + return; + } + } + + @panic("__extendhftf2 test failure"); +} + +test "extendhftf2" { + // qNaN + test__extendhftf2(0x7e00, 0x7fff800000000000, 0x0); + // NaN + test__extendhftf2(0x7d00, 0x7fff400000000000, 0x0); + // inf + test__extendhftf2(0x7c00, 0x7fff000000000000, 0x0); + test__extendhftf2(0xfc00, 0xffff000000000000, 0x0); + // zero + test__extendhftf2(0x0000, 0x0000000000000000, 0x0); + test__extendhftf2(0x8000, 0x8000000000000000, 0x0); + // denormal + test__extendhftf2(0x0010, 0x3feb000000000000, 0x0); + test__extendhftf2(0x0001, 0x3fe7000000000000, 0x0); + test__extendhftf2(0x8001, 0xbfe7000000000000, 0x0); + + // pi + test__extendhftf2(0x4248, 0x4000920000000000, 0x0); + test__extendhftf2(0xc248, 0xc000920000000000, 0x0); + + test__extendhftf2(0x508c, 0x4004230000000000, 0x0); + test__extendhftf2(0x1bb7, 0x3ff6edc000000000, 0x0); +} diff --git a/lib/std/special/compiler_rt/truncXfYf2.zig b/lib/std/special/compiler_rt/truncXfYf2.zig index 470ac17c2c..e85aa363db 100644 --- a/lib/std/special/compiler_rt/truncXfYf2.zig +++ b/lib/std/special/compiler_rt/truncXfYf2.zig @@ -13,6 +13,10 @@ pub fn __truncdfhf2(a: f64) callconv(.C) u16 { return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f64, a })); } +pub fn __trunctfhf2(a: f128) callconv(.C) u16 { + return @bitCast(u16, @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f16, f128, a })); +} + pub fn __trunctfsf2(a: f128) callconv(.C) f32 { return @call(.{ .modifier = .always_inline }, truncXfYf2, .{ f32, f128, a }); } @@ -122,7 +126,7 @@ fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t { if (shift > srcSigBits) { absResult = 0; } else { - const sticky: src_rep_t = significand << @intCast(SrcShift, srcBits - shift); + const sticky: src_rep_t = @boolToInt(significand << @intCast(SrcShift, srcBits - shift) != 0); const denormalizedSignificand: src_rep_t = significand >> @intCast(SrcShift, shift) | sticky; absResult = @intCast(dst_rep_t, denormalizedSignificand >> (srcSigBits - dstSigBits)); const roundBits: src_rep_t = denormalizedSignificand & roundMask; diff --git a/lib/std/special/compiler_rt/truncXfYf2_test.zig b/lib/std/special/compiler_rt/truncXfYf2_test.zig index 6426614b07..4fae5d1fc0 100644 --- a/lib/std/special/compiler_rt/truncXfYf2_test.zig +++ b/lib/std/special/compiler_rt/truncXfYf2_test.zig @@ -242,3 +242,59 @@ test "truncdfsf2" { // huge number becomes inf test__truncdfsf2(340282366920938463463374607431768211456.0, 0x7f800000); } + +const __trunctfhf2 = @import("truncXfYf2.zig").__trunctfhf2; + +fn test__trunctfhf2(a: f128, expected: u16) void { + const x = __trunctfhf2(a); + + const rep = @bitCast(u16, x); + if (rep == expected) { + return; + } + + @import("std").debug.warn("got 0x{x} wanted 0x{x}\n", .{ rep, expected }); + + @panic("__trunctfhf2 test failure"); +} + +test "trunctfhf2" { + // qNaN + test__trunctfhf2(@bitCast(f128, @as(u128, 0x7fff8000000000000000000000000000)), 0x7e00); + // NaN + test__trunctfhf2(@bitCast(f128, @as(u128, 0x7fff0000000000000000000000000001)), 0x7e00); + // inf + test__trunctfhf2(@bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)), 0x7c00); + test__trunctfhf2(-@bitCast(f128, @as(u128, 0x7fff0000000000000000000000000000)), 0xfc00); + // zero + test__trunctfhf2(0.0, 0x0); + test__trunctfhf2(-0.0, 0x8000); + + test__trunctfhf2(3.1415926535, 0x4248); + test__trunctfhf2(-3.1415926535, 0xc248); + test__trunctfhf2(0x1.987124876876324p+100, 0x7c00); + test__trunctfhf2(0x1.987124876876324p+12, 0x6e62); + test__trunctfhf2(0x1.0p+0, 0x3c00); + test__trunctfhf2(0x1.0p-14, 0x0400); + // denormal + test__trunctfhf2(0x1.0p-20, 0x0010); + test__trunctfhf2(0x1.0p-24, 0x0001); + test__trunctfhf2(-0x1.0p-24, 0x8001); + test__trunctfhf2(0x1.5p-25, 0x0001); + // and back to zero + test__trunctfhf2(0x1.0p-25, 0x0000); + test__trunctfhf2(-0x1.0p-25, 0x8000); + // max (precise) + test__trunctfhf2(65504.0, 0x7bff); + // max (rounded) + test__trunctfhf2(65519.0, 0x7bff); + // max (to +inf) + test__trunctfhf2(65520.0, 0x7c00); + test__trunctfhf2(65536.0, 0x7c00); + test__trunctfhf2(-65520.0, 0xfc00); + + test__trunctfhf2(0x1.23a2abb4a2ddee355f36789abcdep+5, 0x508f); + test__trunctfhf2(0x1.e3d3c45bd3abfd98b76a54cc321fp-9, 0x1b8f); + test__trunctfhf2(0x1.234eebb5faa678f4488693abcdefp+453, 0x7c00); + test__trunctfhf2(0x1.edcba9bb8c76a5a43dd21f334634p-43, 0x0); +} diff --git a/lib/std/target.zig b/lib/std/target.zig index 5d246465e2..3e8159aae9 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -800,6 +800,13 @@ pub const Target = struct { }; } + pub fn isPPC(arch: Arch) bool { + return switch (arch) { + .powerpc, .powerpcle => true, + else => false, + }; + } + pub fn isPPC64(arch: Arch) bool { return switch (arch) { .powerpc64, .powerpc64le => true, @@ -1184,8 +1191,8 @@ pub const Target = struct { .mips, .mipsel => &mips.cpu.mips32, .mips64, .mips64el => &mips.cpu.mips64, .msp430 => &msp430.cpu.generic, - .powerpc => &powerpc.cpu.ppc32, - .powerpcle => &powerpc.cpu.ppc32, + .powerpc => &powerpc.cpu.ppc, + .powerpcle => &powerpc.cpu.ppc, .powerpc64 => &powerpc.cpu.ppc64, .powerpc64le => &powerpc.cpu.ppc64le, .amdgcn => &amdgpu.cpu.generic, diff --git a/lib/std/target/powerpc.zig b/lib/std/target/powerpc.zig index 4e2200a47f..db4d5dccdc 100644 --- a/lib/std/target/powerpc.zig +++ b/lib/std/target/powerpc.zig @@ -751,13 +751,6 @@ pub const cpu = struct { .hard_float, }), }; - pub const ppc32 = CpuModel{ - .name = "ppc32", - .llvm_name = "ppc32", - .features = featureSet(&[_]Feature{ - .hard_float, - }), - }; pub const ppc64 = CpuModel{ .name = "ppc64", .llvm_name = "ppc64", diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index 029d8ede50..d6880ad273 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -852,7 +852,7 @@ const Parser = struct { /// <- KEYWORD_comptime? VarDecl /// / KEYWORD_comptime BlockExprStatement /// / KEYWORD_nosuspend BlockExprStatement - /// / KEYWORD_suspend (SEMICOLON / BlockExprStatement) + /// / KEYWORD_suspend BlockExprStatement /// / KEYWORD_defer BlockExprStatement /// / KEYWORD_errdefer Payload? BlockExprStatement /// / IfStatement @@ -892,6 +892,7 @@ const Parser = struct { }, .keyword_suspend => { const token = p.nextToken(); + // TODO remove this special case when 0.9.0 is released. const block_expr: Node.Index = if (p.eatToken(.semicolon) != null) 0 else diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 1bed86f42d..2fa8baa185 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -40,6 +40,21 @@ test "zig fmt: rewrite inline functions as callconv(.Inline)" { ); } +// TODO Remove this after zig 0.9.0 is released. +test "zig fmt: rewrite suspend without block expression" { + try testTransform( + \\fn foo() void { + \\ suspend; + \\} + \\ + , + \\fn foo() void { + \\ suspend {} + \\} + \\ + ); +} + test "zig fmt: simple top level comptime block" { try testCanonical( \\// line comment @@ -1315,6 +1330,27 @@ test "zig fmt: 'zig fmt: (off|on)' works in the middle of code" { ); } +test "zig fmt: 'zig fmt: on' indentation is unchanged" { + try testCanonical( + \\fn initOptionsAndLayouts(output: *Output, context: *Context) !void { + \\ // zig fmt: off + \\ try output.main_amount.init(output, "main_amount"); errdefer optput.main_amount.deinit(); + \\ try output.main_factor.init(output, "main_factor"); errdefer optput.main_factor.deinit(); + \\ try output.view_padding.init(output, "view_padding"); errdefer optput.view_padding.deinit(); + \\ try output.outer_padding.init(output, "outer_padding"); errdefer optput.outer_padding.deinit(); + \\ // zig fmt: on + \\ + \\ // zig fmt: off + \\ try output.top.init(output, .top); errdefer optput.top.deinit(); + \\ try output.right.init(output, .right); errdefer optput.right.deinit(); + \\ try output.bottom.init(output, .bottom); errdefer optput.bottom.deinit(); + \\ try output.left.init(output, .left); errdefer optput.left.deinit(); + \\ // zig fmt: on + \\} + \\ + ); +} + test "zig fmt: pointer of unknown length" { try testCanonical( \\fn foo(ptr: [*]u8) void {} @@ -3644,9 +3680,9 @@ test "zig fmt: async functions" { \\fn simpleAsyncFn() void { \\ const a = async a.b(); \\ x += 1; - \\ suspend; + \\ suspend {} \\ x += 1; - \\ suspend; + \\ suspend {} \\ const p: anyframe->void = async simpleAsyncFn() catch unreachable; \\ await p; \\} @@ -5001,6 +5037,21 @@ test "recovery: invalid comptime" { }); } +test "recovery: missing block after suspend" { + // TODO Enable this after zig 0.9.0 is released. + if (true) return error.SkipZigTest; + + try testError( + \\fn foo() void { + \\ suspend; + \\ nosuspend; + \\} + , &[_]Error{ + .expected_block_or_expr, + .expected_block_or_expr, + }); +} + test "recovery: missing block after for/while loops" { try testError( \\test "" { while (foo) } @@ -5144,7 +5195,7 @@ fn testError(source: []const u8, expected_errors: []const Error) !void { var tree = try std.zig.parse(std.testing.allocator, source); defer tree.deinit(std.testing.allocator); - std.testing.expect(tree.errors.len == expected_errors.len); + std.testing.expectEqual(expected_errors.len, tree.errors.len); for (expected_errors) |expected, i| { std.testing.expectEqual(expected, tree.errors[i].tag); } diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index ae12fc043f..237ecf777d 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -269,7 +269,12 @@ fn renderExpression(gpa: *Allocator, ais: *Ais, tree: ast.Tree, node: ast.Node.I try renderToken(ais, tree, suspend_token, .space); return renderExpression(gpa, ais, tree, body, space); } else { - return renderToken(ais, tree, suspend_token, space); + // TODO remove this special case when 0.9.0 is released. + assert(space == .semicolon); + try renderToken(ais, tree, suspend_token, .space); + try ais.writer().writeAll("{}"); + try ais.insertNewline(); + return; } }, @@ -2310,9 +2315,9 @@ fn renderComments(ais: *Ais, tree: ast.Tree, start: usize, end: usize) Error!boo // to the underlying writer, fixing up invaild whitespace. const disabled_source = tree.source[ais.disabled_offset.?..comment_start]; try writeFixingWhitespace(ais.underlying_writer, disabled_source); - ais.disabled_offset = null; // Write with the canonical single space. - try ais.writer().writeAll("// zig fmt: on\n"); + try ais.underlying_writer.writeAll("// zig fmt: on\n"); + ais.disabled_offset = null; } else if (ais.disabled_offset == null and mem.eql(u8, comment_content, "zig fmt: off")) { // Write with the canonical single space. try ais.writer().writeAll("// zig fmt: off\n"); diff --git a/src/Compilation.zig b/src/Compilation.zig index cb71bb8e0b..be488a20d6 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -2856,25 +2856,29 @@ pub fn addCCArgs( try argv.append("-fPIC"); } }, - .shared_library, .assembly, .ll, .bc, .unknown, .static_library, .object, .zig => {}, + .shared_library, .ll, .bc, .unknown, .static_library, .object, .zig => {}, + .assembly => { + // Argh, why doesn't the assembler accept the list of CPU features?! + // I don't see a way to do this other than hard coding everything. + switch (target.cpu.arch) { + .riscv32, .riscv64 => { + if (std.Target.riscv.featureSetHas(target.cpu.features, .relax)) { + try argv.append("-mrelax"); + } else { + try argv.append("-mno-relax"); + } + }, + else => { + // TODO + }, + } + if (target.cpu.model.llvm_name) |ln| + try argv.append(try std.fmt.allocPrint(arena, "-mcpu={s}", .{ln})); + }, } if (out_dep_path) |p| { try argv.appendSlice(&[_][]const u8{ "-MD", "-MV", "-MF", p }); } - // Argh, why doesn't the assembler accept the list of CPU features?! - // I don't see a way to do this other than hard coding everything. - switch (target.cpu.arch) { - .riscv32, .riscv64 => { - if (std.Target.riscv.featureSetHas(target.cpu.features, .relax)) { - try argv.append("-mrelax"); - } else { - try argv.append("-mno-relax"); - } - }, - else => { - // TODO - }, - } if (target.os.tag == .freestanding) { try argv.append("-ffreestanding"); diff --git a/src/codegen.zig b/src/codegen.zig index b8e1524a28..40b383c24f 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -1247,7 +1247,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }, .stack_offset => |off| { log.debug("reusing stack offset {} => {*}", .{ off, inst }); - return true; }, else => return false, } diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 9a94b90137..aaf88ad815 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -645,8 +645,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void { break :blk true; } - if (self.base.options.link_libcpp or - self.base.options.output_mode == .Lib or + if (self.base.options.output_mode == .Lib or self.base.options.linker_script != null) { // Fallback to LLD in this handful of cases on x86_64 only. diff --git a/src/link/MachO/Archive.zig b/src/link/MachO/Archive.zig index 86e160ba4d..5a0b9609ad 100644 --- a/src/link/MachO/Archive.zig +++ b/src/link/MachO/Archive.zig @@ -208,14 +208,13 @@ pub fn parseObject(self: Archive, offset: u32) !Object { const object_name = try parseName(self.allocator, object_header, reader); defer self.allocator.free(object_name); - const object_basename = std.fs.path.basename(object_name); - log.debug("extracting object '{s}' from archive '{s}'", .{ object_basename, self.name.? }); + log.debug("extracting object '{s}' from archive '{s}'", .{ object_name, self.name.? }); const name = name: { var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; const path = try std.os.realpath(self.name.?, &buffer); - break :name try std.fmt.allocPrint(self.allocator, "{s}({s})", .{ path, object_basename }); + break :name try std.fmt.allocPrint(self.allocator, "{s}({s})", .{ path, object_name }); }; var object = Object.init(self.allocator); diff --git a/src/link/MachO/Object.zig b/src/link/MachO/Object.zig index 7b5f23756a..6703a5bfb7 100644 --- a/src/link/MachO/Object.zig +++ b/src/link/MachO/Object.zig @@ -32,7 +32,9 @@ symtab_cmd_index: ?u16 = null, dysymtab_cmd_index: ?u16 = null, build_version_cmd_index: ?u16 = null, data_in_code_cmd_index: ?u16 = null, + text_section_index: ?u16 = null, +mod_init_func_section_index: ?u16 = null, // __DWARF segment sections dwarf_debug_info_index: ?u16 = null, @@ -49,6 +51,7 @@ stabs: std.ArrayListUnmanaged(Stab) = .{}, tu_path: ?[]const u8 = null, tu_mtime: ?u64 = null, +initializers: std.ArrayListUnmanaged(CppStatic) = .{}, data_in_code_entries: std.ArrayListUnmanaged(macho.data_in_code_entry) = .{}, pub const Section = struct { @@ -68,6 +71,11 @@ pub const Section = struct { } }; +const CppStatic = struct { + symbol: u32, + target_addr: u64, +}; + const Stab = struct { tag: Tag, symbol: u32, @@ -170,6 +178,7 @@ pub fn deinit(self: *Object) void { self.strtab.deinit(self.allocator); self.stabs.deinit(self.allocator); self.data_in_code_entries.deinit(self.allocator); + self.initializers.deinit(self.allocator); if (self.name) |n| { self.allocator.free(n); @@ -216,6 +225,7 @@ pub fn parse(self: *Object) !void { try self.parseSections(); if (self.symtab_cmd_index != null) try self.parseSymtab(); if (self.data_in_code_cmd_index != null) try self.readDataInCode(); + try self.parseInitializers(); try self.parseDebugInfo(); } @@ -250,6 +260,10 @@ pub fn readLoadCommands(self: *Object, reader: anytype) !void { if (mem.eql(u8, sectname, "__text")) { self.text_section_index = index; } + } else if (mem.eql(u8, segname, "__DATA")) { + if (mem.eql(u8, sectname, "__mod_init_func")) { + self.mod_init_func_section_index = index; + } } sect.offset += offset; @@ -298,28 +312,53 @@ pub fn parseSections(self: *Object) !void { var section = Section{ .inner = sect, .code = code, - .relocs = undefined, + .relocs = null, }; // Parse relocations - section.relocs = if (sect.nreloc > 0) relocs: { + if (sect.nreloc > 0) { var raw_relocs = try self.allocator.alloc(u8, @sizeOf(macho.relocation_info) * sect.nreloc); defer self.allocator.free(raw_relocs); _ = try self.file.?.preadAll(raw_relocs, sect.reloff); - break :relocs try reloc.parse( + section.relocs = try reloc.parse( self.allocator, self.arch.?, section.code, mem.bytesAsSlice(macho.relocation_info, raw_relocs), ); - } else null; + } self.sections.appendAssumeCapacity(section); } } +pub fn parseInitializers(self: *Object) !void { + const index = self.mod_init_func_section_index orelse return; + const section = self.sections.items[index]; + + log.debug("parsing initializers in {s}", .{self.name.?}); + + // Parse C++ initializers + const relocs = section.relocs orelse unreachable; + try self.initializers.ensureCapacity(self.allocator, relocs.len); + for (relocs) |rel| { + self.initializers.appendAssumeCapacity(.{ + .symbol = rel.target.symbol, + .target_addr = undefined, + }); + } + + mem.reverse(CppStatic, self.initializers.items); + + for (self.initializers.items) |initializer| { + const sym = self.symtab.items[initializer.symbol]; + const sym_name = self.getString(sym.n_strx); + log.debug(" | {s}", .{sym_name}); + } +} + pub fn parseSymtab(self: *Object) !void { const symtab_cmd = self.load_commands.items[self.symtab_cmd_index.?].Symtab; diff --git a/src/link/MachO/Symbol.zig b/src/link/MachO/Symbol.zig index f65a694f75..9e6c2bf68a 100644 --- a/src/link/MachO/Symbol.zig +++ b/src/link/MachO/Symbol.zig @@ -52,7 +52,7 @@ pub fn isUndf(sym: macho.nlist_64) bool { } pub fn isWeakDef(sym: macho.nlist_64) bool { - return sym.n_desc == macho.N_WEAK_DEF; + return (sym.n_desc & macho.N_WEAK_DEF) != 0; } /// Symbol is local if it is defined and not an extern. diff --git a/src/link/MachO/Zld.zig b/src/link/MachO/Zld.zig index ae475ab30d..a585b1fd1e 100644 --- a/src/link/MachO/Zld.zig +++ b/src/link/MachO/Zld.zig @@ -72,6 +72,7 @@ tlv_bss_section_index: ?u16 = null, la_symbol_ptr_section_index: ?u16 = null, data_section_index: ?u16 = null, bss_section_index: ?u16 = null, +common_section_index: ?u16 = null, symtab: std.StringArrayHashMapUnmanaged(Symbol) = .{}, strtab: std.ArrayListUnmanaged(u8) = .{}, @@ -224,6 +225,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void { self.allocateLinkeditSegment(); try self.allocateSymbols(); try self.allocateStubsAndGotEntries(); + try self.allocateCppStatics(); try self.writeStubHelperCommon(); try self.resolveRelocsAndWriteSections(); try self.flush(); @@ -465,23 +467,43 @@ fn updateMetadata(self: *Zld) !void { }, macho.S_ZEROFILL => { if (!mem.eql(u8, segname, "__DATA")) continue; - if (self.bss_section_index != null) continue; + if (mem.eql(u8, sectname, "__common")) { + if (self.common_section_index != null) continue; - self.bss_section_index = @intCast(u16, data_seg.sections.items.len); - try data_seg.addSection(self.allocator, .{ - .sectname = makeStaticString("__bss"), - .segname = makeStaticString("__DATA"), - .addr = 0, - .size = 0, - .offset = 0, - .@"align" = 0, - .reloff = 0, - .nreloc = 0, - .flags = macho.S_ZEROFILL, - .reserved1 = 0, - .reserved2 = 0, - .reserved3 = 0, - }); + self.common_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, .{ + .sectname = makeStaticString("__common"), + .segname = makeStaticString("__DATA"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_ZEROFILL, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + } else { + if (self.bss_section_index != null) continue; + + self.bss_section_index = @intCast(u16, data_seg.sections.items.len); + try data_seg.addSection(self.allocator, .{ + .sectname = makeStaticString("__bss"), + .segname = makeStaticString("__DATA"), + .addr = 0, + .size = 0, + .offset = 0, + .@"align" = 0, + .reloff = 0, + .nreloc = 0, + .flags = macho.S_ZEROFILL, + .reserved1 = 0, + .reserved2 = 0, + .reserved3 = 0, + }); + } }, macho.S_THREAD_LOCAL_VARIABLES => { if (!mem.eql(u8, segname, "__DATA")) continue; @@ -568,7 +590,9 @@ fn updateMetadata(self: *Zld) !void { const segname = parseName(&source_sect.segname); const sectname = parseName(&source_sect.sectname); + log.debug("section '{s}/{s}' will be unmapped", .{ segname, sectname }); + try self.unhandled_sections.putNoClobber(self.allocator, .{ .object_id = object_id, .source_sect_id = source_sect_id, @@ -585,6 +609,7 @@ const MatchingSection = struct { fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { const segname = parseName(§ion.segname); const sectname = parseName(§ion.sectname); + const res: ?MatchingSection = blk: { switch (section.flags) { macho.S_4BYTE_LITERALS, macho.S_8BYTE_LITERALS, macho.S_16BYTE_LITERALS => { @@ -612,6 +637,12 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { }; }, macho.S_ZEROFILL => { + if (mem.eql(u8, sectname, "__common")) { + break :blk .{ + .seg = self.data_segment_cmd_index.?, + .sect = self.common_section_index.?, + }; + } break :blk .{ .seg = self.data_segment_cmd_index.?, .sect = self.bss_section_index.?, @@ -667,6 +698,7 @@ fn getMatchingSection(self: *Zld, section: macho.section_64) ?MatchingSection { }, } }; + return res; } @@ -737,11 +769,12 @@ fn sortSections(self: *Zld) !void { // __DATA segment const indices = &[_]*?u16{ &self.la_symbol_ptr_section_index, - &self.tlv_section_index, &self.data_section_index, + &self.tlv_section_index, &self.tlv_data_section_index, &self.tlv_bss_section_index, &self.bss_section_index, + &self.common_section_index, }; for (indices) |maybe_index| { const new_index: u16 = if (maybe_index.*) |index| blk: { @@ -959,6 +992,21 @@ fn allocateStubsAndGotEntries(self: *Zld) !void { } } +fn allocateCppStatics(self: *Zld) !void { + for (self.objects.items) |*object| { + for (object.initializers.items) |*initializer| { + const sym = object.symtab.items[initializer.symbol]; + const sym_name = object.getString(sym.n_strx); + initializer.target_addr = object.locals.get(sym_name).?.address; + + log.debug("resolving C++ initializer '{s}' at 0x{x}", .{ + sym_name, + initializer.target_addr, + }); + } + } +} + fn writeStubHelperCommon(self: *Zld) !void { const text_segment = &self.load_commands.items[self.text_segment_cmd_index.?].Segment; const stub_helper = &text_segment.sections.items[self.stub_helper_section_index.?]; @@ -1236,11 +1284,12 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void { continue; } else if (Symbol.isGlobal(sym)) { const sym_name = object.getString(sym.n_strx); + const is_weak = Symbol.isWeakDef(sym) or Symbol.isPext(sym); const global = self.symtab.getEntry(sym_name) orelse { // Put new global symbol into the symbol table. const name = try self.allocator.dupe(u8, sym_name); try self.symtab.putNoClobber(self.allocator, name, .{ - .tag = if (Symbol.isWeakDef(sym)) .weak else .strong, + .tag = if (is_weak) .weak else .strong, .name = name, .address = 0, .section = 0, @@ -1251,15 +1300,20 @@ fn resolveSymbolsInObject(self: *Zld, object_id: u16) !void { }; switch (global.value.tag) { - .weak => continue, // If symbol is weak, nothing to do. + .weak => { + if (is_weak) continue; // Nothing to do for weak symbol. + }, .strong => { - log.err("symbol '{s}' defined multiple times", .{sym_name}); - return error.MultipleSymbolDefinitions; + if (!is_weak) { + log.debug("strong symbol '{s}' defined multiple times", .{sym_name}); + return error.MultipleSymbolDefinitions; + } + continue; }, else => {}, } - global.value.tag = .strong; + global.value.tag = if (is_weak) .weak else .strong; global.value.file = object_id; global.value.index = @intCast(u32, sym_id); } else if (Symbol.isUndef(sym)) { @@ -1340,6 +1394,21 @@ fn resolveSymbols(self: *Zld) !void { .section = 0, .file = 0, }); + + { + log.debug("symtab", .{}); + for (self.symtab.items()) |sym| { + switch (sym.value.tag) { + .weak, .strong => { + log.debug(" | {s} => {s}", .{ sym.key, self.objects.items[sym.value.file.?].name.? }); + }, + .import => { + log.debug(" | {s} => libSystem.B.dylib", .{sym.key}); + }, + else => unreachable, + } + } + } } fn resolveStubsAndGotEntries(self: *Zld) !void { @@ -1412,9 +1481,14 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { log.debug("relocating object {s}", .{object.name}); for (object.sections.items) |sect, source_sect_id| { + if (sect.inner.flags == macho.S_MOD_INIT_FUNC_POINTERS or + sect.inner.flags == macho.S_MOD_TERM_FUNC_POINTERS) continue; + const segname = parseName(§.inner.segname); const sectname = parseName(§.inner.sectname); + log.debug("relocating section '{s},{s}'", .{ segname, sectname }); + // Get mapping const target_mapping = self.mappings.get(.{ .object_id = @intCast(u16, object_id), @@ -1532,6 +1606,7 @@ fn resolveRelocsAndWriteSections(self: *Zld) !void { target_sect_off, target_sect_off + sect.code.len, }); + // Zero-out the space var zeroes = try self.allocator.alloc(u8, sect.code.len); defer self.allocator.free(zeroes); @@ -1571,25 +1646,33 @@ fn relocTargetAddr(self: *Zld, object_id: u16, target: reloc.Relocation.Target) const target_sect = target_seg.sections.items[target_mapping.target_sect_id]; const target_addr = target_sect.addr + target_mapping.offset; break :blk sym.n_value - source_sect.addr + target_addr; - } else { - if (self.stubs.get(sym_name)) |index| { - log.debug(" | symbol stub '{s}'", .{sym_name}); - const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; - const stubs = segment.sections.items[self.stubs_section_index.?]; - break :blk stubs.addr + index * stubs.reserved2; - } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) { - log.debug(" | symbol '__tlv_bootstrap'", .{}); - const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; - const tlv = segment.sections.items[self.tlv_section_index.?]; - break :blk tlv.addr; - } else { - const global = self.symtab.get(sym_name) orelse { - log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name}); - return error.FailedToResolveRelocationTarget; - }; - log.debug(" | global symbol '{s}'", .{sym_name}); - break :blk global.address; + } else if (self.symtab.get(sym_name)) |global| { + switch (global.tag) { + .weak, .strong => { + log.debug(" | global symbol '{s}'", .{sym_name}); + break :blk global.address; + }, + .import => { + if (self.stubs.get(sym_name)) |index| { + log.debug(" | symbol stub '{s}'", .{sym_name}); + const segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment; + const stubs = segment.sections.items[self.stubs_section_index.?]; + break :blk stubs.addr + index * stubs.reserved2; + } else if (mem.eql(u8, sym_name, "__tlv_bootstrap")) { + log.debug(" | symbol '__tlv_bootstrap'", .{}); + const segment = self.load_commands.items[self.data_segment_cmd_index.?].Segment; + const tlv = segment.sections.items[self.tlv_section_index.?]; + break :blk tlv.addr; + } else { + log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name}); + return error.FailedToResolveRelocationTarget; + } + }, + else => unreachable, } + } else { + log.err("failed to resolve symbol '{s}' as a relocation target", .{sym_name}); + return error.FailedToResolveRelocationTarget; } }, .section => |sect_id| { @@ -2008,6 +2091,12 @@ fn populateMetadata(self: *Zld) !void { } fn flush(self: *Zld) !void { + if (self.common_section_index) |index| { + const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; + const sect = &seg.sections.items[index]; + sect.offset = 0; + } + if (self.bss_section_index) |index| { const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment; const sect = &seg.sections.items[index]; @@ -2040,6 +2129,24 @@ fn flush(self: *Zld) !void { try self.file.?.pwriteAll(buffer, sect.offset); } + if (self.mod_init_func_section_index) |index| { + const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; + const sect = &seg.sections.items[index]; + + var initializers = std.ArrayList(u64).init(self.allocator); + defer initializers.deinit(); + + // TODO sort the initializers globally + for (self.objects.items) |object| { + for (object.initializers.items) |initializer| { + try initializers.append(initializer.target_addr); + } + } + + _ = try self.file.?.pwriteAll(mem.sliceAsBytes(initializers.items), sect.offset); + sect.size = @intCast(u32, initializers.items.len * @sizeOf(u64)); + } + try self.writeGotEntries(); try self.setEntryPoint(); try self.writeRebaseInfoTable(); @@ -2139,35 +2246,18 @@ fn writeRebaseInfoTable(self: *Zld) !void { // TODO audit and investigate this. const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; const sect = seg.sections.items[idx]; - const npointers = sect.size * @sizeOf(u64); const base_offset = sect.addr - seg.inner.vmaddr; const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); - try pointers.ensureCapacity(pointers.items.len + npointers); - var i: usize = 0; - while (i < npointers) : (i += 1) { - pointers.appendAssumeCapacity(.{ - .offset = base_offset + i * @sizeOf(u64), - .segment_id = segment_id, - }); - } - } - - if (self.mod_term_func_section_index) |idx| { - // TODO audit and investigate this. - const seg = self.load_commands.items[self.data_const_segment_cmd_index.?].Segment; - const sect = seg.sections.items[idx]; - const npointers = sect.size * @sizeOf(u64); - const base_offset = sect.addr - seg.inner.vmaddr; - const segment_id = @intCast(u16, self.data_const_segment_cmd_index.?); - - try pointers.ensureCapacity(pointers.items.len + npointers); - var i: usize = 0; - while (i < npointers) : (i += 1) { - pointers.appendAssumeCapacity(.{ - .offset = base_offset + i * @sizeOf(u64), - .segment_id = segment_id, - }); + var index: u64 = 0; + for (self.objects.items) |object| { + for (object.initializers.items) |_| { + try pointers.append(.{ + .offset = base_offset + index * @sizeOf(u64), + .segment_id = segment_id, + }); + index += 1; + } } } @@ -2447,7 +2537,7 @@ fn writeDebugInfo(self: *Zld) !void { .n_type = macho.N_OSO, .n_sect = 0, .n_desc = 1, - .n_value = tu_mtime, + .n_value = 0, //tu_mtime, TODO figure out why precalculated mtime value doesn't work }); for (object.stabs.items) |stab| { diff --git a/src/link/MachO/reloc/aarch64.zig b/src/link/MachO/reloc/aarch64.zig index a7dd0919b4..d8e7cebddd 100644 --- a/src/link/MachO/reloc/aarch64.zig +++ b/src/link/MachO/reloc/aarch64.zig @@ -226,7 +226,9 @@ pub const Parser = struct { try parser.parseTlvpLoadPageOff(rel); }, .ARM64_RELOC_POINTER_TO_GOT => { - return error.ToDoRelocPointerToGot; + // TODO Handle pointer to GOT. This reloc seems to appear in + // __LD,__compact_unwind section which we currently don't handle. + log.debug("Unhandled relocation ARM64_RELOC_POINTER_TO_GOT", .{}); }, } } diff --git a/src/main.zig b/src/main.zig index 3edabca95f..5e3823abd6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -355,6 +355,8 @@ const usage_build_generic = \\ -rpath [path] Add directory to the runtime library search path \\ -feach-lib-rpath Ensure adding rpath for each used dynamic library \\ -fno-each-lib-rpath Prevent adding rpath for each used dynamic library + \\ -fallow-shlib-undefined Allows undefined symbols in shared libraries + \\ -fno-allow-shlib-undefined Disallows undefined symbols in shared libraries \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker \\ --emit-relocs Enable output of relocation sections for post build tools \\ -dynamic Force output to be dynamically linked @@ -988,6 +990,10 @@ fn buildOutputType( link_eh_frame_hdr = true; } else if (mem.eql(u8, arg, "--emit-relocs")) { link_emit_relocs = true; + } else if (mem.eql(u8, arg, "-fallow-shlib-undefined")) { + linker_allow_shlib_undefined = true; + } else if (mem.eql(u8, arg, "-fno-allow-shlib-undefined")) { + linker_allow_shlib_undefined = false; } else if (mem.eql(u8, arg, "-Bsymbolic")) { linker_bind_global_refs_locally = true; } else if (mem.eql(u8, arg, "--verbose-link")) { diff --git a/src/register_manager.zig b/src/register_manager.zig index e11f2c3111..01f83aa2f5 100644 --- a/src/register_manager.zig +++ b/src/register_manager.zig @@ -36,7 +36,7 @@ pub fn RegisterManager( } fn isTracked(reg: Register) bool { - return std.mem.indexOfScalar(Register, callee_preserved_regs, reg) != null; + return reg.allocIndex() != null; } fn markRegUsed(self: *Self, reg: Register) void { @@ -55,6 +55,7 @@ pub fn RegisterManager( self.free_registers |= @as(FreeRegInt, 1) << shift; } + /// Returns true when this register is not tracked pub fn isRegFree(self: Self, reg: Register) bool { if (FreeRegInt == u0) return true; const index = reg.allocIndex() orelse return true; @@ -63,7 +64,8 @@ pub fn RegisterManager( } /// Returns whether this register was allocated in the course - /// of this function + /// of this function. + /// Returns false when this register is not tracked pub fn isRegAllocated(self: Self, reg: Register) bool { if (FreeRegInt == u0) return false; const index = reg.allocIndex() orelse return false; @@ -71,57 +73,89 @@ pub fn RegisterManager( return self.allocated_registers & @as(FreeRegInt, 1) << shift != 0; } + /// Before calling, must ensureCapacity + count on self.registers. + /// Returns `null` if all registers are allocated. + pub fn tryAllocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ?[count]Register { + if (self.tryAllocRegsWithoutTracking(count)) |regs| { + for (regs) |reg, i| { + self.markRegUsed(reg); + self.registers.putAssumeCapacityNoClobber(reg, insts[i]); + } + + return regs; + } else { + return null; + } + } + /// Before calling, must ensureCapacity + 1 on self.registers. /// Returns `null` if all registers are allocated. pub fn tryAllocReg(self: *Self, inst: *ir.Inst) ?Register { - const free_index = @ctz(FreeRegInt, self.free_registers); - if (free_index >= callee_preserved_regs.len) { - return null; - } + return if (tryAllocRegs(self, 1, .{inst})) |regs| regs[0] else null; + } - // This is necessary because the return type of @ctz is 1 - // bit longer than ShiftInt if callee_preserved_regs.len - // is a power of two. This int cast is always safe because - // free_index < callee_preserved_regs.len - const shift = @intCast(ShiftInt, free_index); - const mask = @as(FreeRegInt, 1) << shift; - self.free_registers &= ~mask; - self.allocated_registers |= mask; + /// Before calling, must ensureCapacity + count on self.registers. + pub fn allocRegs(self: *Self, comptime count: comptime_int, insts: [count]*ir.Inst) ![count]Register { + comptime assert(count > 0 and count <= callee_preserved_regs.len); - const reg = callee_preserved_regs[free_index]; - self.registers.putAssumeCapacityNoClobber(reg, inst); - log.debug("alloc {} => {*}", .{ reg, inst }); - return reg; + return self.tryAllocRegs(count, insts) orelse blk: { + // We'll take over the first count registers. Spill + // the instructions that were previously there to a + // stack allocations. + var regs: [count]Register = undefined; + std.mem.copy(Register, ®s, callee_preserved_regs[0..count]); + + for (regs) |reg, i| { + if (self.isRegFree(reg)) { + self.markRegUsed(reg); + self.registers.putAssumeCapacityNoClobber(reg, insts[i]); + } else { + const regs_entry = self.registers.getEntry(reg).?; + const spilled_inst = regs_entry.value; + regs_entry.value = insts[i]; + try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); + } + } + + break :blk regs; + }; } /// Before calling, must ensureCapacity + 1 on self.registers. pub fn allocReg(self: *Self, inst: *ir.Inst) !Register { - return self.tryAllocReg(inst) orelse b: { - // We'll take over the first register. Move the instruction that was previously - // there to a stack allocation. - const reg = callee_preserved_regs[0]; - const regs_entry = self.registers.getEntry(reg).?; - const spilled_inst = regs_entry.value; - regs_entry.value = inst; - try self.getFunction().spillInstruction(spilled_inst.src, reg, spilled_inst); + return (try allocRegs(self, 1, .{inst}))[0]; + } - break :b reg; - }; + /// Does not track the registers. + /// Returns `null` if not enough registers are free. + pub fn tryAllocRegsWithoutTracking(self: *Self, comptime count: comptime_int) ?[count]Register { + comptime if (callee_preserved_regs.len == 0) return null; + comptime assert(count > 0 and count <= callee_preserved_regs.len); + + const free_registers = @popCount(FreeRegInt, self.free_registers); + if (free_registers < count) return null; + + var regs: [count]Register = undefined; + var i: usize = 0; + for (callee_preserved_regs) |reg| { + if (i >= count) break; + if (self.isRegFree(reg)) { + regs[i] = reg; + i += 1; + } + } + return regs; } /// Does not track the register. /// Returns `null` if all registers are allocated. - pub fn findUnusedReg(self: *Self) ?Register { - const free_index = @ctz(FreeRegInt, self.free_registers); - if (free_index >= callee_preserved_regs.len) { - return null; - } - return callee_preserved_regs[free_index]; + pub fn tryAllocRegWithoutTracking(self: *Self) ?Register { + return if (tryAllocRegsWithoutTracking(self, 1)) |regs| regs[0] else null; } /// Does not track the register. pub fn allocRegWithoutTracking(self: *Self) !Register { - return self.findUnusedReg() orelse b: { + return self.tryAllocRegWithoutTracking() orelse b: { // We'll take over the first register. Move the instruction that was previously // there to a stack allocation. const reg = callee_preserved_regs[0]; @@ -190,7 +224,10 @@ pub fn RegisterManager( } const MockRegister = enum(u2) { - r0, r1, r2, r3, + r0, + r1, + r2, + r3, pub fn allocIndex(self: MockRegister) ?u2 { inline for (mock_callee_preserved_regs) |cpreg, i| { @@ -213,7 +250,7 @@ const MockFunction = struct { self.register_manager.deinit(self.allocator); self.spilled.deinit(self.allocator); } - + pub fn spillInstruction(self: *Self, src: LazySrcLoc, reg: MockRegister, inst: *ir.Inst) !void { try self.spilled.append(self.allocator, reg); } diff --git a/src/stage1/bigfloat.cpp b/src/stage1/bigfloat.cpp index 58b0aff54a..840cdccc8b 100644 --- a/src/stage1/bigfloat.cpp +++ b/src/stage1/bigfloat.cpp @@ -9,6 +9,7 @@ #include "bigint.hpp" #include "buffer.hpp" #include "softfloat.hpp" +#include "softfloat_ext.hpp" #include "parse_f128.h" #include #include @@ -60,9 +61,7 @@ void bigfloat_init_bigint(BigFloat *dest, const BigInt *op) { if (i == 0) { if (op->is_negative) { - float128_t zero_f128; - ui32_to_f128M(0, &zero_f128); - f128M_sub(&zero_f128, &dest->value, &dest->value); + f128M_neg(&dest->value, &dest->value); } return; } @@ -89,9 +88,7 @@ void bigfloat_add(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { } void bigfloat_negate(BigFloat *dest, const BigFloat *op) { - float128_t zero_f128; - ui32_to_f128M(0, &zero_f128); - f128M_sub(&zero_f128, &op->value, &dest->value); + f128M_neg(&op->value, &dest->value); } void bigfloat_sub(BigFloat *dest, const BigFloat *op1, const BigFloat *op2) { diff --git a/src/stage1/bigint.cpp b/src/stage1/bigint.cpp index 79a05e95a5..acb3e18e41 100644 --- a/src/stage1/bigint.cpp +++ b/src/stage1/bigint.cpp @@ -1446,10 +1446,10 @@ void bigint_negate(BigInt *dest, const BigInt *op) { bigint_normalize(dest); } -void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count) { +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { BigInt zero; bigint_init_unsigned(&zero, 0); - bigint_sub_wrap(dest, &zero, op, bit_count, true); + bigint_sub_wrap(dest, &zero, op, bit_count, is_signed); } void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { diff --git a/src/stage1/bigint.hpp b/src/stage1/bigint.hpp index 044ea66423..aa37b9302a 100644 --- a/src/stage1/bigint.hpp +++ b/src/stage1/bigint.hpp @@ -75,7 +75,7 @@ void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2); void bigint_negate(BigInt *dest, const BigInt *op); -void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count); +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp index 968caaf19b..1f30cd0a85 100644 --- a/src/stage1/codegen.cpp +++ b/src/stage1/codegen.cpp @@ -7436,7 +7436,10 @@ static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *n case ZigTypeIdFloat: switch (type_entry->data.floating.bit_count) { case 16: - return LLVMConstReal(get_llvm_type(g, type_entry), zig_f16_to_double(const_val->data.x_f16)); + { + LLVMValueRef as_int = LLVMConstInt(LLVMInt16Type(), const_val->data.x_f16.v, false); + return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); + } case 32: return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f32); case 64: diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp index 71a233c964..c59f63399c 100644 --- a/src/stage1/ir.cpp +++ b/src/stage1/ir.cpp @@ -9534,7 +9534,7 @@ static IrInstSrc *ir_gen_nosuspend(IrBuilderSrc *irb, Scope *parent_scope, AstNo Scope *child_scope = create_nosuspend_scope(irb->codegen, node, parent_scope); // purposefully pass null for result_loc and let EndExpr handle it - return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr); + return ir_gen_node_extra(irb, node->data.nosuspend_expr.expr, child_scope, lval, nullptr); } static IrInstSrc *ir_gen_return_from_block(IrBuilderSrc *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) { @@ -10199,14 +10199,12 @@ static IrInstSrc *ir_gen_suspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode } IrInstSrcSuspendBegin *begin = ir_build_suspend_begin_src(irb, parent_scope, node); - if (node->data.suspend.block != nullptr) { - ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope); - Scope *child_scope = &suspend_scope->base; - IrInstSrc *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope); - if (susp_res == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res)); - } + ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope); + Scope *child_scope = &suspend_scope->base; + IrInstSrc *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope); + if (susp_res == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res)); return ir_mark_gen(ir_build_suspend_finish_src(irb, parent_scope, node, begin)); } @@ -11363,11 +11361,8 @@ static void float_negate(ZigValue *out_val, ZigValue *op) { } else if (op->type->id == ZigTypeIdFloat) { switch (op->type->data.floating.bit_count) { case 16: - { - const float16_t zero = zig_double_to_f16(0); - out_val->data.x_f16 = f16_sub(zero, op->data.x_f16); - return; - } + out_val->data.x_f16 = f16_neg(op->data.x_f16); + return; case 32: out_val->data.x_f32 = -op->data.x_f32; return; @@ -11375,9 +11370,7 @@ static void float_negate(ZigValue *out_val, ZigValue *op) { out_val->data.x_f64 = -op->data.x_f64; return; case 128: - float128_t zero_f128; - ui32_to_f128M(0, &zero_f128); - f128M_sub(&zero_f128, &op->data.x_f128, &out_val->data.x_f128); + f128M_neg(&op->data.x_f128, &out_val->data.x_f128); return; default: zig_unreachable(); @@ -21665,8 +21658,8 @@ static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInst* source_instr, Z { bool is_float = (scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat); - bool ok_type = ((scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) || - scalar_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op)); + bool ok_type = scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdComptimeInt || + (is_float && !is_wrap_op); if (!ok_type) { const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'"; @@ -21677,7 +21670,7 @@ static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInst* source_instr, Z float_negate(scalar_out_val, operand_val); } else if (is_wrap_op) { bigint_negate_wrap(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint, - scalar_type->data.integral.bit_count); + scalar_type->data.integral.bit_count, scalar_type->data.integral.is_signed); } else { bigint_negate(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint); } diff --git a/src/stage1/parser.cpp b/src/stage1/parser.cpp index c37b3ffefb..d57277cd51 100644 --- a/src/stage1/parser.cpp +++ b/src/stage1/parser.cpp @@ -946,10 +946,7 @@ static AstNode *ast_parse_statement(ParseContext *pc) { Token *suspend = eat_token_if(pc, TokenIdKeywordSuspend); if (suspend != nullptr) { - AstNode *statement = nullptr; - if (eat_token_if(pc, TokenIdSemicolon) == nullptr) - statement = ast_expect(pc, ast_parse_block_expr_statement); - + AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); AstNode *res = ast_create_node(pc, NodeTypeSuspend, suspend); res->data.suspend.block = statement; return res; diff --git a/src/stage1/softfloat_ext.cpp b/src/stage1/softfloat_ext.cpp index 8408a15116..d0b8d1a5b3 100644 --- a/src/stage1/softfloat_ext.cpp +++ b/src/stage1/softfloat_ext.cpp @@ -1,17 +1,21 @@ #include "softfloat_ext.hpp" +#include "zigendian.h" extern "C" { #include "softfloat.h" } void f128M_abs(const float128_t *aPtr, float128_t *zPtr) { - float128_t zero_float; - ui32_to_f128M(0, &zero_float); - if (f128M_lt(aPtr, &zero_float)) { - f128M_sub(&zero_float, aPtr, zPtr); - } else { - *zPtr = *aPtr; - } + // Clear the sign bit. +#if ZIG_BYTE_ORDER == ZIG_LITTLE_ENDIAN + zPtr->v[1] = aPtr->v[1] & ~(UINT64_C(1) << 63); + zPtr->v[0] = aPtr->v[0]; +#elif ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN + zPtr->v[0] = aPtr->v[0] & ~(UINT64_C(1) << 63); + zPtr->v[1] = aPtr->v[1]; +#else +#error Unsupported endian +#endif } void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) { @@ -22,4 +26,24 @@ void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) { } else { f128M_roundToInt(aPtr, softfloat_round_min, false, zPtr); } +} + +float16_t f16_neg(const float16_t a) { + union { uint16_t ui; float16_t f; } uA; + // Toggle the sign bit. + uA.ui = a.v ^ (UINT16_C(1) << 15); + return uA.f; +} + +void f128M_neg(const float128_t *aPtr, float128_t *zPtr) { + // Toggle the sign bit. +#if ZIG_BYTE_ORDER == ZIG_LITTLE_ENDIAN + zPtr->v[1] = aPtr->v[1] ^ (UINT64_C(1) << 63); + zPtr->v[0] = aPtr->v[0]; +#elif ZIG_BYTE_ORDER == ZIG_BIG_ENDIAN + zPtr->v[0] = aPtr->v[0] ^ (UINT64_C(1) << 63); + zPtr->v[1] = aPtr->v[1]; +#else +#error Unsupported endian +#endif } \ No newline at end of file diff --git a/src/stage1/softfloat_ext.hpp b/src/stage1/softfloat_ext.hpp index 0a1f958933..42922a5226 100644 --- a/src/stage1/softfloat_ext.hpp +++ b/src/stage1/softfloat_ext.hpp @@ -5,5 +5,8 @@ void f128M_abs(const float128_t *aPtr, float128_t *zPtr); void f128M_trunc(const float128_t *aPtr, float128_t *zPtr); +void f128M_neg(const float128_t *aPtr, float128_t *zPtr); + +float16_t f16_neg(const float16_t a); #endif \ No newline at end of file diff --git a/src/translate_c.zig b/src/translate_c.zig index 9a1215abd6..ac5c52ee0d 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1353,10 +1353,14 @@ fn transCreatePointerArithmeticSignedOp( const bitcast_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node); - const arith_args = .{ .lhs = lhs_node, .rhs = bitcast_node }; - const arith_node = try if (is_add) Tag.add.create(c.arena, arith_args) else Tag.sub.create(c.arena, arith_args); - - return maybeSuppressResult(c, scope, result_used, arith_node); + return transCreateNodeInfixOp( + c, + scope, + if (is_add) .add else .sub, + lhs_node, + bitcast_node, + result_used, + ); } fn transBinaryOperator( @@ -2161,8 +2165,8 @@ fn transCCast( return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = bool_to_int }); } if (cIsEnum(dst_type)) { - // @intToEnum(dest_type, val) - return Tag.int_to_enum.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); + // import("std").meta.cast(dest_type, val) + return Tag.std_meta_cast.create(c.arena, .{ .lhs = dst_node, .rhs = expr }); } if (cIsEnum(src_type) and !cIsEnum(dst_type)) { // @enumToInt(val) diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 4be0fead97..61d28bb22d 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -1665,7 +1665,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, .array_access => { const payload = node.castTag(.array_access).?.data; - const lhs = try renderNode(c, payload.lhs); + const lhs = try renderNodeGrouped(c, payload.lhs); const l_bracket = try c.addToken(.l_bracket, "["); const index_expr = try renderNode(c, payload.rhs); _ = try c.addToken(.r_bracket, "]"); @@ -1728,7 +1728,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { }, .field_access => { const payload = node.castTag(.field_access).?.data; - const lhs = try renderNode(c, payload.lhs); + const lhs = try renderNodeGrouped(c, payload.lhs); return renderFieldAccess(c, lhs, payload.field_name); }, .@"struct", .@"union" => return renderRecord(c, node), @@ -2073,7 +2073,7 @@ fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIn .main_token = l_bracket, .data = .{ .lhs = len_expr, - .rhs = try c.addExtra(std.zig.ast.Node.ArrayTypeSentinel { + .rhs = try c.addExtra(std.zig.ast.Node.ArrayTypeSentinel{ .sentinel = sentinel_expr, .elem_type = elem_type_expr, }), diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 808f0b31dc..bfa9b592b4 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1021,7 +1021,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\export fn entry() void { \\ nosuspend { \\ const bar = async foo(); - \\ suspend; + \\ suspend {} \\ resume bar; \\ } \\} @@ -2120,7 +2120,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ non_async_fn = func; \\} \\fn func() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:5:1: error: 'func' cannot be async", @@ -2198,7 +2198,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var x: anyframe = &f; \\} \\fn func() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:3:12: error: expected type 'anyframe', found '*const @Frame(func)'", @@ -2231,10 +2231,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ frame = async bar(); \\} \\fn foo() void { - \\ suspend; + \\ suspend {} \\} \\fn bar() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:3:13: error: expected type '*@Frame(bar)', found '*@Frame(foo)'", @@ -2269,7 +2269,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ var result = await frame; \\} \\fn func() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:1:1: error: function with calling convention 'C' cannot be async", @@ -2347,7 +2347,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ bar(); \\} \\fn bar() void { - \\ suspend; + \\ suspend {} \\} , &[_][]const u8{ "tmp.zig:1:1: error: function with calling convention 'C' cannot be async", diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 7e5d0da367..314ef2889d 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -1453,4 +1453,27 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { \\ return 0; \\} , ""); + + cases.add("Cast to enum from larger integral type. Issue #6011", + \\#include + \\#include + \\enum Foo { A, B, C }; + \\static inline enum Foo do_stuff(void) { + \\ int64_t i = 1; + \\ return (enum Foo)i; + \\} + \\int main(void) { + \\ if (do_stuff() != B) abort(); + \\ return 0; + \\} + , ""); + + cases.add("Render array LHS as grouped node if necessary", + \\#include + \\int main(void) { + \\ int arr[] = {40, 41, 42, 43}; + \\ if ((arr + 1)[1] != 42) abort(); + \\ return 0; + \\} + , ""); } diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig index eb49b2dbc1..03a26a2c32 100644 --- a/test/runtime_safety.zig +++ b/test/runtime_safety.zig @@ -13,7 +13,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("switch on corrupted enum value", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\const E = enum(u32) { \\ X = 1, \\}; @@ -28,7 +28,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("switch on corrupted union value", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\const U = union(enum(u32)) { \\ X: u8, \\}; @@ -54,7 +54,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("@tagName on corrupted enum value", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\const E = enum(u32) { \\ X = 1, \\}; @@ -67,7 +67,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("@tagName on corrupted union value", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\const U = union(enum(u32)) { \\ X: u8, \\}; @@ -92,7 +92,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf = [4]u8{'a','b','c',0}; \\ const slice = buf[0..4 :0]; @@ -100,7 +100,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf = [4]u8{'a','b','c',0}; \\ const slice = buf[0..:0]; @@ -108,7 +108,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_zero = [0]u8{}; \\ const slice = buf_zero[0..0 :0]; @@ -116,7 +116,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_zero = [0]u8{}; \\ const slice = buf_zero[0..:0]; @@ -124,7 +124,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_sentinel = [2:0]u8{'a','b'}; \\ @ptrCast(*[3]u8, &buf_sentinel)[2] = 0; @@ -133,7 +133,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_slice: []const u8 = &[3]u8{ 'a', 'b', 0 }; \\ const slice = buf_slice[0..3 :0]; @@ -141,7 +141,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { ); cases.addRuntimeSafety("slicing operator with sentinel", \\const std = @import("std"); - ++ check_panic_msg ++ + ++ check_panic_msg ++ \\pub fn main() void { \\ var buf_slice: []const u8 = &[3]u8{ 'a', 'b', 0 }; \\ const slice = buf_slice[0.. :0]; @@ -367,7 +367,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} \\fn add(a: i32, b: i32) i32 { \\ if (a > 100) { - \\ suspend; + \\ suspend {} \\ } \\ return a + b; \\} @@ -407,7 +407,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ var frame = @asyncCall(&bytes, {}, ptr, .{}); \\} \\fn other() callconv(.Async) void { - \\ suspend; + \\ suspend {} \\} ); @@ -424,7 +424,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ await frame; \\} \\fn other() void { - \\ suspend; + \\ suspend {} \\} ); @@ -440,7 +440,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ other(); \\} \\fn other() void { - \\ suspend; + \\ suspend {} \\} ); @@ -454,7 +454,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\ resume p; //bad \\} \\fn suspendOnce() void { - \\ suspend; + \\ suspend {} \\} ); @@ -1019,7 +1019,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { \\} \\ \\fn failing() anyerror!void { - \\ suspend; + \\ suspend {} \\ return second(); \\} \\ diff --git a/test/stage1/behavior/async_fn.zig b/test/stage1/behavior/async_fn.zig index 40269df5ec..0765eac7e8 100644 --- a/test/stage1/behavior/async_fn.zig +++ b/test/stage1/behavior/async_fn.zig @@ -18,9 +18,9 @@ test "simple coroutine suspend and resume" { } fn simpleAsyncFn() void { global_x += 1; - suspend; + suspend {} global_x += 1; - suspend; + suspend {} global_x += 1; } @@ -34,7 +34,7 @@ test "pass parameter to coroutine" { } fn simpleAsyncFnWithArg(delta: i32) void { global_y += delta; - suspend; + suspend {} global_y += delta; } @@ -50,7 +50,7 @@ test "suspend at end of function" { fn suspendAtEnd() void { x += 1; - suspend; + suspend {} } }; S.doTheTest(); @@ -74,11 +74,11 @@ test "local variable in async function" { fn add(a: i32, b: i32) void { var accum: i32 = 0; - suspend; + suspend {} accum += a; - suspend; + suspend {} accum += b; - suspend; + suspend {} x = accum; } }; @@ -102,7 +102,7 @@ test "calling an inferred async function" { } fn other() void { other_frame = @frame(); - suspend; + suspend {} x += 1; } }; @@ -129,7 +129,7 @@ test "@frameSize" { } fn other(param: i32) void { var local: i32 = undefined; - suspend; + suspend {} } }; S.doTheTest(); @@ -269,7 +269,7 @@ test "async function with dot syntax" { var y: i32 = 1; fn foo() callconv(.Async) void { y += 1; - suspend; + suspend {} } }; const p = async S.foo(); @@ -298,7 +298,7 @@ fn doTheAwait(f: anyframe->void) void { fn simpleAsyncFn2(y: *i32) callconv(.Async) void { defer y.* += 2; y.* += 1; - suspend; + suspend {} } test "@asyncCall with return type" { @@ -312,7 +312,7 @@ test "@asyncCall with return type" { fn afunc() i32 { global_frame = @frame(); - suspend; + suspend {} return 1234; } }; @@ -348,7 +348,7 @@ test "async fn with inferred error set" { fn failing() !void { global_frame = @frame(); - suspend; + suspend {} return error.Fail; } }; @@ -375,7 +375,7 @@ fn nonFailing() (anyframe->anyerror!void) { return &Static.frame; } fn suspendThenFail() callconv(.Async) anyerror!void { - suspend; + suspend {} return error.Fail; } fn printTrace(p: anyframe->(anyerror!void)) callconv(.Async) void { @@ -400,7 +400,7 @@ fn testBreakFromSuspend(my_result: *i32) callconv(.Async) void { resume @frame(); } my_result.* += 1; - suspend; + suspend {} my_result.* += 1; } @@ -421,7 +421,7 @@ test "heap allocated async function frame" { fn someFunc() void { x += 1; - suspend; + suspend {} x += 1; } }; @@ -454,7 +454,7 @@ test "async function call return value" { fn other(x: i32, y: i32) Point { frame = @frame(); - suspend; + suspend {} return Point{ .x = x, .y = y, @@ -487,7 +487,7 @@ test "suspension points inside branching control flow" { fn func(b: bool) void { while (b) { - suspend; + suspend {} result += 1; } } @@ -541,7 +541,7 @@ test "pass string literal to async function" { fn hello(msg: []const u8) void { frame = @frame(); - suspend; + suspend {} expectEqualStrings("hello", msg); ok = true; } @@ -566,7 +566,7 @@ test "await inside an errdefer" { fn func() void { frame = @frame(); - suspend; + suspend {} } }; S.doTheTest(); @@ -590,7 +590,7 @@ test "try in an async function with error union and non-zero-bit payload" { fn theProblem() ![]u8 { frame = @frame(); - suspend; + suspend {} const result = try other(); return result; } @@ -622,7 +622,7 @@ test "returning a const error from async function" { fn fetchUrl(unused: i32, url: []const u8) ![]u8 { frame = @frame(); - suspend; + suspend {} ok = true; return error.OutOfMemory; } @@ -967,7 +967,7 @@ test "@asyncCall with comptime-known function, but not awaited directly" { fn failing() !void { global_frame = @frame(); - suspend; + suspend {} return error.Fail; } }; @@ -977,7 +977,7 @@ test "@asyncCall with comptime-known function, but not awaited directly" { test "@asyncCall with actual frame instead of byte buffer" { const S = struct { fn func() i32 { - suspend; + suspend {} return 1234; } }; @@ -993,7 +993,7 @@ test "@asyncCall using the result location inside the frame" { fn simple2(y: *i32) callconv(.Async) i32 { defer y.* += 2; y.* += 1; - suspend; + suspend {} return 1234; } fn getAnswer(f: anyframe->i32, out: *i32) void { @@ -1095,7 +1095,7 @@ test "nosuspend function call" { } fn add(a: i32, b: i32) i32 { if (a > 100) { - suspend; + suspend {} } return a + b; } @@ -1170,7 +1170,7 @@ test "suspend in for loop" { global_frame = @frame(); var sum: u32 = 0; for (stuff) |x| { - suspend; + suspend {} sum += x; } global_frame = null; @@ -1197,7 +1197,7 @@ test "suspend in while loop" { global_frame = @frame(); defer global_frame = null; while (stuff) |val| { - suspend; + suspend {} return val; } return 0; @@ -1206,7 +1206,7 @@ test "suspend in while loop" { global_frame = @frame(); defer global_frame = null; while (stuff) |val| { - suspend; + suspend {} return val; } else |err| { return 0; @@ -1339,7 +1339,7 @@ test "async function passed 0-bit arg after non-0-bit arg" { fn bar(x: i32, args: anytype) anyerror!void { global_frame = @frame(); - suspend; + suspend {} global_int = x; } }; @@ -1361,7 +1361,7 @@ test "async function passed align(16) arg after align(8) arg" { fn bar(x: u64, args: anytype) anyerror!void { expect(x == 10); global_frame = @frame(); - suspend; + suspend {} global_int = args[0]; } }; @@ -1383,7 +1383,7 @@ test "async function call resolves target fn frame, comptime func" { fn bar() anyerror!void { global_frame = @frame(); - suspend; + suspend {} global_int += 1; } }; @@ -1406,7 +1406,7 @@ test "async function call resolves target fn frame, runtime func" { fn bar() anyerror!void { global_frame = @frame(); - suspend; + suspend {} global_int += 1; } }; @@ -1430,7 +1430,7 @@ test "properly spill optional payload capture value" { fn bar() void { global_frame = @frame(); - suspend; + suspend {} global_int += 1; } }; @@ -1466,13 +1466,13 @@ test "handle defer interfering with return value spill" { fn bar() anyerror!void { global_frame1 = @frame(); - suspend; + suspend {} return error.Bad; } fn baz() void { global_frame2 = @frame(); - suspend; + suspend {} baz_happened = true; } }; @@ -1497,7 +1497,7 @@ test "take address of temporary async frame" { fn foo(arg: i32) i32 { global_frame = @frame(); - suspend; + suspend {} return arg + 1234; } @@ -1520,7 +1520,7 @@ test "nosuspend await" { fn foo(want_suspend: bool) i32 { if (want_suspend) { - suspend; + suspend {} } return 42; } @@ -1569,11 +1569,11 @@ test "nosuspend on async function calls" { // }; // const S1 = struct { // fn c() S0 { -// suspend; +// suspend {} // return S0{}; // } // fn d() !S0 { -// suspend; +// suspend {} // return S0{}; // } // }; @@ -1591,11 +1591,11 @@ test "nosuspend resume async function calls" { }; const S1 = struct { fn c() S0 { - suspend; + suspend {} return S0{}; } fn d() !S0 { - suspend; + suspend {} return S0{}; } }; diff --git a/test/stage1/behavior/math.zig b/test/stage1/behavior/math.zig index 68690c9af8..32f4842702 100644 --- a/test/stage1/behavior/math.zig +++ b/test/stage1/behavior/math.zig @@ -229,16 +229,26 @@ fn testSignedWrappingEval(x: i32) void { expect(max_val == maxInt(i32)); } -test "negation wrapping" { - testNegationWrappingEval(minInt(i16)); - comptime testNegationWrappingEval(minInt(i16)); +test "signed negation wrapping" { + testSignedNegationWrappingEval(minInt(i16)); + comptime testSignedNegationWrappingEval(minInt(i16)); } -fn testNegationWrappingEval(x: i16) void { +fn testSignedNegationWrappingEval(x: i16) void { expect(x == -32768); const neg = -%x; expect(neg == -32768); } +test "unsigned negation wrapping" { + testUnsignedNegationWrappingEval(1); + comptime testUnsignedNegationWrappingEval(1); +} +fn testUnsignedNegationWrappingEval(x: u16) void { + expect(x == 1); + const neg = -%x; + expect(neg == maxInt(u16)); +} + test "unsigned 64-bit division" { test_u64_div(); comptime test_u64_div(); @@ -843,3 +853,20 @@ test "compare undefined literal with comptime_int" { x = true; expect(x); } + +test "signed zeros are represented properly" { + const S = struct { + fn doTheTest() void { + inline for ([_]type{ f16, f32, f64, f128 }) |T| { + const ST = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); + var as_fp_val = -@as(T, 0.0); + var as_uint_val = @bitCast(ST, as_fp_val); + // Ensure the sign bit is set. + expect(as_uint_val >> (@typeInfo(T).Float.bits - 1) == 1); + } + } + }; + + S.doTheTest(); + comptime S.doTheTest(); +} diff --git a/test/tests.zig b/test/tests.zig index 5b537d9cc8..b6168f04e2 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -212,6 +212,22 @@ const test_targets = blk: { // .link_libc = true, //}, + TestTarget{ + .target = .{ + .cpu_arch = .powerpc, + .os_tag = .linux, + .abi = .none, + }, + }, + TestTarget{ + .target = .{ + .cpu_arch = .powerpc, + .os_tag = .linux, + .abi = .musl, + }, + .link_libc = true, + }, + TestTarget{ .target = .{ .cpu_arch = .riscv64, diff --git a/test/translate_c.zig b/test/translate_c.zig index 142579c92c..5fa4c32041 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3,6 +3,22 @@ const std = @import("std"); const CrossTarget = std.zig.CrossTarget; pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("field access is grouped if necessary", + \\unsigned long foo(unsigned long x) { + \\ return ((union{unsigned long _x}){x})._x; + \\} + , &[_][]const u8{ + \\pub export fn foo(arg_x: c_ulong) c_ulong { + \\ var x = arg_x; + \\ const union_unnamed_1 = extern union { + \\ _x: c_ulong, + \\ }; + \\ return (union_unnamed_1{ + \\ ._x = x, + \\ })._x; + \\} + }); + cases.add("unnamed child types of typedef receive typedef's name", \\typedef enum { \\ FooA, @@ -111,7 +127,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ const A = @enumToInt(enum_Foo.A); \\ const B = @enumToInt(enum_Foo.B); \\ const C = @enumToInt(enum_Foo.C); - \\ var a: enum_Foo = @intToEnum(enum_Foo, B); + \\ var a: enum_Foo = @import("std").meta.cast(enum_Foo, B); \\ { \\ const enum_Foo = extern enum(c_int) { \\ A, @@ -122,7 +138,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ const A_2 = @enumToInt(enum_Foo.A); \\ const B_3 = @enumToInt(enum_Foo.B); \\ const C_4 = @enumToInt(enum_Foo.C); - \\ var a_5: enum_Foo = @intToEnum(enum_Foo, B_3); + \\ var a_5: enum_Foo = @import("std").meta.cast(enum_Foo, B_3); \\ } \\} }); @@ -1676,7 +1692,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const e = @enumToInt(enum_unnamed_1.e); \\pub const f = @enumToInt(enum_unnamed_1.f); \\pub const g = @enumToInt(enum_unnamed_1.g); - \\pub export var h: enum_unnamed_1 = @intToEnum(enum_unnamed_1, e); + \\pub export var h: enum_unnamed_1 = @import("std").meta.cast(enum_unnamed_1, e); \\const enum_unnamed_2 = extern enum(c_int) { \\ i, \\ j, @@ -2308,7 +2324,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ var a = arg_a; \\ var b = arg_b; \\ var c = arg_c; - \\ var d: enum_Foo = @intToEnum(enum_Foo, FooA); + \\ var d: enum_Foo = @import("std").meta.cast(enum_Foo, FooA); \\ var e: c_int = @boolToInt((a != 0) and (b != 0)); \\ var f: c_int = @boolToInt((b != 0) and (c != null)); \\ var g: c_int = @boolToInt((a != 0) and (c != null)); diff --git a/tools/update_cpu_features.zig b/tools/update_cpu_features.zig index 7e3c636c31..15143dfca4 100644 --- a/tools/update_cpu_features.zig +++ b/tools/update_cpu_features.zig @@ -663,6 +663,12 @@ const llvm_targets = [_]LlvmTarget{ .zig_name = "powerpc", .llvm_name = "PowerPC", .td_name = "PPC.td", + .feature_overrides = &.{ + .{ + .llvm_name = "ppc32", + .omit = true, + }, + }, }, .{ .zig_name = "riscv",