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