1use rustc_type_ir::data_structures::IndexSet;
4use rustc_type_ir::fast_reject::DeepRejectCtxt;
5use rustc_type_ir::inherent::*;
6use rustc_type_ir::lang_items::SolverTraitLangItem;
7use rustc_type_ir::solve::{CanonicalResponse, SizedTraitKind};
8use rustc_type_ir::{
9 self as ty, Interner, Movability, PredicatePolarity, TraitPredicate, TraitRef,
10 TypeVisitableExt as _, TypingMode, Upcast as _, elaborate,
11};
12use tracing::{debug, instrument, trace};
13
14use crate::delegate::SolverDelegate;
15use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
16use crate::solve::assembly::{
17 self, AllowInferenceConstraints, AssembleCandidatesFrom, Candidate, FailedCandidateInfo,
18};
19use crate::solve::inspect::ProbeKind;
20use crate::solve::{
21 BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
22 MergeCandidateInfo, NoSolution, ParamEnvSource, QueryResult, has_only_region_constraints,
23};
24
25impl<D, I> assembly::GoalKind<D> for TraitPredicate<I>
26where
27 D: SolverDelegate<Interner = I>,
28 I: Interner,
29{
30 fn self_ty(self) -> I::Ty {
31 self.self_ty()
32 }
33
34 fn trait_ref(self, _: I) -> ty::TraitRef<I> {
35 self.trait_ref
36 }
37
38 fn with_replaced_self_ty(self, cx: I, self_ty: I::Ty) -> Self {
39 self.with_replaced_self_ty(cx, self_ty)
40 }
41
42 fn trait_def_id(self, _: I) -> I::TraitId {
43 self.def_id()
44 }
45
46 fn consider_additional_alias_assumptions(
47 _ecx: &mut EvalCtxt<'_, D>,
48 _goal: Goal<I, Self>,
49 _alias_ty: ty::AliasTy<I>,
50 ) -> Vec<Candidate<I>> {
51 vec![]
52 }
53
54 fn consider_impl_candidate(
55 ecx: &mut EvalCtxt<'_, D>,
56 goal: Goal<I, TraitPredicate<I>>,
57 impl_def_id: I::ImplId,
58 ) -> Result<Candidate<I>, NoSolution> {
59 let cx = ecx.cx();
60
61 let impl_trait_ref = cx.impl_trait_ref(impl_def_id);
62 if !DeepRejectCtxt::relate_rigid_infer(ecx.cx())
63 .args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
64 {
65 return Err(NoSolution);
66 }
67
68 let impl_polarity = cx.impl_polarity(impl_def_id);
71 let maximal_certainty = match (impl_polarity, goal.predicate.polarity) {
72 (ty::ImplPolarity::Reservation, _) => match ecx.typing_mode() {
75 TypingMode::Coherence => Certainty::AMBIGUOUS,
76 TypingMode::Analysis { .. }
77 | TypingMode::Borrowck { .. }
78 | TypingMode::PostBorrowckAnalysis { .. }
79 | TypingMode::PostAnalysis => return Err(NoSolution),
80 },
81
82 (ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive)
84 | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => Certainty::Yes,
85
86 (ty::ImplPolarity::Positive, ty::PredicatePolarity::Negative)
88 | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Positive) => {
89 return Err(NoSolution);
90 }
91 };
92
93 ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
94 let impl_args = ecx.fresh_args_for_item(impl_def_id.into());
95 ecx.record_impl_args(impl_args);
96 let impl_trait_ref = impl_trait_ref.instantiate(cx, impl_args);
97
98 ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
99 let where_clause_bounds = cx
100 .predicates_of(impl_def_id.into())
101 .iter_instantiated(cx, impl_args)
102 .map(|pred| goal.with(cx, pred));
103 ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
104
105 ecx.add_goals(
109 GoalSource::Misc,
110 cx.impl_super_outlives(impl_def_id)
111 .iter_instantiated(cx, impl_args)
112 .map(|pred| goal.with(cx, pred)),
113 );
114
115 ecx.evaluate_added_goals_and_make_canonical_response(maximal_certainty)
116 })
117 }
118
119 fn consider_error_guaranteed_candidate(
120 ecx: &mut EvalCtxt<'_, D>,
121 _guar: I::ErrorGuaranteed,
122 ) -> Result<Candidate<I>, NoSolution> {
123 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
124 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
125 }
126
127 fn fast_reject_assumption(
128 ecx: &mut EvalCtxt<'_, D>,
129 goal: Goal<I, Self>,
130 assumption: I::Clause,
131 ) -> Result<(), NoSolution> {
132 fn trait_def_id_matches<I: Interner>(
133 cx: I,
134 clause_def_id: I::TraitId,
135 goal_def_id: I::TraitId,
136 polarity: PredicatePolarity,
137 ) -> bool {
138 clause_def_id == goal_def_id
139 || (polarity == PredicatePolarity::Positive
144 && cx.is_trait_lang_item(clause_def_id, SolverTraitLangItem::Sized)
145 && cx.is_trait_lang_item(goal_def_id, SolverTraitLangItem::MetaSized))
146 }
147
148 if let Some(trait_clause) = assumption.as_trait_clause()
149 && trait_clause.polarity() == goal.predicate.polarity
150 && trait_def_id_matches(
151 ecx.cx(),
152 trait_clause.def_id(),
153 goal.predicate.def_id(),
154 goal.predicate.polarity,
155 )
156 && DeepRejectCtxt::relate_rigid_rigid(ecx.cx()).args_may_unify(
157 goal.predicate.trait_ref.args,
158 trait_clause.skip_binder().trait_ref.args,
159 )
160 {
161 return Ok(());
162 } else {
163 Err(NoSolution)
164 }
165 }
166
167 fn match_assumption(
168 ecx: &mut EvalCtxt<'_, D>,
169 goal: Goal<I, Self>,
170 assumption: I::Clause,
171 then: impl FnOnce(&mut EvalCtxt<'_, D>) -> QueryResult<I>,
172 ) -> QueryResult<I> {
173 let trait_clause = assumption.as_trait_clause().unwrap();
174
175 if ecx.cx().is_trait_lang_item(goal.predicate.def_id(), SolverTraitLangItem::MetaSized)
181 && ecx.cx().is_trait_lang_item(trait_clause.def_id(), SolverTraitLangItem::Sized)
182 {
183 let meta_sized_clause =
184 trait_predicate_with_def_id(ecx.cx(), trait_clause, goal.predicate.def_id());
185 return Self::match_assumption(ecx, goal, meta_sized_clause, then);
186 }
187
188 let assumption_trait_pred = ecx.instantiate_binder_with_infer(trait_clause);
189 ecx.eq(goal.param_env, goal.predicate.trait_ref, assumption_trait_pred.trait_ref)?;
190
191 then(ecx)
192 }
193
194 fn consider_auto_trait_candidate(
195 ecx: &mut EvalCtxt<'_, D>,
196 goal: Goal<I, Self>,
197 ) -> Result<Candidate<I>, NoSolution> {
198 let cx = ecx.cx();
199 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
200 return Err(NoSolution);
201 }
202
203 if let Some(result) = ecx.disqualify_auto_trait_candidate_due_to_possible_impl(goal) {
204 return result;
205 }
206
207 if cx.trait_is_unsafe(goal.predicate.def_id())
210 && goal.predicate.self_ty().has_unsafe_fields()
211 {
212 return Err(NoSolution);
213 }
214
215 if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() {
231 debug_assert!(ecx.opaque_type_is_rigid(opaque_ty.def_id));
232 for item_bound in cx.item_self_bounds(opaque_ty.def_id).skip_binder() {
233 if item_bound
234 .as_trait_clause()
235 .is_some_and(|b| b.def_id() == goal.predicate.def_id())
236 {
237 return Err(NoSolution);
238 }
239 }
240 }
241
242 if let Some(cand) = ecx.try_stall_coroutine(goal.predicate.self_ty()) {
244 return cand;
245 }
246
247 ecx.probe_and_evaluate_goal_for_constituent_tys(
248 CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
249 goal,
250 structural_traits::instantiate_constituent_tys_for_auto_trait,
251 )
252 }
253
254 fn consider_trait_alias_candidate(
255 ecx: &mut EvalCtxt<'_, D>,
256 goal: Goal<I, Self>,
257 ) -> Result<Candidate<I>, NoSolution> {
258 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
259 return Err(NoSolution);
260 }
261
262 let cx = ecx.cx();
263
264 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
265 let nested_obligations = cx
266 .predicates_of(goal.predicate.def_id().into())
267 .iter_instantiated(cx, goal.predicate.trait_ref.args)
268 .map(|p| goal.with(cx, p));
269 ecx.add_goals(GoalSource::Misc, nested_obligations);
275 ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
276 })
277 }
278
279 fn consider_builtin_sizedness_candidates(
280 ecx: &mut EvalCtxt<'_, D>,
281 goal: Goal<I, Self>,
282 sizedness: SizedTraitKind,
283 ) -> Result<Candidate<I>, NoSolution> {
284 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
285 return Err(NoSolution);
286 }
287
288 ecx.probe_and_evaluate_goal_for_constituent_tys(
289 CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial),
290 goal,
291 |ecx, ty| {
292 structural_traits::instantiate_constituent_tys_for_sizedness_trait(
293 ecx, sizedness, ty,
294 )
295 },
296 )
297 }
298
299 fn consider_builtin_copy_clone_candidate(
300 ecx: &mut EvalCtxt<'_, D>,
301 goal: Goal<I, Self>,
302 ) -> Result<Candidate<I>, NoSolution> {
303 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
304 return Err(NoSolution);
305 }
306
307 if let Some(cand) = ecx.try_stall_coroutine(goal.predicate.self_ty()) {
309 return cand;
310 }
311
312 ecx.probe_and_evaluate_goal_for_constituent_tys(
313 CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
314 goal,
315 structural_traits::instantiate_constituent_tys_for_copy_clone_trait,
316 )
317 }
318
319 fn consider_builtin_fn_ptr_trait_candidate(
320 ecx: &mut EvalCtxt<'_, D>,
321 goal: Goal<I, Self>,
322 ) -> Result<Candidate<I>, NoSolution> {
323 let self_ty = goal.predicate.self_ty();
324 match goal.predicate.polarity {
325 ty::PredicatePolarity::Positive => {
327 if self_ty.is_fn_ptr() {
328 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
329 ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
330 })
331 } else {
332 Err(NoSolution)
333 }
334 }
335 ty::PredicatePolarity::Negative => {
337 if !self_ty.is_fn_ptr() && self_ty.is_known_rigid() {
340 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
341 ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
342 })
343 } else {
344 Err(NoSolution)
345 }
346 }
347 }
348 }
349
350 fn consider_builtin_fn_trait_candidates(
351 ecx: &mut EvalCtxt<'_, D>,
352 goal: Goal<I, Self>,
353 goal_kind: ty::ClosureKind,
354 ) -> Result<Candidate<I>, NoSolution> {
355 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
356 return Err(NoSolution);
357 }
358
359 let cx = ecx.cx();
360 let tupled_inputs_and_output =
361 match structural_traits::extract_tupled_inputs_and_output_from_callable(
362 cx,
363 goal.predicate.self_ty(),
364 goal_kind,
365 )? {
366 Some(a) => a,
367 None => {
368 return ecx.forced_ambiguity(MaybeCause::Ambiguity);
369 }
370 };
371
372 let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
375 ty::TraitRef::new(cx, cx.require_trait_lang_item(SolverTraitLangItem::Sized), [output])
376 });
377
378 let pred = tupled_inputs_and_output
379 .map_bound(|(inputs, _)| {
380 ty::TraitRef::new(cx, goal.predicate.def_id(), [goal.predicate.self_ty(), inputs])
381 })
382 .upcast(cx);
383 Self::probe_and_consider_implied_clause(
384 ecx,
385 CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
386 goal,
387 pred,
388 [(GoalSource::ImplWhereBound, goal.with(cx, output_is_sized_pred))],
389 )
390 }
391
392 fn consider_builtin_async_fn_trait_candidates(
393 ecx: &mut EvalCtxt<'_, D>,
394 goal: Goal<I, Self>,
395 goal_kind: ty::ClosureKind,
396 ) -> Result<Candidate<I>, NoSolution> {
397 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
398 return Err(NoSolution);
399 }
400
401 let cx = ecx.cx();
402 let (tupled_inputs_and_output_and_coroutine, nested_preds) =
403 structural_traits::extract_tupled_inputs_and_output_from_async_callable(
404 cx,
405 goal.predicate.self_ty(),
406 goal_kind,
407 Region::new_static(cx),
409 )?;
410
411 let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
414 |AsyncCallableRelevantTypes { output_coroutine_ty, .. }| {
415 ty::TraitRef::new(
416 cx,
417 cx.require_trait_lang_item(SolverTraitLangItem::Sized),
418 [output_coroutine_ty],
419 )
420 },
421 );
422
423 let pred = tupled_inputs_and_output_and_coroutine
424 .map_bound(|AsyncCallableRelevantTypes { tupled_inputs_ty, .. }| {
425 ty::TraitRef::new(
426 cx,
427 goal.predicate.def_id(),
428 [goal.predicate.self_ty(), tupled_inputs_ty],
429 )
430 })
431 .upcast(cx);
432 Self::probe_and_consider_implied_clause(
433 ecx,
434 CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
435 goal,
436 pred,
437 [goal.with(cx, output_is_sized_pred)]
438 .into_iter()
439 .chain(nested_preds.into_iter().map(|pred| goal.with(cx, pred)))
440 .map(|goal| (GoalSource::ImplWhereBound, goal)),
441 )
442 }
443
444 fn consider_builtin_async_fn_kind_helper_candidate(
445 ecx: &mut EvalCtxt<'_, D>,
446 goal: Goal<I, Self>,
447 ) -> Result<Candidate<I>, NoSolution> {
448 let [closure_fn_kind_ty, goal_kind_ty] = *goal.predicate.trait_ref.args.as_slice() else {
449 panic!();
450 };
451
452 let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind() else {
453 return Err(NoSolution);
455 };
456 let goal_kind = goal_kind_ty.expect_ty().to_opt_closure_kind().unwrap();
457 if closure_kind.extends(goal_kind) {
458 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
459 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
460 } else {
461 Err(NoSolution)
462 }
463 }
464
465 fn consider_builtin_tuple_candidate(
472 ecx: &mut EvalCtxt<'_, D>,
473 goal: Goal<I, Self>,
474 ) -> Result<Candidate<I>, NoSolution> {
475 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
476 return Err(NoSolution);
477 }
478
479 if let ty::Tuple(..) = goal.predicate.self_ty().kind() {
480 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
481 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
482 } else {
483 Err(NoSolution)
484 }
485 }
486
487 fn consider_builtin_pointee_candidate(
488 ecx: &mut EvalCtxt<'_, D>,
489 goal: Goal<I, Self>,
490 ) -> Result<Candidate<I>, NoSolution> {
491 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
492 return Err(NoSolution);
493 }
494
495 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
496 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
497 }
498
499 fn consider_builtin_future_candidate(
500 ecx: &mut EvalCtxt<'_, D>,
501 goal: Goal<I, Self>,
502 ) -> Result<Candidate<I>, NoSolution> {
503 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
504 return Err(NoSolution);
505 }
506
507 let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else {
508 return Err(NoSolution);
509 };
510
511 let cx = ecx.cx();
513 if !cx.coroutine_is_async(def_id) {
514 return Err(NoSolution);
515 }
516
517 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
521 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
522 }
523
524 fn consider_builtin_iterator_candidate(
525 ecx: &mut EvalCtxt<'_, D>,
526 goal: Goal<I, Self>,
527 ) -> Result<Candidate<I>, NoSolution> {
528 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
529 return Err(NoSolution);
530 }
531
532 let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else {
533 return Err(NoSolution);
534 };
535
536 let cx = ecx.cx();
538 if !cx.coroutine_is_gen(def_id) {
539 return Err(NoSolution);
540 }
541
542 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
546 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
547 }
548
549 fn consider_builtin_fused_iterator_candidate(
550 ecx: &mut EvalCtxt<'_, D>,
551 goal: Goal<I, Self>,
552 ) -> Result<Candidate<I>, NoSolution> {
553 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
554 return Err(NoSolution);
555 }
556
557 let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else {
558 return Err(NoSolution);
559 };
560
561 let cx = ecx.cx();
563 if !cx.coroutine_is_gen(def_id) {
564 return Err(NoSolution);
565 }
566
567 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
569 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
570 }
571
572 fn consider_builtin_async_iterator_candidate(
573 ecx: &mut EvalCtxt<'_, D>,
574 goal: Goal<I, Self>,
575 ) -> Result<Candidate<I>, NoSolution> {
576 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
577 return Err(NoSolution);
578 }
579
580 let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else {
581 return Err(NoSolution);
582 };
583
584 let cx = ecx.cx();
586 if !cx.coroutine_is_async_gen(def_id) {
587 return Err(NoSolution);
588 }
589
590 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
594 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
595 }
596
597 fn consider_builtin_coroutine_candidate(
598 ecx: &mut EvalCtxt<'_, D>,
599 goal: Goal<I, Self>,
600 ) -> Result<Candidate<I>, NoSolution> {
601 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
602 return Err(NoSolution);
603 }
604
605 let self_ty = goal.predicate.self_ty();
606 let ty::Coroutine(def_id, args) = self_ty.kind() else {
607 return Err(NoSolution);
608 };
609
610 let cx = ecx.cx();
612 if !cx.is_general_coroutine(def_id) {
613 return Err(NoSolution);
614 }
615
616 let coroutine = args.as_coroutine();
617 Self::probe_and_consider_implied_clause(
618 ecx,
619 CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
620 goal,
621 ty::TraitRef::new(cx, goal.predicate.def_id(), [self_ty, coroutine.resume_ty()])
622 .upcast(cx),
623 [],
626 )
627 }
628
629 fn consider_builtin_discriminant_kind_candidate(
630 ecx: &mut EvalCtxt<'_, D>,
631 goal: Goal<I, Self>,
632 ) -> Result<Candidate<I>, NoSolution> {
633 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
634 return Err(NoSolution);
635 }
636
637 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
639 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
640 }
641
642 fn consider_builtin_destruct_candidate(
643 ecx: &mut EvalCtxt<'_, D>,
644 goal: Goal<I, Self>,
645 ) -> Result<Candidate<I>, NoSolution> {
646 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
647 return Err(NoSolution);
648 }
649
650 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
653 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
654 }
655
656 fn consider_builtin_transmute_candidate(
657 ecx: &mut EvalCtxt<'_, D>,
658 goal: Goal<I, Self>,
659 ) -> Result<Candidate<I>, NoSolution> {
660 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
661 return Err(NoSolution);
662 }
663
664 if goal.has_non_region_placeholders() {
666 return Err(NoSolution);
667 }
668
669 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
670 let assume = ecx.structurally_normalize_const(
671 goal.param_env,
672 goal.predicate.trait_ref.args.const_at(2),
673 )?;
674
675 let certainty = ecx.is_transmutable(
676 goal.predicate.trait_ref.args.type_at(0),
677 goal.predicate.trait_ref.args.type_at(1),
678 assume,
679 )?;
680 ecx.evaluate_added_goals_and_make_canonical_response(certainty)
681 })
682 }
683
684 fn consider_builtin_bikeshed_guaranteed_no_drop_candidate(
694 ecx: &mut EvalCtxt<'_, D>,
695 goal: Goal<I, Self>,
696 ) -> Result<Candidate<I>, NoSolution> {
697 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
698 return Err(NoSolution);
699 }
700
701 let cx = ecx.cx();
702 ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
703 let ty = goal.predicate.self_ty();
704 match ty.kind() {
705 ty::Ref(..) => {}
707 ty::Adt(def, _) if def.is_manually_drop() => {}
709 ty::Tuple(tys) => {
712 ecx.add_goals(
713 GoalSource::ImplWhereBound,
714 tys.iter().map(|elem_ty| {
715 goal.with(cx, ty::TraitRef::new(cx, goal.predicate.def_id(), [elem_ty]))
716 }),
717 );
718 }
719 ty::Array(elem_ty, _) => {
720 ecx.add_goal(
721 GoalSource::ImplWhereBound,
722 goal.with(cx, ty::TraitRef::new(cx, goal.predicate.def_id(), [elem_ty])),
723 );
724 }
725
726 ty::FnDef(..)
730 | ty::FnPtr(..)
731 | ty::Error(_)
732 | ty::Uint(_)
733 | ty::Int(_)
734 | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
735 | ty::Bool
736 | ty::Float(_)
737 | ty::Char
738 | ty::RawPtr(..)
739 | ty::Never
740 | ty::Pat(..)
741 | ty::Dynamic(..)
742 | ty::Str
743 | ty::Slice(_)
744 | ty::Foreign(..)
745 | ty::Adt(..)
746 | ty::Alias(..)
747 | ty::Param(_)
748 | ty::Placeholder(..)
749 | ty::Closure(..)
750 | ty::CoroutineClosure(..)
751 | ty::Coroutine(..)
752 | ty::UnsafeBinder(_)
753 | ty::CoroutineWitness(..) => {
754 ecx.add_goal(
755 GoalSource::ImplWhereBound,
756 goal.with(
757 cx,
758 ty::TraitRef::new(
759 cx,
760 cx.require_trait_lang_item(SolverTraitLangItem::Copy),
761 [ty],
762 ),
763 ),
764 );
765 }
766
767 ty::Bound(..)
768 | ty::Infer(
769 ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_),
770 ) => {
771 panic!("unexpected type `{ty:?}`")
772 }
773 }
774
775 ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
776 })
777 }
778
779 fn consider_structural_builtin_unsize_candidates(
787 ecx: &mut EvalCtxt<'_, D>,
788 goal: Goal<I, Self>,
789 ) -> Vec<Candidate<I>> {
790 if goal.predicate.polarity != ty::PredicatePolarity::Positive {
791 return vec![];
792 }
793
794 let result_to_single = |result| match result {
795 Ok(resp) => vec![resp],
796 Err(NoSolution) => vec![],
797 };
798
799 ecx.probe(|_| ProbeKind::UnsizeAssembly).enter(|ecx| {
800 let a_ty = goal.predicate.self_ty();
801 let Ok(b_ty) = ecx.structurally_normalize_ty(
804 goal.param_env,
805 goal.predicate.trait_ref.args.type_at(1),
806 ) else {
807 return vec![];
808 };
809
810 let goal = goal.with(ecx.cx(), (a_ty, b_ty));
811 match (a_ty.kind(), b_ty.kind()) {
812 (ty::Infer(ty::TyVar(..)), ..) => panic!("unexpected infer {a_ty:?} {b_ty:?}"),
813
814 (_, ty::Infer(ty::TyVar(..))) => {
815 result_to_single(ecx.forced_ambiguity(MaybeCause::Ambiguity))
816 }
817
818 (
820 ty::Dynamic(a_data, a_region, ty::Dyn),
821 ty::Dynamic(b_data, b_region, ty::Dyn),
822 ) => ecx.consider_builtin_dyn_upcast_candidates(
823 goal, a_data, a_region, b_data, b_region,
824 ),
825
826 (_, ty::Dynamic(b_region, b_data, ty::Dyn)) => result_to_single(
828 ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data),
829 ),
830
831 (ty::Array(a_elem_ty, ..), ty::Slice(b_elem_ty)) => {
833 result_to_single(ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty))
834 }
835
836 (ty::Adt(a_def, a_args), ty::Adt(b_def, b_args))
838 if a_def.is_struct() && a_def == b_def =>
839 {
840 result_to_single(
841 ecx.consider_builtin_struct_unsize(goal, a_def, a_args, b_args),
842 )
843 }
844
845 _ => vec![],
846 }
847 })
848 }
849}
850
851#[inline(always)]
857fn trait_predicate_with_def_id<I: Interner>(
858 cx: I,
859 clause: ty::Binder<I, ty::TraitPredicate<I>>,
860 did: I::TraitId,
861) -> I::Clause {
862 clause
863 .map_bound(|c| TraitPredicate {
864 trait_ref: TraitRef::new_from_args(cx, did, c.trait_ref.args),
865 polarity: c.polarity,
866 })
867 .upcast(cx)
868}
869
870impl<D, I> EvalCtxt<'_, D>
871where
872 D: SolverDelegate<Interner = I>,
873 I: Interner,
874{
875 fn consider_builtin_dyn_upcast_candidates(
885 &mut self,
886 goal: Goal<I, (I::Ty, I::Ty)>,
887 a_data: I::BoundExistentialPredicates,
888 a_region: I::Region,
889 b_data: I::BoundExistentialPredicates,
890 b_region: I::Region,
891 ) -> Vec<Candidate<I>> {
892 let cx = self.cx();
893 let Goal { predicate: (a_ty, _b_ty), .. } = goal;
894
895 let mut responses = vec![];
896 let b_principal_def_id = b_data.principal_def_id();
899 if a_data.principal_def_id() == b_principal_def_id || b_principal_def_id.is_none() {
900 responses.extend(self.consider_builtin_upcast_to_principal(
901 goal,
902 CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
903 a_data,
904 a_region,
905 b_data,
906 b_region,
907 a_data.principal(),
908 ));
909 } else if let Some(a_principal) = a_data.principal() {
910 for (idx, new_a_principal) in
911 elaborate::supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty))
912 .enumerate()
913 .skip(1)
914 {
915 responses.extend(self.consider_builtin_upcast_to_principal(
916 goal,
917 CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting(idx)),
918 a_data,
919 a_region,
920 b_data,
921 b_region,
922 Some(new_a_principal.map_bound(|trait_ref| {
923 ty::ExistentialTraitRef::erase_self_ty(cx, trait_ref)
924 })),
925 ));
926 }
927 }
928
929 responses
930 }
931
932 fn consider_builtin_unsize_to_dyn_candidate(
933 &mut self,
934 goal: Goal<I, (I::Ty, I::Ty)>,
935 b_data: I::BoundExistentialPredicates,
936 b_region: I::Region,
937 ) -> Result<Candidate<I>, NoSolution> {
938 let cx = self.cx();
939 let Goal { predicate: (a_ty, _), .. } = goal;
940
941 if b_data.principal_def_id().is_some_and(|def_id| !cx.trait_is_dyn_compatible(def_id)) {
943 return Err(NoSolution);
944 }
945
946 self.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
947 ecx.add_goals(
950 GoalSource::ImplWhereBound,
951 b_data.iter().map(|pred| goal.with(cx, pred.with_self_ty(cx, a_ty))),
952 );
953
954 ecx.add_goal(
956 GoalSource::ImplWhereBound,
957 goal.with(
958 cx,
959 ty::TraitRef::new(
960 cx,
961 cx.require_trait_lang_item(SolverTraitLangItem::Sized),
962 [a_ty],
963 ),
964 ),
965 );
966
967 ecx.add_goal(GoalSource::Misc, goal.with(cx, ty::OutlivesPredicate(a_ty, b_region)));
969 ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
970 })
971 }
972
973 fn consider_builtin_upcast_to_principal(
974 &mut self,
975 goal: Goal<I, (I::Ty, I::Ty)>,
976 source: CandidateSource<I>,
977 a_data: I::BoundExistentialPredicates,
978 a_region: I::Region,
979 b_data: I::BoundExistentialPredicates,
980 b_region: I::Region,
981 upcast_principal: Option<ty::Binder<I, ty::ExistentialTraitRef<I>>>,
982 ) -> Result<Candidate<I>, NoSolution> {
983 let param_env = goal.param_env;
984
985 let a_auto_traits: IndexSet<I::TraitId> = a_data
989 .auto_traits()
990 .into_iter()
991 .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| {
992 elaborate::supertrait_def_ids(self.cx(), principal_def_id)
993 .filter(|def_id| self.cx().trait_is_auto(*def_id))
994 }))
995 .collect();
996
997 let projection_may_match =
1002 |ecx: &mut EvalCtxt<'_, D>,
1003 source_projection: ty::Binder<I, ty::ExistentialProjection<I>>,
1004 target_projection: ty::Binder<I, ty::ExistentialProjection<I>>| {
1005 source_projection.item_def_id() == target_projection.item_def_id()
1006 && ecx
1007 .probe(|_| ProbeKind::ProjectionCompatibility)
1008 .enter(|ecx| -> Result<_, NoSolution> {
1009 ecx.enter_forall(target_projection, |ecx, target_projection| {
1010 let source_projection =
1011 ecx.instantiate_binder_with_infer(source_projection);
1012 ecx.eq(param_env, source_projection, target_projection)?;
1013 ecx.try_evaluate_added_goals()
1014 })
1015 })
1016 .is_ok()
1017 };
1018
1019 self.probe_trait_candidate(source).enter(|ecx| {
1020 for bound in b_data.iter() {
1021 match bound.skip_binder() {
1022 ty::ExistentialPredicate::Trait(target_principal) => {
1025 let source_principal = upcast_principal.unwrap();
1026 let target_principal = bound.rebind(target_principal);
1027 ecx.enter_forall(target_principal, |ecx, target_principal| {
1028 let source_principal =
1029 ecx.instantiate_binder_with_infer(source_principal);
1030 ecx.eq(param_env, source_principal, target_principal)?;
1031 ecx.try_evaluate_added_goals()
1032 })?;
1033 }
1034 ty::ExistentialPredicate::Projection(target_projection) => {
1040 let target_projection = bound.rebind(target_projection);
1041 let mut matching_projections =
1042 a_data.projection_bounds().into_iter().filter(|source_projection| {
1043 projection_may_match(ecx, *source_projection, target_projection)
1044 });
1045 let Some(source_projection) = matching_projections.next() else {
1046 return Err(NoSolution);
1047 };
1048 if matching_projections.next().is_some() {
1049 return ecx.evaluate_added_goals_and_make_canonical_response(
1050 Certainty::AMBIGUOUS,
1051 );
1052 }
1053 ecx.enter_forall(target_projection, |ecx, target_projection| {
1054 let source_projection =
1055 ecx.instantiate_binder_with_infer(source_projection);
1056 ecx.eq(param_env, source_projection, target_projection)?;
1057 ecx.try_evaluate_added_goals()
1058 })?;
1059 }
1060 ty::ExistentialPredicate::AutoTrait(def_id) => {
1062 if !a_auto_traits.contains(&def_id) {
1063 return Err(NoSolution);
1064 }
1065 }
1066 }
1067 }
1068
1069 ecx.add_goal(
1071 GoalSource::ImplWhereBound,
1072 Goal::new(ecx.cx(), param_env, ty::OutlivesPredicate(a_region, b_region)),
1073 );
1074
1075 ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
1076 })
1077 }
1078
1079 fn consider_builtin_array_unsize(
1088 &mut self,
1089 goal: Goal<I, (I::Ty, I::Ty)>,
1090 a_elem_ty: I::Ty,
1091 b_elem_ty: I::Ty,
1092 ) -> Result<Candidate<I>, NoSolution> {
1093 self.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
1094 self.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
1095 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
1096 }
1097
1098 fn consider_builtin_struct_unsize(
1112 &mut self,
1113 goal: Goal<I, (I::Ty, I::Ty)>,
1114 def: I::AdtDef,
1115 a_args: I::GenericArgs,
1116 b_args: I::GenericArgs,
1117 ) -> Result<Candidate<I>, NoSolution> {
1118 let cx = self.cx();
1119 let Goal { predicate: (_a_ty, b_ty), .. } = goal;
1120
1121 let unsizing_params = cx.unsizing_params_for_adt(def.def_id());
1122 if unsizing_params.is_empty() {
1125 return Err(NoSolution);
1126 }
1127
1128 let tail_field_ty = def.struct_tail_ty(cx).unwrap();
1129
1130 let a_tail_ty = tail_field_ty.instantiate(cx, a_args);
1131 let b_tail_ty = tail_field_ty.instantiate(cx, b_args);
1132
1133 let new_a_args = cx.mk_args_from_iter(a_args.iter().enumerate().map(|(i, a)| {
1137 if unsizing_params.contains(i as u32) { b_args.get(i).unwrap() } else { a }
1138 }));
1139 let unsized_a_ty = Ty::new_adt(cx, def, new_a_args);
1140
1141 self.eq(goal.param_env, unsized_a_ty, b_ty)?;
1144 self.add_goal(
1145 GoalSource::ImplWhereBound,
1146 goal.with(
1147 cx,
1148 ty::TraitRef::new(
1149 cx,
1150 cx.require_trait_lang_item(SolverTraitLangItem::Unsize),
1151 [a_tail_ty, b_tail_ty],
1152 ),
1153 ),
1154 );
1155 self.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
1156 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
1157 }
1158
1159 fn disqualify_auto_trait_candidate_due_to_possible_impl(
1164 &mut self,
1165 goal: Goal<I, TraitPredicate<I>>,
1166 ) -> Option<Result<Candidate<I>, NoSolution>> {
1167 let self_ty = goal.predicate.self_ty();
1168 let check_impls = || {
1169 let mut disqualifying_impl = None;
1170 self.cx().for_each_relevant_impl(
1171 goal.predicate.def_id(),
1172 goal.predicate.self_ty(),
1173 |impl_def_id| {
1174 disqualifying_impl = Some(impl_def_id);
1175 },
1176 );
1177 if let Some(def_id) = disqualifying_impl {
1178 trace!(?def_id, ?goal, "disqualified auto-trait implementation");
1179 return Some(Err(NoSolution));
1182 } else {
1183 None
1184 }
1185 };
1186
1187 match self_ty.kind() {
1188 ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => {
1194 Some(self.forced_ambiguity(MaybeCause::Ambiguity))
1195 }
1196
1197 ty::Foreign(..) if self.cx().is_default_trait(goal.predicate.def_id()) => check_impls(),
1200
1201 ty::Dynamic(..)
1204 | ty::Param(..)
1205 | ty::Foreign(..)
1206 | ty::Alias(ty::Projection | ty::Free | ty::Inherent, ..)
1207 | ty::Placeholder(..) => Some(Err(NoSolution)),
1208
1209 ty::Infer(_) | ty::Bound(_, _) => panic!("unexpected type `{self_ty:?}`"),
1210
1211 ty::Coroutine(def_id, _)
1215 if self
1216 .cx()
1217 .is_trait_lang_item(goal.predicate.def_id(), SolverTraitLangItem::Unpin) =>
1218 {
1219 match self.cx().coroutine_movability(def_id) {
1220 Movability::Static => Some(Err(NoSolution)),
1221 Movability::Movable => Some(
1222 self.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
1223 ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
1224 }),
1225 ),
1226 }
1227 }
1228
1229 ty::Alias(..) => None,
1234
1235 ty::Bool
1242 | ty::Char
1243 | ty::Int(_)
1244 | ty::Uint(_)
1245 | ty::Float(_)
1246 | ty::Str
1247 | ty::Array(_, _)
1248 | ty::Pat(_, _)
1249 | ty::Slice(_)
1250 | ty::RawPtr(_, _)
1251 | ty::Ref(_, _, _)
1252 | ty::FnDef(_, _)
1253 | ty::FnPtr(..)
1254 | ty::Closure(..)
1255 | ty::CoroutineClosure(..)
1256 | ty::Coroutine(_, _)
1257 | ty::CoroutineWitness(..)
1258 | ty::Never
1259 | ty::Tuple(_)
1260 | ty::Adt(_, _)
1261 | ty::UnsafeBinder(_) => check_impls(),
1262 ty::Error(_) => None,
1263 }
1264 }
1265
1266 fn probe_and_evaluate_goal_for_constituent_tys(
1271 &mut self,
1272 source: CandidateSource<I>,
1273 goal: Goal<I, TraitPredicate<I>>,
1274 constituent_tys: impl Fn(
1275 &EvalCtxt<'_, D>,
1276 I::Ty,
1277 ) -> Result<ty::Binder<I, Vec<I::Ty>>, NoSolution>,
1278 ) -> Result<Candidate<I>, NoSolution> {
1279 self.probe_trait_candidate(source).enter(|ecx| {
1280 let goals =
1281 ecx.enter_forall(constituent_tys(ecx, goal.predicate.self_ty())?, |ecx, tys| {
1282 tys.into_iter()
1283 .map(|ty| {
1284 goal.with(ecx.cx(), goal.predicate.with_replaced_self_ty(ecx.cx(), ty))
1285 })
1286 .collect::<Vec<_>>()
1287 });
1288 ecx.add_goals(GoalSource::ImplWhereBound, goals);
1289 ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
1290 })
1291 }
1292}
1293
1294#[derive(Debug, Clone, Copy)]
1305pub(super) enum TraitGoalProvenVia {
1306 Misc,
1312 ParamEnv,
1313 AliasBound,
1314}
1315
1316impl<D, I> EvalCtxt<'_, D>
1317where
1318 D: SolverDelegate<Interner = I>,
1319 I: Interner,
1320{
1321 pub(super) fn unsound_prefer_builtin_dyn_impl(&mut self, candidates: &mut Vec<Candidate<I>>) {
1334 match self.typing_mode() {
1335 TypingMode::Coherence => return,
1336 TypingMode::Analysis { .. }
1337 | TypingMode::Borrowck { .. }
1338 | TypingMode::PostBorrowckAnalysis { .. }
1339 | TypingMode::PostAnalysis => {}
1340 }
1341
1342 if candidates
1343 .iter()
1344 .find(|c| {
1345 matches!(c.source, CandidateSource::BuiltinImpl(BuiltinImplSource::Object(_)))
1346 })
1347 .is_some_and(|c| has_only_region_constraints(c.result))
1348 {
1349 candidates.retain(|c| {
1350 if matches!(c.source, CandidateSource::Impl(_)) {
1351 debug!(?c, "unsoundly dropping impl in favor of builtin dyn-candidate");
1352 false
1353 } else {
1354 true
1355 }
1356 });
1357 }
1358 }
1359
1360 #[instrument(level = "debug", skip(self), ret)]
1361 pub(super) fn merge_trait_candidates(
1362 &mut self,
1363 mut candidates: Vec<Candidate<I>>,
1364 failed_candidate_info: FailedCandidateInfo,
1365 ) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
1366 if let TypingMode::Coherence = self.typing_mode() {
1367 return if let Some((response, _)) = self.try_merge_candidates(&candidates) {
1368 Ok((response, Some(TraitGoalProvenVia::Misc)))
1369 } else {
1370 self.flounder(&candidates).map(|r| (r, None))
1371 };
1372 }
1373
1374 let mut trivial_builtin_impls = candidates.iter().filter(|c| {
1379 matches!(c.source, CandidateSource::BuiltinImpl(BuiltinImplSource::Trivial))
1380 });
1381 if let Some(candidate) = trivial_builtin_impls.next() {
1382 assert!(trivial_builtin_impls.next().is_none());
1385 return Ok((candidate.result, Some(TraitGoalProvenVia::Misc)));
1386 }
1387
1388 let has_non_global_where_bounds = candidates
1391 .iter()
1392 .any(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::NonGlobal)));
1393 if has_non_global_where_bounds {
1394 let where_bounds: Vec<_> = candidates
1395 .extract_if(.., |c| matches!(c.source, CandidateSource::ParamEnv(_)))
1396 .collect();
1397 if let Some((response, info)) = self.try_merge_candidates(&where_bounds) {
1398 match info {
1399 MergeCandidateInfo::AlwaysApplicable(i) => {
1415 for (j, c) in where_bounds.into_iter().enumerate() {
1416 if i != j {
1417 self.ignore_candidate_head_usages(c.head_usages)
1418 }
1419 }
1420 self.ignore_candidate_head_usages(
1424 failed_candidate_info.param_env_head_usages,
1425 );
1426 }
1427 MergeCandidateInfo::EqualResponse => {}
1428 }
1429 return Ok((response, Some(TraitGoalProvenVia::ParamEnv)));
1430 } else {
1431 return Ok((self.bail_with_ambiguity(&where_bounds), None));
1432 };
1433 }
1434
1435 if candidates.iter().any(|c| matches!(c.source, CandidateSource::AliasBound)) {
1436 let alias_bounds: Vec<_> = candidates
1437 .extract_if(.., |c| matches!(c.source, CandidateSource::AliasBound))
1438 .collect();
1439 return if let Some((response, _)) = self.try_merge_candidates(&alias_bounds) {
1440 Ok((response, Some(TraitGoalProvenVia::AliasBound)))
1441 } else {
1442 Ok((self.bail_with_ambiguity(&alias_bounds), None))
1443 };
1444 }
1445
1446 self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates);
1447 self.unsound_prefer_builtin_dyn_impl(&mut candidates);
1448
1449 let proven_via = if candidates
1454 .iter()
1455 .all(|c| matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global)))
1456 {
1457 TraitGoalProvenVia::ParamEnv
1458 } else {
1459 candidates
1460 .retain(|c| !matches!(c.source, CandidateSource::ParamEnv(ParamEnvSource::Global)));
1461 TraitGoalProvenVia::Misc
1462 };
1463
1464 if let Some((response, _)) = self.try_merge_candidates(&candidates) {
1465 Ok((response, Some(proven_via)))
1466 } else {
1467 self.flounder(&candidates).map(|r| (r, None))
1468 }
1469 }
1470
1471 #[instrument(level = "trace", skip(self))]
1472 pub(super) fn compute_trait_goal(
1473 &mut self,
1474 goal: Goal<I, TraitPredicate<I>>,
1475 ) -> Result<(CanonicalResponse<I>, Option<TraitGoalProvenVia>), NoSolution> {
1476 let (candidates, failed_candidate_info) =
1477 self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
1478 self.merge_trait_candidates(candidates, failed_candidate_info)
1479 }
1480
1481 fn try_stall_coroutine(&mut self, self_ty: I::Ty) -> Option<Result<Candidate<I>, NoSolution>> {
1482 if let ty::Coroutine(def_id, _) = self_ty.kind() {
1483 match self.typing_mode() {
1484 TypingMode::Analysis {
1485 defining_opaque_types_and_generators: stalled_generators,
1486 } => {
1487 if def_id.as_local().is_some_and(|def_id| stalled_generators.contains(&def_id))
1488 {
1489 return Some(self.forced_ambiguity(MaybeCause::Ambiguity));
1490 }
1491 }
1492 TypingMode::Coherence
1493 | TypingMode::PostAnalysis
1494 | TypingMode::Borrowck { defining_opaque_types: _ }
1495 | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => {}
1496 }
1497 }
1498
1499 None
1500 }
1501}