summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-06-07 14:32:11 +0000
committerbors <bors@rust-lang.org>2023-06-07 14:32:11 +0000
commitb2807b2bf3be9fbed9a0aadc97841c20a1ab5f79 (patch)
treef8861cada9423965104c8fb57a7dbfdf1cd68a15
parente94bda3bf13303671427363d1cd93ac5e089f090 (diff)
parent90c361c8376e8ec9d63cbd78b6d68c9647eb32c1 (diff)
downloadrust-b2807b2bf3be9fbed9a0aadc97841c20a1ab5f79.tar.gz
rust-b2807b2bf3be9fbed9a0aadc97841c20a1ab5f79.zip
Auto merge of #112383 - Dylan-DPC:rollup-et2z6nt, r=Dylan-DPC
Rollup of 6 pull requests

Successful merges:

 - #112076 (Fall back to bidirectional normalizes-to if no subst-relate candidate in alias-relate goal)
 - #112122 (Add `-Ztrait-solver=next-coherence`)
 - #112251 (rustdoc: convert `if let Some()` that always matches to variable)
 - #112345 (fix(expand): prevent infinity loop in macro containing only "///")
 - #112359 (Respect `RUST_BACKTRACE` for delayed bugs)
 - #112382 (download-rustc: Fix `x test core` on MacOS)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs2
-rw-r--r--compiler/rustc_errors/src/lib.rs18
-rw-r--r--compiler/rustc_expand/src/mbe/macro_parser.rs1
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs45
-rw-r--r--compiler/rustc_hir_analysis/src/autoderef.rs5
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/inherited.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs4
-rw-r--r--compiler/rustc_infer/src/infer/at.rs1
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs32
-rw-r--r--compiler/rustc_infer/src/infer/equate.rs2
-rw-r--r--compiler/rustc_infer/src/infer/lattice.rs2
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs17
-rw-r--r--compiler/rustc_infer/src/infer/nll_relate/mod.rs6
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types.rs6
-rw-r--r--compiler/rustc_infer/src/infer/projection.rs2
-rw-r--r--compiler/rustc_infer/src/infer/sub.rs2
-rw-r--r--compiler/rustc_middle/src/ty/context.rs10
-rw-r--r--compiler/rustc_session/src/config.rs2
-rw-r--r--compiler/rustc_session/src/options.rs1
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs195
-rw-r--r--compiler/rustc_trait_selection/src/solve/eval_ctxt.rs1
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs137
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs46
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_normalize.rs2
-rw-r--r--compiler/rustc_traits/src/codegen.rs2
-rw-r--r--compiler/rustc_traits/src/evaluate_obligation.rs2
-rw-r--r--src/bootstrap/compile.rs7
-rw-r--r--src/librustdoc/visit_ast.rs46
-rw-r--r--src/tools/compiletest/src/common.rs1
-rw-r--r--src/tools/compiletest/src/runtest.rs3
-rw-r--r--tests/ui/macros/issue-112342-1.rs49
-rw-r--r--tests/ui/macros/issue-112342-1.stderr64
-rw-r--r--tests/ui/macros/issue-112342-2.rs39
-rw-r--r--tests/ui/macros/issue-112342-2.stderr24
-rw-r--r--tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr12
-rw-r--r--tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr12
-rw-r--r--tests/ui/specialization/specialization-default-items-drop-coherence.rs9
-rw-r--r--tests/ui/traits/new-solver/tait-eq-proj-2.rs21
-rw-r--r--tests/ui/traits/new-solver/tait-eq-proj.rs35
-rw-r--r--tests/ui/traits/new-solver/tait-eq-tait.rs17
49 files changed, 671 insertions, 232 deletions
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 13e346b86bc..a561496b026 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -285,7 +285,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
         let infcx = self
             .tcx
             .infer_ctxt()
-            .with_opaque_type_inference(if self.tcx.trait_solver_next() {
+            .with_opaque_type_inference(if self.next_trait_solver() {
                 DefiningAnchor::Bind(def_id)
             } else {
                 DefiningAnchor::Bubble
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 31e20d7e709..00fd3762fa7 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -188,7 +188,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
 
     // FIXME(-Ztrait-solver=next): A bit dubious that we're only registering
     // predefined opaques in the typeck root.
-    if infcx.tcx.trait_solver_next() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
+    if infcx.next_trait_solver() && !infcx.tcx.is_typeck_child(body.source.def_id()) {
         checker.register_predefined_opaques_in_new_solver();
     }
 
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index bf77ed81f9b..7a297ea0d5f 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -383,7 +383,7 @@ pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee, Noted};
 pub use diagnostic_impls::{
     DiagnosticArgFromDisplay, DiagnosticSymbolList, LabelKind, SingleLabelManySpans,
 };
-use std::backtrace::Backtrace;
+use std::backtrace::{Backtrace, BacktraceStatus};
 
 /// A handler deals with errors and other compiler output.
 /// Certain errors (fatal, bug, unimpl) may cause immediate exit,
@@ -1331,7 +1331,7 @@ impl HandlerInner {
             // once *any* errors were emitted (and truncate `delayed_span_bugs`
             // when an error is first emitted, also), but maybe there's a case
             // in which that's not sound? otherwise this is really inefficient.
-            let backtrace = std::backtrace::Backtrace::force_capture();
+            let backtrace = std::backtrace::Backtrace::capture();
             self.delayed_span_bugs
                 .push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace));
 
@@ -1620,7 +1620,7 @@ impl HandlerInner {
         if self.flags.report_delayed_bugs {
             self.emit_diagnostic(&mut diagnostic);
         }
-        let backtrace = std::backtrace::Backtrace::force_capture();
+        let backtrace = std::backtrace::Backtrace::capture();
         self.delayed_good_path_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
     }
 
@@ -1739,7 +1739,17 @@ impl DelayedDiagnostic {
     }
 
     fn decorate(mut self) -> Diagnostic {
-        self.inner.note(format!("delayed at {}\n{}", self.inner.emitted_at, self.note));
+        match self.note.status() {
+            BacktraceStatus::Captured => {
+                self.inner.note(format!("delayed at {}\n{}", self.inner.emitted_at, self.note));
+            }
+            // Avoid the needless newline when no backtrace has been captured,
+            // the display impl should just be a single line.
+            _ => {
+                self.inner.note(format!("delayed at {} - {}", self.inner.emitted_at, self.note));
+            }
+        }
+
         self.inner
     }
 }
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index 1c222fb4a89..f0e67cfd50e 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -249,6 +249,7 @@ pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec<MatcherLoc> {
 }
 
 /// A single matcher position, representing the state of matching.
+#[derive(Debug)]
 struct MatcherPos {
     /// The index into `TtParser::locs`, which represents the "dot".
     idx: usize,
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index e4c65a2049b..ee9616a0f0a 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -628,6 +628,40 @@ fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree)
     // after parsing/expansion. we can report every error in every macro this way.
 }
 
+fn is_empty_token_tree(sess: &ParseSess, seq: &mbe::SequenceRepetition) -> bool {
+    if seq.separator.is_some() {
+        false
+    } else {
+        let mut is_empty = true;
+        let mut iter = seq.tts.iter().peekable();
+        while let Some(tt) = iter.next() {
+            match tt {
+                mbe::TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => {}
+                mbe::TokenTree::Token(t @ Token { kind: DocComment(..), .. }) => {
+                    let mut now = t;
+                    while let Some(&mbe::TokenTree::Token(
+                        next @ Token { kind: DocComment(..), .. },
+                    )) = iter.peek()
+                    {
+                        now = next;
+                        iter.next();
+                    }
+                    let span = t.span.to(now.span);
+                    sess.span_diagnostic.span_note_without_error(
+                        span,
+                        "doc comments are ignored in matcher position",
+                    );
+                }
+                mbe::TokenTree::Sequence(_, sub_seq)
+                    if (sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore
+                        || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne) => {}
+                _ => is_empty = false,
+            }
+        }
+        is_empty
+    }
+}
+
 /// Checks that the lhs contains no repetition which could match an empty token
 /// tree, because then the matcher would hang indefinitely.
 fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool {
@@ -644,16 +678,7 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool {
                 }
             }
             TokenTree::Sequence(span, seq) => {
-                if seq.separator.is_none()
-                    && seq.tts.iter().all(|seq_tt| match seq_tt {
-                        TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => true,
-                        TokenTree::Sequence(_, sub_seq) => {
-                            sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore
-                                || sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne
-                        }
-                        _ => false,
-                    })
-                {
+                if is_empty_token_tree(sess, seq) {
                     let sp = span.entire();
                     sess.span_diagnostic.span_err(sp, "repetition matches empty token tree");
                     return false;
diff --git a/compiler/rustc_hir_analysis/src/autoderef.rs b/compiler/rustc_hir_analysis/src/autoderef.rs
index d6d1498d708..6eb18aecd65 100644
--- a/compiler/rustc_hir_analysis/src/autoderef.rs
+++ b/compiler/rustc_hir_analysis/src/autoderef.rs
@@ -73,7 +73,7 @@ impl<'a, 'tcx> Iterator for Autoderef<'a, 'tcx> {
             // NOTE: we may still need to normalize the built-in deref in case
             // we have some type like `&<Ty as Trait>::Assoc`, since users of
             // autoderef expect this type to have been structurally normalized.
-            if self.infcx.tcx.trait_solver_next()
+            if self.infcx.next_trait_solver()
                 && let ty::Alias(ty::Projection, _) = ty.kind()
             {
                 let (normalized_ty, obligations) = self.structurally_normalize(ty)?;
@@ -161,8 +161,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
         &self,
         ty: Ty<'tcx>,
     ) -> Option<(Ty<'tcx>, Vec<traits::PredicateObligation<'tcx>>)> {
-        let tcx = self.infcx.tcx;
-        let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new_in_snapshot(tcx);
+        let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new_in_snapshot(self.infcx);
 
         let cause = traits::ObligationCause::misc(self.span, self.body_id);
         let normalized_ty = match self
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 4a1f8ece7b2..17be5fe66cf 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -1549,7 +1549,7 @@ pub(super) fn check_generator_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) {
         .with_opaque_type_inference(DefiningAnchor::Bind(def_id))
         .build();
 
-    let mut fulfillment_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
+    let mut fulfillment_cx = <dyn TraitEngine<'_>>::new(&infcx);
     for (predicate, cause) in generator_interior_predicates {
         let obligation = Obligation::new(tcx, cause.clone(), param_env, *predicate);
         fulfillment_cx.register_predicate_obligation(&infcx, obligation);
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index ba49e0c4161..81231e8fe06 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -156,7 +156,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
             // In the new solver, lazy norm may allow us to shallowly equate
             // more types, but we emit possibly impossible-to-satisfy obligations.
             // Filter these cases out to make sure our coercion is more accurate.
-            if self.tcx.trait_solver_next() {
+            if self.next_trait_solver() {
                 if let Ok(res) = &res {
                     for obligation in &res.obligations {
                         if !self.predicate_may_hold(&obligation) {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 38ddb7e7604..fb56b7e74cb 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -1476,7 +1476,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
         let mut ty = self.resolve_vars_with_obligations(ty);
 
-        if self.tcx.trait_solver_next()
+        if self.next_trait_solver()
             && let ty::Alias(ty::Projection, _) = ty.kind()
         {
             match self
diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs
index 294c3bb78a5..aa4f90b4ad8 100644
--- a/compiler/rustc_hir_typeck/src/inherited.rs
+++ b/compiler/rustc_hir_typeck/src/inherited.rs
@@ -86,8 +86,8 @@ impl<'tcx> Inherited<'tcx> {
 
         Inherited {
             typeck_results,
+            fulfillment_cx: RefCell::new(<dyn TraitEngine<'_>>::new(&infcx)),
             infcx,
-            fulfillment_cx: RefCell::new(<dyn TraitEngine<'_>>::new(tcx)),
             locals: RefCell::new(Default::default()),
             deferred_sized_obligations: RefCell::new(Vec::new()),
             deferred_call_resolutions: RefCell::new(Default::default()),
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 964acc4eb77..6a3a46c778a 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -591,7 +591,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
                 .insert(opaque_type_key, hidden_type)
                 && last_opaque_ty.ty != hidden_type.ty
             {
-                assert!(!self.tcx().trait_solver_next());
+                assert!(!self.fcx.next_trait_solver());
                 hidden_type
                     .report_mismatch(&last_opaque_ty, opaque_type_key.def_id, self.tcx())
                     .stash(
@@ -812,7 +812,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Resolver<'cx, 'tcx> {
 
     fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
         match self.fcx.fully_resolve(t) {
-            Ok(t) if self.fcx.tcx.trait_solver_next() => {
+            Ok(t) if self.fcx.next_trait_solver() => {
                 // We must normalize erasing regions here, since later lints
                 // expect that types that show up in the typeck are fully
                 // normalized.
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs
index 6b2dd0a2b4f..1f0bf4f9887 100644
--- a/compiler/rustc_infer/src/infer/at.rs
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -82,6 +82,7 @@ impl<'tcx> InferCtxt<'tcx> {
             in_snapshot: self.in_snapshot.clone(),
             universe: self.universe.clone(),
             intercrate: self.intercrate,
+            next_trait_solver: self.next_trait_solver,
         }
     }
 }
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index ed532aa2e8b..7a80ee98cb3 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -109,11 +109,11 @@ impl<'tcx> InferCtxt<'tcx> {
             | (
                 ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)),
                 ty::Alias(AliasKind::Projection, _),
-            ) if self.tcx.trait_solver_next() => {
+            ) if self.next_trait_solver() => {
                 bug!()
             }
 
-            (_, ty::Alias(..)) | (ty::Alias(..), _) if self.tcx.trait_solver_next() => {
+            (_, ty::Alias(..)) | (ty::Alias(..), _) if self.next_trait_solver() => {
                 relation.register_type_relate_obligation(a, b);
                 Ok(a)
             }
@@ -227,9 +227,20 @@ impl<'tcx> InferCtxt<'tcx> {
                 return self.unify_const_variable(vid, a, relation.param_env());
             }
             (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
-                if self.tcx.features().generic_const_exprs || self.tcx.trait_solver_next() =>
+                if self.tcx.features().generic_const_exprs || self.next_trait_solver() =>
             {
-                relation.register_const_equate_obligation(a, b);
+                let (a, b) = if relation.a_is_expected() { (a, b) } else { (b, a) };
+
+                relation.register_predicates([ty::Binder::dummy(if self.next_trait_solver() {
+                    ty::PredicateKind::AliasRelate(
+                        a.into(),
+                        b.into(),
+                        ty::AliasRelationDirection::Equate,
+                    )
+                } else {
+                    ty::PredicateKind::ConstEquate(a, b)
+                })]);
+
                 return Ok(b);
             }
             _ => {}
@@ -453,19 +464,6 @@ pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> {
     /// be used if control over the obligation causes is required.
     fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ToPredicate<'tcx>>);
 
-    /// Register an obligation that both constants must be equal to each other.
-    ///
-    /// If they aren't equal then the relation doesn't hold.
-    fn register_const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) {
-        let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) };
-
-        self.register_predicates([ty::Binder::dummy(if self.tcx().trait_solver_next() {
-            ty::PredicateKind::AliasRelate(a.into(), b.into(), ty::AliasRelationDirection::Equate)
-        } else {
-            ty::PredicateKind::ConstEquate(a, b)
-        })]);
-    }
-
     /// Register an obligation that both types must be related to each other according to
     /// the [`ty::AliasRelationDirection`] given by [`ObligationEmittingRelation::alias_relate_direction`]
     fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs
index 42dfe4f6bb8..495c250a77d 100644
--- a/compiler/rustc_infer/src/infer/equate.rs
+++ b/compiler/rustc_infer/src/infer/equate.rs
@@ -105,7 +105,7 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
             | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
                 if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
                     && def_id.is_local()
-                    && !self.tcx().trait_solver_next() =>
+                    && !self.fields.infcx.next_trait_solver() =>
             {
                 self.fields.obligations.extend(
                     infcx
diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/lattice.rs
index 7190d33d299..9ef35429fe3 100644
--- a/compiler/rustc_infer/src/infer/lattice.rs
+++ b/compiler/rustc_infer/src/infer/lattice.rs
@@ -113,7 +113,7 @@ where
         | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
             if this.define_opaque_types() == DefineOpaqueTypes::Yes
                 && def_id.is_local()
-                && !this.tcx().trait_solver_next() =>
+                && !this.infcx().next_trait_solver() =>
         {
             this.register_obligations(
                 infcx
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index f36102fe2ea..9526ed144c3 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -330,6 +330,8 @@ pub struct InferCtxt<'tcx> {
     /// there is no type that the user could *actually name* that
     /// would satisfy it. This avoids crippling inference, basically.
     pub intercrate: bool,
+
+    next_trait_solver: bool,
 }
 
 /// See the `error_reporting` module for more details.
@@ -545,6 +547,9 @@ pub struct InferCtxtBuilder<'tcx> {
     skip_leak_check: bool,
     /// Whether we are in coherence mode.
     intercrate: bool,
+    /// Whether we should use the new trait solver in the local inference context,
+    /// which affects things like which solver is used in `predicate_may_hold`.
+    next_trait_solver: bool,
 }
 
 pub trait TyCtxtInferExt<'tcx> {
@@ -559,6 +564,7 @@ impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> {
             considering_regions: true,
             skip_leak_check: false,
             intercrate: false,
+            next_trait_solver: self.next_trait_solver_globally(),
         }
     }
 }
@@ -575,6 +581,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
         self
     }
 
+    pub fn with_next_trait_solver(mut self, next_trait_solver: bool) -> Self {
+        self.next_trait_solver = next_trait_solver;
+        self
+    }
+
     pub fn intercrate(mut self, intercrate: bool) -> Self {
         self.intercrate = intercrate;
         self
@@ -617,6 +628,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
             considering_regions,
             skip_leak_check,
             intercrate,
+            next_trait_solver,
         } = *self;
         InferCtxt {
             tcx,
@@ -634,6 +646,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
             in_snapshot: Cell::new(false),
             universe: Cell::new(ty::UniverseIndex::ROOT),
             intercrate,
+            next_trait_solver,
         }
     }
 }
@@ -670,6 +683,10 @@ pub struct CombinedSnapshot<'tcx> {
 }
 
 impl<'tcx> InferCtxt<'tcx> {
+    pub fn next_trait_solver(&self) -> bool {
+        self.next_trait_solver
+    }
+
     /// Creates a `TypeErrCtxt` for emitting various inference errors.
     /// During typeck, use `FnCtxt::err_ctxt` instead.
     pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> {
diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
index d3fd01b9642..71c07f31bc9 100644
--- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
@@ -491,12 +491,12 @@ where
             (
                 &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
                 &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
-            ) if a_def_id == b_def_id || infcx.tcx.trait_solver_next() => {
+            ) if a_def_id == b_def_id || infcx.next_trait_solver() => {
                 infcx.super_combine_tys(self, a, b).or_else(|err| {
                     // This behavior is only there for the old solver, the new solver
                     // shouldn't ever fail. Instead, it unconditionally emits an
                     // alias-relate goal.
-                    assert!(!self.tcx().trait_solver_next());
+                    assert!(!self.infcx.next_trait_solver());
                     self.tcx().sess.delay_span_bug(
                         self.delegate.span(),
                         "failure to relate an opaque to itself should result in an error later on",
@@ -506,7 +506,7 @@ where
             }
             (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
             | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
-                if def_id.is_local() && !self.tcx().trait_solver_next() =>
+                if def_id.is_local() && !self.infcx.next_trait_solver() =>
             {
                 self.relate_opaques(a, b)
             }
diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs
index 105a3f08c82..a9ead429f4c 100644
--- a/compiler/rustc_infer/src/infer/opaque_types.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types.rs
@@ -49,7 +49,7 @@ impl<'tcx> InferCtxt<'tcx> {
         param_env: ty::ParamEnv<'tcx>,
     ) -> InferOk<'tcx, T> {
         // We handle opaque types differently in the new solver.
-        if self.tcx.trait_solver_next() {
+        if self.next_trait_solver() {
             return InferOk { value, obligations: vec![] };
         }
 
@@ -578,7 +578,7 @@ impl<'tcx> InferCtxt<'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         hidden_ty: Ty<'tcx>,
     ) -> InferResult<'tcx, ()> {
-        assert!(self.tcx.trait_solver_next());
+        assert!(self.next_trait_solver());
         let origin = self
             .opaque_type_origin(opaque_type_key.def_id)
             .expect("should be called for defining usages only");
@@ -614,7 +614,7 @@ impl<'tcx> InferCtxt<'tcx> {
                     ty::Alias(ty::Projection, projection_ty)
                         if !projection_ty.has_escaping_bound_vars()
                             && !tcx.is_impl_trait_in_trait(projection_ty.def_id)
-                            && !tcx.trait_solver_next() =>
+                            && !self.next_trait_solver() =>
                     {
                         self.infer_projection(
                             param_env,
diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs
index fa6529dfa93..4f8c9188cf8 100644
--- a/compiler/rustc_infer/src/infer/projection.rs
+++ b/compiler/rustc_infer/src/infer/projection.rs
@@ -21,7 +21,7 @@ impl<'tcx> InferCtxt<'tcx> {
         recursion_depth: usize,
         obligations: &mut Vec<PredicateObligation<'tcx>>,
     ) -> Ty<'tcx> {
-        if self.tcx.trait_solver_next() {
+        if self.next_trait_solver() {
             // FIXME(-Ztrait-solver=next): Instead of branching here,
             // completely change the normalization routine with the new solver.
             //
diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs
index ceafafb5582..d9f9d2aabdb 100644
--- a/compiler/rustc_infer/src/infer/sub.rs
+++ b/compiler/rustc_infer/src/infer/sub.rs
@@ -132,7 +132,7 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
             | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
                 if self.fields.define_opaque_types == DefineOpaqueTypes::Yes
                     && def_id.is_local()
-                    && !self.tcx().trait_solver_next() =>
+                    && !self.fields.infcx.next_trait_solver() =>
             {
                 self.fields.obligations.extend(
                     infcx
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index bf6f21968d7..4ee543bbfb6 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -2333,10 +2333,18 @@ impl<'tcx> TyCtxt<'tcx> {
         self.opt_local_def_id_to_hir_id(local_def_id).unwrap()
     }
 
-    pub fn trait_solver_next(self) -> bool {
+    pub fn next_trait_solver_globally(self) -> bool {
         self.sess.opts.unstable_opts.trait_solver == rustc_session::config::TraitSolver::Next
     }
 
+    pub fn next_trait_solver_in_coherence(self) -> bool {
+        matches!(
+            self.sess.opts.unstable_opts.trait_solver,
+            rustc_session::config::TraitSolver::Next
+                | rustc_session::config::TraitSolver::NextCoherence
+        )
+    }
+
     pub fn lower_impl_trait_in_trait_to_assoc_ty(self) -> bool {
         self.sess.opts.unstable_opts.lower_impl_trait_in_trait_to_assoc_ty
     }
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 0ce83e79097..24291301a32 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -610,6 +610,8 @@ pub enum TraitSolver {
     Chalk,
     /// Experimental trait solver in `rustc_trait_selection::solve`
     Next,
+    /// Use the new trait solver during coherence
+    NextCoherence,
 }
 
 pub enum Input {
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 7cc2b2c880c..201066e3950 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -986,6 +986,7 @@ mod parse {
             Some("classic") => *slot = TraitSolver::Classic,
             Some("chalk") => *slot = TraitSolver::Chalk,
             Some("next") => *slot = TraitSolver::Next,
+            Some("next-coherence") => *slot = TraitSolver::NextCoherence,
             // default trait solver is subject to change..
             Some("default") => *slot = TraitSolver::Classic,
             _ => return false,
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
new file mode 100644
index 00000000000..66a4d36a1e5
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -0,0 +1,195 @@
+use super::{EvalCtxt, SolverMode};
+use rustc_infer::traits::query::NoSolution;
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::ty;
+
+/// We may need to invert the alias relation direction if dealing an alias on the RHS.
+#[derive(Debug)]
+enum Invert {
+    No,
+    Yes,
+}
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    #[instrument(level = "debug", skip(self), ret)]
+    pub(super) fn compute_alias_relate_goal(
+        &mut self,
+        goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>,
+    ) -> QueryResult<'tcx> {
+        let tcx = self.tcx();
+        let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
+        if lhs.is_infer() || rhs.is_infer() {
+            bug!(
+                "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated"
+            );
+        }
+
+        match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) {
+            (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"),
+
+            // RHS is not a projection, only way this is true is if LHS normalizes-to RHS
+            (Some(alias_lhs), None) => self.assemble_normalizes_to_candidate(
+                param_env,
+                alias_lhs,
+                rhs,
+                direction,
+                Invert::No,
+            ),
+
+            // LHS is not a projection, only way this is true is if RHS normalizes-to LHS
+            (None, Some(alias_rhs)) => self.assemble_normalizes_to_candidate(
+                param_env,
+                alias_rhs,
+                lhs,
+                direction,
+                Invert::Yes,
+            ),
+
+            (Some(alias_lhs), Some(alias_rhs)) => {
+                debug!("both sides are aliases");
+
+                let mut candidates = Vec::new();
+                // LHS normalizes-to RHS
+                candidates.extend(self.assemble_normalizes_to_candidate(
+                    param_env,
+                    alias_lhs,
+                    rhs,
+                    direction,
+                    Invert::No,
+                ));
+                // RHS normalizes-to RHS
+                candidates.extend(self.assemble_normalizes_to_candidate(
+                    param_env,
+                    alias_rhs,
+                    lhs,
+                    direction,
+                    Invert::Yes,
+                ));
+                // Relate via substs
+                let subst_relate_response = self
+                    .assemble_subst_relate_candidate(param_env, alias_lhs, alias_rhs, direction);
+                candidates.extend(subst_relate_response);
+                debug!(?candidates);
+
+                if let Some(merged) = self.try_merge_responses(&candidates) {
+                    Ok(merged)
+                } else {
+                    // When relating two aliases and we have ambiguity, we prefer
+                    // relating the generic arguments of the aliases over normalizing
+                    // them. This is necessary for inference during typeck.
+                    //
+                    // As this is incomplete, we must not do so during coherence.
+                    match self.solver_mode() {
+                        SolverMode::Normal => {
+                            if let Ok(subst_relate_response) = subst_relate_response {
+                                Ok(subst_relate_response)
+                            } else if let Ok(bidirectional_normalizes_to_response) = self
+                                .assemble_bidirectional_normalizes_to_candidate(
+                                    param_env, lhs, rhs, direction,
+                                )
+                            {
+                                Ok(bidirectional_normalizes_to_response)
+                            } else {
+                                self.flounder(&candidates)
+                            }
+                        }
+                        SolverMode::Coherence => self.flounder(&candidates),
+                    }
+                }
+            }
+        }
+    }
+
+    #[instrument(level = "debug", skip(self), ret)]
+    fn assemble_normalizes_to_candidate(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        alias: ty::AliasTy<'tcx>,
+        other: ty::Term<'tcx>,
+        direction: ty::AliasRelationDirection,
+        invert: Invert,
+    ) -> QueryResult<'tcx> {
+        self.probe(|ecx| {
+            ecx.normalizes_to_inner(param_env, alias, other, direction, invert)?;
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
+    }
+
+    fn normalizes_to_inner(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        alias: ty::AliasTy<'tcx>,
+        other: ty::Term<'tcx>,
+        direction: ty::AliasRelationDirection,
+        invert: Invert,
+    ) -> Result<(), NoSolution> {
+        let other = match direction {
+            // This is purely an optimization.
+            ty::AliasRelationDirection::Equate => other,
+
+            ty::AliasRelationDirection::Subtype => {
+                let fresh = self.next_term_infer_of_kind(other);
+                let (sub, sup) = match invert {
+                    Invert::No => (fresh, other),
+                    Invert::Yes => (other, fresh),
+                };
+                self.sub(param_env, sub, sup)?;
+                fresh
+            }
+        };
+        self.add_goal(Goal::new(
+            self.tcx(),
+            param_env,
+            ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }),
+        ));
+
+        Ok(())
+    }
+
+    fn assemble_subst_relate_candidate(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        alias_lhs: ty::AliasTy<'tcx>,
+        alias_rhs: ty::AliasTy<'tcx>,
+        direction: ty::AliasRelationDirection,
+    ) -> QueryResult<'tcx> {
+        self.probe(|ecx| {
+            match direction {
+                ty::AliasRelationDirection::Equate => {
+                    ecx.eq(param_env, alias_lhs, alias_rhs)?;
+                }
+                ty::AliasRelationDirection::Subtype => {
+                    ecx.sub(param_env, alias_lhs, alias_rhs)?;
+                }
+            }
+
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
+    }
+
+    fn assemble_bidirectional_normalizes_to_candidate(
+        &mut self,
+        param_env: ty::ParamEnv<'tcx>,
+        lhs: ty::Term<'tcx>,
+        rhs: ty::Term<'tcx>,
+        direction: ty::AliasRelationDirection,
+    ) -> QueryResult<'tcx> {
+        self.probe(|ecx| {
+            ecx.normalizes_to_inner(
+                param_env,
+                lhs.to_alias_ty(ecx.tcx()).unwrap(),
+                rhs,
+                direction,
+                Invert::No,
+            )?;
+            ecx.normalizes_to_inner(
+                param_env,
+                rhs.to_alias_ty(ecx.tcx()).unwrap(),
+                lhs,
+                direction,
+                Invert::Yes,
+            )?;
+            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        })
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
index bc93b9e99ad..3001d9f1b1f 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
+++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs
@@ -187,6 +187,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
             let (ref infcx, input, var_values) = tcx
                 .infer_ctxt()
                 .intercrate(intercrate)
+                .with_next_trait_solver(true)
                 .with_opaque_type_inference(canonical_input.value.anchor)
                 .build_with_canonical(DUMMY_SP, &canonical_input);
 
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 56a254d9c07..f4c29c837b8 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -20,6 +20,7 @@ use rustc_middle::ty::{
     CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
 };
 
+mod alias_relate;
 mod assembly;
 mod canonicalize;
 mod eval_ctxt;
@@ -155,142 +156,6 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> {
     }
 
     #[instrument(level = "debug", skip(self), ret)]
-    fn compute_alias_relate_goal(
-        &mut self,
-        goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>,
-    ) -> QueryResult<'tcx> {
-        let tcx = self.tcx();
-        // We may need to invert the alias relation direction if dealing an alias on the RHS.
-        #[derive(Debug)]
-        enum Invert {
-            No,
-            Yes,
-        }
-        let evaluate_normalizes_to =
-            |ecx: &mut EvalCtxt<'_, 'tcx>, alias, other, direction, invert| {
-                let span = tracing::span!(
-                    tracing::Level::DEBUG,
-                    "compute_alias_relate_goal(evaluate_normalizes_to)",
-                    ?alias,
-                    ?other,
-                    ?direction,
-                    ?invert
-                );
-                let _enter = span.enter();
-                let result = ecx.probe(|ecx| {
-                    let other = match direction {
-                        // This is purely an optimization.
-                        ty::AliasRelationDirection::Equate => other,
-
-                        ty::AliasRelationDirection::Subtype => {
-                            let fresh = ecx.next_term_infer_of_kind(other);
-                            let (sub, sup) = match invert {
-                                Invert::No => (fresh, other),
-                                Invert::Yes => (other, fresh),
-                            };
-                            ecx.sub(goal.param_env, sub, sup)?;
-                            fresh
-                        }
-                    };
-                    ecx.add_goal(goal.with(
-                        tcx,
-                        ty::Binder::dummy(ty::ProjectionPredicate {
-                            projection_ty: alias,
-                            term: other,
-                        }),
-                    ));
-                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-                });
-                debug!(?result);
-                result
-            };
-
-        let (lhs, rhs, direction) = goal.predicate;
-
-        if lhs.is_infer() || rhs.is_infer() {
-            bug!(
-                "`AliasRelate` goal with an infer var on lhs or rhs which should have been instantiated"
-            );
-        }
-
-        match (lhs.to_alias_ty(tcx), rhs.to_alias_ty(tcx)) {
-            (None, None) => bug!("`AliasRelate` goal without an alias on either lhs or rhs"),
-
-            // RHS is not a projection, only way this is true is if LHS normalizes-to RHS
-            (Some(alias_lhs), None) => {
-                evaluate_normalizes_to(self, alias_lhs, rhs, direction, Invert::No)
-            }
-
-            // LHS is not a projection, only way this is true is if RHS normalizes-to LHS
-            (None, Some(alias_rhs)) => {
-                evaluate_normalizes_to(self, alias_rhs, lhs, direction, Invert::Yes)
-            }
-
-            (Some(alias_lhs), Some(alias_rhs)) => {
-                debug!("both sides are aliases");
-
-                let mut candidates = Vec::new();
-                // LHS normalizes-to RHS
-                candidates.extend(evaluate_normalizes_to(
-                    self,
-                    alias_lhs,
-                    rhs,
-                    direction,
-                    Invert::No,
-                ));
-                // RHS normalizes-to RHS
-                candidates.extend(evaluate_normalizes_to(
-                    self,
-                    alias_rhs,
-                    lhs,
-                    direction,
-                    Invert::Yes,
-                ));
-                // Relate via substs
-                let subst_relate_response = self.probe(|ecx| {
-                    let span = tracing::span!(
-                        tracing::Level::DEBUG,
-                        "compute_alias_relate_goal(relate_via_substs)",
-                        ?alias_lhs,
-                        ?alias_rhs,
-                        ?direction
-                    );
-                    let _enter = span.enter();
-
-                    match direction {
-                        ty::AliasRelationDirection::Equate => {
-                            ecx.eq(goal.param_env, alias_lhs, alias_rhs)?;
-                        }
-                        ty::AliasRelationDirection::Subtype => {
-                            ecx.sub(goal.param_env, alias_lhs, alias_rhs)?;
-                        }
-                    }
-
-                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-                });
-                candidates.extend(subst_relate_response);
-                debug!(?candidates);
-
-                if let Some(merged) = self.try_merge_responses(&candidates) {
-                    Ok(merged)
-                } else {
-                    // When relating two aliases and we have ambiguity, we prefer
-                    // relating the generic arguments of the aliases over normalizing
-                    // them. This is necessary for inference during typeck.
-                    //
-                    // As this is incomplete, we must not do so during coherence.
-                    match (self.solver_mode(), subst_relate_response) {
-                        (SolverMode::Normal, Ok(response)) => Ok(response),
-                        (SolverMode::Normal, Err(NoSolution)) | (SolverMode::Coherence, _) => {
-                            self.flounder(&candidates)
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    #[instrument(level = "debug", skip(self), ret)]
     fn compute_const_arg_has_type_goal(
         &mut self,
         goal: Goal<'tcx, (ty::Const<'tcx>, Ty<'tcx>)>,
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index d6fd457de06..2c793ea33dc 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -182,6 +182,7 @@ fn overlap<'tcx>(
         .with_opaque_type_inference(DefiningAnchor::Bubble)
         .skip_leak_check(skip_leak_check.is_yes())
         .intercrate(true)
+        .with_next_trait_solver(tcx.next_trait_solver_in_coherence())
         .build();
     let selcx = &mut SelectionContext::new(&infcx);
     if track_ambiguity_causes.is_yes() {
diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs
index 2c5ffd664fe..90699c3cadc 100644
--- a/compiler/rustc_trait_selection/src/traits/engine.rs
+++ b/compiler/rustc_trait_selection/src/traits/engine.rs
@@ -27,24 +27,42 @@ use rustc_session::config::TraitSolver;
 use rustc_span::Span;
 
 pub trait TraitEngineExt<'tcx> {
-    fn new(tcx: TyCtxt<'tcx>) -> Box<Self>;
-    fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self>;
+    fn new(infcx: &InferCtxt<'tcx>) -> Box<Self>;
+    fn new_in_snapshot(infcx: &InferCtxt<'tcx>) -> Box<Self>;
 }
 
 impl<'tcx> TraitEngineExt<'tcx> for dyn TraitEngine<'tcx> {
-    fn new(tcx: TyCtxt<'tcx>) -> Box<Self> {
-        match tcx.sess.opts.unstable_opts.trait_solver {
-            TraitSolver::Classic => Box::new(FulfillmentContext::new()),
-            TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new()),
-            TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()),
+    fn new(infcx: &InferCtxt<'tcx>) -> Box<Self> {
+        match (infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.next_trait_solver()) {
+            (TraitSolver::Classic, false) | (TraitSolver::NextCoherence, false) => {
+                Box::new(FulfillmentContext::new())
+            }
+            (TraitSolver::Next | TraitSolver::NextCoherence, true) => {
+                Box::new(NextFulfillmentCtxt::new())
+            }
+            (TraitSolver::Chalk, false) => Box::new(ChalkFulfillmentContext::new()),
+            _ => bug!(
+                "incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})",
+                infcx.tcx.sess.opts.unstable_opts.trait_solver,
+                infcx.next_trait_solver()
+            ),
         }
     }
 
-    fn new_in_snapshot(tcx: TyCtxt<'tcx>) -> Box<Self> {
-        match tcx.sess.opts.unstable_opts.trait_solver {
-            TraitSolver::Classic => Box::new(FulfillmentContext::new_in_snapshot()),
-            TraitSolver::Chalk => Box::new(ChalkFulfillmentContext::new_in_snapshot()),
-            TraitSolver::Next => Box::new(NextFulfillmentCtxt::new()),
+    fn new_in_snapshot(infcx: &InferCtxt<'tcx>) -> Box<Self> {
+        match (infcx.tcx.sess.opts.unstable_opts.trait_solver, infcx.next_trait_solver()) {
+            (TraitSolver::Classic, false) | (TraitSolver::NextCoherence, false) => {
+                Box::new(FulfillmentContext::new_in_snapshot())
+            }
+            (TraitSolver::Next | TraitSolver::NextCoherence, true) => {
+                Box::new(NextFulfillmentCtxt::new())
+            }
+            (TraitSolver::Chalk, false) => Box::new(ChalkFulfillmentContext::new_in_snapshot()),
+            _ => bug!(
+                "incompatible combination of -Ztrait-solver flag ({:?}) and InferCtxt::next_trait_solver ({:?})",
+                infcx.tcx.sess.opts.unstable_opts.trait_solver,
+                infcx.next_trait_solver()
+            ),
         }
     }
 }
@@ -58,11 +76,11 @@ pub struct ObligationCtxt<'a, 'tcx> {
 
 impl<'a, 'tcx> ObligationCtxt<'a, 'tcx> {
     pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self {
-        Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx.tcx)) }
+        Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new(infcx)) }
     }
 
     pub fn new_in_snapshot(infcx: &'a InferCtxt<'tcx>) -> Self {
-        Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new_in_snapshot(infcx.tcx)) }
+        Self { infcx, engine: RefCell::new(<dyn TraitEngine<'_>>::new_in_snapshot(infcx)) }
     }
 
     pub fn register_obligation(&self, obligation: PredicateObligation<'tcx>) {
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 01c74be7057..c4481b39e14 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -1047,7 +1047,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                                 // (which may fail).
                                 span_bug!(span, "WF predicate not satisfied for {:?}", ty);
                             }
-                            TraitSolver::Chalk | TraitSolver::Next => {
+                            TraitSolver::Chalk | TraitSolver::Next | TraitSolver::NextCoherence => {
                                 // FIXME: we'll need a better message which takes into account
                                 // which bounds actually failed to hold.
                                 self.tcx.sess.struct_span_err(
diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
index a8a74d7501a..f8ceee50054 100644
--- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -78,7 +78,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
             _ => obligation.param_env.without_const(),
         };
 
-        if self.tcx.trait_solver_next() {
+        if self.next_trait_solver() {
             self.probe(|snapshot| {
                 let mut fulfill_cx = crate::solve::FulfillmentCtxt::new();
                 fulfill_cx.register_predicate_obligation(self, obligation.clone());
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
index 642fdec2d9a..9d7933e23a8 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
@@ -146,7 +146,7 @@ where
         infcx: &InferCtxt<'tcx>,
         span: Span,
     ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
-        if infcx.tcx.trait_solver_next() {
+        if infcx.next_trait_solver() {
             return Ok(scrape_region_constraints(
                 infcx,
                 |ocx| QueryTypeOp::perform_locally_in_new_solver(ocx, self),
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 42c1b629ac2..25e5b5e17de 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -539,7 +539,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         self.evaluation_probe(|this| {
             let goal =
                 this.infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
-            let mut result = if this.tcx().trait_solver_next() {
+            let mut result = if this.infcx.next_trait_solver() {
                 this.evaluate_predicates_recursively_in_new_solver([obligation.clone()])?
             } else {
                 this.evaluate_predicate_recursively(
@@ -593,7 +593,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
     where
         I: IntoIterator<Item = PredicateObligation<'tcx>> + std::fmt::Debug,
     {
-        if self.tcx().trait_solver_next() {
+        if self.infcx.next_trait_solver() {
             self.evaluate_predicates_recursively_in_new_solver(predicates)
         } else {
             let mut result = EvaluatedToOk;
diff --git a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
index af8dd0da579..84746eba3ec 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_normalize.rs
@@ -21,7 +21,7 @@ impl<'tcx> StructurallyNormalizeExt<'tcx> for At<'_, 'tcx> {
     ) -> Result<Ty<'tcx>, Vec<FulfillmentError<'tcx>>> {
         assert!(!ty.is_ty_var(), "should have resolved vars before calling");
 
-        if self.infcx.tcx.trait_solver_next() {
+        if self.infcx.next_trait_solver() {
             while let ty::Alias(ty::Projection, projection_ty) = *ty.kind() {
                 let new_infer_ty = self.infcx.next_ty_var(TypeVariableOrigin {
                     kind: TypeVariableOriginKind::NormalizeProjectionType,
diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs
index ddba03b0b12..5f84acc8a04 100644
--- a/compiler/rustc_traits/src/codegen.rs
+++ b/compiler/rustc_traits/src/codegen.rs
@@ -55,7 +55,7 @@ pub fn codegen_select_candidate<'tcx>(
     // Currently, we use a fulfillment context to completely resolve
     // all nested obligations. This is because they can inform the
     // inference of the impl's type parameters.
-    let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(tcx);
+    let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(&infcx);
     let impl_source = selection.map(|predicate| {
         fulfill_cx.register_predicate_obligation(&infcx, predicate);
     });
diff --git a/compiler/rustc_traits/src/evaluate_obligation.rs b/compiler/rustc_traits/src/evaluate_obligation.rs
index f5b2753b797..73756caf372 100644
--- a/compiler/rustc_traits/src/evaluate_obligation.rs
+++ b/compiler/rustc_traits/src/evaluate_obligation.rs
@@ -16,7 +16,7 @@ fn evaluate_obligation<'tcx>(
     tcx: TyCtxt<'tcx>,
     canonical_goal: CanonicalPredicateGoal<'tcx>,
 ) -> Result<EvaluationResult, OverflowError> {
-    assert!(!tcx.trait_solver_next());
+    assert!(!tcx.next_trait_solver_globally());
     debug!("evaluate_obligation(canonical_goal={:#?})", canonical_goal);
     // HACK This bubble is required for this tests to pass:
     // impl-trait/issue99642.rs
diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs
index 48685f7a9d5..a9e15d89246 100644
--- a/src/bootstrap/compile.rs
+++ b/src/bootstrap/compile.rs
@@ -1358,7 +1358,12 @@ impl Step for Sysroot {
             // newly compiled std, not the downloaded std.
             add_filtered_files("lib", builder.config.ci_rust_std_contents());
 
-            let filtered_extensions = [OsStr::new("rmeta"), OsStr::new("rlib"), OsStr::new("so")];
+            let filtered_extensions = [
+                OsStr::new("rmeta"),
+                OsStr::new("rlib"),
+                // FIXME: this is wrong when compiler.host != build, but we don't support that today
+                OsStr::new(std::env::consts::DLL_EXTENSION),
+            ];
             let ci_rustc_dir = builder.ci_rustc_dir(builder.config.build);
             builder.cp_filtered(&ci_rustc_dir, &sysroot, &|path| {
                 if path.extension().map_or(true, |ext| !filtered_extensions.contains(&ext)) {
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index db353552893..22c8cc09243 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -14,7 +14,7 @@ use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::Span;
 
-use std::{iter, mem};
+use std::mem;
 
 use crate::clean::{cfg::Cfg, reexport_chain, AttributesExt, NestedAttributesExt};
 use crate::core;
@@ -291,27 +291,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         if !please_inline {
             let inherits_hidden = inherits_doc_hidden(tcx, res_did, None);
             // Only inline if requested or if the item would otherwise be stripped.
-            //
-            // If it's a doc hidden module, we need to keep it in case some of its inner items
-            // are re-exported.
             if (!is_private && !inherits_hidden) || (
                 is_hidden &&
+                // If it's a doc hidden module, we need to keep it in case some of its inner items
+                // are re-exported.
                 !matches!(item, Node::Item(&hir::Item { kind: hir::ItemKind::Mod(_), .. }))
-            ) {
-                return false;
-            } else if let Some(item_def_id) = reexport_chain(tcx, def_id, res_did).iter()
-                .flat_map(|reexport| reexport.id()).map(|id| id.expect_local())
-                .chain(iter::once(res_did)).nth(1) &&
-                item_def_id != def_id &&
-                self
-                    .cx
-                    .cache
-                    .effective_visibilities
-                    .is_directly_public(tcx, item_def_id.to_def_id()) &&
-                !tcx.is_doc_hidden(item_def_id) &&
-                !inherits_doc_hidden(tcx, item_def_id, None)
-            {
+            ) ||
                 // The imported item is public and not `doc(hidden)` so no need to inline it.
+                self.reexport_public_and_not_hidden(def_id, res_did)
+            {
                 return false;
             }
         }
@@ -359,6 +347,28 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
         ret
     }
 
+    /// Returns `true` if the item is visible, meaning it's not `#[doc(hidden)]` or private.
+    ///
+    /// This function takes into account the entire re-export `use` chain, so it needs the
+    /// ID of the "leaf" `use` and the ID of the "root" item.
+    fn reexport_public_and_not_hidden(
+        &self,
+        import_def_id: LocalDefId,
+        target_def_id: LocalDefId,
+    ) -> bool {
+        let tcx = self.cx.tcx;
+        let item_def_id = reexport_chain(tcx, import_def_id, target_def_id)
+            .iter()
+            .flat_map(|reexport| reexport.id())
+            .map(|id| id.expect_local())
+            .nth(1)
+            .unwrap_or(target_def_id);
+        item_def_id != import_def_id
+            && self.cx.cache.effective_visibilities.is_directly_public(tcx, item_def_id.to_def_id())
+            && !tcx.is_doc_hidden(item_def_id)
+            && !inherits_doc_hidden(tcx, item_def_id, None)
+    }
+
     #[inline]
     fn add_to_current_mod(
         &mut self,
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index f796c898731..96fe720630c 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -108,6 +108,7 @@ string_enum! {
         Polonius => "polonius",
         Chalk => "chalk",
         NextSolver => "next-solver",
+        NextSolverCoherence => "next-solver-coherence",
         SplitDwarf => "split-dwarf",
         SplitDwarfSingle => "split-dwarf-single",
     }
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 923b2e63f2e..6582b534488 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -2127,6 +2127,9 @@ impl<'test> TestCx<'test> {
             Some(CompareMode::NextSolver) => {
                 rustc.args(&["-Ztrait-solver=next"]);
             }
+            Some(CompareMode::NextSolverCoherence) => {
+                rustc.args(&["-Ztrait-solver=next-coherence"]);
+            }
             Some(CompareMode::SplitDwarf) if self.config.target.contains("windows") => {
                 rustc.args(&["-Csplit-debuginfo=unpacked", "-Zunstable-options"]);
             }
diff --git a/tests/ui/macros/issue-112342-1.rs b/tests/ui/macros/issue-112342-1.rs
new file mode 100644
index 00000000000..bd2abe7f697
--- /dev/null
+++ b/tests/ui/macros/issue-112342-1.rs
@@ -0,0 +1,49 @@
+// same as #95267, ignore doc comment although it's a bug.
+
+macro_rules! m1 {
+    (
+        $(
+            ///
+        )*
+        //~^^^ERROR repetition matches empty token tree
+    ) => {};
+}
+
+m1! {}
+
+macro_rules! m2 {
+    (
+        $(
+            ///
+        )+
+        //~^^^ERROR repetition matches empty token tree
+    ) => {};
+}
+
+m2! {}
+
+macro_rules! m3 {
+    (
+        $(
+            ///
+        )?
+        //~^^^ERROR repetition matches empty token tree
+    ) => {};
+}
+
+m3! {}
+
+
+macro_rules! m4 {
+    (
+        $(
+            ///
+            ///
+        )*
+        //~^^^^ERROR repetition matches empty token tree
+    ) => {};
+}
+
+m4! {}
+
+fn main() {}
diff --git a/tests/ui/macros/issue-112342-1.stderr b/tests/ui/macros/issue-112342-1.stderr
new file mode 100644
index 00000000000..f2d82bf599e
--- /dev/null
+++ b/tests/ui/macros/issue-112342-1.stderr
@@ -0,0 +1,64 @@
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-1.rs:6:13
+   |
+LL |             ///
+   |             ^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-112342-1.rs:5:10
+   |
+LL |           $(
+   |  __________^
+LL | |             ///
+LL | |         )*
+   | |_________^
+
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-1.rs:17:13
+   |
+LL |             ///
+   |             ^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-112342-1.rs:16:10
+   |
+LL |           $(
+   |  __________^
+LL | |             ///
+LL | |         )+
+   | |_________^
+
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-1.rs:28:13
+   |
+LL |             ///
+   |             ^^^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-112342-1.rs:27:10
+   |
+LL |           $(
+   |  __________^
+LL | |             ///
+LL | |         )?
+   | |_________^
+
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-1.rs:40:13
+   |
+LL | /             ///
+LL | |             ///
+   | |_______________^
+
+error: repetition matches empty token tree
+  --> $DIR/issue-112342-1.rs:39:10
+   |
+LL |           $(
+   |  __________^
+LL | |             ///
+LL | |             ///
+LL | |         )*
+   | |_________^
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/macros/issue-112342-2.rs b/tests/ui/macros/issue-112342-2.rs
new file mode 100644
index 00000000000..e797aff94d2
--- /dev/null
+++ b/tests/ui/macros/issue-112342-2.rs
@@ -0,0 +1,39 @@
+// check-pass
+
+// same as #95267, ignore doc comment although it's a bug.
+
+macro_rules! m1 {
+    (
+        $(
+            ///
+            $expr: expr,
+        )*
+    ) => {};
+}
+
+m1! {}
+
+macro_rules! m2 {
+    (
+        $(
+            ///
+            $expr: expr,
+            ///
+        )*
+    ) => {};
+}
+
+m2! {}
+
+macro_rules! m3 {
+    (
+        $(
+            ///
+            $tt: tt,
+        )*
+    ) => {};
+}
+
+m3! {}
+
+fn main() {}
diff --git a/tests/ui/macros/issue-112342-2.stderr b/tests/ui/macros/issue-112342-2.stderr
new file mode 100644
index 00000000000..8c1b6f9471b
--- /dev/null
+++ b/tests/ui/macros/issue-112342-2.stderr
@@ -0,0 +1,24 @@
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-2.rs:8:13
+   |
+LL |             ///
+   |             ^^^
+
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-2.rs:19:13
+   |
+LL |             ///
+   |             ^^^
+
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-2.rs:21:13
+   |
+LL |             ///
+   |             ^^^
+
+note: doc comments are ignored in matcher position
+  --> $DIR/issue-112342-2.rs:31:13
+   |
+LL |             ///
+   |             ^^^
+
diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr b/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr
new file mode 100644
index 00000000000..578db0cc65e
--- /dev/null
+++ b/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr
@@ -0,0 +1,12 @@
+error[E0119]: conflicting implementations of trait `Overlap` for type `u32`
+  --> $DIR/specialization-default-items-drop-coherence.rs:29:1
+   |
+LL | impl Overlap for u32 {
+   | -------------------- first implementation here
+...
+LL | impl Overlap for <u32 as Default>::Id {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr b/tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr
new file mode 100644
index 00000000000..578db0cc65e
--- /dev/null
+++ b/tests/ui/specialization/specialization-default-items-drop-coherence.next.stderr
@@ -0,0 +1,12 @@
+error[E0119]: conflicting implementations of trait `Overlap` for type `u32`
+  --> $DIR/specialization-default-items-drop-coherence.rs:29:1
+   |
+LL | impl Overlap for u32 {
+   | -------------------- first implementation here
+...
+LL | impl Overlap for <u32 as Default>::Id {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.rs b/tests/ui/specialization/specialization-default-items-drop-coherence.rs
index 16ad942d5ab..44c598f19cb 100644
--- a/tests/ui/specialization/specialization-default-items-drop-coherence.rs
+++ b/tests/ui/specialization/specialization-default-items-drop-coherence.rs
@@ -1,5 +1,8 @@
-// check-pass
-// known-bug: #105782
+// revisions: classic coherence next
+//[next] compile-flags: -Ztrait-solver=next
+//[coherence] compile-flags: -Ztrait-solver=next-coherence
+//[classic] check-pass
+//[classic] known-bug: #105782
 
 // Should fail. Default items completely drop candidates instead of ambiguity,
 // which is unsound during coherence, since coherence requires completeness.
@@ -24,6 +27,8 @@ impl Overlap for u32 {
 }
 
 impl Overlap for <u32 as Default>::Id {
+   //[coherence]~^ ERROR conflicting implementations of trait `Overlap` for type `u32`
+   //[next]~^^ ERROR conflicting implementations of trait `Overlap` for type `u32`
    type Assoc = Box<usize>;
 }
 
diff --git a/tests/ui/traits/new-solver/tait-eq-proj-2.rs b/tests/ui/traits/new-solver/tait-eq-proj-2.rs
new file mode 100644
index 00000000000..99a3d02bd1a
--- /dev/null
+++ b/tests/ui/traits/new-solver/tait-eq-proj-2.rs
@@ -0,0 +1,21 @@
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+#![feature(type_alias_impl_trait)]
+
+// Similar to tests/ui/traits/new-solver/tait-eq-proj.rs
+// but check the alias-sub relation in the other direction.
+
+type Tait = impl Iterator<Item = impl Sized>;
+
+fn mk<T>() -> T { todo!() }
+
+fn a() {
+    let x: Tait = mk();
+    let mut array = mk();
+    let mut z = IntoIterator::into_iter(array);
+    z = x;
+    array = [0i32; 32];
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/tait-eq-proj.rs b/tests/ui/traits/new-solver/tait-eq-proj.rs
new file mode 100644
index 00000000000..01141b2819a
--- /dev/null
+++ b/tests/ui/traits/new-solver/tait-eq-proj.rs
@@ -0,0 +1,35 @@
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+#![feature(type_alias_impl_trait)]
+
+type Tait = impl Iterator<Item = impl Sized>;
+
+/*
+
+Consider the goal - AliasRelate(Tait, <[i32; 32] as IntoIterator>::IntoIter)
+which is registered on the line above.
+
+A. SubstRelate - fails (of course).
+
+B. NormalizesToRhs - Tait normalizes-to <[i32; 32] as IntoIterator>::IntoIter
+    * infer definition - Tait := <[i32; 32] as IntoIterator>::IntoIter
+
+C. NormalizesToLhs - <[i32; 32] as IntoIterator>::IntoIter normalizes-to Tait
+    * Find impl candidate, after substitute - std::array::IntoIter<i32, 32>
+    * Equate std::array::IntoIter<i32, 32> and Tait
+        * infer definition - Tait := std::array::IntoIter<i32, 32>
+
+B and C are not equal, but they are equivalent modulo normalization.
+
+We get around this by evaluating both the NormalizesToRhs and NormalizesToLhs
+goals together. Essentially:
+    A alias-relate B if A normalizes-to B and B normalizes-to A.
+
+*/
+
+fn a() {
+    let _: Tait = IntoIterator::into_iter([0i32; 32]);
+}
+
+fn main() {}
diff --git a/tests/ui/traits/new-solver/tait-eq-tait.rs b/tests/ui/traits/new-solver/tait-eq-tait.rs
new file mode 100644
index 00000000000..532c4c39bd4
--- /dev/null
+++ b/tests/ui/traits/new-solver/tait-eq-tait.rs
@@ -0,0 +1,17 @@
+// compile-flags: -Ztrait-solver=next
+// check-pass
+
+// Not exactly sure if this is the inference behavior we *want*,
+// but it is a side-effect of the lazy normalization of TAITs.
+
+#![feature(type_alias_impl_trait)]
+
+type Tait = impl Sized;
+type Tait2 = impl Sized;
+
+fn mk<T>() -> T { todo!() }
+
+fn main() {
+    let x: Tait = 1u32;
+    let y: Tait2 = x;
+}