|
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