Skip to content

Commit 1dae8fc

Browse files
committed
Auto merge of #155801 - fmease:rustdoc-synth-blanket-impl-rewrite, r=<try>
[perf] rustdoc: Rewrite blanket impl synthesis
2 parents 9838411 + f14760e commit 1dae8fc

1 file changed

Lines changed: 101 additions & 108 deletions

File tree

Lines changed: 101 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,135 +1,128 @@
1+
use std::ops::ControlFlow;
2+
13
use rustc_data_structures::thin_vec::ThinVec;
24
use rustc_hir as hir;
3-
use rustc_infer::infer::{DefineOpaqueTypes, InferOk, TyCtxtInferExt};
4-
use rustc_infer::traits;
5-
use rustc_middle::ty::{self, TypingMode, Unnormalized, Upcast};
6-
use rustc_span::DUMMY_SP;
5+
use rustc_infer::infer::TyCtxtInferExt;
6+
use rustc_middle::ty;
77
use rustc_span::def_id::DefId;
8-
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
9-
use tracing::{debug, instrument, trace};
8+
use rustc_trait_selection::solve::CandidateSource;
9+
use rustc_trait_selection::solve::inspect::{
10+
InferCtxtProofTreeExt as _, InspectGoal, ProbeKind, ProofTreeVisitor,
11+
};
1012

1113
use crate::clean;
1214
use crate::clean::{
1315
clean_middle_assoc_item, clean_middle_ty, clean_trait_ref_with_constraints, clean_ty_generics,
1416
};
1517
use crate::core::DocContext;
1618

17-
#[instrument(level = "debug", skip(cx))]
19+
#[tracing::instrument(level = "debug", skip(cx))]
1820
pub(crate) fn synthesize_blanket_impls(
1921
cx: &mut DocContext<'_>,
2022
item_def_id: DefId,
2123
) -> Vec<clean::Item> {
2224
let tcx = cx.tcx;
23-
let ty = tcx.type_of(item_def_id);
25+
let item_ty = tcx.type_of(item_def_id);
26+
let param_env = ty::ParamEnv::empty();
27+
let typing_mode = ty::TypingMode::non_body_analysis();
2428

2529
let mut blanket_impls = Vec::new();
30+
2631
for trait_def_id in tcx.visible_traits() {
2732
if !cx.cache.effective_visibilities.is_reachable(tcx, trait_def_id)
28-
|| cx.generated_synthetics.contains(&(ty.skip_binder(), trait_def_id))
33+
|| cx.generated_synthetics.contains(&(item_ty.skip_binder(), trait_def_id))
2934
{
3035
continue;
3136
}
32-
// NOTE: doesn't use `for_each_relevant_impl` to avoid looking at anything besides blanket impls
33-
let trait_impls = tcx.trait_impls_of(trait_def_id);
34-
'blanket_impls: for &impl_def_id in trait_impls.blanket_impls() {
35-
trace!("considering impl `{impl_def_id:?}` for trait `{trait_def_id:?}`");
36-
37-
let trait_ref = tcx.impl_trait_ref(impl_def_id);
38-
if !matches!(trait_ref.skip_binder().self_ty().kind(), ty::Param(_)) {
39-
continue;
40-
}
41-
let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
42-
let args = infcx.fresh_args_for_item(DUMMY_SP, item_def_id);
43-
let impl_ty = ty.instantiate(tcx, args).skip_norm_wip();
44-
let param_env = ty::ParamEnv::empty();
45-
46-
let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
47-
let impl_trait_ref = trait_ref.instantiate(tcx, impl_args).skip_norm_wip();
48-
49-
// Require the type the impl is implemented on to match
50-
// our type, and ignore the impl if there was a mismatch.
51-
let Ok(eq_result) = infcx.at(&traits::ObligationCause::dummy(), param_env).eq(
52-
DefineOpaqueTypes::Yes,
53-
impl_trait_ref.self_ty(),
54-
impl_ty,
55-
) else {
56-
continue;
57-
};
58-
let InferOk { value: (), obligations } = eq_result;
59-
// FIXME(eddyb) ignoring `obligations` might cause false positives.
60-
drop(obligations);
61-
62-
let predicates = tcx
63-
.predicates_of(impl_def_id)
64-
.instantiate(tcx, impl_args)
65-
.predicates
66-
.into_iter()
67-
.map(Unnormalized::skip_norm_wip)
68-
.chain(Some(impl_trait_ref.upcast(tcx)));
69-
for predicate in predicates {
70-
let obligation = traits::Obligation::new(
71-
tcx,
72-
traits::ObligationCause::dummy(),
73-
param_env,
74-
predicate,
75-
);
76-
match infcx.evaluate_obligation(&obligation) {
77-
Ok(eval_result) if eval_result.may_apply() => {}
78-
Err(traits::OverflowError::Canonical) => {}
79-
_ => continue 'blanket_impls,
80-
}
81-
}
82-
debug!("found applicable impl for trait ref {trait_ref:?}");
83-
84-
cx.generated_synthetics.insert((ty.skip_binder(), trait_def_id));
85-
86-
blanket_impls.push(clean::Item {
87-
inner: Box::new(clean::ItemInner {
88-
name: None,
89-
item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id },
90-
attrs: Default::default(),
91-
stability: None,
92-
kind: clean::ImplItem(Box::new(clean::Impl {
93-
safety: hir::Safety::Safe,
94-
generics: clean_ty_generics(cx, impl_def_id),
95-
// FIXME(eddyb) compute both `trait_` and `for_` from
96-
// the post-inference `trait_ref`, as it's more accurate.
97-
trait_: Some(clean_trait_ref_with_constraints(
98-
cx,
99-
ty::Binder::dummy(trait_ref.instantiate_identity().skip_norm_wip()),
100-
ThinVec::new(),
101-
)),
102-
for_: clean_middle_ty(
103-
ty::Binder::dummy(ty.instantiate_identity().skip_norm_wip()),
104-
cx,
105-
None,
106-
None,
107-
),
108-
items: tcx
109-
.associated_items(impl_def_id)
110-
.in_definition_order()
111-
.filter(|item| !item.is_impl_trait_in_trait())
112-
.map(|item| clean_middle_assoc_item(item, cx))
113-
.collect(),
114-
polarity: ty::ImplPolarity::Positive,
115-
kind: clean::ImplKind::Blanket(Box::new(clean_middle_ty(
116-
ty::Binder::dummy(
117-
trait_ref.instantiate_identity().skip_norm_wip().self_ty(),
118-
),
119-
cx,
120-
None,
121-
None,
122-
))),
123-
is_deprecated: tcx
124-
.lookup_deprecation(impl_def_id)
125-
.is_some_and(|deprecation| deprecation.is_in_effect()),
126-
})),
127-
cfg: None,
128-
inline_stmt_id: None,
129-
}),
130-
});
37+
38+
// FIXME(fmease): ...
39+
if tcx.is_lang_item(trait_def_id, hir::LangItem::PointeeSized) {
40+
continue;
13141
}
42+
43+
let infcx = tcx.infer_ctxt().with_next_trait_solver(true).build(typing_mode);
44+
45+
let item_args = infcx.fresh_args_for_item(rustc_span::DUMMY_SP, item_def_id);
46+
let self_ty = item_ty.instantiate(tcx, item_args).skip_normalization();
47+
let trait_args = infcx.fresh_args_for_item(rustc_span::DUMMY_SP, trait_def_id);
48+
let trait_ref = ty::TraitRef::new_from_args(tcx, trait_def_id, trait_args)
49+
.with_replaced_self_ty(tcx, self_ty);
50+
let goal = ty::solve::Goal::new(tcx, param_env, trait_ref);
51+
52+
let ControlFlow::Break(impl_def_id) = infcx.visit_proof_tree(goal, &mut HasBlanketImpl)
53+
else {
54+
continue;
55+
};
56+
57+
cx.generated_synthetics.insert((item_ty.skip_binder(), trait_def_id));
58+
59+
let item_ty = item_ty.instantiate_identity().skip_normalization();
60+
let impl_trait_ref =
61+
tcx.impl_trait_ref(impl_def_id).instantiate_identity().skip_normalization();
62+
63+
blanket_impls.push(clean::Item {
64+
inner: Box::new(clean::ItemInner {
65+
name: None,
66+
item_id: clean::ItemId::Blanket { impl_id: impl_def_id, for_: item_def_id },
67+
attrs: Default::default(),
68+
stability: None,
69+
kind: clean::ImplItem(Box::new(clean::Impl {
70+
safety: hir::Safety::Safe,
71+
generics: clean_ty_generics(cx, impl_def_id),
72+
// FIXME(eddyb) compute both `trait_` and `for_` from
73+
// the post-inference `trait_ref`, as it's more accurate.
74+
trait_: Some(clean_trait_ref_with_constraints(
75+
cx,
76+
ty::Binder::dummy(impl_trait_ref),
77+
ThinVec::new(),
78+
)),
79+
for_: clean_middle_ty(ty::Binder::dummy(item_ty), cx, None, None),
80+
items: tcx
81+
.associated_items(impl_def_id)
82+
.in_definition_order()
83+
.filter(|item| !item.is_impl_trait_in_trait())
84+
.map(|item| clean_middle_assoc_item(item, cx))
85+
.collect(),
86+
polarity: ty::ImplPolarity::Positive,
87+
kind: clean::ImplKind::Blanket(Box::new(clean_middle_ty(
88+
ty::Binder::dummy(impl_trait_ref.self_ty()),
89+
cx,
90+
None,
91+
None,
92+
))),
93+
is_deprecated: tcx
94+
.lookup_deprecation(impl_def_id)
95+
.is_some_and(|deprecation| deprecation.is_in_effect()),
96+
})),
97+
cfg: None,
98+
inline_stmt_id: None,
99+
}),
100+
});
132101
}
133102

134103
blanket_impls
135104
}
105+
106+
struct HasBlanketImpl;
107+
108+
impl<'tcx> ProofTreeVisitor<'tcx> for HasBlanketImpl {
109+
type Result = ControlFlow<DefId>;
110+
111+
fn span(&self) -> rustc_span::Span {
112+
rustc_span::DUMMY_SP
113+
}
114+
115+
fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> Self::Result {
116+
for candidate in goal.candidates() {
117+
if candidate.result().is_ok()
118+
&& let ProbeKind::TraitCandidate { source, .. } = candidate.kind()
119+
&& let CandidateSource::Impl(impl_def_id) = source
120+
&& let ty::Param(_) = goal.infcx().tcx.type_of(impl_def_id).skip_binder().kind()
121+
{
122+
return ControlFlow::Break(impl_def_id);
123+
}
124+
}
125+
126+
ControlFlow::Continue(())
127+
}
128+
}

0 commit comments

Comments
 (0)