Skip to content

Commit af54b60

Browse files
committed
Auto merge of rust-lang#138952 - dingxiangfei2009:implicit-receiver-impl, r=<try>
arbitrary_self_type: insert implied Receiver bound on Deref r? `@nikomatsakis` This is an experimental setup which allows us to assess the impact from inserting an implied supertrait bound `Deref: Receiver`. The observations are as follows. - Thanks to the trait solver, not too many hacks are needed to implement the idea behind [this comment](rust-lang#135881 (comment)), where inductive cycles are admissible. - We want to allow users to continue use the `dyn Deref` type as `Deref` has been `dyn`-compatible. Following the current `dyn`-compatibility rule, it would have been impossible to use `dyn Deref<Target = ..>` because one would need to write `dyn Deref<..> + Receiver<..>` while neither `Deref` nor `Receiver` is an `auto` trait. This is the main change that this PR proposes: we fill in the missing projection bound that `Receiver` demands from a `dyn Deref` and this will remain the only exception. - Finally, the type (pretty-)printing is adjusted so as to not present the frivolous `Receiver::Target = ..` bound. It will always be the same term as `Deref::Target`. I am proposing a crater experiment to see the extent of impact on the existing ecosystem and explore corner cases that I have missed.
2 parents 068609c + 81e08c1 commit af54b60

File tree

12 files changed

+327
-339
lines changed

12 files changed

+327
-339
lines changed

compiler/rustc_hir_analysis/src/hir_ty_lowering/dyn_compatibility.rs

+41-3
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use rustc_hir::def::{DefKind, Res};
66
use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS;
77
use rustc_middle::ty::elaborate::ClauseWithSupertraitSpan;
88
use rustc_middle::ty::{
9-
self, BottomUpFolder, DynKind, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable,
10-
TypeVisitableExt, Upcast,
9+
self, BottomUpFolder, DynKind, ExistentialPredicateStableCmpExt as _, Interner as _, Ty,
10+
TyCtxt, TypeFoldable, TypeVisitableExt, Upcast,
1111
};
1212
use rustc_span::{ErrorGuaranteed, Span};
1313
use rustc_trait_selection::error_reporting::traits::report_dyn_incompatibility;
@@ -112,9 +112,29 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
112112
// let _: &dyn Alias<Assoc = u32> = /* ... */;
113113
// ```
114114
let mut projection_bounds = FxIndexMap::default();
115+
let mut auto_fill_bounds: SmallVec<[_; 4]> = smallvec![];
116+
let mut push_auto_fill_receiver_bound = |pred: ty::Binder<'tcx, _>| {
117+
let receiver_target_did =
118+
tcx.require_lang_item(rustc_hir::LangItem::ReceiverTarget, None);
119+
let receiver_trait_ref =
120+
tcx.anonymize_bound_vars(pred.map_bound(|proj: ty::ProjectionPredicate<'_>| {
121+
tcx.trait_ref_and_own_args_for_alias(
122+
receiver_target_did,
123+
proj.projection_term.args,
124+
)
125+
.0
126+
}));
127+
let receiver_pred = pred.map_bound(|mut pred| {
128+
pred.projection_term.def_id = receiver_target_did;
129+
pred
130+
});
131+
auto_fill_bounds.push((receiver_target_did, receiver_trait_ref, receiver_pred, span));
132+
};
115133
for (proj, proj_span) in elaborated_projection_bounds {
134+
debug!(?proj, "elaborated proj bound");
135+
let proj_did = proj.skip_binder().projection_term.def_id;
116136
let key = (
117-
proj.skip_binder().projection_term.def_id,
137+
proj_did,
118138
tcx.anonymize_bound_vars(
119139
proj.map_bound(|proj| proj.projection_term.trait_ref(tcx)),
120140
),
@@ -142,6 +162,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
142162
)
143163
.emit();
144164
}
165+
if tcx.is_lang_item(proj_did, rustc_hir::LangItem::DerefTarget) {
166+
push_auto_fill_receiver_bound(proj);
167+
}
145168
}
146169

147170
let principal_trait = regular_traits.into_iter().next();
@@ -221,6 +244,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
221244
if !projection_bounds.contains_key(&key) {
222245
projection_bounds.insert(key, (pred, supertrait_span));
223246
}
247+
if tcx.is_lang_item(
248+
pred.skip_binder().def_id(),
249+
rustc_hir::LangItem::DerefTarget,
250+
) {
251+
// We need to fill in a `Receiver<Target = ?>` bound because
252+
// neither `Receiver` nor `Deref` is an auto-trait.
253+
push_auto_fill_receiver_bound(pred);
254+
}
224255
}
225256

226257
self.check_elaborated_projection_mentions_input_lifetimes(
@@ -233,6 +264,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
233264
}
234265
}
235266
}
267+
for (did, trait_ref, pred, span) in auto_fill_bounds {
268+
if !projection_bounds.contains_key(&(did, trait_ref)) {
269+
projection_bounds.insert((did, trait_ref), (pred, span));
270+
}
271+
}
236272

237273
// `dyn Trait<Assoc = Foo>` desugars to (not Rust syntax) `dyn Trait where
238274
// <Self as Trait>::Assoc = Foo`. So every `Projection` clause is an
@@ -264,6 +300,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
264300
}
265301
})
266302
.collect();
303+
debug!(?missing_assoc_types, ?projection_bounds);
267304

268305
if let Err(guar) = self.check_for_required_assoc_tys(
269306
principal_trait.as_ref().map_or(smallvec![], |(_, spans)| spans.clone()),
@@ -378,6 +415,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
378415
.collect::<SmallVec<[_; 8]>>();
379416
v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
380417
let existential_predicates = tcx.mk_poly_existential_predicates(&v);
418+
debug!(?existential_predicates);
381419

382420
// Use explicitly-specified region bound, unless the bound is missing.
383421
let region_bound = if !lifetime.is_elided() {

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

+91-73
Original file line numberDiff line numberDiff line change
@@ -1036,90 +1036,108 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
10361036
};
10371037
debug!(?bound);
10381038

1039-
if let Some(bound2) = matching_candidates.next() {
1040-
debug!(?bound2);
1039+
let Some(bound2) = matching_candidates.next() else { return Ok(bound) };
1040+
let mut matching_candidates = matching_candidates.peekable();
1041+
1042+
let is_receiver_target = |def_id| tcx.is_lang_item(def_id, rustc_hir::LangItem::Receiver);
1043+
let is_deref_target = |def_id| tcx.is_lang_item(def_id, rustc_hir::LangItem::Deref);
1044+
// Since `Deref::Target` and `Receiver::Target` are forced to be the same,
1045+
// the `Deref::Target` should be taken.
1046+
if assoc_name.name == sym::Target && matching_candidates.peek().is_none() {
1047+
if is_deref_target(bound.skip_binder().def_id)
1048+
&& is_receiver_target(bound2.skip_binder().def_id)
1049+
{
1050+
return Ok(bound);
1051+
}
1052+
if is_receiver_target(bound.skip_binder().def_id)
1053+
&& is_deref_target(bound2.skip_binder().def_id)
1054+
{
1055+
return Ok(bound2);
1056+
}
1057+
}
10411058

1042-
let assoc_kind_str = errors::assoc_kind_str(assoc_kind);
1043-
let qself_str = qself.to_string(tcx);
1044-
let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem {
1045-
span,
1046-
assoc_kind: assoc_kind_str,
1047-
assoc_name,
1048-
qself: &qself_str,
1049-
});
1050-
// Provide a more specific error code index entry for equality bindings.
1051-
err.code(
1052-
if let Some(constraint) = constraint
1053-
&& let hir::AssocItemConstraintKind::Equality { .. } = constraint.kind
1054-
{
1055-
E0222
1056-
} else {
1057-
E0221
1058-
},
1059-
);
1059+
debug!(?bound2);
10601060

1061-
// FIXME(#97583): Print associated item bindings properly (i.e., not as equality predicates!).
1062-
// FIXME: Turn this into a structured, translateable & more actionable suggestion.
1063-
let mut where_bounds = vec![];
1064-
for bound in [bound, bound2].into_iter().chain(matching_candidates) {
1065-
let bound_id = bound.def_id();
1066-
let bound_span = tcx
1067-
.associated_items(bound_id)
1068-
.find_by_name_and_kind(tcx, assoc_name, assoc_kind, bound_id)
1069-
.and_then(|item| tcx.hir().span_if_local(item.def_id));
1070-
1071-
if let Some(bound_span) = bound_span {
1072-
err.span_label(
1073-
bound_span,
1074-
format!("ambiguous `{assoc_name}` from `{}`", bound.print_trait_sugared(),),
1075-
);
1076-
if let Some(constraint) = constraint {
1077-
match constraint.kind {
1078-
hir::AssocItemConstraintKind::Equality { term } => {
1079-
let term: ty::Term<'_> = match term {
1080-
hir::Term::Ty(ty) => self.lower_ty(ty).into(),
1081-
hir::Term::Const(ct) => {
1082-
self.lower_const_arg(ct, FeedConstTy::No).into()
1083-
}
1084-
};
1085-
if term.references_error() {
1086-
continue;
1061+
let assoc_kind_str = errors::assoc_kind_str(assoc_kind);
1062+
let qself_str = qself.to_string(tcx);
1063+
let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem {
1064+
span,
1065+
assoc_kind: assoc_kind_str,
1066+
assoc_name,
1067+
qself: &qself_str,
1068+
});
1069+
// Provide a more specific error code index entry for equality bindings.
1070+
err.code(
1071+
if let Some(constraint) = constraint
1072+
&& let hir::AssocItemConstraintKind::Equality { .. } = constraint.kind
1073+
{
1074+
E0222
1075+
} else {
1076+
E0221
1077+
},
1078+
);
1079+
1080+
// FIXME(#97583): Print associated item bindings properly (i.e., not as equality predicates!).
1081+
// FIXME: Turn this into a structured, translateable & more actionable suggestion.
1082+
let mut where_bounds = vec![];
1083+
for bound in [bound, bound2].into_iter().chain(matching_candidates) {
1084+
let bound_id = bound.def_id();
1085+
let bound_span = tcx
1086+
.associated_items(bound_id)
1087+
.find_by_name_and_kind(tcx, assoc_name, assoc_kind, bound_id)
1088+
.and_then(|item| tcx.hir().span_if_local(item.def_id));
1089+
1090+
if let Some(bound_span) = bound_span {
1091+
err.span_label(
1092+
bound_span,
1093+
format!("ambiguous `{assoc_name}` from `{}`", bound.print_trait_sugared(),),
1094+
);
1095+
if let Some(constraint) = constraint {
1096+
match constraint.kind {
1097+
hir::AssocItemConstraintKind::Equality { term } => {
1098+
let term: ty::Term<'_> = match term {
1099+
hir::Term::Ty(ty) => self.lower_ty(ty).into(),
1100+
hir::Term::Const(ct) => {
1101+
self.lower_const_arg(ct, FeedConstTy::No).into()
10871102
}
1088-
// FIXME(#97583): This isn't syntactically well-formed!
1089-
where_bounds.push(format!(
1090-
" T: {trait}::{assoc_name} = {term}",
1091-
trait = bound.print_only_trait_path(),
1092-
));
1103+
};
1104+
if term.references_error() {
1105+
continue;
10931106
}
1094-
// FIXME: Provide a suggestion.
1095-
hir::AssocItemConstraintKind::Bound { bounds: _ } => {}
1107+
// FIXME(#97583): This isn't syntactically well-formed!
1108+
where_bounds.push(format!(
1109+
" T: {trait}::{assoc_name} = {term}",
1110+
trait = bound.print_only_trait_path(),
1111+
));
10961112
}
1097-
} else {
1098-
err.span_suggestion_verbose(
1099-
span.with_hi(assoc_name.span.lo()),
1100-
"use fully-qualified syntax to disambiguate",
1101-
format!("<{qself_str} as {}>::", bound.print_only_trait_path()),
1102-
Applicability::MaybeIncorrect,
1103-
);
1113+
// FIXME: Provide a suggestion.
1114+
hir::AssocItemConstraintKind::Bound { bounds: _ } => {}
11041115
}
11051116
} else {
1106-
err.note(format!(
1107-
"associated {assoc_kind_str} `{assoc_name}` could derive from `{}`",
1108-
bound.print_only_trait_path(),
1109-
));
1117+
err.span_suggestion_verbose(
1118+
span.with_hi(assoc_name.span.lo()),
1119+
"use fully-qualified syntax to disambiguate",
1120+
format!("<{qself_str} as {}>::", bound.print_only_trait_path()),
1121+
Applicability::MaybeIncorrect,
1122+
);
11101123
}
1111-
}
1112-
if !where_bounds.is_empty() {
1113-
err.help(format!(
1114-
"consider introducing a new type parameter `T` and adding `where` constraints:\
1115-
\n where\n T: {qself_str},\n{}",
1116-
where_bounds.join(",\n"),
1124+
} else {
1125+
err.note(format!(
1126+
"associated {assoc_kind_str} `{assoc_name}` could derive from `{}`",
1127+
bound.print_only_trait_path(),
11171128
));
1118-
let reported = err.emit();
1119-
return Err(reported);
11201129
}
1121-
err.emit();
11221130
}
1131+
if !where_bounds.is_empty() {
1132+
err.help(format!(
1133+
"consider introducing a new type parameter `T` and adding `where` constraints:\
1134+
\n where\n T: {qself_str},\n{}",
1135+
where_bounds.join(",\n"),
1136+
));
1137+
let reported = err.emit();
1138+
return Err(reported);
1139+
}
1140+
err.emit();
11231141

11241142
Ok(bound)
11251143
}

compiler/rustc_middle/src/ty/print/pretty.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -1392,6 +1392,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
13921392
if let Some(bound_principal) = predicates.principal() {
13931393
self.wrap_binder(&bound_principal, WrapBinderMode::ForAll, |principal, cx| {
13941394
define_scoped_cx!(cx);
1395+
let principal_deref_trait =
1396+
cx.tcx().is_lang_item(principal.def_id, LangItem::Deref);
13951397
p!(print_def_path(principal.def_id, &[]));
13961398

13971399
let mut resugared = false;
@@ -1451,7 +1453,11 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
14511453
let super_proj = cx.tcx().anonymize_bound_vars(super_proj);
14521454

14531455
proj == super_proj
1454-
});
1456+
}) || (principal_deref_trait
1457+
&& cx.tcx().is_lang_item(
1458+
proj.skip_binder().def_id,
1459+
LangItem::ReceiverTarget,
1460+
));
14551461
!proj_is_implied
14561462
})
14571463
.map(|proj| {

compiler/rustc_middle/src/ty/sty.rs

+16-19
Original file line numberDiff line numberDiff line change
@@ -721,26 +721,23 @@ impl<'tcx> Ty<'tcx> {
721721
) -> Ty<'tcx> {
722722
if cfg!(debug_assertions) {
723723
let projection_count = obj.projection_bounds().count();
724-
let expected_count: usize = obj
725-
.principal_def_id()
726-
.into_iter()
727-
.flat_map(|principal_def_id| {
728-
// NOTE: This should agree with `needed_associated_types` in
729-
// dyn trait lowering, or else we'll have ICEs.
730-
elaborate::supertraits(
731-
tcx,
732-
ty::Binder::dummy(ty::TraitRef::identity(tcx, principal_def_id)),
733-
)
734-
.map(|principal| {
735-
tcx.associated_items(principal.def_id())
736-
.in_definition_order()
737-
.filter(|item| item.kind == ty::AssocKind::Type)
738-
.filter(|item| !item.is_impl_trait_in_trait())
739-
.filter(|item| !tcx.generics_require_sized_self(item.def_id))
740-
.count()
741-
})
724+
let expected_count = obj.principal_def_id().map_or(0, |principal_def_id| {
725+
// NOTE: This should agree with `needed_associated_types` in
726+
// dyn trait lowering, or else we'll have ICEs.
727+
elaborate::supertraits(
728+
tcx,
729+
ty::Binder::dummy(ty::TraitRef::identity(tcx, principal_def_id)),
730+
)
731+
.map(|principal| {
732+
tcx.associated_items(principal.def_id())
733+
.in_definition_order()
734+
.filter(|item| item.kind == ty::AssocKind::Type)
735+
.filter(|item| !item.is_impl_trait_in_trait())
736+
.filter(|item| !tcx.generics_require_sized_self(item.def_id))
737+
.count()
742738
})
743-
.sum();
739+
.sum()
740+
});
744741
assert_eq!(
745742
projection_count, expected_count,
746743
"expected {obj:?} to have {expected_count} projections, \

0 commit comments

Comments
 (0)