Auto merge of #142795 - Kobzol:rollup-vvnnjno, r=Kobzol
Rollup of 10 pull requests Successful merges: - rust-lang/rust#142629 (Add config builder for bootstrap tests) - rust-lang/rust#142715 (correct template for `#[align]` attribute) - rust-lang/rust#142720 (De-dup common code from `ExternalCrate` methods) - rust-lang/rust#142736 (add issue template for rustdoc) - rust-lang/rust#142743 (rustc-dev-guide subtree update) - rust-lang/rust#142744 (Add a mailmap entry for y21) - rust-lang/rust#142758 (Make sure to rebuild rustdoc if `src/rustdoc-json-types` is changed) - rust-lang/rust#142764 (Convert `ilog(10)` to `ilog10()`) - rust-lang/rust#142767 (Some symbol and PathRoot cleanups) - rust-lang/rust#142769 (remove equivalent new method on context) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
5526a2f47c
58 changed files with 573 additions and 284 deletions
54
.github/ISSUE_TEMPLATE/rustdoc.md
vendored
Normal file
54
.github/ISSUE_TEMPLATE/rustdoc.md
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
name: Problem with rustdoc
|
||||
about: Report an issue with how docs get generated.
|
||||
labels: C-bug, T-rustdoc
|
||||
---
|
||||
<!--
|
||||
Thank you for filing a rustdoc issue! Rustdoc is the tool that handles the generation of docs. It is usually invoked via `cargo doc`, but can also be used directly.
|
||||
|
||||
If you have an issue with the actual content of the docs, use the "Documentation problem" template instead.
|
||||
-->
|
||||
|
||||
# Code
|
||||
<!-- problematic snippet and/or link to repo and/or full path of standard library function -->
|
||||
|
||||
```rust
|
||||
<code>
|
||||
```
|
||||
|
||||
# Reproduction Steps
|
||||
<!--
|
||||
* command(s) to run, if any
|
||||
* permalink to hosted documentation, if any
|
||||
* search query, if any
|
||||
-->
|
||||
|
||||
# Expected Outcome
|
||||
<!--
|
||||
What did you want to happen?
|
||||
|
||||
For GUI issues, feel free to provide a mockup image of what you want it to look like.
|
||||
|
||||
For diagnostics, please provide a mockup of the desired output in a code block.
|
||||
-->
|
||||
|
||||
# Actual Output
|
||||
<!--
|
||||
* rustdoc console output
|
||||
* browser screenshot of generated html
|
||||
* rustdoc json (prettify by running through `jq` or running thorugh an online formatter)
|
||||
-->
|
||||
```console
|
||||
<code>
|
||||
```
|
||||
|
||||
|
||||
# Version
|
||||
<!--
|
||||
Available via `rustdoc --version` or under the "Help" menu.
|
||||
|
||||
If the issue involves opening the documentation in a browser, please also provide the name and version of the browser used.
|
||||
-->
|
||||
|
||||
# Additional Details
|
||||
<!-- Anything else you think is relevant -->
|
||||
1
.mailmap
1
.mailmap
|
|
@ -698,3 +698,4 @@ Zach Pomerantz <zmp@umich.edu>
|
|||
Zack Corr <zack@z0w0.me> <zackcorr95@gmail.com>
|
||||
Zack Slayton <zack.slayton@gmail.com>
|
||||
Zbigniew Siciarz <zbigniew@siciarz.net> Zbigniew Siciarz <antyqjon@gmail.com>
|
||||
y21 <30553356+y21@users.noreply.github.com>
|
||||
|
|
|
|||
|
|
@ -273,7 +273,7 @@ pub(crate) struct AlignParser(Option<(Align, Span)>);
|
|||
|
||||
impl AlignParser {
|
||||
const PATH: &'static [Symbol] = &[sym::align];
|
||||
const TEMPLATE: AttributeTemplate = template!(Word, List: "<alignment in bytes>");
|
||||
const TEMPLATE: AttributeTemplate = template!(List: "<alignment in bytes>");
|
||||
|
||||
fn parse<'c, S: Stage>(
|
||||
&mut self,
|
||||
|
|
|
|||
|
|
@ -422,19 +422,13 @@ impl<'sess> AttributeParser<'sess, Early> {
|
|||
|
||||
parsed.pop()
|
||||
}
|
||||
|
||||
pub fn new_early(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
|
||||
Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'sess> AttributeParser<'sess, Late> {
|
||||
pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
|
||||
Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'sess, S: Stage> AttributeParser<'sess, S> {
|
||||
pub fn new(sess: &'sess Session, features: &'sess Features, tools: Vec<Symbol>) -> Self {
|
||||
Self { features: Some(features), tools, parse_only: None, sess, stage: PhantomData }
|
||||
}
|
||||
|
||||
pub(crate) fn sess(&self) -> &'sess Session {
|
||||
&self.sess
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ pub(crate) fn expand(
|
|||
fn generate_handler(cx: &ExtCtxt<'_>, handler: Ident, span: Span, sig_span: Span) -> Stmt {
|
||||
let usize = cx.path_ident(span, Ident::new(sym::usize, span));
|
||||
let ty_usize = cx.ty_path(usize);
|
||||
let size = Ident::from_str_and_span("size", span);
|
||||
let align = Ident::from_str_and_span("align", span);
|
||||
let size = Ident::new(sym::size, span);
|
||||
let align = Ident::new(sym::align, span);
|
||||
|
||||
let layout_new = cx.std_path(&[sym::alloc, sym::Layout, sym::from_size_align_unchecked]);
|
||||
let layout_new = cx.expr_path(cx.path(span, layout_new));
|
||||
|
|
|
|||
|
|
@ -652,8 +652,10 @@ mod llvm_enzyme {
|
|||
exprs = ecx.expr_call(new_decl_span, bb_call_expr, thin_vec![exprs]);
|
||||
} else {
|
||||
let q = QSelf { ty: d_ret_ty, path_span: span, position: 0 };
|
||||
let y =
|
||||
ExprKind::Path(Some(P(q)), ecx.path_ident(span, Ident::from_str("default")));
|
||||
let y = ExprKind::Path(
|
||||
Some(P(q)),
|
||||
ecx.path_ident(span, Ident::with_dummy_span(kw::Default)),
|
||||
);
|
||||
let default_call_expr = ecx.expr(span, y);
|
||||
let default_call_expr =
|
||||
ecx.expr_call(new_decl_span, default_call_expr, thin_vec![]);
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ pub fn inject(
|
|||
is_test_crate: bool,
|
||||
dcx: DiagCtxtHandle<'_>,
|
||||
) {
|
||||
let ecfg = ExpansionConfig::default("proc_macro".to_string(), features);
|
||||
let ecfg = ExpansionConfig::default(sym::proc_macro, features);
|
||||
let mut cx = ExtCtxt::new(sess, ecfg, resolver, None);
|
||||
|
||||
let mut collect = CollectProcMacros {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ pub fn inject(
|
|||
let span = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id());
|
||||
let call_site = DUMMY_SP.with_call_site_ctxt(expn_id.to_expn_id());
|
||||
|
||||
let ecfg = ExpansionConfig::default("std_lib_injection".to_string(), features);
|
||||
let ecfg = ExpansionConfig::default(sym::std_lib_injection, features);
|
||||
let cx = ExtCtxt::new(sess, ecfg, resolver, None);
|
||||
|
||||
let ident_span = if edition >= Edition2018 { span } else { call_site };
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ fn generate_test_harness(
|
|||
panic_strategy: PanicStrategy,
|
||||
test_runner: Option<ast::Path>,
|
||||
) {
|
||||
let econfig = ExpansionConfig::default("test".to_string(), features);
|
||||
let econfig = ExpansionConfig::default(sym::test, features);
|
||||
let ext_cx = ExtCtxt::new(sess, econfig, resolver, None);
|
||||
|
||||
let expn_id = ext_cx.resolver.expansion_for_ast_pass(
|
||||
|
|
|
|||
|
|
@ -183,12 +183,12 @@ pub(crate) struct FeatureNotAllowed {
|
|||
#[derive(Diagnostic)]
|
||||
#[diag(expand_recursion_limit_reached)]
|
||||
#[help]
|
||||
pub(crate) struct RecursionLimitReached<'a> {
|
||||
pub(crate) struct RecursionLimitReached {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
pub descr: String,
|
||||
pub suggested_limit: Limit,
|
||||
pub crate_name: &'a str,
|
||||
pub crate_name: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS};
|
|||
use rustc_session::parse::feature_err;
|
||||
use rustc_session::{Limit, Session};
|
||||
use rustc_span::hygiene::SyntaxContext;
|
||||
use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, sym};
|
||||
use rustc_span::{ErrorGuaranteed, FileName, Ident, LocalExpnId, Span, Symbol, sym};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::base::*;
|
||||
|
|
@ -473,7 +473,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
let dir_path = file_path.parent().unwrap_or(&file_path).to_owned();
|
||||
self.cx.root_path = dir_path.clone();
|
||||
self.cx.current_expansion.module = Rc::new(ModuleData {
|
||||
mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)],
|
||||
mod_path: vec![Ident::with_dummy_span(self.cx.ecfg.crate_name)],
|
||||
file_path_stack: vec![file_path],
|
||||
dir_path,
|
||||
});
|
||||
|
|
@ -689,7 +689,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||
span: expn_data.call_site,
|
||||
descr: expn_data.kind.descr(),
|
||||
suggested_limit,
|
||||
crate_name: &self.cx.ecfg.crate_name,
|
||||
crate_name: self.cx.ecfg.crate_name,
|
||||
});
|
||||
|
||||
self.cx.trace_macros_diag();
|
||||
|
|
@ -2458,7 +2458,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
|
|||
}
|
||||
|
||||
pub struct ExpansionConfig<'feat> {
|
||||
pub crate_name: String,
|
||||
pub crate_name: Symbol,
|
||||
pub features: &'feat Features,
|
||||
pub recursion_limit: Limit,
|
||||
pub trace_mac: bool,
|
||||
|
|
@ -2471,7 +2471,7 @@ pub struct ExpansionConfig<'feat> {
|
|||
}
|
||||
|
||||
impl ExpansionConfig<'_> {
|
||||
pub fn default(crate_name: String, features: &Features) -> ExpansionConfig<'_> {
|
||||
pub fn default(crate_name: Symbol, features: &Features) -> ExpansionConfig<'_> {
|
||||
ExpansionConfig {
|
||||
crate_name,
|
||||
features,
|
||||
|
|
|
|||
|
|
@ -510,7 +510,7 @@ declare_features! (
|
|||
(unstable, ffi_pure, "1.45.0", Some(58329)),
|
||||
/// Controlling the behavior of fmt::Debug
|
||||
(unstable, fmt_debug, "1.82.0", Some(129709)),
|
||||
/// Allows using `#[repr(align(...))]` on function items
|
||||
/// Allows using `#[align(...)]` on function items
|
||||
(unstable, fn_align, "1.53.0", Some(82232)),
|
||||
/// Support delegating implementation of functions to other already implemented functions.
|
||||
(incomplete, fn_delegation, "1.76.0", Some(118212)),
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ use rustc_hir::{
|
|||
TyPatKind,
|
||||
};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{FileName, Ident, Span, Symbol, kw};
|
||||
use rustc_span::{FileName, Ident, Span, Symbol, kw, sym};
|
||||
use {rustc_ast as ast, rustc_hir as hir};
|
||||
|
||||
pub fn id_to_string(cx: &dyn rustc_hir::intravisit::HirTyCtxt<'_>, hir_id: HirId) -> String {
|
||||
|
|
@ -1517,7 +1517,7 @@ impl<'a> State<'a> {
|
|||
self.bopen(ib);
|
||||
|
||||
// Print `let _t = $init;`:
|
||||
let temp = Ident::from_str("_t");
|
||||
let temp = Ident::with_dummy_span(sym::_t);
|
||||
self.print_local(false, Some(init), None, |this| this.print_ident(temp));
|
||||
self.word(";");
|
||||
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ fn configure_and_expand(
|
|||
// Create the config for macro expansion
|
||||
let recursion_limit = get_recursion_limit(pre_configured_attrs, sess);
|
||||
let cfg = rustc_expand::expand::ExpansionConfig {
|
||||
crate_name: crate_name.to_string(),
|
||||
crate_name,
|
||||
features,
|
||||
recursion_limit,
|
||||
trace_mac: sess.opts.unstable_opts.trace_macros,
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ use rustc_session::lint::builtin::{
|
|||
UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, UNUSED_ATTRIBUTES,
|
||||
};
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, sym};
|
||||
use rustc_span::{BytePos, DUMMY_SP, Span, Symbol, edition, kw, sym};
|
||||
use rustc_trait_selection::error_reporting::InferCtxtErrorExt;
|
||||
use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs};
|
||||
use rustc_trait_selection::traits::ObligationCtxt;
|
||||
|
|
@ -724,7 +724,10 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
&& !matches!(other_attr.path().as_slice(), [sym::rustfmt, ..])
|
||||
{
|
||||
let path = other_attr.path();
|
||||
let path: Vec<_> = path.iter().map(|s| s.as_str()).collect();
|
||||
let path: Vec<_> = path
|
||||
.iter()
|
||||
.map(|s| if *s == kw::PathRoot { "" } else { s.as_str() })
|
||||
.collect();
|
||||
let other_attr_name = path.join("::");
|
||||
|
||||
self.dcx().emit_err(errors::NakedFunctionIncompatibleAttribute {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::mem;
|
|||
use rustc_ast::visit::FnKind;
|
||||
use rustc_ast::*;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_attr_parsing::{AttributeParser, OmitDoc};
|
||||
use rustc_attr_parsing::{AttributeParser, Early, OmitDoc};
|
||||
use rustc_expand::expand::AstFragment;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
|
||||
|
|
@ -128,7 +128,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
|
|||
// FIXME(jdonszelmann) make one of these in the resolver?
|
||||
// FIXME(jdonszelmann) don't care about tools here maybe? Just parse what we can.
|
||||
// Does that prevents errors from happening? maybe
|
||||
let mut parser = AttributeParser::new_early(
|
||||
let mut parser = AttributeParser::<'_, Early>::new(
|
||||
&self.resolver.tcx.sess,
|
||||
self.resolver.tcx.features(),
|
||||
Vec::new(),
|
||||
|
|
|
|||
|
|
@ -396,8 +396,7 @@ symbols! {
|
|||
__S,
|
||||
__awaitee,
|
||||
__try_var,
|
||||
_d,
|
||||
_e,
|
||||
_t,
|
||||
_task_context,
|
||||
a32,
|
||||
aarch64_target_feature,
|
||||
|
|
@ -2052,6 +2051,7 @@ symbols! {
|
|||
static_recursion,
|
||||
staticlib,
|
||||
std,
|
||||
std_lib_injection,
|
||||
std_panic,
|
||||
std_panic_2015_macro,
|
||||
std_panic_macro,
|
||||
|
|
|
|||
|
|
@ -2841,7 +2841,7 @@ macro_rules! impl_to_string {
|
|||
impl SpecToString for $signed {
|
||||
#[inline]
|
||||
fn spec_to_string(&self) -> String {
|
||||
const SIZE: usize = $signed::MAX.ilog(10) as usize + 1;
|
||||
const SIZE: usize = $signed::MAX.ilog10() as usize + 1;
|
||||
let mut buf = [core::mem::MaybeUninit::<u8>::uninit(); SIZE];
|
||||
// Only difference between signed and unsigned are these 8 lines.
|
||||
let mut out;
|
||||
|
|
@ -2861,7 +2861,7 @@ macro_rules! impl_to_string {
|
|||
impl SpecToString for $unsigned {
|
||||
#[inline]
|
||||
fn spec_to_string(&self) -> String {
|
||||
const SIZE: usize = $unsigned::MAX.ilog(10) as usize + 1;
|
||||
const SIZE: usize = $unsigned::MAX.ilog10() as usize + 1;
|
||||
let mut buf = [core::mem::MaybeUninit::<u8>::uninit(); SIZE];
|
||||
|
||||
self._fmt(&mut buf).to_string()
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ macro_rules! impl_Display {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
#[cfg(not(feature = "optimize_for_size"))]
|
||||
{
|
||||
const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1;
|
||||
const MAX_DEC_N: usize = $unsigned::MAX.ilog10() as usize + 1;
|
||||
// Buffer decimals for $unsigned with right alignment.
|
||||
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
|
||||
|
||||
|
|
@ -226,7 +226,7 @@ macro_rules! impl_Display {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
#[cfg(not(feature = "optimize_for_size"))]
|
||||
{
|
||||
const MAX_DEC_N: usize = $unsigned::MAX.ilog(10) as usize + 1;
|
||||
const MAX_DEC_N: usize = $unsigned::MAX.ilog10() as usize + 1;
|
||||
// Buffer decimals for $unsigned with right alignment.
|
||||
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
|
||||
|
||||
|
|
@ -323,7 +323,7 @@ macro_rules! impl_Display {
|
|||
|
||||
#[cfg(feature = "optimize_for_size")]
|
||||
fn $gen_name(mut n: $u, is_nonnegative: bool, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
const MAX_DEC_N: usize = $u::MAX.ilog(10) as usize + 1;
|
||||
const MAX_DEC_N: usize = $u::MAX.ilog10() as usize + 1;
|
||||
let mut buf = [MaybeUninit::<u8>::uninit(); MAX_DEC_N];
|
||||
let mut curr = MAX_DEC_N;
|
||||
let buf_ptr = MaybeUninit::slice_as_mut_ptr(&mut buf);
|
||||
|
|
@ -565,7 +565,7 @@ mod imp {
|
|||
}
|
||||
impl_Exp!(i128, u128 as u128 via to_u128 named exp_u128);
|
||||
|
||||
const U128_MAX_DEC_N: usize = u128::MAX.ilog(10) as usize + 1;
|
||||
const U128_MAX_DEC_N: usize = u128::MAX.ilog10() as usize + 1;
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl fmt::Display for u128 {
|
||||
|
|
|
|||
|
|
@ -716,7 +716,7 @@ impl Step for Rustdoc {
|
|||
&& target_compiler.stage > 0
|
||||
&& builder.rust_info().is_managed_git_subrepository()
|
||||
{
|
||||
let files_to_track = &["src/librustdoc", "src/tools/rustdoc"];
|
||||
let files_to_track = &["src/librustdoc", "src/tools/rustdoc", "src/rustdoc-json-types"];
|
||||
|
||||
// Check if unchanged
|
||||
if !builder.config.has_changes_from_upstream(files_to_track) {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use crate::Flags;
|
|||
use crate::core::build_steps::doc::DocumentationFormat;
|
||||
use crate::core::config::Config;
|
||||
use crate::utils::cache::ExecutedStep;
|
||||
use crate::utils::helpers::get_host_target;
|
||||
use crate::utils::tests::git::{GitCtx, git_test};
|
||||
|
||||
static TEST_TRIPLE_1: &str = "i686-unknown-haiku";
|
||||
|
|
@ -1236,29 +1237,48 @@ fn any_debug() {
|
|||
/// The staging tests use insta for snapshot testing.
|
||||
/// See bootstrap's README on how to bless the snapshots.
|
||||
mod staging {
|
||||
use crate::Build;
|
||||
use crate::core::builder::Builder;
|
||||
use crate::core::builder::tests::{
|
||||
TEST_TRIPLE_1, configure, configure_with_args, render_steps, run_build,
|
||||
};
|
||||
use crate::utils::tests::{ConfigBuilder, TestCtx};
|
||||
|
||||
#[test]
|
||||
fn build_compiler_stage_1() {
|
||||
let mut cache = run_build(
|
||||
&["compiler".into()],
|
||||
configure_with_args(&["build", "--stage", "1"], &[TEST_TRIPLE_1], &[TEST_TRIPLE_1]),
|
||||
);
|
||||
let steps = cache.into_executed_steps();
|
||||
insta::assert_snapshot!(render_steps(&steps), @r"
|
||||
[build] rustc 0 <target1> -> std 0 <target1>
|
||||
[build] llvm <target1>
|
||||
[build] rustc 0 <target1> -> rustc 1 <target1>
|
||||
[build] rustc 0 <target1> -> rustc 1 <target1>
|
||||
let ctx = TestCtx::new();
|
||||
insta::assert_snapshot!(
|
||||
ctx.config("build")
|
||||
.path("compiler")
|
||||
.stage(1)
|
||||
.get_steps(), @r"
|
||||
[build] rustc 0 <host> -> std 0 <host>
|
||||
[build] llvm <host>
|
||||
[build] rustc 0 <host> -> rustc 1 <host>
|
||||
[build] rustc 0 <host> -> rustc 1 <host>
|
||||
");
|
||||
}
|
||||
|
||||
impl ConfigBuilder {
|
||||
fn get_steps(self) -> String {
|
||||
let config = self.create_config();
|
||||
|
||||
let kind = config.cmd.kind();
|
||||
let build = Build::new(config);
|
||||
let builder = Builder::new(&build);
|
||||
builder.run_step_descriptions(&Builder::get_step_descriptions(kind), &builder.paths);
|
||||
render_steps(&builder.cache.into_executed_steps())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Renders the executed bootstrap steps for usage in snapshot tests with insta.
|
||||
/// Only renders certain important steps.
|
||||
/// Each value in `steps` should be a tuple of (Step, step output).
|
||||
///
|
||||
/// The arrow in the rendered output (`X -> Y`) means `X builds Y`.
|
||||
/// This is similar to the output printed by bootstrap to stdout, but here it is
|
||||
/// generated purely for the purpose of tests.
|
||||
fn render_steps(steps: &[ExecutedStep]) -> String {
|
||||
steps
|
||||
.iter()
|
||||
|
|
@ -1275,18 +1295,17 @@ fn render_steps(steps: &[ExecutedStep]) -> String {
|
|||
}
|
||||
let stage =
|
||||
if let Some(stage) = metadata.stage { format!("{stage} ") } else { "".to_string() };
|
||||
write!(record, "{} {stage}<{}>", metadata.name, metadata.target);
|
||||
write!(record, "{} {stage}<{}>", metadata.name, normalize_target(metadata.target));
|
||||
Some(record)
|
||||
})
|
||||
.map(|line| {
|
||||
line.replace(TEST_TRIPLE_1, "target1")
|
||||
.replace(TEST_TRIPLE_2, "target2")
|
||||
.replace(TEST_TRIPLE_3, "target3")
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
fn render_compiler(compiler: Compiler) -> String {
|
||||
format!("rustc {} <{}>", compiler.stage, compiler.host)
|
||||
fn normalize_target(target: TargetSelection) -> String {
|
||||
target.to_string().replace(&get_host_target().to_string(), "host")
|
||||
}
|
||||
|
||||
fn render_compiler(compiler: Compiler) -> String {
|
||||
format!("rustc {} <{}>", compiler.stage, normalize_target(compiler.host))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ use crate::core::download::is_download_ci_available;
|
|||
use crate::utils::channel;
|
||||
use crate::utils::exec::command;
|
||||
use crate::utils::execution_context::ExecutionContext;
|
||||
use crate::utils::helpers::exe;
|
||||
use crate::utils::helpers::{exe, get_host_target};
|
||||
use crate::{GitInfo, OnceLock, TargetSelection, check_ci_llvm, helpers, t};
|
||||
|
||||
/// Each path in this list is considered "allowed" in the `download-rustc="if-unchanged"` logic.
|
||||
|
|
@ -349,7 +349,7 @@ impl Config {
|
|||
stderr_is_tty: std::io::stderr().is_terminal(),
|
||||
|
||||
// set by build.rs
|
||||
host_target: TargetSelection::from_user(env!("BUILD_TRIPLE")),
|
||||
host_target: get_host_target(),
|
||||
|
||||
src: {
|
||||
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
||||
|
|
|
|||
|
|
@ -178,6 +178,11 @@ pub fn symlink_dir(config: &Config, original: &Path, link: &Path) -> io::Result<
|
|||
}
|
||||
}
|
||||
|
||||
/// Return the host target on which we are currently running.
|
||||
pub fn get_host_target() -> TargetSelection {
|
||||
TargetSelection::from_user(env!("BUILD_TRIPLE"))
|
||||
}
|
||||
|
||||
/// Rename a file if from and to are in the same filesystem or
|
||||
/// copy and remove the file otherwise
|
||||
pub fn move_file<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,74 @@
|
|||
//! This module contains shared utilities for bootstrap tests.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::thread;
|
||||
|
||||
use tempfile::TempDir;
|
||||
|
||||
use crate::core::builder::Builder;
|
||||
use crate::core::config::DryRun;
|
||||
use crate::{Build, Config, Flags, t};
|
||||
|
||||
pub mod git;
|
||||
|
||||
/// Holds temporary state of a bootstrap test.
|
||||
/// Right now it is only used to redirect the build directory of the bootstrap
|
||||
/// invocation, in the future it would be great if we could actually execute
|
||||
/// the whole test with this directory set as the workdir.
|
||||
pub struct TestCtx {
|
||||
directory: TempDir,
|
||||
}
|
||||
|
||||
impl TestCtx {
|
||||
pub fn new() -> Self {
|
||||
let directory = TempDir::new().expect("cannot create temporary directory");
|
||||
eprintln!("Running test in {}", directory.path().display());
|
||||
Self { directory }
|
||||
}
|
||||
|
||||
/// Starts a new invocation of bootstrap that executes `kind` as its top level command
|
||||
/// (i.e. `x <kind>`). Returns a builder that configures the created config through CLI flags.
|
||||
pub fn config(&self, kind: &str) -> ConfigBuilder {
|
||||
ConfigBuilder::from_args(&[kind], self.directory.path().to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to configure an invocation of bootstrap.
|
||||
/// Currently runs in the rustc checkout, long-term it should be switched
|
||||
/// to run in a (cache-primed) temporary directory instead.
|
||||
pub struct ConfigBuilder {
|
||||
args: Vec<String>,
|
||||
directory: PathBuf,
|
||||
}
|
||||
|
||||
impl ConfigBuilder {
|
||||
fn from_args(args: &[&str], directory: PathBuf) -> Self {
|
||||
Self { args: args.iter().copied().map(String::from).collect(), directory }
|
||||
}
|
||||
|
||||
pub fn path(mut self, path: &str) -> Self {
|
||||
self.args.push(path.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn stage(mut self, stage: u32) -> Self {
|
||||
self.args.push("--stage".to_string());
|
||||
self.args.push(stage.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn create_config(mut self) -> Config {
|
||||
// Run in dry-check, otherwise the test would be too slow
|
||||
self.args.push("--dry-run".to_string());
|
||||
|
||||
// Ignore submodules
|
||||
self.args.push("--set".to_string());
|
||||
self.args.push("build.submodules=false".to_string());
|
||||
|
||||
// Do not mess with the local rustc checkout build directory
|
||||
self.args.push("--build-dir".to_string());
|
||||
self.args.push(self.directory.join("build").display().to_string());
|
||||
|
||||
Config::parse(Flags::parse(&self.args))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
14346303d760027e53214e705109a62c0f00b214
|
||||
d1d8e386c5e84c4ba857f56c3291f73c27e2d62a
|
||||
|
|
|
|||
|
|
@ -101,6 +101,8 @@
|
|||
- [The `rustdoc` test suite](./rustdoc-internals/rustdoc-test-suite.md)
|
||||
- [The `rustdoc-gui` test suite](./rustdoc-internals/rustdoc-gui-test-suite.md)
|
||||
- [The `rustdoc-json` test suite](./rustdoc-internals/rustdoc-json-test-suite.md)
|
||||
- [GPU offload internals](./offload/internals.md)
|
||||
- [Installation](./offload/installation.md)
|
||||
- [Autodiff internals](./autodiff/internals.md)
|
||||
- [Installation](./autodiff/installation.md)
|
||||
- [How to debug](./autodiff/debugging.md)
|
||||
|
|
@ -121,8 +123,9 @@
|
|||
- [Feature gate checking](./feature-gate-ck.md)
|
||||
- [Lang Items](./lang-items.md)
|
||||
- [The HIR (High-level IR)](./hir.md)
|
||||
- [Lowering AST to HIR](./ast-lowering.md)
|
||||
- [Debugging](./hir-debugging.md)
|
||||
- [Lowering AST to HIR](./hir/lowering.md)
|
||||
- [Ambig/Unambig Types and Consts](./hir/ambig-unambig-ty-and-consts.md)
|
||||
- [Debugging](./hir/debugging.md)
|
||||
- [The THIR (Typed High-level IR)](./thir.md)
|
||||
- [The MIR (Mid-level IR)](./mir/index.md)
|
||||
- [MIR construction](./mir/construction.md)
|
||||
|
|
@ -181,7 +184,7 @@
|
|||
- [Significant changes and quirks](./solve/significant-changes.md)
|
||||
- [`Unsize` and `CoerceUnsized` traits](./traits/unsize.md)
|
||||
- [Type checking](./type-checking.md)
|
||||
- [Method Lookup](./method-lookup.md)
|
||||
- [Method lookup](./method-lookup.md)
|
||||
- [Variance](./variance.md)
|
||||
- [Coherence checking](./coherence.md)
|
||||
- [Opaque types](./opaque-types-type-alias-impl-trait.md)
|
||||
|
|
@ -189,7 +192,7 @@
|
|||
- [Return Position Impl Trait In Trait](./return-position-impl-trait-in-trait.md)
|
||||
- [Region inference restrictions][opaque-infer]
|
||||
- [Const condition checking](./effects.md)
|
||||
- [Pattern and Exhaustiveness Checking](./pat-exhaustive-checking.md)
|
||||
- [Pattern and exhaustiveness checking](./pat-exhaustive-checking.md)
|
||||
- [Unsafety checking](./unsafety-checking.md)
|
||||
- [MIR dataflow](./mir/dataflow.md)
|
||||
- [Drop elaboration](./mir/drop-elaboration.md)
|
||||
|
|
@ -209,7 +212,7 @@
|
|||
- [Closure capture inference](./closure.md)
|
||||
- [Async closures/"coroutine-closures"](coroutine-closures.md)
|
||||
|
||||
# MIR to Binaries
|
||||
# MIR to binaries
|
||||
|
||||
- [Prologue](./part-5-intro.md)
|
||||
- [MIR optimizations](./mir/optimizations.md)
|
||||
|
|
@ -218,15 +221,15 @@
|
|||
- [Interpreter](./const-eval/interpret.md)
|
||||
- [Monomorphization](./backend/monomorph.md)
|
||||
- [Lowering MIR](./backend/lowering-mir.md)
|
||||
- [Code Generation](./backend/codegen.md)
|
||||
- [Code generation](./backend/codegen.md)
|
||||
- [Updating LLVM](./backend/updating-llvm.md)
|
||||
- [Debugging LLVM](./backend/debugging.md)
|
||||
- [Backend Agnostic Codegen](./backend/backend-agnostic.md)
|
||||
- [Implicit Caller Location](./backend/implicit-caller-location.md)
|
||||
- [Libraries and Metadata](./backend/libs-and-metadata.md)
|
||||
- [Profile-guided Optimization](./profile-guided-optimization.md)
|
||||
- [LLVM Source-Based Code Coverage](./llvm-coverage-instrumentation.md)
|
||||
- [Sanitizers Support](./sanitizers.md)
|
||||
- [Implicit caller location](./backend/implicit-caller-location.md)
|
||||
- [Libraries and metadata](./backend/libs-and-metadata.md)
|
||||
- [Profile-guided optimization](./profile-guided-optimization.md)
|
||||
- [LLVM source-based code coverage](./llvm-coverage-instrumentation.md)
|
||||
- [Sanitizers support](./sanitizers.md)
|
||||
- [Debugging support in the Rust compiler](./debugging-support-in-rustc.md)
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Implicit Caller Location
|
||||
# Implicit caller location
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ adds the [`#[track_caller]`][attr-reference] attribute for functions, the
|
|||
[`caller_location`][intrinsic] intrinsic, and the stabilization-friendly
|
||||
[`core::panic::Location::caller`][wrapper] wrapper.
|
||||
|
||||
## Motivating Example
|
||||
## Motivating example
|
||||
|
||||
Take this example program:
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ These error messages are achieved through a combination of changes to `panic!` i
|
|||
of `core::panic::Location::caller` and a number of `#[track_caller]` annotations in the standard
|
||||
library which propagate caller information.
|
||||
|
||||
## Reading Caller Location
|
||||
## Reading caller location
|
||||
|
||||
Previously, `panic!` made use of the `file!()`, `line!()`, and `column!()` macros to construct a
|
||||
[`Location`] pointing to where the panic occurred. These macros couldn't be given an overridden
|
||||
|
|
@ -51,7 +51,7 @@ was expanded. This function is itself annotated with `#[track_caller]` and wraps
|
|||
[`caller_location`][intrinsic] compiler intrinsic implemented by rustc. This intrinsic is easiest
|
||||
explained in terms of how it works in a `const` context.
|
||||
|
||||
## Caller Location in `const`
|
||||
## Caller location in `const`
|
||||
|
||||
There are two main phases to returning the caller location in a const context: walking up the stack
|
||||
to find the right location and allocating a const value to return.
|
||||
|
|
@ -138,7 +138,7 @@ fn main() {
|
|||
}
|
||||
```
|
||||
|
||||
### Dynamic Dispatch
|
||||
### Dynamic dispatch
|
||||
|
||||
In codegen contexts we have to modify the callee ABI to pass this information down the stack, but
|
||||
the attribute expressly does *not* modify the type of the function. The ABI change must be
|
||||
|
|
@ -156,7 +156,7 @@ probably the best we can do without modifying fully-stabilized type signatures.
|
|||
> whether we'll be called in a const context (safe to ignore shim) or in a codegen context (unsafe
|
||||
> to ignore shim). Even if we did know, the results from const and codegen contexts must agree.
|
||||
|
||||
## The Attribute
|
||||
## The attribute
|
||||
|
||||
The `#[track_caller]` attribute is checked alongside other codegen attributes to ensure the
|
||||
function:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Libraries and Metadata
|
||||
# Libraries and metadata
|
||||
|
||||
When the compiler sees a reference to an external crate, it needs to load some
|
||||
information about that crate. This chapter gives an overview of that process,
|
||||
|
|
|
|||
|
|
@ -174,8 +174,8 @@ compiler, you can use it instead of the JSON file for both arguments.
|
|||
## Promoting a target from tier 2 (target) to tier 2 (host)
|
||||
|
||||
There are two levels of tier 2 targets:
|
||||
a) Targets that are only cross-compiled (`rustup target add`)
|
||||
b) Targets that [have a native toolchain][tier2-native] (`rustup toolchain install`)
|
||||
- Targets that are only cross-compiled (`rustup target add`)
|
||||
- Targets that [have a native toolchain][tier2-native] (`rustup toolchain install`)
|
||||
|
||||
[tier2-native]: https://doc.rust-lang.org/nightly/rustc/target-tier-policy.html#tier-2-with-host-tools
|
||||
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ To find documentation-related issues, use the [A-docs label].
|
|||
|
||||
You can find documentation style guidelines in [RFC 1574].
|
||||
|
||||
To build the standard library documentation, use `x doc --stage 0 library --open`.
|
||||
To build the standard library documentation, use `x doc --stage 1 library --open`.
|
||||
To build the documentation for a book (e.g. the unstable book), use `x doc src/doc/unstable-book.`
|
||||
Results should appear in `build/host/doc`, as well as automatically open in your default browser.
|
||||
See [Building Documentation](./building/compiler-documenting.md#building-documentation) for more
|
||||
|
|
|
|||
|
|
@ -553,7 +553,7 @@ compiler](#linting-early-in-the-compiler).
|
|||
|
||||
|
||||
[AST nodes]: the-parser.md
|
||||
[AST lowering]: ast-lowering.md
|
||||
[AST lowering]: ./hir/lowering.md
|
||||
[HIR nodes]: hir.md
|
||||
[MIR nodes]: mir/index.md
|
||||
[macro expansion]: macro-expansion.md
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
The HIR – "High-Level Intermediate Representation" – is the primary IR used
|
||||
in most of rustc. It is a compiler-friendly representation of the abstract
|
||||
syntax tree (AST) that is generated after parsing, macro expansion, and name
|
||||
resolution (see [Lowering](./ast-lowering.html) for how the HIR is created).
|
||||
resolution (see [Lowering](./hir/lowering.md) for how the HIR is created).
|
||||
Many parts of HIR resemble Rust surface syntax quite closely, with
|
||||
the exception that some of Rust's expression forms have been desugared away.
|
||||
For example, `for` loops are converted into a `loop` and do not appear in
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
# Ambig/Unambig Types and Consts
|
||||
|
||||
Types and Consts args in the HIR can be in two kinds of positions ambiguous (ambig) or unambiguous (unambig). Ambig positions are where
|
||||
it would be valid to parse either a type or a const, unambig positions are where only one kind would be valid to
|
||||
parse.
|
||||
|
||||
```rust
|
||||
fn func<T, const N: usize>(arg: T) {
|
||||
// ^ Unambig type position
|
||||
let a: _ = arg;
|
||||
// ^ Unambig type position
|
||||
|
||||
func::<T, N>(arg);
|
||||
// ^ ^
|
||||
// ^^^^ Ambig position
|
||||
|
||||
let _: [u8; 10];
|
||||
// ^^ ^^ Unambig const position
|
||||
// ^^ Unambig type position
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Most types/consts in ambig positions are able to be disambiguated as either a type or const during parsing. Single segment paths are always represented as types in the AST but may get resolved to a const parameter during name resolution, then lowered to a const argument during ast-lowering. The only generic arguments which remain ambiguous after lowering are inferred generic arguments (`_`) in path segments. For example, in `Foo<_>` it is not clear whether the `_` argument is an inferred type argument, or an inferred const argument.
|
||||
|
||||
In unambig positions, inferred arguments are represented with [`hir::TyKind::Infer`][ty_infer] or [`hir::ConstArgKind::Infer`][const_infer] depending on whether it is a type or const position respectively.
|
||||
In ambig positions, inferred arguments are represented with `hir::GenericArg::Infer`.
|
||||
|
||||
A naive implementation of this would result in there being potentially 5 places where you might think an inferred type/const could be found in the HIR from looking at the structure of the HIR:
|
||||
1. In unambig type position as a `hir::TyKind::Infer`
|
||||
2. In unambig const arg position as a `hir::ConstArgKind::Infer`
|
||||
3. In an ambig position as a [`GenericArg::Type(TyKind::Infer)`][generic_arg_ty]
|
||||
4. In an ambig position as a [`GenericArg::Const(ConstArgKind::Infer)`][generic_arg_const]
|
||||
5. In an ambig position as a [`GenericArg::Infer`][generic_arg_infer]
|
||||
|
||||
Note that places 3 and 4 would never actually be possible to encounter as we always lower to `GenericArg::Infer` in generic arg position.
|
||||
|
||||
This has a few failure modes:
|
||||
- People may write visitors which check for `GenericArg::Infer` but forget to check for `hir::TyKind/ConstArgKind::Infer`, only handling infers in ambig positions by accident.
|
||||
- People may write visitors which check for `hir::TyKind/ConstArgKind::Infer` but forget to check for `GenericArg::Infer`, only handling infers in unambig positions by accident.
|
||||
- People may write visitors which check for `GenerArg::Type/Const(TyKind/ConstArgKind::Infer)` and `GenerigArg::Infer`, not realising that we never represent inferred types/consts in ambig positions as a `GenericArg::Type/Const`.
|
||||
- People may write visitors which check for *only* `TyKind::Infer` and not `ConstArgKind::Infer` forgetting that there are also inferred const arguments (and vice versa).
|
||||
|
||||
To make writing HIR visitors less error prone when caring about inferred types/consts we have a relatively complex system:
|
||||
|
||||
1. We have different types in the compiler for when a type or const is in an unambig or ambig position, `hir::Ty<AmbigArg>` and `hir::Ty<()>`. [`AmbigArg`][ambig_arg] is an uninhabited type which we use in the `Infer` variant of `TyKind` and `ConstArgKind` to selectively "disable" it if we are in an ambig position.
|
||||
|
||||
2. The [`visit_ty`][visit_ty] and [`visit_const_arg`][visit_const_arg] methods on HIR visitors only accept the ambig position versions of types/consts. Unambig types/consts are implicitly converted to ambig types/consts during the visiting process, with the `Infer` variant handled by a dedicated [`visit_infer`][visit_infer] method.
|
||||
|
||||
This has a number of benefits:
|
||||
- It's clear that `GenericArg::Type/Const` cannot represent inferred type/const arguments
|
||||
- Implementors of `visit_ty` and `visit_const_arg` will never encounter inferred types/consts making it impossible to write a visitor that seems to work right but handles edge cases wrong
|
||||
- The `visit_infer` method handles *all* cases of inferred type/consts in the HIR making it easy for visitors to handle inferred type/consts in one dedicated place and not forget cases
|
||||
|
||||
[ty_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.TyKind.html#variant.Infer
|
||||
[const_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.ConstArgKind.html#variant.Infer
|
||||
[generic_arg_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.GenericArg.html#variant.Type
|
||||
[generic_arg_const]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.GenericArg.html#variant.Const
|
||||
[generic_arg_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.GenericArg.html#variant.Infer
|
||||
[ambig_arg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.AmbigArg.html
|
||||
[visit_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/intravisit/trait.Visitor.html#method.visit_ty
|
||||
[visit_const_arg]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/intravisit/trait.Visitor.html#method.visit_const_arg
|
||||
[visit_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/intravisit/trait.Visitor.html#method.visit_infer
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# AST lowering
|
||||
|
||||
The AST lowering step converts AST to [HIR](hir.html).
|
||||
The AST lowering step converts AST to [HIR](../hir.md).
|
||||
This means many structures are removed if they are irrelevant
|
||||
for type analysis or similar syntax agnostic analyses. Examples
|
||||
of such structures include but are not limited to
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# LLVM Source-Based Code Coverage
|
||||
# LLVM source-based code coverage
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
|
|
|
|||
71
src/doc/rustc-dev-guide/src/offload/installation.md
Normal file
71
src/doc/rustc-dev-guide/src/offload/installation.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Installation
|
||||
|
||||
In the future, `std::offload` should become available in nightly builds for users. For now, everyone still needs to build rustc from source.
|
||||
|
||||
## Build instructions
|
||||
|
||||
First you need to clone and configure the Rust repository:
|
||||
```bash
|
||||
git clone --depth=1 git@github.com:rust-lang/rust.git
|
||||
cd rust
|
||||
./configure --enable-llvm-link-shared --release-channel=nightly --enable-llvm-assertions --enable-offload --enable-enzyme --enable-clang --enable-lld --enable-option-checking --enable-ninja --disable-docs
|
||||
```
|
||||
|
||||
Afterwards you can build rustc using:
|
||||
```bash
|
||||
./x.py build --stage 1 library
|
||||
```
|
||||
|
||||
Afterwards rustc toolchain link will allow you to use it through cargo:
|
||||
```
|
||||
rustup toolchain link offload build/host/stage1
|
||||
rustup toolchain install nightly # enables -Z unstable-options
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Build instruction for LLVM itself
|
||||
```bash
|
||||
git clone --depth=1 git@github.com:llvm/llvm-project.git
|
||||
cd llvm-project
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -G Ninja ../llvm -DLLVM_TARGETS_TO_BUILD="host,AMDGPU,NVPTX" -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_ENABLE_PROJECTS="clang;lld" -DLLVM_ENABLE_RUNTIMES="offload,openmp" -DLLVM_ENABLE_PLUGINS=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=.
|
||||
ninja
|
||||
ninja install
|
||||
```
|
||||
This gives you a working LLVM build.
|
||||
|
||||
|
||||
## Testing
|
||||
run
|
||||
```
|
||||
./x.py test --stage 1 tests/codegen/gpu_offload
|
||||
```
|
||||
|
||||
## Usage
|
||||
It is important to use a clang compiler build on the same llvm as rustc. Just calling clang without the full path will likely use your system clang, which probably will be incompatible.
|
||||
```
|
||||
/absolute/path/to/rust/build/x86_64-unknown-linux-gnu/stage1/bin/rustc --edition=2024 --crate-type cdylib src/main.rs --emit=llvm-ir -O -C lto=fat -Cpanic=abort -Zoffload=Enable
|
||||
/absolute/path/to/rust/build/x86_64-unknown-linux-gnu/llvm/bin/clang++ -fopenmp --offload-arch=native -g -O3 main.ll -o main -save-temps
|
||||
LIBOMPTARGET_INFO=-1 ./main
|
||||
```
|
||||
The first step will generate a `main.ll` file, which has enough instructions to cause the offload runtime to move data to and from a gpu.
|
||||
The second step will use clang as the compilation driver to compile our IR file down to a working binary. Only a very small Rust subset will work out of the box here, unless
|
||||
you use features like build-std, which are not covered by this guide. Look at the codegen test to get a feeling for how to write a working example.
|
||||
In the last step you can run your binary, if all went well you will see a data transfer being reported:
|
||||
```
|
||||
omptarget device 0 info: Entering OpenMP data region with being_mapper at unknown:0:0 with 1 arguments:
|
||||
omptarget device 0 info: tofrom(unknown)[1024]
|
||||
omptarget device 0 info: Creating new map entry with HstPtrBase=0x00007fffffff9540, HstPtrBegin=0x00007fffffff9540, TgtAllocBegin=0x0000155547200000, TgtPtrBegin=0x0000155547200000, Size=1024, DynRefCount=1, HoldRefCount=0, Name=unknown
|
||||
omptarget device 0 info: Copying data from host to device, HstPtr=0x00007fffffff9540, TgtPtr=0x0000155547200000, Size=1024, Name=unknown
|
||||
omptarget device 0 info: OpenMP Host-Device pointer mappings after block at unknown:0:0:
|
||||
omptarget device 0 info: Host Ptr Target Ptr Size (B) DynRefCount HoldRefCount Declaration
|
||||
omptarget device 0 info: 0x00007fffffff9540 0x0000155547200000 1024 1 0 unknown at unknown:0:0
|
||||
// some other output
|
||||
omptarget device 0 info: Exiting OpenMP data region with end_mapper at unknown:0:0 with 1 arguments:
|
||||
omptarget device 0 info: tofrom(unknown)[1024]
|
||||
omptarget device 0 info: Mapping exists with HstPtrBegin=0x00007fffffff9540, TgtPtrBegin=0x0000155547200000, Size=1024, DynRefCount=0 (decremented, delayed deletion), HoldRefCount=0
|
||||
omptarget device 0 info: Copying data from device to host, TgtPtr=0x0000155547200000, HstPtr=0x00007fffffff9540, Size=1024, Name=unknown
|
||||
omptarget device 0 info: Removing map entry with HstPtrBegin=0x00007fffffff9540, TgtPtrBegin=0x0000155547200000, Size=1024, Name=unknown
|
||||
```
|
||||
9
src/doc/rustc-dev-guide/src/offload/internals.md
Normal file
9
src/doc/rustc-dev-guide/src/offload/internals.md
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# std::offload
|
||||
|
||||
This module is under active development. Once upstream, it should allow Rust developers to run Rust code on GPUs.
|
||||
We aim to develop a `rusty` GPU programming interface, which is safe, convenient and sufficiently fast by default.
|
||||
This includes automatic data movement to and from the GPU, in a efficient way. We will (later)
|
||||
also offer more advanced, possibly unsafe, interfaces which allow a higher degree of control.
|
||||
|
||||
The implementation is based on LLVM's "offload" project, which is already used by OpenMP to run Fortran or C++ code on GPUs.
|
||||
While the project is under development, users will need to call other compilers like clang to finish the compilation process.
|
||||
|
|
@ -410,7 +410,7 @@ For more details on bootstrapping, see
|
|||
- Guide: [The HIR](hir.md)
|
||||
- Guide: [Identifiers in the HIR](hir.md#identifiers-in-the-hir)
|
||||
- Guide: [The `HIR` Map](hir.md#the-hir-map)
|
||||
- Guide: [Lowering `AST` to `HIR`](ast-lowering.md)
|
||||
- Guide: [Lowering `AST` to `HIR`](./hir/lowering.md)
|
||||
- How to view `HIR` representation for your code `cargo rustc -- -Z unpretty=hir-tree`
|
||||
- Rustc `HIR` definition: [`rustc_hir`](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/index.html)
|
||||
- Main entry point: **TODO**
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# From MIR to Binaries
|
||||
# From MIR to binaries
|
||||
|
||||
All of the preceding chapters of this guide have one thing in common:
|
||||
we never generated any executable machine code at all!
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Pattern and Exhaustiveness Checking
|
||||
# Pattern and exhaustiveness checking
|
||||
|
||||
In Rust, pattern matching and bindings have a few very helpful properties. The
|
||||
compiler will check that bindings are irrefutable when made and that match arms
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Profile Guided Optimization
|
||||
# Profile-guided optimization
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
This chapter describes what PGO is and how the support for it is
|
||||
implemented in `rustc`.
|
||||
|
||||
## What Is Profiled-Guided Optimization?
|
||||
## What is profiled-guided optimization?
|
||||
|
||||
The basic concept of PGO is to collect data about the typical execution of
|
||||
a program (e.g. which branches it is likely to take) and then use this data
|
||||
|
|
@ -52,7 +52,7 @@ instrumentation, via the experimental option
|
|||
[`-C instrument-coverage`](./llvm-coverage-instrumentation.md), but using these
|
||||
coverage results for PGO has not been attempted at this time.
|
||||
|
||||
### Overall Workflow
|
||||
### Overall workflow
|
||||
|
||||
Generating a PGO-optimized program involves the following four steps:
|
||||
|
||||
|
|
@ -62,12 +62,12 @@ Generating a PGO-optimized program involves the following four steps:
|
|||
4. Compile the program again, this time making use of the profiling data
|
||||
(e.g. `rustc -C profile-use=merged.profdata main.rs`)
|
||||
|
||||
### Compile-Time Aspects
|
||||
### Compile-time aspects
|
||||
|
||||
Depending on which step in the above workflow we are in, two different things
|
||||
can happen at compile time:
|
||||
|
||||
#### Create Binaries with Instrumentation
|
||||
#### Create binaries with instrumentation
|
||||
|
||||
As mentioned above, the profiling instrumentation is added by LLVM.
|
||||
`rustc` instructs LLVM to do so [by setting the appropriate][pgo-gen-passmanager]
|
||||
|
|
@ -88,7 +88,7 @@ runtime are not removed [by marking the with the right export level][pgo-gen-sym
|
|||
[pgo-gen-symbols]:https://github.com/rust-lang/rust/blob/1.34.1/src/librustc_codegen_ssa/back/symbol_export.rs#L212-L225
|
||||
|
||||
|
||||
#### Compile Binaries Where Optimizations Make Use Of Profiling Data
|
||||
#### Compile binaries where optimizations make use of profiling data
|
||||
|
||||
In the final step of the workflow described above, the program is compiled
|
||||
again, with the compiler using the gathered profiling data in order to drive
|
||||
|
|
@ -106,7 +106,7 @@ LLVM does the rest (e.g. setting branch weights, marking functions with
|
|||
`cold` or `inlinehint`, etc).
|
||||
|
||||
|
||||
### Runtime Aspects
|
||||
### Runtime aspects
|
||||
|
||||
Instrumentation-based approaches always also have a runtime component, i.e.
|
||||
once we have an instrumented program, that program needs to be run in order
|
||||
|
|
@ -134,7 +134,7 @@ instrumentation artifacts show up in LLVM IR.
|
|||
[rmake-tests]: https://github.com/rust-lang/rust/tree/master/tests/run-make
|
||||
[codegen-test]: https://github.com/rust-lang/rust/blob/master/tests/codegen/pgo-instrumentation.rs
|
||||
|
||||
## Additional Information
|
||||
## Additional information
|
||||
|
||||
Clang's documentation contains a good overview on [PGO in LLVM][llvm-pgo].
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ This is a guide for how to profile rustc with [perf](https://perf.wiki.kernel.or
|
|||
- Get a clean checkout of rust-lang/master, or whatever it is you want
|
||||
to profile.
|
||||
- Set the following settings in your `bootstrap.toml`:
|
||||
- `debuginfo-level = 1` - enables line debuginfo
|
||||
- `jemalloc = false` - lets you do memory use profiling with valgrind
|
||||
- `rust.debuginfo-level = 1` - enables line debuginfo
|
||||
- `rust.jemalloc = false` - lets you do memory use profiling with valgrind
|
||||
- leave everything else the defaults
|
||||
- Run `./x build` to get a full build
|
||||
- Make a rustup toolchain pointing to that result
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Incremental Compilation in detail
|
||||
# Incremental compilation in detail
|
||||
|
||||
<!-- toc -->
|
||||
|
||||
|
|
@ -66,7 +66,7 @@ because it reads the up-to-date version of `Hir(bar)`. Also, we re-run
|
|||
`type_check_item(bar)` because result of `type_of(bar)` might have changed.
|
||||
|
||||
|
||||
## The Problem With The Basic Algorithm: False Positives
|
||||
## The problem with the basic algorithm: false positives
|
||||
|
||||
If you read the previous paragraph carefully you'll notice that it says that
|
||||
`type_of(bar)` *might* have changed because one of its inputs has changed.
|
||||
|
|
@ -93,7 +93,7 @@ of examples like this and small changes to the input often potentially affect
|
|||
very large parts of the output binaries. As a consequence, we had to make the
|
||||
change detection system smarter and more accurate.
|
||||
|
||||
## Improving Accuracy: The red-green Algorithm
|
||||
## Improving accuracy: the red-green algorithm
|
||||
|
||||
The "false positives" problem can be solved by interleaving change detection
|
||||
and query re-evaluation. Instead of walking the graph all the way to the
|
||||
|
|
@ -191,7 +191,7 @@ then itself involve recursively invoking more queries, which can mean we come ba
|
|||
to the `try_mark_green()` algorithm for the dependencies recursively.
|
||||
|
||||
|
||||
## The Real World: How Persistence Makes Everything Complicated
|
||||
## The real world: how persistence makes everything complicated
|
||||
|
||||
The sections above described the underlying algorithm for incremental
|
||||
compilation but because the compiler process exits after being finished and
|
||||
|
|
@ -258,7 +258,7 @@ the `LocalId`s within it are still the same.
|
|||
|
||||
|
||||
|
||||
### Checking Query Results For Changes: HashStable And Fingerprints
|
||||
### Checking query results for changes: `HashStable` and `Fingerprint`s
|
||||
|
||||
In order to do red-green-marking we often need to check if the result of a
|
||||
query has changed compared to the result it had during the previous
|
||||
|
|
@ -306,7 +306,7 @@ This approach works rather well but it's not without flaws:
|
|||
their stable equivalents while doing the hashing.
|
||||
|
||||
|
||||
### A Tale Of Two DepGraphs: The Old And The New
|
||||
### A tale of two `DepGraph`s: the old and the new
|
||||
|
||||
The initial description of dependency tracking glosses over a few details
|
||||
that quickly become a head scratcher when actually trying to implement things.
|
||||
|
|
@ -344,7 +344,7 @@ new graph is serialized out to disk, alongside the query result cache, and can
|
|||
act as the previous dep-graph in a subsequent compilation session.
|
||||
|
||||
|
||||
### Didn't You Forget Something?: Cache Promotion
|
||||
### Didn't you forget something?: cache promotion
|
||||
|
||||
The system described so far has a somewhat subtle property: If all inputs of a
|
||||
dep-node are green then the dep-node itself can be marked as green without
|
||||
|
|
@ -374,7 +374,7 @@ the result cache doesn't unnecessarily shrink again.
|
|||
|
||||
|
||||
|
||||
# Incremental Compilation and the Compiler Backend
|
||||
# Incremental compilation and the compiler backend
|
||||
|
||||
The compiler backend, the part involving LLVM, is using the query system but
|
||||
it is not implemented in terms of queries itself. As a consequence it does not
|
||||
|
|
@ -406,7 +406,7 @@ would save.
|
|||
|
||||
|
||||
|
||||
## Query Modifiers
|
||||
## Query modifiers
|
||||
|
||||
The query system allows for applying [modifiers][mod] to queries. These
|
||||
modifiers affect certain aspects of how the system treats the query with
|
||||
|
|
@ -472,7 +472,7 @@ respect to incremental compilation:
|
|||
[mod]: ../query.html#adding-a-new-kind-of-query
|
||||
|
||||
|
||||
## The Projection Query Pattern
|
||||
## The projection query pattern
|
||||
|
||||
It's interesting to note that `eval_always` and `no_hash` can be used together
|
||||
in the so-called "projection query" pattern. It is often the case that there is
|
||||
|
|
@ -516,7 +516,7 @@ because we have the projections to take care of keeping things green as much
|
|||
as possible.
|
||||
|
||||
|
||||
# Shortcomings of the Current System
|
||||
# Shortcomings of the current system
|
||||
|
||||
There are many things that still can be improved.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<!-- toc -->
|
||||
|
||||
As described in [the high-level overview of the compiler][hl], the Rust compiler
|
||||
As described in [Overview of the compiler], the Rust compiler
|
||||
is still (as of <!-- date-check --> July 2021) transitioning from a
|
||||
traditional "pass-based" setup to a "demand-driven" system. The compiler query
|
||||
system is the key to rustc's demand-driven organization.
|
||||
|
|
@ -13,7 +13,7 @@ there is a query called `type_of` that, given the [`DefId`] of
|
|||
some item, will compute the type of that item and return it to you.
|
||||
|
||||
[`DefId`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/def_id/struct.DefId.html
|
||||
[hl]: ./compiler-src.md
|
||||
[Overview of the compiler]: overview.md#queries
|
||||
|
||||
Query execution is *memoized*. The first time you invoke a
|
||||
query, it will go do the computation, but the next time, the result is
|
||||
|
|
@ -37,12 +37,15 @@ will in turn demand information about that crate, starting from the
|
|||
actual parsing.
|
||||
|
||||
Although this vision is not fully realized, large sections of the
|
||||
compiler (for example, generating [MIR](./mir/index.md)) currently work exactly like this.
|
||||
compiler (for example, generating [MIR]) currently work exactly like this.
|
||||
|
||||
[^incr-comp-detail]: The ["Incremental Compilation in Detail](queries/incremental-compilation-in-detail.md) chapter gives a more
|
||||
[^incr-comp-detail]: The [Incremental compilation in detail] chapter gives a more
|
||||
in-depth description of what queries are and how they work.
|
||||
If you intend to write a query of your own, this is a good read.
|
||||
|
||||
[Incremental compilation in detail]: queries/incremental-compilation-in-detail.md
|
||||
[MIR]: mir/index.md
|
||||
|
||||
## Invoking queries
|
||||
|
||||
Invoking a query is simple. The [`TyCtxt`] ("type context") struct offers a method
|
||||
|
|
@ -67,9 +70,15 @@ are cheaply cloneable; insert an `Rc` if necessary).
|
|||
### Providers
|
||||
|
||||
If, however, the query is *not* in the cache, then the compiler will
|
||||
try to find a suitable **provider**. A provider is a function that has
|
||||
been defined and linked into the compiler somewhere that contains the
|
||||
code to compute the result of the query.
|
||||
call the corresponding **provider** function. A provider is a function
|
||||
implemented in a specific module and **manually registered** into the
|
||||
[`Providers`][providers_struct] struct during compiler initialization.
|
||||
The macro system generates the [`Providers`][providers_struct] struct,
|
||||
which acts as a function table for all query implementations, where each
|
||||
field is a function pointer to the actual provider.
|
||||
|
||||
**Note:** The `Providers` struct is generated by macros and acts as a function table for all query implementations.
|
||||
It is **not** a Rust trait, but a plain struct with function pointer fields.
|
||||
|
||||
**Providers are defined per-crate.** The compiler maintains,
|
||||
internally, a table of providers for every crate, at least
|
||||
|
|
@ -97,62 +106,6 @@ fn provider<'tcx>(
|
|||
Providers take two arguments: the `tcx` and the query key.
|
||||
They return the result of the query.
|
||||
|
||||
### How providers are setup
|
||||
|
||||
When the tcx is created, it is given the providers by its creator using
|
||||
the [`Providers`][providers_struct] struct. This struct is generated by
|
||||
the macros here, but it is basically a big list of function pointers:
|
||||
|
||||
[providers_struct]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/query/struct.Providers.html
|
||||
|
||||
```rust,ignore
|
||||
struct Providers {
|
||||
type_of: for<'tcx> fn(TyCtxt<'tcx>, DefId) -> Ty<'tcx>,
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
At present, we have one copy of the struct for local crates, and one
|
||||
for external crates, though the plan is that we may eventually have
|
||||
one per crate.
|
||||
|
||||
These `Providers` structs are ultimately created and populated by
|
||||
`rustc_driver`, but it does this by distributing the work
|
||||
throughout the other `rustc_*` crates. This is done by invoking
|
||||
various [`provide`][provide_fn] functions. These functions tend to look
|
||||
something like this:
|
||||
|
||||
[provide_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/fn.provide.html
|
||||
|
||||
```rust,ignore
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
*providers = Providers {
|
||||
type_of,
|
||||
..*providers
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
That is, they take an `&mut Providers` and mutate it in place. Usually
|
||||
we use the formulation above just because it looks nice, but you could
|
||||
as well do `providers.type_of = type_of`, which would be equivalent.
|
||||
(Here, `type_of` would be a top-level function, defined as we saw
|
||||
before.) So, if we want to add a provider for some other query,
|
||||
let's call it `fubar`, into the crate above, we might modify the `provide()`
|
||||
function like so:
|
||||
|
||||
```rust,ignore
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
*providers = Providers {
|
||||
type_of,
|
||||
fubar,
|
||||
..*providers
|
||||
};
|
||||
}
|
||||
|
||||
fn fubar<'tcx>(tcx: TyCtxt<'tcx>, key: DefId) -> Fubar<'tcx> { ... }
|
||||
```
|
||||
|
||||
N.B. Most of the `rustc_*` crates only provide **local
|
||||
providers**. Almost all **extern providers** wind up going through the
|
||||
[`rustc_metadata` crate][rustc_metadata], which loads the information
|
||||
|
|
@ -164,6 +117,63 @@ they define both a `provide` and a `provide_extern` function, through
|
|||
[rustc_metadata]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_metadata/index.html
|
||||
[wasm_import_module_map]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/back/symbol_export/fn.wasm_import_module_map.html
|
||||
|
||||
### How providers are set up
|
||||
|
||||
When the tcx is created, it is given the providers by its creator using
|
||||
the [`Providers`][providers_struct] struct. This struct is generated by
|
||||
the macros here, but it is basically a big list of function pointers:
|
||||
|
||||
[providers_struct]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/query/struct.Providers.html
|
||||
|
||||
```rust,ignore
|
||||
struct Providers {
|
||||
type_of: for<'tcx> fn(TyCtxt<'tcx>, DefId) -> Ty<'tcx>,
|
||||
// ... one field for each query
|
||||
}
|
||||
```
|
||||
|
||||
#### How are providers registered?
|
||||
|
||||
The `Providers` struct is filled in during compiler initialization, mainly by the `rustc_driver` crate.
|
||||
But the actual provider functions are implemented in various `rustc_*` crates (like `rustc_middle`, `rustc_hir_analysis`, etc).
|
||||
|
||||
To register providers, each crate exposes a [`provide`][provide_fn] function that looks like this:
|
||||
|
||||
[provide_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/hir/fn.provide.html
|
||||
|
||||
```rust,ignore
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
*providers = Providers {
|
||||
type_of,
|
||||
// ... add more providers here
|
||||
..*providers
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
- This function takes a mutable reference to the `Providers` struct and sets the fields to point to the correct provider functions.
|
||||
- You can also assign fields individually, e.g. `providers.type_of = type_of;`.
|
||||
|
||||
#### Adding a new provider
|
||||
|
||||
Suppose you want to add a new query called `fubar`. You would:
|
||||
|
||||
1. Implement the provider function:
|
||||
```rust,ignore
|
||||
fn fubar<'tcx>(tcx: TyCtxt<'tcx>, key: DefId) -> Fubar<'tcx> { ... }
|
||||
```
|
||||
2. Register it in the `provide` function:
|
||||
```rust,ignore
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
*providers = Providers {
|
||||
fubar,
|
||||
..*providers
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Adding a new query
|
||||
|
||||
How do you add a new query?
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Sanitizers Support
|
||||
# Sanitizers support
|
||||
|
||||
The rustc compiler contains support for following sanitizers:
|
||||
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ Here is a summary:
|
|||
| Describe the *syntax* of a type: what the user wrote (with some desugaring). | Describe the *semantics* of a type: the meaning of what the user wrote. |
|
||||
| Each `rustc_hir::Ty` has its own spans corresponding to the appropriate place in the program. | Doesn’t correspond to a single place in the user’s program. |
|
||||
| `rustc_hir::Ty` has generics and lifetimes; however, some of those lifetimes are special markers like [`LifetimeKind::Implicit`][implicit]. | `ty::Ty` has the full type, including generics and lifetimes, even if the user left them out |
|
||||
| `fn foo(x: u32) → u32 { }` - Two `rustc_hir::Ty` representing each usage of `u32`, each has its own `Span`s, and `rustc_hir::Ty` doesn’t tell us that both are the same type | `fn foo(x: u32) → u32 { }` - One `ty::Ty` for all instances of `u32` throughout the program, and `ty::Ty` tells us that both usages of `u32` mean the same type. |
|
||||
| `fn foo(x: &u32) -> &u32)` - Two `rustc_hir::Ty` again. Lifetimes for the references show up in the `rustc_hir::Ty`s using a special marker, [`LifetimeKind::Implicit`][implicit]. | `fn foo(x: &u32) -> &u32)`- A single `ty::Ty`. The `ty::Ty` has the hidden lifetime param. |
|
||||
| `fn foo(x: u32) -> u32 { }` - Two `rustc_hir::Ty` representing each usage of `u32`, each has its own `Span`s, and `rustc_hir::Ty` doesn’t tell us that both are the same type | `fn foo(x: u32) -> u32 { }` - One `ty::Ty` for all instances of `u32` throughout the program, and `ty::Ty` tells us that both usages of `u32` mean the same type. |
|
||||
| `fn foo(x: &u32) -> &u32 { }` - Two `rustc_hir::Ty` again. Lifetimes for the references show up in the `rustc_hir::Ty`s using a special marker, [`LifetimeKind::Implicit`][implicit]. | `fn foo(x: &u32) -> &u32 { }`- A single `ty::Ty`. The `ty::Ty` has the hidden lifetime param. |
|
||||
|
||||
[implicit]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.LifetimeKind.html#variant.Implicit
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ This flag is equivalent to:
|
|||
- `-fmin-function-alignment` for [GCC](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fmin-function-alignment_003dn)
|
||||
- `-falign-functions` for [Clang](https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang1-falign-functions)
|
||||
|
||||
The specified alignment is a minimum. A higher alignment can be specified for specific functions by using the [`repr(align(...))`](https://github.com/rust-lang/rust/issues/82232) feature and annotating the function with a `#[repr(align(<align>))]` attribute. The attribute's value is ignored when it is lower than the value passed to `min-function-alignment`.
|
||||
The specified alignment is a minimum. A higher alignment can be specified for specific functions by using the [`align(...)`](https://github.com/rust-lang/rust/issues/82232) feature and annotating the function with a `#[align(<align>)]` attribute. The attribute's value is ignored when it is lower than the value passed to `min-function-alignment`.
|
||||
|
||||
There are two additional edge cases for this flag:
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use std::sync::{Arc, OnceLock as OnceCell};
|
|||
use std::{fmt, iter};
|
||||
|
||||
use arrayvec::ArrayVec;
|
||||
use itertools::Either;
|
||||
use rustc_abi::{ExternAbi, VariantIdx};
|
||||
use rustc_attr_data_structures::{
|
||||
AttributeKind, ConstStability, Deprecation, Stability, StableSince,
|
||||
|
|
@ -199,49 +200,49 @@ impl ExternalCrate {
|
|||
.unwrap_or(Unknown) // Well, at least we tried.
|
||||
}
|
||||
|
||||
pub(crate) fn keywords(&self, tcx: TyCtxt<'_>) -> ThinVec<(DefId, Symbol)> {
|
||||
fn mapped_root_modules<T>(
|
||||
&self,
|
||||
tcx: TyCtxt<'_>,
|
||||
f: impl Fn(DefId, TyCtxt<'_>) -> Option<(DefId, T)>,
|
||||
) -> impl Iterator<Item = (DefId, T)> {
|
||||
let root = self.def_id();
|
||||
|
||||
let as_keyword = |res: Res<!>| {
|
||||
if let Res::Def(DefKind::Mod, def_id) = res {
|
||||
let mut keyword = None;
|
||||
let meta_items = tcx
|
||||
.get_attrs(def_id, sym::doc)
|
||||
.flat_map(|attr| attr.meta_item_list().unwrap_or_default());
|
||||
for meta in meta_items {
|
||||
if meta.has_name(sym::keyword)
|
||||
&& let Some(v) = meta.value_str()
|
||||
{
|
||||
keyword = Some(v);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return keyword.map(|p| (def_id, p));
|
||||
}
|
||||
None
|
||||
};
|
||||
if root.is_local() {
|
||||
tcx.hir_root_module()
|
||||
.item_ids
|
||||
.iter()
|
||||
.filter_map(|&id| {
|
||||
let item = tcx.hir_item(id);
|
||||
match item.kind {
|
||||
hir::ItemKind::Mod(..) => {
|
||||
as_keyword(Res::Def(DefKind::Mod, id.owner_id.to_def_id()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
Either::Left(
|
||||
tcx.hir_root_module()
|
||||
.item_ids
|
||||
.iter()
|
||||
.filter(move |&&id| matches!(tcx.hir_item(id).kind, hir::ItemKind::Mod(..)))
|
||||
.filter_map(move |&id| f(id.owner_id.into(), tcx)),
|
||||
)
|
||||
} else {
|
||||
tcx.module_children(root).iter().map(|item| item.res).filter_map(as_keyword).collect()
|
||||
Either::Right(
|
||||
tcx.module_children(root)
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
if let Res::Def(DefKind::Mod, did) = item.res { Some(did) } else { None }
|
||||
})
|
||||
.filter_map(move |did| f(did, tcx)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn primitives(&self, tcx: TyCtxt<'_>) -> ThinVec<(DefId, PrimitiveType)> {
|
||||
let root = self.def_id();
|
||||
pub(crate) fn keywords(&self, tcx: TyCtxt<'_>) -> impl Iterator<Item = (DefId, Symbol)> {
|
||||
fn as_keyword(did: DefId, tcx: TyCtxt<'_>) -> Option<(DefId, Symbol)> {
|
||||
tcx.get_attrs(did, sym::doc)
|
||||
.flat_map(|attr| attr.meta_item_list().unwrap_or_default())
|
||||
.filter(|meta| meta.has_name(sym::keyword))
|
||||
.find_map(|meta| meta.value_str())
|
||||
.map(|value| (did, value))
|
||||
}
|
||||
|
||||
self.mapped_root_modules(tcx, as_keyword)
|
||||
}
|
||||
|
||||
pub(crate) fn primitives(
|
||||
&self,
|
||||
tcx: TyCtxt<'_>,
|
||||
) -> impl Iterator<Item = (DefId, PrimitiveType)> {
|
||||
// Collect all inner modules which are tagged as implementations of
|
||||
// primitives.
|
||||
//
|
||||
|
|
@ -259,40 +260,21 @@ impl ExternalCrate {
|
|||
// Also note that this does not attempt to deal with modules tagged
|
||||
// duplicately for the same primitive. This is handled later on when
|
||||
// rendering by delegating everything to a hash map.
|
||||
let as_primitive = |res: Res<!>| {
|
||||
let Res::Def(DefKind::Mod, def_id) = res else { return None };
|
||||
tcx.get_attrs(def_id, sym::rustc_doc_primitive)
|
||||
.map(|attr| {
|
||||
let attr_value = attr.value_str().expect("syntax should already be validated");
|
||||
let Some(prim) = PrimitiveType::from_symbol(attr_value) else {
|
||||
span_bug!(
|
||||
attr.span(),
|
||||
"primitive `{attr_value}` is not a member of `PrimitiveType`"
|
||||
);
|
||||
};
|
||||
fn as_primitive(def_id: DefId, tcx: TyCtxt<'_>) -> Option<(DefId, PrimitiveType)> {
|
||||
tcx.get_attrs(def_id, sym::rustc_doc_primitive).next().map(|attr| {
|
||||
let attr_value = attr.value_str().expect("syntax should already be validated");
|
||||
let Some(prim) = PrimitiveType::from_symbol(attr_value) else {
|
||||
span_bug!(
|
||||
attr.span(),
|
||||
"primitive `{attr_value}` is not a member of `PrimitiveType`"
|
||||
);
|
||||
};
|
||||
|
||||
(def_id, prim)
|
||||
})
|
||||
.next()
|
||||
};
|
||||
|
||||
if root.is_local() {
|
||||
tcx.hir_root_module()
|
||||
.item_ids
|
||||
.iter()
|
||||
.filter_map(|&id| {
|
||||
let item = tcx.hir_item(id);
|
||||
match item.kind {
|
||||
hir::ItemKind::Mod(..) => {
|
||||
as_primitive(Res::Def(DefKind::Mod, id.owner_id.to_def_id()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
tcx.module_children(root).iter().map(|item| item.res).filter_map(as_primitive).collect()
|
||||
(def_id, prim)
|
||||
})
|
||||
}
|
||||
|
||||
self.mapped_root_modules(tcx, as_primitive)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1966,7 +1948,7 @@ impl PrimitiveType {
|
|||
let e = ExternalCrate { crate_num };
|
||||
let crate_name = e.name(tcx);
|
||||
debug!(?crate_num, ?crate_name);
|
||||
for &(def_id, prim) in &e.primitives(tcx) {
|
||||
for (def_id, prim) in e.primitives(tcx) {
|
||||
// HACK: try to link to std instead where possible
|
||||
if crate_name == sym::core && primitive_locations.contains_key(&prim) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
|
|||
let keywords = local_crate.keywords(cx.tcx);
|
||||
{
|
||||
let ItemKind::ModuleItem(m) = &mut module.inner.kind else { unreachable!() };
|
||||
m.items.extend(primitives.iter().map(|&(def_id, prim)| {
|
||||
m.items.extend(primitives.map(|(def_id, prim)| {
|
||||
Item::from_def_id_and_parts(
|
||||
def_id,
|
||||
Some(prim.as_sym()),
|
||||
|
|
@ -69,7 +69,7 @@ pub(crate) fn krate(cx: &mut DocContext<'_>) -> Crate {
|
|||
cx,
|
||||
)
|
||||
}));
|
||||
m.items.extend(keywords.into_iter().map(|(def_id, kw)| {
|
||||
m.items.extend(keywords.map(|(def_id, kw)| {
|
||||
Item::from_def_id_and_parts(def_id, Some(kw), ItemKind::KeywordItem, cx)
|
||||
}));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -353,7 +353,7 @@ pub(crate) fn print_src(
|
|||
);
|
||||
Ok(())
|
||||
});
|
||||
let max_nb_digits = if lines > 0 { lines.ilog(10) + 1 } else { 1 };
|
||||
let max_nb_digits = if lines > 0 { lines.ilog10() + 1 } else { 1 };
|
||||
match source_context {
|
||||
SourceContext::Standalone { file_path } => Source {
|
||||
code_html: code,
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
|
|||
});
|
||||
|
||||
let local_crate = ExternalCrate { crate_num: LOCAL_CRATE };
|
||||
let prims: FxHashSet<PrimitiveType> = local_crate.primitives(tcx).iter().map(|p| p.1).collect();
|
||||
let prims: FxHashSet<PrimitiveType> = local_crate.primitives(tcx).map(|(_, p)| p).collect();
|
||||
|
||||
let crate_items = {
|
||||
let mut coll = ItemAndAliasCollector::new(&cx.cache);
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -
|
|||
.and_then(|trait_id| {
|
||||
cx.tcx.associated_items(trait_id).find_by_ident_and_kind(
|
||||
cx.tcx,
|
||||
Ident::from_str("Output"),
|
||||
Ident::with_dummy_span(sym::Output),
|
||||
ty::AssocTag::Type,
|
||||
trait_id,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ impl UseSegment {
|
|||
modsep: bool,
|
||||
) -> Option<UseSegment> {
|
||||
let name = rewrite_ident(context, path_seg.ident);
|
||||
if name.is_empty() || name == "{{root}}" {
|
||||
if name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let kind = match name {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ error[E0736]: attribute incompatible with `#[unsafe(naked)]`
|
|||
--> $DIR/naked-invalid-attr.rs:56:1
|
||||
|
|
||||
LL | #[::a]
|
||||
| ^^^^^^ the `{{root}}::a` attribute is incompatible with `#[unsafe(naked)]`
|
||||
| ^^^^^^ the `::a` attribute is incompatible with `#[unsafe(naked)]`
|
||||
...
|
||||
LL | #[unsafe(naked)]
|
||||
| ---------------- function marked with `#[unsafe(naked)]` here
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@
|
|||
|
||||
trait MyTrait {
|
||||
#[align] //~ ERROR malformed `align` attribute input
|
||||
fn myfun();
|
||||
fn myfun1();
|
||||
|
||||
#[align(1, 2)] //~ ERROR malformed `align` attribute input
|
||||
fn myfun2();
|
||||
}
|
||||
|
||||
#[align = 16] //~ ERROR malformed `align` attribute input
|
||||
|
|
|
|||
|
|
@ -2,54 +2,55 @@ error[E0539]: malformed `align` attribute input
|
|||
--> $DIR/malformed-fn-align.rs:5:5
|
||||
|
|
||||
LL | #[align]
|
||||
| ^^^^^^^^ expected this to be a list
|
||||
| ^^^^^^^^
|
||||
| |
|
||||
| expected this to be a list
|
||||
| help: must be of the form: `#[align(<alignment in bytes>)]`
|
||||
|
||||
error[E0805]: malformed `align` attribute input
|
||||
--> $DIR/malformed-fn-align.rs:8:5
|
||||
|
|
||||
help: try changing it to one of the following valid forms of the attribute
|
||||
|
|
||||
LL | #[align(<alignment in bytes>)]
|
||||
| ++++++++++++++++++++++
|
||||
LL | #[align(1, 2)]
|
||||
| ^^^^^^^------^
|
||||
| | |
|
||||
| | expected a single argument here
|
||||
| help: must be of the form: `#[align(<alignment in bytes>)]`
|
||||
|
||||
error[E0539]: malformed `align` attribute input
|
||||
--> $DIR/malformed-fn-align.rs:9:1
|
||||
--> $DIR/malformed-fn-align.rs:12:1
|
||||
|
|
||||
LL | #[align = 16]
|
||||
| ^^^^^^^^^^^^^ expected this to be a list
|
||||
|
|
||||
help: try changing it to one of the following valid forms of the attribute
|
||||
|
|
||||
LL - #[align = 16]
|
||||
LL + #[align(<alignment in bytes>)]
|
||||
|
|
||||
LL - #[align = 16]
|
||||
LL + #[align]
|
||||
|
|
||||
| ^^^^^^^^^^^^^
|
||||
| |
|
||||
| expected this to be a list
|
||||
| help: must be of the form: `#[align(<alignment in bytes>)]`
|
||||
|
||||
error[E0589]: invalid alignment value: not an unsuffixed integer
|
||||
--> $DIR/malformed-fn-align.rs:12:9
|
||||
--> $DIR/malformed-fn-align.rs:15:9
|
||||
|
|
||||
LL | #[align("hello")]
|
||||
| ^^^^^^^
|
||||
|
||||
error[E0589]: invalid alignment value: not a power of two
|
||||
--> $DIR/malformed-fn-align.rs:15:9
|
||||
--> $DIR/malformed-fn-align.rs:18:9
|
||||
|
|
||||
LL | #[align(0)]
|
||||
| ^
|
||||
|
||||
error: `#[repr(align(...))]` is not supported on function items
|
||||
--> $DIR/malformed-fn-align.rs:18:8
|
||||
--> $DIR/malformed-fn-align.rs:21:8
|
||||
|
|
||||
LL | #[repr(align(16))]
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
help: use `#[align(...)]` instead
|
||||
--> $DIR/malformed-fn-align.rs:18:8
|
||||
--> $DIR/malformed-fn-align.rs:21:8
|
||||
|
|
||||
LL | #[repr(align(16))]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `#[align(...)]` is not supported on struct items
|
||||
--> $DIR/malformed-fn-align.rs:21:1
|
||||
--> $DIR/malformed-fn-align.rs:24:1
|
||||
|
|
||||
LL | #[align(16)]
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
@ -60,7 +61,7 @@ LL - #[align(16)]
|
|||
LL + #[repr(align(16))]
|
||||
|
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0539, E0589.
|
||||
Some errors have detailed explanations: E0539, E0589, E0805.
|
||||
For more information about an error, try `rustc --explain E0539`.
|
||||
|
|
|
|||
|
|
@ -22,12 +22,10 @@ error[E0539]: malformed `align` attribute input
|
|||
--> $DIR/feature-gate-fn_align.rs:8:5
|
||||
|
|
||||
LL | #[align]
|
||||
| ^^^^^^^^ expected this to be a list
|
||||
|
|
||||
help: try changing it to one of the following valid forms of the attribute
|
||||
|
|
||||
LL | #[align(<alignment in bytes>)]
|
||||
| ++++++++++++++++++++++
|
||||
| ^^^^^^^^
|
||||
| |
|
||||
| expected this to be a list
|
||||
| help: must be of the form: `#[align(<alignment in bytes>)]`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue