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.resolve_vars_if_possible(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 =
188 self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder());
189 if !self_ty
195 .has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER)
196 && self_ty.is_trivially_pure_clone_copy()
197 {
198 Outcome::TriviallyHolds
199 } else {
200 Outcome::NoFastPath
201 }
202 }
203 _ => Outcome::NoFastPath,
204 }
205 } else {
206 Outcome::NoFastPath
207 }
208 }
209 ty::PredicateKind::DynCompatible(def_id) if self.0.tcx.is_dyn_compatible(def_id) => {
210 Outcome::TriviallyHolds
211 }
212 ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(outlives)) => {
213 if outlives.has_escaping_bound_vars() {
214 return Outcome::NoFastPath;
215 }
216
217 self.0.sub_regions(
218 SubregionOrigin::RelateRegionParamBound(span, None),
219 outlives.1,
220 outlives.0,
221 ty::VisibleForLeakCheck::Yes,
222 );
223 Outcome::TriviallyHolds
224 }
225 ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => {
226 if outlives.has_escaping_bound_vars() {
227 return Outcome::NoFastPath;
228 }
229
230 let ty = self.resolve_vars_if_possible(outlives.0);
231 let mut infer_collector = CollectNonRegionInfer {
232 infers: Default::default(),
233 visited: Default::default(),
234 };
235 ty.visit_with(&mut infer_collector);
236 let infers = infer_collector.infers;
237 if !infers.is_empty() {
238 return goal_stalled_on_args(
239 infers
240 .into_iter()
241 .map(|i| {
242 TyOrConstInferVar::maybe_from_generic_arg::<Self::Interner>(i)
243 .unwrap()
244 })
245 .collect(),
246 );
247 }
248
249 if ty.has_non_rigid_aliases() {
250 return Outcome::NoFastPath;
251 }
252
253 self.0.register_type_outlives_constraint(
254 outlives.0,
255 outlives.1,
256 &ObligationCause::dummy_with_span(span),
257 );
258
259 Outcome::TriviallyHolds
260 }
261 ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, .. })
262 | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
263 if a.has_escaping_bound_vars() || b.has_escaping_bound_vars() {
264 return Outcome::NoFastPath;
265 }
266
267 match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) {
268 (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
269 self.sub_unify_ty_vids_raw(a_vid, b_vid);
270 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![
271 TyOrConstInferVar::Ty(a_vid),
272 TyOrConstInferVar::Ty(b_vid),
273 ])
274 }
275 _ => Outcome::NoFastPath,
276 }
277 }
278 ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) => {
279 if ct.has_escaping_bound_vars() {
280 return Outcome::NoFastPath;
281 }
282
283 let arg = self.shallow_resolve_const(ct);
284 if let Some(vid) = arg.ct_vid() {
285 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)])
286 } else {
287 Outcome::NoFastPath
288 }
289 }
290 ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
291 if arg.has_escaping_bound_vars() {
292 return Outcome::NoFastPath;
293 }
294
295 let arg = self.shallow_resolve_term(arg);
296 if arg.is_trivially_wf(self.tcx) {
297 Outcome::TriviallyHolds
298 } else if arg.is_infer() {
299 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![
300 TyOrConstInferVar::maybe_from_term::<TyCtxt<'tcx>>(arg)
301 .expect("its an infer var"),
302 ])
303 } else {
304 Outcome::NoFastPath
305 }
306 }
307 _ => Outcome::NoFastPath,
308 }
309 }
310
311 fn fresh_var_for_kind(
312 &self,
313 arg: ty::GenericArg<'tcx>,
314 span: Span,
315 universe: ty::UniverseIndex,
316 ) -> ty::GenericArg<'tcx> {
317 match arg.kind() {
318 ty::GenericArgKind::Lifetime(_) => {
319 self.next_region_var_in_universe(RegionVariableOrigin::Misc(span), universe).into()
320 }
321 ty::GenericArgKind::Type(_) => self.next_ty_var_in_universe(span, universe).into(),
322 ty::GenericArgKind::Const(_) => self.next_const_var_in_universe(span, universe).into(),
323 }
324 }
325
326 fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution> {
327 self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution)
328 }
329
330 fn evaluate_const<E: Debug>(
331 &self,
332 param_env: ty::ParamEnv<'tcx>,
333 alias_const: ty::AliasConst<'tcx>,
334 normalize_ty: impl FnOnce(ty::Unnormalized<'tcx, Ty<'tcx>>) -> Result<Ty<'tcx>, E>,
335 ) -> Result<Option<ty::Const<'tcx>>, E> {
336 let ct = ty::Const::new_alias(self.tcx, ty::IsRigid::No, alias_const);
337
338 match crate::traits::try_evaluate_const(&self.0, ct, param_env, normalize_ty) {
339 Ok(ct) => Ok(Some(ct)),
340 Err(EvaluateConstErr::EvaluationFailure(e)) => {
341 Ok(Some(ty::Const::new_error(self.tcx, e)))
342 }
343 Err(
344 EvaluateConstErr::InvalidConstParamTy(_) | EvaluateConstErr::HasGenericsOrInfers,
345 ) => Ok(None),
346 Err(EvaluateConstErr::FailedNormalization(e)) => Err(e),
347 }
348 }
349
350 fn well_formed_goals(
351 &self,
352 param_env: ty::ParamEnv<'tcx>,
353 term: ty::Term<'tcx>,
354 ) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> {
355 crate::traits::wf::unnormalized_obligations(
356 &self.0,
357 param_env,
358 term,
359 DUMMY_SP,
360 CRATE_DEF_ID,
361 )
362 .map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect())
363 }
364
365 fn make_deduplicated_region_constraints(
366 &self,
367 ) -> Vec<(ty::RegionConstraint<'tcx>, ty::VisibleForLeakCheck)> {
368 let region_obligations = self.0.inner.borrow().region_obligations().to_owned();
371 let region_assumptions = self.0.inner.borrow().region_assumptions().to_owned();
372 let region_constraints = self.0.with_region_constraints(|region_constraints| {
373 make_query_region_constraints(
374 region_obligations,
375 region_constraints,
376 region_assumptions,
377 )
378 });
379
380 let mut seen = FxHashMap::default();
381 let mut constraints = ::alloc::vec::Vec::new()vec![];
382 for QueryRegionConstraint { constraint: outlives, visible_for_leak_check: vis, .. } in
383 region_constraints.constraints
384 {
385 match seen.entry(outlives) {
386 Entry::Occupied(occupied) => {
387 let idx = occupied.get();
388 let (_, prev_vis): &mut (_, ty::VisibleForLeakCheck) =
389 constraints.get_mut(*idx).unwrap();
390 *prev_vis = (*prev_vis).or(vis);
391 }
392 Entry::Vacant(vacant) => {
393 vacant.insert(constraints.len());
394 constraints.push((outlives, vis));
395 }
396 }
397 }
398 constraints
399 }
400
401 fn instantiate_canonical<V>(
402 &self,
403 canonical: Canonical<'tcx, V>,
404 values: CanonicalVarValues<'tcx>,
405 ) -> V
406 where
407 V: TypeFoldable<TyCtxt<'tcx>>,
408 {
409 canonical.instantiate(self.tcx, &values)
410 }
411
412 fn instantiate_canonical_var(
413 &self,
414 kind: CanonicalVarKind<'tcx>,
415 span: Span,
416 var_values: &[ty::GenericArg<'tcx>],
417 universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
418 ) -> ty::GenericArg<'tcx> {
419 self.0.instantiate_canonical_var(span, kind, var_values, universe_map)
420 }
421
422 fn add_item_bounds_for_hidden_type(
423 &self,
424 def_id: DefId,
425 args: ty::GenericArgsRef<'tcx>,
426 param_env: ty::ParamEnv<'tcx>,
427 hidden_ty: Ty<'tcx>,
428 goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
429 ) {
430 self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals);
431 }
432
433 fn fetch_eligible_assoc_item(
434 &self,
435 goal_trait_ref: ty::TraitRef<'tcx>,
436 trait_assoc_def_id: DefId,
437 impl_def_id: DefId,
438 ) -> FetchEligibleAssocItemResponse<'tcx> {
439 let node_item =
440 match specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id) {
441 Ok(i) => i,
442 Err(guar) => return FetchEligibleAssocItemResponse::Err(guar),
443 };
444
445 let typing_mode = self.typing_mode_raw();
446
447 let eligible = if node_item.is_final() {
448 true
450 } else {
451 match typing_mode {
456 TypingMode::Coherence
457 | TypingMode::Typeck { .. }
458 | TypingMode::PostTypeckUntilBorrowck { .. }
459 | TypingMode::Reflection
460 | TypingMode::PostBorrowck { .. } => false,
461 TypingMode::PostAnalysis | TypingMode::Codegen => {
462 let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref);
463 !poly_trait_ref.still_further_specializable()
464 }
465 TypingMode::ErasedNotCoherence(MayBeErased) => {
466 return FetchEligibleAssocItemResponse::NotFoundBecauseErased;
467 }
468 }
469 };
470
471 if eligible {
473 FetchEligibleAssocItemResponse::Found(node_item.item.def_id)
474 } else {
475 FetchEligibleAssocItemResponse::NotFound(typing_mode.assert_not_erased())
478 }
479 }
480
481 fn is_transmutable(
484 &self,
485 src: Ty<'tcx>,
486 dst: Ty<'tcx>,
487 assume: ty::Const<'tcx>,
488 ) -> Result<Certainty, NoSolution> {
489 let (dst, src) = self.tcx.erase_and_anonymize_regions((dst, src));
492
493 let Some(assume) = rustc_transmute::Assume::from_const(self.tcx, assume) else {
494 return Err(NoSolution);
495 };
496
497 match rustc_transmute::TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume) {
499 rustc_transmute::Answer::Yes => Ok(Certainty::Yes),
500 rustc_transmute::Answer::No(_) | rustc_transmute::Answer::If(_) => Err(NoSolution),
501 }
502 }
503
504 fn obtain_canonicalizer_state(&self) -> CanonicalizerState<Self::Interner> {
505 mem::take(&mut self.canonicalizer_state.borrow_mut())
507 }
508
509 fn release_canonicalizer_state(&self, mut state: CanonicalizerState<Self::Interner>) {
510 state.clear();
512 *self.canonicalizer_state.borrow_mut() = state;
513 }
514
515 fn emit_next_solver_overflow_fcw(&self, goal: Goal<'tcx, ty::Predicate<'tcx>>, span: Span) {
516 let tcx = self.tcx;
517 let goal = self.resolve_vars_if_possible(goal);
518 let mut visitor = OverflowedGoalChain {
519 span,
520 predicates: ::alloc::vec::Vec::new()vec![],
521 recursion_limit: usize::min(16, tcx.recursion_limit().0),
522 };
523 let _ = self
524 .with_disabled_next_solver_overflow_fcw(|| self.visit_proof_tree(goal, &mut visitor));
525 tcx.emit_node_span_lint(
526 RECURSION_DEPTH_EXCEEDING_LIMIT,
527 CRATE_HIR_ID,
528 span,
529 rustc_errors::DiagDecorator(|diag| {
530 let pred_str = |pred: ty::Predicate<'tcx>| {
532 let s = pred.to_string();
533 if s.len() > 80 {
534 let mut p: FmtPrinter<'_, '_> =
535 FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, Limit(10));
536 pred.print(&mut p).unwrap();
537 p.into_buffer()
538 } else {
539 s
540 }
541 };
542 diag.primary_message(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("overflow evaluating the requirement `{0}`",
pred_str(goal.predicate)))
})format!(
543 "overflow evaluating the requirement `{}`",
544 pred_str(goal.predicate),
545 ));
546 for p in visitor.predicates.into_iter().skip(1) {
547 diag.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("which requires `{0}`",
pred_str(p)))
})format!("which requires `{}`", pred_str(p)));
548 }
549 diag.note("and so on...");
550 diag.help(
551 "consider adding a manual `impl` of auto traits like `Send` for intermediate types, if auto traits are involved",
552 );
553 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!(
554 "or consider increasing the recursion limit by adding a \
555 `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
556 tcx.recursion_limit() * 2,
557 tcx.crate_name(LOCAL_CRATE),
558 ));
559 diag.note("this lint is attached to the whole crate and can't be disabled on a per-function basis");
560 }),
561 )
562 }
563}
564
565struct OverflowedGoalChain<'tcx> {
566 span: Span,
567 predicates: Vec<ty::Predicate<'tcx>>,
568 recursion_limit: usize,
569}
570
571impl<'tcx> ProofTreeVisitor<'tcx> for OverflowedGoalChain<'tcx> {
572 type Result = ControlFlow<()>;
573
574 fn span(&self) -> Span {
575 self.span
576 }
577
578 fn config(&self) -> InspectConfig {
579 InspectConfig { max_depth: self.recursion_limit }
580 }
581
582 fn visit_goal(&mut self, goal: &inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
583 self.predicates.push(goal.goal().predicate);
584 if let Some(cand) = goal.unique_applicable_candidate() {
585 goal.infcx().probe(|_| {
586 if let Some(nested_goal_with_largest_required_depth) = cand
587 .instantiate_nested_goals(self.span)
588 .into_iter()
589 .max_by_key(|g| g.required_depth())
590 {
591 nested_goal_with_largest_required_depth.visit_with(self)
592 } else {
593 ControlFlow::Continue(())
594 }
595 })?;
596 }
597 ControlFlow::Continue(())
598 }
599
600 fn on_recursion_limit(&mut self) -> Self::Result {
601 ControlFlow::Break(())
602 }
603}