aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/compiler/std-docs.zig81
-rw-r--r--lib/libc/glibc/csu/init.c23
-rw-r--r--lib/std/Build.zig1
-rw-r--r--lib/std/Build/Fuzz/WebServer.zig8
-rw-r--r--lib/std/Build/Step.zig10
-rw-r--r--lib/std/Target/Query.zig9
-rw-r--r--lib/std/bounded_array.zig30
-rw-r--r--lib/std/c.zig4
-rw-r--r--lib/std/dynamic_library.zig65
-rw-r--r--lib/std/fs/test.zig1
-rw-r--r--lib/std/os/linux.zig36
-rw-r--r--lib/std/os/linux/bpf/btf.zig3
-rw-r--r--lib/std/os/linux/test.zig17
-rw-r--r--lib/std/posix.zig3
-rw-r--r--lib/std/valgrind.zig2
-rw-r--r--lib/std/valgrind/cachegrind.zig29
-rw-r--r--lib/std/valgrind/callgrind.zig22
-rw-r--r--lib/std/valgrind/memcheck.zig46
-rw-r--r--src/Zcu/PerThread.zig3
-rw-r--r--src/glibc.zig37
-rw-r--r--test/incremental/remove_enum_field23
21 files changed, 327 insertions, 126 deletions
diff --git a/lib/compiler/std-docs.zig b/lib/compiler/std-docs.zig
index 4cfdf9b1e3..0382bbf971 100644
--- a/lib/compiler/std-docs.zig
+++ b/lib/compiler/std-docs.zig
@@ -4,6 +4,7 @@ const mem = std.mem;
const io = std.io;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
+const Cache = std.Build.Cache;
fn usage() noreturn {
io.getStdOut().writeAll(
@@ -232,9 +233,18 @@ fn serveWasm(
// Do the compilation every request, so that the user can edit the files
// and see the changes without restarting the server.
- const wasm_binary_path = try buildWasmBinary(arena, context, optimize_mode);
+ const wasm_base_path = try buildWasmBinary(arena, context, optimize_mode);
+ const bin_name = try std.zig.binNameAlloc(arena, .{
+ .root_name = autodoc_root_name,
+ .target = std.zig.system.resolveTargetQuery(std.Build.parseTargetQuery(.{
+ .arch_os_abi = autodoc_arch_os_abi,
+ .cpu_features = autodoc_cpu_features,
+ }) catch unreachable) catch unreachable,
+ .output_mode = .Exe,
+ });
// std.http.Server does not have a sendfile API yet.
- const file_contents = try std.fs.cwd().readFileAlloc(gpa, wasm_binary_path, 10 * 1024 * 1024);
+ const bin_path = try wasm_base_path.join(arena, bin_name);
+ const file_contents = try bin_path.root_dir.handle.readFileAlloc(gpa, bin_path.sub_path, 10 * 1024 * 1024);
defer gpa.free(file_contents);
try request.respond(file_contents, .{
.extra_headers = &.{
@@ -244,37 +254,42 @@ fn serveWasm(
});
}
+const autodoc_root_name = "autodoc";
+const autodoc_arch_os_abi = "wasm32-freestanding";
+const autodoc_cpu_features = "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext";
+
fn buildWasmBinary(
arena: Allocator,
context: *Context,
optimize_mode: std.builtin.OptimizeMode,
-) ![]const u8 {
+) !Cache.Path {
const gpa = context.gpa;
var argv: std.ArrayListUnmanaged([]const u8) = .{};
try argv.appendSlice(arena, &.{
- context.zig_exe_path,
- "build-exe",
- "-fno-entry",
- "-O",
- @tagName(optimize_mode),
- "-target",
- "wasm32-freestanding",
- "-mcpu",
- "baseline+atomics+bulk_memory+multivalue+mutable_globals+nontrapping_fptoint+reference_types+sign_ext",
- "--cache-dir",
- context.global_cache_path,
- "--global-cache-dir",
- context.global_cache_path,
- "--name",
- "autodoc",
- "-rdynamic",
- "--dep",
- "Walk",
- try std.fmt.allocPrint(arena, "-Mroot={s}/docs/wasm/main.zig", .{context.zig_lib_directory}),
- try std.fmt.allocPrint(arena, "-MWalk={s}/docs/wasm/Walk.zig", .{context.zig_lib_directory}),
- "--listen=-",
+ context.zig_exe_path, //
+ "build-exe", //
+ "-fno-entry", //
+ "-O", @tagName(optimize_mode), //
+ "-target", autodoc_arch_os_abi, //
+ "-mcpu", autodoc_cpu_features, //
+ "--cache-dir", context.global_cache_path, //
+ "--global-cache-dir", context.global_cache_path, //
+ "--name", autodoc_root_name, //
+ "-rdynamic", //
+ "--dep", "Walk", //
+ try std.fmt.allocPrint(
+ arena,
+ "-Mroot={s}/docs/wasm/main.zig",
+ .{context.zig_lib_directory},
+ ),
+ try std.fmt.allocPrint(
+ arena,
+ "-MWalk={s}/docs/wasm/Walk.zig",
+ .{context.zig_lib_directory},
+ ),
+ "--listen=-", //
});
var child = std.process.Child.init(argv.items, gpa);
@@ -293,7 +308,7 @@ fn buildWasmBinary(
try sendMessage(child.stdin.?, .exit);
const Header = std.zig.Server.Message.Header;
- var result: ?[]const u8 = null;
+ var result: ?Cache.Path = null;
var result_error_bundle = std.zig.ErrorBundle.empty;
const stdout = poller.fifo(.stdout);
@@ -330,13 +345,19 @@ fn buildWasmBinary(
.extra = extra_array,
};
},
- .emit_bin_path => {
- const EbpHdr = std.zig.Server.Message.EmitBinPath;
- const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body));
- if (!ebp_hdr.flags.cache_hit) {
+ .emit_digest => {
+ const EmitDigest = std.zig.Server.Message.EmitDigest;
+ const emit_digest = @as(*align(1) const EmitDigest, @ptrCast(body));
+ if (!emit_digest.flags.cache_hit) {
std.log.info("source changes detected; rebuilt wasm component", .{});
}
- result = try arena.dupe(u8, body[@sizeOf(EbpHdr)..]);
+ const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len];
+ result = .{
+ .root_dir = Cache.Directory.cwd(),
+ .sub_path = try std.fs.path.join(arena, &.{
+ context.global_cache_path, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*),
+ }),
+ };
},
else => {}, // ignore other messages
}
diff --git a/lib/libc/glibc/csu/init.c b/lib/libc/glibc/csu/init.c
new file mode 100644
index 0000000000..3ee6148898
--- /dev/null
+++ b/lib/libc/glibc/csu/init.c
@@ -0,0 +1,23 @@
+/* Special startup support.
+ Copyright (C) 1997-2024 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+/* Vestigial libio version number. Some code in libio checks whether
+ this symbol exists in the executable, but nothing looks at its
+ value anymore; the value it was historically set to has been
+ preserved out of an abundance of caution. */
+const int _IO_stdin_used = 0x20001;
diff --git a/lib/std/Build.zig b/lib/std/Build.zig
index 82810bb02f..f8ab20cac1 100644
--- a/lib/std/Build.zig
+++ b/lib/std/Build.zig
@@ -1808,6 +1808,7 @@ pub fn runAllowFail(
child.stderr_behavior = stderr_behavior;
child.env_map = &b.graph.env_map;
+ try Step.handleVerbose2(b, null, child.env_map, argv);
try child.spawn();
const stdout = child.stdout.?.reader().readAllAlloc(b.allocator, max_output_size) catch {
diff --git a/lib/std/Build/Fuzz/WebServer.zig b/lib/std/Build/Fuzz/WebServer.zig
index fbf6b8dbce..26b25b83d9 100644
--- a/lib/std/Build/Fuzz/WebServer.zig
+++ b/lib/std/Build/Fuzz/WebServer.zig
@@ -304,13 +304,13 @@ fn buildWasmBinary(
};
},
.emit_digest => {
- const EbpHdr = std.zig.Server.Message.EmitDigest;
- const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body));
+ const EmitDigest = std.zig.Server.Message.EmitDigest;
+ const ebp_hdr = @as(*align(1) const EmitDigest, @ptrCast(body));
if (!ebp_hdr.flags.cache_hit) {
log.info("source changes detected; rebuilt wasm component", .{});
}
- const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len];
- result = Path{
+ const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len];
+ result = .{
.root_dir = ws.global_cache_directory,
.sub_path = try arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*)),
};
diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig
index 346ab2c9b3..15dbd7faed 100644
--- a/lib/std/Build/Step.zig
+++ b/lib/std/Build/Step.zig
@@ -534,11 +534,11 @@ fn zigProcessUpdate(s: *Step, zp: *ZigProcess, watch: bool) !?Path {
}
},
.emit_digest => {
- const EbpHdr = std.zig.Server.Message.EmitDigest;
- const ebp_hdr = @as(*align(1) const EbpHdr, @ptrCast(body));
- s.result_cached = ebp_hdr.flags.cache_hit;
- const digest = body[@sizeOf(EbpHdr)..][0..Cache.bin_digest_len];
- result = Path{
+ const EmitDigest = std.zig.Server.Message.EmitDigest;
+ const emit_digest = @as(*align(1) const EmitDigest, @ptrCast(body));
+ s.result_cached = emit_digest.flags.cache_hit;
+ const digest = body[@sizeOf(EmitDigest)..][0..Cache.bin_digest_len];
+ result = .{
.root_dir = b.cache_root,
.sub_path = try arena.dupe(u8, "o" ++ std.fs.path.sep_str ++ Cache.binToHex(digest.*)),
};
diff --git a/lib/std/Target/Query.zig b/lib/std/Target/Query.zig
index 8871e360a5..4241e425ad 100644
--- a/lib/std/Target/Query.zig
+++ b/lib/std/Target/Query.zig
@@ -588,14 +588,7 @@ test parse {
const text = try query.zigTriple(std.testing.allocator);
defer std.testing.allocator.free(text);
- var buf: [256]u8 = undefined;
- const triple = std.fmt.bufPrint(
- buf[0..],
- "native-native-{s}.2.1.1",
- .{@tagName(builtin.target.abi)},
- ) catch unreachable;
-
- try std.testing.expectEqualSlices(u8, triple, text);
+ try std.testing.expectEqualSlices(u8, "native-native-gnu.2.1.1", text);
}
{
const query = try Query.parse(.{
diff --git a/lib/std/bounded_array.zig b/lib/std/bounded_array.zig
index 85a175003d..b88ba5a907 100644
--- a/lib/std/bounded_array.zig
+++ b/lib/std/bounded_array.zig
@@ -39,16 +39,14 @@ pub fn BoundedArrayAligned(
) type {
return struct {
const Self = @This();
- const Len = std.math.IntFittingRange(0, buffer_capacity);
-
buffer: [buffer_capacity]T align(alignment) = undefined,
- len: Len = 0,
+ len: usize = 0,
/// Set the actual length of the slice.
/// Returns error.Overflow if it exceeds the length of the backing array.
pub fn init(len: usize) error{Overflow}!Self {
if (len > buffer_capacity) return error.Overflow;
- return Self{ .len = @intCast(len) };
+ return Self{ .len = len };
}
/// View the internal array as a slice whose size was previously set.
@@ -69,7 +67,7 @@ pub fn BoundedArrayAligned(
/// Does not initialize added items if any.
pub fn resize(self: *Self, len: usize) error{Overflow}!void {
if (len > buffer_capacity) return error.Overflow;
- self.len = @intCast(len);
+ self.len = len;
}
/// Remove all elements from the slice.
@@ -178,7 +176,7 @@ pub fn BoundedArrayAligned(
/// This operation is O(N).
pub fn insertSlice(self: *Self, i: usize, items: []const T) error{Overflow}!void {
try self.ensureUnusedCapacity(items.len);
- self.len = @intCast(self.len + items.len);
+ self.len += items.len;
mem.copyBackwards(T, self.slice()[i + items.len .. self.len], self.constSlice()[i .. self.len - items.len]);
@memcpy(self.slice()[i..][0..items.len], items);
}
@@ -208,7 +206,7 @@ pub fn BoundedArrayAligned(
for (self.constSlice()[after_range..], 0..) |item, i| {
self.slice()[after_subrange..][i] = item;
}
- self.len = @intCast(self.len - len + new_items.len);
+ self.len -= len - new_items.len;
}
}
@@ -259,7 +257,7 @@ pub fn BoundedArrayAligned(
/// enough to store the new items.
pub fn appendSliceAssumeCapacity(self: *Self, items: []const T) void {
const old_len = self.len;
- self.len = @intCast(self.len + items.len);
+ self.len += items.len;
@memcpy(self.slice()[old_len..][0..items.len], items);
}
@@ -275,8 +273,8 @@ pub fn BoundedArrayAligned(
/// Asserts the capacity is enough.
pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
const old_len = self.len;
- assert(self.len + n <= buffer_capacity);
- self.len = @intCast(self.len + n);
+ self.len += n;
+ assert(self.len <= buffer_capacity);
@memset(self.slice()[old_len..self.len], value);
}
@@ -406,18 +404,6 @@ test BoundedArray {
try testing.expectEqualStrings(s, a.constSlice());
}
-test "BoundedArray sizeOf" {
- // Just sanity check size on one CPU
- if (@import("builtin").cpu.arch != .x86_64)
- return;
-
- try testing.expectEqual(@sizeOf(BoundedArray(u8, 3)), 4);
-
- // `len` is the minimum required size to hold the maximum capacity
- try testing.expectEqual(@TypeOf(@as(BoundedArray(u8, 15), undefined).len), u4);
- try testing.expectEqual(@TypeOf(@as(BoundedArray(u8, 16), undefined).len), u5);
-}
-
test "BoundedArrayAligned" {
var a = try BoundedArrayAligned(u8, 16, 4).init(0);
try a.append(0);
diff --git a/lib/std/c.zig b/lib/std/c.zig
index d430cf6a83..611f707e15 100644
--- a/lib/std/c.zig
+++ b/lib/std/c.zig
@@ -9499,8 +9499,8 @@ pub const LC = enum(c_int) {
pub extern "c" fn setlocale(category: LC, locale: ?[*:0]const u8) ?[*:0]const u8;
-pub const getcontext = if (builtin.target.isAndroid())
-{} // android bionic libc does not implement getcontext
+pub const getcontext = if (builtin.target.isAndroid() or builtin.target.os.tag == .openbsd)
+{} // android bionic and openbsd libc does not implement getcontext
else if (native_os == .linux and builtin.target.isMusl())
linux.getcontext
else
diff --git a/lib/std/dynamic_library.zig b/lib/std/dynamic_library.zig
index ad37777e43..110393d91a 100644
--- a/lib/std/dynamic_library.zig
+++ b/lib/std/dynamic_library.zig
@@ -147,9 +147,72 @@ pub const ElfDynLib = struct {
pub const Error = ElfDynLibError;
+ fn openPath(path: []const u8) !std.fs.Dir {
+ if (path.len == 0) return error.NotDir;
+ var parts = std.mem.tokenizeScalar(u8, path, '/');
+ var parent = if (path[0] == '/') try std.fs.cwd().openDir("/", .{}) else std.fs.cwd();
+ while (parts.next()) |part| {
+ const child = try parent.openDir(part, .{});
+ parent.close();
+ parent = child;
+ }
+ return parent;
+ }
+
+ fn resolveFromSearchPath(search_path: []const u8, file_name: []const u8, delim: u8) ?posix.fd_t {
+ var paths = std.mem.tokenizeScalar(u8, search_path, delim);
+ while (paths.next()) |p| {
+ var dir = openPath(p) catch continue;
+ defer dir.close();
+ const fd = posix.openat(dir.fd, file_name, .{
+ .ACCMODE = .RDONLY,
+ .CLOEXEC = true,
+ }, 0) catch continue;
+ return fd;
+ }
+ return null;
+ }
+
+ fn resolveFromParent(dir_path: []const u8, file_name: []const u8) ?posix.fd_t {
+ var dir = std.fs.cwd().openDir(dir_path, .{}) catch return null;
+ defer dir.close();
+ return posix.openat(dir.fd, file_name, .{
+ .ACCMODE = .RDONLY,
+ .CLOEXEC = true,
+ }, 0) catch null;
+ }
+
+ // This implements enough to be able to load system libraries in general
+ // Places where it differs from dlopen:
+ // - DT_RPATH of the calling binary is not used as a search path
+ // - DT_RUNPATH of the calling binary is not used as a search path
+ // - /etc/ld.so.cache is not read
+ fn resolveFromName(path_or_name: []const u8) !posix.fd_t {
+ // If filename contains a slash ("/"), then it is interpreted as a (relative or absolute) pathname
+ if (std.mem.indexOfScalarPos(u8, path_or_name, 0, '/')) |_| {
+ return posix.open(path_or_name, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
+ }
+
+ // Only read LD_LIBRARY_PATH if the binary is not setuid/setgid
+ if (std.os.linux.geteuid() == std.os.linux.getuid() and
+ std.os.linux.getegid() == std.os.linux.getgid())
+ {
+ if (posix.getenvZ("LD_LIBRARY_PATH")) |ld_library_path| {
+ if (resolveFromSearchPath(ld_library_path, path_or_name, ':')) |fd| {
+ return fd;
+ }
+ }
+ }
+
+ // Lastly the directories /lib and /usr/lib are searched (in this exact order)
+ if (resolveFromParent("/lib", path_or_name)) |fd| return fd;
+ if (resolveFromParent("/usr/lib", path_or_name)) |fd| return fd;
+ return error.FileNotFound;
+ }
+
/// Trusts the file. Malicious file will be able to execute arbitrary code.
pub fn open(path: []const u8) Error!ElfDynLib {
- const fd = try posix.open(path, .{ .ACCMODE = .RDONLY, .CLOEXEC = true }, 0);
+ const fd = try resolveFromName(path);
defer posix.close(fd);
const stat = try posix.fstat(fd);
diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig
index f596c52322..b0810f81a5 100644
--- a/lib/std/fs/test.zig
+++ b/lib/std/fs/test.zig
@@ -745,6 +745,7 @@ test "directory operations on files" {
test "file operations on directories" {
// TODO: fix this test on FreeBSD. https://github.com/ziglang/zig/issues/1759
if (native_os == .freebsd) return error.SkipZigTest;
+ if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/20747
try testWithAllSupportedPathTypes(struct {
fn impl(ctx: *TestContext) !void {
diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig
index c35059663e..a29b381c40 100644
--- a/lib/std/os/linux.zig
+++ b/lib/std/os/linux.zig
@@ -630,12 +630,13 @@ pub fn futex2_waitv(
nr_futexes,
flags,
@intFromPtr(timeout),
- @bitCast(@as(isize, clockid)),
+ @bitCast(@as(isize, @intFromEnum(clockid))),
);
}
/// Wait on a futex.
-/// Identical to `FUTEX.WAIT`, except it is part of the futex2 family of calls.
+/// Identical to the traditional `FUTEX.FUTEX_WAIT_BITSET` op, except it is part of the
+/// futex2 familiy of calls.
pub fn futex2_wait(
/// Address of the futex to wait on.
uaddr: *const anyopaque,
@@ -646,7 +647,7 @@ pub fn futex2_wait(
/// `FUTEX2` flags.
flags: u32,
/// Optional absolute timeout.
- timeout: *const timespec,
+ timeout: ?*const timespec,
/// Clock to be used for the timeout, realtime or monotonic.
clockid: clockid_t,
) usize {
@@ -657,15 +658,16 @@ pub fn futex2_wait(
mask,
flags,
@intFromPtr(timeout),
- @bitCast(@as(isize, clockid)),
+ @bitCast(@as(isize, @intFromEnum(clockid))),
);
}
/// Wake a number of futexes.
-/// Identical to `FUTEX.WAKE`, except it is part of the futex2 family of calls.
+/// Identical to the traditional `FUTEX.FUTEX_WAIT_BITSET` op, except it is part of the
+/// futex2 family of calls.
pub fn futex2_wake(
/// Address of the futex(es) to wake.
- uaddr: [*]const anyopaque,
+ uaddr: *const anyopaque,
/// Bitmask
mask: usize,
/// Number of the futexes to wake.
@@ -1683,7 +1685,7 @@ pub fn sigaddset(set: *sigset_t, sig: u6) void {
pub fn sigismember(set: *const sigset_t, sig: u6) bool {
const s = sig - 1;
- return ((set.*)[@as(usize, @intCast(s)) / usize_bits] & (@as(usize, @intCast(1)) << (s & (usize_bits - 1)))) != 0;
+ return ((set.*)[@as(usize, @intCast(s)) / usize_bits] & (@as(usize, @intCast(1)) << @intCast(s & (usize_bits - 1)))) != 0;
}
pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
@@ -1740,31 +1742,31 @@ pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize
var next_unsent: usize = 0;
for (msgvec[0..kvlen], 0..) |*msg, i| {
var size: i32 = 0;
- const msg_iovlen = @as(usize, @intCast(msg.msg_hdr.msg_iovlen)); // kernel side this is treated as unsigned
- for (msg.msg_hdr.msg_iov[0..msg_iovlen]) |iov| {
+ const msg_iovlen = @as(usize, @intCast(msg.hdr.iovlen)); // kernel side this is treated as unsigned
+ for (msg.hdr.iov[0..msg_iovlen]) |iov| {
if (iov.len > std.math.maxInt(i32) or @addWithOverflow(size, @as(i32, @intCast(iov.len)))[1] != 0) {
// batch-send all messages up to the current message
if (next_unsent < i) {
const batch_size = i - next_unsent;
const r = syscall4(.sendmmsg, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(&msgvec[next_unsent]), batch_size, flags);
- if (E.init(r) != 0) return next_unsent;
+ if (E.init(r) != .SUCCESS) return next_unsent;
if (r < batch_size) return next_unsent + r;
}
// send current message as own packet
- const r = sendmsg(fd, &msg.msg_hdr, flags);
- if (E.init(r) != 0) return r;
+ const r = sendmsg(fd, &msg.hdr, flags);
+ if (E.init(r) != .SUCCESS) return r;
// Linux limits the total bytes sent by sendmsg to INT_MAX, so this cast is safe.
- msg.msg_len = @as(u32, @intCast(r));
+ msg.len = @as(u32, @intCast(r));
next_unsent = i + 1;
break;
}
- size += iov.len;
+ size += @intCast(iov.len);
}
}
if (next_unsent < kvlen or next_unsent == 0) { // want to make sure at least one syscall occurs (e.g. to trigger MSG.EOR)
const batch_size = kvlen - next_unsent;
const r = syscall4(.sendmmsg, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(&msgvec[next_unsent]), batch_size, flags);
- if (E.init(r) != 0) return r;
+ if (E.init(r) != .SUCCESS) return r;
return next_unsent + r;
}
return kvlen;
@@ -5095,7 +5097,7 @@ pub const epoll_event = extern struct {
pub const VFS_CAP_REVISION_MASK = 0xFF000000;
pub const VFS_CAP_REVISION_SHIFT = 24;
-pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK;
+pub const VFS_CAP_FLAGS_MASK = ~@as(u32, VFS_CAP_REVISION_MASK);
pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001;
pub const VFS_CAP_REVISION_1 = 0x01000000;
@@ -5113,7 +5115,7 @@ pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2;
pub const vfs_cap_data = extern struct {
//all of these are mandated as little endian
//when on disk.
- const Data = struct {
+ const Data = extern struct {
permitted: u32,
inheritable: u32,
};
diff --git a/lib/std/os/linux/bpf/btf.zig b/lib/std/os/linux/bpf/btf.zig
index 39d25014da..3988fce349 100644
--- a/lib/std/os/linux/bpf/btf.zig
+++ b/lib/std/os/linux/bpf/btf.zig
@@ -83,13 +83,14 @@ pub const Kind = enum(u5) {
/// int kind is followed by this struct
pub const IntInfo = packed struct(u32) {
bits: u8,
- unused: u8,
+ reserved_1: u8,
offset: u8,
encoding: enum(u4) {
signed = 1 << 0,
char = 1 << 1,
boolean = 1 << 2,
},
+ reserved_2: u4,
};
test "IntInfo is 32 bits" {
diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig
index 8562d4be8e..a8ebec47a5 100644
--- a/lib/std/os/linux/test.zig
+++ b/lib/std/os/linux/test.zig
@@ -125,6 +125,23 @@ test "fadvise" {
try expectEqual(@as(usize, 0), ret);
}
+test "sigset_t" {
+ var sigset = linux.empty_sigset;
+
+ try expectEqual(linux.sigismember(&sigset, linux.SIG.USR1), false);
+ try expectEqual(linux.sigismember(&sigset, linux.SIG.USR2), false);
+
+ linux.sigaddset(&sigset, linux.SIG.USR1);
+
+ try expectEqual(linux.sigismember(&sigset, linux.SIG.USR1), true);
+ try expectEqual(linux.sigismember(&sigset, linux.SIG.USR2), false);
+
+ linux.sigaddset(&sigset, linux.SIG.USR2);
+
+ try expectEqual(linux.sigismember(&sigset, linux.SIG.USR1), true);
+ try expectEqual(linux.sigismember(&sigset, linux.SIG.USR2), true);
+}
+
test {
_ = linux.IoUring;
}
diff --git a/lib/std/posix.zig b/lib/std/posix.zig
index bc270395cc..665ea17788 100644
--- a/lib/std/posix.zig
+++ b/lib/std/posix.zig
@@ -4746,11 +4746,13 @@ pub fn munmap(memory: []align(mem.page_size) const u8) void {
pub const MSyncError = error{
UnmappedMemory,
+ PermissionDenied,
} || UnexpectedError;
pub fn msync(memory: []align(mem.page_size) u8, flags: i32) MSyncError!void {
switch (errno(system.msync(memory.ptr, memory.len, flags))) {
.SUCCESS => return,
+ .PERM => return error.PermissionDenied,
.NOMEM => return error.UnmappedMemory, // Unsuccessful, provided pointer does not point mapped memory
.INVAL => unreachable, // Invalid parameters.
else => unreachable,
@@ -7106,6 +7108,7 @@ pub const MadviseError = error{
pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) MadviseError!void {
switch (errno(system.madvise(ptr, length, advice))) {
.SUCCESS => return,
+ .PERM => return error.PermissionDenied,
.ACCES => return error.AccessDenied,
.AGAIN => return error.SystemResources,
.BADF => unreachable, // The map exists, but the area maps something that isn't a file.
diff --git a/lib/std/valgrind.zig b/lib/std/valgrind.zig
index 8590302e9a..241f4c732a 100644
--- a/lib/std/valgrind.zig
+++ b/lib/std/valgrind.zig
@@ -278,8 +278,10 @@ pub fn monitorCommand(command: [*]u8) bool {
pub const memcheck = @import("valgrind/memcheck.zig");
pub const callgrind = @import("valgrind/callgrind.zig");
+pub const cachegrind = @import("valgrind/cachegrind.zig");
test {
_ = memcheck;
_ = callgrind;
+ _ = cachegrind;
}
diff --git a/lib/std/valgrind/cachegrind.zig b/lib/std/valgrind/cachegrind.zig
new file mode 100644
index 0000000000..b6aef5391b
--- /dev/null
+++ b/lib/std/valgrind/cachegrind.zig
@@ -0,0 +1,29 @@
+const std = @import("../std.zig");
+const valgrind = std.valgrind;
+
+pub const ClientRequest = enum(usize) {
+ StartInstrumentation = valgrind.ToolBase("CG".*),
+ StopInstrumentation,
+};
+
+fn doClientRequestExpr(default: usize, request: ClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) usize {
+ return valgrind.doClientRequest(default, @as(usize, @intCast(@intFromEnum(request))), a1, a2, a3, a4, a5);
+}
+
+fn doClientRequestStmt(request: ClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) void {
+ _ = doClientRequestExpr(0, request, a1, a2, a3, a4, a5);
+}
+
+/// Start Cachegrind instrumentation if not already enabled. Use this in
+/// combination with `std.valgrind.cachegrind.stopInstrumentation` and
+/// `--instr-at-start` to measure only part of a client program's execution.
+pub fn startInstrumentation() void {
+ doClientRequestStmt(.StartInstrumentation, 0, 0, 0, 0, 0);
+}
+
+/// Stop Cachegrind instrumentation if not already disabled. Use this in
+/// combination with `std.valgrind.cachegrind.startInstrumentation` and
+/// `--instr-at-start` to measure only part of a client program's execution.
+pub fn stopInstrumentation() void {
+ doClientRequestStmt(.StopInstrumentation, 0, 0, 0, 0, 0);
+}
diff --git a/lib/std/valgrind/callgrind.zig b/lib/std/valgrind/callgrind.zig
index 716573e7b0..6718f40643 100644
--- a/lib/std/valgrind/callgrind.zig
+++ b/lib/std/valgrind/callgrind.zig
@@ -1,7 +1,7 @@
const std = @import("../std.zig");
const valgrind = std.valgrind;
-pub const CallgrindClientRequest = enum(usize) {
+pub const ClientRequest = enum(usize) {
DumpStats = valgrind.ToolBase("CT".*),
ZeroStats,
ToggleCollect,
@@ -10,17 +10,19 @@ pub const CallgrindClientRequest = enum(usize) {
StopInstrumentation,
};
-fn doCallgrindClientRequestExpr(default: usize, request: CallgrindClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) usize {
+pub const CallgrindClientRequest = @compileError("std.valgrind.callgrind.CallgrindClientRequest renamed to std.valgrind.callgrind.ClientRequest");
+
+fn doClientRequestExpr(default: usize, request: ClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) usize {
return valgrind.doClientRequest(default, @as(usize, @intCast(@intFromEnum(request))), a1, a2, a3, a4, a5);
}
-fn doCallgrindClientRequestStmt(request: CallgrindClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) void {
- _ = doCallgrindClientRequestExpr(0, request, a1, a2, a3, a4, a5);
+fn doClientRequestStmt(request: ClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) void {
+ _ = doClientRequestExpr(0, request, a1, a2, a3, a4, a5);
}
/// Dump current state of cost centers, and zero them afterwards
pub fn dumpStats() void {
- doCallgrindClientRequestStmt(.DumpStats, 0, 0, 0, 0, 0);
+ doClientRequestStmt(.DumpStats, 0, 0, 0, 0, 0);
}
/// Dump current state of cost centers, and zero them afterwards.
@@ -28,12 +30,12 @@ pub fn dumpStats() void {
/// the dump. This string is written as a description field into the
/// profile data dump.
pub fn dumpStatsAt(pos_str: [*:0]const u8) void {
- doCallgrindClientRequestStmt(.DumpStatsAt, @intFromPtr(pos_str), 0, 0, 0, 0);
+ doClientRequestStmt(.DumpStatsAt, @intFromPtr(pos_str), 0, 0, 0, 0);
}
/// Zero cost centers
pub fn zeroStats() void {
- doCallgrindClientRequestStmt(.ZeroStats, 0, 0, 0, 0, 0);
+ doClientRequestStmt(.ZeroStats, 0, 0, 0, 0, 0);
}
/// Toggles collection state.
@@ -41,7 +43,7 @@ pub fn zeroStats() void {
/// should be noted or if they are to be ignored. Events are noted
/// by increment of counters in a cost center
pub fn toggleCollect() void {
- doCallgrindClientRequestStmt(.ToggleCollect, 0, 0, 0, 0, 0);
+ doClientRequestStmt(.ToggleCollect, 0, 0, 0, 0, 0);
}
/// Start full callgrind instrumentation if not already switched on.
@@ -49,7 +51,7 @@ pub fn toggleCollect() void {
/// this will lead to an artificial cache warmup phase afterwards with
/// cache misses which would not have happened in reality.
pub fn startInstrumentation() void {
- doCallgrindClientRequestStmt(.StartInstrumentation, 0, 0, 0, 0, 0);
+ doClientRequestStmt(.StartInstrumentation, 0, 0, 0, 0, 0);
}
/// Stop full callgrind instrumentation if not already switched off.
@@ -60,5 +62,5 @@ pub fn startInstrumentation() void {
/// To start Callgrind in this mode to ignore the setup phase, use
/// the option "--instr-atstart=no".
pub fn stopInstrumentation() void {
- doCallgrindClientRequestStmt(.StopInstrumentation, 0, 0, 0, 0, 0);
+ doClientRequestStmt(.StopInstrumentation, 0, 0, 0, 0, 0);
}
diff --git a/lib/std/valgrind/memcheck.zig b/lib/std/valgrind/memcheck.zig
index e66943ee2c..950026afc5 100644
--- a/lib/std/valgrind/memcheck.zig
+++ b/lib/std/valgrind/memcheck.zig
@@ -2,7 +2,7 @@ const std = @import("../std.zig");
const testing = std.testing;
const valgrind = std.valgrind;
-pub const MemCheckClientRequest = enum(usize) {
+pub const ClientRequest = enum(usize) {
MakeMemNoAccess = valgrind.ToolBase("MC".*),
MakeMemUndefined,
MakeMemDefined,
@@ -20,29 +20,31 @@ pub const MemCheckClientRequest = enum(usize) {
DisableAddrErrorReportingInRange,
};
-fn doMemCheckClientRequestExpr(default: usize, request: MemCheckClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) usize {
+pub const MemCheckClientRequest = @compileError("std.valgrind.memcheck.MemCheckClientRequest renamed to std.valgrind.memcheck.ClientRequest");
+
+fn doClientRequestExpr(default: usize, request: ClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) usize {
return valgrind.doClientRequest(default, @as(usize, @intCast(@intFromEnum(request))), a1, a2, a3, a4, a5);
}
-fn doMemCheckClientRequestStmt(request: MemCheckClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) void {
- _ = doMemCheckClientRequestExpr(0, request, a1, a2, a3, a4, a5);
+fn doClientRequestStmt(request: ClientRequest, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize) void {
+ _ = doClientRequestExpr(0, request, a1, a2, a3, a4, a5);
}
/// Mark memory at qzz.ptr as unaddressable for qzz.len bytes.
pub fn makeMemNoAccess(qzz: []const u8) void {
- _ = doMemCheckClientRequestExpr(0, // default return
+ _ = doClientRequestExpr(0, // default return
.MakeMemNoAccess, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0);
}
/// Mark memory at qzz.ptr as addressable but undefined for qzz.len bytes.
pub fn makeMemUndefined(qzz: []const u8) void {
- _ = doMemCheckClientRequestExpr(0, // default return
+ _ = doClientRequestExpr(0, // default return
.MakeMemUndefined, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0);
}
/// Mark memory at qzz.ptr as addressable and defined or qzz.len bytes.
pub fn makeMemDefined(qzz: []const u8) void {
- _ = doMemCheckClientRequestExpr(0, // default return
+ _ = doClientRequestExpr(0, // default return
.MakeMemDefined, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0);
}
@@ -50,7 +52,7 @@ pub fn makeMemDefined(qzz: []const u8) void {
/// not altered: bytes which are addressable are marked as defined,
/// but those which are not addressable are left unchanged.
pub fn makeMemDefinedIfAddressable(qzz: []const u8) void {
- _ = doMemCheckClientRequestExpr(0, // default return
+ _ = doClientRequestExpr(0, // default return
.MakeMemDefinedIfAddressable, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0);
}
@@ -59,14 +61,14 @@ pub fn makeMemDefinedIfAddressable(qzz: []const u8) void {
/// within the specified memory range. Has no other effect on the
/// properties of the memory range.
pub fn createBlock(qzz: []const u8, desc: [*:0]const u8) usize {
- return doMemCheckClientRequestExpr(0, // default return
+ return doClientRequestExpr(0, // default return
.CreateBlock, @intFromPtr(qzz.ptr), qzz.len, @intFromPtr(desc), 0, 0);
}
/// Discard a block-description-handle. Returns 1 for an
/// invalid handle, 0 for a valid handle.
pub fn discard(blkindex: usize) bool {
- return doMemCheckClientRequestExpr(0, // default return
+ return doClientRequestExpr(0, // default return
.Discard, 0, blkindex, 0, 0, 0) != 0;
}
@@ -75,7 +77,7 @@ pub fn discard(blkindex: usize) bool {
/// error message and returns the address of the first offending byte.
/// Otherwise it returns zero.
pub fn checkMemIsAddressable(qzz: []const u8) usize {
- return doMemCheckClientRequestExpr(0, .CheckMemIsAddressable, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0);
+ return doClientRequestExpr(0, .CheckMemIsAddressable, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0);
}
/// Check that memory at qzz.ptr is addressable and defined for
@@ -83,31 +85,31 @@ pub fn checkMemIsAddressable(qzz: []const u8) usize {
/// established, Valgrind prints an error message and returns the
/// address of the first offending byte. Otherwise it returns zero.
pub fn checkMemIsDefined(qzz: []const u8) usize {
- return doMemCheckClientRequestExpr(0, .CheckMemIsDefined, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0);
+ return doClientRequestExpr(0, .CheckMemIsDefined, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0);
}
/// Do a full memory leak check (like --leak-check=full) mid-execution.
pub fn doLeakCheck() void {
- doMemCheckClientRequestStmt(.DO_LEAK_CHECK, 0, 0, 0, 0, 0);
+ doClientRequestStmt(.DO_LEAK_CHECK, 0, 0, 0, 0, 0);
}
/// Same as doLeakCheck() but only showing the entries for
/// which there was an increase in leaked bytes or leaked nr of blocks
/// since the previous leak search.
pub fn doAddedLeakCheck() void {
- doMemCheckClientRequestStmt(.DO_LEAK_CHECK, 0, 1, 0, 0, 0);
+ doClientRequestStmt(.DO_LEAK_CHECK, 0, 1, 0, 0, 0);
}
/// Same as doAddedLeakCheck() but showing entries with
/// increased or decreased leaked bytes/blocks since previous leak
/// search.
pub fn doChangedLeakCheck() void {
- doMemCheckClientRequestStmt(.DO_LEAK_CHECK, 0, 2, 0, 0, 0);
+ doClientRequestStmt(.DO_LEAK_CHECK, 0, 2, 0, 0, 0);
}
/// Do a summary memory leak check (like --leak-check=summary) mid-execution.
pub fn doQuickLeakCheck() void {
- doMemCheckClientRequestStmt(.DO_LEAK_CHECK, 1, 0, 0, 0, 0);
+ doClientRequestStmt(.DO_LEAK_CHECK, 1, 0, 0, 0, 0);
}
/// Return number of leaked, dubious, reachable and suppressed bytes found by
@@ -126,7 +128,7 @@ pub fn countLeaks() CountResult {
.reachable = 0,
.suppressed = 0,
};
- doMemCheckClientRequestStmt(
+ doClientRequestStmt(
.CountLeaks,
@intFromPtr(&res.leaked),
@intFromPtr(&res.dubious),
@@ -156,7 +158,7 @@ pub fn countLeakBlocks() CountResult {
.reachable = 0,
.suppressed = 0,
};
- doMemCheckClientRequestStmt(
+ doClientRequestStmt(
.CountLeakBlocks,
@intFromPtr(&res.leaked),
@intFromPtr(&res.dubious),
@@ -189,7 +191,7 @@ test countLeakBlocks {
/// impossible to segfault your system by using this call.
pub fn getVbits(zza: []u8, zzvbits: []u8) u2 {
std.debug.assert(zzvbits.len >= zza.len / 8);
- return @as(u2, @intCast(doMemCheckClientRequestExpr(0, .GetVbits, @intFromPtr(zza.ptr), @intFromPtr(zzvbits), zza.len, 0, 0)));
+ return @as(u2, @intCast(doClientRequestExpr(0, .GetVbits, @intFromPtr(zza.ptr), @intFromPtr(zzvbits), zza.len, 0, 0)));
}
/// Set the validity data for addresses zza, copying it
@@ -202,17 +204,17 @@ pub fn getVbits(zza: []u8, zzvbits: []u8) u2 {
/// impossible to segfault your system by using this call.
pub fn setVbits(zzvbits: []u8, zza: []u8) u2 {
std.debug.assert(zzvbits.len >= zza.len / 8);
- return @as(u2, @intCast(doMemCheckClientRequestExpr(0, .SetVbits, @intFromPtr(zza.ptr), @intFromPtr(zzvbits), zza.len, 0, 0)));
+ return @as(u2, @intCast(doClientRequestExpr(0, .SetVbits, @intFromPtr(zza.ptr), @intFromPtr(zzvbits), zza.len, 0, 0)));
}
/// Disable and re-enable reporting of addressing errors in the
/// specified address range.
pub fn disableAddrErrorReportingInRange(qzz: []u8) usize {
- return doMemCheckClientRequestExpr(0, // default return
+ return doClientRequestExpr(0, // default return
.DisableAddrErrorReportingInRange, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0);
}
pub fn enableAddrErrorReportingInRange(qzz: []u8) usize {
- return doMemCheckClientRequestExpr(0, // default return
+ return doClientRequestExpr(0, // default return
.EnableAddrErrorReportingInRange, @intFromPtr(qzz.ptr), qzz.len, 0, 0, 0);
}
diff --git a/src/Zcu/PerThread.zig b/src/Zcu/PerThread.zig
index 291518f5f0..01063ab2ce 100644
--- a/src/Zcu/PerThread.zig
+++ b/src/Zcu/PerThread.zig
@@ -3378,7 +3378,6 @@ fn recreateStructType(
} else 0;
if (captures_len != key.captures.owned.len) return error.AnalysisFail;
- if (fields_len != struct_obj.field_types.len) return error.AnalysisFail;
// The old type will be unused, so drop its dependency information.
ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .cau = struct_obj.cau.unwrap().? }));
@@ -3466,7 +3465,6 @@ fn recreateUnionType(
} else 0;
if (captures_len != key.captures.owned.len) return error.AnalysisFail;
- if (fields_len != union_obj.field_types.len) return error.AnalysisFail;
// The old type will be unused, so drop its dependency information.
ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .cau = union_obj.cau }));
@@ -3577,7 +3575,6 @@ fn recreateEnumType(
} else 0;
if (captures_len != key.captures.owned.len) return error.AnalysisFail;
- if (fields_len != enum_obj.names.len) return error.AnalysisFail;
extra_index += captures_len;
extra_index += decls_len;
diff --git a/src/glibc.zig b/src/glibc.zig
index 57894f5921..4689805391 100644
--- a/src/glibc.zig
+++ b/src/glibc.zig
@@ -286,7 +286,11 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: std.Progre
.owner = undefined,
};
};
- var files = [_]Compilation.CSourceFile{ start_o, abi_note_o };
+ const init_o: Compilation.CSourceFile = .{
+ .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "init.c"),
+ .owner = undefined,
+ };
+ var files = [_]Compilation.CSourceFile{ start_o, abi_note_o, init_o };
return comp.build_crt_file("Scrt1", .Obj, .@"glibc Scrt1.o", prog_node, &files);
},
.libc_nonshared_a => {
@@ -682,6 +686,12 @@ pub const BuiltSharedObjects = struct {
const all_map_basename = "all.map";
+fn wordDirective(target: std.Target) []const u8 {
+ // Based on its description in the GNU `as` manual, you might assume that `.word` is sized
+ // according to the target word size. But no; that would just make too much sense.
+ return if (target.ptrBitWidth() == 64) ".quad" else ".long";
+}
+
pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !void {
const tracy = trace(@src());
defer tracy.end();
@@ -923,6 +933,31 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: std.Progress.Node) !voi
try stubs_asm.appendSlice(".data\n");
+ // For some targets, the real `libc.so.6` will contain a weak reference to `_IO_stdin_used`,
+ // making the linker put the symbol in the dynamic symbol table. We likewise need to emit a
+ // reference to it here for that effect, or it will not show up, which in turn will cause
+ // the real glibc to think that the program was built against an ancient `FILE` structure
+ // (pre-glibc 2.1).
+ //
+ // Note that glibc only compiles in the legacy compatibility code for some targets; it
+ // depends on what is defined in the `shlib-versions` file for the particular architecture
+ // and ABI. Those files are preprocessed by 2 separate tools during the glibc build to get
+ // the final `abi-versions.h`, so it would be quite brittle to try to condition our emission
+ // of the `_IO_stdin_used` reference in the exact same way. The only downside of emitting
+ // the reference unconditionally is that it ends up being unused for newer targets; it
+ // otherwise has no negative effect.
+ //
+ // glibc uses a weak reference because it has to work with programs compiled against pre-2.1
+ // versions where the symbol didn't exist. We only care about modern glibc versions, so use
+ // a strong reference.
+ if (std.mem.eql(u8, lib.name, "c")) {
+ try stubs_asm.writer().print(
+ \\.globl _IO_stdin_used
+ \\{s} _IO_stdin_used
+ \\
+ , .{wordDirective(target)});
+ }
+
const obj_inclusions_len = mem.readInt(u16, metadata.inclusions[inc_i..][0..2], .little);
inc_i += 2;
diff --git a/test/incremental/remove_enum_field b/test/incremental/remove_enum_field
new file mode 100644
index 0000000000..64193bb25b
--- /dev/null
+++ b/test/incremental/remove_enum_field
@@ -0,0 +1,23 @@
+#target=x86_64-linux
+#update=initial version
+#file=main.zig
+const MyEnum = enum(u8) {
+ foo = 1,
+ bar = 2,
+};
+pub fn main() !void {
+ try std.io.getStdOut().writer().print("{}\n", .{@intFromEnum(MyEnum.foo)});
+}
+const std = @import("std");
+#expect_stdout="1\n"
+#update=remove enum field
+#file=main.zig
+const MyEnum = enum(u8) {
+ //foo = 1,
+ bar = 2,
+};
+pub fn main() !void {
+ try std.io.getStdOut().writer().print("{}\n", .{@intFromEnum(MyEnum.foo)});
+}
+const std = @import("std");
+#expect_error=ignored