diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs index 554b81b14cd3d..725eca3989c48 100644 --- a/src/librustdoc/clean/auto_trait.rs +++ b/src/librustdoc/clean/auto_trait.rs @@ -112,6 +112,7 @@ fn synthesize_auto_trait_impl<'tcx>( } auto_trait::AutoTraitResult::ExplicitImpl => return None, }; + cx.generated_auto_trait_impls.insert((ty, trait_def_id), polarity); Some(clean::Item { inner: Box::new(clean::ItemInner { diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs index 8dda831cac85b..6756b1b327278 100644 --- a/src/librustdoc/clean/blanket_impl.rs +++ b/src/librustdoc/clean/blanket_impl.rs @@ -2,7 +2,7 @@ use rustc_data_structures::thin_vec::ThinVec; use rustc_hir as hir; use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TyCtxtInferExt}; use rustc_infer::traits; -use rustc_middle::ty::{self, TypingMode, Unnormalized, Upcast}; +use rustc_middle::ty::{self, Ty, TypingMode, Unnormalized}; use rustc_span::DUMMY_SP; use rustc_span::def_id::DefId; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -21,11 +21,12 @@ pub(crate) fn synthesize_blanket_impls( ) -> Vec { let tcx = cx.tcx; let ty = tcx.type_of(item_def_id); + let item_ty = ty.instantiate_identity().skip_norm_wip(); let mut blanket_impls = Vec::new(); for trait_def_id in tcx.visible_traits() { if !cx.cache.effective_visibilities.is_reachable(tcx, trait_def_id) - || cx.generated_synthetics.contains(&(ty.skip_binder(), trait_def_id)) + || cx.generated_synthetics.contains(&(item_ty, trait_def_id)) { continue; } @@ -59,14 +60,26 @@ pub(crate) fn synthesize_blanket_impls( // FIXME(eddyb) ignoring `obligations` might cause false positives. drop(obligations); + // Only the impl's where-predicates need to be checked here. The trait ref itself is + // the blanket impl candidate we're considering; proving it would select recursively. let predicates = tcx .predicates_of(impl_def_id) .instantiate(tcx, impl_args) .predicates .into_iter() - .map(Unnormalized::skip_norm_wip) - .chain(Some(impl_trait_ref.upcast(tcx))); + .map(Unnormalized::skip_norm_wip); for predicate in predicates { + let predicate = infcx.resolve_vars_if_possible(predicate); + if let Some(may_apply) = + cached_auto_trait_predicate_result(cx, predicate, impl_ty, item_ty) + { + if may_apply { + continue; + } else { + continue 'blanket_impls; + } + } + let obligation = traits::Obligation::new( tcx, traits::ObligationCause::dummy(), @@ -81,7 +94,7 @@ pub(crate) fn synthesize_blanket_impls( } debug!("found applicable impl for trait ref {trait_ref:?}"); - cx.generated_synthetics.insert((ty.skip_binder(), trait_def_id)); + cx.generated_synthetics.insert((item_ty, trait_def_id)); blanket_impls.push(clean::Item { inner: Box::new(clean::ItemInner { @@ -133,3 +146,31 @@ pub(crate) fn synthesize_blanket_impls( blanket_impls } + +fn cached_auto_trait_predicate_result<'tcx>( + cx: &DocContext<'tcx>, + predicate: ty::Clause<'tcx>, + impl_ty: Ty<'tcx>, + item_ty: Ty<'tcx>, +) -> Option { + let trait_pred = predicate.as_trait_clause()?; + if trait_pred.polarity() != ty::PredicatePolarity::Positive { + return None; + } + + let trait_def_id = trait_pred.def_id(); + if !cx.tcx.trait_is_auto(trait_def_id) { + return None; + } + + let self_ty = trait_pred.self_ty().no_bound_vars()?; + if self_ty != impl_ty { + return None; + } + + match cx.generated_auto_trait_impls.get(&(item_ty, trait_def_id)) { + Some(ty::ImplPolarity::Positive) => Some(true), + Some(ty::ImplPolarity::Negative) => Some(false), + _ => None, + } +} diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 21ce508d8560c..bf42fdcdef7dd 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -59,6 +59,8 @@ pub(crate) struct DocContext<'tcx> { /// Auto-trait or blanket impls processed so far, as `(self_ty, trait_def_id)`. // FIXME(eddyb) make this a `ty::TraitRef<'tcx>` set. pub(crate) generated_synthetics: FxHashSet<(Ty<'tcx>, DefId)>, + /// Polarity of synthesized auto-trait impls processed so far. + pub(crate) generated_auto_trait_impls: FxHashMap<(Ty<'tcx>, DefId), ty::ImplPolarity>, pub(crate) auto_traits: Vec, /// This same cache is used throughout rustdoc, including in [`crate::html::render`]. pub(crate) cache: Cache, @@ -370,6 +372,7 @@ pub(crate) fn run_global_ctxt( current_type_aliases: Default::default(), impl_trait_bounds: Default::default(), generated_synthetics: Default::default(), + generated_auto_trait_impls: Default::default(), auto_traits, cache: Cache::new(render_options.document_private, render_options.document_hidden), inlined: FxHashSet::default(), diff --git a/tests/rustdoc-html/blanket-impl-recursive-auto-traits-issue-155759.rs b/tests/rustdoc-html/blanket-impl-recursive-auto-traits-issue-155759.rs new file mode 100644 index 0000000000000..1482f3b6c72e5 --- /dev/null +++ b/tests/rustdoc-html/blanket-impl-recursive-auto-traits-issue-155759.rs @@ -0,0 +1,268 @@ +#![crate_name = "foo"] + +//@ has 'foo/struct.Class.html' +//@ has - '//*[@id="impl-ThreadSafe-for-T"]' 'impl ThreadSafe for T' +pub struct Class { + pub body: std::sync::Arc<[stmt::Statement]>, + pub decorators: std::sync::Arc<[expr::Expression]>, + pub span: Span, +} + +pub trait ThreadSafe: Send + Sync {} + +// With this line commented out, the slow behavior does not occur. +impl ThreadSafe for T {} + +mod stmt { + pub struct S00(pub Statement); + pub struct S01(pub Statement); + pub struct S02(pub Statement); + pub struct S03(pub Statement); + pub struct S04(pub Statement); + pub struct S05(pub Statement); + pub struct S06(pub Statement); + pub struct S07(pub Statement); + pub struct S08(pub Statement); + pub struct S09(pub Statement); + pub struct S10(pub Statement); + pub struct S11(pub Statement); + pub struct S12(pub Statement); + pub struct S13(pub Statement); + pub struct S14(pub Statement); + pub struct S15(pub Statement); + pub struct S16(pub Statement); + pub struct S17(pub Statement); + pub struct S18(pub Statement); + pub struct S19(pub Statement); + pub struct S20(pub Statement); + pub struct S21(pub Statement); + pub struct S22(pub Statement); + pub struct S23(pub Statement); + pub struct S24(pub Statement); + pub struct S25(pub Statement); + pub struct S26(pub Statement); + pub struct S27(pub Statement); + pub struct S28(pub Statement); + pub struct S29(pub Statement); + pub struct S30(pub Statement); + pub struct S31(pub Statement); + pub struct S32(pub Statement); + pub struct S33(pub Statement); + pub struct S34(pub Statement); + pub struct S35(pub Statement); + pub struct S36(pub Statement); + pub struct S37(pub Statement); + pub struct S38(pub Statement); + pub struct S39(pub Statement); + pub struct S40(pub Statement); + pub struct S41(pub Statement); + pub struct S42(pub Statement); + pub struct S43(pub Statement); + pub struct S44(pub Statement); + pub struct S45(pub Statement); + pub struct S46(pub Statement); + pub struct S47(pub Statement); + pub struct S48(pub Statement); + pub struct S49(pub Statement); + pub struct S50(pub Statement); + pub struct S51(pub Statement); + pub struct S52(pub Statement); + pub struct S53(pub Statement); + pub struct S54(pub Statement); + pub struct S55(pub Statement); + pub struct S56(pub Statement); + pub struct S57(pub Statement); + pub struct S58(pub Statement); + pub struct S59(pub Statement); + + pub enum Statement { + Class(std::sync::Arc>), + S00(std::sync::Arc>), + S01(std::sync::Arc>), + S02(std::sync::Arc>), + S03(std::sync::Arc>), + S04(std::sync::Arc>), + S05(std::sync::Arc>), + S06(std::sync::Arc>), + S07(std::sync::Arc>), + S08(std::sync::Arc>), + S09(std::sync::Arc>), + S10(std::sync::Arc>), + S11(std::sync::Arc>), + S12(std::sync::Arc>), + S13(std::sync::Arc>), + S14(std::sync::Arc>), + S15(std::sync::Arc>), + S16(std::sync::Arc>), + S17(std::sync::Arc>), + S18(std::sync::Arc>), + S19(std::sync::Arc>), + S20(std::sync::Arc>), + S21(std::sync::Arc>), + S22(std::sync::Arc>), + S23(std::sync::Arc>), + S24(std::sync::Arc>), + S25(std::sync::Arc>), + S26(std::sync::Arc>), + S27(std::sync::Arc>), + S28(std::sync::Arc>), + S29(std::sync::Arc>), + S30(std::sync::Arc>), + S31(std::sync::Arc>), + S32(std::sync::Arc>), + S33(std::sync::Arc>), + S34(std::sync::Arc>), + S35(std::sync::Arc>), + S36(std::sync::Arc>), + S37(std::sync::Arc>), + S38(std::sync::Arc>), + S39(std::sync::Arc>), + S40(std::sync::Arc>), + S41(std::sync::Arc>), + S42(std::sync::Arc>), + S43(std::sync::Arc>), + S44(std::sync::Arc>), + S45(std::sync::Arc>), + S46(std::sync::Arc>), + S47(std::sync::Arc>), + S48(std::sync::Arc>), + S49(std::sync::Arc>), + S50(std::sync::Arc>), + S51(std::sync::Arc>), + S52(std::sync::Arc>), + S53(std::sync::Arc>), + S54(std::sync::Arc>), + S55(std::sync::Arc>), + S56(std::sync::Arc>), + S57(std::sync::Arc>), + S58(std::sync::Arc>), + S59(std::sync::Arc>), + } +} + +mod expr { + pub struct E00(pub Expression); + pub struct E01(pub Expression); + pub struct E02(pub Expression); + pub struct E03(pub Expression); + pub struct E04(pub Expression); + pub struct E05(pub Expression); + pub struct E06(pub Expression); + pub struct E07(pub Expression); + pub struct E08(pub Expression); + pub struct E09(pub Expression); + pub struct E10(pub Expression); + pub struct E11(pub Expression); + pub struct E12(pub Expression); + pub struct E13(pub Expression); + pub struct E14(pub Expression); + pub struct E15(pub Expression); + pub struct E16(pub Expression); + pub struct E17(pub Expression); + pub struct E18(pub Expression); + pub struct E19(pub Expression); + pub struct E20(pub Expression); + pub struct E21(pub Expression); + pub struct E22(pub Expression); + pub struct E23(pub Expression); + pub struct E24(pub Expression); + pub struct E25(pub Expression); + pub struct E26(pub Expression); + pub struct E27(pub Expression); + pub struct E28(pub Expression); + pub struct E29(pub Expression); + pub struct E30(pub Expression); + pub struct E31(pub Expression); + pub struct E32(pub Expression); + pub struct E33(pub Expression); + pub struct E34(pub Expression); + pub struct E35(pub Expression); + pub struct E36(pub Expression); + pub struct E37(pub Expression); + pub struct E38(pub Expression); + pub struct E39(pub Expression); + pub struct E40(pub Expression); + pub struct E41(pub Expression); + pub struct E42(pub Expression); + pub struct E43(pub Expression); + pub struct E44(pub Expression); + pub struct E45(pub Expression); + pub struct E46(pub Expression); + pub struct E47(pub Expression); + pub struct E48(pub Expression); + pub struct E49(pub Expression); + pub struct E50(pub Expression); + pub struct E51(pub Expression); + pub struct E52(pub Expression); + pub struct E53(pub Expression); + pub struct E54(pub Expression); + pub struct E55(pub Expression); + pub struct E56(pub Expression); + pub struct E57(pub Expression); + pub struct E58(pub Expression); + pub struct E59(pub Expression); + + pub enum Expression { + Class(std::sync::Arc>), + E00(std::sync::Arc>), + E01(std::sync::Arc>), + E02(std::sync::Arc>), + E03(std::sync::Arc>), + E04(std::sync::Arc>), + E05(std::sync::Arc>), + E06(std::sync::Arc>), + E07(std::sync::Arc>), + E08(std::sync::Arc>), + E09(std::sync::Arc>), + E10(std::sync::Arc>), + E11(std::sync::Arc>), + E12(std::sync::Arc>), + E13(std::sync::Arc>), + E14(std::sync::Arc>), + E15(std::sync::Arc>), + E16(std::sync::Arc>), + E17(std::sync::Arc>), + E18(std::sync::Arc>), + E19(std::sync::Arc>), + E20(std::sync::Arc>), + E21(std::sync::Arc>), + E22(std::sync::Arc>), + E23(std::sync::Arc>), + E24(std::sync::Arc>), + E25(std::sync::Arc>), + E26(std::sync::Arc>), + E27(std::sync::Arc>), + E28(std::sync::Arc>), + E29(std::sync::Arc>), + E30(std::sync::Arc>), + E31(std::sync::Arc>), + E32(std::sync::Arc>), + E33(std::sync::Arc>), + E34(std::sync::Arc>), + E35(std::sync::Arc>), + E36(std::sync::Arc>), + E37(std::sync::Arc>), + E38(std::sync::Arc>), + E39(std::sync::Arc>), + E40(std::sync::Arc>), + E41(std::sync::Arc>), + E42(std::sync::Arc>), + E43(std::sync::Arc>), + E44(std::sync::Arc>), + E45(std::sync::Arc>), + E46(std::sync::Arc>), + E47(std::sync::Arc>), + E48(std::sync::Arc>), + E49(std::sync::Arc>), + E50(std::sync::Arc>), + E51(std::sync::Arc>), + E52(std::sync::Arc>), + E53(std::sync::Arc>), + E54(std::sync::Arc>), + E55(std::sync::Arc>), + E56(std::sync::Arc>), + E57(std::sync::Arc>), + E58(std::sync::Arc>), + E59(std::sync::Arc>), + } +} diff --git a/tests/rustdoc-js/never-search.js b/tests/rustdoc-js/never-search.js index 9cc62a5ed04a5..75d7485bdadf2 100644 --- a/tests/rustdoc-js/never-search.js +++ b/tests/rustdoc-js/never-search.js @@ -4,6 +4,7 @@ const EXPECTED = [ { 'query': '! ->', 'others': [ + { 'path': 'never_search::never', 'name': 'from' }, { 'path': 'never_search', 'name': 'impossible' }, { 'path': 'never_search', 'name': 'box_impossible' }, ], @@ -24,6 +25,7 @@ const EXPECTED = [ { 'query': '!', 'in_args': [ + { 'path': 'never_search::never', 'name': 'from' }, { 'path': 'never_search', 'name': 'impossible' }, { 'path': 'never_search', 'name': 'box_impossible' }, ], @@ -31,6 +33,7 @@ const EXPECTED = [ { 'query': 'never', 'in_args': [ + { 'path': 'never_search::never', 'name': 'from' }, { 'path': 'never_search', 'name': 'impossible' }, { 'path': 'never_search', 'name': 'uninteresting' }, { 'path': 'never_search', 'name': 'box_impossible' },