From 15f0149c3ea7338b829663ec76c2160ea5730087 Mon Sep 17 00:00:00 2001 From: Meysam Azad Date: Sun, 20 Aug 2023 14:54:16 +0700 Subject: [PATCH 001/171] =?UTF-8?q?feat(docs):=20add=20cargo-pgo=20to=20PG?= =?UTF-8?q?O=20documentation=20=F0=9F=93=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rustc/src/profile-guided-optimization.md | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/doc/rustc/src/profile-guided-optimization.md b/src/doc/rustc/src/profile-guided-optimization.md index d9cf7ce30f936..465a26a1301be 100644 --- a/src/doc/rustc/src/profile-guided-optimization.md +++ b/src/doc/rustc/src/profile-guided-optimization.md @@ -145,3 +145,25 @@ in Clang's documentation is therefore an interesting read for anyone who wants to use PGO with Rust. [clang-pgo]: https://clang.llvm.org/docs/UsersManual.html#profile-guided-optimization + +## Community Maintained Tools + +As an alternative to directly using the compiler for Profile-Guided Optimization, +you may choose to go with `cargo-pgo`, which has an intuitive command-line API +and saves you the trouble of doing all the manual work. You can read more about +it in their repository accessible from this link: https://github.com/Kobzol/cargo-pgo + +For the sake of completeness, here are the corresponding steps using `cargo-pgo`: + +```bash +# Install the binary as usual: cargo install cargo-pgo +cargo pgo build +LLVM_PROFILE_FILE=./target/pgo-profiles/rustc-pgo_%m_%p.profraw ./target/x86_64-unknown-linux-gnu/release/myprogram +cargo pgo optimize +``` + +These steps will do the following just as before: + +1. Build an instrumented binary from the source code. +2. Use the instrumentation in the binary when running it for the first time. +3. Use the instrumentation results from the last step to create an optimized binary. From 94c9d0c90f6bb6f7d8fdc6e326e148e7b5bb30cf Mon Sep 17 00:00:00 2001 From: Maybe Waffle Date: Sun, 30 Jul 2023 12:12:26 +0000 Subject: [PATCH 002/171] Make non-zero check more obvious --- compiler/rustc_trait_selection/src/traits/object_safety.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs index 5823b4508d94e..1dbe3039107c5 100644 --- a/compiler/rustc_trait_selection/src/traits/object_safety.rs +++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs @@ -464,7 +464,7 @@ fn virtual_call_violation_for_method<'tcx>( // We can't monomorphize things like `fn foo(...)`. let own_counts = tcx.generics_of(method.def_id).own_counts(); - if own_counts.types + own_counts.consts != 0 { + if own_counts.types > 0 || own_counts.consts > 0 { return Some(MethodViolationCode::Generic); } From 3d86b8acdab25399fc921ab6b522943a02bac410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 30 Jun 2023 03:06:15 +0000 Subject: [PATCH 003/171] Add test for #56607 --- tests/ui/sized/unsized-binding.rs | 5 ++++ tests/ui/sized/unsized-binding.stderr | 39 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 tests/ui/sized/unsized-binding.rs create mode 100644 tests/ui/sized/unsized-binding.stderr diff --git a/tests/ui/sized/unsized-binding.rs b/tests/ui/sized/unsized-binding.rs new file mode 100644 index 0000000000000..7d41071861204 --- /dev/null +++ b/tests/ui/sized/unsized-binding.rs @@ -0,0 +1,5 @@ +fn main() { + let x = *""; //~ ERROR E0277 + println!("{}", x); //~ ERROR E0277 + println!("{}", x); //~ ERROR E0277 +} diff --git a/tests/ui/sized/unsized-binding.stderr b/tests/ui/sized/unsized-binding.stderr new file mode 100644 index 0000000000000..d508d84930aa8 --- /dev/null +++ b/tests/ui/sized/unsized-binding.stderr @@ -0,0 +1,39 @@ +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/unsized-binding.rs:2:9 + | +LL | let x = *""; + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `str` + = note: all local variables must have a statically known size + = help: unsized locals are gated as an unstable feature + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/unsized-binding.rs:3:20 + | +LL | println!("{}", x); + | -- ^ doesn't have a size known at compile-time + | | + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `str` +note: required by a bound in `core::fmt::rt::Argument::<'a>::new_display` + --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0277]: the size for values of type `str` cannot be known at compilation time + --> $DIR/unsized-binding.rs:4:20 + | +LL | println!("{}", x); + | -- ^ doesn't have a size known at compile-time + | | + | required by a bound introduced by this call + | + = help: the trait `Sized` is not implemented for `str` +note: required by a bound in `core::fmt::rt::Argument::<'a>::new_display` + --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. From 2f79681fb9346ba511ad42499d12faee15e6c63a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 30 Jun 2023 03:14:44 +0000 Subject: [PATCH 004/171] Only emit one error per unsized binding, instead of one per usage Fix #56607. --- .../error_reporting/type_err_ctxt_ext.rs | 19 +++++++++++++ tests/ui/sized/unsized-binding.rs | 4 +-- tests/ui/sized/unsized-binding.stderr | 28 +------------------ 3 files changed, 22 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 8adfb27a3f448..18e62fd631989 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -428,6 +428,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { { return; } + if let ObligationCauseCode::FunctionArgumentObligation { + arg_hir_id, + .. + } = obligation.cause.code() + && let Some(Node::Expr(arg)) = self.tcx.hir().find(*arg_hir_id) + && let arg = arg.peel_borrows() + && let hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { res: hir::def::Res::Local(hir_id), .. }, + )) = arg.kind + && let Some(Node::Pat(pat)) = self.tcx.hir().find(*hir_id) + && let Some(preds) = self.reported_trait_errors.borrow().get(&pat.span) + && preds.contains(&obligation.predicate) + && self.tcx.sess.has_errors().is_some() + { + // Silence redundant errors on binding acccess that are already + // reported on the binding definition (#56607). + return; + } let trait_ref = trait_predicate.to_poly_trait_ref(); let (post_message, pre_message, type_def) = self diff --git a/tests/ui/sized/unsized-binding.rs b/tests/ui/sized/unsized-binding.rs index 7d41071861204..3b99b0f6e9654 100644 --- a/tests/ui/sized/unsized-binding.rs +++ b/tests/ui/sized/unsized-binding.rs @@ -1,5 +1,5 @@ fn main() { let x = *""; //~ ERROR E0277 - println!("{}", x); //~ ERROR E0277 - println!("{}", x); //~ ERROR E0277 + println!("{}", x); + println!("{}", x); } diff --git a/tests/ui/sized/unsized-binding.stderr b/tests/ui/sized/unsized-binding.stderr index d508d84930aa8..af306685021e0 100644 --- a/tests/ui/sized/unsized-binding.stderr +++ b/tests/ui/sized/unsized-binding.stderr @@ -8,32 +8,6 @@ LL | let x = *""; = note: all local variables must have a statically known size = help: unsized locals are gated as an unstable feature -error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/unsized-binding.rs:3:20 - | -LL | println!("{}", x); - | -- ^ doesn't have a size known at compile-time - | | - | required by a bound introduced by this call - | - = help: the trait `Sized` is not implemented for `str` -note: required by a bound in `core::fmt::rt::Argument::<'a>::new_display` - --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0277]: the size for values of type `str` cannot be known at compilation time - --> $DIR/unsized-binding.rs:4:20 - | -LL | println!("{}", x); - | -- ^ doesn't have a size known at compile-time - | | - | required by a bound introduced by this call - | - = help: the trait `Sized` is not implemented for `str` -note: required by a bound in `core::fmt::rt::Argument::<'a>::new_display` - --> $SRC_DIR/core/src/fmt/rt.rs:LL:COL - = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 3 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0277`. From 124d6d843e1fc072ec2cd4547d60b61f99450738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 9 Oct 2023 23:14:23 +0000 Subject: [PATCH 005/171] Remove need for `has_errors()` check --- .../error_reporting/type_err_ctxt_ext.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index 18e62fd631989..a3e3a964ca2d3 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -163,12 +163,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { predicate: error.obligation.predicate, index: Some(index), }); - - self.reported_trait_errors - .borrow_mut() - .entry(span) - .or_default() - .push(error.obligation.predicate); } // We do this in 2 passes because we want to display errors in order, though @@ -206,6 +200,18 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { for (error, suppressed) in iter::zip(&errors, &is_suppressed) { if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion { self.report_fulfillment_error(error); + // We want to ignore desugarings here: spans are equivalent even + // if one is the result of a desugaring and the other is not. + let mut span = error.obligation.cause.span; + let expn_data = span.ctxt().outer_expn_data(); + if let ExpnKind::Desugaring(_) = expn_data.kind { + span = expn_data.call_site; + } + self.reported_trait_errors + .borrow_mut() + .entry(span) + .or_default() + .push(error.obligation.predicate); } } } @@ -441,7 +447,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { && let Some(Node::Pat(pat)) = self.tcx.hir().find(*hir_id) && let Some(preds) = self.reported_trait_errors.borrow().get(&pat.span) && preds.contains(&obligation.predicate) - && self.tcx.sess.has_errors().is_some() { // Silence redundant errors on binding acccess that are already // reported on the binding definition (#56607). From 568b316ce3ec25aaaecf8e63a7b3a1301f016d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 9 Oct 2023 23:18:36 +0000 Subject: [PATCH 006/171] Move predicate error early check to its own method --- .../error_reporting/type_err_ctxt_ext.rs | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index a3e3a964ca2d3..da16941b923f8 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -104,6 +104,8 @@ pub trait TypeErrCtxtExt<'tcx> { error: &SelectionError<'tcx>, ); + fn fn_arg_obligation(&self, obligation: &PredicateObligation<'tcx>) -> bool; + fn report_const_param_not_wf( &self, ty: Ty<'tcx>, @@ -434,20 +436,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { { return; } - if let ObligationCauseCode::FunctionArgumentObligation { - arg_hir_id, - .. - } = obligation.cause.code() - && let Some(Node::Expr(arg)) = self.tcx.hir().find(*arg_hir_id) - && let arg = arg.peel_borrows() - && let hir::ExprKind::Path(hir::QPath::Resolved( - None, - hir::Path { res: hir::def::Res::Local(hir_id), .. }, - )) = arg.kind - && let Some(Node::Pat(pat)) = self.tcx.hir().find(*hir_id) - && let Some(preds) = self.reported_trait_errors.borrow().get(&pat.span) - && preds.contains(&obligation.predicate) - { + if self.fn_arg_obligation(&obligation) { // Silence redundant errors on binding acccess that are already // reported on the binding definition (#56607). return; @@ -948,6 +937,26 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.emit(); } + fn fn_arg_obligation(&self, obligation: &PredicateObligation<'tcx>) -> bool { + if let ObligationCauseCode::FunctionArgumentObligation { + arg_hir_id, + .. + } = obligation.cause.code() + && let Some(Node::Expr(arg)) = self.tcx.hir().find(*arg_hir_id) + && let arg = arg.peel_borrows() + && let hir::ExprKind::Path(hir::QPath::Resolved( + None, + hir::Path { res: hir::def::Res::Local(hir_id), .. }, + )) = arg.kind + && let Some(Node::Pat(pat)) = self.tcx.hir().find(*hir_id) + && let Some(preds) = self.reported_trait_errors.borrow().get(&pat.span) + && preds.contains(&obligation.predicate) + { + return true; + } + false + } + fn report_const_param_not_wf( &self, ty: Ty<'tcx>, From 43a08bd29969be0f8b83a525b46cfa9e4b99e863 Mon Sep 17 00:00:00 2001 From: clubby789 Date: Mon, 16 Oct 2023 15:40:32 +0000 Subject: [PATCH 007/171] Add test for 113326 --- .../type-alias-impl-trait/recursive-fn-tait.rs | 17 +++++++++++++++++ .../recursive-fn-tait.stderr | 14 ++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 tests/ui/type-alias-impl-trait/recursive-fn-tait.rs create mode 100644 tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr diff --git a/tests/ui/type-alias-impl-trait/recursive-fn-tait.rs b/tests/ui/type-alias-impl-trait/recursive-fn-tait.rs new file mode 100644 index 0000000000000..3d1759097d6b3 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/recursive-fn-tait.rs @@ -0,0 +1,17 @@ +// test for #113326 +#![feature(type_alias_impl_trait)] + +pub type Diff = impl Fn(usize) -> usize; + +pub fn lift() -> Diff { + |_: usize |loop {} +} + +pub fn add( + n: Diff, + m: Diff, +) -> Diff { + move |x: usize| m(n(x)) //~ ERROR: concrete type differs +} + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr b/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr new file mode 100644 index 0000000000000..b2898a21190ad --- /dev/null +++ b/tests/ui/type-alias-impl-trait/recursive-fn-tait.stderr @@ -0,0 +1,14 @@ +error: concrete type differs from previous defining opaque type use + --> $DIR/recursive-fn-tait.rs:14:5 + | +LL | move |x: usize| m(n(x)) + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `{closure@$DIR/recursive-fn-tait.rs:7:5: 7:16}`, got `{closure@$DIR/recursive-fn-tait.rs:14:5: 14:20}` + | +note: previous use here + --> $DIR/recursive-fn-tait.rs:7:5 + | +LL | |_: usize |loop {} + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to previous error + From f4a9d29c50de93c3320867a4f63c9e36eee43d3b Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 17 Oct 2023 15:12:21 +1100 Subject: [PATCH 008/171] Remove `rustc_symbol_mangling/messages.ftl`. It contains a single message that (a) doesn't contain any natural language, and (b) is only used in tests. --- Cargo.lock | 2 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_symbol_mangling/Cargo.toml | 2 - compiler/rustc_symbol_mangling/messages.ftl | 1 - compiler/rustc_symbol_mangling/src/errors.rs | 40 +++++++++++++------- compiler/rustc_symbol_mangling/src/lib.rs | 4 -- 6 files changed, 26 insertions(+), 24 deletions(-) delete mode 100644 compiler/rustc_symbol_mangling/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 5cfb7feb6f545..11f30b41079e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4543,9 +4543,7 @@ dependencies = [ "rustc-demangle", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hir", - "rustc_macros", "rustc_middle", "rustc_session", "rustc_span", diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 5bb7c41677cd1..f768fd5ef193d 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -137,7 +137,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_query_system::DEFAULT_LOCALE_RESOURCE, rustc_resolve::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE, - rustc_symbol_mangling::DEFAULT_LOCALE_RESOURCE, rustc_trait_selection::DEFAULT_LOCALE_RESOURCE, rustc_ty_utils::DEFAULT_LOCALE_RESOURCE, // tidy-alphabetical-end diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml index 052ef8bb94c3e..d53bc5b6a8e04 100644 --- a/compiler/rustc_symbol_mangling/Cargo.toml +++ b/compiler/rustc_symbol_mangling/Cargo.toml @@ -15,9 +15,7 @@ twox-hash = "1.6.3" rustc_span = { path = "../rustc_span" } rustc_middle = { path = "../rustc_middle" } rustc_hir = { path = "../rustc_hir" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_target = { path = "../rustc_target" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_session = { path = "../rustc_session" } -rustc_macros = { path = "../rustc_macros" } rustc_errors = { path = "../rustc_errors" } diff --git a/compiler/rustc_symbol_mangling/messages.ftl b/compiler/rustc_symbol_mangling/messages.ftl deleted file mode 100644 index b7d48280f4619..0000000000000 --- a/compiler/rustc_symbol_mangling/messages.ftl +++ /dev/null @@ -1 +0,0 @@ -symbol_mangling_test_output = {$kind}({$content}) diff --git a/compiler/rustc_symbol_mangling/src/errors.rs b/compiler/rustc_symbol_mangling/src/errors.rs index f4d0751f75370..2e081e555313c 100644 --- a/compiler/rustc_symbol_mangling/src/errors.rs +++ b/compiler/rustc_symbol_mangling/src/errors.rs @@ -1,18 +1,32 @@ //! Errors emitted by symbol_mangling. -use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg}; -use rustc_macros::Diagnostic; +use rustc_errors::{ErrorGuaranteed, IntoDiagnostic}; use rustc_span::Span; +use std::fmt; -#[derive(Diagnostic)] -#[diag(symbol_mangling_test_output)] pub struct TestOutput { - #[primary_span] pub span: Span, pub kind: Kind, pub content: String, } +// This diagnostic doesn't need translation because (a) it doesn't contain any +// natural language, and (b) it's only used in tests. So we construct it +// manually and avoid the fluent machinery. +impl IntoDiagnostic<'_> for TestOutput { + fn into_diagnostic( + self, + handler: &'_ rustc_errors::Handler, + ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> { + let TestOutput { span, kind, content } = self; + + #[allow(rustc::untranslatable_diagnostic)] + let mut diag = handler.struct_err(format!("{kind}({content})")); + diag.set_span(span); + diag + } +} + pub enum Kind { SymbolName, Demangling, @@ -20,15 +34,13 @@ pub enum Kind { DefPath, } -impl IntoDiagnosticArg for Kind { - fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> { - let kind = match self { - Kind::SymbolName => "symbol-name", - Kind::Demangling => "demangling", - Kind::DemanglingAlt => "demangling-alt", - Kind::DefPath => "def-path", +impl fmt::Display for Kind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Kind::SymbolName => write!(f, "symbol-name"), + Kind::Demangling => write!(f, "demangling"), + Kind::DemanglingAlt => write!(f, "demangling-alt"), + Kind::DefPath => write!(f, "def-path"), } - .into(); - DiagnosticArgValue::Str(kind) } } diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 6ade2d777c741..90cd0479cb257 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -103,8 +103,6 @@ extern crate rustc_middle; #[macro_use] extern crate tracing; -use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage}; -use rustc_fluent_macro::fluent_messages; use rustc_hir::def::DefKind; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; @@ -121,8 +119,6 @@ pub mod errors; pub mod test; pub mod typeid; -fluent_messages! { "../messages.ftl" } - /// This function computes the symbol name for the given `instance` and the /// given instantiating crate. That is, if you know that instance X is /// instantiated in crate Y, this is the symbol name this instance would have. From a69edc68aa224891a7e54d8ced094ffc28c4be3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 9 Oct 2023 23:04:11 +0200 Subject: [PATCH 009/171] Add BootstrapCommand and `run_cmd` --- src/bootstrap/src/lib.rs | 34 ++++++++++++++++++++++++--------- src/bootstrap/src/utils/exec.rs | 21 ++++++++++++++++++++ src/bootstrap/src/utils/mod.rs | 1 + 3 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 src/bootstrap/src/utils/exec.rs diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 97c743074af43..b20c20a360b9a 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -39,6 +39,7 @@ use crate::core::config::flags; use crate::core::config::{DryRun, Target}; use crate::core::config::{LlvmLibunwind, TargetSelection}; use crate::utils::cache::{Interned, INTERNER}; +use crate::utils::exec::BootstrapCommand; use crate::utils::helpers::{ self, dir_is_empty, exe, libdir, mtime, output, run, run_suppressed, symlink_dir, try_run_suppressed, @@ -959,17 +960,32 @@ impl Build { /// Runs a command, printing out contextual info if it fails, and delaying errors until the build finishes. pub(crate) fn run_delaying_failure(&self, cmd: &mut Command) -> bool { - if !self.fail_fast { - #[allow(deprecated)] // can't use Build::try_run, that's us - if self.config.try_run(cmd).is_err() { - let mut failures = self.delayed_failures.borrow_mut(); - failures.push(format!("{cmd:?}")); - return false; + let cmd: BootstrapCommand<'_> = cmd.into(); + self.run_cmd(cmd.delay_failure()) + } + + /// A centralized function for running commands that do not return output. + pub(crate) fn run_cmd<'a, C: Into>>(&self, cmd: C) -> bool { + let command = cmd.into(); + self.verbose(&format!("running: {command:?}")); + + #[allow(deprecated)] // can't use Build::try_run, that's us + let result = self.config.try_run(command.command); + + match result { + Ok(_) => true, + Err(_) => { + if command.delay_failure { + let mut failures = self.delayed_failures.borrow_mut(); + failures.push(format!("{command:?}")); + return false; + } + if self.fail_fast { + exit!(1); + } + false } - } else { - self.run(cmd); } - true } pub fn is_verbose_than(&self, level: usize) -> bool { diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs new file mode 100644 index 0000000000000..a590171984e32 --- /dev/null +++ b/src/bootstrap/src/utils/exec.rs @@ -0,0 +1,21 @@ +use std::process::Command; + +/// Wrapper around `std::process::Command`. +#[derive(Debug)] +pub struct BootstrapCommand<'a> { + pub command: &'a mut Command, + /// Report failure later instead of immediately. + pub delay_failure: bool, +} + +impl<'a> BootstrapCommand<'a> { + pub fn delay_failure(self) -> Self { + Self { delay_failure: true, ..self } + } +} + +impl<'a> From<&'a mut Command> for BootstrapCommand<'a> { + fn from(command: &'a mut Command) -> Self { + Self { command, delay_failure: false } + } +} diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs index 7dcb6a8286202..8ca22d00865b6 100644 --- a/src/bootstrap/src/utils/mod.rs +++ b/src/bootstrap/src/utils/mod.rs @@ -6,6 +6,7 @@ pub(crate) mod cache; pub(crate) mod cc_detect; pub(crate) mod channel; pub(crate) mod dylib; +pub(crate) mod exec; pub(crate) mod helpers; pub(crate) mod job; #[cfg(feature = "build-metrics")] From 0e090e740c9cf4743a57ec61dd91d90357571ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 10 Oct 2023 20:19:22 +0200 Subject: [PATCH 010/171] Add behavior on failure to BootstrapCommand --- src/bootstrap/src/lib.rs | 55 +++++++++++++-------------------- src/bootstrap/src/utils/exec.rs | 19 +++++++++--- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index b20c20a360b9a..7c052f262d866 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -39,11 +39,8 @@ use crate::core::config::flags; use crate::core::config::{DryRun, Target}; use crate::core::config::{LlvmLibunwind, TargetSelection}; use crate::utils::cache::{Interned, INTERNER}; -use crate::utils::exec::BootstrapCommand; -use crate::utils::helpers::{ - self, dir_is_empty, exe, libdir, mtime, output, run, run_suppressed, symlink_dir, - try_run_suppressed, -}; +use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand}; +use crate::utils::helpers::{self, dir_is_empty, exe, libdir, mtime, output, symlink_dir}; mod core; mod utils; @@ -922,44 +919,30 @@ impl Build { /// Runs a command, printing out nice contextual information if it fails. fn run(&self, cmd: &mut Command) { - if self.config.dry_run() { - return; - } - self.verbose(&format!("running: {cmd:?}")); - run(cmd, self.is_verbose()) + // FIXME: output mode -> status + err if self.is_verbose() + let cmd: BootstrapCommand<'_> = cmd.into(); + self.run_cmd(cmd.fail_fast()); } /// Runs a command, printing out nice contextual information if it fails. fn run_quiet(&self, cmd: &mut Command) { - if self.config.dry_run() { - return; - } - self.verbose(&format!("running: {cmd:?}")); - run_suppressed(cmd) + // FIXME: output mode -> output + err + let cmd: BootstrapCommand<'_> = cmd.into(); + self.run_cmd(cmd.fail_fast()); } /// Runs a command, printing out nice contextual information if it fails. /// Exits if the command failed to execute at all, otherwise returns its /// `status.success()`. fn run_quiet_delaying_failure(&self, cmd: &mut Command) -> bool { - if self.config.dry_run() { - return true; - } - if !self.fail_fast { - self.verbose(&format!("running: {cmd:?}")); - if !try_run_suppressed(cmd) { - let mut failures = self.delayed_failures.borrow_mut(); - failures.push(format!("{cmd:?}")); - return false; - } - } else { - self.run_quiet(cmd); - } - true + // FIXME: output mode -> output + err + let cmd: BootstrapCommand<'_> = cmd.into(); + self.run_cmd(cmd.delay_failure()) } /// Runs a command, printing out contextual info if it fails, and delaying errors until the build finishes. pub(crate) fn run_delaying_failure(&self, cmd: &mut Command) -> bool { + // FIXME: output mode -> status + err if self.is_verbose() let cmd: BootstrapCommand<'_> = cmd.into(); self.run_cmd(cmd.delay_failure()) } @@ -975,10 +958,16 @@ impl Build { match result { Ok(_) => true, Err(_) => { - if command.delay_failure { - let mut failures = self.delayed_failures.borrow_mut(); - failures.push(format!("{command:?}")); - return false; + if let Some(failure_behavior) = command.failure_behavior { + match failure_behavior { + BehaviorOnFailure::DelayFail => { + let mut failures = self.delayed_failures.borrow_mut(); + failures.push(format!("{command:?}")); + } + BehaviorOnFailure::Exit => { + exit!(1); + } + } } if self.fail_fast { exit!(1); diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index a590171984e32..a2a0dbad240ed 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -1,21 +1,32 @@ use std::process::Command; +/// What should be done when the command fails. +#[derive(Debug, Copy, Clone)] +pub enum BehaviorOnFailure { + /// Immediately stop bootstrap. + Exit, + /// Delay failure until the end of bootstrap invocation. + DelayFail, +} + /// Wrapper around `std::process::Command`. #[derive(Debug)] pub struct BootstrapCommand<'a> { pub command: &'a mut Command, - /// Report failure later instead of immediately. - pub delay_failure: bool, + pub failure_behavior: Option, } impl<'a> BootstrapCommand<'a> { pub fn delay_failure(self) -> Self { - Self { delay_failure: true, ..self } + Self { failure_behavior: Some(BehaviorOnFailure::DelayFail), ..self } + } + pub fn fail_fast(self) -> Self { + Self { failure_behavior: Some(BehaviorOnFailure::Exit), ..self } } } impl<'a> From<&'a mut Command> for BootstrapCommand<'a> { fn from(command: &'a mut Command) -> Self { - Self { command, delay_failure: false } + Self { command, failure_behavior: None } } } From 7efe8fa8493d23ef0b99f31c7074957e7b298aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 10 Oct 2023 20:54:25 +0200 Subject: [PATCH 011/171] Add output mode to BootstrapCommand --- src/bootstrap/src/lib.rs | 78 ++++++++++++++++++++++-------- src/bootstrap/src/utils/exec.rs | 18 ++++++- src/bootstrap/src/utils/helpers.rs | 34 +------------ 3 files changed, 76 insertions(+), 54 deletions(-) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 7c052f262d866..35d3c78aee2f9 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -23,11 +23,12 @@ use std::fmt::Display; use std::fs::{self, File}; use std::io; use std::path::{Path, PathBuf}; -use std::process::{Command, Stdio}; +use std::process::{Command, Output, Stdio}; use std::str; use build_helper::ci::{gha, CiEnv}; use build_helper::exit; +use build_helper::util::fail; use filetime::FileTime; use once_cell::sync::OnceCell; use termcolor::{ColorChoice, StandardStream, WriteColor}; @@ -39,7 +40,7 @@ use crate::core::config::flags; use crate::core::config::{DryRun, Target}; use crate::core::config::{LlvmLibunwind, TargetSelection}; use crate::utils::cache::{Interned, INTERNER}; -use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand}; +use crate::utils::exec::{BehaviorOnFailure, BootstrapCommand, OutputMode}; use crate::utils::helpers::{self, dir_is_empty, exe, libdir, mtime, output, symlink_dir}; mod core; @@ -919,41 +920,78 @@ impl Build { /// Runs a command, printing out nice contextual information if it fails. fn run(&self, cmd: &mut Command) { - // FIXME: output mode -> status + err if self.is_verbose() - let cmd: BootstrapCommand<'_> = cmd.into(); - self.run_cmd(cmd.fail_fast()); + self.run_cmd(BootstrapCommand::from(cmd).fail_fast().output_mode( + match self.is_verbose() { + true => OutputMode::PrintAll, + false => OutputMode::PrintOutput, + }, + )); + } + + /// Runs a command, printing out contextual info if it fails, and delaying errors until the build finishes. + pub(crate) fn run_delaying_failure(&self, cmd: &mut Command) -> bool { + self.run_cmd(BootstrapCommand::from(cmd).delay_failure().output_mode( + match self.is_verbose() { + true => OutputMode::PrintAll, + false => OutputMode::PrintOutput, + }, + )) } /// Runs a command, printing out nice contextual information if it fails. fn run_quiet(&self, cmd: &mut Command) { - // FIXME: output mode -> output + err - let cmd: BootstrapCommand<'_> = cmd.into(); - self.run_cmd(cmd.fail_fast()); + self.run_cmd(BootstrapCommand::from(cmd).fail_fast().output_mode(OutputMode::Suppress)); } /// Runs a command, printing out nice contextual information if it fails. /// Exits if the command failed to execute at all, otherwise returns its /// `status.success()`. fn run_quiet_delaying_failure(&self, cmd: &mut Command) -> bool { - // FIXME: output mode -> output + err - let cmd: BootstrapCommand<'_> = cmd.into(); - self.run_cmd(cmd.delay_failure()) - } - - /// Runs a command, printing out contextual info if it fails, and delaying errors until the build finishes. - pub(crate) fn run_delaying_failure(&self, cmd: &mut Command) -> bool { - // FIXME: output mode -> status + err if self.is_verbose() - let cmd: BootstrapCommand<'_> = cmd.into(); - self.run_cmd(cmd.delay_failure()) + self.run_cmd(BootstrapCommand::from(cmd).delay_failure().output_mode(OutputMode::Suppress)) } /// A centralized function for running commands that do not return output. pub(crate) fn run_cmd<'a, C: Into>>(&self, cmd: C) -> bool { + if self.config.dry_run() { + return true; + } + let command = cmd.into(); self.verbose(&format!("running: {command:?}")); - #[allow(deprecated)] // can't use Build::try_run, that's us - let result = self.config.try_run(command.command); + let (output, print_error) = match command.output_mode { + mode @ (OutputMode::PrintAll | OutputMode::PrintOutput) => ( + command.command.status().map(|status| Output { + status, + stdout: Vec::new(), + stderr: Vec::new(), + }), + matches!(mode, OutputMode::PrintAll), + ), + OutputMode::Suppress => (command.command.output(), true), + }; + + let output = match output { + Ok(output) => output, + Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", command, e)), + }; + let result = if !output.status.success() { + if print_error { + println!( + "\n\ncommand did not execute successfully: {:?}\n\ + expected success, got: {}\n\n\ + stdout ----\n{}\n\ + stderr ----\n{}\n\n", + command.command, + output.status, + String::from_utf8_lossy(&output.stdout), + String::from_utf8_lossy(&output.stderr) + ); + } + Err(()) + } else { + Ok(()) + }; match result { Ok(_) => true, diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index a2a0dbad240ed..a3520ad5f983a 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -9,11 +9,24 @@ pub enum BehaviorOnFailure { DelayFail, } +/// How should the output of the command be handled. +#[derive(Debug, Copy, Clone)] +pub enum OutputMode { + /// Print both the output (by inheriting stdout/stderr) and also the command itself, if it + /// fails. + PrintAll, + /// Print the output (by inheriting stdout/stderr). + PrintOutput, + /// Suppress the output if the command succeeds, otherwise print the output. + Suppress, +} + /// Wrapper around `std::process::Command`. #[derive(Debug)] pub struct BootstrapCommand<'a> { pub command: &'a mut Command, pub failure_behavior: Option, + pub output_mode: OutputMode, } impl<'a> BootstrapCommand<'a> { @@ -23,10 +36,13 @@ impl<'a> BootstrapCommand<'a> { pub fn fail_fast(self) -> Self { Self { failure_behavior: Some(BehaviorOnFailure::Exit), ..self } } + pub fn output_mode(self, output_mode: OutputMode) -> Self { + Self { output_mode, ..self } + } } impl<'a> From<&'a mut Command> for BootstrapCommand<'a> { fn from(command: &'a mut Command) -> Self { - Self { command, failure_behavior: None } + Self { command, failure_behavior: None, output_mode: OutputMode::Suppress } } } diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index bb84b70d987f9..b58a1c2584259 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -3,7 +3,7 @@ //! Simple things like testing the various filesystem operations here and there, //! not a lot of interesting happenings here unfortunately. -use build_helper::util::{fail, try_run}; +use build_helper::util::fail; use std::env; use std::fs; use std::io; @@ -216,12 +216,6 @@ pub fn is_valid_test_suite_arg<'a, P: AsRef>( } } -pub fn run(cmd: &mut Command, print_cmd_on_fail: bool) { - if try_run(cmd, print_cmd_on_fail).is_err() { - crate::exit!(1); - } -} - pub fn check_run(cmd: &mut Command, print_cmd_on_fail: bool) -> bool { let status = match cmd.status() { Ok(status) => status, @@ -239,32 +233,6 @@ pub fn check_run(cmd: &mut Command, print_cmd_on_fail: bool) -> bool { status.success() } -pub fn run_suppressed(cmd: &mut Command) { - if !try_run_suppressed(cmd) { - crate::exit!(1); - } -} - -pub fn try_run_suppressed(cmd: &mut Command) -> bool { - let output = match cmd.output() { - Ok(status) => status, - Err(e) => fail(&format!("failed to execute command: {cmd:?}\nerror: {e}")), - }; - if !output.status.success() { - println!( - "\n\ncommand did not execute successfully: {:?}\n\ - expected success, got: {}\n\n\ - stdout ----\n{}\n\ - stderr ----\n{}\n\n", - cmd, - output.status, - String::from_utf8_lossy(&output.stdout), - String::from_utf8_lossy(&output.stderr) - ); - } - output.status.success() -} - pub fn make(host: &str) -> PathBuf { if host.contains("dragonfly") || host.contains("freebsd") From 17011d02ead7dad95a26f9599f3ddac170da0db6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 9 Oct 2023 23:42:13 +0200 Subject: [PATCH 012/171] Remove usages of `Config::try_run` Commands should be run on Builder, if possible. --- src/bootstrap/src/core/build_steps/test.rs | 9 +++++---- src/bootstrap/src/core/build_steps/tool.rs | 4 ++-- src/bootstrap/src/lib.rs | 15 ++++++--------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 831a86940fb48..ee9effa468e1b 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -27,6 +27,7 @@ use crate::core::config::flags::Subcommand; use crate::core::config::TargetSelection; use crate::utils; use crate::utils::cache::{Interned, INTERNER}; +use crate::utils::exec::BootstrapCommand; use crate::utils::helpers::{ self, add_link_lib_path, dylib_path, dylib_path_var, output, t, up_to_date, }; @@ -808,8 +809,8 @@ impl Step for Clippy { let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "clippy", host, host); - #[allow(deprecated)] // Clippy reports errors if it blessed the outputs - if builder.config.try_run(&mut cargo).is_ok() { + // Clippy reports errors if it blessed the outputs + if builder.run_cmd(&mut cargo) { // The tests succeeded; nothing to do. return; } @@ -3094,7 +3095,7 @@ impl Step for CodegenCranelift { .arg("testsuite.extended_sysroot"); cargo.args(builder.config.test_args()); - #[allow(deprecated)] - builder.config.try_run(&mut cargo.into()).unwrap(); + let mut cmd: Command = cargo.into(); + builder.run_cmd(BootstrapCommand::from(&mut cmd).fail_fast()); } } diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 5702fa62d7ccc..7c35e2a0e9f95 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -108,8 +108,8 @@ impl Step for ToolBuild { ); let mut cargo = Command::from(cargo); - #[allow(deprecated)] // we check this in `is_optional_tool` in a second - let is_expected = builder.config.try_run(&mut cargo).is_ok(); + // we check this in `is_optional_tool` in a second + let is_expected = builder.run_cmd(&mut cargo); builder.save_toolstate( tool, diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 35d3c78aee2f9..27d5faa3289ad 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -579,15 +579,12 @@ impl Build { } // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error). - #[allow(deprecated)] // diff-index reports the modifications through the exit status - let has_local_modifications = self - .config - .try_run( - Command::new("git") - .args(&["diff-index", "--quiet", "HEAD"]) - .current_dir(&absolute_path), - ) - .is_err(); + // diff-index reports the modifications through the exit status + let has_local_modifications = !self.run_cmd( + Command::new("git") + .args(&["diff-index", "--quiet", "HEAD"]) + .current_dir(&absolute_path), + ); if has_local_modifications { self.run(Command::new("git").args(&["stash", "push"]).current_dir(&absolute_path)); } From 03d48dd735b18593f9e361f2156efae7bb551236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 11 Oct 2023 19:00:35 +0200 Subject: [PATCH 013/171] Rename Supress variant --- src/bootstrap/src/lib.rs | 10 +++++++--- src/bootstrap/src/utils/exec.rs | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 27d5faa3289ad..6f9a9beb33165 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -937,14 +937,18 @@ impl Build { /// Runs a command, printing out nice contextual information if it fails. fn run_quiet(&self, cmd: &mut Command) { - self.run_cmd(BootstrapCommand::from(cmd).fail_fast().output_mode(OutputMode::Suppress)); + self.run_cmd( + BootstrapCommand::from(cmd).fail_fast().output_mode(OutputMode::SuppressOnSuccess), + ); } /// Runs a command, printing out nice contextual information if it fails. /// Exits if the command failed to execute at all, otherwise returns its /// `status.success()`. fn run_quiet_delaying_failure(&self, cmd: &mut Command) -> bool { - self.run_cmd(BootstrapCommand::from(cmd).delay_failure().output_mode(OutputMode::Suppress)) + self.run_cmd( + BootstrapCommand::from(cmd).delay_failure().output_mode(OutputMode::SuppressOnSuccess), + ) } /// A centralized function for running commands that do not return output. @@ -965,7 +969,7 @@ impl Build { }), matches!(mode, OutputMode::PrintAll), ), - OutputMode::Suppress => (command.command.output(), true), + OutputMode::SuppressOnSuccess => (command.command.output(), true), }; let output = match output { diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index a3520ad5f983a..f244f5152beb6 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -18,7 +18,7 @@ pub enum OutputMode { /// Print the output (by inheriting stdout/stderr). PrintOutput, /// Suppress the output if the command succeeds, otherwise print the output. - Suppress, + SuppressOnSuccess, } /// Wrapper around `std::process::Command`. @@ -43,6 +43,6 @@ impl<'a> BootstrapCommand<'a> { impl<'a> From<&'a mut Command> for BootstrapCommand<'a> { fn from(command: &'a mut Command) -> Self { - Self { command, failure_behavior: None, output_mode: OutputMode::Suppress } + Self { command, failure_behavior: None, output_mode: OutputMode::SuppressOnSuccess } } } From b153a0182875b420bdecb19c81be0731e2e2f6b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 11 Oct 2023 19:02:00 +0200 Subject: [PATCH 014/171] Simplify BehaviorOnFailure --- src/bootstrap/src/lib.rs | 19 +++++++++---------- src/bootstrap/src/utils/exec.rs | 12 ++++++++---- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 6f9a9beb33165..701c8df11a723 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -997,19 +997,18 @@ impl Build { match result { Ok(_) => true, Err(_) => { - if let Some(failure_behavior) = command.failure_behavior { - match failure_behavior { - BehaviorOnFailure::DelayFail => { - let mut failures = self.delayed_failures.borrow_mut(); - failures.push(format!("{command:?}")); - } - BehaviorOnFailure::Exit => { + match command.failure_behavior { + BehaviorOnFailure::DelayFail => { + if self.fail_fast { exit!(1); } + + let mut failures = self.delayed_failures.borrow_mut(); + failures.push(format!("{command:?}")); + } + BehaviorOnFailure::Exit => { + exit!(1); } - } - if self.fail_fast { - exit!(1); } false } diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index f244f5152beb6..1b9e5cb174a0c 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -25,16 +25,16 @@ pub enum OutputMode { #[derive(Debug)] pub struct BootstrapCommand<'a> { pub command: &'a mut Command, - pub failure_behavior: Option, + pub failure_behavior: BehaviorOnFailure, pub output_mode: OutputMode, } impl<'a> BootstrapCommand<'a> { pub fn delay_failure(self) -> Self { - Self { failure_behavior: Some(BehaviorOnFailure::DelayFail), ..self } + Self { failure_behavior: BehaviorOnFailure::DelayFail, ..self } } pub fn fail_fast(self) -> Self { - Self { failure_behavior: Some(BehaviorOnFailure::Exit), ..self } + Self { failure_behavior: BehaviorOnFailure::Exit, ..self } } pub fn output_mode(self, output_mode: OutputMode) -> Self { Self { output_mode, ..self } @@ -43,6 +43,10 @@ impl<'a> BootstrapCommand<'a> { impl<'a> From<&'a mut Command> for BootstrapCommand<'a> { fn from(command: &'a mut Command) -> Self { - Self { command, failure_behavior: None, output_mode: OutputMode::SuppressOnSuccess } + Self { + command, + failure_behavior: BehaviorOnFailure::Exit, + output_mode: OutputMode::SuppressOnSuccess, + } } } From 0bca45f620ef436f2101d24d88c1f5a6b79eb988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 20 Sep 2023 22:13:43 +0000 Subject: [PATCH 015/171] allow target specs to declare self-contained linking components --- compiler/rustc_codegen_ssa/src/back/link.rs | 39 ++++- .../rustc_target/src/spec/linux_musl_base.rs | 5 +- compiler/rustc_target/src/spec/mod.rs | 159 +++++++++++++++++- .../rustc_target/src/spec/tests/tests_impl.rs | 2 +- compiler/rustc_target/src/spec/wasm32_wasi.rs | 5 +- .../src/spec/wasm32_wasi_preview1_threads.rs | 5 +- compiler/rustc_target/src/spec/wasm_base.rs | 4 +- .../rustc_target/src/spec/windows_gnu_base.rs | 5 +- 8 files changed, 197 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 28a51711b9361..9a796356593f2 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -22,7 +22,8 @@ use rustc_session::utils::NativeLibKind; /// need out of the shared crate context before we get rid of it. use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; -use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; +use rustc_target::spec::crt_objects::CrtObjects; +use rustc_target::spec::LinkSelfContained; use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy}; use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo}; @@ -1703,21 +1704,37 @@ fn detect_self_contained_mingw(sess: &Session) -> bool { /// instead of being found somewhere on the host system. /// We only provide such support for a very limited number of targets. fn self_contained(sess: &Session, crate_type: CrateType) -> bool { + // Emit an error if the user requested self-contained mode on the CLI but the target explicitly + // refuses it. if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set { - if sess.target.link_self_contained == LinkSelfContainedDefault::False { + if sess.target.link_self_contained.is_disabled() { sess.emit_err(errors::UnsupportedLinkSelfContained); } return self_contained; } match sess.target.link_self_contained { - LinkSelfContainedDefault::False => false, - LinkSelfContainedDefault::True => true, + LinkSelfContained::True => true, + LinkSelfContained::False => false, + LinkSelfContained::WithComponents(components) => { + if components.is_all() { + true + } else if components.is_empty() { + false + } else { + // FIXME: Currently no target makes use of individual components to mean + // self-contained linking is fully enabled, in the sense of what the code downstream + // from here expects. Until components are handled a bit more deeply, we can + // consider that it's disabled and remain backwards compatible. + false + } + } + // FIXME: Find a better heuristic for "native musl toolchain is available", // based on host and linker path, for example. // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). - LinkSelfContainedDefault::Musl => sess.crt_static(Some(crate_type)), - LinkSelfContainedDefault::Mingw => { + LinkSelfContained::InferredForMusl => sess.crt_static(Some(crate_type)), + LinkSelfContained::InferredForMingw => { sess.host == sess.target && sess.target.vendor != "uwp" && detect_self_contained_mingw(&sess) @@ -2978,9 +2995,13 @@ fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { } // 1. Implement the "self-contained" part of this feature by adding rustc distribution - // directories to the tool's search path: - // - if the self-contained linker is enabled on the CLI. - if sess.opts.cg.link_self_contained.is_linker_enabled() { + // directories to the tool's search path, depending on a mix between what users can specify on + // the CLI, and what the target spec enables (as it can't disable components): + // - if the self-contained linker is enabled on the CLI or by the target spec, + // - and if the self-contained linker is not disabled on the CLI. + let self_contained_linker = sess.opts.cg.link_self_contained.is_linker_enabled() + || sess.target.options.link_self_contained.is_linker_enabled(); + if self_contained_linker && !sess.opts.cg.link_self_contained.is_linker_disabled() { for path in sess.get_tools_search_paths(false) { cmd.arg({ let mut arg = OsString::from("-B"); diff --git a/compiler/rustc_target/src/spec/linux_musl_base.rs b/compiler/rustc_target/src/spec/linux_musl_base.rs index 61553e71b4500..318d06e9889dd 100644 --- a/compiler/rustc_target/src/spec/linux_musl_base.rs +++ b/compiler/rustc_target/src/spec/linux_musl_base.rs @@ -1,4 +1,5 @@ -use crate::spec::crt_objects::{self, LinkSelfContainedDefault}; +use crate::spec::crt_objects; +use crate::spec::LinkSelfContained; use crate::spec::TargetOptions; pub fn opts() -> TargetOptions { @@ -7,7 +8,7 @@ pub fn opts() -> TargetOptions { base.env = "musl".into(); base.pre_link_objects_self_contained = crt_objects::pre_musl_self_contained(); base.post_link_objects_self_contained = crt_objects::post_musl_self_contained(); - base.link_self_contained = LinkSelfContainedDefault::Musl; + base.link_self_contained = LinkSelfContained::InferredForMusl; // These targets statically link libc by default base.crt_static_default = true; diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 16f70cf43b3fb..8c41acdf4c656 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -520,6 +520,92 @@ impl ToJson for LinkerFlavorCli { } } +/// The different `-Clink-self-contained` options that can be specified in a target spec: +/// - enabling or disabling in bulk +/// - some target-specific pieces of inference to determine whether to use self-contained linking +/// if `-Clink-self-contained` is not specified explicitly (e.g. on musl/mingw) +/// - explicitly enabling some of the self-contained linking components, e.g. the linker component +/// to use `rust-lld` +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum LinkSelfContained { + /// The target spec explicitly enables self-contained linking. + True, + + /// The target spec explicitly disables self-contained linking. + False, + + /// The target spec requests that the self-contained mode is inferred, in the context of musl. + InferredForMusl, + + /// The target spec requests that the self-contained mode is inferred, in the context of mingw. + InferredForMingw, + + /// The target spec explicitly enables a list of self-contained linking components: e.g. for + /// targets opting into a subset of components like the CLI's `-C link-self-contained=+linker`. + WithComponents(LinkSelfContainedComponents), +} + +impl ToJson for LinkSelfContained { + fn to_json(&self) -> Json { + match *self { + LinkSelfContained::WithComponents(components) => { + // Serialize the components in a json object's `components` field, to prepare for a + // future where `crt-objects-fallback` is removed from the json specs and + // incorporated as a field here. + let mut map = BTreeMap::new(); + map.insert("components", components); + map.to_json() + } + + // Stable values backwards-compatible with `LinkSelfContainedDefault` + LinkSelfContained::True => "true".to_json(), + LinkSelfContained::False => "false".to_json(), + LinkSelfContained::InferredForMusl => "musl".to_json(), + LinkSelfContained::InferredForMingw => "mingw".to_json(), + } + } +} + +impl LinkSelfContained { + /// Returns whether the target spec has self-contained linking explicitly disabled. Used to emit + /// errors if the user then enables it on the CLI. + pub fn is_disabled(self) -> bool { + self == LinkSelfContained::False + } + + /// Returns whether the target spec explictly requests self-contained linking, i.e. not via + /// inference. + pub fn is_linker_enabled(self) -> bool { + match self { + LinkSelfContained::True => true, + LinkSelfContained::False => false, + LinkSelfContained::WithComponents(c) => c.contains(LinkSelfContainedComponents::LINKER), + _ => false, + } + } + + /// Returns the key to use when serializing the setting to json: + /// - individual components in a `link-self-contained` object value + /// - the other variants as a backwards-compatible `crt-objects-fallback` string + fn json_key(self) -> &'static str { + match self { + LinkSelfContained::WithComponents(_) => "link-self-contained", + _ => "crt-objects-fallback", + } + } +} + +impl From for LinkSelfContained { + fn from(value: LinkSelfContainedDefault) -> Self { + match value { + LinkSelfContainedDefault::True => LinkSelfContained::True, + LinkSelfContainedDefault::False => LinkSelfContained::False, + LinkSelfContainedDefault::Musl => LinkSelfContained::InferredForMusl, + LinkSelfContainedDefault::Mingw => LinkSelfContained::InferredForMingw, + } + } +} + bitflags::bitflags! { #[derive(Default)] /// The `-C link-self-contained` components that can individually be enabled or disabled. @@ -594,6 +680,22 @@ impl IntoIterator for LinkSelfContainedComponents { } } +impl ToJson for LinkSelfContainedComponents { + fn to_json(&self) -> Json { + let components: Vec<_> = Self::all_components() + .into_iter() + .filter(|c| self.contains(*c)) + .map(|c| { + // We can unwrap because we're iterating over all the known singular components, + // not an actual set of flags where `as_str` can fail. + c.as_str().unwrap().to_owned() + }) + .collect(); + + components.to_json() + } +} + #[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)] pub enum PanicStrategy { Unwind, @@ -1769,7 +1871,9 @@ pub struct TargetOptions { /// Same as `(pre|post)_link_objects`, but when self-contained linking mode is enabled. pub pre_link_objects_self_contained: CrtObjects, pub post_link_objects_self_contained: CrtObjects, - pub link_self_contained: LinkSelfContainedDefault, + /// Behavior for the self-contained linking mode: inferred for some targets, or explicitly + /// enabled (in bulk, or with individual components). + pub link_self_contained: LinkSelfContained, /// Linker arguments that are passed *before* any user-defined libraries. pub pre_link_args: LinkArgs, @@ -2242,7 +2346,7 @@ impl Default for TargetOptions { post_link_objects: Default::default(), pre_link_objects_self_contained: Default::default(), post_link_objects_self_contained: Default::default(), - link_self_contained: LinkSelfContainedDefault::False, + link_self_contained: LinkSelfContained::False, pre_link_args: LinkArgs::new(), pre_link_args_json: LinkArgsCli::new(), late_link_args: LinkArgs::new(), @@ -2723,12 +2827,47 @@ impl Target { } Ok::<(), String>(()) } ); - - ($key_name:ident = $json_name:expr, link_self_contained) => ( { + ($key_name:ident, LinkSelfContained) => ( { + // Skeleton of what needs to be parsed: + // + // ``` + // $name: { + // "components": [ + // + // ] + // } + // ``` + let name = (stringify!($key_name)).replace("_", "-"); + if let Some(o) = obj.remove(&name) { + if let Some(o) = o.as_object() { + let component_array = o.get("components") + .ok_or_else(|| format!("{name}: expected a \ + JSON object with a `components` field."))?; + let component_array = component_array.as_array() + .ok_or_else(|| format!("{name}.components: expected a JSON array"))?; + let mut components = LinkSelfContainedComponents::empty(); + for s in component_array { + components |= match s.as_str() { + Some(s) => { + LinkSelfContainedComponents::from_str(s) + .ok_or_else(|| format!("unknown \ + `-Clink-self-contained` component: {s}"))? + }, + _ => return Err(format!("not a string: {:?}", s)), + }; + } + base.$key_name = LinkSelfContained::WithComponents(components); + } else { + incorrect_type.push(name) + } + } + Ok::<(), String>(()) + } ); + ($key_name:ident = $json_name:expr, LinkSelfContainedDefault) => ( { let name = $json_name; obj.remove(name).and_then(|o| o.as_str().and_then(|s| { match s.parse::() { - Ok(lsc_default) => base.$key_name = lsc_default, + Ok(lsc_default) => base.$key_name = lsc_default.into(), _ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \ Use 'false', 'true', 'musl' or 'mingw'", s))), } @@ -2877,7 +3016,10 @@ impl Target { key!(post_link_objects = "post-link-objects", link_objects); key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects); key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects); - key!(link_self_contained = "crt-objects-fallback", link_self_contained)?; + // Deserializes the backwards-compatible variants of `-Clink-self-contained` + key!(link_self_contained = "crt-objects-fallback", LinkSelfContainedDefault)?; + // Deserializes the components variant of `-Clink-self-contained` + key!(link_self_contained, LinkSelfContained)?; key!(pre_link_args_json = "pre-link-args", link_args); key!(late_link_args_json = "late-link-args", link_args); key!(late_link_args_dynamic_json = "late-link-args-dynamic", link_args); @@ -3133,7 +3275,6 @@ impl ToJson for Target { target_option_val!(post_link_objects); target_option_val!(pre_link_objects_self_contained, "pre-link-objects-fallback"); target_option_val!(post_link_objects_self_contained, "post-link-objects-fallback"); - target_option_val!(link_self_contained, "crt-objects-fallback"); target_option_val!(link_args - pre_link_args_json, "pre-link-args"); target_option_val!(link_args - late_link_args_json, "late-link-args"); target_option_val!(link_args - late_link_args_dynamic_json, "late-link-args-dynamic"); @@ -3230,6 +3371,10 @@ impl ToJson for Target { d.insert("default-adjusted-cabi".into(), Abi::name(abi).to_json()); } + // Serializing `-Clink-self-contained` needs a dynamic key to support the + // backwards-compatible variants. + d.insert(self.link_self_contained.json_key().into(), self.link_self_contained.to_json()); + Json::Object(d) } } diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs index e0ecf8037c3e5..257867b1b807b 100644 --- a/compiler/rustc_target/src/spec/tests/tests_impl.rs +++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs @@ -97,7 +97,7 @@ impl Target { ); } - if self.link_self_contained == LinkSelfContainedDefault::False { + if self.link_self_contained.is_disabled() { assert!( self.pre_link_objects_self_contained.is_empty() && self.post_link_objects_self_contained.is_empty() diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index a0476d542e642..2c00c601b394e 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -72,7 +72,8 @@ //! best we can with this target. Don't start relying on too much here unless //! you know what you're getting in to! -use super::crt_objects::{self, LinkSelfContainedDefault}; +use super::crt_objects; +use super::LinkSelfContained; use super::{wasm_base, Cc, LinkerFlavor, Target}; pub fn target() -> Target { @@ -85,7 +86,7 @@ pub fn target() -> Target { options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained(); // FIXME: Figure out cases in which WASM needs to link with a native toolchain. - options.link_self_contained = LinkSelfContainedDefault::True; + options.link_self_contained = LinkSelfContained::True; // Right now this is a bit of a workaround but we're currently saying that // the target by default has a static crt which we're taking as a signal diff --git a/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs b/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs index c567155fee653..93a49acb15180 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs @@ -72,7 +72,8 @@ //! best we can with this target. Don't start relying on too much here unless //! you know what you're getting in to! -use super::crt_objects::{self, LinkSelfContainedDefault}; +use super::crt_objects; +use super::LinkSelfContained; use super::{wasm_base, Cc, LinkerFlavor, Target}; pub fn target() -> Target { @@ -98,7 +99,7 @@ pub fn target() -> Target { options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained(); // FIXME: Figure out cases in which WASM needs to link with a native toolchain. - options.link_self_contained = LinkSelfContainedDefault::True; + options.link_self_contained = LinkSelfContained::True; // Right now this is a bit of a workaround but we're currently saying that // the target by default has a static crt which we're taking as a signal diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs index 341763aadbaf1..a29bddd849b43 100644 --- a/compiler/rustc_target/src/spec/wasm_base.rs +++ b/compiler/rustc_target/src/spec/wasm_base.rs @@ -1,5 +1,5 @@ -use super::crt_objects::LinkSelfContainedDefault; use super::{cvs, Cc, LinkerFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel}; +use crate::spec::LinkSelfContained; pub fn options() -> TargetOptions { macro_rules! args { @@ -100,7 +100,7 @@ pub fn options() -> TargetOptions { // rust-lang/rust#104137: cannot blindly remove this without putting in // some other way to compensate for lack of `-nostartfiles` in linker // invocation. - link_self_contained: LinkSelfContainedDefault::True, + link_self_contained: LinkSelfContained::True, // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when // PIC code is implemented this has quite a drastic effect if it stays diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs index 2231983f07126..d99a95a77e145 100644 --- a/compiler/rustc_target/src/spec/windows_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -1,4 +1,5 @@ -use crate::spec::crt_objects::{self, LinkSelfContainedDefault}; +use crate::spec::crt_objects; +use crate::spec::LinkSelfContained; use crate::spec::{cvs, Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions}; use std::borrow::Cow; @@ -90,7 +91,7 @@ pub fn opts() -> TargetOptions { post_link_objects: crt_objects::post_mingw(), pre_link_objects_self_contained: crt_objects::pre_mingw_self_contained(), post_link_objects_self_contained: crt_objects::post_mingw_self_contained(), - link_self_contained: LinkSelfContainedDefault::Mingw, + link_self_contained: LinkSelfContained::InferredForMingw, late_link_args, late_link_args_dynamic, late_link_args_static, From 5f24e314ef9a87a796dd8a06b2e0ecff4ac2e8da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 20 Sep 2023 22:50:49 +0000 Subject: [PATCH 016/171] use asymmetric json roundtripping this ensures roundtripping of stable and unstable values: - backwards-compatible values can be deserialized, as well as the new unstable values - unstable values are serialized. --- compiler/rustc_target/src/spec/mod.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 8c41acdf4c656..2d8b1b2398081 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -278,6 +278,7 @@ impl LinkerFlavor { } } + /// Returns the corresponding backwards-compatible CLI flavor. fn to_cli(self) -> LinkerFlavorCli { match self { LinkerFlavor::Gnu(Cc::Yes, _) @@ -298,6 +299,20 @@ impl LinkerFlavor { } } + /// Returns the modern CLI flavor that is the counterpart of this flavor. + fn to_cli_counterpart(self) -> LinkerFlavorCli { + match self { + LinkerFlavor::Gnu(cc, lld) => LinkerFlavorCli::Gnu(cc, lld), + LinkerFlavor::Darwin(cc, lld) => LinkerFlavorCli::Darwin(cc, lld), + LinkerFlavor::WasmLld(cc) => LinkerFlavorCli::WasmLld(cc), + LinkerFlavor::Unix(cc) => LinkerFlavorCli::Unix(cc), + LinkerFlavor::Msvc(lld) => LinkerFlavorCli::Msvc(lld), + LinkerFlavor::EmCc => LinkerFlavorCli::EmCc, + LinkerFlavor::Bpf => LinkerFlavorCli::Bpf, + LinkerFlavor::Ptx => LinkerFlavorCli::Ptx, + } + } + fn infer_cli_hints(cli: LinkerFlavorCli) -> (Option, Option) { match cli { LinkerFlavorCli::Gnu(cc, lld) | LinkerFlavorCli::Darwin(cc, lld) => { @@ -2273,7 +2288,7 @@ impl TargetOptions { } fn update_to_cli(&mut self) { - self.linker_flavor_json = self.linker_flavor.to_cli(); + self.linker_flavor_json = self.linker_flavor.to_cli_counterpart(); self.lld_flavor_json = self.linker_flavor.lld_flavor(); self.linker_is_gnu_json = self.linker_flavor.is_gnu(); for (args, args_json) in [ @@ -2283,8 +2298,10 @@ impl TargetOptions { (&self.late_link_args_static, &mut self.late_link_args_static_json), (&self.post_link_args, &mut self.post_link_args_json), ] { - *args_json = - args.iter().map(|(flavor, args)| (flavor.to_cli(), args.clone())).collect(); + *args_json = args + .iter() + .map(|(flavor, args)| (flavor.to_cli_counterpart(), args.clone())) + .collect(); } } } From fe7a843278343ed7406c420548da0c26aebdd170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Thu, 21 Sep 2023 14:05:09 +0000 Subject: [PATCH 017/171] add end-to-end test of custom target using rust-lld starting from the x86_64-unknown-linux-gnu specs, we add the lld linker flavor and self-contained linker component --- .../run-make/rust-lld-custom-target/Makefile | 7 +++ .../rust-lld-custom-target/custom-target.json | 57 +++++++++++++++++++ tests/run-make/rust-lld-custom-target/lib.rs | 9 +++ 3 files changed, 73 insertions(+) create mode 100644 tests/run-make/rust-lld-custom-target/Makefile create mode 100644 tests/run-make/rust-lld-custom-target/custom-target.json create mode 100644 tests/run-make/rust-lld-custom-target/lib.rs diff --git a/tests/run-make/rust-lld-custom-target/Makefile b/tests/run-make/rust-lld-custom-target/Makefile new file mode 100644 index 0000000000000..007493ab0b99c --- /dev/null +++ b/tests/run-make/rust-lld-custom-target/Makefile @@ -0,0 +1,7 @@ +include ../tools.mk + +# needs-rust-lld +# only-x86_64-unknown-linux-gnu +all: + RUSTC_LOG=rustc_codegen_ssa::back::link=info $(RUSTC) --crate-type cdylib --target custom-target.json -Clink-args=-Wl,-v lib.rs 2> $(TMPDIR)/output.txt + $(CGREP) -e "^LLD [0-9]+\.[0-9]+\.[0-9]+" < $(TMPDIR)/output.txt diff --git a/tests/run-make/rust-lld-custom-target/custom-target.json b/tests/run-make/rust-lld-custom-target/custom-target.json new file mode 100644 index 0000000000000..7828a99f235c1 --- /dev/null +++ b/tests/run-make/rust-lld-custom-target/custom-target.json @@ -0,0 +1,57 @@ +{ + "arch": "x86_64", + "cpu": "x86-64", + "crt-static-respected": true, + "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", + "dynamic-linking": true, + "env": "gnu", + "has-rpath": true, + "has-thread-local": true, + "link-self-contained": { + "components": [ + "linker" + ] + }, + "linker-flavor": "gnu-lld-cc", + "llvm-target": "x86_64-unknown-linux-gnu", + "max-atomic-width": 64, + "os": "linux", + "plt-by-default": false, + "position-independent-executables": true, + "pre-link-args": { + "gnu-cc": [ + "-m64" + ], + "gnu-lld-cc": [ + "-m64" + ] + }, + "relro-level": "full", + "stack-probes": { + "kind": "inline-or-call", + "min-llvm-version-for-inline": [ + 16, + 0, + 0 + ] + }, + "static-position-independent-executables": true, + "supported-sanitizers": [ + "address", + "cfi", + "leak", + "memory", + "thread", + "safestack" + ], + "supported-split-debuginfo": [ + "packed", + "unpacked", + "off" + ], + "supports-xray": true, + "target-family": [ + "unix" + ], + "target-pointer-width": "64" +} diff --git a/tests/run-make/rust-lld-custom-target/lib.rs b/tests/run-make/rust-lld-custom-target/lib.rs new file mode 100644 index 0000000000000..d8f5e310821f5 --- /dev/null +++ b/tests/run-make/rust-lld-custom-target/lib.rs @@ -0,0 +1,9 @@ +// Test linking using `cc` with `rust-lld`, using a custom target with features described in MCP 510 +// see https://github.com/rust-lang/compiler-team/issues/510 for more info: +// +// Starting from the `x86_64-unknown-linux-gnu` target spec, we add the following options: +// - a linker-flavor using lld via a C compiler +// - the self-contained linker component is enabled + +#![feature(no_core)] +#![no_core] From b816207e0562a1e9fb6e6e7b93b2fcc3e3c324f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 27 Sep 2023 13:41:34 +0000 Subject: [PATCH 018/171] limit `lld` flavors to the llvm backend --- compiler/rustc_codegen_ssa/src/back/link.rs | 23 +++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 9a796356593f2..fb79df4f791c0 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -2999,8 +2999,27 @@ fn add_lld_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) { // the CLI, and what the target spec enables (as it can't disable components): // - if the self-contained linker is enabled on the CLI or by the target spec, // - and if the self-contained linker is not disabled on the CLI. - let self_contained_linker = sess.opts.cg.link_self_contained.is_linker_enabled() - || sess.target.options.link_self_contained.is_linker_enabled(); + let self_contained_cli = sess.opts.cg.link_self_contained.is_linker_enabled(); + let self_contained_target = sess.target.options.link_self_contained.is_linker_enabled(); + + // FIXME: in the future, codegen backends may need to have more control over this process: they + // don't always support all the features the linker expects here, and vice versa. For example, + // at the time of writing this, lld expects a newer style of aarch64 TLS relocations that + // cranelift doesn't implement yet. That in turn can impact whether linking would succeed on + // such a target when using the `cg_clif` backend and lld. + // + // Until interactions between backends and linker features are expressible, we limit target + // specs to opt-in to lld only when we're on the llvm backend, where it's expected to work and + // tested on CI. As usual, the CLI still has precedence over this, so that users and developers + // can still override this default when needed (e.g. for tests). + let uses_llvm_backend = + matches!(sess.opts.unstable_opts.codegen_backend.as_deref(), None | Some("llvm")); + if !uses_llvm_backend && !self_contained_cli && sess.opts.cg.linker_flavor.is_none() { + // We bail if we're not using llvm and lld was not explicitly requested on the CLI. + return; + } + + let self_contained_linker = self_contained_cli || self_contained_target; if self_contained_linker && !sess.opts.cg.link_self_contained.is_linker_disabled() { for path in sess.get_tools_search_paths(false) { cmd.arg({ From e569a3691a80e597b382ec6773e4d227402fcc3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 18 Oct 2023 13:15:20 +0000 Subject: [PATCH 019/171] unify `LinkSelfContained` and `LinkSelfContainedDefault` Removes the backwards-compatible `LinkSelfContainedDefault`, by incorporating the remaining specifics into `LinkSelfContained`. Then renames the modern options to keep the old name. --- compiler/rustc_codegen_ssa/src/back/link.rs | 12 +-- compiler/rustc_target/src/spec/crt_objects.rs | 38 --------- .../rustc_target/src/spec/linux_musl_base.rs | 5 +- compiler/rustc_target/src/spec/mod.rs | 77 +++++++++++-------- compiler/rustc_target/src/spec/wasm32_wasi.rs | 4 +- .../src/spec/wasm32_wasi_preview1_threads.rs | 7 +- compiler/rustc_target/src/spec/wasm_base.rs | 4 +- .../rustc_target/src/spec/windows_gnu_base.rs | 4 +- 8 files changed, 60 insertions(+), 91 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index fb79df4f791c0..104f30697bd9f 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -23,7 +23,7 @@ use rustc_session::utils::NativeLibKind; use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; use rustc_target::spec::crt_objects::CrtObjects; -use rustc_target::spec::LinkSelfContained; +use rustc_target::spec::LinkSelfContainedDefault; use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy}; use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo}; @@ -1714,9 +1714,9 @@ fn self_contained(sess: &Session, crate_type: CrateType) -> bool { } match sess.target.link_self_contained { - LinkSelfContained::True => true, - LinkSelfContained::False => false, - LinkSelfContained::WithComponents(components) => { + LinkSelfContainedDefault::False => false, + LinkSelfContainedDefault::True => true, + LinkSelfContainedDefault::WithComponents(components) => { if components.is_all() { true } else if components.is_empty() { @@ -1733,8 +1733,8 @@ fn self_contained(sess: &Session, crate_type: CrateType) -> bool { // FIXME: Find a better heuristic for "native musl toolchain is available", // based on host and linker path, for example. // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). - LinkSelfContained::InferredForMusl => sess.crt_static(Some(crate_type)), - LinkSelfContained::InferredForMingw => { + LinkSelfContainedDefault::InferredForMusl => sess.crt_static(Some(crate_type)), + LinkSelfContainedDefault::InferredForMingw => { sess.host == sess.target && sess.target.vendor != "uwp" && detect_self_contained_mingw(&sess) diff --git a/compiler/rustc_target/src/spec/crt_objects.rs b/compiler/rustc_target/src/spec/crt_objects.rs index c126390f5a908..53f710b8f9e14 100644 --- a/compiler/rustc_target/src/spec/crt_objects.rs +++ b/compiler/rustc_target/src/spec/crt_objects.rs @@ -40,11 +40,9 @@ //! but not gcc's. As a result rustc cannot link with C++ static libraries (#36710) //! when linking in self-contained mode. -use crate::json::{Json, ToJson}; use crate::spec::LinkOutputKind; use std::borrow::Cow; use std::collections::BTreeMap; -use std::str::FromStr; pub type CrtObjects = BTreeMap>>; @@ -123,39 +121,3 @@ pub(super) fn pre_wasi_self_contained() -> CrtObjects { pub(super) fn post_wasi_self_contained() -> CrtObjects { new(&[]) } - -/// Which logic to use to determine whether to use self-contained linking mode -/// if `-Clink-self-contained` is not specified explicitly. -#[derive(Clone, Copy, PartialEq, Hash, Debug)] -pub enum LinkSelfContainedDefault { - False, - True, - Musl, - Mingw, -} - -impl FromStr for LinkSelfContainedDefault { - type Err = (); - - fn from_str(s: &str) -> Result { - Ok(match s { - "false" => LinkSelfContainedDefault::False, - "true" | "wasm" => LinkSelfContainedDefault::True, - "musl" => LinkSelfContainedDefault::Musl, - "mingw" => LinkSelfContainedDefault::Mingw, - _ => return Err(()), - }) - } -} - -impl ToJson for LinkSelfContainedDefault { - fn to_json(&self) -> Json { - match *self { - LinkSelfContainedDefault::False => "false", - LinkSelfContainedDefault::True => "true", - LinkSelfContainedDefault::Musl => "musl", - LinkSelfContainedDefault::Mingw => "mingw", - } - .to_json() - } -} diff --git a/compiler/rustc_target/src/spec/linux_musl_base.rs b/compiler/rustc_target/src/spec/linux_musl_base.rs index 318d06e9889dd..b698bcbcef698 100644 --- a/compiler/rustc_target/src/spec/linux_musl_base.rs +++ b/compiler/rustc_target/src/spec/linux_musl_base.rs @@ -1,6 +1,5 @@ use crate::spec::crt_objects; -use crate::spec::LinkSelfContained; -use crate::spec::TargetOptions; +use crate::spec::{LinkSelfContainedDefault, TargetOptions}; pub fn opts() -> TargetOptions { let mut base = super::linux_base::opts(); @@ -8,7 +7,7 @@ pub fn opts() -> TargetOptions { base.env = "musl".into(); base.pre_link_objects_self_contained = crt_objects::pre_musl_self_contained(); base.post_link_objects_self_contained = crt_objects::post_musl_self_contained(); - base.link_self_contained = LinkSelfContained::InferredForMusl; + base.link_self_contained = LinkSelfContainedDefault::InferredForMusl; // These targets statically link libc by default base.crt_static_default = true; diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 2d8b1b2398081..9141b169ae984 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -38,7 +38,7 @@ use crate::abi::call::Conv; use crate::abi::{Endian, Integer, Size, TargetDataLayout, TargetDataLayoutErrors}; use crate::json::{Json, ToJson}; use crate::spec::abi::{lookup as lookup_abi, Abi}; -use crate::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault}; +use crate::spec::crt_objects::CrtObjects; use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; use rustc_fs_util::try_canonicalize; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; @@ -542,7 +542,7 @@ impl ToJson for LinkerFlavorCli { /// - explicitly enabling some of the self-contained linking components, e.g. the linker component /// to use `rust-lld` #[derive(Clone, Copy, PartialEq, Debug)] -pub enum LinkSelfContained { +pub enum LinkSelfContainedDefault { /// The target spec explicitly enables self-contained linking. True, @@ -560,10 +560,25 @@ pub enum LinkSelfContained { WithComponents(LinkSelfContainedComponents), } -impl ToJson for LinkSelfContained { +/// Parses a backwards-compatible `-Clink-self-contained` option string, without components. +impl FromStr for LinkSelfContainedDefault { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "false" => LinkSelfContainedDefault::False, + "true" | "wasm" => LinkSelfContainedDefault::True, + "musl" => LinkSelfContainedDefault::InferredForMusl, + "mingw" => LinkSelfContainedDefault::InferredForMingw, + _ => return Err(()), + }) + } +} + +impl ToJson for LinkSelfContainedDefault { fn to_json(&self) -> Json { match *self { - LinkSelfContained::WithComponents(components) => { + LinkSelfContainedDefault::WithComponents(components) => { // Serialize the components in a json object's `components` field, to prepare for a // future where `crt-objects-fallback` is removed from the json specs and // incorporated as a field here. @@ -572,29 +587,31 @@ impl ToJson for LinkSelfContained { map.to_json() } - // Stable values backwards-compatible with `LinkSelfContainedDefault` - LinkSelfContained::True => "true".to_json(), - LinkSelfContained::False => "false".to_json(), - LinkSelfContained::InferredForMusl => "musl".to_json(), - LinkSelfContained::InferredForMingw => "mingw".to_json(), + // Stable backwards-compatible values + LinkSelfContainedDefault::True => "true".to_json(), + LinkSelfContainedDefault::False => "false".to_json(), + LinkSelfContainedDefault::InferredForMusl => "musl".to_json(), + LinkSelfContainedDefault::InferredForMingw => "mingw".to_json(), } } } -impl LinkSelfContained { +impl LinkSelfContainedDefault { /// Returns whether the target spec has self-contained linking explicitly disabled. Used to emit /// errors if the user then enables it on the CLI. pub fn is_disabled(self) -> bool { - self == LinkSelfContained::False + self == LinkSelfContainedDefault::False } /// Returns whether the target spec explictly requests self-contained linking, i.e. not via /// inference. pub fn is_linker_enabled(self) -> bool { match self { - LinkSelfContained::True => true, - LinkSelfContained::False => false, - LinkSelfContained::WithComponents(c) => c.contains(LinkSelfContainedComponents::LINKER), + LinkSelfContainedDefault::True => true, + LinkSelfContainedDefault::False => false, + LinkSelfContainedDefault::WithComponents(c) => { + c.contains(LinkSelfContainedComponents::LINKER) + } _ => false, } } @@ -604,23 +621,12 @@ impl LinkSelfContained { /// - the other variants as a backwards-compatible `crt-objects-fallback` string fn json_key(self) -> &'static str { match self { - LinkSelfContained::WithComponents(_) => "link-self-contained", + LinkSelfContainedDefault::WithComponents(_) => "link-self-contained", _ => "crt-objects-fallback", } } } -impl From for LinkSelfContained { - fn from(value: LinkSelfContainedDefault) -> Self { - match value { - LinkSelfContainedDefault::True => LinkSelfContained::True, - LinkSelfContainedDefault::False => LinkSelfContained::False, - LinkSelfContainedDefault::Musl => LinkSelfContained::InferredForMusl, - LinkSelfContainedDefault::Mingw => LinkSelfContained::InferredForMingw, - } - } -} - bitflags::bitflags! { #[derive(Default)] /// The `-C link-self-contained` components that can individually be enabled or disabled. @@ -1888,7 +1894,7 @@ pub struct TargetOptions { pub post_link_objects_self_contained: CrtObjects, /// Behavior for the self-contained linking mode: inferred for some targets, or explicitly /// enabled (in bulk, or with individual components). - pub link_self_contained: LinkSelfContained, + pub link_self_contained: LinkSelfContainedDefault, /// Linker arguments that are passed *before* any user-defined libraries. pub pre_link_args: LinkArgs, @@ -2363,7 +2369,7 @@ impl Default for TargetOptions { post_link_objects: Default::default(), pre_link_objects_self_contained: Default::default(), post_link_objects_self_contained: Default::default(), - link_self_contained: LinkSelfContained::False, + link_self_contained: LinkSelfContainedDefault::False, pre_link_args: LinkArgs::new(), pre_link_args_json: LinkArgsCli::new(), late_link_args: LinkArgs::new(), @@ -2844,7 +2850,7 @@ impl Target { } Ok::<(), String>(()) } ); - ($key_name:ident, LinkSelfContained) => ( { + ($key_name:ident, link_self_contained_components) => ( { // Skeleton of what needs to be parsed: // // ``` @@ -2873,18 +2879,18 @@ impl Target { _ => return Err(format!("not a string: {:?}", s)), }; } - base.$key_name = LinkSelfContained::WithComponents(components); + base.$key_name = LinkSelfContainedDefault::WithComponents(components); } else { incorrect_type.push(name) } } Ok::<(), String>(()) } ); - ($key_name:ident = $json_name:expr, LinkSelfContainedDefault) => ( { + ($key_name:ident = $json_name:expr, link_self_contained_backwards_compatible) => ( { let name = $json_name; obj.remove(name).and_then(|o| o.as_str().and_then(|s| { match s.parse::() { - Ok(lsc_default) => base.$key_name = lsc_default.into(), + Ok(lsc_default) => base.$key_name = lsc_default, _ => return Some(Err(format!("'{}' is not a valid `-Clink-self-contained` default. \ Use 'false', 'true', 'musl' or 'mingw'", s))), } @@ -3034,9 +3040,12 @@ impl Target { key!(pre_link_objects_self_contained = "pre-link-objects-fallback", link_objects); key!(post_link_objects_self_contained = "post-link-objects-fallback", link_objects); // Deserializes the backwards-compatible variants of `-Clink-self-contained` - key!(link_self_contained = "crt-objects-fallback", LinkSelfContainedDefault)?; + key!( + link_self_contained = "crt-objects-fallback", + link_self_contained_backwards_compatible + )?; // Deserializes the components variant of `-Clink-self-contained` - key!(link_self_contained, LinkSelfContained)?; + key!(link_self_contained, link_self_contained_components)?; key!(pre_link_args_json = "pre-link-args", link_args); key!(late_link_args_json = "late-link-args", link_args); key!(late_link_args_dynamic_json = "late-link-args-dynamic", link_args); diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs index 2c00c601b394e..23fabcdc90de5 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs @@ -73,7 +73,7 @@ //! you know what you're getting in to! use super::crt_objects; -use super::LinkSelfContained; +use super::LinkSelfContainedDefault; use super::{wasm_base, Cc, LinkerFlavor, Target}; pub fn target() -> Target { @@ -86,7 +86,7 @@ pub fn target() -> Target { options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained(); // FIXME: Figure out cases in which WASM needs to link with a native toolchain. - options.link_self_contained = LinkSelfContained::True; + options.link_self_contained = LinkSelfContainedDefault::True; // Right now this is a bit of a workaround but we're currently saying that // the target by default has a static crt which we're taking as a signal diff --git a/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs b/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs index 93a49acb15180..ba9a99ae380e4 100644 --- a/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs +++ b/compiler/rustc_target/src/spec/wasm32_wasi_preview1_threads.rs @@ -72,9 +72,8 @@ //! best we can with this target. Don't start relying on too much here unless //! you know what you're getting in to! -use super::crt_objects; -use super::LinkSelfContained; -use super::{wasm_base, Cc, LinkerFlavor, Target}; +use super::{crt_objects, wasm_base}; +use super::{Cc, LinkSelfContainedDefault, LinkerFlavor, Target}; pub fn target() -> Target { let mut options = wasm_base::options(); @@ -99,7 +98,7 @@ pub fn target() -> Target { options.post_link_objects_self_contained = crt_objects::post_wasi_self_contained(); // FIXME: Figure out cases in which WASM needs to link with a native toolchain. - options.link_self_contained = LinkSelfContained::True; + options.link_self_contained = LinkSelfContainedDefault::True; // Right now this is a bit of a workaround but we're currently saying that // the target by default has a static crt which we're taking as a signal diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs index a29bddd849b43..82a3afeae31e1 100644 --- a/compiler/rustc_target/src/spec/wasm_base.rs +++ b/compiler/rustc_target/src/spec/wasm_base.rs @@ -1,5 +1,5 @@ +use super::LinkSelfContainedDefault; use super::{cvs, Cc, LinkerFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel}; -use crate::spec::LinkSelfContained; pub fn options() -> TargetOptions { macro_rules! args { @@ -100,7 +100,7 @@ pub fn options() -> TargetOptions { // rust-lang/rust#104137: cannot blindly remove this without putting in // some other way to compensate for lack of `-nostartfiles` in linker // invocation. - link_self_contained: LinkSelfContained::True, + link_self_contained: LinkSelfContainedDefault::True, // This has no effect in LLVM 8 or prior, but in LLVM 9 and later when // PIC code is implemented this has quite a drastic effect if it stays diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs index d99a95a77e145..b84e0fc078303 100644 --- a/compiler/rustc_target/src/spec/windows_gnu_base.rs +++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs @@ -1,5 +1,5 @@ use crate::spec::crt_objects; -use crate::spec::LinkSelfContained; +use crate::spec::LinkSelfContainedDefault; use crate::spec::{cvs, Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions}; use std::borrow::Cow; @@ -91,7 +91,7 @@ pub fn opts() -> TargetOptions { post_link_objects: crt_objects::post_mingw(), pre_link_objects_self_contained: crt_objects::pre_mingw_self_contained(), post_link_objects_self_contained: crt_objects::post_mingw_self_contained(), - link_self_contained: LinkSelfContained::InferredForMingw, + link_self_contained: LinkSelfContainedDefault::InferredForMingw, late_link_args, late_link_args_dynamic, late_link_args_static, From 0b40c7c6822939e97c008864067d9dac59cc3931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Rakic?= Date: Wed, 18 Oct 2023 21:24:02 +0000 Subject: [PATCH 020/171] make `self_contained` return `LinkSelfContainedComponents` --- compiler/rustc_codegen_ssa/src/back/link.rs | 115 +++++++++++--------- compiler/rustc_target/src/spec/mod.rs | 15 +++ 2 files changed, 80 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 104f30697bd9f..62b482cf92e2c 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -23,6 +23,7 @@ use rustc_session::utils::NativeLibKind; use rustc_session::{filesearch, Session}; use rustc_span::symbol::Symbol; use rustc_target::spec::crt_objects::CrtObjects; +use rustc_target::spec::LinkSelfContainedComponents; use rustc_target::spec::LinkSelfContainedDefault; use rustc_target::spec::{Cc, LinkOutputKind, LinkerFlavor, Lld, PanicStrategy}; use rustc_target::spec::{RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo}; @@ -721,6 +722,7 @@ fn link_natively<'a>( ) -> Result<(), ErrorGuaranteed> { info!("preparing {:?} to {:?}", crate_type, out_filename); let (linker_path, flavor) = linker_and_flavor(sess); + let self_contained_components = self_contained_components(sess, crate_type); let mut cmd = linker_with_args( &linker_path, flavor, @@ -730,6 +732,7 @@ fn link_natively<'a>( tmpdir, out_filename, codegen_results, + self_contained_components, )?; linker::disable_localization(&mut cmd); @@ -805,14 +808,14 @@ fn link_natively<'a>( "Linker does not support -static-pie command line option. Retrying with -static instead." ); // Mirror `add_(pre,post)_link_objects` to replace CRT objects. - let self_contained = self_contained(sess, crate_type); + let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled(); let opts = &sess.target; - let pre_objects = if self_contained { + let pre_objects = if self_contained_crt_objects { &opts.pre_link_objects_self_contained } else { &opts.pre_link_objects }; - let post_objects = if self_contained { + let post_objects = if self_contained_crt_objects { &opts.post_link_objects_self_contained } else { &opts.post_link_objects @@ -823,7 +826,9 @@ fn link_natively<'a>( .iter() .copied() .flatten() - .map(|obj| get_object_file_path(sess, obj, self_contained).into_os_string()) + .map(|obj| { + get_object_file_path(sess, obj, self_contained_crt_objects).into_os_string() + }) .collect::>() }; let pre_objects_static_pie = get_objects(pre_objects, LinkOutputKind::StaticPicExe); @@ -1703,42 +1708,43 @@ fn detect_self_contained_mingw(sess: &Session) -> bool { /// Various toolchain components used during linking are used from rustc distribution /// instead of being found somewhere on the host system. /// We only provide such support for a very limited number of targets. -fn self_contained(sess: &Session, crate_type: CrateType) -> bool { - // Emit an error if the user requested self-contained mode on the CLI but the target explicitly - // refuses it. - if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set { - if sess.target.link_self_contained.is_disabled() { - sess.emit_err(errors::UnsupportedLinkSelfContained); - } - return self_contained; - } - - match sess.target.link_self_contained { - LinkSelfContainedDefault::False => false, - LinkSelfContainedDefault::True => true, - LinkSelfContainedDefault::WithComponents(components) => { - if components.is_all() { - true - } else if components.is_empty() { - false - } else { - // FIXME: Currently no target makes use of individual components to mean - // self-contained linking is fully enabled, in the sense of what the code downstream - // from here expects. Until components are handled a bit more deeply, we can - // consider that it's disabled and remain backwards compatible. - false +fn self_contained_components(sess: &Session, crate_type: CrateType) -> LinkSelfContainedComponents { + // Turn the backwards compatible bool values for `self_contained` into fully inferred + // `LinkSelfContainedComponents`. + let self_contained = + if let Some(self_contained) = sess.opts.cg.link_self_contained.explicitly_set { + // Emit an error if the user requested self-contained mode on the CLI but the target + // explicitly refuses it. + if sess.target.link_self_contained.is_disabled() { + sess.emit_err(errors::UnsupportedLinkSelfContained); } - } + self_contained + } else { + match sess.target.link_self_contained { + LinkSelfContainedDefault::False => false, + LinkSelfContainedDefault::True => true, + + LinkSelfContainedDefault::WithComponents(components) => { + // For target specs with explicitly enabled components, we can return them + // directly. + return components; + } - // FIXME: Find a better heuristic for "native musl toolchain is available", - // based on host and linker path, for example. - // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). - LinkSelfContainedDefault::InferredForMusl => sess.crt_static(Some(crate_type)), - LinkSelfContainedDefault::InferredForMingw => { - sess.host == sess.target - && sess.target.vendor != "uwp" - && detect_self_contained_mingw(&sess) - } + // FIXME: Find a better heuristic for "native musl toolchain is available", + // based on host and linker path, for example. + // (https://github.com/rust-lang/rust/pull/71769#issuecomment-626330237). + LinkSelfContainedDefault::InferredForMusl => sess.crt_static(Some(crate_type)), + LinkSelfContainedDefault::InferredForMingw => { + sess.host == sess.target + && sess.target.vendor != "uwp" + && detect_self_contained_mingw(&sess) + } + } + }; + if self_contained { + LinkSelfContainedComponents::all() + } else { + LinkSelfContainedComponents::empty() } } @@ -2062,13 +2068,14 @@ fn linker_with_args<'a>( tmpdir: &Path, out_filename: &Path, codegen_results: &CodegenResults, + self_contained_components: LinkSelfContainedComponents, ) -> Result { - let self_contained = self_contained(sess, crate_type); + let self_contained_crt_objects = self_contained_components.is_crt_objects_enabled(); let cmd = &mut *super::linker::get_linker( sess, path, flavor, - self_contained, + self_contained_components.are_any_components_enabled(), &codegen_results.crate_info.target_cpu, ); let link_output_kind = link_output_kind(sess, crate_type); @@ -2095,7 +2102,7 @@ fn linker_with_args<'a>( // ------------ Object code and libraries, order-dependent ------------ // Pre-link CRT objects. - add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained); + add_pre_link_objects(cmd, sess, flavor, link_output_kind, self_contained_crt_objects); add_linked_symbol_object( cmd, @@ -2238,7 +2245,7 @@ fn linker_with_args<'a>( cmd, sess, link_output_kind, - self_contained, + self_contained_components, flavor, crate_type, codegen_results, @@ -2254,7 +2261,7 @@ fn linker_with_args<'a>( // ------------ Object code and libraries, order-dependent ------------ // Post-link CRT objects. - add_post_link_objects(cmd, sess, link_output_kind, self_contained); + add_post_link_objects(cmd, sess, link_output_kind, self_contained_crt_objects); // ------------ Late order-dependent options ------------ @@ -2271,7 +2278,7 @@ fn add_order_independent_options( cmd: &mut dyn Linker, sess: &Session, link_output_kind: LinkOutputKind, - self_contained: bool, + self_contained_components: LinkSelfContainedComponents, flavor: LinkerFlavor, crate_type: CrateType, codegen_results: &CodegenResults, @@ -2279,7 +2286,7 @@ fn add_order_independent_options( tmpdir: &Path, ) { // Take care of the flavors and CLI options requesting the `lld` linker. - add_lld_args(cmd, sess, flavor); + add_lld_args(cmd, sess, flavor, self_contained_components); add_apple_sdk(cmd, sess, flavor); @@ -2304,7 +2311,7 @@ fn add_order_independent_options( // Make the binary compatible with data execution prevention schemes. cmd.add_no_exec(); - if self_contained { + if self_contained_components.is_crt_objects_enabled() { cmd.no_crt_objects(); } @@ -2335,7 +2342,7 @@ fn add_order_independent_options( cmd.linker_plugin_lto(); - add_library_search_dirs(cmd, sess, self_contained); + add_library_search_dirs(cmd, sess, self_contained_components.are_any_components_enabled()); cmd.output_filename(out_filename); @@ -2985,8 +2992,16 @@ fn get_apple_sdk_root(sdk_name: &str) -> Result bool { + !self.is_empty() + } + + /// Returns whether `LinkSelfContainedComponents::LINKER` is enabled. + pub fn is_linker_enabled(self) -> bool { + self.contains(LinkSelfContainedComponents::LINKER) + } + + /// Returns whether `LinkSelfContainedComponents::CRT_OBJECTS` is enabled. + pub fn is_crt_objects_enabled(self) -> bool { + self.contains(LinkSelfContainedComponents::CRT_OBJECTS) + } } impl IntoIterator for LinkSelfContainedComponents { From e68edb89ad1b34a3112c1cddd5541917743ccecd Mon Sep 17 00:00:00 2001 From: Vitaliy Busko Date: Thu, 19 Oct 2023 04:24:35 +0700 Subject: [PATCH 021/171] refactor(compiler/resolve): simplify some code Removes unnecessary allocates and double-sorting the same vector, makes the code a little nicer. --- compiler/rustc_resolve/src/check_unused.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs index 7dbbd4c34ea7d..6b39d7907f140 100644 --- a/compiler/rustc_resolve/src/check_unused.rs +++ b/compiler/rustc_resolve/src/check_unused.rs @@ -338,7 +338,7 @@ impl Resolver<'_, '_> { for unused in visitor.unused_imports.values() { let mut fixes = Vec::new(); - let mut spans = match calc_unused_spans(unused, unused.use_tree, unused.use_tree_id) { + let spans = match calc_unused_spans(unused, unused.use_tree, unused.use_tree_id) { UnusedSpanResult::Used => continue, UnusedSpanResult::FlatUnused(span, remove) => { fixes.push((remove, String::new())); @@ -356,20 +356,19 @@ impl Resolver<'_, '_> { } }; - let len = spans.len(); - spans.sort(); - let ms = MultiSpan::from_spans(spans.clone()); - let mut span_snippets = spans + let ms = MultiSpan::from_spans(spans); + + let mut span_snippets = ms + .primary_spans() .iter() - .filter_map(|s| match tcx.sess.source_map().span_to_snippet(*s) { - Ok(s) => Some(format!("`{s}`")), - _ => None, - }) + .filter_map(|span| tcx.sess.source_map().span_to_snippet(*span).ok()) + .map(|s| format!("`{s}`")) .collect::>(); span_snippets.sort(); + let msg = format!( "unused import{}{}", - pluralize!(len), + pluralize!(ms.primary_spans().len()), if !span_snippets.is_empty() { format!(": {}", span_snippets.join(", ")) } else { @@ -379,7 +378,7 @@ impl Resolver<'_, '_> { let fix_msg = if fixes.len() == 1 && fixes[0].0 == unused.item_span { "remove the whole `use` item" - } else if spans.len() > 1 { + } else if ms.primary_spans().len() > 1 { "remove the unused imports" } else { "remove the unused import" From 9017b974eef3b43cb4a0c781cc24404b896b0ce5 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Thu, 19 Oct 2023 08:44:15 +0200 Subject: [PATCH 022/171] Improve the warning messages for the `#[diagnostic::on_unimplemented]` This commit improves warnings emitted for malformed on unimplemented attributes by: * Improving the span of the warnings * Adding a label message to them * Separating the messages for missing and unexpected options * Adding a help message that says which options are supported --- compiler/rustc_trait_selection/messages.ftl | 5 ++ .../error_reporting/on_unimplemented.rs | 57 +++++++++++--- ...o_not_fail_parsing_on_invalid_options_1.rs | 8 ++ ...t_fail_parsing_on_invalid_options_1.stderr | 77 +++++++++++++++---- ...ptions_and_continue_to_use_fallback.stderr | 6 +- 5 files changed, 126 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 20253b32add8c..a9792ca2795c9 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -28,6 +28,11 @@ trait_selection_invalid_on_clause_in_rustc_on_unimplemented = invalid `on`-claus .label = invalid on-clause here trait_selection_malformed_on_unimplemented_attr = malformed `on_unimplemented` attribute + .help = only `message`, `note` and `label` are allowed as options + .label = invalid option found here + +trait_selection_missing_options_for_on_unimplemented_attr = missing options for `on_unimplemented` attribute + .help = at least one of the `message`, `note` and `label` options are expected trait_selection_negative_positive_conflict = found both positive and negative implementation of trait `{$trait_desc}`{$self_desc -> [none] {""} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs index 9c9b78f415238..6a17dea02b4f2 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs @@ -1,5 +1,8 @@ use super::{ObligationCauseCode, PredicateObligation}; use crate::infer::error_reporting::TypeErrCtxt; +use rustc_ast::AttrArgs; +use rustc_ast::AttrArgsEq; +use rustc_ast::AttrKind; use rustc_ast::{Attribute, MetaItem, NestedMetaItem}; use rustc_attr as attr; use rustc_data_structures::fx::FxHashMap; @@ -342,7 +345,22 @@ pub enum AppendConstMessage { #[derive(LintDiagnostic)] #[diag(trait_selection_malformed_on_unimplemented_attr)] -pub struct NoValueInOnUnimplementedLint; +#[help] +pub struct MalformedOnUnimplementedAttrLint { + #[label] + pub span: Span, +} + +impl MalformedOnUnimplementedAttrLint { + fn new(span: Span) -> Self { + Self { span } + } +} + +#[derive(LintDiagnostic)] +#[diag(trait_selection_missing_options_for_on_unimplemented_attr)] +#[help] +pub struct MissingOptionsForOnUnimplementedAttr; impl<'tcx> OnUnimplementedDirective { fn parse( @@ -453,7 +471,7 @@ impl<'tcx> OnUnimplementedDirective { UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), vec![item.span()], - NoValueInOnUnimplementedLint, + MalformedOnUnimplementedAttrLint::new(item.span()), ); } else { // nothing found @@ -530,21 +548,40 @@ impl<'tcx> OnUnimplementedDirective { append_const_msg: None, })) } else { + let item = attr.get_normal_item(); + let report_span = match &item.args { + AttrArgs::Empty => item.path.span, + AttrArgs::Delimited(args) => args.dspan.entire(), + AttrArgs::Eq(eq_span, AttrArgsEq::Ast(expr)) => eq_span.to(expr.span), + AttrArgs::Eq(span, AttrArgsEq::Hir(expr)) => span.to(expr.span), + }; + tcx.emit_spanned_lint( UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), - attr.span, - NoValueInOnUnimplementedLint, + report_span, + MalformedOnUnimplementedAttrLint::new(report_span), ); Ok(None) } } else if is_diagnostic_namespace_variant { - tcx.emit_spanned_lint( - UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, - tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), - attr.span, - NoValueInOnUnimplementedLint, - ); + match &attr.kind { + AttrKind::Normal(p) if !matches!(p.item.args, AttrArgs::Empty) => { + tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + attr.span, + MalformedOnUnimplementedAttrLint::new(attr.span), + ); + } + _ => tcx.emit_spanned_lint( + UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES, + tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()), + attr.span, + MissingOptionsForOnUnimplementedAttr, + ), + }; + Ok(None) } else { let reported = diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs index 00fb59d14d76a..346d8373f7398 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.rs @@ -23,9 +23,15 @@ trait Boom {} //~^WARN malformed `on_unimplemented` attribute trait Doom {} +#[diagnostic::on_unimplemented] +//~^WARN missing options for `on_unimplemented` attribute +//~|WARN missing options for `on_unimplemented` attribute +trait Whatever {} + fn take_foo(_: impl Foo) {} fn take_baz(_: impl Baz) {} fn take_boom(_: impl Boom) {} +fn take_whatever(_: impl Whatever) {} fn main() { take_foo(1_i32); @@ -34,4 +40,6 @@ fn main() { //~^ERROR Boom take_boom(1_i32); //~^ERROR Boom + take_whatever(1_i32); + //~^ERROR the trait bound `i32: Whatever` is not satisfied } diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr index bd39c91ffe88f..162ddd79fbbd9 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_fail_parsing_on_invalid_options_1.stderr @@ -10,36 +10,53 @@ warning: malformed `on_unimplemented` attribute --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32 | LL | #[diagnostic::on_unimplemented(unsupported = "foo")] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options warning: malformed `on_unimplemented` attribute --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50 | LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options warning: malformed `on_unimplemented` attribute --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50 | LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options warning: malformed `on_unimplemented` attribute - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:22:1 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:22:32 | LL | #[diagnostic::on_unimplemented = "boom"] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ invalid option found here + | + = help: only `message`, `note` and `label` are allowed as options + +warning: missing options for `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:26:1 + | +LL | #[diagnostic::on_unimplemented] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: at least one of the `message`, `note` and `label` options are expected warning: malformed `on_unimplemented` attribute --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:3:32 | LL | #[diagnostic::on_unimplemented(unsupported = "foo")] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ invalid option found here | + = help: only `message`, `note` and `label` are allowed as options = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `i32: Foo` is not satisfied - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:14 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:37:14 | LL | take_foo(1_i32); | -------- ^^^^^ the trait `Foo` is not implemented for `i32` @@ -52,7 +69,7 @@ help: this trait has no implementations, consider adding one LL | trait Foo {} | ^^^^^^^^^ note: required by a bound in `take_foo` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:26:21 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:31:21 | LL | fn take_foo(_: impl Foo) {} | ^^^ required by this bound in `take_foo` @@ -61,12 +78,13 @@ warning: malformed `on_unimplemented` attribute --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:12:50 | LL | #[diagnostic::on_unimplemented(message = "Boom", unsupported = "Bar")] - | ^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^ invalid option found here | + = help: only `message`, `note` and `label` are allowed as options = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: Boom - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:33:14 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:39:14 | LL | take_baz(1_i32); | -------- ^^^^^ the trait `Baz` is not implemented for `i32` @@ -79,7 +97,7 @@ help: this trait has no implementations, consider adding one LL | trait Baz {} | ^^^^^^^^^ note: required by a bound in `take_baz` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:27:21 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:32:21 | LL | fn take_baz(_: impl Baz) {} | ^^^ required by this bound in `take_baz` @@ -88,12 +106,13 @@ warning: malformed `on_unimplemented` attribute --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:17:50 | LL | #[diagnostic::on_unimplemented(message = "Boom", on(_Self = "i32", message = "whatever"))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here | + = help: only `message`, `note` and `label` are allowed as options = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: Boom - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:35:15 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:41:15 | LL | take_boom(1_i32); | --------- ^^^^^ the trait `Boom` is not implemented for `i32` @@ -106,11 +125,39 @@ help: this trait has no implementations, consider adding one LL | trait Boom {} | ^^^^^^^^^^ note: required by a bound in `take_boom` - --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:28:22 + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:33:22 | LL | fn take_boom(_: impl Boom) {} | ^^^^ required by this bound in `take_boom` -error: aborting due to 3 previous errors; 8 warnings emitted +warning: missing options for `on_unimplemented` attribute + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:26:1 + | +LL | #[diagnostic::on_unimplemented] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: at least one of the `message`, `note` and `label` options are expected + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0277]: the trait bound `i32: Whatever` is not satisfied + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:43:19 + | +LL | take_whatever(1_i32); + | ------------- ^^^^^ the trait `Whatever` is not implemented for `i32` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:29:1 + | +LL | trait Whatever {} + | ^^^^^^^^^^^^^^ +note: required by a bound in `take_whatever` + --> $DIR/do_not_fail_parsing_on_invalid_options_1.rs:34:26 + | +LL | fn take_whatever(_: impl Whatever) {} + | ^^^^^^^^ required by this bound in `take_whatever` + +error: aborting due to 4 previous errors; 10 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr index 6a83d8e39c656..ba14c9c84b809 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr @@ -8,8 +8,9 @@ LL | | if(Self = ()), ... | LL | | note = "not used yet" LL | | )] - | |__^ + | |__^ invalid option found here | + = help: only `message`, `note` and `label` are allowed as options = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default warning: malformed `on_unimplemented` attribute @@ -22,8 +23,9 @@ LL | | if(Self = ()), ... | LL | | note = "not used yet" LL | | )] - | |__^ + | |__^ invalid option found here | + = help: only `message`, `note` and `label` are allowed as options = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: fallback!! From 6c97f136126557a982990a1d0ef879996b268cba Mon Sep 17 00:00:00 2001 From: Ethan Brierley Date: Fri, 20 Oct 2023 09:46:17 +0100 Subject: [PATCH 023/171] Invalid `?` suggestion on mismatched `Ok(T)` --- compiler/rustc_hir_typeck/src/demand.rs | 5 +++++ .../issue-116967-cannot-coerce-returned-result.rs | 6 ++++++ ...ue-116967-cannot-coerce-returned-result.stderr | 15 +++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.rs create mode 100644 tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.stderr diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 65ec2f232aed7..3b09fdda0ce0e 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -983,6 +983,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { return false; } + let e_ok = args_e.type_at(0); + let f_ok = args_f.type_at(0); + if !self.infcx.can_eq(self.param_env, f_ok, e_ok) { + return false; + } let e = args_e.type_at(1); let f = args_f.type_at(1); if self diff --git a/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.rs b/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.rs new file mode 100644 index 0000000000000..adf3049b4bab9 --- /dev/null +++ b/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.rs @@ -0,0 +1,6 @@ +fn foo() -> Result { + let out: Result<(), ()> = Ok(()); + out //~ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.stderr b/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.stderr new file mode 100644 index 0000000000000..447b22a152d7e --- /dev/null +++ b/tests/ui/type/type-check/issue-116967-cannot-coerce-returned-result.stderr @@ -0,0 +1,15 @@ +error[E0308]: mismatched types + --> $DIR/issue-116967-cannot-coerce-returned-result.rs:3:5 + | +LL | fn foo() -> Result { + | ------------------ expected `Result` because of return type +LL | let out: Result<(), ()> = Ok(()); +LL | out + | ^^^ expected `Result`, found `Result<(), ()>` + | + = note: expected enum `Result` + found enum `Result<(), _>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. From 3d03a8a6537ae944d38fb22b2edd1df0dcbaeeb1 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 20 Oct 2023 13:24:58 +0200 Subject: [PATCH 024/171] Fix a span for one of the test cases --- ...ed_options_and_continue_to_use_fallback.rs | 10 +++--- ...ptions_and_continue_to_use_fallback.stderr | 34 ++++++------------- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs index 353075863918c..8410b3eb10513 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.rs @@ -1,22 +1,20 @@ #![feature(diagnostic_namespace)] #[diagnostic::on_unimplemented( + if(Self = "()"), //~^WARN malformed `on_unimplemented` attribute //~|WARN malformed `on_unimplemented` attribute - if(Self = ()), - message = "not used yet", - label = "not used yet", - note = "not used yet" + message = "custom message", + note = "custom note" )] #[diagnostic::on_unimplemented(message = "fallback!!")] #[diagnostic::on_unimplemented(label = "fallback label")] #[diagnostic::on_unimplemented(note = "fallback note")] -#[diagnostic::on_unimplemented(message = "fallback2!!")] trait Foo {} fn takes_foo(_: impl Foo) {} fn main() { takes_foo(()); - //~^ERROR fallback!! + //~^ERROR custom message } diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr index ba14c9c84b809..7860e540589db 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/ignore_unsupported_options_and_continue_to_use_fallback.stderr @@ -1,35 +1,23 @@ warning: malformed `on_unimplemented` attribute - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:3:1 + --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:4:5 | -LL | / #[diagnostic::on_unimplemented( -LL | | -LL | | -LL | | if(Self = ()), -... | -LL | | note = "not used yet" -LL | | )] - | |__^ invalid option found here +LL | if(Self = "()"), + | ^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options = note: `#[warn(unknown_or_malformed_diagnostic_attributes)]` on by default warning: malformed `on_unimplemented` attribute - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:3:1 + --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:4:5 | -LL | / #[diagnostic::on_unimplemented( -LL | | -LL | | -LL | | if(Self = ()), -... | -LL | | note = "not used yet" -LL | | )] - | |__^ invalid option found here +LL | if(Self = "()"), + | ^^^^^^^^^^^^^^^ invalid option found here | = help: only `message`, `note` and `label` are allowed as options = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0277]: fallback!! - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:20:15 +error[E0277]: custom message + --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:18:15 | LL | takes_foo(()); | --------- ^^ fallback label @@ -37,14 +25,14 @@ LL | takes_foo(()); | required by a bound introduced by this call | = help: the trait `Foo` is not implemented for `()` - = note: fallback note + = note: custom note help: this trait has no implementations, consider adding one - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:1 + --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:13:1 | LL | trait Foo {} | ^^^^^^^^^ note: required by a bound in `takes_foo` - --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:17:22 + --> $DIR/ignore_unsupported_options_and_continue_to_use_fallback.rs:15:22 | LL | fn takes_foo(_: impl Foo) {} | ^^^ required by this bound in `takes_foo` From 573f47585354f6c98f9306410bfd4e040bc7fd19 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 20 Oct 2023 20:43:33 +0000 Subject: [PATCH 025/171] Uplift ClauseKind and PredicateKind --- compiler/rustc_middle/src/ty/codec.rs | 4 +- compiler/rustc_middle/src/ty/context.rs | 17 +- compiler/rustc_middle/src/ty/mod.rs | 100 +-- compiler/rustc_middle/src/ty/print/pretty.rs | 98 +-- .../rustc_middle/src/ty/structural_impls.rs | 38 -- compiler/rustc_middle/src/ty/sty.rs | 4 + compiler/rustc_smir/src/rustc_smir/mod.rs | 18 +- compiler/rustc_type_ir/src/codec.rs | 6 +- compiler/rustc_type_ir/src/const_kind.rs | 7 +- compiler/rustc_type_ir/src/interner.rs | 16 +- compiler/rustc_type_ir/src/lib.rs | 2 + compiler/rustc_type_ir/src/macros.rs | 1 + compiler/rustc_type_ir/src/predicate_kind.rs | 626 ++++++++++++++++++ compiler/rustc_type_ir/src/region_kind.rs | 7 +- compiler/rustc_type_ir/src/ty_kind.rs | 8 +- 15 files changed, 735 insertions(+), 217 deletions(-) create mode 100644 compiler/rustc_type_ir/src/predicate_kind.rs diff --git a/compiler/rustc_middle/src/ty/codec.rs b/compiler/rustc_middle/src/ty/codec.rs index e4069e11df274..d52a717b6b09b 100644 --- a/compiler/rustc_middle/src/ty/codec.rs +++ b/compiler/rustc_middle/src/ty/codec.rs @@ -230,9 +230,9 @@ impl<'tcx, D: TyDecoder>> Decodable assert!(pos >= SHORTHAND_OFFSET); let shorthand = pos - SHORTHAND_OFFSET; - decoder.with_position(shorthand, ty::PredicateKind::decode) + decoder.with_position(shorthand, as Decodable>::decode) } else { - ty::PredicateKind::decode(decoder) + as Decodable>::decode(decoder) }, bound_vars, ) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 561699d317088..dfd8104b5bc12 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -84,10 +84,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type AdtDef = ty::AdtDef<'tcx>; type GenericArgs = ty::GenericArgsRef<'tcx>; type GenericArg = ty::GenericArg<'tcx>; + type Term = ty::Term<'tcx>; + type Binder = Binder<'tcx, T>; - type Predicate = Predicate<'tcx>; - type PredicateKind = ty::PredicateKind<'tcx>; type TypeAndMut = TypeAndMut<'tcx>; + type Ty = Ty<'tcx>; type Tys = &'tcx List>; type AliasTy = ty::AliasTy<'tcx>; @@ -95,10 +96,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type BoundTy = ty::BoundTy; type PlaceholderTy = ty::PlaceholderType; type InferTy = InferTy; + type ErrorGuaranteed = ErrorGuaranteed; type BoundExistentialPredicates = &'tcx List>; type PolyFnSig = PolyFnSig<'tcx>; type AllocId = crate::mir::interpret::AllocId; + type Const = ty::Const<'tcx>; type InferConst = ty::InferConst<'tcx>; type AliasConst = ty::UnevaluatedConst<'tcx>; @@ -107,6 +110,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type BoundConst = ty::BoundVar; type ValueConst = ty::ValTree<'tcx>; type ExprConst = ty::Expr<'tcx>; + type Region = Region<'tcx>; type EarlyBoundRegion = ty::EarlyBoundRegion; type BoundRegion = ty::BoundRegion; @@ -114,6 +118,15 @@ impl<'tcx> Interner for TyCtxt<'tcx> { type InferRegion = ty::RegionVid; type PlaceholderRegion = ty::PlaceholderRegion; + type Predicate = Predicate<'tcx>; + type TraitPredicate = ty::TraitPredicate<'tcx>; + type RegionOutlivesPredicate = ty::RegionOutlivesPredicate<'tcx>; + type TypeOutlivesPredicate = ty::TypeOutlivesPredicate<'tcx>; + type ProjectionPredicate = ty::ProjectionPredicate<'tcx>; + type SubtypePredicate = ty::SubtypePredicate<'tcx>; + type CoercePredicate = ty::CoercePredicate<'tcx>; + type ClosureKind = ty::ClosureKind; + fn ty_and_mut_to_parts( TypeAndMut { ty, mutbl }: TypeAndMut<'tcx>, ) -> (Self::Ty, ty::Mutability) { diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 9b0ceb23e3eab..f53214b322313 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -97,12 +97,12 @@ pub use self::rvalue_scopes::RvalueScopes; pub use self::sty::BoundRegionKind::*; pub use self::sty::{ AliasTy, Article, Binder, BoundRegion, BoundRegionKind, BoundTy, BoundTyKind, BoundVar, - BoundVariableKind, CanonicalPolyFnSig, ClosureArgs, ClosureArgsParts, ConstKind, ConstVid, - CoroutineArgs, CoroutineArgsParts, EarlyBoundRegion, EffectVid, ExistentialPredicate, + BoundVariableKind, CanonicalPolyFnSig, ClauseKind, ClosureArgs, ClosureArgsParts, ConstKind, + ConstVid, CoroutineArgs, CoroutineArgsParts, EarlyBoundRegion, EffectVid, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, FnSig, FreeRegion, GenSig, InlineConstArgs, InlineConstArgsParts, ParamConst, ParamTy, PolyExistentialPredicate, PolyExistentialProjection, - PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, Region, RegionKind, RegionVid, - TraitRef, TyKind, TypeAndMut, UpvarArgs, VarianceDiagInfo, + PolyExistentialTraitRef, PolyFnSig, PolyGenSig, PolyTraitRef, PredicateKind, Region, + RegionKind, RegionVid, TraitRef, TyKind, TypeAndMut, UpvarArgs, VarianceDiagInfo, }; pub use self::trait_def::TraitDef; pub use self::typeck_results::{ @@ -626,98 +626,6 @@ impl<'tcx> Clause<'tcx> { } } -#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] -/// A clause is something that can appear in where bounds or be inferred -/// by implied bounds. -pub enum ClauseKind<'tcx> { - /// Corresponds to `where Foo: Bar`. `Foo` here would be - /// the `Self` type of the trait reference and `A`, `B`, and `C` - /// would be the type parameters. - Trait(TraitPredicate<'tcx>), - - /// `where 'a: 'b` - RegionOutlives(RegionOutlivesPredicate<'tcx>), - - /// `where T: 'a` - TypeOutlives(TypeOutlivesPredicate<'tcx>), - - /// `where ::Name == X`, approximately. - /// See the `ProjectionPredicate` struct for details. - Projection(ProjectionPredicate<'tcx>), - - /// Ensures that a const generic argument to a parameter `const N: u8` - /// is of type `u8`. - ConstArgHasType(Const<'tcx>, Ty<'tcx>), - - /// No syntax: `T` well-formed. - WellFormed(GenericArg<'tcx>), - - /// Constant initializer must evaluate successfully. - ConstEvaluatable(ty::Const<'tcx>), -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] -pub enum PredicateKind<'tcx> { - /// Prove a clause - Clause(ClauseKind<'tcx>), - - /// Trait must be object-safe. - ObjectSafe(DefId), - - /// No direct syntax. May be thought of as `where T: FnFoo<...>` - /// for some generic args `...` and `T` being a closure type. - /// Satisfied (or refuted) once we know the closure's kind. - ClosureKind(DefId, GenericArgsRef<'tcx>, ClosureKind), - - /// `T1 <: T2` - /// - /// This obligation is created most often when we have two - /// unresolved type variables and hence don't have enough - /// information to process the subtyping obligation yet. - Subtype(SubtypePredicate<'tcx>), - - /// `T1` coerced to `T2` - /// - /// Like a subtyping obligation, this is created most often - /// when we have two unresolved type variables and hence - /// don't have enough information to process the coercion - /// obligation yet. At the moment, we actually process coercions - /// very much like subtyping and don't handle the full coercion - /// logic. - Coerce(CoercePredicate<'tcx>), - - /// Constants must be equal. The first component is the const that is expected. - ConstEquate(Const<'tcx>, Const<'tcx>), - - /// A marker predicate that is always ambiguous. - /// Used for coherence to mark opaque types as possibly equal to each other but ambiguous. - Ambiguous, - - /// Separate from `ClauseKind::Projection` which is used for normalization in new solver. - /// This predicate requires two terms to be equal to eachother. - /// - /// Only used for new solver - AliasRelate(Term<'tcx>, Term<'tcx>, AliasRelationDirection), -} - -#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable)] -#[derive(HashStable, Debug)] -pub enum AliasRelationDirection { - Equate, - Subtype, -} - -impl std::fmt::Display for AliasRelationDirection { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - AliasRelationDirection::Equate => write!(f, "=="), - AliasRelationDirection::Subtype => write!(f, "<:"), - } - } -} - /// The crate outlives map is computed during typeck and contains the /// outlives of every item in the local crate. You should not use it /// directly, because to do so will make your pass dependent on the diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 38b3096f85182..69a1f33d64b49 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2632,6 +2632,13 @@ macro_rules! forward_display_to_print { } macro_rules! define_print_and_forward_display { + (($self:ident, $cx:ident): $($ty:ty $print:block)+) => { + define_print!(($self, $cx): $($ty $print)*); + forward_display_to_print!($($ty),+); + }; +} + +macro_rules! define_print { (($self:ident, $cx:ident): $($ty:ty $print:block)+) => { $(impl<'tcx, P: PrettyPrinter<'tcx>> Print<'tcx, P> for $ty { fn print(&$self, $cx: P) -> Result { @@ -2643,8 +2650,6 @@ macro_rules! define_print_and_forward_display { Ok($cx) } })+ - - forward_display_to_print!($($ty),+); }; } @@ -2742,6 +2747,51 @@ forward_display_to_print! { ty::OutlivesPredicate, ty::Region<'tcx>> } +define_print! { + (self, cx): + + ty::ClauseKind<'tcx> { + match *self { + ty::ClauseKind::Trait(ref data) => { + p!(print(data)) + } + ty::ClauseKind::RegionOutlives(predicate) => p!(print(predicate)), + ty::ClauseKind::TypeOutlives(predicate) => p!(print(predicate)), + ty::ClauseKind::Projection(predicate) => p!(print(predicate)), + ty::ClauseKind::ConstArgHasType(ct, ty) => { + p!("the constant `", print(ct), "` has type `", print(ty), "`") + }, + ty::ClauseKind::WellFormed(arg) => p!(print(arg), " well-formed"), + ty::ClauseKind::ConstEvaluatable(ct) => { + p!("the constant `", print(ct), "` can be evaluated") + } + } + } + + ty::PredicateKind<'tcx> { + match *self { + ty::PredicateKind::Clause(data) => { + p!(print(data)) + } + ty::PredicateKind::Subtype(predicate) => p!(print(predicate)), + ty::PredicateKind::Coerce(predicate) => p!(print(predicate)), + ty::PredicateKind::ObjectSafe(trait_def_id) => { + p!("the trait `", print_def_path(trait_def_id, &[]), "` is object-safe") + } + ty::PredicateKind::ClosureKind(closure_def_id, _closure_args, kind) => p!( + "the closure `", + print_value_path(closure_def_id, &[]), + write("` implements the trait `{}`", kind) + ), + ty::PredicateKind::ConstEquate(c1, c2) => { + p!("the constant `", print(c1), "` equals `", print(c2), "`") + } + ty::PredicateKind::Ambiguous => p!("ambiguous"), + ty::PredicateKind::AliasRelate(t1, t2, dir) => p!(print(t1), write(" {} ", dir), print(t2)), + } + } +} + define_print_and_forward_display! { (self, cx): @@ -2870,55 +2920,13 @@ define_print_and_forward_display! { } ty::Predicate<'tcx> { - let binder = self.kind(); - p!(print(binder)) + p!(print(self.kind())) } ty::Clause<'tcx> { p!(print(self.kind())) } - ty::ClauseKind<'tcx> { - match *self { - ty::ClauseKind::Trait(ref data) => { - p!(print(data)) - } - ty::ClauseKind::RegionOutlives(predicate) => p!(print(predicate)), - ty::ClauseKind::TypeOutlives(predicate) => p!(print(predicate)), - ty::ClauseKind::Projection(predicate) => p!(print(predicate)), - ty::ClauseKind::ConstArgHasType(ct, ty) => { - p!("the constant `", print(ct), "` has type `", print(ty), "`") - }, - ty::ClauseKind::WellFormed(arg) => p!(print(arg), " well-formed"), - ty::ClauseKind::ConstEvaluatable(ct) => { - p!("the constant `", print(ct), "` can be evaluated") - } - } - } - - ty::PredicateKind<'tcx> { - match *self { - ty::PredicateKind::Clause(data) => { - p!(print(data)) - } - ty::PredicateKind::Subtype(predicate) => p!(print(predicate)), - ty::PredicateKind::Coerce(predicate) => p!(print(predicate)), - ty::PredicateKind::ObjectSafe(trait_def_id) => { - p!("the trait `", print_def_path(trait_def_id, &[]), "` is object-safe") - } - ty::PredicateKind::ClosureKind(closure_def_id, _closure_args, kind) => p!( - "the closure `", - print_value_path(closure_def_id, &[]), - write("` implements the trait `{}`", kind) - ), - ty::PredicateKind::ConstEquate(c1, c2) => { - p!("the constant `", print(c1), "` equals `", print(c2), "`") - } - ty::PredicateKind::Ambiguous => p!("ambiguous"), - ty::PredicateKind::AliasRelate(t1, t2, dir) => p!(print(t1), write(" {} ", dir), print(t2)), - } - } - GenericArg<'tcx> { match self.unpack() { GenericArgKind::Lifetime(lt) => p!(print(lt)), diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs index 012bb74941286..1509bfbd59e38 100644 --- a/compiler/rustc_middle/src/ty/structural_impls.rs +++ b/compiler/rustc_middle/src/ty/structural_impls.rs @@ -199,43 +199,6 @@ impl<'tcx> fmt::Debug for ty::Clause<'tcx> { } } -impl<'tcx> fmt::Debug for ty::ClauseKind<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::ClauseKind::ConstArgHasType(ct, ty) => write!(f, "ConstArgHasType({ct:?}, {ty:?})"), - ty::ClauseKind::Trait(ref a) => a.fmt(f), - ty::ClauseKind::RegionOutlives(ref pair) => pair.fmt(f), - ty::ClauseKind::TypeOutlives(ref pair) => pair.fmt(f), - ty::ClauseKind::Projection(ref pair) => pair.fmt(f), - ty::ClauseKind::WellFormed(ref data) => write!(f, "WellFormed({data:?})"), - ty::ClauseKind::ConstEvaluatable(ct) => { - write!(f, "ConstEvaluatable({ct:?})") - } - } - } -} - -impl<'tcx> fmt::Debug for ty::PredicateKind<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ty::PredicateKind::Clause(ref a) => a.fmt(f), - ty::PredicateKind::Subtype(ref pair) => pair.fmt(f), - ty::PredicateKind::Coerce(ref pair) => pair.fmt(f), - ty::PredicateKind::ObjectSafe(trait_def_id) => { - write!(f, "ObjectSafe({trait_def_id:?})") - } - ty::PredicateKind::ClosureKind(closure_def_id, closure_args, kind) => { - write!(f, "ClosureKind({closure_def_id:?}, {closure_args:?}, {kind:?})") - } - ty::PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({c1:?}, {c2:?})"), - ty::PredicateKind::Ambiguous => write!(f, "Ambiguous"), - ty::PredicateKind::AliasRelate(t1, t2, dir) => { - write!(f, "AliasRelate({t1:?}, {dir:?}, {t2:?})") - } - } - } -} - impl<'tcx> fmt::Debug for AliasTy<'tcx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { OptWithInfcx::new_no_ctx(self).fmt(f) @@ -518,7 +481,6 @@ TrivialTypeTraversalAndLiftImpls! { ::rustc_hir::Mutability, ::rustc_hir::Unsafety, ::rustc_target::spec::abi::Abi, - crate::ty::AliasRelationDirection, crate::ty::ClosureKind, crate::ty::ParamConst, crate::ty::ParamTy, diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 46aa5d950cb18..e8ff5f66d7463 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -33,10 +33,12 @@ use std::marker::PhantomData; use std::ops::{ControlFlow, Deref, Range}; use ty::util::IntTypeExt; +use rustc_type_ir::ClauseKind as IrClauseKind; use rustc_type_ir::CollectAndApply; use rustc_type_ir::ConstKind as IrConstKind; use rustc_type_ir::DebugWithInfcx; use rustc_type_ir::DynKind; +use rustc_type_ir::PredicateKind as IrPredicateKind; use rustc_type_ir::RegionKind as IrRegionKind; use rustc_type_ir::TyKind as IrTyKind; use rustc_type_ir::TyKind::*; @@ -48,6 +50,8 @@ use super::GenericParamDefKind; pub type TyKind<'tcx> = IrTyKind>; pub type RegionKind<'tcx> = IrRegionKind>; pub type ConstKind<'tcx> = IrConstKind>; +pub type PredicateKind<'tcx> = IrPredicateKind>; +pub type ClauseKind<'tcx> = IrClauseKind>; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)] #[derive(HashStable, TypeFoldable, TypeVisitable, Lift)] diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index 8a1b6b6bfe9ea..9f966a687d418 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -1497,30 +1497,32 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> { type T = stable_mir::ty::ClauseKind; fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { - use ty::ClauseKind::*; + use ty::ClauseKind; match *self { - Trait(trait_object) => stable_mir::ty::ClauseKind::Trait(trait_object.stable(tables)), - RegionOutlives(region_outlives) => { + ClauseKind::Trait(trait_object) => { + stable_mir::ty::ClauseKind::Trait(trait_object.stable(tables)) + } + ClauseKind::RegionOutlives(region_outlives) => { stable_mir::ty::ClauseKind::RegionOutlives(region_outlives.stable(tables)) } - TypeOutlives(type_outlives) => { + ClauseKind::TypeOutlives(type_outlives) => { let ty::OutlivesPredicate::<_, _>(a, b) = type_outlives; stable_mir::ty::ClauseKind::TypeOutlives(stable_mir::ty::OutlivesPredicate( tables.intern_ty(a), b.stable(tables), )) } - Projection(projection_predicate) => { + ClauseKind::Projection(projection_predicate) => { stable_mir::ty::ClauseKind::Projection(projection_predicate.stable(tables)) } - ConstArgHasType(const_, ty) => stable_mir::ty::ClauseKind::ConstArgHasType( + ClauseKind::ConstArgHasType(const_, ty) => stable_mir::ty::ClauseKind::ConstArgHasType( const_.stable(tables), tables.intern_ty(ty), ), - WellFormed(generic_arg) => { + ClauseKind::WellFormed(generic_arg) => { stable_mir::ty::ClauseKind::WellFormed(generic_arg.unpack().stable(tables)) } - ConstEvaluatable(const_) => { + ClauseKind::ConstEvaluatable(const_) => { stable_mir::ty::ClauseKind::ConstEvaluatable(const_.stable(tables)) } } diff --git a/compiler/rustc_type_ir/src/codec.rs b/compiler/rustc_type_ir/src/codec.rs index 3b638934629b5..2fbc8f76fa4f9 100644 --- a/compiler/rustc_type_ir/src/codec.rs +++ b/compiler/rustc_type_ir/src/codec.rs @@ -1,4 +1,4 @@ -use crate::Interner; +use crate::{Interner, PredicateKind}; use rustc_data_structures::fx::FxHashMap; use rustc_serialize::{Decoder, Encoder}; @@ -30,9 +30,7 @@ pub trait TyEncoder: Encoder { fn type_shorthands(&mut self) -> &mut FxHashMap<::Ty, usize>; - fn predicate_shorthands( - &mut self, - ) -> &mut FxHashMap<::PredicateKind, usize>; + fn predicate_shorthands(&mut self) -> &mut FxHashMap, usize>; fn encode_alloc_id(&mut self, alloc_id: &::AllocId); } diff --git a/compiler/rustc_type_ir/src/const_kind.rs b/compiler/rustc_type_ir/src/const_kind.rs index f84841c9f6455..fe43db9c2ab0b 100644 --- a/compiler/rustc_type_ir/src/const_kind.rs +++ b/compiler/rustc_type_ir/src/const_kind.rs @@ -1,4 +1,5 @@ use rustc_data_structures::stable_hasher::HashStable; +use rustc_data_structures::stable_hasher::StableHasher; use rustc_serialize::{Decodable, Decoder, Encodable}; use std::cmp::Ordering; use std::fmt; @@ -86,11 +87,7 @@ where I::ErrorGuaranteed: HashStable, I::ExprConst: HashStable, { - fn hash_stable( - &self, - hcx: &mut CTX, - hasher: &mut rustc_data_structures::stable_hasher::StableHasher, - ) { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { const_kind_discriminant(self).hash_stable(hcx, hasher); match self { Param(p) => p.hash_stable(hcx, hasher), diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 60e4c58799358..6e5d3ee0b6de7 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -14,13 +14,9 @@ pub trait Interner: Sized { + Ord + IntoIterator; type GenericArg: Clone + DebugWithInfcx + Hash + Ord; + type Term: Clone + Debug + Hash + Ord; type Binder; - - // Predicates - type Predicate; - type PredicateKind: Clone + Debug + Hash + PartialEq + Eq; - type TypeAndMut: Clone + Debug + Hash + Ord; // Kinds of tys @@ -56,6 +52,16 @@ pub trait Interner: Sized { type InferRegion: Clone + DebugWithInfcx + Hash + Ord; type PlaceholderRegion: Clone + Debug + Hash + Ord; + // Predicates + type Predicate: Clone + Debug + Hash + Eq; + type TraitPredicate: Clone + Debug + Hash + Eq; + type RegionOutlivesPredicate: Clone + Debug + Hash + Eq; + type TypeOutlivesPredicate: Clone + Debug + Hash + Eq; + type ProjectionPredicate: Clone + Debug + Hash + Eq; + type SubtypePredicate: Clone + Debug + Hash + Eq; + type CoercePredicate: Clone + Debug + Hash + Eq; + type ClosureKind: Clone + Debug + Hash + Eq; + fn ty_and_mut_to_parts(ty_and_mut: Self::TypeAndMut) -> (Self::Ty, Mutability); } diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index d4ca9da96e4ed..299943260f44a 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -30,6 +30,7 @@ mod const_kind; mod debug; mod flags; mod interner; +mod predicate_kind; mod region_kind; pub use codec::*; @@ -37,6 +38,7 @@ pub use const_kind::*; pub use debug::{DebugWithInfcx, InferCtxtLike, OptWithInfcx}; pub use flags::*; pub use interner::*; +pub use predicate_kind::*; pub use region_kind::*; pub use ty_info::*; pub use ty_kind::*; diff --git a/compiler/rustc_type_ir/src/macros.rs b/compiler/rustc_type_ir/src/macros.rs index 9e10c65c20dc4..88314aca6f36c 100644 --- a/compiler/rustc_type_ir/src/macros.rs +++ b/compiler/rustc_type_ir/src/macros.rs @@ -49,4 +49,5 @@ TrivialTypeTraversalImpls! { u64, String, crate::DebruijnIndex, + crate::AliasRelationDirection, } diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs new file mode 100644 index 0000000000000..f6fabe691c6a7 --- /dev/null +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -0,0 +1,626 @@ +use rustc_data_structures::stable_hasher::{HashStable, StableHasher}; +use rustc_serialize::Decoder; +use rustc_serialize::{Decodable, Encodable}; +use std::fmt; +use std::hash; +use std::ops::ControlFlow; + +use crate::fold::{FallibleTypeFolder, TypeFoldable}; +use crate::visit::{TypeVisitable, TypeVisitor}; +use crate::{HashStableContext, Interner}; +use crate::{TyDecoder, TyEncoder}; + +/// A clause is something that can appear in where bounds or be inferred +/// by implied bounds. +pub enum ClauseKind { + /// Corresponds to `where Foo: Bar`. `Foo` here would be + /// the `Self` type of the trait reference and `A`, `B`, and `C` + /// would be the type parameters. + Trait(I::TraitPredicate), + + /// `where 'a: 'b` + RegionOutlives(I::RegionOutlivesPredicate), + + /// `where T: 'a` + TypeOutlives(I::TypeOutlivesPredicate), + + /// `where ::Name == X`, approximately. + /// See the `ProjectionPredicate` struct for details. + Projection(I::ProjectionPredicate), + + /// Ensures that a const generic argument to a parameter `const N: u8` + /// is of type `u8`. + ConstArgHasType(I::Const, I::Ty), + + /// No syntax: `T` well-formed. + WellFormed(I::GenericArg), + + /// Constant initializer must evaluate successfully. + ConstEvaluatable(I::Const), +} + +impl Clone for ClauseKind { + fn clone(&self) -> Self { + match self { + Self::Trait(arg0) => Self::Trait(arg0.clone()), + Self::RegionOutlives(arg0) => Self::RegionOutlives(arg0.clone()), + Self::TypeOutlives(arg0) => Self::TypeOutlives(arg0.clone()), + Self::Projection(arg0) => Self::Projection(arg0.clone()), + Self::ConstArgHasType(arg0, arg1) => Self::ConstArgHasType(arg0.clone(), arg1.clone()), + Self::WellFormed(arg0) => Self::WellFormed(arg0.clone()), + Self::ConstEvaluatable(arg0) => Self::ConstEvaluatable(arg0.clone()), + } + } +} + +impl Copy for ClauseKind +where + I::Ty: Copy, + I::Const: Copy, + I::GenericArg: Copy, + I::TraitPredicate: Copy, + I::ProjectionPredicate: Copy, + I::TypeOutlivesPredicate: Copy, + I::RegionOutlivesPredicate: Copy, +{ +} + +impl PartialEq for ClauseKind { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Trait(l0), Self::Trait(r0)) => l0 == r0, + (Self::RegionOutlives(l0), Self::RegionOutlives(r0)) => l0 == r0, + (Self::TypeOutlives(l0), Self::TypeOutlives(r0)) => l0 == r0, + (Self::Projection(l0), Self::Projection(r0)) => l0 == r0, + (Self::ConstArgHasType(l0, l1), Self::ConstArgHasType(r0, r1)) => l0 == r0 && l1 == r1, + (Self::WellFormed(l0), Self::WellFormed(r0)) => l0 == r0, + (Self::ConstEvaluatable(l0), Self::ConstEvaluatable(r0)) => l0 == r0, + _ => false, + } + } +} + +impl Eq for ClauseKind {} + +fn clause_kind_discriminant(value: &ClauseKind) -> usize { + match value { + ClauseKind::Trait(_) => 0, + ClauseKind::RegionOutlives(_) => 1, + ClauseKind::TypeOutlives(_) => 2, + ClauseKind::Projection(_) => 3, + ClauseKind::ConstArgHasType(_, _) => 4, + ClauseKind::WellFormed(_) => 5, + ClauseKind::ConstEvaluatable(_) => 6, + } +} + +impl hash::Hash for ClauseKind { + fn hash(&self, state: &mut H) { + clause_kind_discriminant(self).hash(state); + match self { + ClauseKind::Trait(p) => p.hash(state), + ClauseKind::RegionOutlives(p) => p.hash(state), + ClauseKind::TypeOutlives(p) => p.hash(state), + ClauseKind::Projection(p) => p.hash(state), + ClauseKind::ConstArgHasType(c, t) => { + c.hash(state); + t.hash(state); + } + ClauseKind::WellFormed(t) => t.hash(state), + ClauseKind::ConstEvaluatable(c) => c.hash(state), + } + } +} + +impl HashStable for ClauseKind +where + I::Ty: HashStable, + I::Const: HashStable, + I::GenericArg: HashStable, + I::TraitPredicate: HashStable, + I::ProjectionPredicate: HashStable, + I::TypeOutlivesPredicate: HashStable, + I::RegionOutlivesPredicate: HashStable, +{ + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + clause_kind_discriminant(self).hash_stable(hcx, hasher); + match self { + ClauseKind::Trait(p) => p.hash_stable(hcx, hasher), + ClauseKind::RegionOutlives(p) => p.hash_stable(hcx, hasher), + ClauseKind::TypeOutlives(p) => p.hash_stable(hcx, hasher), + ClauseKind::Projection(p) => p.hash_stable(hcx, hasher), + ClauseKind::ConstArgHasType(c, t) => { + c.hash_stable(hcx, hasher); + t.hash_stable(hcx, hasher); + } + ClauseKind::WellFormed(t) => t.hash_stable(hcx, hasher), + ClauseKind::ConstEvaluatable(c) => c.hash_stable(hcx, hasher), + } + } +} + +impl TypeFoldable for ClauseKind +where + I::Ty: TypeFoldable, + I::Const: TypeFoldable, + I::GenericArg: TypeFoldable, + I::TraitPredicate: TypeFoldable, + I::ProjectionPredicate: TypeFoldable, + I::TypeOutlivesPredicate: TypeFoldable, + I::RegionOutlivesPredicate: TypeFoldable, +{ + fn try_fold_with>(self, folder: &mut F) -> Result { + Ok(match self { + ClauseKind::Trait(p) => ClauseKind::Trait(p.try_fold_with(folder)?), + ClauseKind::RegionOutlives(p) => ClauseKind::RegionOutlives(p.try_fold_with(folder)?), + ClauseKind::TypeOutlives(p) => ClauseKind::TypeOutlives(p.try_fold_with(folder)?), + ClauseKind::Projection(p) => ClauseKind::Projection(p.try_fold_with(folder)?), + ClauseKind::ConstArgHasType(c, t) => { + ClauseKind::ConstArgHasType(c.try_fold_with(folder)?, t.try_fold_with(folder)?) + } + ClauseKind::WellFormed(p) => ClauseKind::WellFormed(p.try_fold_with(folder)?), + ClauseKind::ConstEvaluatable(p) => { + ClauseKind::ConstEvaluatable(p.try_fold_with(folder)?) + } + }) + } +} + +impl TypeVisitable for ClauseKind +where + I::Ty: TypeVisitable, + I::Const: TypeVisitable, + I::GenericArg: TypeVisitable, + I::TraitPredicate: TypeVisitable, + I::ProjectionPredicate: TypeVisitable, + I::TypeOutlivesPredicate: TypeVisitable, + I::RegionOutlivesPredicate: TypeVisitable, +{ + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + match self { + ClauseKind::Trait(p) => p.visit_with(visitor), + ClauseKind::RegionOutlives(p) => p.visit_with(visitor), + ClauseKind::TypeOutlives(p) => p.visit_with(visitor), + ClauseKind::Projection(p) => p.visit_with(visitor), + ClauseKind::ConstArgHasType(c, t) => { + c.visit_with(visitor)?; + t.visit_with(visitor) + } + ClauseKind::WellFormed(p) => p.visit_with(visitor), + ClauseKind::ConstEvaluatable(p) => p.visit_with(visitor), + } + } +} + +impl> Decodable for ClauseKind +where + I::Ty: Decodable, + I::Const: Decodable, + I::GenericArg: Decodable, + I::TraitPredicate: Decodable, + I::ProjectionPredicate: Decodable, + I::TypeOutlivesPredicate: Decodable, + I::RegionOutlivesPredicate: Decodable, +{ + fn decode(d: &mut D) -> Self { + match Decoder::read_usize(d) { + 0 => ClauseKind::Trait(Decodable::decode(d)), + 1 => ClauseKind::RegionOutlives(Decodable::decode(d)), + 2 => ClauseKind::TypeOutlives(Decodable::decode(d)), + 3 => ClauseKind::Projection(Decodable::decode(d)), + 4 => ClauseKind::ConstArgHasType(Decodable::decode(d), Decodable::decode(d)), + 5 => ClauseKind::WellFormed(Decodable::decode(d)), + 6 => ClauseKind::ConstEvaluatable(Decodable::decode(d)), + _ => panic!( + "{}", + format!( + "invalid enum variant tag while decoding `{}`, expected 0..{}", + "ClauseKind", 7, + ) + ), + } + } +} + +impl Encodable for ClauseKind +where + I::Ty: Encodable, + I::Const: Encodable, + I::GenericArg: Encodable, + I::TraitPredicate: Encodable, + I::ProjectionPredicate: Encodable, + I::TypeOutlivesPredicate: Encodable, + I::RegionOutlivesPredicate: Encodable, +{ + fn encode(&self, s: &mut E) { + let discriminant = clause_kind_discriminant(self); + match self { + ClauseKind::Trait(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)), + ClauseKind::RegionOutlives(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)), + ClauseKind::TypeOutlives(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)), + ClauseKind::Projection(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)), + ClauseKind::ConstArgHasType(c, t) => s.emit_enum_variant(discriminant, |s| { + c.encode(s); + t.encode(s); + }), + ClauseKind::WellFormed(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)), + ClauseKind::ConstEvaluatable(p) => s.emit_enum_variant(discriminant, |s| p.encode(s)), + } + } +} + +pub enum PredicateKind { + /// Prove a clause + Clause(ClauseKind), + + /// Trait must be object-safe. + ObjectSafe(I::DefId), + + /// No direct syntax. May be thought of as `where T: FnFoo<...>` + /// for some generic args `...` and `T` being a closure type. + /// Satisfied (or refuted) once we know the closure's kind. + ClosureKind(I::DefId, I::GenericArgs, I::ClosureKind), + + /// `T1 <: T2` + /// + /// This obligation is created most often when we have two + /// unresolved type variables and hence don't have enough + /// information to process the subtyping obligation yet. + Subtype(I::SubtypePredicate), + + /// `T1` coerced to `T2` + /// + /// Like a subtyping obligation, this is created most often + /// when we have two unresolved type variables and hence + /// don't have enough information to process the coercion + /// obligation yet. At the moment, we actually process coercions + /// very much like subtyping and don't handle the full coercion + /// logic. + Coerce(I::CoercePredicate), + + /// Constants must be equal. The first component is the const that is expected. + ConstEquate(I::Const, I::Const), + + /// A marker predicate that is always ambiguous. + /// Used for coherence to mark opaque types as possibly equal to each other but ambiguous. + Ambiguous, + + /// Separate from `ClauseKind::Projection` which is used for normalization in new solver. + /// This predicate requires two terms to be equal to eachother. + /// + /// Only used for new solver + AliasRelate(I::Term, I::Term, AliasRelationDirection), +} + +impl Copy for PredicateKind +where + I::DefId: Copy, + I::Const: Copy, + I::GenericArgs: Copy, + I::Term: Copy, + I::CoercePredicate: Copy, + I::SubtypePredicate: Copy, + I::ClosureKind: Copy, + ClauseKind: Copy, +{ +} + +impl Clone for PredicateKind { + fn clone(&self) -> Self { + match self { + Self::Clause(arg0) => Self::Clause(arg0.clone()), + Self::ObjectSafe(arg0) => Self::ObjectSafe(arg0.clone()), + Self::ClosureKind(arg0, arg1, arg2) => { + Self::ClosureKind(arg0.clone(), arg1.clone(), arg2.clone()) + } + Self::Subtype(arg0) => Self::Subtype(arg0.clone()), + Self::Coerce(arg0) => Self::Coerce(arg0.clone()), + Self::ConstEquate(arg0, arg1) => Self::ConstEquate(arg0.clone(), arg1.clone()), + Self::Ambiguous => Self::Ambiguous, + Self::AliasRelate(arg0, arg1, arg2) => { + Self::AliasRelate(arg0.clone(), arg1.clone(), arg2.clone()) + } + } + } +} + +impl PartialEq for PredicateKind { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Clause(l0), Self::Clause(r0)) => l0 == r0, + (Self::ObjectSafe(l0), Self::ObjectSafe(r0)) => l0 == r0, + (Self::ClosureKind(l0, l1, l2), Self::ClosureKind(r0, r1, r2)) => { + l0 == r0 && l1 == r1 && l2 == r2 + } + (Self::Subtype(l0), Self::Subtype(r0)) => l0 == r0, + (Self::Coerce(l0), Self::Coerce(r0)) => l0 == r0, + (Self::ConstEquate(l0, l1), Self::ConstEquate(r0, r1)) => l0 == r0 && l1 == r1, + (Self::AliasRelate(l0, l1, l2), Self::AliasRelate(r0, r1, r2)) => { + l0 == r0 && l1 == r1 && l2 == r2 + } + _ => core::mem::discriminant(self) == core::mem::discriminant(other), + } + } +} + +impl Eq for PredicateKind {} + +fn predicate_kind_discriminant(value: &PredicateKind) -> usize { + match value { + PredicateKind::Clause(_) => 0, + PredicateKind::ObjectSafe(_) => 1, + PredicateKind::ClosureKind(_, _, _) => 2, + PredicateKind::Subtype(_) => 3, + PredicateKind::Coerce(_) => 4, + PredicateKind::ConstEquate(_, _) => 5, + PredicateKind::Ambiguous => 6, + PredicateKind::AliasRelate(_, _, _) => 7, + } +} + +impl hash::Hash for PredicateKind { + fn hash(&self, state: &mut H) { + predicate_kind_discriminant(self).hash(state); + match self { + PredicateKind::Clause(p) => p.hash(state), + PredicateKind::ObjectSafe(d) => d.hash(state), + PredicateKind::ClosureKind(d, g, k) => { + d.hash(state); + g.hash(state); + k.hash(state); + } + PredicateKind::Subtype(p) => p.hash(state), + PredicateKind::Coerce(p) => p.hash(state), + PredicateKind::ConstEquate(c1, c2) => { + c1.hash(state); + c2.hash(state); + } + PredicateKind::Ambiguous => {} + PredicateKind::AliasRelate(t1, t2, r) => { + t1.hash(state); + t2.hash(state); + r.hash(state); + } + } + } +} + +impl HashStable for PredicateKind +where + I::DefId: HashStable, + I::Const: HashStable, + I::GenericArgs: HashStable, + I::Term: HashStable, + I::CoercePredicate: HashStable, + I::SubtypePredicate: HashStable, + I::ClosureKind: HashStable, + ClauseKind: HashStable, +{ + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { + predicate_kind_discriminant(self).hash_stable(hcx, hasher); + match self { + PredicateKind::Clause(p) => p.hash_stable(hcx, hasher), + PredicateKind::ObjectSafe(d) => d.hash_stable(hcx, hasher), + PredicateKind::ClosureKind(d, g, k) => { + d.hash_stable(hcx, hasher); + g.hash_stable(hcx, hasher); + k.hash_stable(hcx, hasher); + } + PredicateKind::Subtype(p) => p.hash_stable(hcx, hasher), + PredicateKind::Coerce(p) => p.hash_stable(hcx, hasher), + PredicateKind::ConstEquate(c1, c2) => { + c1.hash_stable(hcx, hasher); + c2.hash_stable(hcx, hasher); + } + PredicateKind::Ambiguous => {} + PredicateKind::AliasRelate(t1, t2, r) => { + t1.hash_stable(hcx, hasher); + t2.hash_stable(hcx, hasher); + r.hash_stable(hcx, hasher); + } + } + } +} + +impl TypeFoldable for PredicateKind +where + I::DefId: TypeFoldable, + I::Const: TypeFoldable, + I::GenericArgs: TypeFoldable, + I::Term: TypeFoldable, + I::CoercePredicate: TypeFoldable, + I::SubtypePredicate: TypeFoldable, + I::ClosureKind: TypeFoldable, + ClauseKind: TypeFoldable, +{ + fn try_fold_with>(self, folder: &mut F) -> Result { + Ok(match self { + PredicateKind::Clause(c) => PredicateKind::Clause(c.try_fold_with(folder)?), + PredicateKind::ObjectSafe(d) => PredicateKind::ObjectSafe(d.try_fold_with(folder)?), + PredicateKind::ClosureKind(d, g, k) => PredicateKind::ClosureKind( + d.try_fold_with(folder)?, + g.try_fold_with(folder)?, + k.try_fold_with(folder)?, + ), + PredicateKind::Subtype(s) => PredicateKind::Subtype(s.try_fold_with(folder)?), + PredicateKind::Coerce(s) => PredicateKind::Coerce(s.try_fold_with(folder)?), + PredicateKind::ConstEquate(a, b) => { + PredicateKind::ConstEquate(a.try_fold_with(folder)?, b.try_fold_with(folder)?) + } + PredicateKind::Ambiguous => PredicateKind::Ambiguous, + PredicateKind::AliasRelate(a, b, d) => PredicateKind::AliasRelate( + a.try_fold_with(folder)?, + b.try_fold_with(folder)?, + d.try_fold_with(folder)?, + ), + }) + } +} + +impl TypeVisitable for PredicateKind +where + I::DefId: TypeVisitable, + I::Const: TypeVisitable, + I::GenericArgs: TypeVisitable, + I::Term: TypeVisitable, + I::CoercePredicate: TypeVisitable, + I::SubtypePredicate: TypeVisitable, + I::ClosureKind: TypeVisitable, + ClauseKind: TypeVisitable, +{ + fn visit_with>(&self, visitor: &mut V) -> ControlFlow { + match self { + PredicateKind::Clause(p) => p.visit_with(visitor), + PredicateKind::ObjectSafe(d) => d.visit_with(visitor), + PredicateKind::ClosureKind(d, g, k) => { + d.visit_with(visitor)?; + g.visit_with(visitor)?; + k.visit_with(visitor) + } + PredicateKind::Subtype(s) => s.visit_with(visitor), + PredicateKind::Coerce(s) => s.visit_with(visitor), + PredicateKind::ConstEquate(a, b) => { + a.visit_with(visitor)?; + b.visit_with(visitor) + } + PredicateKind::Ambiguous => ControlFlow::Continue(()), + PredicateKind::AliasRelate(a, b, d) => { + a.visit_with(visitor)?; + b.visit_with(visitor)?; + d.visit_with(visitor) + } + } + } +} + +impl> Decodable for PredicateKind +where + I::DefId: Decodable, + I::Const: Decodable, + I::GenericArgs: Decodable, + I::Term: Decodable, + I::CoercePredicate: Decodable, + I::SubtypePredicate: Decodable, + I::ClosureKind: Decodable, + ClauseKind: Decodable, +{ + fn decode(d: &mut D) -> Self { + match Decoder::read_usize(d) { + 0 => PredicateKind::Clause(Decodable::decode(d)), + 1 => PredicateKind::ObjectSafe(Decodable::decode(d)), + 2 => PredicateKind::ClosureKind( + Decodable::decode(d), + Decodable::decode(d), + Decodable::decode(d), + ), + 3 => PredicateKind::Subtype(Decodable::decode(d)), + 4 => PredicateKind::Coerce(Decodable::decode(d)), + 5 => PredicateKind::ConstEquate(Decodable::decode(d), Decodable::decode(d)), + 6 => PredicateKind::Ambiguous, + 7 => PredicateKind::AliasRelate( + Decodable::decode(d), + Decodable::decode(d), + Decodable::decode(d), + ), + _ => panic!( + "{}", + format!( + "invalid enum variant tag while decoding `{}`, expected 0..{}", + "PredicateKind", 8, + ) + ), + } + } +} + +impl Encodable for PredicateKind +where + I::DefId: Encodable, + I::Const: Encodable, + I::GenericArgs: Encodable, + I::Term: Encodable, + I::CoercePredicate: Encodable, + I::SubtypePredicate: Encodable, + I::ClosureKind: Encodable, + ClauseKind: Encodable, +{ + fn encode(&self, s: &mut E) { + let discriminant = predicate_kind_discriminant(self); + match self { + PredicateKind::Clause(c) => s.emit_enum_variant(discriminant, |s| c.encode(s)), + PredicateKind::ObjectSafe(d) => s.emit_enum_variant(discriminant, |s| d.encode(s)), + PredicateKind::ClosureKind(d, g, k) => s.emit_enum_variant(discriminant, |s| { + d.encode(s); + g.encode(s); + k.encode(s); + }), + PredicateKind::Subtype(c) => s.emit_enum_variant(discriminant, |s| c.encode(s)), + PredicateKind::Coerce(c) => s.emit_enum_variant(discriminant, |s| c.encode(s)), + PredicateKind::ConstEquate(a, b) => s.emit_enum_variant(discriminant, |s| { + a.encode(s); + b.encode(s); + }), + PredicateKind::Ambiguous => s.emit_enum_variant(discriminant, |_s| {}), + PredicateKind::AliasRelate(a, b, d) => s.emit_enum_variant(discriminant, |s| { + a.encode(s); + b.encode(s); + d.encode(s); + }), + } + } +} + +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)] +#[derive(HashStable_Generic, Encodable, Decodable)] +pub enum AliasRelationDirection { + Equate, + Subtype, +} + +impl std::fmt::Display for AliasRelationDirection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AliasRelationDirection::Equate => write!(f, "=="), + AliasRelationDirection::Subtype => write!(f, "<:"), + } + } +} + +// FIXME: Convert to DebugWithInfcx impl +impl fmt::Debug for ClauseKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ClauseKind::ConstArgHasType(ct, ty) => write!(f, "ConstArgHasType({ct:?}, {ty:?})"), + ClauseKind::Trait(a) => a.fmt(f), + ClauseKind::RegionOutlives(pair) => pair.fmt(f), + ClauseKind::TypeOutlives(pair) => pair.fmt(f), + ClauseKind::Projection(pair) => pair.fmt(f), + ClauseKind::WellFormed(data) => write!(f, "WellFormed({data:?})"), + ClauseKind::ConstEvaluatable(ct) => { + write!(f, "ConstEvaluatable({ct:?})") + } + } + } +} + +// FIXME: Convert to DebugWithInfcx impl +impl fmt::Debug for PredicateKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + PredicateKind::Clause(a) => a.fmt(f), + PredicateKind::Subtype(pair) => pair.fmt(f), + PredicateKind::Coerce(pair) => pair.fmt(f), + PredicateKind::ObjectSafe(trait_def_id) => { + write!(f, "ObjectSafe({trait_def_id:?})") + } + PredicateKind::ClosureKind(closure_def_id, closure_args, kind) => { + write!(f, "ClosureKind({closure_def_id:?}, {closure_args:?}, {kind:?})") + } + PredicateKind::ConstEquate(c1, c2) => write!(f, "ConstEquate({c1:?}, {c2:?})"), + PredicateKind::Ambiguous => write!(f, "Ambiguous"), + PredicateKind::AliasRelate(t1, t2, dir) => { + write!(f, "AliasRelate({t1:?}, {dir:?}, {t2:?})") + } + } + } +} diff --git a/compiler/rustc_type_ir/src/region_kind.rs b/compiler/rustc_type_ir/src/region_kind.rs index 0006eec4d3007..70e2ba3e30806 100644 --- a/compiler/rustc_type_ir/src/region_kind.rs +++ b/compiler/rustc_type_ir/src/region_kind.rs @@ -1,4 +1,5 @@ use rustc_data_structures::stable_hasher::HashStable; +use rustc_data_structures::stable_hasher::StableHasher; use rustc_serialize::{Decodable, Decoder, Encodable}; use std::cmp::Ordering; use std::fmt; @@ -381,11 +382,7 @@ where I::PlaceholderRegion: HashStable, { #[inline] - fn hash_stable( - &self, - hcx: &mut CTX, - hasher: &mut rustc_data_structures::stable_hasher::StableHasher, - ) { + fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) { std::mem::discriminant(self).hash_stable(hcx, hasher); match self { ReErased | ReStatic | ReError(_) => { diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs index 91bfce9a142f9..76f85a42b7be4 100644 --- a/compiler/rustc_type_ir/src/ty_kind.rs +++ b/compiler/rustc_type_ir/src/ty_kind.rs @@ -640,7 +640,6 @@ where I::BoundTy: Encodable, I::PlaceholderTy: Encodable, I::InferTy: Encodable, - I::PredicateKind: Encodable, I::AllocId: Encodable, { fn encode(&self, e: &mut E) { @@ -753,7 +752,6 @@ where I::BoundTy: Decodable, I::PlaceholderTy: Decodable, I::InferTy: Decodable, - I::PredicateKind: Decodable, I::AllocId: Decodable, { fn decode(d: &mut D) -> Self { @@ -817,11 +815,7 @@ where I::ErrorGuaranteed: HashStable, { #[inline] - fn hash_stable( - &self, - __hcx: &mut CTX, - __hasher: &mut rustc_data_structures::stable_hasher::StableHasher, - ) { + fn hash_stable(&self, __hcx: &mut CTX, __hasher: &mut StableHasher) { std::mem::discriminant(self).hash_stable(__hcx, __hasher); match self { Bool => {} From 4aaf8e03e1a1e2a53e95196569213baafc421b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Sat, 21 Oct 2023 06:04:41 +0200 Subject: [PATCH 026/171] on unresolved import disambiguate suggested path if it would collide --- compiler/rustc_resolve/src/diagnostics.rs | 58 ++++++++++++++----- tests/ui/imports/issue-56125.stderr | 12 ++-- tests/ui/unresolved/auxiliary/library.rs | 1 + ...ved-import-avoid-suggesting-global-path.rs | 31 ++++++++++ ...import-avoid-suggesting-global-path.stderr | 25 ++++++++ ...ort-suggest-disambiguated-crate-name.fixed | 19 ++++++ ...import-suggest-disambiguated-crate-name.rs | 19 ++++++ ...rt-suggest-disambiguated-crate-name.stderr | 14 +++++ 8 files changed, 159 insertions(+), 20 deletions(-) create mode 100644 tests/ui/unresolved/auxiliary/library.rs create mode 100644 tests/ui/unresolved/unresolved-import-avoid-suggesting-global-path.rs create mode 100644 tests/ui/unresolved/unresolved-import-avoid-suggesting-global-path.stderr create mode 100644 tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.fixed create mode 100644 tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.rs create mode 100644 tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.stderr diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 925ee615b095b..3e43b6bfdaec8 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -25,7 +25,7 @@ use rustc_span::hygiene::MacroKind; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, Span, SyntaxContext}; -use thin_vec::ThinVec; +use thin_vec::{thin_vec, ThinVec}; use crate::errors::{ AddedMacroUse, ChangeImportBinding, ChangeImportBindingSuggestion, ConsiderAddingADerive, @@ -1147,7 +1147,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { namespace: Namespace, parent_scope: &ParentScope<'a>, start_module: Module<'a>, - crate_name: Ident, + crate_path: ThinVec, filter_fn: FilterFn, ) -> Vec where @@ -1163,8 +1163,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { Some(x) => Some(x), } { let in_module_is_extern = !in_module.def_id().is_local(); - // We have to visit module children in deterministic order to avoid - // instabilities in reported imports (#43552). in_module.for_each_child(self, |this, ident, ns, name_binding| { // avoid non-importable candidates if !name_binding.is_importable() { @@ -1214,12 +1212,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { let res = name_binding.res(); if filter_fn(res) { // create the path - let mut segms = path_segments.clone(); - if lookup_ident.span.at_least_rust_2018() { + let mut segms = if lookup_ident.span.at_least_rust_2018() { // crate-local absolute paths start with `crate::` in edition 2018 // FIXME: may also be stabilized for Rust 2015 (Issues #45477, #44660) - segms.insert(0, ast::PathSegment::from_ident(crate_name)); - } + crate_path.clone() + } else { + ThinVec::new() + }; + segms.append(&mut path_segments.clone()); segms.push(ast::PathSegment::from_ident(ident)); let path = Path { span: name_binding.span, segments: segms, tokens: None }; @@ -1318,18 +1318,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { where FilterFn: Fn(Res) -> bool, { + let crate_path = thin_vec![ast::PathSegment::from_ident(Ident::with_dummy_span(kw::Crate))]; let mut suggestions = self.lookup_import_candidates_from_module( lookup_ident, namespace, parent_scope, self.graph_root, - Ident::with_dummy_span(kw::Crate), + crate_path, &filter_fn, ); if lookup_ident.span.at_least_rust_2018() { - let extern_prelude_names = self.extern_prelude.clone(); - for (ident, _) in extern_prelude_names.into_iter() { + for ident in self.extern_prelude.clone().into_keys() { if ident.span.from_expansion() { // Idents are adjusted to the root context before being // resolved in the extern prelude, so reporting this to the @@ -1340,13 +1340,43 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> { } let crate_id = self.crate_loader(|c| c.maybe_process_path_extern(ident.name)); if let Some(crate_id) = crate_id { - let crate_root = self.expect_module(crate_id.as_def_id()); + let crate_def_id = crate_id.as_def_id(); + let crate_root = self.expect_module(crate_def_id); + + // Check if there's already an item in scope with the same name as the crate. + // If so, we have to disambiguate the potential import suggestions by making + // the paths *global* (i.e., by prefixing them with `::`). + let needs_disambiguation = + self.resolutions(parent_scope.module).borrow().iter().any( + |(key, name_resolution)| { + if key.ns == TypeNS + && key.ident == ident + && let Some(binding) = name_resolution.borrow().binding + { + match binding.res() { + // No disambiguation needed if the identically named item we + // found in scope actually refers to the crate in question. + Res::Def(_, def_id) => def_id != crate_def_id, + Res::PrimTy(_) => true, + _ => false, + } + } else { + false + } + }, + ); + let mut crate_path = ThinVec::new(); + if needs_disambiguation { + crate_path.push(ast::PathSegment::path_root(rustc_span::DUMMY_SP)); + } + crate_path.push(ast::PathSegment::from_ident(ident)); + suggestions.extend(self.lookup_import_candidates_from_module( lookup_ident, namespace, parent_scope, crate_root, - ident, + crate_path, &filter_fn, )); } @@ -2541,7 +2571,7 @@ fn show_candidates( candidates.iter().for_each(|c| { (if c.accessible { &mut accessible_path_strings } else { &mut inaccessible_path_strings }) - .push((path_names_to_string(&c.path), c.descr, c.did, &c.note, c.via_import)) + .push((pprust::path_to_string(&c.path), c.descr, c.did, &c.note, c.via_import)) }); // we want consistent results across executions, but candidates are produced diff --git a/tests/ui/imports/issue-56125.stderr b/tests/ui/imports/issue-56125.stderr index 15477fb6f1015..d2a0f436c42d0 100644 --- a/tests/ui/imports/issue-56125.stderr +++ b/tests/ui/imports/issue-56125.stderr @@ -6,14 +6,14 @@ LL | use empty::issue_56125; | help: consider importing one of these items instead | +LL | use ::issue_56125::issue_56125; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | use ::issue_56125::last_segment::issue_56125; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | use ::issue_56125::non_last_segment::non_last_segment::issue_56125; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LL | use crate::m3::last_segment::issue_56125; | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | use crate::m3::non_last_segment::non_last_segment::issue_56125; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -LL | use issue_56125::issue_56125; - | ~~~~~~~~~~~~~~~~~~~~~~~~ -LL | use issue_56125::last_segment::issue_56125; - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ and 1 other candidate error[E0659]: `issue_56125` is ambiguous diff --git a/tests/ui/unresolved/auxiliary/library.rs b/tests/ui/unresolved/auxiliary/library.rs new file mode 100644 index 0000000000000..1169ed9622523 --- /dev/null +++ b/tests/ui/unresolved/auxiliary/library.rs @@ -0,0 +1 @@ +pub struct SomeUsefulType; diff --git a/tests/ui/unresolved/unresolved-import-avoid-suggesting-global-path.rs b/tests/ui/unresolved/unresolved-import-avoid-suggesting-global-path.rs new file mode 100644 index 0000000000000..af8207aaadded --- /dev/null +++ b/tests/ui/unresolved/unresolved-import-avoid-suggesting-global-path.rs @@ -0,0 +1,31 @@ +// Test that we don't prepend `::` to paths referencing crates from the extern prelude +// when it can be avoided[^1] since it's more idiomatic to do so. +// +// [^1]: Counterexample: `unresolved-import-suggest-disambiguated-crate-name.rs` +#![feature(decl_macro)] // allows us to create items with hygienic names + +// aux-crate:library=library.rs +// edition: 2021 + +mod hygiene { + make!(); + macro make() { + // This won't conflict with the suggested *non-global* path as the syntax context differs. + mod library {} + } + + mod module {} + use module::SomeUsefulType; //~ ERROR unresolved import `module::SomeUsefulType` +} + +mod glob { + use inner::*; + mod inner { + mod library {} + } + + mod module {} + use module::SomeUsefulType; //~ ERROR unresolved import `module::SomeUsefulType` +} + +fn main() {} diff --git a/tests/ui/unresolved/unresolved-import-avoid-suggesting-global-path.stderr b/tests/ui/unresolved/unresolved-import-avoid-suggesting-global-path.stderr new file mode 100644 index 0000000000000..b0352ab675435 --- /dev/null +++ b/tests/ui/unresolved/unresolved-import-avoid-suggesting-global-path.stderr @@ -0,0 +1,25 @@ +error[E0432]: unresolved import `module::SomeUsefulType` + --> $DIR/unresolved-import-avoid-suggesting-global-path.rs:18:9 + | +LL | use module::SomeUsefulType; + | ^^^^^^^^^^^^^^^^^^^^^^ no `SomeUsefulType` in `hygiene::module` + | +help: consider importing this struct instead + | +LL | use library::SomeUsefulType; + | ~~~~~~~~~~~~~~~~~~~~~~~ + +error[E0432]: unresolved import `module::SomeUsefulType` + --> $DIR/unresolved-import-avoid-suggesting-global-path.rs:28:9 + | +LL | use module::SomeUsefulType; + | ^^^^^^^^^^^^^^^^^^^^^^ no `SomeUsefulType` in `glob::module` + | +help: consider importing this struct instead + | +LL | use library::SomeUsefulType; + | ~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0432`. diff --git a/tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.fixed b/tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.fixed new file mode 100644 index 0000000000000..2b20d3f106b9d --- /dev/null +++ b/tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.fixed @@ -0,0 +1,19 @@ +// Regression test for issue #116970. +// +// When we suggest importing an item from a crate found in the extern prelude and there +// happens to exist a module or type in the current scope with the same name as the crate, +// disambiguate the suggested path by making it global (i.e., by prefixing it with `::`). +// +// For context, when it can be avoided we don't prepend `::` to paths referencing crates +// from the extern prelude. See also `unresolved-import-avoid-suggesting-global-path.rs`. + +// run-rustfix + +// compile-flags: --crate-type=lib +// aux-crate:library=library.rs +// edition: 2021 + +mod library {} // this module shares the same name as the external crate! + +mod module {} +pub use ::library::SomeUsefulType; //~ ERROR unresolved import `module::SomeUsefulType` diff --git a/tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.rs b/tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.rs new file mode 100644 index 0000000000000..b810a7f52966a --- /dev/null +++ b/tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.rs @@ -0,0 +1,19 @@ +// Regression test for issue #116970. +// +// When we suggest importing an item from a crate found in the extern prelude and there +// happens to exist a module or type in the current scope with the same name as the crate, +// disambiguate the suggested path by making it global (i.e., by prefixing it with `::`). +// +// For context, when it can be avoided we don't prepend `::` to paths referencing crates +// from the extern prelude. See also `unresolved-import-avoid-suggesting-global-path.rs`. + +// run-rustfix + +// compile-flags: --crate-type=lib +// aux-crate:library=library.rs +// edition: 2021 + +mod library {} // this module shares the same name as the external crate! + +mod module {} +pub use module::SomeUsefulType; //~ ERROR unresolved import `module::SomeUsefulType` diff --git a/tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.stderr b/tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.stderr new file mode 100644 index 0000000000000..f139c0f3cf1e8 --- /dev/null +++ b/tests/ui/unresolved/unresolved-import-suggest-disambiguated-crate-name.stderr @@ -0,0 +1,14 @@ +error[E0432]: unresolved import `module::SomeUsefulType` + --> $DIR/unresolved-import-suggest-disambiguated-crate-name.rs:19:9 + | +LL | pub use module::SomeUsefulType; + | ^^^^^^^^^^^^^^^^^^^^^^ no `SomeUsefulType` in `module` + | +help: consider importing this struct instead + | +LL | pub use ::library::SomeUsefulType; + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0432`. From 24cdb27e2842e5abab2875ce29c365ac5503773e Mon Sep 17 00:00:00 2001 From: Ethan Brierley Date: Sun, 22 Oct 2023 12:05:28 +0100 Subject: [PATCH 027/171] let_chainify `suggest_coercing_result_via_try_operator` --- compiler/rustc_hir_typeck/src/demand.rs | 58 ++++++++++--------------- 1 file changed, 22 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 3b09fdda0ce0e..b385902c2869a 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -961,43 +961,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Ty<'tcx>, found: Ty<'tcx>, ) -> bool { - let ty::Adt(e, args_e) = expected.kind() else { - return false; - }; - let ty::Adt(f, args_f) = found.kind() else { - return false; - }; - if e.did() != f.did() { - return false; - } - if Some(e.did()) != self.tcx.get_diagnostic_item(sym::Result) { - return false; - } let map = self.tcx.hir(); - if let Some(hir::Node::Expr(expr)) = map.find_parent(expr.hir_id) - && let hir::ExprKind::Ret(_) = expr.kind - { - // `return foo;` - } else if map.get_return_block(expr.hir_id).is_some() { - // Function's tail expression. - } else { - return false; - } - let e_ok = args_e.type_at(0); - let f_ok = args_f.type_at(0); - if !self.infcx.can_eq(self.param_env, f_ok, e_ok) { - return false; - } - let e = args_e.type_at(1); - let f = args_f.type_at(1); - if self - .infcx - .type_implements_trait( - self.tcx.get_diagnostic_item(sym::Into).unwrap(), - [f, e], - self.param_env, - ) - .must_apply_modulo_regions() + let returned = matches!( + map.find_parent(expr.hir_id), + Some(hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Ret(_), .. })) + ) || map.get_return_block(expr.hir_id).is_some(); + if returned + && let ty::Adt(e, args_e) = expected.kind() + && let ty::Adt(f, args_f) = found.kind() + && e.did() == f.did() + && Some(e.did()) == self.tcx.get_diagnostic_item(sym::Result) + && let e_ok = args_e.type_at(0) + && let f_ok = args_f.type_at(0) + && self.infcx.can_eq(self.param_env, f_ok, e_ok) + && let e_err = args_e.type_at(1) + && let f_err = args_f.type_at(1) + && self + .infcx + .type_implements_trait( + self.tcx.get_diagnostic_item(sym::Into).unwrap(), + [f_err, e_err], + self.param_env, + ) + .must_apply_modulo_regions() { err.multipart_suggestion( "use `?` to coerce and return an appropriate `Err`, and wrap the resulting value \ From 87168909b0ad24ef82066480768e2cabd7fd600d Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Sat, 21 Oct 2023 20:08:32 +0000 Subject: [PATCH 028/171] Enable cg_clif tests for riscv64gc --- src/bootstrap/src/core/build_steps/test.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 831a86940fb48..fa35fff333ec6 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2999,7 +2999,10 @@ impl Step for CodegenCranelift { let triple = run.target.triple; let target_supported = if triple.contains("linux") { - triple.contains("x86_64") || triple.contains("aarch64") || triple.contains("s390x") + triple.contains("x86_64") + || triple.contains("aarch64") + || triple.contains("s390x") + || triple.contains("riscv64gc") } else if triple.contains("darwin") || triple.contains("windows") { triple.contains("x86_64") } else { From d03b3db95b098f164161d793c3a0fd099a402612 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 22 Oct 2023 12:04:50 -0700 Subject: [PATCH 029/171] Rustfmt-compatible formatting for code snippets in rustc_builtin_macros --- .../src/deriving/generic/mod.rs | 104 +++++++++++------- 1 file changed, 64 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index 7252658d460d0..d8f4d82551685 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -37,8 +37,9 @@ //! following snippet //! //! ```rust -//! # #![allow(dead_code)] -//! struct A { x : i32 } +//! struct A { +//! x: i32, +//! } //! //! struct B(i32); //! @@ -74,6 +75,7 @@ //! trait PartialEq { //! fn eq(&self, other: &Self) -> bool; //! } +//! //! impl PartialEq for i32 { //! fn eq(&self, other: &i32) -> bool { //! *self == *other @@ -90,22 +92,22 @@ //! //! ```text //! Struct(vec![FieldInfo { -//! span: -//! name: Some(), -//! self_: , -//! other: vec![, +//! name: Some(), +//! self_: , +//! other: vec![], +//! }]) //! ``` //! //! For the `B` impl, called with `B(a)` and `B(b)`, //! //! ```text //! Struct(vec![FieldInfo { -//! span: , -//! name: None, -//! self_: -//! other: vec![] -//! }]) +//! span: , +//! name: None, +//! self_: , +//! other: vec![], +//! }]) //! ``` //! //! ## Enums @@ -114,33 +116,42 @@ //! == C0(b)`, the SubstructureFields is //! //! ```text -//! EnumMatching(0, , -//! vec![FieldInfo { -//! span: -//! name: None, -//! self_: , -//! other: vec![] -//! }]) +//! EnumMatching( +//! 0, +//! , +//! vec![FieldInfo { +//! span: , +//! name: None, +//! self_: , +//! other: vec![], +//! }], +//! ) //! ``` //! //! For `C1 {x}` and `C1 {x}`, //! //! ```text -//! EnumMatching(1, , -//! vec![FieldInfo { -//! span: -//! name: Some(), -//! self_: , -//! other: vec![] -//! }]) +//! EnumMatching( +//! 1, +//! , +//! vec![FieldInfo { +//! span: , +//! name: Some(), +//! self_: , +//! other: vec![], +//! }], +//! ) //! ``` //! //! For the tags, //! //! ```text //! EnumTag( -//! &[, ], ) +//! &[, ], +//! , +//! ) //! ``` +//! //! Note that this setup doesn't allow for the brute-force "match every variant //! against every other variant" approach, which is bad because it produces a //! quadratic amount of code (see #15375). @@ -154,9 +165,13 @@ //! //! StaticStruct(, Unnamed(vec![])) //! -//! StaticEnum(, -//! vec![(, , Unnamed(vec![])), -//! (, , Named(vec![(, )]))]) +//! StaticEnum( +//! , +//! vec![ +//! (, , Unnamed(vec![])), +//! (, , Named(vec![(, )])), +//! ], +//! ) //! ``` pub use StaticFields::*; @@ -522,7 +537,10 @@ impl<'a> TraitDef<'a> { /// Given that we are deriving a trait `DerivedTrait` for a type like: /// /// ```ignore (only-for-syntax-highlight) - /// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> where C: WhereTrait { + /// struct Struct<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> + /// where + /// C: WhereTrait, + /// { /// a: A, /// b: B::Item, /// b1: ::Item, @@ -535,12 +553,13 @@ impl<'a> TraitDef<'a> { /// create an impl like: /// /// ```ignore (only-for-syntax-highlight) - /// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ... Z> where - /// C: WhereTrait, + /// impl<'a, ..., 'z, A, B: DeclaredTrait, C, ..., Z> + /// where + /// C: WhereTrait, /// A: DerivedTrait + B1 + ... + BN, /// B: DerivedTrait + B1 + ... + BN, /// C: DerivedTrait + B1 + ... + BN, - /// B::Item: DerivedTrait + B1 + ... + BN, + /// B::Item: DerivedTrait + B1 + ... + BN, /// ::Item: DerivedTrait + B1 + ... + BN, /// ... /// { @@ -1026,6 +1045,7 @@ impl<'a> MethodDef<'a> { } /// The normal case uses field access. + /// /// ``` /// #[derive(PartialEq)] /// # struct Dummy; @@ -1038,10 +1058,12 @@ impl<'a> MethodDef<'a> { /// } /// } /// ``` + /// /// But if the struct is `repr(packed)`, we can't use something like /// `&self.x` because that might cause an unaligned ref. So for any trait /// method that takes a reference, we use a local block to force a copy. /// This requires that the field impl `Copy`. + /// /// ```rust,ignore (example) /// # struct A { x: u8, y: u8 } /// impl PartialEq for A { @@ -1053,7 +1075,7 @@ impl<'a> MethodDef<'a> { /// impl Hash for A { /// fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) -> () { /// ::core::hash::Hash::hash(&{ self.x }, state); - /// ::core::hash::Hash::hash(&{ self.y }, state) + /// ::core::hash::Hash::hash(&{ self.y }, state); /// } /// } /// ``` @@ -1107,7 +1129,9 @@ impl<'a> MethodDef<'a> { /// A2(i32) /// } /// ``` + /// /// is equivalent to: + /// /// ``` /// #![feature(core_intrinsics)] /// enum A { @@ -1119,15 +1143,15 @@ impl<'a> MethodDef<'a> { /// fn eq(&self, other: &A) -> bool { /// let __self_tag = ::core::intrinsics::discriminant_value(self); /// let __arg1_tag = ::core::intrinsics::discriminant_value(other); - /// __self_tag == __arg1_tag && - /// match (self, other) { - /// (A::A2(__self_0), A::A2(__arg1_0)) => - /// *__self_0 == *__arg1_0, + /// __self_tag == __arg1_tag + /// && match (self, other) { + /// (A::A2(__self_0), A::A2(__arg1_0)) => *__self_0 == *__arg1_0, /// _ => true, /// } /// } /// } /// ``` + /// /// Creates a tag check combined with a match for a tuple of all /// `selflike_args`, with an arm for each variant with fields, possibly an /// arm for each fieldless variant (if `unify_fieldless_variants` is not @@ -1349,7 +1373,7 @@ impl<'a> MethodDef<'a> { // (Variant1, Variant1, ...) => Body1 // (Variant2, Variant2, ...) => Body2, // ... - // _ => ::core::intrinsics::unreachable() + // _ => ::core::intrinsics::unreachable(), // } let get_match_expr = |mut selflike_args: ThinVec>| { let match_arg = if selflike_args.len() == 1 { From bd2b53ba6dc0e41c82cce900dc7d6bcc329d9a60 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 22 Oct 2023 12:05:45 -0700 Subject: [PATCH 030/171] Eliminate an "Extra scope required" obsoleted by NLL --- .../src/deriving/generic/mod.rs | 106 +++++++++--------- 1 file changed, 51 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index d8f4d82551685..b37418f1df6c9 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -695,65 +695,61 @@ impl<'a> TraitDef<'a> { } })); - { - // Extra scope required here so ty_params goes out of scope before params is moved - - let mut ty_params = params - .iter() - .filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) - .peekable(); - - if ty_params.peek().is_some() { - let ty_param_names: Vec = - ty_params.map(|ty_param| ty_param.ident.name).collect(); - - for field_ty in field_tys { - let field_ty_params = find_type_parameters(&field_ty, &ty_param_names, cx); - - for field_ty_param in field_ty_params { - // if we have already handled this type, skip it - if let ast::TyKind::Path(_, p) = &field_ty_param.ty.kind - && let [sole_segment] = &*p.segments - && ty_param_names.contains(&sole_segment.ident.name) - { - continue; - } - let mut bounds: Vec<_> = self - .additional_bounds - .iter() - .map(|p| { - cx.trait_bound( - p.to_path(cx, self.span, type_ident, generics), - self.is_const, - ) - }) - .collect(); - - // Require the current trait. - if !self.skip_path_as_bound { - bounds.push(cx.trait_bound(trait_path.clone(), self.is_const)); - } - - // Add a `Copy` bound if required. - if is_packed && self.needs_copy_as_bound_if_packed { - let p = deriving::path_std!(marker::Copy); - bounds.push(cx.trait_bound( + let mut ty_params = params + .iter() + .filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) + .peekable(); + + if ty_params.peek().is_some() { + let ty_param_names: Vec = + ty_params.map(|ty_param| ty_param.ident.name).collect(); + + for field_ty in field_tys { + let field_ty_params = find_type_parameters(&field_ty, &ty_param_names, cx); + + for field_ty_param in field_ty_params { + // if we have already handled this type, skip it + if let ast::TyKind::Path(_, p) = &field_ty_param.ty.kind + && let [sole_segment] = &*p.segments + && ty_param_names.contains(&sole_segment.ident.name) + { + continue; + } + let mut bounds: Vec<_> = self + .additional_bounds + .iter() + .map(|p| { + cx.trait_bound( p.to_path(cx, self.span, type_ident, generics), self.is_const, - )); - } + ) + }) + .collect(); - if !bounds.is_empty() { - let predicate = ast::WhereBoundPredicate { - span: self.span, - bound_generic_params: field_ty_param.bound_generic_params, - bounded_ty: field_ty_param.ty, - bounds, - }; + // Require the current trait. + if !self.skip_path_as_bound { + bounds.push(cx.trait_bound(trait_path.clone(), self.is_const)); + } - let predicate = ast::WherePredicate::BoundPredicate(predicate); - where_clause.predicates.push(predicate); - } + // Add a `Copy` bound if required. + if is_packed && self.needs_copy_as_bound_if_packed { + let p = deriving::path_std!(marker::Copy); + bounds.push(cx.trait_bound( + p.to_path(cx, self.span, type_ident, generics), + self.is_const, + )); + } + + if !bounds.is_empty() { + let predicate = ast::WhereBoundPredicate { + span: self.span, + bound_generic_params: field_ty_param.bound_generic_params, + bounded_ty: field_ty_param.ty, + bounds, + }; + + let predicate = ast::WherePredicate::BoundPredicate(predicate); + where_clause.predicates.push(predicate); } } } From 45363f11efce0e06a743d6e0dce25178462cfa1f Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 22 Oct 2023 12:08:20 -0700 Subject: [PATCH 031/171] Directly collect into ty_param_names instead of peeking to see if empty --- .../rustc_builtin_macros/src/deriving/generic/mod.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs index b37418f1df6c9..aa1ce1b929d0f 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -695,15 +695,13 @@ impl<'a> TraitDef<'a> { } })); - let mut ty_params = params + let ty_param_names: Vec = params .iter() .filter(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) - .peekable(); - - if ty_params.peek().is_some() { - let ty_param_names: Vec = - ty_params.map(|ty_param| ty_param.ident.name).collect(); + .map(|ty_param| ty_param.ident.name) + .collect(); + if !ty_param_names.is_empty() { for field_ty in field_tys { let field_ty_params = find_type_parameters(&field_ty, &ty_param_names, cx); From ade7ecf90943a7b865a6281112f8b2bbda55dba2 Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Thu, 5 Oct 2023 18:44:40 -0700 Subject: [PATCH 032/171] rustdoc: rename `/implementors` to `/impl.trait` This is shorter, avoids potential conflicts with a crate named `implementors`[^1], and will be less confusing when JS include files are added for type aliases. [^1]: AFAIK, this couldn't actually cause any problems right now, but it's simpler just to make it impossible than relying on never having a file named `trait.Foo.js` in the crate data area. --- src/librustdoc/html/render/print_item.rs | 8 ++++---- src/librustdoc/html/render/write_shared.rs | 2 +- tests/rustdoc-gui/code-tags.goml | 2 +- tests/rustdoc-gui/item-decl-colors.goml | 2 +- tests/rustdoc-gui/no-docblock.goml | 2 +- .../setting-auto-hide-content-large-items.goml | 2 +- tests/rustdoc-gui/trait-sidebar-item-order.goml | 2 +- tests/rustdoc-gui/type-declation-overflow.goml | 2 +- tests/rustdoc/hidden-impls.rs | 2 +- tests/rustdoc/impl-parts-crosscrate.rs | 2 +- tests/rustdoc/inline_cross/implementors-js.rs | 10 +++++----- tests/rustdoc/issue-43701.rs | 2 +- 12 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index f6432dc61ae06..b553954845b41 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -1101,7 +1101,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: // ``` // // Basically, we want `C::Baz` and `A::Foo` to show the same set of - // impls, which is easier if they both treat `/implementors/A/trait.Foo.js` + // impls, which is easier if they both treat `/trait.impl/A/trait.Foo.js` // as the Single Source of Truth. // // We also want the `impl Baz for Quux` to be written to @@ -1110,7 +1110,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: // because that'll load faster, and it's better for SEO. And we don't want // the same impl to show up twice on the same page. // - // To make this work, the implementors JS file has a structure kinda + // To make this work, the trait.impl/A/trait.Foo.js JS file has a structure kinda // like this: // // ```js @@ -1127,7 +1127,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: // So C's HTML will have something like this: // // ```html - // // ``` // @@ -1137,7 +1137,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: // [JSONP]: https://en.wikipedia.org/wiki/JSONP let mut js_src_path: UrlPartsBuilder = std::iter::repeat("..") .take(cx.current.len()) - .chain(std::iter::once("implementors")) + .chain(std::iter::once("trait.impl")) .collect(); if let Some(did) = it.item_id.as_def_id() && let get_extern = { || cache.external_paths.get(&did).map(|s| &s.0) } && diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index e68d5ab2fbdb5..b162ea99d8f21 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -362,7 +362,7 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; } // Update the list of all implementors for traits - let dst = cx.dst.join("implementors"); + let dst = cx.dst.join("trait.impl"); let cache = cx.cache(); for (&did, imps) in &cache.implementors { // Private modules can leak through to this phase of rustdoc, which diff --git a/tests/rustdoc-gui/code-tags.goml b/tests/rustdoc-gui/code-tags.goml index 3405d3295e69d..577f932e576bf 100644 --- a/tests/rustdoc-gui/code-tags.goml +++ b/tests/rustdoc-gui/code-tags.goml @@ -1,6 +1,6 @@ // This test ensures that items and documentation code blocks are wrapped in

 
-// We need to disable this check because `implementors/test_docs/trait.AnotherOne.js`
+// We need to disable this check because `trait.impl/test_docs/trait.AnotherOne.js`
 // doesn't exist.
 fail-on-request-error: false
 go-to: "file://" + |DOC_PATH| + "/test_docs/fn.foo.html"
diff --git a/tests/rustdoc-gui/item-decl-colors.goml b/tests/rustdoc-gui/item-decl-colors.goml
index a777842735c24..7bbd20c4ee006 100644
--- a/tests/rustdoc-gui/item-decl-colors.goml
+++ b/tests/rustdoc-gui/item-decl-colors.goml
@@ -1,6 +1,6 @@
 // This test ensures that the color of the items in the type decl are working as expected.
 
-// We need to disable this check because `implementors/test_docs/trait.TraitWithoutGenerics.js`
+// We need to disable this check because `trait.impl/test_docs/trait.TraitWithoutGenerics.js`
 // doesn't exist.
 fail-on-request-error: false
 
diff --git a/tests/rustdoc-gui/no-docblock.goml b/tests/rustdoc-gui/no-docblock.goml
index 1b4638ef067c5..2115b6f5390c3 100644
--- a/tests/rustdoc-gui/no-docblock.goml
+++ b/tests/rustdoc-gui/no-docblock.goml
@@ -1,6 +1,6 @@
 // This test checks that there are margins applied to methods with no docblocks.
 
-// We need to disable this check because `implementors/test_docs/trait.TraitWithNoDocblock.js`
+// We need to disable this check because `trait.impl/test_docs/trait.TraitWithNoDocblock.js`
 // doesn't exist.
 fail-on-request-error: false
 
diff --git a/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml b/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml
index 6cd725043f4ad..b55a1cfd92bc5 100644
--- a/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml
+++ b/tests/rustdoc-gui/setting-auto-hide-content-large-items.goml
@@ -1,7 +1,7 @@
 // This test ensures that the "Auto-hide item contents for large items" setting is working as
 // expected.
 
-// We need to disable this check because `implementors/test_docs/trait.Iterator.js` doesn't exist.
+// We need to disable this check because `trait.impl/test_docs/trait.Iterator.js` doesn't exist.
 fail-on-request-error: false
 
 define-function: (
diff --git a/tests/rustdoc-gui/trait-sidebar-item-order.goml b/tests/rustdoc-gui/trait-sidebar-item-order.goml
index 9330ef040ec9e..73e362ca81349 100644
--- a/tests/rustdoc-gui/trait-sidebar-item-order.goml
+++ b/tests/rustdoc-gui/trait-sidebar-item-order.goml
@@ -1,6 +1,6 @@
 // Checks that the elements in the sidebar are alphabetically sorted.
 
-// We need to disable this check because `implementors/test_docs/trait.AnotherOne.js`
+// We need to disable this check because `trait.impl/test_docs/trait.AnotherOne.js`
 // doesn't exist.
 fail-on-request-error: false
 
diff --git a/tests/rustdoc-gui/type-declation-overflow.goml b/tests/rustdoc-gui/type-declation-overflow.goml
index f212781e9b340..5780f5c88f82c 100644
--- a/tests/rustdoc-gui/type-declation-overflow.goml
+++ b/tests/rustdoc-gui/type-declation-overflow.goml
@@ -2,7 +2,7 @@
 // This test ensures that the items declaration content overflow is handled inside the 
 directly.
 
 // We need to disable this check because
-// `implementors/test_docs/trait.ALongNameBecauseItHelpsTestingTheCurrentProblem.js`
+// `trait.impl/test_docs/trait.ALongNameBecauseItHelpsTestingTheCurrentProblem.js`
 // doesn't exist.
 fail-on-request-error: false
 
diff --git a/tests/rustdoc/hidden-impls.rs b/tests/rustdoc/hidden-impls.rs
index 26e2e0e066008..3283fbfecce81 100644
--- a/tests/rustdoc/hidden-impls.rs
+++ b/tests/rustdoc/hidden-impls.rs
@@ -12,6 +12,6 @@ pub mod __hidden {
 
 // @has foo/trait.Clone.html
 // @!hasraw - 'Foo'
-// @has implementors/core/clone/trait.Clone.js
+// @has trait.impl/core/clone/trait.Clone.js
 // @!hasraw - 'Foo'
 pub use std::clone::Clone;
diff --git a/tests/rustdoc/impl-parts-crosscrate.rs b/tests/rustdoc/impl-parts-crosscrate.rs
index 34733f1f8ccb8..da109ea709001 100644
--- a/tests/rustdoc/impl-parts-crosscrate.rs
+++ b/tests/rustdoc/impl-parts-crosscrate.rs
@@ -12,7 +12,7 @@ pub struct Bar { t: T }
 // full impl string.  Instead, just make sure something from each part
 // is mentioned.
 
-// @hasraw implementors/rustdoc_impl_parts_crosscrate/trait.AnAutoTrait.js Bar
+// @hasraw trait.impl/rustdoc_impl_parts_crosscrate/trait.AnAutoTrait.js Bar
 // @hasraw - Send
 // @hasraw - !AnAutoTrait
 // @hasraw - Copy
diff --git a/tests/rustdoc/inline_cross/implementors-js.rs b/tests/rustdoc/inline_cross/implementors-js.rs
index c79f05d8d3c9b..c17d52d0f4147 100644
--- a/tests/rustdoc/inline_cross/implementors-js.rs
+++ b/tests/rustdoc/inline_cross/implementors-js.rs
@@ -4,13 +4,13 @@
 
 extern crate implementors_inline;
 
-// @!has implementors/implementors_js/trait.MyTrait.js
-// @has implementors/implementors_inline/my_trait/trait.MyTrait.js
-// @!has implementors/implementors_inline/prelude/trait.MyTrait.js
+// @!has trait.impl/implementors_js/trait.MyTrait.js
+// @has trait.impl/implementors_inline/my_trait/trait.MyTrait.js
+// @!has trait.impl/implementors_inline/prelude/trait.MyTrait.js
 // @has implementors_inline/my_trait/trait.MyTrait.html
-// @has - '//script/@src' '../../implementors/implementors_inline/my_trait/trait.MyTrait.js'
+// @has - '//script/@src' '../../trait.impl/implementors_inline/my_trait/trait.MyTrait.js'
 // @has implementors_js/trait.MyTrait.html
-// @has - '//script/@src' '../implementors/implementors_inline/my_trait/trait.MyTrait.js'
+// @has - '//script/@src' '../trait.impl/implementors_inline/my_trait/trait.MyTrait.js'
 /// When re-exporting this trait, the HTML will be inlined,
 /// but, vitally, the JavaScript will be located only at the
 /// one canonical path.
diff --git a/tests/rustdoc/issue-43701.rs b/tests/rustdoc/issue-43701.rs
index 44335e961f90d..de772881e7323 100644
--- a/tests/rustdoc/issue-43701.rs
+++ b/tests/rustdoc/issue-43701.rs
@@ -2,4 +2,4 @@
 
 pub use std::vec::Vec;
 
-// @!has implementors/core/clone/trait.Clone.js
+// @!has trait.impl/core/clone/trait.Clone.js

From aa76a59bf024f7b721e7e0da6d39b30a3f7adbc5 Mon Sep 17 00:00:00 2001
From: Michael Howell 
Date: Thu, 5 Oct 2023 18:44:42 -0700
Subject: [PATCH 033/171] Revert "rustdoc: filter before storing in vec"

This reverts commit c79b960747487f6724ebe5b163a22c82a2b636d3.
---
 src/librustdoc/html/render/context.rs | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index bf8d1a80337e8..7e432ecedc563 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -160,15 +160,8 @@ impl SharedContext<'_> {
     ) -> Vec<&'a formats::Impl> {
         let tcx = self.tcx;
         let cache = &self.cache;
-        let mut saw_impls = FxHashSet::default();
-        let mut v: Vec<&formats::Impl> = cache
-            .impls
-            .get(&did)
-            .map(Vec::as_slice)
-            .unwrap_or(&[])
-            .iter()
-            .filter(|i| saw_impls.insert(i.def_id()))
-            .collect();
+        let mut v: Vec<&formats::Impl> =
+            cache.impls.get(&did).map(Vec::as_slice).unwrap_or(&[]).iter().collect();
         if let TypeAliasItem(ait) = &*it.kind &&
             let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
             let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
@@ -189,12 +182,13 @@ impl SharedContext<'_> {
             v.extend(av.iter().filter(|impl_| {
                 if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
                     reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
-                        && saw_impls.insert(impl_def_id)
                 } else {
                     false
                 }
             }));
         }
+        let mut saw_impls = FxHashSet::default();
+        v.retain(|i| saw_impls.insert(i.def_id()));
         v
     }
 }

From ab125a2e0b52601a5de1b85a937a4eea2309669c Mon Sep 17 00:00:00 2001
From: Michael Howell 
Date: Thu, 5 Oct 2023 18:44:43 -0700
Subject: [PATCH 034/171] Revert "rustdoc: factor all-impls-for-item out into
 its own method"

This reverts commit c3e5ad448b87be31e570c048cf7ba3b1e7daec44.
---
 src/librustdoc/html/render/context.rs | 47 ++------------------------
 src/librustdoc/html/render/mod.rs     | 48 ++++++++++++++++++++++-----
 src/librustdoc/html/render/sidebar.rs | 38 +++++++++++++++++++--
 3 files changed, 78 insertions(+), 55 deletions(-)

diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 7e432ecedc563..6da9e45a1da40 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -7,10 +7,8 @@ use std::sync::mpsc::{channel, Receiver};
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE};
-use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
-use rustc_span::def_id::DefId;
 use rustc_span::edition::Edition;
 use rustc_span::source_map::FileName;
 use rustc_span::{sym, Symbol};
@@ -25,13 +23,13 @@ use super::{
     AllTypes, LinkFromSrc, StylePath,
 };
 use crate::clean::utils::has_doc_flag;
-use crate::clean::{self, types::ExternalLocation, ExternalCrate, TypeAliasItem};
+use crate::clean::{self, types::ExternalLocation, ExternalCrate};
 use crate::config::{ModuleSorting, RenderOptions};
 use crate::docfs::{DocFS, PathError};
 use crate::error::Error;
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
-use crate::formats::{self, FormatRenderer};
+use crate::formats::FormatRenderer;
 use crate::html::escape::Escape;
 use crate::html::format::{join_with_double_colon, Buffer};
 use crate::html::markdown::{self, plain_text_summary, ErrorCodes, IdMap};
@@ -150,47 +148,6 @@ impl SharedContext<'_> {
     pub(crate) fn edition(&self) -> Edition {
         self.tcx.sess.edition()
     }
-
-    /// Returns a list of impls on the given type, and, if it's a type alias,
-    /// other types that it aliases.
-    pub(crate) fn all_impls_for_item<'a>(
-        &'a self,
-        it: &clean::Item,
-        did: DefId,
-    ) -> Vec<&'a formats::Impl> {
-        let tcx = self.tcx;
-        let cache = &self.cache;
-        let mut v: Vec<&formats::Impl> =
-            cache.impls.get(&did).map(Vec::as_slice).unwrap_or(&[]).iter().collect();
-        if let TypeAliasItem(ait) = &*it.kind &&
-            let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
-            let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
-            let Some(av) = cache.impls.get(&aliased_type_defid) &&
-            let Some(alias_def_id) = it.item_id.as_def_id()
-        {
-            // This branch of the compiler compares types structually, but does
-            // not check trait bounds. That's probably fine, since type aliases
-            // don't normally constrain on them anyway.
-            // https://github.com/rust-lang/rust/issues/21903
-            //
-            // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification.
-            // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress.
-            let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
-            let reject_cx = DeepRejectCtxt {
-                treat_obligation_params: TreatParams::AsCandidateKey,
-            };
-            v.extend(av.iter().filter(|impl_| {
-                if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
-                    reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
-                } else {
-                    false
-                }
-            }));
-        }
-        let mut saw_impls = FxHashSet::default();
-        v.retain(|i| saw_impls.insert(i.def_id()));
-        v
-    }
 }
 
 impl<'tcx> Context<'tcx> {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 89e29d8b59b2f..515dedbb85fb5 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -55,6 +55,7 @@ use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_hir::Mutability;
 use rustc_middle::middle::stability;
 use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_span::{
     symbol::{sym, Symbol},
     BytePos, FileName, RealFileName,
@@ -62,6 +63,7 @@ use rustc_span::{
 use serde::ser::{SerializeMap, SerializeSeq};
 use serde::{Serialize, Serializer};
 
+use crate::clean::types::TypeAliasItem;
 use crate::clean::{self, ItemId, RenderedLink, SelfTy};
 use crate::error::Error;
 use crate::formats::cache::Cache;
@@ -1132,13 +1134,13 @@ pub(crate) fn render_all_impls(
 fn render_assoc_items<'a, 'cx: 'a>(
     cx: &'a mut Context<'cx>,
     containing_item: &'a clean::Item,
-    did: DefId,
+    it: DefId,
     what: AssocItemRender<'a>,
 ) -> impl fmt::Display + 'a + Captures<'cx> {
     let mut derefs = DefIdSet::default();
-    derefs.insert(did);
+    derefs.insert(it);
     display_fn(move |f| {
-        render_assoc_items_inner(f, cx, containing_item, did, what, &mut derefs);
+        render_assoc_items_inner(f, cx, containing_item, it, what, &mut derefs);
         Ok(())
     })
 }
@@ -1147,16 +1149,46 @@ fn render_assoc_items_inner(
     mut w: &mut dyn fmt::Write,
     cx: &mut Context<'_>,
     containing_item: &clean::Item,
-    did: DefId,
+    it: DefId,
     what: AssocItemRender<'_>,
     derefs: &mut DefIdSet,
 ) {
     info!("Documenting associated items of {:?}", containing_item.name);
     let shared = Rc::clone(&cx.shared);
-    let v = shared.all_impls_for_item(containing_item, did);
-    let v = v.as_slice();
-    let (non_trait, traits): (Vec<&Impl>, _) =
-        v.iter().partition(|i| i.inner_impl().trait_.is_none());
+    let cache = &shared.cache;
+    let tcx = cx.tcx();
+    let av = if let TypeAliasItem(ait) = &*containing_item.kind &&
+        let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
+        let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
+        let Some(mut av) = cache.impls.get(&aliased_type_defid).cloned() &&
+        let Some(alias_def_id) = containing_item.item_id.as_def_id()
+    {
+        // This branch of the compiler compares types structually, but does
+        // not check trait bounds. That's probably fine, since type aliases
+        // don't normally constrain on them anyway.
+        // https://github.com/rust-lang/rust/issues/21903
+        //
+        // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification.
+        // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress.
+        let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
+        let reject_cx = DeepRejectCtxt {
+            treat_obligation_params: TreatParams::AsCandidateKey,
+        };
+        av.retain(|impl_| {
+            if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
+                reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
+            } else {
+                false
+            }
+        });
+        av
+    } else {
+        Vec::new()
+    };
+    let blank = Vec::new();
+    let v = cache.impls.get(&it).unwrap_or(&blank);
+    let (non_trait, traits): (Vec<_>, _) =
+        v.iter().chain(&av[..]).partition(|i| i.inner_impl().trait_.is_none());
     let mut saw_impls = FxHashSet::default();
     if !non_trait.is_empty() {
         let mut tmp_buf = Buffer::html();
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index fb429f237e3db..8ff95477dd810 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -3,10 +3,12 @@ use std::{borrow::Cow, rc::Rc};
 use askama::Template;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::{def::CtorKind, def_id::DefIdSet};
+use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::{self, TyCtxt};
 
 use crate::{
     clean,
+    clean::types::TypeAliasItem,
     formats::{item_type::ItemType, Impl},
     html::{format::Buffer, markdown::IdMap},
 };
@@ -287,8 +289,40 @@ fn sidebar_assoc_items<'a>(
     links: &mut Vec>,
 ) {
     let did = it.item_id.expect_def_id();
-    let v = cx.shared.all_impls_for_item(it, it.item_id.expect_def_id());
-    let v = v.as_slice();
+    let cache = cx.cache();
+    let tcx = cx.tcx();
+    let mut v: Vec<&Impl> =
+        cache.impls.get(&did).map(Vec::as_slice).unwrap_or(&[]).iter().collect();
+    if let TypeAliasItem(ait) = &*it.kind &&
+        let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
+        let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
+        let Some(av) = cache.impls.get(&aliased_type_defid) &&
+        let Some(alias_def_id) = it.item_id.as_def_id()
+    {
+        // This branch of the compiler compares types structually, but does
+        // not check trait bounds. That's probably fine, since type aliases
+        // don't normally constrain on them anyway.
+        // https://github.com/rust-lang/rust/issues/21903
+        //
+        // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification.
+        // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress.
+        let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
+        let reject_cx = DeepRejectCtxt {
+            treat_obligation_params: TreatParams::AsCandidateKey,
+        };
+        v.extend(av.iter().filter(|impl_| {
+            if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
+                reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
+            } else {
+                false
+            }
+        }));
+    }
+    let v = {
+        let mut saw_impls = FxHashSet::default();
+        v.retain(|i| saw_impls.insert(i.def_id()));
+        v.as_slice()
+    };
 
     let mut assoc_consts = Vec::new();
     let mut methods = Vec::new();

From 36b8d58b41c6071bff0f5640b0e01f83cae8cd21 Mon Sep 17 00:00:00 2001
From: Michael Howell 
Date: Thu, 5 Oct 2023 18:44:44 -0700
Subject: [PATCH 035/171] Revert "rustdoc: add impl items from aliased type
 into sidebar"

This reverts commit d882b2118e505d86a9f770ef862fb1ee6e91ced8.
---
 src/librustdoc/html/render/sidebar.rs   | 45 +++----------------------
 tests/rustdoc/type-alias-impls-32077.rs |  5 ---
 2 files changed, 5 insertions(+), 45 deletions(-)

diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index 8ff95477dd810..9f7744b45d36e 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -3,12 +3,10 @@ use std::{borrow::Cow, rc::Rc};
 use askama::Template;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::{def::CtorKind, def_id::DefIdSet};
-use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::{self, TyCtxt};
 
 use crate::{
     clean,
-    clean::types::TypeAliasItem,
     formats::{item_type::ItemType, Impl},
     html::{format::Buffer, markdown::IdMap},
 };
@@ -290,43 +288,10 @@ fn sidebar_assoc_items<'a>(
 ) {
     let did = it.item_id.expect_def_id();
     let cache = cx.cache();
-    let tcx = cx.tcx();
-    let mut v: Vec<&Impl> =
-        cache.impls.get(&did).map(Vec::as_slice).unwrap_or(&[]).iter().collect();
-    if let TypeAliasItem(ait) = &*it.kind &&
-        let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
-        let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
-        let Some(av) = cache.impls.get(&aliased_type_defid) &&
-        let Some(alias_def_id) = it.item_id.as_def_id()
-    {
-        // This branch of the compiler compares types structually, but does
-        // not check trait bounds. That's probably fine, since type aliases
-        // don't normally constrain on them anyway.
-        // https://github.com/rust-lang/rust/issues/21903
-        //
-        // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification.
-        // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress.
-        let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
-        let reject_cx = DeepRejectCtxt {
-            treat_obligation_params: TreatParams::AsCandidateKey,
-        };
-        v.extend(av.iter().filter(|impl_| {
-            if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
-                reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
-            } else {
-                false
-            }
-        }));
-    }
-    let v = {
-        let mut saw_impls = FxHashSet::default();
-        v.retain(|i| saw_impls.insert(i.def_id()));
-        v.as_slice()
-    };
 
     let mut assoc_consts = Vec::new();
     let mut methods = Vec::new();
-    if !v.is_empty() {
+    if let Some(v) = cache.impls.get(&did) {
         let mut used_links = FxHashSet::default();
         let mut id_map = IdMap::new();
 
@@ -362,7 +327,7 @@ fn sidebar_assoc_items<'a>(
                     cx,
                     &mut deref_methods,
                     impl_,
-                    v.iter().copied(),
+                    v,
                     &mut derefs,
                     &mut used_links,
                 );
@@ -392,7 +357,7 @@ fn sidebar_deref_methods<'a>(
     cx: &'a Context<'_>,
     out: &mut Vec>,
     impl_: &Impl,
-    v: impl Iterator,
+    v: &[Impl],
     derefs: &mut DefIdSet,
     used_links: &mut FxHashSet,
 ) {
@@ -417,7 +382,7 @@ fn sidebar_deref_methods<'a>(
             // Avoid infinite cycles
             return;
         }
-        let deref_mut = { v }.any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
+        let deref_mut = v.iter().any(|i| i.trait_did() == cx.tcx().lang_items().deref_mut_trait());
         let inner_impl = target
             .def_id(c)
             .or_else(|| {
@@ -468,7 +433,7 @@ fn sidebar_deref_methods<'a>(
                 cx,
                 out,
                 target_deref_impl,
-                target_impls.iter(),
+                target_impls,
                 derefs,
                 used_links,
             );
diff --git a/tests/rustdoc/type-alias-impls-32077.rs b/tests/rustdoc/type-alias-impls-32077.rs
index 7bb763f86afb4..2108749d1a6e4 100644
--- a/tests/rustdoc/type-alias-impls-32077.rs
+++ b/tests/rustdoc/type-alias-impls-32077.rs
@@ -32,11 +32,6 @@ impl Bar for GenericStruct {}
 // @!has - '//h3' 'impl Bar for GenericStruct {}'
 // Same goes for the `Deref` impl.
 // @!has - '//h2' 'Methods from Deref'
-// @count - '//nav[@class="sidebar"]//a' 'on_alias' 1
-// @count - '//nav[@class="sidebar"]//a' 'on_gen' 1
-// @count - '//nav[@class="sidebar"]//a' 'Foo' 1
-// @!has - '//nav[@class="sidebar"]//a' 'Bar'
-// @!has - '//nav[@class="sidebar"]//a' 'on_u32'
 pub type TypedefStruct = GenericStruct;
 
 impl TypedefStruct {

From 77da7c655ee2583f336f23f61246bc0885beaf5d Mon Sep 17 00:00:00 2001
From: Michael Howell 
Date: Thu, 5 Oct 2023 18:44:46 -0700
Subject: [PATCH 036/171] Revert "Add note about lazy_type_alias"

This reverts commit b3686c2fd6ad57912e1b0e778bedb0b9a05c73fa.
---
 src/librustdoc/html/render/mod.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 515dedbb85fb5..029ad750f5b95 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1168,8 +1168,8 @@ fn render_assoc_items_inner(
         // don't normally constrain on them anyway.
         // https://github.com/rust-lang/rust/issues/21903
         //
-        // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this to use type unification.
-        // Be aware of `tests/rustdoc/issue-112515-impl-ty-alias.rs` which might regress.
+        // If that changes, then this will need to check them with type
+        // unification.
         let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
         let reject_cx = DeepRejectCtxt {
             treat_obligation_params: TreatParams::AsCandidateKey,

From e701e64d59b8666161ca526a3c1bf3048019c8d9 Mon Sep 17 00:00:00 2001
From: Michael Howell 
Date: Thu, 5 Oct 2023 18:44:48 -0700
Subject: [PATCH 037/171] Revert "rustdoc: list matching impls on type aliases"

This reverts commit 19edb3ce808ee2b1190026b9d56cc6187e1ad9b1.
---
 src/librustdoc/html/render/mod.rs       | 46 ++----------------
 tests/rustdoc/type-alias-impls-32077.rs | 62 -------------------------
 2 files changed, 4 insertions(+), 104 deletions(-)
 delete mode 100644 tests/rustdoc/type-alias-impls-32077.rs

diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 029ad750f5b95..51cc836cd157d 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -63,7 +63,6 @@ use rustc_span::{
 use serde::ser::{SerializeMap, SerializeSeq};
 use serde::{Serialize, Serializer};
 
-use crate::clean::types::TypeAliasItem;
 use crate::clean::{self, ItemId, RenderedLink, SelfTy};
 use crate::error::Error;
 use crate::formats::cache::Cache;
@@ -1156,40 +1155,8 @@ fn render_assoc_items_inner(
     info!("Documenting associated items of {:?}", containing_item.name);
     let shared = Rc::clone(&cx.shared);
     let cache = &shared.cache;
-    let tcx = cx.tcx();
-    let av = if let TypeAliasItem(ait) = &*containing_item.kind &&
-        let aliased_clean_type = ait.item_type.as_ref().unwrap_or(&ait.type_) &&
-        let Some(aliased_type_defid) = aliased_clean_type.def_id(cache) &&
-        let Some(mut av) = cache.impls.get(&aliased_type_defid).cloned() &&
-        let Some(alias_def_id) = containing_item.item_id.as_def_id()
-    {
-        // This branch of the compiler compares types structually, but does
-        // not check trait bounds. That's probably fine, since type aliases
-        // don't normally constrain on them anyway.
-        // https://github.com/rust-lang/rust/issues/21903
-        //
-        // If that changes, then this will need to check them with type
-        // unification.
-        let aliased_ty = tcx.type_of(alias_def_id).skip_binder();
-        let reject_cx = DeepRejectCtxt {
-            treat_obligation_params: TreatParams::AsCandidateKey,
-        };
-        av.retain(|impl_| {
-            if let Some(impl_def_id) = impl_.impl_item.item_id.as_def_id() {
-                reject_cx.types_may_unify(aliased_ty, tcx.type_of(impl_def_id).skip_binder())
-            } else {
-                false
-            }
-        });
-        av
-    } else {
-        Vec::new()
-    };
-    let blank = Vec::new();
-    let v = cache.impls.get(&it).unwrap_or(&blank);
-    let (non_trait, traits): (Vec<_>, _) =
-        v.iter().chain(&av[..]).partition(|i| i.inner_impl().trait_.is_none());
-    let mut saw_impls = FxHashSet::default();
+    let Some(v) = cache.impls.get(&it) else { return };
+    let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none());
     if !non_trait.is_empty() {
         let mut tmp_buf = Buffer::html();
         let (render_mode, id, class_html) = match what {
@@ -1218,9 +1185,6 @@ fn render_assoc_items_inner(
         };
         let mut impls_buf = Buffer::html();
         for i in &non_trait {
-            if !saw_impls.insert(i.def_id()) {
-                continue;
-            }
             render_impl(
                 &mut impls_buf,
                 cx,
@@ -1266,10 +1230,8 @@ fn render_assoc_items_inner(
 
         let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
             traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
-        let (blanket_impl, concrete): (Vec<&Impl>, _) = concrete
-            .into_iter()
-            .filter(|t| saw_impls.insert(t.def_id()))
-            .partition(|t| t.inner_impl().kind.is_blanket());
+        let (blanket_impl, concrete): (Vec<&Impl>, _) =
+            concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
 
         render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
     }
diff --git a/tests/rustdoc/type-alias-impls-32077.rs b/tests/rustdoc/type-alias-impls-32077.rs
deleted file mode 100644
index 2108749d1a6e4..0000000000000
--- a/tests/rustdoc/type-alias-impls-32077.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-// Regression test for .
-
-// https://github.com/rust-lang/rust/issues/32077
-#![crate_name = "foo"]
-
-pub struct GenericStruct(T);
-
-impl GenericStruct {
-    pub fn on_gen(arg: T) {}
-}
-
-impl GenericStruct {
-    pub fn on_u32(arg: u32) {}
-}
-
-pub trait Foo {}
-pub trait Bar {}
-
-impl Foo for GenericStruct {}
-impl Bar for GenericStruct {}
-
-// @has 'foo/type.TypedefStruct.html'
-// We check that "Aliased type" is also present as a title in the sidebar.
-// @has - '//*[@class="sidebar-elems"]//h3/a[@href="#aliased-type"]' 'Aliased type'
-// We check that we have the implementation of the type alias itself.
-// @has - '//*[@id="impl-GenericStruct%3Cu8%3E"]/h3' 'impl TypedefStruct'
-// @has - '//*[@id="method.on_alias"]/h4' 'pub fn on_alias()'
-// @has - '//*[@id="impl-GenericStruct%3CT%3E"]/h3' 'impl GenericStruct'
-// @has - '//*[@id="method.on_gen"]/h4' 'pub fn on_gen(arg: T)'
-// @has - '//*[@id="impl-Foo-for-GenericStruct%3CT%3E"]/h3' 'impl Foo for GenericStruct'
-// This trait implementation doesn't match the type alias parameters so shouldn't appear in docs.
-// @!has - '//h3' 'impl Bar for GenericStruct {}'
-// Same goes for the `Deref` impl.
-// @!has - '//h2' 'Methods from Deref'
-pub type TypedefStruct = GenericStruct;
-
-impl TypedefStruct {
-    pub fn on_alias() {}
-}
-
-impl std::ops::Deref for GenericStruct {
-    type Target = u32;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-pub struct Wrap(GenericStruct);
-
-// @has 'foo/type.Alias.html'
-// @has - '//h2' 'Methods from Deref'
-// @has - '//*[@id="impl-Deref-for-Wrap%3CT%3E"]/h3' 'impl Deref for Wrap'
-pub type Alias = Wrap;
-
-impl std::ops::Deref for Wrap {
-    type Target = GenericStruct;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}

From 4dfd82713353a40058a674d7df3a3c05cf3dcfd6 Mon Sep 17 00:00:00 2001
From: Michael Howell 
Date: Thu, 5 Oct 2023 18:44:49 -0700
Subject: [PATCH 038/171] Added a new item, need to bump this count

---
 tests/rustdoc-gui/search-tab.goml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/rustdoc-gui/search-tab.goml b/tests/rustdoc-gui/search-tab.goml
index 427201e1b5da4..1c0c9e79e4050 100644
--- a/tests/rustdoc-gui/search-tab.goml
+++ b/tests/rustdoc-gui/search-tab.goml
@@ -79,8 +79,8 @@ call-function: ("check-colors", {
 set-window-size: (851, 600)
 
 // Check the size and count in tabs
-assert-text: ("#search-tabs > button:nth-child(1) > .count", " (23) ")
-assert-text: ("#search-tabs > button:nth-child(2) > .count", " (4)  ")
+assert-text: ("#search-tabs > button:nth-child(1) > .count", " (24) ")
+assert-text: ("#search-tabs > button:nth-child(2) > .count", " (5)  ")
 assert-text: ("#search-tabs > button:nth-child(3) > .count", " (0)  ")
 store-property: ("#search-tabs > button:nth-child(1)", {"offsetWidth": buttonWidth})
 assert-property: ("#search-tabs > button:nth-child(2)", {"offsetWidth": |buttonWidth|})

From fa10e4d667aea7ca869eb53f9af925a5fa120c84 Mon Sep 17 00:00:00 2001
From: Michael Howell 
Date: Thu, 5 Oct 2023 18:44:52 -0700
Subject: [PATCH 039/171] rustdoc: use JS to inline target type impl docs into
 alias

This is an attempt to balance three problems, each of which would
be violated by a simpler implementation:

- A type alias should show all the `impl` blocks for the target
  type, and vice versa, if they're applicable. If nothing was
  done, and rustdoc continues to match them up in HIR, this
  would not work.

- Copying the target type's docs into its aliases' HTML pages
  directly causes far too much redundant HTML text to be generated
  when a crate has large numbers of methods and large numbers
  of type aliases.

- Using JavaScript exclusively for type alias impl docs would
  be a functional regression, and could make some docs very hard
  to find for non-JS readers.

- Making sure that only applicable docs are show in the
  resulting page requires a type checkers. Do not reimplement
  the type checker in JavaScript.

So, to make it work, rustdoc stashes these type-alias-inlined docs
in a JSONP "database-lite". The file is generated in `write_shared.rs`,
included in a `",
+            src = js_src_path.finish(),
+        );
+    }
 }
 
 fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) {
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index 9f7744b45d36e..4e8d88c55b64f 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -38,18 +38,19 @@ pub(crate) struct LinkBlock<'a> {
     /// as well as the link to it, e.g. `#implementations`.
     /// Will be rendered inside an `

` tag heading: Link<'a>, + class: &'static str, links: Vec>, /// Render the heading even if there are no links force_render: bool, } impl<'a> LinkBlock<'a> { - pub fn new(heading: Link<'a>, links: Vec>) -> Self { - Self { heading, links, force_render: false } + pub fn new(heading: Link<'a>, class: &'static str, links: Vec>) -> Self { + Self { heading, links, class, force_render: false } } - pub fn forced(heading: Link<'a>) -> Self { - Self { heading, links: vec![], force_render: true } + pub fn forced(heading: Link<'a>, class: &'static str) -> Self { + Self { heading, links: vec![], class, force_render: true } } pub fn should_render(&self) -> bool { @@ -157,7 +158,7 @@ fn sidebar_struct<'a>( }; let mut items = vec![]; if let Some(name) = field_name { - items.push(LinkBlock::new(Link::new("fields", name), fields)); + items.push(LinkBlock::new(Link::new("fields", name), "structfield", fields)); } sidebar_assoc_items(cx, it, &mut items); items @@ -214,12 +215,15 @@ fn sidebar_trait<'a>( ("foreign-impls", "Implementations on Foreign Types", foreign_impls), ] .into_iter() - .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), items)) + .map(|(id, title, items)| LinkBlock::new(Link::new(id, title), "", items)) .collect(); sidebar_assoc_items(cx, it, &mut blocks); - blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"))); + blocks.push(LinkBlock::forced(Link::new("implementors", "Implementors"), "impl")); if t.is_auto(cx.tcx()) { - blocks.push(LinkBlock::forced(Link::new("synthetic-implementors", "Auto Implementors"))); + blocks.push(LinkBlock::forced( + Link::new("synthetic-implementors", "Auto Implementors"), + "impl-auto", + )); } blocks } @@ -245,7 +249,7 @@ fn sidebar_type_alias<'a>( ) -> Vec> { let mut items = vec![]; if let Some(inner_type) = &t.inner_type { - items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type"))); + items.push(LinkBlock::forced(Link::new("aliased-type", "Aliased type"), "type")); match inner_type { clean::TypeAliasInnerType::Enum { variants, is_non_exhaustive: _ } => { let mut variants = variants @@ -256,12 +260,12 @@ fn sidebar_type_alias<'a>( .collect::>(); variants.sort_unstable(); - items.push(LinkBlock::new(Link::new("variants", "Variants"), variants)); + items.push(LinkBlock::new(Link::new("variants", "Variants"), "variant", variants)); } clean::TypeAliasInnerType::Union { fields } | clean::TypeAliasInnerType::Struct { ctor_kind: _, fields } => { let fields = get_struct_fields_name(fields); - items.push(LinkBlock::new(Link::new("fields", "Fields"), fields)); + items.push(LinkBlock::new(Link::new("fields", "Fields"), "field", fields)); } } } @@ -275,7 +279,7 @@ fn sidebar_union<'a>( u: &'a clean::Union, ) -> Vec> { let fields = get_struct_fields_name(&u.fields); - let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), fields)]; + let mut items = vec![LinkBlock::new(Link::new("fields", "Fields"), "structfield", fields)]; sidebar_assoc_items(cx, it, &mut items); items } @@ -340,12 +344,16 @@ fn sidebar_assoc_items<'a>( sidebar_render_assoc_items(cx, &mut id_map, concrete, synthetic, blanket_impl) } else { - std::array::from_fn(|_| LinkBlock::new(Link::empty(), vec![])) + std::array::from_fn(|_| LinkBlock::new(Link::empty(), "", vec![])) }; let mut blocks = vec![ - LinkBlock::new(Link::new("implementations", "Associated Constants"), assoc_consts), - LinkBlock::new(Link::new("implementations", "Methods"), methods), + LinkBlock::new( + Link::new("implementations", "Associated Constants"), + "associatedconstant", + assoc_consts, + ), + LinkBlock::new(Link::new("implementations", "Methods"), "method", methods), ]; blocks.append(&mut deref_methods); blocks.extend([concrete, synthetic, blanket]); @@ -414,7 +422,7 @@ fn sidebar_deref_methods<'a>( ); // We want links' order to be reproducible so we don't use unstable sort. ret.sort(); - out.push(LinkBlock::new(Link::new(id, title), ret)); + out.push(LinkBlock::new(Link::new(id, title), "deref-methods", ret)); } } @@ -453,7 +461,7 @@ fn sidebar_enum<'a>( .collect::>(); variants.sort_unstable(); - let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), variants)]; + let mut items = vec![LinkBlock::new(Link::new("variants", "Variants"), "variant", variants)]; sidebar_assoc_items(cx, it, &mut items); items } @@ -467,7 +475,7 @@ pub(crate) fn sidebar_module_like( .filter(|sec| item_sections_in_use.contains(sec)) .map(|sec| Link::new(sec.id(), sec.name())) .collect(); - LinkBlock::new(Link::empty(), item_sections) + LinkBlock::new(Link::empty(), "", item_sections) } fn sidebar_module(items: &[clean::Item]) -> LinkBlock<'static> { @@ -528,12 +536,21 @@ fn sidebar_render_assoc_items( let synthetic = format_impls(synthetic, id_map); let blanket = format_impls(blanket_impl, id_map); [ - LinkBlock::new(Link::new("trait-implementations", "Trait Implementations"), concrete), + LinkBlock::new( + Link::new("trait-implementations", "Trait Implementations"), + "trait-implementation", + concrete, + ), LinkBlock::new( Link::new("synthetic-implementations", "Auto Trait Implementations"), + "synthetic-implementation", synthetic, ), - LinkBlock::new(Link::new("blanket-implementations", "Blanket Implementations"), blanket), + LinkBlock::new( + Link::new("blanket-implementations", "Blanket Implementations"), + "blanket-implementation", + blanket, + ), ] } diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index b162ea99d8f21..4054281f34681 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -5,18 +5,28 @@ use std::io::{self, BufReader}; use std::path::{Component, Path}; use std::rc::{Rc, Weak}; +use indexmap::IndexMap; use itertools::Itertools; use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams}; +use rustc_span::def_id::DefId; +use rustc_span::Symbol; use serde::ser::SerializeSeq; use serde::{Serialize, Serializer}; use super::{collect_paths_for_type, ensure_trailing_slash, Context}; -use crate::clean::Crate; +use crate::clean::{Crate, Item, ItemId, ItemKind}; use crate::config::{EmitType, RenderOptions}; use crate::docfs::PathError; use crate::error::Error; +use crate::formats::cache::Cache; +use crate::formats::item_type::ItemType; +use crate::formats::{Impl, RenderMode}; +use crate::html::format::Buffer; +use crate::html::render::{AssocItemLink, ImplRenderingParameters}; use crate::html::{layout, static_files}; +use crate::visit::DocVisitor; use crate::{try_err, try_none}; /// Rustdoc writes out two kinds of shared files: @@ -361,9 +371,247 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; } } + let cloned_shared = Rc::clone(&cx.shared); + let cache = &cloned_shared.cache; + + // Collect the list of aliased types and their aliases. + // + // + // The clean AST has type aliases that point at their types, but + // this visitor works to reverse that: `aliased_types` is a map + // from target to the aliases that reference it, and each one + // will generate one file. + struct TypeImplCollector<'cx, 'cache> { + // Map from DefId-of-aliased-type to its data. + aliased_types: IndexMap>, + visited_aliases: FxHashSet, + cache: &'cache Cache, + cx: &'cache mut Context<'cx>, + } + // Data for an aliased type. + // + // In the final file, the format will be roughly: + // + // ```json + // // type.impl/CRATE/TYPENAME.js + // JSONP( + // "CRATE": [ + // ["IMPL1 HTML", "ALIAS1", "ALIAS2", ...], + // ["IMPL2 HTML", "ALIAS3", "ALIAS4", ...], + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ struct AliasedType + // ... + // ] + // ) + // ``` + struct AliasedType<'cache> { + // This is used to generate the actual filename of this aliased type. + target_fqp: &'cache [Symbol], + target_type: ItemType, + // This is the data stored inside the file. + // ItemId is used to deduplicate impls. + impl_: IndexMap>, + } + // The `impl_` contains data that's used to figure out if an alias will work, + // and to generate the HTML at the end. + // + // The `type_aliases` list is built up with each type alias that matches. + struct AliasedTypeImpl<'cache> { + impl_: &'cache Impl, + type_aliases: Vec<(&'cache [Symbol], Item)>, + } + impl<'cx, 'cache> DocVisitor for TypeImplCollector<'cx, 'cache> { + fn visit_item(&mut self, it: &Item) { + self.visit_item_recur(it); + let cache = self.cache; + let ItemKind::TypeAliasItem(ref t) = *it.kind else { return }; + let Some(self_did) = it.item_id.as_def_id() else { return }; + if !self.visited_aliases.insert(self_did) { + return; + } + let Some(target_did) = t.type_.def_id(cache) else { return }; + let get_extern = { || cache.external_paths.get(&target_did) }; + let Some(&(ref target_fqp, target_type)) = + cache.paths.get(&target_did).or_else(get_extern) + else { + return; + }; + let aliased_type = self.aliased_types.entry(target_did).or_insert_with(|| { + let impl_ = cache + .impls + .get(&target_did) + .map(|v| &v[..]) + .unwrap_or_default() + .iter() + .map(|impl_| { + ( + impl_.impl_item.item_id, + AliasedTypeImpl { impl_, type_aliases: Vec::new() }, + ) + }) + .collect(); + AliasedType { target_fqp: &target_fqp[..], target_type, impl_ } + }); + let get_local = { || cache.paths.get(&self_did).map(|(p, _)| p) }; + let Some(self_fqp) = cache.exact_paths.get(&self_did).or_else(get_local) else { + return; + }; + let aliased_ty = self.cx.tcx().type_of(self_did).skip_binder(); + // Exclude impls that are directly on this type. They're already in the HTML. + // Some inlining scenarios can cause there to be two versions of the same + // impl: one on the type alias and one on the underlying target type. + let mut seen_impls: FxHashSet = cache + .impls + .get(&self_did) + .map(|s| &s[..]) + .unwrap_or_default() + .iter() + .map(|i| i.impl_item.item_id) + .collect(); + for (impl_item_id, aliased_type_impl) in &mut aliased_type.impl_ { + // Only include this impl if it actually unifies with this alias. + // Synthetic impls are not included; those are also included in the HTML. + // + // FIXME(lazy_type_alias): Once the feature is complete or stable, rewrite this + // to use type unification. + // Be aware of `tests/rustdoc/type-alias/deeply-nested-112515.rs` which might regress. + let Some(impl_did) = impl_item_id.as_def_id() else { continue }; + let for_ty = self.cx.tcx().type_of(impl_did).skip_binder(); + let reject_cx = + DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey }; + if !reject_cx.types_may_unify(aliased_ty, for_ty) { + continue; + } + // Avoid duplicates + if !seen_impls.insert(*impl_item_id) { + continue; + } + // This impl was not found in the set of rejected impls + aliased_type_impl.type_aliases.push((&self_fqp[..], it.clone())); + } + } + } + let mut type_impl_collector = TypeImplCollector { + aliased_types: IndexMap::default(), + visited_aliases: FxHashSet::default(), + cache, + cx, + }; + DocVisitor::visit_crate(&mut type_impl_collector, &krate); + // Final serialized form of the alias impl + struct AliasSerializableImpl { + text: String, + trait_: Option, + aliases: Vec, + } + impl Serialize for AliasSerializableImpl { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(None)?; + seq.serialize_element(&self.text)?; + if let Some(trait_) = &self.trait_ { + seq.serialize_element(trait_)?; + } else { + seq.serialize_element(&0)?; + } + for type_ in &self.aliases { + seq.serialize_element(type_)?; + } + seq.end() + } + } + let cx = type_impl_collector.cx; + let dst = cx.dst.join("type.impl"); + let aliased_types = type_impl_collector.aliased_types; + for aliased_type in aliased_types.values() { + let impls = aliased_type + .impl_ + .values() + .flat_map(|AliasedTypeImpl { impl_, type_aliases }| { + let mut ret = Vec::new(); + let trait_ = impl_.inner_impl().trait_.as_ref().map(|path| path.last().to_string()); + // render_impl will filter out "impossible-to-call" methods + // to make that functionality work here, it needs to be called with + // each type alias, and if it gives a different result, split the impl + for &(type_alias_fqp, ref type_alias_item) in type_aliases { + let mut buf = Buffer::html(); + cx.id_map = Default::default(); + cx.deref_id_map = Default::default(); + super::render_impl( + &mut buf, + cx, + *impl_, + &type_alias_item, + AssocItemLink::Anchor(None), + RenderMode::Normal, + None, + &[], + ImplRenderingParameters { + show_def_docs: true, + show_default_items: true, + show_non_assoc_items: true, + toggle_open_by_default: true, + }, + ); + let text = buf.into_inner(); + let type_alias_fqp = (*type_alias_fqp).iter().join("::"); + if Some(&text) == ret.last().map(|s: &AliasSerializableImpl| &s.text) { + ret.last_mut() + .expect("already established that ret.last() is Some()") + .aliases + .push(type_alias_fqp); + } else { + ret.push(AliasSerializableImpl { + text, + trait_: trait_.clone(), + aliases: vec![type_alias_fqp], + }) + } + } + ret + }) + .collect::>(); + let impls = format!( + r#""{}":{}"#, + krate.name(cx.tcx()), + serde_json::to_string(&impls).expect("failed serde conversion"), + ); + + let mut mydst = dst.clone(); + for part in &aliased_type.target_fqp[..aliased_type.target_fqp.len() - 1] { + mydst.push(part.to_string()); + } + cx.shared.ensure_dir(&mydst)?; + let aliased_item_type = aliased_type.target_type; + mydst.push(&format!( + "{aliased_item_type}.{}.js", + aliased_type.target_fqp[aliased_type.target_fqp.len() - 1] + )); + + let (mut all_impls, _) = try_err!(collect(&mydst, krate.name(cx.tcx()).as_str()), &mydst); + all_impls.push(impls); + // Sort the implementors by crate so the file will be generated + // identically even with rustdoc running in parallel. + all_impls.sort(); + + let mut v = String::from("(function() {var type_impls = {\n"); + v.push_str(&all_impls.join(",\n")); + v.push_str("\n};"); + v.push_str( + "if (window.register_type_impls) {\ + window.register_type_impls(type_impls);\ + } else {\ + window.pending_type_impls = type_impls;\ + }", + ); + v.push_str("})()"); + cx.shared.fs.write(mydst, v)?; + } + // Update the list of all implementors for traits + // let dst = cx.dst.join("trait.impl"); - let cache = cx.cache(); for (&did, imps) in &cache.implementors { // Private modules can leak through to this phase of rustdoc, which // could contain implementations for otherwise private types. In some diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 2e9897ef82ba2..6cd57c8c598ee 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -549,6 +549,7 @@ function preLoadCss(cssUrl) { } } + // window.register_implementors = imp => { const implementors = document.getElementById("implementors-list"); const synthetic_implementors = document.getElementById("synthetic-implementors-list"); @@ -615,7 +616,7 @@ function preLoadCss(cssUrl) { onEachLazy(code.getElementsByTagName("a"), elem => { const href = elem.getAttribute("href"); - if (href && !/^(?:[a-z+]+:)?\/\//.test(href)) { + if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) { elem.setAttribute("href", window.rootPath + href); } }); @@ -639,6 +640,177 @@ function preLoadCss(cssUrl) { window.register_implementors(window.pending_implementors); } + // + window.register_type_impls = imp => { + if (!imp || !imp[window.currentCrate]) { + return; + } + window.pending_type_impls = null; + const idMap = new Map(); + + let implementations = document.getElementById("implementations-list"); + let trait_implementations = document.getElementById("trait-implementations-list"); + + // We want to include the current type alias's impls, and no others. + const script = document.querySelector("script[data-self-path]"); + const selfPath = script ? script.getAttribute("data-self-path") : null; + + // These sidebar blocks need filled in, too. + const sidebarSection = document.querySelector(".sidebar section"); + let methods = document.querySelector(".sidebar .block.method"); + let associatedTypes = document.querySelector(".sidebar .block.associatedtype"); + let associatedConstants = document.querySelector(".sidebar .block.associatedconstant"); + let sidebarTraitList = document.querySelector(".sidebar .block.trait-implementation"); + + for (const impList of imp[window.currentCrate]) { + const types = impList.slice(2); + const text = impList[0]; + const isTrait = impList[1] !== 0; + const traitName = impList[1]; + if (types.indexOf(selfPath) === -1) { + continue; + } + let outputList = isTrait ? trait_implementations : implementations; + if (outputList === null) { + const outputListName = isTrait ? "Trait Implementations" : "Implementations"; + const outputListId = isTrait ? + "trait-implementations-list" : + "implementations-list"; + const outputListHeaderId = isTrait ? "trait-implementations" : "implementations"; + const outputListH = document.createElement("h2"); + outputListH.id = outputListHeaderId; + outputListH.innerText = outputListName; + outputList = document.createElement("div"); + outputList.id = outputListId; + if (isTrait) { + const link = document.createElement("a"); + link.href = `#${outputListHeaderId}`; + link.innerText = "Trait Implementations"; + const h = document.createElement("h3"); + h.appendChild(link); + trait_implementations = outputList; + sidebarSection.appendChild(h); + sidebarTraitList = document.createElement("ul"); + sidebarTraitList.className = "block trait-implementation"; + sidebarSection.appendChild(sidebarTraitList); + const mainContent = document.querySelector("#main-content"); + mainContent.appendChild(outputListH); + mainContent.appendChild(outputList); + } else { + implementations = outputList; + if (trait_implementations) { + document.insertBefore(outputListH, trait_implementations); + document.insertBefore(outputList, trait_implementations); + } else { + const mainContent = document.querySelector("#main-content"); + mainContent.appendChild(outputListH); + mainContent.appendChild(outputList); + } + } + } + const template = document.createElement("template"); + template.innerHTML = text; + + onEachLazy(template.content.querySelectorAll("a"), elem => { + const href = elem.getAttribute("href"); + + if (href && !href.startsWith("#") && !/^(?:[a-z+]+:)?\/\//.test(href)) { + elem.setAttribute("href", window.rootPath + href); + } + }); + onEachLazy(template.content.querySelectorAll("[id]"), el => { + let i = 0; + if (idMap.has(el.id)) { + i = idMap.get(el.id); + } else if (document.getElementById(el.id)) { + i = 1; + while (document.getElementById(`${el.id}-${2 * i}`)) { + i = 2 * i; + } + while (document.getElementById(`${el.id}-${i}`)) { + i += 1; + } + } + if (i !== 0) { + el.id = `${el.id}-${i}`; + } + idMap.set(el.id, i + 1); + }); + const templateAssocItems = template.content.querySelectorAll("section.tymethod, " + + "section.method, section.associatedtype, section.associatedconstant"); + if (isTrait) { + const li = document.createElement("li"); + const a = document.createElement("a"); + a.href = `#${template.content.querySelector(".impl").id}`; + a.textContent = traitName; + li.appendChild(a); + sidebarTraitList.append(li); + } else { + onEachLazy(templateAssocItems, item => { + let block = hasClass(item, "associatedtype") ? associatedTypes : ( + hasClass(item, "associatedconstant") ? associatedConstants : ( + methods)); + if (!block) { + const blockTitle = hasClass(item, "associatedtype") ? "Associated Types" : ( + hasClass(item, "associatedconstant") ? "Associated Constants" : ( + "Methods")); + const blockClass = hasClass(item, "associatedtype") ? "associatedtype" : ( + hasClass(item, "associatedconstant") ? "associatedconstant" : ( + "method")); + const blockH = document.createElement("h3"); + const blockA = document.createElement("a"); + blockA.href = "#implementations"; + blockA.innerText = blockTitle; + blockH.appendChild(blockA); + block = document.createElement("ul"); + block.className = `block ${blockClass}`; + const insertionReference = methods || sidebarTraitList; + if (insertionReference) { + const insertionReferenceH = insertionReference.previousElementSibling; + sidebarSection.insertBefore(blockH, insertionReferenceH); + sidebarSection.insertBefore(block, insertionReferenceH); + } else { + sidebarSection.appendChild(blockH); + sidebarSection.appendChild(block); + } + if (hasClass(item, "associatedtype")) { + associatedTypes = block; + } else if (hasClass(item, "associatedconstant")) { + associatedConstants = block; + } else { + methods = block; + } + } + const li = document.createElement("li"); + const a = document.createElement("a"); + a.innerText = item.id.split("-")[0].split(".")[1]; + a.href = `#${item.id}`; + li.appendChild(a); + block.appendChild(li); + }); + } + outputList.appendChild(template.content); + } + + for (const list of [methods, associatedTypes, associatedConstants, sidebarTraitList]) { + if (!list) { + continue; + } + const newChildren = Array.prototype.slice.call(list.children); + newChildren.sort((a, b) => { + const aI = a.innerText; + const bI = b.innerText; + return aI < bI ? -1 : + aI > bI ? 1 : + 0; + }); + list.replaceChildren(...newChildren); + } + }; + if (window.pending_type_impls) { + window.register_type_impls(window.pending_type_impls); + } + function addSidebarCrates() { if (!window.ALL_CRATES) { return; diff --git a/src/librustdoc/html/templates/sidebar.html b/src/librustdoc/html/templates/sidebar.html index a99198141e281..4318d680276cc 100644 --- a/src/librustdoc/html/templates/sidebar.html +++ b/src/librustdoc/html/templates/sidebar.html @@ -18,7 +18,11 @@

{# #}

{% endif %} {% if !block.links.is_empty() %} -

{{block.heading.name}}