rustc_trait_selection/solve/
delegate.rs1use std::collections::hash_map::Entry;
2use std::ops::Deref;
3
4use rustc_data_structures::fx::FxHashMap;
5use rustc_hir::LangItem;
6use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
7use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
8use rustc_infer::infer::canonical::{
9 Canonical, CanonicalExt as _, CanonicalQueryInput, CanonicalVarKind, CanonicalVarValues,
10 QueryRegionConstraint,
11};
12use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt};
13use rustc_infer::traits::solve::{
14 ComputeGoalFastPathOutcome, FetchEligibleAssocItemResponse, Goal, SucceededInErased,
15};
16use rustc_middle::traits::query::NoSolution;
17use rustc_middle::traits::solve::Certainty;
18use rustc_middle::ty::{
19 self, MayBeErased, Ty, TyCtxt, TypeFlags, TypeFoldable, TypeVisitableExt, TypingMode,
20};
21use rustc_next_trait_solver::solve::{GoalStalledOn, GoalStalledOnOpaques};
22use rustc_span::{DUMMY_SP, Span};
23
24use crate::traits::{EvaluateConstErr, ObligationCause, sizedness_fast_path, specialization_graph};
25
26#[repr(transparent)]
27pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>);
28
29impl<'a, 'tcx> From<&'a InferCtxt<'tcx>> for &'a SolverDelegate<'tcx> {
30 fn from(infcx: &'a InferCtxt<'tcx>) -> Self {
31 unsafe { std::mem::transmute(infcx) }
33 }
34}
35
36impl<'tcx> Deref for SolverDelegate<'tcx> {
37 type Target = InferCtxt<'tcx>;
38
39 fn deref(&self) -> &Self::Target {
40 &self.0
41 }
42}
43
44impl<'tcx> SolverDelegate<'tcx> {
45 fn known_no_opaque_types_in_storage(&self) -> bool {
46 self.inner.borrow_mut().opaque_types().is_empty()
47 && !self.typing_mode_raw().is_erased_not_coherence()
50 }
51}
52
53fn goal_stalled_on_args<'tcx>(
56 stalled_vars: Vec<ty::GenericArg<'tcx>>,
57) -> ComputeGoalFastPathOutcome<'tcx> {
58 ComputeGoalFastPathOutcome::TriviallyStalled {
59 stalled_on: GoalStalledOn {
60 stalled_vars,
61 sub_roots: Vec::new(),
62 stalled_certainty: Certainty::AMBIGUOUS,
63 opaques: GoalStalledOnOpaques::No,
64 },
65 }
66}
67
68fn goal_stalled_on_args_or_nonempty_opaques<'tcx>(
72 stalled_vars: Vec<ty::GenericArg<'tcx>>,
73) -> ComputeGoalFastPathOutcome<'tcx> {
74 ComputeGoalFastPathOutcome::TriviallyStalled {
75 stalled_on: GoalStalledOn {
76 stalled_vars,
77 sub_roots: Vec::new(),
78 stalled_certainty: Certainty::AMBIGUOUS,
79 opaques: GoalStalledOnOpaques::Yes {
80 num_opaques_in_storage: 0,
81 previously_succeeded_in_erased: SucceededInErased::No,
85 },
86 },
87 }
88}
89
90impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<'tcx> {
91 type Infcx = InferCtxt<'tcx>;
92 type Interner = TyCtxt<'tcx>;
93
94 fn cx(&self) -> TyCtxt<'tcx> {
95 self.0.tcx
96 }
97
98 fn build_with_canonical<V>(
99 interner: TyCtxt<'tcx>,
100 canonical: &CanonicalQueryInput<'tcx, V>,
101 ) -> (Self, V, CanonicalVarValues<'tcx>)
102 where
103 V: TypeFoldable<TyCtxt<'tcx>>,
104 {
105 let (infcx, value, vars) = interner
106 .infer_ctxt()
107 .with_next_trait_solver(true)
108 .build_with_canonical(DUMMY_SP, canonical);
109 (SolverDelegate(infcx), value, vars)
110 }
111
112 fn compute_goal_fast_path(
113 &self,
114 goal: Goal<'tcx, ty::Predicate<'tcx>>,
115 span: Span,
116 ) -> ComputeGoalFastPathOutcome<'tcx> {
117 use ComputeGoalFastPathOutcome as Outcome;
118
119 if self.tcx.assumptions_on_binders() {
121 return Outcome::NoFastPath;
122 }
123
124 let pred = goal.predicate.kind();
125 match pred.skip_binder() {
126 ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {
127 let trait_pred = pred.rebind(trait_pred);
128
129 let self_ty = self.shallow_resolve(trait_pred.self_ty().skip_binder());
130 if self_ty.is_ty_var()
131 && self.known_no_opaque_types_in_storage()
136 {
137 goal_stalled_on_args_or_nonempty_opaques(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[self_ty.into()]))vec![self_ty.into()])
138 } else if trait_pred.polarity() == ty::PredicatePolarity::Positive {
139 match self.0.tcx.as_lang_item(trait_pred.def_id()) {
140 Some(LangItem::Sized) | Some(LangItem::MetaSized) => {
141 let predicate = self.resolve_vars_if_possible(goal.predicate);
142 if sizedness_fast_path(self.tcx, predicate, goal.param_env) {
143 Outcome::TriviallyHolds
144 } else {
145 Outcome::NoFastPath
146 }
147 }
148 Some(LangItem::Copy | LangItem::Clone) => {
149 let self_ty =
150 self.resolve_vars_if_possible(trait_pred.self_ty().skip_binder());
151 if !self_ty
157 .has_type_flags(TypeFlags::HAS_FREE_REGIONS | TypeFlags::HAS_INFER)
158 && self_ty.is_trivially_pure_clone_copy()
159 {
160 Outcome::TriviallyHolds
161 } else {
162 Outcome::NoFastPath
163 }
164 }
165 _ => Outcome::NoFastPath,
166 }
167 } else {
168 Outcome::NoFastPath
169 }
170 }
171 ty::PredicateKind::DynCompatible(def_id) if self.0.tcx.is_dyn_compatible(def_id) => {
172 Outcome::TriviallyHolds
173 }
174 ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(outlives)) => {
175 if outlives.has_escaping_bound_vars() {
176 return Outcome::NoFastPath;
177 }
178
179 self.0.sub_regions(
180 SubregionOrigin::RelateRegionParamBound(span, None),
181 outlives.1,
182 outlives.0,
183 ty::VisibleForLeakCheck::Yes,
184 );
185 Outcome::TriviallyHolds
186 }
187 ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(outlives)) => {
188 if outlives.has_escaping_bound_vars() {
189 return Outcome::NoFastPath;
190 }
191
192 self.0.register_type_outlives_constraint(
193 outlives.0,
194 outlives.1,
195 &ObligationCause::dummy_with_span(span),
196 );
197
198 Outcome::TriviallyHolds
199 }
200 ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, .. })
201 | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
202 if a.has_escaping_bound_vars() || b.has_escaping_bound_vars() {
203 return Outcome::NoFastPath;
204 }
205
206 match (self.shallow_resolve(a).kind(), self.shallow_resolve(b).kind()) {
207 (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => {
208 self.sub_unify_ty_vids_raw(a_vid, b_vid);
209 goal_stalled_on_args(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[a.into(), b.into()]))vec![a.into(), b.into()])
210 }
211 _ => Outcome::NoFastPath,
212 }
213 }
214 ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) => {
215 if ct.has_escaping_bound_vars() {
216 return Outcome::NoFastPath;
217 }
218
219 let arg = self.shallow_resolve_const(ct);
220 if arg.is_ct_infer() {
221 goal_stalled_on_args(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[arg.into()]))vec![arg.into()])
222 } else {
223 Outcome::NoFastPath
224 }
225 }
226 ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
227 if arg.has_escaping_bound_vars() {
228 return Outcome::NoFastPath;
229 }
230
231 let arg = self.shallow_resolve_term(arg);
232 if arg.is_trivially_wf(self.tcx) {
233 Outcome::TriviallyHolds
234 } else if arg.is_infer() {
235 goal_stalled_on_args(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[arg.into_arg()]))vec![arg.into_arg()])
236 } else {
237 Outcome::NoFastPath
238 }
239 }
240 _ => Outcome::NoFastPath,
241 }
242 }
243
244 fn fresh_var_for_kind_with_span(
245 &self,
246 arg: ty::GenericArg<'tcx>,
247 span: Span,
248 ) -> ty::GenericArg<'tcx> {
249 match arg.kind() {
250 ty::GenericArgKind::Lifetime(_) => {
251 self.next_region_var(RegionVariableOrigin::Misc(span)).into()
252 }
253 ty::GenericArgKind::Type(_) => self.next_ty_var(span).into(),
254 ty::GenericArgKind::Const(_) => self.next_const_var(span).into(),
255 }
256 }
257
258 fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution> {
259 self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution)
260 }
261
262 fn evaluate_const(
263 &self,
264 param_env: ty::ParamEnv<'tcx>,
265 alias_const: ty::AliasConst<'tcx>,
266 ) -> Option<ty::Const<'tcx>> {
267 let ct = ty::Const::new_alias(self.tcx, ty::IsRigid::No, alias_const);
268
269 match crate::traits::try_evaluate_const(&self.0, ct, param_env) {
270 Ok(ct) => Some(ct),
271 Err(EvaluateConstErr::EvaluationFailure(e)) => Some(ty::Const::new_error(self.tcx, e)),
272 Err(
273 EvaluateConstErr::InvalidConstParamTy(_) | EvaluateConstErr::HasGenericsOrInfers,
274 ) => None,
275 }
276 }
277
278 fn well_formed_goals(
279 &self,
280 param_env: ty::ParamEnv<'tcx>,
281 term: ty::Term<'tcx>,
282 ) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> {
283 crate::traits::wf::unnormalized_obligations(
284 &self.0,
285 param_env,
286 term,
287 DUMMY_SP,
288 CRATE_DEF_ID,
289 )
290 .map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect())
291 }
292
293 fn make_deduplicated_region_constraints(
294 &self,
295 ) -> Vec<(ty::RegionConstraint<'tcx>, ty::VisibleForLeakCheck)> {
296 let region_obligations = self.0.inner.borrow().region_obligations().to_owned();
299 let region_assumptions = self.0.inner.borrow().region_assumptions().to_owned();
300 let region_constraints = self.0.with_region_constraints(|region_constraints| {
301 make_query_region_constraints(
302 region_obligations,
303 region_constraints,
304 region_assumptions,
305 )
306 });
307
308 let mut seen = FxHashMap::default();
309 let mut constraints = ::alloc::vec::Vec::new()vec![];
310 for QueryRegionConstraint { constraint: outlives, visible_for_leak_check: vis, .. } in
311 region_constraints.constraints
312 {
313 match seen.entry(outlives) {
314 Entry::Occupied(occupied) => {
315 let idx = occupied.get();
316 let (_, prev_vis): &mut (_, ty::VisibleForLeakCheck) =
317 constraints.get_mut(*idx).unwrap();
318 *prev_vis = (*prev_vis).or(vis);
319 }
320 Entry::Vacant(vacant) => {
321 vacant.insert(constraints.len());
322 constraints.push((outlives, vis));
323 }
324 }
325 }
326 constraints
327 }
328
329 fn instantiate_canonical<V>(
330 &self,
331 canonical: Canonical<'tcx, V>,
332 values: CanonicalVarValues<'tcx>,
333 ) -> V
334 where
335 V: TypeFoldable<TyCtxt<'tcx>>,
336 {
337 canonical.instantiate(self.tcx, &values)
338 }
339
340 fn instantiate_canonical_var(
341 &self,
342 kind: CanonicalVarKind<'tcx>,
343 span: Span,
344 var_values: &[ty::GenericArg<'tcx>],
345 universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
346 ) -> ty::GenericArg<'tcx> {
347 self.0.instantiate_canonical_var(span, kind, var_values, universe_map)
348 }
349
350 fn add_item_bounds_for_hidden_type(
351 &self,
352 def_id: DefId,
353 args: ty::GenericArgsRef<'tcx>,
354 param_env: ty::ParamEnv<'tcx>,
355 hidden_ty: Ty<'tcx>,
356 goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
357 ) {
358 self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals);
359 }
360
361 fn fetch_eligible_assoc_item(
362 &self,
363 goal_trait_ref: ty::TraitRef<'tcx>,
364 trait_assoc_def_id: DefId,
365 impl_def_id: DefId,
366 ) -> FetchEligibleAssocItemResponse<'tcx> {
367 let node_item =
368 match specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id) {
369 Ok(i) => i,
370 Err(guar) => return FetchEligibleAssocItemResponse::Err(guar),
371 };
372
373 let typing_mode = self.typing_mode_raw();
374
375 let eligible = if node_item.is_final() {
376 true
378 } else {
379 match typing_mode {
384 TypingMode::Coherence
385 | TypingMode::Typeck { .. }
386 | TypingMode::PostTypeckUntilBorrowck { .. }
387 | TypingMode::PostBorrowck { .. } => false,
388 TypingMode::PostAnalysis | TypingMode::Codegen => {
389 let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref);
390 !poly_trait_ref.still_further_specializable()
391 }
392 TypingMode::ErasedNotCoherence(MayBeErased) => {
393 return FetchEligibleAssocItemResponse::NotFoundBecauseErased;
394 }
395 }
396 };
397
398 if eligible {
400 FetchEligibleAssocItemResponse::Found(node_item.item.def_id)
401 } else {
402 FetchEligibleAssocItemResponse::NotFound(typing_mode.assert_not_erased())
405 }
406 }
407
408 fn is_transmutable(
411 &self,
412 src: Ty<'tcx>,
413 dst: Ty<'tcx>,
414 assume: ty::Const<'tcx>,
415 ) -> Result<Certainty, NoSolution> {
416 let (dst, src) = self.tcx.erase_and_anonymize_regions((dst, src));
419
420 let Some(assume) = rustc_transmute::Assume::from_const(self.tcx, assume) else {
421 return Err(NoSolution);
422 };
423
424 match rustc_transmute::TransmuteTypeEnv::new(self.0.tcx).is_transmutable(src, dst, assume) {
426 rustc_transmute::Answer::Yes => Ok(Certainty::Yes),
427 rustc_transmute::Answer::No(_) | rustc_transmute::Answer::If(_) => Err(NoSolution),
428 }
429 }
430}