Skip to content

Commit 178d48d

Browse files
Do not elaborate implicit supertrait associated type bounds into dyn trait's existential predicates
1 parent a6434ef commit 178d48d

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

compiler/rustc_hir_typeck/src/closure.rs

+15-11
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_span::def_id::LocalDefId;
1818
use rustc_span::{DUMMY_SP, Span};
1919
use rustc_trait_selection::error_reporting::traits::ArgKind;
2020
use rustc_trait_selection::traits;
21-
use rustc_type_ir::ClosureKind;
21+
use rustc_type_ir::{ClosureKind, Upcast as _};
2222
use tracing::{debug, instrument, trace};
2323

2424
use super::{CoroutineTypes, Expectation, FnCtxt, check_fn};
@@ -61,6 +61,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6161
Some(ty) => self.deduce_closure_signature(
6262
self.try_structurally_resolve_type(expr_span, ty),
6363
closure.kind,
64+
expr_span,
6465
),
6566
None => (None, None),
6667
};
@@ -301,6 +302,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
301302
&self,
302303
expected_ty: Ty<'tcx>,
303304
closure_kind: hir::ClosureKind,
305+
span: Span,
304306
) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
305307
match *expected_ty.kind() {
306308
ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self
@@ -312,16 +314,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
312314
.iter_instantiated_copied(self.tcx, args)
313315
.map(|(c, s)| (c.as_predicate(), s)),
314316
),
315-
ty::Dynamic(object_type, ..) => {
316-
let sig = object_type.projection_bounds().find_map(|pb| {
317-
let pb = pb.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self);
318-
self.deduce_sig_from_projection(None, closure_kind, pb)
319-
});
320-
let kind = object_type
321-
.principal_def_id()
322-
.and_then(|did| self.tcx.fn_trait_kind_from_def_id(did));
323-
(sig, kind)
324-
}
317+
ty::Dynamic(data, ..) => self.deduce_closure_signature_from_predicates(
318+
self.tcx.types.trait_object_dummy_self,
319+
closure_kind,
320+
data.iter().map(|bound| {
321+
(
322+
bound
323+
.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self)
324+
.upcast(self.tcx),
325+
span,
326+
)
327+
}),
328+
),
325329
ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates(
326330
Ty::new_var(self.tcx, self.root_var(vid)),
327331
closure_kind,

compiler/rustc_lint_defs/src/builtin.rs

+16
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ declare_lint_pass! {
3939
DEPRECATED_SAFE_2024,
4040
DEPRECATED_WHERE_CLAUSE_LOCATION,
4141
DUPLICATE_MACRO_ATTRIBUTES,
42+
DYN_ASSOC_REDUNDANT,
43+
DYN_ASSOC_SHADOWED,
4244
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
4345
ELIDED_LIFETIMES_IN_PATHS,
4446
ELIDED_NAMED_LIFETIMES,
@@ -5120,6 +5122,20 @@ declare_lint! {
51205122
crate_level_only
51215123
}
51225124

5125+
declare_lint! {
5126+
/// Hi
5127+
pub DYN_ASSOC_REDUNDANT,
5128+
Warn,
5129+
"oops",
5130+
}
5131+
5132+
declare_lint! {
5133+
/// Hi
5134+
pub DYN_ASSOC_SHADOWED,
5135+
Deny,
5136+
"oops",
5137+
}
5138+
51235139
declare_lint! {
51245140
/// The `abi_unsupported_vector_types` lint detects function definitions and calls
51255141
/// whose ABI depends on enabling certain target features, but those features are not enabled.

compiler/rustc_middle/src/ty/relate.rs

+4-12
Original file line numberDiff line numberDiff line change
@@ -80,19 +80,12 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate<
8080
) -> RelateResult<'tcx, Self> {
8181
let tcx = relation.cx();
8282

83-
// FIXME: this is wasteful, but want to do a perf run to see how slow it is.
84-
// We need to perform this deduplication as we sometimes generate duplicate projections
85-
// in `a`.
86-
let mut a_v: Vec<_> = a.into_iter().collect();
87-
let mut b_v: Vec<_> = b.into_iter().collect();
88-
a_v.dedup();
89-
b_v.dedup();
90-
if a_v.len() != b_v.len() {
83+
if a.len() != b.len() {
9184
return Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b)));
9285
}
9386

94-
let v = iter::zip(a_v, b_v).map(|(ep_a, ep_b)| {
95-
match (ep_a.skip_binder(), ep_b.skip_binder()) {
87+
let v =
88+
iter::zip(a, b).map(|(ep_a, ep_b)| match (ep_a.skip_binder(), ep_b.skip_binder()) {
9689
(ty::ExistentialPredicate::Trait(a), ty::ExistentialPredicate::Trait(b)) => {
9790
Ok(ep_a.rebind(ty::ExistentialPredicate::Trait(
9891
relation.relate(ep_a.rebind(a), ep_b.rebind(b))?.skip_binder(),
@@ -109,8 +102,7 @@ impl<'tcx> Relate<TyCtxt<'tcx>> for &'tcx ty::List<ty::PolyExistentialPredicate<
109102
ty::ExistentialPredicate::AutoTrait(b),
110103
) if a == b => Ok(ep_a.rebind(ty::ExistentialPredicate::AutoTrait(a))),
111104
_ => Err(TypeError::ExistentialMismatch(ExpectedFound::new(a, b))),
112-
}
113-
});
105+
});
114106
tcx.mk_poly_existential_predicates_from_iter(v)
115107
}
116108
}

compiler/rustc_middle/src/ty/sty.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1025,6 +1025,10 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
10251025
fn has_unsafe_fields(self) -> bool {
10261026
Ty::has_unsafe_fields(self)
10271027
}
1028+
1029+
fn is_self_param(self) -> bool {
1030+
self.is_param(0)
1031+
}
10281032
}
10291033

10301034
/// Type utilities

compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,15 @@ where
725725
assumption.upcast(cx),
726726
));
727727
}
728+
729+
for assumption in elaborate::implied_supertrait_projections(cx, principal) {
730+
candidates.extend(G::probe_and_consider_object_bound_candidate(
731+
self,
732+
CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
733+
goal,
734+
assumption.with_self_ty(cx, self_ty).upcast(cx),
735+
));
736+
}
728737
}
729738
}
730739

0 commit comments

Comments
 (0)