Skip to content

Commit b1af9a5

Browse files
committedJan 30, 2025
Do not elaborate implicit supertrait associated type bounds into dyn trait's existential predicates
1 parent a6434ef commit b1af9a5

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+620
-145
lines changed
 

‎compiler/rustc_codegen_cranelift/example/issue-59326.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
// Based on https://github.com/rust-lang/rust/blob/689511047a75a30825e367d4fd45c74604d0b15e/tests/ui/issues/issue-59326.rs#L1
22
// check-pass
3+
4+
#![allow(dyn_assoc_redundant, dyn_assoc_shadowed)]
5+
36
trait Service {
47
type S;
58
}

‎compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs

+135-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
1+
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
22
use rustc_errors::codes::*;
33
use rustc_errors::struct_span_code_err;
44
use rustc_hir as hir;
5+
use rustc_hir::HirId;
56
use rustc_hir::def::{DefKind, Res};
6-
use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
7+
use rustc_hir::def_id::DefId;
8+
use rustc_lint_defs::builtin::{
9+
DYN_ASSOC_REDUNDANT, DYN_ASSOC_SHADOWED, UNUSED_ASSOCIATED_TYPE_BOUNDS,
10+
};
711
use rustc_middle::ty::fold::BottomUpFolder;
812
use rustc_middle::ty::{
913
self, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable,
@@ -28,7 +32,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
2832
pub(super) fn lower_trait_object_ty(
2933
&self,
3034
span: Span,
31-
hir_id: hir::HirId,
35+
hir_id: HirId,
3236
hir_bounds: &[hir::PolyTraitRef<'tcx>],
3337
lifetime: &hir::Lifetime,
3438
representation: DynKind,
@@ -59,12 +63,49 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
5963
}
6064
}
6165

62-
let (trait_bounds, mut projection_bounds) =
66+
let (trait_bounds, elaborated_projection_bounds) =
6367
traits::expand_trait_aliases(tcx, user_written_bounds.clauses());
6468
let (regular_traits, mut auto_traits): (Vec<_>, Vec<_>) = trait_bounds
6569
.into_iter()
6670
.partition(|(trait_ref, _)| !tcx.trait_is_auto(trait_ref.def_id()));
6771

72+
// Map the projection bounds onto a key that makes it easy to remove redundant
73+
// bounds that are constrained by supertraits of the principal def id.
74+
//
75+
// Also make sure we detect conflicting bounds from expanding a trait alias and
76+
// also specifying it manually, like:
77+
// ```
78+
// type Alias = Trait<Assoc = i32>;
79+
// let _: &dyn Alias<Assoc = u32> = /* ... */;
80+
// ```
81+
let mut projection_bounds = FxIndexMap::default();
82+
for (proj, proj_span) in elaborated_projection_bounds {
83+
if let Some((old_proj, old_proj_span)) = projection_bounds.insert(
84+
tcx.anonymize_bound_vars(proj.map_bound(|proj| proj.projection_term)),
85+
(proj, proj_span),
86+
) && tcx.anonymize_bound_vars(proj) != tcx.anonymize_bound_vars(old_proj)
87+
{
88+
let item = tcx.item_name(proj.item_def_id());
89+
self.dcx()
90+
.struct_span_err(
91+
span,
92+
format!(
93+
"conflicting associated type bounds for `{item}` when \
94+
expanding trait alias"
95+
),
96+
)
97+
.with_span_label(
98+
old_proj_span,
99+
format!("`{item}` is specified to be `{}` here", old_proj.term()),
100+
)
101+
.with_span_label(
102+
proj_span,
103+
format!("`{item}` is specified to be `{}` here", proj.term()),
104+
)
105+
.emit();
106+
}
107+
}
108+
68109
// We don't support empty trait objects.
69110
if regular_traits.is_empty() && auto_traits.is_empty() {
70111
let guar =
@@ -105,6 +146,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
105146
let principal_trait = regular_traits.into_iter().next();
106147

107148
let mut needed_associated_types = FxIndexSet::default();
149+
150+
// These are the projection bounds that we get from supertraits that
151+
// don't mention the dyn trait recursively. See comment below.
152+
let mut implied_projection_bounds = vec![];
153+
108154
if let Some((principal_trait, spans)) = &principal_trait {
109155
let pred: ty::Predicate<'tcx> = (*principal_trait).upcast(tcx);
110156
for ClauseWithSupertraitSpan { pred, supertrait_span } in
@@ -162,7 +208,34 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
162208
// the discussion in #56288 for alternatives.
163209
if !references_self {
164210
// Include projections defined on supertraits.
165-
projection_bounds.push((pred, supertrait_span));
211+
implied_projection_bounds.push(pred);
212+
213+
if let Some((user_written_projection, user_written_span)) =
214+
projection_bounds.shift_remove(&tcx.anonymize_bound_vars(
215+
pred.map_bound(|pred| pred.projection_term),
216+
))
217+
{
218+
if tcx.anonymize_bound_vars(user_written_projection)
219+
== tcx.anonymize_bound_vars(pred)
220+
{
221+
self.lint_redundant_projection(
222+
hir_id,
223+
user_written_projection,
224+
principal_trait.def_id(),
225+
user_written_span,
226+
supertrait_span,
227+
);
228+
} else {
229+
self.lint_shadowed_projection(
230+
hir_id,
231+
user_written_projection,
232+
pred,
233+
principal_trait.def_id(),
234+
user_written_span,
235+
supertrait_span,
236+
);
237+
}
238+
}
166239
}
167240

168241
self.check_elaborated_projection_mentions_input_lifetimes(
@@ -182,12 +255,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
182255
// types that we expect to be provided by the user, so the following loop
183256
// removes all the associated types that have a corresponding `Projection`
184257
// clause, either from expanding trait aliases or written by the user.
185-
for &(projection_bound, span) in &projection_bounds {
258+
for &(projection_bound, span) in projection_bounds.values() {
186259
let def_id = projection_bound.item_def_id();
187260
let trait_ref = tcx.anonymize_bound_vars(
188261
projection_bound.map_bound(|p| p.projection_term.trait_ref(tcx)),
189262
);
190-
needed_associated_types.swap_remove(&(def_id, trait_ref));
263+
needed_associated_types.shift_remove(&(def_id, trait_ref));
191264
if tcx.generics_require_sized_self(def_id) {
192265
tcx.emit_node_span_lint(
193266
UNUSED_ASSOCIATED_TYPE_BOUNDS,
@@ -197,6 +270,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
197270
);
198271
}
199272
}
273+
for projection_bound in &implied_projection_bounds {
274+
let def_id = projection_bound.item_def_id();
275+
let trait_ref = tcx.anonymize_bound_vars(
276+
projection_bound.map_bound(|p| p.projection_term.trait_ref(tcx)),
277+
);
278+
needed_associated_types.swap_remove(&(def_id, trait_ref));
279+
}
200280

201281
if let Err(guar) = self.check_for_required_assoc_tys(
202282
principal_trait.as_ref().map_or(smallvec![], |(_, spans)| spans.clone()),
@@ -266,7 +346,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
266346
})
267347
});
268348

269-
let existential_projections = projection_bounds.iter().map(|(bound, _)| {
349+
let existential_projections = projection_bounds.values().map(|(bound, _)| {
270350
bound.map_bound(|mut b| {
271351
assert_eq!(b.projection_term.self_ty(), dummy_self);
272352

@@ -343,6 +423,53 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
343423
Ty::new_dynamic(tcx, existential_predicates, region_bound, representation)
344424
}
345425

426+
fn lint_shadowed_projection(
427+
&self,
428+
hir_id: HirId,
429+
user_written_projection: ty::PolyProjectionPredicate<'tcx>,
430+
elaborated_projection: ty::PolyProjectionPredicate<'tcx>,
431+
principal_def_id: DefId,
432+
user_written_span: Span,
433+
supertrait_span: Span,
434+
) {
435+
let tcx = self.tcx();
436+
let assoc = tcx.item_name(user_written_projection.item_def_id());
437+
let principal = tcx.item_name(principal_def_id);
438+
self.tcx().node_span_lint(DYN_ASSOC_SHADOWED, hir_id, user_written_span, |diag| {
439+
diag.primary_message(format!(
440+
"associated type bound for `{assoc}` in `dyn {principal}` differs from \
441+
associated type bound from supertrait",
442+
));
443+
diag.span_label(user_written_span, "this bound has no effect and will be ignored");
444+
diag.note(format!(
445+
"`{assoc} = {}` was implied by a supertrait and shadows any user-written bounds, \
446+
so `{assoc} = {}` will be ignored",
447+
elaborated_projection.term(),
448+
user_written_projection.term(),
449+
));
450+
diag.span_label(supertrait_span, "shadowed due to this supertrait bound");
451+
});
452+
}
453+
454+
fn lint_redundant_projection(
455+
&self,
456+
hir_id: HirId,
457+
user_written_projection: ty::PolyProjectionPredicate<'tcx>,
458+
principal_def_id: DefId,
459+
user_written_span: Span,
460+
supertrait_span: Span,
461+
) {
462+
let tcx = self.tcx();
463+
let assoc = tcx.item_name(user_written_projection.item_def_id());
464+
let principal = tcx.item_name(principal_def_id);
465+
self.tcx().node_span_lint(DYN_ASSOC_REDUNDANT, hir_id, user_written_span, |diag| {
466+
diag.primary_message(format!(
467+
"associated type bound for `{assoc}` in `dyn {principal}` is redundant",
468+
));
469+
diag.span_label(supertrait_span, "redundant due to this supertrait bound");
470+
});
471+
}
472+
346473
/// Check that elaborating the principal of a trait ref doesn't lead to projections
347474
/// that are unconstrained. This can happen because an otherwise unconstrained
348475
/// *type variable* can be substituted with a type that has late-bound regions. See

0 commit comments

Comments
 (0)