Skip to content

Commit b020ef4

Browse files
committed
Auto merge of #133830 - compiler-errors:span-key, r=<try>
Rework dyn trait lowering to stop being so intertwined with trait alias expansion This PR reworks the trait object lowering code to stop handling trait aliases so funky, and removes the `TraitAliasExpander` in favor of a much simpler design. This refactoring is important for making the code that I'm writing in #133397 understandable and easy to maintain, so the diagnostics regressions are IMO inevitable. ##### First commit: We used to use Span to maintain a per-principal list of missing associated types. We don't support multiple object principals, and I don't believe we intend to do so anytime soon. Let's remove this to greatly simplify the bookkeeping of this function. ##### Second commit: In the old trait object lowering code, we used to be a bit sloppy with the lists of traits in their unexpanded and expanded forms. This PR largely rewrites this logic to expand the trait aliases *once* and handle them more responsibly throughout afterwards. Please review both of these with whitespace disabled. r? lcnr
2 parents 96e51d9 + af94314 commit b020ef4

26 files changed

+679
-777
lines changed

compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs

Lines changed: 92 additions & 126 deletions
Large diffs are not rendered by default.

compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs

Lines changed: 141 additions & 147 deletions
Large diffs are not rendered by default.

compiler/rustc_hir_typeck/src/method/probe.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use std::cell::{Cell, RefCell};
22
use std::cmp::max;
3-
use std::iter;
43
use std::ops::Deref;
54

65
use rustc_data_structures::fx::FxHashSet;
@@ -901,11 +900,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
901900

902901
if self.tcx.is_trait_alias(trait_def_id) {
903902
// For trait aliases, recursively assume all explicitly named traits are relevant
904-
for expansion in traits::expand_trait_aliases(
905-
self.tcx,
906-
iter::once((ty::Binder::dummy(trait_ref), self.span)),
907-
) {
908-
let bound_trait_ref = expansion.trait_ref();
903+
for (bound_trait_pred, _) in
904+
traits::expand_trait_aliases(self.tcx, [(trait_ref.upcast(self.tcx), self.span)]).0
905+
{
906+
assert_eq!(bound_trait_pred.polarity(), ty::PredicatePolarity::Positive);
907+
let bound_trait_ref = bound_trait_pred.map_bound(|pred| pred.trait_ref);
909908
for item in self.impl_or_trait_item(bound_trait_ref.def_id()) {
910909
if !self.has_applicable_self(&item) {
911910
self.record_static_candidate(CandidateSource::Trait(

compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,8 @@ pub fn report_dyn_incompatibility<'tcx>(
436436
tcx.dcx(),
437437
span,
438438
E0038,
439-
"the trait `{}` cannot be made into an object",
439+
"the {} `{}` cannot be made into an object",
440+
tcx.def_descr(trait_def_id),
440441
trait_str
441442
);
442443
err.span_label(span, format!("`{trait_str}` cannot be made into an object"));

compiler/rustc_trait_selection/src/traits/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ pub use self::specialize::{
6666
};
6767
pub use self::structural_normalize::StructurallyNormalizeExt;
6868
pub use self::util::{
69-
BoundVarReplacer, PlaceholderReplacer, TraitAliasExpander, TraitAliasExpansionInfo, elaborate,
70-
expand_trait_aliases, impl_item_is_final, supertraits,
71-
transitive_bounds_that_define_assoc_item, upcast_choices, with_replaced_escaping_bound_vars,
69+
BoundVarReplacer, PlaceholderReplacer, elaborate, expand_trait_aliases, impl_item_is_final,
70+
supertraits, transitive_bounds_that_define_assoc_item, upcast_choices,
71+
with_replaced_escaping_bound_vars,
7272
};
7373
use crate::error_reporting::InferCtxtErrorExt;
7474
use crate::infer::outlives::env::OutlivesEnvironment;

compiler/rustc_trait_selection/src/traits/util.rs

Lines changed: 66 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,85 @@
1-
use std::collections::BTreeMap;
1+
use std::collections::{BTreeMap, VecDeque};
22

3-
use rustc_data_structures::fx::FxIndexMap;
4-
use rustc_errors::Diag;
3+
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
54
use rustc_hir::def_id::DefId;
65
use rustc_infer::infer::InferCtxt;
76
pub use rustc_infer::traits::util::*;
87
use rustc_middle::bug;
98
use rustc_middle::ty::{
10-
self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast,
9+
self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
1110
};
1211
use rustc_span::Span;
1312
use smallvec::{SmallVec, smallvec};
1413
use tracing::debug;
1514

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+
/// ```
8830
pub fn expand_trait_aliases<'tcx>(
8931
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+
}
15665
}
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(..) => {}
15779
}
158-
None
15980
}
81+
82+
(trait_preds, projection_preds)
16083
}
16184

16285
///////////////////////////////////////////////////////////////////////////

compiler/rustc_type_ir/src/visit.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ use std::ops::ControlFlow;
4747
use rustc_ast_ir::visit::VisitorResult;
4848
use rustc_ast_ir::{try_visit, walk_visitable_list};
4949
use rustc_index::{Idx, IndexVec};
50+
use smallvec::SmallVec;
5051
use thin_vec::ThinVec;
5152

5253
use crate::data_structures::Lrc;
@@ -192,6 +193,13 @@ impl<I: Interner, T: TypeVisitable<I>> TypeVisitable<I> for ThinVec<T> {
192193
}
193194
}
194195

196+
impl<I: Interner, T: TypeVisitable<I>, const N: usize> TypeVisitable<I> for SmallVec<[T; N]> {
197+
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> V::Result {
198+
walk_visitable_list!(visitor, self.iter());
199+
V::Result::output()
200+
}
201+
}
202+
195203
// `TypeFoldable` isn't impl'd for `&[T]`. It doesn't make sense in the general
196204
// case, because we can't return a new slice. But note that there are a couple
197205
// of trivial impls of `TypeFoldable` for specific slice types elsewhere.

tests/rustdoc-ui/issues/issue-105742.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,10 +295,10 @@ LL | Output = <Self as SVec>::Item> as SVec>::Item<T>,
295295
| +++
296296

297297
error[E0038]: the trait `SVec` cannot be made into an object
298-
--> $DIR/issue-105742.rs:4:31
298+
--> $DIR/issue-105742.rs:4:35
299299
|
300300
LL | pub fn next<'a, T>(s: &'a mut dyn SVec<Item = T, Output = T>) {
301-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SVec` cannot be made into an object
301+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ `SVec` cannot be made into an object
302302
|
303303
note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
304304
--> $DIR/issue-105742.rs:14:17

tests/ui/const-generics/adt_const_params/const_param_ty_dyn_compatibility.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0038]: the trait `ConstParamTy_` cannot be made into an object
2-
--> $DIR/const_param_ty_dyn_compatibility.rs:6:12
2+
--> $DIR/const_param_ty_dyn_compatibility.rs:6:16
33
|
44
LL | fn foo(a: &dyn ConstParamTy_) {}
5-
| ^^^^^^^^^^^^^^^^^ `ConstParamTy_` cannot be made into an object
5+
| ^^^^^^^^^^^^^ `ConstParamTy_` cannot be made into an object
66
|
77
note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
88
--> $SRC_DIR/core/src/cmp.rs:LL:COL
@@ -14,10 +14,10 @@ LL | fn foo(a: &impl ConstParamTy_) {}
1414
| ~~~~
1515

1616
error[E0038]: the trait `UnsizedConstParamTy` cannot be made into an object
17-
--> $DIR/const_param_ty_dyn_compatibility.rs:9:12
17+
--> $DIR/const_param_ty_dyn_compatibility.rs:9:16
1818
|
1919
LL | fn bar(a: &dyn UnsizedConstParamTy) {}
20-
| ^^^^^^^^^^^^^^^^^^^^^^^ `UnsizedConstParamTy` cannot be made into an object
20+
| ^^^^^^^^^^^^^^^^^^^ `UnsizedConstParamTy` cannot be made into an object
2121
|
2222
note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
2323
--> $SRC_DIR/core/src/cmp.rs:LL:COL

tests/ui/dyn-compatibility/mentions-Self-in-super-predicates.stderr

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0038]: the trait `Expr` cannot be made into an object
2-
--> $DIR/mentions-Self-in-super-predicates.rs:12:23
2+
--> $DIR/mentions-Self-in-super-predicates.rs:12:27
33
|
44
LL | elements: Vec<Box<dyn Expr + 'x>>,
5-
| ^^^^^^^^^^^^^ `Expr` cannot be made into an object
5+
| ^^^^ `Expr` cannot be made into an object
66
|
77
note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
88
--> $DIR/mentions-Self-in-super-predicates.rs:5:21
@@ -14,10 +14,10 @@ LL | trait Expr: Debug + PartialEq {
1414
= help: only type `SExpr<'x>` implements the trait, consider using it directly instead
1515

1616
error[E0038]: the trait `Expr` cannot be made into an object
17-
--> $DIR/mentions-Self-in-super-predicates.rs:38:16
17+
--> $DIR/mentions-Self-in-super-predicates.rs:38:20
1818
|
1919
LL | let a: Box<dyn Expr> = Box::new(SExpr::new());
20-
| ^^^^^^^^ `Expr` cannot be made into an object
20+
| ^^^^ `Expr` cannot be made into an object
2121
|
2222
note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
2323
--> $DIR/mentions-Self-in-super-predicates.rs:5:21
@@ -29,10 +29,10 @@ LL | trait Expr: Debug + PartialEq {
2929
= help: only type `SExpr<'x>` implements the trait, consider using it directly instead
3030

3131
error[E0038]: the trait `Expr` cannot be made into an object
32-
--> $DIR/mentions-Self-in-super-predicates.rs:40:16
32+
--> $DIR/mentions-Self-in-super-predicates.rs:40:20
3333
|
3434
LL | let b: Box<dyn Expr> = Box::new(SExpr::new());
35-
| ^^^^^^^^ `Expr` cannot be made into an object
35+
| ^^^^ `Expr` cannot be made into an object
3636
|
3737
note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
3838
--> $DIR/mentions-Self-in-super-predicates.rs:5:21

tests/ui/dyn-compatibility/supertrait-mentions-Self.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ LL | trait Bar<T: ?Sized> {
1919
| ++++++++
2020

2121
error[E0038]: the trait `Baz` cannot be made into an object
22-
--> $DIR/supertrait-mentions-Self.rs:16:31
22+
--> $DIR/supertrait-mentions-Self.rs:16:35
2323
|
2424
LL | fn make_baz<T:Baz>(t: &T) -> &dyn Baz {
25-
| ^^^^^^^ `Baz` cannot be made into an object
25+
| ^^^ `Baz` cannot be made into an object
2626
|
2727
note: for a trait to be "dyn-compatible" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
2828
--> $DIR/supertrait-mentions-Self.rs:8:13

tests/ui/error-codes/E0225.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ LL | trait Foo = std::io::Read + std::io::Write;
2020
LL | let _: Box<dyn Foo>;
2121
| ^^^
2222
| |
23-
| trait alias used in trait object type (additional use)
24-
| trait alias used in trait object type (first use)
23+
| first non-auto trait comes from this alias
24+
| second non-auto trait comes from this alias
2525
|
2626
= help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: std::io::Read + std::io::Write {}`
2727
= note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>

0 commit comments

Comments
 (0)