Skip to content

Commit ff150f9

Browse files
committed
normalize in trait_ref_is_knowable in new solver
1 parent 63a81b0 commit ff150f9

10 files changed

+214
-83
lines changed

compiler/rustc_trait_selection/src/solve/assembly/mod.rs

+35-19
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
316316

317317
self.assemble_param_env_candidates(goal, &mut candidates);
318318

319+
self.assemble_coherence_unknowable_candidates(goal, &mut candidates);
320+
319321
candidates
320322
}
321323

@@ -363,10 +365,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
363365

364366
self.assemble_object_bound_candidates(goal, &mut candidates);
365367

366-
self.assemble_coherence_unknowable_candidates(goal, &mut candidates);
367-
368368
self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates, num_steps);
369-
370369
candidates
371370
}
372371

@@ -877,26 +876,43 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
877876
goal: Goal<'tcx, G>,
878877
candidates: &mut Vec<Candidate<'tcx>>,
879878
) {
879+
let tcx = self.tcx();
880880
match self.solver_mode() {
881881
SolverMode::Normal => return,
882-
SolverMode::Coherence => {
883-
let trait_ref = goal.predicate.trait_ref(self.tcx());
884-
match coherence::trait_ref_is_knowable(self.tcx(), trait_ref) {
885-
Ok(()) => {}
886-
Err(_) => match self
887-
.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
888-
{
889-
Ok(result) => candidates.push(Candidate {
890-
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
891-
result,
892-
}),
893-
// FIXME: This will be reachable at some point if we're in
894-
// `assemble_candidates_after_normalizing_self_ty` and we get a
895-
// universe error. We'll deal with it at this point.
896-
Err(NoSolution) => bug!("coherence candidate resulted in NoSolution"),
897-
},
882+
SolverMode::Coherence => {}
883+
};
884+
885+
let result = self.probe_candidate("coherence unknowable").enter(|ecx| {
886+
let trait_ref = goal.predicate.trait_ref(tcx);
887+
888+
#[derive(Debug)]
889+
enum FailureKind {
890+
Overflow,
891+
NoSolution(NoSolution),
892+
}
893+
let lazily_normalize_ty = |ty| match ecx.try_normalize_ty(goal.param_env, ty) {
894+
Ok(Some(ty)) => Ok(ty),
895+
Ok(None) => Err(FailureKind::Overflow),
896+
Err(e) => Err(FailureKind::NoSolution(e)),
897+
};
898+
899+
match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty) {
900+
Err(FailureKind::Overflow) => {
901+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW)
902+
}
903+
Err(FailureKind::NoSolution(NoSolution)) | Ok(Ok(())) => Err(NoSolution),
904+
Ok(Err(_)) => {
905+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
898906
}
899907
}
908+
});
909+
910+
match result {
911+
Ok(result) => candidates.push(Candidate {
912+
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
913+
result,
914+
}),
915+
Err(NoSolution) => {}
900916
}
901917
}
902918

compiler/rustc_trait_selection/src/solve/mod.rs

+31
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,37 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
283283

284284
Ok(self.make_ambiguous_response_no_constraints(maybe_cause))
285285
}
286+
287+
/// Normalize a type when it is structually matched on.
288+
///
289+
/// For self types this is generally already handled through
290+
/// `assemble_candidates_after_normalizing_self_ty`, so anything happening
291+
/// in [`EvalCtxt::assemble_candidates_via_self_ty`] does not have to normalize
292+
/// the self type. It is required when structurally matching on any other
293+
/// arguments of a trait goal, e.g. when assembling builtin unsize candidates.
294+
fn try_normalize_ty(
295+
&mut self,
296+
param_env: ty::ParamEnv<'tcx>,
297+
mut ty: Ty<'tcx>,
298+
) -> Result<Option<Ty<'tcx>>, NoSolution> {
299+
for _ in 0..self.local_overflow_limit() {
300+
let ty::Alias(_, projection_ty) = *ty.kind() else {
301+
return Ok(Some(ty));
302+
};
303+
304+
let normalized_ty = self.next_ty_infer();
305+
let normalizes_to_goal = Goal::new(
306+
self.tcx(),
307+
param_env,
308+
ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() },
309+
);
310+
self.add_goal(normalizes_to_goal);
311+
self.try_evaluate_added_goals()?;
312+
ty = self.resolve_vars_if_possible(normalized_ty);
313+
}
314+
315+
Ok(None)
316+
}
286317
}
287318

288319
fn response_no_constraints_raw<'tcx>(

compiler/rustc_trait_selection/src/solve/trait_goals.rs

+1-38
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
448448
// We need to normalize the b_ty since it's matched structurally
449449
// in the other functions below.
450450
let b_ty = match ecx
451-
.normalize_non_self_ty(goal.predicate.trait_ref.args.type_at(1), goal.param_env)
451+
.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))
452452
{
453453
Ok(Some(b_ty)) => b_ty,
454454
Ok(None) => return vec![misc_candidate(ecx, Certainty::OVERFLOW)],
@@ -927,41 +927,4 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
927927
let candidates = self.assemble_and_evaluate_candidates(goal);
928928
self.merge_candidates(candidates)
929929
}
930-
931-
/// Normalize a non-self type when it is structually matched on when solving
932-
/// a built-in goal.
933-
///
934-
/// This is handled already through `assemble_candidates_after_normalizing_self_ty`
935-
/// for the self type, but for other goals, additional normalization of other
936-
/// arguments may be needed to completely implement the semantics of the trait.
937-
///
938-
/// This is required when structurally matching on any trait argument that is
939-
/// not the self type.
940-
fn normalize_non_self_ty(
941-
&mut self,
942-
mut ty: Ty<'tcx>,
943-
param_env: ty::ParamEnv<'tcx>,
944-
) -> Result<Option<Ty<'tcx>>, NoSolution> {
945-
if !matches!(ty.kind(), ty::Alias(..)) {
946-
return Ok(Some(ty));
947-
}
948-
949-
for _ in 0..self.local_overflow_limit() {
950-
let ty::Alias(_, projection_ty) = *ty.kind() else {
951-
return Ok(Some(ty));
952-
};
953-
954-
let normalized_ty = self.next_ty_infer();
955-
let normalizes_to_goal = Goal::new(
956-
self.tcx(),
957-
param_env,
958-
ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() },
959-
);
960-
self.add_goal(normalizes_to_goal);
961-
self.try_evaluate_added_goals()?;
962-
ty = self.resolve_vars_if_possible(normalized_ty);
963-
}
964-
965-
Ok(None)
966-
}
967930
}

compiler/rustc_trait_selection/src/traits/coherence.rs

+43-25
Original file line numberDiff line numberDiff line change
@@ -452,22 +452,23 @@ fn prove_negated_obligation<'tcx>(
452452
/// This both checks whether any downstream or sibling crates could
453453
/// implement it and whether an upstream crate can add this impl
454454
/// without breaking backwards compatibility.
455-
#[instrument(level = "debug", skip(tcx), ret)]
456-
pub fn trait_ref_is_knowable<'tcx>(
455+
#[instrument(level = "debug", skip(tcx, lazily_normalize_ty), ret)]
456+
pub fn trait_ref_is_knowable<'tcx, E: Debug>(
457457
tcx: TyCtxt<'tcx>,
458458
trait_ref: ty::TraitRef<'tcx>,
459-
) -> Result<(), Conflict> {
459+
mut lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
460+
) -> Result<Result<(), Conflict>, E> {
460461
if Some(trait_ref.def_id) == tcx.lang_items().fn_ptr_trait() {
461462
// The only types implementing `FnPtr` are function pointers,
462463
// so if there's no impl of `FnPtr` in the current crate,
463464
// then such an impl will never be added in the future.
464-
return Ok(());
465+
return Ok(Ok(()));
465466
}
466467

467-
if orphan_check_trait_ref(trait_ref, InCrate::Remote).is_ok() {
468+
if orphan_check_trait_ref(trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok() {
468469
// A downstream or cousin crate is allowed to implement some
469470
// substitution of this trait-ref.
470-
return Err(Conflict::Downstream);
471+
return Ok(Err(Conflict::Downstream));
471472
}
472473

473474
if trait_ref_is_local_or_fundamental(tcx, trait_ref) {
@@ -476,7 +477,7 @@ pub fn trait_ref_is_knowable<'tcx>(
476477
// allowed to implement a substitution of this trait ref, which
477478
// means impls could only come from dependencies of this crate,
478479
// which we already know about.
479-
return Ok(());
480+
return Ok(Ok(()));
480481
}
481482

482483
// This is a remote non-fundamental trait, so if another crate
@@ -487,10 +488,10 @@ pub fn trait_ref_is_knowable<'tcx>(
487488
// and if we are an intermediate owner, then we don't care
488489
// about future-compatibility, which means that we're OK if
489490
// we are an owner.
490-
if orphan_check_trait_ref(trait_ref, InCrate::Local).is_ok() {
491-
Ok(())
491+
if orphan_check_trait_ref(trait_ref, InCrate::Local, &mut lazily_normalize_ty)?.is_ok() {
492+
Ok(Ok(()))
492493
} else {
493-
Err(Conflict::Upstream)
494+
Ok(Err(Conflict::Upstream))
494495
}
495496
}
496497

@@ -526,7 +527,7 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe
526527
return Ok(());
527528
}
528529

529-
orphan_check_trait_ref(trait_ref, InCrate::Local)
530+
orphan_check_trait_ref::<!>(trait_ref, InCrate::Local, |ty| Ok(ty)).unwrap()
530531
}
531532

532533
/// Checks whether a trait-ref is potentially implementable by a crate.
@@ -615,21 +616,23 @@ pub fn orphan_check(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Result<(), OrphanChe
615616
///
616617
/// Note that this function is never called for types that have both type
617618
/// parameters and inference variables.
618-
#[instrument(level = "trace", ret)]
619-
fn orphan_check_trait_ref<'tcx>(
619+
#[instrument(level = "trace", skip(lazily_normalize_ty), ret)]
620+
fn orphan_check_trait_ref<'tcx, E: Debug>(
620621
trait_ref: ty::TraitRef<'tcx>,
621622
in_crate: InCrate,
622-
) -> Result<(), OrphanCheckErr<'tcx>> {
623+
lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
624+
) -> Result<Result<(), OrphanCheckErr<'tcx>>, E> {
623625
if trait_ref.has_infer() && trait_ref.has_param() {
624626
bug!(
625627
"can't orphan check a trait ref with both params and inference variables {:?}",
626628
trait_ref
627629
);
628630
}
629631

630-
let mut checker = OrphanChecker::new(in_crate);
631-
match trait_ref.visit_with(&mut checker) {
632+
let mut checker = OrphanChecker::new(in_crate, lazily_normalize_ty);
633+
Ok(match trait_ref.visit_with(&mut checker) {
632634
ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
635+
ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)) => return Err(err),
633636
ControlFlow::Break(OrphanCheckEarlyExit::ParamTy(ty)) => {
634637
// Does there exist some local type after the `ParamTy`.
635638
checker.search_first_local_ty = true;
@@ -642,34 +645,39 @@ fn orphan_check_trait_ref<'tcx>(
642645
}
643646
}
644647
ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(_)) => Ok(()),
645-
}
648+
})
646649
}
647650

648-
struct OrphanChecker<'tcx> {
651+
struct OrphanChecker<'tcx, F> {
649652
in_crate: InCrate,
650653
in_self_ty: bool,
654+
lazily_normalize_ty: F,
651655
/// Ignore orphan check failures and exclusively search for the first
652656
/// local type.
653657
search_first_local_ty: bool,
654658
non_local_tys: Vec<(Ty<'tcx>, bool)>,
655659
}
656660

657-
impl<'tcx> OrphanChecker<'tcx> {
658-
fn new(in_crate: InCrate) -> Self {
661+
impl<'tcx, F, E> OrphanChecker<'tcx, F>
662+
where
663+
F: FnOnce(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
664+
{
665+
fn new(in_crate: InCrate, lazily_normalize_ty: F) -> Self {
659666
OrphanChecker {
660667
in_crate,
661668
in_self_ty: true,
669+
lazily_normalize_ty,
662670
search_first_local_ty: false,
663671
non_local_tys: Vec::new(),
664672
}
665673
}
666674

667-
fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> {
675+
fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
668676
self.non_local_tys.push((t, self.in_self_ty));
669677
ControlFlow::Continue(())
670678
}
671679

672-
fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx>> {
680+
fn found_param_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
673681
if self.search_first_local_ty {
674682
ControlFlow::Continue(())
675683
} else {
@@ -685,18 +693,28 @@ impl<'tcx> OrphanChecker<'tcx> {
685693
}
686694
}
687695

688-
enum OrphanCheckEarlyExit<'tcx> {
696+
enum OrphanCheckEarlyExit<'tcx, E> {
697+
NormalizationFailure(E),
689698
ParamTy(Ty<'tcx>),
690699
LocalTy(Ty<'tcx>),
691700
}
692701

693-
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx> {
694-
type BreakTy = OrphanCheckEarlyExit<'tcx>;
702+
impl<'tcx, F, E> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'tcx, F>
703+
where
704+
F: FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
705+
{
706+
type BreakTy = OrphanCheckEarlyExit<'tcx, E>;
695707
fn visit_region(&mut self, _r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
696708
ControlFlow::Continue(())
697709
}
698710

699711
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
712+
// Need to lazily normalize here in with `-Ztrait-solver=next-coherence`.
713+
let ty = match (self.lazily_normalize_ty)(ty) {
714+
Ok(ty) => ty,
715+
Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)),
716+
};
717+
700718
let result = match *ty.kind() {
701719
ty::Bool
702720
| ty::Char

compiler/rustc_trait_selection/src/traits/select/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1457,7 +1457,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
14571457
// bound regions.
14581458
let trait_ref = predicate.skip_binder().trait_ref;
14591459

1460-
coherence::trait_ref_is_knowable(self.tcx(), trait_ref)
1460+
coherence::trait_ref_is_knowable::<!>(self.tcx(), trait_ref, |ty| Ok(ty)).unwrap()
14611461
}
14621462

14631463
/// Returns `true` if the global caches can be used.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// compile-flags: -Ztrait-solver=next
2+
3+
// Coherence should handle overflow while normalizing for
4+
// `trait_ref_is_knowable` correctly.
5+
6+
trait Overflow {
7+
type Assoc;
8+
}
9+
impl<T> Overflow for T {
10+
type Assoc = <T as Overflow>::Assoc;
11+
}
12+
13+
14+
trait Trait {}
15+
impl<T: Copy> Trait for T {}
16+
struct LocalTy;
17+
impl Trait for <LocalTy as Overflow>::Assoc {}
18+
//~^ ERROR conflicting implementations of trait `Trait` for type `<LocalTy as Overflow>::Assoc`
19+
20+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0119]: conflicting implementations of trait `Trait` for type `<LocalTy as Overflow>::Assoc`
2+
--> $DIR/trait_ref_is_knowable-norm-overflow.rs:17:1
3+
|
4+
LL | impl<T: Copy> Trait for T {}
5+
| ------------------------- first implementation here
6+
LL | struct LocalTy;
7+
LL | impl Trait for <LocalTy as Overflow>::Assoc {}
8+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `<LocalTy as Overflow>::Assoc`
9+
10+
error: aborting due to previous error
11+
12+
For more information about this error, try `rustc --explain E0119`.

0 commit comments

Comments
 (0)