Skip to content

Commit fdad1f9

Browse files
committed
allow method resolution with Self Sized bound on methods with reference rcvr type
1 parent f24ce9b commit fdad1f9

File tree

2 files changed

+274
-27
lines changed

2 files changed

+274
-27
lines changed

compiler/rustc_typeck/src/check/method/confirm.rs

+224-26
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::hir::def_id::DefId;
66
use crate::hir::GenericArg;
77
use rustc_hir as hir;
88
use rustc_infer::infer::{self, InferOk};
9+
use rustc_infer::traits::EvaluationResult;
910
use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext};
1011
use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
1112
use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
@@ -14,7 +15,9 @@ use rustc_middle::ty::subst::{self, Subst, SubstsRef};
1415
use rustc_middle::ty::{self, GenericParamDefKind, Ty};
1516
use rustc_span::Span;
1617
use rustc_trait_selection::traits;
18+
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
1719

20+
use std::collections::hash_map::Entry;
1821
use std::ops::Deref;
1922

2023
struct ConfirmContext<'a, 'tcx> {
@@ -55,6 +58,26 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5558
let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr);
5659
confirm_cx.confirm(unadjusted_self_ty, pick, segment)
5760
}
61+
62+
fn apply_adjustments_for_sized_bound(
63+
&self,
64+
expr: &hir::Expr<'tcx>,
65+
adj: Vec<Adjustment<'tcx>>,
66+
) {
67+
debug!("apply_adjustments_for_sized_bound(expr={:?}, adj={:?})", expr, adj);
68+
if adj.is_empty() {
69+
return;
70+
}
71+
72+
match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) {
73+
Entry::Vacant(entry) => {
74+
entry.insert(adj);
75+
}
76+
Entry::Occupied(mut entry) => {
77+
*entry.get_mut() = adj;
78+
}
79+
}
80+
}
5881
}
5982

6083
impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
@@ -74,16 +97,12 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
7497
segment: &hir::PathSegment<'_>,
7598
) -> ConfirmResult<'tcx> {
7699
// Adjust the self expression the user provided and obtain the adjusted type.
77-
let self_ty = self.adjust_self_ty(unadjusted_self_ty, &pick);
78-
79-
// Create substitutions for the method's type parameters.
80-
let rcvr_substs = self.fresh_receiver_substs(self_ty, &pick);
81-
let all_substs = self.instantiate_method_substs(&pick, segment, rcvr_substs);
82-
83-
debug!("all_substs={:?}", all_substs);
100+
let (adjusted_self_ty, autoderef_obligations) =
101+
self.adjust_self_ty(unadjusted_self_ty, &pick, FnCtxt::apply_adjustments);
84102

85103
// Create the final signature for the method, replacing late-bound regions.
86-
let (method_sig, method_predicates) = self.instantiate_method_sig(&pick, all_substs);
104+
let (all_substs, method_sig, method_predicates) =
105+
self.create_substs_and_instantiate_method_sig(adjusted_self_ty, &pick, segment);
87106

88107
// Unify the (adjusted) self type with what the method expects.
89108
//
@@ -92,15 +111,16 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
92111
// could alter our Self-type, except for normalizing the receiver from the
93112
// signature (which is also done during probing).
94113
let method_sig_rcvr = self.normalize_associated_types_in(self.span, method_sig.inputs()[0]);
95-
debug!(
96-
"confirm: self_ty={:?} method_sig_rcvr={:?} method_sig={:?} method_predicates={:?}",
97-
self_ty, method_sig_rcvr, method_sig, method_predicates
98-
);
99-
self.unify_receivers(self_ty, method_sig_rcvr, &pick, all_substs);
114+
self.unify_receivers(adjusted_self_ty, method_sig_rcvr, &pick, all_substs);
100115

101116
let (method_sig, method_predicates) =
102117
self.normalize_associated_types_in(self.span, (method_sig, method_predicates));
103118

119+
debug!(
120+
"confirm: adjusted_self_ty={:?} method_sig_rcvr={:?} method_sig={:?} method_predicates={:?}",
121+
adjusted_self_ty, method_sig_rcvr, method_sig, method_predicates
122+
);
123+
104124
// Make sure nobody calls `drop()` explicitly.
105125
self.enforce_illegal_method_limitations(&pick);
106126

@@ -114,12 +134,31 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
114134
// appropriate hint suggesting to import the trait.
115135
let illegal_sized_bound = self.predicates_require_illegal_sized_bound(&method_predicates);
116136

117-
// Add any trait/regions obligations specified on the method's type parameters.
118-
// We won't add these if we encountered an illegal sized bound, so that we can use
119-
// a custom error in that case.
120-
if illegal_sized_bound.is_none() {
121-
let method_ty = self.tcx.mk_fn_ptr(ty::Binder::bind(method_sig));
122-
self.add_obligations(method_ty, all_substs, method_predicates);
137+
match illegal_sized_bound {
138+
Some(_) => {
139+
// try to autoref receiver to fulfill sized bound (see issue #82825 for an example of
140+
// why this might be necessary)
141+
if let Some(sized_confirm_result) = self.try_autoref_for_sized_bound(
142+
unadjusted_self_ty,
143+
method_sig_rcvr,
144+
pick.clone(),
145+
&segment,
146+
) {
147+
return sized_confirm_result;
148+
}
149+
}
150+
None => {
151+
// We only register predicates from adjusting `self_ty` now, since these
152+
// obligations might violate those found in a successful call of
153+
// `try_autoref_for_sized_bound
154+
self.register_predicates(autoderef_obligations);
155+
156+
// Add any trait/regions obligations specified on the method's type parameters.
157+
// We won't add these if we encountered an illegal sized bound, so that we can use
158+
// a custom error in that case.
159+
let method_ty = self.tcx.mk_fn_ptr(ty::Binder::bind(method_sig));
160+
self.add_obligations(method_ty, all_substs, method_predicates);
161+
}
123162
}
124163

125164
// Create the final `MethodCallee`.
@@ -134,17 +173,21 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
134173
&mut self,
135174
unadjusted_self_ty: Ty<'tcx>,
136175
pick: &probe::Pick<'tcx>,
137-
) -> Ty<'tcx> {
176+
apply_adjustments: fn(&FnCtxt<'a, 'tcx>, &hir::Expr<'tcx>, Vec<Adjustment<'tcx>>) -> (),
177+
) -> (Ty<'tcx>, Vec<traits::PredicateObligation<'tcx>>) {
138178
// Commit the autoderefs by calling `autoderef` again, but this
139179
// time writing the results into the various typeck results.
140180
let mut autoderef =
141181
self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span);
142182
let (_, n) = match autoderef.nth(pick.autoderefs) {
143183
Some(n) => n,
144184
None => {
145-
return self.tcx.ty_error_with_message(
146-
rustc_span::DUMMY_SP,
147-
&format!("failed autoderef {}", pick.autoderefs),
185+
return (
186+
self.tcx.ty_error_with_message(
187+
rustc_span::DUMMY_SP,
188+
&format!("failed autoderef {}", pick.autoderefs),
189+
),
190+
vec![],
148191
);
149192
}
150193
};
@@ -197,12 +240,12 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
197240
None => {}
198241
}
199242

200-
self.register_predicates(autoderef.into_obligations());
243+
let autoderef_obligations = autoderef.into_obligations();
201244

202245
// Write out the final adjustments.
203-
self.apply_adjustments(self.self_expr, adjustments);
246+
apply_adjustments(self, self.self_expr, adjustments);
204247

205-
target
248+
(target, autoderef_obligations)
206249
}
207250

208251
/// Returns a set of substitutions for the method *receiver* where all type and region
@@ -557,4 +600,159 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
557600
{
558601
self.fcx.replace_bound_vars_with_fresh_vars(self.span, infer::FnCall, value).0
559602
}
603+
604+
fn create_substs_and_instantiate_method_sig(
605+
&mut self,
606+
self_ty: Ty<'tcx>,
607+
pick: &probe::Pick<'tcx>,
608+
segment: &hir::PathSegment<'_>,
609+
) -> (SubstsRef<'tcx>, ty::FnSig<'tcx>, ty::InstantiatedPredicates<'tcx>) {
610+
// Create substitutions for the method's type parameters.
611+
let rcvr_substs = self.fresh_receiver_substs(self_ty, &pick);
612+
let all_substs = self.instantiate_method_substs(pick, segment, rcvr_substs);
613+
614+
debug!("all_substs={:?}", all_substs);
615+
616+
// Create the final signature for the method, replacing late-bound regions.
617+
let (method_sig, method_predicates) = self.instantiate_method_sig(pick, all_substs);
618+
619+
(all_substs, method_sig, method_predicates)
620+
}
621+
622+
/// In situations in which we have a trait method that takes a reference receiver type,
623+
/// a Self : Sized bound and a trait implementation for a reference type, the adjustments
624+
/// that the compiler applies in `ProbeMode::Normal` are insufficient to fulfill the Sized bound.
625+
/// Example:
626+
///
627+
/// ```
628+
/// trait Trait {
629+
/// fn foo(&self) where Self: Sized;
630+
///
631+
/// impl<T: ?Sized + Trait> Trait for &T {
632+
/// fn foo(&self) where Self: Sized {
633+
/// ...
634+
/// }
635+
///
636+
/// fn bar(x: &dyn Trait) {
637+
/// // The compiler needs to include an additional autoref here, a Deref -> Borrow adjustment
638+
/// // will not pick the correct trait implementation
639+
/// x.foo()
640+
/// ```
641+
///
642+
/// Specifically in `ProbeMode::Normal` we deref to the base type and then autoref once,
643+
/// which fulfills the reference receiver type, but violates the `Sized` bound on `Self`.
644+
/// In order to generate the correct predicate (i.e. <&dyn Trait as Trait> we try create a
645+
/// `&&` receiver type during method probing, try to confirm the probe and see if all obligations hold.
646+
/// See issue #82825 for further details.
647+
fn try_autoref_for_sized_bound(
648+
&mut self,
649+
unadjusted_self_ty: Ty<'tcx>,
650+
method_rcvr: Ty<'tcx>,
651+
pick: probe::Pick<'tcx>,
652+
segment: &hir::PathSegment<'_>,
653+
) -> Option<ConfirmResult<'tcx>> {
654+
debug!(
655+
"try_autoref_for_sized_bound(unadjusted_self_ty: {:?}, pick: {:?}",
656+
unadjusted_self_ty, pick
657+
);
658+
659+
// methods that move dyn values always violate sized constraint.
660+
match method_rcvr.kind() {
661+
ty::Ref(_, _, _) => {}
662+
_ => {
663+
return None;
664+
}
665+
}
666+
667+
let mode = probe::Mode::MethodCall;
668+
let self_ty = self.resolve_vars_if_possible(unadjusted_self_ty);
669+
670+
let mut new_pick = match self.probe_for_name_with_sized_bound(
671+
self.span,
672+
mode,
673+
segment.ident,
674+
probe::IsSuggestion(false),
675+
self_ty,
676+
self.call_expr.hir_id,
677+
probe::ProbeScope::TraitsInScope,
678+
) {
679+
Ok(new_pick) => new_pick,
680+
_ => {
681+
return None;
682+
}
683+
};
684+
685+
debug!("new_pick: {:?}", new_pick);
686+
687+
// Note: The previous, failed `confirm` call already set adjustments in `self.typeck_results`.
688+
// Since `TyCtxt::apply_adjustments` cannot compose `Borrow` adjustments on top of previous
689+
// adjustments, which do not contain `Deref` adjustments, we need to use
690+
// `FnCtxt::apply_adjustments_for_sized_bound` to apply the adjustments generated by
691+
// `probe_for_name_with_sized_bound`.
692+
let (adjusted_self_ty, autoderef_obligations) = self.adjust_self_ty(
693+
unadjusted_self_ty,
694+
&mut new_pick,
695+
FnCtxt::apply_adjustments_for_sized_bound,
696+
);
697+
debug!(
698+
"try_autoref_for_sized_bound: self_ty after adjust_self_ty call is {:?}",
699+
adjusted_self_ty
700+
);
701+
702+
// Create the final signature for the method, replacing late-bound regions.
703+
let (all_substs, method_sig, method_predicates) =
704+
self.create_substs_and_instantiate_method_sig(adjusted_self_ty, &new_pick, segment);
705+
706+
// Unify the (adjusted) self type with what the method expects.
707+
//
708+
// SUBTLE: if we want good error messages, because of "guessing" while matching
709+
// traits, no trait system method can be called before this point because they
710+
// could alter our Self-type, except for normalizing the receiver from the
711+
// signature (which is also done during probing).
712+
let method_sig_rcvr = self.normalize_associated_types_in(self.span, method_sig.inputs()[0]);
713+
self.unify_receivers(adjusted_self_ty, method_sig_rcvr, &new_pick, all_substs);
714+
715+
let (method_sig, method_predicates) =
716+
self.normalize_associated_types_in(self.span, (method_sig, method_predicates));
717+
718+
debug!("method_sig {:?}, predicates: {:?}", method_sig, method_predicates);
719+
720+
let cause = traits::ObligationCause::misc(self.span, self.body_id);
721+
let obligations_satisfied =
722+
traits::predicates_for_generics(cause, self.param_env, method_predicates.clone()).all(
723+
|o| {
724+
debug!("obligation: {:?}", o);
725+
let eval_result = self.infcx.evaluate_obligation(&o);
726+
debug!("evaluation result: {:?}", eval_result);
727+
match eval_result {
728+
Ok(EvaluationResult::EvaluatedToOk)
729+
| Ok(EvaluationResult::EvaluatedToOkModuloRegions) => true,
730+
_ => false,
731+
}
732+
},
733+
);
734+
735+
if !obligations_satisfied {
736+
return None;
737+
}
738+
739+
self.enforce_illegal_method_limitations(&new_pick);
740+
let illegal_sized_bound = self.predicates_require_illegal_sized_bound(&method_predicates);
741+
742+
match illegal_sized_bound {
743+
Some(_) => {
744+
return None;
745+
}
746+
None => {
747+
self.register_predicates(autoderef_obligations);
748+
let method_ty = self.tcx.mk_fn_ptr(ty::Binder::bind(method_sig));
749+
self.add_obligations(method_ty, all_substs, method_predicates);
750+
}
751+
}
752+
753+
// Create the final `MethodCallee`.
754+
let callee =
755+
MethodCallee { def_id: new_pick.item.def_id, substs: all_substs, sig: method_sig };
756+
Some(ConfirmResult { callee, illegal_sized_bound })
757+
}
560758
}

0 commit comments

Comments
 (0)