rustc_trait_selection/solve/
delegate.rs1use std::collections::hash_map::Entry;
2use std::fmt::Debug;
3use std::ops::{ControlFlow, Deref};
4
5use rustc_attr_ir::lang_items::LangItem;
6use rustc_data_structures::fx::{FxHashMap, FxHashSet};
7use rustc_hir::CRATE_HIR_ID;
8use rustc_hir::def::Namespace;
9use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE};
10use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
11use rustc_infer::infer::canonical::{
12 Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarKind, CanonicalVarValues,
13 Canonicalized, QueryRegionConstraint, instantiate_value,
14};
15use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt};
16use rustc_infer::traits::solve::{
17 ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, SucceededInErased,
18};
19use rustc_lint_defs::builtin::RECURSION_DEPTH_EXCEEDING_LIMIT;
20use rustc_middle::traits::query::NoSolution;
21use rustc_middle::traits::solve::MaybeInfo;
22use rustc_middle::ty::print::{FmtPrinter, Print};
23use rustc_middle::ty::transmute::Answer;
24use rustc_middle::ty::{
25 self, CanonicalizerState, MayBeErased, Region, Ty, TyCtxt, TypeFlags, TypeFoldable,
26 TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode,
27};
28use rustc_next_trait_solver::solve::{GoalStalledOn, GoalStalledOnOpaques, TyOrConstInferVar};
29use rustc_span::{DUMMY_SP, Span};
30use rustc_structures::Limit;
31use rustc_transmute::TransmuteTypeEnv;
32use thin_vec::{ThinVec, thin_vec};
33
34use super::inspect::InferCtxtProofTreeExt;
35use crate::solve::inspect::{self, InspectConfig, ProofTreeVisitor};
36use crate::traits::{EvaluateConstErr, ObligationCause, implicit_fast_path, specialization_graph};
37
38#[repr(transparent)]
39pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>);
40
41impl<'a, 'tcx> From<&'a InferCtxt<'tcx>> for &'a SolverDelegate<'tcx> {
42 fn from(infcx: &'a InferCtxt<'tcx>) -> Self {
43 unsafe { std::mem::transmute(infcx) }
45 }
46}
47
48impl<'tcx> Deref for SolverDelegate<'tcx> {
49 type Target = InferCtxt<'tcx>;
50
51 fn deref(&self) -> &Self::Target {
52 &self.0
53 }
54}
55
56impl<'tcx> SolverDelegate<'tcx> {
57 fn known_no_opaque_types_in_storage(&self) -> bool {
58 self.inner.borrow_mut().opaque_types().is_empty()
59 && !self.typing_mode_raw().is_erased_not_coherence()
62 }
63}
64
65fn goal_stalled_on_args<'tcx>(
68 stalled_vars: ThinVec<TyOrConstInferVar>,
69) -> ComputeGoalFastPathOutcome<'tcx> {
70 ComputeGoalFastPathOutcome::TriviallyStalled {
71 stalled_on: GoalStalledOn {
72 stalled_vars,
73 sub_roots: ThinVec::new(),
74 stalled_maybe_info: MaybeInfo::AMBIGUOUS,
75 opaques: GoalStalledOnOpaques::No,
76 },
77 }
78}
79
80fn goal_stalled_on_args_or_nonempty_opaques<'tcx>(
84 stalled_vars: ThinVec<TyOrConstInferVar>,
85) -> ComputeGoalFastPathOutcome<'tcx> {
86 ComputeGoalFastPathOutcome::TriviallyStalled {
87 stalled_on: GoalStalledOn {
88 stalled_vars,
89 sub_roots: ThinVec::new(),
90 stalled_maybe_info: MaybeInfo::AMBIGUOUS,
91 opaques: GoalStalledOnOpaques::Yes {
92 num_opaques_in_storage: 0,
93 previously_succeeded_in_erased: SucceededInErased::No,
97 },
98 },
99 }
100}
101
102struct CollectNonRegionInfer<'tcx> {
103 infers: ThinVec<ty::GenericArg<'tcx>>,
104 visited: FxHashSet<Ty<'tcx>>,
105}
106
107impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CollectNonRegionInfer<'tcx> {
108 fn visit_ty(&mut self, ty: Ty<'tcx>) {
109 if self.visited.contains(&ty) {
110 return;
111 }
112
113 match ty.kind() {
114 ty::Infer(_) => self.infers.push(ty.into()),
115 _ => ty.super_visit_with(self),
116 }
117
118 self.visited.insert(ty);
119 }
120
121 fn visit_const(&mut self, ct: ty::Const<'tcx>) {
122 match ct.kind() {
123 ty::ConstKind::Infer(_) => self.infers.push(ct.into()),
124 _ => ct.super_visit_with(self),
125 }
126 }
127}
128
129impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<'tcx> {
130 type Infcx = InferCtxt<'tcx>;
131 type Interner = TyCtxt<'tcx>;
132
133 fn cx(&self) -> TyCtxt<'tcx> {
134 self.0.tcx
135 }
136
137 fn build_with_canonical<V>(
138 interner: TyCtxt<'tcx>,
139 canonical: &CanonicalQueryInput<'tcx, V>,
140 ) -> (Self, V, CanonicalVarValues<'tcx>)
141 where
142 V: TypeFoldable<TyCtxt<'tcx>>,
143 {
144 let (infcx, value, vars) = interner
145 .infer_ctxt()
146 .with_next_trait_solver(true)
147 .build_with_canonical(DUMMY_SP, canonical);
148 (SolverDelegate(infcx), value, vars)
149 }
150
151 fn compute_goal_fast_path(
152 &self,
153 goal: Goal<'tcx, ty::Predicate<'tcx>>,
154 span: Span,
155 ) -> ComputeGoalFastPathOutcome<'tcx> {
156 use ComputeGoalFastPathOutcome as Outcome;
157
158 if self.tcx.assumptions_on_binders() {
160 return Outcome::NoFastPath;
161 }
162
163 let pred = goal.predicate.kind();
164 match pred.skip_binder() {
165 ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {
166 let trait_pred = pred.rebind(trait_pred);
167
168 let self_ty = self.shallow_resolve(trait_pred.self_ty().skip_binder());
169 if let Some(vid) = self_ty.ty_vid()
170 && self.known_no_opaque_types_in_storage()
175 {
176 goal_stalled_on_args_or_nonempty_opaques({
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(TyOrConstInferVar::Ty(vid));
vec
}thin_vec![TyOrConstInferVar::Ty(vid)])
177 } else if trait_pred.polarity() == ty::ClausePolarity::Positive {
178 match self.0.tcx.as_lang_item(trait_pred.def_id()) {
179 Some(LangItem::Sized) | Some(LangItem::MetaSized) => {
180 let predicate = self.deeply_resolve_ignoring_regions(goal.predicate);
181 if implicit_fast_path(self.tcx, predicate, goal.param_env) {
182 Outcome::TriviallyHolds
183 } else {
184 Outcome::NoFastPath
185 }
186 }
187 Some(LangItem::Copy | LangItem::Clone) => {
188 let self_ty = self.deeply_resolve_ignoring_regions(
189 trait_pred.self_ty().skip_binder(),
190 );
191 if !self_ty
197 .has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER)
198 && self_ty.is_trivially_pure_clone_copy()
199 {
200 Outcome::TriviallyHolds
201 } else {
202 Outcome::NoFastPath
203 }
204 }
205 _ => Outcome::NoFastPath,
206 }
207 } else {
208 Outcome::NoFastPath
209 }
210 }
211 ty::PredicateKind::DynCompatible(def_id) if self.0.tcx.is_dyn_compatible(def_id) => {
212 Outcome::TriviallyHolds
213 }
214 ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(outlives)) => {
215 if outlives.has_escaping_bound_vars() {
216 return Outcome::NoFastPath;
217 }
218
219 self.0.sub_regions(
225 SubregionOrigin::RelateRegionParamBound(span, None),
226 outlives.1,
227 outlives.0,
228 ty::VisibleForLeakCheck::No,
229 );
230 Outcome::TriviallyHolds
231 }
232 ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => {
233 if outlives.has_escaping_bound_vars() {
234 return Outcome::NoFastPath;
235 }
236
237 let ty = self.deeply_resolve_ignoring_regions(outlives.0);
238 if ty.has_non_rigid_aliases() {
239 return Outcome::NoFastPath;
240 }
241
242 let mut infer_collector = CollectNonRegionInfer {
243 infers: Default::default(),
244 visited: Default::default(),
245 };
246 ty.visit_with(&mut infer_collector);
247 let infers = infer_collector.infers;
248 if !infers.is_empty() {
249 return goal_stalled_on_args(
250 infers
251 .into_iter()
252 .map(|i| {
253 TyOrConstInferVar::maybe_from_generic_arg::<Self::Interner>(i)
254 .unwrap()
255 })
256 .collect(),
257 );
258 }
259
260 self.0.register_type_outlives_constraint(
261 outlives.0,
262 outlives.1,
263 &ObligationCause::dummy_with_span(span),
264 );
265
266 Outcome::TriviallyHolds
267 }
268 ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, .. })
269 | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
270 if a.has_escaping_bound_vars() || b.has_escaping_bound_vars() {
271 return Outcome::NoFastPath;
272 }
273
274 match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) {
275 (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
276 self.sub_unify_ty_vids_raw(a_vid, b_vid);
277 goal_stalled_on_args({
let len = [(), ()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(TyOrConstInferVar::Ty(a_vid));
vec.push(TyOrConstInferVar::Ty(b_vid));
vec
}thin_vec![
278 TyOrConstInferVar::Ty(a_vid),
279 TyOrConstInferVar::Ty(b_vid),
280 ])
281 }
282 _ => Outcome::NoFastPath,
283 }
284 }
285 ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) => {
286 if ct.has_escaping_bound_vars() {
287 return Outcome::NoFastPath;
288 }
289
290 let arg = self.shallow_resolve_const(ct);
291 if let Some(vid) = arg.ct_vid() {
292 goal_stalled_on_args({
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(TyOrConstInferVar::Const(vid));
vec
}thin_vec![TyOrConstInferVar::Const(vid)])
293 } else {
294 Outcome::NoFastPath
295 }
296 }
297 ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
298 if arg.has_escaping_bound_vars() {
299 return Outcome::NoFastPath;
300 }
301
302 let arg = self.shallow_resolve_term(arg);
303 if arg.is_trivially_wf(self.tcx) {
304 Outcome::TriviallyHolds
305 } else if arg.is_infer() {
306 goal_stalled_on_args({
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(TyOrConstInferVar::maybe_from_term::<TyCtxt<'tcx>>(arg).expect("its an infer var"));
vec
}thin_vec![
307 TyOrConstInferVar::maybe_from_term::<TyCtxt<'tcx>>(arg)
308 .expect("its an infer var"),
309 ])
310 } else {
311 Outcome::NoFastPath
312 }
313 }
314 _ => Outcome::NoFastPath,
315 }
316 }
317
318 fn fresh_var_for_kind(
319 &self,
320 arg: ty::GenericArg<'tcx>,
321 span: Span,
322 universe: ty::UniverseIndex,
323 ) -> ty::GenericArg<'tcx> {
324 match arg.kind() {
325 ty::GenericArgKind::Lifetime(_) => {
326 self.next_region_var_in_universe(RegionVariableOrigin::Misc(span), universe).into()
327 }
328 ty::GenericArgKind::Type(_) => self.next_ty_var_in_universe(span, universe).into(),
329 ty::GenericArgKind::Const(_) => self.next_const_var_in_universe(span, universe).into(),
330 }
331 }
332
333 fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution> {
334 self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution)
335 }
336
337 fn evaluate_const<E: Debug>(
338 &self,
339 param_env: ty::ParamEnv<'tcx>,
340 alias_const: ty::AliasConst<'tcx>,
341 normalize_ty: impl FnOnce(ty::Unnormalized<'tcx, Ty<'tcx>>) -> Result<Ty<'tcx>, E>,
342 ) -> Result<Option<ty::Const<'tcx>>, E> {
343 let ct = ty::Const::new_alias(self.tcx, ty::IsRigid::No, alias_const);
344
345 match crate::traits::try_evaluate_const(&self.0, ct, param_env, normalize_ty) {
346 Ok(ct) => Ok(Some(ct)),
347 Err(EvaluateConstErr::EvaluationFailure(e)) => {
348 Ok(Some(ty::Const::new_error(self.tcx, e)))
349 }
350 Err(
351 EvaluateConstErr::InvalidConstParamTy(_) | EvaluateConstErr::HasGenericsOrInfers,
352 ) => Ok(None),
353 Err(EvaluateConstErr::FailedNormalization(e)) => Err(e),
354 }
355 }
356
357 fn well_formed_goals(
358 &self,
359 param_env: ty::ParamEnv<'tcx>,
360 term: ty::Term<'tcx>,
361 ) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> {
362 crate::traits::wf::unnormalized_obligations(
363 &self.0,
364 param_env,
365 term,
366 DUMMY_SP,
367 CRATE_DEF_ID,
368 )
369 .map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect())
370 }
371
372 fn make_deduplicated_region_constraints(
373 &self,
374 ) -> Vec<(ty::RegionConstraint<'tcx>, ty::VisibleForLeakCheck)> {
375 let region_obligations = self.0.inner.borrow().region_obligations().to_owned();
378 let region_assumptions = self.0.inner.borrow().region_assumptions().to_owned();
379 let region_constraints = self.0.with_region_constraints(|region_constraints| {
380 make_query_region_constraints(
381 region_obligations,
382 region_constraints,
383 region_assumptions,
384 )
385 });
386
387 let mut seen = FxHashMap::default();
388 let mut constraints = ::alloc::vec::Vec::new()vec![];
389 for QueryRegionConstraint { constraint: outlives, visible_for_leak_check: vis, .. } in
390 region_constraints.constraints
391 {
392 match seen.entry(outlives) {
393 Entry::Occupied(occupied) => {
394 let idx = occupied.get();
395 let (_, prev_vis): &mut (_, ty::VisibleForLeakCheck) =
396 constraints.get_mut(*idx).unwrap();
397 *prev_vis = (*prev_vis).or(vis);
398 }
399 Entry::Vacant(vacant) => {
400 vacant.insert(constraints.len());
401 constraints.push((outlives, vis));
402 }
403 }
404 }
405 constraints
406 }
407
408 fn instantiate_value<V>(&self, value: Canonicalized<V>, values: CanonicalVarValues<'tcx>) -> V
409 where
410 V: TypeFoldable<TyCtxt<'tcx>>,
411 {
412 instantiate_value(self.tcx, &values, value.into_uninstantiated())
413 }
414
415 fn instantiate_canonical<V>(
416 &self,
417 canonical: Canonical<'tcx, V>,
418 values: CanonicalVarValues<'tcx>,
419 ) -> V
420 where
421 V: TypeFoldable<TyCtxt<'tcx>>,
422 {
423 canonical.instantiate(self.tcx, &values)
424 }
425
426 fn instantiate_canonical_var(
427 &self,
428 kind: CanonicalVarKind<'tcx>,
429 span: Span,
430 prev_var_values: &[ty::GenericArg<'tcx>],
431 universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
432 ) -> ty::GenericArg<'tcx> {
433 self.0.instantiate_canonical_var(span, kind, prev_var_values, universe_map)
434 }
435
436 fn add_item_bounds_for_hidden_type(
437 &self,
438 def_id: DefId,
439 args: ty::GenericArgs<'tcx>,
440 param_env: ty::ParamEnv<'tcx>,
441 hidden_ty: Ty<'tcx>,
442 goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
443 ) {
444 self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals);
445 }
446
447 fn fetch_eligible_assoc_item(
448 &self,
449 goal_trait_ref: ty::TraitRef<'tcx>,
450 trait_assoc_def_id: DefId,
451 impl_def_id: DefId,
452 ) -> FetchEligibleAssocItemResponse<'tcx> {
453 let node_item =
454 match specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id) {
455 Ok(i) => i,
456 Err(guar) => return FetchEligibleAssocItemResponse::Err(guar),
457 };
458
459 let typing_mode = self.typing_mode_raw();
460
461 let eligible = if node_item.is_final() {
462 true
464 } else {
465 match typing_mode {
470 TypingMode::Coherence
471 | TypingMode::Typeck { .. }
472 | TypingMode::PostTypeckThroughBorrowck { .. }
473 | TypingMode::Reflection
474 | TypingMode::PostBorrowck { .. } => false,
475 TypingMode::PostAnalysis | TypingMode::Codegen => {
476 let poly_trait_ref = self.deeply_resolve_ignoring_regions(goal_trait_ref);
477 !poly_trait_ref.still_further_specializable()
478 }
479 TypingMode::ErasedNotCoherence(MayBeErased) => {
480 return FetchEligibleAssocItemResponse::NotFoundBecauseErased;
481 }
482 }
483 };
484
485 if eligible {
487 FetchEligibleAssocItemResponse::Found(node_item.item.def_id)
488 } else {
489 FetchEligibleAssocItemResponse::NotFound(typing_mode.assert_not_erased())
492 }
493 }
494
495 fn is_transmutable(
498 &self,
499 src: Ty<'tcx>,
500 dst: Ty<'tcx>,
501 assume: ty::Const<'tcx>,
502 ) -> Result<Answer<Region<'tcx>, Ty<'tcx>>, NoSolution> {
503 rustc_transmute::Assume::from_const(self.tcx, assume)
504 .map(|assume| TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume))
505 .ok_or(NoSolution)
506 }
507
508 fn obtain_canonicalizer_state(&self) -> Box<CanonicalizerState<Self::Interner>> {
509 self.cx()
511 .caches
512 .canonicalizer_states
513 .lock()
514 .pop()
515 .unwrap_or_else(|| Box::new(CanonicalizerState::default()))
516 }
517
518 fn release_canonicalizer_state(&self, mut state: Box<CanonicalizerState<Self::Interner>>) {
519 state.clear();
521 self.cx().caches.canonicalizer_states.lock().push(state);
522 }
523
524 fn emit_next_solver_overflow_fcw(&self, goal: Goal<'tcx, ty::Predicate<'tcx>>, span: Span) {
525 let tcx = self.tcx;
526 tcx.emit_node_span_lint(
527 RECURSION_DEPTH_EXCEEDING_LIMIT,
528 CRATE_HIR_ID,
529 span,
530 rustc_errors::DiagDecorator(|diag| {
531 let goal = self.deeply_resolve_ignoring_regions(goal);
534 let recursion_limit = if let Some(specified_limit) = tcx.next_solver_overflow_fcw_limit()
535 {
536 usize::min(specified_limit, tcx.recursion_limit().0 * 2)
538 } else {
539 usize::min(16, tcx.recursion_limit().0)
540 };
541 let mut visitor = OverflowedGoalChain {
542 span,
543 predicates: ::alloc::vec::Vec::new()vec![],
544 recursion_limit,
545 };
546
547 let _ = self.with_disabled_next_solver_overflow_fcw(|| {
548 self.visit_proof_tree(goal, &mut visitor)
549 });
550
551 let pred_str = |pred: ty::Predicate<'tcx>| {
553 let s = pred.to_string();
554 if s.len() > 80 {
555 let mut p: FmtPrinter<'_, '_> =
556 FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, Limit(10));
557 pred.print(&mut p).unwrap();
558 p.into_buffer()
559 } else {
560 s
561 }
562 };
563 diag.primary_message(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("overflow evaluating the requirement `{0}`",
pred_str(goal.predicate)))
})format!(
564 "overflow evaluating the requirement `{}`",
565 pred_str(goal.predicate),
566 ));
567 for p in visitor.predicates.into_iter().skip(1) {
568 diag.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("which requires `{0}`",
pred_str(p)))
})format!("which requires `{}`", pred_str(p)));
569 }
570 diag.note("and so on...");
571 diag.help(
572 "consider adding a manual `impl` of auto traits like `Send` for intermediate types, if auto traits are involved",
573 );
574 diag.help(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("or consider increasing the recursion limit by adding a `#![recursion_limit = \"{0}\"]` attribute to your crate (`{1}`)",
tcx.recursion_limit() * 2, tcx.crate_name(LOCAL_CRATE)))
})format!(
575 "or consider increasing the recursion limit by adding a \
576 `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
577 tcx.recursion_limit() * 2,
578 tcx.crate_name(LOCAL_CRATE),
579 ));
580 diag.note("this lint is attached to the whole crate and can't be disabled on a per-function basis");
581 }),
582 )
583 }
584}
585
586struct OverflowedGoalChain<'tcx> {
587 span: Span,
588 predicates: Vec<ty::Predicate<'tcx>>,
589 recursion_limit: usize,
590}
591
592impl<'tcx> ProofTreeVisitor<'tcx> for OverflowedGoalChain<'tcx> {
593 type Result = ControlFlow<()>;
594
595 fn span(&self) -> Span {
596 self.span
597 }
598
599 fn config(&self) -> InspectConfig {
600 InspectConfig { max_depth: self.recursion_limit }
601 }
602
603 fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
604 self.predicates.push(goal.goal().predicate);
605 if let Some(cand) = goal.unique_applicable_candidate() {
606 goal.infcx().probe(|_| {
607 if let Some(nested_goal_with_largest_required_depth) = cand
608 .instantiate_nested_goals(self.span)
609 .into_iter()
610 .max_by_key(|g| g.required_depth())
611 {
612 nested_goal_with_largest_required_depth.visit_with(self)
613 } else {
614 ControlFlow::Continue(())
615 }
616 })?;
617 }
618 ControlFlow::Continue(())
619 }
620
621 fn on_recursion_limit(&mut self) -> Self::Result {
622 ControlFlow::Break(())
623 }
624}