rustc_trait_selection/solve/
delegate.rs1use std::collections::hash_map::Entry;
2use std::mem;
3use std::ops::Deref;
4
5use rustc_data_structures::fx::{FxHashMap, FxHashSet};
6use rustc_hir::attrs::lang_items::LangItem;
7use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
8use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
9use rustc_infer::infer::canonical::{
10 Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarKind, CanonicalVarValues,
11 QueryRegionConstraint,
12};
13use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt};
14use rustc_infer::traits::solve::{
15 ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, SucceededInErased,
16};
17use rustc_middle::traits::query::NoSolution;
18use rustc_middle::traits::solve::{Certainty, MaybeInfo};
19use rustc_middle::ty::{
20 self, CanonicalizerState, MayBeErased, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeSuperVisitable,
21 TypeVisitable, TypeVisitableExt, TypeVisitor, TypingMode,
22};
23use rustc_next_trait_solver::solve::{GoalStalledOn, GoalStalledOnOpaques, TyOrConstInferVar};
24use rustc_span::{DUMMY_SP, Span};
25use thin_vec::{ThinVec, thin_vec};
26
27use crate::traits::{EvaluateConstErr, ObligationCause, sizedness_fast_path, specialization_graph};
28
29#[repr(transparent)]
30pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>);
31
32impl<'a, 'tcx> From<&'a InferCtxt<'tcx>> for &'a SolverDelegate<'tcx> {
33 fn from(infcx: &'a InferCtxt<'tcx>) -> Self {
34 unsafe { std::mem::transmute(infcx) }
36 }
37}
38
39impl<'tcx> Deref for SolverDelegate<'tcx> {
40 type Target = InferCtxt<'tcx>;
41
42 fn deref(&self) -> &Self::Target {
43 &self.0
44 }
45}
46
47impl<'tcx> SolverDelegate<'tcx> {
48 fn known_no_opaque_types_in_storage(&self) -> bool {
49 self.inner.borrow_mut().opaque_types().is_empty()
50 && !self.typing_mode_raw().is_erased_not_coherence()
53 }
54}
55
56fn goal_stalled_on_args<'tcx>(
59 stalled_vars: ThinVec<TyOrConstInferVar>,
60) -> ComputeGoalFastPathOutcome<'tcx> {
61 ComputeGoalFastPathOutcome::TriviallyStalled {
62 stalled_on: GoalStalledOn {
63 stalled_vars,
64 sub_roots: ThinVec::new(),
65 stalled_maybe_info: MaybeInfo::AMBIGUOUS,
66 opaques: GoalStalledOnOpaques::No,
67 },
68 }
69}
70
71fn goal_stalled_on_args_or_nonempty_opaques<'tcx>(
75 stalled_vars: ThinVec<TyOrConstInferVar>,
76) -> ComputeGoalFastPathOutcome<'tcx> {
77 ComputeGoalFastPathOutcome::TriviallyStalled {
78 stalled_on: GoalStalledOn {
79 stalled_vars,
80 sub_roots: ThinVec::new(),
81 stalled_maybe_info: MaybeInfo::AMBIGUOUS,
82 opaques: GoalStalledOnOpaques::Yes {
83 num_opaques_in_storage: 0,
84 previously_succeeded_in_erased: SucceededInErased::No,
88 },
89 },
90 }
91}
92
93struct CollectNonRegionInfer<'tcx> {
94 infers: ThinVec<ty::GenericArg<'tcx>>,
95 visited: FxHashSet<Ty<'tcx>>,
96}
97
98impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for CollectNonRegionInfer<'tcx> {
99 fn visit_ty(&mut self, ty: Ty<'tcx>) {
100 if self.visited.contains(&ty) {
101 return;
102 }
103
104 match ty.kind() {
105 ty::Infer(_) => self.infers.push(ty.into()),
106 _ => ty.super_visit_with(self),
107 }
108
109 self.visited.insert(ty);
110 }
111
112 fn visit_const(&mut self, ct: ty::Const<'tcx>) {
113 match ct.kind() {
114 ty::ConstKind::Infer(_) => self.infers.push(ct.into()),
115 _ => ct.super_visit_with(self),
116 }
117 }
118}
119
120impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<'tcx> {
121 type Infcx = InferCtxt<'tcx>;
122 type Interner = TyCtxt<'tcx>;
123
124 fn cx(&self) -> TyCtxt<'tcx> {
125 self.0.tcx
126 }
127
128 fn build_with_canonical<V>(
129 interner: TyCtxt<'tcx>,
130 canonical: &CanonicalQueryInput<'tcx, V>,
131 ) -> (Self, V, CanonicalVarValues<'tcx>)
132 where
133 V: TypeFoldable<TyCtxt<'tcx>>,
134 {
135 let (infcx, value, vars) = interner
136 .infer_ctxt()
137 .with_next_trait_solver(true)
138 .build_with_canonical(DUMMY_SP, canonical);
139 (SolverDelegate(infcx), value, vars)
140 }
141
142 fn compute_goal_fast_path(
143 &self,
144 goal: Goal<'tcx, ty::Predicate<'tcx>>,
145 span: Span,
146 ) -> ComputeGoalFastPathOutcome<'tcx> {
147 use ComputeGoalFastPathOutcome as Outcome;
148
149 if self.tcx.assumptions_on_binders() {
151 return Outcome::NoFastPath;
152 }
153
154 let pred = goal.predicate.kind();
155 match pred.skip_binder() {
156 ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {
157 let trait_pred = pred.rebind(trait_pred);
158
159 let self_ty = self.shallow_resolve(trait_pred.self_ty().skip_binder());
160 if let Some(vid) = self_ty.ty_vid()
161 && self.known_no_opaque_types_in_storage()
166 {
167 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)])
168 } else if trait_pred.polarity() == ty::PredicatePolarity::Positive {
169 match self.0.tcx.as_lang_item(trait_pred.def_id()) {
170 Some(LangItem::Sized) | Some(LangItem::MetaSized) => {
171 let predicate = self.resolve_vars_if_possible(goal.predicate);
172 if sizedness_fast_path(self.tcx, predicate, goal.param_env) {
173 Outcome::TriviallyHolds
174 } else {
175 Outcome::NoFastPath
176 }
177 }
178 Some(LangItem::Copy | LangItem::Clone) => {
179 let self_ty =
180 self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder());
181 if !self_ty
187 .has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER)
188 && self_ty.is_trivially_pure_clone_copy()
189 {
190 Outcome::TriviallyHolds
191 } else {
192 Outcome::NoFastPath
193 }
194 }
195 _ => Outcome::NoFastPath,
196 }
197 } else {
198 Outcome::NoFastPath
199 }
200 }
201 ty::PredicateKind::DynCompatible(def_id) if self.0.tcx.is_dyn_compatible(def_id) => {
202 Outcome::TriviallyHolds
203 }
204 ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(outlives)) => {
205 if outlives.has_escaping_bound_vars() {
206 return Outcome::NoFastPath;
207 }
208
209 self.0.sub_regions(
210 SubregionOrigin::RelateRegionParamBound(span, None),
211 outlives.1,
212 outlives.0,
213 ty::VisibleForLeakCheck::Yes,
214 );
215 Outcome::TriviallyHolds
216 }
217 ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => {
218 if outlives.has_escaping_bound_vars() {
219 return Outcome::NoFastPath;
220 }
221
222 let ty = self.resolve_vars_if_possible(outlives.0);
223 let mut infer_collector = CollectNonRegionInfer {
224 infers: Default::default(),
225 visited: Default::default(),
226 };
227 ty.visit_with(&mut infer_collector);
228 let infers = infer_collector.infers;
229 if !infers.is_empty() {
230 return goal_stalled_on_args(
231 infers
232 .into_iter()
233 .map(|i| {
234 TyOrConstInferVar::maybe_from_generic_arg::<Self::Interner>(i)
235 .unwrap()
236 })
237 .collect(),
238 );
239 }
240
241 if ty.has_non_rigid_aliases() {
242 return Outcome::NoFastPath;
243 }
244
245 self.0.register_type_outlives_constraint(
246 outlives.0,
247 outlives.1,
248 &ObligationCause::dummy_with_span(span),
249 );
250
251 Outcome::TriviallyHolds
252 }
253 ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, .. })
254 | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
255 if a.has_escaping_bound_vars() || b.has_escaping_bound_vars() {
256 return Outcome::NoFastPath;
257 }
258
259 match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) {
260 (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
261 self.sub_unify_ty_vids_raw(a_vid, b_vid);
262 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![
263 TyOrConstInferVar::Ty(a_vid),
264 TyOrConstInferVar::Ty(b_vid),
265 ])
266 }
267 _ => Outcome::NoFastPath,
268 }
269 }
270 ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) => {
271 if ct.has_escaping_bound_vars() {
272 return Outcome::NoFastPath;
273 }
274
275 let arg = self.shallow_resolve_const(ct);
276 if let Some(vid) = arg.ct_vid() {
277 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)])
278 } else {
279 Outcome::NoFastPath
280 }
281 }
282 ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
283 if arg.has_escaping_bound_vars() {
284 return Outcome::NoFastPath;
285 }
286
287 let arg = self.shallow_resolve_term(arg);
288 if arg.is_trivially_wf(self.tcx) {
289 Outcome::TriviallyHolds
290 } else if arg.is_infer() {
291 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![
292 TyOrConstInferVar::maybe_from_term::<TyCtxt<'tcx>>(arg)
293 .expect("its an infer var"),
294 ])
295 } else {
296 Outcome::NoFastPath
297 }
298 }
299 _ => Outcome::NoFastPath,
300 }
301 }
302
303 fn fresh_var_for_kind(
304 &self,
305 arg: ty::GenericArg<'tcx>,
306 span: Span,
307 universe: ty::UniverseIndex,
308 ) -> ty::GenericArg<'tcx> {
309 match arg.kind() {
310 ty::GenericArgKind::Lifetime(_) => {
311 self.next_region_var_in_universe(RegionVariableOrigin::Misc(span), universe).into()
312 }
313 ty::GenericArgKind::Type(_) => self.next_ty_var_in_universe(span, universe).into(),
314 ty::GenericArgKind::Const(_) => self.next_const_var_in_universe(span, universe).into(),
315 }
316 }
317
318 fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution> {
319 self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution)
320 }
321
322 fn evaluate_const(
323 &self,
324 param_env: ty::ParamEnv<'tcx>,
325 alias_const: ty::AliasConst<'tcx>,
326 ) -> Option<ty::Const<'tcx>> {
327 let ct = ty::Const::new_alias(self.tcx, ty::IsRigid::No, alias_const);
328
329 match crate::traits::try_evaluate_const(&self.0, ct, param_env) {
330 Ok(ct) => Some(ct),
331 Err(EvaluateConstErr::EvaluationFailure(e)) => Some(ty::Const::new_error(self.tcx, e)),
332 Err(
333 EvaluateConstErr::InvalidConstParamTy(_) | EvaluateConstErr::HasGenericsOrInfers,
334 ) => None,
335 }
336 }
337
338 fn well_formed_goals(
339 &self,
340 param_env: ty::ParamEnv<'tcx>,
341 term: ty::Term<'tcx>,
342 ) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> {
343 crate::traits::wf::unnormalized_obligations(
344 &self.0,
345 param_env,
346 term,
347 DUMMY_SP,
348 CRATE_DEF_ID,
349 )
350 .map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect())
351 }
352
353 fn make_deduplicated_region_constraints(
354 &self,
355 ) -> Vec<(ty::RegionConstraint<'tcx>, ty::VisibleForLeakCheck)> {
356 let region_obligations = self.0.inner.borrow().region_obligations().to_owned();
359 let region_assumptions = self.0.inner.borrow().region_assumptions().to_owned();
360 let region_constraints = self.0.with_region_constraints(|region_constraints| {
361 make_query_region_constraints(
362 region_obligations,
363 region_constraints,
364 region_assumptions,
365 )
366 });
367
368 let mut seen = FxHashMap::default();
369 let mut constraints = ::alloc::vec::Vec::new()vec![];
370 for QueryRegionConstraint { constraint: outlives, visible_for_leak_check: vis, .. } in
371 region_constraints.constraints
372 {
373 match seen.entry(outlives) {
374 Entry::Occupied(occupied) => {
375 let idx = occupied.get();
376 let (_, prev_vis): &mut (_, ty::VisibleForLeakCheck) =
377 constraints.get_mut(*idx).unwrap();
378 *prev_vis = (*prev_vis).or(vis);
379 }
380 Entry::Vacant(vacant) => {
381 vacant.insert(constraints.len());
382 constraints.push((outlives, vis));
383 }
384 }
385 }
386 constraints
387 }
388
389 fn instantiate_canonical<V>(
390 &self,
391 canonical: Canonical<'tcx, V>,
392 values: CanonicalVarValues<'tcx>,
393 ) -> V
394 where
395 V: TypeFoldable<TyCtxt<'tcx>>,
396 {
397 canonical.instantiate(self.tcx, &values)
398 }
399
400 fn instantiate_canonical_var(
401 &self,
402 kind: CanonicalVarKind<'tcx>,
403 span: Span,
404 var_values: &[ty::GenericArg<'tcx>],
405 universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
406 ) -> ty::GenericArg<'tcx> {
407 self.0.instantiate_canonical_var(span, kind, var_values, universe_map)
408 }
409
410 fn add_item_bounds_for_hidden_type(
411 &self,
412 def_id: DefId,
413 args: ty::GenericArgsRef<'tcx>,
414 param_env: ty::ParamEnv<'tcx>,
415 hidden_ty: Ty<'tcx>,
416 goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
417 ) {
418 self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals);
419 }
420
421 fn fetch_eligible_assoc_item(
422 &self,
423 goal_trait_ref: ty::TraitRef<'tcx>,
424 trait_assoc_def_id: DefId,
425 impl_def_id: DefId,
426 ) -> FetchEligibleAssocItemResponse<'tcx> {
427 let node_item =
428 match specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id) {
429 Ok(i) => i,
430 Err(guar) => return FetchEligibleAssocItemResponse::Err(guar),
431 };
432
433 let typing_mode = self.typing_mode_raw();
434
435 let eligible = if node_item.is_final() {
436 true
438 } else {
439 match typing_mode {
444 TypingMode::Coherence
445 | TypingMode::Typeck { .. }
446 | TypingMode::PostTypeckUntilBorrowck { .. }
447 | TypingMode::Reflection
448 | TypingMode::PostBorrowck { .. } => false,
449 TypingMode::PostAnalysis | TypingMode::Codegen => {
450 let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref);
451 !poly_trait_ref.still_further_specializable()
452 }
453 TypingMode::ErasedNotCoherence(MayBeErased) => {
454 return FetchEligibleAssocItemResponse::NotFoundBecauseErased;
455 }
456 }
457 };
458
459 if eligible {
461 FetchEligibleAssocItemResponse::Found(node_item.item.def_id)
462 } else {
463 FetchEligibleAssocItemResponse::NotFound(typing_mode.assert_not_erased())
466 }
467 }
468
469 fn is_transmutable(
472 &self,
473 src: Ty<'tcx>,
474 dst: Ty<'tcx>,
475 assume: ty::Const<'tcx>,
476 ) -> Result<Certainty, NoSolution> {
477 let (dst, src) = self.tcx.erase_and_anonymize_regions((dst, src));
480
481 let Some(assume) = rustc_transmute::Assume::from_const(self.tcx, assume) else {
482 return Err(NoSolution);
483 };
484
485 match rustc_transmute::TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume) {
487 rustc_transmute::Answer::Yes => Ok(Certainty::Yes),
488 rustc_transmute::Answer::No(_) | rustc_transmute::Answer::If(_) => Err(NoSolution),
489 }
490 }
491
492 fn obtain_canonicalizer_state(&self) -> CanonicalizerState<Self::Interner> {
493 mem::take(&mut self.canonicalizer_state.borrow_mut())
495 }
496
497 fn release_canonicalizer_state(&self, mut state: CanonicalizerState<Self::Interner>) {
498 state.clear();
500 *self.canonicalizer_state.borrow_mut() = state;
501 }
502}