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