From 1045b70304ab6a875fc9fbf10cb7e7c010edd3ab Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 26 Jun 2025 18:03:00 -0700 Subject: [PATCH 01/37] tests: add test for invalid interrupt signatures --- .../interrupt-invalid-signature.avr.stderr | 28 +++++ .../interrupt-invalid-signature.i686.stderr | 15 +++ .../interrupt-invalid-signature.msp430.stderr | 28 +++++ ...interrupt-invalid-signature.riscv32.stderr | 54 +++++++++ ...interrupt-invalid-signature.riscv64.stderr | 54 +++++++++ tests/ui/abi/interrupt-invalid-signature.rs | 110 ++++++++++++++++++ .../interrupt-invalid-signature.x64.stderr | 15 +++ 7 files changed, 304 insertions(+) create mode 100644 tests/ui/abi/interrupt-invalid-signature.avr.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.i686.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.msp430.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.riscv32.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.riscv64.stderr create mode 100644 tests/ui/abi/interrupt-invalid-signature.rs create mode 100644 tests/ui/abi/interrupt-invalid-signature.x64.stderr diff --git a/tests/ui/abi/interrupt-invalid-signature.avr.stderr b/tests/ui/abi/interrupt-invalid-signature.avr.stderr new file mode 100644 index 0000000000000..91f5ca2022e38 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.avr.stderr @@ -0,0 +1,28 @@ +error: invalid signature for `extern "avr-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:44:35 + | +LL | extern "avr-interrupt" fn avr_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "avr-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "avr-interrupt" fn avr_arg(_byte: u8) {} +LL + extern "avr-interrupt" fn avr_arg() {} + | + +error: invalid signature for `extern "avr-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:59:40 + | +LL | extern "avr-interrupt" fn avr_ret() -> u8 { + | ^^ + | + = note: functions with the "avr-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "avr-interrupt" fn avr_ret() -> u8 { +LL + extern "avr-interrupt" fn avr_ret() { + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.i686.stderr b/tests/ui/abi/interrupt-invalid-signature.i686.stderr new file mode 100644 index 0000000000000..06fd513df19ea --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.i686.stderr @@ -0,0 +1,15 @@ +error: invalid signature for `extern "x86-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + | + = note: functions with the "custom" ABI cannot have a return type +help: remove the return type + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/abi/interrupt-invalid-signature.msp430.stderr b/tests/ui/abi/interrupt-invalid-signature.msp430.stderr new file mode 100644 index 0000000000000..38479f93de58f --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.msp430.stderr @@ -0,0 +1,28 @@ +error: invalid signature for `extern "msp430-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:40:41 + | +LL | extern "msp430-interrupt" fn msp430_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "msp430-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "msp430-interrupt" fn msp430_arg(_byte: u8) {} +LL + extern "msp430-interrupt" fn msp430_arg() {} + | + +error: invalid signature for `extern "msp430-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:65:46 + | +LL | extern "msp430-interrupt" fn msp430_ret() -> u8 { + | ^^ + | + = note: functions with the "msp430-interrupt" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "msp430-interrupt" fn msp430_ret() -> u8 { +LL + extern "msp430-interrupt" fn msp430_ret() { + | + +error: aborting due to 2 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.riscv32.stderr b/tests/ui/abi/interrupt-invalid-signature.riscv32.stderr new file mode 100644 index 0000000000000..d632b17ab83f7 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.riscv32.stderr @@ -0,0 +1,54 @@ +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:48:43 + | +LL | extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} +LL + extern "riscv-interrupt-m" fn riscv_m_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:52:43 + | +LL | extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} +LL + extern "riscv-interrupt-s" fn riscv_s_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:71:48 + | +LL | extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { +LL + extern "riscv-interrupt-m" fn riscv_m_ret() { + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:77:48 + | +LL | extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { +LL + extern "riscv-interrupt-s" fn riscv_s_ret() { + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.riscv64.stderr b/tests/ui/abi/interrupt-invalid-signature.riscv64.stderr new file mode 100644 index 0000000000000..d632b17ab83f7 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.riscv64.stderr @@ -0,0 +1,54 @@ +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:48:43 + | +LL | extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} +LL + extern "riscv-interrupt-m" fn riscv_m_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:52:43 + | +LL | extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} + | ^^^^^^^^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} +LL + extern "riscv-interrupt-s" fn riscv_s_arg() {} + | + +error: invalid signature for `extern "riscv-interrupt-m"` function + --> $DIR/interrupt-invalid-signature.rs:71:48 + | +LL | extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-m" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { +LL + extern "riscv-interrupt-m" fn riscv_m_ret() { + | + +error: invalid signature for `extern "riscv-interrupt-s"` function + --> $DIR/interrupt-invalid-signature.rs:77:48 + | +LL | extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { + | ^^ + | + = note: functions with the "riscv-interrupt-s" ABI cannot have any parameters or return type +help: remove the parameters and return type + | +LL - extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { +LL + extern "riscv-interrupt-s" fn riscv_s_ret() { + | + +error: aborting due to 4 previous errors + diff --git a/tests/ui/abi/interrupt-invalid-signature.rs b/tests/ui/abi/interrupt-invalid-signature.rs new file mode 100644 index 0000000000000..e389285b06917 --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.rs @@ -0,0 +1,110 @@ +/*! Tests interrupt ABIs have a constricted signature + +Most interrupt ABIs share a similar restriction in terms of not allowing most signatures. +Specifically, they generally cannot have arguments or return types. +So we test that they error in essentially all of the same places. +A notable and interesting exception is x86. + +This test uses `cfg` because it is not testing whether these ABIs work on the platform. +*/ +//@ add-core-stubs +//@ revisions: x64 i686 riscv32 riscv64 avr msp430 +// +//@ [x64] needs-llvm-components: x86 +//@ [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib +//@ [i686] needs-llvm-components: x86 +//@ [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib +//@ [riscv32] needs-llvm-components: riscv +//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf --crate-type=rlib +//@ [riscv64] needs-llvm-components: riscv +//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf --crate-type=rlib +//@ [avr] needs-llvm-components: avr +//@ [avr] compile-flags: --target=avr-none -C target-cpu=atmega328p --crate-type=rlib +//@ [msp430] needs-llvm-components: msp430 +//@ [msp430] compile-flags: --target=msp430-none-elf --crate-type=rlib +#![no_core] +#![feature( + no_core, + abi_msp430_interrupt, + abi_avr_interrupt, + abi_x86_interrupt, + abi_riscv_interrupt +)] + +extern crate minicore; +use minicore::*; + +/* most extern "interrupt" definitions should not accept args */ + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_arg(_byte: u8) {} +//[msp430]~^ ERROR invalid signature + +#[cfg(avr)] +extern "avr-interrupt" fn avr_arg(_byte: u8) {} +//[avr]~^ ERROR invalid signature + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_arg(_byte: u8) {} +//[riscv32,riscv64]~^ ERROR invalid signature + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_arg(_byte: u8) {} +//[riscv32,riscv64]~^ ERROR invalid signature + + +/* all extern "interrupt" definitions should not return non-1ZST values */ + +#[cfg(avr)] +extern "avr-interrupt" fn avr_ret() -> u8 { + //[avr]~^ ERROR invalid signature + 1 +} + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_ret() -> u8 { + //[msp430]~^ ERROR invalid signature + 1 +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_ret() -> u8 { + //[riscv32,riscv64]~^ ERROR invalid signature + 1 +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_ret() -> u8 { + //[riscv32,riscv64]~^ ERROR invalid signature + 1 +} + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_ret() -> u8 { + //[x64,i686]~^ ERROR invalid signature + 1 +} + + + +/* extern "interrupt" fnptrs with invalid signatures */ + +#[cfg(avr)] +fn avr_ptr(_f: extern "avr-interrupt" fn(u8) -> u8) { +} + +#[cfg(msp430)] +fn msp430_ptr(_f: extern "msp430-interrupt" fn(u8) -> u8) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_m_ptr(_f: extern "riscv-interrupt-m" fn(u8) -> u8) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_s_ptr(_f: extern "riscv-interrupt-s" fn(u8) -> u8) { +} + +#[cfg(any(x64,i686))] +fn x86_ptr(_f: extern "x86-interrupt" fn() -> u8) { +} diff --git a/tests/ui/abi/interrupt-invalid-signature.x64.stderr b/tests/ui/abi/interrupt-invalid-signature.x64.stderr new file mode 100644 index 0000000000000..06fd513df19ea --- /dev/null +++ b/tests/ui/abi/interrupt-invalid-signature.x64.stderr @@ -0,0 +1,15 @@ +error: invalid signature for `extern "x86-interrupt"` function + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + | + = note: functions with the "custom" ABI cannot have a return type +help: remove the return type + --> $DIR/interrupt-invalid-signature.rs:83:40 + | +LL | extern "x86-interrupt" fn x86_ret() -> u8 { + | ^^ + +error: aborting due to 1 previous error + From db33e98540fca8f3268cafc6276dbefd55681d55 Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 26 Jun 2025 18:02:54 -0700 Subject: [PATCH 02/37] compiler: fixup error message for x86-interrupt invalid returns --- compiler/rustc_ast_passes/messages.ftl | 2 +- tests/ui/abi/interrupt-invalid-signature.i686.stderr | 2 +- tests/ui/abi/interrupt-invalid-signature.x64.stderr | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 6eddce7b590ac..869c3a58ae3df 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -17,7 +17,7 @@ ast_passes_abi_must_not_have_parameters_or_return_type= ast_passes_abi_must_not_have_return_type= invalid signature for `extern {$abi}` function - .note = functions with the "custom" ABI cannot have a return type + .note = functions with the {$abi} ABI cannot have a return type .help = remove the return type ast_passes_assoc_const_without_body = diff --git a/tests/ui/abi/interrupt-invalid-signature.i686.stderr b/tests/ui/abi/interrupt-invalid-signature.i686.stderr index 06fd513df19ea..86f2e097c37e1 100644 --- a/tests/ui/abi/interrupt-invalid-signature.i686.stderr +++ b/tests/ui/abi/interrupt-invalid-signature.i686.stderr @@ -4,7 +4,7 @@ error: invalid signature for `extern "x86-interrupt"` function LL | extern "x86-interrupt" fn x86_ret() -> u8 { | ^^ | - = note: functions with the "custom" ABI cannot have a return type + = note: functions with the "x86-interrupt" ABI cannot have a return type help: remove the return type --> $DIR/interrupt-invalid-signature.rs:83:40 | diff --git a/tests/ui/abi/interrupt-invalid-signature.x64.stderr b/tests/ui/abi/interrupt-invalid-signature.x64.stderr index 06fd513df19ea..86f2e097c37e1 100644 --- a/tests/ui/abi/interrupt-invalid-signature.x64.stderr +++ b/tests/ui/abi/interrupt-invalid-signature.x64.stderr @@ -4,7 +4,7 @@ error: invalid signature for `extern "x86-interrupt"` function LL | extern "x86-interrupt" fn x86_ret() -> u8 { | ^^ | - = note: functions with the "custom" ABI cannot have a return type + = note: functions with the "x86-interrupt" ABI cannot have a return type help: remove the return type --> $DIR/interrupt-invalid-signature.rs:83:40 | From d6b3314fb29cc688a968811b6a78dc8a5309cb7d Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Thu, 26 Jun 2025 19:35:28 -0700 Subject: [PATCH 03/37] compiler: allow interrupts to return () or ! --- .../rustc_ast_passes/src/ast_validation.rs | 16 ++- .../ui/abi/interrupt-returns-never-or-unit.rs | 110 ++++++++++++++++++ 2 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 tests/ui/abi/interrupt-returns-never-or-unit.rs diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index da24825120376..478cec23fd57e 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -396,7 +396,13 @@ impl<'a> AstValidator<'a> { if let InterruptKind::X86 = interrupt_kind { // "x86-interrupt" is special because it does have arguments. // FIXME(workingjubilee): properly lint on acceptable input types. - if let FnRetTy::Ty(ref ret_ty) = sig.decl.output { + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output + && match &ret_ty.kind { + TyKind::Never => false, + TyKind::Tup(tup) if tup.is_empty() => false, + _ => true, + } + { self.dcx().emit_err(errors::AbiMustNotHaveReturnType { span: ret_ty.span, abi, @@ -455,7 +461,13 @@ impl<'a> AstValidator<'a> { fn reject_params_or_return(&self, abi: ExternAbi, ident: &Ident, sig: &FnSig) { let mut spans: Vec<_> = sig.decl.inputs.iter().map(|p| p.span).collect(); - if let FnRetTy::Ty(ref ret_ty) = sig.decl.output { + if let FnRetTy::Ty(ref ret_ty) = sig.decl.output + && match &ret_ty.kind { + TyKind::Never => false, + TyKind::Tup(tup) if tup.is_empty() => false, + _ => true, + } + { spans.push(ret_ty.span); } diff --git a/tests/ui/abi/interrupt-returns-never-or-unit.rs b/tests/ui/abi/interrupt-returns-never-or-unit.rs new file mode 100644 index 0000000000000..34d509c832872 --- /dev/null +++ b/tests/ui/abi/interrupt-returns-never-or-unit.rs @@ -0,0 +1,110 @@ +/*! Tests interrupt ABIs can return ! + +Most interrupt ABIs share a similar restriction in terms of not allowing most signatures, +but it makes sense to allow them to return ! because they could indeed be divergent. + +This test uses `cfg` because it is not testing whether these ABIs work on the platform. +*/ +//@ add-core-stubs +//@ revisions: x64 i686 riscv32 riscv64 avr msp430 +//@ build-pass +// +//@ [x64] needs-llvm-components: x86 +//@ [x64] compile-flags: --target=x86_64-unknown-linux-gnu --crate-type=rlib +//@ [i686] needs-llvm-components: x86 +//@ [i686] compile-flags: --target=i686-unknown-linux-gnu --crate-type=rlib +//@ [riscv32] needs-llvm-components: riscv +//@ [riscv32] compile-flags: --target=riscv32i-unknown-none-elf --crate-type=rlib +//@ [riscv64] needs-llvm-components: riscv +//@ [riscv64] compile-flags: --target=riscv64gc-unknown-none-elf --crate-type=rlib +//@ [avr] needs-llvm-components: avr +//@ [avr] compile-flags: --target=avr-none -C target-cpu=atmega328p --crate-type=rlib +//@ [msp430] needs-llvm-components: msp430 +//@ [msp430] compile-flags: --target=msp430-none-elf --crate-type=rlib +#![no_core] +#![feature( + no_core, + abi_msp430_interrupt, + abi_avr_interrupt, + abi_x86_interrupt, + abi_riscv_interrupt +)] + +extern crate minicore; +use minicore::*; + +/* interrupts can return never */ + +#[cfg(avr)] +extern "avr-interrupt" fn avr_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_ret_never() -> ! { + unsafe { hint::unreachable_unchecked() } +} + +/* interrupts can return explicit () */ + +#[cfg(avr)] +extern "avr-interrupt" fn avr_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(msp430)] +extern "msp430-interrupt" fn msp430_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-m" fn riscv_m_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(riscv32,riscv64))] +extern "riscv-interrupt-s" fn riscv_s_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +#[cfg(any(x64,i686))] +extern "x86-interrupt" fn x86_ret_unit() -> () { + unsafe { hint::unreachable_unchecked() } +} + +/* extern "interrupt" fnptrs can return ! too */ + +#[cfg(avr)] +fn avr_ptr(_f: extern "avr-interrupt" fn() -> !) { +} + +#[cfg(msp430)] +fn msp430_ptr(_f: extern "msp430-interrupt" fn() -> !) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_m_ptr(_f: extern "riscv-interrupt-m" fn() -> !) { +} + +#[cfg(any(riscv32,riscv64))] +fn riscv_s_ptr(_f: extern "riscv-interrupt-s" fn() -> !) { +} + +#[cfg(any(x64,i686))] +fn x86_ptr(_f: extern "x86-interrupt" fn() -> !) { +} From b5ab966626e006172027e658fb1e43cf79f1c21a Mon Sep 17 00:00:00 2001 From: Jubilee Young Date: Fri, 27 Jun 2025 11:03:23 -0700 Subject: [PATCH 04/37] remember how to write never returns --- .../ui/abi/interrupt-returns-never-or-unit.rs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/ui/abi/interrupt-returns-never-or-unit.rs b/tests/ui/abi/interrupt-returns-never-or-unit.rs index 34d509c832872..8e224229a0b17 100644 --- a/tests/ui/abi/interrupt-returns-never-or-unit.rs +++ b/tests/ui/abi/interrupt-returns-never-or-unit.rs @@ -37,54 +37,54 @@ use minicore::*; #[cfg(avr)] extern "avr-interrupt" fn avr_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } #[cfg(msp430)] extern "msp430-interrupt" fn msp430_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } #[cfg(any(riscv32,riscv64))] extern "riscv-interrupt-m" fn riscv_m_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } #[cfg(any(riscv32,riscv64))] extern "riscv-interrupt-s" fn riscv_s_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } #[cfg(any(x64,i686))] extern "x86-interrupt" fn x86_ret_never() -> ! { - unsafe { hint::unreachable_unchecked() } + loop {} } /* interrupts can return explicit () */ #[cfg(avr)] extern "avr-interrupt" fn avr_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } #[cfg(msp430)] extern "msp430-interrupt" fn msp430_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } #[cfg(any(riscv32,riscv64))] extern "riscv-interrupt-m" fn riscv_m_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } #[cfg(any(riscv32,riscv64))] extern "riscv-interrupt-s" fn riscv_s_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } #[cfg(any(x64,i686))] extern "x86-interrupt" fn x86_ret_unit() -> () { - unsafe { hint::unreachable_unchecked() } + () } /* extern "interrupt" fnptrs can return ! too */ From a42833e0aae1a0c296609040f4a893f37e199525 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 10 Aug 2025 00:27:35 +0000 Subject: [PATCH 05/37] cargo update compiler & tools dependencies: Locking 18 packages to latest compatible versions Updating anstream v0.6.19 -> v0.6.20 Updating anstyle-query v1.1.3 -> v1.1.4 Updating anstyle-svg v0.1.9 -> v0.1.10 Updating anstyle-wincon v3.0.9 -> v3.0.10 Updating camino v1.1.10 -> v1.1.11 Updating clap v4.5.42 -> v4.5.43 Updating clap_builder v4.5.42 -> v4.5.43 Updating cxx v1.0.161 -> v1.0.166 Updating cxx-build v1.0.161 -> v1.0.166 Updating cxxbridge-cmd v1.0.161 -> v1.0.166 Updating cxxbridge-flags v1.0.161 -> v1.0.166 Updating cxxbridge-macro v1.0.161 -> v1.0.166 Updating derive-where v1.5.0 -> v1.6.0 Updating hashbrown v0.15.4 -> v0.15.5 Updating indenter v0.3.3 -> v0.3.4 Updating rustversion v1.0.21 -> v1.0.22 Updating scratch v1.0.8 -> v1.0.9 Updating zerovec v0.11.2 -> v0.11.4 note: pass `--verbose` to see 36 unchanged dependencies behind latest library dependencies: Locking 1 package to latest compatible version Updating hashbrown v0.15.4 -> v0.15.5 note: pass `--verbose` to see 2 unchanged dependencies behind latest rustbook dependencies: Locking 10 packages to latest compatible versions Updating anstream v0.6.19 -> v0.6.20 Updating anstyle-query v1.1.3 -> v1.1.4 Updating anstyle-wincon v3.0.9 -> v3.0.10 Updating cc v1.2.31 -> v1.2.32 Updating clap v4.5.42 -> v4.5.43 Updating clap_builder v4.5.42 -> v4.5.43 Updating clap_complete v4.5.55 -> v4.5.56 Updating hashbrown v0.15.4 -> v0.15.5 Updating rustversion v1.0.21 -> v1.0.22 Updating zerovec v0.11.2 -> v0.11.4 --- Cargo.lock | 90 +++++++++++++++++------------------ library/Cargo.lock | 4 +- src/tools/rustbook/Cargo.lock | 44 ++++++++--------- 3 files changed, 69 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dbb76ada8377f..a69fa56f051bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -119,18 +119,18 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-svg" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a43964079ef399480603125d5afae2b219aceffb77478956e25f17b9bc3435c" +checksum = "dc03a770ef506fe1396c0e476120ac0e6523cf14b74218dd5f18cd6833326fa9" dependencies = [ "anstyle", "anstyle-lossy", @@ -141,13 +141,13 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -353,9 +353,9 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "camino" -version = "1.1.10" +version = "1.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" +checksum = "5d07aa9a93b00c76f71bc35d598bed923f6d4f3a9ca5c24b7737ae1a292841c0" dependencies = [ "serde", ] @@ -518,9 +518,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.42" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" dependencies = [ "clap_builder", "clap_derive", @@ -538,9 +538,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.42" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" dependencies = [ "anstream", "anstyle", @@ -937,9 +937,9 @@ dependencies = [ [[package]] name = "cxx" -version = "1.0.161" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3523cc02ad831111491dd64b27ad999f1ae189986728e477604e61b81f828df" +checksum = "b5287274dfdf7e7eaa3d97d460eb2a94922539e6af214bda423f292105011ee2" dependencies = [ "cc", "cxxbridge-cmd", @@ -951,9 +951,9 @@ dependencies = [ [[package]] name = "cxx-build" -version = "1.0.161" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212b754247a6f07b10fa626628c157593f0abf640a3dd04cce2760eca970f909" +checksum = "65f3ce027a744135db10a1ebffa0863dab685aeef48f40a02c201f5e70c667d3" dependencies = [ "cc", "codespan-reporting", @@ -966,9 +966,9 @@ dependencies = [ [[package]] name = "cxxbridge-cmd" -version = "1.0.161" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f426a20413ec2e742520ba6837c9324b55ffac24ead47491a6e29f933c5b135a" +checksum = "a07dc23f2eea4774297f4c9a17ae4065fecb63127da556e6c9fadb0216d93595" dependencies = [ "clap", "codespan-reporting", @@ -980,15 +980,15 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.161" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258b6069020b4e5da6415df94a50ee4f586a6c38b037a180e940a43d06a070d" +checksum = "f7a4dbad6171f763c4066c83dcd27546b6e93c5c5ae2229f9813bda7233f571d" [[package]] name = "cxxbridge-macro" -version = "1.0.161" +version = "1.0.166" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dec184b52be5008d6eaf7e62fc1802caf1ad1227d11b3b7df2c409c7ffc3f4" +checksum = "a9be4b527950fc42db06163705e78e73eedc8fd723708e942afe3572a9a2c366" dependencies = [ "indexmap", "proc-macro2", @@ -1055,9 +1055,9 @@ version = "0.1.91" [[package]] name = "derive-where" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510c292c8cf384b1a340b816a9a6cf2599eb8f566a44949024af88418000c50b" +checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", @@ -1567,9 +1567,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", @@ -1688,7 +1688,7 @@ dependencies = [ "potential_utf", "yoke 0.8.0", "zerofrom", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -1721,7 +1721,7 @@ dependencies = [ "litemap 0.8.0", "tinystr 0.8.1", "writeable 0.6.1", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -1769,7 +1769,7 @@ dependencies = [ "icu_properties", "icu_provider 2.0.0", "smallvec", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -1791,7 +1791,7 @@ dependencies = [ "icu_provider 2.0.0", "potential_utf", "zerotrie", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -1831,7 +1831,7 @@ dependencies = [ "yoke 0.8.0", "zerofrom", "zerotrie", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -1909,9 +1909,9 @@ dependencies = [ [[package]] name = "indenter" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" @@ -2971,7 +2971,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" dependencies = [ - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -4918,9 +4918,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ruzstd" @@ -4978,9 +4978,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "scratch" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f6280af86e5f559536da57a45ebc84948833b3bee313a7dd25232e09c878a52" +checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" [[package]] name = "self_cell" @@ -5506,7 +5506,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ "displaydoc", - "zerovec 0.11.2", + "zerovec 0.11.4", ] [[package]] @@ -6852,9 +6852,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke 0.8.0", "zerofrom", diff --git a/library/Cargo.lock b/library/Cargo.lock index 09228825086ee..4b06f14c7eea7 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -120,9 +120,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "rustc-std-workspace-alloc", "rustc-std-workspace-core", diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index d0eeab2206237..e42f266391e2f 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -47,9 +47,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -77,22 +77,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -156,9 +156,9 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "cc" -version = "1.2.31" +version = "1.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" +checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" dependencies = [ "shlex", ] @@ -185,9 +185,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.42" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" +checksum = "50fd97c9dc2399518aa331917ac6f274280ec5eb34e555dd291899745c48ec6f" dependencies = [ "clap_builder", "clap_derive", @@ -195,9 +195,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.42" +version = "4.5.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" +checksum = "c35b5830294e1fa0462034af85cc95225a4cb07092c088c55bda3147cfcd8f65" dependencies = [ "anstream", "anstyle", @@ -208,9 +208,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.55" +version = "4.5.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5abde44486daf70c5be8b8f8f1b66c49f86236edf6fa2abadb4d961c4c6229a" +checksum = "67e4efcbb5da11a92e8a609233aa1e8a7d91e38de0be865f016d14700d45a7fd" dependencies = [ "clap", ] @@ -564,9 +564,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "heck" @@ -1406,9 +1406,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" @@ -2183,9 +2183,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", From bc14ad38a394df0e74eb6e204d75038a8cf6ef99 Mon Sep 17 00:00:00 2001 From: Pierre Tardy Date: Wed, 6 Aug 2025 16:18:25 +0200 Subject: [PATCH 06/37] strip prefix of temporary file names when it exceeds filesystem name length limit When doing lto, rustc generates filenames that are concatenating many information. In the case of this testcase, it is concatenating crate name and rust file name, plus some hash, and the extension. In some other cases it will concatenate even more information reducing the maximum effective crate name to about 110 chars on linux filesystems where filename max length is 255 This commit is ensuring that the temporary file names are limited in size, while still reasonabily ensuring the unicity (with hashing of the stripped part) --- compiler/rustc_session/src/config.rs | 23 ++++++++++++++-- tests/run-make/lto-long-filenames/main.rs | 7 +++++ tests/run-make/lto-long-filenames/rmake.rs | 32 ++++++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 tests/run-make/lto-long-filenames/main.rs create mode 100644 tests/run-make/lto-long-filenames/rmake.rs diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index cfeadf3c7595a..ac6eeca300775 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -16,10 +16,11 @@ use std::{cmp, fmt, fs, iter}; use externs::{ExternOpt, split_extern_opt}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; -use rustc_data_structures::stable_hasher::{StableOrd, ToStableHashKey}; +use rustc_data_structures::stable_hasher::{StableHasher, StableOrd, ToStableHashKey}; use rustc_errors::emitter::HumanReadableErrorType; use rustc_errors::{ColorConfig, DiagArgValue, DiagCtxtFlags, IntoDiagArg}; use rustc_feature::UnstableFeatures; +use rustc_hashes::Hash64; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::edition::{DEFAULT_EDITION, EDITION_NAME_LIST, Edition, LATEST_STABLE_EDITION}; use rustc_span::source_map::FilePathMapping; @@ -1198,7 +1199,25 @@ pub struct OutputFilenames { pub const RLINK_EXT: &str = "rlink"; pub const RUST_CGU_EXT: &str = "rcgu"; pub const DWARF_OBJECT_EXT: &str = "dwo"; +pub const MAX_FILENAME_LENGTH: usize = 143; // ecryptfs limits filenames to 143 bytes see #49914 +/// Ensure the filename is not too long, as some filesystems have a limit. +/// If the filename is too long, hash part of it and append the hash to the filename. +/// This is a workaround for long crate names generating overly long filenames. +fn maybe_strip_file_name(mut path: PathBuf) -> PathBuf { + if path.file_name().map_or(0, |name| name.len()) > MAX_FILENAME_LENGTH { + let filename = path.file_name().unwrap().to_string_lossy(); + let hash_len = 64 / 4; // Hash64 is 64 bits encoded in hex + let stripped_len = filename.len() - MAX_FILENAME_LENGTH + hash_len; + + let mut hasher = StableHasher::new(); + filename[..stripped_len].hash(&mut hasher); + let hash = hasher.finish::(); + + path.set_file_name(format!("{:x}-{}", hash, &filename[stripped_len..])); + } + path +} impl OutputFilenames { pub fn new( out_directory: PathBuf, @@ -1291,7 +1310,7 @@ impl OutputFilenames { } let temps_directory = self.temps_directory.as_ref().unwrap_or(&self.out_directory); - self.with_directory_and_extension(temps_directory, &extension) + maybe_strip_file_name(self.with_directory_and_extension(temps_directory, &extension)) } pub fn temp_path_for_diagnostic(&self, ext: &str) -> PathBuf { diff --git a/tests/run-make/lto-long-filenames/main.rs b/tests/run-make/lto-long-filenames/main.rs new file mode 100644 index 0000000000000..daedff5c05fc1 --- /dev/null +++ b/tests/run-make/lto-long-filenames/main.rs @@ -0,0 +1,7 @@ +// This file has very long lines, but there is no way to avoid it as we are testing +// long crate names. so: +// ignore-tidy-linelength + +extern crate generated_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_crate_name; + +fn main() {} diff --git a/tests/run-make/lto-long-filenames/rmake.rs b/tests/run-make/lto-long-filenames/rmake.rs new file mode 100644 index 0000000000000..9e0ba63e9f5b8 --- /dev/null +++ b/tests/run-make/lto-long-filenames/rmake.rs @@ -0,0 +1,32 @@ +// This file has very long lines, but there is no way to avoid it as we are testing +// long crate names. so: +// ignore-tidy-linelength + +// A variant of the smoke test to check that link time optimization +// (LTO) is accepted by the compiler, and that +// passing its various flags still results in successful compilation, even for very long crate names. +// See https://github.com/rust-lang/rust/issues/49914 + +//@ ignore-cross-compile + +use std::fs; + +use run_make_support::{rfs, rustc}; + +// This test make sure we don't get such following error: +// error: could not write output to generated_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_crate_name.generated_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_crate_name.9384edb61bfd127c-cgu.0.rcgu.o: File name too long +// as reported in issue #49914 +fn main() { + let lto_flags = ["-Clto", "-Clto=yes", "-Clto=off", "-Clto=thin", "-Clto=fat"]; + let aux_file = "generated_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_large_crate_name.rs"; + // The auxiliary file is used to test long crate names. + // The file name is intentionally long to test the handling of long filenames. + // We don't commit it to avoid issues with Windows paths which have known limitations for the full path length. + // Posix usually only have a limit for the length of the file name. + rfs::write(aux_file, "#![crate_type = \"rlib\"]\n"); + + for flag in lto_flags { + rustc().input(aux_file).arg(flag).run(); + rustc().input("main.rs").arg(flag).run(); + } +} From d6c7cf7bb33ceefb9639783425be2f9e9cd8532a Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 7 Aug 2025 19:43:15 +0300 Subject: [PATCH 07/37] resolve: Introduce `RibKind::Block` to avoid confusing module items, blocks with items, and blocks without items. --- compiler/rustc_resolve/src/ident.rs | 5 ++- compiler/rustc_resolve/src/late.rs | 33 ++++++++++--------- .../rustc_resolve/src/late/diagnostics.rs | 8 ++--- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 9efcef695b7b4..8d3a19bbf2a52 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -331,7 +331,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } module = match rib.kind { - RibKind::Module(module) => module, + RibKind::Module(module) | RibKind::Block(Some(module)) => module, RibKind::MacroDefinition(def) if def == self.macro_def(ident.span.ctxt()) => { // If an invocation of this macro created `ident`, give up on `ident` // and switch to `ident`'s source from the macro definition. @@ -1167,6 +1167,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for rib in ribs { match rib.kind { RibKind::Normal + | RibKind::Block(..) | RibKind::FnOrCoroutine | RibKind::Module(..) | RibKind::MacroDefinition(..) @@ -1259,6 +1260,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for rib in ribs { let (has_generic_params, def_kind) = match rib.kind { RibKind::Normal + | RibKind::Block(..) | RibKind::FnOrCoroutine | RibKind::Module(..) | RibKind::MacroDefinition(..) @@ -1352,6 +1354,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { for rib in ribs { let (has_generic_params, def_kind) = match rib.kind { RibKind::Normal + | RibKind::Block(..) | RibKind::FnOrCoroutine | RibKind::Module(..) | RibKind::MacroDefinition(..) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 953b72fd72b8f..9c41e2a2d58fd 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -192,6 +192,13 @@ pub(crate) enum RibKind<'ra> { /// No restriction needs to be applied. Normal, + /// We passed through an `ast::Block`. + /// Behaves like `Normal`, but also partially like `Module` if the block contains items. + /// `Block(None)` must be always processed in the same way as `Block(Some(module))` + /// with empty `module`. The module can be `None` only because creation of some definitely + /// empty modules is skipped as an optimization. + Block(Option>), + /// We passed through an impl or trait and are now in one of its /// methods or associated types. Allow references to ty params that impl or trait /// binds. Disallow any other upvars (including other ty params that are @@ -210,7 +217,7 @@ pub(crate) enum RibKind<'ra> { /// All other constants aren't allowed to use generic params at all. ConstantItem(ConstantHasGenerics, Option<(Ident, ConstantItemKind)>), - /// We passed through a module. + /// We passed through a module item. Module(Module<'ra>), /// We passed through a `macro_rules!` statement @@ -242,6 +249,7 @@ impl RibKind<'_> { pub(crate) fn contains_params(&self) -> bool { match self { RibKind::Normal + | RibKind::Block(..) | RibKind::FnOrCoroutine | RibKind::ConstantItem(..) | RibKind::Module(_) @@ -258,15 +266,8 @@ impl RibKind<'_> { fn is_label_barrier(self) -> bool { match self { RibKind::Normal | RibKind::MacroDefinition(..) => false, - - RibKind::AssocItem - | RibKind::FnOrCoroutine - | RibKind::Item(..) - | RibKind::ConstantItem(..) - | RibKind::Module(..) - | RibKind::ForwardGenericParamBan(_) - | RibKind::ConstParamTy - | RibKind::InlineAsmSym => true, + RibKind::FnOrCoroutine | RibKind::ConstantItem(..) => true, + kind => bug!("unexpected rib kind: {kind:?}"), } } } @@ -2821,9 +2822,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // We also can't shadow bindings from associated parent items. for ns in [ValueNS, TypeNS] { for parent_rib in self.ribs[ns].iter().rev() { - // Break at mod level, to account for nested items which are + // Break at module or block level, to account for nested items which are // allowed to shadow generic param names. - if matches!(parent_rib.kind, RibKind::Module(..)) { + if matches!(parent_rib.kind, RibKind::Module(..) | RibKind::Block(..)) { break; } @@ -4664,16 +4665,16 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { debug!("(resolving block) entering block"); // Move down in the graph, if there's an anonymous module rooted here. let orig_module = self.parent_scope.module; - let anonymous_module = self.r.block_map.get(&block.id).cloned(); // clones a reference + let anonymous_module = self.r.block_map.get(&block.id).copied(); let mut num_macro_definition_ribs = 0; if let Some(anonymous_module) = anonymous_module { debug!("(resolving block) found anonymous module, moving down"); - self.ribs[ValueNS].push(Rib::new(RibKind::Module(anonymous_module))); - self.ribs[TypeNS].push(Rib::new(RibKind::Module(anonymous_module))); + self.ribs[ValueNS].push(Rib::new(RibKind::Block(Some(anonymous_module)))); + self.ribs[TypeNS].push(Rib::new(RibKind::Block(Some(anonymous_module)))); self.parent_scope.module = anonymous_module; } else { - self.ribs[ValueNS].push(Rib::new(RibKind::Normal)); + self.ribs[ValueNS].push(Rib::new(RibKind::Block(None))); } // Descend into the block. diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index aca251da71d39..8a90b2af8da08 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -850,9 +850,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } // Try to find in last block rib - if let Some(rib) = &self.last_block_rib - && let RibKind::Normal = rib.kind - { + if let Some(rib) = &self.last_block_rib { for (ident, &res) in &rib.bindings { if let Res::Local(_) = res && path.len() == 1 @@ -901,7 +899,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { if path.len() == 1 { for rib in self.ribs[ns].iter().rev() { let item = path[0].ident; - if let RibKind::Module(module) = rib.kind + if let RibKind::Module(module) | RibKind::Block(Some(module)) = rib.kind && let Some(did) = find_doc_alias_name(self.r, module, item.name) { return Some((did, item)); @@ -2469,7 +2467,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } // Items in scope - if let RibKind::Module(module) = rib.kind { + if let RibKind::Module(module) | RibKind::Block(Some(module)) = rib.kind { // Items from this module self.r.add_module_candidates(module, &mut names, &filter_fn, Some(ctxt)); From 7af4ee8237e0eb02447813b4109a8a6ae7231c89 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 7 Aug 2025 21:27:29 +0300 Subject: [PATCH 08/37] resolve: Inline `with_mod_rib` --- compiler/rustc_resolve/src/late.rs | 44 ++++++++++++------------------ 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 9c41e2a2d58fd..26c2c2ce5246d 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1528,19 +1528,6 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { ret } - fn with_mod_rib(&mut self, id: NodeId, f: impl FnOnce(&mut Self) -> T) -> T { - let module = self.r.expect_module(self.r.local_def_id(id).to_def_id()); - // Move down in the graph. - let orig_module = replace(&mut self.parent_scope.module, module); - self.with_rib(ValueNS, RibKind::Module(module), |this| { - this.with_rib(TypeNS, RibKind::Module(module), |this| { - let ret = f(this); - this.parent_scope.module = orig_module; - ret - }) - }) - } - fn visit_generic_params(&mut self, params: &'ast [GenericParam], add_self_upper: bool) { // For type parameter defaults, we have to ban access // to following type parameters, as the GenericArgs can only @@ -2678,20 +2665,25 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { } ItemKind::Mod(..) => { - self.with_mod_rib(item.id, |this| { - if mod_inner_docs { - this.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); - } - let old_macro_rules = this.parent_scope.macro_rules; - visit::walk_item(this, item); - // Maintain macro_rules scopes in the same way as during early resolution - // for diagnostics and doc links. - if item.attrs.iter().all(|attr| { - !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape) - }) { - this.parent_scope.macro_rules = old_macro_rules; - } + let module = self.r.expect_module(self.r.local_def_id(item.id).to_def_id()); + let orig_module = replace(&mut self.parent_scope.module, module); + self.with_rib(ValueNS, RibKind::Module(module), |this| { + this.with_rib(TypeNS, RibKind::Module(module), |this| { + if mod_inner_docs { + this.resolve_doc_links(&item.attrs, MaybeExported::Ok(item.id)); + } + let old_macro_rules = this.parent_scope.macro_rules; + visit::walk_item(this, item); + // Maintain macro_rules scopes in the same way as during early resolution + // for diagnostics and doc links. + if item.attrs.iter().all(|attr| { + !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape) + }) { + this.parent_scope.macro_rules = old_macro_rules; + } + }) }); + self.parent_scope.module = orig_module; } ItemKind::Static(box ast::StaticItem { From 4c4c3d554f4942c59abbce0307bad17067582b96 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 7 Aug 2025 21:50:17 +0300 Subject: [PATCH 09/37] resolve: Restructure `resolve_ident_in_lexical_scope` for better clarity --- compiler/rustc_resolve/src/ident.rs | 79 ++++++++++++++--------------- 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 8d3a19bbf2a52..2c3790cf025c2 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -312,7 +312,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let normalized_ident = Ident { span: normalized_span, ..ident }; // Walk backwards up the ribs in scope. - let mut module = self.graph_root; for (i, rib) in ribs.iter().enumerate().rev() { debug!("walk rib\n{:?}", rib.bindings); // Use the rib kind to determine whether we are resolving parameters @@ -328,51 +327,47 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { *original_rib_ident_def, ribs, ))); + } else if let RibKind::Block(Some(module)) = rib.kind + && let Ok(binding) = self.cm().resolve_ident_in_module_unadjusted( + ModuleOrUniformRoot::Module(module), + ident, + ns, + parent_scope, + Shadowing::Unrestricted, + finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), + ignore_binding, + None, + ) + { + // The ident resolves to an item in a block. + return Some(LexicalScopeBinding::Item(binding)); + } else if let RibKind::Module(module) = rib.kind { + // Encountered a module item, abandon ribs and look into that module and preludes. + return self + .cm() + .early_resolve_ident_in_lexical_scope( + orig_ident, + ScopeSet::Late(ns, module, finalize.map(|finalize| finalize.node_id)), + parent_scope, + finalize, + finalize.is_some(), + ignore_binding, + None, + ) + .ok() + .map(LexicalScopeBinding::Item); } - module = match rib.kind { - RibKind::Module(module) | RibKind::Block(Some(module)) => module, - RibKind::MacroDefinition(def) if def == self.macro_def(ident.span.ctxt()) => { - // If an invocation of this macro created `ident`, give up on `ident` - // and switch to `ident`'s source from the macro definition. - ident.span.remove_mark(); - continue; - } - _ => continue, - }; - - match module.kind { - ModuleKind::Block => {} // We can see through blocks - _ => break, - } - - let item = self.cm().resolve_ident_in_module_unadjusted( - ModuleOrUniformRoot::Module(module), - ident, - ns, - parent_scope, - Shadowing::Unrestricted, - finalize.map(|finalize| Finalize { used: Used::Scope, ..finalize }), - ignore_binding, - None, - ); - if let Ok(binding) = item { - // The ident resolves to an item. - return Some(LexicalScopeBinding::Item(binding)); + if let RibKind::MacroDefinition(def) = rib.kind + && def == self.macro_def(ident.span.ctxt()) + { + // If an invocation of this macro created `ident`, give up on `ident` + // and switch to `ident`'s source from the macro definition. + ident.span.remove_mark(); } } - self.cm() - .early_resolve_ident_in_lexical_scope( - orig_ident, - ScopeSet::Late(ns, module, finalize.map(|finalize| finalize.node_id)), - parent_scope, - finalize, - finalize.is_some(), - ignore_binding, - None, - ) - .ok() - .map(LexicalScopeBinding::Item) + + unreachable!() } /// Resolve an identifier in lexical scope. From 7418fa9f8ec4ee9b4b011311eac706ecf321eb54 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 11 Aug 2025 19:10:45 +0300 Subject: [PATCH 10/37] resolve: Add one more test case for "binding is available in a different scope" help --- tests/ui/resolve/issue-104700-inner_scope.rs | 7 +++++++ tests/ui/resolve/issue-104700-inner_scope.stderr | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/ui/resolve/issue-104700-inner_scope.rs b/tests/ui/resolve/issue-104700-inner_scope.rs index e8f28c113e3ad..fb77ab2626b5f 100644 --- a/tests/ui/resolve/issue-104700-inner_scope.rs +++ b/tests/ui/resolve/issue-104700-inner_scope.rs @@ -7,5 +7,12 @@ fn main() { if bar == 2 { //~ ERROR cannot find value println!("yes"); } + { + let baz = 3; + struct S; + } + if baz == 3 { //~ ERROR cannot find value + println!("yes"); + } test_func(1); //~ ERROR cannot find function } diff --git a/tests/ui/resolve/issue-104700-inner_scope.stderr b/tests/ui/resolve/issue-104700-inner_scope.stderr index 051b234fc72d8..b579f8e76d951 100644 --- a/tests/ui/resolve/issue-104700-inner_scope.stderr +++ b/tests/ui/resolve/issue-104700-inner_scope.stderr @@ -10,12 +10,24 @@ help: the binding `bar` is available in a different scope in the same function LL | let bar = 2; | ^^^ +error[E0425]: cannot find value `baz` in this scope + --> $DIR/issue-104700-inner_scope.rs:14:8 + | +LL | if baz == 3 { + | ^^^ + | +help: the binding `baz` is available in a different scope in the same function + --> $DIR/issue-104700-inner_scope.rs:11:13 + | +LL | let baz = 3; + | ^^^ + error[E0425]: cannot find function `test_func` in this scope - --> $DIR/issue-104700-inner_scope.rs:10:5 + --> $DIR/issue-104700-inner_scope.rs:17:5 | LL | test_func(1); | ^^^^^^^^^ not found in this scope -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0425`. From e6dddcb8ec095dc520be16f7c45b91de8c55b10a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 10 Aug 2025 21:13:50 +0200 Subject: [PATCH 11/37] Store bootstrap tracing outputs to a unified directory --- src/bootstrap/src/bin/main.rs | 25 ++++++++++++++++++++----- src/bootstrap/src/lib.rs | 8 ++++---- src/bootstrap/src/utils/exec.rs | 19 ++++--------------- src/bootstrap/src/utils/step_graph.rs | 10 ++++++---- 4 files changed, 34 insertions(+), 28 deletions(-) diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index cf24fedaebb15..70db6fad0f601 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -18,10 +18,14 @@ use bootstrap::{ #[cfg(feature = "tracing")] use tracing::instrument; -fn is_bootstrap_profiling_enabled() -> bool { +fn is_profiling_enabled() -> bool { env::var("BOOTSTRAP_PROFILE").is_ok_and(|v| v == "1") } +fn is_tracing_enabled() -> bool { + is_profiling_enabled() || cfg!(feature = "tracing") +} + #[cfg_attr(feature = "tracing", instrument(level = "trace", name = "main"))] fn main() { #[cfg(feature = "tracing")] @@ -102,6 +106,13 @@ fn main() { let dump_bootstrap_shims = config.dump_bootstrap_shims; let out_dir = config.out.clone(); + let tracing_enabled = is_tracing_enabled(); + let tracing_dir = out_dir.join("bootstrap-trace").join(std::process::id().to_string()); + if tracing_enabled { + let _ = std::fs::remove_dir_all(&tracing_dir); + std::fs::create_dir_all(&tracing_dir).unwrap(); + } + debug!("creating new build based on config"); let mut build = Build::new(config); build.build(); @@ -156,12 +167,16 @@ fn main() { } } - if is_bootstrap_profiling_enabled() { - build.report_summary(start_time); + if is_profiling_enabled() { + build.report_summary(&tracing_dir.join("command-stats.txt"), start_time); } #[cfg(feature = "tracing")] - build.report_step_graph(); + build.report_step_graph(&tracing_dir); + + if tracing_enabled { + eprintln!("Tracing/profiling output has been written to {}", tracing_dir.display()); + } } fn check_version(config: &Config) -> Option { @@ -241,7 +256,7 @@ fn setup_tracing() -> impl Drop { let mut chrome_layer = tracing_chrome::ChromeLayerBuilder::new().include_args(true); // Writes the Chrome profile to trace-.json if enabled - if !is_bootstrap_profiling_enabled() { + if !is_profiling_enabled() { chrome_layer = chrome_layer.writer(io::sink()); } diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 4abf386e5de61..b02c1081a7469 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -2016,13 +2016,13 @@ to download LLVM rather than building it. &self.config.exec_ctx } - pub fn report_summary(&self, start_time: Instant) { - self.config.exec_ctx.profiler().report_summary(start_time); + pub fn report_summary(&self, path: &Path, start_time: Instant) { + self.config.exec_ctx.profiler().report_summary(path, start_time); } #[cfg(feature = "tracing")] - pub fn report_step_graph(self) { - self.step_graph.into_inner().store_to_dot_files(); + pub fn report_step_graph(self, directory: &Path) { + self.step_graph.into_inner().store_to_dot_files(directory); } } diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index 03760faec690b..ff79a1765526e 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -15,7 +15,6 @@ use std::hash::Hash; use std::io::{BufWriter, Write}; use std::panic::Location; use std::path::Path; -use std::process; use std::process::{ Child, ChildStderr, ChildStdout, Command, CommandArgs, CommandEnvs, ExitStatus, Output, Stdio, }; @@ -26,10 +25,10 @@ use build_helper::ci::CiEnv; use build_helper::drop_bomb::DropBomb; use build_helper::exit; -use crate::PathBuf; use crate::core::config::DryRun; #[cfg(feature = "tracing")] use crate::trace_cmd; +use crate::{PathBuf, t}; /// What should be done when the command fails. #[derive(Debug, Copy, Clone)] @@ -121,17 +120,9 @@ impl CommandProfiler { entry.traces.push(ExecutionTrace::CacheHit); } - pub fn report_summary(&self, start_time: Instant) { - let pid = process::id(); - let filename = format!("bootstrap-profile-{pid}.txt"); - - let file = match File::create(&filename) { - Ok(f) => f, - Err(e) => { - eprintln!("Failed to create profiler output file: {e}"); - return; - } - }; + /// Report summary of executed commands file at the specified `path`. + pub fn report_summary(&self, path: &Path, start_time: Instant) { + let file = t!(File::create(path)); let mut writer = BufWriter::new(file); let stats = self.stats.lock().unwrap(); @@ -221,8 +212,6 @@ impl CommandProfiler { writeln!(writer, "Total cache hits: {total_cache_hits}").unwrap(); writeln!(writer, "Estimated time saved due to cache hits: {total_saved_duration:.2?}") .unwrap(); - - println!("Command profiler report saved to {filename}"); } } diff --git a/src/bootstrap/src/utils/step_graph.rs b/src/bootstrap/src/utils/step_graph.rs index c45825a42223a..30a7853b81664 100644 --- a/src/bootstrap/src/utils/step_graph.rs +++ b/src/bootstrap/src/utils/step_graph.rs @@ -1,8 +1,10 @@ use std::collections::{HashMap, HashSet}; use std::fmt::Debug; use std::io::BufWriter; +use std::path::Path; use crate::core::builder::{AnyDebug, Step}; +use crate::t; /// Records the executed steps and their dependencies in a directed graph, /// which can then be rendered into a DOT file for visualization. @@ -80,10 +82,10 @@ impl StepGraph { } } - pub fn store_to_dot_files(self) { + pub fn store_to_dot_files(self, directory: &Path) { for (key, graph) in self.graphs.into_iter() { - let filename = format!("bootstrap-steps{key}.dot"); - graph.render(&filename).unwrap(); + let filename = directory.join(format!("step-graph{key}.dot")); + t!(graph.render(&filename)); } } } @@ -147,7 +149,7 @@ impl DotGraph { self.key_to_index.get(key).copied() } - fn render(&self, path: &str) -> std::io::Result<()> { + fn render(&self, path: &Path) -> std::io::Result<()> { use std::io::Write; let mut file = BufWriter::new(std::fs::File::create(path)?); From c97b6065239b319182465a04c2989002a62d0a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 10 Aug 2025 21:20:27 +0200 Subject: [PATCH 12/37] Store `latest` symlink to the latest tracing output directory --- src/bootstrap/src/bin/main.rs | 15 +++++++++++++-- src/bootstrap/src/lib.rs | 6 ++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 70db6fad0f601..c5ada88462f41 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -13,7 +13,7 @@ use std::{env, process}; use bootstrap::{ Build, CONFIG_CHANGE_HISTORY, ChangeId, Config, Flags, Subcommand, debug, - find_recent_config_change_ids, human_readable_changes, t, + find_recent_config_change_ids, human_readable_changes, symlink_dir, t, }; #[cfg(feature = "tracing")] use tracing::instrument; @@ -107,10 +107,21 @@ fn main() { let out_dir = config.out.clone(); let tracing_enabled = is_tracing_enabled(); + + // Prepare a directory for tracing output + // Also store a symlink named "latest" to point to the latest tracing directory. let tracing_dir = out_dir.join("bootstrap-trace").join(std::process::id().to_string()); + let latest_trace_dir = tracing_dir.parent().unwrap().join("latest"); if tracing_enabled { let _ = std::fs::remove_dir_all(&tracing_dir); std::fs::create_dir_all(&tracing_dir).unwrap(); + + #[cfg(windows)] + let _ = std::fs::remove_dir(&latest_trace_dir); + #[cfg(not(windows))] + let _ = std::fs::remove_file(&latest_trace_dir); + + t!(symlink_dir(&config, &tracing_dir, &latest_trace_dir)); } debug!("creating new build based on config"); @@ -175,7 +186,7 @@ fn main() { build.report_step_graph(&tracing_dir); if tracing_enabled { - eprintln!("Tracing/profiling output has been written to {}", tracing_dir.display()); + eprintln!("Tracing/profiling output has been written to {}", latest_trace_dir.display()); } } diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index b02c1081a7469..7d6c0658f4754 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -37,9 +37,7 @@ use crate::core::builder; use crate::core::builder::Kind; use crate::core::config::{DryRun, LldMode, LlvmLibunwind, TargetSelection, flags}; use crate::utils::exec::{BootstrapCommand, command}; -use crate::utils::helpers::{ - self, dir_is_empty, exe, libdir, set_file_times, split_debuginfo, symlink_dir, -}; +use crate::utils::helpers::{self, dir_is_empty, exe, libdir, set_file_times, split_debuginfo}; mod core; mod utils; @@ -53,7 +51,7 @@ use tracing::{instrument, span}; pub use utils::change_tracker::{ CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes, }; -pub use utils::helpers::PanicTracker; +pub use utils::helpers::{PanicTracker, symlink_dir}; use crate::core::build_steps::vendor::VENDOR_DIR; From f819729539481f6ca302466caf527fa798815850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 10 Aug 2025 21:41:26 +0200 Subject: [PATCH 13/37] Store Chrome step trace into the tracing directory --- src/bootstrap/Cargo.toml | 3 +- src/bootstrap/src/bin/main.rs | 59 +++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 8dc41d1dec697..ecb332eb7e3ad 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -7,7 +7,7 @@ default-run = "bootstrap" [features] build-metrics = ["sysinfo"] -tracing = ["dep:tracing", "dep:tracing-chrome", "dep:tracing-subscriber", "dep:tracing-forest"] +tracing = ["dep:tracing", "dep:tracing-chrome", "dep:tracing-subscriber", "dep:tracing-forest", "dep:tempfile"] [lib] path = "src/lib.rs" @@ -65,6 +65,7 @@ tracing = { version = "0.1", optional = true, features = ["attributes"] } tracing-chrome = { version = "0.7", optional = true } tracing-subscriber = { version = "0.3", optional = true, features = ["env-filter", "fmt", "registry", "std"] } tracing-forest = { version = "0.1.6", optional = true, default-features = false, features = ["smallvec", "ansi", "env-filter"] } +tempfile = { version = "3.15.0", optional = true } [target.'cfg(windows)'.dependencies.junction] version = "1.0.0" diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index c5ada88462f41..a078a519b85cd 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -29,7 +29,7 @@ fn is_tracing_enabled() -> bool { #[cfg_attr(feature = "tracing", instrument(level = "trace", name = "main"))] fn main() { #[cfg(feature = "tracing")] - let _guard = setup_tracing(); + let guard = setup_tracing(is_profiling_enabled()); let start_time = Instant::now(); @@ -183,7 +183,12 @@ fn main() { } #[cfg(feature = "tracing")] - build.report_step_graph(&tracing_dir); + { + build.report_step_graph(&tracing_dir); + if let Some(guard) = guard { + guard.copy_to_dir(&tracing_dir); + } + } if tracing_enabled { eprintln!("Tracing/profiling output has been written to {}", latest_trace_dir.display()); @@ -257,25 +262,53 @@ fn check_version(config: &Config) -> Option { // - `tracing`'s `#[instrument(..)]` macro will need to be gated like `#![cfg_attr(feature = // "tracing", instrument(..))]`. #[cfg(feature = "tracing")] -fn setup_tracing() -> impl Drop { +fn setup_tracing(profiling_enabled: bool) -> Option { + use std::fs::File; + use std::io::BufWriter; + use tracing_forest::ForestLayer; use tracing_subscriber::EnvFilter; use tracing_subscriber::layer::SubscriberExt; let filter = EnvFilter::from_env("BOOTSTRAP_TRACING"); - let mut chrome_layer = tracing_chrome::ChromeLayerBuilder::new().include_args(true); + let registry = tracing_subscriber::registry().with(filter).with(ForestLayer::default()); - // Writes the Chrome profile to trace-.json if enabled - if !is_profiling_enabled() { - chrome_layer = chrome_layer.writer(io::sink()); - } + let guard = if profiling_enabled { + // When we're creating this layer, we do not yet know the location of the tracing output + // directory, because it is stored in the output directory determined after Config is parsed, + // but we already want to make tracing calls during (and before) config parsing. + // So we store the output into a temporary file, and then move it to the tracing directory + // before bootstrap ends. + let tempdir = tempfile::TempDir::new().expect("Cannot create temporary directory"); + let chrome_tracing_path = tempdir.path().join("bootstrap-trace.json"); + let file = BufWriter::new(File::create(&chrome_tracing_path).unwrap()); - let (chrome_layer, _guard) = chrome_layer.build(); + let chrome_layer = + tracing_chrome::ChromeLayerBuilder::new().writer(file).include_args(true); + let (chrome_layer, guard) = chrome_layer.build(); - let registry = - tracing_subscriber::registry().with(filter).with(ForestLayer::default()).with(chrome_layer); + tracing::subscriber::set_global_default(registry.with(chrome_layer)).unwrap(); + Some(TracingGuard { guard, _tempdir: tempdir, chrome_tracing_path }) + } else { + tracing::subscriber::set_global_default(registry).unwrap(); + None + }; - tracing::subscriber::set_global_default(registry).unwrap(); - _guard + guard +} + +#[cfg(feature = "tracing")] +struct TracingGuard { + guard: tracing_chrome::FlushGuard, + _tempdir: tempfile::TempDir, + chrome_tracing_path: std::path::PathBuf, +} + +#[cfg(feature = "tracing")] +impl TracingGuard { + fn copy_to_dir(self, dir: &std::path::Path) { + drop(self.guard); + std::fs::rename(&self.chrome_tracing_path, dir.join("chrome-trace.json")).unwrap(); + } } From aad579537f2cb004f193511659b084f928c1ba51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 10 Aug 2025 21:48:01 +0200 Subject: [PATCH 14/37] Use shorter command span label --- src/bootstrap/src/utils/tracing.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs index 109407bc5f235..4e596e35060f0 100644 --- a/src/bootstrap/src/utils/tracing.rs +++ b/src/bootstrap/src/utils/tracing.rs @@ -55,7 +55,7 @@ macro_rules! trace_cmd { ::tracing::span!( target: "COMMAND", ::tracing::Level::TRACE, - "executing command", + "cmd", cmd = $cmd.fingerprint().format_short_cmd(), full_cmd = ?$cmd ).entered() From 3a115ba69b58e5f3bca037f483ad93bf244c75f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sun, 10 Aug 2025 22:15:44 +0200 Subject: [PATCH 15/37] Print step timings also when the stap starts to execute So that it is easier to see which was the last started step when a failure happens on CI. --- src/bootstrap/src/core/builder/mod.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 163a498d4b48c..fc454ebe81971 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1700,10 +1700,15 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s #[cfg(feature = "build-metrics")] self.metrics.enter_step(&step, self); + if self.config.print_step_timings && !self.config.dry_run() { + println!("[TIMING:start] {}", pretty_print_step(&step)); + } + let (out, dur) = { let start = Instant::now(); let zero = Duration::new(0, 0); let parent = self.time_spent_on_dependencies.replace(zero); + let out = step.clone().run(self); let dur = start.elapsed(); let deps = self.time_spent_on_dependencies.replace(parent + dur); @@ -1711,13 +1716,9 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s }; if self.config.print_step_timings && !self.config.dry_run() { - let step_string = format!("{step:?}"); - let brace_index = step_string.find('{').unwrap_or(0); - let type_string = type_name::(); println!( - "[TIMING] {} {} -- {}.{:03}", - &type_string.strip_prefix("bootstrap::").unwrap_or(type_string), - &step_string[brace_index..], + "[TIMING:end] {} -- {}.{:03}", + pretty_print_step(&step), dur.as_secs(), dur.subsec_millis() ); @@ -1804,6 +1805,17 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s } } +fn pretty_print_step(step: &S) -> String { + let step_dbg_repr = format!("{step:?}"); + let brace_index = step_dbg_repr.find('{').unwrap_or(0); + + // Normalize step type path to only keep the module and the type name + let path = type_name::().rsplit("::").take(2).collect::>(); + let type_string = path.into_iter().rev().collect::>().join("::"); + + format!("{type_string} {}", &step_dbg_repr[brace_index..]) +} + impl<'a> AsRef for Builder<'a> { fn as_ref(&self) -> &ExecutionContext { self.exec_ctx() From c846f7c8bfc202ae86ce667a4c7e10f57ff9c89d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 10:15:53 +0200 Subject: [PATCH 16/37] Replace `tracing_forest` with custom span formatting --- src/bootstrap/Cargo.lock | 188 ++++++++++++++++++++++++++------- src/bootstrap/Cargo.toml | 4 +- src/bootstrap/src/bin/main.rs | 190 +++++++++++++++++++++++++++++++++- 3 files changed, 338 insertions(+), 44 deletions(-) diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index e091c94eb53aa..f5d04c39d3f33 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -12,12 +12,18 @@ dependencies = [ ] [[package]] -name = "ansi_term" -version = "0.12.1" +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "winapi", + "libc", ] [[package]] @@ -26,6 +32,12 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "bitflags" version = "2.6.0" @@ -47,6 +59,7 @@ version = "0.0.0" dependencies = [ "build_helper", "cc", + "chrono", "clap", "clap_complete", "cmake", @@ -71,7 +84,6 @@ dependencies = [ "toml", "tracing", "tracing-chrome", - "tracing-forest", "tracing-subscriber", "walkdir", "windows", @@ -97,6 +109,12 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + [[package]] name = "cc" version = "1.2.23" @@ -112,6 +130,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + [[package]] name = "clap" version = "4.5.20" @@ -180,6 +212,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "cpufeatures" version = "0.2.15" @@ -335,6 +373,30 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "ignore" version = "0.4.23" @@ -368,6 +430,16 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "junction" version = "1.2.0" @@ -458,6 +530,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "objc2-core-foundation" version = "0.3.1" @@ -620,6 +701,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "ryu" version = "1.0.18" @@ -775,26 +862,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "thread_local" version = "1.1.8" @@ -857,19 +924,6 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-forest" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee40835db14ddd1e3ba414292272eddde9dad04d3d4b65509656414d1c42592f" -dependencies = [ - "ansi_term", - "smallvec", - "thiserror", - "tracing", - "tracing-subscriber", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -942,6 +996,64 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index ecb332eb7e3ad..ae5a5d798e57c 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -7,7 +7,7 @@ default-run = "bootstrap" [features] build-metrics = ["sysinfo"] -tracing = ["dep:tracing", "dep:tracing-chrome", "dep:tracing-subscriber", "dep:tracing-forest", "dep:tempfile"] +tracing = ["dep:tracing", "dep:tracing-chrome", "dep:tracing-subscriber", "dep:chrono", "dep:tempfile"] [lib] path = "src/lib.rs" @@ -61,10 +61,10 @@ xz2 = "0.1" sysinfo = { version = "0.36.0", default-features = false, optional = true, features = ["system"] } # Dependencies needed by the `tracing` feature +chrono = { version = "0.4", optional = true } tracing = { version = "0.1", optional = true, features = ["attributes"] } tracing-chrome = { version = "0.7", optional = true } tracing-subscriber = { version = "0.3", optional = true, features = ["env-filter", "fmt", "registry", "std"] } -tracing-forest = { version = "0.1.6", optional = true, default-features = false, features = ["smallvec", "ansi", "env-filter"] } tempfile = { version = "3.15.0", optional = true } [target.'cfg(windows)'.dependencies.junction] diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index a078a519b85cd..a98fc447e89da 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -263,16 +263,198 @@ fn check_version(config: &Config) -> Option { // "tracing", instrument(..))]`. #[cfg(feature = "tracing")] fn setup_tracing(profiling_enabled: bool) -> Option { + use std::fmt::Debug; use std::fs::File; use std::io::BufWriter; + use std::sync::atomic::{AtomicU32, Ordering}; - use tracing_forest::ForestLayer; - use tracing_subscriber::EnvFilter; - use tracing_subscriber::layer::SubscriberExt; + use chrono::{DateTime, Utc}; + use tracing::field::{Field, Visit}; + use tracing::{Event, Id, Level, Subscriber}; + use tracing_subscriber::layer::{Context, SubscriberExt}; + use tracing_subscriber::registry::{LookupSpan, SpanRef}; + use tracing_subscriber::{EnvFilter, Layer}; let filter = EnvFilter::from_env("BOOTSTRAP_TRACING"); - let registry = tracing_subscriber::registry().with(filter).with(ForestLayer::default()); + #[derive(Default)] + struct FieldValues { + message: Option, + fields: Vec<(&'static str, String)>, + } + + impl Visit for FieldValues { + fn record_debug(&mut self, field: &Field, value: &dyn Debug) { + if field.name() == "message" { + self.message = Some(format!("{value:?}")); + } else { + self.fields.push((field.name(), format!("{value:?}"))); + } + } + } + + #[derive(Copy, Clone)] + enum SpanAction { + Enter, + } + + #[derive(Default)] + struct TracingPrinter { + indent: AtomicU32, + span_values: std::sync::Mutex>, + } + + impl TracingPrinter { + fn format_header( + &self, + writer: &mut W, + time: DateTime, + level: &Level, + ) -> std::io::Result<()> { + // Use a fixed-width timestamp without date, that shouldn't be very important + let timestamp = time.format("%H:%M:%S.%3f"); + write!(writer, "{timestamp} ")?; + // Make sure that levels are aligned to the same number of characters, in order not to + // break the layout + write!(writer, "{level:>5} ")?; + write!(writer, "{}", " ".repeat(self.indent.load(Ordering::Relaxed) as usize)) + } + + fn write_event(&self, writer: &mut W, event: &Event<'_>) -> std::io::Result<()> { + let now = Utc::now(); + + self.format_header(writer, now, event.metadata().level())?; + + let mut field_values = FieldValues::default(); + event.record(&mut field_values); + + if let Some(msg) = &field_values.message { + write!(writer, "{msg}")?; + } + + if !field_values.fields.is_empty() { + if field_values.message.is_some() { + write!(writer, " ")?; + } + write!(writer, "[")?; + for (index, (name, value)) in field_values.fields.iter().enumerate() { + write!(writer, "{name} = {value}")?; + if index < field_values.fields.len() - 1 { + write!(writer, ", ")?; + } + } + write!(writer, "]")?; + } + write_location(writer, event.metadata())?; + writeln!(writer)?; + Ok(()) + } + + fn write_span( + &self, + writer: &mut W, + span: SpanRef<'_, S>, + field_values: Option<&FieldValues>, + action: SpanAction, + ) -> std::io::Result<()> + where + S: for<'lookup> LookupSpan<'lookup>, + { + let now = Utc::now(); + + self.format_header(writer, now, span.metadata().level())?; + match action { + SpanAction::Enter => { + write!(writer, "> ")?; + } + } + + write!(writer, "{}", span.name())?; + if let Some(values) = field_values.filter(|v| !v.fields.is_empty()) { + write!(writer, " [")?; + for (index, (name, value)) in values.fields.iter().enumerate() { + write!(writer, "{name} = {value}")?; + if index < values.fields.len() - 1 { + write!(writer, ", ")?; + } + } + write!(writer, "]")?; + } + + write_location(writer, span.metadata())?; + writeln!(writer)?; + Ok(()) + } + } + + fn write_location( + writer: &mut W, + metadata: &'static tracing::Metadata<'static>, + ) -> std::io::Result<()> { + use std::path::{Path, PathBuf}; + + if let Some(filename) = metadata.file() { + // Keep only the module name and file name to make it shorter + let filename: PathBuf = Path::new(filename) + .components() + // Take last two path components + .rev() + .take(2) + .collect::>() + .into_iter() + .rev() + .collect(); + + write!(writer, " ({}", filename.display())?; + if let Some(line) = metadata.line() { + write!(writer, ":{line}")?; + } + write!(writer, ")")?; + } + Ok(()) + } + + impl Layer for TracingPrinter + where + S: Subscriber, + S: for<'lookup> LookupSpan<'lookup>, + { + fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) { + let mut writer = std::io::stderr().lock(); + self.write_event(&mut writer, event).unwrap(); + } + + fn on_new_span( + &self, + attrs: &tracing::span::Attributes<'_>, + id: &Id, + _ctx: Context<'_, S>, + ) { + // Record value of span fields + // Note that we do not implement changing values of span fields after they are created. + // For that we would also need to implement the `on_record` method + + let mut field_values = FieldValues::default(); + attrs.record(&mut field_values); + self.span_values.lock().unwrap().insert(id.clone(), field_values); + } + + fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { + if let Some(span) = ctx.span(id) { + let mut writer = std::io::stderr().lock(); + let values = self.span_values.lock().unwrap(); + let values = values.get(id); + self.write_span(&mut writer, span, values, SpanAction::Enter).unwrap(); + } + self.indent.fetch_add(1, Ordering::Relaxed); + } + + fn on_exit(&self, _id: &Id, _ctx: Context<'_, S>) { + self.indent.fetch_sub(1, Ordering::Relaxed); + } + } + + let registry = tracing_subscriber::registry().with(filter).with(TracingPrinter::default()); let guard = if profiling_enabled { // When we're creating this layer, we do not yet know the location of the tracing output From 1e14229e40aae28390d11f8de8997771614c68dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 16:56:47 +0200 Subject: [PATCH 17/37] Create a span for each executed step --- src/bootstrap/src/bin/main.rs | 97 +++++++++++++++++++++------ src/bootstrap/src/core/builder/mod.rs | 37 ++++++++-- src/bootstrap/src/lib.rs | 2 + 3 files changed, 108 insertions(+), 28 deletions(-) diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index a98fc447e89da..bdf85a50afd37 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -268,6 +268,7 @@ fn setup_tracing(profiling_enabled: bool) -> Option { use std::io::BufWriter; use std::sync::atomic::{AtomicU32, Ordering}; + use bootstrap::STEP_NAME_TARGET; use chrono::{DateTime, Utc}; use tracing::field::{Field, Visit}; use tracing::{Event, Id, Level, Subscriber}; @@ -277,18 +278,40 @@ fn setup_tracing(profiling_enabled: bool) -> Option { let filter = EnvFilter::from_env("BOOTSTRAP_TRACING"); + /// Visitor that extracts both known and unknown field values from events and spans. #[derive(Default)] struct FieldValues { + /// Main event message message: Option, + /// Name of a recorded psna + step_name: Option, + /// The rest of arbitrary event/span fields fields: Vec<(&'static str, String)>, } impl Visit for FieldValues { + /// Record fields if possible using `record_str`, to avoid rendering simple strings with + /// their `Debug` representation, which adds extra quotes. + fn record_str(&mut self, field: &Field, value: &str) { + match field.name() { + "step_name" => { + self.step_name = Some(value.to_string()); + } + name => { + self.fields.push((name, value.to_string())); + } + } + } + fn record_debug(&mut self, field: &Field, value: &dyn Debug) { - if field.name() == "message" { - self.message = Some(format!("{value:?}")); - } else { - self.fields.push((field.name(), format!("{value:?}"))); + let formatted = format!("{value:?}"); + match field.name() { + "message" => { + self.message = Some(formatted); + } + name => { + self.fields.push((name, formatted)); + } } } } @@ -298,6 +321,9 @@ fn setup_tracing(profiling_enabled: bool) -> Option { Enter, } + /// Holds the name of a step, stored in `tracing_subscriber`'s extensions. + struct StepNameExtension(String); + #[derive(Default)] struct TracingPrinter { indent: AtomicU32, @@ -369,17 +395,31 @@ fn setup_tracing(profiling_enabled: bool) -> Option { } } - write!(writer, "{}", span.name())?; - if let Some(values) = field_values.filter(|v| !v.fields.is_empty()) { - write!(writer, " [")?; - for (index, (name, value)) in values.fields.iter().enumerate() { - write!(writer, "{name} = {value}")?; - if index < values.fields.len() - 1 { - write!(writer, ", ")?; + // We handle steps specially. We instrument them dynamically in `Builder::ensure`, + // and we want to have custom name for each step span. But tracing doesn't allow setting + // dynamic span names. So we detect step spans here and override their name. + if span.metadata().target() == STEP_NAME_TARGET { + let name = field_values.and_then(|v| v.step_name.as_deref()).unwrap_or(span.name()); + write!(writer, "{name}")?; + + // There should be only one more field called `args` + if let Some(values) = field_values { + let field = &values.fields[0]; + write!(writer, " {{{}}}", field.1)?; + } + } else { + write!(writer, "{}", span.name())?; + if let Some(values) = field_values.filter(|v| !v.fields.is_empty()) { + write!(writer, " [")?; + for (index, (name, value)) in values.fields.iter().enumerate() { + write!(writer, "{name} = {value}")?; + if index < values.fields.len() - 1 { + write!(writer, ", ")?; + } } + write!(writer, "]")?; } - write!(writer, "]")?; - } + }; write_location(writer, span.metadata())?; writeln!(writer)?; @@ -424,18 +464,18 @@ fn setup_tracing(profiling_enabled: bool) -> Option { self.write_event(&mut writer, event).unwrap(); } - fn on_new_span( - &self, - attrs: &tracing::span::Attributes<'_>, - id: &Id, - _ctx: Context<'_, S>, - ) { + fn on_new_span(&self, attrs: &tracing::span::Attributes<'_>, id: &Id, ctx: Context<'_, S>) { // Record value of span fields // Note that we do not implement changing values of span fields after they are created. // For that we would also need to implement the `on_record` method - let mut field_values = FieldValues::default(); attrs.record(&mut field_values); + + // We need to propagate the actual name of the span to the Chrome layer below, because + // it cannot access field values. We do that through extensions. + if let Some(step_name) = field_values.step_name.clone() { + ctx.span(id).unwrap().extensions_mut().insert(StepNameExtension(step_name)); + } self.span_values.lock().unwrap().insert(id.clone(), field_values); } @@ -466,8 +506,21 @@ fn setup_tracing(profiling_enabled: bool) -> Option { let chrome_tracing_path = tempdir.path().join("bootstrap-trace.json"); let file = BufWriter::new(File::create(&chrome_tracing_path).unwrap()); - let chrome_layer = - tracing_chrome::ChromeLayerBuilder::new().writer(file).include_args(true); + let chrome_layer = tracing_chrome::ChromeLayerBuilder::new() + .writer(file) + .include_args(true) + .name_fn(Box::new(|event_or_span| match event_or_span { + tracing_chrome::EventOrSpan::Event(e) => e.metadata().name().to_string(), + tracing_chrome::EventOrSpan::Span(s) => { + if s.metadata().target() == STEP_NAME_TARGET + && let Some(extension) = s.extensions().get::() + { + extension.0.clone() + } else { + s.metadata().name().to_string() + } + } + })); let (chrome_layer, guard) = chrome_layer.build(); tracing::subscriber::set_global_default(registry.with(chrome_layer)).unwrap(); diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index fc454ebe81971..0eae2f58a68c2 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -947,6 +947,9 @@ impl Step for Libdir { } } +#[cfg(feature = "tracing")] +pub const STEP_NAME_TARGET: &str = "STEP"; + impl<'a> Builder<'a> { fn get_step_descriptions(kind: Kind) -> Vec { macro_rules! describe { @@ -1709,6 +1712,20 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s let zero = Duration::new(0, 0); let parent = self.time_spent_on_dependencies.replace(zero); + #[cfg(feature = "tracing")] + let _span = { + // Keep the target and field names synchronized with `setup_tracing`. + let span = tracing::info_span!( + target: STEP_NAME_TARGET, + // We cannot use a dynamic name here, so instead we record the actual step name + // in the step_name field. + "step", + step_name = step_name::(), + args = step_debug_args(&step) + ); + span.entered() + }; + let out = step.clone().run(self); let dur = start.elapsed(); let deps = self.time_spent_on_dependencies.replace(parent + dur); @@ -1805,15 +1822,23 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s } } -fn pretty_print_step(step: &S) -> String { - let step_dbg_repr = format!("{step:?}"); - let brace_index = step_dbg_repr.find('{').unwrap_or(0); - +/// Return qualified step name, e.g. `compile::Rustc`. +fn step_name() -> String { // Normalize step type path to only keep the module and the type name let path = type_name::().rsplit("::").take(2).collect::>(); - let type_string = path.into_iter().rev().collect::>().join("::"); + path.into_iter().rev().collect::>().join("::") +} + +/// Renders `step` using its `Debug` implementation and extract the field arguments out of it. +fn step_debug_args(step: &S) -> String { + let step_dbg_repr = format!("{step:?}"); + let brace_start = step_dbg_repr.find('{').unwrap_or(0); + let brace_end = step_dbg_repr.rfind('}').unwrap_or(step_dbg_repr.len()); + step_dbg_repr[brace_start + 1..brace_end - 1].trim().to_string() +} - format!("{type_string} {}", &step_dbg_repr[brace_index..]) +fn pretty_print_step(step: &S) -> String { + format!("{} {{ {} }}", step_name::(), step_debug_args(step)) } impl<'a> AsRef for Builder<'a> { diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 7d6c0658f4754..d980c91d8ebdb 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -43,6 +43,8 @@ mod core; mod utils; pub use core::builder::PathSet; +#[cfg(feature = "tracing")] +pub use core::builder::STEP_NAME_TARGET; pub use core::config::flags::{Flags, Subcommand}; pub use core::config::{ChangeId, Config}; From 690c781475acb890f33d928186bdaea9ef179330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 16:57:31 +0200 Subject: [PATCH 18/37] Remove ad-hoc print of executed/cached steps in verbose mode When verbose mode is enabled, it is very hard to see the actually executed steps. --- src/bootstrap/src/core/builder/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 0eae2f58a68c2..4ac040ad0ad63 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1677,8 +1677,6 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s panic!("{}", out); } if let Some(out) = self.cache.get(&step) { - self.verbose_than(1, || println!("{}c {:?}", " ".repeat(stack.len()), step)); - #[cfg(feature = "tracing")] { if let Some(parent) = stack.last() { @@ -1688,7 +1686,6 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s } return out; } - self.verbose_than(1, || println!("{}> {:?}", " ".repeat(stack.len()), step)); #[cfg(feature = "tracing")] { @@ -1749,7 +1746,6 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s let cur_step = stack.pop().expect("step stack empty"); assert_eq!(cur_step.downcast_ref(), Some(&step)); } - self.verbose_than(1, || println!("{}< {:?}", " ".repeat(self.stack.borrow().len()), step)); self.cache.put(step, out.clone()); out } From d4039349d1c116d386e1acb8695bacc482d8688d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 17:00:31 +0200 Subject: [PATCH 19/37] Remove manual `#[instrument]` annotations on steps They could easily get out of sync and miss some fields. Now all steps are instrumented automatically. --- src/bootstrap/src/bin/main.rs | 3 - src/bootstrap/src/core/build_steps/compile.rs | 100 +----------------- src/bootstrap/src/core/build_steps/llvm.rs | 20 ---- src/bootstrap/src/core/build_steps/test.rs | 6 -- src/bootstrap/src/core/build_steps/tool.rs | 33 ------ 5 files changed, 1 insertion(+), 161 deletions(-) diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index bdf85a50afd37..aa062e14202b5 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -15,8 +15,6 @@ use bootstrap::{ Build, CONFIG_CHANGE_HISTORY, ChangeId, Config, Flags, Subcommand, debug, find_recent_config_change_ids, human_readable_changes, symlink_dir, t, }; -#[cfg(feature = "tracing")] -use tracing::instrument; fn is_profiling_enabled() -> bool { env::var("BOOTSTRAP_PROFILE").is_ok_and(|v| v == "1") @@ -26,7 +24,6 @@ fn is_tracing_enabled() -> bool { is_profiling_enabled() || cfg!(feature = "tracing") } -#[cfg_attr(feature = "tracing", instrument(level = "trace", name = "main"))] fn main() { #[cfg(feature = "tracing")] let guard = setup_tracing(is_profiling_enabled()); diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 79174eb281f65..d20b12b256e70 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -16,7 +16,7 @@ use std::{env, fs, str}; use serde_derive::Deserialize; #[cfg(feature = "tracing")] -use tracing::{instrument, span}; +use tracing::span; use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags}; use crate::core::build_steps::tool::{RustcPrivateCompilers, SourceType, copy_lld_artifacts}; @@ -104,7 +104,6 @@ impl Step for Std { run.crate_or_deps("sysroot").path("library") } - #[cfg_attr(feature = "tracing", instrument(level = "trace", name = "Std::make_run", skip_all))] fn make_run(run: RunConfig<'_>) { let crates = std_crates_for_run_make(&run); let builder = run.builder; @@ -135,19 +134,6 @@ impl Step for Std { /// This will build the standard library for a particular stage of the build /// using the `compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "Std::run", - skip_all, - fields( - target = ?self.target, - compiler = ?self.compiler, - force_recompile = self.force_recompile - ), - ), - )] fn run(self, builder: &Builder<'_>) { let target = self.target; @@ -711,19 +697,6 @@ impl Step for StdLink { /// Note that this assumes that `compiler` has already generated the libstd /// libraries for `target`, and this method will find them in the relevant /// output directory. - #[cfg_attr( - feature = "tracing", - instrument( - level = "trace", - name = "StdLink::run", - skip_all, - fields( - compiler = ?self.compiler, - target_compiler = ?self.target_compiler, - target = ?self.target - ), - ), - )] fn run(self, builder: &Builder<'_>) { let compiler = self.compiler; let target_compiler = self.target_compiler; @@ -889,15 +862,6 @@ impl Step for StartupObjects { /// They don't require any library support as they're just plain old object /// files, so we just use the nightly snapshot compiler to always build them (as /// no other compilers are guaranteed to be available). - #[cfg_attr( - feature = "tracing", - instrument( - level = "trace", - name = "StartupObjects::run", - skip_all, - fields(compiler = ?self.compiler, target = ?self.target), - ), - )] fn run(self, builder: &Builder<'_>) -> Vec<(PathBuf, DependencyType)> { let for_compiler = self.compiler; let target = self.target; @@ -1027,15 +991,6 @@ impl Step for Rustc { /// This will build the compiler for a particular stage of the build using /// the `build_compiler` targeting the `target` architecture. The artifacts /// created will also be linked into the sysroot directory. - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "Rustc::run", - skip_all, - fields(previous_compiler = ?self.build_compiler, target = ?self.target), - ), - )] fn run(self, builder: &Builder<'_>) -> u32 { let build_compiler = self.build_compiler; let target = self.target; @@ -1511,19 +1466,6 @@ impl Step for RustcLink { } /// Same as `std_link`, only for librustc - #[cfg_attr( - feature = "tracing", - instrument( - level = "trace", - name = "RustcLink::run", - skip_all, - fields( - compiler = ?self.compiler, - previous_stage_compiler = ?self.previous_stage_compiler, - target = ?self.target, - ), - ), - )] fn run(self, builder: &Builder<'_>) { let compiler = self.compiler; let previous_stage_compiler = self.previous_stage_compiler; @@ -1556,17 +1498,6 @@ impl Step for GccCodegenBackend { }); } - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "GccCodegenBackend::run", - skip_all, - fields( - compilers = ?self.compilers, - ), - ), - )] fn run(self, builder: &Builder<'_>) -> Self::Output { let target = self.compilers.target(); let build_compiler = self.compilers.build_compiler(); @@ -1641,17 +1572,6 @@ impl Step for CraneliftCodegenBackend { }); } - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "CraneliftCodegenBackend::run", - skip_all, - fields( - compilers = ?self.compilers, - ), - ), - )] fn run(self, builder: &Builder<'_>) -> Self::Output { let target = self.compilers.target(); let build_compiler = self.compilers.build_compiler(); @@ -1816,15 +1736,6 @@ impl Step for Sysroot { /// Returns the sysroot that `compiler` is supposed to use. /// For the stage0 compiler, this is stage0-sysroot (because of the initial std build). /// For all other stages, it's the same stage directory that the compiler lives in. - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "Sysroot::run", - skip_all, - fields(compiler = ?self.compiler), - ), - )] fn run(self, builder: &Builder<'_>) -> PathBuf { let compiler = self.compiler; let host_dir = builder.out.join(compiler.host); @@ -2000,15 +1911,6 @@ impl Step for Assemble { /// This will assemble a compiler in `build/$host/stage$stage`. The compiler /// must have been previously produced by the `stage - 1` builder.build /// compiler. - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "Assemble::run", - skip_all, - fields(target_compiler = ?self.target_compiler), - ), - )] fn run(self, builder: &Builder<'_>) -> Compiler { let target_compiler = self.target_compiler; diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 16941a32bb16e..aab2b634c43be 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -15,8 +15,6 @@ use std::sync::OnceLock; use std::{env, fs}; use build_helper::git::PathFreshness; -#[cfg(feature = "tracing")] -use tracing::instrument; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step, StepMetadata}; use crate::core::config::{Config, TargetSelection}; @@ -266,15 +264,6 @@ impl Step for Llvm { } /// Compile LLVM for `target`. - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "Llvm::run", - skip_all, - fields(target = ?self.target), - ), - )] fn run(self, builder: &Builder<'_>) -> LlvmResult { let target = self.target; let target_native = if self.target.starts_with("riscv") { @@ -919,15 +908,6 @@ impl Step for Enzyme { } /// Compile Enzyme for `target`. - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "Enzyme::run", - skip_all, - fields(target = ?self.target), - ), - )] fn run(self, builder: &Builder<'_>) -> PathBuf { builder.require_submodule( "src/tools/enzyme", diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 2f933baa4a4cd..2ea4cab20281f 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -10,8 +10,6 @@ use std::path::{Path, PathBuf}; use std::{env, fs, iter}; use build_helper::exit; -#[cfg(feature = "tracing")] -use tracing::instrument; use crate::core::build_steps::compile::{Std, run_cargo}; use crate::core::build_steps::doc::DocumentationFormat; @@ -760,10 +758,6 @@ impl Step for CompiletestTest { } /// Runs `cargo test` for compiletest. - #[cfg_attr( - feature = "tracing", - instrument(level = "debug", name = "CompiletestTest::run", skip_all) - )] fn run(self, builder: &Builder<'_>) { let host = self.host; diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index aa00cd03c5bb7..a5ebe9a79f9ba 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -13,9 +13,6 @@ use std::ffi::OsStr; use std::path::PathBuf; use std::{env, fs}; -#[cfg(feature = "tracing")] -use tracing::instrument; - use crate::core::build_steps::compile::is_lto_stage; use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, llvm}; @@ -443,14 +440,6 @@ macro_rules! bootstrap_tool { }); } - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = $tool_name, - skip_all, - ), - )] fn run(self, builder: &Builder<'_>) -> ToolBuildResult { $( for submodule in $submodules { @@ -927,15 +916,6 @@ impl Step for LldWrapper { }); } - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "LldWrapper::run", - skip_all, - fields(build_compiler = ?self.build_compiler), - ), - )] fn run(self, builder: &Builder<'_>) -> Self::Output { let lld_dir = builder.ensure(llvm::Lld { target: self.target }); let tool = builder.ensure(ToolBuild { @@ -1028,15 +1008,6 @@ impl Step for WasmComponentLd { }); } - #[cfg_attr( - feature = "tracing", - instrument( - level = "debug", - name = "WasmComponentLd::run", - skip_all, - fields(build_compiler = ?self.build_compiler), - ), - )] fn run(self, builder: &Builder<'_>) -> ToolBuildResult { builder.ensure(ToolBuild { build_compiler: self.build_compiler, @@ -1233,10 +1204,6 @@ impl Step for LlvmBitcodeLinker { }); } - #[cfg_attr( - feature = "tracing", - instrument(level = "debug", name = "LlvmBitcodeLinker::run", skip_all) - )] fn run(self, builder: &Builder<'_>) -> ToolBuildResult { builder.ensure(ToolBuild { build_compiler: self.build_compiler, From 604c180bf583822d34a25c935779bbea12c5003e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 17:18:21 +0200 Subject: [PATCH 20/37] Move tracing setup to the `tracing` module --- src/bootstrap/src/bin/main.rs | 299 +--------------------------- src/bootstrap/src/lib.rs | 2 + src/bootstrap/src/utils/tracing.rs | 301 +++++++++++++++++++++++++++++ 3 files changed, 304 insertions(+), 298 deletions(-) diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index aa062e14202b5..0dc3cad0cd8f5 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -26,7 +26,7 @@ fn is_tracing_enabled() -> bool { fn main() { #[cfg(feature = "tracing")] - let guard = setup_tracing(is_profiling_enabled()); + let guard = bootstrap::setup_tracing(is_profiling_enabled()); let start_time = Instant::now(); @@ -247,300 +247,3 @@ fn check_version(config: &Config) -> Option { Some(msg) } - -// # Note on `tracing` usage in bootstrap -// -// Due to the conditional compilation via the `tracing` cargo feature, this means that `tracing` -// usages in bootstrap need to be also gated behind the `tracing` feature: -// -// - `tracing` macros with log levels (`trace!`, `debug!`, `warn!`, `info`, `error`) should not be -// used *directly*. You should use the wrapped `tracing` macros which gate the actual invocations -// behind `feature = "tracing"`. -// - `tracing`'s `#[instrument(..)]` macro will need to be gated like `#![cfg_attr(feature = -// "tracing", instrument(..))]`. -#[cfg(feature = "tracing")] -fn setup_tracing(profiling_enabled: bool) -> Option { - use std::fmt::Debug; - use std::fs::File; - use std::io::BufWriter; - use std::sync::atomic::{AtomicU32, Ordering}; - - use bootstrap::STEP_NAME_TARGET; - use chrono::{DateTime, Utc}; - use tracing::field::{Field, Visit}; - use tracing::{Event, Id, Level, Subscriber}; - use tracing_subscriber::layer::{Context, SubscriberExt}; - use tracing_subscriber::registry::{LookupSpan, SpanRef}; - use tracing_subscriber::{EnvFilter, Layer}; - - let filter = EnvFilter::from_env("BOOTSTRAP_TRACING"); - - /// Visitor that extracts both known and unknown field values from events and spans. - #[derive(Default)] - struct FieldValues { - /// Main event message - message: Option, - /// Name of a recorded psna - step_name: Option, - /// The rest of arbitrary event/span fields - fields: Vec<(&'static str, String)>, - } - - impl Visit for FieldValues { - /// Record fields if possible using `record_str`, to avoid rendering simple strings with - /// their `Debug` representation, which adds extra quotes. - fn record_str(&mut self, field: &Field, value: &str) { - match field.name() { - "step_name" => { - self.step_name = Some(value.to_string()); - } - name => { - self.fields.push((name, value.to_string())); - } - } - } - - fn record_debug(&mut self, field: &Field, value: &dyn Debug) { - let formatted = format!("{value:?}"); - match field.name() { - "message" => { - self.message = Some(formatted); - } - name => { - self.fields.push((name, formatted)); - } - } - } - } - - #[derive(Copy, Clone)] - enum SpanAction { - Enter, - } - - /// Holds the name of a step, stored in `tracing_subscriber`'s extensions. - struct StepNameExtension(String); - - #[derive(Default)] - struct TracingPrinter { - indent: AtomicU32, - span_values: std::sync::Mutex>, - } - - impl TracingPrinter { - fn format_header( - &self, - writer: &mut W, - time: DateTime, - level: &Level, - ) -> std::io::Result<()> { - // Use a fixed-width timestamp without date, that shouldn't be very important - let timestamp = time.format("%H:%M:%S.%3f"); - write!(writer, "{timestamp} ")?; - // Make sure that levels are aligned to the same number of characters, in order not to - // break the layout - write!(writer, "{level:>5} ")?; - write!(writer, "{}", " ".repeat(self.indent.load(Ordering::Relaxed) as usize)) - } - - fn write_event(&self, writer: &mut W, event: &Event<'_>) -> std::io::Result<()> { - let now = Utc::now(); - - self.format_header(writer, now, event.metadata().level())?; - - let mut field_values = FieldValues::default(); - event.record(&mut field_values); - - if let Some(msg) = &field_values.message { - write!(writer, "{msg}")?; - } - - if !field_values.fields.is_empty() { - if field_values.message.is_some() { - write!(writer, " ")?; - } - write!(writer, "[")?; - for (index, (name, value)) in field_values.fields.iter().enumerate() { - write!(writer, "{name} = {value}")?; - if index < field_values.fields.len() - 1 { - write!(writer, ", ")?; - } - } - write!(writer, "]")?; - } - write_location(writer, event.metadata())?; - writeln!(writer)?; - Ok(()) - } - - fn write_span( - &self, - writer: &mut W, - span: SpanRef<'_, S>, - field_values: Option<&FieldValues>, - action: SpanAction, - ) -> std::io::Result<()> - where - S: for<'lookup> LookupSpan<'lookup>, - { - let now = Utc::now(); - - self.format_header(writer, now, span.metadata().level())?; - match action { - SpanAction::Enter => { - write!(writer, "> ")?; - } - } - - // We handle steps specially. We instrument them dynamically in `Builder::ensure`, - // and we want to have custom name for each step span. But tracing doesn't allow setting - // dynamic span names. So we detect step spans here and override their name. - if span.metadata().target() == STEP_NAME_TARGET { - let name = field_values.and_then(|v| v.step_name.as_deref()).unwrap_or(span.name()); - write!(writer, "{name}")?; - - // There should be only one more field called `args` - if let Some(values) = field_values { - let field = &values.fields[0]; - write!(writer, " {{{}}}", field.1)?; - } - } else { - write!(writer, "{}", span.name())?; - if let Some(values) = field_values.filter(|v| !v.fields.is_empty()) { - write!(writer, " [")?; - for (index, (name, value)) in values.fields.iter().enumerate() { - write!(writer, "{name} = {value}")?; - if index < values.fields.len() - 1 { - write!(writer, ", ")?; - } - } - write!(writer, "]")?; - } - }; - - write_location(writer, span.metadata())?; - writeln!(writer)?; - Ok(()) - } - } - - fn write_location( - writer: &mut W, - metadata: &'static tracing::Metadata<'static>, - ) -> std::io::Result<()> { - use std::path::{Path, PathBuf}; - - if let Some(filename) = metadata.file() { - // Keep only the module name and file name to make it shorter - let filename: PathBuf = Path::new(filename) - .components() - // Take last two path components - .rev() - .take(2) - .collect::>() - .into_iter() - .rev() - .collect(); - - write!(writer, " ({}", filename.display())?; - if let Some(line) = metadata.line() { - write!(writer, ":{line}")?; - } - write!(writer, ")")?; - } - Ok(()) - } - - impl Layer for TracingPrinter - where - S: Subscriber, - S: for<'lookup> LookupSpan<'lookup>, - { - fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) { - let mut writer = std::io::stderr().lock(); - self.write_event(&mut writer, event).unwrap(); - } - - fn on_new_span(&self, attrs: &tracing::span::Attributes<'_>, id: &Id, ctx: Context<'_, S>) { - // Record value of span fields - // Note that we do not implement changing values of span fields after they are created. - // For that we would also need to implement the `on_record` method - let mut field_values = FieldValues::default(); - attrs.record(&mut field_values); - - // We need to propagate the actual name of the span to the Chrome layer below, because - // it cannot access field values. We do that through extensions. - if let Some(step_name) = field_values.step_name.clone() { - ctx.span(id).unwrap().extensions_mut().insert(StepNameExtension(step_name)); - } - self.span_values.lock().unwrap().insert(id.clone(), field_values); - } - - fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { - if let Some(span) = ctx.span(id) { - let mut writer = std::io::stderr().lock(); - let values = self.span_values.lock().unwrap(); - let values = values.get(id); - self.write_span(&mut writer, span, values, SpanAction::Enter).unwrap(); - } - self.indent.fetch_add(1, Ordering::Relaxed); - } - - fn on_exit(&self, _id: &Id, _ctx: Context<'_, S>) { - self.indent.fetch_sub(1, Ordering::Relaxed); - } - } - - let registry = tracing_subscriber::registry().with(filter).with(TracingPrinter::default()); - - let guard = if profiling_enabled { - // When we're creating this layer, we do not yet know the location of the tracing output - // directory, because it is stored in the output directory determined after Config is parsed, - // but we already want to make tracing calls during (and before) config parsing. - // So we store the output into a temporary file, and then move it to the tracing directory - // before bootstrap ends. - let tempdir = tempfile::TempDir::new().expect("Cannot create temporary directory"); - let chrome_tracing_path = tempdir.path().join("bootstrap-trace.json"); - let file = BufWriter::new(File::create(&chrome_tracing_path).unwrap()); - - let chrome_layer = tracing_chrome::ChromeLayerBuilder::new() - .writer(file) - .include_args(true) - .name_fn(Box::new(|event_or_span| match event_or_span { - tracing_chrome::EventOrSpan::Event(e) => e.metadata().name().to_string(), - tracing_chrome::EventOrSpan::Span(s) => { - if s.metadata().target() == STEP_NAME_TARGET - && let Some(extension) = s.extensions().get::() - { - extension.0.clone() - } else { - s.metadata().name().to_string() - } - } - })); - let (chrome_layer, guard) = chrome_layer.build(); - - tracing::subscriber::set_global_default(registry.with(chrome_layer)).unwrap(); - Some(TracingGuard { guard, _tempdir: tempdir, chrome_tracing_path }) - } else { - tracing::subscriber::set_global_default(registry).unwrap(); - None - }; - - guard -} - -#[cfg(feature = "tracing")] -struct TracingGuard { - guard: tracing_chrome::FlushGuard, - _tempdir: tempfile::TempDir, - chrome_tracing_path: std::path::PathBuf, -} - -#[cfg(feature = "tracing")] -impl TracingGuard { - fn copy_to_dir(self, dir: &std::path::Path) { - drop(self.guard); - std::fs::rename(&self.chrome_tracing_path, dir.join("chrome-trace.json")).unwrap(); - } -} diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index d980c91d8ebdb..ba3d3fb46e2ee 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -54,6 +54,8 @@ pub use utils::change_tracker::{ CONFIG_CHANGE_HISTORY, find_recent_config_change_ids, human_readable_changes, }; pub use utils::helpers::{PanicTracker, symlink_dir}; +#[cfg(feature = "tracing")] +pub use utils::tracing::setup_tracing; use crate::core::build_steps::vendor::VENDOR_DIR; diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs index 4e596e35060f0..68063131d157a 100644 --- a/src/bootstrap/src/utils/tracing.rs +++ b/src/bootstrap/src/utils/tracing.rs @@ -62,3 +62,304 @@ macro_rules! trace_cmd { } }; } + +// # Note on `tracing` usage in bootstrap +// +// Due to the conditional compilation via the `tracing` cargo feature, this means that `tracing` +// usages in bootstrap need to be also gated behind the `tracing` feature: +// +// - `tracing` macros with log levels (`trace!`, `debug!`, `warn!`, `info`, `error`) should not be +// used *directly*. You should use the wrapped `tracing` macros which gate the actual invocations +// behind `feature = "tracing"`. +// - `tracing`'s `#[instrument(..)]` macro will need to be gated like `#![cfg_attr(feature = +// "tracing", instrument(..))]`. +#[cfg(feature = "tracing")] +mod inner { + use std::fmt::Debug; + use std::fs::File; + use std::io::Write; + use std::sync::atomic::Ordering; + + use chrono::{DateTime, Utc}; + use tracing::field::{Field, Visit}; + use tracing::{Event, Id, Level, Subscriber}; + use tracing_subscriber::layer::{Context, SubscriberExt}; + use tracing_subscriber::registry::{LookupSpan, SpanRef}; + use tracing_subscriber::{EnvFilter, Layer}; + + use crate::STEP_NAME_TARGET; + + pub fn setup_tracing(profiling_enabled: bool) -> Option { + let filter = EnvFilter::from_env("BOOTSTRAP_TRACING"); + + let registry = tracing_subscriber::registry().with(filter).with(TracingPrinter::default()); + + let guard = if profiling_enabled { + // When we're creating this layer, we do not yet know the location of the tracing output + // directory, because it is stored in the output directory determined after Config is parsed, + // but we already want to make tracing calls during (and before) config parsing. + // So we store the output into a temporary file, and then move it to the tracing directory + // before bootstrap ends. + let tempdir = tempfile::TempDir::new().expect("Cannot create temporary directory"); + let chrome_tracing_path = tempdir.path().join("bootstrap-trace.json"); + let file = std::io::BufWriter::new(File::create(&chrome_tracing_path).unwrap()); + + let chrome_layer = tracing_chrome::ChromeLayerBuilder::new() + .writer(file) + .include_args(true) + .name_fn(Box::new(|event_or_span| match event_or_span { + tracing_chrome::EventOrSpan::Event(e) => e.metadata().name().to_string(), + tracing_chrome::EventOrSpan::Span(s) => { + if s.metadata().target() == STEP_NAME_TARGET + && let Some(extension) = s.extensions().get::() + { + extension.0.clone() + } else { + s.metadata().name().to_string() + } + } + })); + let (chrome_layer, guard) = chrome_layer.build(); + + tracing::subscriber::set_global_default(registry.with(chrome_layer)).unwrap(); + Some(TracingGuard { guard, _tempdir: tempdir, chrome_tracing_path }) + } else { + tracing::subscriber::set_global_default(registry).unwrap(); + None + }; + + guard + } + + pub struct TracingGuard { + guard: tracing_chrome::FlushGuard, + _tempdir: tempfile::TempDir, + chrome_tracing_path: std::path::PathBuf, + } + + impl TracingGuard { + pub fn copy_to_dir(self, dir: &std::path::Path) { + drop(self.guard); + std::fs::rename(&self.chrome_tracing_path, dir.join("chrome-trace.json")).unwrap(); + } + } + + /// Visitor that extracts both known and unknown field values from events and spans. + #[derive(Default)] + struct FieldValues { + /// Main event message + message: Option, + /// Name of a recorded psna + step_name: Option, + /// The rest of arbitrary event/span fields + fields: Vec<(&'static str, String)>, + } + + impl Visit for FieldValues { + /// Record fields if possible using `record_str`, to avoid rendering simple strings with + /// their `Debug` representation, which adds extra quotes. + fn record_str(&mut self, field: &Field, value: &str) { + match field.name() { + "step_name" => { + self.step_name = Some(value.to_string()); + } + name => { + self.fields.push((name, value.to_string())); + } + } + } + + fn record_debug(&mut self, field: &Field, value: &dyn Debug) { + let formatted = format!("{value:?}"); + match field.name() { + "message" => { + self.message = Some(formatted); + } + name => { + self.fields.push((name, formatted)); + } + } + } + } + + #[derive(Copy, Clone)] + enum SpanAction { + Enter, + } + + /// Holds the name of a step, stored in `tracing_subscriber`'s extensions. + struct StepNameExtension(String); + + #[derive(Default)] + struct TracingPrinter { + indent: std::sync::atomic::AtomicU32, + span_values: std::sync::Mutex>, + } + + impl TracingPrinter { + fn format_header( + &self, + writer: &mut W, + time: DateTime, + level: &Level, + ) -> std::io::Result<()> { + // Use a fixed-width timestamp without date, that shouldn't be very important + let timestamp = time.format("%H:%M:%S.%3f"); + write!(writer, "{timestamp} ")?; + // Make sure that levels are aligned to the same number of characters, in order not to + // break the layout + write!(writer, "{level:>5} ")?; + write!(writer, "{}", " ".repeat(self.indent.load(Ordering::Relaxed) as usize)) + } + + fn write_event(&self, writer: &mut W, event: &Event<'_>) -> std::io::Result<()> { + let now = Utc::now(); + + self.format_header(writer, now, event.metadata().level())?; + + let mut field_values = FieldValues::default(); + event.record(&mut field_values); + + if let Some(msg) = &field_values.message { + write!(writer, "{msg}")?; + } + + if !field_values.fields.is_empty() { + if field_values.message.is_some() { + write!(writer, " ")?; + } + write!(writer, "[")?; + for (index, (name, value)) in field_values.fields.iter().enumerate() { + write!(writer, "{name} = {value}")?; + if index < field_values.fields.len() - 1 { + write!(writer, ", ")?; + } + } + write!(writer, "]")?; + } + write_location(writer, event.metadata())?; + writeln!(writer)?; + Ok(()) + } + + fn write_span( + &self, + writer: &mut W, + span: SpanRef<'_, S>, + field_values: Option<&FieldValues>, + action: SpanAction, + ) -> std::io::Result<()> + where + S: for<'lookup> LookupSpan<'lookup>, + { + let now = Utc::now(); + + self.format_header(writer, now, span.metadata().level())?; + match action { + SpanAction::Enter => { + write!(writer, "> ")?; + } + } + + // We handle steps specially. We instrument them dynamically in `Builder::ensure`, + // and we want to have custom name for each step span. But tracing doesn't allow setting + // dynamic span names. So we detect step spans here and override their name. + if span.metadata().target() == STEP_NAME_TARGET { + let name = field_values.and_then(|v| v.step_name.as_deref()).unwrap_or(span.name()); + write!(writer, "{name}")?; + + // There should be only one more field called `args` + if let Some(values) = field_values { + let field = &values.fields[0]; + write!(writer, " {{{}}}", field.1)?; + } + } else { + write!(writer, "{}", span.name())?; + if let Some(values) = field_values.filter(|v| !v.fields.is_empty()) { + write!(writer, " [")?; + for (index, (name, value)) in values.fields.iter().enumerate() { + write!(writer, "{name} = {value}")?; + if index < values.fields.len() - 1 { + write!(writer, ", ")?; + } + } + write!(writer, "]")?; + } + }; + + write_location(writer, span.metadata())?; + writeln!(writer)?; + Ok(()) + } + } + + fn write_location( + writer: &mut W, + metadata: &'static tracing::Metadata<'static>, + ) -> std::io::Result<()> { + use std::path::{Path, PathBuf}; + + if let Some(filename) = metadata.file() { + // Keep only the module name and file name to make it shorter + let filename: PathBuf = Path::new(filename) + .components() + // Take last two path components + .rev() + .take(2) + .collect::>() + .into_iter() + .rev() + .collect(); + + write!(writer, " ({}", filename.display())?; + if let Some(line) = metadata.line() { + write!(writer, ":{line}")?; + } + write!(writer, ")")?; + } + Ok(()) + } + + impl Layer for TracingPrinter + where + S: Subscriber, + S: for<'lookup> LookupSpan<'lookup>, + { + fn on_new_span(&self, attrs: &tracing::span::Attributes<'_>, id: &Id, ctx: Context<'_, S>) { + // Record value of span fields + // Note that we do not implement changing values of span fields after they are created. + // For that we would also need to implement the `on_record` method + let mut field_values = FieldValues::default(); + attrs.record(&mut field_values); + + // We need to propagate the actual name of the span to the Chrome layer below, because + // it cannot access field values. We do that through extensions. + if let Some(step_name) = field_values.step_name.clone() { + ctx.span(id).unwrap().extensions_mut().insert(StepNameExtension(step_name)); + } + self.span_values.lock().unwrap().insert(id.clone(), field_values); + } + + fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) { + let mut writer = std::io::stderr().lock(); + self.write_event(&mut writer, event).unwrap(); + } + + fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { + if let Some(span) = ctx.span(id) { + let mut writer = std::io::stderr().lock(); + let values = self.span_values.lock().unwrap(); + let values = values.get(id); + self.write_span(&mut writer, span, values, SpanAction::Enter).unwrap(); + } + self.indent.fetch_add(1, Ordering::Relaxed); + } + + fn on_exit(&self, _id: &Id, _ctx: Context<'_, S>) { + self.indent.fetch_sub(1, Ordering::Relaxed); + } + } +} + +#[cfg(feature = "tracing")] +pub use inner::setup_tracing; From c3682b24d792d5348f2c95017a007a619934b521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 17:30:41 +0200 Subject: [PATCH 21/37] Correctly show executed command name in Chrome trace --- src/bootstrap/src/core/build_steps/compile.rs | 2 +- src/bootstrap/src/core/builder/mod.rs | 4 +- src/bootstrap/src/lib.rs | 2 +- src/bootstrap/src/utils/exec.rs | 16 ++++-- src/bootstrap/src/utils/tracing.rs | 57 +++++++++++++------ 5 files changed, 54 insertions(+), 27 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index d20b12b256e70..d1c8b97d98067 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2469,7 +2469,7 @@ pub fn stream_cargo( let mut cmd = cargo.into_cmd(); #[cfg(feature = "tracing")] - let _run_span = crate::trace_cmd!(cmd); + let _run_span = crate::utils::tracing::trace_cmd(&cmd); // Instruct Cargo to give us json messages on stdout, critically leaving // stderr as piped so we can get those pretty colors. diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 4ac040ad0ad63..9dab16f5a42a1 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -948,7 +948,7 @@ impl Step for Libdir { } #[cfg(feature = "tracing")] -pub const STEP_NAME_TARGET: &str = "STEP"; +pub const STEP_SPAN_TARGET: &str = "STEP"; impl<'a> Builder<'a> { fn get_step_descriptions(kind: Kind) -> Vec { @@ -1713,7 +1713,7 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s let _span = { // Keep the target and field names synchronized with `setup_tracing`. let span = tracing::info_span!( - target: STEP_NAME_TARGET, + target: STEP_SPAN_TARGET, // We cannot use a dynamic name here, so instead we record the actual step name // in the step_name field. "step", diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index ba3d3fb46e2ee..6d168693e8f90 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -44,7 +44,7 @@ mod utils; pub use core::builder::PathSet; #[cfg(feature = "tracing")] -pub use core::builder::STEP_NAME_TARGET; +pub use core::builder::STEP_SPAN_TARGET; pub use core::config::flags::{Flags, Subcommand}; pub use core::config::{ChangeId, Config}; diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index ff79a1765526e..ac75e27d57c85 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -26,8 +26,6 @@ use build_helper::drop_bomb::DropBomb; use build_helper::exit; use crate::core::config::DryRun; -#[cfg(feature = "tracing")] -use crate::trace_cmd; use crate::{PathBuf, t}; /// What should be done when the command fails. @@ -76,9 +74,17 @@ pub struct CommandFingerprint { } impl CommandFingerprint { + #[cfg(feature = "tracing")] + pub(crate) fn program_name(&self) -> String { + Path::new(&self.program) + .file_name() + .map(|p| p.to_string_lossy().to_string()) + .unwrap_or_else(|| "".to_string()) + } + /// Helper method to format both Command and BootstrapCommand as a short execution line, /// without all the other details (e.g. environment variables). - pub fn format_short_cmd(&self) -> String { + pub(crate) fn format_short_cmd(&self) -> String { use std::fmt::Write; let mut cmd = self.program.to_string_lossy().to_string(); @@ -676,7 +682,7 @@ impl ExecutionContext { let fingerprint = command.fingerprint(); #[cfg(feature = "tracing")] - let span_guard = trace_cmd!(command); + let span_guard = crate::utils::tracing::trace_cmd(command); if let Some(cached_output) = self.command_cache.get(&fingerprint) { command.mark_as_executed(); @@ -768,7 +774,7 @@ impl ExecutionContext { } #[cfg(feature = "tracing")] - let span_guard = trace_cmd!(command); + let span_guard = crate::utils::tracing::trace_cmd(command); let start_time = Instant::now(); let fingerprint = command.fingerprint(); diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs index 68063131d157a..18d19738de96f 100644 --- a/src/bootstrap/src/utils/tracing.rs +++ b/src/bootstrap/src/utils/tracing.rs @@ -48,19 +48,21 @@ macro_rules! error { } } -#[macro_export] -macro_rules! trace_cmd { - ($cmd:expr) => { - { - ::tracing::span!( - target: "COMMAND", - ::tracing::Level::TRACE, - "cmd", - cmd = $cmd.fingerprint().format_short_cmd(), - full_cmd = ?$cmd - ).entered() - } - }; +#[cfg(feature = "tracing")] +const COMMAND_SPAN_TARGET: &str = "COMMAND"; + +#[cfg(feature = "tracing")] +pub fn trace_cmd(command: &crate::BootstrapCommand) -> tracing::span::EnteredSpan { + let fingerprint = command.fingerprint(); + tracing::span!( + target: COMMAND_SPAN_TARGET, + tracing::Level::TRACE, + "cmd", + cmd_name = fingerprint.program_name().to_string(), + cmd = fingerprint.format_short_cmd(), + full_cmd = ?command + ) + .entered() } // # Note on `tracing` usage in bootstrap @@ -87,7 +89,8 @@ mod inner { use tracing_subscriber::registry::{LookupSpan, SpanRef}; use tracing_subscriber::{EnvFilter, Layer}; - use crate::STEP_NAME_TARGET; + use crate::STEP_SPAN_TARGET; + use crate::utils::tracing::COMMAND_SPAN_TARGET; pub fn setup_tracing(profiling_enabled: bool) -> Option { let filter = EnvFilter::from_env("BOOTSTRAP_TRACING"); @@ -110,10 +113,14 @@ mod inner { .name_fn(Box::new(|event_or_span| match event_or_span { tracing_chrome::EventOrSpan::Event(e) => e.metadata().name().to_string(), tracing_chrome::EventOrSpan::Span(s) => { - if s.metadata().target() == STEP_NAME_TARGET + if s.metadata().target() == STEP_SPAN_TARGET && let Some(extension) = s.extensions().get::() { extension.0.clone() + } else if s.metadata().target() == COMMAND_SPAN_TARGET + && let Some(extension) = s.extensions().get::() + { + extension.0.clone() } else { s.metadata().name().to_string() } @@ -151,6 +158,8 @@ mod inner { message: Option, /// Name of a recorded psna step_name: Option, + /// Short name of an executed command + cmd_name: Option, /// The rest of arbitrary event/span fields fields: Vec<(&'static str, String)>, } @@ -163,6 +172,9 @@ mod inner { "step_name" => { self.step_name = Some(value.to_string()); } + "cmd_name" => { + self.cmd_name = Some(value.to_string()); + } name => { self.fields.push((name, value.to_string())); } @@ -187,9 +199,12 @@ mod inner { Enter, } - /// Holds the name of a step, stored in `tracing_subscriber`'s extensions. + /// Holds the name of a step span, stored in `tracing_subscriber`'s extensions. struct StepNameExtension(String); + /// Holds the name of a command span, stored in `tracing_subscriber`'s extensions. + struct CommandNameExtension(String); + #[derive(Default)] struct TracingPrinter { indent: std::sync::atomic::AtomicU32, @@ -264,7 +279,7 @@ mod inner { // We handle steps specially. We instrument them dynamically in `Builder::ensure`, // and we want to have custom name for each step span. But tracing doesn't allow setting // dynamic span names. So we detect step spans here and override their name. - if span.metadata().target() == STEP_NAME_TARGET { + if span.metadata().target() == STEP_SPAN_TARGET { let name = field_values.and_then(|v| v.step_name.as_deref()).unwrap_or(span.name()); write!(writer, "{name}")?; @@ -334,8 +349,14 @@ mod inner { // We need to propagate the actual name of the span to the Chrome layer below, because // it cannot access field values. We do that through extensions. - if let Some(step_name) = field_values.step_name.clone() { + if attrs.metadata().target() == STEP_SPAN_TARGET + && let Some(step_name) = field_values.step_name.clone() + { ctx.span(id).unwrap().extensions_mut().insert(StepNameExtension(step_name)); + } else if attrs.metadata().target() == COMMAND_SPAN_TARGET + && let Some(cmd_name) = field_values.cmd_name.clone() + { + ctx.span(id).unwrap().extensions_mut().insert(CommandNameExtension(cmd_name)); } self.span_values.lock().unwrap().insert(id.clone(), field_values); } From b4a357fbaaac1f79cb6648e8a0a519e96f4cd0fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 17:33:47 +0200 Subject: [PATCH 22/37] Always profile commands and generate Chrome profile when tracing is enabled --- src/bootstrap/src/bin/main.rs | 22 +++------ src/bootstrap/src/utils/tracing.rs | 71 ++++++++++++++---------------- 2 files changed, 37 insertions(+), 56 deletions(-) diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 0dc3cad0cd8f5..95bd242baf7d8 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -16,19 +16,15 @@ use bootstrap::{ find_recent_config_change_ids, human_readable_changes, symlink_dir, t, }; -fn is_profiling_enabled() -> bool { - env::var("BOOTSTRAP_PROFILE").is_ok_and(|v| v == "1") -} - fn is_tracing_enabled() -> bool { - is_profiling_enabled() || cfg!(feature = "tracing") + cfg!(feature = "tracing") } fn main() { #[cfg(feature = "tracing")] - let guard = bootstrap::setup_tracing(is_profiling_enabled()); + let guard = bootstrap::setup_tracing("BOOTSTRAP_TRACING"); - let start_time = Instant::now(); + let _start_time = Instant::now(); let args = env::args().skip(1).collect::>(); @@ -175,19 +171,11 @@ fn main() { } } - if is_profiling_enabled() { - build.report_summary(&tracing_dir.join("command-stats.txt"), start_time); - } - #[cfg(feature = "tracing")] { + build.report_summary(&tracing_dir.join("command-stats.txt"), _start_time); build.report_step_graph(&tracing_dir); - if let Some(guard) = guard { - guard.copy_to_dir(&tracing_dir); - } - } - - if tracing_enabled { + guard.copy_to_dir(&tracing_dir); eprintln!("Tracing/profiling output has been written to {}", latest_trace_dir.display()); } } diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs index 18d19738de96f..8b9d6ca213c55 100644 --- a/src/bootstrap/src/utils/tracing.rs +++ b/src/bootstrap/src/utils/tracing.rs @@ -92,50 +92,43 @@ mod inner { use crate::STEP_SPAN_TARGET; use crate::utils::tracing::COMMAND_SPAN_TARGET; - pub fn setup_tracing(profiling_enabled: bool) -> Option { - let filter = EnvFilter::from_env("BOOTSTRAP_TRACING"); + pub fn setup_tracing(env_name: &str) -> TracingGuard { + let filter = EnvFilter::from_env(env_name); let registry = tracing_subscriber::registry().with(filter).with(TracingPrinter::default()); - let guard = if profiling_enabled { - // When we're creating this layer, we do not yet know the location of the tracing output - // directory, because it is stored in the output directory determined after Config is parsed, - // but we already want to make tracing calls during (and before) config parsing. - // So we store the output into a temporary file, and then move it to the tracing directory - // before bootstrap ends. - let tempdir = tempfile::TempDir::new().expect("Cannot create temporary directory"); - let chrome_tracing_path = tempdir.path().join("bootstrap-trace.json"); - let file = std::io::BufWriter::new(File::create(&chrome_tracing_path).unwrap()); - - let chrome_layer = tracing_chrome::ChromeLayerBuilder::new() - .writer(file) - .include_args(true) - .name_fn(Box::new(|event_or_span| match event_or_span { - tracing_chrome::EventOrSpan::Event(e) => e.metadata().name().to_string(), - tracing_chrome::EventOrSpan::Span(s) => { - if s.metadata().target() == STEP_SPAN_TARGET - && let Some(extension) = s.extensions().get::() - { - extension.0.clone() - } else if s.metadata().target() == COMMAND_SPAN_TARGET - && let Some(extension) = s.extensions().get::() - { - extension.0.clone() - } else { - s.metadata().name().to_string() - } + // When we're creating this layer, we do not yet know the location of the tracing output + // directory, because it is stored in the output directory determined after Config is parsed, + // but we already want to make tracing calls during (and before) config parsing. + // So we store the output into a temporary file, and then move it to the tracing directory + // before bootstrap ends. + let tempdir = tempfile::TempDir::new().expect("Cannot create temporary directory"); + let chrome_tracing_path = tempdir.path().join("bootstrap-trace.json"); + let file = std::io::BufWriter::new(File::create(&chrome_tracing_path).unwrap()); + + let chrome_layer = tracing_chrome::ChromeLayerBuilder::new() + .writer(file) + .include_args(true) + .name_fn(Box::new(|event_or_span| match event_or_span { + tracing_chrome::EventOrSpan::Event(e) => e.metadata().name().to_string(), + tracing_chrome::EventOrSpan::Span(s) => { + if s.metadata().target() == STEP_SPAN_TARGET + && let Some(extension) = s.extensions().get::() + { + extension.0.clone() + } else if s.metadata().target() == COMMAND_SPAN_TARGET + && let Some(extension) = s.extensions().get::() + { + extension.0.clone() + } else { + s.metadata().name().to_string() } - })); - let (chrome_layer, guard) = chrome_layer.build(); - - tracing::subscriber::set_global_default(registry.with(chrome_layer)).unwrap(); - Some(TracingGuard { guard, _tempdir: tempdir, chrome_tracing_path }) - } else { - tracing::subscriber::set_global_default(registry).unwrap(); - None - }; + } + })); + let (chrome_layer, guard) = chrome_layer.build(); - guard + tracing::subscriber::set_global_default(registry.with(chrome_layer)).unwrap(); + TracingGuard { guard, _tempdir: tempdir, chrome_tracing_path } } pub struct TracingGuard { From f9a458874c3673492ce5382a82de462c844a533e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 17:56:16 +0200 Subject: [PATCH 23/37] Do not create a span for cached commands --- src/bootstrap/src/utils/exec.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index ac75e27d57c85..9a536f75ab741 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -681,9 +681,6 @@ impl ExecutionContext { ) -> DeferredCommand<'a> { let fingerprint = command.fingerprint(); - #[cfg(feature = "tracing")] - let span_guard = crate::utils::tracing::trace_cmd(command); - if let Some(cached_output) = self.command_cache.get(&fingerprint) { command.mark_as_executed(); self.verbose(|| println!("Cache hit: {command:?}")); @@ -691,6 +688,9 @@ impl ExecutionContext { return DeferredCommand { state: CommandState::Cached(cached_output) }; } + #[cfg(feature = "tracing")] + let span_guard = crate::utils::tracing::trace_cmd(command); + let created_at = command.get_created_location(); let executed_at = std::panic::Location::caller(); From 12828f7cb123cd0f66e42da21f314f3ee167363b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 18:20:45 +0200 Subject: [PATCH 24/37] Create tracing directory symlink even during dry run --- src/bootstrap/src/bin/main.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs index 95bd242baf7d8..8b2d67266a77b 100644 --- a/src/bootstrap/src/bin/main.rs +++ b/src/bootstrap/src/bin/main.rs @@ -7,13 +7,14 @@ use std::fs::{self, OpenOptions}; use std::io::{self, BufRead, BufReader, IsTerminal, Write}; +use std::path::Path; use std::str::FromStr; use std::time::Instant; use std::{env, process}; use bootstrap::{ Build, CONFIG_CHANGE_HISTORY, ChangeId, Config, Flags, Subcommand, debug, - find_recent_config_change_ids, human_readable_changes, symlink_dir, t, + find_recent_config_change_ids, human_readable_changes, t, }; fn is_tracing_enabled() -> bool { @@ -114,7 +115,18 @@ fn main() { #[cfg(not(windows))] let _ = std::fs::remove_file(&latest_trace_dir); - t!(symlink_dir(&config, &tracing_dir, &latest_trace_dir)); + #[cfg(not(windows))] + fn symlink_dir_inner(original: &Path, link: &Path) -> io::Result<()> { + use std::os::unix::fs; + fs::symlink(original, link) + } + + #[cfg(windows)] + fn symlink_dir_inner(target: &Path, junction: &Path) -> io::Result<()> { + junction::create(target, junction) + } + + t!(symlink_dir_inner(&tracing_dir, &latest_trace_dir)); } debug!("creating new build based on config"); From 3e77562b7d1325ee667a53a7364b463d655ab756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 18:20:53 +0200 Subject: [PATCH 25/37] Use `pretty_step_name` in `step_graph` --- src/bootstrap/src/core/builder/mod.rs | 6 +++--- src/bootstrap/src/utils/step_graph.rs | 10 ++-------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 9dab16f5a42a1..621c1e2c92930 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1717,7 +1717,7 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s // We cannot use a dynamic name here, so instead we record the actual step name // in the step_name field. "step", - step_name = step_name::(), + step_name = pretty_step_name::(), args = step_debug_args(&step) ); span.entered() @@ -1819,7 +1819,7 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s } /// Return qualified step name, e.g. `compile::Rustc`. -fn step_name() -> String { +pub fn pretty_step_name() -> String { // Normalize step type path to only keep the module and the type name let path = type_name::().rsplit("::").take(2).collect::>(); path.into_iter().rev().collect::>().join("::") @@ -1834,7 +1834,7 @@ fn step_debug_args(step: &S) -> String { } fn pretty_print_step(step: &S) -> String { - format!("{} {{ {} }}", step_name::(), step_debug_args(step)) + format!("{} {{ {} }}", pretty_step_name::(), step_debug_args(step)) } impl<'a> AsRef for Builder<'a> { diff --git a/src/bootstrap/src/utils/step_graph.rs b/src/bootstrap/src/utils/step_graph.rs index 30a7853b81664..cffe0dbb3e3a5 100644 --- a/src/bootstrap/src/utils/step_graph.rs +++ b/src/bootstrap/src/utils/step_graph.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use std::io::BufWriter; use std::path::Path; -use crate::core::builder::{AnyDebug, Step}; +use crate::core::builder::{AnyDebug, Step, pretty_step_name}; use crate::t; /// Records the executed steps and their dependencies in a directed graph, @@ -43,13 +43,7 @@ impl StepGraph { metadata.get_target() ) } else { - let type_name = std::any::type_name::(); - type_name - .strip_prefix("bootstrap::core::") - .unwrap_or(type_name) - .strip_prefix("build_steps::") - .unwrap_or(type_name) - .to_string() + pretty_step_name::() }; let node = Node { label, tooltip: node_key.clone() }; From a0306bfa314ba328ddb22123fe021c0ca90a0dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 18:10:29 +0200 Subject: [PATCH 26/37] Update debugging/profiling bootstrap page --- .../bootstrapping/debugging-bootstrap.md | 148 ++++++------------ 1 file changed, 49 insertions(+), 99 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md index 9c5ebbd36c465..fb90c0fdb4353 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md @@ -1,120 +1,100 @@ # Debugging bootstrap -There are two main ways to debug bootstrap itself. The first is through println logging, and the second is through the `tracing` feature. - -> FIXME: this section should be expanded +There are two main ways of debugging (and profiling bootstrap). The first is through println logging, and the second is through the `tracing` feature. ## `println` logging Bootstrap has extensive unstructured logging. Most of it is gated behind the `--verbose` flag (pass `-vv` for even more detail). -If you want to know which `Step` ran a command, you could invoke bootstrap like so: +If you want to see verbose output of executed Cargo commands and other kinds of detailed logs, pass `-v` or `-vv` when invoking bootstrap. Note that the logs are unstructured and may be overwhelming. ``` $ ./x dist rustc --dry-run -vv learning about cargo running: RUSTC_BOOTSTRAP="1" "/home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "metadata" "--format-version" "1" "--no-deps" "--manifest-path" "/home/jyn/src/rust2/Cargo.toml" (failure_mode=Exit) (created at src/bootstrap/src/core/metadata.rs:81:25, executed at src/bootstrap/src/core/metadata.rs:92:50) running: RUSTC_BOOTSTRAP="1" "/home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "metadata" "--format-version" "1" "--no-deps" "--manifest-path" "/home/jyn/src/rust2/library/Cargo.toml" (failure_mode=Exit) (created at src/bootstrap/src/core/metadata.rs:81:25, executed at src/bootstrap/src/core/metadata.rs:92:50) -> Assemble { target_compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu } } - > Libdir { compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu }, target: x86_64-unknown-linux-gnu } - > Sysroot { compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu }, force_recompile: false } -Removing sysroot /home/jyn/src/rust2/build/tmp-dry-run/x86_64-unknown-linux-gnu/stage1 to avoid caching bugs - < Sysroot { compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu }, force_recompile: false } - < Libdir { compiler: Compiler { stage: 1, host: x86_64-unknown-linux-gnu }, target: x86_64-unknown-linux-gnu } -... -``` - -This will go through all the recursive dependency calculations, where `Step`s internally call `builder.ensure()`, without actually running cargo or the compiler. - -In some cases, even this may not be enough logging (if so, please add more!). In that case, you can omit `--dry-run`, which will show the normal output inline with the debug logging: - -``` - c Sysroot { compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu }, force_recompile: false } -using sysroot /home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage0-sysroot -Building stage0 library artifacts (x86_64-unknown-linux-gnu) -running: cd "/home/jyn/src/rust2" && env ... RUSTC_VERBOSE="2" RUSTC_WRAPPER="/home/jyn/src/rust2/build/bootstrap/debug/rustc" "/home/jyn/src/rust2/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "build" "--target" "x86_64-unknown-linux-gnu" "-Zbinary-dep-depinfo" "-Zroot-dir=/home/jyn/src/rust2" "-v" "-v" "--manifest-path" "/home/jyn/src/rust2/library/sysroot/Cargo.toml" "--message-format" "json-render-diagnostics" - 0.293440230s INFO prepare_target{force=false package_id=sysroot v0.0.0 (/home/jyn/src/rust2/library/sysroot) target="sysroot"}: cargo::core::compiler::fingerprint: fingerprint error for sysroot v0.0.0 (/home/jyn/src/rust2/library/sysroot)/Build/TargetInner { name_inferred: true, ..: lib_target("sysroot", ["lib"], "/home/jyn/src/rust2/library/sysroot/src/lib.rs", Edition2021) } ... ``` -In most cases this should not be necessary. +## `tracing` in bootstrap -TODO: we should convert all this to structured logging so it's easier to control precisely. +Bootstrap has a conditional `tracing` feature, which provides the following features: +- It enables structured logging using [`tracing`][tracing] events and spans. +- It generates a [Chrome trace file] that can be used to visualize the hierarchy and durations of executed steps and commands. + - You can open the generated `chrome-trace.json` file using Chrome, on the `chrome://tracing` tab, or e.g. using [Perfetto]. +- It generates [GraphViz] graphs that visualize the dependencies between executed steps. + - You can open the generated `step-graph-*.dot` file using e.g. [xdot] to visualize the step graph, or use e.g. `dot -Tsvg` to convert the GraphViz file to an SVG file. +- It generates a command execution summary, which shows which commands were executed, how many of their executions were cached, and what commands were the slowest to run. + - The generated `command-stats.txt` file is in a simple human-readable format. -## `tracing` in bootstrap +The structured logs will be written to standard error output (`stderr`), while the other outputs will be stored in files in the `/bootstrap-trace/` directory. For convenience, bootstrap will also create a symlink to the latest generated trace output directory at `/bootstrap-trace/latest`. -Bootstrap has conditional [`tracing`][tracing] setup to provide structured logging. +> Note that if you execute bootstrap with `--dry-run`, the tracing output directory might change. Bootstrap will always print a path where the tracing output files were stored at the end of its execution. [tracing]: https://docs.rs/tracing/0.1.41/tracing/index.html +[Chrome trace file]: https://www.chromium.org/developers/how-tos/trace-event-profiling-tool/ +[Perfetto]: https://ui.perfetto.dev/ +[GraphViz]: https://graphviz.org/doc/info/lang.html +[xdot]: https://github.com/jrfonseca/xdot.py ### Enabling `tracing` output -Bootstrap will conditionally build `tracing` support and enable `tracing` output if the `BOOTSTRAP_TRACING` env var is set. - -#### Basic usage - -Example basic usage[^just-trace]: +To enable the conditional `tracing` feature, run bootstrap with the `BOOTSTRAP_TRACING` environment variable. -[^just-trace]: It is not recommended to use *just* `BOOTSTRAP_TRACING=TRACE` because that will dump *everything* at `TRACE` level, including logs intentionally gated behind custom targets as they are too verbose even for `TRACE` level by default. +[tracing_subscriber filter]: https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html ```bash -$ BOOTSTRAP_TRACING=bootstrap=TRACE ./x build library --stage 1 +$ BOOTSTRAP_TRACING=trace ./x build library --stage 1 ``` Example output[^unstable]: ``` -$ BOOTSTRAP_TRACING=bootstrap=TRACE ./x check src/bootstrap/ +$ BOOTSTRAP_TRACING=trace ./x build library --stage 1 --dry-run Building bootstrap - Compiling bootstrap v0.0.0 (/home/joe/repos/rust/src/bootstrap) - Finished `dev` profile [unoptimized] target(s) in 2.74s - DEBUG bootstrap parsing flags - bootstrap::core::config::flags::Flags::parse args=["check", "src/bootstrap/"] - DEBUG bootstrap parsing config based on flags - DEBUG bootstrap creating new build based on config - bootstrap::Build::build - TRACE bootstrap setting up job management - TRACE bootstrap downloading rustfmt early - bootstrap::handling hardcoded subcommands (Format, Suggest, Perf) - DEBUG bootstrap not a hardcoded subcommand; returning to normal handling, cmd=Check { all_targets: false } - DEBUG bootstrap handling subcommand normally - bootstrap::executing real run - bootstrap::(1) executing dry-run sanity-check - bootstrap::(2) executing actual run -Checking stage0 library artifacts (x86_64-unknown-linux-gnu) - Finished `release` profile [optimized + debuginfo] target(s) in 0.04s -Checking stage0 compiler artifacts {rustc-main, rustc_abi, rustc_arena, rustc_ast, rustc_ast_ir, rustc_ast_lowering, rustc_ast_passes, rustc_ast_pretty, rustc_attr_data_structures, rustc_attr_parsing, rustc_baked_icu_data, rustc_borrowck, rustc_builtin_macros, rustc_codegen_llvm, rustc_codegen_ssa, rustc_const_eval, rustc_data_structures, rustc_driver, rustc_driver_impl, rustc_error_codes, rustc_error_messages, rustc_errors, rustc_expand, rustc_feature, rustc_fluent_macro, rustc_fs_util, rustc_graphviz, rustc_hir, rustc_hir_analysis, rustc_hir_pretty, rustc_hir_typeck, rustc_incremental, rustc_index, rustc_index_macros, rustc_infer, rustc_interface, rustc_lexer, rustc_lint, rustc_lint_defs, rustc_llvm, rustc_log, rustc_macros, rustc_metadata, rustc_middle, rustc_mir_build, rustc_mir_dataflow, rustc_mir_transform, rustc_monomorphize, rustc_next_trait_solver, rustc_parse, rustc_parse_format, rustc_passes, rustc_pattern_analysis, rustc_privacy, rustc_query_impl, rustc_query_system, rustc_resolve, rustc_sanitizers, rustc_serialize, rustc_session, rustc_smir, rustc_span, rustc_symbol_mangling, rustc_target, rustc_trait_selection, rustc_traits, rustc_transmute, rustc_ty_utils, rustc_type_ir, rustc_type_ir_macros, stable_mir} (x86_64-unknown-linux-gnu) - Finished `release` profile [optimized + debuginfo] target(s) in 0.23s -Checking stage0 bootstrap artifacts (x86_64-unknown-linux-gnu) - Checking bootstrap v0.0.0 (/home/joe/repos/rust/src/bootstrap) - Finished `release` profile [optimized + debuginfo] target(s) in 0.64s - DEBUG bootstrap checking for postponed test failures from `test --no-fail-fast` -Build completed successfully in 0:00:08 + Finished `dev` profile [unoptimized] target(s) in 0.05s +15:56:52.477 INFO > tool::LibcxxVersionTool {target: x86_64-unknown-linux-gnu} (builder/mod.rs:1715) +15:56:52.575 INFO > compile::Assemble {target_compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }} (builder/mod.rs:1715) +15:56:52.575 INFO > tool::Compiletest {compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu} (builder/mod.rs:1715) +15:56:52.576 INFO > tool::ToolBuild {build_compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu, tool: "compiletest", path: "src/tools/compiletest", mode: ToolBootstrap, source_type: InTree, extra_features: [], allow_features: "internal_output_capture", cargo_args: [], artifact_kind: Binary} (builder/mod.rs:1715) +15:56:52.576 INFO > builder::Libdir {compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu} (builder/mod.rs:1715) +15:56:52.576 INFO > compile::Sysroot {compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, force_recompile: false} (builder/mod.rs:1715) +15:56:52.578 INFO > compile::Assemble {target_compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }} (builder/mod.rs:1715) +15:56:52.578 INFO > tool::Compiletest {compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu} (builder/mod.rs:1715) +15:56:52.578 INFO > tool::ToolBuild {build_compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu, tool: "compiletest", path: "src/tools/compiletest", mode: ToolBootstrap, source_type: InTree, extra_features: [], allow_features: "internal_output_capture", cargo_args: [], artifact_kind: Binary} (builder/mod.rs:1715) +15:56:52.578 INFO > builder::Libdir {compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, target: x86_64-unknown-linux-gnu} (builder/mod.rs:1715) +15:56:52.578 INFO > compile::Sysroot {compiler: Compiler { stage: 0, host: x86_64-unknown-linux-gnu, forced_compiler: false }, force_recompile: false} (builder/mod.rs:1715) + Finished `release` profile [optimized] target(s) in 0.11s +Tracing/profiling output has been written to /build/bootstrap-trace/latest +Build completed successfully in 0:00:00 ``` +[^unstable]: This output is always subject to further changes. + #### Controlling tracing output -The env var `BOOTSTRAP_TRACING` accepts a [`tracing` env-filter][tracing-env-filter]. +The environment variable `BOOTSTRAP_TRACING` accepts a [`tracing_subscriber` filter][tracing-env-filter]. If you set `BOOTSTRAP_TRACING=trace`, you will enable all logs, but that can be overwhelming. You can thus use the filter to reduce the amount of data logged. There are two orthogonal ways to control which kind of tracing logs you want: -1. You can specify the log **level**, e.g. `DEBUG` or `TRACE`. -2. You can also control the log **target**, e.g. `bootstrap` or `bootstrap::core::config` vs custom targets like `CONFIG_HANDLING`. - - Custom targets are used to limit what is output when `BOOTSTRAP_TRACING=bootstrap=TRACE` is used, as they can be too verbose even for `TRACE` level by default. Currently used custom targets: - - `CONFIG_HANDLING` - -The `TRACE` filter will enable *all* `trace` level or less verbose level tracing output. +1. You can specify the log **level**, e.g. `debug` or `trace`. + - If you select a level, all events/spans with an equal or higher priority level will be shown. +2. You can also control the log **target**, e.g. `bootstrap` or `bootstrap::core::config` or a custom target like `CONFIG_HANDLING` or `STEP`. + - Custom targets are used to limit what kinds of spans you are interested in, as the `BOOTSTRAP_TRACING=trace` output can be quite verbose. Currently, you can use the following custom targets: + - `CONFIG_HANDLING`: show spans related to config handling + - `STEP`: show all executed steps. Note that executed commands have `info` event level. + - `COMMAND`: show all executed commands. Note that executed commands have `trace` event level. You can of course combine them (custom target logs are typically gated behind `TRACE` log level additionally): ```bash -$ BOOTSTRAP_TRACING=CONFIG_HANDLING=TRACE ./x build library --stage 1 +$ BOOTSTRAP_TRACING=CONFIG_HANDLING=trace,STEP=info,COMMAND=trace ./x build library --stage 1 ``` -[^unstable]: This output is always subject to further changes. - [tracing-env-filter]: https://docs.rs/tracing-subscriber/0.3.19/tracing_subscriber/filter/struct.EnvFilter.html +Note that the level that you specify using `BOOTSTRAP_TRACING` also has an effect on the spans that will be recorded in the Chrome trace file. + ##### FIXME(#96176): specific tracing for `compiler()` vs `compiler_for()` The additional targets `COMPILER` and `COMPILER_FOR` are used to help trace what @@ -123,12 +103,6 @@ if [#96176][cleanup-compiler-for] is resolved. [cleanup-compiler-for]: https://github.com/rust-lang/rust/issues/96176 -### Rendering step graph - -When you run bootstrap with the `BOOTSTRAP_TRACING` environment variable configured, bootstrap will automatically output a DOT file that shows all executed steps and their dependencies. The files will have a prefix `bootstrap-steps`. You can use e.g. `xdot` to visualize the file or e.g. `dot -Tsvg` to convert the DOT file to a SVG file. - -A separate DOT file will be outputted for dry-run and non-dry-run execution. - ### Using `tracing` in bootstrap Both `tracing::*` macros and the `tracing::instrument` proc-macro attribute need to be gated behind `tracing` feature. Examples: @@ -149,15 +123,6 @@ impl Step for Foo { todo!() } - #[cfg_attr( - feature = "tracing", - instrument( - level = "trace", - name = "Foo::run", - skip_all, - fields(compiler = ?builder.compiler), - ), - )] fn run(self, builder: &Builder<'_>) -> Self::Output { trace!(?run, "entered Foo::run"); @@ -172,21 +137,6 @@ For `#[instrument]`, it's recommended to: - Explicitly pick an instrumentation name via `name = ".."` to distinguish between e.g. `run` of different steps. - Take care to not cause diverging behavior via tracing, e.g. building extra things only when tracing infra is enabled. -### Profiling bootstrap - -You can set the `BOOTSTRAP_PROFILE=1` environment variable to enable command execution profiling during bootstrap. This generates: - -* A Chrome trace file (for visualization in `chrome://tracing` or [Perfetto](https://ui.perfetto.dev)) if tracing is enabled via `BOOTSTRAP_TRACING=COMMAND=trace` -* A plain-text summary file, `bootstrap-profile-{pid}.txt`, listing all commands sorted by execution time (slowest first), along with cache hits and working directories - -Note: the `.txt` report is always generated when `BOOTSTRAP_PROFILE=1` is set — tracing is not required. - -Example usage: - -```bash -$ BOOTSTRAP_PROFILE=1 BOOTSTRAP_TRACING=COMMAND=trace ./x build library -``` - ### rust-analyzer integration? Unfortunately, because bootstrap is a `rust-analyzer.linkedProjects`, you can't ask r-a to check/build bootstrap itself with `tracing` feature enabled to get relevant completions, due to lack of support as described in . From 6f584bc21e9446faa982864b3eeb5e6b6dadccf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 11 Aug 2025 18:32:05 +0200 Subject: [PATCH 27/37] Print created location of executed commands --- src/bootstrap/src/utils/tracing.rs | 77 ++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs index 8b9d6ca213c55..428ba013c985f 100644 --- a/src/bootstrap/src/utils/tracing.rs +++ b/src/bootstrap/src/utils/tracing.rs @@ -54,13 +54,17 @@ const COMMAND_SPAN_TARGET: &str = "COMMAND"; #[cfg(feature = "tracing")] pub fn trace_cmd(command: &crate::BootstrapCommand) -> tracing::span::EnteredSpan { let fingerprint = command.fingerprint(); + let location = command.get_created_location(); + let location = format!("{}:{}", location.file(), location.line()); + tracing::span!( target: COMMAND_SPAN_TARGET, tracing::Level::TRACE, "cmd", cmd_name = fingerprint.program_name().to_string(), cmd = fingerprint.format_short_cmd(), - full_cmd = ?command + full_cmd = ?command, + location ) .entered() } @@ -269,33 +273,66 @@ mod inner { } } - // We handle steps specially. We instrument them dynamically in `Builder::ensure`, - // and we want to have custom name for each step span. But tracing doesn't allow setting - // dynamic span names. So we detect step spans here and override their name. - if span.metadata().target() == STEP_SPAN_TARGET { - let name = field_values.and_then(|v| v.step_name.as_deref()).unwrap_or(span.name()); - write!(writer, "{name}")?; - - // There should be only one more field called `args` - if let Some(values) = field_values { - let field = &values.fields[0]; - write!(writer, " {{{}}}", field.1)?; - } - } else { - write!(writer, "{}", span.name())?; - if let Some(values) = field_values.filter(|v| !v.fields.is_empty()) { + fn write_fields<'a, I: IntoIterator, W: Write>( + writer: &mut W, + iter: I, + ) -> std::io::Result<()> { + let items = iter.into_iter().collect::>(); + if !items.is_empty() { write!(writer, " [")?; - for (index, (name, value)) in values.fields.iter().enumerate() { + for (index, (name, value)) in items.iter().enumerate() { write!(writer, "{name} = {value}")?; - if index < values.fields.len() - 1 { + if index < items.len() - 1 { write!(writer, ", ")?; } } write!(writer, "]")?; } - }; + Ok(()) + } + + // We handle steps specially. We instrument them dynamically in `Builder::ensure`, + // and we want to have custom name for each step span. But tracing doesn't allow setting + // dynamic span names. So we detect step spans here and override their name. + match span.metadata().target() { + // Executed step + STEP_SPAN_TARGET => { + let name = + field_values.and_then(|v| v.step_name.as_deref()).unwrap_or(span.name()); + write!(writer, "{name}")?; + + // There should be only one more field called `args` + if let Some(values) = field_values { + let field = &values.fields[0]; + write!(writer, " {{{}}}", field.1)?; + } + write_location(writer, span.metadata())?; + } + // Executed command + COMMAND_SPAN_TARGET => { + write!(writer, "{}", span.name())?; + if let Some(values) = field_values { + write_fields( + writer, + values.fields.iter().filter(|(name, _)| *name != "location"), + )?; + write!( + writer, + " ({})", + values.fields.iter().find(|(name, _)| *name == "location").unwrap().1 + )?; + } + } + // Other span + _ => { + write!(writer, "{}", span.name())?; + if let Some(values) = field_values { + write_fields(writer, values.fields.iter())?; + } + write_location(writer, span.metadata())?; + } + } - write_location(writer, span.metadata())?; writeln!(writer)?; Ok(()) } From 258915a55539593423c3d4c30f7b741f65c56e51 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Fri, 8 Aug 2025 17:38:03 +0000 Subject: [PATCH 28/37] llvm: Accept new LLVM lifetime format LLVM removed the size parameter from the lifetime format. Tolerate not having that size parameter. --- compiler/rustc_codegen_llvm/src/builder.rs | 6 ++++- .../align-byval-alignment-mismatch.rs | 4 ++-- tests/codegen-llvm/call-tmps-lifetime.rs | 16 ++++++------- .../codegen-llvm/intrinsics/transmute-simd.rs | 24 +++++++++---------- tests/codegen-llvm/intrinsics/transmute.rs | 8 +++---- .../issues/issue-105386-ub-in-debuginfo.rs | 2 +- tests/codegen-llvm/lifetime_start_end.rs | 16 ++++++------- .../uninhabited-transparent-return-abi.rs | 4 ++-- 8 files changed, 42 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index bd175e560c77a..35a619da71015 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1686,7 +1686,11 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> { return; } - self.call_intrinsic(intrinsic, &[self.val_ty(ptr)], &[self.cx.const_u64(size), ptr]); + if crate::llvm_util::get_version() >= (22, 0, 0) { + self.call_intrinsic(intrinsic, &[self.val_ty(ptr)], &[ptr]); + } else { + self.call_intrinsic(intrinsic, &[self.val_ty(ptr)], &[self.cx.const_u64(size), ptr]); + } } } impl<'a, 'll, CX: Borrow>> GenericBuilder<'a, 'll, CX> { diff --git a/tests/codegen-llvm/align-byval-alignment-mismatch.rs b/tests/codegen-llvm/align-byval-alignment-mismatch.rs index c69fc2de9d257..8eaf2751ed789 100644 --- a/tests/codegen-llvm/align-byval-alignment-mismatch.rs +++ b/tests/codegen-llvm/align-byval-alignment-mismatch.rs @@ -55,10 +55,10 @@ extern "C" { pub unsafe fn rust_to_c_increases_alignment(x: Align1) { // i686-linux: start: // i686-linux-NEXT: [[ALLOCA:%[0-9a-z]+]] = alloca [48 x i8], align 4 - // i686-linux-NEXT: call void @llvm.lifetime.start.p0(i64 48, ptr {{.*}}[[ALLOCA]]) + // i686-linux-NEXT: call void @llvm.lifetime.start.p0({{(i64 48, )?}}ptr {{.*}}[[ALLOCA]]) // i686-linux-NEXT: call void @llvm.memcpy.{{.+}}(ptr {{.*}}align 4 {{.*}}[[ALLOCA]], ptr {{.*}}align 1 {{.*}}%x // i686-linux-NEXT: call void @extern_c_align1({{.+}} [[ALLOCA]]) - // i686-linux-NEXT: call void @llvm.lifetime.end.p0(i64 48, ptr {{.*}}[[ALLOCA]]) + // i686-linux-NEXT: call void @llvm.lifetime.end.p0({{(i64 48, )?}}ptr {{.*}}[[ALLOCA]]) // x86_64-linux: start: // x86_64-linux-NEXT: call void @extern_c_align1 diff --git a/tests/codegen-llvm/call-tmps-lifetime.rs b/tests/codegen-llvm/call-tmps-lifetime.rs index 7b7b6e17bddd3..0d7657ed758b0 100644 --- a/tests/codegen-llvm/call-tmps-lifetime.rs +++ b/tests/codegen-llvm/call-tmps-lifetime.rs @@ -16,14 +16,14 @@ use minicore::*; // CHECK-NEXT: start: // CHECK-NEXT: [[B:%.*]] = alloca // CHECK-NEXT: [[A:%.*]] = alloca -// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4096, ptr [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 4096, )?}}ptr [[A]]) // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 4 {{.*}}, i32 4096, i1 false) // CHECK-NEXT: call void %h(ptr {{.*}} [[A]]) -// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4096, ptr [[A]]) -// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 4096, ptr [[B]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0({{(i64 4096, )?}}ptr [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 4096, )?}}ptr [[B]]) // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[B]], ptr align 4 {{.*}}, i32 4096, i1 false) // CHECK-NEXT: call void %h(ptr {{.*}} [[B]]) -// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 4096, ptr [[B]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0({{(i64 4096, )?}}ptr [[B]]) #[no_mangle] pub fn const_indirect(h: extern "C" fn([u32; 1024])) { const C: [u32; 1024] = [0; 1024]; @@ -42,12 +42,12 @@ pub struct Str { // CHECK-LABEL: define void @immediate_indirect(ptr {{.*}}%s.0, i32 {{.*}}%s.1, ptr {{.*}}%g) // CHECK-NEXT: start: // CHECK-NEXT: [[A:%.*]] = alloca -// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 8, ptr [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr [[A]]) // CHECK-NEXT: store ptr %s.0, ptr [[A]] // CHECK-NEXT: [[B:%.]] = getelementptr inbounds i8, ptr [[A]], i32 4 // CHECK-NEXT: store i32 %s.1, ptr [[B]] // CHECK-NEXT: call void %g(ptr {{.*}} [[A]]) -// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 8, ptr [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0({{(i64 8, )?}}ptr [[A]]) #[no_mangle] pub fn immediate_indirect(s: Str, g: extern "C" fn(Str)) { g(s); @@ -58,10 +58,10 @@ pub fn immediate_indirect(s: Str, g: extern "C" fn(Str)) { // CHECK-LABEL: define void @align_indirect(ptr{{.*}} align 1{{.*}} %a, ptr{{.*}} %fun) // CHECK-NEXT: start: // CHECK-NEXT: [[A:%.*]] = alloca [1024 x i8], align 4 -// CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 1024, ptr [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 1024, )?}}ptr [[A]]) // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 [[A]], ptr align 1 %a, i32 1024, i1 false) // CHECK-NEXT: call void %fun(ptr {{.*}} [[A]]) -// CHECK-NEXT: call void @llvm.lifetime.end.p0(i64 1024, ptr [[A]]) +// CHECK-NEXT: call void @llvm.lifetime.end.p0({{(i64 1024, )?}}ptr [[A]]) #[no_mangle] pub fn align_indirect(a: [u8; 1024], fun: extern "C" fn([u8; 1024])) { fun(a); diff --git a/tests/codegen-llvm/intrinsics/transmute-simd.rs b/tests/codegen-llvm/intrinsics/transmute-simd.rs index e34b27e133359..92af16945abf3 100644 --- a/tests/codegen-llvm/intrinsics/transmute-simd.rs +++ b/tests/codegen-llvm/intrinsics/transmute-simd.rs @@ -97,10 +97,10 @@ pub extern "C" fn float_ptr_same_lanes(v: f64x2) -> PtrX2 { // CHECK-NOT: alloca // CHECK: %[[TEMP:.+]] = alloca [16 x i8] // CHECK-NOT: alloca - // CHECK: call void @llvm.lifetime.start.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: store <2 x double> %v, ptr %[[TEMP]] // CHECK: %[[RET:.+]] = load <2 x ptr>, ptr %[[TEMP]] - // CHECK: call void @llvm.lifetime.end.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: ret <2 x ptr> %[[RET]] unsafe { transmute(v) } } @@ -111,10 +111,10 @@ pub extern "C" fn ptr_float_same_lanes(v: PtrX2) -> f64x2 { // CHECK-NOT: alloca // CHECK: %[[TEMP:.+]] = alloca [16 x i8] // CHECK-NOT: alloca - // CHECK: call void @llvm.lifetime.start.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: store <2 x ptr> %v, ptr %[[TEMP]] // CHECK: %[[RET:.+]] = load <2 x double>, ptr %[[TEMP]] - // CHECK: call void @llvm.lifetime.end.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: ret <2 x double> %[[RET]] unsafe { transmute(v) } } @@ -125,10 +125,10 @@ pub extern "C" fn int_ptr_same_lanes(v: i64x2) -> PtrX2 { // CHECK-NOT: alloca // CHECK: %[[TEMP:.+]] = alloca [16 x i8] // CHECK-NOT: alloca - // CHECK: call void @llvm.lifetime.start.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: store <2 x i64> %v, ptr %[[TEMP]] // CHECK: %[[RET:.+]] = load <2 x ptr>, ptr %[[TEMP]] - // CHECK: call void @llvm.lifetime.end.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: ret <2 x ptr> %[[RET]] unsafe { transmute(v) } } @@ -139,10 +139,10 @@ pub extern "C" fn ptr_int_same_lanes(v: PtrX2) -> i64x2 { // CHECK-NOT: alloca // CHECK: %[[TEMP:.+]] = alloca [16 x i8] // CHECK-NOT: alloca - // CHECK: call void @llvm.lifetime.start.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: store <2 x ptr> %v, ptr %[[TEMP]] // CHECK: %[[RET:.+]] = load <2 x i64>, ptr %[[TEMP]] - // CHECK: call void @llvm.lifetime.end.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: ret <2 x i64> %[[RET]] unsafe { transmute(v) } } @@ -153,10 +153,10 @@ pub extern "C" fn float_ptr_widen(v: f32x4) -> PtrX2 { // CHECK-NOT: alloca // CHECK: %[[TEMP:.+]] = alloca [16 x i8] // CHECK-NOT: alloca - // CHECK: call void @llvm.lifetime.start.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: store <4 x float> %v, ptr %[[TEMP]] // CHECK: %[[RET:.+]] = load <2 x ptr>, ptr %[[TEMP]] - // CHECK: call void @llvm.lifetime.end.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: ret <2 x ptr> %[[RET]] unsafe { transmute(v) } } @@ -167,10 +167,10 @@ pub extern "C" fn int_ptr_widen(v: i32x4) -> PtrX2 { // CHECK-NOT: alloca // CHECK: %[[TEMP:.+]] = alloca [16 x i8] // CHECK-NOT: alloca - // CHECK: call void @llvm.lifetime.start.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: store <4 x i32> %v, ptr %[[TEMP]] // CHECK: %[[RET:.+]] = load <2 x ptr>, ptr %[[TEMP]] - // CHECK: call void @llvm.lifetime.end.p0(i64 16, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 16, )?}}ptr %[[TEMP]]) // CHECK: ret <2 x ptr> %[[RET]] unsafe { transmute(v) } } diff --git a/tests/codegen-llvm/intrinsics/transmute.rs b/tests/codegen-llvm/intrinsics/transmute.rs index 91cff38773d78..e327c100d7ddd 100644 --- a/tests/codegen-llvm/intrinsics/transmute.rs +++ b/tests/codegen-llvm/intrinsics/transmute.rs @@ -192,12 +192,12 @@ pub unsafe fn check_byte_from_bool(x: bool) -> u8 { #[no_mangle] pub unsafe fn check_to_pair(x: u64) -> Option { // CHECK: %[[TEMP:.+]] = alloca [8 x i8], align 8 - // CHECK: call void @llvm.lifetime.start.p0(i64 8, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr %[[TEMP]]) // CHECK: store i64 %x, ptr %[[TEMP]], align 8 // CHECK: %[[PAIR0:.+]] = load i32, ptr %[[TEMP]], align 8 // CHECK: %[[PAIR1P:.+]] = getelementptr inbounds i8, ptr %[[TEMP]], i64 4 // CHECK: %[[PAIR1:.+]] = load i32, ptr %[[PAIR1P]], align 4 - // CHECK: call void @llvm.lifetime.end.p0(i64 8, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 8, )?}}ptr %[[TEMP]]) // CHECK: insertvalue {{.+}}, i32 %[[PAIR0]], 0 // CHECK: insertvalue {{.+}}, i32 %[[PAIR1]], 1 transmute(x) @@ -207,12 +207,12 @@ pub unsafe fn check_to_pair(x: u64) -> Option { #[no_mangle] pub unsafe fn check_from_pair(x: Option) -> u64 { // CHECK: %[[TEMP:.+]] = alloca [8 x i8], align 8 - // CHECK: call void @llvm.lifetime.start.p0(i64 8, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr %[[TEMP]]) // CHECK: store i32 %x.0, ptr %[[TEMP]], align 8 // CHECK: %[[PAIR1P:.+]] = getelementptr inbounds i8, ptr %[[TEMP]], i64 4 // CHECK: store i32 %x.1, ptr %[[PAIR1P]], align 4 // CHECK: %[[R:.+]] = load i64, ptr %[[TEMP]], align 8 - // CHECK: call void @llvm.lifetime.end.p0(i64 8, ptr %[[TEMP]]) + // CHECK: call void @llvm.lifetime.end.p0({{(i64 8, )?}}ptr %[[TEMP]]) // CHECK: ret i64 %[[R]] transmute(x) } diff --git a/tests/codegen-llvm/issues/issue-105386-ub-in-debuginfo.rs b/tests/codegen-llvm/issues/issue-105386-ub-in-debuginfo.rs index 848aa910b584e..95e70bf56784d 100644 --- a/tests/codegen-llvm/issues/issue-105386-ub-in-debuginfo.rs +++ b/tests/codegen-llvm/issues/issue-105386-ub-in-debuginfo.rs @@ -19,6 +19,6 @@ pub fn outer_function(x: S, y: S) -> usize { // CHECK: [[spill:%.*]] = alloca // CHECK-NOT: [[ptr_tmp:%.*]] = getelementptr inbounds i8, ptr [[spill]] // CHECK-NOT: [[load:%.*]] = load ptr, ptr -// CHECK: call void @llvm.lifetime.start{{.*}}({{.*}}, ptr [[spill]]) +// CHECK: call void @llvm.lifetime.start{{.*}}({{(.*, )?}}ptr [[spill]]) // CHECK: [[inner:%.*]] = getelementptr inbounds i8, ptr [[spill]] // CHECK: call void @llvm.memcpy{{.*}}(ptr {{align .*}} [[inner]], ptr {{align .*}} %x diff --git a/tests/codegen-llvm/lifetime_start_end.rs b/tests/codegen-llvm/lifetime_start_end.rs index 0639e7640aa15..2df5acf54df58 100644 --- a/tests/codegen-llvm/lifetime_start_end.rs +++ b/tests/codegen-llvm/lifetime_start_end.rs @@ -8,27 +8,27 @@ pub fn test() { let a = 0u8; &a; // keep variable in an alloca - // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, ptr %a) + // CHECK: call void @llvm.lifetime.start{{.*}}({{(i[0-9 ]+, )?}}ptr %a) { let b = &Some(a); &b; // keep variable in an alloca - // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, {{.*}}) + // CHECK: call void @llvm.lifetime.start{{.*}}({{(i[0-9 ]+, )?}}{{.*}}) - // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, {{.*}}) + // CHECK: call void @llvm.lifetime.start{{.*}}({{(i[0-9 ]+, )?}}{{.*}}) - // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, {{.*}}) + // CHECK: call void @llvm.lifetime.end{{.*}}({{(i[0-9 ]+, )?}}{{.*}}) - // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, {{.*}}) + // CHECK: call void @llvm.lifetime.end{{.*}}({{(i[0-9 ]+, )?}}{{.*}}) } let c = 1u8; &c; // keep variable in an alloca - // CHECK: call void @llvm.lifetime.start{{.*}}(i{{[0-9 ]+}}, ptr %c) + // CHECK: call void @llvm.lifetime.start{{.*}}({{(i[0-9 ]+, )?}}ptr %c) - // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, ptr %c) + // CHECK: call void @llvm.lifetime.end{{.*}}({{(i[0-9 ]+, )?}}ptr %c) - // CHECK: call void @llvm.lifetime.end{{.*}}(i{{[0-9 ]+}}, ptr %a) + // CHECK: call void @llvm.lifetime.end{{.*}}({{(i[0-9 ]+, )?}}ptr %a) } diff --git a/tests/codegen-llvm/uninhabited-transparent-return-abi.rs b/tests/codegen-llvm/uninhabited-transparent-return-abi.rs index face1577c3f69..83d9a7a32ab21 100644 --- a/tests/codegen-llvm/uninhabited-transparent-return-abi.rs +++ b/tests/codegen-llvm/uninhabited-transparent-return-abi.rs @@ -23,7 +23,7 @@ extern "Rust" { #[no_mangle] pub fn test_uninhabited_ret_by_ref() { // CHECK: %_1 = alloca [24 x i8], align {{8|4}} - // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %_1) + // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 24, )?}}ptr nonnull %_1) // CHECK-NEXT: call void @opaque({{.*}} sret([24 x i8]) {{.*}} %_1) #2 // CHECK-NEXT: unreachable unsafe { @@ -35,7 +35,7 @@ pub fn test_uninhabited_ret_by_ref() { #[no_mangle] pub fn test_uninhabited_ret_by_ref_with_arg(rsi: u32) { // CHECK: %_2 = alloca [24 x i8], align {{8|4}} - // CHECK-NEXT: call void @llvm.lifetime.start.p0(i64 24, ptr nonnull %_2) + // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 24, )?}}ptr nonnull %_2) // CHECK-NEXT: call void @opaque_with_arg({{.*}} sret([24 x i8]) {{.*}} %_2, i32 noundef %rsi) #2 // CHECK-NEXT: unreachable unsafe { From 0b8c6ad2b7863d34bfe5a55714039d2039a0ede5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 12 Aug 2025 17:39:07 +0200 Subject: [PATCH 29/37] Remove one dependency from tracing bootstrap build --- src/bootstrap/Cargo.lock | 130 --------------------------------------- src/bootstrap/Cargo.toml | 2 +- 2 files changed, 1 insertion(+), 131 deletions(-) diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock index f5d04c39d3f33..605ac687ab814 100644 --- a/src/bootstrap/Cargo.lock +++ b/src/bootstrap/Cargo.lock @@ -11,21 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anstyle" version = "1.0.10" @@ -109,12 +94,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "bumpalo" -version = "3.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" - [[package]] name = "cc" version = "1.2.23" @@ -136,12 +115,7 @@ version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", "num-traits", - "wasm-bindgen", - "windows-link", ] [[package]] @@ -212,12 +186,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - [[package]] name = "cpufeatures" version = "0.2.15" @@ -373,30 +341,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "iana-time-zone" -version = "0.1.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "ignore" version = "0.4.23" @@ -430,16 +374,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -[[package]] -name = "js-sys" -version = "0.3.77" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - [[package]] name = "junction" version = "1.2.0" @@ -701,12 +635,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "rustversion" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" - [[package]] name = "ryu" version = "1.0.18" @@ -996,64 +924,6 @@ dependencies = [ "wit-bindgen-rt", ] -[[package]] -name = "wasm-bindgen" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" -dependencies = [ - "bumpalo", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" -dependencies = [ - "unicode-ident", -] - [[package]] name = "winapi" version = "0.3.9" diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index ae5a5d798e57c..eb9cfaff31b38 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -61,7 +61,7 @@ xz2 = "0.1" sysinfo = { version = "0.36.0", default-features = false, optional = true, features = ["system"] } # Dependencies needed by the `tracing` feature -chrono = { version = "0.4", optional = true } +chrono = { version = "0.4", default-features = false, optional = true, features = ["now", "std"] } tracing = { version = "0.1", optional = true, features = ["attributes"] } tracing-chrome = { version = "0.7", optional = true } tracing-subscriber = { version = "0.3", optional = true, features = ["env-filter", "fmt", "registry", "std"] } From c439a59dbd275aef9bc24c7172e2111ccc3794c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 17 Mar 2024 20:46:36 +0000 Subject: [PATCH 30/37] Change the desugaring of `assert!` for better error output In the desugaring of `assert!`, we now expand to a `match` expression instead of `if !cond {..}`. The span of incorrect conditions will point only at the expression, and not the whole `assert!` invocation. ``` error[E0308]: mismatched types --> $DIR/issue-14091.rs:2:13 | LL | assert!(1,1); | ^ expected `bool`, found integer ``` We no longer mention the expression needing to implement the `Not` trait. ``` error[E0308]: mismatched types --> $DIR/issue-14091-2.rs:15:13 | LL | assert!(x, x); | ^ expected `bool`, found `BytePos` ``` `assert!(val)` now desugars to: ```rust match val { true => {}, _ => $crate::panic::panic_2021!(), } ``` Fix #122159. We make some minor changes to some diagnostics to avoid span overlap on type mismatch or inverted "expected"/"found" on type errors. We remove some unnecessary parens from core, alloc and miri. address review comments --- compiler/rustc_builtin_macros/src/assert.rs | 35 +++++++++------- .../src/error_reporting/infer/mod.rs | 40 +++++++++++++++++-- .../src/missing_asserts_for_indexing.rs | 12 +++--- src/tools/clippy/tests/ui/const_is_empty.rs | 1 + .../clippy/tests/ui/const_is_empty.stderr | 10 ++++- .../clippy/tests/ui/incompatible_msrv.rs | 2 +- tests/ui/codemap_tests/issue-28308.rs | 16 +++++++- tests/ui/codemap_tests/issue-28308.stderr | 28 ++++++++++--- tests/ui/consts/control-flow/assert.stderr | 4 +- ...st_monomorphization_error_backtrace.stderr | 8 ++-- .../const-expr-generic-err.stderr | 4 +- tests/ui/macros/assert-macro-lifetimes.rs | 8 ++++ .../ui/simd/const-err-trumps-simd-err.stderr | 4 +- tests/ui/transmutability/uninhabited.stderr | 12 +++--- 14 files changed, 134 insertions(+), 50 deletions(-) create mode 100644 tests/ui/macros/assert-macro-lifetimes.rs diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs index 855da5caa312c..013258a1b4efa 100644 --- a/compiler/rustc_builtin_macros/src/assert.rs +++ b/compiler/rustc_builtin_macros/src/assert.rs @@ -1,8 +1,8 @@ mod context; -use rustc_ast::token::Delimiter; +use rustc_ast::token::{self, Delimiter}; use rustc_ast::tokenstream::{DelimSpan, TokenStream}; -use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment, UnOp, token}; +use rustc_ast::{DelimArgs, Expr, ExprKind, MacCall, Path, PathSegment}; use rustc_ast_pretty::pprust; use rustc_errors::PResult; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; @@ -29,7 +29,7 @@ pub(crate) fn expand_assert<'cx>( // `core::panic` and `std::panic` are different macros, so we use call-site // context to pick up whichever is currently in scope. - let call_site_span = cx.with_call_site_ctxt(span); + let call_site_span = cx.with_call_site_ctxt(cond_expr.span); let panic_path = || { if use_panic_2021(span) { @@ -63,7 +63,7 @@ pub(crate) fn expand_assert<'cx>( }), })), ); - expr_if_not(cx, call_site_span, cond_expr, then, None) + assert_cond_check(cx, call_site_span, cond_expr, then) } // If `generic_assert` is enabled, generates rich captured outputs // @@ -88,26 +88,33 @@ pub(crate) fn expand_assert<'cx>( )), )], ); - expr_if_not(cx, call_site_span, cond_expr, then, None) + assert_cond_check(cx, call_site_span, cond_expr, then) }; ExpandResult::Ready(MacEager::expr(expr)) } +/// `assert!($cond_expr, $custom_message)` struct Assert { cond_expr: Box, custom_message: Option, } -// if !{ ... } { ... } else { ... } -fn expr_if_not( - cx: &ExtCtxt<'_>, - span: Span, - cond: Box, - then: Box, - els: Option>, -) -> Box { - cx.expr_if(span, cx.expr(span, ExprKind::Unary(UnOp::Not, cond)), then, els) +/// `match { true => {} _ => }` +fn assert_cond_check(cx: &ExtCtxt<'_>, span: Span, cond: Box, then: Box) -> Box { + // Instead of expanding to `if ! { }`, we expand to + // `match { true => {} _ => }`. + // This allows us to always complain about mismatched types instead of "cannot apply unary + // operator `!` to type `X`" when passing an invalid ``, while also allowing `` to + // be `&true`. + let els = cx.expr_block(cx.block(span, thin_vec![])); + let mut arms = thin_vec![]; + arms.push(cx.arm(span, cx.pat_lit(span, cx.expr_bool(span, true)), els)); + arms.push(cx.arm(span, cx.pat_wild(span), then)); + + // We wrap the `match` in a statement to limit the length of any borrows introduced in the + // condition. + cx.expr_block(cx.block(span, [cx.stmt_expr(cx.expr_match(span, cond, arms))].into())) } fn parse_assert<'a>(cx: &ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 8551780bcd5f4..4c7a7e93648a5 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -1620,8 +1620,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { { let e = self.tcx.erase_regions(e); let f = self.tcx.erase_regions(f); - let expected = with_forced_trimmed_paths!(e.sort_string(self.tcx)); - let found = with_forced_trimmed_paths!(f.sort_string(self.tcx)); + let mut expected = with_forced_trimmed_paths!(e.sort_string(self.tcx)); + let mut found = with_forced_trimmed_paths!(f.sort_string(self.tcx)); + if let ObligationCauseCode::Pattern { span, .. } = cause.code() + && let Some(span) = span + && !span.from_expansion() + && cause.span.from_expansion() + { + // When the type error comes from a macro like `assert!()`, and we are pointing at + // code the user wrote the cause and effect are reversed as the expected value is + // what the macro expanded to. + (found, expected) = (expected, found); + } if expected == found { label_or_note(span, terr.to_string(self.tcx)); } else { @@ -2144,7 +2154,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ) -> Option<(DiagStyledString, DiagStyledString)> { match values { ValuePairs::Regions(exp_found) => self.expected_found_str(exp_found), - ValuePairs::Terms(exp_found) => self.expected_found_str_term(exp_found, long_ty_path), + ValuePairs::Terms(exp_found) => { + self.expected_found_str_term(cause, exp_found, long_ty_path) + } ValuePairs::Aliases(exp_found) => self.expected_found_str(exp_found), ValuePairs::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found), ValuePairs::ExistentialProjection(exp_found) => self.expected_found_str(exp_found), @@ -2183,6 +2195,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { fn expected_found_str_term( &self, + cause: &ObligationCause<'tcx>, exp_found: ty::error::ExpectedFound>, long_ty_path: &mut Option, ) -> Option<(DiagStyledString, DiagStyledString)> { @@ -2190,8 +2203,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if exp_found.references_error() { return None; } + let (mut expected, mut found) = (exp_found.expected, exp_found.found); + + if let ObligationCauseCode::Pattern { span, .. } = cause.code() + && let Some(span) = span + && !span.from_expansion() + && cause.span.from_expansion() + { + // When the type error comes from a macro like `assert!()`, and we are pointing at + // code the user wrote, the cause and effect are reversed as the expected value is + // what the macro expanded to. So if the user provided a `Type` when the macro is + // written in such a way that a `bool` was expected, we want to print: + // = note: expected `bool` + // found `Type`" + // but as far as the compiler is concerned, after expansion what was expected was `Type` + // = note: expected `Type` + // found `bool`" + // so we reverse them here to match user expectation. + (expected, found) = (found, expected); + } - Some(match (exp_found.expected.kind(), exp_found.found.kind()) { + Some(match (expected.kind(), found.kind()) { (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => { let (mut exp, mut fnd) = self.cmp(expected, found); // Use the terminal width as the basis to determine when to compress the printed diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index cf0c85990b150..788a04357b1e2 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -11,7 +11,7 @@ use rustc_ast::{BinOpKind, LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_errors::{Applicability, Diag}; -use rustc_hir::{Block, Body, Expr, ExprKind, UnOp}; +use rustc_hir::{Body, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; @@ -135,12 +135,12 @@ fn assert_len_expr<'hir>( cx: &LateContext<'_>, expr: &'hir Expr<'hir>, ) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { - let (cmp, asserted_len, slice_len) = if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr) - && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind - && let ExprKind::Binary(bin_op, left, right) = &condition.kind + let (cmp, asserted_len, slice_len) = if let Some( + higher::IfLetOrMatch::Match(cond, [_, then], _) + ) = higher::IfLetOrMatch::parse(cx, expr) + && let ExprKind::Binary(bin_op, left, right) = &cond.kind // check if `then` block has a never type expression - && let ExprKind::Block(Block { expr: Some(then_expr), .. }, _) = then.kind - && cx.typeck_results().expr_ty(then_expr).is_never() + && cx.typeck_results().expr_ty(then.body).is_never() { len_comparison(bin_op.node, left, right)? } else if let Some((macro_call, bin_op)) = first_node_macro_backtrace(cx, expr).find_map(|macro_call| { diff --git a/src/tools/clippy/tests/ui/const_is_empty.rs b/src/tools/clippy/tests/ui/const_is_empty.rs index 8bb4f0e5d9750..63c6342a323ce 100644 --- a/src/tools/clippy/tests/ui/const_is_empty.rs +++ b/src/tools/clippy/tests/ui/const_is_empty.rs @@ -196,6 +196,7 @@ fn issue_13106() { const { assert!(EMPTY_STR.is_empty()); + //~^ const_is_empty } const { diff --git a/src/tools/clippy/tests/ui/const_is_empty.stderr b/src/tools/clippy/tests/ui/const_is_empty.stderr index 2ba189058e832..9a42518698e39 100644 --- a/src/tools/clippy/tests/ui/const_is_empty.stderr +++ b/src/tools/clippy/tests/ui/const_is_empty.stderr @@ -158,10 +158,16 @@ LL | let _ = val.is_empty(); | ^^^^^^^^^^^^^^ error: this expression always evaluates to true - --> tests/ui/const_is_empty.rs:202:9 + --> tests/ui/const_is_empty.rs:198:17 + | +LL | assert!(EMPTY_STR.is_empty()); + | ^^^^^^^^^^^^^^^^^^^^ + +error: this expression always evaluates to true + --> tests/ui/const_is_empty.rs:203:9 | LL | EMPTY_STR.is_empty(); | ^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 27 previous errors +error: aborting due to 28 previous errors diff --git a/src/tools/clippy/tests/ui/incompatible_msrv.rs b/src/tools/clippy/tests/ui/incompatible_msrv.rs index f7f21e1850d0a..882f909e30c97 100644 --- a/src/tools/clippy/tests/ui/incompatible_msrv.rs +++ b/src/tools/clippy/tests/ui/incompatible_msrv.rs @@ -1,6 +1,6 @@ #![warn(clippy::incompatible_msrv)] #![feature(custom_inner_attributes)] -#![allow(stable_features)] +#![allow(stable_features, clippy::diverging_sub_expression)] #![feature(strict_provenance)] // For use in test #![clippy::msrv = "1.3.0"] diff --git a/tests/ui/codemap_tests/issue-28308.rs b/tests/ui/codemap_tests/issue-28308.rs index 81493f8c45311..b0e04d0f1f6b1 100644 --- a/tests/ui/codemap_tests/issue-28308.rs +++ b/tests/ui/codemap_tests/issue-28308.rs @@ -1,4 +1,16 @@ fn main() { - assert!("foo"); - //~^ ERROR cannot apply unary operator `!` + assert!("foo"); //~ ERROR mismatched types + //~^ NOTE expected `bool`, found `str` + //~| NOTE in this expansion of assert! + let x = Some(&1); + assert!(x); //~ ERROR mismatched types + //~^ NOTE expected `bool`, found `Option<&{integer}>` + //~| NOTE expected enum `bool` + //~| NOTE in this expansion of assert! + //~| NOTE in this expansion of assert! + assert!(x, ""); //~ ERROR mismatched types + //~^ NOTE expected `bool`, found `Option<&{integer}>` + //~| NOTE expected enum `bool` + //~| NOTE in this expansion of assert! + //~| NOTE in this expansion of assert! } diff --git a/tests/ui/codemap_tests/issue-28308.stderr b/tests/ui/codemap_tests/issue-28308.stderr index 7bc9e05dfc024..e84ceb44aacba 100644 --- a/tests/ui/codemap_tests/issue-28308.stderr +++ b/tests/ui/codemap_tests/issue-28308.stderr @@ -1,9 +1,27 @@ -error[E0600]: cannot apply unary operator `!` to type `&'static str` - --> $DIR/issue-28308.rs:2:5 +error[E0308]: mismatched types + --> $DIR/issue-28308.rs:2:13 | LL | assert!("foo"); - | ^^^^^^^^^^^^^^ cannot apply unary operator `!` + | ^^^^^ expected `bool`, found `str` -error: aborting due to 1 previous error +error[E0308]: mismatched types + --> $DIR/issue-28308.rs:6:13 + | +LL | assert!(x); + | ^ expected `bool`, found `Option<&{integer}>` + | + = note: expected enum `bool` + found type `Option<&{integer}>` + +error[E0308]: mismatched types + --> $DIR/issue-28308.rs:11:13 + | +LL | assert!(x, ""); + | ^ expected `bool`, found `Option<&{integer}>` + | + = note: expected enum `bool` + found type `Option<&{integer}>` + +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0600`. +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/consts/control-flow/assert.stderr b/tests/ui/consts/control-flow/assert.stderr index 026097a6ba0e1..deaad6abbccf6 100644 --- a/tests/ui/consts/control-flow/assert.stderr +++ b/tests/ui/consts/control-flow/assert.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/assert.rs:5:15 + --> $DIR/assert.rs:5:23 | LL | const _: () = assert!(false); - | ^^^^^^^^^^^^^^ evaluation of `_` failed here + | ^^^^^ evaluation of `_` failed here error: aborting due to 1 previous error diff --git a/tests/ui/generics/post_monomorphization_error_backtrace.stderr b/tests/ui/generics/post_monomorphization_error_backtrace.stderr index 6953414f0c235..92c7df73638f5 100644 --- a/tests/ui/generics/post_monomorphization_error_backtrace.stderr +++ b/tests/ui/generics/post_monomorphization_error_backtrace.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::() == 0 - --> $DIR/post_monomorphization_error_backtrace.rs:6:23 + --> $DIR/post_monomorphization_error_backtrace.rs:6:31 | LL | const V: () = assert!(std::mem::size_of::() == 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::::V` failed here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::::V` failed here note: erroneous constant encountered --> $DIR/post_monomorphization_error_backtrace.rs:14:5 @@ -17,10 +17,10 @@ LL | assert_zst::() | ^^^^^^^^^^^^^^^^^ error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::() == 0 - --> $DIR/post_monomorphization_error_backtrace.rs:6:23 + --> $DIR/post_monomorphization_error_backtrace.rs:6:31 | LL | const V: () = assert!(std::mem::size_of::() == 0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::::V` failed here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `assert_zst::F::::V` failed here note: erroneous constant encountered --> $DIR/post_monomorphization_error_backtrace.rs:14:5 diff --git a/tests/ui/inline-const/const-expr-generic-err.stderr b/tests/ui/inline-const/const-expr-generic-err.stderr index 26039ba6d4449..e053e88db172f 100644 --- a/tests/ui/inline-const/const-expr-generic-err.stderr +++ b/tests/ui/inline-const/const-expr-generic-err.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: std::mem::size_of::() == 0 - --> $DIR/const-expr-generic-err.rs:4:13 + --> $DIR/const-expr-generic-err.rs:4:21 | LL | const { assert!(std::mem::size_of::() == 0); } - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `foo::::{constant#0}` failed here + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `foo::::{constant#0}` failed here note: erroneous constant encountered --> $DIR/const-expr-generic-err.rs:4:5 diff --git a/tests/ui/macros/assert-macro-lifetimes.rs b/tests/ui/macros/assert-macro-lifetimes.rs new file mode 100644 index 0000000000000..cc25928320477 --- /dev/null +++ b/tests/ui/macros/assert-macro-lifetimes.rs @@ -0,0 +1,8 @@ +//@ check-pass +#[derive(PartialEq, Eq, Hash)] +struct S; +fn main() { + let foo = std::rc::Rc::new(std::cell::RefCell::new(std::collections::HashMap::::new())); + // Ensure that the lifetimes of the borrow do not leak past the end of `main`. + assert!(matches!(foo.borrow().get(&S).unwrap(), S)) +} diff --git a/tests/ui/simd/const-err-trumps-simd-err.stderr b/tests/ui/simd/const-err-trumps-simd-err.stderr index 93d1fce637f2f..6d25a28c92c5c 100644 --- a/tests/ui/simd/const-err-trumps-simd-err.stderr +++ b/tests/ui/simd/const-err-trumps-simd-err.stderr @@ -1,8 +1,8 @@ error[E0080]: evaluation panicked: assertion failed: LANE < 4 - --> $DIR/const-err-trumps-simd-err.rs:17:13 + --> $DIR/const-err-trumps-simd-err.rs:17:21 | LL | const { assert!(LANE < 4); } // the error should be here... - | ^^^^^^^^^^^^^^^^^ evaluation of `get_elem::<4>::{constant#0}` failed here + | ^^^^^^^^ evaluation of `get_elem::<4>::{constant#0}` failed here note: erroneous constant encountered --> $DIR/const-err-trumps-simd-err.rs:17:5 diff --git a/tests/ui/transmutability/uninhabited.stderr b/tests/ui/transmutability/uninhabited.stderr index 4757daec9978a..9f289852809c2 100644 --- a/tests/ui/transmutability/uninhabited.stderr +++ b/tests/ui/transmutability/uninhabited.stderr @@ -41,10 +41,10 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/uninhabited.rs:41:9 + --> $DIR/uninhabited.rs:41:17 | LL | assert!(false); - | ^^^^^^^^^^^^^^ evaluation of `yawning_void_struct::_` failed here + | ^^^^^ evaluation of `yawning_void_struct::_` failed here error[E0277]: `()` cannot be safely transmuted into `yawning_void_enum::Void` --> $DIR/uninhabited.rs:71:41 @@ -68,10 +68,10 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/uninhabited.rs:63:9 + --> $DIR/uninhabited.rs:63:17 | LL | assert!(false); - | ^^^^^^^^^^^^^^ evaluation of `yawning_void_enum::_` failed here + | ^^^^^ evaluation of `yawning_void_enum::_` failed here error[E0277]: `u128` cannot be safely transmuted into `DistantVoid` --> $DIR/uninhabited.rs:92:43 @@ -95,10 +95,10 @@ LL | | }> | |__________^ required by this bound in `is_maybe_transmutable` error[E0080]: evaluation panicked: assertion failed: false - --> $DIR/uninhabited.rs:87:9 + --> $DIR/uninhabited.rs:87:17 | LL | assert!(false); - | ^^^^^^^^^^^^^^ evaluation of `distant_void::_` failed here + | ^^^^^ evaluation of `distant_void::_` failed here error[E0277]: `Src` cannot be safely transmuted into `issue_126267::Error` --> $DIR/uninhabited.rs:108:42 From 605621224f985454d7a3e1185314098863377df0 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas) Chirananthavat" Date: Wed, 13 Aug 2025 11:16:00 +0700 Subject: [PATCH 31/37] Make std use the edition 2024 prelude This seem to have been overlooked in --- library/std/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index fd06a3b540cda..21892ee68027f 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -426,7 +426,7 @@ // to import the prelude implicitly when building crates that depend on std. #[prelude_import] #[allow(unused)] -use prelude::rust_2021::*; +use prelude::rust_2024::*; // Access to Bencher, etc. #[cfg(test)] From ebcbcc8b73cb6fa8aad42e258ca0382db68e7876 Mon Sep 17 00:00:00 2001 From: Cathal Mullan Date: Wed, 13 Aug 2025 13:18:06 +0100 Subject: [PATCH 32/37] bootstrap: Fix jemalloc 64K page support for aarch64 tools --- src/bootstrap/src/core/build_steps/compile.rs | 1 + src/bootstrap/src/core/build_steps/tool.rs | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 4519731ada7f3..edd9492225a98 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1391,6 +1391,7 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS // Build jemalloc on AArch64 with support for page sizes up to 64K // See: https://github.com/rust-lang/rust/pull/135081 + // See also the "JEMALLOC_SYS_WITH_LG_PAGE" setting in the tool build step. if builder.config.jemalloc(target) && target.starts_with("aarch64") && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index aa00cd03c5bb7..0c263120eeec9 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -257,6 +257,14 @@ pub fn prepare_tool_cargo( // own copy cargo.env("LZMA_API_STATIC", "1"); + // Build jemalloc on AArch64 with support for page sizes up to 64K + // See: https://github.com/rust-lang/rust/pull/135081 + // Note that `miri` always uses jemalloc. As such, there is no checking of the jemalloc build flag. + // See also the "JEMALLOC_SYS_WITH_LG_PAGE" setting in the compile build step. + if target.starts_with("aarch64") && env::var_os("JEMALLOC_SYS_WITH_LG_PAGE").is_none() { + cargo.env("JEMALLOC_SYS_WITH_LG_PAGE", "16"); + } + // CFG_RELEASE is needed by rustfmt (and possibly other tools) which // import rustc-ap-rustc_attr which requires this to be set for the // `#[cfg(version(...))]` attribute. From a0eea23317e60ef1b648abd0fe76ca89362ac44d Mon Sep 17 00:00:00 2001 From: Eval EXEC Date: Tue, 12 Aug 2025 18:36:16 +0800 Subject: [PATCH 33/37] doc test: fix mpsc.rs try_send doc test Signed-off-by: Eval EXEC --- library/std/src/sync/mpsc.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library/std/src/sync/mpsc.rs b/library/std/src/sync/mpsc.rs index 41d1dd3ce674b..03d7fddc2faef 100644 --- a/library/std/src/sync/mpsc.rs +++ b/library/std/src/sync/mpsc.rs @@ -697,14 +697,14 @@ impl SyncSender { /// let sync_sender2 = sync_sender.clone(); /// /// // First thread owns sync_sender - /// thread::spawn(move || { + /// let handle1 = thread::spawn(move || { /// sync_sender.send(1).unwrap(); /// sync_sender.send(2).unwrap(); /// // Thread blocked /// }); /// /// // Second thread owns sync_sender2 - /// thread::spawn(move || { + /// let handle2 = thread::spawn(move || { /// // This will return an error and send /// // no message if the buffer is full /// let _ = sync_sender2.try_send(3); @@ -722,6 +722,10 @@ impl SyncSender { /// Ok(msg) => println!("message {msg} received"), /// Err(_) => println!("the third message was never sent"), /// } + /// + /// // Wait for threads to complete + /// handle1.join().unwrap(); + /// handle2.join().unwrap(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn try_send(&self, t: T) -> Result<(), TrySendError> { From d4eb0947f133d8f44594d7551d99a48daff21c02 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Tue, 12 Aug 2025 17:57:02 -0500 Subject: [PATCH 34/37] Cleanup assoc parent utils --- .../src/diagnostics/mutability_errors.rs | 3 +- compiler/rustc_borrowck/src/type_check/mod.rs | 5 +-- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 43 ++++++++----------- .../rustc_const_eval/src/interpret/call.rs | 2 +- .../src/check/compare_impl_item.rs | 6 +-- .../rustc_hir_analysis/src/check/wfcheck.rs | 3 +- compiler/rustc_lint/src/pass_by_value.rs | 6 +-- compiler/rustc_lint/src/types.rs | 5 +-- compiler/rustc_middle/src/middle/privacy.rs | 6 +-- compiler/rustc_middle/src/ty/mod.rs | 39 ++++++++++++++--- .../src/check_call_recursion.rs | 4 +- .../src/check_packed_ref.rs | 2 +- compiler/rustc_mir_transform/src/shim.rs | 2 +- .../rustc_monomorphize/src/partitioning.rs | 13 +++--- compiler/rustc_passes/src/dead.rs | 2 +- .../src/traits/project.rs | 2 +- 16 files changed, 79 insertions(+), 64 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 5d9416b59fcec..c0ca35f9ff83a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -677,12 +677,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { /// - is the trait from the local crate? If not, we can't suggest changing signatures /// - `Span` of the argument in the trait definition fn is_error_in_trait(&self, local: Local) -> (bool, bool, Option) { + let tcx = self.infcx.tcx; if self.body.local_kind(local) != LocalKind::Arg { return (false, false, None); } let my_def = self.body.source.def_id(); let Some(td) = - self.infcx.tcx.impl_of_assoc(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x)) + tcx.trait_impl_of_assoc(my_def).and_then(|id| self.infcx.tcx.trait_id_of_impl(id)) else { return (false, false, None); }; diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index c3aa205d5aab3..a960b96b91c20 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -1773,10 +1773,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { locations, ); - assert!(!matches!( - tcx.impl_of_assoc(def_id).map(|imp| tcx.def_kind(imp)), - Some(DefKind::Impl { of_trait: true }) - )); + assert_eq!(tcx.trait_impl_of_assoc(def_id), None); self.prove_predicates( args.types().map(|ty| ty::ClauseKind::WellFormed(ty.into())), locations, diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index 6cbf2dbf7d3fd..2c3a84499ac55 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -533,31 +533,26 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { // First, let's see if this is a method within an inherent impl. Because // if yes, we want to make the result subroutine DIE a child of the // subroutine's self-type. - if let Some(impl_def_id) = cx.tcx.impl_of_assoc(instance.def_id()) { - // If the method does *not* belong to a trait, proceed - if cx.tcx.trait_id_of_impl(impl_def_id).is_none() { - let impl_self_ty = cx.tcx.instantiate_and_normalize_erasing_regions( - instance.args, - cx.typing_env(), - cx.tcx.type_of(impl_def_id), - ); - - // Only "class" methods are generally understood by LLVM, - // so avoid methods on other types (e.g., `<*mut T>::null`). - if let ty::Adt(def, ..) = impl_self_ty.kind() - && !def.is_box() - { - // Again, only create type information if full debuginfo is enabled - if cx.sess().opts.debuginfo == DebugInfo::Full && !impl_self_ty.has_param() - { - return (type_di_node(cx, impl_self_ty), true); - } else { - return (namespace::item_namespace(cx, def.did()), false); - } + // For trait method impls we still use the "parallel namespace" + // strategy + if let Some(imp_def_id) = cx.tcx.inherent_impl_of_assoc(instance.def_id()) { + let impl_self_ty = cx.tcx.instantiate_and_normalize_erasing_regions( + instance.args, + cx.typing_env(), + cx.tcx.type_of(imp_def_id), + ); + + // Only "class" methods are generally understood by LLVM, + // so avoid methods on other types (e.g., `<*mut T>::null`). + if let ty::Adt(def, ..) = impl_self_ty.kind() + && !def.is_box() + { + // Again, only create type information if full debuginfo is enabled + if cx.sess().opts.debuginfo == DebugInfo::Full && !impl_self_ty.has_param() { + return (type_di_node(cx, impl_self_ty), true); + } else { + return (namespace::item_namespace(cx, def.did()), false); } - } else { - // For trait method impls we still use the "parallel namespace" - // strategy } } diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index b1cc0cc2878aa..21237cfe49216 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -731,7 +731,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { ) { let tcx = *self.tcx; - let trait_def_id = tcx.trait_of_assoc(def_id).unwrap(); + let trait_def_id = tcx.parent(def_id); let virtual_trait_ref = ty::TraitRef::from_assoc(tcx, trait_def_id, virtual_instance.args); let existential_trait_ref = ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref); let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty); diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 6767e5ed88d4d..e482725619398 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -445,10 +445,10 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>( tcx: TyCtxt<'tcx>, impl_m_def_id: LocalDefId, ) -> Result<&'tcx DefIdMap>>, ErrorGuaranteed> { - let impl_m = tcx.opt_associated_item(impl_m_def_id.to_def_id()).unwrap(); - let trait_m = tcx.opt_associated_item(impl_m.trait_item_def_id.unwrap()).unwrap(); + let impl_m = tcx.associated_item(impl_m_def_id.to_def_id()); + let trait_m = tcx.associated_item(impl_m.trait_item_def_id.unwrap()); let impl_trait_ref = - tcx.impl_trait_ref(impl_m.impl_container(tcx).unwrap()).unwrap().instantiate_identity(); + tcx.impl_trait_ref(tcx.parent(impl_m_def_id.to_def_id())).unwrap().instantiate_identity(); // First, check a few of the same things as `compare_impl_method`, // just so we don't ICE during instantiation later. check_method_is_structurally_compatible(tcx, impl_m, trait_m, impl_trait_ref, true)?; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index c642435b9893c..e6a1f6d8d8bb7 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -2287,8 +2287,7 @@ fn lint_redundant_lifetimes<'tcx>( // Proceed } DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst => { - let parent_def_id = tcx.local_parent(owner_id); - if matches!(tcx.def_kind(parent_def_id), DefKind::Impl { of_trait: true }) { + if tcx.trait_impl_of_assoc(owner_id.to_def_id()).is_some() { // Don't check for redundant lifetimes for associated items of trait // implementations, since the signature is required to be compatible // with the trait, even if the implementation implies some lifetimes diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index 4f65acd8001ed..29006732aade5 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -24,10 +24,8 @@ impl<'tcx> LateLintPass<'tcx> for PassByValue { fn check_ty(&mut self, cx: &LateContext<'_>, ty: &'tcx hir::Ty<'tcx, AmbigArg>) { match &ty.kind { TyKind::Ref(_, hir::MutTy { ty: inner_ty, mutbl: hir::Mutability::Not }) => { - if let Some(impl_did) = cx.tcx.impl_of_assoc(ty.hir_id.owner.to_def_id()) { - if cx.tcx.impl_trait_ref(impl_did).is_some() { - return; - } + if cx.tcx.trait_impl_of_assoc(ty.hir_id.owner.to_def_id()).is_some() { + return; } if let Some(t) = path_for_pass_by_value(cx, inner_ty) { cx.emit_span_lint( diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index b0afc333ebe2f..f8a692313f030 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -1904,10 +1904,9 @@ impl InvalidAtomicOrdering { if let ExprKind::MethodCall(method_path, _, args, _) = &expr.kind && recognized_names.contains(&method_path.ident.name) && let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) - && let Some(impl_did) = cx.tcx.impl_of_assoc(m_def_id) - && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() // skip extension traits, only lint functions from the standard library - && cx.tcx.trait_id_of_impl(impl_did).is_none() + && let Some(impl_did) = cx.tcx.inherent_impl_of_assoc(m_def_id) + && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def() && let parent = cx.tcx.parent(adt.did()) && cx.tcx.is_diagnostic_item(sym::atomic_mod, parent) && ATOMIC_TYPES.contains(&cx.tcx.item_name(adt.did())) diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs index 785ddd1ee2980..e3e04c9d1800b 100644 --- a/compiler/rustc_middle/src/middle/privacy.rs +++ b/compiler/rustc_middle/src/middle/privacy.rs @@ -181,11 +181,7 @@ impl EffectiveVisibilities { // nominal visibility. For some items nominal visibility doesn't make sense so we // don't check this condition for them. let is_impl = matches!(tcx.def_kind(def_id), DefKind::Impl { .. }); - let is_associated_item_in_trait_impl = tcx - .impl_of_assoc(def_id.to_def_id()) - .and_then(|impl_id| tcx.trait_id_of_impl(impl_id)) - .is_some(); - if !is_impl && !is_associated_item_in_trait_impl { + if !is_impl && tcx.trait_impl_of_assoc(def_id.to_def_id()).is_none() { let nominal_vis = tcx.visibility(def_id); if !nominal_vis.is_at_least(ev.reachable, tcx) { span_bug!( diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 73e1661106eaf..e70c98ab70425 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1925,21 +1925,50 @@ impl<'tcx> TyCtxt<'tcx> { self.impl_trait_ref(def_id).map(|tr| tr.skip_binder().def_id) } - /// If the given `DefId` is an associated item, returns the `DefId` of the parent trait or impl. - pub fn assoc_parent(self, def_id: DefId) -> Option { - self.def_kind(def_id).is_assoc().then(|| self.parent(def_id)) + /// If the given `DefId` is an associated item, returns the `DefId` and `DefKind` of the parent trait or impl. + pub fn assoc_parent(self, def_id: DefId) -> Option<(DefId, DefKind)> { + if !self.def_kind(def_id).is_assoc() { + return None; + } + let parent = self.parent(def_id); + let def_kind = self.def_kind(parent); + Some((parent, def_kind)) } /// If the given `DefId` is an associated item of a trait, /// returns the `DefId` of the trait; otherwise, returns `None`. pub fn trait_of_assoc(self, def_id: DefId) -> Option { - self.assoc_parent(def_id).filter(|id| self.def_kind(id) == DefKind::Trait) + match self.assoc_parent(def_id) { + Some((id, DefKind::Trait)) => Some(id), + _ => None, + } } /// If the given `DefId` is an associated item of an impl, /// returns the `DefId` of the impl; otherwise returns `None`. pub fn impl_of_assoc(self, def_id: DefId) -> Option { - self.assoc_parent(def_id).filter(|id| matches!(self.def_kind(id), DefKind::Impl { .. })) + match self.assoc_parent(def_id) { + Some((id, DefKind::Impl { .. })) => Some(id), + _ => None, + } + } + + /// If the given `DefId` is an associated item of an inherent impl, + /// returns the `DefId` of the impl; otherwise, returns `None`. + pub fn inherent_impl_of_assoc(self, def_id: DefId) -> Option { + match self.assoc_parent(def_id) { + Some((id, DefKind::Impl { of_trait: false })) => Some(id), + _ => None, + } + } + + /// If the given `DefId` is an associated item of a trait impl, + /// returns the `DefId` of the impl; otherwise, returns `None`. + pub fn trait_impl_of_assoc(self, def_id: DefId) -> Option { + match self.assoc_parent(def_id) { + Some((id, DefKind::Impl { of_trait: true })) => Some(id), + _ => None, + } } pub fn is_exportable(self, def_id: DefId) -> bool { diff --git a/compiler/rustc_mir_transform/src/check_call_recursion.rs b/compiler/rustc_mir_transform/src/check_call_recursion.rs index 6d61ac2dd8037..a9acb1da5a3e0 100644 --- a/compiler/rustc_mir_transform/src/check_call_recursion.rs +++ b/compiler/rustc_mir_transform/src/check_call_recursion.rs @@ -43,8 +43,8 @@ impl<'tcx> MirLint<'tcx> for CheckDropRecursion { // First check if `body` is an `fn drop()` of `Drop` if let DefKind::AssocFn = tcx.def_kind(def_id) - && let Some(trait_ref) = - tcx.impl_of_assoc(def_id.to_def_id()).and_then(|def_id| tcx.impl_trait_ref(def_id)) + && let Some(impl_id) = tcx.trait_impl_of_assoc(def_id.to_def_id()) + && let trait_ref = tcx.impl_trait_ref(impl_id).unwrap() && tcx.is_lang_item(trait_ref.instantiate_identity().def_id, LangItem::Drop) // avoid erroneous `Drop` impls from causing ICEs below && let sig = tcx.fn_sig(def_id).instantiate_identity() diff --git a/compiler/rustc_mir_transform/src/check_packed_ref.rs b/compiler/rustc_mir_transform/src/check_packed_ref.rs index dcb812c78993e..100104e9de03e 100644 --- a/compiler/rustc_mir_transform/src/check_packed_ref.rs +++ b/compiler/rustc_mir_transform/src/check_packed_ref.rs @@ -40,7 +40,7 @@ impl<'tcx> Visitor<'tcx> for PackedRefChecker<'_, 'tcx> { if context.is_borrow() && util::is_disaligned(self.tcx, self.body, self.typing_env, *place) { let def_id = self.body.source.instance.def_id(); - if let Some(impl_def_id) = self.tcx.impl_of_assoc(def_id) + if let Some(impl_def_id) = self.tcx.trait_impl_of_assoc(def_id) && self.tcx.is_builtin_derived(impl_def_id) { // If we ever reach here it means that the generated derive diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index c687036f544dc..c6760b3583f20 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -75,7 +75,7 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< build_call_shim(tcx, instance, Some(adjustment), CallKind::Direct(def_id)) } ty::InstanceKind::FnPtrShim(def_id, ty) => { - let trait_ = tcx.trait_of_assoc(def_id).unwrap(); + let trait_ = tcx.parent(def_id); // Supports `Fn` or `async Fn` traits. let adjustment = match tcx .fn_trait_kind_from_def_id(trait_) diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index d76b27d9970b6..54d77b182f8d8 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -650,17 +650,18 @@ fn characteristic_def_id_of_mono_item<'tcx>( // its self-type. If the self-type does not provide a characteristic // DefId, we use the location of the impl after all. - if tcx.trait_of_assoc(def_id).is_some() { + let assoc_parent = tcx.assoc_parent(def_id); + + if let Some((_, DefKind::Trait)) = assoc_parent { let self_ty = instance.args.type_at(0); // This is a default implementation of a trait method. return characteristic_def_id_of_type(self_ty).or(Some(def_id)); } - if let Some(impl_def_id) = tcx.impl_of_assoc(def_id) { - if tcx.sess.opts.incremental.is_some() - && tcx - .trait_id_of_impl(impl_def_id) - .is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::Drop)) + if let Some((impl_def_id, DefKind::Impl { of_trait })) = assoc_parent { + if of_trait + && tcx.sess.opts.incremental.is_some() + && tcx.is_lang_item(tcx.trait_id_of_impl(impl_def_id).unwrap(), LangItem::Drop) { // Put `Drop::drop` into the same cgu as `drop_in_place` // since `drop_in_place` is the only thing that can diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index de52973acbb4a..08d06402000ae 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -371,7 +371,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { /// will be ignored for the purposes of dead code analysis (see PR #85200 /// for discussion). fn should_ignore_item(&mut self, def_id: DefId) -> bool { - if let Some(impl_of) = self.tcx.impl_of_assoc(def_id) { + if let Some(impl_of) = self.tcx.trait_impl_of_assoc(def_id) { if !self.tcx.is_automatically_derived(impl_of) { return false; } diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 581191b2036d2..884d53732fe2a 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1507,7 +1507,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>( let tcx = selcx.tcx(); let self_ty = obligation.predicate.self_ty(); let item_def_id = obligation.predicate.def_id; - let trait_def_id = tcx.trait_of_assoc(item_def_id).unwrap(); + let trait_def_id = tcx.parent(item_def_id); let args = tcx.mk_args(&[self_ty.into()]); let (term, obligations) = if tcx.is_lang_item(trait_def_id, LangItem::DiscriminantKind) { let discriminant_def_id = From 12d1665d1163d62de0dd2c2fcb2f508fea8123f1 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Wed, 13 Aug 2025 23:17:29 +0800 Subject: [PATCH 35/37] Add test suggest-add-wrapper-issue-145294 Signed-off-by: xizheyin --- .../suggest-add-wrapper-issue-145294.rs | 26 +++++++++++++++++ .../suggest-add-wrapper-issue-145294.stderr | 29 +++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/ui/typeck/suggestions/suggest-add-wrapper-issue-145294.rs create mode 100644 tests/ui/typeck/suggestions/suggest-add-wrapper-issue-145294.stderr diff --git a/tests/ui/typeck/suggestions/suggest-add-wrapper-issue-145294.rs b/tests/ui/typeck/suggestions/suggest-add-wrapper-issue-145294.rs new file mode 100644 index 0000000000000..cfe167cf88d76 --- /dev/null +++ b/tests/ui/typeck/suggestions/suggest-add-wrapper-issue-145294.rs @@ -0,0 +1,26 @@ +// Suppress the suggestion that adding a wrapper. +// When expected_ty and expr_ty are the same ADT, +// we prefer to compare their internal generic params, +// so when the current variant corresponds to an unresolved infer, +// the suggestion is rejected. +// e.g. `Ok(Some("hi"))` is type of `Result, _>`, +// where `E` is still an unresolved inference variable. + +fn foo() -> Result, ()> { + todo!() +} + +#[derive(PartialEq, Debug)] +enum Bar { + A(T), + B(E), +} + +fn bar() -> Bar { + todo!() +} + +fn main() { + assert_eq!(Ok(Some("hi")), foo()); //~ ERROR mismatched types [E0308] + assert_eq!(Bar::A("hi"), bar()); //~ ERROR mismatched types [E0308] +} diff --git a/tests/ui/typeck/suggestions/suggest-add-wrapper-issue-145294.stderr b/tests/ui/typeck/suggestions/suggest-add-wrapper-issue-145294.stderr new file mode 100644 index 0000000000000..ef5eeb29cf504 --- /dev/null +++ b/tests/ui/typeck/suggestions/suggest-add-wrapper-issue-145294.stderr @@ -0,0 +1,29 @@ +error[E0308]: mismatched types + --> $DIR/suggest-add-wrapper-issue-145294.rs:24:32 + | +LL | assert_eq!(Ok(Some("hi")), foo()); + | ^^^^^ expected `Result, _>`, found `Result, ()>` + | + = note: expected enum `Result, _>` + found enum `Result, ()>` +help: try wrapping the expression in `Err` + | +LL | assert_eq!(Ok(Some("hi")), Err(foo())); + | ++++ + + +error[E0308]: mismatched types + --> $DIR/suggest-add-wrapper-issue-145294.rs:25:30 + | +LL | assert_eq!(Bar::A("hi"), bar()); + | ^^^^^ expected `Bar<&str, _>`, found `Bar` + | + = note: expected enum `Bar<&str, _>` + found enum `Bar` +help: try wrapping the expression in `Bar::B` + | +LL | assert_eq!(Bar::A("hi"), Bar::B(bar())); + | +++++++ + + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. From e0cc2beea3d52ce34b16df9730b65a670c68c623 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Wed, 13 Aug 2025 23:23:18 +0800 Subject: [PATCH 36/37] Suppress wrapper suggestion when expected and actual ty are the same adt and the variant is unresolved Signed-off-by: xizheyin --- compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 9 +++++++++ .../suggestions/suggest-add-wrapper-issue-145294.stderr | 8 -------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 2345cdab208e3..c44e007dbdb82 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2378,6 +2378,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .filter_map(|variant| { let sole_field = &variant.single_field(); + // When expected_ty and expr_ty are the same ADT, we prefer to compare their internal generic params, + // When the current variant has a sole field whose type is still an unresolved inference variable, + // suggestions would be often wrong. So suppress the suggestion. See #145294. + if let (ty::Adt(exp_adt, _), ty::Adt(act_adt, _)) = (expected.kind(), expr_ty.kind()) + && exp_adt.did() == act_adt.did() + && sole_field.ty(self.tcx, args).is_ty_var() { + return None; + } + let field_is_local = sole_field.did.is_local(); let field_is_accessible = sole_field.vis.is_accessible_from(expr.hir_id.owner.def_id, self.tcx) diff --git a/tests/ui/typeck/suggestions/suggest-add-wrapper-issue-145294.stderr b/tests/ui/typeck/suggestions/suggest-add-wrapper-issue-145294.stderr index ef5eeb29cf504..5e4ad13221046 100644 --- a/tests/ui/typeck/suggestions/suggest-add-wrapper-issue-145294.stderr +++ b/tests/ui/typeck/suggestions/suggest-add-wrapper-issue-145294.stderr @@ -6,10 +6,6 @@ LL | assert_eq!(Ok(Some("hi")), foo()); | = note: expected enum `Result, _>` found enum `Result, ()>` -help: try wrapping the expression in `Err` - | -LL | assert_eq!(Ok(Some("hi")), Err(foo())); - | ++++ + error[E0308]: mismatched types --> $DIR/suggest-add-wrapper-issue-145294.rs:25:30 @@ -19,10 +15,6 @@ LL | assert_eq!(Bar::A("hi"), bar()); | = note: expected enum `Bar<&str, _>` found enum `Bar` -help: try wrapping the expression in `Bar::B` - | -LL | assert_eq!(Bar::A("hi"), Bar::B(bar())); - | +++++++ + error: aborting due to 2 previous errors From 7b5d9efb7d0833e7d9b3ca8690ec2b8173ee499c Mon Sep 17 00:00:00 2001 From: Urgau Date: Wed, 11 Jun 2025 18:23:06 +0200 Subject: [PATCH 37/37] Improve `--remap-path-prefix` documentation Co-Authored-By: Weihang Lo Co-Authored-By: Wesley Wiser --- src/doc/rustc/src/SUMMARY.md | 1 + src/doc/rustc/src/command-line-arguments.md | 21 +++----- src/doc/rustc/src/remap-source-paths.md | 53 +++++++++++++++++++++ 3 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 src/doc/rustc/src/remap-source-paths.md diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md index 25f154f11807c..9efcbe82f6325 100644 --- a/src/doc/rustc/src/SUMMARY.md +++ b/src/doc/rustc/src/SUMMARY.md @@ -23,6 +23,7 @@ - [Linker-plugin-based LTO](linker-plugin-lto.md) - [Checking Conditional Configurations](check-cfg.md) - [Cargo Specifics](check-cfg/cargo-specifics.md) +- [Remap source paths](remap-source-paths.md) - [Exploit Mitigations](exploit-mitigations.md) - [Symbol Mangling](symbol-mangling/index.md) - [v0 Symbol Format](symbol-mangling/v0.md) diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md index d45ad1be27b8c..0b15fbc24dfc2 100644 --- a/src/doc/rustc/src/command-line-arguments.md +++ b/src/doc/rustc/src/command-line-arguments.md @@ -418,22 +418,15 @@ This flag takes a number that specifies the width of the terminal in characters. Formatting of diagnostics will take the width into consideration to make them better fit on the screen. -## `--remap-path-prefix`: remap source names in output +## `--remap-path-prefix`: remap source paths in output Remap source path prefixes in all output, including compiler diagnostics, -debug information, macro expansions, etc. It takes a value of the form -`FROM=TO` where a path prefix equal to `FROM` is rewritten to the value `TO`. -The `FROM` may itself contain an `=` symbol, but the `TO` value may not. This -flag may be specified multiple times. - -This is useful for normalizing build products, for example by removing the -current directory out of pathnames emitted into the object files. The -replacement is purely textual, with no consideration of the current system's -pathname syntax. For example `--remap-path-prefix foo=bar` will match -`foo/lib.rs` but not `./foo/lib.rs`. - -When multiple remappings are given and several of them match, the **last** -matching one is applied. +debug information, macro expansions, etc. It takes a value of the form `FROM=TO` +where a path prefix equal to `FROM` is rewritten to the value `TO`. This flag may be +specified multiple times. + +Refer to the [Remap source paths](remap-source-paths.md) section of this book for +further details and explanation. ## `--json`: configure json messages printed by the compiler diff --git a/src/doc/rustc/src/remap-source-paths.md b/src/doc/rustc/src/remap-source-paths.md new file mode 100644 index 0000000000000..03f5d98091cc6 --- /dev/null +++ b/src/doc/rustc/src/remap-source-paths.md @@ -0,0 +1,53 @@ +# Remap source paths + +`rustc` supports remapping source paths prefixes **as a best effort** in all compiler generated +output, including compiler diagnostics, debugging information, macro expansions, etc. + +This is useful for normalizing build products, for example by removing the current directory +out of the paths emitted into object files. + +The remapping is done via the `--remap-path-prefix` option. + +## `--remap-path-prefix` + +It takes a value of the form `FROM=TO` where a path prefix equal to `FROM` is rewritten +to the value `TO`. `FROM` may itself contain an `=` symbol, but `TO` value may not. + +The replacement is purely textual, with no consideration of the current system's path separator. + +When multiple remappings are given and several of them match, the **last** matching one is applied. + +### Example + +```bash +rustc --remap-path-prefix "/home/user/project=/redacted" +``` + +This example replaces all occurrences of `/home/user/project` in emitted paths with `/redacted`. + +## Caveats and Limitations + +### Linkers generated paths + +On some platforms like `x86_64-pc-windows-msvc`, the linker may embed absolute host paths and compiler +arguments into debug info files (like `.pdb`) independently of `rustc`. + +Additionally, on Apple platforms, linkers generate [OSO entries] which are not remapped by the compiler +and need to be manually remapped with `-oso_prefix`. + +The `--remap-path-prefix` option does not affect these linker-generated paths. + +### Textual replacement only + +The remapping is strictly textual and does not account for different path separator conventions across +platforms. Care must be taken when specifying prefixes, especially on Windows where both `/` and `\` may +appear in paths. + +### External tools + +Paths introduced by external tools or environment variables may not be covered by `--remap-path-prefix` +unless explicitly accounted for. + +For example, generated code introduced by Cargo's build script may still contain un-remapped paths. + +[OSO entries]: https://wiki.dwarfstd.org/Apple%27s_%22Lazy%22_DWARF_Scheme.md