├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── book.toml └── src ├── README.md ├── SUMMARY.md ├── about.md ├── appendix ├── background.md ├── bibliography.md ├── code-index.md ├── compiler-lecture.md ├── glossary.md └── humorust.md ├── ast-validation.md ├── backend ├── backend-agnostic.md ├── codegen.md ├── debugging.md ├── implicit-caller-location.md ├── lowering-mir.md ├── monomorph.md └── updating-llvm.md ├── borrow_check.md ├── borrow_check ├── moves_and_initialization.md ├── moves_and_initialization │ └── move_paths.md ├── region_inference.md ├── region_inference │ ├── closure_constraints.md │ ├── constraint_propagation.md │ ├── error_reporting.md │ ├── lifetime_parameters.md │ ├── member_constraints.md │ └── placeholders_and_universes.md ├── two_phase_borrows.md └── type_check.md ├── bug-fix-procedure.md ├── building ├── bootstrapping.md ├── build-install-distribution-artifacts.md ├── compiler-documenting.md ├── ctags.md ├── how-to-build-and-run.md ├── new-target.md ├── prerequisites.md └── suggested.md ├── cli.md ├── closure.md ├── compiler-debugging.md ├── compiler-src.md ├── compiler-team.md ├── compiletest.md ├── const-eval.md ├── contributing.md ├── conventions.md ├── crates-io.md ├── debugging-support-in-rustc.md ├── diagnostics.md ├── diagnostics ├── diagnostic-codes.md ├── lintstore.md └── sessiondiagnostic.md ├── early-late-bound.md ├── feature-gate-ck.md ├── feature-gates.md ├── generic_arguments.md ├── generics.md ├── getting_start.md ├── git.md ├── hir-debugging.md ├── hir.md ├── identifiers.md ├── img └── dataflow-graphviz-example.png ├── implementing_new_features.md ├── incrcomp-debugging.md ├── licenses.md ├── llvm-coverage-instrumentation.md ├── lowering.md ├── macro-expansion.md ├── memory.md ├── method-lookup.md ├── mir ├── construction.md ├── dataflow.md ├── debugging.md ├── index.md ├── optimizations.md ├── passes.md └── visitor.md ├── miri.md ├── name-resolution.md ├── notification-groups ├── about.md ├── arm.md ├── cleanup-crew.md ├── llvm.md ├── risc-v.md └── windows.md ├── opaque-types-type-alias-impl-trait.md ├── overview.md ├── panic-implementation.md ├── parallel-rustc.md ├── param_env.md ├── part-2-intro.md ├── part-3-intro.md ├── part-4-intro.md ├── part-5-intro.md ├── pat-exhaustive-checking.md ├── profile-guided-optimization.md ├── profiling.md ├── profiling └── with_perf.md ├── queries ├── incremental-compilation-in-detail.md ├── incremental-compilation.md ├── profiling.md └── query-evaluation-model-in-detail.md ├── query.md ├── rustbot.md ├── rustc-driver-getting-diagnostics.md ├── rustc-driver-interacting-with-the-ast.md ├── rustc-driver.md ├── rustdoc-internals.md ├── rustdoc.md ├── salsa.md ├── sanitizers.md ├── serialization.md ├── stability.md ├── stabilization_guide.md ├── syntax-intro.md ├── test-implementation.md ├── tests ├── adding.md ├── intro.md └── running.md ├── the-parser.md ├── traits ├── caching.md ├── canonical-queries.md ├── chalk.md ├── goals-and-clauses.md ├── hrtb.md ├── lowering-to-logic.md ├── resolution.md └── specialization.md ├── ty-fold.md ├── ty.md ├── type-checking.md ├── type-inference.md ├── variance.md └── walkthrough.md /.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | .DS_Store 3 | .idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 RustcRustc 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: deploy 2 | 3 | init: 4 | git worktree remove -f /tmp/rustcbook 5 | git worktree add -f /tmp/rustcbook gh-pages 6 | 7 | deploy: init 8 | @echo "====> deploying to github" 9 | mdbook build 10 | rm -rf /tmp/rustcbook/* 11 | cp -rp book/* /tmp/rustcbook/ 12 | cd /tmp/rustcbook && \ 13 | git add -A && \ 14 | git commit -m "deployed on $(shell date) by ${USER}" && \ 15 | git push origin gh-pages -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rustc Dev Guide 中文翻译 2 | 3 | [Rust编译器开发指南(Rustc Dev Guide)](https://github.com/rust-lang/rustc-dev-guide) 的中文翻译已经启动。因为原项目还在变动期,为了翻译方便,所以此翻译项目组织结构就不和原项目保持一致了。 4 | 5 | - [官方原文在线阅读](https://rustc-dev-guide.rust-lang.org/) 6 | - [中文版在线阅读](https://rustcrustc.github.io/rustc-dev-guide-zh/) 7 | - [中文版翻译仓库地址](https://github.com/RustcRustc/rustc-dev-guide-zh) 8 | 9 | #### 志愿者招募要求: 10 | 11 | - 热爱 Rust,对 Rust 已经有一定了解 12 | - 想深入了解 Rust 编译器 13 | - 想为 Rust 编译器做贡献 14 | - 业余时间充足 15 | 16 | #### 如何参与 17 | 18 | 1. 认领感兴趣到章节 19 | 2. 找到对应到 markdown 文件 20 | 3. 直接发 PR 21 | 4. 或者帮忙审校别人的 PR 22 | 23 | ### Q & A: 24 | 25 | 1. 如何避免每个人翻译上的冲突呢,需要提前pr说翻译哪一章节吗? 26 | 27 | 其实没必要怕冲突,对于参与翻译的来说,翻译本身也是一次学习过程,是有收获的。了解编译器工作原理对理解 Rust 概念也有帮助的。如果同一篇有多个翻译,那我这边选翻译更好的就可以了。 28 | 29 | 这个项目倡导参与者自组织,但为了更加方便大家协作,还是来设置一个规则避免大家冲突。为了大家认领方便,特别创建了认领打卡的 issues,都去这里打一下卡:[【翻译认领】避免翻译冲突,来此打卡](https://github.com/RustcRustc/rustc-dev-guide-zh/issues/1)。 30 | 31 | 如果你想发一个自己专属的「认领issue」也没问题,可以给该issue打上「已认领」标签。开一个独立的issue好处是可以有一个专属的地方讨论你翻译章节内容里的各种问题。 32 | 33 | 2. 为什么要翻译 《Rust 编译器开发指南》 ? 34 | 35 | 年初的时候,我立下一个五年的 Flag : 五年内要为 Rust 语言发 1000 个 PR。 36 | 37 | 然后社区里的朋友就帮我做了一个计算:五年 1000 个,那么每年 200 个,那么一天就得 0.5 个。也有朋友说,Rust 的 PR 每次 Review 周期都很长,就算你能一年提 200 个 PR,官方也不可能给你合并那么多。 38 | 39 | 这样的计算,确实很有道理。这个目标,确实很难完成。但其实这个 Flag 我并没有打算个人完成,而是想推动社区对 Rust 感兴趣对朋友一起完成。如果五年内,我能推动 1000 个人参与,那么每个人只提交一个 PR,那么这个 1000 个 PR 的 Flag 就轻松完成了。 40 | 41 | 所以,翻译 《Rust 编译器开发指南》就成了我完成这个 Flag 的第一步。希望大家踊跃参与。 -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["blackanger"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "RustcDevGuideZh" 7 | 8 | [build] 9 | create-missing = true 10 | 11 | [preprocessor.toc] 12 | command = "mdbook-toc" 13 | renderer = ["html"] 14 | 15 | [output.html] 16 | git-repository-url = "https://github.com/RustcRustc/rustc-dev-guide-zh" 17 | 18 | [output.html.fold] 19 | enable = true 20 | level = 0 -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Rustc Dev Guide 中文翻译 2 | 3 | 4 | [Rust编译器开发指南(Rustc Dev Guide)](https://github.com/rust-lang/rustc-dev-guide) 的中文翻译已经启动。因为原项目还在变动期,为了翻译方便,所以此翻译项目组织结构就不和原项目保持一致了。 5 | 6 | - [官方原文在线阅读](https://rustc-dev-guide.rust-lang.org/) 7 | - [中文版在线阅读](https://rustcrustc.github.io/rustc-dev-guide-zh/) 8 | - [中文版翻译仓库地址](https://github.com/RustcRustc/rustc-dev-guide-zh) 9 | 10 | #### 志愿者招募要求: 11 | 12 | - 热爱 Rust,对 Rust 已经有一定了解 13 | - 想深入了解 Rust 编译器 14 | - 想为 Rust 编译器做贡献 15 | - 业余时间充足 16 | 17 | #### 如何参与 18 | 19 | 1. 认领感兴趣到章节 20 | 2. 找到对应到 markdown 文件 21 | 3. 直接发 PR 22 | 4. 或者帮忙审校别人的 PR 23 | 24 | ### Q & A: 25 | 26 | 1. 如何避免每个人翻译上的冲突呢,需要提前pr说翻译哪一章节吗? 27 | 28 | 其实没必要怕冲突,对于参与翻译的来说,翻译本身也是一次学习过程,是有收获的。了解编译器工作原理对理解 Rust 概念也有帮助的。如果同一篇有多个翻译,那我这边选翻译更好的就可以了。 29 | 30 | 这个项目倡导参与者自组织,但为了更加方便大家协作,还是来设置一个规则避免大家冲突。为了大家认领方便,特别创建了认领打卡的 issues,都去这里打一下卡:[【翻译认领】避免翻译冲突,来此打卡](https://github.com/RustcRustc/rustc-dev-guide-zh/issues/1)。 31 | 32 | 如果你想发一个自己专属的「认领issue」也没问题,可以给该issue打上「已认领」标签。开一个独立的issue好处是可以有一个专属的地方讨论你翻译章节内容里的各种问题。 33 | 34 | 2. 为什么要翻译 《Rust 编译器开发指南》 ? 35 | 36 | 年初的时候,我立下一个五年的 Flag : 五年内要为 Rust 语言发 1000 个 PR。 37 | 38 | 然后社区里的朋友就帮我做了一个计算:五年 1000 个,那么每年 200 个,那么一天就得 0.5 个。也有朋友说,Rust 的 PR 每次 Review 周期都很长,就算你能一年提 200 个 PR,官方也不可能给你合并那么多。 39 | 40 | 这样的计算,确实很有道理。这个目标,确实很难完成。但其实这个 Flag 我并没有打算个人完成,而是想推动社区对 Rust 感兴趣对朋友一起完成。如果五年内,我能推动 1000 个人参与,那么每个人只提交一个 PR,那么这个 1000 个 PR 的 Flag 就轻松完成了。 41 | 42 | 所以,翻译 《Rust 编译器开发指南》就成了我完成这个 Flag 的第一步。希望大家踊跃参与。 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [介绍](./README.md) 4 | - [关于](./about.md) 5 | - [Getting Start](./getting_start.md) 6 | 7 | --- 8 | 9 | # 构建与调试`rustc` (Building and debugging `rustc`) 10 | 11 | - [如何构建和运行 Rust 编译器](./building/how-to-build-and-run.md) 12 | - [Prerequisites](./building/prerequisites.md) 13 | - [Suggested Workflows](./building/suggested.md) 14 | - [Distribution artifacts](./building/build-install-distribution-artifacts.md) 15 | - [Documenting Compiler](./building/compiler-documenting.md) 16 | - [Rustdoc](./rustdoc.md) 17 | - [ctags](./building/ctags.md) 18 | - [Adding a new target](./building/new-target.md) 19 | - [The compiler testing framework](./tests/intro.md) 20 | - [Running tests](./tests/running.md) 21 | - [Adding new tests](./tests/adding.md) 22 | - [Using `compiletest` commands to control test execution](./compiletest.md) 23 | - [Debugging the Compiler](./compiler-debugging.md) 24 | - [Profiling the compiler](./profiling.md) 25 | - [with the linux perf tool](./profiling/with_perf.md) 26 | - [crates.io Dependencies](./crates-io.md) 27 | 28 | 29 | 30 | # 为 Rust 做贡献 (Contributing to Rust) 31 | 32 | - [介绍](./contributing.md) 33 | - [About the compiler team](./compiler-team.md) 34 | - [Using Git](./git.md) 35 | - [Mastering @rustbot](./rustbot.md) 36 | - [Walkthrough: a typical contribution](./walkthrough.md) 37 | - [Bug Fix Procedure](./bug-fix-procedure.md) 38 | - [Implementing new features](./implementing_new_features.md) 39 | - [Stability attributes](./stability.md) 40 | - [Stabilizing Features](./stabilization_guide.md) 41 | - [Feature Gates](./feature-gates.md) 42 | - [Coding conventions](./conventions.md) 43 | - [Notification groups](notification-groups/about.md) 44 | - [ARM](notification-groups/arm.md) 45 | - [Cleanup Crew](notification-groups/cleanup-crew.md) 46 | - [LLVM](notification-groups/llvm.md) 47 | - [RISC-V](notification-groups/risc-v.md) 48 | - [Windows](notification-groups/windows.md) 49 | - [Licenses](./licenses.md) 50 | 51 | # 编译器架构概要 (High-level Compiler Architecture) 52 | 53 | - [序言](./part-2-intro.md) 54 | - [Overview of the Compiler](./overview.md) 55 | - [The compiler source code](./compiler-src.md) 56 | - [Bootstrapping](./building/bootstrapping.md) 57 | - [Queries: demand-driven compilation](./query.md) 58 | - [The Query Evaluation Model in Detail](./queries/query-evaluation-model-in-detail.md) 59 | - [Incremental compilation](./queries/incremental-compilation.md) 60 | - [Incremental compilation In Detail](./queries/incremental-compilation-in-detail.md) 61 | - [Debugging and Testing](./incrcomp-debugging.md) 62 | - [Profiling Queries](./queries/profiling.md) 63 | - [Salsa](./salsa.md) 64 | - [Memory Management in Rustc](./memory.md) 65 | - [Serialization in Rustc](./serialization.md) 66 | - [Parallel Compilation](./parallel-rustc.md) 67 | - [Rustdoc](./rustdoc-internals.md) 68 | 69 | # 源码表示 (Source Code Representation) 70 | 71 | - [序言](./part-3-intro.md) 72 | - [命令行参数](./cli.md) 73 | - [Rustc Driver 和 Rustc Interface](./rustc-driver.md) 74 | - [示例:通过 `rustc_interface` 进行类型检查](./rustc-driver-interacting-with-the-ast.md) 75 | - [示例:通过 `rustc_interface` 获取诊断信息](./rustc-driver-getting-diagnostics.md) 76 | - [Syntax and the AST](./syntax-intro.md) 77 | - [Lexing and Parsing](./the-parser.md) 78 | - [Macro expansion](./macro-expansion.md) 79 | - [Name resolution](./name-resolution.md) 80 | - [`#[test]` Implementation](./test-implementation.md) 81 | - [Panic Implementation](./panic-implementation.md) 82 | - [AST Validation](./ast-validation.md) 83 | - [Feature Gate Checking](./feature-gate-ck.md) 84 | - [The HIR (High-level IR)](./hir.md) 85 | - [Lowering AST to HIR](./lowering.md) 86 | - [Debugging](./hir-debugging.md) 87 | - [The MIR (Mid-level IR)](./mir/index.md) 88 | - [THIR and MIR construction](./mir/construction.md) 89 | - [MIR visitor and traversal](./mir/visitor.md) 90 | - [MIR passes: getting the MIR for a function](./mir/passes.md) 91 | - [Identifiers in the Compiler](./identifiers.md) 92 | - [Closure expansion](./closure.md) 93 | 94 | # 静态分析 (Analysis) 95 | 96 | - [序言](./part-4-intro.md) 97 | - [The `ty` module: representing types](./ty.md) 98 | - [Generics and substitutions](./generics.md) 99 | - [`TypeFolder` and `TypeFoldable`](./ty-fold.md) 100 | - [Generic arguments](./generic_arguments.md) 101 | - [Type inference](./type-inference.md) 102 | - [Trait solving](./traits/resolution.md) 103 | - [Early and Late Bound Parameters](./early-late-bound.md) 104 | - [Higher-ranked trait bounds](./traits/hrtb.md) 105 | - [Caching subtleties](./traits/caching.md) 106 | - [Specialization](./traits/specialization.md) 107 | - [Chalk-based trait solving](./traits/chalk.md) 108 | - [Lowering to logic](./traits/lowering-to-logic.md) 109 | - [Goals and clauses](./traits/goals-and-clauses.md) 110 | - [Canonical queries](./traits/canonical-queries.md) 111 | - [Type checking](./type-checking.md) 112 | - [Method Lookup](./method-lookup.md) 113 | - [Variance](./variance.md) 114 | - [Opaque Types](./opaque-types-type-alias-impl-trait.md) 115 | - [Pattern and Exhaustiveness Checking](./pat-exhaustive-checking.md) 116 | - [MIR dataflow](./mir/dataflow.md) 117 | - [The borrow checker](./borrow_check.md) 118 | - [Tracking moves and initialization](./borrow_check/moves_and_initialization.md) 119 | - [Move paths](./borrow_check/moves_and_initialization/move_paths.md) 120 | - [MIR type checker](./borrow_check/type_check.md) 121 | - [Region inference](./borrow_check/region_inference.md) 122 | - [Constraint propagation](./borrow_check/region_inference/constraint_propagation.md) 123 | - [Lifetime parameters](./borrow_check/region_inference/lifetime_parameters.md) 124 | - [Member constraints](./borrow_check/region_inference/member_constraints.md) 125 | - [Placeholders and universes][pau] 126 | - [Closure constraints](./borrow_check/region_inference/closure_constraints.md) 127 | - [Error reporting](./borrow_check/region_inference/error_reporting.md) 128 | - [Two-phase-borrows](./borrow_check/two_phase_borrows.md) 129 | - [Parameter Environments](./param_env.md) 130 | - [Errors and Lints](diagnostics.md) 131 | - [Creating Errors With SessionDiagnostic](./diagnostics/sessiondiagnostic.md) 132 | - [`LintStore`](./diagnostics/lintstore.md) 133 | - [Diagnostic Codes](./diagnostics/diagnostic-codes.md) 134 | 135 | # 从 MIR 到 二进制 (MIR to Binaries) 136 | 137 | - [序言](./part-5-intro.md) 138 | - [MIR 优化](./mir/optimizations.md) 139 | - [Debugging](./mir/debugging.md) 140 | - [Constant evaluation](./const-eval.md) 141 | - [miri const evaluator](./miri.md) 142 | - [Monomorphization](./backend/monomorph.md) 143 | - [Lowering MIR](./backend/lowering-mir.md) 144 | - [Code Generation](./backend/codegen.md) 145 | - [Updating LLVM](./backend/updating-llvm.md) 146 | - [Debugging LLVM](./backend/debugging.md) 147 | - [Backend Agnostic Codegen](./backend/backend-agnostic.md) 148 | - [Implicit Caller Location](./backend/implicit-caller-location.md) 149 | - [Profile-guided Optimization](./profile-guided-optimization.md) 150 | - [LLVM Source-Based Code Coverage](./llvm-coverage-instrumentation.md) 151 | - [Sanitizers Support](./sanitizers.md) 152 | - [Debugging Support in the Rust Compiler](./debugging-support-in-rustc.md) 153 | 154 | --- 155 | 156 | [附录 A: Background topics](./appendix/background.md) 157 | [附录 B: Glossary](./appendix/glossary.md) 158 | [附录 C: Code Index](./appendix/code-index.md) 159 | [附录 D: Compiler Lecture Series](./appendix/compiler-lecture.md) 160 | [附录 E: Bibliography](./appendix/bibliography.md) 161 | 162 | [附录 Z: HumorRust](./appendix/humorust.md) 163 | 164 | --- 165 | 166 | [pau]: ./borrow_check/region_inference/placeholders_and_universes.md 167 | -------------------------------------------------------------------------------- /src/about.md: -------------------------------------------------------------------------------- 1 | # 关于 2 | 3 | 本指南旨在记录rustc(Rust编译器)的工作方式,并帮助新的开发者参与到rustc的开发中来。 4 | 5 | 本指南分为六个部分: 6 | 7 | 1. [构建和调试 `rustc`][p1]: 包含有关构建,调试,性能分析等方面的有用信息,无论您以何种方式进行贡献。 8 | 2. [为 `rustc`做贡献][p1-5]: 包含有关贡献代码的步骤,稳定功能等方面有用的信息,无论您以何种方式进行贡献。 9 | 2. [编译器架构概要][p2]: 讨论编译器的高级架构和编译过程的各个阶段。 10 | 3. [源码表示][p3]: 描述了获取用户的代码,并将其转换为编译器可以使用的各种形式的过程。 11 | 4. [静态分析][p4]: 讨论编译器如何分析代码,从而能够检查代码的各种属性并告知编译过程的后续阶段(例如,类型检查)。 12 | 5. [从MIR到二进制][p5]: 如何连接生成的可执行机器码。 13 | 6. [附录][app]: 在本指南的结尾提供了一些有关的参考信息,如词汇表、推荐书目等。 14 | 15 | [p1]: ./getting-started.md 16 | [p1-5]: ./compiler-team.md 17 | [p2]: ./part-2-intro.md 18 | [p3]: ./part-3-intro.md 19 | [p4]: ./part-4-intro.md 20 | [p5]: ./part-5-intro.md 21 | [app]: ./appendix/background.md 22 | 23 | ### 持续更新 24 | 25 | 请记住,这 `rustc` 是一种真正的生产质量管理工具,由大量的贡献者不断进行研究贡献。因此,它有相当一部分代码库变更和技术上的欠缺。此外,本指南中讨论的许多想法都是尚未完全实现的理想化设计。所有这些使本指南在所有方面都保持最新,这非常困难! 26 | 27 | 该指南本身当然也是开源的,可以在[GitHub存储库]中找到这些源(译者注: 这里的Github存储库为此文档的英文原文链接)。 28 | 如果您在指南中发现任何错误,请提出相关问题,甚至更好的是,打开带有更正的PR! 29 | 30 | 如果您想要为本指南(译者注: 指英文版)作出帮助,请参阅本指南中。 31 | [有关编写文档的相应小节]. 32 | 33 | [有关编写文档的相应小节]: contributing.md#contributing-to-rustc-dev-guide 34 | 35 | > “‘All conditioned things are impermanent’ — when one sees this with wisdom, one turns away from 36 | > suffering.” _The Dhammapada, verse 277_ 37 | 38 | ## 其他查找信息的网站 39 | 40 | 以下站点可能对你有所帮助: 41 | 42 | - [rustc API docs] -- 编译器的rustdoc文档。 43 | - [Forge] -- 包含有关Rust的补充文档。 44 | - [compiler-team] -- rust编译器团队的主页,描述了开发的过程,活动工作组,团队日历等。 45 | 46 | [GitHub存储库]: https://github.com/rust-lang/rustc-dev-guide/ 47 | [rustc API docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ 48 | [Forge]: https://forge.rust-lang.org/ 49 | [compiler-team]: https://github.com/rust-lang/compiler-team/ 50 | -------------------------------------------------------------------------------- /src/appendix/background.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RustcRustc/rustc-dev-guide-zh/6f41aee51cb30fe94a6c8173342f7a8051e217e7/src/appendix/background.md -------------------------------------------------------------------------------- /src/appendix/bibliography.md: -------------------------------------------------------------------------------- 1 | # 附录 E: Bibliography 2 | -------------------------------------------------------------------------------- /src/appendix/code-index.md: -------------------------------------------------------------------------------- 1 | # 附录 C: Code Index 2 | -------------------------------------------------------------------------------- /src/appendix/compiler-lecture.md: -------------------------------------------------------------------------------- 1 | # 附录 D: Compiler Lecture Series 2 | -------------------------------------------------------------------------------- /src/appendix/glossary.md: -------------------------------------------------------------------------------- 1 | # 附录 B: Glossary 2 | 3 | # Glossary 4 | 5 | 6 | 术语 | 中文 | 意义 7 | ------------------------------------------------------|--------|-------- 8 | arena/arena allocation   | 竞技场分配   | arena 是一个大内存缓冲区,从中可以进行其他内存分配,这种分配方式称为竞技场分配。 9 | AST   | 抽象语法树 | 由`rustc_ast` crate 产生的抽象语法树。 10 | binder   | 绑定器 | 绑定器是声明变量和类型的地方。例如,`` 是`fn foo(..)`中泛型类型参数 `T`的绑定器,以及 \|`a`\|` ...` 是 参数`a`的绑定器。 11 | BodyId   | 主体ID | 一个标识符,指的是crate 中的一个特定主体(函数或常量的定义)。 12 | bound variable   | 绑定变量   | "绑定变量 "是在表达式/术语中声明的变量。例如,变量`a`被绑定在闭包表达式中\|`a`\|` a * 2`。 13 | codegen   | 代码生成   |由 MIR 转译为 LLVM IR。 14 | codegen unit   | 代码生成单元   | 当生成LLVM IR时,编译器将Rust代码分成若干个代码生成单元(有时缩写为CGU)。这些单元中的每一个都是由LLVM独立处理的,实现了并行化。它们也是增量编译的单位。 15 | completeness   | 完整性   | 类型理论中的一个技术术语,它意味着每个类型安全的程序也会进行类型检查。同时拥有健全性(soundness)和完整性(completeness)是非常困难的,而且通常健全性(soundness)更重要。 16 | control-flow graph   | 控制流图   | 程序的控制流表示。 17 | CTFE   | 编译时函数求值   | 编译时函数求值(Compile-Time Function Evaluation)的简称,是指编译器在编译时计算 "const fn "的能力。这是编译器常量计算系统的一部分。 18 | cx   | 上下文   | Rust 编译器内倾向于使用 "cx "作为上下文的缩写。另见 "tcx"、"infcx "等。 19 | ctxt   | 上下文(另一个缩写)   | 我们也使用 "ctxt "作为上下文的缩写,例如, [`TyCtxt`](#TyCtxt),以及 [cx](#cx) 或 [tcx](#tcx)。 20 | DAG   | 有向无环图   | 在编译过程中,一个有向无环图被用来跟踪查询之间的依赖关系 21 | data-flow analysis   | 数据流分析   | 静态分析,找出程序控制流中每一个点的属性。 22 | DeBruijn Index   | 德布鲁因索引   | 一种只用整数来描述一个变量被绑定的绑定器的技术。它的好处是,在变量重命名下,它是不变的。 23 | DefId   | 定义Id   | 一个识别定义的索引(见`rustc_middle/src/hir/def_id.rs`)。`DefPath`的唯一标识。 24 | discriminant   | 判别式   | 与枚举变体或生成器状态相关的基础值,以表明它是 "激活的(avtive)"(但不要与它的["变体索引"](#variant-idx)混淆)。在运行时,激活变体的判别值被编码在[tag](#tag)中。 25 | double pointer   | 双指针   | 一个带有额外元数据的指针。同指「胖指针」。 26 | drop glue   | drop胶水   | (内部)编译器生成的指令,处理调用数据类型的析构器(`Drop`)。 27 | DST   | DST   | Dynamically-Sized Type的缩写,这是一种编译器无法静态知道内存大小的类型(例如:`str'或`[u8]`)。这种类型没有实现`Sized`,不能在栈中分配。它们只能作为结构中的最后一个字段出现。它们只能在指针后面使用(例如:`&str`或`&[u8]`)。 28 | early-bound lifetime   | 早绑定生存期   | 一个在其定义处被替换的生存期区域(region)。绑定在一个项目的`Generics'中,并使用`Substs'进行替换。与**late-bound lifetime**形成对比。 29 | empty type   | 空类型   | 参考 "uninhabited type". 30 | fat pointer   |胖指针   | 一个两字(word)的值,携带着一些值的地址,以及一些使用该值所需的进一步信息。Rust包括两种 "胖指针":对切片(slice)的引用和特质(trait)对象。对切片的引用带有切片的起始地址和它的长度。特质对象携带一个值的地址和一个指向适合该值的特质实现的指针。"胖指针 "也被称为 "宽指针",和 "双指针"。 31 | free variable   | 自由变量   | 自由变量 是指没有被绑定在表达式或术语中的变量; 32 | generics   | 泛型   | 通用类型参数集。 33 | HIR   | 高级中间语言   | 高级中间语言,通过对AST进行降级(lowering)和去糖(desugaring)而创建。 34 | HirId   | HirId   | 通过结合“def-id”和 "intra-definition offset"来识别HIR中的一个特定节点。 35 | HIR map   |HIR map   | 通过`tcx.hir()`访问的HIR Map,可以让你快速浏览HIR并在各种形式的标识符之间进行转换。 36 | ICE   | ICE   | 内部编译器错误的简称,这是指编译器崩溃的情况。 37 | ICH   | ICH   | 增量编译哈希值的简称,它们被用作HIR和crate metadata等的指纹,以检查是否有变化。这在增量编译中是很有用的,可以查看crate的一部分是否发生了变化,应该重新编译。 38 | infcx   | 类型推导上下文   | 类型推导上下文(`InferCtxt`)。 39 | inference variable   | 推导变量   | 在进行类型或区域推理时,"推导变量 "是一种特殊的类型/区域,代表你试图推理的内容。想想代数中的X。例如,如果我们试图推断一个程序中某个变量的类型,我们就创建一个推导变量来代表这个未知的类型。 40 | intern   |intern   | intern是指存储某些经常使用的常量数据,如字符串,然后用一个标识符(如`符号')而不是数据本身来引用这些数据,以减少内存的使用和分配的次数。 41 | intrinsic   | 内部函数   | 内部函数是在编译器本身中实现的特殊功能,但向用户暴露(通常是不稳定的)。它们可以做神奇而危险的事情。 42 | IR   | IR   | Intermediate Representation的简称,是编译器中的一个通用术语。在编译过程中,代码被从原始源码(ASCII文本)转换为各种IR。在Rust中,这些主要是HIR、MIR和LLVM IR。每种IR都适合于某些计算集。例如,MIR非常适用于借用检查器,LLVM IR非常适用于codegen,因为LLVM接受它。 43 | IRLO   | IRLO   | `IRLO`或`irlo`有时被用作[internals.rust-lang.org](https://internals.rust-lang.org)的缩写。 44 | item   | 语法项   | 语言中的一种 "定义",如静态、常量、使用语句、模块、结构等。具体来说,这对应于 "item"类型。 45 | lang item   | 语言项   | 代表语言本身固有的概念的项目,如特殊的内置特质,如`同步`和`发送`;或代表操作的特质,如`添加`;或由编译器调用的函数。 46 | late-bound lifetime   | 晚绑定生存期   | 一个在其调用位置被替换的生存期区域。绑定在HRTB中,由编译器中的特定函数替代,如`liberate_late_bound_regions`。与**早绑定的生存期**形成对比。 47 | local crate   | 本地crate   | 目前正在编译的crate。这与 "上游crate"相反,后者指的是本地crate的依赖关系。 48 | [LTO]   | [LTO]   | 链接时优化(Link-Time Optimizations)的简称,这是LLVM提供的一套优化,在最终二进制文件被链接之前进行。这些优化包括删除最终程序中从未使用的函数,例如。_[ThinLTO]_是LTO的一个变种,旨在提高可扩展性和效率,但可能牺牲了一些优化。 49 | [LLVM]   | [LLVM]   | (实际上不是一个缩写 :P) 一个开源的编译器后端。它接受LLVM IR并输出本地二进制文件。然后,各种语言(例如Rust)可以实现一个编译器前端,输出LLVM IR,并使用LLVM编译到所有LLVM支持的平台。 50 | memoization   | memoization   | 储存(纯)计算结果(如纯函数调用)的过程,以避免在未来重复计算。这通常是执行速度和内存使用之间的权衡。 51 | MIR   | 中级中间语言   | 在类型检查后创建的中级中间语言,供borrowck和codegen使用。 52 | miri   | mir解释器   | MIR的一个解释器,用于常量计算。 53 | monomorphization   | 单态化   | 采取类型和函数的通用实现并将其与具体类型实例化的过程。例如,在代码中可能有`Vec`,但在最终的可执行文件中,将为程序中使用的每个具体类型有一个`Vec`代码的副本(例如,`Vec`的副本,`Vec`的副本,等等)。 54 | normalize   |归一化   | 转换为更标准的形式的一般术语,但在rustc的情况下,通常指的是关联类型归一化。 55 | newtype   | newtype   | 对其他类型的封装(例如,`struct Foo(T)`是`T`的一个 "新类型")。这在Rust中通常被用来为索引提供一个更强大的类型。 56 | niche   | 利基   | 一个类型的无效位模式*可用于*布局优化。有些类型不能有某些位模式。例如,"非零*"整数或引用"&T "不能用0比特串表示。这意味着编译器可以通过利用无效的 "利基值 "来进行布局优化。这方面的一个应用实例是[*Discriminant elision on `Option`-like enums*](https://rust-lang.github.io/unsafe-code-guidelines/layout/enums.html#discriminant-elision-on-option-like-enums),它允许使用一个类型的niche作为一个`enum`的["标签"](#tag),而不需要一个单独的字段。 57 | NLL   | NLL   | 这是非词法作用域生存期的简称,它是对Rust的借用系统的扩展,使其基于控制流图。 58 | node-id or NodeId   | node-id or NodeId   | 识别AST或HIR中特定节点的索引;逐渐被淘汰,被`HirId`取代。 59 | obligation   | obligation   | 必须由特质系统证明的东西。 60 | placeholder   | placeholder   | **注意:skolemization被placeholder废弃**一种处理围绕 "for-all "类型的子类型的方法(例如,`for<'a> fn(&'a u32)`),以及解决更高等级的trait边界(例如,`for<'a> T: Trait<'a>`)。 61 | point   | point   | 在NLL分析中用来指代MIR中的某个特定位置;通常用来指代控制流图中的一个节点。 62 | polymorphize   | 多态化   | 一种避免不必要的单态化的优化。 63 | projection   | 投影   | 一个 "相对路径 "的一般术语,例如,`x.f`是一个 "字段投影",而`T::Item`是一个"关联类型投影" 64 | promoted constants   | 常量提升   | 从函数中提取的常量,并提升到静态范围 65 | provider   | provider   | 执行查询的函数。 66 | quantified   | 量化   |在数学或逻辑学中,存在量词和普遍量词被用来提出诸如 "是否有任何类型的T是真的?"或 "这对所有类型的T都是真的吗?"这样的问题 67 | query   | 查询   | 编译过程中的一个子计算。查询结果可以缓存在当前会话中,也可以缓存到磁盘上,用于增量编译。 68 | recovery   | 恢复   | 恢复是指在解析过程中处理无效的语法(例如,缺少逗号),并继续解析AST。这可以避免向用户显示虚假的错误(例如,当结构定义包含错误时,显示 "缺少字段 "的错误)。 69 | region   | 区域   | 和生存期精彩使用的另一个术语。 70 | rib   | rib   | 名称解析器中的一个数据结构,用于跟踪名称的单一范围。 71 | scrutinee   | 审查对象   | 审查对象是在`match`表达式和类似模式匹配结构中被匹配的表达式。例如,在`match x { A => 1, B => 2 }`中,表达式`x`是被审查者。 72 | sess   | sess   | 编译器会话,它存储了整个编译过程中使用的全局数据 73 | side tables   | side tables   | 由于AST和HIR一旦创建就不可改变,我们经常以哈希表的形式携带关于它们的额外信息,并以特定节点的ID为索引。 74 | sigil   | 符号   | 就像一个关键词,但完全由非字母数字的标记组成。例如,`&`是引用的标志。 75 | soundness   | 健全性   | 类型理论中的一个技术术语。粗略的说,如果一个类型系统是健全的,那么一个进行类型检查的程序就是类型安全的。也就是说,人们永远不可能(在安全的Rust中)把一个值强加到一个错误类型的变量中。 76 | span   | span   | 用户的源代码中的一个位置,主要用于错误报告。这就像一个文件名/行号/列的立体元组:它们携带一个开始/结束点,也跟踪宏的扩展和编译器去糖。所有这些都被装在几个字节里(实际上,它是一个表的索引)。 77 | substs   | 替换   | 给定的通用类型或项目的替换(例如,`HashMap`中的`i32'、`u32')。 78 | sysroot   | sysroot   | 用于编译器在运行时加载的构建工件的目录。 79 | tag   | tag   | 枚举/生成器的 "标签 "编码激活变体/状态的判别式(discriminant)。 标签可以是 "直接的"(简单地将判别式存储在一个字段中)或使用"利基"。 80 | tcx   | tcx   | "类型化上下文"(`TyCtxt`),编译器的主要数据结构。 81 | `'tcx`   | `'tcx`   | `TyCtxt'所使用的分配区域的生存期。在编译过程中,大多数数据都会使用这个生存期,但HIR数据除外,它使用`'hir`生存期。 82 | token   | 词条   | 解析的最小单位。词条是在词法运算后产生的 83 | [TLS]   | [TLS]   | 线程本地存储。变量可以被定义为每个线程都有自己的副本(而不是所有线程都共享该变量)。这与LLVM有一些相互作用。并非所有平台都支持TLS。 84 | trait reference   | trait 引用   | 一个特质的名称,以及一组合适的输入类型/生存期。 85 | trans   | trans   | 是 "转译"的简称,是将MIR转译成LLVM IR的代码。已经重命名为codegen。 86 | `Ty`   | `Ty`   | 一个类型的内部表示。 87 | TyCtxt   | TyCtxt   | 在代码中经常被称为tcx的数据结构,它提供对会话数据和查询系统的访问。 88 | UFCS   | UFCS   | 通用函数调用语法(Universal Function Call Syntax)的简称,这是一种调用方法的明确语法。 89 | uninhabited type   | 孤类型   | 一个没有值的类型。这与ZST不同,ZST正好有一个值。一个孤类型的例子是`enum Foo {}`,它没有变体,所以,永远不能被创建。编译器可以将处理孤类型的代码视为死代码,因为没有这样的值可以操作。`!`(从未出现过的类型)是一个孤类型。孤类型也被称为 "空类型"。 90 | upvar   | upvar   | 一个闭合体从闭合体外部捕获的变量 91 | variance   | 型变   | 确定通用类型/寿命参数的变化如何影响子类型;例如,如果`T`是`U`的子类型,那么`Vec`是`Vec`的子类型,因为`Vec`在其通用参数中是协变的。 92 | variant index   |变体索引   | 在一个枚举中,通过给它们分配从0开始的索引来识别一个变体。这纯粹是内部的,不要与"判别式"相混淆,后者可以被用户覆盖(例如,`enum Bool { True = 42, False = 0 }`)。 93 | wide pointer   |宽指针   |一个带有额外元数据的指针。 94 | ZST   | ZST   | 零大小类型。这种类型,其值的大小为0字节。由于`2^0 = 1`,这种类型正好有一个值。例如,`()`(单位)是一个ZST。`struct Foo;`也是一个ZST。编译器可以围绕ZST做一些很好的优化。 95 | 96 | [LLVM]: https://llvm.org/ 97 | [LTO]: https://llvm.org/docs/LinkTimeOptimization.html 98 | [ThinLTO]: https://clang.llvm.org/docs/ThinLTO.html 99 | [TLS]: https://llvm.org/docs/LangRef.html#thread-local-storage-models -------------------------------------------------------------------------------- /src/appendix/humorust.md: -------------------------------------------------------------------------------- 1 | # 附录 Z: HumorRust 2 | -------------------------------------------------------------------------------- /src/ast-validation.md: -------------------------------------------------------------------------------- 1 | # AST Validation 2 | -------------------------------------------------------------------------------- /src/backend/backend-agnostic.md: -------------------------------------------------------------------------------- 1 | # Backend Agnostic Codegen 2 | -------------------------------------------------------------------------------- /src/backend/codegen.md: -------------------------------------------------------------------------------- 1 | # 代码生成 2 | 3 | 代码生成或"codegen"是编译器生成可执行二进制文件的一部分。通常,rustc 使用 LLVM 来生成代码; 它也支持 [Cranelift]。关键是 rustc 本身并不实现 codegen。但是值得注意的是,在 rust 源代码中,后端的许多部分在名称中都有 `codegen` (没有严格的界限)。 4 | 5 | [Cranelift]: https://github.com/bytecodealliance/wasmtime/tree/HEAD/cranelift 6 | 7 | > 注意: 如果您正在寻找关于如何调试代码生成错误的提示,请参阅[调试章节的这一部分][debugging]。 8 | 9 | [debugging]: ./debugging.md 10 | 11 | ## LLVM 是什么? 12 | 13 | [LLVM](https://llvm.org) 是“模块化和可重用的编译器和工具链技术的集合”。特别是,LLVM 项目包含一个可插拔的编译器后端(也称为"LLVM") ,许多编译器项目都使用它,包括 `clang` C 编译器和我们心爱的 `rustc`。 14 | 15 | LLVM 接受 LLVM IR 的形式输入。它基本上是带有附加的低级类型和注释的汇编代码。这些注释有助于对 LLVM IR 和输出的机器代码进行优化。所有这一切的最终结果是(最终)一些可执行的东西(例如一个 ELF 对象、一个 EXE 或者一个 wasm)。 16 | 17 | 使用 LLVM 有几个好处: 18 | 19 | - 不需要编写一个完整的编译器后端,减少了实现和维护的负担。 20 | - 从 LLVM 项目收集的大量高级优化套件中受益。 21 | - 可以自动将 Rust 编译到 LLVM 支持的任何平台上。例如,一旦 LLVM 添加了对 wasm 的支持,瞧!Rustc,clang,和一堆其他语言都能编译成 wasm!(嗯,还有一些额外的工作要做,但我们已经完成了90%)。 22 | - 我们和其他编译器项目互相受益. 例如, 当[Spectre 和 Meltdown 安全漏洞][spectre]被发现,只需要修补 LLVM。 23 | 24 | [spectre]: https://meltdownattack.com/ 25 | 26 | ## 运行 LLVM, 链接和元数据生成 27 | 28 | 一旦建立了所有函数和静态等的 LLVM IR,就可以开始运行 LLVM 并进行优化。LLVM IR 分为“模块”。可以同时编写多个“模块”,以帮助实现多核使用。这些“模块”就是我们所说的 _codegen 29 | units_。这些单元是在单态化收集阶段建立起来的。 30 | 31 | 一旦 LLVM 从这些模块生成对象,这些对象就会被传递给链接器,还可以选择生成元数据对象和归档文件或可执行文件。 32 | 33 | 运行优化的不一定是上面描述的代码原阶段。对于某些类型的 LTO,优化可能发生在链路时间。在将对象传递到链接器之前还可能进行一些优化,而在链接过程中也可能进行一些优化。 34 | 35 | 这些都发生在编译的最后阶段。代码可以在 [`rustc_codegen_ssa::back`][ssaback] 和 [`rustc_codegen_llvm::back`][llvmback] 中找到。遗憾的是,这段代码与 LLVM 相关的代码并没有很好地分离; [`rustc_codegen_ssa`][ssa] 包含了大量特定于 LLVM 后端的代码。 36 | 37 | 一旦这些组件完成了它们的工作,您的文件系统中就会出现许多与您所请求的输出相对应的文件。 38 | 39 | [ssa]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/index.html 40 | [ssaback]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/back/index.html 41 | [llvmback]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/back/index.html -------------------------------------------------------------------------------- /src/backend/debugging.md: -------------------------------------------------------------------------------- 1 | # Debugging LLVM 2 | -------------------------------------------------------------------------------- /src/backend/implicit-caller-location.md: -------------------------------------------------------------------------------- 1 | # Implicit Caller Location 2 | -------------------------------------------------------------------------------- /src/backend/lowering-mir.md: -------------------------------------------------------------------------------- 1 | # 降级 MIR 到 Codegen IR 2 | 3 | 现在我们有了一个要从收集器生成的符号列表,我们需要生成某种类型的代码 codegen IR。在本章中,我们将假设是 LLVM IR,因为这是 rustc 常用的。实际的单态化是在我们翻译的过程中进行的。 4 | 5 | 回想一下,后端是由 [`rustc_codegen_ssa::base::codegen_crate`][codegen1] 开始的。最终到达 [`rustc_codegen_ssa::mir::codegen_mir`][codegen2],从 MIR 降级到 LLVM IR。 6 | 7 | [codegen1]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/base/fn.codegen_crate.html 8 | [codegen2]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/fn.codegen_mir.html 9 | 10 | 该代码被分成处理特定 MIR 原语的模块: 11 | 12 | - [`rustc_codegen_ssa::mir::block`][mirblk] 将处理翻译块及其终结符。这个模块做的最复杂也是最有趣的事情是为函数调用生成代码,包括必要的展开处理 IR。 13 | - [`rustc_codegen_ssa::mir::statement`][mirst] 翻译 MIR 语句。 14 | - [`rustc_codegen_ssa::mir::operand`][mirop] 翻译 MIR 操作。 15 | - [`rustc_codegen_ssa::mir::place`][mirpl] 翻译 MIR 位置参考。 16 | - [`rustc_codegen_ssa::mir::rvalue`][mirrv] 翻译 MIR 右值。 17 | 18 | [mirblk]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/block/index.html 19 | [mirst]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/statement/index.html 20 | [mirop]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/operand/index.html 21 | [mirpl]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/place/index.html 22 | [mirrv]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/rvalue/index.html 23 | 24 | 在转换一个函数之前,将运行一些简单的和基本的分析步骤以帮助我们生成更简单、更有效的 LLVM IR。这种分析方法的一个例子是找出哪些变量类似于 SSA,这样我们就可以直接将它们转换为 SSA,而不必依赖 LLVM 的 `mem2reg` 来处理这些变量。分析可以在 [`rustc_codegen_ssa::mir::analyze`][mirana] 中找到。 25 | 26 | [mirana]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/analyze/index.html 27 | 28 | 通常一个 MIR 基本块会映射到一个 LLVM 基本块,除了极少数的例外: 内部调用或函数调用以及较少的基本的像 `assert` 这样 MIR 语句可能会产生多个基本块。这是对代码生成中不可移植的 LLVM 特定部分的完美诠释。内部生成是相当容易理解的,因为它涉及的抽象级别很低,可以在[`rustc_codegen_llvm::intrinsic`][llvmint] 中找到。 29 | 30 | [llvmint]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/intrinsic/index.html 31 | 32 | 其他的都将使用[builder interface][builder],这是在 [`rustc_codegen_ssa::mir::*`][ssamir] 模块中调用的代码。 33 | 34 | [builder]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/builder/index.html 35 | [ssamir]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/index.html 36 | 37 | > TODO: 讨论常量是如何生成的 -------------------------------------------------------------------------------- /src/backend/monomorph.md: -------------------------------------------------------------------------------- 1 | # Monomorphization 2 | -------------------------------------------------------------------------------- /src/backend/updating-llvm.md: -------------------------------------------------------------------------------- 1 | # Updating LLVM 2 | -------------------------------------------------------------------------------- /src/borrow_check.md: -------------------------------------------------------------------------------- 1 | # The borrow checker 2 | -------------------------------------------------------------------------------- /src/borrow_check/moves_and_initialization.md: -------------------------------------------------------------------------------- 1 | # Tracking moves and initialization 2 | -------------------------------------------------------------------------------- /src/borrow_check/moves_and_initialization/move_paths.md: -------------------------------------------------------------------------------- 1 | # Move paths 2 | -------------------------------------------------------------------------------- /src/borrow_check/region_inference.md: -------------------------------------------------------------------------------- 1 | # Region inference 2 | -------------------------------------------------------------------------------- /src/borrow_check/region_inference/closure_constraints.md: -------------------------------------------------------------------------------- 1 | # Closure constraints 2 | -------------------------------------------------------------------------------- /src/borrow_check/region_inference/constraint_propagation.md: -------------------------------------------------------------------------------- 1 | # Constraint propagation 2 | -------------------------------------------------------------------------------- /src/borrow_check/region_inference/error_reporting.md: -------------------------------------------------------------------------------- 1 | # Error reporting 2 | -------------------------------------------------------------------------------- /src/borrow_check/region_inference/lifetime_parameters.md: -------------------------------------------------------------------------------- 1 | # Lifetime parameters 2 | -------------------------------------------------------------------------------- /src/borrow_check/region_inference/member_constraints.md: -------------------------------------------------------------------------------- 1 | # Member constraints 2 | -------------------------------------------------------------------------------- /src/borrow_check/region_inference/placeholders_and_universes.md: -------------------------------------------------------------------------------- 1 | # Placeholders and universes 2 | -------------------------------------------------------------------------------- /src/borrow_check/two_phase_borrows.md: -------------------------------------------------------------------------------- 1 | # Two-phase-borrows 2 | -------------------------------------------------------------------------------- /src/borrow_check/type_check.md: -------------------------------------------------------------------------------- 1 | # MIR type checker 2 | -------------------------------------------------------------------------------- /src/bug-fix-procedure.md: -------------------------------------------------------------------------------- 1 | # Bug Fix Procedure 2 | -------------------------------------------------------------------------------- /src/building/bootstrapping.md: -------------------------------------------------------------------------------- 1 | # Bootstrapping 2 | -------------------------------------------------------------------------------- /src/building/build-install-distribution-artifacts.md: -------------------------------------------------------------------------------- 1 | # Distribution artifacts 2 | -------------------------------------------------------------------------------- /src/building/compiler-documenting.md: -------------------------------------------------------------------------------- 1 | # Documenting Compiler 2 | -------------------------------------------------------------------------------- /src/building/ctags.md: -------------------------------------------------------------------------------- 1 | # ctags 2 | -------------------------------------------------------------------------------- /src/building/how-to-build-and-run.md: -------------------------------------------------------------------------------- 1 | # 如何构建并运行编译器 2 | 3 | 编译器是使用 `x.py` 工具进行构建。需要安装Python才能运行它。在此之前,如果您打算修改 `rustc` 的代码,则需要调整编译器的配置。默认配置面向的是编译器用户而非开发人员。有关如何安装 Python 和其他依赖,请参阅[下一章](./prerequisites.md)。 4 | 5 | ## 获取源代码 6 | 7 | 修改`rustc`的第一步是 clone 其代码仓库: 8 | 9 | ```bash 10 | git clone https://github.com/rust-lang/rust.git 11 | cd rust 12 | ``` 13 | 14 | ## 创建一个 config.toml 15 | 16 | 首先先将 [`config.toml.example`] 复制为 `config.toml`: 17 | 18 | [`config.toml.example`]: https://github.com/rust-lang/rust/blob/master/config.toml.example 19 | 20 | ```bash 21 | cp config.toml.example config.toml 22 | ``` 23 | 24 | 然后,您将需要打开这个文件并修改以下配置(根据需求不同可能也要修改其他的配置,例如`llvm.ccache`): 25 | 26 | ```toml 27 | [llvm] 28 | # Indicates whether the LLVM assertions are enabled or not 29 | assertions = true 30 | 31 | [rust] 32 | # Whether or not to leave debug! and trace! calls in the rust binary. 33 | # Overrides the `debug-assertions` option, if defined. 34 | # 35 | # Defaults to rust.debug-assertions value 36 | # 37 | # If you see a message from `tracing` saying 38 | # `max_level_info` is enabled and means logging won't be shown, 39 | # set this value to `true`. 40 | debug-logging = true 41 | 42 | # Whether to always use incremental compilation when building rustc 43 | incremental = true 44 | ``` 45 | 46 | 如果您已经构建过了`rustc`,那么您可能必须执行`rm -rf build`才能使配置更改生效。 47 | 请注意,`./x.py clean` 不会导致重新构建LLVM。 48 | 因此,如果您的配置更改影响LLVM,则在重新构建之前,您将需要手动`rm -rf build /`。 49 | 50 | ## `x.py`是什么? 51 | 52 | `x.py` 是用于编排 `rustc` 代码仓库中的工具的脚本。 它可以构建文档,运行测试以及编译 `rustc` 的脚本。现在它替代了以前的makefile,是构建`rustc`的首选方法。下面将会介绍使用`x.py`来有效处理常见任务的不同方式。 53 | 54 | 注意本章将侧重于如何把 `x.py` 用起来,因此介绍的内容比较基础。如果您想了解有关 `x.py` 的更多信息,请阅读[其README.md](https://github.com/rust-lang/rust/blob/master/src/bootstrap/README.md)。 要了解有关引导过程以及为什么需要使用 `x.py` 的更多信息,请[阅读这一章][bootstrap]。 55 | 56 | ### 更方便地运行`x.py` 57 | 58 | 在 `src/tools/x` 中有一个 `x.py` 的二进制封装。它只是调用 `x.py` ,但是它可以直接在整个操作系统范围内安装并可以从任何子目录运行。 它还会查找并使用适当版本的 `python`。 59 | 60 | 您可以使用 `cargo install --path src/tools/x` 安装它。 61 | 62 | [bootstrap]: ./bootstrapping.md 63 | 64 | ## 构建编译器 65 | 66 | 要完整构建编译器,请运行 `./x.py build`。这将构建包括 `rustdoc` 在内的 stage1 编译器,并根据您签出的源代码生成可用的编译器工具链。 67 | 68 | 请注意,构建将需要相对大量的存储空间。推荐预留 10 到 15 GB 以上的可用空间来构建编译器。 69 | 70 | `x.py` 有很多选项,这些选项可以帮助你减少编译时间或者适应你对其他内容的修改: 71 | 72 | ```txt 73 | Options: 74 | -v, --verbose use verbose output (-vv for very verbose) 75 | -i, --incremental use incremental compilation 76 | --config FILE TOML configuration file for build 77 | --build BUILD build target of the stage0 compiler 78 | --host HOST host targets to build 79 | --target TARGET target targets to build 80 | --on-fail CMD command to run on failure 81 | --stage N stage to build 82 | --keep-stage N stage to keep without recompiling 83 | --src DIR path to the root of the rust checkout 84 | -j, --jobs JOBS number of jobs to run in parallel 85 | -h, --help print this help message 86 | ``` 87 | 88 | 如果你只是在 hacking 编译器,则通常构建stage 1编译器就足够了,但是对于最终测试和发布,则需要使用stage 2编译器。 89 | 90 | `./x.py check` 可以快速构建 rust 编译器。 当您在执行某种“基于类型的重构”(例如重命名方法或更改某些函数的签名)时,它特别有用。 91 | 92 | 创建`config.toml`之后,就可以运行`x.py`了。 虽然 `x.py` 有很多选项,但让我们从本地构建 rust 的最佳“一键式”命令开始: 93 | 94 | ```bash 95 | ./x.py build -i library/std 96 | ``` 97 | 98 | *看起来*好像这只会构建`std`,但事实并非如此。 99 | 100 | 该命令的实际作用如下: 101 | 102 | - 使用 stage0 编译器构建 `std`(增量构建) 103 | - 使用 stage0 编译器构建 `rustc`(增量构建) 104 | - 产生的编译器即为 stage1 编译器 105 | - 使用 stage1 编译器构建 `std`(不能增量构建) 106 | 107 | 最终产品 (stage1编译器 + 使用该编译器构建的库)是构建其他 rust 程序所需要的(除非使用`#![no_std]`或`#![no_core]`)。 108 | 109 | 该命令自动启用 `-i` 选项,该选项启用增量编译。这会加快该过程的前两个步骤:如果您的修改比较小,我们应该能够使用您上一次编译的结果来更快地生成stage1编译器。 110 | 111 | 不幸的是,stage1 库的构建不能使用增量编译来加速。这是因为增量编译仅在连续运行*同一*编译器两次时才起作用。 112 | 由于我们每次都会构建一个 *新的 stage1 编译器* ,旧的增量结果可能不适用。 113 | **因此您可能会发现构建 stage1 `std` 对您的工作效率来说是一个瓶颈** —— 但不要担心,这有一个(hacky的)解决方法。请参阅下面[“推荐的工作流程”](./suggested.md)部分。 114 | 115 | 请注意,这整个命令只是为您提供完整 rustc 构建的一部分。**完整**的 rustc 构建(即 `./x.py build 116 | --stage 2 compiler/rustc` 命令)还有几个步骤: 117 | 118 | - 使用 stage1编译器构建 rustc。 119 | - 此处生成的编译器为 stage2 编译器。 120 | - 使用 stage2 编译器构建 `std`。 121 | - 使用 stage2 编译器构建 `librustdoc` 和其他内容。 122 | 123 | 124 | 125 | ## 构建特定组件 126 | 127 | - 只构建 core 库 128 | 129 | ```bash 130 | ./x.py build library/core 131 | ``` 132 | 133 | - 只构建 core 库和 `proc_macro` 库 134 | 135 | ```bash 136 | ./x.py build library/core library/proc_macro 137 | ``` 138 | 139 | 有时您可能只想测试您正在处理的部分是否可以编译。 140 | 使用这些命令,您可以在进行较为完整的构建之前进行测试。 141 | 如前所示,您还可以在命令的最后传递选项,例如 `--stage`。 142 | 143 | ## 创建一个rustup工具链 144 | 145 | 成功构建rustc之后,您在构建目录中已经创建了一堆文件。为了实际运行生成的`rustc`,我们建议创建两个rustup工具链。 第一个将运行stage1编译器(上面构建的结果)。第二个将执行stage2编译器(我们尚未构建这个编译器,但是您可能需要在某个时候构建它;例如,如果您想运行整个测试套件)。 146 | 147 | ```bash 148 | rustup toolchain link stage1 build//stage1 149 | rustup toolchain link stage2 build//stage2 150 | ``` 151 | 152 | `` 一般来说是以下三者之一: 153 | 154 | - Linux: `x86_64-unknown-linux-gnu` 155 | - Mac: `x86_64-apple-darwin` 156 | - Windows: `x86_64-pc-windows-msvc` 157 | 158 | 现在,您可以运行构建出的`rustc`。 如果使用`-vV`运行,则应该可以看到以`-dev`结尾的版本号,表示从本地环境构建的版本: 159 | 160 | ```bash 161 | $ rustc +stage1 -vV 162 | rustc 1.48.0-dev 163 | binary: rustc 164 | commit-hash: unknown 165 | commit-date: unknown 166 | host: x86_64-unknown-linux-gnu 167 | release: 1.48.0-dev 168 | LLVM version: 11.0 169 | ``` 170 | 171 | ## 其他 `x.py` 命令 172 | 173 | 这是其他一些有用的`x.py`命令。其中一部分我们将在其他章节中详细介绍: 174 | 175 | - 构建: 176 | - `./x.py build --stage 1` – 使用stage 1 编译器构建所有东西,不止是 `std` 177 | - `./x.py build` – 构建 stage2 编译器 178 | - 运行测试 (见 [运行测试](../tests/running.html) 章节): 179 | - `./x.py test --stage 1 src/libstd` – 为`libstd`运行 `#[test]` 测试 180 | 181 | - `./x.py test --stage 1 src/test/ui` – 运行 `ui` 测试套件 182 | 183 | - `./x.py test --stage 1 src/test/ui/const-generics` - 运行`ui` 测试套件下的 `const-generics/` 子文件夹中的测试 184 | 185 | - `./x.py test --stage 1 src/test/ui/const-generics/const-types.rs` 186 | 187 | - 运行`ui`测试组下的 `const-types.rs` 中的测试 188 | 189 | ### 清理构建文件夹 190 | 191 | 有时您可能会想要清理掉一切构建的产物并重新开始,一般情况下这么做并没有必要,如果你想要这么做的原因是 `rustbuild`无法正确执行,你应该报告一个 bug 来告知我们什么出错了。 192 | 如果确实需要清理所有内容,则只需运行一个命令! 193 | 194 | ```bash 195 | ./x.py clean 196 | ``` 197 | 198 | `rm -rf build` 也能达到效果,但这也会导致接下来你要重新构建LLVM,即使在相对快的计算机上这也会花费比较长的时间。 -------------------------------------------------------------------------------- /src/building/new-target.md: -------------------------------------------------------------------------------- 1 | # Adding a new target 2 | -------------------------------------------------------------------------------- /src/building/prerequisites.md: -------------------------------------------------------------------------------- 1 | # 前置准备 2 | 3 | ## 依赖 4 | 5 | 在构建编译器之前,您需要安装以下内容: 6 | 7 | * `python` 3 或者 2.7 (需要以 `python`名字。 `python2` 或者 `python3` 都将会不工作) 8 | * `curl` 9 | * `git` 10 | * `ssl` 在 `libssl-dev` 或者 `openssl-devel` 中 11 | * `pkg-config` 如果你在Linux中编译并且面向Linux 12 | 13 | 如果 构建 LLVM from source (the default),你需要补充以下工具: 14 | 15 | * `g++` 5.1 或更新版本, `clang++` 3.5 或更新版本, 或 MSVC 2017 或更新版本. 16 | * `ninja`, 或者 GNU `make` 3.81 或更新版本 (ninja 更加推荐, 特别是在 Windows 操作系统中) 17 | * `cmake` 3.13.4 或更新版本 18 | 19 | 否则, 你需要安装 LLVM 并且 `llvm-config` 在路径中. 20 | 看 [这一章节获取更多信息][sysllvm]. 21 | 22 | [sysllvm]: ./suggested.md#skipping-llvm-build 23 | 24 | ### Windows 25 | 26 | * 安装 [winget](https://github.com/microsoft/winget-cli) 27 | 28 | `winget` 是一个 WIndows 下的包管理器.它将会使包的安装在 Windows 下更加简单 29 | 30 | 在终端运行以下命令: 31 | 32 | ```powershell 33 | winget install python 34 | winget install cmake 35 | ``` 36 | 37 | 如果其中任何一个已经安装,winget 将会检测到它。 38 | 然后编辑系统的 `PATH` 变量并且添加 `C:\Program Files\CMake\bin`. 39 | 40 | 有关在WIndows下编译的更多信息看 [the `rust-lang/rust` README](https://github.com/rust-lang/rust#building-on-windows). 41 | 42 | ## 硬件 43 | 44 | 这些与其说是要求,不如说是要求 _推荐_: 45 | 46 | * ~15GB 的空闲磁盘 (或更多,如果要做额外的构建,~25GB). 47 | * \>= 8GB RAM 48 | * \>= 2 cores 49 | * 网络连接 50 | 51 | 性能好的电脑将会编译的更加快。如果你的电脑性能不是非常好,一个 52 | 常见的策略是只使用 `./x.py check` 在你的本地几期 53 | 并且当你推送一个PR分支的时候,让 CI 打包测试你的改动 54 | 55 | ## `rustc` 和工具链的安装 56 | 57 | 按照 [Rust book][install] 中给出的安装步骤安装 58 | `rustc` 和平台上必要的C/++工具链。 59 | 60 | [install]: https://doc.rust-lang.org/book/ch01-01-installation.html 61 | -------------------------------------------------------------------------------- /src/building/suggested.md: -------------------------------------------------------------------------------- 1 | # Suggested Workflows 2 | -------------------------------------------------------------------------------- /src/cli.md: -------------------------------------------------------------------------------- 1 | # 命令行参数 2 | 3 | 命令行参数记录在 [rustc book][cli-docs] 中。 所有*稳定的*参数都应在此处记录。不稳定的参数应记录在 [unstable book] 中。 4 | 5 | 有关添加新命令行参数的*过程*的详细信息,请参见 [forge guide for new options] 。 6 | 7 | ## 指南 8 | 9 | - 参数应彼此正交。例如,如果我们有多个操作,如 `foo` 和 `bar` ,具有生成 json 的变体,则添加额外的 `--json` 参数比添加 `--foo-json` 和 `--bar-json` 更好。 10 | - 避免使用带有 `no-` 前缀的参数。相反,使用 [`parse_bool`] 函数,比如 `-C embed-bitcode=no` 。 11 | - 考虑参数被多次传递时的行为。在某些情况下,应该(按顺序)累积值。在另一些情况下,后面的参数应覆盖前面的参数(例如,lint-level 参数)。如果多个参数的含义太模糊,那么一些参数(比如 `-o` )应该生成一个错误。 12 | - 如果仅为了编译器脚本更易于理解,请始终为选项提供长的描述性名称。 13 | - `--verbose` 参数用于向 rustc 的输出中添加详细信息。例如,将其与 `--version` 参数一起使用可提供有关编译器代码哈希值的信息。 14 | - 实验性参数和选项必须放在 `-Z unstable-options` 后面。 15 | 16 | [cli-docs]: https://doc.rust-lang.org/rustc/command-line-arguments.html 17 | [forge guide for new options]: https://forge.rust-lang.org/compiler/new_option.html 18 | [unstable book]: https://doc.rust-lang.org/nightly/unstable-book/ 19 | [`parse_bool`]: https://github.com/rust-lang/rust/blob/e5335592e78354e33d798d20c04bcd677c1df62d/src/librustc_session/options.rs#L307-L313 20 | -------------------------------------------------------------------------------- /src/closure.md: -------------------------------------------------------------------------------- 1 | # rustc中的闭包扩展 2 | 3 | 这一节描述了rustc是如何处理闭包的。Rust中的闭包实际上沦为了来自其创建者栈帧的结构体,该结构体包含了他们使用的值(或使用值的引用)。rustc的工作是要弄清楚闭包使用了哪些值,以及是如何使用的,这样他就可以决定是通过共享引用,可变引用还是通过移动来捕获给定的变量。rustc也需要弄清楚闭包能够实现哪种闭包特征([`Fn`][fn],[`FnMut`][fn_mut],或[`FnOnce`][fn_once])。 4 | 5 | [fn]: https://doc.rust-lang.org/std/ops/trait.Fn.html 6 | [fn_mut]:https://doc.rust-lang.org/std/ops/trait.FnMut.html 7 | [fn_once]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html 8 | 9 | 让我们来从一个小例子开始: 10 | 11 | ### 示例 1 12 | 13 | 首先,让我们来看一下以下示例中的闭包是如何实现的: 14 | 15 | ```rust 16 | fn closure(f: impl Fn()) { 17 | f(); 18 | } 19 | 20 | fn main() { 21 | let x: i32 = 10; 22 | closure(|| println!("Hi {}", x)); // 闭包仅仅读取了x变量. 23 | println!("Value of x after return {}", x); 24 | } 25 | ``` 26 | 27 | 假设上面是名为`immut.rs`文件的内容。如果我们用以下的命令来编译`immut.rs`,[`-Z dump-mir=all`][dump-mir]参数将会使`rustc`生成[MIR][mir]并将其转储到`mir_dump`目录中。 28 | ```console 29 | > rustc +stage1 immut.rs -Z dump-mir=all 30 | ``` 31 | 32 | [mir]: ./mir/index.md 33 | [dump-mir]: ./mir/passes.md 34 | 35 | 在我们执行了这个命令之后,我们将会看到在当前的工作目录下生成了一个名为`mir_dump`的新目录,其中包含了多个文件,如果我们打开`rustc.main.-------.mir_map.0.mir`文件将会发现,除了其他内容外,还包括此行: 36 | 37 | ```rust,ignore 38 | _4 = &_1; 39 | _3 = [closure@immut.rs:7:13: 7:36] { x: move _4 }; 40 | ``` 41 | 42 | 请注意在这节的MIR示例中,`_1`就是`x`。 43 | 44 | 在第一行`_4 = &_1;`中,`mir_dump`告诉我们`x`作为不可变引用被借用了。这是我们希望的,因为我们的闭包需要读取`x`。 45 | 46 | ### 示例 2 47 | 48 | 这里是另一个示例: 49 | 50 | ```rust 51 | fn closure(mut f: impl FnMut()) { 52 | f(); 53 | } 54 | 55 | fn main() { 56 | let mut x: i32 = 10; 57 | closure(|| { 58 | x += 10; // The closure mutates the value of x 59 | println!("Hi {}", x) 60 | }); 61 | println!("Value of x after return {}", x); 62 | } 63 | ``` 64 | 65 | ```rust,ignore 66 | _4 = &mut _1; 67 | _3 = [closure@mut.rs:7:13: 10:6] { x: move _4 }; 68 | ``` 69 | 70 | 这一次,在第一行`_4 = &mut _1;`中,我们可以看到借用变成了可变借用。这是十分合理的,使得闭包可以将`x`加10。 71 | 72 | ### 示例 3 73 | 74 | 又一个示例: 75 | 76 | ```rust 77 | fn closure(f: impl FnOnce()) { 78 | f(); 79 | } 80 | 81 | fn main() { 82 | let x = vec![21]; 83 | closure(|| { 84 | drop(x); // 在这之后使x不可用 85 | }); 86 | // println!("Value of x after return {:?}", x); 87 | } 88 | ``` 89 | 90 | ```rust,ignore 91 | _6 = [closure@move.rs:7:13: 9:6] { x: move _1 }; // bb16[3]: scope 1 at move.rs:7:13: 9:6 92 | ``` 93 | 这里, `x`直接被移入了闭包内,因此在闭包代码块之后将不允许访问这个变量了。 94 | 95 | ## 编译器中的推断 96 | 97 | 现在,让我们深入研究rustc的代码,看看编译器是如何完成所有这些推断的。 98 | 99 | 首先,我们先定义一个术语*upvar*,它在我们之后的讨论中会经常使用到。**upvar**是定义闭包的函数的本地变量。所以,在上述示例中,**x**对于闭包来说是一个upvar。它们有时也会被称为*空闲变量*以表示它们并未绑定到闭包的上下文中。[`compiler/rustc_middle/src/ty/query/mod.rs`][upvars]为此定义了一个被成为*upv.rs_mentioned*的查询。 100 | 101 | [upvars]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/query/queries/struct.upvars_mentioned.html 102 | 103 | 除了懒调用,另一个将闭包区别于普通函数的特征就是它可以从上下文中借用这些upvar;因此编译器必须确定upvar的借用类型。基于这个用途,编译器从分配一个不可变的借用类型开始,可以根据需要来减少限制(将它从**不可变**变成**可变**,再变成**移动**)。 在上述的示例1中,闭包仅仅将变量用于打印,而不以任何方式对其进行修改,因此在`mir_dump`中,我们发现借用类型的upvar变量`x`是不可变的。但是,在示例2中,闭包修改了`x`并将其加上了某个值。由于这种改变,编译器从将`x`分配为不可变的引用类型开始,必须将其调整为可变的引用。同样的,在示例3中,闭包释放了向量`x`,因此要求将变量`x`移入闭包内。依赖于借用类型,闭包需要实现合适的特征:`Fn`特征对应不可变借用, `FnMut`对应可变借用,`FnOnce`对应于移动语义。 104 | 105 | 大多数与闭包相关的代码在[`compiler/rustc_typeck/src/check/upvar.rs`][upvar]文件中,数据结构定义在[`compiler/rustc_middle/src/ty/mod.rs`][ty]文件中。 106 | 107 | [upvar]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_typeck/check/upvar/index.html 108 | [ty]:https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/index.html 109 | 110 | 在我们进一步深入之前,一起讨论下如何通过rustc代码库来检测控制流。对于闭包来说,像下面一样设置`RUST_LOG`环境变量并在文件中收集输出。 111 | 112 | ```console 113 | > RUST_LOG=rustc_typeck::check::upvar rustc +stage1 -Z dump-mir=all \ 114 | <.rs file to compile> 2> 115 | ``` 116 | 117 | 这里使用了stage1编译器,并为`rustc_typeck::check::upvar`模块启用了`debug!`日志。 118 | 119 | 另一种选择是使用lldb或gdb逐步执行代码。 120 | 121 | 1. `rust-lldb build/x86_64-apple-darwin/stage1/bin/rustc test.rs` 122 | 2. 在lldb中: 123 | 1. `b upvar.rs:134` // 在upvar.rs文件中的某行上设置断点 124 | 2. `r` // 一直运行程序直到打到了该断点上 125 | 126 | 让我们从[`upvar.rs`][upvar]开始. 这个文件有一个叫[`euv::ExprUseVisitor`]的结构,该结构遍历闭包的源码并为每一个被借用,被更改,被移动的upvar触发了一个回调。 127 | 128 | [`euv::ExprUseVisitor`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_typeck/expr_use_visitor/struct.ExprUseVisitor.html 129 | 130 | ```rust 131 | fn main() { 132 | let mut x = vec![21]; 133 | let _cl = || { 134 | let y = x[0]; // 1. 135 | x[0] += 1; // 2. 136 | }; 137 | } 138 | ``` 139 | 140 | 在上面的示例中,我们的访问器将会调用两次,对于标记了1和2的代码行,一个用于共享借用,另一个用于可变借用。它还会告诉我们借用了什么。 141 | 142 | 通过实现[`Delegate`]特征来定义回调。[`InferBorrowKind`][ibk]类型实现了`Delegate`并维护了一个map来记录每个upvar需要哪种捕获方式。捕获的方式可以是`ByValue`(被移动)或者是`ByRef`(被借用)。对于`ByRef`借用,[`BorrowKind`]可能是定义在[`compiler/rustc_middle/src/ty/mod.rs`][middle_ty]中的`ImmBorrow`,`UniqueImmBorrow`,`MutBorrow`。 143 | 144 | [`BorrowKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.BorrowKind.html 145 | [middle_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/index.html 146 | 147 | `Delegate`定义了一些不同的方法(不同的回调): 148 | **consume**方法用于*移动*变量,**borrow**方法用于某种(共享的或可变的)借用,而当我们看到某种事物的分配时,则调用**mutate**方法。 149 | 150 | 所有的这些回调都有一个共同的参数*cmt*,该参数代表类别,可变形和类型。他定义在[`compiler/rustc_middle/src/middle/mem_categorization.rs`][cmt]中。代码注释中写到:“`cmt`是一个值的完整分类,它指明了该值的起源和位置,以及存储该值的内存的可变性”。根据这些回调(consume,borrow等),我们将会调用相关的`adjust_upvar_borrow_kind_for_`并传递`cmt`。一旦借用类型有了调整,我们将它存储在表中,基本上说明了每个闭包都借用了什么。 151 | 152 | ```rust,ignore 153 | self.tables 154 | .borrow_mut() 155 | .upvar_capture_map 156 | .extend(delegate.adjust_upvar_captures); 157 | ``` 158 | 159 | [`Delegate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_typeck/expr_use_visitor/trait.Delegate.html 160 | [ibk]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_typeck/check/upvar/struct.InferBorrowKind.html 161 | [cmt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_typeck/mem_categorization/index.html 162 | -------------------------------------------------------------------------------- /src/compiler-debugging.md: -------------------------------------------------------------------------------- 1 | # Debugging the Compiler 2 | -------------------------------------------------------------------------------- /src/compiler-src.md: -------------------------------------------------------------------------------- 1 | # 编译器源代码概览 2 | 3 | 4 | 5 | > **注意**:代码仓库的结构正在经历许多转变。特别是,我们希望最终顶层目录下具有编译器、构建系统、标准库等的单独目录,而不是一个庞大的 `src/` 目录。 自2021年 1月起,标准库已移至 `library/`,构成 `rustc` 编译器本身的 crate 已移至 `compiler/`。 6 | 7 | 现在,我们已经[大体了解了编译器的工作](./overview.md),让我们看一下 rust-lang/rust 仓库内容的结构。 8 | 9 | ## Workspace 结构 10 | 11 | `rust-lang/rust` 存储库由一个大型cargo workspace组成,该 Workspace 包含编译器,标准库(`core`、 `alloc`、 `std`、 `proc_macro`等)和 `rustdoc`,以及构建系统以及用于构建完整 Rust 发行版的一些工具和子模块。 在撰写本文时,此结构正在逐步进行一些转换,以使其变得不再是一个巨大的代码仓库且更易于理解,尤其是对于新手。 该存储库由三个主要目录组成: 12 | 13 | - `compiler/` 包含了 `rustc` 的源代码。它包含了组成编译器的一系列crate。 14 | - `library/` 包含标准库 (`core`、 `alloc`、 `std`、 15 | `proc_macro`、 `test`)以及 Rust 运行时(`backtrace`、 `rtstartup`、 16 | `lang_start`) 17 | - `src/` 包含 `rustdoc`、`clippy`、`cargo`、 构建系统、语言文档等等。 18 | 19 | ## 标准库 20 | 21 | 标准库 crate 都在 `library/`中。它们的名称都非常直观,如 `std`、`core`、`alloc`等。还有 `proc_macro`,`test` 和其他运行时库。这些代码和其他 Rust crate 非常相似,区别在于它们必须以特殊的方式构建,因为其中可以使用不稳定的功能。 22 | 23 | ## 编译器 24 | 25 | >建议先阅读[概述章节](./overview.md),它概述了编译器的工作方式。 26 | > 27 | >本节中提到的 crate 组成了整个编译器,它们位于 `compiler/` 中。 28 | 29 | `compiler/` 下的 crate 们的名称均以`rustc_ *`开头。这里有大约 50 个或大或小,相互依赖的 crate。 还有一个 `rustc` crate,它是实际的二进制文件入口点(即 `main` 函数)所在之处; 除了调用`rustc_driver`crate之外,`rustc` crate实际上并不做任何事情,`rustc_driver` crate 会驱动其他 crate 中的各个部分来进行编译。 30 | 31 | 这些 crate 之间的依赖关系很复杂,但大体来说: 32 | 33 | - `rustc` (二进制文件入口点)调用 [`rustc_driver::main`][main] 34 | - [`rustc_driver`] 依赖许多其他 crate,其中最主要的是 [`rustc_interface`]。 35 | - [`rustc_interface`] 依赖于大多数其他编译器 crate。它是用于驱动整个编译的相当通用的接口。 36 | - 大部分其他 `rustc_*` crates 依赖于 [`rustc_middle`],[`rustc_middle`] 中定义了编译器中的许多核心数据结构 37 | 38 | - [`rustc_middle`] 和编译器中大多数其他部分都依赖一些代表了编译中更早的阶段的 crate(例如 parser),基础数据结构(如[`Span`]),或者错误报告相关的内容:[`rustc_data_structures`],[`rustc_span`],[`rustc_errors`],等等 39 | 40 | [main]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/fn.main.html 41 | [`rustc_driver`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/index.html 42 | [`rustc_interface`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/index.html 43 | [`rustc_middle`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/index.html 44 | [`rustc_data_structures`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_data_structures/index.html 45 | [`rustc_span`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/index.html 46 | [`Span`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html 47 | [`rustc_errors`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/index.html 48 | 49 | 您可以通过读取各个 crate 的 `Cargo.toml` 来查看确切的依赖关系,就像普通的Rust crate一样。 50 | 51 | 最后一件事:[`src/llvm-project`] 是指向我们自己的 LLVM fork的子模块。 在bootstrap过程中,将会构建LLVM, [`compiler/rustc_llvm`] 是LLVM(用C++编写)的 Rust 包装,以便编译器可以与其交互。 本书的大部分内容是关于 Rust 编译器的,因此在这里我们将不对这些 crate 做任何进一步的解释。 52 | 53 | [`src/llvm-project`]: https://github.com/rust-lang/rust/tree/master/src/ 54 | [`compiler/rustc_llvm`]: https://github.com/rust-lang/rust/tree/master/compiler/rustc_llvm 55 | 56 | ## Big Picture 57 | 58 | 这种由多个 crate 互相依赖的代码结构受两个主要因素的强烈影响: 59 | 60 | 1. 组织。编译器是一个 _巨大的_ 代码库;将其放在一整个大 crate 中是不可能的。依赖关系结构部分反映了编译器的代码结构。 61 | 2. 编译时间。通过将编译器分成多个 crate,我们可以更好地利用 cargo 进行增量/并行编译。特别是,我们尝试使板条箱之间的依赖关系尽可能少,这样,如果您更改一个 crate,我们就不必重新构建大量的 crate。 62 | 63 | 在依赖关系树的最底部是整个编译器使用的少数 crate(例如 [`rustc_span`])。编译过程中的非常早期的部分(例如,parsing 和 AST)仅取决于这些。 64 | 65 | 构建AST之后不久,编译器的 [查询系统][query] 就建立好了。查询系统是使用函数指针以巧妙的方式设置的。这使我们可以打破 crate 之间的依赖关系,从而可以并行地进行更多编译。 但是,由于查询系统是在 [`rustc_middle`] 中定义的,编译器的几乎所有后续部分都依赖于此 crate。这是一个非常大的 crate,导致其编译时间极长。我们已经做出了一些努力来将内容从其中移出,但效果有限。另一个不幸的副作用是,有时相关功能分散在不同的 crate 中。例如,linting 功能分散在板条箱的较早部分 [`rustc_lint`],[`rustc_middle`] 和其他地方。 66 | 67 | [`rustc_lint`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/index.html 68 | 69 | 一般而言,在理想世界中,应当使用更少的,更内聚的板条箱,使用增量和并行编译确保编译时间保持合理。 但是,我们的增量和并行编译暂时还没有那么好用,所以目前为止我们的解决方案只能是东西分进单独的 crate。 70 | 71 | 在依赖树的顶部是 [`rustc_interface`] 和 [`rustc_driver`]板条箱。 72 | 73 | [`rustc_interface`] 是一个不稳定的查询系统的包装,用于帮助推动编译的各个阶段。 74 | 75 | 其他编译器中的的消费者(例如 `rustdoc` 或者甚至是 rust-analyzer)可以以不同的方式使用此接口。 76 | 77 | [`rustc_driver`] crate 首先解析命令行参数,然后使[`rustc_interface`]驱动编译完成。 78 | 79 | [query]: ./query.md 80 | 81 | [orgch]: ./overview.md 82 | 83 | ## rustdoc 84 | 85 | `rustdoc` 的大部分位于 [`librustdoc`] 中。 但是,`rustdoc`二进制文件本身 [`src/tools/rustdoc`],除了调用 [`rustdoc::main`]外,它什么都不做。 在 [`src/tools/ rustdoc-js`] 和 [`src/tools/rustdoc-themes`] 中,还有 rustdocs 的 javascript 和 CSS。 您可以在[本章][rustdocch]中阅读有关 rustdoc 的更多信息。 86 | 87 | [`librustdoc`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc/index.html 88 | [`rustdoc::main`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc/fn.main.html 89 | [`src/tools/rustdoc`]: https://github.com/rust-lang/rust/tree/master/src/tools/rustdoc 90 | [`src/tools/rustdoc-js`]: https://github.com/rust-lang/rust/tree/master/src/tools/rustdoc-js 91 | [`src/tools/rustdoc-themes`]: https://github.com/rust-lang/rust/tree/master/src/tools/rustdoc-themes 92 | 93 | [rustdocch]: ./rustdoc.md 94 | 95 | ## 测试 96 | 97 | 以上所有内容的测试套件都在 [`src/test/`] 中。 您可以在[本章][testsch]中了解有关测试套件的更多信息。 测试工具本身在 [`src/tools/compiletest`] 中。 98 | 99 | [testsch]: ./tests/intro.md 100 | 101 | [`src/test/`]: https://github.com/rust-lang/rust/tree/master/src/test 102 | [`src/tools/compiletest`]: https://github.com/rust-lang/rust/tree/master/src/tools/compiletest 103 | 104 | ## 构建系统 105 | 106 | 代码仓库中有许多工具,可用于构建编译器,标准库,rustdoc,以及进行测试,构建完整的 Rust 发行版等。 主要工具之一是 [`src/bootstrap`]。 您可以[在这一章][bootstch]中了解有关 bootstrap的更多信息。 构建过程重还可能使用 `src/tools/`中的其他工具,例如 [tidy] 或 [compiletest]。 107 | 108 | [`src/bootstrap`]: https://github.com/rust-lang/rust/tree/master/src/bootstrap 109 | [`tidy`]: https://github.com/rust-lang/rust/tree/master/src/tools/tidy 110 | [`compiletest`]: https://github.com/rust-lang/rust/tree/master/src/tools/compiletest 111 | 112 | [bootstch]: ./building/bootstrapping.md 113 | 114 | ## 其他 115 | 116 | 在 `rust-lang/rust` 仓库中还有很多其他与构建完整 Rust 发行版有关的东西。 大多数时候,您无需关心它们。 这些包括: 117 | 118 | - [`src/ci`]:CI配置。 这里的代码实际上相当多,因为我们在许多平台上运行了许多测试。 119 | - [`src/doc`]:各种文档,包括指向几本书的submodule。 120 | - [`src/etc`]:其他实用程序。 121 | - [`src/tools/rustc-workspace-hack`],以及其他:各种变通方法以使 cargo 在bootstrapping 过程中运行。 122 | - 以及更多…… -------------------------------------------------------------------------------- /src/compiler-team.md: -------------------------------------------------------------------------------- 1 | # About the compiler team 2 | -------------------------------------------------------------------------------- /src/compiletest.md: -------------------------------------------------------------------------------- 1 | # Using compiletest commands to control test execution 2 | -------------------------------------------------------------------------------- /src/const-eval.md: -------------------------------------------------------------------------------- 1 | # Constant evaluation 2 | -------------------------------------------------------------------------------- /src/contributing.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RustcRustc/rustc-dev-guide-zh/6f41aee51cb30fe94a6c8173342f7a8051e217e7/src/contributing.md -------------------------------------------------------------------------------- /src/conventions.md: -------------------------------------------------------------------------------- 1 | # Coding conventions 2 | -------------------------------------------------------------------------------- /src/crates-io.md: -------------------------------------------------------------------------------- 1 | # crates.io Dependencies 2 | -------------------------------------------------------------------------------- /src/debugging-support-in-rustc.md: -------------------------------------------------------------------------------- 1 | # Debugging Support in the Rust Compiler 2 | -------------------------------------------------------------------------------- /src/diagnostics.md: -------------------------------------------------------------------------------- 1 | # Errors and Lints 2 | -------------------------------------------------------------------------------- /src/diagnostics/diagnostic-codes.md: -------------------------------------------------------------------------------- 1 | # Diagnostic Codes 2 | -------------------------------------------------------------------------------- /src/diagnostics/lintstore.md: -------------------------------------------------------------------------------- 1 | # LintStore 2 | -------------------------------------------------------------------------------- /src/diagnostics/sessiondiagnostic.md: -------------------------------------------------------------------------------- 1 | # Creating Errors With SessionDiagnostic 2 | -------------------------------------------------------------------------------- /src/early-late-bound.md: -------------------------------------------------------------------------------- 1 | # Early and Late Bound Parameters 2 | -------------------------------------------------------------------------------- /src/feature-gate-ck.md: -------------------------------------------------------------------------------- 1 | # Feature Gate Checking 2 | -------------------------------------------------------------------------------- /src/feature-gates.md: -------------------------------------------------------------------------------- 1 | # Feature Gates 2 | -------------------------------------------------------------------------------- /src/generic_arguments.md: -------------------------------------------------------------------------------- 1 | # Generic arguments 2 | -------------------------------------------------------------------------------- /src/generics.md: -------------------------------------------------------------------------------- 1 | # Generics and substitutions 2 | -------------------------------------------------------------------------------- /src/getting_start.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RustcRustc/rustc-dev-guide-zh/6f41aee51cb30fe94a6c8173342f7a8051e217e7/src/getting_start.md -------------------------------------------------------------------------------- /src/git.md: -------------------------------------------------------------------------------- 1 | # Using Git 2 | -------------------------------------------------------------------------------- /src/hir-debugging.md: -------------------------------------------------------------------------------- 1 | # Debugging 2 | -------------------------------------------------------------------------------- /src/hir.md: -------------------------------------------------------------------------------- 1 | # HIR 2 | 3 | 4 | 5 | HIR ——“高级中间表示” ——是大多数 rustc 组件中使用的主要IR。 6 | 它是抽象语法树(AST)的对编译器更为友好的表示形式,该结构在语法分析,宏展开和名称解析之后生成(有关如何创建HIR,请参见[Lowering](./lowering.html))。 7 | HIR 的许多部分都非常类似于普通 Rust 的语法,但是 Rust 中的的某些表达式已被“脱糖”。 8 | 例如,`for` 循环将转换为了 `loop`,因此在HIR中不会出现 `for` 。 这使HIR比普通AST更易于分析。 9 | 10 | 本章介绍了HIR的主要概念。 11 | 12 | 您可以通过给 rustc 传递 `-Zunpretty=hir-tree` 标志来查看代码的 HIR 表示形式: 13 | 14 | ```bash 15 | cargo rustc -- -Zunpretty=hir-tree 16 | ``` 17 | 18 | ### Out-of-band 存储和`Crate`类型 19 | 20 | HIR中的顶层数据结构是 [`Crate`],它存储当前正在编译的 crate 的内容(我们从来就只为当前 crate 构造 HIR)。 21 | 在 AST 中,crate 数据结构基本上只包含根模块,而 HIR `Crate` 结构则包含许多map 和其他用于组织 crate 内容以便于访问的数据。 22 | 23 | [`Crate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/struct.Crate.html 24 | 25 | 例如,HIR 中单个项目(例如模块、函数、trait、impl等)的内容不能在其父级中直接访问。 26 | 因此,例如,如果有一个包含函数 `bar()` 的模块 `foo`: 27 | 28 | ```rust 29 | mod foo { 30 | fn bar() { } 31 | } 32 | ``` 33 | 34 | 那么在模块 `foo` 的HIR中表示([`Mod`] 结构)中将只有`bar()`的**`ItemId`** `I`。 35 | 要获取函数 `bar()` 的详细信息,我们将在 `items` 映射中查找 `I`。 36 | 37 | [`Mod`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/struct.Mod.html 38 | 39 | 这种表示形式的一个好处是,可以通过遍历这些映射中的键值对来遍历 crate 中的所有项目(而无需遍历整个HIR)。 40 | 对于 trait 项和 impl 项以及“实体”(如下所述)也有类似的map。 41 | 42 | 使用这种表示形式的另一个原因是为了更好地与增量编译集成。 43 | 这样,如果您访问 [`&rustc_hir::Item`](例如mod `foo`),不会同时立即去访问函数`bar()`的内容。 44 | 相反,您只能访问 `bar()` 的**id**,必须将 id 传入某些函数来查找 `bar` 的内容。 这使编译器有机会观察到您访问了`bar()`的数据,然后记录依赖。 45 | 46 | [`&rustc_hir::Item`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/struct.Item.html 47 | 48 | 49 | 50 | ### HIR 中的标识符 51 | 52 | 有许多不同的标识符可以引用HIR中的其他节点或定义: 53 | 简单来说有: 54 | 55 | - [`DefId`] 表示对任何其他 crate 中的一个*定义*的引用。 56 | - [`LocalDefId`] 表示当前正在编译的 crate 中的一个*定义*的引用。 57 | - [`HirId`] 表示对 HIR 中任何节点的引用。 58 | 59 | 更多详细信息,请查看[有关标识符的章节][ids]。 60 | 61 | [`DefId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html 62 | [`LocalDefId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.LocalDefId.html 63 | [`HirId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir_id/struct.HirId.html 64 | [ids]: ./identifiers.md#in-the-hir 65 | 66 | ### HIR Map 67 | 68 | 在大多数情况下,当您使用HIR时,您将通过 **HIR Map** 进行操作,该map可通过[`tcx.hir()`] 在tcx中访问(它在[`hir::map`]模块中定义)。 69 | [HIR map] 包含[多个方法],用于在各种 ID 之间进行转换并查找与 HIR 节点关联的数据。 70 | 71 | [`tcx.hir()`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.hir 72 | [`hir::map`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/index.html 73 | [HIR map]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html 74 | [多个方法]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#methods 75 | 76 | 例如,如果您有一个 [`DefId`],并且想将其转换为 [`NodeId`],则可以使用 [`tcx.hir().as_local_node_id(def_id)`][as_local_node_id]。 77 | 这将返回一个 `Option` —— 如果 def-id 引用了当前 crate 之外的内容(因为这种内容没有HIR节点),则将为`None`; 78 | 否则这个函数将返回 `Some(n)`,其中 `n` 是定义对应的节点ID。 79 | 80 | [`NodeId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/node_id/struct.NodeId.html 81 | [as_local_node_id]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#method.as_local_node_id 82 | 83 | 同样,您可以使用[`tcx.hir().find(n)`][find]在节点上查找[`NodeId`]。 84 | 这将返回一个`Option>`,其中[`Node`]是在map中定义的枚举。 85 | 86 | 通过对此枚举进行 match ,您可以找出 node-id 所指的节点类型,并获得指向数据本身的指针。 87 | 一般来说,您已经事先知道了节点 `n` 是哪种类型——例如,如果您已经知道了 `n` 肯定是某个 HIR 表达式, 88 | 则可以执行[`tcx.hir().expect_expr(n)`][expect_expr],它将试图提取并返回[`&hir::Expr`][Expr],此时如果`n`实际上不是一个表达式,那么会程序会 panic。 89 | 90 | [find]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#method.find 91 | [`Node`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/enum.Node.html 92 | [expect_expr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#method.expect_expr 93 | [Expr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/struct.Expr.html 94 | 95 | 最后,您可以通过 [`tcx.hir().get_parent_node(n)`][get_parent_node] 之类的调用,使用HIR map来查找节点的父节点。 96 | 97 | [get_parent_node]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#method.get_parent_node 98 | 99 | ### HIR Bodies 100 | 101 | [`rustc_hir::Body`] 代表某种可以执行的代码,例如函数/闭包的函数体或常量的定义。 102 | body 与一个**所有者**相关联,“所有者”通常是某种Item(例如,`fn()`或`const`),但也可以是闭包表达式(例如, `|x, y| x + y`)。 103 | 您可以使用 HIR 映射来查找与给定 def-id([`maybe_body_owned_by`])关联的body,或找到 body 的所有者([`body_owner_def_id`])。 104 | 105 | [`rustc_hir::Body`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/struct.Body.html 106 | [`maybe_body_owned_by`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#method.maybe_body_owned_by 107 | [`body_owner_def_id`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/map/struct.Map.html#method.body_owner_def_id 108 | -------------------------------------------------------------------------------- /src/identifiers.md: -------------------------------------------------------------------------------- 1 | # Identifiers in the Compiler 2 | -------------------------------------------------------------------------------- /src/img/dataflow-graphviz-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RustcRustc/rustc-dev-guide-zh/6f41aee51cb30fe94a6c8173342f7a8051e217e7/src/img/dataflow-graphviz-example.png -------------------------------------------------------------------------------- /src/implementing_new_features.md: -------------------------------------------------------------------------------- 1 | # Implementing new features 2 | -------------------------------------------------------------------------------- /src/incrcomp-debugging.md: -------------------------------------------------------------------------------- 1 | # Debugging and Testing 2 | -------------------------------------------------------------------------------- /src/licenses.md: -------------------------------------------------------------------------------- 1 | # Licenses 2 | -------------------------------------------------------------------------------- /src/llvm-coverage-instrumentation.md: -------------------------------------------------------------------------------- 1 | # LLVM Source-Based Code Coverage 2 | -------------------------------------------------------------------------------- /src/lowering.md: -------------------------------------------------------------------------------- 1 | # Lowering 2 | 3 | Lowering 步骤将 AST 转换为 [HIR](hir.html)。 4 | 这意味着许多在类型分析或类似的语法无关分析中没有用的代码在这一阶段被删除了。 5 | 这种结构的例子包括但不限于 6 | 7 | * 括号 8 | * 无需替换,直接删除,树结构本身就能明确运算顺序 9 | * `for` 循环和 `while (let)` 循环 10 | * 转换为 `loop` + `match` 和一些 `let` binding 11 | * `if let` 12 | * 转换为 `match` 13 | * Universal `impl Trait` 14 | * 转换成范型参数(会添加flag来标志这些参数不是由用户写的) 15 | * Existential `impl Trait` 16 | * 转换为虚拟的 `existential type` 声明 17 | 18 | Lowering 需要遵守几点,否则就会违反 `src/librustc_middle/hir/map/hir_id_validator.rs` 中的制定的检查规则: 19 | 20 | 1. 如果创建了一个`HirId`,那就必须使用它。 21 | 因此,如果您使用了 `lower_node_id`,则*必须*使用生成的 `NodeId` 或 `HirId`(两个都可以,因为检查 `HIR` 中的 `NodeId` 时也会检查是否存在现有的 `HirId`) 22 | 2. Lowering `HirId` 必须在对 item 有所有权的作用域内完成。 23 | 这意味着如果要创建除当前正在 Lower 的 item 之外的其他 item,则需要使用 `with_hir_id_owner`。 24 | 例如,在lower existential的 `impl Trait` 时会发生这种情况. 25 | 3. 即使其 `HirId` 未使用,要放入HIR结构中的 `NodeId` 也必须被 lower。 26 | 此时一个合理的方案是调用 `let _ = self.lower_node_id(node_id);`。 27 | 4. 如果要创建在 `AST` 中不存在的新节点,则*必须*通过调用 `next_id` 方法为它们创建新的 ID。 28 | 该方法会生成一个新的 `NodeId` 并自动为您 lowering 它,以便您获得 `HirId`。 29 | 30 | 如果您要创建新的 `DefId` ,由于每个 `DefId` 需要具有一个对应的 `NodeId`,建议将这些 `NodeId` 添加到 `AST` 中,这样您就不必在lowering时生成新的`DefId`。 31 | 这样做的好处是创建了一种通过 `NodeId` 查找某物的 `DefID` 的方法。 32 | 如果 lower 操作需要在多个位置使用该 `DefId`,则不能在所有这些位置生成一个新的 `NodeId`,因为那样的话,您将获得多余的的 `DefId`。 33 | 而对于来自AST的`NodeId`来说,这些就都不是问题了。 34 | 35 | 有一个 `NodeId` 也使得 `DefCollector` 可以生成 `DefId`,而不需要立即进行操作。 36 | 将 `DefId` 的生成集中在一个地方可以使重构和理解变得更加容易。 -------------------------------------------------------------------------------- /src/macro-expansion.md: -------------------------------------------------------------------------------- 1 | # 宏展开 2 | 3 | 4 | 5 | > `rustc_ast`, `rustc_expand`, 和 `rustc_builtin_macros` 都在重构中,所以本章节中的部分链接可能会打不开。 6 | 7 | Rust 有一个非常强大的宏系统。在之前的章节中,我们了解了解析器(parser)如何预留要展开的宏(使用临时的[占位符][placeholders] )。这个章节将介绍迭代地展开这些宏的过程,直到我们的 crate 会有一个完整的 AST,且没有任何未展开的宏(或编译错误)。 8 | 9 | [placeholders]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/placeholders/index.html 10 | 11 | 首先,我们将讨论宏展开和集成并输出到 ASTs 中的算法。随后,我们将看到健全的(hygiene)数据是如何被收集的。最后,我们将研究展开不同种类宏的细节。 12 | 13 | 非常多的算法和数据结构都在 [`rustc_expand`] 中,基础数据结构在 [`rustc_expand::base`][base] 中。 14 | 15 | 还要注意的是,`cfg` 和 `cfg_attr` 是其他宏中被特殊处理的,并在 [`rustc_expand::config`][cfg] 中处理。 16 | 17 | [`rustc_expand`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/index.html 18 | [base]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/base/index.html 19 | [cfg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/config/index.html 20 | 21 | ## 展开和 AST 集成 22 | 23 | 首先,展开是发生在 crate 层面。给定一个 crate 的原始代码,编译器将生成一个包含所有宏展开、所有模块内联、等的巨大的 AST。这个过程的主要入口是在 [`MacroExpander::fully_expand_fragment`][fef] 方法中。除了少数例外情况,我们整个 crate 上都使用这个方法(获得更详细的关于边缘案例的扩展的讨论,请参考 ["eager-expansion"](#eager-expansion),)。 24 | 25 | [`rustc_builtin_macros`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_builtin_macros/index.html 26 | [reb]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/build/index.html 27 | 28 | 29 | 在更高层次上,[`fully_expand_fragment`][fef] 在迭代(反复)运行的,我们将保留一个未解析的宏调用队列(即尚未找到定义的宏)。我们反复地在队列中选择一个宏,对其进行解析,扩展,并将其集成回去。如果我们无法在迭代中取得进展,这代表着存在编译错误。算法如下 [algorithm][original]: 30 | 31 | [fef]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/expand/struct.MacroExpander.html#method.fully_expand_fragment 32 | [original]: https://github.com/rust-lang/rust/pull/53778#issuecomment-419224049 33 | 34 | 0. 初始化一个队列(`queue`)用于保存未解析的宏调用。 35 | 1. 反复直到队列(`queue`)晴空(或者没有任何进展,即有错误) 36 | 0. 尽可能地在我们已部分构建的 create 中[解析(Resolve)](./name-resolution.md) 导入(imports)。 37 | 1. 从我们部分已构建的 crate (类似方法、属性、派生)中尽可能多得收集宏[`调用`][inv],并将它们添加到队列中。 38 | 2. 将第一元素从队列中取出,并尝试解析它。 39 | 3. 如果它被成功解析: 40 | 0. 运行宏扩展器(macro's expander)函数,该函数消费(consumes)一个 [`TokenStream`] 或 AST 并生成一个 [`TokenStream`]或 [`AstFragment`] (取决于宏的种类). (`TokenStream`是一个[`TokenTree`s][tt] 的集合, 41 | 每一个都是一个 token (标点、标识符或文字)或被分隔的组合(在`()`/`[]`/`{}`中的任何内容) 42 | 现在,我们以及知道了宏本身的一切,并且可以调用 `set_expn_data` 去填满全局数据重的属性;这是与 `ExpnId` 相关的 hygiene data 。(见[下文"hygiene"章节][hybelow]) 43 | 1. 将 AST 集成到一个现有的大型的 AST 中。从本质上讲,这是“类似 token 的块” 变成适当的固定的 AST 并带有 side-tables。 44 | 它的发生过程如下: 45 | - 如果宏产生 tokens(例如 proc macro),我们将其解析为 AST ,这可能会产生解析错误。 46 | - 在展开的过程中,我们构建 `SyntaxContext`s (hierarchy 2). (见[下文"hygiene"章节][hybelow]) 47 | - 这三个过程在每个刚从宏展开的 AST 片段上依次地发生: 48 | - [`NodeId`]s 由[`InvocationCollector`] 分配的。这还会从新的 AST 片段中收集新的宏调用,并将它们添加到队列中。 49 | - ["Def paths"][defpath] 被创建,同时 [`DefId`]s 由 50 | [`DefCollector`] 分配的。 51 | - 名字由 [`BuildReducedGraphVisitor`] 放入模块中(从解析器(resolver's)的角度来看)。 52 | 53 | 2. 在展开单个宏并集成输出后,继续执行 54 | [`fully_expand_fragment`][fef] 的下一个迭代。 55 | 4. 如果它没有被成功解析: 56 | 0. 将宏放回队列中 57 | 1. 继续下一个迭代。 58 | 59 | [defpath]: https://rustc-dev-guide.rust-lang.org/hir.html?highlight=def,path#identifiers-in-the-hir 60 | [`NodeId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/node_id/struct.NodeId.html 61 | [`InvocationCollector`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/expand/struct.InvocationCollector.html 62 | [`DefId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/def_id/struct.DefId.html 63 | [`DefCollector`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/def_collector/struct.DefCollector.html 64 | [`BuildReducedGraphVisitor`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/build_reduced_graph/struct.BuildReducedGraphVisitor.html 65 | [hybelow]: #hygiene-and-hierarchies 66 | [tt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/tokenstream/enum.TokenTree.html 67 | [`TokenStream`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/tokenstream/struct.TokenStream.html 68 | [inv]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/expand/struct.Invocation.html 69 | [`AstFragment`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/expand/enum.AstFragment.html 70 | 71 | ### 错误恢复 72 | 73 | 如果我们在一次迭代中没有取得任何进展,那么我们就遇到了编译错误(例如一个未定义的宏或导入)。为了进行诊断,我们尝试从错误(未解析的宏或导入)中恢复。这允许编译在第一个错误之后继续进行,这样我们就可以一次报告更多错误。恢复不能使得编译通过。我们知道在这一节点上它会失败。恢复是通过将未成功解析的宏展开为 [`ExprKind::Err`][err] 来实现的。 74 | 75 | [err]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/enum.ExprKind.html#variant.Err 76 | 77 | ### 名称解析 78 | 79 | 注意,这里涉及到名称解析:我们需要解析上述算法中的导入和宏名。这在 [`rustc_resolve::macros`][mresolve] 中完成,它解析宏路径,验证这些解析,并报告各种错误(例如:“未找到”或“找到了,但它不稳定(unstable)”或“预期的x,但发现的y”)。但是,我们还没有尝试解析其他名称。这将在后面发生,我们将在[下一章](./name-resolution.md)中看到。 80 | 81 | [mresolve]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/macros/index.html 82 | 83 | ### Eager Expansion 84 | 85 | _Eager expansion_ 代表着我们在展开宏调用之前,先展开宏调用的参数。这仅对少数需要文字的特殊内置宏实现;首先对其中的一些宏展开参数可以获得更流畅的用户体验。作为一个例子,请考虑下属情况: 86 | 87 | ```rust,ignore 88 | macro bar($i: ident) { $i } 89 | macro foo($i: ident) { $i } 90 | 91 | foo!(bar!(baz)); 92 | ``` 93 | 94 | lazy expansion 会首先扩展 `foo!` ,eager expansion 会扩展 `bar!`。 95 | 96 | Eager expansion 不是一个普遍的(通用的) Rust 特性(feature)。实现更加普遍的 eager expansion 是具有挑战性的,但是为了用户体验,我们为一些内置宏实现了它(eager expansion)。内置宏是在 [`rustc_builtin_macros`] 实现,还有一些其他早期的代码生成工具,例如注入标准库的导入或生成测试的工具。在 [`rustc_expand::build`] 有一些额外的帮助工具来构建 AST 片段(fragments)。Eager expansion 通常执行 lazy (normal) expansion 来展开子集。它是通过只在一个部分的 crate 的上来调用 [`fully_expand_fragment`][fef] 来完成的。(与我们通常使用整个 crate 来调用相反)。 97 | 98 | ### 其他数据结构 99 | 100 | 以下是涉及到扩展和扩展的其他重要数据结构 101 | - [`ResolverExpand`] - 一个用来阻隔(break)crate 的依赖的 trait。这允许解析服务在 [`rustc_ast`] 中使用,虽然 [`rustc_resolve`] 和 几乎所有其他的东西都依赖于 [`rustc_ast`] 。 102 | - [`ExtCtxt`]/[`ExpansionData`] - 用来保存在处理过程中各种中间数据。 103 | - [`Annotatable`] - 可以作为属性目标的 AST 片段。几乎和 AstFragment 相同,除了类型和可以由宏生成但不能用属性注释。 104 | - [`MacResult`] - 一个“多态的” AST 片段,可以根据他的 [`AstFragmentKind`](item、expression、pattern)转换成不同的 `AstFragment`。 105 | 106 | 107 | [`rustc_ast`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/index.html 108 | [`rustc_resolve`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/index.html 109 | [`ResolverExpand`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/base/trait.ResolverExpand.html 110 | [`ExtCtxt`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/base/struct.ExtCtxt.html 111 | [`ExpansionData`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/base/struct.ExpansionData.html 112 | [`Annotatable`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/base/enum.Annotatable.html 113 | [`MacResult`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/base/trait.MacResult.html 114 | [`AstFragmentKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/expand/enum.AstFragmentKind.html 115 | 116 | ## hygiene 和结构层次 117 | 118 | 如果您曾使用过 C/C++ 预处理器宏,就会知道有一些烦人的、难以调试的陷阱!例如,考虑以下代码: 119 | 120 | ```c 121 | #define DEFINE_FOO struct Bar {int x;}; struct Foo {Bar bar;}; 122 | 123 | // Then, somewhere else 124 | struct Bar { 125 | ... 126 | }; 127 | 128 | DEFINE_FOO 129 | ``` 130 | 131 | 大多数人都避免这样写 C - 因为他无法通过编译。宏定义的 `struct Bar` 与代码中的结构 `struct Bar` 定义冲突。请再考虑以下代码: 132 | 133 | 134 | ```c 135 | #define DO_FOO(x) {\ 136 | int y = 0;\ 137 | foo(x, y);\ 138 | } 139 | 140 | // Then elsewhere 141 | int y = 22; 142 | DO_FOO(y); 143 | ``` 144 | 145 | 你看到任何问题了吗?我们想去生成调用 `foo(22, 0)` 但是我们得到了 `foo(0, 0)` ,因为在宏中已经定义了 `y`! 146 | 147 | 这两个都是 _macro hygiene_ 问题的例子。 _Hygiene_ 关于如何处理名字定义在宏中。特别是,一个健康的宏系统可以防止由于宏中引入的名称而产生的错误。Rust 宏是卫生的(hygienic),因为不允许编写上述的 bugs。 148 | 149 | 在更高层次上,rust 编译器的卫生(hygiene)性是通过跟踪定义(引入)和使用名称的上下文来保证的。然后我们可以根据上下文消除名字的歧义。宏系统未来的迭代将允许宏的编写者更好地控制该上下文。例如宏的编写者可能想在宏调用的上下文中定义(引入)一个新的名称。另一种情况是,宏的编写者只在宏的作用域内使用变量(也就是说在宏的外部不可见)。 150 | 151 | 152 | [code_dir]: https://github.com/rust-lang/rust/tree/master/compiler/rustc_expand/src/mbe 153 | [code_mp]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/mbe/macro_parser 154 | [code_mr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/mbe/macro_rules 155 | [code_parse_int]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/mbe/macro_parser/fn.parse_tt.html 156 | [parsing]: ./the-parser.html 157 | 158 | 上下文被添加到 AST 节点。所有由宏生成的 AST 节点都附加了上下文。此外,可能还有些具有上下文的节点,例如一些解析语法糖(非宏展开节点被认为只有 root 上下文,将在后面阐述)。这个编译器,我们使用 [`rustc_span::Span`s][span] 定位代码的位置。这个结构同样有卫生(hygiene)性信息,我们将在后面看到。 159 | 160 | [span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html 161 | 162 | 因为宏调用和定义可以是嵌套的,所以节点的语法上下文也必须是有层次的。比如说,如果我们扩展一个宏,有一个宏调用或者定义在生成的输出中,那么语法上下文应该反映出嵌套。 163 | 164 | 然而,事实证明,出于不同目的,我们实际上需要跟踪一些类型的上下文。因此一个 crate 的卫生(hygiene)信息不只是由一个而是由三个扩展层次构成的。 165 | 166 | 所有层次结构都需要某种 "macro ID" 来标识展开链中的单个元素。这个 ID 是 [`ExpnId`]。所有的宏收到一个整数 ID ,当我们发现新的宏调用时,从 0 开始自增。所有层次结构都是从 [`ExpnId::root()`][rootid] 开始的(当前层次的父节点)。 167 | 168 | [`rustc_span::hygiene`][hy] 包含了所有卫生(hygiene)相关的算法([`Resolver::resolve_crate_root`][hacks] 中的一些 hacks 在除外)和卫生(hygiene)相关的数据结构,这些结构都保存在全局数据中。 169 | 170 | 实际的层次结构存储在 [`HygieneData`][hd] 中。这是一个全局数据,包含将装修和展开信息,可以从任意的 [`Ident`] 访问,无需任何上下文。 171 | 172 | [`ExpnId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.ExpnId.html 173 | [rootid]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.ExpnId.html#method.root 174 | [hd]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.HygieneData.html 175 | [hy]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/index.html 176 | [hacks]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/struct.Resolver.html#method.resolve_crate_root 177 | [`Ident`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html 178 | 179 | ### 展开顺序层次结构 180 | 181 | 第一,层次结构将跟踪展开的顺序,即宏调用出现在另一个宏的输出中。 182 | 183 | 在这里,层次结构中的子元素将被标记为“最内层的”,[`ExpnData`] 结构自身包含宏定义和宏调用的属性子集,这些属性是全局可用的。[`ExpnData::parent`][edp] 在当前层次结构中,跟踪 子节点 -> 父节点的链接。 184 | 185 | 186 | [`ExpnData`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.ExpnData.html 187 | [edp]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.ExpnData.html#structfield.parent 188 | 189 | 例如 190 | 191 | ```rust,ignore 192 | macro_rules! foo { () => { println!(); } } 193 | 194 | fn main() { foo!(); } 195 | ``` 196 | 197 | 在代码中,AST 节点最终会生成以下层次结构。 198 | 199 | ``` 200 | root 201 | expn_id_foo 202 | expn_id_println 203 | ``` 204 | 205 | ### 宏定义的结构层次 206 | 207 | 第二,层次结构将跟踪宏定义的顺序。即我们展开一个宏,在其输出中出现另一个宏定义。这个层次结构比其他两个结构层次更复杂,更棘手。 208 | 209 | [`SyntaxContext`][sc] 通过 ID 表示此层次结构中的整个链。[`SyntaxContextData`][scd] 包含了与给定的 210 | `SyntaxContext` 相关的数据;大多数情况下,它是一个缓存,用于以不同方式过滤该链的结果。[`SyntaxContextData::parent`][scdp] 是此处 子节点-> 父节点 的链接,[`SyntaxContextData::outer_expns`][scdoe] 是链中的各个元素。“链接运算符”在编译器代码中是[`SyntaxContext::apply_mark`][am]。 211 | 212 | 上述提到的 [`Span`][span] 实际上只是代码位置和 `SyntaxContext` 的紧凑表现。同样的,[`Ident`] 只是 213 | [`Symbol`] + `Span`(即一个被替换的字符串+健全性数据) 214 | 215 | 216 | [`Symbol`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html 217 | [scd]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.SyntaxContextData.html 218 | [scdp]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.SyntaxContextData.html#structfield.parent 219 | [sc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.SyntaxContext.html 220 | [scdoe]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.SyntaxContextData.html#structfield.outer_expn 221 | [am]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.SyntaxContext.html#method.apply_mark 222 | 223 | 对于内置宏,我们使用 `SyntaxContext::empty().apply_mark(expn_id)` 上下文,这样的宏是被认为是定义在 root 层次结构纸上。我们为 proc-macros 做一样的事,因为我们还没有实现跨 crate 并保证其卫生(hygiene)。 224 | 225 | 如果 token 在宏生成之前有上下文 `X` ,那么在宏生成后上下文会有 `X -> macro_id`。以下是一些例子: 226 | 227 | Example 0: 228 | 229 | ```rust,ignore 230 | macro m() { ident } 231 | 232 | m!(); 233 | ``` 234 | 这里 `ident` 有最初的上下文 [`SyntaxContext::root()`][scr]。在 `m` 生成后,`ident` 会有上下文 `ROOT -> id(m)`。 235 | 236 | [scr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.SyntaxContext.html#method.root 237 | 238 | 239 | Example 1: 240 | 241 | ```rust,ignore 242 | macro m() { macro n() { ident } } 243 | 244 | m!(); 245 | n!(); 246 | ``` 247 | 248 | 这个例子中,`ident` 有最初的 `ROOT` ,在第一个宏被展开后上下文变为 ` ROOT -> id(m)` ,继续展开后得到上下文 `ROOT -> id(m) -> id(n)`。 249 | 250 | Example 2: 251 | 252 | 注意,这些链并不完全由他们最后的一个元素决定,换句话来说 `ExpnId` 和 `SyntaxContext` 不是同构的。 253 | 254 | ```rust,ignore 255 | macro m($i: ident) { macro n() { ($i, bar) } } 256 | 257 | m!(foo); 258 | ``` 259 | 260 | 在所有展开后,`foo` 有上下文 `ROOT -> id(n)` ,`bar` 有上下文 261 | `ROOT -> id(m) -> id(n)`。 262 | 263 | 最后要提的一点是,目前的结构层次受限于 ["context transplantation hack"][hack] 。基本上,更现代(实现性的)宏(`macro`) 比旧的 MBE 系统有更强的卫生(hygiene)性,但这可能导致两者之间奇怪的交互。这种 hack 实现是为了让所有事暂时“正常工作”。 264 | 265 | [hack]: https://github.com/rust-lang/rust/pull/51762#issuecomment-401400732 266 | 267 | ### 调用的结构层次 268 | 269 | 第三也是最后一个,结构层次是跟踪宏调用的位置。 270 | 271 | 在结构层次 [`ExpnData::call_site`][callsite] 中是 子节点 -> 父节点 的链接。 272 | 273 | [callsite]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/hygiene/struct.ExpnData.html#structfield.call_site 274 | 275 | 这里有一个例子: 276 | 277 | ```rust,ignore 278 | macro bar($i: ident) { $i } 279 | macro foo($i: ident) { $i } 280 | 281 | foo!(bar!(baz)); 282 | ``` 283 | 284 | 对于 `baz` AST 节点是最后输出的,第一个结构层次是 `ROOT -> 285 | id(foo) -> id(bar) -> baz` ,而第三结构层次是 `ROOT -> baz`。 286 | 287 | ### 宏回溯 288 | 289 | 在 [`rustc_span`] 中实现了宏回溯,其使用了 [`rustc_span::hygiene`][hy] 的健全机制。 290 | 291 | [`rustc_span`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/index.html 292 | 293 | ## 产生宏输出 294 | 295 | 上述内容中我们看到了中的宏的输出如何被集成到用于 crate 的 AST 中,我们还看到了如何为一个 crate 生成卫生(hygiene)数据。但是我们如何实际产生一个宏的输出呢?这将取决于宏的类型。 296 | 297 | Rust 中有两种类型的宏:`macro_rules!` 类型(或称 示例宏( Macros By Example,MBE))和过程宏(procedural macros)(或 proc macros;包括自定义派生)。在解析阶段,正常的 Rust 解析器将保留宏及其调用内容。稍后将使用这部分代码将宏展开。 298 | 299 | 这里有一些重要的数结构和接口: 300 | - [`SyntaxExtension`] - 一个更底层的宏表示,包含了它扩展函数,他将一个 token 流(`TokenStream`)或 AST 转换成另一个 `TokenStream` 或 AST 加上一些额外信息,例如稳定性,或在宏内允许使用的不稳定特性的列表。 301 | - [`SyntaxExtensionKind`] - 展开方法可能会有很多不同的函数签名(接受一个 token 流,或者两个;或者接受一部分 AST 等等)。这是一个列出他们的枚举。 302 | - [`ProcMacro`]/[`TTMacroExpander`]/[`AttrProcMacro`]/[`MultiItemModifier`] - traits 用于标识展开函数的签名 303 | 304 | [`SyntaxExtension`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/base/struct.SyntaxExtension.html 305 | [`SyntaxExtensionKind`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/base/enum.SyntaxExtensionKind.html 306 | [`ProcMacro`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/base/trait.ProcMacro.html 307 | [`TTMacroExpander`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/base/trait.TTMacroExpander.html 308 | [`AttrProcMacro`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/base/trait.AttrProcMacro.html 309 | [`MultiItemModifier`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/base/trait.MultiItemModifier.html 310 | 311 | ## 示例宏(Macros By Example) 312 | 313 | MBEs 有自己等等解析器,不同于普通的 Rust 解析器。当宏展开时,我们可以调用 MBE 解析器去解析和展开宏。反过来,MBE 解析器在解析宏调用的内容时需要绑定元变量(例如`$my_expr`),这可能会调用普通的 Rust 解析器。宏展开的代码在 [`compiler/rustc_expand/src/mbe/`][code_dir] 314 | 315 | ### 示例 316 | 317 | 有个例子供参考提供是有助的。在本章的其他部分,每当我们提到 "示例 _定义_" 时,我们指得失以下内容: 318 | 319 | ```rust,ignore 320 | macro_rules! printer { 321 | (print $mvar:ident) => { 322 | println!("{}", $mvar); 323 | }; 324 | (print twice $mvar:ident) => { 325 | println!("{}", $mvar); 326 | println!("{}", $mvar); 327 | }; 328 | } 329 | ``` 330 | 331 | `$mvar` 是一个 _元变量_ 。与正常的变量不同,元变量不是绑定到计算中的值,而是在 _编译时_ 绑定到 _tokens_ 树。 _token_ 是一个单独的语法“单元”,例如标识符(例 `foo`)或者标点符号(例 `=>`)。还有其他特殊的 tokens,例如 `EOF` 他表示没有其他更多的 tokens。Token 树由类似成对的圆括号的字符(`(`...`)`, 332 | `[`...`]`, 和 `{`...`}`) - 他们包括了 open 和 close,以及它们之间的所有标记(我们确实要求类似括号的字符需要保持要平衡)。让宏展开操作 token 流而不是源文件的原始字节,从而减少复杂性。宏扩展器(以及编译器的其余大多数)实际上并不十分在乎代码中某些语法构造的确切行和列。它只关心代码中使用了哪些构造。使用 tokens 使得我们可以关心 _什么_ 而不必担心在 _哪里_ ,关于 tokens 跟多内容,可以参考本书 [Parsing][parsing] 一章。 333 | 334 | 当我们提到 “示例 _调用_” ,我们指以下代码片段: 335 | 336 | ```rust,ignore 337 | printer!(print foo); // Assume `foo` is a variable defined somewhere else... 338 | ``` 339 | 340 | 将宏调用展开为语法树的过程 `println!("{}", foo)` ,然后展开成 341 | `Display::fmt` 调用成为 _宏展开_ ,是本章的主题。 342 | 343 | ### 示例宏 (MBE) 解析器 344 | 345 | MBE 展开包括两个部分:解析定义和解析调用。有趣的是,两者都是由宏解析器完成的。 346 | 347 | 基本上,MBE 解析器类似于基于 NFA 的正则解析器。它使用的算法本质上类似于 [Earley parsing 348 | algorithm](https://en.wikipedia.org/wiki/Earley_parser) 。 宏解析器定义在 [`compiler/rustc_expand/src/mbe/macro_parser.rs`][code_mp]。 349 | 350 | 宏解析器的接口如下(稍作简化): 351 | 352 | ```rust,ignore 353 | fn parse_tt( 354 | parser: &mut Cow, 355 | ms: &[TokenTree], 356 | ) -> NamedParseResult 357 | ``` 358 | 359 | 我们在宏解析器中使用这些项: 360 | - `parser` 是一个对普通 Rust 解析器的引用,包括了 token 流和解析会话(parsing session)。Token 流是我们将请求 MBE 解析器解析的内容。我们将使用原始的 token 流,将元变量绑定到对应的 token 树。解析会话(parsing session)可用于报告解析器错误。 361 | - `ms` 是一个 _匹配器_ 。这是一个 token 树序列,我们希望以此来匹配 token 树。 362 | 363 | 类似于正则解析器,token 流是输入,我们将其与 pattern `ms` 匹配。使用我们的示例,token 流可以是包含示例 _调用_ `print foo` 内部的 token 流,`ms` 可以是 token(树)`print $mvar:ident`。 364 | 365 | 解析器的输出是 `NamedParseResult`,它指示发生了三种情况中的哪一种: 366 | - 成功:token 流匹配给定的匹配器 `ms`,并且我们已经产生了从元变量到响应令牌树的绑定。 367 | - 失败:token 流与 `ms` 不匹配。浙江导致出现错误消息,例:“No rule expected token _blah_” 368 | - 错误: 解析器中发生了一些致命的错误。例如,如果存在多个模式匹配,则会发生这种情况,因为这表明宏不明确。 369 | 370 | 所有的接口定义在 [这里][code_parse_int]。 371 | 372 | 宏解析器的工作与普通的正则解析器几乎相同,只有一个例外:为了解析不通的元变量,例如`ident`, `block`, `expr` 等,宏解析器有时候必须回调到普通的 Rust 解析器。 373 | 374 | 如上所述,宏的定义和调用都使用宏解析器进行解析。这是非常不直观和自引用的。 375 | 解析宏的代码定义在 [`compiler/rustc_expand/src/mbe/macro_rules.rs`][code_mr] 中。它定义用于匹配宏定义模式为 `$( $lhs:tt => $rhs:tt );+` 。换句话说,一个 `macro_rules` 定义在其主体中应最少出现一个 token 树,后面跟着 `=>`,然后是另一个 token 树。当编译器遇到 `macro_rules` 定义时,它使用这个模式来匹配定义中每个规则的两个 token 树, _并使用宏解析器本身_ 。在示例定义中,元变量 `$lhs` 将会匹配 partten `(print $mvar:ident)` 和 `(print twice $mvar:ident)`。 `$rhs` 将匹配 `{ println!("{}", $mvar); }` 和 `{ 376 | println!("{}", $mvar); println!("{}", $mvar); }` partten 的主体。解析器将保留这些内容,以便在需要展开宏调用时使用。 377 | 378 | 当编译器遇到宏调用时,它会使用上述基于 NFA 的宏解析器解析该调用。但是,使用的匹配器是从宏定义的 arms 中提取的第一个 token 树(`$lhs` ),使用我们的示例,我们尝试匹配 token 流中的 `print foo` (来自匹配器的) `print $mvar:ident` 和从前面定义中提取的 `print twice $mvar:ident`。算法是完全相同的,但是当宏解析器在当前匹配其中需要匹配非 _non-terminal_ (例如 `$mvar:ident`) 时,它会回调正常的 Rust 解析器以获取该非终结符的内容。这种情况下,Rust 会寻找一个 `ident` token,它会找到 `foo` 并返回给宏解析器。然后,宏解析器照常进行解析。另外,请注意来自于不同 arms 的匹配器应该恰好有一个匹配调用;如果有多个匹配项,则该解析有二义性,而如果根本没有匹配项,则存在语法错误。 379 | 380 | 跟多关于解析器实现的信息请参考 [`compiler/rustc_expand/src/mbe/macro_parser.rs`][code_mp] 381 | 382 | ### `macro`s and Macros 2.0 383 | 384 | 改进 MBE 系统,为它提供更多与卫生(hygiene)性相关的功能,更好的范围和可见性规则等,这是一个古老的,几乎没有文献记载的工作。不幸的是,最近在这方面还没有进行很多工作。 在内部,宏使用与当今的 MBE 相同的机制。 它们只是具有附加的语法糖,并且允许在名称空间中使用。 385 | 386 | ## 过程(Procedural)宏 387 | 388 | 如上所述,过程宏也在解析过程中进行了扩展。 但是,它们使用了一种完全不同的机制。 过程宏不是作为编译器中的解析器,而是作为自定义的第三方 crate 实现的。 编译器将在其中编译 proc macro crate 和带有特殊注释的函数(即 proc macro 本身),并向它们传递 tokens 流。 389 | 390 | 然后 proc macro 可以转换 token 流和输出新的 token 流,该 token 流被合称为 AST。 391 | 392 | 值得注意的是,proc macros 使用的 token 流类型是 _稳定的_ ,因此`rustc` 不在内部使用它(因为内部数据结构是不稳定的)。 像以前一样,编译器的 token 流为 [`rustc_ast::tokenstream::TokenStream`][rustcts]。 这将转换为稳定的 [`proc_macro::TokenStream`][stablets] 并返回 [`rustc_expand::proc_macro`][pm] 和[`rustc_expand::proc_macro_server`][pms] 。 因为 Rust ABI 不稳定,所以我们使用 C ABI 进行转换。 393 | 394 | 395 | [tsmod]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/tokenstream/index.html 396 | [rustcts]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/tokenstream/struct.TokenStream.html 397 | [stablets]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html 398 | [pm]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/proc_macro/index.html 399 | [pms]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/proc_macro_server/index.html 400 | 401 | TODO: more here. 402 | 403 | ### Custom Derive 404 | 405 | 自定义派生是 proc macro 的一种特殊类型。 406 | 407 | TODO: more? 408 | -------------------------------------------------------------------------------- /src/memory.md: -------------------------------------------------------------------------------- 1 | # Rustc 中的内存管理 2 | 3 | Rustc 在内存管理方面相当谨慎。编译器在整个编译过程中需要分配 _大量_ 的数据结构,如果我们不够谨慎,这将会耗费大量时间和空间。 4 | 5 | 使用 arenas 和 interning 是编译器管理内存的主要方式之一。 6 | 7 | ## Arenas 和 Interning 8 | 9 | 在编译期间我们需要创建大量的数据结构。出于对性能的考虑,我们通常从全局内存池中分配这些数据结构; 每个数据结构都从一个长期 *arena* 中分配一次。这就是所谓的 _arena allocation_。这个系统减少了内存的分配/释放。它还允许简单地比较类型是否相等: 对每个 interned 类型 `X` 实现了 [`X` 的 `PartialEq`][peqimpl],因此我们只比较指针就可以判断是否相等。 [`CtxtInterners`] 类型包含一系列 interned 类型和 arena 本身的映射。 10 | 11 | [peqimpl]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html#implementations 12 | [`CtxtInterners`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.CtxtInterners.html#structfield.arena 13 | 14 | ### 例: `ty::TyS` 15 | 16 | 以 [`ty::TyS`] 为例,它表示编译器中的类型(在[这里](./ty.md)了解更多)。每当我们想要构造一个类型时,编译器都不会傻乎乎地直接从缓冲区分配。相反,编译器检查是否构造过该类型。如果构造过的话,只需要获取一个指向之前构造个的类型的指针,否则,就会创建一个新的指针。对于这个设计,如果想知道两种类型是否相同,只需要比较两个指针。`TyS` 是精心设计的,所以你永远无法在栈上构造 `TyS`。你只能从这个 arena 分配并 intern `TyS`,所以它是独一无二的。 17 | 18 | 在编译开始时,我们会创建一个缓冲区,每当需要分配一个类型时,就从缓冲区中使用这些类型。如果用完了,就会再创建一个。缓冲区的生命周期为 `'tcx` 。我们的类型绑定到该生命周期,因此当编译完成时,与该缓冲区相关的所有内存都被释放,`'tcx` 的引用将无效。 19 | 20 | 除了类型之外,还可以分配很多其它的 arena-allocated 数据结构,这些数据结构可以在该模块中找到。以下是一些例子: 21 | 22 | - [`Substs`][subst],分配给 `mk_substs` – 这会 intern 一个切片类型,通常用于指定要替换泛型的值(例如 `HashMap` 将被表示为切片 `&'tcx [tcx.types.i32, tcx.types.u32]`)。 23 | - [`TraitRef`],通常通过值传递 – 一个 **trait 引用** 包含一个引用的 trait 及其各种类型参数(包括 `Self`),如 `i32: Display` (这里 def-id 会引用 `Display` trait,并且子类型包含 `i32`)。 注意 `def-id` 的定义及讨论在 `AdtDef and DefId` 部分。 24 | - [`Predicate`] 定义 trait 系统要保证的东西 (见 `traits` 模块)。 25 | 26 | [subst]: ./generic_arguments.html#subst 27 | [`TraitRef`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TraitRef.html 28 | [`Predicate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Predicate.html 29 | 30 | [`ty::TyS`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html 31 | 32 | ## tcx 和怎样使用生命周期 33 | 34 | `tcx`(“typing context”)是编译器中的中枢数据结构。它是用于执行各种查询的上下文。 `TyCtxt` 结构体定义了对这个共享上下文的引用: 35 | 36 | ```rust,ignore 37 | tcx: TyCtxt<'tcx> 38 | // ---- 39 | // | 40 | // arena lifetime 41 | ``` 42 | 43 | 如你所见,`TyCtxt` 类型使用生命周期参数。当你看到类似 `'tcx` 生命周期的引用时,你就知道它指的是 arena-allocated 的数据(或者说,数据的生命周期至少与 arenas 一样长)。 44 | 45 | ### 关于生命周期 46 | 47 | Rust 编译器是一个相当大的程序,包含大量的大数据结构(如 AST、 HIR 和类型系统),因此非常依赖于 arenas 和引用(references)来减少不必要的内存使用。这体现在使用插入编译器(例如 [driver](./rustc-driver.md))的方式上,倾向于使用“push”风格(回调)的 API ,而不是 Rust-ic 风格的“pull”风格(考虑 `Iterator` trait)。 48 | 49 | 编译器通过大量使用线程本地存储和 interning 来减少复制,同时也避免了无处不在的生命期而导致的用户不友好。[`rustc_middle::ty::tls`][tls] 模块用于访问这些线程局部变量,尽管你很少需要接触。 50 | 51 | [tls]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/tls/index.html -------------------------------------------------------------------------------- /src/method-lookup.md: -------------------------------------------------------------------------------- 1 | # Method Lookup 2 | -------------------------------------------------------------------------------- /src/mir/construction.md: -------------------------------------------------------------------------------- 1 | # MIR 的构建 2 | 3 | 从 [HIR] lower到 [MIR] 的过程会在下面(可能不完整)这些item上进行: 4 | 5 | * 函数体和闭包体 6 | * `static` 和 `const` 的初始化 7 | * 枚举 discriminant 的初始化 8 | * 任何类型的胶水和补充代码 9 | * Tuple 结构体的初始化函数 10 | * Drop 代码 (即没有手动调用的 `Drop::drop` 函数的对象的 `drop`) 11 | * 没有显式实现 `Drop` 的对象的 `drop` 12 | 13 | Lowering 是通过调用 [`mir_built`] 查询触发的。 14 | MIR 构建器实际上并不使用 HIR,而是对 [THIR] 中的表达式进行递归处理。 15 | 16 | Lowering会为函数签名中指定的每个参数创建局部变量。 17 | 接下来,它为指定的每个绑定创建局部变量(例如, `(a, b): (i32, String)`)产生3个绑定,一个用于参数,两个用于绑定。 18 | 接下来,它生成字段访问,该访问从参数读取字段并将其值写入绑定变量。 19 | 20 | 在解决了初始化之后,lowering为函数体递归生成 MIR( `Block` 表达式)并将结果写入 `RETURN_PLACE`。 21 | 22 | ## `unpack!` 所有东西 23 | 24 | 生成 MIR 的函数有两种模式。 25 | 第一种情况,如果该函数仅生成语句,则它将以基本块作为参数,这些语句应放入该基本块。 26 | 然后可以正常返回结果: 27 | 28 | ```rust,ignore 29 | fn generate_some_mir(&mut self, block: BasicBlock) -> ResultType { 30 | ... 31 | } 32 | ``` 33 | 34 | 但是还有其他一些函数会生成新的基本块。 35 | 例如,lowering 像 `if foo { 22 } else { 44 }` 这样的表达式需要生成一个小的“菱形图”。 36 | 在这种情况下,函数将在其代码开始处使用一个基本块,并在代码生成结束时返回一个(可能是)新的基本块。 37 | `BlockAnd` 类型用于表示此类情况: 38 | 39 | ```rust,ignore 40 | fn generate_more_mir(&mut self, block: BasicBlock) -> BlockAnd { 41 | ... 42 | } 43 | ``` 44 | 45 | 当您调用这些函数时,通常有一个局部变量 `block`,它实际上是一个“光标”。 它代表了我们要添加新的MIR的位置。 46 | 当调用 `generate_more_mir` 时,您会想要更新该光标。 47 | 您可以手动执行此操作,但这很繁琐: 48 | 49 | ```rust,ignore 50 | let mut block; 51 | let v = match self.generate_more_mir(..) { 52 | BlockAnd { block: new_block, value: v } => { 53 | block = new_block; 54 | v 55 | } 56 | }; 57 | ``` 58 | 59 | 因此,我们提供了一个宏,可让您通过如下方式完成更新: 60 | `let v = unpack!(block = self.generate_more_mir(...))`。 61 | 它简单地提取新的块并覆盖在 `unpack!` 中指明的变量 `block`。 62 | 63 | ## 将表达式 Lowering 到 MIR 64 | 65 | 本质上一个表达式可以有四种表示形式: 66 | 67 | * `Place` 指一个(或一部分)已经存在的内存地址(本地,静态,或者提升过的) 68 | * `Rvalue` 是可以给一个 `Place` 赋值的东西 69 | * `Operand` 是一个给像 `+` 这样的运算符或者一个函数调用的参数 70 | * 一个存放了一个值的拷贝的临时变量 71 | 72 | 下图简要描绘了这些表示形式之间的交互: 73 | 74 | 75 | 76 | [点此看更为详细的交互图](https://raw.githubusercontent.com/rust-lang/rustc-dev-guide/9a676ee3a4bc9d8d054efd1ff57fc15ce19c00bd/src/mir/mir_detailed.svg) 77 | 78 | 我们首先将函数体 lowering 到一个 `Rvalue`,这样我们就可以为 `RETURN_PLACE` 创建一个赋值, 79 | 这个 `Rvalue` 的 lowering 反过来会触发其参数的 `Operand` lowering(如果有的话) 80 | lowering `Operand` 会产生一个 `const` 操作数,或者移动/复制出 `Place`,从而触发 `Place` lowering。 81 | 如果 lowering 的表达式包含操作,则 lowering 到 `Place` 的表达式可以触发创建一个临时变量。 82 | 这是蛇咬自己的尾巴的地方,我们需要触发 `Rvalue` lowering,以将表达式的值写入本地变量。 83 | 84 | ## Operator lowering 85 | 86 | 内置类型的运算符不会 lower 为函数调用(这将导致无限递归调用,因为这些 trait 实现包含了操作本身)。 87 | 相反,对于这些类型已经存在了用于二元和一元运算符和索引运算的 `Rvalue`。 88 | 这些 `Rvalue` 稍后将生成为 llvm 基本操作或 llvm 内部函数。 89 | 90 | 所有其他类型的运算符都被 lower 为对运算符对应 trait 的实现中的的函数调用。 91 | 92 | 无论采用哪种 lower 方式,运算符的参数都会 lower 为`Operand`。 93 | 这意味着所有参数都是常量或者引用局部或静态位置中已经存在的值。 94 | 95 | ## 方法调用的 lowering 96 | 97 | 方法调用被降低到与一般函数调用相同的`TerminatorKind`。 98 | 在[MIR]中,方法调用和一般函数调用之间不再存在差异。 99 | 100 | ## 条件 101 | 102 | 不带字段变量的 `enum` 的 `if` 条件判断和 `match` 语句都会被lower为 `TerminatorKind::SwitchInt`。 103 | 每个可能的值(如果为 `if` 条件判断,则对应的值为 `0` 和 `1`)都有一个对应的 `BasicBlock`。 104 | 分支的参数是表示if条件值的 `Operand`。 105 | 106 | ### 模式匹配 107 | 108 | 具有字段的 `enum` 上的 `match` 语句也被 lower 为 `TerminatorKind::SwitchInt`,但是其 `Operand` 是一个 `Place`,可以在其中找到该值的判别式。 109 | 这通常涉及将判别式读取为新的临时变量。 110 | 111 | ## 聚合构造 112 | 113 | 任何类型的聚合值(例如结构或元组)都是通过 `Rvalue::Aggregate` 建立的。 114 | 所有字段都 lower 为 `Operator`。 115 | 从本质上讲,这等效于对每个聚合上的字段都会有一个赋值语句,如果必要的话还会再加上一个对 `enum` 的判别式的赋值。 116 | 117 | [MIR]: ./index.html 118 | [HIR]: ../hir.html 119 | [THIR]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/thir/index.html 120 | 121 | [MIR]: ./index.html 122 | [HIR]: ../hir.html 123 | [THIR]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/thir/index.html 124 | 125 | [`rustc_mir_build::thir::cx::expr`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/thir/cx/expr/index.html 126 | [`mir_built`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_build/build/fn.mir_built.html 127 | -------------------------------------------------------------------------------- /src/mir/dataflow.md: -------------------------------------------------------------------------------- 1 | # 数据流分析 2 | 3 | 4 | 5 | 如果您在MIR上进行开发,将会频繁遇到各种[数据流分析][wiki]。 6 | `rustc`通过数据流发现未初始化变量,确定生成器`yield`中的存活变量,以及在控制流图指定位置计算借用`Place`。 7 | 数据流分析在现代编译器中是一个基础概念,该主题的知识对潜在贡献者十分有帮助。 8 | 9 | 注意,本文档不是对数据流分析的一般介绍。只用于对`rustc`中分析框架的描述。 10 | 它假设读者熟悉一些核心思想和基本概念,比如转换函数(transfer function),不动点(fixpoint)和格(lattice)。 11 | 如果您不熟悉这些术语,或者想要快速复习,那么Anders Møller和Michael I. Schwartzbach撰写的[*静态程序分析*][*Static Program Analysis*]是一本很好的免费教材。 12 | 对于您喜欢视听学习,法兰克福歌德大学已经在[YouTube][goethe]上用英语发布了一系列简短的讲座,非常容易上手。 13 | 14 | ## 定义一个数据流分析 15 | 16 | 数据流分析的接口被分解成三个trait。首先是[`AnalysisDomain`],所有分析都必须实现它。 17 | 除了定义数据流状态类型,该trait还定义了每个基本块入口处状态的初始值,以及向前分析或者向后分析的方向。 18 | 数据流分析的域(domain)必须是一个有正确`join`操作符的[格][lattice] (严格来讲是连接语义)。 19 | 更多内容可以参考[`lattice`]和[`JoinSemiLattice`]的文档。 20 | 21 | 您必须提供一个[`Analysis`]的直接实现或者是一个[`GenKillAnalysis`]代理的实现。后者用于所谓的["gen-kill" problems],该问题可以通过一类简单的转换函数解决。 22 | 如果一个分析的域不是`BitSet`(位图),或者转换函数无法使用“gen”、“kill”操作实现,那么必须直接实现`Analysis`,这样的实现可能会变慢。 23 | 而所有的`GenKillAnalysis`将会通过默认的`impl`自动实现`Analysis`。 24 | 25 | 26 | ```text 27 | AnalysisDomain 28 | ^ 29 | | | = has as a supertrait 30 | | . = provides a default impl for 31 | | 32 | Analysis 33 | ^ ^ 34 | | . 35 | | . 36 | | . 37 | GenKillAnalysis 38 | 39 | ``` 40 | 41 | ### 转换函数与作用函数 42 | 43 | `rustc`的数据流分析框架允许基本块内的每条语句(包括终结语句terminator)定义自己的转换函数。 44 | 简便起见,这些独立的转换函数在下面被成为“作用函数”。 45 | 每个作用函数都以数据流顺序依次执行,并且它们共同定义了整个基本块的传递函数。 46 | 也可以为终结语句(terminator)的特定的传出边(译注:分支跳转的特定边)定义一个“作用函数”(例如[`apply_call_return_effect`]作用于`Call`指令的后继边), 47 | 这些被称为“单边作用”(per-edge effects)。 48 | 49 | `GenKillAnalysis`的方法与`Analysis`的方法之间唯一有意义的区别(除“apply”前缀之外)是`Analysis`对数据流状态可以直接修改, 50 | 而`GenKillAnalysis`仅能看到`GenKill` trait的实现者,即只允许`gen`和`kill`操作可变。 51 | 52 | ### “前序”作用 53 | 54 | 细心的读者可能会注意到,每个语句实际上都有两种可能的作用函数,即“前序”作用("before" effects)和“非前序”作用(主要作用,effects)。 55 | **无论分析的方向如何**,“前序”作用都会在主要作用函数之前应用。 56 | 换句话说,后向分析将在计算基本块的传递函数时先应用“前序”作用函数,然后调用“主要”作用函数,就像正向分析一样。 57 | 58 | 大多数分析仅使用“主要”作用,如果每条语句具有多个作用函数,会使得调用者难以抉择。 59 | 但是,“前序”作用在某些情况下很有用,例如当必须将赋值语句左右表达式分开考虑时。 60 | 61 | ### 终止条件 62 | 63 | 您的分析必须能收敛到不动点,否则会一直执行下去。 64 | 不动点的收敛是一种“达到平衡”的方式。为了达到平衡,您的分析必须满足某些定律。其中之一的定律是任意值与底值(bottom)结合等于该值,即满足如下等式: 65 | 66 | > *bottom* join *x* = *x* 67 | 68 | 另一条定律是您的分析需要有一个满足如下等式的顶值(top): 69 | 70 | > *top* join *x* = *top* 71 | 72 | 顶值可以确保半格的高度是有限的,并且上述的定律保证了一旦数据流状态到达顶值,将不在发生变化。 73 | 74 | ## 例子 75 | 76 | 本节提供了一个简单的数据流分析。它没有解释您需要了解的所有内容,但希望它将使本页面的其余部分更加清晰。 77 | 78 | 假设我们要做一个简单的分析,以确定程序中的某个点是否已经调用过`mem::transmute`。 79 | 我们的分析域将是一个布尔变量,它表示到目前为止是否已调用了`transmute`。 80 | 底值是`false`,因为初始情况下未调用`transmute`。 81 | 顶值是`true`,因为一旦我们确定调用了`transmute`,分析就完成了。 82 | 我们的join运算符是OR(||)运算符。使用OR而不是AND是由于存在以下场景: 83 | 84 | ``` 85 | let x = if some_cond { 86 | std::mem::transmute(0_i32); // transmute 被调用! 87 | } else { 88 | 1_u32; // transmute 未被调用 89 | }; 90 | 91 | // 此处 transmute 被调用了吗? 保守结果认为是true 92 | println!("x: {}", x); 93 | ``` 94 | 95 | ## 检查结果 96 | 97 | 如果您实现了一个分析,您必须将它传给引擎[`Engine`]。 98 | 这个功能通过定义`Analysis`中的[`into_engine`]函数,比`Engine::new_gen_kill`来构造效率更高。 99 | 100 | 调用`Engin`中的`iterate_to_fixpoint`可以返回`Results`,该结构中包含每个基本块进入时不动点的数据流状态。 101 | 当拥有一个`Results`结构,您可以在CFG的任意位置检查不动点的数据流状态。如果只需要少量位置的状态,可以使用[`ResultsCursor`]。 102 | 如果需要**每个**位置的状态,使用[`ResultsVisitor`]更加高效。 103 | 104 | ```text 105 | Analysis 106 | | 107 | | into_engine(…) 108 | | 109 | Engine 110 | | 111 | | iterate_to_fixpoint() 112 | | 113 | Results 114 | / \ 115 | into_results_cursor(…) / \ visit_with(…) 116 | / \ 117 | ResultsCursor ResultsVisitor 118 | ``` 119 | 120 | 下方是[`ResultsVisitor`]的示例代码: 121 | 122 | 123 | ```rust,ignore 124 | // Assuming `MyVisitor` implements `ResultsVisitor`... 125 | let mut my_visitor = MyVisitor::new(); 126 | 127 | // inspect the fixpoint state for every location within every block in RPO. 128 | let results = MyAnalysis::new() 129 | .into_engine(tcx, body, def_id) 130 | .iterate_to_fixpoint() 131 | .visit_in_rpo_with(body, &mut my_visitor); 132 | ``` 133 | 134 | [`ResultsCursor`]示例代码: 135 | 136 | ```rust,ignore 137 | let mut results = MyAnalysis::new() 138 | .into_engine(tcx, body, def_id) 139 | .iterate_to_fixpoint() 140 | .into_results_cursor(body); 141 | 142 | // Inspect the fixpoint state immediately before each `Drop` terminator. 143 | for (bb, block) in body.basic_blocks().iter_enumerated() { 144 | if let TerminatorKind::Drop { .. } = block.terminator().kind { 145 | results.seek_before_primary_effect(body.terminator_loc(bb)); 146 | let state = results.get(); 147 | println!("state before drop: {:#?}", state); 148 | } 149 | } 150 | ``` 151 | 152 | ### Graphviz图 153 | 154 | 当需要调试数据流分析结果的时候,可以使用可视化工具。通过[MIR调试命令][Debugging MIR]中的`-Z dump-mir`来完成。 155 | 以`-Z dump-mir=F -Z dump-mir-dataflow`开头,其中`F`是"all"或是您感兴趣的MIR函数的名称。 156 | 157 | 这些`.dot`文件将保存在您的`mir_dump`目录中,并将分析的[`NAME`] (例如,`maybe_inits`)作为其文件名的一部分。 158 | 每个可视化文件将在每个基本块的入口和出口显示完整的数据流状态,以及每个语句和终止语句中发生的任何更改。请参见下面的示例: 159 | 160 | ![A graphviz diagram for a dataflow analysis](../img/dataflow-graphviz-example.png) 161 | 162 | ["gen-kill" problems]: https://en.wikipedia.org/wiki/Data-flow_analysis#Bit_vector_problems 163 | [*Static Program Analysis*]: https://cs.au.dk/~amoeller/spa/ 164 | [Debugging MIR]: ./debugging.html 165 | [`AnalysisDomain`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/dataflow/trait.AnalysisDomain.html 166 | [`Analysis`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/dataflow/trait.Analysis.html 167 | [`Engine`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/dataflow/struct.Engine.html 168 | [`GenKillAnalysis`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/dataflow/trait.GenKillAnalysis.html 169 | [`JoinSemiLattice`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/dataflow/lattice/trait.JoinSemiLattice.html 170 | [`NAME`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/dataflow/trait.AnalysisDomain.html#associatedconstant.NAME 171 | [`ResultsCursor`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/dataflow/struct.ResultsCursor.html 172 | [`ResultsVisitor`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/dataflow/trait.ResultsVisitor.html 173 | [`apply_call_return_effect`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/dataflow/trait.Analysis.html#tymethod.apply_call_return_effect 174 | [`into_engine`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/dataflow/trait.Analysis.html#method.into_engine 175 | [`lattice`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/dataflow/lattice/index.html 176 | [goethe]: https://www.youtube.com/watch?v=NVBQSR_HdL0&list=PL_sGR8T76Y58l3Gck3ZwIIHLWEmXrOLV_&index=2 177 | [lattice]: https://en.wikipedia.org/wiki/Lattice_(order) 178 | [wiki]: https://en.wikipedia.org/wiki/Data-flow_analysis#Basic_principles 179 | -------------------------------------------------------------------------------- /src/mir/debugging.md: -------------------------------------------------------------------------------- 1 | # Debugging 2 | -------------------------------------------------------------------------------- /src/mir/index.md: -------------------------------------------------------------------------------- 1 | # The MIR (Mid-level IR) 2 | -------------------------------------------------------------------------------- /src/mir/optimizations.md: -------------------------------------------------------------------------------- 1 | # MIR优化 2 | 3 | MIR优化是指在代码生成之前,为了产生更好的MIR指令而执行的优化。 4 | 这些优化十分重要,体现在两个方面: 5 | 首先,它使得最终生成的可执行代码的质量更好;其次,这意味着LLVM需要的工作量更少,编译速度更快。 6 | 请注意,由于MIR是通用的(不是[monomorphized] [monomorph])所以这些优化特别有效,我们可以优化通用代码,使得所有代码的特化版本同样受益! 7 | 8 | [mir]: https://rustc-dev-guide.rust-lang.org/mir/index.html 9 | [monomorph]: https://rustc-dev-guide.rust-lang.org/appendix/glossary.html#mono 10 | 11 | MIR的优化执行在借用检查之后。通过执行一系列的pass不断优化MIR。 12 | 一些pass需要执行在全量的代码上,一些pass则不执行实际的优化操作只进行代码检查,还有些pass只在`release`模式下适用。 13 | 14 | 调用[`optimized_mir`][optmir] 来 [查询][query]为给定的[`DefId`][defid]生成优化的MIR,该查询确保借用检查器已运行并且已经进行了一些校验。 15 | 然后,[窃取][steal]MIR,执行优化后,返回被优化后的MIR。 16 | 17 | [optmir]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/transform/fn.optimized_mir.html 18 | [query]: ../query.md 19 | [defid]: https://rustc-dev-guide.rust-lang.org/appendix/glossary.html#def-id 20 | [steal]: https://rustc-dev-guide.rust-lang.org/mir/passes.html?highlight=steal#stealing 21 | 22 | ## 定义优化Passes 23 | 24 | 优化pass的声明和执行顺序由[`run_optimization_passes`][rop]函数定义。 25 | 它包含了一组待执行的pass,其中的每个pass都是一个实现了[`MirPass`] trait的结构体。通过一个元素类型为`&dyn MirPass`的数组实现。 26 | 这些pass通常在[`rustc_mir::transform`][trans]模块下完成自己的实现。 27 | 28 | [rop]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/transform/fn.run_optimization_passes.html 29 | [`MirPass`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/transform/trait.MirPass.html 30 | [trans]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/transform/index.html 31 | 32 | 下面有一些pass的示例: 33 | - `CleanupNonCodegenStatements`: 清理那些只用于分析而不用于代码生成的信息; 34 | - `ConstProp`: [常量传播][constprop]。 35 | 36 | 您可以查看[关于`MirPass`实现的相关章节][impl]中的更多示例。 37 | 38 | [impl]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/transform/trait.MirPass.html#implementors 39 | [constprop]: https://en.wikipedia.org/wiki/Constant_folding#Constant_propagation 40 | 41 | ## MIR优化 levels 42 | 43 | MIR优化有不同程度的行为。 44 | 实验性的优化可能会导致错误编译或增加编译时间。 45 | 这样的pass包含在`nightly`版本中,以收集反馈并修改。要启用这些缓慢的或实验性的优化,可以指定`-Z mir-opt-level`调试标志。 46 | 您可以在[compiler MCP]中找到这些级别的定义。 47 | 如果您正在开发MIR优化pass,并且想查询您的优化是否应该运行,可以使用`tcx.sess.opts.debugging_opts.mir_opt_level`检查输入的级别。 48 | 49 | [compiler MCP]: https://github.com/rust-lang/compiler-team/issues/319 50 | 51 | ## 优化参数`fuel` 52 | 53 | `fuel`是一个编译器选项 (`-Z fuel==`),可以精细地控制在编译过程中的优化情况:每次优化将`fuel`减少1,当`fuel`达到0时不再进行任何优化。 54 | `fuel`的主要用途是调试那些可能不正确或使用不当的优化。通过更改选项,您可以通过二分法定位到发生错误的优化。 55 | 56 | 一般来讲,MIR优化执行过程中会通过调用[`tcx.consider_optimizing`][consideroptimizing]来检查`fuel`,如果`fuel`为空则跳过优化。 57 | 有如下注意事项: 58 | 59 | 1. 如果认为一个优化行为是有保证的(即,为了结果的正确性每次编译都要执行),那么`fuel`是可以跳过的,比如`PromoteTemps`。 60 | 2. 在某些情况下,需要执行一个初始pass来收集候选,然后对他们迭代执行以达到优化的目的。在这种情况下,我们应该让初始pass对`fuel`的值尽可能地突变。 61 | 这可以获得最佳的调试体验,因为可以确定候选列表中的某个优化pass可能未正确调用。 例如`InstCombine`和`ConstantPropagation`。 62 | 63 | [consideroptimizing]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.consider_optimizing 64 | 65 | -------------------------------------------------------------------------------- /src/mir/passes.md: -------------------------------------------------------------------------------- 1 | # MIR passes: getting the MIR for a function 2 | -------------------------------------------------------------------------------- /src/mir/visitor.md: -------------------------------------------------------------------------------- 1 | # MIR visitor 2 | 3 | MIR visitor 是遍历 MIR 并查找事物或对其进行更改的便捷工具。Visitor trait 是在 [the `rustc_middle::mir::visit` module][m-v] 中定义的-其中有两个是通过单个宏生成的:`Visitor`(工作于 `&Mir` 之上,返回共享引用)和 `MutVisitor`(工作于 `&mut Mir`之上,并返回可变引用)。 4 | 5 | [m-v]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/visit/index.html 6 | 7 | 要实现 Visitor,您必须创建一个代表您的 Visitor 的类型。 8 | 通常,此类型希望在处理 MIR 时“挂”到您需要的任何状态上: 9 | 10 | ```rust,ignore 11 | struct MyVisitor<...> { 12 | tcx: TyCtxt<'tcx>, 13 | ... 14 | } 15 | ``` 16 | 17 | 然后为该类型实现 `Visitor` 或 `MutVisitor`: 18 | 19 | ```rust,ignore 20 | impl<'tcx> MutVisitor<'tcx> for NoLandingPads { 21 | fn visit_foo(&mut self, ...) { 22 | ... 23 | self.super_foo(...); 24 | } 25 | } 26 | ``` 27 | 28 | 如上所示,在实现过程中,您可以覆盖任何 `visit_foo` 方法(例如,`visit_terminator`),以便编写一些代码,这些代码将在遇到`foo` 时执行。如果要递归遍历foo的内容,则可以调用 `super_foo` 方法。 (注意:您永远都不应该覆盖 `super_foo`) 29 | 30 | 一个非常简单的 Visitor 示例可以在 [`NoLandingPads`] 中找到。该 Visitor 甚至不需要任何状态:它仅访问所有终止符并删除其“展开”的后继者。 31 | 32 | [`NoLandingPads`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/transform/no_landing_pads/struct.NoLandingPads.html 33 | 34 | ## 遍历 35 | 36 | 除了 Visitor 之外,[`rustc_middle::mir::traversal` 模块][t] 也包含一些有用的函数,用于以[不同的标准顺序][traversal](例如,前序,反向后序,依此类推)遍历 MIR CFG。 37 | 38 | [t]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/traversal/index.html 39 | [traversal]: https://en.wikipedia.org/wiki/Tree_traversal -------------------------------------------------------------------------------- /src/miri.md: -------------------------------------------------------------------------------- 1 | # miri const evaluator 2 | -------------------------------------------------------------------------------- /src/name-resolution.md: -------------------------------------------------------------------------------- 1 | # 名称解析 2 | 3 | 4 | 5 | 在上一个章节,我们看到了如何在展开所有宏的情况下构建 AST。这个过程需要用名称解析(name resolution)来解析导入和宏的名字。在本章中,我们将展示这是如何实现的。 6 | 7 | 事实上,我们在展开宏的过程中并不做完全的名称解析 -- 我们只解析导入和宏在这个过程中。这要求知道什么是需要展开。在这之后,在获得整个 AST 之后,我们将执行全名解析来解析 crate 中的所有名称。这发生在 [`rustc_resolve::late`][late] 中。与宏展开不同,在这个后期展开中,我们只需要尝试解析一个名称一次,因为没有新增的名字,如果失败了,那么将抛出一个编译错误。 8 | 9 | 名称解析可能很复杂。这里有几个不同的命名空间(例如,宏,值,类型,生命周期)和名称可能在不同(嵌套的)范围。此外,针对不同类型的名称解析,其解析失败的原因也可能会不同。例如,在一个模块的作用域内,模块中存在尚未展开的宏和未解析的 glob 导入会导致解析失败。另一方面,在函数内,在我们所在的 block 中,外部作用域和全局作用域中都没有该名称会导致解析失败。 10 | 11 | [late]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/late/index.html 12 | 13 | ## 基础 14 | 15 | 在我们的程序中,我们可以通过给一个变量名,来引用 变量,类型,函数等等。这些名字并不总是唯一的。例如,下面是一个有效 Rust 程序: 16 | 17 | ```rust 18 | type x = u32; 19 | let x: x = 1; 20 | let y: x = 2; 21 | ``` 22 | 23 | 我们是如何知道 第三行 `x` 是一个类型 (u32) 还是 数值 (1)。这个冲突将在名称解析中被解析。在这个特殊栗子中,名称解析定义 类型名称(type names) 和 变量名称(variable names) 在独立的命名空间,所以他们可以共存。 24 | 25 | Rust 中的名称解析分为两个阶段。在第一阶段,将运行宏展开,我们将构建一个模块的书结构和解析导入。宏展开和名字解析通过[`ResolverAstLowering`] 特性(trait)来通信。 26 | 27 | 输入的第二阶段的输入是语法树,它通过解析输入文件和展开宏。这个阶段将 28 | 从链接源文件中所有的名称到相关关联的地方(即名称被引用的地方)。它还会生成有用的错误信息,如输入错误建议,要导入的特性(trait)或 lints 关于未使用的项。(or lints about 29 | unused items) 30 | 31 | 成功运行第二阶段([`Resolver::resolve_crate`]) 将创建一个索引,剩余的编译部分可以使用它来查询当前的名称(通过 `hir::lowering::Resolver` 接口) 32 | 33 | 名称解析在 `rustc_resolve` crate 中,部分内容位于 `lib.rs` 中,其他模块中有一些帮助程序或 symbol-type logic 。 34 | 35 | [`Resolver::resolve_crate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_resolve/struct.Resolver.html#method.resolve_crate 36 | [`ResolverAstLowering`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast_lowering/trait.ResolverAstLowering.html 37 | 38 | ## 命名空间 39 | 40 | 不同类型的符号存在于在不同的命名空间中。例如 类型的名称 不与 变量的名称 冲突。这通常不会发生,因为变量以小写字母开头,而类型以大写字母开头。但这仅仅是一种约定。以下 Rust 代码是合法的,并通过编译(带有 warnings): 41 | 42 | ```rust 43 | type x = u32; 44 | let x: x = 1; 45 | let y: x = 2; // See? x is still a type here. 46 | ``` 47 | 48 | 为了应对这种情况,并使用这些名称的作用域规则少有不同,解析起将它们分开,并为他们创建不同的机构。 49 | 50 | 也就是说,当(code)代码谈到命名空间,它并不代表模块的结构层次,它是 类型 vs 值 vs 宏。 51 | 52 | ## 作用域和 ribs 53 | 54 | 名称只在源代码的特定区域可见。这形成了一个层次结构,但不一定是简单的-如果一个作用域是另一个作用域的一部分,这不意味在外部可见的名称也是在内部可见的,或指的是同一样内容。 55 | 56 | 为了处理这种情况,编译器引入一个 Ribs 概念。这是一种抽象作用域。每每次可见名称可能发生变化时,一个新的 rib 被推入栈中。可能发生这种情况的地方包括: 57 | * 明显的地方 - 花括号包围块,函数边界,模块。 58 | * 通过 let 绑定引入 - shodow 另一个同名变量。 59 | * 宏展开边缘 - 因对宏的卫生(hygiene) 60 | 61 | ribs 栈会由内到外的搜索名称。这有助于找到名称最近的意思(这个名称不会被其他任何东西覆盖)。向外过渡 rib 也可能会改则未使用名称的规则 - 如果这里又一个嵌套的函数(非闭包),内层作用域内不能访问参数和进行本地绑定外层作用域的内容,即使他们在常规作用域规则中应该可见。一个例子: 62 | 63 | 64 | ```rust 65 | fn do_something(val: T) { // <- New rib in both types and values (1) 66 | // `val` is accessible, as is the helper function 67 | // `T` is accessible 68 | let helper = || { // New rib on `helper` (2) and another on the block (3) 69 | // `val` is accessible here 70 | }; // End of (3) 71 | // `val` is accessible, `helper` variable shadows `helper` function 72 | fn helper() { // <- New rib in both types and values (4) 73 | // `val` is not accessible here, (4) is not transparent for locals) 74 | // `T` is not accessible here 75 | } // End of (4) 76 | let val = T::default(); // New rib (5) 77 | // `val` is the variable, not the parameter here 78 | } // End of (5), (2) and (1) 79 | ``` 80 | 81 | 因为对于不同作用域的规则有所不同,每个作用域会有他自己独立的与命名空间并行构造的 rib 栈。此外,也有对于那些没有完整命名空间的 local lables 也有一个 rib 栈(例如 loops 或者 blocks 的名称)。 82 | 83 | ## 总体策略 84 | 85 | 为了执行整个 crate 的名称解析,自上而下的遍历语法树,并解析每个遇到的名称。这适用于大多数类型的名称,因为在使用名称时,已经在 Rib 层次结构中引入了该名称。 86 | 87 | 这里有一些例外,一些会有些棘手,因为它们甚至可以在遇到之前就可以使用 - 因此需要扫描每一个项去填满 Rib。 88 | 89 | 其他甚至更有问题的导入,需要递归的定点解析和宏,需要在处理剩下代码之前进行解析和展开。 90 | 91 | 因此,名称解析是在多个阶段执行的。 92 | 93 | ## Speculative crate loading 94 | 95 | 为了给出有用的错误,rustc 建议将未找到的路径导入作用域中。它是怎么做到的呢?他会检查每一个 crate 的每一个模块,并寻找可能的匹配项。这甚至包括还没有加载的 crate。 96 | 97 | 为尚未加载的导入的提供导入建议被称为_speculative crate loading_,因为不应报告任何遇到的错误:决定去加载这些导入的并非用户。执行此功能的函数是在 `rustc_resolve/src/diagnostics.rs` 中的`lookup_import_candidates`。 98 | 99 | 为了 speculative loads 和用户的加载,解析通过传递一个 `record_used` 参数,当 speculative loads 时候,值为 false。 100 | 101 | ## TODO: 102 | 103 | 这是第一遍学习代码的结果。绝对是不完整的,不够详细的。在某系地方也可能不准确。不过,它可能在将来能提供有用的帮助。 104 | 105 | * 它究竟链接到什么?后续的编译阶段如何发布和使用该链接? 106 | 谁调用它以及如何实际使用它。 107 | * 它是通过,然后仅使用结果,还是可以递增计算(例如,对于RLS)? 108 | * 总体策略描述有点模糊。 109 | * Rib这个名字来自哪里? 110 | * 这东西有自己的测试,还是仅作为某些端到端测试的一部分进行测试? 111 | -------------------------------------------------------------------------------- /src/notification-groups/about.md: -------------------------------------------------------------------------------- 1 | # Notification groups 2 | -------------------------------------------------------------------------------- /src/notification-groups/arm.md: -------------------------------------------------------------------------------- 1 | # ARM 2 | -------------------------------------------------------------------------------- /src/notification-groups/cleanup-crew.md: -------------------------------------------------------------------------------- 1 | # Cleanup Crew 2 | -------------------------------------------------------------------------------- /src/notification-groups/llvm.md: -------------------------------------------------------------------------------- 1 | # LLVM 2 | -------------------------------------------------------------------------------- /src/notification-groups/risc-v.md: -------------------------------------------------------------------------------- 1 | # RISC-V 2 | -------------------------------------------------------------------------------- /src/notification-groups/windows.md: -------------------------------------------------------------------------------- 1 | # Windows 2 | -------------------------------------------------------------------------------- /src/opaque-types-type-alias-impl-trait.md: -------------------------------------------------------------------------------- 1 | # Opaque Types 2 | -------------------------------------------------------------------------------- /src/overview.md: -------------------------------------------------------------------------------- 1 | # 编译器概览 2 | 3 | 4 | 5 | 这一章是关于编译程序时的总体过程 —— 所有东西是如何组合起来的。 6 | 7 | rust的编译器在两方面独具特色:首先它会对你的代码进行别的编译器不会进行的操作(比如借用检查),并且有许多非常规的实现选择(比如查询)。 8 | 我们将会在这一章中逐一讨论这些,并且在指南接下来的部分,我们会更深入细节的审视所有单独的部分。 9 | 10 | ## 编译器对你的代码做了什么 11 | 12 | 首先,我们来看看编译器对你的代码做了些什么。现在,除非必须,我们会避免提及编译器是如何实现这些步骤的;我们之后才会讨论这些。 13 | 14 | - 编译步骤从用户编写Rust程序文本并且使用 `rustc` 编译器对其进行处理开始。命令行参数指明了编译器需要做的工作。 15 | 举个例子,我们可以启用开发版特性(`-Z` 标识),执行 `check`——仅执行构建,或者得到LLVM-IR而不是可执行机器码。 16 | 通过使用 `cargo`,`rustc` 的执行可能是不直接的。 17 | - 命令行参数解析在 [`rustc_driver`] 中发生。这个 crate 定义了用户请求的编译配置 18 | 并且将其作为一个 [`rustc_interface::Config`] 传给接下来的编译过程。 19 | - 原始的 Rust 源文本被位于 [`rustc_lexer`] 的底层词法分析器分析。在这个阶段,源文本被转化成被称为 _tokens_ 的 20 | 原子源码单位序列。 词法分析器支持 Unicode 字符编码。 21 | - token 序列传给了位于 [`rustc_parse`] 的高层词法分析器以为编译流程的下一个阶段做准备。 22 | [`StringReader`] 结构体在这个阶段被用于执行一系列的验证工作并且将字符串转化为驻留符号(稍后便会讨论 _驻留_)。 23 | [字符串驻留] 是一种将多个相同的不可变字符串只存储一次的技术。 24 | 25 | - 词法分析器有小的接口并且不直接依赖于`rustc`中的诊断基础设施。反之,它提供在`rustc_parse::lexer::mod`中被发送为真实诊断 26 | 的作为普通数据的诊断。 27 | - 词法分析器为 IDE 以及 过程宏 保留有全保真度的信息。 28 | - 解析器 [将从词法分析器中得到的token序列转化为抽象语法树(AST)][parser]。它使用递归下降(自上而下)的方式来进行语法解析。 29 | 解析器的 crate 入为`rustc_parse::parser::item`中的`Parser::parse_crate_mod()`以及`Parser::parse_mod()`函数。 30 | 外部模块解析入口为`rustc_expand::module::parse_external_mod`。 31 | 以及宏解析入口为[`Parser::parse_nonterminal()`][parse_nonterminal]。 32 | - 解析经由一系列 `Parser` 工具函数执行,包括`fn bump`,`fn check`,`fn eat`,`fn expect`,`fn look_ahead`。 33 | - 解析是由要被解析的语义构造所组织的。分离的`parse_*`方法可以在`rustc_parse` `parser`文件夹中找到。 34 | 源文件的名字和构造名相同。举个例子,在解析器中能找到以下的文件: 35 | - `expr.rs` 36 | - `pat.rs` 37 | - `ty.rs` 38 | - `stmt.rs` 39 | - 这种命名方案被广泛地应用于编译器的各个阶段。你会发现有文件或者文件夹在解析、降低、类型检查、THIR降低、以及MIR源构建。 40 | - 宏展开、AST验证、命名解析、以及程序错误检查都在编译过程的这个阶段进行。 41 | - 解析器使用标准 `DiagnosticBuilder` API 来进行错误处理,但是我们希望在一个错误发生时, 42 | 尝试恢复、解析Rust语法的一个超集。 43 | - `rustc_ast::ast::{Crate, Mod, Expr, Pat, ...}` AST节点从解析器中被返回。 44 | - 我们接下来拿到AST并且[将其转化为高级中间标识(HIR)][hir]。这是一种编译器友好的AST表示方法。 45 | 这包括到很多如循环、`async fn`之类的解糖化的东西。 46 | - 我们使用 HIR 来进行[类型推导]。 这是对于一个表达式,自动检测其类型的过程。 47 | - **TODO:也许在这里还有其他事情被完成了?我认为初始化类型检查在这里进行了?以及 trait 解析?** 48 | - HIR之后 [被降低为中级中间标识(MIR)][mir]。 49 | - 同时,我们构造 THIR ,THIR是更解糖化的 HIR。THIR被用于模式和详尽性检验。 50 | 同时,它相较于 HIR 更容易被转化为MIR。 51 | - MIR被用于[借用检查]。 52 | - 我们(想要)[在 MIR 上做许多优化][mir-opt]因为它仍然是通用的, 53 | 并且这样能改进我们接下来生成的代码,同时也能加快编译速度。 54 | - MIR 是高级(并且通用的)表示形式,所以在 MIR 层做优化要相较于在 LLVM-IR 层更容易。 55 | 举个例子,LLVM看起来是无法优化 [`simplify_try`] 这样的模式,而mir优化则可以。 56 | - Rust 代码是 _单态化_ 的,这意味着对于所有所有通用代码进行带被具体类型替换的类型参数的拷贝。 57 | 要做到这一点,我们要生成一个列表来存储需要为什么具体类型生成代码。这被称为 _单态集合_。 58 | - 我们接下来开始进行被依稀称作 _代码生成_ 或者 _codegen_。 59 | - [代码生成(codegen)][codegen]是将高等级源表示转化为可执行二进制码的过程。 60 | `rustc`使用LLVM来进行代码生成。第一步就是将 MIR 转化为 LLVM 中间表示(LLVM IR)。 61 | 这是 MIR 依据我们由上一步生成的列表来真正被单态化的时候。 62 | - LLVM IR 被传给 LLVM,并且由其进行更多的优化。之后它产生机器码, 63 | 这基本就是添加了附加底层类型以及注解的汇编代码。(比如一个 ELF 对象或者 wasm)。 64 | - 不同的库/二进制内容被链接以产生最终的二进制内容。 65 | 66 | [String interning]: https://en.wikipedia.org/wiki/String_interning 67 | [`rustc_lexer`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lexer/index.html 68 | [`rustc_driver`]: https://rustc-dev-guide.rust-lang.org/rustc-driver.html 69 | [`rustc_interface::Config`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Config.html 70 | [lex]: https://rustc-dev-guide.rust-lang.org/the-parser.html 71 | [`StringReader`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.StringReader.html 72 | [`rustc_parse`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/index.html 73 | [parser]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/index.html 74 | [hir]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/index.html 75 | [type inference]: https://rustc-dev-guide.rust-lang.org/type-inference.html 76 | [mir]: https://rustc-dev-guide.rust-lang.org/mir/index.html 77 | [borrow checking]: https://rustc-dev-guide.rust-lang.org/borrow_check.html 78 | [mir-opt]: https://rustc-dev-guide.rust-lang.org/mir/optimizations.html 79 | [`simplify_try`]: https://github.com/rust-lang/rust/pull/66282 80 | [codegen]: https://rustc-dev-guide.rust-lang.org/backend/codegen.html 81 | [parse_nonterminal]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/parser/struct.Parser.html#method.parse_nonterminal 82 | 83 | ## 编译器是怎么做的 84 | 85 | 好,我们现在已经从高层视角看了编译器对你的代码做了什么,那让我们再从高层视角看看编译器是 _怎么_ 做到这些的。 86 | 这里有很多编译器需要满足/优化的限制以及冲突目标。举个例子, 87 | 88 | - 编译速度:编译一份程序有多快。更多/好的编译时分析通常意味着编译会更慢。 89 | - 与此同时,我们想要支持增量编译,因此我们需要将其纳入考虑。 90 | 我们怎样才能衡量哪些工作需要被重做,以及当用户修改程序时哪些东西能被重用? 91 | - 与此同时,我们不能在增量缓存中存储太多东西,因为这样会花费很多时间来从磁盘上加载 92 | 并且会占用很多用户的系统空间…… 93 | - 编译器内存占用:当编译一份程序时,我们不希望使用多余的内存。 94 | - 程序运行速度:编译出来的程序运行得有多快。更多/好的编译时分析通常意味着编译器可以做更好的优化。 95 | - 程序大小:编译出来的二进制程序有多大?和前一个点类似。 96 | - 编译器编译速度:编译这个编译器要花多长的时间?这影响着贡献者和编译器的维护。 97 | - 实现复杂度:制造一个编译器是一个人/组能做到的最困难的事之一,并且 Rust 不是一门非常简单的语言, 98 | 那么我们应该如何让编译器的代码基础便于管理? 99 | - 编译正确性:编译器创建的二进制程序应该完成输入程序告诉要做的事, 100 | 并且应该不论后面持续发生的大量变化持续进行。 101 | - 整合工作:编译器需要对以不同方式使用编译器的其他工具(比如 cargo,clippy,miri,RLS)提供支持。 102 | - 编译器稳定性:发布在 stable channel 上的编译器不应该无故崩溃或者出故障。 103 | - Rust 稳定性:编译器必须遵守 Rust 的稳定性承诺,保证之前能够编译的程序不会因为编译器的实现的许多变化 104 | 而无法编译。 105 | - 其他工具的限制:rustc 在后端使用了 LLVM ,一方面我们希望借助 LLVM 的一些好处来优化编译器, 106 | 另一方面我们需要针对它的一些限制/坏处做一些处理。 107 | 108 | 总之,当你阅读指南的接下来的部分的时候,好好记住这些事。他们将通常会指引我们作出选择。 109 | 110 | ### 中间形式表示 111 | 112 | 和大多数编译器一样,`rustc`使用了某种中间表示(IRs)来简化计算。通常, 113 | 直接用源代码来进行我们的工作是极度不方便并且容易出错的。源代码通常被设计的对人类友好,有复意的, 114 | 但是当做一些工作,比如类型检查的时候会较为不方便。 115 | 116 | 因此大多数编译器,包括`rustc`,根据源代码创建某种便于分析的 IR 。`rust` 有一些 IRs, 117 | 其各自根据不同的目的做了优化: 118 | 119 | - Token 序列:词法分析器根据源代码直接生成了一个 token 序列。这个 token 序列相较于原始文本 120 | 更便于解析器处理。 121 | - 抽象语法树(AST):抽象语法树根据词法分析器生成的 token 序列创建。它几乎表示的就是用户所写的。 122 | 它帮助进行句法健全性检查(比如检查用户是否在正确的位置写了所期望的类型)。 123 | - 高级 IR(HIR):它是一些解糖的 AST。从句法的角度上,它仍然接近于用户所写的内容, 124 | 但是它包含了一些诸如省略了的生命周期之类的信息。这种 IR 可以被用于类型检查。 125 | - 类型化的 HIR(THIR):这是介于 HIR 与 MIR 之间的中间形式,曾被称为高级抽象 IR (HAIR)。 126 | 它类似于 HIR 但是它完整地类型化了并且稍微更加地解糖化(比如方法调用以及隐式解引用在这里被完全地显式化)。 127 | 此外,相较于HIR,THIR更容易降低化到 MIR。 128 | - 中级 IR(MIR):这种 IR 基本属于控制流程图(CFG)。控制流程图是一种展示程序基础块以及控制流是如何在其间流通的图表。 129 | 同时,MIR 也有一些带有简单类型化语句的基础块(比如赋值语句、简单计算语句等等)以及链接其他基础块的控制流边 130 | (比如调用语句、丢弃值等等)。MIR 被用于借用检查和其他重要的基于数据流的检查,比如检查未初始化的值。 131 | 它同样被用来做一系列优化以及常值评估(通过 MIRI)。因为 MIR 仍然是普通形式,比起在单态化之后我们在这里可以做更多分析。 132 | - LLVM IR:这是 LLVM 编译器所有输入的标准形式。LLVM IR 是一些带有许多注解的类型化的汇编语言。 133 | 它是所有使用 LLVM 的编译器的标准格式(比如 C 编译器 clang 同样输出 LLVM IR)。 134 | 135 | 另一件要注意的事是,许多在编译器中的值被 _驻留_ 了。这是一种性能和内存优化手段, 136 | 我们将值收集到一个特殊的被称作 _arena_ 的收集器中。之后,我们将引用逐个对应到 arena 中收集的值上。 137 | 这使得我们可以保证相同的值(比如你程序中的类型)只被收集一次并且可以廉价地使用指针进行比较。 138 | 许多内部表示都被驻留了。 139 | 140 | ### 查询 141 | 142 | 第一个主要的选择是 _查询_ 系统。rust 编译器使用了一种不同于大多数书本上的所写编译器的查询系统, 143 | 后者是按顺序执行的一系列代码传递组织的。而 rust 编译器这样做是为了能够做到增量编译 ── 即, 144 | 当用户对其程序作出修改并且重新编译,我们希望尽可能少地做(与上一次编译所做的)相重复的工作来创建新的二进制文件。 145 | 146 | 在`rustc`中,所有以上这些主要步骤被组织为互相调用的一些查询。举个例子。假如有一条查询负责询问某个东西的类型, 147 | 而另一条查询负责询问某个函数的优化后的 MIR。这些查询可以相互调用并且由查询系统所跟踪。 148 | 查询的角果被缓存于硬盘上,这样我们就可以分辨相较于上次编译,哪些查询的结果改变了,并且仅重做这些查询。 149 | 这就是增量编译是如何工作的。 150 | 151 | 理论上讲,对于查询化步骤,我们独立完成上述每一项工作。举个例子,我们会将 HIR 带入一个函数 152 | 并且使用查询来请求该 HIR 的 LLVM IR。这驱动了优化 MIR 的生成,MIR 驱动了借用检查器,借用 153 | 检查器又驱动了 MIR 的生成,等等。 154 | 155 | ……除了那以外,这是非常过于简化的。事实上,有些查询并不是缓存于磁盘上的,并且编译器的某些部分 156 | 需要对所有代码运行正确性检查,即便是代码是无效的(比如借用检查器)。举个例子,[目前对于一个crate的所有函数`mir_borrowck`查询是第一个运行的。][passes] 157 | 之后代码生成器后端触发`collect_and_partition_mono_items`查询,它首先递归地对所有可达函数 158 | 请求`optimized_mir`,而接下来对函数运行`mir_borrowck`并且之后创建代码生成单元。 159 | 这种分割将需要保留下来以保证不可达的函数仍然将他们的错误发送出来。 160 | 161 | [passes]: https://github.com/rust-lang/rust/blob/45ebd5808afd3df7ba842797c0fcd4447ddf30fb/src/librustc_interface/passes.rs#L824 162 | 163 | 此外,编译器建造之初是不使用查询系统的;查询系统是被加装到编译器中的,所以它有些部分还没被查询化。 164 | 同时,LLVM不是我们的代码,所以它也不是查询化的。计划是将前些部分所列举的步骤最终全部查询化, 165 | 但是对于本文,只有介于 HIR 和 LLVM-IR 之间的步骤是被查询化了的。这意味着对于整个程序, 166 | 词法分析以及解析都是被一次性完成的。 167 | 168 | 另一件这里要提到的事是非常重要的“类型上下文”,[`TyCtxt`],它是一个相当巨大的结构体, 169 | 是所有东西的中心。(注意它的名字极其有历史性。这 _不_ 是指类型理论中的`Γ`或`Δ`一类的东西。 170 | 这个名字被保留下来是因为它就是源代码中结构体的名称。)所有查询都被定义为在[`TyCtxt`]类型上 171 | 的方法,并且内存中的查询缓存也同样被存储在此。在代码中,通常会有一个名为`tcx`变量,它是 172 | 类型上下文上的一个句柄。有同样会见到名为`'tcx`的生命周期,这意味着有东西被和`TyCtxt`的 173 | 生命周期绑定在了一起(通常它会被存储或者被驻留化)。 174 | 175 | [`TyCtxt`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html 176 | 177 | ### `ty::Ty` 178 | 179 | 类型在 Rust 中相当重要,并且他们形成了许多编译器分析的核心。用于表示类型(在用户程序中)的 180 | 主要类型(在编译器中)是 [`rustc_middle::ty::Ty`][ty]。它是如此的重要以至于我们为其 181 | 设置了一整章[`ty::Ty`][ty],但是对于现在而言,我们只想提到它存在并且是`rustc`用来表示类型的方法! 182 | 183 | 同样注意到`rustc_middle::ty`模块定义了我们之前提到的`TyCtxt`结构体。 184 | 185 | [ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/type.Ty.html 186 | 187 | ### 并行性 188 | 189 | 编译器表现是我们希望改进的一个问题(并且一直为之努力)。一个方面便是将 `rustc` 自身并行化。 190 | 191 | 目前,rustc 只有一个部分已经实现了并行化:代码生成。在单态化的过程中,编译器会将所有的代码 192 | 分割生成为叫做 _代码生成单元_ 的小块。它们之后由独立的 LLVM 实例生成。由于它们都是独立的, 193 | 我们可以并行地运行它们。最后,运行链接器来组合所有地代码生成单元成为一个二进制文件。 194 | 195 | 但是,编译器余下的部分仍然是未并行化的。我们已经为此付出了很多努力,但是它始终是一个难题。 196 | 目前的方法是把 `RefCell`s 转化为一些 `Mutex`s —— 那代表着我们转换到了线程安全的内部可变性。 197 | 但是仍然有许多在途的挑战比如锁争夺、维护并发下的查询系统不变量以及代码库的复杂性。 198 | 你可以通过在`config.toml`中启用并行编译来尝试并行工作。它仍处于早期阶段,但是有一些 199 | 有保障的性能改进。 200 | 201 | ### 自举 202 | 203 | `rustc`自身是由 Rust 编写的。所以我们如何编译编译器?我们使用一个较老的编译器来编译 204 | 更新的编译器。这被称作 [_自举_]。 205 | 206 | 自举有许多有趣的含义。举个例子,它意味着 Rust 一个主要用户是 Rust 编译器,所以我们 207 | 持续的测试我们自己的软件(“吃我们自己的狗粮”)。 208 | 209 | 对于更多关于自举的细节,详见[这份指导书的自举部分][rustc-bootstrap]。 210 | 211 | [_自举_]: https://en.wikipedia.org/wiki/Bootstrapping_(compilers) 212 | [rustc-bootstrap]: building/bootstrapping.md 213 | 214 | # 未被解决的问题 215 | 216 | - LLVM 在 debug 建造的时候做优化了吗? 217 | - 我如何在我自己的资源下浏览编译的各个过程(词法分析器、解析器、HIR 等等)?—— 比如,`cargo rustc -- -Z unpretty=hir-tree` 允许你查看 HIR 表示 218 | - 什么是`X`的主要入口点? 219 | - 交叉翻译到不同平台的机器码时,哪个阶段发生了分歧? 220 | 221 | # 参考 222 | 223 | - 命令行解析 224 | - 指南: [Rustc驱动以及接口](https://rustc-dev-guide.rust-lang.org/rustc-driver.html) 225 | - 驱动定义: [`rustc_driver`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/) 226 | - 主要入口点: [`rustc_session::config::build_session_options`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/config/fn.build_session_options.html) 227 | - 词法分析:将用户程序转化为 token 流 228 | - 指南: [词法分析以及解析](https://rustc-dev-guide.rust-lang.org/the-parser.html) 229 | - 词法分析器定义: [`rustc_lexer`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lexer/index.html) 230 | - 主要入口: [`rustc_lexer::first_token`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lexer/fn.first_token.html) 231 | - 解析:将 token 流解析为抽象语法树(AST) 232 | - 指南: [词法分析以及解析](https://rustc-dev-guide.rust-lang.org/the-parser.html) 233 | - 解析定义: [`rustc_parse`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/index.html) 234 | - 入口点: 235 | - [crate第一个文件的入口点](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/passes/fn.parse.html) 236 | - [大纲模块解析的入口点](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_expand/module/fn.parse_external_mod.html) 237 | - [宏段的入口点][parse_nonterminal] 238 | - AST 定义:[`rustc_ast`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html) 239 | - 展开: **TODO** 240 | - 命名解析: **TODO** 241 | - 特征门控: **TODO** 242 | - 早期程序检查: **TODO** 243 | - 高级中间表示 (HIR) 244 | - 指南: [HIR](https://rustc-dev-guide.rust-lang.org/hir.html) 245 | - 指南: [HIR中的标识](https://rustc-dev-guide.rust-lang.org/hir.html#identifiers-in-the-hir) 246 | - 指南: [HIR映射](https://rustc-dev-guide.rust-lang.org/hir.html#the-hir-map) 247 | - 指南: [将AST低转为HIR](https://rustc-dev-guide.rust-lang.org/lowering.html) 248 | - 如何查看你代码的 HIR 表示 `cargo rustc -- -Z unpretty=hir-tree` 249 | - Rustc HIR 定义: [`rustc_hir`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/index.html) 250 | - 主要入口点: **TODO** 251 | - 后期程序检查: **TODO** 252 | - 类型推导 253 | - 指南: [类型推导](https://rustc-dev-guide.rust-lang.org/type-inference.html) 254 | - 指南: [ty模块:表示类型](https://rustc-dev-guide.rust-lang.org/ty.html) (语义学) 255 | - 主要入口点 (类型推导): [`InferCtxtBuilder::enter`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_infer/infer/struct.InferCtxtBuilder.html#method.enter) 256 | - 主要入口点 (类型检查主题): [`typeck`查询](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html#method.typeck) 257 | - 这两个函数不能被解耦。 258 | - 中级中间表示 (MIR) 259 | - 指南: [MIR (中级 IR)](https://rustc-dev-guide.rust-lang.org/mir/index.html) 260 | - 定义: [`rustc_middle/src/mir`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/index.html) 261 | - 操纵 MIR 的源代码定义: [`rustc_mir`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/index.html) 262 | - 借用检查器 263 | - 指南: [MIR 借用检查](https://rustc-dev-guide.rust-lang.org/borrow_check.html) 264 | - 定义: [`rustc_mir/borrow_check`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/index.html) 265 | - 主要入口点: [`mir_borrowck` 查询](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/borrow_check/fn.mir_borrowck.html) 266 | - MIR 优化 267 | - 指南: [MIR Optimizations](https://rustc-dev-guide.rust-lang.org/mir/optimizations.html) 268 | - 定义: [`rustc_mir/transform`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/transform/index.html) 269 | - 主要入口点: [`optimized_mir` 查询](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/transform/fn.optimized_mir.html) 270 | - 代码生成 271 | - 指南: [代码生成](https://rustc-dev-guide.rust-lang.org/backend/codegen.html) 272 | - 使用 LLVM 通过 LLVM IR 生成机器代码 - **TODO: 参考?** 273 | - 主要入口点: [`rustc_codegen_ssa::base::codegen_crate`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/base/fn.codegen_crate.html) 274 | - 它单态化并且产出 LLVM IR给一个代码生成单元。 275 | 它之后启动一个后台线程来运行一个之后必须被接合的LLVM。 276 | - 单态化通过[`FunctionCx::monomorphize`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/mir/struct.FunctionCx.html#method.monomorphize) 懒启动以及[`rustc_codegen_ssa::base::codegen_instance `](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/base/fn.codegen_instance.html) -------------------------------------------------------------------------------- /src/panic-implementation.md: -------------------------------------------------------------------------------- 1 | # rust 中的 panic 2 | 3 | 4 | 5 | ## 步骤1: 调用 `panic!` 宏 6 | 7 | 实际上有两个 panic 宏 - 一个定义在 `core` 中,一个定义在 `std` 中。这是因为 `core` 中的代码可能 panic。`core` 是在 `std` 之前构建的,但不管是 `core` 或 `std` 中的 panic,我们希望运行时使用相同的机制。 8 | 9 | ### core 中 panic! 的定义 10 | 11 | `core` `panic!` 宏最终调用如下 (在 `library/core/src/panicking.rs`): 12 | 13 | ```rust 14 | // 注意 这个函数永远不会越过 FFI 边界; 这是一个 Rust 到 Rust 的调用 15 | extern "Rust" { 16 | #[lang = "panic_impl"] 17 | fn panic_impl(pi: &PanicInfo<'_>) -> !; 18 | } 19 | 20 | let pi = PanicInfo::internal_constructor(Some(&fmt), location); 21 | unsafe { panic_impl(&pi) } 22 | ``` 23 | 24 | 实际上解决该问题需要通过几个间接层: 25 | 26 | 1. 在 `compiler/rustc_middle/src/middle/weak_lang_items.rs` 中,`panic_impl` 用 `rust_begin_unwind` 声明标记为 '弱 lang 项'。在 `rustc_typeck/src/collect.rs` 中将实际符号名设置为 `rust_begin_unwind`。 27 | 28 | 注意 `panic_impl` 被声明在一个 `extern "Rust"` 块中,这意味着 core 将尝试调用一个名为 `rust_begin_unwind` 的外部符号(在链接时解决) 29 | 30 | 2. 在 `library/std/src/panicking.rs` 中,我们有这样的定义: 31 | 32 | ```rust 33 | /// core crate panic 的进入点。 34 | #[cfg(not(test))] 35 | #[panic_handler] 36 | #[unwind(allowed)] 37 | pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! { 38 | ... 39 | } 40 | ``` 41 | 42 | 特殊 `panic_handler` 属性是通过 `compiler/rustc_middle/src/middle/lang_items` 解析。`extract` 函数将 `panic_handler` 属性转换一个 `panic_impl` lang 项。 43 | 44 | 现在,我们在 `std` 中有一个匹配的 `panic_handler` lang 项。这个函数与定义在 `core` 中的 `extern { fn panic_impl }` 经过相同的过程,最终得到一个名为 `rust_begin_unwind` 的符号。在链接时,`core` 中的符号引用将被解析为 `std` 中的定义(Rust 源中调用 `begin_panic_handler`)。 45 | 46 | 因此,控制流将在运行时从 core 传递到 std。 这允许来自 core 的 panic 使用和其它 panic 相同的基础结构(panic 钩子,unwinding 等) 47 | 48 | ### std 中 panic! 的实现 49 | 50 | 这就是真正的 panic 相关逻辑开始的地方。在 `library/std/src/panicking.rs`,控制传递给 `rust_panic_with_hook`。这个方法负责调用全局 panic 钩子,并检查是否出现双重 panic。最后,调用由 panic 运行时提供的 `__rust_start_panic`。 51 | 52 | 对 `__rust_start_panic` 的调用非常奇怪 - 它被传递给 `*mut &mut dyn BoxMeUp`,转换成一个 `usize`。一起分解一下这种类型: 53 | 54 | 1. `BoxMeUp` 是一个内部 trait。它是给 `PanicPayload` 55 | (用户提供的有效负载类型的包装器)实现的,并且有一个方法`fn box_me_up(&mut self) -> *mut (dyn Any + Send)`。这个方法获取用户提供的有效负载 (`T: Any + Send`),将其打包,并将其转换为一个原始指针。 56 | 57 | 2. 当我们调用 `__rust_start_panic` 时,会得到一个 `&mut dyn BoxMeUp`。但是,这是一个胖指针 (是 `usize` 的两倍大)。为了跨 FFI 边界上将其传递给 panic 运行时,我们对*的可变引用* (`&mut &mut dyn BoxMeUp`)进行可变引用,并将其转换为原始指针(`*mut &mut dyn BoxMeUp`)。外部的原始指针是一个瘦指针,它指向一个 `Sized` 类型 (一个可变引用)。因此,可以将这个瘦指针转换为一个 `usize`,它适用于跨 FFI 边界传递。 58 | 59 | 最后,调用使用 `usize` 调用 `__rust_start_panic` 。现在进入 panic 运行时。 60 | 61 | ## 步骤 2: panic 运行时 62 | 63 | Rust 提供两个 panic 运行时: `panic_abort` 和 `panic_unwind`。用户可以在构建时通过 `Cargo.toml` 在它们之间进行选择 64 | 65 | `panic_abort` 非常简单: 正如你所期望的那样,它实现 `__rust_start_panic` 只为中断。 66 | 67 | `panic_unwind` 是更有趣的情况。 68 | 69 | 在它的实现 `__rust_start_panic` 中,我们使用 `usize`,将其转换回 `*mut &mut dyn BoxMeUp`,解引用它,并调用 `&mut dyn BoxMeUp` 上的 `box_me_up`。在这个指针中,我们有一个指向负载本身的原始指针 (一个 `*mut (dyn Send + Any)`): 即一个指向调用 `panic!` 的用户提供真实值的原始指针。 70 | 71 | 至此,与平台无关的代码结束。现在,我们现在调用特定于平台的展开逻辑 (例如 `unwind`)。这个代码负责展开栈,运行与每个帧(当前,运行析构函数)相关联的所有 'landing pads',并将控制权转移到 `catch_unwind` 帧。 72 | 73 | 请注意,所有 panic 要么中止进程,要么被调用的 `catch_unwind` 捕获: 在 `library/std/src/rt.rs` 中,调用用户提供的 74 | `main` 函数是包装在 `catch_unwind` 中。 -------------------------------------------------------------------------------- /src/parallel-rustc.md: -------------------------------------------------------------------------------- 1 | # 并行编译 2 | 3 | 大多数编译器都不是并行的,这是一个提高编译器性能的机会。 4 | 5 | 截止 2021 年 1 月,用于显式并行化编译器的工作已停止。有很多设计和正确性的工作需要完成。 6 | 7 | 可以在 `config.toml` 中启用它来尝试当前的并行编译器工作。 8 | 9 | 这项工作有一些基本思路: 10 | 11 | - 编译器中有很多循环,它们只是迭代一个 crate 中的所有项,。这些都可能可以并行化。 12 | - 我们可以使用(一个自定义分支) [`rayon`] 并行运行任务。自定义分支允许执行 DAG 任务,而不仅仅是树。 13 | - 目前有许多全局数据结构需要设置为线程安全的。这里的一个关键策略是将内部可变的数据结构(如: Cell) 转换为与它们同级的线程安全结构(如: Mutex)。 14 | 15 | [`rayon`]: https://crates.io/crates/rayon 16 | 17 | 截至 2021 年 2 月,由于人力不足,大部分这方面的努力被搁置。我们有一个可以正常工作的原型,在许多情况下都有很好的性能收益。然而,有两个障碍: 18 | 19 | - 目前尚不清楚哪些并发需要保持不变的不变性。审核工作正在进行中,但似乎已停滞不前。 20 | 21 | - 有很多锁竞争,随着线程数增加到 4 以上,实际上会降低性能。 22 | 23 | 这里有一些可以用来学习更多的资源(注意其中一些有点过时了): 24 | 25 | - [Zoxc 的 IRLO 线程,这项工作的先驱之一][irlo0] 26 | - [nikomatsakis 在编译器中列出的内部可变性][imlist] 27 | - [alexchricton 关于 IRLO 线程的性能][irlo1] 28 | - [跟踪该问题][tracking] 29 | 30 | [irlo0]: https://internals.rust-lang.org/t/parallelizing-rustc-using-rayon/6606 31 | [imlist]: https://github.com/nikomatsakis/rustc-parallelization/blob/master/interior-mutability-list.md 32 | [irlo1]: https://internals.rust-lang.org/t/help-test-parallel-rustc/11503 33 | [tracking]: https://github.com/rust-lang/rust/issues/48685 -------------------------------------------------------------------------------- /src/param_env.md: -------------------------------------------------------------------------------- 1 | # Parameter Environments 2 | -------------------------------------------------------------------------------- /src/part-2-intro.md: -------------------------------------------------------------------------------- 1 | # 第三部分:高层编译器架构 2 | 3 | 本指南的剩余部分将会讨论编译器是如何工作的。他们会从编译器高层结构的角度逐一介绍编译的每个阶段是如何工作的。对于那些对端到端的编译过程感兴趣的读者, _以及_ 想要了解自己希望作出贡献的特定系统的读者,这些指南会是友好的。如果觉得有不清楚的事情,尽管在[rustc-dev-guide 仓库](https://github.com/rust-lang/rustc-dev-guide/issues)中提出 issue,或者联系在[第一章这个部分](./compiler-team.md)中提到的编译器团队。 4 | 5 | 在这个部分,我们将会着眼于高层编译器架构。特别的,我们会将目光放在影响整个编译器的三个总体设计上:查询系统,增量编译以及驻留。 6 | -------------------------------------------------------------------------------- /src/part-3-intro.md: -------------------------------------------------------------------------------- 1 | # 第三部分:源码表示 2 | 3 | 这部分描述了从用户那里获取原始源代码并将其转换为编译器可以轻松处理的各种形式的过程。他们被称作*中间表示(IRs)*。 4 | 5 | 此过程首先从编译器了解用户的要求开始:解析给定的命令行参数并确定要编译的内容。之后,编译器将用户输入转换为一系列 IR ,这些 IR 看起来越来越不像用户写的内容。 6 | -------------------------------------------------------------------------------- /src/part-4-intro.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RustcRustc/rustc-dev-guide-zh/6f41aee51cb30fe94a6c8173342f7a8051e217e7/src/part-4-intro.md -------------------------------------------------------------------------------- /src/part-5-intro.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RustcRustc/rustc-dev-guide-zh/6f41aee51cb30fe94a6c8173342f7a8051e217e7/src/part-5-intro.md -------------------------------------------------------------------------------- /src/pat-exhaustive-checking.md: -------------------------------------------------------------------------------- 1 | # Pattern and Exhaustiveness Checking 2 | -------------------------------------------------------------------------------- /src/profile-guided-optimization.md: -------------------------------------------------------------------------------- 1 | # Profile-guided Optimization 2 | -------------------------------------------------------------------------------- /src/profiling.md: -------------------------------------------------------------------------------- 1 | # Profiling the compiler 2 | -------------------------------------------------------------------------------- /src/profiling/with_perf.md: -------------------------------------------------------------------------------- 1 | # with the linux perf tool 2 | -------------------------------------------------------------------------------- /src/queries/incremental-compilation-in-detail.md: -------------------------------------------------------------------------------- 1 | # Incremental compilation In Detail 2 | -------------------------------------------------------------------------------- /src/queries/incremental-compilation.md: -------------------------------------------------------------------------------- 1 | # Incremental compilation 2 | -------------------------------------------------------------------------------- /src/queries/profiling.md: -------------------------------------------------------------------------------- 1 | # Profiling Queries 2 | -------------------------------------------------------------------------------- /src/queries/query-evaluation-model-in-detail.md: -------------------------------------------------------------------------------- 1 | # The Query Evaluation Model in Detail 2 | -------------------------------------------------------------------------------- /src/query.md: -------------------------------------------------------------------------------- 1 | # 查询: 需求驱动的编译 2 | 3 | 如[编译器高级概述][hl]中所述,Rust编译器当前(2021年1月 )仍然正在从传统的“基于 pass”的编译过程过渡到“需求驱动”的编译过程。**编译器查询系统是我们新的需求驱动型编译过程的关键。**背后的想法很简单。 您可以使用各种查询来计算某一输入的相关信息 – 例如,有一个名为`type_of(def_id)`的查询,传入某项的 [def-id] ,它将计算该项的类型并将其返回给您。 4 | 5 | [def-id]: appendix/glossary.md#def-id 6 | [hl]: ./compiler-src.md 7 | 8 | 查询执行是**记忆化**的 —— 因此,第一次调用查询时,它将进行实际的计算,但是下一次,结果将从哈希表中返回。 9 | 此外,查询执行非常适合“**增量计算**”; 大致的想法是,当您执行查询时,**可能**会通过从磁盘加载存储的数据来将结果返回给您(这是一个单独的主题,我们将不在此处进一步讨论)。 10 | 11 | 总体上我们希望最终整个编译器控制流将由查询驱动。由一个顶层的查询("compile")来驱动一个crate上的编译;这会依次要求这个crate的各种信息。例如: 12 | 13 | - 此 "compile" 查询可能需要获取代码生成单元列表(即需要由LLVM编译的模块)。 14 | - 但是计算代码生成单元列表将调用一些子查询,该子查询返回 Rust 源代码中定义的所有模块的列表。 15 | - 这些子查询会要求查询HIR。 16 | - 就这样越推越远,直到我们完成实际的 parsing。 17 | 18 | 这一愿景尚未完全实现。尽管如此,编译器的大量代码(例如生成MIR)已经完全像这样工作了。 19 | 20 | ### 增量编译的详细说明 21 | 22 | [增量编译的详细说明][query-model]一章提供了关于什么是查询及其工作方式的深入描述。 23 | 如果您打算编写自己的查询,那么可以读一读这一章节。 24 | 25 | ### 调用查询 26 | 27 | 调用查询很简单。 `tcx`(“类型上下文”)为每个定义好的查询都提供了一种方法。 因此,例如,要调用`type_of`查询,只需执行以下操作: 28 | 29 | ```rust,ignore 30 | let ty = tcx.type_of(some_def_id); 31 | ``` 32 | 33 | ### 编译器如何执行查询 34 | 35 | 您可能想知道调用查询方法时会发生什么。 36 | 答案是,对于每个查询,编译器都会将结果缓存——如果您的查询已经执行过,那么我们将简单地从缓存中复制上一次的返回值并将其返回(因此,您应尝试确保查询的返回类型可以低成本的克隆;如有必要,请使用`Rc`)。 37 | 38 | #### Providers 39 | 40 | 但是,如果查询不在缓存中,则编译器将尝试找到合适的 **provider**。 41 | provider 是已定义并链接到编译器的某个函数,其包含用于计算查询结果的代码。 42 | 43 | **Provider是按crate定义的。** 44 | 编译器(至少在概念上)在内部维护每个 crate 的 provider 表。 45 | 目前,实际上 provider 分为了两组:用于查询“**本crate**”的 provider(即正在编译的crate)和用于查询“**外部crate**”(即正在编译的crate的依赖) 的 provider。 46 | 请注意,确定查询所在的crate的类型不是查询的*类型*,而是*键*。 47 | 例如,当您调用 `tcx.type_of(def_id)` 时,它可以是本地查询,也可以是外部查询, 48 | 这取决于`def_id`所指的crate(请参阅[`self::keys::Key`][Key] trait 以获取有关其工作原理的更多信息)。 49 | 50 | Provider 始终具有相同的函数签名: 51 | 52 | ```rust,ignore 53 | fn provider<'tcx>( 54 | tcx: TyCtxt<'tcx>, 55 | key: QUERY_KEY, 56 | ) -> QUERY_RESULT { 57 | ... 58 | } 59 | ``` 60 | 61 | Provider 接受两个参数:`tcx` 和查询键,并返回查询结果。 62 | 63 | #### 如何初始化 provider 64 | 65 | 创建 tcx 时,它的创建者会使用[`Providers`][providers_struct]结构为它提供provider。 66 | 此结构是由此处的宏生成的,但基本上就是一大堆函数指针: 67 | 68 | [providers_struct]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/query/struct.Providers.html 69 | 70 | ```rust,ignore 71 | struct Providers { 72 | type_of: for<'tcx> fn(TyCtxt<'tcx>, DefId) -> Ty<'tcx>, 73 | ... 74 | } 75 | ``` 76 | 77 | 目前,我们为本地 crate 和所有外部 crate 各提供一份该结构的副本,最终计划是为每个crate提供一份。 78 | 79 | 这些 `Provider` 结构最终是由 `librustc_driver` 创建并填充的,它通过调用各种[`provide`][provide_fn]函数,将工作分配给其他`rustc_*` crate。这些函数看起来像这样: 80 | 81 | [provide_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/fn.provide.html 82 | 83 | ```rust,ignore 84 | pub fn provide(providers: &mut Providers) { 85 | *providers = Providers { 86 | type_of, 87 | ..*providers 88 | }; 89 | } 90 | ``` 91 | 92 | 也就是说,他们接收一个 `&mut Providers` 并对其进行原地修改。 93 | 通常我们使用上面的写法只是因为它看起来比较漂亮,但是您也可以 `providers.type_of = type_of`,这是等效的。 94 | (在这里,`type_of` 将是一个顶层函数,如我们之前看到的那样定义。) 95 | 因此,如果我们想为其他查询添加 provider,比如向前面的 crate 添加一个 `fubar`,我们可以这样修改 `provide` 函数: 96 | 97 | ```rust,ignore 98 | pub fn provide(providers: &mut Providers) { 99 | *providers = Providers { 100 | type_of, 101 | fubar, 102 | ..*providers 103 | }; 104 | } 105 | 106 | fn fubar<'tcx>(tcx: TyCtxt<'tcx>, key: DefId) -> Fubar<'tcx> { ... } 107 | ``` 108 | 109 | 注意:大多数 `rustc_*` crate仅提供 **本crate provider**。 110 | 几乎所有的**外部 provider** 都会通过 [`rustc_metadata` crate][rustc_metadata] 进行处理,后者会从 crate 元数据中加载信息。 111 | 但是在某些情况下,某些crate可以既提供本地也提供外部crate查询,在这种情况下,他们通过[`provide_both`][ext_provide_both] 定义了 `provide` 和 `provide_extern` 函数,供`rustc_driver` 调用。 112 | 113 | [rustc_metadata]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/index.html 114 | [ext_provide_both]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_llvm/attributes/fn.provide_both.html 115 | 116 | 117 | ### 添加一种新的查询 118 | 119 | 假设您想添加一种新的查询,您该怎么做? 120 | 定义查询分为两个步骤: 121 | 122 | 1. 首先,必须指定查询名称和参数; 然后, 123 | 2. 您必须在需要的地方提供查询提供程序。 124 | 125 | 要指定查询名称和参数,您只需将条目添加到 126 | [`compiler/rustc_middle/src/query/mod.rs`][query-mod] 中的大型宏调用之中,类似于: 127 | 128 | [query-mod]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/query/index.html 129 | 130 | ```rust,ignore 131 | rustc_queries! { 132 | Other { 133 | /// Records the type of every item. 134 | query type_of(key: DefId) -> Ty<'tcx> { 135 | cache { key.is_local() } 136 | } 137 | } 138 | 139 | ... 140 | } 141 | ``` 142 | 143 | 查询分为几类(`Other`,`Codegn`,`TypeChecking`等)。 144 | 每组包含一个或多个查询。 每个查询的定义都是这样分解的: 145 | 146 | ```rust,ignore 147 | query type_of(key: DefId) -> Ty<'tcx> { ... } 148 | ^^ ^^^^^^^ ^^^^^ ^^^^^^^^ ^^^ 149 | | | | | | 150 | | | | | 查询修饰符 151 | | | | 查询的结果类型 152 | | | 查询的 key 的类型 153 | | 查询名称 154 | query 关键字 155 | ``` 156 | 157 | 让我们一一介绍它们: 158 | 159 | - **query关键字:** 表示查询定义的开始。 160 | 161 | - **查询名称:**查询方法的名称(`tcx.type_of(..)`)。也用作生成的表示此查询的结构体的名称(`ty::queries::type_of`)。 162 | 163 | - **查询的 key 的类型:**此查询的参数类型。此类型必须实现 [`ty::query::keys::Key`][Key] trait,该trait定义了如何将其映射到 crate 等等。 164 | 165 | - **查询的结果类型:** 此查询产生的类型。 166 | 这种类型应该 167 | 168 | (a)不使用 `RefCell` 等内部可变性模式,并且 169 | (b)可以廉价地克隆。对于非平凡的数据类型,建议使用 Interning 方法或使用`Rc`或`Arc`。 170 | 171 | - 一个例外是`ty::steal::Steal`类型,该类型用于廉价地修改MIR。有关更多详细信息,请参见`Steal`的定义。不应该在不警告`@rust-lang/compiler`的情况下添加对`Steal`的新的使用。 172 | 173 | - **查询修饰符:** 用于自定义查询处理方式的各种标志和选项(主要用于[增量编译][incrcomp])。 174 | 175 | [Key]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/query/keys/trait.Key.html 176 | [incrcomp]: queries/incremental-compilation-in-detail.html#query-modifiers 177 | 178 | 因此,要添加查询: 179 | 180 | - 使用上述格式在 `rustc_queries!` 中添加一个条目。 181 | - 通过修改适当的 `provide` 方法建立和 provider 的关联; 或根据需要添加一个新文件,并确保`rustc_driver` 会调用它。 182 | 183 | #### 查询结构体和查询描述 184 | 185 | 对于每种类型,`rustc_queries` 宏都会生成一个以查询名字命名的“查询结构体”。 186 | 此结构体是描述查询的一种占位符。 每个这样的结构都要实现[`self::config::QueryConfig`][QueryConfig] trait, 187 | 该 trait 上有该特定查询的 键/值 的关联类型。 188 | 基本上,生成的代码如下所示: 189 | 190 | ```rust,ignore 191 | // Dummy struct representing a particular kind of query: 192 | pub struct type_of<'tcx> { data: PhantomData<&'tcx ()> } 193 | 194 | impl<'tcx> QueryConfig for type_of<'tcx> { 195 | type Key = DefId; 196 | type Value = Ty<'tcx>; 197 | 198 | const NAME: QueryName = QueryName::type_of; 199 | const CATEGORY: ProfileCategory = ProfileCategory::Other; 200 | } 201 | ``` 202 | 203 | 您可能希望实现一个额外的trait,称为 [`self::config::QueryDescription`][QueryDescription]。 204 | 这个 trait 在发生循环引用错误时会被使用,为查询提供一个“人类可读”的名称,以便我们可以探明循环引用发生的情况。 205 | 如果查询键是 `DefId`,则可以不实现这个 trait,但是如果*不*实现它,则会得到一个相当普遍的错误(“processing `foo` ...”)。 206 | 您可以将新的 impl 放入`config`模块中。 像这样: 207 | 208 | [QueryConfig]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/query/trait.QueryConfig.html 209 | [QueryDescription]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_query_system/query/config/trait.QueryDescription.html 210 | 211 | ```rust,ignore 212 | impl<'tcx> QueryDescription for queries::type_of<'tcx> { 213 | fn describe(tcx: TyCtxt, key: DefId) -> String { 214 | format!("computing the type of `{}`", tcx.def_path_str(key)) 215 | } 216 | } 217 | ``` 218 | 219 | 另一个选择是添加`desc`修饰符: 220 | 221 | ```rust,ignore 222 | rustc_queries! { 223 | Other { 224 | /// Records the type of every item. 225 | query type_of(key: DefId) -> Ty<'tcx> { 226 | desc { |tcx| "computing the type of `{}`", tcx.def_path_str(key) } 227 | } 228 | } 229 | } 230 | ``` 231 | 232 | `rustc_queries` 宏会自动生成合适的 `impl`。 233 | 234 | [query-model]: queries/incremental-compilation-in-detail.md -------------------------------------------------------------------------------- /src/rustbot.md: -------------------------------------------------------------------------------- 1 | # Mastering @rustbot 2 | -------------------------------------------------------------------------------- /src/rustc-driver-getting-diagnostics.md: -------------------------------------------------------------------------------- 1 | # 示例:通过 `rustc_interface` 获取诊断信息 2 | 3 | `rustc_interface` 允许您拦截将被打印到 stderr 的诊断信息。 4 | 5 | ## 获取诊断信息 6 | 7 | 要从编译器获取诊断信息,请配置 `rustc_interface::Config` 将诊断信息输出到一个缓冲区,然后运行 `TyCtxt.analysis` : 8 | 9 | ```rust 10 | // 完整程序见 https://github.com/rust-lang/rustc-dev-guide/blob/master/examples/rustc-driver-getting-diagnostics.rs 。 11 | let buffer = sync::Arc::new(sync::Mutex::new(Vec::new())); 12 | let config = rustc_interface::Config { 13 | opts: config::Options { 14 | // 将编译器配置为以紧凑的JSON格式发出诊断信息。 15 | error_format: config::ErrorOutputType::Json { 16 | pretty: false, 17 | json_rendered: rustc_errors::emitter::HumanReadableErrorType::Default( 18 | rustc_errors::emitter::ColorConfig::Never, 19 | ), 20 | }, 21 | /* 其他配置 */ 22 | }, 23 | // 重定向编译器的诊断信息输出到一个缓冲区。 24 | diagnostic_output: rustc_session::DiagnosticOutput::Raw(Box::from(DiagnosticSink( 25 | buffer.clone(), 26 | ))), 27 | /* 其他配置 */ 28 | }; 29 | rustc_interface::run_compiler(config, |compiler| { 30 | compiler.enter(|queries| { 31 | queries.global_ctxt().unwrap().take().enter(|tcx| { 32 | // 在本地 crate 上运行分析阶段以触发类型错误。 33 | tcx.analysis(rustc_hir::def_id::LOCAL_CRATE); 34 | }); 35 | }); 36 | }); 37 | // 读取缓冲区中的诊断信息。 38 | let diagnostics = String::from_utf8(buffer.lock().unwrap().clone()).unwrap(); 39 | ``` 40 | -------------------------------------------------------------------------------- /src/rustc-driver-interacting-with-the-ast.md: -------------------------------------------------------------------------------- 1 | # 示例:通过 `rustc_interface` 进行类型检查 2 | 3 | `rustc_interface` 允许您在编译的各个阶段与 Rust 代码交互。 4 | 5 | ## 获取表达式的类型 6 | 7 | ```rust 8 | // 完整程序见 https://github.com/rust-lang/rustc-dev-guide/blob/master/examples/rustc-driver-interacting-with-the-ast.rs 。 9 | let config = rustc_interface::Config { 10 | input: config::Input::Str { 11 | name: source_map::FileName::Custom("main.rs".to_string()), 12 | input: "fn main() { let message = \"Hello, world!\"; println!(\"{}\", message); }" 13 | .to_string(), 14 | }, 15 | /* 其他配置 */ 16 | }; 17 | rustc_interface::run_compiler(config, |compiler| { 18 | compiler.enter(|queries| { 19 | // 分析 crate 并检查光标下的类型。 20 | queries.global_ctxt().unwrap().take().enter(|tcx| { 21 | // 每次编译包含一个单独的 crate 。 22 | let krate = tcx.hir().krate(); 23 | // 遍历 crate 中的顶层项,寻找 main 函数。 24 | for (_, item) in &krate.items { 25 | // 使用模式匹配在 main 函数中查找特定节点。 26 | if let rustc_hir::ItemKind::Fn(_, _, body_id) = item.kind { 27 | let expr = &tcx.hir().body(body_id).value; 28 | if let rustc_hir::ExprKind::Block(block, _) = expr.kind { 29 | if let rustc_hir::StmtKind::Local(local) = block.stmts[0].kind { 30 | if let Some(expr) = local.init { 31 | let hir_id = expr.hir_id; // hir_id 标识字符串 "Hello, world!" 32 | let def_id = tcx.hir().local_def_id(item.hir_id); // def_id 标识 main 函数 33 | let ty = tcx.typeck(def_id).node_type(hir_id); 34 | println!("{:?}: {:?}", expr, ty); // 打印出 expr(HirId { owner: DefIndex(3), local_id: 4 }: "Hello, world!"): &'static str 35 | } 36 | } 37 | } 38 | } 39 | } 40 | }) 41 | }); 42 | }); 43 | ``` 44 | -------------------------------------------------------------------------------- /src/rustc-driver.md: -------------------------------------------------------------------------------- 1 | # Rustc Driver 和 Rustc Interface 2 | 3 | [`rustc_driver`] 本质上是 `rustc` 的 `main()` 函数。它使用 [`rustc_interface`] crate 中定义的接口,按正确顺序执行编译器各个阶段。 4 | 5 | `rustc_interface` crate 为外部用户提供了一个(不稳定的)API,用于在编译过程中的特定时间运行代码,从而允许第三方有效地使用 `rustc` 的内部代码作为库来分析 crate 或在进程中模拟编译器(例如 RLS 或 rustdoc )。 6 | 7 | 对于那些将 `rustc` 作为库使用的用户,[`rustc_interface::run_compiler()`][i_rc] 函数是编译器的主要入口点。它接受一个编译器配置参数,以及一个接受 [`Compiler`] 参数的闭包。`run_compiler` 从配置中创建一个 `Compiler` 并将其传递给闭包。在闭包内部,您可以使用 `Compiler` 来驱动查询以编译 crate 并获取结果。这也是 `rustc_driver` 所做的。您可以在[这里][example]看到有关如何使用 `rustc_interface` 的最小示例。 8 | 9 | 您可以通过 rustdocs 查看 [`Compiler`] 当前可用的查询。您可以通过查看 `rustc_driver` 的实现,特别是 [`rustc_driver::run_compiler` 函数][rd_rc](不要与 [`rustc_interface::run_compiler`][i_rc] 混淆)来查看如何使用它们的示例。`rustc_driver::run_compiler` 函数接受一堆命令行参数和一些其他配置,并推动编译完成。 10 | 11 | `rustc_driver::run_compiler` 还接受一个 [`Callbacks`][cb],它是一个允许自定义编译器配置以及允许一些自定义代码在编译的不同阶段之后运行的 trait 。 12 | 13 | > **警告:** 本质来说,编译器内部 API 总是不稳定的,但是我们会尽力避免不必要的破坏。 14 | 15 | [cb]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/trait.Callbacks.html 16 | [rd_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/fn.run_compiler.html 17 | [i_rc]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/fn.run_compiler.html 18 | [example]: https://github.com/rust-lang/rustc-dev-guide/blob/master/examples/rustc-driver-example.rs 19 | [`rustc_interface`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/index.html 20 | [`rustc_driver`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_driver/ 21 | [`Compiler`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Compiler.html 22 | [`Session`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/struct.Session.html 23 | [`TyCtxt`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyCtxt.html 24 | [`SourceMap`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/source_map/struct.SourceMap.html 25 | [stupid-stats]: https://github.com/nrc/stupid-stats 26 | [Appendix A]: appendix/stupid-stats.html 27 | -------------------------------------------------------------------------------- /src/rustdoc-internals.md: -------------------------------------------------------------------------------- 1 | # Rustdoc 内部工作原理 2 | 3 | 4 | 5 | 本页介绍了 rustdoc 的 pass 和模式。有关rustdoc的概述, 6 | 请参阅[“Rustdoc概述”一章](./rustdoc.md)。 7 | 8 | ## 从 crate 到 clean 9 | 10 | 在 core.rs 中有两个主要项目:`DocContext` 结构和 `run_core` 函数。 11 | 后者会让 `rustdoc` 调用 `rustc` 将 crate 编译到 `rustdoc` 可以接手的地步。 12 | 前者是状态容器,用于在 crate 中爬取信息时收集其文档。 13 | 14 | crate 爬取的主要过程是通过几个在 `clean/mod.rs` 中的 `Clean` trait 实现完成的。 15 | `Clean` trait 是一个转换 trait,它定义了一个方法: 16 | 17 | ```rust,ignore 18 | pub trait Clean { 19 | fn clean(&self, cx: &DocContext) -> T; 20 | } 21 | ``` 22 | 23 | `clean/mod.rs` 还定义了稍后用于渲染文档页面的 “clean 过的” AST 类型。 24 | 通常,对于每个 `Clean` 的实现,都会从 rustc 中获取一些 AST 或 HIR 类型, 25 | 并将其转换为适当的“clean 过的”的类型。 26 | 更“大型”的构造(例如模块或相关项目)可能会在其 `Clean` 实现中进行一些额外的处理, 27 | 但是在大多数情况下,这些实现都是直接的转换。 28 | 该模块的入口是 `impl Clean for visit_ast::RustdocVisitor`,由上面的 `run_core` 调用。 29 | 30 | 您看,我实际上前面撒了一点小谎: 31 | 在 `clean/mod.rs` 中的事件发生之前,还有另一个AST转换。 32 | 在 `visit_ast.rs` 中的 `RustdocVisitor` 类型*实际上*抓取了一个 33 | `rustc_hir::Crate` 以获取第一个中间表示形式, 34 | 该中间表示形式在 `doctree.rs` 中定义。 35 | 此过程主要是为了获得有关 HIR 类型的一些中间包装,并处理可见性和内联。 36 | 这是处理 `#[doc(inline)]`、 `#[doc(no_inline)]` 和 `#[doc(hidden)]` 的地方, 37 | 以及决定 pub use 是否应该渲染为一整页还是模块页面中的“Reexport”行。 38 | 39 | 在 `clean/mod.rs` 中发生的另一件主要事情是将 doc 注释和 `#[doc=""]` 属性收集到 Attributes 结构的单独字段中, 40 | 这个字段出现在任何需要手写文档的地方。这使得之后容易收集此文档。 41 | 42 | 该过程的主要输出是一个 `clean::Crate`,其中有一个项目树描述了目标 crate 中有公开文档的项目。 43 | 44 | ### Hot potato 45 | 46 | 在继续进行下一步之前,在文档会上有一些重要的“pass”。 47 | 这些操作包括将单独的“属性”组合为单个字符串并去除前导空格, 48 | 以使文档能更容易地被 markdown 解析器解析, 49 | 或者删除未公开的项目或使用 `#[doc(hidden)]` 故意隐藏的项目。 50 | 这些都在 `passes/` 目录中实现,每文件一个 pass。 51 | 默认情况下,所有这些 pass 都会在 crate 进行, 52 | 但是与私有/隐藏的条目有关的 pass 可以通过将 `--document-private-items` 传入 rustdoc来绕过。 53 | 请注意,与之前的 AST 转换组不同,这些 pass 是在 _cleaned_ crate 上运行的。 54 | 55 | (严格来说,您可以微调 pass 甚至添加自己的pass,但是[我们正在尝试 deprecate 这种行为][44136]。 56 | 如果您需要对这些 pass 进行更细粒度的控制,请告诉我们!) 57 | 58 | [44136]: https://github.com/rust-lang/rust/issues/44136 59 | 60 | 以下是截至 2021年2月的 pass 列表: 61 | 62 | - `calculate-doc-coverage` 计算 `--show-coverage` 使用的信息。 63 | 64 | - `check-code-block-syntax` 验证 Rust 代码块的语法 65 | (`` ```rust ``) 66 | 67 | - `check-invalid-html-tags` 检测 doc comments 中的不合法 HTML(如没有被正确关闭的 ``)。 68 | 69 | - `check-non-autolinks` 检测可以或者应该使用尖括号写的链接 (这些代码应该由 nightly-only 的 lint 选项 `non_autolinks` 开启)。 70 | 71 | - `collapse-docs` 将所有文档 attributes 拼接成一个文档 attribute。 72 | 这是必须的,因为每行文档注释都是单独的文档 attribute,`collapse-docs` 会将它们合并成单独的一个字符串,其中每个 attribute 之间都有换行符连接。 73 | 74 | - `collect-intra-doc-links` 解析 [intra-doc links](https://doc.rust-lang.org/rustdoc/linking-to-items-by-name.html)。 75 | 76 | - `collect-trait-impls` 为 crate 中的每个项目收集 trait 提示。 77 | 例如,如果我们定义一个实现 trait 的结构,则此过程将注意到该结构实现了该 trait。 78 | 79 | - `doc-test-lints` 在 doctests 上运行各种 lint。 80 | 81 | - `propagate-doc-cfg` 将 `#[doc(cfg(...))]` 传递给子 item。 82 | 83 | - `strip-priv-imports` 删去所有私有导入语句(`use`、 `extern crate`)。 84 | 这是必需的,因为 rustdoc 将通过将项目的文档内联到模块中或创建带有导入的 “Reexport” 部分来处理 *公有* 导入。 85 | 这个 pass 保证了这些导入能反应在文档上。 86 | 87 | - `strip-hidden` 和 `strip-private` 从输出中删除所有 `doc(hidden)` 和 私有 item。 88 | `strip-private` 包含了 `strip-priv-imports`。基本上,目标就是移除和公共文档无关的 item。 89 | 90 | - `unindent-comments` 移除了注释中多余的缩进,以使得 Markdown 能被正确地解析。 91 | 这是必需的,因为编写文档的约定是在 `///` 或 `//!` 标记与文档文本之间空一格,但是 Markdown 对空格敏感。 92 | 例如,具有四个空格缩进的文本块会被解析为代码块,因此如果我们不移除注释中的缩进,这些列表项 93 | 94 | ```rust,ignore 95 | /// A list: 96 | /// 97 | /// - Foo 98 | /// - Bar 99 | ``` 100 | 101 | 会被违反用户期望地解析为代码块。 102 | 103 | `passes/` 中也有一个 `stripper` 模块,但其中是一些 `strip-*` pass 使用的工具函数,它并非是一个 pass。 104 | 105 | ## 从 clean 到 crate 106 | 107 | 这是 rustdoc 中“第二阶段”开始的地方。 108 | 这个阶段主要位于 `html/` 文件夹中,并且以 `html/render.rs` 中的 `run()` 开始。 109 | 该代码在渲染这个 crate 的所有文档前会负责设置渲染期间使用的 `Context`、`SharedContext` 和 `Cache`, 110 | 并复制每个渲染文档集中的静态文件(字体,CSS 和 JavaScript 等保存在 `html/static/` 中的文件), 111 | 创建搜索索引并打印出源代码渲染。 112 | 113 | 直接在 `Context` 上实现的几个函数接受 `clean::Crate` 参数, 114 | 并在渲染项或其递归模块子项之间建立某种状态。 115 | 从这里开始,通过 `html/layout.rs` 中的巨大 `write!()` 调用,开始进行“页面渲染”。 116 | 从项目和文档中实际生成HTML的部分发生在一系列 `std::fmt::Display` 实现和接受 `&mut std::fmt::Formatter` 的函数中。 117 | 写出页面正文的顶层实现是 `html/render.rs` 中的 `impl <'a> fmt::Display for Item <'a>`, 118 | 它会基于被渲染的 `Item` 调用多个 `item_*` 之一。 119 | 120 | 根据您要查找的渲染代码的类型,您可能会在 `html/render.rs` 中找到主要项目, 121 | 例如 “结构体页面应如何渲染” 或者对于较小的组件,对应项目可能在 `html/format.rs` 中, 122 | 如“我应该如何将 where 子句作为其他项目的一部分进行打印”。 123 | 124 | 每当 rustdoc 遇到应在其上打印手写文档的项目时, 125 | 它就会调用 `html/markdown.rs` 中的与 Markdown 部分的接口。 126 | 其中暴露了一系列包装了字符串 Markdown 的类型, 127 | 并了实现 `fmt::Display` 以输出 HTML 文本。 128 | 在运行 Markdown 解析器之前,要特别注意启用某些功能(如脚注和表格)并在 Rust 代码块中添加语法高亮显示(通过 `html/highlight.rs`)。 129 | 这里还有一个函数(`find_testable_code`), 130 | 该函数专门扫描Rust代码块,以便测试运行程序代码可以在 crate 中找到所有 doctest。 131 | 132 | ### 从 soup 到 nuts 133 | 134 | (另一个标题: ["An unbroken thread that stretches from those first `Cell`s to us"][video]) 135 | 136 | [video]: https://www.youtube.com/watch?v=hOLAGYmUQV0 137 | 138 | 重要的是要注意,AST 清理可以向编译器询问信息 139 | (至关重要的是,`DocContext` 包含 `TyCtxt`), 140 | 但是页面渲染则不能。在 `run_core` 中创建的 `clean::Crate` 在传递给 141 | `html::render::run` 之前传递到编译器上下文之外。 142 | 这意味着,在项目定义内无法立即获得的许多“补充数据”, 143 | 例如哪个 trait 是语言使用的 `Deref` trait,需要在清理过程中收集并存储在 `DocContext` 中, 144 | 并在 HTML 渲染期间传递给 `SharedContext`。 145 | 这表现为一堆共享状态,上下文变量和 `RefCell`。 146 | 147 | 还要注意的是,某些来自“请求编译器”的项不会直接进入 `DocContext` 中 —— 148 | 例如,当从外部 crate 中加载项时, 149 | rustdoc 会询问 trait 实现并基于该信息生成新的 `Item`。 150 | 它直接进入返回的 `Crate`,而不是通过 `DocContext`。 151 | 这样,就可以在呈现 HTML 之前将这些实现与其他实现一起收集。 152 | 153 | ## 其他技巧 154 | 155 | 所有这些都描述了从 Rust crate 生成HTML文档的过程, 156 | 但是 rustdoc 可以以其他几种主要模式运行。 157 | 它也可以在独立的 Markdown 文件上运行,也可以在 Rust 代码或独立的 Markdown 文件上运行 doctest。 158 | 对于前者,它直接调用 `html/markdown.rs`,可以通过选项将目录插入到输出 HTML 的模式。 159 | 160 | 对于后者,rustdoc 运行类似的部分编译以获取在 `test.rs` 中的文档的相关信息。 161 | 但是它并不经过完整的清理和渲染过程,而是运行了一个简单得多的 crate walk,仅抓取手写的文档。 162 | 与上述 `html/markdown.rs` 中的 `find_testable_code` 结合,它会建立一组要运行的测试,然后再将其交给测试运行器。 163 | `test.rs` 中一个值得注意的的位置是函数 `make_test`,在该函数中,手写 `doctest` 被转换为可以执行的东西。 164 | 165 | 可以[在这里](https://quietmisdreavus.net/code/2018/02/23/how-the-doctests-get-made/)找到一些关于 `make_test` 的更多信息。 166 | 167 | ## Dotting i's and crossing t's 168 | 169 | 所以简而言之,这就是rustdoc的代码,但是 repo 中还有很多事情要处理。 170 | 由于我们手头有完整的 `compiletest` 套件,因此在 `src/test/rustdoc` 中有一组测试可以确保最终的 HTML 符合我们在各种情况下的期望。 171 | 这些测试还使用了补充脚本 `src/etc/htmldocck.py`, 172 | 该脚本允许它使用 XPath 表示法浏览最终的 HTML,以精确查看输出结果。 173 | rustdoc测试可用的所有命令的完整说明(例如 [`@has`] 和 [`@matches`])位于 [`htmldocck.py`] 中。 174 | 175 | 要在 rustdoc 测试中使用多个 crate,请添加 `// aux-build:filename.rs` 176 | 到测试文件的顶部。应该将 `filename.rs` 放置在相对于带有注释的测试文件的 `auxiliary` 目录中。 177 | 如果您需要为辅助文件构建文档,请使用 `// build-aux-docs`。 178 | 179 | 此外,还有针对搜索索引和 rustdoc 查询它的能力的独立测试 。 180 | `src/test/rustdoc-js` 中的文件每个都包含一个不同的搜索查询和预期结果(按“搜索”标签细分)。 181 | 这些文件由 `src/tools/rustdoc-js` 和 Node.js 运行时中的脚本处理。 182 | 这些测试没有详尽的描述,但是可以在 `basic.js` 中找到一个包含所有选项卡结果的宽泛示例。 183 | 基本思想是,将给定的 `QUERY` 与一组 `EXPECTED` 结果相匹配,并附上每个 item 的完整路径。 184 | 185 | [`htmldocck.py`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py 186 | [`@has`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py#L39 187 | [`@matches`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py#L44 188 | 189 | ## 本地测试 190 | 191 | 生成的 HTML 文档的某些功能可能需要跨页面使用本地存储,如果没有 HTTP 服务器,这将无法正常工作。 192 | 要在本地测试这些功能,可以运行本地 HTTP 服务器,如下所示: 193 | 194 | ```bash 195 | $ ./x.py doc library/std --stage 1 196 | # The documentation has been generated into `build/[YOUR ARCH]/doc`. 197 | $ python3 -m http.server -d build/[YOUR ARCH]/doc 198 | ``` 199 | 200 | 现在,您可以像浏览 Internet 上的文档一样浏览本地文档。 例如,`std` 的网址将是 `/std/`。 201 | 202 | ## See also 203 | 204 | - [`rustdoc` api docs] 205 | - [An overview of `rustdoc`](./rustdoc.md) 206 | - [The rustdoc user guide] 207 | 208 | [`rustdoc` api docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustdoc/ 209 | [The rustdoc user guide]: https://doc.rust-lang.org/nightly/rustdoc/ -------------------------------------------------------------------------------- /src/rustdoc.md: -------------------------------------------------------------------------------- 1 | # Rustdoc 概述 2 | 3 | Rustdoc 实际上直接使用了 rustc 的内部功能。 4 | 它与编译器和标准库一起存在于代码树中。 5 | 本章是关于它如何工作的。 6 | 有关Rustdoc功能及其使用方法的信息,请参见 [Rustdoc book](https://doc.rust-lang.org/nightly/rustdoc/)。 7 | 有关rustdoc如何工作的更多详细信息,请参见 [“Rustdoc 内部工作原理” 一章]。 8 | 9 | [“Rustdoc 内部工作原理” 一章]:./rustdoc-internals.md 10 | 11 | Rustdoc 完全在 [`librustdoc`][rd] crate 中实现。 12 | 它可以运行编译器来获取 crate 的内部表示(HIR), 13 | 以及查询项目类型的一些信息。 14 | [HIR] 和 [查询] 在相应的章节中进行了讨论。 15 | 16 | [HIR]: ./hir.md 17 | [queries]: ./query.md 18 | [rd]: https://github.com/rust-lang/rust/tree/master/src/librustdoc 19 | 20 | `librustdoc` 主要执行两个步骤来渲染一组文档: 21 | 22 | * 将 AST “清理”为更适合于创建文档的形式(并且稍微更耐编译器中的“搅动”)。 23 | * 使用此清理后的 AST 一次渲染一个 crate 的文档。 24 | 25 | 当然实际上并不仅限于此,这样描述简化了许多细节,但这只是一个高层次的概述。 26 | 27 | (注意:`librustdoc` 是一个库 crate! 28 | "rustdoc" 二进制文件是使用 [`src/tools/rustdoc`][bin] 29 | 中的项目创建的。注意所有上述操作都是在 `librustdoc` crate 的 `lib.rs` 中的 `main` 函数中执行的。) 30 | 31 | [bin]: https://github.com/rust-lang/rust/tree/master/src/tools/rustdoc 32 | 33 | ## Cheat sheet 34 | 35 | * 使用 `./x.py build` 制作一个可以在其他项目上运行的 rustdoc。 36 | * 添加 `library/test` 之后才能使用 `rustdoc --test`。 37 | * 如果您以前使用过 `rustup toolchain link local /path/to/build/$TARGET/stage1`,则在执行上一个构建命令后,`cargo +local doc` 将可以正常工作。 38 | 39 | * 使用 `./x.py doc --stage 1 library/std` 来用这个 rustdoc 来生成标准库文档。 40 | * 生成的文档位于 `build/$TARGET/doc/std`, 但这个生成出来的 bundle 期望你将其从 `doc` 文件夹拷贝到一个 web 服务器上,以便首页和 CSS/JS 可以正常加载。 41 | 42 | * 使用 `x.py test src/test/rustdoc*` 来用 stage1 rustdoc 运行测试。 43 | * 参见 [“Rustdoc 内部工作原理” 一章] 来了解更多和测试有关的信息。 44 | 45 | * 大多数 HTML 打印代码位于 `html/format.rs` 和 `html/render.rs`中。 46 | 它主要由一堆 `fmt::Display` 实现和补充函数构成。 47 | 48 | * 上面实现了 `Display` 的类型是在 `clean/mod.rs` 中定义的, 49 | 就在自定义 `Clean` trait 旁边,该 trait 用于将这些类型的对象从 rustc HIR 中提取出来。 50 | 51 | * 使用 rustdoc 进行测试的代码在 `test.rs` 中。 52 | 53 | * Markdown 渲染器位于 `html/markdown.rs` 中,包括用于从给定的 Markdown 块中提取文档测试的功能。 54 | 55 | * rustdoc *输出* 上的测试位于 `src/test/rustdoc` 中,由 rustbuild 的测试运行器和补充脚本 `src/etc/htmldocck.py` 处理。 56 | 57 | * 搜索索引生成的测试位于 `src/test/rustdoc-js` 中,是一系列 JavaScript 文件,用于对标准库搜索索引和预期结果的查询进行编码。 -------------------------------------------------------------------------------- /src/salsa.md: -------------------------------------------------------------------------------- 1 | # Salsa 2 | -------------------------------------------------------------------------------- /src/sanitizers.md: -------------------------------------------------------------------------------- 1 | # Sanitizers Support 2 | -------------------------------------------------------------------------------- /src/serialization.md: -------------------------------------------------------------------------------- 1 | # Rustc 中的序列化 2 | 3 | Rustc 需要在编译期 [序列化][serialize] 和反序列化各种数据。特别是: 4 | 5 | - "Crate 元数据",主要是查询输出,在编译 crate 时,以二进制格式序列化并输出到 `rlib` 和 `rmeta` 文件中,由依赖该库的 crate 将这些文件反序列化。 6 | - 某些查询输出以二进制格式序列化为[持久化增量编译结果][persist incremental compilation results]。 7 | - `-Z ast-json` 和 `-Z ast-json-noexpand` 标记以 json 格式序列化 [AST], 并将结果输出到标准输出。 8 | - [`CrateInfo`]使用 `-Z no-link` 标记时被序列化到 json,使用 `-Z link-only` 标志时,从 json 反序列化。 9 | 10 | ## `Encodable` 和 `Decodable` trait 11 | 12 | [`rustc_serialize`] crate 为可序列化类型定义了两个 trait: 13 | 14 | ```rust,ignore 15 | pub trait Encodable { 16 | fn encode(&self, s: &mut S) -> Result<(), S::Error>; 17 | } 18 | 19 | pub trait Decodable: Sized { 20 | fn decode(d: &mut D) -> Result; 21 | } 22 | ``` 23 | 24 | 还为整型,浮点型,`bool`,`char`,`str` 和各种通用标准库类型都定义了这两个 trait 的实现。 25 | 26 | 由这些类型组合成的类型,通常通过 [derives] 实现 `Encodable` 和 `Decodable`。这些生成的实现将结构体或枚举中的字段反序列化。对于一个结构体的实现像下面这样: 27 | 28 | ```rust,ignore 29 | #![feature(rustc_private)] 30 | extern crate rustc_serialize; 31 | use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; 32 | 33 | struct MyStruct { 34 | int: u32, 35 | float: f32, 36 | } 37 | 38 | impl Encodable for MyStruct { 39 | fn encode(&self, s: &mut E) -> Result<(), E::Error> { 40 | s.emit_struct("MyStruct", 2, |s| { 41 | s.emit_struct_field("int", 0, |s| self.int.encode(s))?; 42 | s.emit_struct_field("float", 1, |s| self.float.encode(s)) 43 | }) 44 | } 45 | } 46 | impl Decodable for MyStruct { 47 | fn decode(s: &mut D) -> Result { 48 | s.read_struct("MyStruct", 2, |d| { 49 | let int = d.read_struct_field("int", 0, Decodable::decode)?; 50 | let float = d.read_struct_field("float", 1, Decodable::decode)?; 51 | 52 | Ok(MyStruct { int, float }) 53 | }) 54 | } 55 | } 56 | ``` 57 | 58 | ## 编码和解码 arena allocated 类型 59 | 60 | Rustc 有许多 [arena allocated 类型][arena allocated types]。如果不访问分配这些类型的 arena 就无法反序列化这些类型。[`TyDecoder`] 和 [`TyEncoder`] trait 是允许访问 `TyCtxt` 的 `Decoder` 和 `Encoder` 的 super trait。 61 | 62 | 对于包含 arena allocated 类型的类型,则将实现这些 trait 的 `Encodable` 和 `Decodable` 的类型参数绑定在一起。例如 63 | 64 | ```rust,ignore 65 | impl<'tcx, D: TyDecoder<'tcx>> Decodable for MyStruct<'tcx> { 66 | /* ... */ 67 | } 68 | ``` 69 | 70 | `TyEncodable` 和 `TyDecodable` [derive 宏][derives] 将其扩展为这种实现。 71 | 72 | 解码实际的 arena allocated 类型比较困难,因为孤儿规则导致一些实现无法编写。为解决这个问题,`rustc_middle` 中的定义的 [`RefDecodable`] trait。可以给任意类型实现。`TyDecodable` 宏会调用 `RefDecodable` 去解码引用,但是对不同的泛型代码实际上需要特定的类型解码器 `Decodable`。 73 | 74 | 对 interned 类型而言,使用新的类型包装器,如 `ty::Predicate` 和手动实现 `Encodable` 和 `Decodable` 可能更简单,而不是手动实现 `RefDecodable`。 75 | 76 | ## Derive 宏 77 | 78 | `rustc_macros` crate 定义各种 drive,帮助实现 `Decodable` 和 `Encodable`。 79 | 80 | - `Encodable` 和 `Decodable` 宏会生成适用于所有 `Encoders` 和 `Decoders` 的实现。这些应该用在不依赖 `rustc_middle` 的 crate 中,或必须序列化但没有实现 `TyEncoder` 的类型。 81 | - `MetadataEncodable` 和 `MetadataDecodable` 生成仅允许通过 [`rustc_metadata::rmeta::encoder::EncodeContext`] 和 [`rustc_metadata::rmeta::decoder::DecodeContext`] 解码的实现。这些用在包含 `rustc_metadata::rmeta::Lazy` 的类型中。 82 | - `TyEncodable` 和 `TyDecoder` 生成适用于任意 `TyEncoder` 或 `TyDecoder` 的实现。这些仅用于 crate 元数据和/或增量缓存中序列化类型,`rustc_middle` 中大多数是可序列化类型。 83 | 84 | ## Shorthands 85 | 86 | `Ty` 可以深度递归,如果每个 `Ty` 被编码会导致 crate 元数据变的非常大。为解决这个问题,每个 `TyEncoder` 的输出中都有序列化类型的位置缓存。如果要编码的类型在缓存中,则编码写入文件的字节偏移量,而不是像通常那样的序列化类型。类似的方案用于 `ty::Predicate`。 87 | 88 | ## `Lazy` 89 | 90 | 在创建 `TyCtxt<'tcx>` 之前先加载 crate 元数据,因此一些反序列化需要推迟到元数据的初始化载入。[`Lazy`] 类型将(相对)偏移量包装在了已序列化的 `T` 的 crate 元数据中。 91 | 92 | `Lazy<[T]>` 和 `Lazy>` 类型提供了一些功能 `Lazy>` 和 `Lazy>` : 93 | 94 | - 可以直接从迭代器编码 `Lazy<[T]>`,无需事先收集到 `Vec` 中。 95 | - 索引到 `Lazy>` 不需要解码除正在读取条目以外的条目。 96 | 97 | **注意**: 不缓存 `Lazy` 第一次反序列化后的值。相反,查询系统是缓存这些结果的主要方式。 98 | 99 | ## Specialization 100 | 101 | 少数类型,特别是 `DefId`,针对不同的 `Encoder` 需要采用不同的实现。目前,这是通过 ad-hoc 专门处理: 102 | `DefId` 有一个 `default` 实现 `Encodable` 和一个专有的 `Encodable`。 103 | 104 | [arena allocated types]: memory.md 105 | [AST]: the-parser.md 106 | [derives]: #derive-macros 107 | [persist incremental compilation results]: queries/incremental-compilation-in-detail.md#the-real-world-how-persistence-makes-everything-complicated 108 | [serialize]: https://en.wikipedia.org/wiki/Serialization 109 | 110 | [`CrateInfo`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/struct.CrateInfo.html 111 | [`Lazy`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/rmeta/struct.Lazy.html 112 | [`RefDecodable`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/codec/trait.RefDecodable.html 113 | [`rustc_metadata::rmeta::decoder::DecodeContext`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/rmeta/decoder/struct.DecodeContext.html 114 | [`rustc_metadata::rmeta::encoder::EncodeContext`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/rmeta/encoder/struct.EncodeContext.html 115 | [`rustc_serialize`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_serialize/index.html 116 | [`TyDecoder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/codec/trait.TyEncoder.html 117 | [`TyEncoder`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/codec/trait.TyDecoder.html -------------------------------------------------------------------------------- /src/stability.md: -------------------------------------------------------------------------------- 1 | # Stability attributes 2 | -------------------------------------------------------------------------------- /src/stabilization_guide.md: -------------------------------------------------------------------------------- 1 | # Stabilizing Features 2 | -------------------------------------------------------------------------------- /src/syntax-intro.md: -------------------------------------------------------------------------------- 1 | # 语法和 AST 2 | 直接使用源代码(source code)是非常不方便的和容易出错的,因此在我们做任何事之前,我们需要将源代码(raw source code)转换成抽象语法树(AST)。事实证明,即使我们这样做,仍需要做大量的工作,包括包括词法分析(lexing),解析(parsing),宏展开(macro expansion),名称解析(name resolution),条件编译(conditional compilation),功能门检查(feature-gate checking)和抽象语法树的验证(validation of the AST)。在这一章,我们来看一看所有这些步骤。 3 | 4 | 值得注意的是,这些工作之间并不总是有明确顺序的。例如,宏展开(macro expansion)依赖于名称解析(name resolution)来解析宏和导入的名称。解析(parsing)需要宏展开(macro expansion),这又可能需要解析宏的输出(output of the macro)。 -------------------------------------------------------------------------------- /src/test-implementation.md: -------------------------------------------------------------------------------- 1 | # `#[test]` 属性 2 | 3 | 4 | 5 | 今天,Rust 程序员依赖于称为 `#[test]` 的内置属性。您需要做的只是将一个函数标记为测试(test),并包含一些 断言(asserts),如下所示: 6 | 7 | ```rust,ignore 8 | #[test] 9 | fn my_test() { 10 | assert!(2+2 == 4); 11 | } 12 | ``` 13 | 14 | 当程序使用 `rustc --test` 或 `cargo test` 命令进行编译的时候,它将生成可运行该程序及其他测试函数的可执行文件。这种测试方式允许所有测试于代码并存。你甚至可以将测试放入到私有模块中: 15 | 16 | ```rust,ignore 17 | mod my_priv_mod { 18 | fn my_priv_func() -> bool {} 19 | 20 | #[test] 21 | fn test_priv_func() { 22 | assert!(my_priv_func()); 23 | } 24 | } 25 | ``` 26 | 27 | 此外,可以很容易的测试私有项目,而不用担心如何将它们导出给任何类型的外部测试设备。这是 Rust 中 工效(ergonomics)测试的关键。然而从语法上讲,这是相当奇怪的。如果这些函数是不可见的(private)的,主函数如何调用他们呢?`rustc --test` 是怎么做到的? 28 | 29 | 编译器中的 [`rustc_ast` crate][rustc_ast] 为 `#[test]` 实现了语法转译。本质上这是一个 fancy 的宏,它通过三个步骤重写了 crate。 30 | 31 | ## Step 1: 重新导出(Re-Exporting) 32 | 33 | 如前所述,测试可以存在于私有模块内部,因此我们需要一种在不破坏现有代码的情况下将其暴露给主函数。因此 `rustc_ast` 将创建一个名为 `__test_reexports` 的本地模块,该模块递归地重复导出(Re-Exporting)测试。此扩展的代码示例转换为: 34 | 35 | ```rust,ignore 36 | mod my_priv_mod { 37 | fn my_priv_func() -> bool {} 38 | 39 | pub fn test_priv_func() { 40 | assert!(my_priv_func()); 41 | } 42 | 43 | pub mod __test_reexports { 44 | pub use super::test_priv_func; 45 | } 46 | } 47 | ``` 48 | 49 | 现在,可以通过 `my_priv_mod::__test_reexports::test_priv_func` 访问我们的测试。对于更深的模块结构,`__test_reexports` 讲重新导出包含测试模块,因此位于 `a::b::my_test` 将变成 `a::__test_reexports::b::__test_reexports::my_test`。尽管此过程看起来很安全,但是如果当前已存在 `__test_reexports` 模块会怎么样?答案:并不要紧。 50 | 51 | 为了解释,我们需要了解 AST 如何表示标识符([how the AST represents 52 | identifiers][Ident])。每个函数,变量,模块的名称都不直接存储为 string,而是存储为不透明的 [Symbol][Symbol],它本质是每个标识符的 ID 号。编译器保留一个独立的哈希表,使我们可以在必要时(例如在打印语法错误时)恢复人类可读的 Symbol 名称。当编译器生成 `__test_reexports` 模块是,它会为标识符生成一个新的符号,因此尽管编译器生成的`__test_reexports` 可能与您创建的包共享一个名称,但不会共享一个 Symbol 。此技术可以防止在代码生成过程中发生名称冲突,这是 Rust 宏卫生(hygiene)的基础 53 | 54 | ## Step 2: Harness Generation 55 | 56 | 现在我们可以从 crate 根目录访问我们的测试,我们需要对它们进行一些操作。 `rustc_ast` 生成如下模块: 57 | 58 | ```rust,ignore 59 | #[main] 60 | pub fn main() { 61 | extern crate test; 62 | test::test_main_static(&[&path::to::test1, /*...*/]); 63 | } 64 | ``` 65 | 66 | 其中 `path::to::test1` 是类型 `test::TestDescAndFn` 的常量。 67 | 68 | 尽管这种转换很简单,但它使我们对测试的实际运行方式有很多了解。将测试汇总到一个数组中,然后传递给名称为 `test_main_static` 的测试运行器。我们将返回到 `TestDescAndFn` 到底是什么,但是现在,关键点是有一个名为 [`test`][test] crate,它是 Rust Core 的一部分,他实现了测试所有运行时,`test` 接口是不稳定的,所以与它交互的唯一方式是通过 `#[test]` 宏。 69 | 70 | ## Step 3: Test Object Generation 71 | 72 | 如果您以前用 Rust 编写过测试,那么您可能熟悉一些测试函数上可用的一些可选属性。例如,如果我们预测测试会 panic ,可以用 `#[should_panic]` 来注释测试。看起来是如下的: 73 | 74 | ```rust,ignore 75 | #[test] 76 | #[should_panic] 77 | fn foo() { 78 | panic!("intentional"); 79 | } 80 | ``` 81 | 82 | 这意味着我们的测试不仅仅是简单的函数,它们也有配置信息。`test` 将这个配置数据编码到一个名为 [`TestDesc`][TestDesc] 的结构体中。对于 crate 中的每一个测试函数,`rustc_ast` 将解析其属性并生成 `TestDesc` 实例。然后它将 `TestDesc` 和 test 函数组合到可预测名称的 `TestDescAndFn` 结构体中,`test_main_static` 对其进行操作。对于给定的测试,生成 `TestDescAndFn` 实例如下: 83 | 84 | ```rust,ignore 85 | self::test::TestDescAndFn{ 86 | desc: self::test::TestDesc{ 87 | name: self::test::StaticTestName("foo"), 88 | ignore: false, 89 | should_panic: self::test::ShouldPanic::Yes, 90 | allow_fail: false, 91 | }, 92 | testfn: self::test::StaticTestFn(|| 93 | self::test::assert_test_result(::crate::__test_reexports::foo())), 94 | } 95 | ``` 96 | 97 | 一旦我们构建了这些测试对象的数组,它们就会通过步骤2中生成的管理传递给测试运行器。 98 | 99 | ## 检查生成的代码 100 | 101 | 在 nightly rust 中,有一个不稳定的标签叫做 `unpretty` ,你可以使用它在宏展开后打印出模块的源代码: 102 | 103 | ```bash 104 | $ rustc my_mod.rs -Z unpretty=hir 105 | ``` 106 | 107 | [test]: https://doc.rust-lang.org/test/index.html 108 | [TestDesc]: https://doc.rust-lang.org/test/struct.TestDesc.html 109 | [Symbol]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html 110 | [Ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html 111 | [eRFC]: https://github.com/rust-lang/rfcs/blob/master/text/2318-custom-test-frameworks.md 112 | [rustc_ast]: https://github.com/rust-lang/rust/tree/master/compiler/rustc_ast 113 | -------------------------------------------------------------------------------- /src/tests/adding.md: -------------------------------------------------------------------------------- 1 | # 添加新测试 2 | 3 | 4 | 5 | **总体而言,我们希望每个修复rustc错误的PR都能够有一些相应的回归测试** 。这些测试在修复之前是错误的但是在PR之后应该是通过的。这些测试能有效的防止我们重复过去的错误。 6 | 7 | 为了添加新测试,通常要做的第一件事是创建一个文件,往往是Rust源文件。测试文件有特定的结构: 8 | 9 | 10 | - 它们应该包含一些[解释测试内容的注释](#explanatory_comment); 11 | - 接下来,它们应该有一个或多个[头部命令](#header_commands),这些头部命令是能够让测试解释器知道如何解释特殊的注释,。 12 | - 最后,它们应该有Rust源码。源码可能包含不同的[错误注释](#error_annotation),这些错误指示预期的编译错误或警告。 13 | 14 | 15 | 根据测试套件的不同,可能还其它一些需要注意的细节: 16 | 17 | - 对于[`ui`测试套件](#ui),您需要生成参考输出文件。 18 | 19 | ## 我应该添加哪种测试 20 | 知道该使用哪种测试是十分困难的。这里有一些粗略的启发: 21 | - 一些测试特殊的需求 22 | - 需要运行gdb或者lldb?使用`debuginfo`测试套件 23 | - 需要检查LLVM IR或者MIR IR?使用`codegen`或者`mir-opt`测试套件 24 | - 需要运行rustdoc?首选`rustdoc`或者`rustdoc-ui`测试,有时,您也需要`rustc-js` 25 | - 需要以某种方式检查生成的二进制文件?请使用`use-make` 26 | - 库测试应该放在`library/${crate}/tests`中(其中的`${crate}`通常是`core`,`alloc`,`std`)。库测试应该包括: 27 | - API是否正常运行,包括接受各种类型或者具有某些运行时行为的测试 28 | - 是否存在任何与测试不相关的编译器警告的测试 29 | - 当使用一个API时给出的错误与它真正的错误无关时的测试。这些测试在代码块中应该有一个[错误编号](`EOXXX`),用于确保它是正确的错误信息。 30 | - 对于剩余的大多数,首选[`ui`(或者`ui-fulldeps`)测试](#ui) 31 | - [`ui`](#ui)测试同时包含`run-pass`,`compile-fail`,和`parse=fail`测试 32 | - 在警告或错误的情况下,`ui`测试会捕获全部输出,这使得评审变得更容易,同时也有助于防止输出中的"隐藏"回归 33 | 34 | [错误编号]: https://doc.rust-lang.org/rustdoc/unstable-features.html#error-numbers-for-compile-fail-doctests 35 | 36 | 37 | 38 | ## 命名您的测试 39 | 传统上,对于测试名字,我们并没有太多的结构。并且,在很长一段时间中,rustc测试运行程序不支持子目录(现在可以了),所以测试套件譬如[`src/test/ui`]中有很多文件。这并不是一个理想的设置。 40 | 41 | [`src/test/ui`]: https://github.com/rust-lang/rust/tree/master/src/test/ui/ 42 | 43 | 对于回归测试-基本上,一些随机的来自于互联网上的代码片段-我们经常用问题(issue)加上简短的说明来命名这些测试。理想情况下,应该将测试添加到目录中,这样能够帮助我们确定哪段代码正在被测试(例如`src/test/ui/borrowck/issue-54597-reject-move-out-of-borrow-via-pat.rs`)如果您已经尝试过但是找不到更相关的地方,这个测试可以被添加到`src/test/ui/issues/`。同样,**请在某处添加上问题编号(issue numbeer)**。但是,请尽量避免把您的测试放在那,因为这样会使目录中的测试过多,造成语义组织不佳。 44 | 当在编写一个新特性时候时,**请创建一个子目录用于存放您的测试**。例如,如果您要实现RFC1234("Widgets"),那么最好将测试放在类似`src/test/ui/rfc1234-widgets`的目录。 45 | 46 | 在其它情况下,可能已经存在合适的目录。(被正确使用的目录结构实际上是一个活跃的讨论区) 47 | 48 | 49 | 50 | 51 | 52 | ## 注释说明测试内容 53 | 当您在创建测试文件时,请在文件的开头添加总结测试要点的注释。注释应该突出显示哪一部分测试更为重要,以及这个测试正在解决什么问题。引用问题编号通常非常有帮助。 54 | 55 | 该注释不必过于广泛,类似"Regression test for #18060: match arms were matching in the wrong order."的注释就已经足够了。 56 | 57 | 以后当您的测试崩溃时,这些注释对其他人非常有用,因为它们通常已经突出显示了问题所在。当出于某些原因测试需要重构时,这些注释也同样有用,因为它能让其他人知道哪一部分的测试是重要的(通常,必须重写测试,因为它不再测试它曾经被用于测试的内容,所以知道测试曾经的含义是十分有用的) 58 | 59 | 60 | 61 | 62 | ## 头部指令: 配置rustc 63 | 头部指令是一种特殊的注释,它让测试运行程序知道如何解释。在测试中,它们必须出现在Rust源代码之前。它们通常被放在段注释后,这些注释用来解释本测试的关键点。例如,这个测试使用了`//compile-flags`指令,该指令在编译测试时给rustc指定了自定义的标志。 64 | 65 | ```rust,ignore 66 | // Test the behavior of `0 - 1` when overflow checks are disabled. 67 | 68 | // compile-flags: -C overflow-checks=off 69 | 70 | fn main() { 71 | let x = 0 - 1; 72 | ... 73 | } 74 | ``` 75 | 76 | ### 忽略测试 77 | 下列是用于在某些情况下忽略测试,这意味着测试不会被编译或者运行 78 | * `ignore-X` 其中X是会忽略相应测试的目标细节或阶段(见下文) 79 | * `only-X`和`ignore-X`相似,不过*只*会在那个目标或阶段下运行测试 80 | * `ignore-pretty`将不会编译打印美化的测试(这样做是为了测试打印美化器,但它并不总是有效) 81 | * `ignore-test`总是忽略测试 82 | * `ignore-lldb`和`ignore-gdb`会跳过调试器的调试信息 83 | * `ignore-gdb-version`当使用某些gdb版本时,可以使用它来忽略测试 84 | 85 | 一些关于`ignore-X`中`X`的例子: 86 | 87 | * 架构: `aarch64`, `arm`, `asmjs`, `mips`, `wasm32`, `x86_64`,`x86`, ... 88 | * OS: `android`, `emscripten`, `freebsd`, `ios`, `linux`, `macos`, `windows`, ... 89 | * 环境(即目标三元组("target-triple")的第四个词):`gnu`, `msvc`, `musl`. 90 | * 指针宽度: `32bit`, `64bit`. 91 | * 阶段: `stage0`, `stage1`, `stage2`. 92 | * 当交叉编译时: `compare-mode-nll` 93 | * 当使用远程测试时: `remote` 94 | * 当启用调试断言时: `debug` 95 | * 当测试特定的调试器时: `cdb`, `gdb`, `lldb` 96 | * 特定比较模式时: `compare-mode-nll`, `compare-mode-polonius` 97 | 98 | ### 其它头部指令 99 | 这是一份关于其它头部指令的列表。该表并不详尽,您通常可以通过浏览来自compiletest源的[`header.rs`]中的`TestProps`找到头部命令。 100 | * `run-rustfix` ,该命令是用于UI测试,表示测试产生结构化建议。测试编写者应该创建一个`.fixed`文件,其中包含应用了建议的源码。当运行测试时,compiletest 首先检查正确的lint/warning是否产生。然后,它应用建议并且与`.fixed`(两者必须匹配)比较。最后,fixed源码被编译,并且此次编译必须成功。`.fixed`文件可以通过`bless`选项自动生成,在[本节](bless)进行了介绍 101 | * `min-gdb-version`指定了本测试所需的最低gdb版本。 102 | * `min-lldb-version`指定了本测试所需的最低lldb版本。 103 | * `no-system-llvm`,如果使用系统llvm,该命令会导致测试被忽略 104 | * `min-system-llvm-version`指定最低的系统llvm版本;如果系统llvm被使用并且未达到所需的最低版本,那么本测试会被忽略。当一个llvm功能被反向移植到rust-llvm时,这条命令十分有效。 105 | * `ignore-llvm-version`,当特定的LLVM版本被使用时,该命令可以用于跳过测试。它需要一个或两个参数。第一个参数是第一个被忽略的版本,如果没有第二个参数,那么后续版本都会被忽略;否则,第二个参数就是被忽略的最后一个版本。 106 | * `build-pass`适用于UI测试,该命令表示测试应该成功编译和链接,与此相反的是默认情况下测试应该会出错。 107 | * `compile-flags`将额外的命令行参数传递给编译器,例如`compile-flags -g`会强制启用debuginfo 108 | * `edition`控制测试应该使用的版本(默认为2014)。用法示例`// edition:2018`。 109 | * `should-fail`表示测试应该失败;被用于元测试("meta testing"),该测试是我们测试compiletest程序本身是否能够在适当的情况下产生错误。在格式美化测试中该头部命令会被忽略。 110 | * `gate-test-X`中的`X`是一个特性,该命令把测试标记为对于特性X的"门控测试"("gate test")。此类测试应该确保当尝试使用门控功能而没有正确的`#![feature(X)]`标签时,编译器会发生错误。每个不稳定的语言特性都需要一个门测试。 111 | * `needs-profiler-support`-需要profiler运行时,例如,rustc的`config.toml`中的`profiler=true`。 112 | * `needs-sanitizer-support`-需要sanitizer运行时,例如,rustc的`config.toml`中的`sanitizers = true`。 113 | * `needs-sanitizer-{address,leak,memory,thread}`-表示该测试需要一个目标分别支持AddressSanitizer, LeakSanitizer,MemorySanitizer 或者 ThreadSanitizer。 114 | * `error-pattern`像`ERROR`注释一样检查诊断,而不指定错误行。当错误没有给出任何范围时,这个命令十分有用。 115 | 116 | [`header.rs`]: https://github.com/rust-lang/rust/tree/master/src/tools/compiletest/src/header.rs 117 | [bless]: ./running.md#editing-and-updating-the-reference-files 118 | 119 | 120 | 121 | ### 错误注释示例 122 | 这是一些UI测试源上不同的错误注释示例。 123 | 124 | #### 置于错误行上 125 | 使用`//~ERROR`语法 126 | ```rust,ignore 127 | fn main() { 128 | let x = (1, 2, 3); 129 | match x { 130 | (_a, _x @ ..) => {} //~ ERROR `_x @` is not allowed in a tuple 131 | _ => {} 132 | } 133 | } 134 | ``` 135 | #### 置于错误行下 136 | 使用`//~^`语法,字符串中插入号(`^`)的数量表示上方的行数。在下面这个例子中,错误行在错误注释行的上四行位置,因此注释中有四个插入号。 137 | 138 | ```rust,ignore 139 | fn main() { 140 | let x = (1, 2, 3); 141 | match x { 142 | (_a, _x @ ..) => {} // <- the error is on this line 143 | _ => {} 144 | } 145 | } 146 | //~^^^^ ERROR `_x @` is not allowed in a tuple 147 | ``` 148 | 149 | #### 使用与上面错误注释行相同的错误行 150 | 使用`//~|`语法定义与上面错误注释行相同的错误行 151 | ```rust,ignore 152 | struct Binder(i32, i32, i32); 153 | 154 | fn main() { 155 | let x = Binder(1, 2, 3); 156 | match x { 157 | Binder(_a, _x @ ..) => {} // <- the error is on this line 158 | _ => {} 159 | } 160 | } 161 | //~^^^^ ERROR `_x @` is not allowed in a tuple struct 162 | //~| ERROR this pattern has 1 field, but the corresponding tuple struct has 3 fields [E0023] 163 | ``` 164 | 165 | #### 无法指定错误行时 166 | 让我们思考一下这个测试 167 | ```rust,ignore 168 | fn main() { 169 | let a: *const [_] = &[1, 2, 3]; 170 | unsafe { 171 | let _b = (*a)[3]; 172 | } 173 | } 174 | ``` 175 | 我们想要确保它显示"超出索引范围"("index out of bounds"),但是我们不能使用`ERROR`注释,因为这个错误没有范围。那么是时候使用`error-pattern`: 176 | ```rust,ignore 177 | // error-pattern: index out of bounds 178 | fn main() { 179 | let a: *const [_] = &[1, 2, 3]; 180 | unsafe { 181 | let _b = (*a)[3]; 182 | } 183 | } 184 | ``` 185 | 但是对于严格测试,请尽量使用`ERROR`注释。 186 | #### 错误等级 187 | 您可以拥有的错误等级是: 188 | 1. `ERROR` 189 | 2. `WARNING` 190 | 3. `NOTE` 191 | 4. `HELP` and `SUGGESTION`* 192 | 193 | \* **注意**: `SUGGESTION`必须紧随`HELP`之后 194 | 195 | 196 | ## 版本 197 | 某些测试类支持"版本"("revision")(截至本文撰写之时,这包括编译失败,运行失败和增量测试,虽然增量测试有些差异)。版本允许将一个测试文件用于多个测试。这通过在文件顶部添加一个特殊的头部来完成: 198 | ```rust 199 | // revisions: foo bar baz 200 | ``` 201 | 这会导致测试被编译(和测试)三次,一次使用`--cfg foo`,一次使用`--cfg bar`,一次使用`--cfg baz`。因此您可以在测试中使用`#[cfg(foo)]`等来调整每个结果。 202 | 203 | 您也可以将头部和期望的错误信息来自定义为特定的修订。为此,您需要在`//`注释后添加`[foo]`(或者`bar`,`baz`等),如下所示 204 | ```rust 205 | // A flag to pass in only for cfg `foo`: 206 | //[foo]compile-flags: -Z verbose 207 | 208 | #[cfg(foo)] 209 | fn test_foo() { 210 | let x: usize = 32_u32; //[foo]~ ERROR mismatched types 211 | } 212 | ``` 213 | 请注意,并非所有的头部在被自定义为版本时都有意义。例如,`ignore-test`头部(和所有的`ignore`头部)目前只适用于整个测试而不适用于特定的版本。当被自定义为版本时,唯一真正起作用的头部只有错误模式(error patterns)和编译器标志(compiler flags)。 214 | 215 | 216 | 217 | ## UI测试指南 218 | UI测试旨在抓取编译器完整的输出,这样我们可以测试可以测试表现的各个方面。它们通过编译文件(例如[`ui/hello_world/main.rs`](hw-main)),捕获输出,然后进行一些标准化(参见下文)。然后将标准化的结果与名为`ui/hello_world/main.stderr`和`ui/hello_world/main.stdout`的参考文件进行比较。如果其中任意一文件不存在,那么输出必须为空(实际上是[该特定测试][hw]的实例)。如果测试运行失败,我们将打印出当前输出,但是输出也被保存在`build//test/ui/hello_world/main.stdout`(这个路径会被当作测试失败信息的一部分而打印出来),这样你就可以通过运行`diff`等命令来比较。 219 | 220 | [hw-main]: https://github.com/rust-lang/rust/blob/master/src/test/ui/hello_world/main.rs 221 | [hw]: https://github.com/rust-lang/rust/blob/master/src/test/ui/hello_world/ 222 | 223 | 现在我们有大量的UI测试并且一些目录中的条目过多。这是一个问题,因为它对editor/IDE是不友好的并且GitHub UI也不会显示超过1000个的目录。为了解决这个问题并组织语义结构,我们有一个整洁检查(tidy check),用以确保条目数小于1000,我们为每个目录设置了不同的上限。所以,请避免将新测试放在这,并且尝试去寻找更相关的位置。例如,你的测试和闭包相关,你应该把它放在`src/test/ui/closures`。如果你不确定哪里最佳的位置.添加到`src/test/ui/issues/`也是可以的。当到达上限时,你可以通过调整[这][ui test tidy]来增加上限。 224 | 225 | [ui test tidy]: https://github.com/rust-lang/rust/blob/master/src/tools/tidy/src/ui_tests.rs 226 | 227 | ### 不会导致编译错误的测试 228 | 默认情况下,预期UI测试**不会编译**(在这种情况下,应该至少包含一个`//~ERROR`注释)。但是,您也可以在期望编译成功的地方进行UI测试,甚至还可以运行生成的程序。只需要添加任意下列[头部命令](#header_commands): 229 | * `// check-pass`-编译应该成功,但是跳过代码生成(它的代价是昂贵的,在大部分情况下不应该失败) 230 | * `// build-pass`-编译和链接应该成功但是不运行生成的二进制文件 231 | * `// run-pass` -编译应该成功并且我们应该运行生成的二进制文件 232 | 233 | 234 | ### 标准化 235 | 编译器的输出被标准化以消除不同平台输出的差异,主要和文件名相关。 236 | 237 | 下面的字符串会被替换成相应的值: 238 | * `$DIR`:被定义为测试的目录 239 | * 例如:`/path/to/rust/src/test/ui/error-codes` 240 | * `$SRC_DIR`:源码根目录 241 | * 例如:`/path/to/rust/src` 242 | * `$TEST_BUILD_DIR`:测试输出所在的基本目录 243 | * 例如:`/path/to/rust/build/x86_64-unknown-linux-gnu/test/ui` 244 | 245 | 此外,会进行以下更改: 246 | * `$SRC_DIR`中的行号和列号被`LL:CC`代替。例如,`/path/to/rust/library/core/src/clone.rs:122:8` 被替代为`$SRC_DIR/core/src/clone.rs:LL:COL`。 247 | 248 | 注意:指向测试的`-->`行的行号和列号是*未*规范的,并保持原样。这确保编译器继续指向正确的位置并且保持stderr文件的可读性。理想情况下,所有行和列的信息都被保留,但是源的小变化会造成巨大的差异,更为频繁的合并冲突和测试错误。另请参见下面的`-Z ui-testing`,它适用于附加的行号规范化。 249 | * `\t`被替换为实际的制表符 250 | * 错误行注释例如`// ~Error some messgage`被移除 251 | * 反斜杠(`\`)在路径内转换为正斜杠(`/`)(使用启发式)。这有助于规范Windows样式路径的差异。 252 | * CRLF换行符被转换为LF。 253 | 254 | 此外,编译器使用`-Z ui-testing`标志运行,这导致编译器本身对诊断输出进行一些修改以使其更适合于UI测试。例如,它将匿名化输出中的行好(每行源代码前的行号会被替换为`LL`)。在极少数情况下,可以使用头部命令`// compile-flags: -Z ui-testing=no`来禁用此模式。 255 | 256 | 257 | 有时,这些内置的规范化并不够。在这种情况下,你可以提供通过头部命令自定义的规范规则,例如 258 | ```rust 259 | // normalize-stdout-test: "foo" -> "bar" 260 | // normalize-stderr-32bit: "fn\(\) \(32 bits\)" -> "fn\(\) \($$PTR bits\)" 261 | // normalize-stderr-64bit: "fn\(\) \(64 bits\)" -> "fn\(\) \($$PTR bits\)" 262 | ``` 263 | 这告诉测试,在32位平台上,只要编译器将`fn() (32 bits)`写入stderr时,都应该被标准化为读取`fn() ($PTR bits)`。64位同样如此。替换是由正则表达式完成,它使用由`regex`crate提供的默认正则风格。 264 | 265 | 相应的参考文件将使用规范化的输出来测试32位和64位平台: 266 | ```text 267 | ... 268 | | 269 | = note: source type: fn() ($PTR bits) 270 | = note: target type: u16 (16 bits) 271 | ... 272 | ``` 273 | 请参阅[`ui/transmute/main.rs`][mrs]和 [`main.stderr`][]了解具体的用法示例。 274 | [mrs]: https://github.com/rust-lang/rust/blob/master/src/test/ui/transmute/main.rs 275 | [`main.stderr`]: https://github.com/rust-lang/rust/blob/master/src/test/ui/transmute/main.stderr 276 | 277 | 除了`normalize-stderr-32bit`和`-64bit`,在这里也可以使用 [`ignore-X`](#ignoring-tests) 支持的任何目标信息或阶段(例如`normalize-stderr-windows` 或简单地使用`normalize-stderr-test` 进行无条件替代) -------------------------------------------------------------------------------- /src/tests/intro.md: -------------------------------------------------------------------------------- 1 | # 编译器测试框架 2 | 3 | Rust项目可以运行各种不同的测试,(它们)由构建系统(`x.py test`)编排。测试编译器本身的主要测试工具是一个叫做compiletest的工具(位于[`src/tools/compiletest`]目录)。本节简要介绍如何设置测试框架,然后将会详细介绍[如何运行测试](./running.html)和[如果添加新测试](./adding.html) 4 | 5 | 6 | [`src/tools/compiletest`]: https://github.com/rust-lang/rust/tree/master/src/tools/compiletest 7 | 8 | 9 | ## Compiletest测试套件 10 | compiletest测试位于[`src/test`]的目录树中。您会在其中看见一系列的子目录(例如`ui`,`run-make`等)。每一个这样的目录都被称为**测试套件**-它们包含一组以不同模式运行的测试。 11 | 12 | [`src/test`]: https://github.com/rust-lang/rust/tree/master/src/test  13 | 14 | 下列是一个对于测试套件及其含义的简要概述。在某些情况下,测试套件会连接到手册的各部分,以提供更多信息细节。 15 | - [`ui`]((./adding.html#ui))-从编译和/或运行测试中检查正确的stdout/stderr的测试 16 | - `run-pass-valgrind`-应该与valgrind一起运行的测试 17 | - `pretty`-针对Rust的“打印美化器”进行测试,从AST生成有效的Rust代码 18 | - `debuginfo`-在gdb或lldb中运行并查找调试信息的测试 19 | - `codegen`-编译然后测试生成的LLVM代码,以确保我们预期的优化生效的测试。欲了解如何编写此类测试的信息,请参见[LLVM docs](https://llvm.org/docs/CommandGuide/FileCheck.html) 20 | - `codegen-units`-有关[单态化](../backend/monomorph.md)和CGU分区的测试 21 | - `assembly`-与`codegen`测试类似,但会验证程序集输出以确保LLVM目标后端可以处理提供的代码。 22 | - `mir-opt`-检查部分生成的MIR,以确保我们在正确构建事物并且正在进行我们期望的优化的测试。 23 | - `incremental`-针对增量编译的测试,检查当执行某些特定修改后,我们能否重用以前的编译结果 24 | - `run-make`-基本上只执行`Makefile`的测试,非常的灵活但是编写起来也会非常麻烦。 25 | - `rustdoc`-针对rustdoc的测试,确保生成的文件中含有期望的文档内容。 26 | - `rustfix`-应用了 [diagnostic suggestions](../diagnostics.md#suggestions)和[`rustfix`](https://github.com/rust-lang/rustfix/) crate的测试 27 | - `*-fulldeps`-与上述相同,单表示测试依赖除`std`有以外的东西(因此必须构建这些东西) 28 | 29 | ## 其它测试 30 | 31 | Rust构建系统可以处理其它的各种测试,包括: 32 | - **Tidy**-这是一个自定义的工具,用于验证源代码风格和编码规范,例如拒绝长行。在[关于编码规范部分](../conventions.html#formatting)有更多的信息。 33 | 34 | 范例:`./x.py test tidy` 35 | 36 | - **格式**-Rustfmt与构建系统集成在一起,用以在整个编译器中实施统一的样式。在CI中,我们检查格式是否正确。 格式检查也可以通过上述Tidy工具自动运行。 37 | - **单元测试**-Rust标准库和许多Rust软件包都包含典型的Rust`#[test]`单元测试。在后台,`x.py`将对每个软件包运行`cargo test`来运行所有测试。 38 | 39 | 范例:`./x.py test library/std` 40 | 41 | - **文档测试**-嵌入在Rust文档中的示例代码是通过`rustdoc --test`执行的。例如: 42 | 43 | `./x.py test src/doc`-对所有在`src/doc`中的运行`rustdoc --test`。 44 | 45 | `./x.py test --doc library/std` -在标准库上运行`rustdoc --test`。 46 | 47 | - **链接检查**-一个用于验证文档中的`href`链接的小工具。 48 | - **分发检查**-用于验证由构建系统创建的源代码分发压缩包的解压、构建和运行所有测试。 49 | 50 | 范例:`./x.py test distcheck` 51 | 52 | - **工具测试**-Rust随附的软件包也都可以正常运行(通常通过在目录中运行`cargo test`)。这包括诸如cargo,clippy,rustfmt,rls,miri,bootstrap(测试Rust构建系统本身)之类的东西。 53 | 54 | - **Cargo测试**- 这是一个小型的工具,它在一些重要项目(如`servo`,`ripgrep`,`tokei`等)上运行`cargo test`,以确保没有他们没有任何显著回归。 55 | 56 | 范例:- `./x.py test src/tools/cargotest` 57 | 58 | ## 测试基础架构 59 | 当GitHub上一个提交请求(Pull Request)被打开之后,[GitHub Actions]将自动启动一个构建,这个构建会在某些配置(x86_64-gnu-llvm-8 linux. x86_64-gnu-tools linux, mingw-check linux)下运行所有测试。本质上,在每个配置构建之后,它会运行`./x.py test`。 60 | 61 | 集成机器人[bors]用于协调主分支的合并,当一个PR被批准后,它将会进入一个[队列],在这里将会使用GitHub Actions在一组广泛的平台上一个个地测试这些合并。由于并行作业数量的限制,除PR外,我们在[rust-lang-ci]组织下运行CI。大多数平台仅仅运行构建步骤,一些平台会运行一组受限的测试,只有一个子集可以运行全套的测试(参见 Rust的[platform tiers]) 62 | 63 | [GitHub Actions]: https://github.com/rust-lang/rust/actions 64 | [rust-lang-ci]: https://github.com/rust-lang-ci/rust/actions 65 | [bors]: https://github.com/servo/homu 66 | [queue]: https://bors.rust-lang.org/queue/rust 67 | [platform tiers]: https://forge.rust-lang.org/release/platform-support.html#rust-platform-support 68 | 69 | ## 使用Docker镜像进行测试 70 | Rust树包含[`src/ci/docker`]中GitHub Actions所使用的平台的[Docker]镜像定义。[`src/ci/docker/run.sh`]被用于构建、运行Docker镜像,在镜像中构建Rust,然后运行测试。 71 | 72 | 您可以在本地开发计算机上运行这些映像。这对于测试与本地系统不同的环境可能会有所帮助。首先,您需要在Linux,Windows或macOS系统上安装Docker(通常Linux将比Windows或macOS快得多,因为稍后将使用虚拟机来模拟Linux环境)。想要在容器中启动bash shell进入交互模式,请运行`src/ci/docker/run.sh --dev `,其中``是`src/ci/docker`中目录名称之一(例如`x86_64-gnu`是一个相当标准的Ubuntu环境)。 73 | 74 | 75 | docker脚本将以只读模式挂载本地rust源树,以读写模式挂载`obj`目录。所有的编译器工件都将被存储在`obj`目录中。shell将会从`obj`目录开始。从那里,您可以运行`../src/ci/run.sh`,这将运行镜像定义的构建。 76 | 77 | 另外,您可以运行单个命令来执行特定的任务。例如,您可以运行`python3 ../x.py test src/test/ui`来仅运行UI测试。请注意[`src / ci / run.sh`]脚本中有一些配置可能需要重新创建。特别是,在您的`config.toml`中设置`submodules=false`,以便它不会尝试修改只读目录。 78 | 79 | 有关使用Docker镜像的一些其他说明: 80 | - 一些std测试需要IPv6的支持。Linux上的Docker似乎默认禁用了它。在创建容器之前,运行[`enable-docker-ipv6.sh`]中的命令以启用IPv6。这仅需要执行一次。 81 | 82 | - 当您退出shell之后,容器将自动删除,但是构建工件仍然保留在`obj`目录中。如果您在不同的Docker映像之间切换,则存储在`obj`目录中的先前环境中的工件可能会混淆构建系统。有时候在容器内构建之前,您需要删除部分或全部`obj`目录。 83 | - 容器是一个只有最小数量的包的准系统,您可能需要安装`apt install less vim`之类的东西。 84 | - 您可以在容器内打开多个shell。首先您需要知道容器的名字(一个简短的哈希),它显示在shell的提示符中,或者您可以在容器外部运行`docker container ls`列出可用的容器。使用容器名称运行`docker exec -it /bin/bash`,其中``是例如`4ba195e95cef`的容器名称。 85 | 86 | 87 | [Docker]: https://www.docker.com/ 88 | [`src/ci/docker`]: https://github.com/rust-lang/rust/tree/master/src/ci/docker 89 | [`src/ci/docker/run.sh`]: https://github.com/rust-lang/rust/blob/master/src/ci/docker/run.sh 90 | [`src/ci/run.sh`]: https://github.com/rust-lang/rust/blob/master/src/ci/run.sh 91 | [`enable-docker-ipv6.sh`]: https://github.com/rust-lang/rust/blob/master/src/ci/scripts/enable-docker-ipv6.sh 92 | 93 | ## 在远程计算机上运行测试 94 | 95 | 测试可以在远程计算机上运行(例如:针对不同的架构测试构建)。这通过使用构建计算机上的`remote-test-client`向`remote-test-server`发送测试程序并在远程计算机上运行实现。`remote-test-server`执行测试程序并且将结果返回给构建计算机。`remote-test-server`提供*未经身份验证的远程代码执行*,所以在使用它的时候请务必小心。 96 | 97 | 为此,首先为远程计算机构建`remote-test-server`,例如,用RISC-V 98 | 99 | ```sh 100 | ./x.py build src/tools/remote-test-server --target riscv64gc-unknown-linux-gnu 101 | ``` 102 | 二进制文件将在`./build/$HOST_ARCH/stage2-tools/$TARGET_ARCH/release/remote-test-server`被创建。将该文件复制到远程计算机。 103 | 104 | 在远程计算机上,运行带有`remote`参数的`remote-test-server`(以及可选的-v表示详细输出)。 输出应如下所示: 105 | 106 | ```sh 107 | $ ./remote-test-server -v remote 108 | starting test server 109 | listening on 0.0.0.0:12345! 110 | ``` 111 | 您可以通过连接到远程测试服务器并发送`ping\n`来测试其是否正常工作。 它应该回复`pong`: 112 | ```sh 113 | $ nc $REMOTE_IP 12345 114 | ping 115 | pong 116 | ``` 117 | 要使用远程运行程序运行测试,请设置`TEST_DEVICE_ADDR`环境变量,然后照常使用`x.py`。例如,要对IP地址为`1.2.3.4`的RISC-V计算机运行`ui`测试,请使用 118 | ```sh 119 | export TEST_DEVICE_ADDR="1.2.3.4:12345" 120 | ./x.py test src/test/ui --target riscv64gc-unknown-linux-gnu 121 | ``` 122 | 如果`remote-test-server`是使用详细标志运行的,则测试计算机上的输出可能类似于 123 | ``` 124 | [...] 125 | run "/tmp/work/test1007/a" 126 | run "/tmp/work/test1008/a" 127 | run "/tmp/work/test1009/a" 128 | run "/tmp/work/test1010/a" 129 | run "/tmp/work/test1011/a" 130 | run "/tmp/work/test1012/a" 131 | run "/tmp/work/test1013/a" 132 | run "/tmp/work/test1014/a" 133 | run "/tmp/work/test1015/a" 134 | run "/tmp/work/test1016/a" 135 | run "/tmp/work/test1017/a" 136 | run "/tmp/work/test1018/a" 137 | [...] 138 | ``` 139 | 测试实在运行`x.py`的计算机上构建的而不是在远程计算机上。意外构建错误的测试可能会失败,并且将无需在远程计算机上运行。 140 | 141 | ## 在模拟器上测试 142 | 143 | 某些平台已通过仿真器针对尚不可用的体系结构进行了测试。对于良好支持标准库和宿主系统支持TCP/IP网络的体系结构,请参见上述有关在远程计算机上测试的说明(在这种情况下将模拟远程计算机) 144 | 145 | 146 | 这是一组用于在仿真环境中协调运行测试的工具。设置了诸如 `arm-android` 和`arm-unknown-linux-gnueabihf`之类的平台,以在GitHub Actions的仿真下自动运行测试。接下来我们将窥探一下如何在仿真下运行目标测试。 147 | 148 | [armhf-gnu]的Docker镜像包含[QEMU]来模拟ARM CPU架构.Rust树中包含的工具[remote-test-client]和[remote-test-server]是将测试程序和库发送到仿真计算机,并在仿真计算机中运行测试并读取结果的程序。Docker被设置为启动`remote-test-server` ,并且用`remote-test-server`来构建工具与服务器通信以协调正在运行的测试。(请参阅[src/bootstrap/test.rs]) 149 | 150 | > TODO: 151 | > 是否支持使用IOS模拟器? 152 | > 153 | > 同时我也也不清楚wasm或asm.js测试如何运行 154 | 155 | [armhf-gnu]: https://github.com/rust-lang/rust/tree/master/src/ci/docker/host-x86_64/armhf-gnu/Dockerfile 156 | [QEMU]: https://www.qemu.org/ 157 | [remote-test-client]: https://github.com/rust-lang/rust/tree/master/src/tools/remote-test-client 158 | [remote-test-server]: https://github.com/rust-lang/rust/tree/master/src/tools/remote-test-server 159 | [src/bootstrap/test.rs]: https://github.com/rust-lang/rust/tree/master/src/bootstrap/test.rs 160 | 161 | 162 | ## Crater 163 | [Crater](https://github.com/rust-lang/crater)是一个为[crates.io](https://crates.io)中的*每个*测试进行编译和运行的工具。它主要用于当实施潜在的重要更改时,检查破坏的程度,并且通过运行beta和stable编译器版本来确保没有破坏。 164 | 165 | ### 何时运行Crater 166 | 167 | 如果您的PR对编译器造成了很大更改或者可能导致损坏,那么您应该运行crater。如果您不确定,请随时询问您的PR审阅者。 168 | 169 | ### 要求运行Crater 170 | 171 | rust小组维护了一些机器,这些机器可以用来PR引入修改下运行crater。如果您的PR需要运行cater,请在PR线中为会审小组留下评论。请告知团队是否需要运行`check-only`crater,运行`build-only`crater或者运行`build-and-test`crater。区别主要时间。保守选项(如果您不确定)是运行build-and-test。如果您的修改仅在编译时(例如,实现新trait)起作用,那么您只需要check run。 172 | 173 | 会审小组会将您的PR入队,并且在结果准备好时将结果发布。check run大约需要3~4天,其它两个平均需要5~6天。 174 | 175 | 尽管crater非常有用,但注意一些注意事项也很重要: 176 | 177 | - 并非所有代码都在crates.io上! 也有很多代码在GitHub和其它地方的仓库中。此外,公司可能不希望发布其代码。因此,crater运行成功并不是万无一失的神奇绿灯。您仍然需要小心。 178 | - Crater仅在x86_64上运行Linux构建。 因此,其它体系结构和平台没有测试。最重要的是,这包括Windows。 179 | - 许多crate未经测试。许多crate未经测试。这可能有很多原因,包括crate不再编译(例如使用的旧的nightly特性),测试失败或不稳定,需要网络访问或其他原因。 180 | - 在crater运行之前,必须先使用`@bors try`来成功构建工件。这意味着,如果您的代码无法编译,则无法运行crater。 181 | 182 | 183 | ## 性能运行 184 | 为了改善编译器的性能并防止性能下降,需要进行大量工作。“性能运行”用于比较大量流行crate在不同配置下编译器的性能。不同的配置包括“新构建”,带有增量编译的构建等。 185 | 186 | 性能运行的结果是两个版本的编译器之间的比较(通过它们的提交哈希(commit hash))。 187 | 188 | 如果您的PR可能会影响性能,尤其是可能对性能产生不利影响,则应请求进行性能测试。 189 | 190 | ## 进一步阅读 191 | 192 | 以下博客文章也可能会引起您的兴趣: 193 | - brson的经典文章[“如何测试Rust”] [howtest] 194 | 195 | [howtest]: https://brson.github.io/2017/07/10/how-rust-is-tested 196 | 197 | 198 | -------------------------------------------------------------------------------- /src/tests/running.md: -------------------------------------------------------------------------------- 1 | # 运行测试 2 | 3 | 您可以使用x.py来运行测试。这是最基本的命令-您几乎永远不想使用它!–如下: 4 | ``` 5 | ./x.py test 6 | ``` 7 | 这将构建第1阶段的编译器,然后运行整个测试套件。 您可能不想经常执行此操作,因为这需要很长时间,并且无论如何bors/GitHub Actions都会为您执行此操作。(通常,在打开我认为已完成的PR后,我会在后台运行此命令,但很少这样做。-nmatsakis) 8 | 9 | 测试结果将被缓存,并且在测试过程中以前成功的测试将被`忽略`。stdout/stderr内容以及每个测试的时间戳文件都可以在build/ARCH/test/下找到。要强制重新运行测试(例如,如果测试运行程序未能注意到更改),您只需删除时间戳文件即可。 10 | 11 | 请注意,某些测试需要启用支持Python的gdb。您可以通过在gdb中使用`python`命令来测试gdb安装是否支持Python。调用后,您可以输入一些Python代码(例如`print("hi")`),然后返回,然后再按CTRL + D执行它。如果要从源代码构建gdb,则需要使用`--with-python = `进行配置。 12 | 13 | ## 运行部分测试套件 14 | 在特定PR上工作时,您通常将需要运行少量测试。例如,可以在修改rustc之后使用一个好的“冒烟测试”,以查看事物是否正常运行,如下所示: 15 | ```bash 16 | ./x.py test src/test/{ui,compile-fail} 17 | ``` 18 | 19 | 这将运行`ui`和`compile-fail`测试套件。当然,测试套件的选择有些随意,并且可能不适合您正在执行的任务。例如,如果您正在使用debuginfo进行调试,那么使用debuginfo测试套件可能会更好: 20 | ```bash 21 | ./x.py test src/test/debuginfo 22 | ``` 23 | 24 | 如果您只需要为任何给定的测试套件测试特定的测试子目录,则可以将该目录传递给`x.py test`: 25 | ```bash 26 | ./x.py test src/test/ui/const-generics 27 | ``` 28 | 同样,您可以通过传递单个文件的路径来测试该文件: 29 | ```bash 30 | ./x.py test src/test/ui/const-generics/const-test.rs 31 | ``` 32 | 33 | ### 只运行整洁测试脚本 34 | ```bash 35 | ./x.py test tidy 36 | ``` 37 | ### 在标准库上运行测试 38 | ```bash 39 | ./x.py test --stage 0 library/std 40 | ``` 41 | ### 运行整洁测试脚本并且在标准库上运行测试 42 | ```bash 43 | ./x.py test --stage 0 tidy library/std 44 | ``` 45 | ### 使用阶段1编译器在标准库上运行测试 46 | ```bash 47 | ./x.py test library/std 48 | ``` 49 | 50 | 通过列出要运行的测试套件,可以避免为根本没有更改的组件运行测试。 51 | 52 | **警告:**请注意,bors仅在完整的第2阶段构建中运行测试;因此,尽管测试在第1阶段**通常**可以正常进行,但仍有一些局限。 53 | 54 | ## 运行单个测试 55 | 56 | 人们想要做的另一件事是运行**单个测试**,通常是他们试图修复的测试。如前所述,您可以传递完整的文件路径来实现这一目标,或者可以使用`--test-args`选项调用`x.py`: 57 | ```bash 58 | ./x.py test src/test/ui --test-args issue-1234 59 | ``` 60 | 在后台,测试运行程序调用标准rust测试运行程序(与您在`#[test]`中获得的运行程序相同),因此此命令将最终筛选出名称中包含`issue-1234`的测试。(因此,`--test-args`是运行相关测试集合的好方法。) 61 | 62 | ## 编辑和更新参考文件 63 | 如果您有意更改了编译器的输出,或者正在进行新的测试,那么您可以将`--bless`传递给test子命令。例如,如果`src/test/ui`中的某些测试失败,则可以运行 64 | ```bash 65 | ./x.py test src/test/ui --bless 66 | ``` 67 | 来自动调整`.stderr`,`.stdout`或者`.fixed`文件中的所有测试。当然,您也可以使用`--test-args your_test_name`标志来定位特定的测试,就像运行测试时一样。 68 | 69 | 70 | ## 传递`--pass $mode` 71 | 72 | 通过UI测试现在具有三种模式:`check-pass`, `build-pass` 和`run-pass`。当传递`--pass $mode`时,这些测试将被强制在给定的`$mode`下运行,除非指令测试文件存在指令`//ignore-pass`。您可以将`src/test/ui`中的所有测试作为`check-pass`运行: 73 | ```bash 74 | ./x.py test src/test/ui --pass check 75 | ``` 76 | 通过传递`--pass $mode`,可以减少测试时间。对于每种模式,请参见[此处][mode]。 77 | 78 | [mode]:./adding.md#不会导致编译错误的测试  79 | 80 | ## 使用增量编译 81 | 82 | 您可以进一步启用`--incremental`标志,以在以后的重建中节省更多时间: 83 | ```bash 84 | ./x.py test src/test/ui --incremental --test-args issue-1234 85 | ``` 86 | 如果您不想在每个命令中都包含该标志,则可以在`config.toml`中启用它: 87 | ```toml 88 | [rust] 89 | incremental = true 90 | ``` 91 | 请注意,增量编译将使用比平常更多的磁盘空间。如果您担心磁盘空间,则可能需要不时地检查`build`目录的大小。 92 | 93 | ## 使用不同的“比较模式”运行测试 94 | UI测试可能会有不同的输出,具体取决于编译器所处的特定“模式”。例如,当处于“非词法作用域生命周期”("non-lexical liftimes",NLL)模式时,测试`foo.rs`将首先在`foo.nll.stderr`中寻找期望的输出,如果没有找到,则回到寻常的`foo.stderr`。要以NLL模式运行UI测试套件,可以使用以下命令: 95 | ```bash 96 | ./x.py test src/test/ui --compare-mode=nll 97 | ``` 98 | 其它比较模式的示例是"noopt","migrat"和[revisions](./adding.html#版本)。 99 | 100 | ## 手动运行测试 101 | 有时候,手动进行测试会更容易,更快捷。 大多数测试只是`rs`文件,因此您可以执行操作类似 102 | ```bash 103 | rustc +stage1 src/test/ui/issue-1234.rs 104 | ``` 105 | 这要快得多,但并不总是有效。例如,某些测试包含指定特定的编译器标志或依赖于其它crate的指令,并且如果没有这些选项,它们可能无法以相同的方式运行。 -------------------------------------------------------------------------------- /src/the-parser.md: -------------------------------------------------------------------------------- 1 | # 分析(lexing)和解析(parsing) 2 | 3 | 2021年一月,词法分析器(lexer)和解析器(parser)正在进行重构, 以允许将它们提取到库(libraries)中。 4 | 5 | 编译器要做的第一件事是将程序(Unicode字符)转换为比字符串更方便编译器使用的内容。这发生在词法分析(lexing)和解析(parsing)阶段。 6 | 7 | 词法分析(lexing)接受字符串并将其转换成 [tokens] 流(streams of tokens)。例如, 8 | `a.b + c` 会被转换成 tokens `a`, `.`, `b`, `+`, `c` 。该词法分析器(lexer)位于 9 | [`rustc_lexer`] 中。 10 | 11 | [tokens]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/token/index.html 12 | [lexer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lexer/index.html 13 | 14 | 解析(Parsing)接受 [tokens] 流(streams of tokens)并将其装换位结构化的形式,这对于编译器来说更加容易使用,通常成为抽象语法树([*Abstract 15 | Syntax Tree*][ast],AST)。AST 镜像内存中的Rust 程序的结构(structure),使用 `Span` 将特定的 AST 节点链接(link)回其源文本。 16 | 17 | 在 [`rustc_ast`][rustc_ast] 中定义了 AST ,此外还有一些关于 tokens 和 tokens 流(tokens and token streams)的定义,用于变异的(mutating) ASTs 数据结构/特征(traits),以及用于编译器的其他 AST 相关部分的共享定义(如词法分析器和宏扩张)。 18 | 19 | 解析器(parser)是在 [`rustc_parse`][rustc_parse] 中定义的,以及词法分析器(lexer)的高级接口和在宏展开后运行的一些验证例行程序。特别是,[`rustc_parse::parser`][parser] 包含了解析器(parser)的实现 20 | 21 | 解析器的主入口是通过各种在 [parser crate][parser_lib] 中的`parse_*`函数和其他函数。它们允许你将[`SourceFile`][sourcefile](例如单个文件的源文件)转换为 token 流(token stream ),从 token 流(token stream )创建解析器(parser),然后执行解析器(parser)去获得一个`Crate`(AST 的 root 节点) 22 | 23 | 为了减少复制的次数,`StringReader` 和 `Parser` 的生命周期都绑定到父节点 `ParseSess`。它包含了解析时所需要的所有信息以及 `SourceMap` 本身。 24 | 25 | 注意,在解析时,我们可能遇到宏定义或调用,我们把这些放在一旁以进行展开 (见 [本章](./macro-expansion.md))。展开本身可能需要解析宏的输出,这可能会涉及到更多需要展开的宏,等等。 26 | 27 | ## 更多源于词法分析(Lexical Analysis) 28 | 词法分析的代码被分为两个箱子(crates): 29 | - `rustc_lexer` crate 负责将 `&str` 分解为组成标记的块。将分析器(lexer)作为生成的有限状态机来实现是很流行的,但`rustc_lexer`重的分析器(lexer)是手写的。 30 | 31 | - 来自于 [`rustc_ast`][rustc_ast] 的 [`StringReader`] 将 `rustc_lexer` 与 `rustc` 详细的数据结构集成在一起。具体来说,它将 `Span` 信息添加到 `rustc_lexer` 和 interns 标识符返回的 tokens 中。 32 | 33 | 34 | [rustc_ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/index.html 35 | [rustc_errors]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/index.html 36 | [ast]: https://en.wikipedia.org/wiki/Abstract_syntax_tree 37 | [`SourceMap`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/source_map/struct.SourceMap.html 38 | [ast module]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html 39 | [rustc_parse]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/index.html 40 | [parser_lib]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/index.html 41 | [parser]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/parser/index.html 42 | [`Parser`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/parse/parser/struct.Parser.html 43 | [`StringReader`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_parse/lexer/struct.StringReader.html 44 | [visit module]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/index.html 45 | [sourcefile]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.SourceFile.html 46 | -------------------------------------------------------------------------------- /src/traits/caching.md: -------------------------------------------------------------------------------- 1 | # Caching subtleties 2 | -------------------------------------------------------------------------------- /src/traits/canonical-queries.md: -------------------------------------------------------------------------------- 1 | # Canonical queries 2 | -------------------------------------------------------------------------------- /src/traits/chalk.md: -------------------------------------------------------------------------------- 1 | # Chalk-based trait solving 2 | -------------------------------------------------------------------------------- /src/traits/goals-and-clauses.md: -------------------------------------------------------------------------------- 1 | # Goals and clauses 2 | -------------------------------------------------------------------------------- /src/traits/hrtb.md: -------------------------------------------------------------------------------- 1 | # Higher-ranked trait bounds 2 | -------------------------------------------------------------------------------- /src/traits/lowering-to-logic.md: -------------------------------------------------------------------------------- 1 | # Lowering to logic 2 | -------------------------------------------------------------------------------- /src/traits/resolution.md: -------------------------------------------------------------------------------- 1 | # Trait solving 2 | -------------------------------------------------------------------------------- /src/traits/specialization.md: -------------------------------------------------------------------------------- 1 | # Specialization 2 | -------------------------------------------------------------------------------- /src/ty-fold.md: -------------------------------------------------------------------------------- 1 | # TypeFolder and TypeFoldable 2 | -------------------------------------------------------------------------------- /src/ty.md: -------------------------------------------------------------------------------- 1 | # The ty module: representing types 2 | -------------------------------------------------------------------------------- /src/type-checking.md: -------------------------------------------------------------------------------- 1 | # 类型检查 2 | 3 | [`rustc_typeck`][typeck] crate 包含"类型收集"和"类型检查"的源代码,和其它一些相关功能。(它很大程度依赖于[type inference]和[trait solving]。) 4 | 5 | [typeck]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_typeck/index.html 6 | [type inference]: ./type-inference.md 7 | [trait solving]: ./traits/resolution.md 8 | 9 | ## 类型收集 10 | 11 | 类型"收集"是将用户写入的语法内容 HIR(`hir::Ty`) 中的类型转化为编译器使用的**内部表示**(`Ty<'tcx>`)的过程 – 对 where 子句和函数签名的其他位也进行类似的转换。 12 | 13 | 为了尝试并感受到这种差异,请考虑下面的函数: 14 | 15 | ```rust,ignore 16 | struct Foo { } 17 | fn foo(x: Foo, y: self::Foo) { ... } 18 | // ^^^ ^^^^^^^^^ 19 | ``` 20 | 21 | 这两个参数 `x` 和 `y` 有相同的类型: 但他们是不懂的 `hir::Ty` 节点。这些节点有不同的 span,当然它们的编码路径也有所不同。但它们一旦"被收集"到 `Ty<'tcx>` 节点,它们会使用完全相同的内部类型。 22 | 23 | 集合被定义为计算关于正在编译的 crate 中的各种函数、特性和其他项的信息的一组[查询][queries]。请注意,每个查询都与*过程间*事物有关——例如,对于函数定义,集合将计算出函数的类型和签名,但它不会以任何方式访问*函数体*,也不会检查局部变量的类型注释(这是类型*检查*的工作)。 24 | 25 | 更多有关详细信息,请参阅 [`collect`][collect] 模块。 26 | 27 | [queries]: ./query.md 28 | [collect]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_typeck/collect/ 29 | 30 | **TODO**: 实际上谈到类型检查... -------------------------------------------------------------------------------- /src/type-inference.md: -------------------------------------------------------------------------------- 1 | # Type inference 2 | -------------------------------------------------------------------------------- /src/variance.md: -------------------------------------------------------------------------------- 1 | # Variance 2 | -------------------------------------------------------------------------------- /src/walkthrough.md: -------------------------------------------------------------------------------- 1 | # Walkthrough: a typical contribution 2 | --------------------------------------------------------------------------------