Skip to content

Commit c4af6e0

Browse files
authored
Rollup merge of rust-lang#71618 - ecstatic-morse:issue-71394, r=nikomatsakis
Preserve substitutions when making trait obligations for suggestions Resolves rust-lang#71394. I *think* `map_bound_ref` is correct here. In any case, I think a lot of the diagnostic code is using `skip_binder` more aggressively than it should be, so I doubt that this is worse than the status quo. The assertion that `new_self_ty` has no escaping bound vars should be enough. r? @estebank cc @nikomatsakis Is the call to `skip_binder` on line 551 (and elsewhere in this file) appropriate? https://github.com/rust-lang/rust/blob/46ec74e60f238f694b46c976d6217e7cf8d4cf1a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs#L537-L565
2 parents 8970e8b + 1fad3b7 commit c4af6e0

File tree

5 files changed

+69
-43
lines changed

5 files changed

+69
-43
lines changed

src/librustc_trait_selection/traits/error_reporting/mod.rs

+22-11
Original file line numberDiff line numberDiff line change
@@ -1000,12 +1000,15 @@ trait InferCtxtPrivExt<'tcx> {
10001000
trait_ref: &ty::PolyTraitRef<'tcx>,
10011001
);
10021002

1003-
fn mk_obligation_for_def_id(
1003+
/// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the
1004+
/// `trait_ref`.
1005+
///
1006+
/// For this to work, `new_self_ty` must have no escaping bound variables.
1007+
fn mk_trait_obligation_with_new_self_ty(
10041008
&self,
1005-
def_id: DefId,
1006-
output_ty: Ty<'tcx>,
1007-
cause: ObligationCause<'tcx>,
10081009
param_env: ty::ParamEnv<'tcx>,
1010+
trait_ref: &ty::PolyTraitRef<'tcx>,
1011+
new_self_ty: Ty<'tcx>,
10091012
) -> PredicateObligation<'tcx>;
10101013

10111014
fn maybe_report_ambiguity(
@@ -1380,16 +1383,24 @@ impl<'a, 'tcx> InferCtxtPrivExt<'tcx> for InferCtxt<'a, 'tcx> {
13801383
}
13811384
}
13821385

1383-
fn mk_obligation_for_def_id(
1386+
fn mk_trait_obligation_with_new_self_ty(
13841387
&self,
1385-
def_id: DefId,
1386-
output_ty: Ty<'tcx>,
1387-
cause: ObligationCause<'tcx>,
13881388
param_env: ty::ParamEnv<'tcx>,
1389+
trait_ref: &ty::PolyTraitRef<'tcx>,
1390+
new_self_ty: Ty<'tcx>,
13891391
) -> PredicateObligation<'tcx> {
1390-
let new_trait_ref =
1391-
ty::TraitRef { def_id, substs: self.tcx.mk_substs_trait(output_ty, &[]) };
1392-
Obligation::new(cause, param_env, new_trait_ref.without_const().to_predicate(self.tcx))
1392+
assert!(!new_self_ty.has_escaping_bound_vars());
1393+
1394+
let trait_ref = trait_ref.map_bound_ref(|tr| ty::TraitRef {
1395+
substs: self.tcx.mk_substs_trait(new_self_ty, &tr.substs[1..]),
1396+
..*tr
1397+
});
1398+
1399+
Obligation::new(
1400+
ObligationCause::dummy(),
1401+
param_env,
1402+
trait_ref.without_const().to_predicate(self.tcx),
1403+
)
13931404
}
13941405

13951406
fn maybe_report_ambiguity(

src/librustc_trait_selection/traits/error_reporting/suggestions.rs

+31-31
Original file line numberDiff line numberDiff line change
@@ -532,14 +532,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
532532
};
533533
let msg = format!("use parentheses to call the {}", callable);
534534

535-
let obligation = self.mk_obligation_for_def_id(
536-
trait_ref.def_id(),
537-
output_ty.skip_binder(),
538-
obligation.cause.clone(),
539-
obligation.param_env,
540-
);
535+
// `mk_trait_obligation_with_new_self_ty` only works for types with no escaping bound
536+
// variables, so bail out if we have any.
537+
let output_ty = match output_ty.no_bound_vars() {
538+
Some(ty) => ty,
539+
None => return,
540+
};
541+
542+
let new_obligation =
543+
self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_ref, output_ty);
541544

542-
match self.evaluate_obligation(&obligation) {
545+
match self.evaluate_obligation(&new_obligation) {
543546
Ok(
544547
EvaluationResult::EvaluatedToOk
545548
| EvaluationResult::EvaluatedToOkModuloRegions
@@ -694,7 +697,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
694697
err: &mut DiagnosticBuilder<'_>,
695698
trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
696699
) {
697-
let trait_ref = trait_ref.skip_binder();
698700
let span = obligation.cause.span;
699701

700702
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
@@ -705,17 +707,16 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
705707
return;
706708
}
707709

708-
let mut trait_type = trait_ref.self_ty();
710+
let mut suggested_ty = trait_ref.self_ty();
709711

710712
for refs_remaining in 0..refs_number {
711-
if let ty::Ref(_, t_type, _) = trait_type.kind {
712-
trait_type = t_type;
713+
if let ty::Ref(_, inner_ty, _) = suggested_ty.kind {
714+
suggested_ty = inner_ty;
713715

714-
let new_obligation = self.mk_obligation_for_def_id(
715-
trait_ref.def_id,
716-
trait_type,
717-
ObligationCause::dummy(),
716+
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
718717
obligation.param_env,
718+
trait_ref,
719+
suggested_ty,
719720
);
720721

721722
if self.predicate_may_hold(&new_obligation) {
@@ -782,20 +783,20 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
782783
return;
783784
}
784785

785-
let trait_type = match mutability {
786+
let suggested_ty = match mutability {
786787
hir::Mutability::Mut => self.tcx.mk_imm_ref(region, t_type),
787788
hir::Mutability::Not => self.tcx.mk_mut_ref(region, t_type),
788789
};
789790

790-
let new_obligation = self.mk_obligation_for_def_id(
791-
trait_ref.skip_binder().def_id,
792-
trait_type,
793-
ObligationCause::dummy(),
791+
let new_obligation = self.mk_trait_obligation_with_new_self_ty(
794792
obligation.param_env,
793+
&trait_ref,
794+
suggested_ty,
795795
);
796-
797-
if self.evaluate_obligation_no_overflow(&new_obligation).must_apply_modulo_regions()
798-
{
796+
let suggested_ty_would_satisfy_obligation = self
797+
.evaluate_obligation_no_overflow(&new_obligation)
798+
.must_apply_modulo_regions();
799+
if suggested_ty_would_satisfy_obligation {
799800
let sp = self
800801
.tcx
801802
.sess
@@ -812,7 +813,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
812813
err.note(&format!(
813814
"`{}` is implemented for `{:?}`, but not for `{:?}`",
814815
trait_ref.print_only_trait_path(),
815-
trait_type,
816+
suggested_ty,
816817
trait_ref.skip_binder().self_ty(),
817818
));
818819
}
@@ -1891,7 +1892,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
18911892
span: Span,
18921893
) {
18931894
debug!(
1894-
"suggest_await_befor_try: obligation={:?}, span={:?}, trait_ref={:?}, trait_ref_self_ty={:?}",
1895+
"suggest_await_before_try: obligation={:?}, span={:?}, trait_ref={:?}, trait_ref_self_ty={:?}",
18951896
obligation,
18961897
span,
18971898
trait_ref,
@@ -1946,16 +1947,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
19461947
);
19471948

19481949
debug!(
1949-
"suggest_await_befor_try: normalized_projection_type {:?}",
1950+
"suggest_await_before_try: normalized_projection_type {:?}",
19501951
self.resolve_vars_if_possible(&normalized_ty)
19511952
);
1952-
let try_obligation = self.mk_obligation_for_def_id(
1953-
trait_ref.def_id(),
1954-
normalized_ty,
1955-
obligation.cause.clone(),
1953+
let try_obligation = self.mk_trait_obligation_with_new_self_ty(
19561954
obligation.param_env,
1955+
trait_ref,
1956+
normalized_ty,
19571957
);
1958-
debug!("suggest_await_befor_try: try_trait_obligation {:?}", try_obligation);
1958+
debug!("suggest_await_before_try: try_trait_obligation {:?}", try_obligation);
19591959
if self.predicate_may_hold(&try_obligation) && impls_future {
19601960
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
19611961
if snippet.ends_with('?') {

src/test/ui/suggestions/into-str.stderr

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ LL | foo(String::new());
88
| ^^^ the trait `std::convert::From<std::string::String>` is not implemented for `&str`
99
|
1010
= note: to coerce a `std::string::String` into a `&str`, use `&*` as a prefix
11-
= note: `std::convert::From<std::string::String>` is implemented for `&mut str`, but not for `&str`
1211
= note: required because of the requirements on the impl of `std::convert::Into<&str>` for `std::string::String`
1312

1413
error: aborting due to previous error
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
fn main() {
2+
let data: &[u8] = &[0; 10];
3+
let _: &[i8] = data.into();
4+
//~^ ERROR the trait bound `&[i8]: std::convert::From<&[u8]>` is not satisfied
5+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0277]: the trait bound `&[i8]: std::convert::From<&[u8]>` is not satisfied
2+
--> $DIR/issue-71394-no-from-impl.rs:3:25
3+
|
4+
LL | let _: &[i8] = data.into();
5+
| ^^^^ the trait `std::convert::From<&[u8]>` is not implemented for `&[i8]`
6+
|
7+
= note: required because of the requirements on the impl of `std::convert::Into<&[i8]>` for `&[u8]`
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)