开发日志

此页面包含 Zig 主分支最近更改的精选列表。

也可通过RSS feed获取。

此页面包含2024年的条目。其他年份可在开发日志存档页面中查看。

2024年12月25日

圣诞节特辑:自引用全局值

作者:Matthew Lugg

我最近着手解决了issue #131。这是编译器中一个长期存在的bug,可以追溯到最早的 C++ 引导编译器时期:它是 Zig 仓库中第9个最老的开放问题!本质上,修复它允许你编写这样的代码:

const S = struct { ptr: *const S, x: u32 };

// `foo` contains a pointer to itself
const foo: S = .{ .ptr = &foo, .x = 123 };

// Or:

// `a` contains a pointer to `b`, and...
// `b` contains a pointer to `a`
const a: S = .{ .ptr = &b, .x = 123 };
const b: S = .{ .ptr = &a, .x = 456 };

此前,上述两个示例都会因“检测到依赖循环”错误而失败;现在,它们可以像你预期那样编译(当然,如果它们被声明为var,同样有效)。

这个问题的开放时间如此之长,是因为它实际上需要对编译器进行非平凡的扩展:分离容器级别声明的类型的语义分析。由于某些全局声明可能没有类型注解,以及某些(如extern声明)有类型但没有,这使得情况更加复杂。它还以非平凡的方式与正在进行中的增量编译功能相互作用,因此我们必须确保正确处理。总而言之,这导致了编译器中一个相当大的差异

无论如何,重点是,这现在在 Zig master 分支上可以正常工作了!如果你有用到它的场景,请随意尝试——如果在过程中遇到任何问题,请务必开一个 issue。

祝所有庆祝的人圣诞快乐!

2024年11月19日

Zig 引导程序移植进展

作者:Alex Rønne Petersen

zig-bootstrap项目使得在(几乎)任何目标上生成 Zig 编译器成为可能,并且在构建机器上只需要最少的系统依赖。作为我最近在 Zig 上进行的移植工作的一部分,我将 zig-bootstrap 在我们支持的尽可能多的交叉编译目标三元组(zig targets | jq -r .libc[])上运行作为目标。我很高兴地报告,这项工作现已完成。

撰写本文时,zig-bootstrap 支持的目标三元组列表如下:

  • aarch64-linux-(gnu,musl)
  • aarch64-macos-none
  • aarch64-windows-gnu
  • aarch64_be-linux-(gnu,musl)
  • arm[eb]-linux-(gnu,musl)eabi[hf]
  • loongarch64-linux-(gnu,gnusf,musl)
  • mips[el]-linux-(gnu,musl)eabi[hf]
  • mips64[el]-linux-(gnu,musl)abi(64,n32)
  • powerpc-linux-(gnu,musl)eabi[hf]
  • powerpc64-linux-musl
  • powerpc64le-linux-(gnu,musl)
  • riscv(32,64)-linux-(gnu,musl)
  • s390x-linux-(gnu,musl)
  • thumb[eb]-linux-musleabi[hf]
  • thumb-windows-gnu
  • x86-linux-(gnu,musl)
  • x86-windows-gnu
  • x86_64-linux-(gnu,musl)[x32]
  • x86_64-macos-none
  • x86_64-windows-gnu

最新列表可在此处找到。

如果你一直渴望在某些不常见或冷门的目标上运行 Zig,请尝试使用 zig-bootstrap,看看它对你而言效果如何。如果你遇到任何构建问题,请在zig-bootstrap仓库中报告;运行构建的编译器时遇到的问题应在ziglang/zig中报告。

2024年11月4日

整数命名的不可思议的有效性

作者:Andrew Kelley

最近,我的 Zig 代码在所有项目中都开始像这样:

/// Uniquely identifies a section index across all objects. Each Object has a section_start field.
/// By subtracting that value from this one, the Object section index is obtained.
const SectionIndex = enum(u32) {
    _,
};

/// Index into object_function_imports
pub const ObjectFunctionImportIndex = enum(u32) {
    _,
};

/// Index into `object_global_imports`
pub const ObjectGlobalImportIndex = enum(u32) {
    _,
};

/// Index into object_table_imports.
pub const ObjectTableImportIndex = enum(u32) {
    _,
};

/// Index into `function_imports`.
pub const FunctionImportIndex = enum(u32) {
    _,
};

/// Index into `global_imports`.
pub const GlobalImportIndex = enum(u32) {
    _,
};

/// Index into `table_imports`.
pub const TableImportIndex = enum(u32) {
    _,
};

/// Index into `memory_imports`.
pub const MemoryImportIndex = enum(u32) {
    _,
};

/// Index into `output_globals`.
pub const GlobalIndex = enum(u32) {
    _,
};

/// Index into `functions`.
pub const FunctionIndex = enum(u32) {
    _,
};

/// Index into object_functions
pub const ObjectFunctionIndex = enum(u32) {
    _,

    pub fn toOptional(i: ObjectFunctionIndex) OptionalObjectFunctionIndex {
        const result: OptionalObjectFunctionIndex = @enumFromInt(@intFromEnum(i));
        assert(result != .none);
        return result;
    }
};

/// Index into object_functions, or null.
pub const OptionalObjectFunctionIndex = enum(u32) {
    none = std.math.maxInt(u32),
    _,

    pub fn unwrap(i: OptionalObjectFunctionIndex) ?ObjectFunctionIndex {
        if (i == .none) return null;
        return @enumFromInt(@intFromEnum(i));
    }
};

仅仅通过命名整数,从而让 Zig 的类型系统有机会捕获错误,来避免bug,其效果之惊人令人难以置信。在运行时排除这类错误极其烦人且耗时,但当你从类型不匹配中得到编译错误时,解决起来就微不足道了。

2024年11月1日

匿名结构体类型已移除

作者:Matthew Lugg

信徒们,欢欣鼓舞吧;无文档结构化聚合类型的时代结束了!

直到最近,Zig 还有“匿名结构体类型”的概念。当你编写.{ .x = 123 }而没有结果类型时,它会给这个值一个“匿名结构体”类型,这是一种特殊的结构体,可以根据结构而不是标识符强制转换为其他类型。换句话说,这曾是可行的:

test "coerce anonymous struct" {
    const foo = .{ .x = 123 };
    const S = struct { x: ?u32 };
    // It's true that `@TypeOf(foo) != S`, and yet...
    const bar: S = foo; // ...this works!
    try std.testing.expect(bar.x.? == 123);
}
const std = @import("std");

一个小小的花絮:这之所以存在,是因为很久以前 Zig 并没有“结果类型”的概念,所以所有类型注解都通过这种强制转换来工作!

我去年提议移除此系统,并最终在上周实际完成。你仍然可以编写.{ .x = 123 }而无需结果类型,但它会被赋予一个“普通”结构体类型,不允许进行神奇的强制转换。所以现在,上面的代码示例会像你预期一样失败:

example.zig:5:20: error: expected type 'example.test.coerce anonymous struct.S', found 'example.test.coerce anonymous struct__struct_90'
    const bar: S = foo; // ...this works!
                   ^~~
example.zig:2:18: note: struct declared here
    const foo = .{ .x = 123 };
                ~^~~~~~~~~~~~
example.zig:3:15: note: struct declared here
    const S = struct { x: ?u32 };
              ^~~~~~~~~~~~~~~~~~

真是谢天谢地!

2024年10月29日

kqueue 文件监听已实现

作者:Andrew Kelley

我上周在飞机上(巧合的是,在去参加KQ 锦标赛的路上)完成了#20599,所以如果你正在跟踪 master 分支,你现在可以在 macOS、FreeBSD、OpenBSD、NetBSD、DragonFlyBSD 和 Haiku 上使用zig build --watch功能了。

此功能跟踪所有文件系统输入,自动重复无效的构建步骤。在重构使用基于终端的工作流的代码库时,它会非常方便,因为这意味着当任何构建输入(源代码或其他)更新时,都能即时获得反馈。

KQueue 允许对开放的目录句柄放置一个监听器,但事件不指示任何给定目录中更改的文件名称。这对 Zig 构建系统来说不是问题。如果任何包含被监听文件的目录被触发,相应的步骤就会失效,然后这些步骤使用 fstat 来确定缓存的工件是否仍然有效。

2024年10月6日

增量编译已添加 CI 覆盖

作者:Matthew Lugg

在过去的几个月里,我和 Jakub 一直在努力进行增量编译,即编译器可以“记住”先前构建的部分,并且只重新编译更改过的代码。我们刚刚达到了一个重要的里程碑:我们的 CI 测试套件现在包含了我们的“增量编译”测试!这组测试目前很小,但随着我们修复 bug 并最终对实现进行模糊测试,它会迅速增长。

你可以在这里找到我们所有的增量编译测试用例。CI 在x86_64-linux上运行所有这些测试,使用自托管的 x86_64 后端,以及在一些目标上使用 C 后端(-ofmt=c)。它正在接近你可以开始在一些非常基本的情况下使用增量编译的程度;例如,我最近成功地对Andrew 的俄罗斯方块克隆进行了增量更新。

如果你在 x86_64 架构的 Linux 上使用 Zig 的master构建版本,你现在就可以尝试使用以下命令进行增量编译:

zig build -fincremental --watch

(确保你的Step.Compile设置了.use_llvm = false, .use_lld = false,这样 Zig 就不会尝试使用 LLVM!)

但请注意,你很快就会遇到错误,包括误报的编译错误,甚至是错误的编译;请自行承担风险。

2024年9月30日

新开发日志结构

作者:Loris Cro

开发日志已切换到按年份预分片结构,以避免页面无限增长的问题。

注意: RSS feed 链接未更改。RSS feed 用户无需采取任何操作。指向旧条目的缓存链接可能会失效,但从现在起的新条目将具有正确的链接。

如果你想为你的个人博客设置类似的环境,请查看https://github.com/kristoff-it/zine-devlog-examples

2024年9月24日

下载页面已添加 loongarch64

作者:Andrew Kelley

感谢YANG XudongAlex Rønne Petersen的贡献,以及升级到 LLVM 19,Zig 对 loongarch64 的支持已足够成熟,zig-bootstrap可以支持此目标。为表庆祝,我已将 loongarch64-linux 添加到下载页面

2024年9月16日

2024-09-16

作者:Andrew Kelley

我一直在将 stb_truetype.h 移植到 Zig。看看这个片段:

{
    float sum = 0;
    for (i=0; i < result->w; ++i) {
      float k;
      int m;
      sum += scanline2[i];
      k = scanline[i] + sum;
      k = (float) STBTT_fabs(k)*255 + 0.5f;
      m = (int) k;
      if (m > 255) m = 255;
      result->pixels[j*result->stride + i] = (unsigned char) m;
    }
}

⬇️

{
    var sum: f32 = 0;
    for (scanline, scanline2, result.pixels[j*result.stride..][0..result.w]) |s, s2, *p| {
        sum += s2;
        p.* = @min(@abs(s + sum)*255 + 0.5, 255);
    }
}

啊,好多了。

2024年9月11日

Zig tokenizer 已更新为使用带标签的 switch 语句

作者:Andrew Kelley

既然 Matthew 已合并了带标签的 switch continue 语法,是时候开始使用了。

Eric Petersen 首次贡献,更新了 Zig 的 tokenizer 以使用新语法,测得zig ast-check命令的运行时间性能提升了 13%:

perf benchmark screenshot

这行代码让我很开心:

state: switch (State.start) {