|
| 1 | +use std::ops::ControlFlow; |
| 2 | + |
1 | 3 | use rustc_data_structures::thin_vec::ThinVec; |
2 | 4 | 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; |
7 | 7 | 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 | +}; |
10 | 12 |
|
11 | 13 | use crate::clean; |
12 | 14 | use crate::clean::{ |
13 | 15 | clean_middle_assoc_item, clean_middle_ty, clean_trait_ref_with_constraints, clean_ty_generics, |
14 | 16 | }; |
15 | 17 | use crate::core::DocContext; |
16 | 18 |
|
17 | | -#[instrument(level = "debug", skip(cx))] |
| 19 | +#[tracing::instrument(level = "debug", skip(cx))] |
18 | 20 | pub(crate) fn synthesize_blanket_impls( |
19 | 21 | cx: &mut DocContext<'_>, |
20 | 22 | item_def_id: DefId, |
21 | 23 | ) -> Vec<clean::Item> { |
22 | 24 | 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(); |
24 | 28 |
|
25 | 29 | let mut blanket_impls = Vec::new(); |
| 30 | + |
26 | 31 | for trait_def_id in tcx.visible_traits() { |
27 | 32 | 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)) |
29 | 34 | { |
30 | 35 | continue; |
31 | 36 | } |
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; |
131 | 41 | } |
| 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 | + }); |
132 | 101 | } |
133 | 102 |
|
134 | 103 | blanket_impls |
135 | 104 | } |
| 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