Skip to content

Zig 0.16 - #14

Merged
WJQSERVER merged 10 commits into
masterfrom
zig-0.16-io
Apr 17, 2026
Merged

Zig 0.16 #14
WJQSERVER merged 10 commits into
masterfrom
zig-0.16-io

Conversation

@WJQSERVER

@WJQSERVER WJQSERVER commented Apr 17, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

发行说明

  • 新功能

    • 在 Windows 平台上启用流文件句柄复制基准测试
    • 增强 Windows 上的流操作兼容性支持
  • 文档

    • 更新 Zig 工具链版本需求:0.15.2 → 0.16.0(涵盖多语言文档)
  • 基础设施

    • 新增 Windows ARM64 交叉编译 CI 工作流支持

@coderabbitai

coderabbitai Bot commented Apr 17, 2026

Copy link
Copy Markdown

Walkthrough

本PR更新Zig版本要求从0.15.2至0.16.0,完善CI流程以支持Windows ARM64交叉编译,改进流运行时与Windows兼容性,调整汇编堆栈管理策略,并修订相关测试。

Changes

Cohort / File(s) Summary
Zig版本文档更新
README.md, README_ja.md, README_zh.md, docs/usage*.md, docs/*/usage.md, docs/*/ci.md
将所有文档中Zig工具链版本要求从0.15.2更新至0.16.0。
CI工作流扩展
.github/workflows/ci.yml
添加build-winarm64-runtime任务进行Windows ARM64交叉编译,引入矩阵中的setup_zigprepare_cmd字段控制Zig安装与准备步骤,新增GO2ZIG_BASIC_RUNTIME_PATH环境变量以支持预编译运行时。
汇编堆栈管理重构
asmcall/amd64.s, asmcall/amd64_windows.s
替换影子堆栈空间宏定义,改用寄存器显式保存/恢复堆栈指针的方式;引入RTMP0临时寄存器,调整16字节对齐与堆栈分配逻辑。
流运行时Windows兼容性
examples/stream/go2zig_runtime.zig, examples/stream/lib.zig, internal/generator/generator.go
添加builtin导入进行Windows检测;将句柄类型改为std.Io.File.Handle;引入streamFileFromEncodedstreamFilestreamReadCompat/streamWriteCompat兼容层,在Windows上通过std.Io.File流式API处理I/O,其他平台使用POSIX系统调用。
生成器和测试调整
examples/basic/testlib_test.go, examples/stream/bench_test.go, examples/stream/stream_test.go, internal/generator/generator_test.go
testlib_test.go添加环境变量GO2ZIG_BASIC_RUNTIME_PATH支持;bench_test.go移除Windows跳过条件;stream_test.go重构为显式客户端加载与多次调用测试;generator_test.go更新期望的Zig运行时片段以反映新的兼容性实现。

Sequence Diagram

sequenceDiagram
    participant Go as Go 测试/应用
    participant ZigRT as Zig 运行时<br/>go2zig_runtime.zig
    participant Platform as 平台层<br/>(Windows/POSIX)
    
    Go->>ZigRT: copy_stream(reader, writer)
    ZigRT->>ZigRT: streamFile(reader_handle)
    alt 有效文件句柄
        ZigRT->>ZigRT: copy_stream_file_handles()
        alt Windows 平台
            ZigRT->>Platform: std.Io.File 流式读/写
            Platform-->>ZigRT: 数据/错误
        else 非 Windows 平台
            ZigRT->>Platform: std.posix.read/write
            Platform-->>ZigRT: 字节计数/错误
        end
        ZigRT-->>Go: 复制字节数
    else 无文件句柄
        ZigRT->>ZigRT: 缓冲读/写循环
        ZigRT-->>Go: 复制字节数
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 拉链升级到 0.16 版,
ARM64 窗口也开放,
汇编栈舞重排序,
流兼容跨平台翔,
小白兔的代码又进步啦!🎉

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch zig-0.16-io

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the project to Zig 0.16.0 across documentation, CI configurations, and source code. Key changes include the implementation of stack alignment in amd64 assembly macros and a refactor of the stream I/O logic to utilize std.Io.Threaded for better Windows compatibility. Review feedback focuses on ensuring thread safety when the Zig runtime is invoked from Go's multi-threaded environment, specifically recommending the use of std.Io.Threaded.global instead of single-threaded instances. Additionally, it is suggested to simplify the manual POSIX write loops by leveraging the improved std.posix.write wrapper available in Zig 0.16.0.

Comment thread examples/stream/go2zig_runtime.zig
Comment thread examples/stream/go2zig_runtime.zig
Comment thread internal/generator/generator.go
Comment thread internal/generator/generator.go
Comment thread examples/stream/go2zig_runtime.zig
Comment thread examples/stream/lib.zig
Comment thread internal/generator/generator.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
docs/en/ci.md (1)

13-20: ⚠️ Potential issue | 🟡 Minor

Keep the Windows ARM64 exception reflected here.

This says every matrix entry installs Zig, but the workflow skips Zig setup for windows/arm64 tests and uses the downloaded prebuilt basic.dll instead. Please document that exception.

Suggested wording
- - Install Zig `0.16.0`
+ - Install Zig `0.16.0`, except for the `windows/arm64` test job, which uses a prebuilt downloaded `basic.dll`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/en/ci.md` around lines 13 - 20, Update the CI docs to reflect the
Windows ARM64 exception: in docs/en/ci.md add a brief note after the matrix
bullet list explaining that the workflow does not install Zig for the
windows/arm64 job but instead skips Zig setup and uses a prebuilt
examples/basic/basic.dll artifact; reference the job/platform as "windows/arm64"
and mention that other matrix entries still install Zig 0.16.0 and regenerate
examples/basic before testing.
docs/ci.md (1)

13-20: ⚠️ Potential issue | 🟡 Minor

同步 Windows ARM64 的 CI 描述。

这里写成“每个矩阵条目都会安装 Zig”,但工作流里 windows/arm64 测试项是 setup_zig: false,改为下载预构建 basic.dll。建议把这条改成例外描述,避免本地复现或排查 CI 时误判。

建议调整
- - 安装 Zig `0.16.0`
+ - 除 `windows/arm64` 测试 job 外安装 Zig `0.16.0`;`windows/arm64` 使用预构建并下载的 `basic.dll`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/ci.md` around lines 13 - 20, 把文档中通用说明“安装 Zig `0.16.0`”改为对 windows/arm64
的例外说明:在说明列表中保留一条通用行为“安装 Zig `0.16.0`”,并在同一段或紧随其后的句子里明确标注当测试矩阵项为
`windows/arm64`(workflow 中 `setup_zig: false`)时不会安装 Zig 而是下载预构建的
`basic.dll`,以便读者和 CI 排查时不会误以为该平台也做本地 Zig 安装或编译。
examples/stream/stream_test.go (1)

183-229: ⚠️ Potential issue | 🟡 Minor

TestCopyStreamHandlesPartialWrites 依赖对生成代码的字符串替换,耦合脆弱

Line 186-187 对 go2zig_runtime.zig 源做字面字符串替换来注入"每次最多写 17 字节"的逻辑。只要生成器或静态样例中那段代码的空白 / 换行 / 分号有任何微调,strings.Replace(..., 1) 会静默命中 0 次、然后用"未改过的运行时"去测试,测试仍然会通过(因为逻辑上等价),失去了"验证分片写入"的覆盖意义。

建议要么:

  • 在替换后立即断言 runtimeSource 与原文不同(if runtimeSource == original { t.Fatalf(...) });或
  • 将"人为限制最大写"这一行为抽成 runtime 中可参数化的开关(如 extern const stream_write_cap),避免测试依赖字面匹配。
🛡️ 建议增加替换生效的前置断言
 	runtimeSource := strings.Replace(
 		string(readStreamExampleFile(t, "go2zig_runtime.zig")),
 		"if (buffer.len > available) return error.StreamWriteFailed;\n        const dst = `@as`([*]u8, `@ptrCast`(state.ptr.?))[state.len..][0..buffer.len];\n        `@memcpy`(dst, buffer);\n        state.len += buffer.len;\n        return buffer.len;",
 		"const limit = `@min`(buffer.len, `@as`(usize, 17));\n        if (limit > available) return error.StreamWriteFailed;\n        const dst = `@as`([*]u8, `@ptrCast`(state.ptr.?))[state.len..][0..limit];\n        `@memcpy`(dst, buffer[0..limit]);\n        state.len += limit;\n        return limit;",
 		1,
 	)
+	if !strings.Contains(runtimeSource, "const limit = `@min`(buffer.len, `@as`(usize, 17));") {
+		t.Fatalf("runtime source replacement did not take effect; update the anchor string")
+	}
 	libPath := buildCustomStreamRuntime(t, "", runtimeSource)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/stream/stream_test.go` around lines 183 - 229, The test
TestCopyStreamHandlesPartialWrites mutates the runtime by calling
strings.Replace on the string returned by readStreamExampleFile to inject a
17-byte write limit but doesn't verify the replacement succeeded; add an
immediate assertion after creating runtimeSource (using the original string from
readStreamExampleFile or by checking runtimeSource contains the expected "const
limit" snippet) and call t.Fatalf if the replacement didn't change runtimeSource
so the test fails fast when strings.Replace matched 0 occurrences; reference
runtimeSource, readStreamExampleFile, strings.Replace and
buildCustomStreamRuntime when adding this check.
🧹 Nitpick comments (5)
asmcall/amd64.s (1)

5-17: 移除不可达的 Windows 条件分支。

文件 Line 1 限定 //go:build amd64 && !windows,因此 Line 5 的 #ifdef GOOS_windows 分支在此文件中永远不会被编译。Windows 的 ABI 处理已在独立的 asmcall/amd64_windows.s 中实现。在不可达分支中添加 RTMP0 定义容易误导维护者。

♻️ 可选简化
-#ifdef GOOS_windows
-#define RARG0 CX
-#define RARG1 DX
-#define RARG2 R8
-#define RRET AX
-#define RTMP0 R9
-#else
 `#define` RARG0 DI
 `#define` RARG1 SI
 `#define` RARG2 DX
 `#define` RRET AX
 `#define` RTMP0 CX
-#endif
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@asmcall/amd64.s` around lines 5 - 17, Remove the unreachable Windows
conditional block guarded by `#ifdef GOOS_windows` and keep only the non-Windows
macro definitions so the file matches the `//go:build amd64 && !windows`
restriction; specifically delete the entire `#ifdef GOOS_windows ... `#else``
branch and ensure the remaining definitions set RARG0=DI, RARG1=SI, RARG2=DX,
RRET=AX and RTMP0=CX (the symbols to look for are the macros RARG0, RARG1,
RARG2, RRET, RTMP0 and the `#ifdef GOOS_windows`/`#else`/`#endif` conditional).
internal/generator/generator.go (1)

305-324: streamFile 仅剔除了低位标志,未过滤 direct reader/writer 标志之外的情形

streamFile 通过 (value & stream_handle_mask) != 0 拒绝所有带低 2 位标志的句柄(包括 direct_reader=0x1、direct_writer=0x2),仅放行标志为 0 的"编码过的 fd"路径,与 streamRead/streamWrite 中对 direct tag 的处理保持互补,整体一致。

不过此处有一个隐藏前提:streamFileFromEncoded 会直接计算 (value >> 2) - 1。若 value 恰为某个"编码为 0"的合法值(编码规则 (handle + 1) << 2,当 handle == usize_max >> 2 - 1 时),外层已经由 _go2zigEncodeFileHandlemaxEncodedFileHandle 检查在 Go 侧拦截(见 Line 759–761 生成片段)。但 streamFile 本身并没有做这层自校验——只依赖调用方传入的是合法编码。作为面向公共 API 的 pub inline fn(被 examples/stream/lib.zig:6-7 直接使用),建议补充对 (value >> 2) == 0 的防御性 return null,避免下溢触发未定义行为。

♻️ 建议增加下溢防御
 pub inline fn streamFile(value: usize) ?std.Io.File {
     if (value == 0) return null;
     if ((value & stream_handle_mask) != 0) return null;
+    if ((value >> 2) == 0) return null;
     return streamFileFromEncoded(value);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/generator/generator.go` around lines 305 - 324, streamFile currently
only masks low bits and then calls streamFileFromEncoded which computes (value
>> 2) - 1, risking underflow if value encodes to 0; add a defensive check in pub
inline fn streamFile(value: usize) ?std.Io.File to return null when (value >> 2)
== 0 before calling streamFileFromEncoded. Update streamFile to perform both the
existing low-bit mask check and this new zero-after-shift check so
streamFileFromEncoded/streamHandleFromEncoded never see a value that would
underflow.
internal/generator/generator_test.go (1)

325-325: 单行过长的 checks 列表降低可读性

TestRenderStreamsWithExperimentalFlag 的 checks 切片被全部塞进一行(Line 325),包含 20+ 条字符串断言。这类"黄金字符串"测试在跨行格式时通常更易维护(diff 也更局部化)。建议拆分为多行字面量:

♻️ 建议拆分为多行以便 diff 与评审
-	for _, check := range []string{"const builtin = `@import`(\"builtin\");", "inline fn streamHandleFromUsize(value: usize) std.Io.File.Handle", /* …many items… */, "return streamWriteCompat(streamHandleFromEncoded(writer), buffer) catch { return error.StreamWriteFailed; };"} {
+	for _, check := range []string{
+		"const builtin = `@import`(\"builtin\");",
+		"inline fn streamHandleFromUsize(value: usize) std.Io.File.Handle",
+		"inline fn streamHandleFromEncoded(value: usize) std.Io.File.Handle",
+		"inline fn streamFileFromEncoded(value: usize) std.Io.File",
+		"pub inline fn streamFile(value: usize) ?std.Io.File",
+		"if ((value & stream_handle_mask) != 0) return null;",
+		"const stream_handle_direct_reader: usize = 0x1;",
+		"const DirectReaderState = extern struct",
+		"const DirectWriterState = extern struct",
+		"const stream_is_windows = builtin.os.tag == .windows;",
+		"inline fn streamReadCompat(handle: std.Io.File.Handle, buffer: []u8) anyerror!usize",
+		"const io = std.Io.Threaded.global_single_threaded.io();",
+		"return file.readStreaming(io, &.{buffer}) catch |err| switch (err) {",
+		"error.EndOfStream => 0,",
+		"std.posix.read(handle, buffer)",
+		"inline fn streamWriteCompat(handle: std.Io.File.Handle, buffer: []const u8) anyerror!usize",
+		"return file.writeStreaming(io, &.{}, &.{buffer}, 1);",
+		"std.posix.system.write(handle, buffer.ptr, buffer.len)",
+		"std.posix.errno(rc)",
+		"pub fn streamRead(reader: usize, buffer: []u8)",
+		"pub fn streamWrite(writer: usize, buffer: []const u8)",
+		"const n = streamReadCompat(streamHandleFromEncoded(reader), buffer) catch { return error.StreamReadFailed; };",
+		"if (n == 0) return error.EndOfStream;",
+		"return streamWriteCompat(streamHandleFromEncoded(writer), buffer) catch { return error.StreamWriteFailed; };",
+	} {

另外建议补充一条断言覆盖 streamWriteCompat 在非 Windows 分支的 errno case(例如 ".PIPE => return error.BrokenPipe,"),保证 errno→error 映射表被测试显式锁定,避免未来误删。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/generator/generator_test.go` at line 325, The checks slice in
TestRenderStreamsWithExperimentalFlag is a single very long line which harms
readability; split the []string literal across multiple lines so each expected
string (e.g. "const builtin = `@import`(\"builtin\");", "inline fn
streamHandleFromUsize(value: usize) std.Io.File.Handle", "inline fn
streamWriteCompat(handle: std.Io.File.Handle, buffer: []const u8)
anyerror!usize", "pub fn streamRead(reader: usize, buffer: []u8)", etc.) is on
its own line to make diffs smaller and reviews easier, and while editing add one
more assertion covering the non-Windows errno→error mapping for
streamWriteCompat (for example an entry for ".PIPE => return error.BrokenPipe,")
so the errno mapping logic for streamWriteCompat is explicitly tested; update
the checks used in TestRenderStreamsWithExperimentalFlag accordingly.
examples/stream/stream_test.go (1)

88-114: 三处 client/rt/lib 关闭 cleanup 完全重复,建议抽取辅助函数

TestCopyStreamFileHandlesTestCopyStreamFileHandlesRepeatedTestCopyStreamDoesNotPanicOnWriterCloseError(以及部分 TestCopyStreamHandlesPartialWrites)都重复了同一个 client 构造+Load+Cleanup 的 5 行模板。可提炼一个 helper:

♻️ 建议抽取 `newLoadedClient` helper
+func newLoadedClient(t *testing.T, path string) *Go2ZigClient {
+	t.Helper()
+	client := NewGo2ZigClient(path)
+	if err := client.Load(); err != nil {
+		t.Fatalf("Load() error = %v", err)
+	}
+	t.Cleanup(func() {
+		if client != nil && client.rt != nil && client.rt.lib != nil {
+			_ = client.rt.lib.Close()
+		}
+	})
+	return client
+}

 func TestCopyStreamFileHandles(t *testing.T) {
-	prepareStreamRuntime(t)
-	client := NewGo2ZigClient(prepareRuntimePath)
-	if err := client.Load(); err != nil {
-		t.Fatalf("Load() error = %v", err)
-	}
-	t.Cleanup(func() {
-		if client != nil && client.rt != nil && client.rt.lib != nil {
-			_ = client.rt.lib.Close()
-		}
-	})
+	prepareStreamRuntime(t)
+	client := newLoadedClient(t, prepareRuntimePath)
 
 	payload := bytes.Repeat([]byte("go2zig-stream-copy-"), 1024)
 	copyStreamFileHandlesOnce(t, client, payload)
 }

Also applies to: 195-241

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/stream/stream_test.go` around lines 88 - 114, Several tests repeat
the same client setup and cleanup (prepareStreamRuntime,
NewGo2ZigClient(prepareRuntimePath), client.Load() and the t.Cleanup that closes
client.rt.lib) — extract a helper like newLoadedClient(t *testing.T) that calls
prepareStreamRuntime, constructs NewGo2ZigClient(prepareRuntimePath), checks
client.Load() and registers a single t.Cleanup that safely closes client.rt.lib
(checking nils) and return the loaded client; update TestCopyStreamFileHandles,
TestCopyStreamFileHandlesRepeated, TestCopyStreamDoesNotPanicOnWriterCloseError
and TestCopyStreamHandlesPartialWrites to use newLoadedClient instead of
duplicating the five-line setup/cleanup.
examples/stream/lib.zig (1)

9-14: 每次调用都新建 std.Io.Threaded 实例,与 runtime 的 global_single_threaded 不一致

此处使用 .init_single_threaded 在栈上创建一次性实例,而 go2zig_runtime.zig 中的 streamReadCompat/streamWriteCompat 使用的是 std.Io.Threaded.global_single_threaded.io()。两者都不支持真正的多线程并发,但行为细节存在差异:

  • init_single_threaded 是栈上的局部实例,生命周期与本函数一致,scope 退出即回收;
  • global_single_threaded 是进程级全局单例,根据 Zig 0.16 release notes,它与 -fsingle-threaded 配合使用时不支持任务级并发与取消。

copy_stream_file_handles 在 Go 多 goroutine 并发调用下被触发(FFI 场景常见),sendFileAll 内部可能借助 io 实例持有瞬态资源,各实例相互独立是安全的;但如果未来切换为 global_single_threaded 以保持一致性,则需要重新评估并发安全。建议保留当前写法并添加一行注释说明"为并发安全而刻意每次新建",或用其他手段明确两处选择的差异。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/stream/lib.zig` around lines 9 - 14, The current code creates a new
std.Io.Threaded via .init_single_threaded for each call (variables
reader_buf/writer_buf, src.readerStreaming and dst.writerStreaming), which
differs from the runtime's std.Io.Threaded.global_single_threaded used by
streamReadCompat/streamWriteCompat; either explicitly document why we create
per-call instances for concurrency safety (e.g., when copy_stream_file_handles
or sendFileAll may be called concurrently from FFI) or switch both call sites to
use the same global_single_threaded strategy—add a concise comment next to the
.init_single_threaded instantiation explaining the deliberate choice (or replace
with global_single_threaded and ensure concurrency implications are
re-evaluated).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 13-30: The build-winarm64-runtime job compiles
examples/basic/go2zig_build_root.zig before the repository's "Prepare basic
example" step runs, so the artifact can be built from stale generated sources;
modify the job to regenerate the basic example first (e.g., add the same
"Prepare basic example" step or run the generator script) prior to invoking zig,
ensuring the job name build-winarm64-runtime runs the prepare step that produces
examples/basic/go2zig_build_root.zig (or depends on the job that does) so the
produced basic.dll reflects the latest generator/runtime changes.

In `@examples/basic/testlib_test.go`:
- Around line 33-37: 当前用 os.Stat 只检查了路径存在但没拒绝目录:在对 basicRuntimePathEnv
做存在性检查后(在现在设置 prepareRuntimeErr 的分支中或紧接其后),调用 os.Stat(...) 返回的 FileInfo 并用
fi.IsDir() 判断是否为目录;如果是目录则设置 prepareRuntimeErr 为类似 "override path %s is a
directory" 的错误并返回,确保不要把目录赋给
prepareRuntimePath。引用符号:basicRuntimePathEnv、prepareRuntimeErr、prepareRuntimePath、os.Stat、FileInfo.IsDir。

In `@examples/stream/lib.zig`:
- Around line 5-22: The function copy_stream_file_handles currently returns the
byte count from dst_writer.interface.sendFileAll (copied) before calling
dst_writer.flush, which means a later flush failure invalidates the reported
"written" bytes; change the flow in copy_stream_file_handles to call
dst_writer.flush() immediately after sendFileAll succeeds and only then return
`@intCast`(copied); keep the existing error mapping for sendFileAll
(error.ReadFailed / error.WriteFailed) and make flush failures return
error.StreamWriteFailed as before so the returned integer always represents
bytes that have been flushed/persisted; update references to sendFileAll,
dst_writer.flush, and copied accordingly.

In `@internal/generator/generator.go`:
- Around line 346-376: The non-Windows branch of streamWriteCompat reimplements
errno-to-error mapping manually (including mapping BADF to error.Unexpected)
which is asymmetric with streamReadCompat and risks incorrect/ incomplete
mappings; change streamWriteCompat to call std.posix.write directly (like
streamReadCompat) so the stdlib handles EINTR/retries and full errno
translation, and if you still need special-casing replace the BADF =>
error.Unexpected mapping with a clear intented error (e.g.
error.NotOpenForWriting or error.InvalidHandle) only after calling
std.posix.write and inspecting the returned error.

---

Outside diff comments:
In `@docs/ci.md`:
- Around line 13-20: 把文档中通用说明“安装 Zig `0.16.0`”改为对 windows/arm64
的例外说明:在说明列表中保留一条通用行为“安装 Zig `0.16.0`”,并在同一段或紧随其后的句子里明确标注当测试矩阵项为
`windows/arm64`(workflow 中 `setup_zig: false`)时不会安装 Zig 而是下载预构建的
`basic.dll`,以便读者和 CI 排查时不会误以为该平台也做本地 Zig 安装或编译。

In `@docs/en/ci.md`:
- Around line 13-20: Update the CI docs to reflect the Windows ARM64 exception:
in docs/en/ci.md add a brief note after the matrix bullet list explaining that
the workflow does not install Zig for the windows/arm64 job but instead skips
Zig setup and uses a prebuilt examples/basic/basic.dll artifact; reference the
job/platform as "windows/arm64" and mention that other matrix entries still
install Zig 0.16.0 and regenerate examples/basic before testing.

In `@examples/stream/stream_test.go`:
- Around line 183-229: The test TestCopyStreamHandlesPartialWrites mutates the
runtime by calling strings.Replace on the string returned by
readStreamExampleFile to inject a 17-byte write limit but doesn't verify the
replacement succeeded; add an immediate assertion after creating runtimeSource
(using the original string from readStreamExampleFile or by checking
runtimeSource contains the expected "const limit" snippet) and call t.Fatalf if
the replacement didn't change runtimeSource so the test fails fast when
strings.Replace matched 0 occurrences; reference runtimeSource,
readStreamExampleFile, strings.Replace and buildCustomStreamRuntime when adding
this check.

---

Nitpick comments:
In `@asmcall/amd64.s`:
- Around line 5-17: Remove the unreachable Windows conditional block guarded by
`#ifdef GOOS_windows` and keep only the non-Windows macro definitions so the
file matches the `//go:build amd64 && !windows` restriction; specifically delete
the entire `#ifdef GOOS_windows ... `#else`` branch and ensure the remaining
definitions set RARG0=DI, RARG1=SI, RARG2=DX, RRET=AX and RTMP0=CX (the symbols
to look for are the macros RARG0, RARG1, RARG2, RRET, RTMP0 and the `#ifdef
GOOS_windows`/`#else`/`#endif` conditional).

In `@examples/stream/lib.zig`:
- Around line 9-14: The current code creates a new std.Io.Threaded via
.init_single_threaded for each call (variables reader_buf/writer_buf,
src.readerStreaming and dst.writerStreaming), which differs from the runtime's
std.Io.Threaded.global_single_threaded used by
streamReadCompat/streamWriteCompat; either explicitly document why we create
per-call instances for concurrency safety (e.g., when copy_stream_file_handles
or sendFileAll may be called concurrently from FFI) or switch both call sites to
use the same global_single_threaded strategy—add a concise comment next to the
.init_single_threaded instantiation explaining the deliberate choice (or replace
with global_single_threaded and ensure concurrency implications are
re-evaluated).

In `@examples/stream/stream_test.go`:
- Around line 88-114: Several tests repeat the same client setup and cleanup
(prepareStreamRuntime, NewGo2ZigClient(prepareRuntimePath), client.Load() and
the t.Cleanup that closes client.rt.lib) — extract a helper like
newLoadedClient(t *testing.T) that calls prepareStreamRuntime, constructs
NewGo2ZigClient(prepareRuntimePath), checks client.Load() and registers a single
t.Cleanup that safely closes client.rt.lib (checking nils) and return the loaded
client; update TestCopyStreamFileHandles, TestCopyStreamFileHandlesRepeated,
TestCopyStreamDoesNotPanicOnWriterCloseError and
TestCopyStreamHandlesPartialWrites to use newLoadedClient instead of duplicating
the five-line setup/cleanup.

In `@internal/generator/generator_test.go`:
- Line 325: The checks slice in TestRenderStreamsWithExperimentalFlag is a
single very long line which harms readability; split the []string literal across
multiple lines so each expected string (e.g. "const builtin =
`@import`(\"builtin\");", "inline fn streamHandleFromUsize(value: usize)
std.Io.File.Handle", "inline fn streamWriteCompat(handle: std.Io.File.Handle,
buffer: []const u8) anyerror!usize", "pub fn streamRead(reader: usize, buffer:
[]u8)", etc.) is on its own line to make diffs smaller and reviews easier, and
while editing add one more assertion covering the non-Windows errno→error
mapping for streamWriteCompat (for example an entry for ".PIPE => return
error.BrokenPipe,") so the errno mapping logic for streamWriteCompat is
explicitly tested; update the checks used in
TestRenderStreamsWithExperimentalFlag accordingly.

In `@internal/generator/generator.go`:
- Around line 305-324: streamFile currently only masks low bits and then calls
streamFileFromEncoded which computes (value >> 2) - 1, risking underflow if
value encodes to 0; add a defensive check in pub inline fn streamFile(value:
usize) ?std.Io.File to return null when (value >> 2) == 0 before calling
streamFileFromEncoded. Update streamFile to perform both the existing low-bit
mask check and this new zero-after-shift check so
streamFileFromEncoded/streamHandleFromEncoded never see a value that would
underflow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 7bd105cf-69d1-4a04-b515-e902fda25063

📥 Commits

Reviewing files that changed from the base of the PR and between a3edfa4 and ebfcfea.

📒 Files selected for processing (23)
  • .github/workflows/ci.yml
  • README.md
  • README_ja.md
  • README_zh.md
  • asmcall/amd64.s
  • asmcall/amd64_windows.s
  • docs/ci.en.md
  • docs/ci.md
  • docs/en/ci.md
  • docs/en/usage.md
  • docs/ja/ci.md
  • docs/ja/usage.md
  • docs/usage.en.md
  • docs/usage.md
  • docs/zh/ci.md
  • docs/zh/usage.md
  • examples/basic/testlib_test.go
  • examples/stream/bench_test.go
  • examples/stream/go2zig_runtime.zig
  • examples/stream/lib.zig
  • examples/stream/stream_test.go
  • internal/generator/generator.go
  • internal/generator/generator_test.go
💤 Files with no reviewable changes (1)
  • examples/stream/bench_test.go

Comment thread .github/workflows/ci.yml
Comment thread examples/basic/testlib_test.go
Comment thread examples/stream/lib.zig
Comment thread internal/generator/generator.go
@WJQSERVER
WJQSERVER merged commit 913e268 into master Apr 17, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant