又来了?感觉好像几周前我们才做过一次...6 周前 😉。总之,本次冲刺的大部分工作是前两次的延续:努力使 Chalk 功能完善,并最终在 rustc 中使用它来进行特性解析。
对于以前没见过此类总结的人,如果你对特性工作组感到好奇,可以在此处找到一份总结。
鸣谢
一如既往,非常感谢所有参与本次冲刺的人
- Wilco Kusee
- Florian Diebold
- Jack Huey
- Charles Lew
- Niko Matsakis
- Nathan Whitaker
- Adam Bratschi-Kaye
- super-tuple
- David Ross
- Zahari Dichev
- Mikhail Babenko
- Mark Drobnak
- Aaron Hill
- Pavan Kumar Sunkara
- Nathan Corbyn
- Matthew Jasper
- Bastian Kauschke
Chalk crate 清理和每周发布
自诞生以来,Chalk 在 crate 结构方面经历了不少变化,这对于任何规模适中的项目来说都很正常。在本次冲刺中,我们花时间稍微清理了一下 crate 结构。最简单的做法可能是简要介绍一下我们最终得到的结构。更全面的概述可以在Chalk 手册中找到。
chalk-derive
- 定义 derive proc 宏chalk-ir
- 一个基本的“类型库”,将来可能在 rustc、Chalk 和 rust-analyzer 之间共享chalk-solve
- 定义来自chalk-ir
的类型的 Rust 语义chalk-engine
- 实现 SLG 解析器chalk-recursive
- 实现递归解析器chalk-parse
- 用于测试,将类似 Rust 的语法解析为chalk-ir
和chalk-solve
类型chalk-integration
- 用于测试,提供有用的测试类型chalk
- 用于测试,提供 REPL
在本次冲刺期间,我们还设立了 Chalk crates 的每周定期发布。虽然目前这些都是 0.*.0
的补丁版本,但这为未来的稳定版本建立了基础设施,并提供了可用于 rustc 和 rust-analyzer 的已发布 crates。将来,当 Chalk 开发更加稳定时,我们希望切换到手动发布。我们还计划设置 bors 来确保 master
分支始终构建并通过测试。
推进 rustc 对 GATs 的支持
Chalk 对 GATs 的支持已经有一段时间了;在 Chalk 中,GATs 是其他一切的自然延伸。然而,在 rustc 中实现 GATs 却有些困难,过去几年进展相对停滞,主要精力都放在了让 Chalk 准备就绪上。但最近,在 rustc 中已经完成了一些工作,以便让 GATs 在当前的 rustc 特性系统下工作。
提取一个表示类型的共享库
作为一个长期目标,我们希望有一天在 Chalk 和 rustc 之间拥有一个共享的类型库。此外,这个类型库也可以用于其他项目,例如 rust-analyzer。在 Chalk 方面,添加了更多类型——例如闭包 (closures) 和枚举 (enums)——以及更多特性——例如 Fn
系列和 Unsize
。此外,还做了一些反方向的工作:让 rustc 更接近 Chalk,例如Predicate
的内部化 (interning),以及引入 ForAll
谓词 (predicates)。
.chalk
文件用于调试
编写 作为 Chalk 测试的一部分,我们可以编写类似 Rust 的“程序”,这些程序会被解析成 Chalk 类型。重要的是,这些程序比它们被降低成的类型简洁得多。为了更好地支持调试,我们实现了一个反向的系统:能够从底层类型生成类似 Rust 的程序。这对于调试 rustc 尝试编译的特定代码片段中的 bug 非常有用。此外,它还可以用于生成针对性能问题的程序。
impl Trait
的支持
改进对 在上次冲刺中,我们落地了初步的 impl Trait
支持,用于处理诸如 type Foo<T> = impl Bar
这样的简单情况。在本次冲刺中,我们开始着手添加对更复杂情况的支持,例如 type Foo<T>: Debug = impl Bar where T: Debug
。此外,还进行了一些设计工作来支持检查这些结构的格式是否正确 (well-formed)。
扩展 Chalk 以支持 Rust 语义
这个目标与“提取一个表示类型的共享库”有些重叠,但它更侧重于表达类型的语义,而不是表示类型本身。例如,考虑下面的程序
trait Foo: Sized {
fn foo(self) {}
}
impl Foo for u32 {}
impl Foo for String {}
fn main() {
let x = 0;
x.foo();
}
在本次冲刺之前,Chalk 无法正确处理这种情况;它不会知道你可以对 0
调用 foo
。事实上,要正确编译这个程序,编译器必须知道 0
不可能是 String
。考虑一下,如果你将 String
的实现改为 u64
会发生什么:rustc 不会知道你是希望 0
是 u32
还是 u64
。这基本上就是本次冲刺之前 Chalk 如何看待这个程序的。然而,现在 Chalk 能够正确处理这种情况了。
在 rustc 集成中处理生命周期约束
因此,作为特性解析的一部分,Chalk 和 rustc 有时可能会发现一个生命周期需要比另一个生命周期更长,或者一个类型必须比某个生命周期更长。例如,
trait Foo<'a, 'b, T> where 'a: 'b, T: 'a {}
Chalk 本身不解决这些生命周期约束,而是将它们传回给 rustc。在本次冲刺期间,我们添加了在 Chalk 中表达这些 where
子句的能力。
其他工作
在本次冲刺期间还完成了一些不适合独立列出的零散工作。Chalk 在建议方面变得更智能了一些。我们引入了 tracing
用于日志记录。我们为递归解析器做了一些设计工作。当然,还有相当多的内部重构和清理。而且,rustc 集成也一直与时俱进,并针对 Chalk 的新特性进行了更新。
暑假
今年到目前为止一直很忙碌!自从第一次冲刺在二月初开始以来,我们取得了巨大的进展。然而,与前几次冲刺不同的是,我们不会立即开始下一次冲刺。相反,我们将休息几个月放假,并于九月份重新开始。在此之前,我们不会在 Zulip 上举行每周会议,也没有任何明确的目标。这部分是因为一些成员可能正在度假。但同时,代码倦怠 (burnout) 是真实存在的,时不时的休息会很有帮助。在此期间,还有一些项目需要完成/清理。
如果你有兴趣帮忙,请不要气馁!Zulip 应该仍然相当活跃,所以随时欢迎过来看看!