Skip to content

Commit fe86a84

Browse files
committed
hir typeck: look into nested goals
uses a `ProofTreeVisitor` to look into nested goals when looking at the pending obligations during hir typeck. Used by closure signature inference, coercion, and for async functions.
1 parent 99f8b32 commit fe86a84

File tree

7 files changed

+158
-75
lines changed

7 files changed

+158
-75
lines changed

compiler/rustc_hir_typeck/src/closure.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
342342
ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates(
343343
Ty::new_var(self.tcx, self.root_var(vid)),
344344
closure_kind,
345-
self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)),
345+
self.obligations_for_self_ty(vid)
346+
.into_iter()
347+
.map(|obl| (obl.predicate, obl.cause.span)),
346348
),
347349
ty::FnPtr(sig) => match closure_kind {
348350
hir::ClosureKind::Closure => {
@@ -889,7 +891,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
889891

890892
let output_ty = match *ret_ty.kind() {
891893
ty::Infer(ty::TyVar(ret_vid)) => {
892-
self.obligations_for_self_ty(ret_vid).find_map(|obligation| {
894+
self.obligations_for_self_ty(ret_vid).into_iter().find_map(|obligation| {
893895
get_future_output(obligation.predicate, obligation.cause.span)
894896
})?
895897
}

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
637637

638638
pub(crate) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool {
639639
let sized_did = self.tcx.lang_items().sized_trait();
640-
self.obligations_for_self_ty(self_ty).any(|obligation| {
640+
self.obligations_for_self_ty(self_ty).into_iter().any(|obligation| {
641641
match obligation.predicate.kind().skip_binder() {
642642
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
643643
Some(data.def_id()) == sized_did
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,54 @@
11
use crate::FnCtxt;
2+
use rustc_infer::traits::solve::Goal;
3+
use rustc_infer::traits::{self, ObligationCause};
24
use rustc_middle::ty::{self, Ty};
3-
use rustc_infer::traits;
4-
use rustc_data_structures::captures::Captures;
5+
use rustc_trait_selection::solve::inspect::ProofTreeInferCtxtExt;
6+
use rustc_trait_selection::solve::inspect::{InspectConfig, InspectGoal, ProofTreeVisitor};
57

68
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
79
#[instrument(skip(self), level = "debug")]
8-
pub(crate) fn obligations_for_self_ty<'b>(
9-
&'b self,
10+
pub(crate) fn obligations_for_self_ty(
11+
&self,
1012
self_ty: ty::TyVid,
11-
) -> impl DoubleEndedIterator<Item = traits::PredicateObligation<'tcx>> + Captures<'tcx> + 'b
12-
{
13-
let ty_var_root = self.root_var(self_ty);
14-
trace!("pending_obligations = {:#?}", self.fulfillment_cx.borrow().pending_obligations());
15-
16-
self.fulfillment_cx.borrow().pending_obligations().into_iter().filter_map(
17-
move |obligation| match &obligation.predicate.kind().skip_binder() {
18-
ty::PredicateKind::Clause(ty::ClauseKind::Projection(data))
19-
if self.self_type_matches_expected_vid(
20-
data.projection_ty.self_ty(),
21-
ty_var_root,
22-
) =>
23-
{
24-
Some(obligation)
25-
}
26-
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data))
27-
if self.self_type_matches_expected_vid(data.self_ty(), ty_var_root) =>
28-
{
29-
Some(obligation)
30-
}
13+
) -> Vec<traits::PredicateObligation<'tcx>> {
14+
if self.next_trait_solver() {
15+
self.obligations_for_self_ty_next(self_ty)
16+
} else {
17+
let ty_var_root = self.root_var(self_ty);
18+
let mut obligations = self.fulfillment_cx.borrow().pending_obligations();
19+
trace!("pending_obligations = {:#?}", obligations);
20+
obligations
21+
.retain(|obligation| self.predicate_has_self_ty(obligation.predicate, ty_var_root));
22+
obligations
23+
}
24+
}
3125

32-
ty::PredicateKind::Clause(ty::ClauseKind::Trait(..))
33-
| ty::PredicateKind::Clause(ty::ClauseKind::Projection(..))
34-
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
35-
| ty::PredicateKind::Subtype(..)
36-
| ty::PredicateKind::Coerce(..)
37-
| ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..))
38-
| ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..))
39-
| ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..))
40-
| ty::PredicateKind::ObjectSafe(..)
41-
| ty::PredicateKind::NormalizesTo(..)
42-
| ty::PredicateKind::AliasRelate(..)
43-
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
44-
| ty::PredicateKind::ConstEquate(..)
45-
| ty::PredicateKind::Ambiguous => None,
46-
},
47-
)
26+
#[instrument(level = "debug", skip(self), ret)]
27+
fn predicate_has_self_ty(
28+
&self,
29+
predicate: ty::Predicate<'tcx>,
30+
expected_vid: ty::TyVid,
31+
) -> bool {
32+
match predicate.kind().skip_binder() {
33+
ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
34+
self.self_type_matches_expected_vid(data.self_ty(), expected_vid)
35+
}
36+
ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
37+
self.self_type_matches_expected_vid(data.projection_ty.self_ty(), expected_vid)
38+
}
39+
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..))
40+
| ty::PredicateKind::Subtype(..)
41+
| ty::PredicateKind::Coerce(..)
42+
| ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..))
43+
| ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..))
44+
| ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..))
45+
| ty::PredicateKind::ObjectSafe(..)
46+
| ty::PredicateKind::NormalizesTo(..)
47+
| ty::PredicateKind::AliasRelate(..)
48+
| ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..))
49+
| ty::PredicateKind::ConstEquate(..)
50+
| ty::PredicateKind::Ambiguous => false,
51+
}
4852
}
4953

5054
#[instrument(level = "debug", skip(self), ret)]
@@ -53,12 +57,63 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5357
debug!(?self_ty);
5458

5559
match *self_ty.kind() {
56-
ty::Infer(ty::TyVar(found_vid)) => {
57-
let found_vid = self.root_var(found_vid);
58-
debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid);
59-
expected_vid == found_vid
60-
}
60+
ty::Infer(ty::TyVar(found_vid)) => expected_vid == self.root_var(found_vid),
6161
_ => false,
6262
}
6363
}
64-
}
64+
65+
pub(crate) fn obligations_for_self_ty_next(
66+
&self,
67+
self_ty: ty::TyVid,
68+
) -> Vec<traits::PredicateObligation<'tcx>> {
69+
let obligations = self.fulfillment_cx.borrow().pending_obligations();
70+
let mut obligations_for_self_ty = vec![];
71+
for obligation in obligations {
72+
let mut visitor = NestedObligationsForSelfTy {
73+
fcx: self,
74+
ty_var_root: self.root_var(self_ty),
75+
obligations_for_self_ty: &mut obligations_for_self_ty,
76+
root_cause: &obligation.cause,
77+
};
78+
79+
let goal = Goal::new(self.tcx, obligation.param_env, obligation.predicate);
80+
self.visit_proof_tree(goal, &mut visitor);
81+
}
82+
obligations_for_self_ty
83+
}
84+
}
85+
86+
struct NestedObligationsForSelfTy<'a, 'tcx> {
87+
fcx: &'a FnCtxt<'a, 'tcx>,
88+
ty_var_root: ty::TyVid,
89+
root_cause: &'a ObligationCause<'tcx>,
90+
obligations_for_self_ty: &'a mut Vec<traits::PredicateObligation<'tcx>>,
91+
}
92+
93+
impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> {
94+
type Result = ();
95+
96+
fn config(&self) -> InspectConfig {
97+
// Using an intentionally low depth to minimize the chance of future
98+
// breaking changes in case we adapt the approach later on. This also
99+
// avoids any hangs for exponentially growing proof trees.
100+
InspectConfig { max_depth: 3 }
101+
}
102+
103+
fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) {
104+
let tcx = self.fcx.tcx;
105+
let goal = inspect_goal.goal();
106+
if self.fcx.predicate_has_self_ty(goal.predicate, self.ty_var_root) {
107+
self.obligations_for_self_ty.push(traits::Obligation::new(
108+
tcx,
109+
self.root_cause.clone(),
110+
goal.param_env,
111+
goal.predicate,
112+
));
113+
}
114+
115+
if let Some(candidate) = inspect_goal.unique_applicable_candidate() {
116+
candidate.visit_nested_no_probe(self)
117+
}
118+
}
119+
}

compiler/rustc_trait_selection/src/solve/inspect/analyse.rs

+50-7
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ use rustc_middle::ty;
2020
use crate::solve::inspect::ProofTreeBuilder;
2121
use crate::solve::{GenerateProofTree, InferCtxtEvalExt};
2222

23+
pub struct InspectConfig {
24+
pub max_depth: usize,
25+
}
26+
2327
pub struct InspectGoal<'a, 'tcx> {
2428
infcx: &'a InferCtxt<'tcx>,
2529
depth: usize,
@@ -48,15 +52,42 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
4852
self.result.map(|c| c.value.certainty)
4953
}
5054

55+
pub fn visit_nested_no_probe<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {
56+
if self.goal.depth < visitor.config().max_depth {
57+
let infcx = self.goal.infcx;
58+
let mut instantiated_goals = vec![];
59+
for goal in &self.nested_goals {
60+
let goal = ProofTreeBuilder::instantiate_canonical_state(
61+
infcx,
62+
self.goal.goal.param_env,
63+
self.goal.orig_values,
64+
*goal,
65+
);
66+
instantiated_goals.push(goal);
67+
}
68+
69+
for &goal in &instantiated_goals {
70+
let (_, proof_tree) = infcx.evaluate_root_goal(goal, GenerateProofTree::Yes);
71+
let proof_tree = proof_tree.unwrap();
72+
try_visit!(visitor.visit_goal(&InspectGoal::new(
73+
infcx,
74+
self.goal.depth + 1,
75+
&proof_tree,
76+
)));
77+
}
78+
}
79+
80+
V::Result::output()
81+
}
82+
5183
/// Visit the nested goals of this candidate.
5284
///
5385
/// FIXME(@lcnr): we have to slightly adapt this API
5486
/// to also use it to compute the most relevant goal
5587
/// for fulfillment errors. Will do that once we actually
5688
/// need it.
5789
pub fn visit_nested<V: ProofTreeVisitor<'tcx>>(&self, visitor: &mut V) -> V::Result {
58-
// HACK: An arbitrary cutoff to avoid dealing with overflow and cycles.
59-
if self.goal.depth <= 10 {
90+
if self.goal.depth < visitor.config().max_depth {
6091
let infcx = self.goal.infcx;
6192
infcx.probe(|_| {
6293
let mut instantiated_goals = vec![];
@@ -167,6 +198,16 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
167198
}
168199
}
169200

201+
/// Returns the single candidate applicable for the current goal, if it exists.
202+
///
203+
/// Returns `None` if there are no multiple applicable candidates.
204+
pub fn unique_applicable_candidate(&'a self) -> Option<InspectCandidate<'a, 'tcx>> {
205+
// FIXME(-Znext-solver): This does not handle impl candidates
206+
// hidden by env candidates.
207+
let mut candidates = self.candidates();
208+
candidates.pop().filter(|_| candidates.is_empty())
209+
}
210+
170211
pub fn candidates(&'a self) -> Vec<InspectCandidate<'a, 'tcx>> {
171212
let mut candidates = vec![];
172213
let last_eval_step = match self.evaluation.evaluation.kind {
@@ -213,6 +254,10 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
213254
pub trait ProofTreeVisitor<'tcx> {
214255
type Result: VisitorResult = ();
215256

257+
fn config(&self) -> InspectConfig {
258+
InspectConfig { max_depth: 10 }
259+
}
260+
216261
fn visit_goal(&mut self, goal: &InspectGoal<'_, 'tcx>) -> Self::Result;
217262
}
218263

@@ -223,10 +268,8 @@ impl<'tcx> InferCtxt<'tcx> {
223268
goal: Goal<'tcx, ty::Predicate<'tcx>>,
224269
visitor: &mut V,
225270
) -> V::Result {
226-
self.probe(|_| {
227-
let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
228-
let proof_tree = proof_tree.unwrap();
229-
visitor.visit_goal(&InspectGoal::new(self, 0, &proof_tree))
230-
})
271+
let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
272+
let proof_tree = proof_tree.unwrap();
273+
visitor.visit_goal(&InspectGoal::new(self, 0, &proof_tree))
231274
}
232275
}

compiler/rustc_trait_selection/src/traits/coherence.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1157,5 +1157,5 @@ fn search_ambiguity_causes<'tcx>(
11571157
goal: Goal<'tcx, ty::Predicate<'tcx>>,
11581158
causes: &mut FxIndexSet<IntercrateAmbiguityCause<'tcx>>,
11591159
) {
1160-
infcx.visit_proof_tree(goal, &mut AmbiguityCausesVisitor { causes });
1160+
infcx.probe(|_| infcx.visit_proof_tree(goal, &mut AmbiguityCausesVisitor { causes }));
11611161
}

tests/ui/closures/infer-signature-from-impl.next.stderr

-16
This file was deleted.

tests/ui/closures/infer-signature-from-impl.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
//@ revisions: current next
22
//@ ignore-compare-mode-next-solver (explicit revisions)
33
//@[next] compile-flags: -Znext-solver
4-
//@[next] known-bug: trait-system-refactor-initiative#71
5-
//@[current] check-pass
4+
//@ check-pass
65

76
trait Foo {}
87
fn needs_foo<T>(_: T)

0 commit comments

Comments
 (0)