|
1 |
| -use std::collections::BTreeMap; |
| 1 | +use std::collections::{BTreeMap, VecDeque}; |
2 | 2 |
|
3 |
| -use rustc_data_structures::fx::FxIndexMap; |
4 |
| -use rustc_errors::Diag; |
| 3 | +use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; |
5 | 4 | use rustc_hir::def_id::DefId;
|
6 | 5 | use rustc_infer::infer::InferCtxt;
|
7 | 6 | pub use rustc_infer::traits::util::*;
|
8 | 7 | use rustc_middle::bug;
|
9 | 8 | use rustc_middle::ty::{
|
10 |
| - self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast, |
| 9 | + self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, |
11 | 10 | };
|
12 | 11 | use rustc_span::Span;
|
13 | 12 | use smallvec::{SmallVec, smallvec};
|
14 | 13 | use tracing::debug;
|
15 | 14 |
|
16 |
| -/////////////////////////////////////////////////////////////////////////// |
17 |
| -// `TraitAliasExpander` iterator |
18 |
| -/////////////////////////////////////////////////////////////////////////// |
19 |
| - |
20 |
| -/// "Trait alias expansion" is the process of expanding a sequence of trait |
21 |
| -/// references into another sequence by transitively following all trait |
22 |
| -/// aliases. e.g. If you have bounds like `Foo + Send`, a trait alias |
23 |
| -/// `trait Foo = Bar + Sync;`, and another trait alias |
24 |
| -/// `trait Bar = Read + Write`, then the bounds would expand to |
25 |
| -/// `Read + Write + Sync + Send`. |
26 |
| -/// Expansion is done via a DFS (depth-first search), and the `visited` field |
27 |
| -/// is used to avoid cycles. |
28 |
| -pub struct TraitAliasExpander<'tcx> { |
29 |
| - tcx: TyCtxt<'tcx>, |
30 |
| - stack: Vec<TraitAliasExpansionInfo<'tcx>>, |
31 |
| -} |
32 |
| - |
33 |
| -/// Stores information about the expansion of a trait via a path of zero or more trait aliases. |
34 |
| -#[derive(Debug, Clone)] |
35 |
| -pub struct TraitAliasExpansionInfo<'tcx> { |
36 |
| - pub path: SmallVec<[(ty::PolyTraitRef<'tcx>, Span); 4]>, |
37 |
| -} |
38 |
| - |
39 |
| -impl<'tcx> TraitAliasExpansionInfo<'tcx> { |
40 |
| - fn new(trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { |
41 |
| - Self { path: smallvec![(trait_ref, span)] } |
42 |
| - } |
43 |
| - |
44 |
| - /// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate |
45 |
| - /// trait aliases. |
46 |
| - pub fn label_with_exp_info( |
47 |
| - &self, |
48 |
| - diag: &mut Diag<'_>, |
49 |
| - top_label: &'static str, |
50 |
| - use_desc: &str, |
51 |
| - ) { |
52 |
| - diag.span_label(self.top().1, top_label); |
53 |
| - if self.path.len() > 1 { |
54 |
| - for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) { |
55 |
| - diag.span_label(*sp, format!("referenced here ({use_desc})")); |
56 |
| - } |
57 |
| - } |
58 |
| - if self.top().1 != self.bottom().1 { |
59 |
| - // When the trait object is in a return type these two spans match, we don't want |
60 |
| - // redundant labels. |
61 |
| - diag.span_label( |
62 |
| - self.bottom().1, |
63 |
| - format!("trait alias used in trait object type ({use_desc})"), |
64 |
| - ); |
65 |
| - } |
66 |
| - } |
67 |
| - |
68 |
| - pub fn trait_ref(&self) -> ty::PolyTraitRef<'tcx> { |
69 |
| - self.top().0 |
70 |
| - } |
71 |
| - |
72 |
| - pub fn top(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { |
73 |
| - self.path.last().unwrap() |
74 |
| - } |
75 |
| - |
76 |
| - pub fn bottom(&self) -> &(ty::PolyTraitRef<'tcx>, Span) { |
77 |
| - self.path.first().unwrap() |
78 |
| - } |
79 |
| - |
80 |
| - fn clone_and_push(&self, trait_ref: ty::PolyTraitRef<'tcx>, span: Span) -> Self { |
81 |
| - let mut path = self.path.clone(); |
82 |
| - path.push((trait_ref, span)); |
83 |
| - |
84 |
| - Self { path } |
85 |
| - } |
86 |
| -} |
87 |
| - |
| 15 | +/// Return the trait and projection predicates that come from eagerly expanding the |
| 16 | +/// trait aliases in the list of clauses. For each trait predicate, record a stack |
| 17 | +/// of spans that trace from the user-written trait alias bound. For projection predicates, |
| 18 | +/// just record the span of the projection itself. |
| 19 | +/// |
| 20 | +/// For trait aliases, we don't deduplicte the predicates, since we currently do not |
| 21 | +/// consider duplicated traits as a single trait for the purposes of our "one trait principal" |
| 22 | +/// restriction; however, for projections we do deduplicate them. |
| 23 | +/// |
| 24 | +/// ```rust,ignore (fails) |
| 25 | +/// trait Bar {} |
| 26 | +/// trait Foo = Bar + Bar; |
| 27 | +/// |
| 28 | +/// let not_object_safe: dyn Foo; // bad, two `Bar` principals. |
| 29 | +/// ``` |
88 | 30 | pub fn expand_trait_aliases<'tcx>(
|
89 | 31 | tcx: TyCtxt<'tcx>,
|
90 |
| - trait_refs: impl Iterator<Item = (ty::PolyTraitRef<'tcx>, Span)>, |
91 |
| -) -> TraitAliasExpander<'tcx> { |
92 |
| - let items: Vec<_> = |
93 |
| - trait_refs.map(|(trait_ref, span)| TraitAliasExpansionInfo::new(trait_ref, span)).collect(); |
94 |
| - TraitAliasExpander { tcx, stack: items } |
95 |
| -} |
96 |
| - |
97 |
| -impl<'tcx> TraitAliasExpander<'tcx> { |
98 |
| - /// If `item` is a trait alias and its predicate has not yet been visited, then expands `item` |
99 |
| - /// to the definition, pushes the resulting expansion onto `self.stack`, and returns `false`. |
100 |
| - /// Otherwise, immediately returns `true` if `item` is a regular trait, or `false` if it is a |
101 |
| - /// trait alias. |
102 |
| - /// The return value indicates whether `item` should be yielded to the user. |
103 |
| - fn expand(&mut self, item: &TraitAliasExpansionInfo<'tcx>) -> bool { |
104 |
| - let tcx = self.tcx; |
105 |
| - let trait_ref = item.trait_ref(); |
106 |
| - let pred = trait_ref.upcast(tcx); |
107 |
| - |
108 |
| - debug!("expand_trait_aliases: trait_ref={:?}", trait_ref); |
109 |
| - |
110 |
| - // Don't recurse if this bound is not a trait alias. |
111 |
| - let is_alias = tcx.is_trait_alias(trait_ref.def_id()); |
112 |
| - if !is_alias { |
113 |
| - return true; |
114 |
| - } |
115 |
| - |
116 |
| - // Don't recurse if this trait alias is already on the stack for the DFS search. |
117 |
| - let anon_pred = anonymize_predicate(tcx, pred); |
118 |
| - if item |
119 |
| - .path |
120 |
| - .iter() |
121 |
| - .rev() |
122 |
| - .skip(1) |
123 |
| - .any(|&(tr, _)| anonymize_predicate(tcx, tr.upcast(tcx)) == anon_pred) |
124 |
| - { |
125 |
| - return false; |
126 |
| - } |
127 |
| - |
128 |
| - // Get components of trait alias. |
129 |
| - let predicates = tcx.explicit_super_predicates_of(trait_ref.def_id()); |
130 |
| - debug!(?predicates); |
131 |
| - |
132 |
| - let items = predicates.skip_binder().iter().rev().filter_map(|(pred, span)| { |
133 |
| - pred.instantiate_supertrait(tcx, trait_ref) |
134 |
| - .as_trait_clause() |
135 |
| - .map(|trait_ref| item.clone_and_push(trait_ref.map_bound(|t| t.trait_ref), *span)) |
136 |
| - }); |
137 |
| - debug!("expand_trait_aliases: items={:?}", items.clone().collect::<Vec<_>>()); |
138 |
| - |
139 |
| - self.stack.extend(items); |
140 |
| - |
141 |
| - false |
142 |
| - } |
143 |
| -} |
144 |
| - |
145 |
| -impl<'tcx> Iterator for TraitAliasExpander<'tcx> { |
146 |
| - type Item = TraitAliasExpansionInfo<'tcx>; |
147 |
| - |
148 |
| - fn size_hint(&self) -> (usize, Option<usize>) { |
149 |
| - (self.stack.len(), None) |
150 |
| - } |
151 |
| - |
152 |
| - fn next(&mut self) -> Option<TraitAliasExpansionInfo<'tcx>> { |
153 |
| - while let Some(item) = self.stack.pop() { |
154 |
| - if self.expand(&item) { |
155 |
| - return Some(item); |
| 32 | + clauses: impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>, |
| 33 | +) -> ( |
| 34 | + Vec<(ty::PolyTraitPredicate<'tcx>, SmallVec<[Span; 1]>)>, |
| 35 | + Vec<(ty::PolyProjectionPredicate<'tcx>, Span)>, |
| 36 | +) { |
| 37 | + let mut trait_preds = vec![]; |
| 38 | + let mut projection_preds = vec![]; |
| 39 | + let mut seen_projection_preds = FxHashSet::default(); |
| 40 | + |
| 41 | + let mut queue: VecDeque<_> = clauses.into_iter().map(|(p, s)| (p, smallvec![s])).collect(); |
| 42 | + |
| 43 | + while let Some((clause, spans)) = queue.pop_front() { |
| 44 | + match clause.kind().skip_binder() { |
| 45 | + ty::ClauseKind::Trait(trait_pred) => { |
| 46 | + if tcx.is_trait_alias(trait_pred.def_id()) { |
| 47 | + queue.extend( |
| 48 | + tcx.explicit_super_predicates_of(trait_pred.def_id()) |
| 49 | + .iter_identity_copied() |
| 50 | + .map(|(clause, span)| { |
| 51 | + let mut spans = spans.clone(); |
| 52 | + spans.push(span); |
| 53 | + ( |
| 54 | + clause.instantiate_supertrait( |
| 55 | + tcx, |
| 56 | + clause.kind().rebind(trait_pred.trait_ref), |
| 57 | + ), |
| 58 | + spans, |
| 59 | + ) |
| 60 | + }), |
| 61 | + ); |
| 62 | + } else { |
| 63 | + trait_preds.push((clause.kind().rebind(trait_pred), spans)); |
| 64 | + } |
156 | 65 | }
|
| 66 | + ty::ClauseKind::Projection(projection_pred) => { |
| 67 | + let projection_pred = clause.kind().rebind(projection_pred); |
| 68 | + if !seen_projection_preds.insert(tcx.anonymize_bound_vars(projection_pred)) { |
| 69 | + continue; |
| 70 | + } |
| 71 | + projection_preds.push((projection_pred, *spans.last().unwrap())); |
| 72 | + } |
| 73 | + ty::ClauseKind::RegionOutlives(..) |
| 74 | + | ty::ClauseKind::TypeOutlives(..) |
| 75 | + | ty::ClauseKind::ConstArgHasType(_, _) |
| 76 | + | ty::ClauseKind::WellFormed(_) |
| 77 | + | ty::ClauseKind::ConstEvaluatable(_) |
| 78 | + | ty::ClauseKind::HostEffect(..) => {} |
157 | 79 | }
|
158 |
| - None |
159 | 80 | }
|
| 81 | + |
| 82 | + (trait_preds, projection_preds) |
160 | 83 | }
|
161 | 84 |
|
162 | 85 | ///////////////////////////////////////////////////////////////////////////
|
|
0 commit comments