|
1 | 1 | //! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
|
2 | 2 |
|
3 | 3 | use super::assembly::{self, structural_traits};
|
| 4 | +use super::search_graph::OverflowHandler; |
4 | 5 | use super::{EvalCtxt, SolverMode};
|
5 | 6 | use rustc_hir::def_id::DefId;
|
6 | 7 | use rustc_hir::{LangItem, Movability};
|
7 | 8 | use rustc_infer::traits::query::NoSolution;
|
8 | 9 | use rustc_infer::traits::util::supertraits;
|
9 |
| -use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult}; |
| 10 | +use rustc_middle::traits::solve::inspect::CandidateKind; |
| 11 | +use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; |
10 | 12 | use rustc_middle::traits::Reveal;
|
11 | 13 | use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
|
12 | 14 | use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
|
@@ -376,11 +378,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
376 | 378 | let tcx = ecx.tcx();
|
377 | 379 | let a_ty = goal.predicate.self_ty();
|
378 | 380 | let b_ty = goal.predicate.trait_ref.args.type_at(1);
|
379 |
| - if b_ty.is_ty_var() { |
380 |
| - return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); |
381 |
| - } |
| 381 | + |
382 | 382 | ecx.probe_candidate("builtin unsize").enter(|ecx| {
|
| 383 | + let Some(b_ty) = ecx.normalize_non_self_ty(b_ty, goal.param_env)? else { |
| 384 | + return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe( |
| 385 | + MaybeCause::Overflow, |
| 386 | + )); |
| 387 | + }; |
| 388 | + |
383 | 389 | match (a_ty.kind(), b_ty.kind()) {
|
| 390 | + (_, ty::Infer(ty::TyVar(_))) => { |
| 391 | + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) |
| 392 | + } |
384 | 393 | // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
|
385 | 394 | (&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
|
386 | 395 | // Dyn upcasting is handled separately, since due to upcasting,
|
@@ -489,74 +498,90 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
489 | 498 |
|
490 | 499 | let tcx = ecx.tcx();
|
491 | 500 |
|
492 |
| - let a_ty = goal.predicate.self_ty(); |
493 |
| - let b_ty = goal.predicate.trait_ref.args.type_at(1); |
494 |
| - let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { |
495 |
| - return vec![]; |
496 |
| - }; |
497 |
| - let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { |
498 |
| - return vec![]; |
499 |
| - }; |
| 501 | + // Need to wrap in a probe since `normalize_non_self_ty` has side-effects. |
| 502 | + ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| { |
| 503 | + let a_ty = goal.predicate.self_ty(); |
| 504 | + let b_ty = goal.predicate.trait_ref.args.type_at(1); |
| 505 | + let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { |
| 506 | + return vec![]; |
| 507 | + }; |
500 | 508 |
|
501 |
| - // All of a's auto traits need to be in b's auto traits. |
502 |
| - let auto_traits_compatible = |
503 |
| - b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); |
504 |
| - if !auto_traits_compatible { |
505 |
| - return vec![]; |
506 |
| - } |
| 509 | + // We don't care about `ty::Infer` here or errors here, since we'll |
| 510 | + // register an ambiguous/error response in the other unsize candidate |
| 511 | + // assembly function. |
| 512 | + let Ok(Some(b_ty)) = ecx.normalize_non_self_ty(b_ty, goal.param_env) else { |
| 513 | + return vec![]; |
| 514 | + }; |
| 515 | + let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { |
| 516 | + return vec![]; |
| 517 | + }; |
507 | 518 |
|
508 |
| - let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| { |
509 |
| - ecx.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> { |
510 |
| - // Require that all of the trait predicates from A match B, except for |
511 |
| - // the auto traits. We do this by constructing a new A type with B's |
512 |
| - // auto traits, and equating these types. |
513 |
| - let new_a_data = principal |
514 |
| - .into_iter() |
515 |
| - .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) |
516 |
| - .chain(a_data.iter().filter(|a| { |
517 |
| - matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) |
518 |
| - })) |
519 |
| - .chain( |
520 |
| - b_data |
521 |
| - .auto_traits() |
522 |
| - .map(ty::ExistentialPredicate::AutoTrait) |
523 |
| - .map(ty::Binder::dummy), |
524 |
| - ); |
525 |
| - let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); |
526 |
| - let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); |
| 519 | + // All of a's auto traits need to be in b's auto traits. |
| 520 | + let auto_traits_compatible = |
| 521 | + b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b)); |
| 522 | + if !auto_traits_compatible { |
| 523 | + return vec![]; |
| 524 | + } |
527 | 525 |
|
528 |
| - // We also require that A's lifetime outlives B's lifetime. |
529 |
| - ecx.eq(goal.param_env, new_a_ty, b_ty)?; |
530 |
| - ecx.add_goal( |
531 |
| - goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))), |
532 |
| - ); |
533 |
| - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) |
534 |
| - }) |
535 |
| - }; |
| 526 | + let mut unsize_dyn_to_principal = |principal: Option< |
| 527 | + ty::PolyExistentialTraitRef<'tcx>, |
| 528 | + >| { |
| 529 | + ecx.probe_candidate("upcast dyn to principle").enter( |
| 530 | + |ecx| -> Result<_, NoSolution> { |
| 531 | + // Require that all of the trait predicates from A match B, except for |
| 532 | + // the auto traits. We do this by constructing a new A type with B's |
| 533 | + // auto traits, and equating these types. |
| 534 | + let new_a_data = principal |
| 535 | + .into_iter() |
| 536 | + .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) |
| 537 | + .chain(a_data.iter().filter(|a| { |
| 538 | + matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) |
| 539 | + })) |
| 540 | + .chain( |
| 541 | + b_data |
| 542 | + .auto_traits() |
| 543 | + .map(ty::ExistentialPredicate::AutoTrait) |
| 544 | + .map(ty::Binder::dummy), |
| 545 | + ); |
| 546 | + let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); |
| 547 | + let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); |
| 548 | + |
| 549 | + // We also require that A's lifetime outlives B's lifetime. |
| 550 | + ecx.eq(goal.param_env, new_a_ty, b_ty)?; |
| 551 | + ecx.add_goal(goal.with( |
| 552 | + tcx, |
| 553 | + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), |
| 554 | + )); |
| 555 | + ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) |
| 556 | + }, |
| 557 | + ) |
| 558 | + }; |
536 | 559 |
|
537 |
| - let mut responses = vec![]; |
538 |
| - // If the principal def ids match (or are both none), then we're not doing |
539 |
| - // trait upcasting. We're just removing auto traits (or shortening the lifetime). |
540 |
| - if a_data.principal_def_id() == b_data.principal_def_id() { |
541 |
| - if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { |
542 |
| - responses.push(response); |
543 |
| - } |
544 |
| - } else if let Some(a_principal) = a_data.principal() |
545 |
| - && let Some(b_principal) = b_data.principal() |
546 |
| - { |
547 |
| - for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { |
548 |
| - if super_trait_ref.def_id() != b_principal.def_id() { |
549 |
| - continue; |
550 |
| - } |
551 |
| - let erased_trait_ref = super_trait_ref |
552 |
| - .map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)); |
553 |
| - if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { |
| 560 | + let mut responses = vec![]; |
| 561 | + // If the principal def ids match (or are both none), then we're not doing |
| 562 | + // trait upcasting. We're just removing auto traits (or shortening the lifetime). |
| 563 | + if a_data.principal_def_id() == b_data.principal_def_id() { |
| 564 | + if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) { |
554 | 565 | responses.push(response);
|
555 | 566 | }
|
| 567 | + } else if let Some(a_principal) = a_data.principal() |
| 568 | + && let Some(b_principal) = b_data.principal() |
| 569 | + { |
| 570 | + for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { |
| 571 | + if super_trait_ref.def_id() != b_principal.def_id() { |
| 572 | + continue; |
| 573 | + } |
| 574 | + let erased_trait_ref = super_trait_ref.map_bound(|trait_ref| { |
| 575 | + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) |
| 576 | + }); |
| 577 | + if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) { |
| 578 | + responses.push(response); |
| 579 | + } |
| 580 | + } |
556 | 581 | }
|
557 |
| - } |
558 | 582 |
|
559 |
| - responses |
| 583 | + responses |
| 584 | + }) |
560 | 585 | }
|
561 | 586 |
|
562 | 587 | fn consider_builtin_discriminant_kind_candidate(
|
@@ -750,4 +775,47 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
750 | 775 | let candidates = self.assemble_and_evaluate_candidates(goal);
|
751 | 776 | self.merge_candidates(candidates)
|
752 | 777 | }
|
| 778 | + |
| 779 | + /// Normalize a non-self type when it is structually matched on when solving |
| 780 | + /// a built-in goal. This is handled already through `assemble_candidates_after_normalizing_self_ty` |
| 781 | + /// for the self type, but for other goals, additional normalization of other |
| 782 | + /// arguments may be needed to completely implement the semantics of the trait. |
| 783 | + /// |
| 784 | + /// This is required when structurally matching on any trait argument that is |
| 785 | + /// not the self type. |
| 786 | + fn normalize_non_self_ty( |
| 787 | + &mut self, |
| 788 | + mut ty: Ty<'tcx>, |
| 789 | + param_env: ty::ParamEnv<'tcx>, |
| 790 | + ) -> Result<Option<Ty<'tcx>>, NoSolution> { |
| 791 | + if !matches!(ty.kind(), ty::Alias(..)) { |
| 792 | + return Ok(Some(ty)); |
| 793 | + } |
| 794 | + |
| 795 | + self.repeat_while_none( |
| 796 | + |_| Ok(None), |
| 797 | + |ecx| { |
| 798 | + let ty::Alias(_, projection_ty) = *ty.kind() else { |
| 799 | + return Some(Ok(Some(ty))); |
| 800 | + }; |
| 801 | + |
| 802 | + let normalized_ty = ecx.next_ty_infer(); |
| 803 | + let normalizes_to_goal = Goal::new( |
| 804 | + ecx.tcx(), |
| 805 | + param_env, |
| 806 | + ty::Binder::dummy(ty::ProjectionPredicate { |
| 807 | + projection_ty, |
| 808 | + term: normalized_ty.into(), |
| 809 | + }), |
| 810 | + ); |
| 811 | + ecx.add_goal(normalizes_to_goal); |
| 812 | + if let Err(err) = ecx.try_evaluate_added_goals() { |
| 813 | + return Some(Err(err)); |
| 814 | + } |
| 815 | + |
| 816 | + ty = ecx.resolve_vars_if_possible(normalized_ty); |
| 817 | + None |
| 818 | + }, |
| 819 | + ) |
| 820 | + } |
753 | 821 | }
|
0 commit comments