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