发布 Rust 1.52.1

2021 年 5 月 10 日 · Felix Klock、Mark Rousskov 代表编译器团队

Rust 团队发布了新版本 1.52.1,该版本旨在解决 1.52.0 中被转化为编译器错误的增量编译(incremental compilation)bug。我们建议所有 Rust 用户,包括目前使用早于 1.52.0 的稳定版本的用户,升级到 1.52.1 或禁用增量编译。有关操作指导,请参阅下文。

如果您之前通过 rustup 安装了 Rust,获取 Rust 1.52.1 非常简单,只需执行以下操作:

$ rustup update stable

如果您还没有安装,可以从我们的网站上获取 rustup

概述

此版本旨在解决 1.52.0 版本中由于新增的验证机制导致的构建中断问题。此验证检测到的 bug 存在于所有 Rust 版本中1,并且可能在增量构建中引发错误编译(miscompilations),因此降级到之前的稳定版本并不能解决问题。

建议用户升级到 1.52.1,或者如果正在使用早期版本,则在其本地环境中禁用增量编译:请参阅您应该如何做部分了解详细操作方法。

对于 release 构建,增量编译默认是关闭的,因此很少有生产构建会受到影响(仅限于选择启用的用户)。

增量编译中的 bug 可能导致的错误编译会在最终的 artifact 中生成错误的代码,本质上是产生畸形的二进制文件,这意味着理论上任何行为都有可能发生。实际上,我们目前只知道一个特定的已知错误编译实例,但由于增量编译导致的 bug 素来难以追踪:如果用户看到其二进制文件产生意外结果,通常只是在轻微编辑后重新构建,这通常会引起足够的重新编译来修复 bug。

本文将:

  1. 解释错误是什么样子
  2. 概述性地解释此检查的作用
  3. 解释此检查在 Rust 1.52.0 版本中是如何表现的
  4. 告诉您如果在项目中看到不稳定指纹(unstable fingerprint),您应该如何做
  5. 描述 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 编译器内部的一个 bug。在这种情况下,ICE 揭示了一个早于 1.52.0 版本的增量编译 bug,如果未能捕获,可能导致错误编译。

什么是指纹?为什么我们要检查它们?

Rust 编译器支持“增量编译”,这已在一篇 2016 年的博文中有所描述。启用增量编译后,编译器会将输入的源代码分解成多个部分,并跟踪这些输入部分如何影响最终的构建产物。然后,当输入发生变化时,它会检测到这一点并重用先前构建的 artifact,力求仅在需要响应输入源代码变化的部分上花费精力。

指纹是我们检测输入何时变化架构的一部分。更具体地说,指纹(以及一些用于建立上下文的其他状态)是一个 128 位的值,旨在唯一标识编译器内部使用的值。一些编译器内部的结果会在运行之间存储在磁盘上(“缓存”)。指纹用于验证新计算的结果是否与缓存的结果没有变化。(关于此的更多细节可在rustc 开发指南的相关章节中找到。)

指纹稳定性检查是一种确保指纹内部一致性的安全措施。有时编译器被迫重新运行一个查询,并期望输出与先前的增量编译会话中的相同。新启用的验证机制会检查该值是否确实如预期,而不是想当然地认为如此。在某些情况下,由于编译器实现中的 bug,实际情况并非如此。

历史

我们在 2017 年最初添加了这些指纹检查,作为开发 rustc 本身时使用的工具。它仅通过不稳定的 -Z 标志提供,只适用于 nightly 和开发构建。

最近,在三月份,我们遇到了一个错误编译问题,这促使我们默认开启了 verify-ich。Rust 编译器团队认为,捕获指纹问题并中止编译,要比允许潜在的错误编译(以及随之而来的错误行为)潜入 Rust 程序员的二进制文件要好。

当我们首次默认开启指纹检查时,nightly(和 beta)工具链的用户提交了大量问题,并且在确定修复方案方面取得了稳步进展,其中许多已经合并。

在过去一周,我们已经开始制定计划改进用户体验,以便检查发出的诊断信息能更好地告诉程序员如何应对。不幸的是,这是在假设新的验证将在 1.53 版本中发布而不是 1.52 版本中进行的。

结果是,verify-ich 在 1.52.0 版本中默认开启了,该版本最近刚刚发布

今天发布的新版本 1.52.1,通过临时修改 Rust 编译器的默认设置来禁用增量编译(除非用户明确选择启用),从而解决了新增验证机制导致的构建中断问题。

这是如何显现的

本质上,对于某些 crate,特定的编辑-编译循环序列会导致 rustc 触发“不稳定指纹”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 调用期间计算的值进行比较时出现的不一致性,这意味着它们都源于使用增量编译。

您可能有几种方式启用了增量编译:

  1. 您可能正在使用默认启用增量编译的 devtest 配置文件(profile)进行构建。
  2. 您可能设置了环境变量 CARGO_INCREMENTAL=1
  3. 您可能在 Cargo 配置中启用了 build.incremental 设置
  4. 您可能在给定配置文件的 Cargo.toml 中启用了 incremental 设置

如果您的项目没有调整默认设置,那么在运行 cargo build --release 或在 release 配置文件配置下,所有 Rust 版本中增量编译都是禁用的1,这些问题不应影响您的发布构建。

Rust 程序员应该如何做

内部编译器错误要求您报告 bug,如果您能做到,我们仍然需要这些信息。我们希望了解那些失败的情况。

但无论您是否提交 bug 报告,您都可以在您的环境中通过以下方式解决此问题:

  1. 升级到 1.52.1(如果您尚未升级),这将为您禁用增量编译,或者
  2. 删除您的增量编译缓存(例如,通过运行 cargo clean),或者
  3. 强制禁用增量编译,方法是在您的环境中设置 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 项目将如何解决这个问题

短期计划

我们今天发布了 1.52.1 版本,它:

  • 在 Rust 编译器中禁用了增量编译(除非通过新的环境变量 RUSTC_FORCE_INCREMENTAL=1 要求启用)。
  • 如果启用了增量编译,则改进了新验证的诊断输出,指示如何通过清除增量状态或禁用增量编译来解决 bug。

这旨在成为一种缓解措施,帮助大多数 Rust 用户升级到安全的 Rust 编译器,该编译器没有错误编译其代码的风险,同时也为愿意处理错误的用户提供了一个选项。

我们期望继续积极投入修复这些 bug,并根据我们对修复的信心,可能会发布一个 1.52.2 的点版本,将这些修复反向移植到 stable 通道。希望帮助我们测试的用户可以使用 nightly 通道,并将他们遇到的任何 ICE 报告到 rust-lang/rust。

我们目前也计划不在 beta 通道上禁用增量编译,但这个决定尚未最终确定。1.53 beta 版本目前已有许多修复,因此希望继续使用增量编译的用户可能需要切换到该版本。当然,nightly 版本总是包含最新的修复。

长期计划

长期计划是修复这些 bug!增量编译是 Rust 编译器能够为所有程序员提供快速编辑-编译-运行周期的唯一现实方式,因此我们需要解决目前通过 verify-ich 识别出的所有问题。(截至撰写本文时,共有 32 个此类问题,尽管其中许多是重复的。)

我们正在积极投入解决此问题,并且已经确定并修复了许多 bug。根据修复的进展情况,未来的 stable 版本(1.53 及更高版本)可能会重新启用增量编译。

Rust 团队还将制定计划,确保未来建立更好的 bug 跟踪系统,既是为了防止类似情况再次发生,也是为了通过更准确地跟踪 bug 在不同通道中的传播来进一步提高我们版本的稳定性。