Skip to content

Commit a2c2720

Browse files
authored
Rollup merge of #97123 - ricked-twice:issue-96223-clean-fix, r=jackh726
Clean fix for #96223 Okay, so here we are (hopefully) 👍 Closes #96223 Thanks a lot to `@jackh726` for your help and explanation 🙏 - Modified `InferCtxt::mk_trait_obligation_with_new_self_ty` to take as argument a `Binder<(TraitPredicate, Ty)>` instead of a `Binder<TraitPredicate>` and a separate `Ty` with no bound vars. - Modified all call places to avoid calling `Binder::no_bounds_var` or `Binder::skip_binder` when it is not safe. r? `@jackh726`
2 parents 2d95c6a + ac5366b commit a2c2720

File tree

4 files changed

+135
-138
lines changed

4 files changed

+135
-138
lines changed

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

+4-8
Original file line numberDiff line numberDiff line change
@@ -1384,8 +1384,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
13841384
fn mk_trait_obligation_with_new_self_ty(
13851385
&self,
13861386
param_env: ty::ParamEnv<'tcx>,
1387-
trait_ref: ty::PolyTraitPredicate<'tcx>,
1388-
new_self_ty: Ty<'tcx>,
1387+
trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>,
13891388
) -> PredicateObligation<'tcx>;
13901389

13911390
fn maybe_report_ambiguity(
@@ -1923,14 +1922,11 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
19231922
fn mk_trait_obligation_with_new_self_ty(
19241923
&self,
19251924
param_env: ty::ParamEnv<'tcx>,
1926-
trait_ref: ty::PolyTraitPredicate<'tcx>,
1927-
new_self_ty: Ty<'tcx>,
1925+
trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>,
19281926
) -> PredicateObligation<'tcx> {
1929-
assert!(!new_self_ty.has_escaping_bound_vars());
1930-
1931-
let trait_pred = trait_ref.map_bound_ref(|tr| ty::TraitPredicate {
1927+
let trait_pred = trait_ref_and_ty.map_bound_ref(|(tr, new_self_ty)| ty::TraitPredicate {
19321928
trait_ref: ty::TraitRef {
1933-
substs: self.tcx.mk_substs_trait(new_self_ty, &tr.trait_ref.substs[1..]),
1929+
substs: self.tcx.mk_substs_trait(*new_self_ty, &tr.trait_ref.substs[1..]),
19341930
..tr.trait_ref
19351931
},
19361932
..*tr

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

+126-120
Original file line numberDiff line numberDiff line change
@@ -628,17 +628,21 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
628628
if let Some(parent_trait_pred) = parent_trait_pred {
629629
real_trait_pred = parent_trait_pred;
630630
}
631-
let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else {
632-
continue;
633-
};
631+
632+
// Skipping binder here, remapping below
633+
let real_ty = real_trait_pred.self_ty().skip_binder();
634634

635635
if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
636636
let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span);
637637
if let Some(steps) = autoderef.find_map(|(ty, steps)| {
638638
// Re-add the `&`
639639
let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
640-
let obligation =
641-
self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty);
640+
641+
// Remapping bound vars here
642+
let real_trait_pred_and_ty =
643+
real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty));
644+
let obligation = self
645+
.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred_and_ty);
642646
Some(steps).filter(|_| self.predicate_may_hold(&obligation))
643647
}) {
644648
if steps > 0 {
@@ -659,10 +663,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
659663
}
660664
} else if real_trait_pred != trait_pred {
661665
// This branch addresses #87437.
666+
667+
// Remapping bound vars here
668+
let real_trait_pred_and_base_ty =
669+
real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, base_ty));
662670
let obligation = self.mk_trait_obligation_with_new_self_ty(
663671
param_env,
664-
real_trait_pred,
665-
base_ty,
672+
real_trait_pred_and_base_ty,
666673
);
667674
if self.predicate_may_hold(&obligation) {
668675
err.span_suggestion_verbose(
@@ -720,9 +727,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
720727
err: &mut Diagnostic,
721728
trait_pred: ty::PolyTraitPredicate<'tcx>,
722729
) -> bool {
723-
let Some(self_ty) = trait_pred.self_ty().no_bound_vars() else {
724-
return false;
725-
};
730+
// Skipping binder here, remapping below
731+
let self_ty = trait_pred.self_ty().skip_binder();
726732

727733
let (def_id, output_ty, callable) = match *self_ty.kind() {
728734
ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"),
@@ -731,14 +737,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
731737
};
732738
let msg = format!("use parentheses to call the {}", callable);
733739

734-
// `mk_trait_obligation_with_new_self_ty` only works for types with no escaping bound
735-
// variables, so bail out if we have any.
736-
let Some(output_ty) = output_ty.no_bound_vars() else {
737-
return false;
738-
};
740+
// "We should really create a single list of bound vars from the combined vars
741+
// from the predicate and function, but instead we just liberate the function bound vars"
742+
let output_ty = self.tcx.liberate_late_bound_regions(def_id, output_ty);
743+
744+
// Remapping bound vars here
745+
let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output_ty));
739746

740747
let new_obligation =
741-
self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred, output_ty);
748+
self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self);
742749

743750
match self.evaluate_obligation(&new_obligation) {
744751
Ok(
@@ -842,96 +849,97 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
842849
let param_env = obligation.param_env;
843850

844851
// Try to apply the original trait binding obligation by borrowing.
845-
let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>,
846-
blacklist: &[DefId]|
847-
-> bool {
848-
if blacklist.contains(&old_pred.def_id()) {
849-
return false;
850-
}
851-
852-
// This is a quick fix to resolve an ICE (#96223).
853-
// This change should probably be deeper.
854-
// As suggested by @jackh726, `mk_trait_obligation_with_new_self_ty` could take a `Binder<(TraitRef, Ty)>
855-
// instead of `Binder<Ty>` leading to some changes to its call places.
856-
let Some(orig_ty) = old_pred.self_ty().no_bound_vars() else {
857-
return false;
858-
};
859-
let mk_result = |new_ty| {
860-
let obligation =
861-
self.mk_trait_obligation_with_new_self_ty(param_env, old_pred, new_ty);
862-
self.predicate_must_hold_modulo_regions(&obligation)
863-
};
864-
let imm_result = mk_result(self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, orig_ty));
865-
let mut_result = mk_result(self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, orig_ty));
866-
867-
if imm_result || mut_result {
868-
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
869-
// We have a very specific type of error, where just borrowing this argument
870-
// might solve the problem. In cases like this, the important part is the
871-
// original type obligation, not the last one that failed, which is arbitrary.
872-
// Because of this, we modify the error to refer to the original obligation and
873-
// return early in the caller.
874-
875-
let msg = format!(
876-
"the trait bound `{}: {}` is not satisfied",
877-
orig_ty,
878-
old_pred.print_modifiers_and_trait_path(),
879-
);
880-
if has_custom_message {
881-
err.note(&msg);
882-
} else {
883-
err.message =
884-
vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)];
885-
}
886-
if snippet.starts_with('&') {
887-
// This is already a literal borrow and the obligation is failing
888-
// somewhere else in the obligation chain. Do not suggest non-sense.
889-
return false;
890-
}
891-
err.span_label(
892-
span,
893-
&format!(
894-
"expected an implementor of trait `{}`",
895-
old_pred.print_modifiers_and_trait_path(),
896-
),
897-
);
852+
let mut try_borrowing =
853+
|old_pred: ty::PolyTraitPredicate<'tcx>, blacklist: &[DefId]| -> bool {
854+
if blacklist.contains(&old_pred.def_id()) {
855+
return false;
856+
}
857+
// We map bounds to `&T` and `&mut T`
858+
let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {
859+
(
860+
trait_pred,
861+
self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
862+
)
863+
});
864+
let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {
865+
(
866+
trait_pred,
867+
self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, trait_pred.self_ty()),
868+
)
869+
});
898870

899-
// This if is to prevent a special edge-case
900-
if matches!(
901-
span.ctxt().outer_expn_data().kind,
902-
ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)
903-
) {
904-
// We don't want a borrowing suggestion on the fields in structs,
905-
// ```
906-
// struct Foo {
907-
// the_foos: Vec<Foo>
908-
// }
909-
// ```
910-
911-
if imm_result && mut_result {
912-
err.span_suggestions(
913-
span.shrink_to_lo(),
914-
"consider borrowing here",
915-
["&".to_string(), "&mut ".to_string()].into_iter(),
916-
Applicability::MaybeIncorrect,
917-
);
871+
let mk_result = |trait_pred_and_new_ty| {
872+
let obligation =
873+
self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty);
874+
self.predicate_must_hold_modulo_regions(&obligation)
875+
};
876+
let imm_result = mk_result(trait_pred_and_imm_ref);
877+
let mut_result = mk_result(trait_pred_and_mut_ref);
878+
879+
if imm_result || mut_result {
880+
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
881+
// We have a very specific type of error, where just borrowing this argument
882+
// might solve the problem. In cases like this, the important part is the
883+
// original type obligation, not the last one that failed, which is arbitrary.
884+
// Because of this, we modify the error to refer to the original obligation and
885+
// return early in the caller.
886+
887+
let msg = format!("the trait bound `{}` is not satisfied", old_pred);
888+
if has_custom_message {
889+
err.note(&msg);
918890
} else {
919-
err.span_suggestion_verbose(
920-
span.shrink_to_lo(),
921-
&format!(
922-
"consider{} borrowing here",
923-
if mut_result { " mutably" } else { "" }
924-
),
925-
format!("&{}", if mut_result { "mut " } else { "" }),
926-
Applicability::MaybeIncorrect,
927-
);
891+
err.message =
892+
vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)];
928893
}
894+
if snippet.starts_with('&') {
895+
// This is already a literal borrow and the obligation is failing
896+
// somewhere else in the obligation chain. Do not suggest non-sense.
897+
return false;
898+
}
899+
err.span_label(
900+
span,
901+
&format!(
902+
"expected an implementor of trait `{}`",
903+
old_pred.print_modifiers_and_trait_path(),
904+
),
905+
);
906+
907+
// This if is to prevent a special edge-case
908+
if matches!(
909+
span.ctxt().outer_expn_data().kind,
910+
ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)
911+
) {
912+
// We don't want a borrowing suggestion on the fields in structs,
913+
// ```
914+
// struct Foo {
915+
// the_foos: Vec<Foo>
916+
// }
917+
// ```
918+
919+
if imm_result && mut_result {
920+
err.span_suggestions(
921+
span.shrink_to_lo(),
922+
"consider borrowing here",
923+
["&".to_string(), "&mut ".to_string()].into_iter(),
924+
Applicability::MaybeIncorrect,
925+
);
926+
} else {
927+
err.span_suggestion_verbose(
928+
span.shrink_to_lo(),
929+
&format!(
930+
"consider{} borrowing here",
931+
if mut_result { " mutably" } else { "" }
932+
),
933+
format!("&{}", if mut_result { "mut " } else { "" }),
934+
Applicability::MaybeIncorrect,
935+
);
936+
}
937+
}
938+
return true;
929939
}
930-
return true;
931940
}
932-
}
933-
return false;
934-
};
941+
return false;
942+
};
935943

936944
if let ObligationCauseCode::ImplDerivedObligation(cause) = &*code {
937945
try_borrowing(cause.derived.parent_trait_pred, &[])
@@ -992,20 +1000,22 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
9921000
return false;
9931001
}
9941002

995-
let Some(mut suggested_ty) = trait_pred.self_ty().no_bound_vars() else {
996-
return false;
997-
};
1003+
// Skipping binder here, remapping below
1004+
let mut suggested_ty = trait_pred.self_ty().skip_binder();
9981005

9991006
for refs_remaining in 0..refs_number {
10001007
let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else {
10011008
break;
10021009
};
10031010
suggested_ty = *inner_ty;
10041011

1012+
// Remapping bound vars here
1013+
let trait_pred_and_suggested_ty =
1014+
trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty));
1015+
10051016
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
10061017
obligation.param_env,
1007-
trait_pred,
1008-
suggested_ty,
1018+
trait_pred_and_suggested_ty,
10091019
);
10101020

10111021
if self.predicate_may_hold(&new_obligation) {
@@ -1125,26 +1135,21 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
11251135
return;
11261136
}
11271137

1138+
// Skipping binder here, remapping below
11281139
if let ty::Ref(region, t_type, mutability) = *trait_pred.skip_binder().self_ty().kind()
11291140
{
1130-
if region.is_late_bound() || t_type.has_escaping_bound_vars() {
1131-
// Avoid debug assertion in `mk_obligation_for_def_id`.
1132-
//
1133-
// If the self type has escaping bound vars then it's not
1134-
// going to be the type of an expression, so the suggestion
1135-
// probably won't apply anyway.
1136-
return;
1137-
}
1138-
11391141
let suggested_ty = match mutability {
11401142
hir::Mutability::Mut => self.tcx.mk_imm_ref(region, t_type),
11411143
hir::Mutability::Not => self.tcx.mk_mut_ref(region, t_type),
11421144
};
11431145

1146+
// Remapping bound vars here
1147+
let trait_pred_and_suggested_ty =
1148+
trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty));
1149+
11441150
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
11451151
obligation.param_env,
1146-
trait_pred,
1147-
suggested_ty,
1152+
trait_pred_and_suggested_ty,
11481153
);
11491154
let suggested_ty_would_satisfy_obligation = self
11501155
.evaluate_obligation_no_overflow(&new_obligation)
@@ -1195,7 +1200,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
11951200
// Only suggest this if the expression behind the semicolon implements the predicate
11961201
&& let Some(typeck_results) = self.in_progress_typeck_results
11971202
&& let Some(ty) = typeck_results.borrow().expr_ty_opt(expr)
1198-
&& self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred, ty))
1203+
&& self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty(
1204+
obligation.param_env, trait_pred.map_bound(|trait_pred| (trait_pred, ty))
1205+
))
11991206
{
12001207
err.span_label(
12011208
expr.span,
@@ -2727,8 +2734,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
27272734
);
27282735
let try_obligation = self.mk_trait_obligation_with_new_self_ty(
27292736
obligation.param_env,
2730-
trait_pred,
2731-
normalized_ty.ty().unwrap(),
2737+
trait_pred.map_bound(|trait_pred| (trait_pred, normalized_ty.ty().unwrap())),
27322738
);
27332739
debug!("suggest_await_before_try: try_trait_obligation {:?}", try_obligation);
27342740
if self.predicate_may_hold(&try_obligation)

src/test/ui/binop/issue-77910-1.stderr

+4-10
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,14 @@ LL | assert_eq!(foo, y);
1212
error[E0277]: `for<'r> fn(&'r i32) -> &'r i32 {foo}` doesn't implement `Debug`
1313
--> $DIR/issue-77910-1.rs:8:5
1414
|
15+
LL | fn foo(s: &i32) -> &i32 {
16+
| --- consider calling this function
17+
...
1518
LL | assert_eq!(foo, y);
1619
| ^^^^^^^^^^^^^^^^^^ `for<'r> fn(&'r i32) -> &'r i32 {foo}` cannot be formatted using `{:?}` because it doesn't implement `Debug`
1720
|
1821
= help: the trait `Debug` is not implemented for `for<'r> fn(&'r i32) -> &'r i32 {foo}`
19-
= help: the following other types implement trait `Debug`:
20-
extern "C" fn() -> Ret
21-
extern "C" fn(A) -> Ret
22-
extern "C" fn(A, ...) -> Ret
23-
extern "C" fn(A, B) -> Ret
24-
extern "C" fn(A, B, ...) -> Ret
25-
extern "C" fn(A, B, C) -> Ret
26-
extern "C" fn(A, B, C, ...) -> Ret
27-
extern "C" fn(A, B, C, D) -> Ret
28-
and 68 others
22+
= help: use parentheses to call the function: `foo(s)`
2923
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
3024

3125
error: aborting due to 2 previous errors

0 commit comments

Comments
 (0)