Rust 团队准备了一个新的版本 1.52.1,解决了 1.52.0 中引入的一个增量编译错误。我们建议所有 Rust 用户,包括当前使用 1.52.0 之前稳定版本的用户,都升级到 1.52.1 或禁用增量编译。有关如何操作的指南如下。
如果您已通过 rustup 安装了之前的 Rust 版本,那么获取 Rust 1.52.1 非常简单,只需执行以下命令:
$ rustup update stable
如果您还没有安装,可以从我们网站上的相应页面获取 rustup
。
概要
此版本解决了 1.52.0 上由于新添加的验证而导致的构建失败问题。此验证检测到的错误存在于所有 Rust 版本中1,并且可能在增量构建中触发错误编译,因此降级到之前的稳定版本并不能解决问题。
我们鼓励用户升级到 1.52.1,或者如果使用的是之前的版本,则在本地环境中禁用增量编译:请参阅你应该怎么做部分,了解如何操作的详细信息。
增量编译在发布版本中默认关闭,因此很少有生产构建会受到影响(仅限于选择启用的用户)。
增量编译中的错误可能导致错误编译,在最终的工件中生成不正确的代码,本质上会生成格式错误的二进制文件,这意味着理论上可能会出现任何行为。在实践中,我们目前只知道一个特定的已知错误编译,但由于增量编译导致的错误非常难以追踪:如果用户在二进制文件中看到意外结果,通常会在进行少量编辑后重新构建,而这通常会导致足够的重新编译来修复错误。
这篇文章将:
- 解释错误是什么样子,
- 解释检查的作用,从高层次上讲,
- 解释检查是如何在 Rust 1.52.0 版本中呈现的,
- 告诉您,如果您在项目中看到不稳定的指纹,您应该怎么做,
- 描述我们Rust 项目将如何解决这里讨论的问题的计划。
错误是什么样子?
错误消息看起来像这样,关键部分是“found unstable fingerprints”(发现不稳定的指纹)文本。
thread 'rustc' panicked at 'assertion failed: `(left == right)`
left: `Some(Fingerprint(4565771098143344972, 7869445775526300234))`,
right: `Some(Fingerprint(14934403843752251060, 623484215826468126))`: found unstable fingerprints for <massive text describing rustc internals elided>
error: internal compiler error: unexpected panic
note: the compiler unexpectedly panicked. this is a bug.
这是由内部一致性检查引起的错误,正如诊断中所述,它会产生“内部编译器错误”(或 ICE)。换句话说,它表示 Rust 编译器本身的内部错误。在这种情况下,ICE 揭示了增量编译中的一个错误,该错误早于 1.52.0 版本,并且如果未被捕获,可能导致错误编译。
什么是指纹?为什么要检查它们?
Rust 编译器支持“增量编译”,这在2016 年的博客文章中有所描述。启用增量编译后,编译器会将输入源分解为多个部分,并跟踪这些输入部分如何影响最终的构建产品。然后,当输入发生更改时,它会检测到这一点,并重用先前构建的工件,力求仅在构建需要响应输入源代码更改的部分上花费精力。
指纹是我们检测输入何时更改的架构的一部分。更具体地说,指纹(以及一些其他状态以建立上下文)是一个 128 位的值,旨在唯一标识编译器内部使用的值。一些编译器内部结果会存储在运行之间的磁盘上(“缓存”)。指纹用于验证新计算的结果与缓存的结果是否未发生更改。(有关此的更多详细信息,请参见 rustc 开发指南的相关章节。)
指纹稳定性检查是一种断言指纹内部一致性的安全措施。有时,编译器会被迫重新运行查询,并期望输出与先前增量编译会话的输出相同。新启用的验证会检查该值是否确实如预期的那样,而不是假设是这样。在某些情况下,由于编译器实现中的错误,情况并非如此。
历史
我们在 2017 年最初添加这些指纹检查作为开发 rustc 本身时使用的工具。它仅通过不稳定的 -Z
标志提供,仅适用于 nightly 和开发版本。
最近,在 3 月份,我们遇到了一个错误编译,这导致我们默认启用 verify-ich
。Rust 编译器团队认为,捕获指纹问题并中止编译比允许潜在的错误编译(以及随后的错误行为)潜入 Rust 程序员的二进制文件中更好。
当我们首次默认启用指纹检查时,nightly(和 beta)工具链的用户提交了大量的 issue,并且在确定修复程序方面取得了稳步进展,其中许多修复程序已经落实。
在过去的一周中,我们开始制定计划以改善用户体验,以便该检查发出的诊断程序能够更好地告诉程序员应该如何响应。不幸的是,这是在假设新验证将在 1.53 中发布,而不是 1.52 的情况下完成的。
事实证明,verify-ich
已在 1.52.0 版本中启用,该版本是最近发布的。
今天的最新版本 1.52.1 通过临时更改 Rust 编译器中的默认设置来禁用增量编译(除非用户明确选择启用),从而解决了新添加的验证造成的破坏。
这是如何发生的
本质上,对于某些 crate,某些编辑-编译循环序列会导致 rustc
命中“unstable fingerprints”(不稳定的指纹)ICE。我在本博客文章的开头展示了一个示例。
另一个最近的示例如下所示
thread 'rustc' panicked at 'found unstable fingerprints for predicates_of(<massive text describing rustc internals elided>)', /rustc/.../compiler/rustc_query_system/src/query/plumbing.rs:593:5
它们都源于将存储在磁盘上的增量编译缓存与当前 rustc
调用期间计算的值进行比较时的不一致,这意味着它们都源于使用增量编译。
您可以通过多种方式启用增量编译
- 您可能正在使用默认启用增量编译的
dev
或test
配置文件进行构建。 - 您可能已设置环境变量
CARGO_INCREMENTAL=1
- 您可能已在 Cargo 配置中启用了
build.incremental
设置 - 您可能已在 Cargo.toml 中为给定的配置文件启用了
incremental
设置
如果您的项目没有调整默认设置,那么在运行 cargo build --release
或其他 release
配置文件配置时,所有 Rust 版本上都会禁用增量编译1,并且这些问题不应影响您的发布版本。
Rust 程序员应该如何响应
内部编译器错误会要求您报告 bug,如果您可以这样做,我们仍然希望获得该信息。我们希望了解失败的案例。
但是,无论您是否提交 bug,都可以通过以下方式在您的端解决问题:
- 升级到 1.52.1(如果您尚未升级)(这将为您禁用增量编译),或者
- 删除您的增量编译缓存(例如,通过运行
cargo clean
),或者 - 通过在环境中设置
CARGO_INCREMENTAL=0
或在config.toml
中将build.incremental
设置为false
,强制禁用增量编译。
我们建议 1.52.0 的用户升级到 1.52.1,这将禁用增量编译。
我们不建议 1.52.0 的用户为了解决此问题而降级到较早版本的 Rust。如上所述,至少有一个由增量编译引起的无声错误编译,直到我们添加指纹检查才被发现。
如果用户愿意处理增量验证 ICE,并希望重新选择回到 1.52.0 的行为,则可以在其环境中将 RUSTC_FORCE_INCREMENTAL
设置为 1
。然后,Rust 编译器将遵守 Cargo 传递的 -Cincremental
选项,并且一切将像以前一样工作,但会添加验证。请注意,如果尚未单独启用增量编译(无论是通过 Cargo 还是其他方式),则此标志不会启用增量编译。
如果您当前使用的是 1.52.0 之前的工具链,并希望继续使用它,我们建议您禁用增量编译,以避免遇到无声的错误编译。
自从增量编译落地以来,在所有 Rust 构建中,它都大大缩短了许多用户的编译时间,并且随着时间的推移,它只会变得更好。我们承认这里提出的解决方法和建议令人痛苦,并将努力确保这种情况尽可能地只是暂时的。
Rust 项目将如何解决此问题
短期计划
我们今天发布了 1.52.1,其中:
- 在 Rust 编译器中禁用增量编译(除非新的环境变量
RUSTC_FORCE_INCREMENTAL=1
要求启用)。 - 如果启用了增量编译,则会改进新验证的诊断输出,指示如何通过清除增量状态或禁用增量编译来解决 bug。
这旨在作为一种缓解措施,帮助大多数 Rust 用户拥有升级到安全的 Rust 编译器的途径,该编译器没有错误编译代码的风险,同时也为愿意处理错误的用户提供了选择。
我们希望继续积极投入精力来修复这些错误,并且根据我们对修复程序的信心,可能会发布 1.52.2 版本,将这些修复程序向后移植到稳定通道。希望帮助我们测试的用户可以使用 nightly 通道,并向 rust-lang/rust 报告他们看到的任何 ICE。
我们目前也没有计划在 beta 频道禁用增量编译,但这个决定尚未最终确定。今天 1.53 beta 版本上提供了一些修复,因此希望继续使用增量编译的用户可能需要切换到该版本。当然,Nightly 版本将始终包含最新的修复。
长期计划
长期计划是修复这些错误!增量编译是 Rust 编译器为其所有程序员提供快速编辑-编译-运行周期的唯一现实方法,因此我们需要解决通过 verify-ich
识别出的所有问题。(截至本文撰写时,有 32 个此类问题,但其中许多是重复的。)
我们正在积极投入这项工作,并且已经发现并修复了一些错误。根据修复的状态,未来的稳定版本(1.53 及更高版本)可能会重新启用增量编译。
Rust 团队还将制定计划,以确保我们在未来拥有更好的错误跟踪系统,既可以防止再次出现这种情况,也可以通过更准确地跟踪跨渠道传播的错误来进一步提高我们版本的稳定性。