Skip to content

Commit 4ba1135

Browse files
committed
bye bye assemble_candidates_via_self_ty
1 parent 0ee9cfd commit 4ba1135

File tree

2 files changed

+31
-111
lines changed
  • compiler/rustc_trait_selection/src/solve

2 files changed

+31
-111
lines changed

compiler/rustc_trait_selection/src/solve/assembly/mod.rs

+28-106
Original file line numberDiff line numberDiff line change
@@ -271,64 +271,32 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
271271
&mut self,
272272
goal: Goal<'tcx, G>,
273273
) -> Vec<Candidate<'tcx>> {
274-
debug_assert_eq!(goal, self.resolve_vars_if_possible(goal));
275-
if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) {
276-
return vec![ambig];
277-
}
278-
279-
let mut candidates = self.assemble_candidates_via_self_ty(goal, 0);
280-
281-
self.assemble_unsize_to_dyn_candidate(goal, &mut candidates);
282-
283-
self.assemble_blanket_impl_candidates(goal, &mut candidates);
284-
285-
self.assemble_param_env_candidates(goal, &mut candidates);
286-
287-
self.assemble_coherence_unknowable_candidates(goal, &mut candidates);
288-
289-
candidates
290-
}
291-
292-
/// `?0: Trait` is ambiguous, because it may be satisfied via a builtin rule,
293-
/// object bound, alias bound, etc. We are unable to determine this until we can at
294-
/// least structurally resolve the type one layer.
295-
///
296-
/// It would also require us to consider all impls of the trait, which is both pretty
297-
/// bad for perf and would also constrain the self type if there is just a single impl.
298-
fn assemble_self_ty_infer_ambiguity_response<G: GoalKind<'tcx>>(
299-
&mut self,
300-
goal: Goal<'tcx, G>,
301-
) -> Option<Candidate<'tcx>> {
302-
if goal.predicate.self_ty().is_ty_var() {
303-
debug!("adding self_ty_infer_ambiguity_response");
274+
let dummy_candidate = |this: &mut EvalCtxt<'_, 'tcx>, certainty| {
304275
let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
305-
let result = self
306-
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
307-
.unwrap();
308-
let mut dummy_probe = self.inspect.new_probe();
276+
let result = this.evaluate_added_goals_and_make_canonical_response(certainty).unwrap();
277+
let mut dummy_probe = this.inspect.new_probe();
309278
dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) });
310-
self.inspect.finish_probe(dummy_probe);
311-
Some(Candidate { source, result })
312-
} else {
313-
None
279+
this.inspect.finish_probe(dummy_probe);
280+
vec![Candidate { source, result }]
281+
};
282+
283+
let Some(normalized_self_ty) =
284+
self.try_normalize_ty(goal.param_env, goal.predicate.self_ty())
285+
else {
286+
debug!("overflow while evaluating self type");
287+
return dummy_candidate(self, Certainty::OVERFLOW);
288+
};
289+
290+
if normalized_self_ty.is_ty_var() {
291+
debug!("self type has been normalized to infer");
292+
return dummy_candidate(self, Certainty::AMBIGUOUS);
314293
}
315-
}
316294

317-
/// Assemble candidates which apply to the self type. This only looks at candidate which
318-
/// apply to the specific self type and ignores all others.
319-
///
320-
/// Returns `None` if the self type is still ambiguous.
321-
fn assemble_candidates_via_self_ty<G: GoalKind<'tcx>>(
322-
&mut self,
323-
goal: Goal<'tcx, G>,
324-
num_steps: usize,
325-
) -> Vec<Candidate<'tcx>> {
295+
let goal =
296+
goal.with(self.tcx(), goal.predicate.with_self_ty(self.tcx(), normalized_self_ty));
326297
debug_assert_eq!(goal, self.resolve_vars_if_possible(goal));
327-
if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) {
328-
return vec![ambig];
329-
}
330298

331-
let mut candidates = Vec::new();
299+
let mut candidates = vec![];
332300

333301
self.assemble_non_blanket_impl_candidates(goal, &mut candidates);
334302

@@ -338,61 +306,15 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
338306

339307
self.assemble_object_bound_candidates(goal, &mut candidates);
340308

341-
self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates, num_steps);
342-
candidates
343-
}
309+
self.assemble_unsize_to_dyn_candidate(goal, &mut candidates);
344310

345-
/// If the self type of a goal is an alias we first try to normalize the self type
346-
/// and compute the candidates for the normalized self type in case that succeeds.
347-
///
348-
/// These candidates are used in addition to the ones with the alias as a self type.
349-
/// We do this to simplify both builtin candidates and for better performance.
350-
///
351-
/// We generate the builtin candidates on the fly by looking at the self type, e.g.
352-
/// add `FnPtr` candidates if the self type is a function pointer. Handling builtin
353-
/// candidates while the self type is still an alias seems difficult. This is similar
354-
/// to `try_structurally_resolve_type` during hir typeck (FIXME once implemented).
355-
///
356-
/// Looking at all impls for some trait goal is prohibitively expensive. We therefore
357-
/// only look at implementations with a matching self type. Because of this function,
358-
/// we can avoid looking at all existing impls if the self type is an alias.
359-
#[instrument(level = "debug", skip_all)]
360-
fn assemble_candidates_after_normalizing_self_ty<G: GoalKind<'tcx>>(
361-
&mut self,
362-
goal: Goal<'tcx, G>,
363-
candidates: &mut Vec<Candidate<'tcx>>,
364-
num_steps: usize,
365-
) {
366-
let tcx = self.tcx();
367-
let &ty::Alias(_, alias) = goal.predicate.self_ty().kind() else { return };
368-
369-
candidates.extend(self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| {
370-
if tcx.recursion_limit().value_within_limit(num_steps) {
371-
let normalized_ty = ecx.next_ty_infer();
372-
let normalizes_to_goal =
373-
goal.with(tcx, ty::NormalizesTo { alias, term: normalized_ty.into() });
374-
ecx.add_goal(GoalSource::Misc, normalizes_to_goal);
375-
if let Err(NoSolution) = ecx.try_evaluate_added_goals() {
376-
debug!("self type normalization failed");
377-
return vec![];
378-
}
379-
let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty);
380-
debug!(?normalized_ty, "self type normalized");
381-
// NOTE: Alternatively we could call `evaluate_goal` here and only
382-
// have a `Normalized` candidate. This doesn't work as long as we
383-
// use `CandidateSource` in winnowing.
384-
let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty));
385-
ecx.assemble_candidates_via_self_ty(goal, num_steps + 1)
386-
} else {
387-
match ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW) {
388-
Ok(result) => vec![Candidate {
389-
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
390-
result,
391-
}],
392-
Err(NoSolution) => vec![],
393-
}
394-
}
395-
}));
311+
self.assemble_blanket_impl_candidates(goal, &mut candidates);
312+
313+
self.assemble_param_env_candidates(goal, &mut candidates);
314+
315+
self.assemble_coherence_unknowable_candidates(goal, &mut candidates);
316+
317+
candidates
396318
}
397319

398320
#[instrument(level = "debug", skip_all)]

compiler/rustc_trait_selection/src/solve/mod.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -287,11 +287,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
287287

288288
/// Normalize a type when it is structually matched on.
289289
///
290-
/// For self types this is generally already handled through
291-
/// `assemble_candidates_after_normalizing_self_ty`, so anything happening
292-
/// in [`EvalCtxt::assemble_candidates_via_self_ty`] does not have to normalize
293-
/// the self type. It is required when structurally matching on any other
294-
/// arguments of a trait goal, e.g. when assembling builtin unsize candidates.
290+
/// In nearly all cases this function must be used before matching on a type.
291+
/// Not doing so is likely to be incomplete and therefore unsound during
292+
/// coherence.
295293
#[instrument(level = "debug", skip(self), ret)]
296294
fn try_normalize_ty(
297295
&mut self,

0 commit comments

Comments
 (0)