mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-27 19:09:47 +03:00
incremental: handle loss of main struct instruction
My changes to how incremental compilation handles container types mean that, at least for now, it is possible for the ZIR `.main_struct_inst` of a source file to be lost (this happens if the number of top-level fields in a file changes for instance). I missed a few things which needed changing to account for this, which could lead to crashes with certain (trivial) changes---oops! Adds two new incremental test cases. They are currently disabled for wasm32-wasi-selfhosted because they both trigger a crash in the WASM backend.
This commit is contained in:
+4
-4
@@ -3649,7 +3649,7 @@ const Header = extern struct {
|
||||
type_layout_deps_len: u32,
|
||||
struct_defaults_deps_len: u32,
|
||||
func_ies_deps_len: u32,
|
||||
zon_file_deps_len: u32,
|
||||
source_file_deps_len: u32,
|
||||
embed_file_deps_len: u32,
|
||||
namespace_deps_len: u32,
|
||||
namespace_name_deps_len: u32,
|
||||
@@ -3699,7 +3699,7 @@ pub fn saveState(comp: *Compilation) !void {
|
||||
.type_layout_deps_len = @intCast(ip.type_layout_deps.count()),
|
||||
.struct_defaults_deps_len = @intCast(ip.struct_defaults_deps.count()),
|
||||
.func_ies_deps_len = @intCast(ip.func_ies_deps.count()),
|
||||
.zon_file_deps_len = @intCast(ip.zon_file_deps.count()),
|
||||
.source_file_deps_len = @intCast(ip.source_file_deps.count()),
|
||||
.embed_file_deps_len = @intCast(ip.embed_file_deps.count()),
|
||||
.namespace_deps_len = @intCast(ip.namespace_deps.count()),
|
||||
.namespace_name_deps_len = @intCast(ip.namespace_name_deps.count()),
|
||||
@@ -3738,8 +3738,8 @@ pub fn saveState(comp: *Compilation) !void {
|
||||
addBuf(&bufs, @ptrCast(ip.struct_defaults_deps.values()));
|
||||
addBuf(&bufs, @ptrCast(ip.func_ies_deps.keys()));
|
||||
addBuf(&bufs, @ptrCast(ip.func_ies_deps.values()));
|
||||
addBuf(&bufs, @ptrCast(ip.zon_file_deps.keys()));
|
||||
addBuf(&bufs, @ptrCast(ip.zon_file_deps.values()));
|
||||
addBuf(&bufs, @ptrCast(ip.source_file_deps.keys()));
|
||||
addBuf(&bufs, @ptrCast(ip.source_file_deps.values()));
|
||||
addBuf(&bufs, @ptrCast(ip.embed_file_deps.keys()));
|
||||
addBuf(&bufs, @ptrCast(ip.embed_file_deps.values()));
|
||||
addBuf(&bufs, @ptrCast(ip.namespace_deps.keys()));
|
||||
|
||||
@@ -305,7 +305,7 @@ fn handleCommand(zcu: *Zcu, w: *Io.Writer, cmd_str: []const u8, arg_str: []const
|
||||
for (unit_info.deps.items, 0..) |dependee, i| {
|
||||
try w.print("[{d}] ", .{i});
|
||||
switch (dependee) {
|
||||
.src_hash, .namespace, .namespace_name, .zon_file, .embed_file => try w.print("{f}", .{zcu.fmtDependee(dependee)}),
|
||||
.src_hash, .namespace, .namespace_name, .source_file, .embed_file => try w.print("{f}", .{zcu.fmtDependee(dependee)}),
|
||||
.nav_val, .nav_ty => |nav| try w.print("{t} {d}", .{ dependee, @intFromEnum(nav) }),
|
||||
.type_layout, .struct_defaults, .func_ies => |ip_index| try w.print("{t} {d}", .{ dependee, @intFromEnum(ip_index) }),
|
||||
.memoized_state => |stage| try w.print("memoized_state {s}", .{@tagName(stage)}),
|
||||
|
||||
+19
-14
@@ -57,9 +57,14 @@ type_layout_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),
|
||||
/// Dependencies on the resolved default field values of a `struct` type.
|
||||
/// Value is index into `dep_entries` of the first dependency on this type's inits.
|
||||
struct_defaults_deps: std.AutoArrayHashMapUnmanaged(Index, DepEntry.Index),
|
||||
/// Dependencies on a ZON file. Triggered by `@import` of ZON.
|
||||
/// Value is index into `dep_entries` of the first dependency on this ZON file.
|
||||
zon_file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index),
|
||||
/// Dependencies on a Zig or ZON source file. Triggered by `@import`.
|
||||
/// * For ZON source files, the dependency is invalidated if the file changes at all. The `@import`
|
||||
/// must be re-analyzed to return the new data structure.
|
||||
/// * For Zig source files, the dependency is invalidated if the file's root struct type changes
|
||||
/// (which can only happen because the `.main_struct_inst` got lost). The `@import` must be
|
||||
/// re-analyzed to return the new type.
|
||||
/// Value is index into `dep_entries` of the first dependency on this Zig/ZON file.
|
||||
source_file_deps: std.AutoArrayHashMapUnmanaged(FileIndex, DepEntry.Index),
|
||||
/// Dependencies on an embedded file.
|
||||
/// Introduced by `@embedFile`; invalidated when the file changes.
|
||||
/// Value is index into `dep_entries` of the first dependency on this `Zcu.EmbedFile`.
|
||||
@@ -112,7 +117,7 @@ pub const empty: InternPool = .{
|
||||
.func_ies_deps = .empty,
|
||||
.type_layout_deps = .empty,
|
||||
.struct_defaults_deps = .empty,
|
||||
.zon_file_deps = .empty,
|
||||
.source_file_deps = .empty,
|
||||
.embed_file_deps = .empty,
|
||||
.namespace_deps = .empty,
|
||||
.namespace_name_deps = .empty,
|
||||
@@ -859,7 +864,7 @@ pub const Dependee = union(enum) {
|
||||
func_ies: Index,
|
||||
type_layout: Index,
|
||||
struct_defaults: Index,
|
||||
zon_file: FileIndex,
|
||||
source_file: FileIndex,
|
||||
embed_file: Zcu.EmbedFile.Index,
|
||||
namespace: TrackedInst.Index,
|
||||
namespace_name: NamespaceNameKey,
|
||||
@@ -913,7 +918,7 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI
|
||||
.func_ies => |x| ip.func_ies_deps.get(x),
|
||||
.type_layout => |x| ip.type_layout_deps.get(x),
|
||||
.struct_defaults => |x| ip.struct_defaults_deps.get(x),
|
||||
.zon_file => |x| ip.zon_file_deps.get(x),
|
||||
.source_file => |x| ip.source_file_deps.get(x),
|
||||
.embed_file => |x| ip.embed_file_deps.get(x),
|
||||
.namespace => |x| ip.namespace_deps.get(x),
|
||||
.namespace_name => |x| ip.namespace_name_deps.get(x),
|
||||
@@ -988,7 +993,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
|
||||
.func_ies => ip.func_ies_deps,
|
||||
.type_layout => ip.type_layout_deps,
|
||||
.struct_defaults => ip.struct_defaults_deps,
|
||||
.zon_file => ip.zon_file_deps,
|
||||
.source_file => ip.source_file_deps,
|
||||
.embed_file => ip.embed_file_deps,
|
||||
.namespace => ip.namespace_deps,
|
||||
.namespace_name => ip.namespace_name_deps,
|
||||
@@ -6477,7 +6482,7 @@ pub fn deinit(ip: *InternPool, gpa: Allocator, io: Io) void {
|
||||
ip.func_ies_deps.deinit(gpa);
|
||||
ip.type_layout_deps.deinit(gpa);
|
||||
ip.struct_defaults_deps.deinit(gpa);
|
||||
ip.zon_file_deps.deinit(gpa);
|
||||
ip.source_file_deps.deinit(gpa);
|
||||
ip.embed_file_deps.deinit(gpa);
|
||||
ip.namespace_deps.deinit(gpa);
|
||||
ip.namespace_name_deps.deinit(gpa);
|
||||
@@ -10643,7 +10648,7 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {
|
||||
const func_ies_deps_len = ip.func_ies_deps.count();
|
||||
const type_layout_deps_len = ip.type_layout_deps.count();
|
||||
const struct_defaults_deps_len = ip.struct_defaults_deps.count();
|
||||
const zon_file_deps_len = ip.zon_file_deps.count();
|
||||
const source_file_deps_len = ip.source_file_deps.count();
|
||||
const embed_file_deps_len = ip.embed_file_deps.count();
|
||||
const namespace_deps_len = ip.namespace_deps.count();
|
||||
const namespace_name_deps_len = ip.namespace_name_deps.count();
|
||||
@@ -10654,7 +10659,7 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {
|
||||
const func_ies_deps_size = func_ies_deps_len * 8;
|
||||
const type_layout_deps_size = type_layout_deps_len * 8;
|
||||
const struct_defaults_deps_size = struct_defaults_deps_len * 8;
|
||||
const zon_file_deps_size = zon_file_deps_len * 8;
|
||||
const source_file_deps_size = source_file_deps_len * 8;
|
||||
const embed_file_deps_size = embed_file_deps_len * 8;
|
||||
const namespace_deps_size = namespace_deps_len * 8;
|
||||
const namespace_name_deps_size = namespace_name_deps_len * (@sizeOf(NamespaceNameKey) + 4);
|
||||
@@ -10668,14 +10673,14 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {
|
||||
\\ {d} func_ies: {d} bytes
|
||||
\\ {d} type_layout: {d} bytes
|
||||
\\ {d} struct_defaults: {d} bytes
|
||||
\\ {d} zon_file: {d} bytes
|
||||
\\ {d} source_file: {d} bytes
|
||||
\\ {d} embed_file: {d} bytes
|
||||
\\ {d} namespace: {d} bytes
|
||||
\\ {d} namespace_name: {d} bytes
|
||||
\\
|
||||
, .{
|
||||
dep_entries_size + src_hash_deps_size + nav_val_deps_size + nav_ty_deps_size +
|
||||
func_ies_deps_size + type_layout_deps_size + struct_defaults_deps_size + zon_file_deps_size +
|
||||
func_ies_deps_size + type_layout_deps_size + struct_defaults_deps_size + source_file_deps_size +
|
||||
embed_file_deps_size + namespace_deps_size + namespace_name_deps_size,
|
||||
dep_entries_len,
|
||||
dep_entries_size,
|
||||
@@ -10691,8 +10696,8 @@ fn dumpDependencyStatsFallible(ip: *const InternPool, w: *Io.Writer) !void {
|
||||
type_layout_deps_size,
|
||||
struct_defaults_deps_len,
|
||||
struct_defaults_deps_size,
|
||||
zon_file_deps_len,
|
||||
zon_file_deps_size,
|
||||
source_file_deps_len,
|
||||
source_file_deps_size,
|
||||
embed_file_deps_len,
|
||||
embed_file_deps_size,
|
||||
namespace_deps_len,
|
||||
|
||||
+2
-2
@@ -13011,6 +13011,7 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
|
||||
};
|
||||
const file_index = result.file;
|
||||
const file = zcu.fileByIndex(file_index);
|
||||
try sema.declareDependency(.{ .source_file = file_index });
|
||||
switch (file.getMode()) {
|
||||
.zig => {
|
||||
try pt.ensureFilePopulated(file_index);
|
||||
@@ -13028,8 +13029,6 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
|
||||
if (res_ty.isGenericPoison()) break :b .none;
|
||||
break :b res_ty.toIntern();
|
||||
};
|
||||
|
||||
try sema.declareDependency(.{ .zon_file = file_index });
|
||||
const interned = try LowerZon.run(
|
||||
sema,
|
||||
file,
|
||||
@@ -34084,6 +34083,7 @@ pub fn analyzeMemoizedState(sema: *Sema, stage: InternPool.MemoizedStateStage) C
|
||||
// Get the main struct type of the root source file of `std`. No need for a reference entry
|
||||
// because `std` is always an analysis root.
|
||||
const std_file_index = zcu.module_roots.get(zcu.std_mod).?.unwrap().?;
|
||||
try sema.declareDependency(.{ .source_file = std_file_index });
|
||||
try pt.ensureFilePopulated(std_file_index);
|
||||
const std_type: Type = .fromInterned(zcu.fileRootType(std_file_index));
|
||||
break :block .{
|
||||
|
||||
+3
-3
@@ -1014,7 +1014,7 @@ pub const File = struct {
|
||||
/// changed -- this field is just a simple boolean.
|
||||
///
|
||||
/// When `zoir` is updated, this field is set to `true`. In `updateZirRefs`, if this is `true`,
|
||||
/// we invalidate the corresponding `zon_file` dependency, and reset it to `false`.
|
||||
/// we invalidate the corresponding `source_file` dependency, and reset it to `false`.
|
||||
zoir_invalidated: bool,
|
||||
|
||||
pub const Path = struct {
|
||||
@@ -4496,9 +4496,9 @@ fn formatDependee(data: FormatDependee, writer: *Io.Writer) Io.Writer.Error!void
|
||||
const fqn = ip.getNav(ip.indexToKey(ip_index).func.owner_nav).fqn;
|
||||
return writer.print("func_ies('{f}')", .{fqn.fmt(ip)});
|
||||
},
|
||||
.zon_file => |file| {
|
||||
.source_file => |file| {
|
||||
const file_path = zcu.fileByIndex(file).path;
|
||||
return writer.print("zon_file('{f}')", .{file_path.fmt(zcu.comp)});
|
||||
return writer.print("source_file('{f}')", .{file_path.fmt(zcu.comp)});
|
||||
},
|
||||
.embed_file => |ef_idx| {
|
||||
const ef = ef_idx.get(zcu);
|
||||
|
||||
+37
-15
@@ -838,7 +838,7 @@ fn updateZirRefs(pt: Zcu.PerThread) (Io.Cancelable || Allocator.Error)!void {
|
||||
.zig => {}, // logic below
|
||||
.zon => {
|
||||
if (file.zoir_invalidated) {
|
||||
try zcu.markDependeeOutdated(.not_marked_po, .{ .zon_file = file_index });
|
||||
try zcu.markDependeeOutdated(.not_marked_po, .{ .source_file = file_index });
|
||||
file.zoir_invalidated = false;
|
||||
}
|
||||
continue;
|
||||
@@ -988,8 +988,8 @@ fn updateZirRefs(pt: Zcu.PerThread) (Io.Cancelable || Allocator.Error)!void {
|
||||
// be re-analyzed (causing the struct's namespace to be re-scanned). It's fine to do this
|
||||
// now because this work is fast (no actual Sema work is happening, we're just updating the
|
||||
// namespace contents). We must do this after updating ZIR refs above, since `scanNamespace`
|
||||
// will track some instructions.
|
||||
try pt.updateFileNamespace(file_index);
|
||||
// calls will track some instructions.
|
||||
try pt.updateFileRootStructType(file_index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2350,27 +2350,49 @@ fn analyzeFuncBody(
|
||||
return .{ .ies_outdated = ies_outdated };
|
||||
}
|
||||
|
||||
/// Re-scan the namespace of a file's root struct type on an incremental update.
|
||||
/// The file must have successfully populated ZIR.
|
||||
/// If the file's root struct type is not populated (the file is unreferenced), nothing is done.
|
||||
/// This is called by `updateZirRefs` for all updated files before the main work loop.
|
||||
/// This function does not perform any semantic analysis.
|
||||
fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.Error!void {
|
||||
/// The given file has been modified on this incremental update, so if it has a populated root
|
||||
/// struct type, either re-scan its namespace, or clear it and invalidate dependencies if the
|
||||
/// type is no longer valid. See comments in body for more details.
|
||||
///
|
||||
/// Called by `updateZirRefs` for all updated Zig source files before the main update loop.
|
||||
///
|
||||
/// Asserts that the file has successfully populated ZIR.
|
||||
fn updateFileRootStructType(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.Error!void {
|
||||
const zcu = pt.zcu;
|
||||
const ip = &zcu.intern_pool;
|
||||
|
||||
const file = zcu.fileByIndex(file_index);
|
||||
const file_root_type = zcu.fileRootType(file_index);
|
||||
if (file_root_type == .none) return;
|
||||
if (file_root_type == .none) {
|
||||
// We haven't analyzed any `@import` of this file so far, so there's nothing to update. If
|
||||
// an `@import` gets analyzed, then `ensureFilePopulated` will create the root struct type
|
||||
// and scan the namespace.
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug("updateFileNamespace mod={s} sub_file_path={s}", .{
|
||||
const loaded_struct = ip.loadStructType(file_root_type);
|
||||
|
||||
log.debug("updateFileRootStructType mod={s} sub_file_path={s}", .{
|
||||
file.mod.?.fully_qualified_name,
|
||||
file.sub_file_path,
|
||||
});
|
||||
|
||||
const namespace_index = Type.fromInterned(file_root_type).getNamespaceIndex(zcu);
|
||||
const decls = file.zir.?.getStructDecl(.main_struct_inst).decls;
|
||||
try pt.scanNamespace(namespace_index, decls);
|
||||
zcu.namespacePtr(namespace_index).generation = zcu.generation;
|
||||
if (loaded_struct.zir_index.resolve(ip) == null) {
|
||||
// The file's root struct decl has been lost, so a new struct type must be interned at a new
|
||||
// `InternPool.Index`. Clear the file's root type so that `ensureFilePopulated` will do that
|
||||
// work, and invalidate dependencies on this file to force re-analysis of `@import` sites.
|
||||
zcu.setFileRootType(file_index, .none);
|
||||
try zcu.markDependeeOutdated(.not_marked_po, .{ .source_file = file_index });
|
||||
} else {
|
||||
// The existing struct type is valid, but the namespace contents might have changed. For
|
||||
// most struct types, that would cause the surrounding declaration to be invalidated which
|
||||
// causes `Sema.zirStructType` (or whatever) to call `ensureNamespaceUpToDate`. However,
|
||||
// there is no "surrounding declaration" for the root struct type of a Zig source file, so
|
||||
// update this namespace now.
|
||||
const decls = file.zir.?.getStructDecl(.main_struct_inst).decls;
|
||||
try pt.scanNamespace(loaded_struct.namespace, decls);
|
||||
zcu.namespacePtr(loaded_struct.namespace).generation = zcu.generation;
|
||||
}
|
||||
}
|
||||
|
||||
/// Called by AstGen worker threads when an import is seen. If `new_file` is returned, the caller is
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
#target=x86_64-linux-selfhosted
|
||||
#target=x86_64-windows-selfhosted
|
||||
#target=x86_64-linux-cbe
|
||||
#target=x86_64-windows-cbe
|
||||
//#target=wasm32-wasi-selfhosted
|
||||
#update=initial version
|
||||
#file=main.zig
|
||||
const S = struct { x: u8 };
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{});
|
||||
printFieldCount(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
printOneField(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
}
|
||||
fn printFieldCount(w: *Writer) Writer.Error!void {
|
||||
try w.print("{d} ", .{@typeInfo(S).@"struct".fields.len});
|
||||
}
|
||||
fn printOneField(w: *Writer) Writer.Error!void {
|
||||
const val: S = .{ .x = 100 };
|
||||
try w.print("{d}\n", .{val.x});
|
||||
}
|
||||
const std = @import("std");
|
||||
const Writer = std.Io.Writer;
|
||||
#expect_stdout="1 100\n"
|
||||
|
||||
#update=add a field
|
||||
#file=main.zig
|
||||
const S = struct { x: u8, y: u16 = 200 };
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{});
|
||||
printFieldCount(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
printOneField(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
}
|
||||
fn printFieldCount(w: *Writer) Writer.Error!void {
|
||||
try w.print("{d} ", .{@typeInfo(S).@"struct".fields.len});
|
||||
}
|
||||
fn printOneField(w: *Writer) Writer.Error!void {
|
||||
const val: S = .{ .x = 100 };
|
||||
try w.print("{d}\n", .{val.x});
|
||||
}
|
||||
const std = @import("std");
|
||||
const Writer = std.Io.Writer;
|
||||
#expect_stdout="2 100\n"
|
||||
|
||||
#update=remove all fields
|
||||
#file=main.zig
|
||||
const S = struct {};
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{});
|
||||
printFieldCount(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
printOneField(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
}
|
||||
fn printFieldCount(w: *Writer) Writer.Error!void {
|
||||
try w.print("{d} ", .{@typeInfo(S).@"struct".fields.len});
|
||||
}
|
||||
fn printOneField(w: *Writer) Writer.Error!void {
|
||||
const val: S = .{ .x = 100 };
|
||||
try w.print("{d}\n", .{val.x});
|
||||
}
|
||||
const std = @import("std");
|
||||
const Writer = std.Io.Writer;
|
||||
#expect_error=main.zig:15:24: error: no field named 'x' in struct 'main.S'
|
||||
#expect_error=main.zig:1:11: note: struct declared here
|
||||
|
||||
#update=remove reference to non-existent field
|
||||
#file=main.zig
|
||||
const S = struct {};
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{});
|
||||
printFieldCount(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
printOneField(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
}
|
||||
fn printFieldCount(w: *Writer) Writer.Error!void {
|
||||
try w.print("{d} ", .{@typeInfo(S).@"struct".fields.len});
|
||||
}
|
||||
fn printOneField(w: *Writer) Writer.Error!void {
|
||||
//const val: S = .{ .x = 100 };
|
||||
//try w.print("{d}\n", .{val.x});
|
||||
try w.writeAll("<no fields>\n");
|
||||
}
|
||||
const std = @import("std");
|
||||
const Writer = std.Io.Writer;
|
||||
#expect_stdout="0 <no fields>\n"
|
||||
@@ -0,0 +1,101 @@
|
||||
#target=x86_64-linux-selfhosted
|
||||
#target=x86_64-windows-selfhosted
|
||||
#target=x86_64-linux-cbe
|
||||
#target=x86_64-windows-cbe
|
||||
//#target=wasm32-wasi-selfhosted
|
||||
#update=initial version
|
||||
#file=main.zig
|
||||
const S = @This();
|
||||
x: u8,
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{});
|
||||
printFieldCount(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
printOneField(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
}
|
||||
fn printFieldCount(w: *Writer) Writer.Error!void {
|
||||
try w.print("{d} ", .{@typeInfo(S).@"struct".fields.len});
|
||||
}
|
||||
fn printOneField(w: *Writer) Writer.Error!void {
|
||||
const val: S = .{ .x = 100 };
|
||||
try w.print("{d}\n", .{val.x});
|
||||
}
|
||||
const std = @import("std");
|
||||
const Writer = std.Io.Writer;
|
||||
#expect_stdout="1 100\n"
|
||||
|
||||
#update=add a field
|
||||
#file=main.zig
|
||||
const S = @This();
|
||||
x: u8,
|
||||
y: u16 = 200,
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{});
|
||||
printFieldCount(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
printOneField(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
}
|
||||
fn printFieldCount(w: *Writer) Writer.Error!void {
|
||||
try w.print("{d} ", .{@typeInfo(S).@"struct".fields.len});
|
||||
}
|
||||
fn printOneField(w: *Writer) Writer.Error!void {
|
||||
const val: S = .{ .x = 100 };
|
||||
try w.print("{d}\n", .{val.x});
|
||||
}
|
||||
const std = @import("std");
|
||||
const Writer = std.Io.Writer;
|
||||
#expect_stdout="2 100\n"
|
||||
|
||||
#update=remove all fields
|
||||
#file=main.zig
|
||||
const S = @This();
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{});
|
||||
printFieldCount(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
printOneField(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
}
|
||||
fn printFieldCount(w: *Writer) Writer.Error!void {
|
||||
try w.print("{d} ", .{@typeInfo(S).@"struct".fields.len});
|
||||
}
|
||||
fn printOneField(w: *Writer) Writer.Error!void {
|
||||
const val: S = .{ .x = 100 };
|
||||
try w.print("{d}\n", .{val.x});
|
||||
}
|
||||
const std = @import("std");
|
||||
const Writer = std.Io.Writer;
|
||||
#expect_error=main.zig:15:24: error: no field named 'x' in struct 'main'
|
||||
#expect_error=main.zig:1:1: note: struct declared here
|
||||
|
||||
#update=remove reference to non-existent field
|
||||
#file=main.zig
|
||||
const S = @This();
|
||||
pub fn main(init: std.process.Init) !void {
|
||||
var stdout_writer = std.Io.File.stdout().writerStreaming(init.io, &.{});
|
||||
printFieldCount(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
printOneField(&stdout_writer.interface) catch |err| switch (err) {
|
||||
error.WriteFailed => return stdout_writer.err.?,
|
||||
};
|
||||
}
|
||||
fn printFieldCount(w: *Writer) Writer.Error!void {
|
||||
try w.print("{d} ", .{@typeInfo(S).@"struct".fields.len});
|
||||
}
|
||||
fn printOneField(w: *Writer) Writer.Error!void {
|
||||
//const val: S = .{ .x = 100 };
|
||||
//try w.print("{d}\n", .{val.x});
|
||||
try w.writeAll("<no fields>\n");
|
||||
}
|
||||
const std = @import("std");
|
||||
const Writer = std.Io.Writer;
|
||||
#expect_stdout="0 <no fields>\n"
|
||||
Reference in New Issue
Block a user