From b21c9e7bfb0180b67b486013a7137fb200cb1076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= <69964857+Sa4dUs@users.noreply.github.com> Date: Tue, 6 May 2025 09:19:33 +0200 Subject: [PATCH 01/26] Split `autodiff` into `autodiff_forward` and `autodiff_reverse` Pending fix. ``` error: cannot find a built-in macro with name `autodiff_forward` --> library\core\src\macros\mod.rs:1542:5 | 1542 | / pub macro autodiff_forward($item:item) { 1543 | | /* compiler built-in */ 1544 | | } | |_____^ error: cannot find a built-in macro with name `autodiff_reverse` --> library\core\src\macros\mod.rs:1549:5 | 1549 | / pub macro autodiff_reverse($item:item) { 1550 | | /* compiler built-in */ 1551 | | } | |_____^ error: could not compile `core` (lib) due to 2 previous errors ``` --- compiler/rustc_builtin_macros/src/autodiff.rs | 41 +++++++++++++------ compiler/rustc_builtin_macros/src/lib.rs | 3 +- compiler/rustc_passes/src/check_attr.rs | 2 +- compiler/rustc_span/src/symbol.rs | 3 +- library/core/src/lib.rs | 2 +- library/core/src/macros/mod.rs | 14 +++++++ 6 files changed, 48 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 1ff4fc6aaab22..8073de15925e6 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -88,25 +88,20 @@ mod llvm_enzyme { has_ret: bool, ) -> AutoDiffAttrs { let dcx = ecx.sess.dcx(); - let mode = name(&meta_item[1]); - let Ok(mode) = DiffMode::from_str(&mode) else { - dcx.emit_err(errors::AutoDiffInvalidMode { span: meta_item[1].span(), mode }); - return AutoDiffAttrs::error(); - }; // Now we check, whether the user wants autodiff in batch/vector mode, or scalar mode. // If he doesn't specify an integer (=width), we default to scalar mode, thus width=1. - let mut first_activity = 2; + let mut first_activity = 1; - let width = if let [_, _, x, ..] = &meta_item[..] + let width = if let [_, x, ..] = &meta_item[..] && let Some(x) = width(x) { - first_activity = 3; + first_activity = 2; match x.try_into() { Ok(x) => x, Err(_) => { dcx.emit_err(errors::AutoDiffInvalidWidth { - span: meta_item[2].span(), + span: meta_item[1].span(), width: x, }); return AutoDiffAttrs::error(); @@ -150,7 +145,7 @@ mod llvm_enzyme { }; AutoDiffAttrs { - mode, + mode: DiffMode::Error, width, ret_activity: *ret_activity, input_activity: input_activity.to_vec(), @@ -165,6 +160,24 @@ mod llvm_enzyme { ts.push(TokenTree::Token(comma.clone(), Spacing::Alone)); } + pub(crate) fn expand_forward( + ecx: &mut ExtCtxt<'_>, + expand_span: Span, + meta_item: &ast::MetaItem, + item: Annotatable, + ) -> Vec { + expand_with_mode(ecx, expand_span, meta_item, item, DiffMode::Forward) + } + + pub(crate) fn expand_reverse( + ecx: &mut ExtCtxt<'_>, + expand_span: Span, + meta_item: &ast::MetaItem, + item: Annotatable, + ) -> Vec { + expand_with_mode(ecx, expand_span, meta_item, item, DiffMode::Reverse) + } + /// We expand the autodiff macro to generate a new placeholder function which passes /// type-checking and can be called by users. The function body of the placeholder function will /// later be replaced on LLVM-IR level, so the design of the body is less important and for now @@ -198,11 +211,12 @@ mod llvm_enzyme { /// ``` /// FIXME(ZuseZ4): Once autodiff is enabled by default, make this a doc comment which is checked /// in CI. - pub(crate) fn expand( + pub(crate) fn expand_with_mode( ecx: &mut ExtCtxt<'_>, expand_span: Span, meta_item: &ast::MetaItem, mut item: Annotatable, + mode: DiffMode, ) -> Vec { if cfg!(not(llvm_enzyme)) { ecx.sess.dcx().emit_err(errors::AutoDiffSupportNotBuild { span: meta_item.span }); @@ -289,7 +303,8 @@ mod llvm_enzyme { ts.pop(); let ts: TokenStream = TokenStream::from_iter(ts); - let x: AutoDiffAttrs = from_ast(ecx, &meta_item_vec, has_ret); + let mut x: AutoDiffAttrs = from_ast(ecx, &meta_item_vec, has_ret); + x.mode = mode; if !x.is_active() { // We encountered an error, so we return the original item. // This allows us to potentially parse other attributes. @@ -1017,4 +1032,4 @@ mod llvm_enzyme { } } -pub(crate) use llvm_enzyme::expand; +pub(crate) use llvm_enzyme::{expand_forward, expand_reverse}; diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index 9cd4d17059a0f..a89b3642f7e53 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -112,7 +112,8 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { register_attr! { alloc_error_handler: alloc_error_handler::expand, - autodiff: autodiff::expand, + autodiff_forward: autodiff::expand_forward, + autodiff_reverse: autodiff::expand_reverse, bench: test::expand_bench, cfg_accessible: cfg_accessible::Expander, cfg_eval: cfg_eval::expand, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 5c0d0cf47969a..5aff24f7aa0c1 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -255,7 +255,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { self.check_generic_attr(hir_id, attr, target, Target::Fn); self.check_proc_macro(hir_id, target, ProcMacroKind::Derive) } - [sym::autodiff, ..] => { + [sym::autodiff_forward, ..] | [sym::autodiff_reverse, ..] => { self.check_autodiff(hir_id, attr, span, target) } [sym::coroutine, ..] => { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index efae6250b0720..bb19b5761bb19 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -531,7 +531,8 @@ symbols! { audit_that, augmented_assignments, auto_traits, - autodiff, + autodiff_forward, + autodiff_reverse, automatically_derived, avx, avx10_target_feature, diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index e605d7e0d7844..aaa8c872f985c 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -229,7 +229,7 @@ pub mod assert_matches { /// Unstable module containing the unstable `autodiff` macro. pub mod autodiff { #[unstable(feature = "autodiff", issue = "124509")] - pub use crate::macros::builtin::autodiff; + pub use crate::macros::builtin::{autodiff_forward, autodiff_reverse}; } #[unstable(feature = "contracts", issue = "128044")] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 7dc8c060cd5bc..dc50ad6a09076 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1536,6 +1536,20 @@ pub(crate) mod builtin { /* compiler built-in */ } + #[unstable(feature = "autodiff", issue = "124509")] + #[allow_internal_unstable(rustc_attrs)] + #[rustc_builtin_macro] + pub macro autodiff_forward($item:item) { + /* compiler built-in */ + } + + #[unstable(feature = "autodiff", issue = "124509")] + #[allow_internal_unstable(rustc_attrs)] + #[rustc_builtin_macro] + pub macro autodiff_reverse($item:item) { + /* compiler built-in */ + } + /// Asserts that a boolean expression is `true` at runtime. /// /// This will invoke the [`panic!`] macro if the provided expression cannot be From 9efad3a87bb69ce62f9a5b1703e1b8decfe5c4a6 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Sun, 27 Apr 2025 16:18:29 +0200 Subject: [PATCH 02/26] Add data_ptr method to Mutex and RwLock --- library/std/src/sync/poison/mutex.rs | 6 ++++++ library/std/src/sync/poison/rwlock.rs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 1c29c619edc3a..372bf0a302929 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -608,6 +608,12 @@ impl Mutex { let data = self.data.get_mut(); poison::map_result(self.poison.borrow(), |()| data) } + + /// Returns a raw pointer to the underlying data. + #[unstable(feature = "mutex_data_ptr", issue = "140368")] + pub fn data_ptr(&self) -> *mut T { + self.data.get() + } } #[stable(feature = "mutex_from", since = "1.24.0")] diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index 6976c0a64e23f..0b32a2f1be153 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -634,6 +634,12 @@ impl RwLock { let data = self.data.get_mut(); poison::map_result(self.poison.borrow(), |()| data) } + + /// Returns a raw pointer to the underlying data. + #[unstable(feature = "rwlock_data_ptr", issue = "140368")] + pub fn data_ptr(&self) -> *mut T { + self.data.get() + } } #[stable(feature = "rust1", since = "1.0.0")] From 9d6c5a88a18405eb53e91645d35371ab089f0a37 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 21 May 2025 08:05:44 +0200 Subject: [PATCH 03/26] Add more docs to new data_ptr methods --- library/std/src/sync/poison/mutex.rs | 5 +++++ library/std/src/sync/poison/rwlock.rs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/library/std/src/sync/poison/mutex.rs b/library/std/src/sync/poison/mutex.rs index 372bf0a302929..30325be685c32 100644 --- a/library/std/src/sync/poison/mutex.rs +++ b/library/std/src/sync/poison/mutex.rs @@ -610,6 +610,11 @@ impl Mutex { } /// Returns a raw pointer to the underlying data. + /// + /// The returned pointer is always non-null and properly aligned, but it is + /// the user's responsibility to ensure that any reads and writes through it + /// are properly synchronized to avoid data races, and that it is not read + /// or written through after the mutex is dropped. #[unstable(feature = "mutex_data_ptr", issue = "140368")] pub fn data_ptr(&self) -> *mut T { self.data.get() diff --git a/library/std/src/sync/poison/rwlock.rs b/library/std/src/sync/poison/rwlock.rs index 0b32a2f1be153..a060e2ea57a7b 100644 --- a/library/std/src/sync/poison/rwlock.rs +++ b/library/std/src/sync/poison/rwlock.rs @@ -636,6 +636,11 @@ impl RwLock { } /// Returns a raw pointer to the underlying data. + /// + /// The returned pointer is always non-null and properly aligned, but it is + /// the user's responsibility to ensure that any reads and writes through it + /// are properly synchronized to avoid data races, and that it is not read + /// or written through after the lock is dropped. #[unstable(feature = "rwlock_data_ptr", issue = "140368")] pub fn data_ptr(&self) -> *mut T { self.data.get() From 20589bd6050ff9596348c751231ac84b7538adb4 Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 21 May 2025 08:06:04 +0200 Subject: [PATCH 04/26] Add ReentrantLock::data_ptr --- library/std/src/sync/reentrant_lock.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs index 96a4cf12659cc..727252f03a24e 100644 --- a/library/std/src/sync/reentrant_lock.rs +++ b/library/std/src/sync/reentrant_lock.rs @@ -349,6 +349,17 @@ impl ReentrantLock { } } + /// Returns a raw pointer to the underlying data. + /// + /// The returned pointer is always non-null and properly aligned, but it is + /// the user's responsibility to ensure that any reads through it are + /// properly synchronized to avoid data races, and that it is not read + /// through after the lock is dropped. + #[unstable(feature = "reentrant_lock_data_ptr", issue = "140368")] + pub fn data_ptr(&self) -> *const T { + &raw const self.data + } + unsafe fn increment_lock_count(&self) -> Option<()> { unsafe { *self.lock_count.get() = (*self.lock_count.get()).checked_add(1)?; From b725cf6af8c5a709828d649c86185b30872219ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Sat, 10 May 2025 00:11:06 +0000 Subject: [PATCH 05/26] Disable autodiff bootstrapping --- compiler/rustc_builtin_macros/src/errors.rs | 8 ----- compiler/rustc_builtin_macros/src/lib.rs | 2 +- library/core/src/lib.rs | 1 + library/core/src/macros/mod.rs | 40 ++++++++++++--------- library/std/src/lib.rs | 7 ++-- 5 files changed, 30 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index b28f7d312d937..b3cc460abbfc8 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -187,14 +187,6 @@ mod autodiff { pub(crate) act: String, } - #[derive(Diagnostic)] - #[diag(builtin_macros_autodiff_mode)] - pub(crate) struct AutoDiffInvalidMode { - #[primary_span] - pub(crate) span: Span, - pub(crate) mode: String, - } - #[derive(Diagnostic)] #[diag(builtin_macros_autodiff_width)] pub(crate) struct AutoDiffInvalidWidth { diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index a89b3642f7e53..b16f3cff5cfb1 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -5,10 +5,10 @@ #![allow(internal_features)] #![allow(rustc::diagnostic_outside_of_impl)] #![allow(rustc::untranslatable_diagnostic)] +#![cfg_attr(not(bootstrap), feature(autodiff))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] #![feature(assert_matches)] -#![feature(autodiff)] #![feature(box_patterns)] #![feature(decl_macro)] #![feature(if_let_guard)] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index aaa8c872f985c..e1982116994b4 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -226,6 +226,7 @@ pub mod assert_matches { // We don't export this through #[macro_export] for now, to avoid breakage. #[unstable(feature = "autodiff", issue = "124509")] +#[cfg(not(bootstrap))] /// Unstable module containing the unstable `autodiff` macro. pub mod autodiff { #[unstable(feature = "autodiff", issue = "124509")] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index dc50ad6a09076..99a4ab52b6c57 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1519,33 +1519,39 @@ pub(crate) mod builtin { ($file:expr $(,)?) => {{ /* compiler built-in */ }}; } - /// Automatic Differentiation macro which allows generating a new function to compute - /// the derivative of a given function. It may only be applied to a function. - /// The expected usage syntax is - /// `#[autodiff(NAME, MODE, INPUT_ACTIVITIES, OUTPUT_ACTIVITY)]` - /// where: - /// NAME is a string that represents a valid function name. - /// MODE is any of Forward, Reverse, ForwardFirst, ReverseFirst. - /// INPUT_ACTIVITIES consists of one valid activity for each input parameter. - /// OUTPUT_ACTIVITY must not be set if we implicitly return nothing (or explicitly return - /// `-> ()`). Otherwise it must be set to one of the allowed activities. - #[unstable(feature = "autodiff", issue = "124509")] - #[allow_internal_unstable(rustc_attrs)] - #[rustc_builtin_macro] - pub macro autodiff($item:item) { - /* compiler built-in */ - } - + /// the derivative of a given function in the forward mode of differentiation. + /// It may only be applied to a function. + /// + /// The expected usage syntax is: + /// `#[autodiff_forward(NAME, INPUT_ACTIVITIES, OUTPUT_ACTIVITY)]` + /// + /// - `NAME`: A string that represents a valid function name. + /// - `INPUT_ACTIVITIES`: Specifies one valid activity for each input parameter. + /// - `OUTPUT_ACTIVITY`: Must not be set if the function implicitly returns nothing + /// (or explicitly returns `-> ()`). Otherwise, it must be set to one of the allowed activities. #[unstable(feature = "autodiff", issue = "124509")] #[allow_internal_unstable(rustc_attrs)] #[rustc_builtin_macro] + #[cfg(not(bootstrap))] pub macro autodiff_forward($item:item) { /* compiler built-in */ } + /// Automatic Differentiation macro which allows generating a new function to compute + /// the derivative of a given function in the reverse mode of differentiation. + /// It may only be applied to a function. + /// + /// The expected usage syntax is: + /// `#[autodiff_reverse(NAME, INPUT_ACTIVITIES, OUTPUT_ACTIVITY)]` + /// + /// - `NAME`: A string that represents a valid function name. + /// - `INPUT_ACTIVITIES`: Specifies one valid activity for each input parameter. + /// - `OUTPUT_ACTIVITY`: Must not be set if the function implicitly returns nothing + /// (or explicitly returns `-> ()`). Otherwise, it must be set to one of the allowed activities. #[unstable(feature = "autodiff", issue = "124509")] #[allow_internal_unstable(rustc_attrs)] #[rustc_builtin_macro] + #[cfg(not(bootstrap))] pub macro autodiff_reverse($item:item) { /* compiler built-in */ } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index ef41b47384d61..0ac674d989060 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -276,12 +276,12 @@ // tidy-alphabetical-start // stabilization was reverted after it hit beta +#![cfg_attr(not(bootstrap), feature(autodiff))] #![feature(alloc_error_handler)] #![feature(allocator_internals)] #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] #![feature(asm_experimental_arch)] -#![feature(autodiff)] #![feature(cfg_sanitizer_cfi)] #![feature(cfg_target_thread_local)] #![feature(cfi_encoding)] @@ -636,12 +636,15 @@ pub mod simd { #[doc(inline)] pub use crate::std_float::StdFloat; } + #[unstable(feature = "autodiff", issue = "124509")] +#[cfg(not(bootstrap))] /// This module provides support for automatic differentiation. pub mod autodiff { /// This macro handles automatic differentiation. - pub use core::autodiff::autodiff; + pub use core::autodiff::{autodiff_forward, autodiff_reverse}; } + #[stable(feature = "futures_api", since = "1.36.0")] pub mod task { //! Types and Traits for working with asynchronous tasks. From 2041de7083c114025646220068d3943be517af4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Sat, 10 May 2025 00:37:15 +0000 Subject: [PATCH 06/26] Update codegen and pretty tests UI tests are pending, will depend on error messages change. --- tests/codegen/autodiff/batched.rs | 8 +++--- tests/codegen/autodiff/identical_fnc.rs | 6 ++--- tests/codegen/autodiff/inline.rs | 4 +-- tests/codegen/autodiff/scalar.rs | 4 +-- tests/codegen/autodiff/sret.rs | 4 +-- tests/pretty/autodiff/autodiff_forward.pp | 2 +- tests/pretty/autodiff/autodiff_forward.rs | 30 +++++++++++------------ tests/pretty/autodiff/autodiff_reverse.pp | 2 +- tests/pretty/autodiff/autodiff_reverse.rs | 12 ++++----- tests/pretty/autodiff/inherent_impl.pp | 2 +- tests/pretty/autodiff/inherent_impl.rs | 4 +-- 11 files changed, 39 insertions(+), 39 deletions(-) diff --git a/tests/codegen/autodiff/batched.rs b/tests/codegen/autodiff/batched.rs index e004711640553..d27aed50e6cc4 100644 --- a/tests/codegen/autodiff/batched.rs +++ b/tests/codegen/autodiff/batched.rs @@ -11,11 +11,11 @@ #![feature(autodiff)] -use std::autodiff::autodiff; +use std::autodiff::autodiff_forward; -#[autodiff(d_square3, Forward, Dual, DualOnly)] -#[autodiff(d_square2, Forward, 4, Dual, DualOnly)] -#[autodiff(d_square1, Forward, 4, Dual, Dual)] +#[autodiff_forward(d_square3, Dual, DualOnly)] +#[autodiff_forward(d_square2, 4, Dual, DualOnly)] +#[autodiff_forward(d_square1, 4, Dual, Dual)] #[no_mangle] fn square(x: &f32) -> f32 { x * x diff --git a/tests/codegen/autodiff/identical_fnc.rs b/tests/codegen/autodiff/identical_fnc.rs index 1c3277f52b488..1c25b3d09ab0d 100644 --- a/tests/codegen/autodiff/identical_fnc.rs +++ b/tests/codegen/autodiff/identical_fnc.rs @@ -11,14 +11,14 @@ // identical function calls in the LLVM-IR, while having two different calls in the Rust code. #![feature(autodiff)] -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; -#[autodiff(d_square, Reverse, Duplicated, Active)] +#[autodiff_reverse(d_square, Duplicated, Active)] fn square(x: &f64) -> f64 { x * x } -#[autodiff(d_square2, Reverse, Duplicated, Active)] +#[autodiff_reverse(d_square2, Duplicated, Active)] fn square2(x: &f64) -> f64 { x * x } diff --git a/tests/codegen/autodiff/inline.rs b/tests/codegen/autodiff/inline.rs index e90faa4aa3802..65bed170207cc 100644 --- a/tests/codegen/autodiff/inline.rs +++ b/tests/codegen/autodiff/inline.rs @@ -4,9 +4,9 @@ #![feature(autodiff)] -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; -#[autodiff(d_square, Reverse, Duplicated, Active)] +#[autodiff_reverse(d_square, Duplicated, Active)] fn square(x: &f64) -> f64 { x * x } diff --git a/tests/codegen/autodiff/scalar.rs b/tests/codegen/autodiff/scalar.rs index 85358f5fcb693..096b4209e84ad 100644 --- a/tests/codegen/autodiff/scalar.rs +++ b/tests/codegen/autodiff/scalar.rs @@ -3,9 +3,9 @@ //@ needs-enzyme #![feature(autodiff)] -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; -#[autodiff(d_square, Reverse, Duplicated, Active)] +#[autodiff_reverse(d_square, Duplicated, Active)] #[no_mangle] fn square(x: &f64) -> f64 { x * x diff --git a/tests/codegen/autodiff/sret.rs b/tests/codegen/autodiff/sret.rs index 5ead90041edc3..d2fa85e3e3787 100644 --- a/tests/codegen/autodiff/sret.rs +++ b/tests/codegen/autodiff/sret.rs @@ -9,10 +9,10 @@ #![feature(autodiff)] -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; #[no_mangle] -#[autodiff(df, Reverse, Active, Active, Active)] +#[autodiff_reverse(df, Active, Active, Active)] fn primal(x: f32, y: f32) -> f64 { (x * x * y) as f64 } diff --git a/tests/pretty/autodiff/autodiff_forward.pp b/tests/pretty/autodiff/autodiff_forward.pp index 8253603e8079c..a2525abc83207 100644 --- a/tests/pretty/autodiff/autodiff_forward.pp +++ b/tests/pretty/autodiff/autodiff_forward.pp @@ -13,7 +13,7 @@ // Test that forward mode ad macros are expanded correctly. -use std::autodiff::autodiff; +use std::autodiff::{autodiff_forward, autodiff_reverse}; #[rustc_autodiff] #[inline(never)] diff --git a/tests/pretty/autodiff/autodiff_forward.rs b/tests/pretty/autodiff/autodiff_forward.rs index ae974f9b4dbf9..181b6ebc77048 100644 --- a/tests/pretty/autodiff/autodiff_forward.rs +++ b/tests/pretty/autodiff/autodiff_forward.rs @@ -7,48 +7,48 @@ // Test that forward mode ad macros are expanded correctly. -use std::autodiff::autodiff; +use std::autodiff::{autodiff_forward, autodiff_reverse}; -#[autodiff(df1, Forward, Dual, Const, Dual)] +#[autodiff_forward(df1, Dual, Const, Dual)] pub fn f1(x: &[f64], y: f64) -> f64 { unimplemented!() } -#[autodiff(df2, Forward, Dual, Const, Const)] +#[autodiff_forward(df2, Dual, Const, Const)] pub fn f2(x: &[f64], y: f64) -> f64 { unimplemented!() } -#[autodiff(df3, Forward, Dual, Const, Const)] +#[autodiff_forward(df3, Dual, Const, Const)] pub fn f3(x: &[f64], y: f64) -> f64 { unimplemented!() } // Not the most interesting derivative, but who are we to judge -#[autodiff(df4, Forward)] +#[autodiff_forward(df4)] pub fn f4() {} // We want to be sure that the same function can be differentiated in different ways -#[autodiff(df5_rev, Reverse, Duplicated, Const, Active)] -#[autodiff(df5_x, Forward, Dual, Const, Const)] -#[autodiff(df5_y, Forward, Const, Dual, Const)] +#[autodiff_reverse(df5_rev, Duplicated, Const, Active)] +#[autodiff_forward(df5_x, Dual, Const, Const)] +#[autodiff_forward(df5_y, Const, Dual, Const)] pub fn f5(x: &[f64], y: f64) -> f64 { unimplemented!() } struct DoesNotImplDefault; -#[autodiff(df6, Forward, Const)] +#[autodiff_forward(df6, Const)] pub fn f6() -> DoesNotImplDefault { unimplemented!() } // Make sure, that we add the None for the default return. -#[autodiff(df7, Forward, Const)] +#[autodiff_forward(df7, Const)] pub fn f7(x: f32) -> () {} -#[autodiff(f8_1, Forward, Dual, DualOnly)] -#[autodiff(f8_2, Forward, 4, Dual, DualOnly)] -#[autodiff(f8_3, Forward, 4, Dual, Dual)] +#[autodiff_forward(f8_1, Dual, DualOnly)] +#[autodiff_forward(f8_2, 4, Dual, DualOnly)] +#[autodiff_forward(f8_3, 4, Dual, Dual)] #[no_mangle] fn f8(x: &f32) -> f32 { unimplemented!() @@ -56,8 +56,8 @@ fn f8(x: &f32) -> f32 { // We want to make sure that we can use the macro for functions defined inside of functions pub fn f9() { - #[autodiff(d_inner_1, Forward, Dual, DualOnly)] - #[autodiff(d_inner_2, Forward, Dual, Dual)] + #[autodiff_forward(d_inner_1, Dual, DualOnly)] + #[autodiff_forward(d_inner_2, Dual, Dual)] fn inner(x: f32) -> f32 { x * x } diff --git a/tests/pretty/autodiff/autodiff_reverse.pp b/tests/pretty/autodiff/autodiff_reverse.pp index 31920694a3ad5..e67c3443ddef1 100644 --- a/tests/pretty/autodiff/autodiff_reverse.pp +++ b/tests/pretty/autodiff/autodiff_reverse.pp @@ -13,7 +13,7 @@ // Test that reverse mode ad macros are expanded correctly. -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; #[rustc_autodiff] #[inline(never)] diff --git a/tests/pretty/autodiff/autodiff_reverse.rs b/tests/pretty/autodiff/autodiff_reverse.rs index 3c024272f407e..d37e5e3eb4cec 100644 --- a/tests/pretty/autodiff/autodiff_reverse.rs +++ b/tests/pretty/autodiff/autodiff_reverse.rs @@ -7,18 +7,18 @@ // Test that reverse mode ad macros are expanded correctly. -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; -#[autodiff(df1, Reverse, Duplicated, Const, Active)] +#[autodiff_reverse(df1, Duplicated, Const, Active)] pub fn f1(x: &[f64], y: f64) -> f64 { unimplemented!() } // Not the most interesting derivative, but who are we to judge -#[autodiff(df2, Reverse)] +#[autodiff_reverse(df2)] pub fn f2() {} -#[autodiff(df3, Reverse, Duplicated, Const, Active)] +#[autodiff_reverse(df3, Duplicated, Const, Active)] pub fn f3(x: &[f64], y: f64) -> f64 { unimplemented!() } @@ -27,12 +27,12 @@ enum Foo { Reverse } use Foo::Reverse; // What happens if we already have Reverse in type (enum variant decl) and value (enum variant // constructor) namespace? > It's expected to work normally. -#[autodiff(df4, Reverse, Const)] +#[autodiff_reverse(df4, Const)] pub fn f4(x: f32) { unimplemented!() } -#[autodiff(df5, Reverse, DuplicatedOnly, Duplicated)] +#[autodiff_reverse(df5, DuplicatedOnly, Duplicated)] pub fn f5(x: *const f32, y: &f32) { unimplemented!() } diff --git a/tests/pretty/autodiff/inherent_impl.pp b/tests/pretty/autodiff/inherent_impl.pp index 97ac766b6b99e..d18061b2dbdef 100644 --- a/tests/pretty/autodiff/inherent_impl.pp +++ b/tests/pretty/autodiff/inherent_impl.pp @@ -11,7 +11,7 @@ //@ pretty-compare-only //@ pp-exact:inherent_impl.pp -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; struct Foo { a: f64, diff --git a/tests/pretty/autodiff/inherent_impl.rs b/tests/pretty/autodiff/inherent_impl.rs index 59de93f7e0ffd..11ff209f9d89e 100644 --- a/tests/pretty/autodiff/inherent_impl.rs +++ b/tests/pretty/autodiff/inherent_impl.rs @@ -5,7 +5,7 @@ //@ pretty-compare-only //@ pp-exact:inherent_impl.pp -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; struct Foo { a: f64, @@ -17,7 +17,7 @@ trait MyTrait { } impl MyTrait for Foo { - #[autodiff(df, Reverse, Const, Active, Active)] + #[autodiff_reverse(df, Const, Active, Active)] fn f(&self, x: f64) -> f64 { self.a * 0.25 * (x * x - 1.0 - 2.0 * x.ln()) } From f92d84cc6e0770c0d8f3dc775bafdf7f8786db61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Sat, 10 May 2025 00:52:47 +0000 Subject: [PATCH 07/26] Initial naive implementation using `Symbols` to represent autodiff modes (`Forward`, `Reverse`) Since the mode is no longer part of `meta_item`, we must insert it manually (otherwise macro expansion with `#[rustc_autodiff]` won't work). This can be revised later if a more structured representation becomes necessary (using enums, annotated structs, etc). Some tests are currently failing. I'll address them next. --- compiler/rustc_builtin_macros/src/autodiff.rs | 28 +++++++++++++------ compiler/rustc_span/src/symbol.rs | 2 ++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 8073de15925e6..d60bb0ae5cbde 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -259,29 +259,41 @@ mod llvm_enzyme { // create TokenStream from vec elemtents: // meta_item doesn't have a .tokens field let mut ts: Vec = vec![]; - if meta_item_vec.len() < 2 { - // At the bare minimum, we need a fnc name and a mode, even for a dummy function with no - // input and output args. + if meta_item_vec.len() < 1 { + // At the bare minimum, we need a fnc name. dcx.emit_err(errors::AutoDiffMissingConfig { span: item.span() }); return vec![item]; } - meta_item_inner_to_ts(&meta_item_vec[1], &mut ts); + let mode_symbol = match mode { + DiffMode::Forward => sym::Forward, + DiffMode::Reverse => sym::Reverse, + _ => unreachable!("Unsupported mode: {:?}", mode), + }; + + // Insert mode token + let mode_token = Token::new(TokenKind::Ident(mode_symbol, false.into()), Span::default()); + ts.insert(0, TokenTree::Token(mode_token, Spacing::Joint)); + ts.insert( + 1, + TokenTree::Token(Token::new(TokenKind::Comma, Span::default()), Spacing::Alone), + ); // Now, if the user gave a width (vector aka batch-mode ad), then we copy it. // If it is not given, we default to 1 (scalar mode). let start_position; let kind: LitKind = LitKind::Integer; let symbol; - if meta_item_vec.len() >= 3 - && let Some(width) = width(&meta_item_vec[2]) + if meta_item_vec.len() >= 2 + && let Some(width) = width(&meta_item_vec[1]) { - start_position = 3; + start_position = 2; symbol = Symbol::intern(&width.to_string()); } else { - start_position = 2; + start_position = 1; symbol = sym::integer(1); } + let l: Lit = Lit { kind, symbol, suffix: None }; let t = Token::new(TokenKind::Literal(l), Span::default()); let comma = Token::new(TokenKind::Comma, Span::default()); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index bb19b5761bb19..9dff87e0a0011 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -253,6 +253,7 @@ symbols! { FnMut, FnOnce, Formatter, + Forward, From, FromIterator, FromResidual, @@ -348,6 +349,7 @@ symbols! { Result, ResumeTy, Return, + Reverse, Right, Rust, RustaceansAreAwesome, From 8dd62e8188d08baeb5d28cb221241cb61a65e532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Sat, 10 May 2025 21:50:06 +0000 Subject: [PATCH 08/26] Update UI tests --- compiler/rustc_builtin_macros/messages.ftl | 1 - compiler/rustc_builtin_macros/src/autodiff.rs | 6 +- tests/ui/autodiff/autodiff_illegal.rs | 60 +++++--------- tests/ui/autodiff/autodiff_illegal.stderr | 83 ++++++++----------- tests/ui/autodiff/auxiliary/my_macro.rs | 2 +- tests/ui/autodiff/visibility.rs | 9 +- .../autodiff/visibility.std_autodiff.stderr | 26 +++--- ...ature-gate-autodiff-use.has_support.stderr | 8 +- ...eature-gate-autodiff-use.no_support.stderr | 12 +-- .../feature-gate-autodiff-use.rs | 4 +- .../feature-gate-autodiff.has_support.stderr | 8 +- .../feature-gate-autodiff.no_support.stderr | 8 +- .../ui/feature-gates/feature-gate-autodiff.rs | 6 +- 13 files changed, 101 insertions(+), 132 deletions(-) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 73be954cefd76..4796f3a188897 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -71,7 +71,6 @@ builtin_macros_assert_requires_expression = macro requires an expression as an a builtin_macros_autodiff = autodiff must be applied to function builtin_macros_autodiff_missing_config = autodiff requires at least a name and mode -builtin_macros_autodiff_mode = unknown Mode: `{$mode}`. Use `Forward` or `Reverse` builtin_macros_autodiff_mode_activity = {$act} can not be used in {$mode} Mode builtin_macros_autodiff_not_build = this rustc version does not support autodiff builtin_macros_autodiff_number_activities = expected {$expected} activities, but found {$found} diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index d60bb0ae5cbde..dc3bb8ab52a5d 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -86,6 +86,7 @@ mod llvm_enzyme { ecx: &mut ExtCtxt<'_>, meta_item: &ThinVec, has_ret: bool, + mode: DiffMode, ) -> AutoDiffAttrs { let dcx = ecx.sess.dcx(); @@ -145,7 +146,7 @@ mod llvm_enzyme { }; AutoDiffAttrs { - mode: DiffMode::Error, + mode, width, ret_activity: *ret_activity, input_activity: input_activity.to_vec(), @@ -315,8 +316,7 @@ mod llvm_enzyme { ts.pop(); let ts: TokenStream = TokenStream::from_iter(ts); - let mut x: AutoDiffAttrs = from_ast(ecx, &meta_item_vec, has_ret); - x.mode = mode; + let x: AutoDiffAttrs = from_ast(ecx, &meta_item_vec, has_ret, mode); if !x.is_active() { // We encountered an error, so we return the original item. // This allows us to potentially parse other attributes. diff --git a/tests/ui/autodiff/autodiff_illegal.rs b/tests/ui/autodiff/autodiff_illegal.rs index a916bd8b857b9..a53b6d5e58981 100644 --- a/tests/ui/autodiff/autodiff_illegal.rs +++ b/tests/ui/autodiff/autodiff_illegal.rs @@ -7,38 +7,38 @@ // Test that invalid ad macros give nice errors and don't ICE. -use std::autodiff::autodiff; +use std::autodiff::{autodiff_forward, autodiff_reverse}; // We can't use Duplicated on scalars -#[autodiff(df1, Reverse, Duplicated)] +#[autodiff_reverse(df1, Duplicated)] pub fn f1(x: f64) { //~^ ERROR Duplicated can not be used for this type unimplemented!() } // Too many activities -#[autodiff(df3, Reverse, Duplicated, Const)] +#[autodiff_reverse(df3, Duplicated, Const)] pub fn f3(x: f64) { //~^^ ERROR expected 1 activities, but found 2 unimplemented!() } // To few activities -#[autodiff(df4, Reverse)] +#[autodiff_reverse(df4)] pub fn f4(x: f64) { //~^^ ERROR expected 1 activities, but found 0 unimplemented!() } // We can't use Dual in Reverse mode -#[autodiff(df5, Reverse, Dual)] +#[autodiff_reverse(df5, Dual)] pub fn f5(x: f64) { //~^^ ERROR Dual can not be used in Reverse Mode unimplemented!() } // We can't use Duplicated in Forward mode -#[autodiff(df6, Forward, Duplicated)] +#[autodiff_forward(df6, Duplicated)] pub fn f6(x: f64) { //~^^ ERROR Duplicated can not be used in Forward Mode //~^^ ERROR Duplicated can not be used for this type @@ -46,36 +46,36 @@ pub fn f6(x: f64) { } fn dummy() { - #[autodiff(df7, Forward, Dual)] + #[autodiff_forward(df7, Dual)] let mut x = 5; //~^ ERROR autodiff must be applied to function - #[autodiff(df7, Forward, Dual)] + #[autodiff_forward(df7, Dual)] x = x + 3; //~^^ ERROR attributes on expressions are experimental [E0658] //~^^ ERROR autodiff must be applied to function - #[autodiff(df7, Forward, Dual)] + #[autodiff_forward(df7, Dual)] let add_one_v2 = |x: u32| -> u32 { x + 1 }; //~^ ERROR autodiff must be applied to function } // Malformed, where args? -#[autodiff] +#[autodiff_forward] pub fn f7(x: f64) { //~^ ERROR autodiff requires at least a name and mode unimplemented!() } // Malformed, where args? -#[autodiff()] +#[autodiff_forward()] pub fn f8(x: f64) { //~^ ERROR autodiff requires at least a name and mode unimplemented!() } // Invalid attribute syntax -#[autodiff = ""] +#[autodiff_forward = ""] pub fn f9(x: f64) { //~^ ERROR autodiff requires at least a name and mode unimplemented!() @@ -84,29 +84,15 @@ pub fn f9(x: f64) { fn fn_exists() {} // We colide with an already existing function -#[autodiff(fn_exists, Reverse, Active)] +#[autodiff_reverse(fn_exists, Active)] pub fn f10(x: f64) { //~^^ ERROR the name `fn_exists` is defined multiple times [E0428] unimplemented!() } -// Malformed, missing a mode -#[autodiff(df11)] -pub fn f11() { - //~^ ERROR autodiff requires at least a name and mode - unimplemented!() -} - -// Invalid Mode -#[autodiff(df12, Debug)] -pub fn f12() { - //~^^ ERROR unknown Mode: `Debug`. Use `Forward` or `Reverse` - unimplemented!() -} - // Invalid, please pick one Mode // or use two autodiff macros. -#[autodiff(df13, Forward, Reverse)] +#[autodiff_reverse(df13, Reverse)] pub fn f13() { //~^^ ERROR did not recognize Activity: `Reverse` unimplemented!() @@ -117,7 +103,7 @@ struct Foo {} // We can't handle Active structs, because that would mean (in the general case), that we would // need to allocate and initialize arbitrary user types. We have Duplicated/Dual input args for // that. FIXME: Give a nicer error and suggest to the user to have a `&mut Foo` input instead. -#[autodiff(df14, Reverse, Active, Active)] +#[autodiff_reverse(df14, Active, Active)] fn f14(x: f32) -> Foo { unimplemented!() } @@ -127,14 +113,14 @@ type MyFloat = f32; // We would like to support type alias to f32/f64 in argument type in the future, // but that requires us to implement our checks at a later stage // like THIR which has type information available. -#[autodiff(df15, Reverse, Active, Active)] +#[autodiff_reverse(df15, Active, Active)] fn f15(x: MyFloat) -> f32 { //~^^ ERROR failed to resolve: use of undeclared type `MyFloat` [E0433] unimplemented!() } // We would like to support type alias to f32/f64 in return type in the future -#[autodiff(df16, Reverse, Active, Active)] +#[autodiff_reverse(df16, Active, Active)] fn f16(x: f32) -> MyFloat { unimplemented!() } @@ -145,40 +131,40 @@ struct F64Trans { } // We would like to support `#[repr(transparent)]` f32/f64 wrapper in return type in the future -#[autodiff(df17, Reverse, Active, Active)] +#[autodiff_reverse(df17, Active, Active)] fn f17(x: f64) -> F64Trans { unimplemented!() } // We would like to support `#[repr(transparent)]` f32/f64 wrapper in argument type in the future -#[autodiff(df18, Reverse, Active, Active)] +#[autodiff_reverse(df18, Active, Active)] fn f18(x: F64Trans) -> f64 { //~^^ ERROR failed to resolve: use of undeclared type `F64Trans` [E0433] unimplemented!() } // Invalid return activity -#[autodiff(df19, Forward, Dual, Active)] +#[autodiff_forward(df19, Dual, Active)] fn f19(x: f32) -> f32 { //~^^ ERROR invalid return activity Active in Forward Mode unimplemented!() } -#[autodiff(df20, Reverse, Active, Dual)] +#[autodiff_reverse(df20, Active, Dual)] fn f20(x: f32) -> f32 { //~^^ ERROR invalid return activity Dual in Reverse Mode unimplemented!() } // Duplicated cannot be used as return activity -#[autodiff(df21, Reverse, Active, Duplicated)] +#[autodiff_reverse(df21, Active, Duplicated)] fn f21(x: f32) -> f32 { //~^^ ERROR invalid return activity Duplicated in Reverse Mode unimplemented!() } struct DoesNotImplDefault; -#[autodiff(df22, Forward, Dual)] +#[autodiff_forward(df22, Dual)] pub fn f22() -> DoesNotImplDefault { //~^^ ERROR the function or associated item `default` exists for tuple `(DoesNotImplDefault, DoesNotImplDefault)`, but its trait bounds were not satisfied unimplemented!() diff --git a/tests/ui/autodiff/autodiff_illegal.stderr b/tests/ui/autodiff/autodiff_illegal.stderr index b119f61b8ae66..ad6f10af46780 100644 --- a/tests/ui/autodiff/autodiff_illegal.stderr +++ b/tests/ui/autodiff/autodiff_illegal.stderr @@ -1,8 +1,8 @@ error[E0658]: attributes on expressions are experimental --> $DIR/autodiff_illegal.rs:53:5 | -LL | #[autodiff(df7, Forward, Dual)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_forward(df7, Dual)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #15701 for more information = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable @@ -17,26 +17,26 @@ LL | pub fn f1(x: f64) { error: expected 1 activities, but found 2 --> $DIR/autodiff_illegal.rs:20:1 | -LL | #[autodiff(df3, Reverse, Duplicated, Const)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_reverse(df3, Duplicated, Const)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected 1 activities, but found 0 --> $DIR/autodiff_illegal.rs:27:1 | -LL | #[autodiff(df4, Reverse)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_reverse(df4)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ error: Dual can not be used in Reverse Mode --> $DIR/autodiff_illegal.rs:34:1 | -LL | #[autodiff(df5, Reverse, Dual)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_reverse(df5, Dual)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Duplicated can not be used in Forward Mode --> $DIR/autodiff_illegal.rs:41:1 | -LL | #[autodiff(df6, Forward, Duplicated)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_forward(df6, Duplicated)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Duplicated can not be used for this type --> $DIR/autodiff_illegal.rs:42:14 @@ -95,69 +95,54 @@ error[E0428]: the name `fn_exists` is defined multiple times LL | fn fn_exists() {} | -------------- previous definition of the value `fn_exists` here ... -LL | #[autodiff(fn_exists, Reverse, Active)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `fn_exists` redefined here +LL | #[autodiff_reverse(fn_exists, Active)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `fn_exists` redefined here | = note: `fn_exists` must be defined only once in the value namespace of this module -error: autodiff requires at least a name and mode - --> $DIR/autodiff_illegal.rs:95:1 - | -LL | / pub fn f11() { -LL | | -LL | | unimplemented!() -LL | | } - | |_^ - -error: unknown Mode: `Debug`. Use `Forward` or `Reverse` - --> $DIR/autodiff_illegal.rs:101:18 - | -LL | #[autodiff(df12, Debug)] - | ^^^^^ - error: did not recognize Activity: `Reverse` - --> $DIR/autodiff_illegal.rs:109:27 + --> $DIR/autodiff_illegal.rs:95:26 | -LL | #[autodiff(df13, Forward, Reverse)] - | ^^^^^^^ +LL | #[autodiff_reverse(df13, Reverse)] + | ^^^^^^^ error: invalid return activity Active in Forward Mode - --> $DIR/autodiff_illegal.rs:161:1 + --> $DIR/autodiff_illegal.rs:147:1 | -LL | #[autodiff(df19, Forward, Dual, Active)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_forward(df19, Dual, Active)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid return activity Dual in Reverse Mode - --> $DIR/autodiff_illegal.rs:167:1 + --> $DIR/autodiff_illegal.rs:153:1 | -LL | #[autodiff(df20, Reverse, Active, Dual)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_reverse(df20, Active, Dual)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid return activity Duplicated in Reverse Mode - --> $DIR/autodiff_illegal.rs:174:1 + --> $DIR/autodiff_illegal.rs:160:1 | -LL | #[autodiff(df21, Reverse, Active, Duplicated)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_reverse(df21, Active, Duplicated)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0433]: failed to resolve: use of undeclared type `MyFloat` - --> $DIR/autodiff_illegal.rs:130:1 + --> $DIR/autodiff_illegal.rs:116:1 | -LL | #[autodiff(df15, Reverse, Active, Active)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `MyFloat` +LL | #[autodiff_reverse(df15, Active, Active)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `MyFloat` error[E0433]: failed to resolve: use of undeclared type `F64Trans` - --> $DIR/autodiff_illegal.rs:154:1 + --> $DIR/autodiff_illegal.rs:140:1 | -LL | #[autodiff(df18, Reverse, Active, Active)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `F64Trans` +LL | #[autodiff_reverse(df18, Active, Active)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of undeclared type `F64Trans` error[E0599]: the function or associated item `default` exists for tuple `(DoesNotImplDefault, DoesNotImplDefault)`, but its trait bounds were not satisfied - --> $DIR/autodiff_illegal.rs:181:1 + --> $DIR/autodiff_illegal.rs:167:1 | LL | struct DoesNotImplDefault; | ------------------------- doesn't satisfy `DoesNotImplDefault: Default` -LL | #[autodiff(df22, Forward, Dual)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item cannot be called on `(DoesNotImplDefault, DoesNotImplDefault)` due to unsatisfied trait bounds +LL | #[autodiff_forward(df22, Dual)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function or associated item cannot be called on `(DoesNotImplDefault, DoesNotImplDefault)` due to unsatisfied trait bounds | = note: the following trait bounds were not satisfied: `DoesNotImplDefault: Default` @@ -168,7 +153,7 @@ LL + #[derive(Default)] LL | struct DoesNotImplDefault; | -error: aborting due to 23 previous errors +error: aborting due to 21 previous errors Some errors have detailed explanations: E0428, E0433, E0599, E0658. For more information about an error, try `rustc --explain E0428`. diff --git a/tests/ui/autodiff/auxiliary/my_macro.rs b/tests/ui/autodiff/auxiliary/my_macro.rs index 217631a33c9bc..1d5a6de145483 100644 --- a/tests/ui/autodiff/auxiliary/my_macro.rs +++ b/tests/ui/autodiff/auxiliary/my_macro.rs @@ -3,6 +3,6 @@ use proc_macro::TokenStream; #[proc_macro_attribute] #[macro_use] -pub fn autodiff(_attr: TokenStream, item: TokenStream) -> TokenStream { +pub fn autodiff_forward(_attr: TokenStream, item: TokenStream) -> TokenStream { item // identity proc-macro } diff --git a/tests/ui/autodiff/visibility.rs b/tests/ui/autodiff/visibility.rs index dfaec03aef01f..a84df75e79966 100644 --- a/tests/ui/autodiff/visibility.rs +++ b/tests/ui/autodiff/visibility.rs @@ -6,12 +6,11 @@ #![feature(autodiff)] #[cfg(std_autodiff)] -use std::autodiff::autodiff; - +use std::autodiff::autodiff_forward; extern crate my_macro; -use my_macro::autodiff; // bring `autodiff` in scope +use my_macro::autodiff_forward; // bring `autodiff_forward` in scope -#[autodiff] -//[std_autodiff]~^^^ ERROR the name `autodiff` is defined multiple times +#[autodiff_forward(dfoo)] +//[std_autodiff]~^^^ ERROR the name `autodiff_forward` is defined multiple times //[std_autodiff]~^^ ERROR this rustc version does not support autodiff fn foo() {} diff --git a/tests/ui/autodiff/visibility.std_autodiff.stderr b/tests/ui/autodiff/visibility.std_autodiff.stderr index 720c9a00170e9..e45f1139012a6 100644 --- a/tests/ui/autodiff/visibility.std_autodiff.stderr +++ b/tests/ui/autodiff/visibility.std_autodiff.stderr @@ -1,23 +1,23 @@ -error[E0252]: the name `autodiff` is defined multiple times - --> $DIR/visibility.rs:12:5 +error[E0252]: the name `autodiff_forward` is defined multiple times + --> $DIR/visibility.rs:11:5 | -LL | use std::autodiff::autodiff; - | ----------------------- previous import of the macro `autodiff` here -... -LL | use my_macro::autodiff; // bring `autodiff` in scope - | ^^^^^^^^^^^^^^^^^^ `autodiff` reimported here +LL | use std::autodiff::autodiff_forward; + | ------------------------------- previous import of the macro `autodiff_forward` here +LL | extern crate my_macro; +LL | use my_macro::autodiff_forward; // bring `autodiff_forward` in scope + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `autodiff_forward` reimported here | - = note: `autodiff` must be defined only once in the macro namespace of this module + = note: `autodiff_forward` must be defined only once in the macro namespace of this module help: you can use `as` to change the binding name of the import | -LL | use my_macro::autodiff as other_autodiff; // bring `autodiff` in scope - | +++++++++++++++++ +LL | use my_macro::autodiff_forward as other_autodiff_forward; // bring `autodiff_forward` in scope + | +++++++++++++++++++++++++ error: this rustc version does not support autodiff - --> $DIR/visibility.rs:14:1 + --> $DIR/visibility.rs:13:1 | -LL | #[autodiff] - | ^^^^^^^^^^^ +LL | #[autodiff_forward(dfoo)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/feature-gates/feature-gate-autodiff-use.has_support.stderr b/tests/ui/feature-gates/feature-gate-autodiff-use.has_support.stderr index 15ef257fbd84d..e5edd8e45e6c2 100644 --- a/tests/ui/feature-gates/feature-gate-autodiff-use.has_support.stderr +++ b/tests/ui/feature-gates/feature-gate-autodiff-use.has_support.stderr @@ -1,8 +1,8 @@ error[E0658]: use of unstable library feature `autodiff` --> $DIR/feature-gate-autodiff-use.rs:13:3 | -LL | #[autodiff(dfoo, Reverse)] - | ^^^^^^^^ +LL | #[autodiff_reverse(dfoo)] + | ^^^^^^^^^^^^^^^^ | = note: see issue #124509 for more information = help: add `#![feature(autodiff)]` to the crate attributes to enable @@ -11,8 +11,8 @@ LL | #[autodiff(dfoo, Reverse)] error[E0658]: use of unstable library feature `autodiff` --> $DIR/feature-gate-autodiff-use.rs:9:5 | -LL | use std::autodiff::autodiff; - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | use std::autodiff::autodiff_reverse; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #124509 for more information = help: add `#![feature(autodiff)]` to the crate attributes to enable diff --git a/tests/ui/feature-gates/feature-gate-autodiff-use.no_support.stderr b/tests/ui/feature-gates/feature-gate-autodiff-use.no_support.stderr index f59e495545202..65ba033b3589c 100644 --- a/tests/ui/feature-gates/feature-gate-autodiff-use.no_support.stderr +++ b/tests/ui/feature-gates/feature-gate-autodiff-use.no_support.stderr @@ -1,8 +1,8 @@ error[E0658]: use of unstable library feature `autodiff` --> $DIR/feature-gate-autodiff-use.rs:13:3 | -LL | #[autodiff(dfoo, Reverse)] - | ^^^^^^^^ +LL | #[autodiff_reverse(dfoo)] + | ^^^^^^^^^^^^^^^^ | = note: see issue #124509 for more information = help: add `#![feature(autodiff)]` to the crate attributes to enable @@ -11,14 +11,14 @@ LL | #[autodiff(dfoo, Reverse)] error: this rustc version does not support autodiff --> $DIR/feature-gate-autodiff-use.rs:13:1 | -LL | #[autodiff(dfoo, Reverse)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[autodiff_reverse(dfoo)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0658]: use of unstable library feature `autodiff` --> $DIR/feature-gate-autodiff-use.rs:9:5 | -LL | use std::autodiff::autodiff; - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | use std::autodiff::autodiff_reverse; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: see issue #124509 for more information = help: add `#![feature(autodiff)]` to the crate attributes to enable diff --git a/tests/ui/feature-gates/feature-gate-autodiff-use.rs b/tests/ui/feature-gates/feature-gate-autodiff-use.rs index 602e830b0b21c..2864b786c121f 100644 --- a/tests/ui/feature-gates/feature-gate-autodiff-use.rs +++ b/tests/ui/feature-gates/feature-gate-autodiff-use.rs @@ -6,11 +6,11 @@ #![crate_type = "lib"] -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; //[has_support]~^ ERROR use of unstable library feature `autodiff` //[no_support]~^^ ERROR use of unstable library feature `autodiff` -#[autodiff(dfoo, Reverse)] +#[autodiff_reverse(dfoo)] //[has_support]~^ ERROR use of unstable library feature `autodiff` [E0658] //[no_support]~^^ ERROR use of unstable library feature `autodiff` [E0658] //[no_support]~| ERROR this rustc version does not support autodiff diff --git a/tests/ui/feature-gates/feature-gate-autodiff.has_support.stderr b/tests/ui/feature-gates/feature-gate-autodiff.has_support.stderr index c25cf7d337371..dcbaba716459b 100644 --- a/tests/ui/feature-gates/feature-gate-autodiff.has_support.stderr +++ b/tests/ui/feature-gates/feature-gate-autodiff.has_support.stderr @@ -1,12 +1,12 @@ -error: cannot find attribute `autodiff` in this scope +error: cannot find attribute `autodiff_reverse` in this scope --> $DIR/feature-gate-autodiff.rs:9:3 | -LL | #[autodiff(dfoo, Reverse)] - | ^^^^^^^^ +LL | #[autodiff_reverse(dfoo)] + | ^^^^^^^^^^^^^^^^ | help: consider importing this attribute macro | -LL + use std::autodiff::autodiff; +LL + use std::autodiff::autodiff_reverse; | error: aborting due to 1 previous error diff --git a/tests/ui/feature-gates/feature-gate-autodiff.no_support.stderr b/tests/ui/feature-gates/feature-gate-autodiff.no_support.stderr index c25cf7d337371..dcbaba716459b 100644 --- a/tests/ui/feature-gates/feature-gate-autodiff.no_support.stderr +++ b/tests/ui/feature-gates/feature-gate-autodiff.no_support.stderr @@ -1,12 +1,12 @@ -error: cannot find attribute `autodiff` in this scope +error: cannot find attribute `autodiff_reverse` in this scope --> $DIR/feature-gate-autodiff.rs:9:3 | -LL | #[autodiff(dfoo, Reverse)] - | ^^^^^^^^ +LL | #[autodiff_reverse(dfoo)] + | ^^^^^^^^^^^^^^^^ | help: consider importing this attribute macro | -LL + use std::autodiff::autodiff; +LL + use std::autodiff::autodiff_reverse; | error: aborting due to 1 previous error diff --git a/tests/ui/feature-gates/feature-gate-autodiff.rs b/tests/ui/feature-gates/feature-gate-autodiff.rs index 4249b229a6985..adb35cb8e3356 100644 --- a/tests/ui/feature-gates/feature-gate-autodiff.rs +++ b/tests/ui/feature-gates/feature-gate-autodiff.rs @@ -6,7 +6,7 @@ // This checks that without the autodiff feature enabled, we can't use it. -#[autodiff(dfoo, Reverse)] -//[has_support]~^ ERROR cannot find attribute `autodiff` in this scope -//[no_support]~^^ ERROR cannot find attribute `autodiff` in this scope +#[autodiff_reverse(dfoo)] +//[has_support]~^ ERROR cannot find attribute `autodiff_reverse` in this scope +//[no_support]~^^ ERROR cannot find attribute `autodiff_reverse` in this scope fn foo() {} From 8917ff602446372e45abed7ab3a58dc879638b1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Tue, 20 May 2025 12:36:12 +0000 Subject: [PATCH 09/26] Update generic tests --- tests/codegen/autodiff/generic.rs | 4 ++-- tests/pretty/autodiff/autodiff_forward.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/codegen/autodiff/generic.rs b/tests/codegen/autodiff/generic.rs index 15e7d8a495745..2f674079be021 100644 --- a/tests/codegen/autodiff/generic.rs +++ b/tests/codegen/autodiff/generic.rs @@ -3,9 +3,9 @@ //@ needs-enzyme #![feature(autodiff)] -use std::autodiff::autodiff; +use std::autodiff::autodiff_reverse; -#[autodiff(d_square, Reverse, Duplicated, Active)] +#[autodiff_reverse(d_square, Duplicated, Active)] fn square + Copy>(x: &T) -> T { *x * *x } diff --git a/tests/pretty/autodiff/autodiff_forward.rs b/tests/pretty/autodiff/autodiff_forward.rs index 181b6ebc77048..e23a1b3e241e9 100644 --- a/tests/pretty/autodiff/autodiff_forward.rs +++ b/tests/pretty/autodiff/autodiff_forward.rs @@ -64,7 +64,7 @@ pub fn f9() { } // Make sure we can handle generics -#[autodiff(d_square, Reverse, Duplicated, Active)] +#[autodiff_reverse(d_square, Duplicated, Active)] pub fn f10 + Copy>(x: &T) -> T { *x * *x } From 1e9e17704adfc6bd6b01a0031b73fbc1682a0f1f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 21 May 2025 15:34:15 +0000 Subject: [PATCH 10/26] Move some code around in codegen_call_terminator --- compiler/rustc_codegen_ssa/src/mir/block.rs | 202 ++++++++++---------- 1 file changed, 103 insertions(+), 99 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 922b8a5824bec..de827a475c418 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -8,7 +8,7 @@ use rustc_hir::lang_items::LangItem; use rustc_middle::mir::{self, AssertKind, InlineAsmMacro, SwitchTargets, UnwindTerminateReason}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; -use rustc_middle::ty::{self, Instance, Ty}; +use rustc_middle::ty::{self, Instance, List, Ty}; use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; use rustc_span::source_map::Spanned; @@ -827,7 +827,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { helper: &TerminatorCodegenHelper<'tcx>, bx: &mut Bx, intrinsic: ty::IntrinsicDef, - instance: Option>, + instance: Instance<'tcx>, source_info: mir::SourceInfo, target: Option, unwind: mir::UnwindAction, @@ -837,7 +837,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // These are intrinsics that compile to panics so that we can get a message // which mentions the offending type, even from a const context. if let Some(requirement) = ValidityRequirement::from_intrinsic(intrinsic.name) { - let ty = instance.unwrap().args.type_at(0); + let ty = instance.args.type_at(0); let do_panic = !bx .tcx() @@ -910,35 +910,116 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let callee = self.codegen_operand(bx, func); let (instance, mut llfn) = match *callee.layout.ty.kind() { - ty::FnDef(def_id, args) => ( - Some(ty::Instance::expect_resolve( + ty::FnDef(def_id, generic_args) => { + let instance = ty::Instance::expect_resolve( bx.tcx(), bx.typing_env(), def_id, - args, + generic_args, fn_span, - )), - None, - ), + ); + + let instance = match instance.def { + // We don't need AsyncDropGlueCtorShim here because it is not `noop func`, + // it is `func returning noop future` + ty::InstanceKind::DropGlue(_, None) => { + // Empty drop glue; a no-op. + let target = target.unwrap(); + return helper.funclet_br(self, bx, target, mergeable_succ); + } + ty::InstanceKind::Intrinsic(def_id) => { + let intrinsic = bx.tcx().intrinsic(def_id).unwrap(); + if let Some(merging_succ) = self.codegen_panic_intrinsic( + &helper, + bx, + intrinsic, + instance, + source_info, + target, + unwind, + mergeable_succ, + ) { + return merging_succ; + } + + let fn_abi = bx.fn_abi_of_instance(instance, List::empty()); + + let mut llargs = Vec::with_capacity(1); + let ret_dest = self.make_return_dest( + bx, + destination, + &fn_abi.ret, + &mut llargs, + Some(intrinsic), + ); + let dest = match ret_dest { + _ if fn_abi.ret.is_indirect() => llargs[0], + ReturnDest::Nothing => bx.const_undef(bx.type_ptr()), + ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) => { + dst.val.llval + } + ReturnDest::DirectOperand(_) => { + bug!("Cannot use direct operand with an intrinsic call") + } + }; + + let args: Vec<_> = + args.iter().map(|arg| self.codegen_operand(bx, &arg.node)).collect(); + + if matches!(intrinsic, ty::IntrinsicDef { name: sym::caller_location, .. }) + { + let location = self.get_caller_location( + bx, + mir::SourceInfo { span: fn_span, ..source_info }, + ); + + assert_eq!(llargs, []); + if let ReturnDest::IndirectOperand(tmp, _) = ret_dest { + location.val.store(bx, tmp); + } + self.store_return(bx, ret_dest, &fn_abi.ret, location.immediate()); + return helper.funclet_br(self, bx, target.unwrap(), mergeable_succ); + } + + match Self::codegen_intrinsic_call(bx, instance, fn_abi, &args, dest, span) + { + Ok(()) => { + if let ReturnDest::IndirectOperand(dst, _) = ret_dest { + self.store_return(bx, ret_dest, &fn_abi.ret, dst.val.llval); + } + + return if let Some(target) = target { + helper.funclet_br(self, bx, target, mergeable_succ) + } else { + bx.unreachable(); + MergingSucc::False + }; + } + Err(instance) => { + if intrinsic.must_be_overridden { + span_bug!( + span, + "intrinsic {} must be overridden by codegen backend, but isn't", + intrinsic.name, + ); + } + instance + } + } + } + _ => instance, + }; + + (Some(instance), None) + } ty::FnPtr(..) => (None, Some(callee.immediate())), _ => bug!("{} is not callable", callee.layout.ty), }; - let def = instance.map(|i| i.def); - - // We don't need AsyncDropGlueCtorShim here because it is not `noop func`, - // it is `func returning noop future` - if let Some(ty::InstanceKind::DropGlue(_, None)) = def { - // Empty drop glue; a no-op. - let target = target.unwrap(); - return helper.funclet_br(self, bx, target, mergeable_succ); - } - // FIXME(eddyb) avoid computing this if possible, when `instance` is // available - right now `sig` is only needed for getting the `abi` // and figuring out how many extra args were passed to a C-variadic `fn`. let sig = callee.layout.ty.fn_sig(bx.tcx()); - let abi = sig.abi(); let extra_args = &args[sig.inputs().skip_binder().len()..]; let extra_args = bx.tcx().mk_type_list_from_iter(extra_args.iter().map(|op_arg| { @@ -954,83 +1035,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // The arguments we'll be passing. Plus one to account for outptr, if used. let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize; - let instance = match def { - Some(ty::InstanceKind::Intrinsic(def_id)) => { - let intrinsic = bx.tcx().intrinsic(def_id).unwrap(); - if let Some(merging_succ) = self.codegen_panic_intrinsic( - &helper, - bx, - intrinsic, - instance, - source_info, - target, - unwind, - mergeable_succ, - ) { - return merging_succ; - } - - let mut llargs = Vec::with_capacity(1); - let ret_dest = self.make_return_dest( - bx, - destination, - &fn_abi.ret, - &mut llargs, - Some(intrinsic), - ); - let dest = match ret_dest { - _ if fn_abi.ret.is_indirect() => llargs[0], - ReturnDest::Nothing => bx.const_undef(bx.type_ptr()), - ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) => dst.val.llval, - ReturnDest::DirectOperand(_) => { - bug!("Cannot use direct operand with an intrinsic call") - } - }; - - let args: Vec<_> = - args.iter().map(|arg| self.codegen_operand(bx, &arg.node)).collect(); - - if matches!(intrinsic, ty::IntrinsicDef { name: sym::caller_location, .. }) { - let location = self - .get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info }); - - assert_eq!(llargs, []); - if let ReturnDest::IndirectOperand(tmp, _) = ret_dest { - location.val.store(bx, tmp); - } - self.store_return(bx, ret_dest, &fn_abi.ret, location.immediate()); - return helper.funclet_br(self, bx, target.unwrap(), mergeable_succ); - } - - let instance = *instance.as_ref().unwrap(); - match Self::codegen_intrinsic_call(bx, instance, fn_abi, &args, dest, span) { - Ok(()) => { - if let ReturnDest::IndirectOperand(dst, _) = ret_dest { - self.store_return(bx, ret_dest, &fn_abi.ret, dst.val.llval); - } - - return if let Some(target) = target { - helper.funclet_br(self, bx, target, mergeable_succ) - } else { - bx.unreachable(); - MergingSucc::False - }; - } - Err(instance) => { - if intrinsic.must_be_overridden { - span_bug!( - span, - "intrinsic {} must be overridden by codegen backend, but isn't", - intrinsic.name, - ); - } - Some(instance) - } - } - } - _ => instance, - }; - let mut llargs = Vec::with_capacity(arg_count); // We still need to call `make_return_dest` even if there's no `target`, since @@ -1040,7 +1044,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let destination = target.map(|target| (return_dest, target)); // Split the rust-call tupled arguments off. - let (first_args, untuple) = if abi == ExternAbi::RustCall + let (first_args, untuple) = if sig.abi() == ExternAbi::RustCall && let Some((tup, args)) = args.split_last() { (args, Some(tup)) @@ -1055,7 +1059,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { 'make_args: for (i, arg) in first_args.iter().enumerate() { let mut op = self.codegen_operand(bx, &arg.node); - if let (0, Some(ty::InstanceKind::Virtual(_, idx))) = (i, def) { + if let (0, Some(ty::InstanceKind::Virtual(_, idx))) = (i, instance.map(|i| i.def)) { match op.val { Pair(data_ptr, meta) => { // In the case of Rc, we need to explicitly pass a From e4700e76d83428a99f2c7a8b19d3bf50606cf7e1 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 22 May 2025 11:17:51 +0000 Subject: [PATCH 11/26] Always use fn_span in codegen_call_terminator --- compiler/rustc_codegen_ssa/src/mir/block.rs | 26 +++++++++------------ 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index de827a475c418..beb13ba28bc31 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -903,8 +903,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { fn_span: Span, mergeable_succ: bool, ) -> MergingSucc { - let source_info = terminator.source_info; - let span = source_info.span; + let source_info = mir::SourceInfo { span: fn_span, ..terminator.source_info }; // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. let callee = self.codegen_operand(bx, func); @@ -968,10 +967,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { if matches!(intrinsic, ty::IntrinsicDef { name: sym::caller_location, .. }) { - let location = self.get_caller_location( - bx, - mir::SourceInfo { span: fn_span, ..source_info }, - ); + let location = self.get_caller_location(bx, source_info); assert_eq!(llargs, []); if let ReturnDest::IndirectOperand(tmp, _) = ret_dest { @@ -981,8 +977,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return helper.funclet_br(self, bx, target.unwrap(), mergeable_succ); } - match Self::codegen_intrinsic_call(bx, instance, fn_abi, &args, dest, span) - { + match Self::codegen_intrinsic_call( + bx, instance, fn_abi, &args, dest, fn_span, + ) { Ok(()) => { if let ReturnDest::IndirectOperand(dst, _) = ret_dest { self.store_return(bx, ret_dest, &fn_abi.ret, dst.val.llval); @@ -998,7 +995,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { Err(instance) => { if intrinsic.must_be_overridden { span_bug!( - span, + fn_span, "intrinsic {} must be overridden by codegen backend, but isn't", intrinsic.name, ); @@ -1113,7 +1110,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Make sure that we've actually unwrapped the rcvr down // to a pointer or ref to `dyn* Trait`. if !op.layout.ty.builtin_deref(true).unwrap().is_dyn_star() { - span_bug!(span, "can't codegen a virtual call on {:#?}", op); + span_bug!(fn_span, "can't codegen a virtual call on {:#?}", op); } let place = op.deref(bx.cx()); let data_place = place.project_field(bx, 0); @@ -1129,7 +1126,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { continue; } _ => { - span_bug!(span, "can't codegen a virtual call on {:#?}", op); + span_bug!(fn_span, "can't codegen a virtual call on {:#?}", op); } } } @@ -1179,8 +1176,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir_args + 1, "#[track_caller] fn's must have 1 more argument in their ABI than in their MIR: {instance:?} {fn_span:?} {fn_abi:?}", ); - let location = - self.get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info }); + let location = self.get_caller_location(bx, source_info); debug!( "codegen_call_terminator({:?}): location={:?} (fn_span {:?})", terminator, location, fn_span @@ -1199,9 +1195,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let fn_ptr = match (instance, llfn) { (Some(instance), None) => bx.get_fn_addr(instance), (_, Some(llfn)) => llfn, - _ => span_bug!(span, "no instance or llfn for call"), + _ => span_bug!(fn_span, "no instance or llfn for call"), }; - self.set_debug_loc(bx, mir::SourceInfo { span: fn_span, ..source_info }); + self.set_debug_loc(bx, source_info); helper.do_call( self, bx, From c83358beb5f40a8b1c6e422f229ca4db1f927599 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 22 May 2025 13:50:47 +0000 Subject: [PATCH 12/26] Move caller_location handling into codegen_intrinsic_call --- compiler/rustc_codegen_ssa/src/mir/block.rs | 25 +++++++------------ .../rustc_codegen_ssa/src/mir/intrinsic.rs | 13 ++++++++-- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index beb13ba28bc31..1d11b907e9081 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -11,8 +11,8 @@ use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; use rustc_middle::ty::{self, Instance, List, Ty}; use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; +use rustc_span::Span; use rustc_span::source_map::Spanned; -use rustc_span::{Span, sym}; use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; use tracing::{debug, info}; @@ -965,20 +965,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let args: Vec<_> = args.iter().map(|arg| self.codegen_operand(bx, &arg.node)).collect(); - if matches!(intrinsic, ty::IntrinsicDef { name: sym::caller_location, .. }) - { - let location = self.get_caller_location(bx, source_info); - - assert_eq!(llargs, []); - if let ReturnDest::IndirectOperand(tmp, _) = ret_dest { - location.val.store(bx, tmp); - } - self.store_return(bx, ret_dest, &fn_abi.ret, location.immediate()); - return helper.funclet_br(self, bx, target.unwrap(), mergeable_succ); - } - - match Self::codegen_intrinsic_call( - bx, instance, fn_abi, &args, dest, fn_span, + match self.codegen_intrinsic_call( + bx, + instance, + fn_abi, + &args, + dest, + source_info, ) { Ok(()) => { if let ReturnDest::IndirectOperand(dst, _) = ret_dest { @@ -1667,7 +1660,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { tuple.layout.fields.count() } - fn get_caller_location( + pub(super) fn get_caller_location( &mut self, bx: &mut Bx, source_info: mir::SourceInfo, diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index b0fcfee2adf5f..6cdd8480cbe31 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -1,8 +1,9 @@ use rustc_abi::WrappingRange; +use rustc_middle::mir::SourceInfo; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; -use rustc_span::{Span, sym}; +use rustc_span::sym; use rustc_target::callconv::{FnAbi, PassMode}; use super::FunctionCx; @@ -52,13 +53,15 @@ fn memset_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { /// In the `Err` case, returns the instance that should be called instead. pub fn codegen_intrinsic_call( + &mut self, bx: &mut Bx, instance: ty::Instance<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, Bx::Value>], llresult: Bx::Value, - span: Span, + source_info: SourceInfo, ) -> Result<(), ty::Instance<'tcx>> { + let span = source_info.span; let callee_ty = instance.ty(bx.tcx(), bx.typing_env()); let ty::FnDef(def_id, fn_args) = *callee_ty.kind() else { @@ -105,6 +108,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return Ok(()); } + sym::caller_location => { + let location = self.get_caller_location(bx, source_info); + location.val.store(bx, result); + return Ok(()); + } + sym::va_start => bx.va_start(args[0].immediate()), sym::va_end => bx.va_end(args[0].immediate()), sym::size_of_val => { From 6016f84e716c443f64c491a26f5ec1dfd42f2491 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 22 May 2025 14:15:41 +0000 Subject: [PATCH 13/26] Pass PlaceRef rather than Bx::Value to codegen_intrinsic_call --- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 21 ++++---- compiler/rustc_codegen_llvm/src/intrinsic.rs | 50 ++++++++----------- compiler/rustc_codegen_ssa/src/mir/block.rs | 4 +- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 5 +- .../rustc_codegen_ssa/src/traits/intrinsic.rs | 3 +- 5 files changed, 38 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index ba65c8205a500..18dabe9ea16c2 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -22,7 +22,7 @@ use rustc_codegen_ssa::traits::{ }; use rustc_middle::bug; #[cfg(feature = "master")] -use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt}; +use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::{Span, Symbol, sym}; @@ -202,7 +202,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc instance: Instance<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, RValue<'gcc>>], - llresult: RValue<'gcc>, + result: PlaceRef<'tcx, RValue<'gcc>>, span: Span, ) -> Result<(), Instance<'tcx>> { let tcx = self.tcx; @@ -221,7 +221,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc let name_str = name.as_str(); let llret_ty = self.layout_of(ret_ty).gcc_type(self); - let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout); let simple = get_simple_intrinsic(self, name); let simple_func = get_simple_function(self, name); @@ -271,7 +270,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc args[0].immediate(), args[1].immediate(), args[2].immediate(), - llresult, + result, ); return Ok(()); } @@ -1230,14 +1229,13 @@ fn try_intrinsic<'a, 'b, 'gcc, 'tcx>( try_func: RValue<'gcc>, data: RValue<'gcc>, _catch_func: RValue<'gcc>, - dest: RValue<'gcc>, + dest: PlaceRef<'tcx, RValue<'gcc>>, ) { if bx.sess().panic_strategy() == PanicStrategy::Abort { bx.call(bx.type_void(), None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. - let ret_align = bx.tcx.data_layout.i32_align.abi; - bx.store(bx.const_i32(0), dest, ret_align); + OperandValue::Immediate(bx.const_i32(0)).store(bx, dest); } else { if wants_msvc_seh(bx.sess()) { unimplemented!(); @@ -1261,12 +1259,12 @@ fn try_intrinsic<'a, 'b, 'gcc, 'tcx>( // functions in play. By calling a shim we're guaranteed that our shim will have // the right personality function. #[cfg(feature = "master")] -fn codegen_gnu_try<'gcc>( - bx: &mut Builder<'_, 'gcc, '_>, +fn codegen_gnu_try<'gcc, 'tcx>( + bx: &mut Builder<'_, 'gcc, 'tcx>, try_func: RValue<'gcc>, data: RValue<'gcc>, catch_func: RValue<'gcc>, - dest: RValue<'gcc>, + dest: PlaceRef<'tcx, RValue<'gcc>>, ) { let cx: &CodegenCx<'gcc, '_> = bx.cx; let (llty, func) = get_rust_try_fn(cx, &mut |mut bx| { @@ -1322,8 +1320,7 @@ fn codegen_gnu_try<'gcc>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). let ret = bx.call(llty, None, None, func, &[try_func, data, catch_func], None, None); - let i32_align = bx.tcx().data_layout.i32_align.abi; - bx.store(ret, dest, i32_align); + OperandValue::Immediate(ret).store(bx, dest); } // Helper function used to get a handle to the `__rust_try` function used to diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 5ca5737529229..067dd9b1681f2 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -167,7 +167,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { instance: ty::Instance<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, &'ll Value>], - llresult: &'ll Value, + result: PlaceRef<'tcx, &'ll Value>, span: Span, ) -> Result<(), ty::Instance<'tcx>> { let tcx = self.tcx; @@ -184,7 +184,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let name = tcx.item_name(def_id); let llret_ty = self.layout_of(ret_ty).llvm_type(self); - let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout); let simple = get_simple_intrinsic(self, name); let llval = match name { @@ -255,7 +254,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { args[0].immediate(), args[1].immediate(), args[2].immediate(), - llresult, + result, ); return Ok(()); } @@ -688,20 +687,19 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } } -fn catch_unwind_intrinsic<'ll>( - bx: &mut Builder<'_, 'll, '_>, +fn catch_unwind_intrinsic<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, try_func: &'ll Value, data: &'ll Value, catch_func: &'ll Value, - dest: &'ll Value, + dest: PlaceRef<'tcx, &'ll Value>, ) { if bx.sess().panic_strategy() == PanicStrategy::Abort { let try_func_ty = bx.type_func(&[bx.type_ptr()], bx.type_void()); bx.call(try_func_ty, None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. - let ret_align = bx.tcx().data_layout.i32_align.abi; - bx.store(bx.const_i32(0), dest, ret_align); + OperandValue::Immediate(bx.const_i32(0)).store(bx, dest); } else if wants_msvc_seh(bx.sess()) { codegen_msvc_try(bx, try_func, data, catch_func, dest); } else if wants_wasm_eh(bx.sess()) { @@ -720,12 +718,12 @@ fn catch_unwind_intrinsic<'ll>( // instructions are meant to work for all targets, as of the time of this // writing, however, LLVM does not recommend the usage of these new instructions // as the old ones are still more optimized. -fn codegen_msvc_try<'ll>( - bx: &mut Builder<'_, 'll, '_>, +fn codegen_msvc_try<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, try_func: &'ll Value, data: &'ll Value, catch_func: &'ll Value, - dest: &'ll Value, + dest: PlaceRef<'tcx, &'ll Value>, ) { let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| { bx.set_personality_fn(bx.eh_personality()); @@ -865,17 +863,16 @@ fn codegen_msvc_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); - let i32_align = bx.tcx().data_layout.i32_align.abi; - bx.store(ret, dest, i32_align); + OperandValue::Immediate(ret).store(bx, dest); } // WASM's definition of the `rust_try` function. -fn codegen_wasm_try<'ll>( - bx: &mut Builder<'_, 'll, '_>, +fn codegen_wasm_try<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, try_func: &'ll Value, data: &'ll Value, catch_func: &'ll Value, - dest: &'ll Value, + dest: PlaceRef<'tcx, &'ll Value>, ) { let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| { bx.set_personality_fn(bx.eh_personality()); @@ -939,8 +936,7 @@ fn codegen_wasm_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); - let i32_align = bx.tcx().data_layout.i32_align.abi; - bx.store(ret, dest, i32_align); + OperandValue::Immediate(ret).store(bx, dest); } // Definition of the standard `try` function for Rust using the GNU-like model @@ -954,12 +950,12 @@ fn codegen_wasm_try<'ll>( // function calling it, and that function may already have other personality // functions in play. By calling a shim we're guaranteed that our shim will have // the right personality function. -fn codegen_gnu_try<'ll>( - bx: &mut Builder<'_, 'll, '_>, +fn codegen_gnu_try<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, try_func: &'ll Value, data: &'ll Value, catch_func: &'ll Value, - dest: &'ll Value, + dest: PlaceRef<'tcx, &'ll Value>, ) { let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| { // Codegens the shims described above: @@ -1006,19 +1002,18 @@ fn codegen_gnu_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); - let i32_align = bx.tcx().data_layout.i32_align.abi; - bx.store(ret, dest, i32_align); + OperandValue::Immediate(ret).store(bx, dest); } // Variant of codegen_gnu_try used for emscripten where Rust panics are // implemented using C++ exceptions. Here we use exceptions of a specific type // (`struct rust_panic`) to represent Rust panics. -fn codegen_emcc_try<'ll>( - bx: &mut Builder<'_, 'll, '_>, +fn codegen_emcc_try<'ll, 'tcx>( + bx: &mut Builder<'_, 'll, 'tcx>, try_func: &'ll Value, data: &'ll Value, catch_func: &'ll Value, - dest: &'ll Value, + dest: PlaceRef<'tcx, &'ll Value>, ) { let (llty, llfn) = get_rust_try_fn(bx, &mut |mut bx| { // Codegens the shims described above: @@ -1089,8 +1084,7 @@ fn codegen_emcc_try<'ll>( // Note that no invoke is used here because by definition this function // can't panic (that's what it's catching). let ret = bx.call(llty, None, None, llfn, &[try_func, data, catch_func], None, None); - let i32_align = bx.tcx().data_layout.i32_align.abi; - bx.store(ret, dest, i32_align); + OperandValue::Immediate(ret).store(bx, dest); } // Helper function to give a Block to a closure to codegen a shim function. diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 1d11b907e9081..1a291b17886cc 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -965,12 +965,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let args: Vec<_> = args.iter().map(|arg| self.codegen_operand(bx, &arg.node)).collect(); + let result = PlaceRef::new_sized(dest, fn_abi.ret.layout); + match self.codegen_intrinsic_call( bx, instance, fn_abi, &args, - dest, + result, source_info, ) { Ok(()) => { diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 6cdd8480cbe31..6a0778832981b 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -58,7 +58,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { instance: ty::Instance<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, Bx::Value>], - llresult: Bx::Value, + result: PlaceRef<'tcx, Bx::Value>, source_info: SourceInfo, ) -> Result<(), ty::Instance<'tcx>> { let span = source_info.span; @@ -100,7 +100,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } let llret_ty = bx.backend_type(bx.layout_of(ret_ty)); - let result = PlaceRef::new_sized(llresult, fn_abi.ret.layout); let llval = match name { sym::abort => { @@ -537,7 +536,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => { // Need to use backend-specific things in the implementation. - return bx.codegen_intrinsic_call(instance, fn_abi, args, llresult, span); + return bx.codegen_intrinsic_call(instance, fn_abi, args, result, span); } }; diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index 88cf8dbf0c5c8..f9d6dab6fafc3 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -4,6 +4,7 @@ use rustc_target::callconv::FnAbi; use super::BackendTypes; use crate::mir::operand::OperandRef; +use crate::mir::place::PlaceRef; pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { /// Remember to add all intrinsics here, in `compiler/rustc_hir_analysis/src/check/mod.rs`, @@ -16,7 +17,7 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { instance: ty::Instance<'tcx>, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, Self::Value>], - llresult: Self::Value, + result: PlaceRef<'tcx, Self::Value>, span: Span, ) -> Result<(), ty::Instance<'tcx>>; From 0a14e1b2e7483179fa74f44d3c198c33325f6d29 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 22 May 2025 14:58:58 +0000 Subject: [PATCH 14/26] Remove usage of FnAbi in codegen_intrinsic_call --- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 32 +++++++------------ compiler/rustc_codegen_llvm/src/intrinsic.rs | 30 ++++++----------- compiler/rustc_codegen_ssa/src/mir/block.rs | 10 ++---- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 18 ++++------- .../rustc_codegen_ssa/src/traits/intrinsic.rs | 4 +-- 5 files changed, 31 insertions(+), 63 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 18dabe9ea16c2..1bcb891a2504a 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -26,7 +26,7 @@ use rustc_middle::ty::layout::FnAbiOf; use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf}; use rustc_middle::ty::{self, Instance, Ty}; use rustc_span::{Span, Symbol, sym}; -use rustc_target::callconv::{ArgAbi, FnAbi, PassMode}; +use rustc_target::callconv::{ArgAbi, PassMode}; use rustc_target::spec::PanicStrategy; #[cfg(feature = "master")] @@ -200,7 +200,6 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc fn codegen_intrinsic_call( &mut self, instance: Instance<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, RValue<'gcc>>], result: PlaceRef<'tcx, RValue<'gcc>>, span: Span, @@ -285,17 +284,10 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc } sym::volatile_load | sym::unaligned_volatile_load => { - let tp_ty = fn_args.type_at(0); let ptr = args[0].immediate(); - let layout = self.layout_of(tp_ty); - let load = if let PassMode::Cast { cast: ref ty, pad_i32: _ } = fn_abi.ret.mode { - let gcc_ty = ty.gcc_type(self); - self.volatile_load(gcc_ty, ptr) - } else { - self.volatile_load(layout.gcc_type(self), ptr) - }; + let load = self.volatile_load(result.layout.gcc_type(self), ptr); // TODO(antoyo): set alignment. - if let BackendRepr::Scalar(scalar) = layout.backend_repr { + if let BackendRepr::Scalar(scalar) = result.layout.backend_repr { self.to_immediate_scalar(load, scalar) } else { load @@ -510,16 +502,14 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc _ => return Err(Instance::new_raw(instance.def_id(), instance.args)), }; - if !fn_abi.ret.is_ignore() { - if let PassMode::Cast { cast: ref ty, .. } = fn_abi.ret.mode { - let ptr_llty = self.type_ptr_to(ty.gcc_type(self)); - let ptr = self.pointercast(result.val.llval, ptr_llty); - self.store(value, ptr, result.val.align); - } else { - OperandRef::from_immediate_or_packed_pair(self, value, result.layout) - .val - .store(self, result); - } + if result.layout.ty.is_bool() { + OperandRef::from_immediate_or_packed_pair(self, value, result.layout) + .val + .store(self, result); + } else if !result.layout.ty.is_unit() { + let ptr_llty = self.type_ptr_to(result.layout.gcc_type(self)); + let ptr = self.pointercast(result.val.llval, ptr_llty); + self.store(value, ptr, result.val.align); } Ok(()) } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index 067dd9b1681f2..e8629aeebb95a 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -15,11 +15,10 @@ use rustc_middle::ty::{self, GenericArgsRef, Ty}; use rustc_middle::{bug, span_bug}; use rustc_span::{Span, Symbol, sym}; use rustc_symbol_mangling::mangle_internal_symbol; -use rustc_target::callconv::{FnAbi, PassMode}; use rustc_target::spec::{HasTargetSpec, PanicStrategy}; use tracing::debug; -use crate::abi::{FnAbiLlvmExt, LlvmType}; +use crate::abi::FnAbiLlvmExt; use crate::builder::Builder; use crate::context::CodegenCx; use crate::llvm::{self, Metadata}; @@ -165,7 +164,6 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { fn codegen_intrinsic_call( &mut self, instance: ty::Instance<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, &'ll Value>], result: PlaceRef<'tcx, &'ll Value>, span: Span, @@ -263,7 +261,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.call_intrinsic("llvm.va_copy", &[args[0].immediate(), args[1].immediate()]) } sym::va_arg => { - match fn_abi.ret.layout.backend_repr { + match result.layout.backend_repr { BackendRepr::Scalar(scalar) => { match scalar.primitive() { Primitive::Int(..) => { @@ -298,18 +296,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } sym::volatile_load | sym::unaligned_volatile_load => { - let tp_ty = fn_args.type_at(0); let ptr = args[0].immediate(); - let load = if let PassMode::Cast { cast: ty, pad_i32: _ } = &fn_abi.ret.mode { - let llty = ty.llvm_type(self); - self.volatile_load(llty, ptr) - } else { - self.volatile_load(self.layout_of(tp_ty).llvm_type(self), ptr) - }; + let load = self.volatile_load(result.layout.llvm_type(self), ptr); let align = if name == sym::unaligned_volatile_load { 1 } else { - self.align_of(tp_ty).bytes() as u32 + result.layout.align.abi.bytes() as u32 }; unsafe { llvm::LLVMSetAlignment(load, align); @@ -628,14 +620,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } }; - if !fn_abi.ret.is_ignore() { - if let PassMode::Cast { .. } = &fn_abi.ret.mode { - self.store(llval, result.val.llval, result.val.align); - } else { - OperandRef::from_immediate_or_packed_pair(self, llval, result.layout) - .val - .store(self, result); - } + if result.layout.ty.is_bool() { + OperandRef::from_immediate_or_packed_pair(self, llval, result.layout) + .val + .store(self, result); + } else if !result.layout.ty.is_unit() { + self.store_to_place(llval, result.val); } Ok(()) } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index 1a291b17886cc..a2565c4f69d3a 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -967,14 +967,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let result = PlaceRef::new_sized(dest, fn_abi.ret.layout); - match self.codegen_intrinsic_call( - bx, - instance, - fn_abi, - &args, - result, - source_info, - ) { + match self.codegen_intrinsic_call(bx, instance, &args, result, source_info) + { Ok(()) => { if let ReturnDest::IndirectOperand(dst, _) = ret_dest { self.store_return(bx, ret_dest, &fn_abi.ret, dst.val.llval); diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 6a0778832981b..a6d159c51e132 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -4,7 +4,6 @@ use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; use rustc_span::sym; -use rustc_target::callconv::{FnAbi, PassMode}; use super::FunctionCx; use super::operand::OperandRef; @@ -56,7 +55,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { &mut self, bx: &mut Bx, instance: ty::Instance<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, Bx::Value>], result: PlaceRef<'tcx, Bx::Value>, source_info: SourceInfo, @@ -536,18 +534,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { _ => { // Need to use backend-specific things in the implementation. - return bx.codegen_intrinsic_call(instance, fn_abi, args, result, span); + return bx.codegen_intrinsic_call(instance, args, result, span); } }; - if !fn_abi.ret.is_ignore() { - if let PassMode::Cast { .. } = &fn_abi.ret.mode { - bx.store_to_place(llval, result.val); - } else { - OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) - .val - .store(bx, result); - } + if result.layout.ty.is_bool() { + OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) + .val + .store(bx, result); + } else if !result.layout.ty.is_unit() { + bx.store_to_place(llval, result.val); } Ok(()) } diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index f9d6dab6fafc3..a07c569a03237 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -1,6 +1,5 @@ -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty; use rustc_span::Span; -use rustc_target::callconv::FnAbi; use super::BackendTypes; use crate::mir::operand::OperandRef; @@ -15,7 +14,6 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { fn codegen_intrinsic_call( &mut self, instance: ty::Instance<'tcx>, - fn_abi: &FnAbi<'tcx, Ty<'tcx>>, args: &[OperandRef<'tcx, Self::Value>], result: PlaceRef<'tcx, Self::Value>, span: Span, From 7122648e3403bbbd471cd2667d5960a109bb9bd7 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 22 May 2025 17:49:28 +0000 Subject: [PATCH 15/26] Don't depend on FnAbi for intrinsics Intrinsics are not real functions and as such don't have any calling convention. Trying to compute a calling convention for an intrinsic anyway is a nonsensical operation. --- compiler/rustc_codegen_ssa/src/mir/block.rs | 74 ++++++++++++--------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index a2565c4f69d3a..cb8088e9efba7 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -8,7 +8,7 @@ use rustc_hir::lang_items::LangItem; use rustc_middle::mir::{self, AssertKind, InlineAsmMacro, SwitchTargets, UnwindTerminateReason}; use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement}; use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths}; -use rustc_middle::ty::{self, Instance, List, Ty}; +use rustc_middle::ty::{self, Instance, Ty}; use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; use rustc_span::Span; @@ -941,37 +941,55 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { return merging_succ; } - let fn_abi = bx.fn_abi_of_instance(instance, List::empty()); + let result_layout = + self.cx.layout_of(self.monomorphized_place_ty(destination.as_ref())); - let mut llargs = Vec::with_capacity(1); - let ret_dest = self.make_return_dest( - bx, - destination, - &fn_abi.ret, - &mut llargs, - Some(intrinsic), - ); - let dest = match ret_dest { - _ if fn_abi.ret.is_indirect() => llargs[0], - ReturnDest::Nothing => bx.const_undef(bx.type_ptr()), - ReturnDest::IndirectOperand(dst, _) | ReturnDest::Store(dst) => { - dst.val.llval - } - ReturnDest::DirectOperand(_) => { - bug!("Cannot use direct operand with an intrinsic call") + let (result, store_in_local) = if result_layout.is_zst() { + ( + PlaceRef::new_sized(bx.const_undef(bx.type_ptr()), result_layout), + None, + ) + } else if let Some(local) = destination.as_local() { + match self.locals[local] { + LocalRef::Place(dest) => (dest, None), + LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), + LocalRef::PendingOperand => { + // Currently, intrinsics always need a location to store + // the result, so we create a temporary `alloca` for the + // result. + let tmp = PlaceRef::alloca(bx, result_layout); + tmp.storage_live(bx); + (tmp, Some(local)) + } + LocalRef::Operand(_) => { + bug!("place local already assigned to"); + } } + } else { + (self.codegen_place(bx, destination.as_ref()), None) }; + if result.val.align < result.layout.align.abi { + // Currently, MIR code generation does not create calls + // that store directly to fields of packed structs (in + // fact, the calls it creates write only to temps). + // + // If someone changes that, please update this code path + // to create a temporary. + span_bug!(self.mir.span, "can't directly store to unaligned value"); + } + let args: Vec<_> = args.iter().map(|arg| self.codegen_operand(bx, &arg.node)).collect(); - let result = PlaceRef::new_sized(dest, fn_abi.ret.layout); - match self.codegen_intrinsic_call(bx, instance, &args, result, source_info) { Ok(()) => { - if let ReturnDest::IndirectOperand(dst, _) = ret_dest { - self.store_return(bx, ret_dest, &fn_abi.ret, dst.val.llval); + if let Some(local) = store_in_local { + let op = bx.load_operand(result); + result.storage_dead(bx); + self.overwrite_local(local, LocalRef::Operand(op)); + self.debug_introduce_local(bx, local); } return if let Some(target) = target { @@ -1026,7 +1044,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // We still need to call `make_return_dest` even if there's no `target`, since // `fn_abi.ret` could be `PassMode::Indirect`, even if it is uninhabited, // and `make_return_dest` adds the return-place indirect pointer to `llargs`. - let return_dest = self.make_return_dest(bx, destination, &fn_abi.ret, &mut llargs, None); + let return_dest = self.make_return_dest(bx, destination, &fn_abi.ret, &mut llargs); let destination = target.map(|target| (return_dest, target)); // Split the rust-call tupled arguments off. @@ -1857,7 +1875,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { dest: mir::Place<'tcx>, fn_ret: &ArgAbi<'tcx, Ty<'tcx>>, llargs: &mut Vec, - intrinsic: Option, ) -> ReturnDest<'tcx, Bx::Value> { // If the return is ignored, we can just return a do-nothing `ReturnDest`. if fn_ret.is_ignore() { @@ -1877,13 +1894,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { tmp.storage_live(bx); llargs.push(tmp.val.llval); ReturnDest::IndirectOperand(tmp, index) - } else if intrinsic.is_some() { - // Currently, intrinsics always need a location to store - // the result, so we create a temporary `alloca` for the - // result. - let tmp = PlaceRef::alloca(bx, fn_ret.layout); - tmp.storage_live(bx); - ReturnDest::IndirectOperand(tmp, index) } else { ReturnDest::DirectOperand(index) }; @@ -1893,7 +1903,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } } else { - self.codegen_place(bx, mir::PlaceRef { local: dest.local, projection: dest.projection }) + self.codegen_place(bx, dest.as_ref()) }; if fn_ret.is_indirect() { if dest.val.align < dest.layout.align.abi { From 165fb988499b771afb7c2ddb60f05e9e337afb6a Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Fri, 23 May 2025 08:30:58 +0000 Subject: [PATCH 16/26] Reduce indentation in codegen_panic_intrinsic --- compiler/rustc_codegen_ssa/src/mir/block.rs | 96 ++++++++++----------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index cb8088e9efba7..1baab62ae43ac 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -836,58 +836,56 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // Emit a panic or a no-op for `assert_*` intrinsics. // These are intrinsics that compile to panics so that we can get a message // which mentions the offending type, even from a const context. - if let Some(requirement) = ValidityRequirement::from_intrinsic(intrinsic.name) { - let ty = instance.args.type_at(0); - - let do_panic = !bx - .tcx() - .check_validity_requirement((requirement, bx.typing_env().as_query_input(ty))) - .expect("expect to have layout during codegen"); - - let layout = bx.layout_of(ty); - - Some(if do_panic { - let msg_str = with_no_visible_paths!({ - with_no_trimmed_paths!({ - if layout.is_uninhabited() { - // Use this error even for the other intrinsics as it is more precise. - format!("attempted to instantiate uninhabited type `{ty}`") - } else if requirement == ValidityRequirement::Zero { - format!("attempted to zero-initialize type `{ty}`, which is invalid") - } else { - format!( - "attempted to leave type `{ty}` uninitialized, which is invalid" - ) - } - }) - }); - let msg = bx.const_str(&msg_str); + let Some(requirement) = ValidityRequirement::from_intrinsic(intrinsic.name) else { + return None; + }; - // Obtain the panic entry point. - let (fn_abi, llfn, instance) = - common::build_langcall(bx, Some(source_info.span), LangItem::PanicNounwind); + let ty = instance.args.type_at(0); - // Codegen the actual panic invoke/call. - helper.do_call( - self, - bx, - fn_abi, - llfn, - &[msg.0, msg.1], - target.as_ref().map(|bb| (ReturnDest::Nothing, *bb)), - unwind, - &[], - Some(instance), - mergeable_succ, - ) - } else { - // a NOP - let target = target.unwrap(); - helper.funclet_br(self, bx, target, mergeable_succ) - }) - } else { - None + let is_valid = bx + .tcx() + .check_validity_requirement((requirement, bx.typing_env().as_query_input(ty))) + .expect("expect to have layout during codegen"); + + if is_valid { + // a NOP + let target = target.unwrap(); + return Some(helper.funclet_br(self, bx, target, mergeable_succ)); } + + let layout = bx.layout_of(ty); + + let msg_str = with_no_visible_paths!({ + with_no_trimmed_paths!({ + if layout.is_uninhabited() { + // Use this error even for the other intrinsics as it is more precise. + format!("attempted to instantiate uninhabited type `{ty}`") + } else if requirement == ValidityRequirement::Zero { + format!("attempted to zero-initialize type `{ty}`, which is invalid") + } else { + format!("attempted to leave type `{ty}` uninitialized, which is invalid") + } + }) + }); + let msg = bx.const_str(&msg_str); + + // Obtain the panic entry point. + let (fn_abi, llfn, instance) = + common::build_langcall(bx, Some(source_info.span), LangItem::PanicNounwind); + + // Codegen the actual panic invoke/call. + Some(helper.do_call( + self, + bx, + fn_abi, + llfn, + &[msg.0, msg.1], + target.as_ref().map(|bb| (ReturnDest::Nothing, *bb)), + unwind, + &[], + Some(instance), + mergeable_succ, + )) } fn codegen_call_terminator( From 7aef56d9b938a0df077c346007921497c092fdc9 Mon Sep 17 00:00:00 2001 From: Jacob Pratt Date: Mon, 26 May 2025 15:06:36 -0400 Subject: [PATCH 17/26] Call out possibility of invariant result --- library/core/src/marker/variance.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/core/src/marker/variance.rs b/library/core/src/marker/variance.rs index 235f8a3bb79f2..f9638fea225b7 100644 --- a/library/core/src/marker/variance.rs +++ b/library/core/src/marker/variance.rs @@ -131,6 +131,8 @@ phantom_lifetime! { /// /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance /// + /// Note: If `'a` is otherwise contravariant or invariant, the resulting type is invariant. + /// /// ## Layout /// /// For all `'a`, the following are guaranteed: @@ -146,6 +148,8 @@ phantom_lifetime! { /// /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance /// + /// Note: If `'a` is otherwise covariant or invariant, the resulting type is invariant. + /// /// ## Layout /// /// For all `'a`, the following are guaranteed: @@ -180,6 +184,8 @@ phantom_type! { /// /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance /// + /// Note: If `T` is otherwise contravariant or invariant, the resulting type is invariant. + /// /// ## Layout /// /// For all `T`, the following are guaranteed: @@ -196,6 +202,8 @@ phantom_type! { /// /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance /// + /// Note: If `T` is otherwise covariant or invariant, the resulting type is invariant. + /// /// ## Layout /// /// For all `T`, the following are guaranteed: From c6c2fde737e5d6730cc7135afd841a0afed6096e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Dom=C3=ADnguez?= Date: Mon, 26 May 2025 19:47:42 +0000 Subject: [PATCH 18/26] Minor macro docs fixes --- library/core/src/macros/mod.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 99a4ab52b6c57..fac1b1ae6123a 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -1519,8 +1519,9 @@ pub(crate) mod builtin { ($file:expr $(,)?) => {{ /* compiler built-in */ }}; } - /// the derivative of a given function in the forward mode of differentiation. - /// It may only be applied to a function. + /// This macro uses forward-mode automatic differentiation to generate a new function. + /// It may only be applied to a function. The new function will compute the derivative + /// of the function to which the macro was applied. /// /// The expected usage syntax is: /// `#[autodiff_forward(NAME, INPUT_ACTIVITIES, OUTPUT_ACTIVITY)]` @@ -1537,9 +1538,9 @@ pub(crate) mod builtin { /* compiler built-in */ } - /// Automatic Differentiation macro which allows generating a new function to compute - /// the derivative of a given function in the reverse mode of differentiation. - /// It may only be applied to a function. + /// This macro uses reverse-mode automatic differentiation to generate a new function. + /// It may only be applied to a function. The new function will compute the derivative + /// of the function to which the macro was applied. /// /// The expected usage syntax is: /// `#[autodiff_reverse(NAME, INPUT_ACTIVITIES, OUTPUT_ACTIVITY)]` From df09feddfa7900c841877faa3cda86bfc08d9610 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 20 May 2025 16:39:59 +1000 Subject: [PATCH 19/26] Remove `DropNodeKey::kind`. It's not needed, because `next` and `local` fields uniquely identify the drop. This is a ~2% speed win on the very large program in #134404, and it's also a tiny bit simpler. --- compiler/rustc_mir_build/src/builder/scope.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 2a30777e98c4f..bc39ad6b267ce 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -230,7 +230,6 @@ struct DropNode { struct DropNodeKey { next: DropIdx, local: Local, - kind: DropKind, } impl Scope { @@ -291,7 +290,7 @@ impl DropTree { let drops = &mut self.drops; *self .existing_drops_map - .entry(DropNodeKey { next, local: data.local, kind: data.kind }) + .entry(DropNodeKey { next, local: data.local }) // Create a new node, and also add its index to the map. .or_insert_with(|| drops.push(DropNode { data, next })) } From bf4532c05d2537c091de6787b8bb80e2cd24cd63 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 21 May 2025 05:46:03 +1000 Subject: [PATCH 20/26] Rename `DropTree::drops` as `DropTree::drop_nodes`. Because `Scope` also has a field named `drops`, and I found having two fields with the same name made this code harder to read. --- compiler/rustc_mir_build/src/builder/scope.rs | 80 +++++++++++-------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index bc39ad6b267ce..d7900cb0845eb 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -209,7 +209,7 @@ const ROOT_NODE: DropIdx = DropIdx::ZERO; #[derive(Debug)] struct DropTree { /// Nodes in the drop tree, containing drop data and a link to the next node. - drops: IndexVec, + drop_nodes: IndexVec, /// Map for finding the index of an existing node, given its contents. existing_drops_map: FxHashMap, /// Edges into the `DropTree` that need to be added once it's lowered. @@ -277,8 +277,8 @@ impl DropTree { let fake_source_info = SourceInfo::outermost(DUMMY_SP); let fake_data = DropData { source_info: fake_source_info, local: Local::MAX, kind: DropKind::Storage }; - let drops = IndexVec::from_raw(vec![DropNode { data: fake_data, next: DropIdx::MAX }]); - Self { drops, entry_points: Vec::new(), existing_drops_map: FxHashMap::default() } + let drop_nodes = IndexVec::from_raw(vec![DropNode { data: fake_data, next: DropIdx::MAX }]); + Self { drop_nodes, entry_points: Vec::new(), existing_drops_map: FxHashMap::default() } } /// Adds a node to the drop tree, consisting of drop data and the index of @@ -287,12 +287,12 @@ impl DropTree { /// If there is already an equivalent node in the tree, nothing is added, and /// that node's index is returned. Otherwise, the new node's index is returned. fn add_drop(&mut self, data: DropData, next: DropIdx) -> DropIdx { - let drops = &mut self.drops; + let drop_nodes = &mut self.drop_nodes; *self .existing_drops_map .entry(DropNodeKey { next, local: data.local }) // Create a new node, and also add its index to the map. - .or_insert_with(|| drops.push(DropNode { data, next })) + .or_insert_with(|| drop_nodes.push(DropNode { data, next })) } /// Registers `from` as an entry point to this drop tree, at `to`. @@ -300,7 +300,7 @@ impl DropTree { /// During [`Self::build_mir`], `from` will be linked to the corresponding /// block within the drop tree. fn add_entry_point(&mut self, from: BasicBlock, to: DropIdx) { - debug_assert!(to < self.drops.next_index()); + debug_assert!(to < self.drop_nodes.next_index()); self.entry_points.push((to, from)); } @@ -340,10 +340,10 @@ impl DropTree { Own, } - let mut blocks = IndexVec::from_elem(None, &self.drops); + let mut blocks = IndexVec::from_elem(None, &self.drop_nodes); blocks[ROOT_NODE] = root_node; - let mut needs_block = IndexVec::from_elem(Block::None, &self.drops); + let mut needs_block = IndexVec::from_elem(Block::None, &self.drop_nodes); if root_node.is_some() { // In some cases (such as drops for `continue`) the root node // already has a block. In this case, make sure that we don't @@ -355,7 +355,7 @@ impl DropTree { let entry_points = &mut self.entry_points; entry_points.sort(); - for (drop_idx, drop_node) in self.drops.iter_enumerated().rev() { + for (drop_idx, drop_node) in self.drop_nodes.iter_enumerated().rev() { if entry_points.last().is_some_and(|entry_point| entry_point.0 == drop_idx) { let block = *blocks[drop_idx].get_or_insert_with(|| T::make_block(cfg)); needs_block[drop_idx] = Block::Own; @@ -395,7 +395,7 @@ impl DropTree { cfg: &mut CFG<'tcx>, blocks: &IndexSlice>, ) { - for (drop_idx, drop_node) in self.drops.iter_enumerated().rev() { + for (drop_idx, drop_node) in self.drop_nodes.iter_enumerated().rev() { let Some(block) = blocks[drop_idx] else { continue }; match drop_node.data.kind { DropKind::Value => { @@ -828,9 +828,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // `unwind_to` should drop the value that we're about to // schedule. If dropping this value panics, then we continue // with the *next* value on the unwind path. - debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local); - debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); - unwind_to = unwind_drops.drops[unwind_to].next; + debug_assert_eq!( + unwind_drops.drop_nodes[unwind_to].data.local, + drop_data.local + ); + debug_assert_eq!( + unwind_drops.drop_nodes[unwind_to].data.kind, + drop_data.kind + ); + unwind_to = unwind_drops.drop_nodes[unwind_to].next; let mut unwind_entry_point = unwind_to; @@ -1550,14 +1556,14 @@ where // // We adjust this BEFORE we create the drop (e.g., `drops[n]`) // because `drops[n]` should unwind to `drops[n-1]`. - debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local); - debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); - unwind_to = unwind_drops.drops[unwind_to].next; + debug_assert_eq!(unwind_drops.drop_nodes[unwind_to].data.local, drop_data.local); + debug_assert_eq!(unwind_drops.drop_nodes[unwind_to].data.kind, drop_data.kind); + unwind_to = unwind_drops.drop_nodes[unwind_to].next; if let Some(idx) = dropline_to { - debug_assert_eq!(coroutine_drops.drops[idx].data.local, drop_data.local); - debug_assert_eq!(coroutine_drops.drops[idx].data.kind, drop_data.kind); - dropline_to = Some(coroutine_drops.drops[idx].next); + debug_assert_eq!(coroutine_drops.drop_nodes[idx].data.local, drop_data.local); + debug_assert_eq!(coroutine_drops.drop_nodes[idx].data.kind, drop_data.kind); + dropline_to = Some(coroutine_drops.drop_nodes[idx].next); } // If the operand has been moved, and we are not on an unwind @@ -1597,9 +1603,12 @@ where // cases we emit things ALSO on the unwind path, so we need to adjust // `unwind_to` in that case. if storage_dead_on_unwind { - debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local); - debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); - unwind_to = unwind_drops.drops[unwind_to].next; + debug_assert_eq!( + unwind_drops.drop_nodes[unwind_to].data.local, + drop_data.local + ); + debug_assert_eq!(unwind_drops.drop_nodes[unwind_to].data.kind, drop_data.kind); + unwind_to = unwind_drops.drop_nodes[unwind_to].next; } // If the operand has been moved, and we are not on an unwind @@ -1628,14 +1637,17 @@ where // the storage-dead has completed, we need to adjust the `unwind_to` pointer // so that any future drops we emit will not register storage-dead. if storage_dead_on_unwind { - debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local); - debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); - unwind_to = unwind_drops.drops[unwind_to].next; + debug_assert_eq!( + unwind_drops.drop_nodes[unwind_to].data.local, + drop_data.local + ); + debug_assert_eq!(unwind_drops.drop_nodes[unwind_to].data.kind, drop_data.kind); + unwind_to = unwind_drops.drop_nodes[unwind_to].next; } if let Some(idx) = dropline_to { - debug_assert_eq!(coroutine_drops.drops[idx].data.local, drop_data.local); - debug_assert_eq!(coroutine_drops.drops[idx].data.kind, drop_data.kind); - dropline_to = Some(coroutine_drops.drops[idx].next); + debug_assert_eq!(coroutine_drops.drop_nodes[idx].data.local, drop_data.local); + debug_assert_eq!(coroutine_drops.drop_nodes[idx].data.kind, drop_data.kind); + dropline_to = Some(coroutine_drops.drop_nodes[idx].next); } // Only temps and vars need their storage dead. assert!(local.index() > arg_count); @@ -1662,10 +1674,10 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { let is_coroutine = self.coroutine.is_some(); // Link the exit drop tree to unwind drop tree. - if drops.drops.iter().any(|drop_node| drop_node.data.kind == DropKind::Value) { + if drops.drop_nodes.iter().any(|drop_node| drop_node.data.kind == DropKind::Value) { let unwind_target = self.diverge_cleanup_target(else_scope, span); let mut unwind_indices = IndexVec::from_elem_n(unwind_target, 1); - for (drop_idx, drop_node) in drops.drops.iter_enumerated().skip(1) { + for (drop_idx, drop_node) in drops.drop_nodes.iter_enumerated().skip(1) { match drop_node.data.kind { DropKind::Storage | DropKind::ForLint => { if is_coroutine { @@ -1694,13 +1706,13 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { } // Link the exit drop tree to dropline drop tree (coroutine drop path) for async drops if is_coroutine - && drops.drops.iter().any(|DropNode { data, next: _ }| { + && drops.drop_nodes.iter().any(|DropNode { data, next: _ }| { data.kind == DropKind::Value && self.is_async_drop(data.local) }) { let dropline_target = self.diverge_dropline_target(else_scope, span); let mut dropline_indices = IndexVec::from_elem_n(dropline_target, 1); - for (drop_idx, drop_data) in drops.drops.iter_enumerated().skip(1) { + for (drop_idx, drop_data) in drops.drop_nodes.iter_enumerated().skip(1) { match drop_data.data.kind { DropKind::Storage | DropKind::ForLint => { let coroutine_drop = self @@ -1768,11 +1780,11 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { // prevent drop elaboration from creating drop flags that would have // to be captured by the coroutine. I'm not sure how important this // optimization is, but it is here. - for (drop_idx, drop_node) in drops.drops.iter_enumerated() { + for (drop_idx, drop_node) in drops.drop_nodes.iter_enumerated() { if let DropKind::Value = drop_node.data.kind && let Some(bb) = blocks[drop_idx] { - debug_assert!(drop_node.next < drops.drops.next_index()); + debug_assert!(drop_node.next < drops.drop_nodes.next_index()); drops.entry_points.push((drop_node.next, bb)); } } From 84bb48fc886d5de72c86fb56c11f8586ca465647 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 21 May 2025 05:48:40 +1000 Subject: [PATCH 21/26] Factor out some repeated code in `build_exit_tree`. --- compiler/rustc_mir_build/src/builder/scope.rs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index d7900cb0845eb..28d72f73155a0 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -1713,28 +1713,22 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { let dropline_target = self.diverge_dropline_target(else_scope, span); let mut dropline_indices = IndexVec::from_elem_n(dropline_target, 1); for (drop_idx, drop_data) in drops.drop_nodes.iter_enumerated().skip(1) { + let coroutine_drop = self + .scopes + .coroutine_drops + .add_drop(drop_data.data, dropline_indices[drop_data.next]); match drop_data.data.kind { - DropKind::Storage | DropKind::ForLint => { - let coroutine_drop = self - .scopes - .coroutine_drops - .add_drop(drop_data.data, dropline_indices[drop_data.next]); - dropline_indices.push(coroutine_drop); - } + DropKind::Storage | DropKind::ForLint => {} DropKind::Value => { - let coroutine_drop = self - .scopes - .coroutine_drops - .add_drop(drop_data.data, dropline_indices[drop_data.next]); if self.is_async_drop(drop_data.data.local) { self.scopes.coroutine_drops.add_entry_point( blocks[drop_idx].unwrap(), dropline_indices[drop_data.next], ); } - dropline_indices.push(coroutine_drop); } } + dropline_indices.push(coroutine_drop); } } blocks[ROOT_NODE].map(BasicBlock::unit) From ec8baa5d43903a6f39ebc36370120db129a52deb Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 21 May 2025 05:54:13 +1000 Subject: [PATCH 22/26] Avoid `fold`/`flat_map`. This pattern of iterating over scopes and drops occurs multiple times in this file, with slight variations. All of them use `for` loops except this one. This commits changes it for consistency. --- compiler/rustc_mir_build/src/builder/scope.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 28d72f73155a0..67988f1fcbc20 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -725,11 +725,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { drops }; - let drop_idx = self.scopes.scopes[scope_index + 1..] - .iter() - .flat_map(|scope| &scope.drops) - .fold(ROOT_NODE, |drop_idx, &drop| drops.add_drop(drop, drop_idx)); - + let mut drop_idx = ROOT_NODE; + for scope in &self.scopes.scopes[scope_index + 1..] { + for drop in &scope.drops { + drop_idx = drops.add_drop(*drop, drop_idx); + } + } drops.add_entry_point(block, drop_idx); // `build_drop_trees` doesn't have access to our source_info, so we From f83ecd82701923c38f847d290a4e0859d8cf0126 Mon Sep 17 00:00:00 2001 From: Mu001999 Date: Fri, 23 May 2025 03:51:15 +0800 Subject: [PATCH 23/26] Refactor the two-phase check for impls and impl items --- compiler/rustc_middle/src/query/mod.rs | 2 +- compiler/rustc_passes/src/dead.rs | 250 +++++++++--------- tests/ui/derives/clone-debug-dead-code.stderr | 2 +- tests/ui/deriving/deriving-in-macro.rs | 3 +- tests/ui/lint/dead-code/issue-41883.stderr | 2 + ...tiple-dead-codes-in-the-same-struct.stderr | 2 + 6 files changed, 128 insertions(+), 133 deletions(-) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 30245bc82d427..279033ee0724c 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1137,7 +1137,7 @@ rustc_queries! { /// their respective impl (i.e., part of the derive macro) query live_symbols_and_ignored_derived_traits(_: ()) -> &'tcx ( LocalDefIdSet, - LocalDefIdMap> + LocalDefIdMap> ) { arena_cache desc { "finding live symbols in crate" } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 6e5357d800743..f83c7471770ab 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -8,12 +8,13 @@ use std::mem; use hir::ItemKind; use hir::def_id::{LocalDefIdMap, LocalDefIdSet}; use rustc_abi::FieldIdx; +use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::unord::UnordSet; use rustc_errors::MultiSpan; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{self as hir, Node, PatKind, TyKind}; +use rustc_hir::{self as hir, Node, PatKind, QPath, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; @@ -44,15 +45,20 @@ fn should_explore(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { ) } -fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> bool { - if let TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind - && let Res::Def(def_kind, def_id) = path.res - && def_id.is_local() - && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) - { - tcx.visibility(def_id).is_public() - } else { - true +/// Returns the local def id of the ADT if the given ty refers to a local one. +fn local_adt_def_of_ty<'tcx>(ty: &hir::Ty<'tcx>) -> Option { + match ty.kind { + TyKind::Path(QPath::Resolved(_, path)) => { + if let Res::Def(def_kind, def_id) = path.res + && let Some(local_def_id) = def_id.as_local() + && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) + { + Some(local_def_id) + } else { + None + } + } + _ => None, } } @@ -78,7 +84,7 @@ struct MarkSymbolVisitor<'tcx> { // maps from ADTs to ignored derived traits (e.g. Debug and Clone) // and the span of their respective impl (i.e., part of the derive // macro) - ignored_derived_traits: LocalDefIdMap>, + ignored_derived_traits: LocalDefIdMap>, } impl<'tcx> MarkSymbolVisitor<'tcx> { @@ -360,7 +366,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { && let Some(fn_sig) = self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id)) && matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None) - && let TyKind::Path(hir::QPath::Resolved(_, path)) = + && let TyKind::Path(QPath::Resolved(_, path)) = self.tcx.hir_expect_item(local_impl_of).expect_impl().self_ty.kind && let Res::Def(def_kind, did) = path.res { @@ -388,7 +394,7 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { self.ignored_derived_traits .entry(adt_def_id) .or_default() - .push((trait_of, impl_of)); + .insert((trait_of, impl_of)); } return true; } @@ -420,51 +426,22 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { intravisit::walk_item(self, item) } hir::ItemKind::ForeignMod { .. } => {} - hir::ItemKind::Trait(..) => { - for &impl_def_id in self.tcx.local_trait_impls(item.owner_id.def_id) { - if let ItemKind::Impl(impl_ref) = self.tcx.hir_expect_item(impl_def_id).kind - { - // skip items - // mark dependent traits live - intravisit::walk_generics(self, impl_ref.generics); - // mark dependent parameters live - intravisit::walk_path(self, impl_ref.of_trait.unwrap().path); + hir::ItemKind::Trait(.., trait_item_refs) => { + // mark assoc ty live if the trait is live + for trait_item in trait_item_refs { + if matches!(trait_item.kind, hir::AssocItemKind::Type) { + self.check_def_id(trait_item.id.owner_id.to_def_id()); } } - intravisit::walk_item(self, item) } _ => intravisit::walk_item(self, item), }, Node::TraitItem(trait_item) => { - // mark corresponding ImplTerm live + // mark the trait live let trait_item_id = trait_item.owner_id.to_def_id(); if let Some(trait_id) = self.tcx.trait_of_item(trait_item_id) { - // mark the trait live self.check_def_id(trait_id); - - for impl_id in self.tcx.all_impls(trait_id) { - if let Some(local_impl_id) = impl_id.as_local() - && let ItemKind::Impl(impl_ref) = - self.tcx.hir_expect_item(local_impl_id).kind - { - if !matches!(trait_item.kind, hir::TraitItemKind::Type(..)) - && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty) - { - // skip methods of private ty, - // they would be solved in `solve_rest_impl_items` - continue; - } - - // mark self_ty live - intravisit::walk_unambig_ty(self, impl_ref.self_ty); - if let Some(&impl_item_id) = - self.tcx.impl_item_implementor_ids(impl_id).get(&trait_item_id) - { - self.check_def_id(impl_item_id); - } - } - } } intravisit::walk_trait_item(self, trait_item); } @@ -508,48 +485,58 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { } } - fn solve_rest_impl_items(&mut self, mut unsolved_impl_items: Vec<(hir::ItemId, LocalDefId)>) { - let mut ready; - (ready, unsolved_impl_items) = - unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| { - self.impl_item_with_used_self(impl_id, impl_item_id) - }); - - while !ready.is_empty() { - self.worklist = - ready.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect(); - self.mark_live_symbols(); - - (ready, unsolved_impl_items) = - unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| { - self.impl_item_with_used_self(impl_id, impl_item_id) - }); + /// Returns whether `local_def_id` is potentially alive or not. + /// `local_def_id` points to an impl or an impl item, + /// both impl and impl item that may be passed to this function are of a trait, + /// and added into the unsolved_items during `create_and_seed_worklist` + fn check_impl_or_impl_item_live( + &mut self, + impl_id: hir::ItemId, + local_def_id: LocalDefId, + ) -> bool { + if self.should_ignore_item(local_def_id.to_def_id()) { + return false; } - } - fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool { - if let TyKind::Path(hir::QPath::Resolved(_, path)) = - self.tcx.hir_item(impl_id).expect_impl().self_ty.kind - && let Res::Def(def_kind, def_id) = path.res - && let Some(local_def_id) = def_id.as_local() - && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union) - { - if self.tcx.visibility(impl_item_id).is_public() { - // for the public method, we don't know the trait item is used or not, - // so we mark the method live if the self is used - return self.live_symbols.contains(&local_def_id); + let trait_def_id = match self.tcx.def_kind(local_def_id) { + // assoc impl items of traits are live if the corresponding trait items are live + DefKind::AssocFn => self.tcx.associated_item(local_def_id).trait_item_def_id, + // impl items are live if the corresponding traits are live + DefKind::Impl { of_trait: true } => self + .tcx + .impl_trait_ref(impl_id.owner_id.def_id) + .and_then(|trait_ref| Some(trait_ref.skip_binder().def_id)), + _ => None, + }; + + if let Some(trait_def_id) = trait_def_id { + if let Some(trait_def_id) = trait_def_id.as_local() + && !self.live_symbols.contains(&trait_def_id) + { + return false; } - if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id - && let Some(local_id) = trait_item_id.as_local() + // FIXME: legacy logic to check whether the function may construct `Self`, + // this can be removed after supporting marking ADTs appearing in patterns + // as live, then we can check private impls of public traits directly + if let Some(fn_sig) = + self.tcx.hir_fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id)) + && matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None) + && self.tcx.visibility(trait_def_id).is_public() { - // for the private method, we can know the trait item is used or not, - // so we mark the method live if the self is used and the trait item is used - return self.live_symbols.contains(&local_id) - && self.live_symbols.contains(&local_def_id); + return true; } } - false + + // The impl or impl item is used if the corresponding trait or trait item is used and the ty is used. + if let Some(local_def_id) = + local_adt_def_of_ty(self.tcx.hir_item(impl_id).expect_impl().self_ty) + && !self.live_symbols.contains(&local_def_id) + { + return false; + } + + true } } @@ -584,7 +571,7 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { match expr.kind { - hir::ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(..)) => { + hir::ExprKind::Path(ref qpath @ QPath::TypeRelative(..)) => { let res = self.typeck_results().qpath_res(qpath, expr.hir_id); self.handle_res(res); } @@ -738,7 +725,7 @@ fn check_item<'tcx>( tcx: TyCtxt<'tcx>, worklist: &mut Vec<(LocalDefId, ComesFromAllowExpect)>, struct_constructors: &mut LocalDefIdMap, - unsolved_impl_items: &mut Vec<(hir::ItemId, LocalDefId)>, + unsolved_items: &mut Vec<(hir::ItemId, LocalDefId)>, id: hir::ItemId, ) { let allow_dead_code = has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id); @@ -764,41 +751,33 @@ fn check_item<'tcx>( } } DefKind::Impl { of_trait } => { - // get DefIds from another query - let local_def_ids = tcx - .associated_item_def_ids(id.owner_id) - .iter() - .filter_map(|def_id| def_id.as_local()); - - let ty_is_pub = ty_ref_to_pub_struct(tcx, tcx.hir_item(id).expect_impl().self_ty); - - // And we access the Map here to get HirId from LocalDefId - for local_def_id in local_def_ids { - // check the function may construct Self - let mut may_construct_self = false; - if let Some(fn_sig) = - tcx.hir_fn_sig_by_hir_id(tcx.local_def_id_to_hir_id(local_def_id)) - { - may_construct_self = - matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None); - } + if let Some(comes_from_allow) = + has_allow_dead_code_or_lang_attr(tcx, id.owner_id.def_id) + { + worklist.push((id.owner_id.def_id, comes_from_allow)); + } else if of_trait { + unsolved_items.push((id, id.owner_id.def_id)); + } - // for trait impl blocks, - // mark the method live if the self_ty is public, - // or the method is public and may construct self - if of_trait - && (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) - || tcx.visibility(local_def_id).is_public() - && (ty_is_pub || may_construct_self)) - { - worklist.push((local_def_id, ComesFromAllowExpect::No)); - } else if let Some(comes_from_allow) = - has_allow_dead_code_or_lang_attr(tcx, local_def_id) + for def_id in tcx.associated_item_def_ids(id.owner_id) { + let local_def_id = def_id.expect_local(); + + if let Some(comes_from_allow) = has_allow_dead_code_or_lang_attr(tcx, local_def_id) { worklist.push((local_def_id, comes_from_allow)); } else if of_trait { - // private method || public method not constructs self - unsolved_impl_items.push((id, local_def_id)); + // FIXME: This condition can be removed + // if we support dead check for assoc consts and tys. + if !matches!(tcx.def_kind(local_def_id), DefKind::AssocFn) { + worklist.push((local_def_id, ComesFromAllowExpect::No)); + } else { + // We only care about associated items of traits, + // because they cannot be visited directly, + // so we later mark them as live if their corresponding traits + // or trait items and self types are both live, + // but inherent associated items can be visited and marked directly. + unsolved_items.push((id, local_def_id)); + } } } } @@ -892,8 +871,8 @@ fn create_and_seed_worklist( fn live_symbols_and_ignored_derived_traits( tcx: TyCtxt<'_>, (): (), -) -> (LocalDefIdSet, LocalDefIdMap>) { - let (worklist, struct_constructors, unsolved_impl_items) = create_and_seed_worklist(tcx); +) -> (LocalDefIdSet, LocalDefIdMap>) { + let (worklist, struct_constructors, mut unsolved_items) = create_and_seed_worklist(tcx); let mut symbol_visitor = MarkSymbolVisitor { worklist, tcx, @@ -907,7 +886,22 @@ fn live_symbols_and_ignored_derived_traits( ignored_derived_traits: Default::default(), }; symbol_visitor.mark_live_symbols(); - symbol_visitor.solve_rest_impl_items(unsolved_impl_items); + let mut items_to_check; + (items_to_check, unsolved_items) = + unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| { + symbol_visitor.check_impl_or_impl_item_live(impl_id, local_def_id) + }); + + while !items_to_check.is_empty() { + symbol_visitor.worklist = + items_to_check.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect(); + symbol_visitor.mark_live_symbols(); + + (items_to_check, unsolved_items) = + unsolved_items.into_iter().partition(|&(impl_id, local_def_id)| { + symbol_visitor.check_impl_or_impl_item_live(impl_id, local_def_id) + }); + } (symbol_visitor.live_symbols, symbol_visitor.ignored_derived_traits) } @@ -921,7 +915,7 @@ struct DeadItem { struct DeadVisitor<'tcx> { tcx: TyCtxt<'tcx>, live_symbols: &'tcx LocalDefIdSet, - ignored_derived_traits: &'tcx LocalDefIdMap>, + ignored_derived_traits: &'tcx LocalDefIdMap>, } enum ShouldWarnAboutField { @@ -1188,19 +1182,15 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) { let def_kind = tcx.def_kind(item.owner_id); let mut dead_codes = Vec::new(); - // if we have diagnosed the trait, do not diagnose unused methods - if matches!(def_kind, DefKind::Impl { .. }) + // Only diagnose unused assoc items in inherient impl and used trait, + // for unused assoc items in impls of trait, + // we have diagnosed them in the trait if they are unused, + // for unused assoc items in unused trait, + // we have diagnosed the unused trait. + if matches!(def_kind, DefKind::Impl { of_trait: false }) || (def_kind == DefKind::Trait && live_symbols.contains(&item.owner_id.def_id)) { for &def_id in tcx.associated_item_def_ids(item.owner_id.def_id) { - // We have diagnosed unused methods in traits - if matches!(def_kind, DefKind::Impl { of_trait: true }) - && tcx.def_kind(def_id) == DefKind::AssocFn - || def_kind == DefKind::Trait && tcx.def_kind(def_id) != DefKind::AssocFn - { - continue; - } - if let Some(local_def_id) = def_id.as_local() && !visitor.is_live_code(local_def_id) { diff --git a/tests/ui/derives/clone-debug-dead-code.stderr b/tests/ui/derives/clone-debug-dead-code.stderr index 38be486e33207..34b7f929ec5ec 100644 --- a/tests/ui/derives/clone-debug-dead-code.stderr +++ b/tests/ui/derives/clone-debug-dead-code.stderr @@ -40,7 +40,7 @@ LL | struct D { f: () } | | | field in this struct | - = note: `D` has derived impls for the traits `Clone` and `Debug`, but these are intentionally ignored during dead code analysis + = note: `D` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis error: field `f` is never read --> $DIR/clone-debug-dead-code.rs:21:12 diff --git a/tests/ui/deriving/deriving-in-macro.rs b/tests/ui/deriving/deriving-in-macro.rs index 493c1415c7fa2..739d9b3068226 100644 --- a/tests/ui/deriving/deriving-in-macro.rs +++ b/tests/ui/deriving/deriving-in-macro.rs @@ -1,5 +1,6 @@ -//@ run-pass +//@ check-pass #![allow(non_camel_case_types)] +#![allow(dead_code)] macro_rules! define_vec { () => ( diff --git a/tests/ui/lint/dead-code/issue-41883.stderr b/tests/ui/lint/dead-code/issue-41883.stderr index 47ccef9a53069..cf079e4dda33a 100644 --- a/tests/ui/lint/dead-code/issue-41883.stderr +++ b/tests/ui/lint/dead-code/issue-41883.stderr @@ -29,6 +29,8 @@ error: struct `UnusedStruct` is never constructed | LL | struct UnusedStruct; | ^^^^^^^^^^^^ + | + = note: `UnusedStruct` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis error: aborting due to 4 previous errors diff --git a/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr b/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr index 25a7d96cb8978..b992005318f2e 100644 --- a/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr +++ b/tests/ui/lint/dead-code/multiple-dead-codes-in-the-same-struct.stderr @@ -56,6 +56,8 @@ warning: struct `Foo` is never constructed | LL | struct Foo(usize, #[allow(unused)] usize); | ^^^ + | + = note: `Foo` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis error: aborting due to 2 previous errors; 2 warnings emitted From 871327e9c7a6d44e233b31213858a224451afa74 Mon Sep 17 00:00:00 2001 From: binarycat Date: Thu, 22 May 2025 17:44:10 -0500 Subject: [PATCH 24/26] rustdoc: linking to a local proc macro no longer warns fixes https://github.com/rust-lang/rust/issues/91274 Co-authored-by: Guillaume Gomez --- .../passes/collect_intra_doc_links.rs | 36 ++++++++++++++----- .../intra-doc/bad-link-to-proc-macro.rs | 21 +++++++++++ .../intra-doc/bad-link-to-proc-macro.stderr | 22 ++++++++++++ tests/rustdoc/intra-doc/link-to-proc-macro.rs | 13 +++++++ 4 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 tests/rustdoc-ui/intra-doc/bad-link-to-proc-macro.rs create mode 100644 tests/rustdoc-ui/intra-doc/bad-link-to-proc-macro.stderr create mode 100644 tests/rustdoc/intra-doc/link-to-proc-macro.rs diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index f3e2138d1a575..b3a57ff3ff39f 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -22,6 +22,7 @@ use rustc_resolve::rustdoc::{ MalformedGenerics, has_primitive_or_keyword_docs, prepare_to_doc_link_resolution, source_span_for_markdown_range, strip_generics_from_path, }; +use rustc_session::config::CrateType; use rustc_session::lint::Lint; use rustc_span::BytePos; use rustc_span::hygiene::MacroKind; @@ -1174,7 +1175,6 @@ impl LinkCollector<'_, '_> { #[allow(rustc::potential_query_instability)] pub(crate) fn resolve_ambiguities(&mut self) { let mut ambiguous_links = mem::take(&mut self.ambiguous_links); - for ((item_id, path_str), info_items) in ambiguous_links.iter_mut() { for info in info_items { info.resolved.retain(|(res, _)| match res { @@ -2232,15 +2232,35 @@ fn ambiguity_error( emit_error: bool, ) -> bool { let mut descrs = FxHashSet::default(); - let kinds = candidates + // proc macro can exist in multiple namespaces at once, so we need to compare `DefIds` + // to remove the candidate in the fn namespace. + let mut possible_proc_macro_id = None; + let is_proc_macro_crate = cx.tcx.crate_types() == &[CrateType::ProcMacro]; + let mut kinds = candidates .iter() - .map( - |(res, def_id)| { - if let Some(def_id) = def_id { Res::from_def_id(cx.tcx, *def_id) } else { *res } - }, - ) - .filter(|res| descrs.insert(res.descr())) + .map(|(res, def_id)| { + let r = + if let Some(def_id) = def_id { Res::from_def_id(cx.tcx, *def_id) } else { *res }; + if is_proc_macro_crate && let Res::Def(DefKind::Macro(_), id) = r { + possible_proc_macro_id = Some(id); + } + r + }) .collect::>(); + // In order to properly dedup proc macros, we have to do it in two passes: + // 1. Completing the full traversal to find the possible duplicate in the macro namespace, + // 2. Another full traversal to eliminate the candidate in the fn namespace. + // + // Thus, we have to do an iteration after collection is finished. + // + // As an optimization, we only deduplicate if we're in a proc-macro crate, + // and only if we already found something that looks like a proc macro. + if is_proc_macro_crate && let Some(macro_id) = possible_proc_macro_id { + kinds.retain(|res| !matches!(res, Res::Def(DefKind::Fn, fn_id) if macro_id == *fn_id)); + } + + kinds.retain(|res| descrs.insert(res.descr())); + if descrs.len() == 1 { // There is no way for users to disambiguate at this point, so better return the first // candidate and not show a warning. diff --git a/tests/rustdoc-ui/intra-doc/bad-link-to-proc-macro.rs b/tests/rustdoc-ui/intra-doc/bad-link-to-proc-macro.rs new file mode 100644 index 0000000000000..b449465768ef7 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/bad-link-to-proc-macro.rs @@ -0,0 +1,21 @@ +//@ compile-flags: --crate-type=proc-macro --document-private-items +#![deny(rustdoc::broken_intra_doc_links)] + +//! Link to [`m`]. +//~^ ERROR `m` is both a module and a macro + +// test a further edge case related to https://github.com/rust-lang/rust/issues/91274 + +// we need to make sure that when there is actually an ambiguity +// in a proc-macro crate, we print out a sensible error. +// because proc macro crates can't normally export modules, +// this can only happen in --document-private-items mode. + +extern crate proc_macro; + +mod m {} + +#[proc_macro] +pub fn m(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + input +} diff --git a/tests/rustdoc-ui/intra-doc/bad-link-to-proc-macro.stderr b/tests/rustdoc-ui/intra-doc/bad-link-to-proc-macro.stderr new file mode 100644 index 0000000000000..09a5d26ededc9 --- /dev/null +++ b/tests/rustdoc-ui/intra-doc/bad-link-to-proc-macro.stderr @@ -0,0 +1,22 @@ +error: `m` is both a module and a macro + --> $DIR/bad-link-to-proc-macro.rs:4:15 + | +LL | //! Link to [`m`]. + | ^ ambiguous link + | +note: the lint level is defined here + --> $DIR/bad-link-to-proc-macro.rs:2:9 + | +LL | #![deny(rustdoc::broken_intra_doc_links)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: to link to the module, prefix with `mod@` + | +LL | //! Link to [`mod@m`]. + | ++++ +help: to link to the macro, add an exclamation mark + | +LL | //! Link to [`m!`]. + | + + +error: aborting due to 1 previous error + diff --git a/tests/rustdoc/intra-doc/link-to-proc-macro.rs b/tests/rustdoc/intra-doc/link-to-proc-macro.rs new file mode 100644 index 0000000000000..6c289078db864 --- /dev/null +++ b/tests/rustdoc/intra-doc/link-to-proc-macro.rs @@ -0,0 +1,13 @@ +//@ compile-flags: --crate-type=proc-macro +//@ has 'foo/index.html' '//a[@href="macro.my_macro.html"]' 'my_macro' +//! Link to [`my_macro`]. +#![crate_name = "foo"] + +// regression test for https://github.com/rust-lang/rust/issues/91274 + +extern crate proc_macro; + +#[proc_macro] +pub fn my_macro(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + input +} From e9080948c64cdb12014f11004a921a676fbc0964 Mon Sep 17 00:00:00 2001 From: bohan Date: Sun, 25 May 2025 23:14:59 +0800 Subject: [PATCH 25/26] consider glob imports in cfg suggestion --- compiler/rustc_expand/src/expand.rs | 6 +- compiler/rustc_resolve/src/diagnostics.rs | 50 +++++++++++- tests/ui/cfg/diagnostics-reexport-2.rs | 61 +++++++++++++++ tests/ui/cfg/diagnostics-reexport-2.stderr | 88 ++++++++++++++++++++++ 4 files changed, 200 insertions(+), 5 deletions(-) create mode 100644 tests/ui/cfg/diagnostics-reexport-2.rs create mode 100644 tests/ui/cfg/diagnostics-reexport-2.stderr diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 81d4d59ee0451..e5749ba96a6dd 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -1319,10 +1319,10 @@ impl InvocationCollectorNode for P { let mut idents = Vec::new(); collect_use_tree_leaves(ut, &mut idents); - return idents; + idents + } else { + self.kind.ident().into_iter().collect() } - - if let Some(ident) = self.kind.ident() { vec![ident] } else { vec![] } } } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index d09750fa281ba..201b1c0a493bc 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -6,7 +6,7 @@ use rustc_ast::{ }; use rustc_ast_pretty::pprust; use rustc_attr_data_structures::{self as attr, Stability}; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::unord::UnordSet; use rustc_errors::codes::*; use rustc_errors::{ @@ -2623,7 +2623,53 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; for &StrippedCfgItem { parent_module, ident, ref cfg } in symbols { - if parent_module != module || ident.name != *segment { + if ident.name != *segment { + continue; + } + + fn comes_from_same_module_for_glob( + r: &Resolver<'_, '_>, + parent_module: DefId, + module: DefId, + visited: &mut FxHashMap, + ) -> bool { + if let Some(&cached) = visited.get(&parent_module) { + // this branch is prevent from being called recursively infinity, + // because there has some cycles in globs imports, + // see more spec case at `tests/ui/cfg/diagnostics-reexport-2.rs#reexport32` + return cached; + } + visited.insert(parent_module, false); + let res = r.module_map.get(&parent_module).is_some_and(|m| { + for importer in m.glob_importers.borrow().iter() { + if let Some(next_parent_module) = importer.parent_scope.module.opt_def_id() + { + if next_parent_module == module + || comes_from_same_module_for_glob( + r, + next_parent_module, + module, + visited, + ) + { + return true; + } + } + } + false + }); + visited.insert(parent_module, res); + res + } + + let comes_from_same_module = parent_module == module + || comes_from_same_module_for_glob( + self, + parent_module, + module, + &mut Default::default(), + ); + if !comes_from_same_module { continue; } diff --git a/tests/ui/cfg/diagnostics-reexport-2.rs b/tests/ui/cfg/diagnostics-reexport-2.rs new file mode 100644 index 0000000000000..f66b9ed99ee6e --- /dev/null +++ b/tests/ui/cfg/diagnostics-reexport-2.rs @@ -0,0 +1,61 @@ +// issue#141256 + +mod original { + #[cfg(false)] + //~^ NOTE the item is gated here + //~| NOTE the item is gated here + //~| NOTE the item is gated here + //~| NOTE the item is gated here + //~| NOTE the item is gated here + pub mod gated { + //~^ NOTE found an item that was configured out + //~| NOTE found an item that was configured out + //~| NOTE found an item that was configured out + //~| NOTE found an item that was configured out + //~| NOTE found an item that was configured out + pub fn foo() {} + } +} + +mod reexport { + pub use super::original::*; +} + +mod reexport2 { + pub use super::reexport::*; +} + +mod reexport30 { + pub use super::original::*; + pub use super::reexport31::*; +} + +mod reexport31 { + pub use super::reexport30::*; +} + +mod reexport32 { + pub use super::reexport30::*; +} + +fn main() { + reexport::gated::foo(); + //~^ ERROR failed to resolve: could not find `gated` in `reexport` + //~| NOTE could not find `gated` in `reexport` + + reexport2::gated::foo(); + //~^ ERROR failed to resolve: could not find `gated` in `reexport2` + //~| NOTE could not find `gated` in `reexport2` + + reexport30::gated::foo(); + //~^ ERROR failed to resolve: could not find `gated` in `reexport30` + //~| NOTE could not find `gated` in `reexport30` + + reexport31::gated::foo(); + //~^ ERROR failed to resolve: could not find `gated` in `reexport31` + //~| NOTE could not find `gated` in `reexport31` + + reexport32::gated::foo(); + //~^ ERROR failed to resolve: could not find `gated` in `reexport32` + //~| NOTE could not find `gated` in `reexport32` +} diff --git a/tests/ui/cfg/diagnostics-reexport-2.stderr b/tests/ui/cfg/diagnostics-reexport-2.stderr new file mode 100644 index 0000000000000..95ac5a19b0b99 --- /dev/null +++ b/tests/ui/cfg/diagnostics-reexport-2.stderr @@ -0,0 +1,88 @@ +error[E0433]: failed to resolve: could not find `gated` in `reexport` + --> $DIR/diagnostics-reexport-2.rs:42:15 + | +LL | reexport::gated::foo(); + | ^^^^^ could not find `gated` in `reexport` + | +note: found an item that was configured out + --> $DIR/diagnostics-reexport-2.rs:10:13 + | +LL | pub mod gated { + | ^^^^^ +note: the item is gated here + --> $DIR/diagnostics-reexport-2.rs:4:5 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: could not find `gated` in `reexport2` + --> $DIR/diagnostics-reexport-2.rs:46:16 + | +LL | reexport2::gated::foo(); + | ^^^^^ could not find `gated` in `reexport2` + | +note: found an item that was configured out + --> $DIR/diagnostics-reexport-2.rs:10:13 + | +LL | pub mod gated { + | ^^^^^ +note: the item is gated here + --> $DIR/diagnostics-reexport-2.rs:4:5 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: could not find `gated` in `reexport30` + --> $DIR/diagnostics-reexport-2.rs:50:17 + | +LL | reexport30::gated::foo(); + | ^^^^^ could not find `gated` in `reexport30` + | +note: found an item that was configured out + --> $DIR/diagnostics-reexport-2.rs:10:13 + | +LL | pub mod gated { + | ^^^^^ +note: the item is gated here + --> $DIR/diagnostics-reexport-2.rs:4:5 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: could not find `gated` in `reexport31` + --> $DIR/diagnostics-reexport-2.rs:54:17 + | +LL | reexport31::gated::foo(); + | ^^^^^ could not find `gated` in `reexport31` + | +note: found an item that was configured out + --> $DIR/diagnostics-reexport-2.rs:10:13 + | +LL | pub mod gated { + | ^^^^^ +note: the item is gated here + --> $DIR/diagnostics-reexport-2.rs:4:5 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + +error[E0433]: failed to resolve: could not find `gated` in `reexport32` + --> $DIR/diagnostics-reexport-2.rs:58:17 + | +LL | reexport32::gated::foo(); + | ^^^^^ could not find `gated` in `reexport32` + | +note: found an item that was configured out + --> $DIR/diagnostics-reexport-2.rs:10:13 + | +LL | pub mod gated { + | ^^^^^ +note: the item is gated here + --> $DIR/diagnostics-reexport-2.rs:4:5 + | +LL | #[cfg(false)] + | ^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0433`. From adcd0bf5c36ee49acf390f0d75125da4efda35ac Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 28 May 2025 09:34:08 +0800 Subject: [PATCH 26/26] Fix ICE in tokenstream with contracts from parser recovery --- compiler/rustc_parse/src/parser/stmt.rs | 13 ++++--- tests/crashes/140683.rs | 5 --- ...-tokenstream-for-contracts-issue-140683.rs | 13 +++++++ ...enstream-for-contracts-issue-140683.stderr | 34 +++++++++++++++++++ .../ui/macros/no-close-delim-issue-139248.rs | 5 ++- .../macros/no-close-delim-issue-139248.stderr | 16 +++------ 6 files changed, 60 insertions(+), 26 deletions(-) delete mode 100644 tests/crashes/140683.rs create mode 100644 tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs create mode 100644 tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 396ded96bde13..ccc3410674b46 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -515,8 +515,8 @@ impl<'a> Parser<'a> { fn error_block_no_opening_brace_msg(&mut self, msg: Cow<'static, str>) -> Diag<'a> { let prev = self.prev_token.span; let sp = self.token.span; - let mut e = self.dcx().struct_span_err(sp, msg); - self.label_expected_raw_ref(&mut e); + let mut err = self.dcx().struct_span_err(sp, msg); + self.label_expected_raw_ref(&mut err); let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon @@ -558,20 +558,19 @@ impl<'a> Parser<'a> { stmt.span }; self.suggest_fixes_misparsed_for_loop_head( - &mut e, + &mut err, prev.between(sp), stmt_span, &stmt.kind, ); } Err(e) => { - self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore); - e.cancel(); + e.delay_as_bug(); } _ => {} } - e.span_label(sp, "expected `{`"); - e + err.span_label(sp, "expected `{`"); + err } fn suggest_fixes_misparsed_for_loop_head( diff --git a/tests/crashes/140683.rs b/tests/crashes/140683.rs deleted file mode 100644 index 74ea5c2533bb9..0000000000000 --- a/tests/crashes/140683.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ known-bug: #140683 -impl T { -#[core::contracts::ensures] - fn b() { (loop) } -} diff --git a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs new file mode 100644 index 0000000000000..68346a00ae1a7 --- /dev/null +++ b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.rs @@ -0,0 +1,13 @@ +#![feature(contracts)] +#![allow(incomplete_features)] + +struct T; + +impl T { + #[core::contracts::ensures] //~ ERROR expected a `Fn(&_)` closure, found `()` + fn b() {(loop)} + //~^ ERROR expected `{`, found `)` + //~| ERROR expected `{`, found `)` +} + +fn main() {} diff --git a/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr new file mode 100644 index 0000000000000..f1ffda2a9bee6 --- /dev/null +++ b/tests/ui/macros/ice-in-tokenstream-for-contracts-issue-140683.stderr @@ -0,0 +1,34 @@ +error: expected `{`, found `)` + --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:8:18 + | +LL | fn b() {(loop)} + | ----^ expected `{` + | | + | while parsing this `loop` expression + +error: expected `{`, found `)` + --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:8:18 + | +LL | fn b() {(loop)} + | ----^ expected `{` + | | + | while parsing this `loop` expression + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: expected a `Fn(&_)` closure, found `()` + --> $DIR/ice-in-tokenstream-for-contracts-issue-140683.rs:7:5 + | +LL | #[core::contracts::ensures] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | expected an `Fn(&_)` closure, found `()` + | required by a bound introduced by this call + | + = help: the trait `for<'a> Fn(&'a _)` is not implemented for `()` +note: required by a bound in `build_check_ensures` + --> $SRC_DIR/core/src/contracts.rs:LL:COL + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/macros/no-close-delim-issue-139248.rs b/tests/ui/macros/no-close-delim-issue-139248.rs index 86583b2724ed0..f15234eaff1a7 100644 --- a/tests/ui/macros/no-close-delim-issue-139248.rs +++ b/tests/ui/macros/no-close-delim-issue-139248.rs @@ -2,9 +2,8 @@ macro_rules! m { (static a : () = $e:expr) => { - static a : () = $e; - //~^ ERROR macro expansion ends with an incomplete expression: expected expression - } + static a: () = $e; + }; } m! { static a : () = (if b) } diff --git a/tests/ui/macros/no-close-delim-issue-139248.stderr b/tests/ui/macros/no-close-delim-issue-139248.stderr index 6ed41ae9b46a6..8aa39851b4b0f 100644 --- a/tests/ui/macros/no-close-delim-issue-139248.stderr +++ b/tests/ui/macros/no-close-delim-issue-139248.stderr @@ -1,33 +1,27 @@ error: expected `{`, found `)` - --> $DIR/no-close-delim-issue-139248.rs:10:27 + --> $DIR/no-close-delim-issue-139248.rs:9:27 | LL | m! { static a : () = (if b) } | ^ expected `{` | note: the `if` expression is missing a block after this condition - --> $DIR/no-close-delim-issue-139248.rs:10:26 + --> $DIR/no-close-delim-issue-139248.rs:9:26 | LL | m! { static a : () = (if b) } | ^ error: expected `{`, found `)` - --> $DIR/no-close-delim-issue-139248.rs:10:27 + --> $DIR/no-close-delim-issue-139248.rs:9:27 | LL | m! { static a : () = (if b) } | ^ expected `{` | note: the `if` expression is missing a block after this condition - --> $DIR/no-close-delim-issue-139248.rs:10:26 + --> $DIR/no-close-delim-issue-139248.rs:9:26 | LL | m! { static a : () = (if b) } | ^ = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: macro expansion ends with an incomplete expression: expected expression - --> $DIR/no-close-delim-issue-139248.rs:5:28 - | -LL | static a : () = $e; - | ^ expected expression - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors