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