about summary refs log tree commit diff
diff options
context:
space:
mode:
authorlcnr <rust@lcnr.de>2025-08-21 13:56:56 +0200
committerlcnr <rust@lcnr.de>2025-08-25 14:20:18 +0200
commit14b0ba6a0543cdbbd19b5a0aaa3ae03500fb72d2 (patch)
tree72210f2292e74d5d4b04191a43bb20be99eab577
parent7ee5cf6087eff3555bf0c7517f42e99a9b1a47a5 (diff)
downloadrust-14b0ba6a0543cdbbd19b5a0aaa3ae03500fb72d2.tar.gz
rust-14b0ba6a0543cdbbd19b5a0aaa3ae03500fb72d2.zip
support non-defining uses in HIR typeck
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs20
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/opaque_types.rs218
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs34
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs25
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs174
-rw-r--r--compiler/rustc_trait_selection/src/opaque_types.rs26
-rw-r--r--tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr24
-rw-r--r--tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr2
-rw-r--r--tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs1
-rw-r--r--tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr14
-rw-r--r--tests/ui/impl-trait/auto-trait-selection.next.stderr14
-rw-r--r--tests/ui/impl-trait/recursive-in-exhaustiveness.current.stderr2
-rw-r--r--tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr87
-rw-r--r--tests/ui/impl-trait/recursive-in-exhaustiveness.rs13
-rw-r--r--tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr12
-rw-r--r--tests/ui/impl-trait/two_tait_defining_each_other2.rs3
-rw-r--r--tests/ui/traits/next-solver/opaques/different-bound-vars.current.stderr14
-rw-r--r--tests/ui/traits/next-solver/opaques/different-bound-vars.rs20
-rw-r--r--tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr17
-rw-r--r--tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr17
-rw-r--r--tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.rs2
-rw-r--r--tests/ui/traits/next-solver/opaques/non-defining-use-hir-typeck.rs28
-rw-r--r--tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs16
-rw-r--r--tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr23
-rw-r--r--tests/ui/type-alias-impl-trait/constrain_in_projection2.rs2
-rw-r--r--tests/ui/type-alias-impl-trait/normalize-args-before-defining-use-check.rs33
30 files changed, 498 insertions, 363 deletions
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
index 33c4879af98..d1222ab21a2 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs
@@ -17,7 +17,7 @@ use rustc_middle::ty::{
 use rustc_mir_dataflow::points::DenseLocationMap;
 use rustc_span::Span;
 use rustc_trait_selection::opaque_types::{
-    InvalidOpaqueTypeArgs, check_opaque_type_parameter_valid,
+    NonDefiningUseReason, opaque_type_has_defining_use_args,
 };
 use rustc_trait_selection::solve::NoSolution;
 use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
@@ -42,7 +42,8 @@ use region_ctxt::RegionCtxt;
 /// if there are no `RegionErrors`. If there are region errors, it's likely
 /// that errors here are caused by them and don't need to be handled separately.
 pub(crate) enum DeferredOpaqueTypeError<'tcx> {
-    InvalidOpaqueTypeArgs(InvalidOpaqueTypeArgs<'tcx>),
+    /// FIXME(-Znext-solver=no): Only used by the old solver.
+    InvalidOpaqueTypeArgs(NonDefiningUseReason<'tcx>),
     LifetimeMismatchOpaqueParam(LifetimeMismatchOpaqueParam<'tcx>),
     UnexpectedHiddenRegion {
         /// The opaque type.
@@ -238,7 +239,7 @@ fn collect_defining_uses<'tcx>(
         let non_nll_opaque_type_key = opaque_type_key.fold_captured_lifetime_args(infcx.tcx, |r| {
             nll_var_to_universal_region(&rcx, r.as_var()).unwrap_or(r)
         });
-        if let Err(err) = check_opaque_type_parameter_valid(
+        if let Err(err) = opaque_type_has_defining_use_args(
             infcx,
             non_nll_opaque_type_key,
             hidden_type.span,
@@ -248,11 +249,10 @@ fn collect_defining_uses<'tcx>(
             // with `TypingMode::Borrowck`.
             if infcx.tcx.use_typing_mode_borrowck() {
                 match err {
-                    InvalidOpaqueTypeArgs::AlreadyReported(guar) => root_cx
-                        .add_concrete_opaque_type(
-                            opaque_type_key.def_id,
-                            OpaqueHiddenType::new_error(infcx.tcx, guar),
-                        ),
+                    NonDefiningUseReason::Tainted(guar) => root_cx.add_concrete_opaque_type(
+                        opaque_type_key.def_id,
+                        OpaqueHiddenType::new_error(infcx.tcx, guar),
+                    ),
                     _ => debug!(?non_nll_opaque_type_key, ?err, "ignoring non-defining use"),
                 }
             } else {
@@ -676,8 +676,8 @@ impl<'tcx> InferCtxt<'tcx> {
         &self,
         opaque_type_key: OpaqueTypeKey<'tcx>,
         instantiated_ty: OpaqueHiddenType<'tcx>,
-    ) -> Result<Ty<'tcx>, InvalidOpaqueTypeArgs<'tcx>> {
-        check_opaque_type_parameter_valid(
+    ) -> Result<Ty<'tcx>, NonDefiningUseReason<'tcx>> {
+        opaque_type_has_defining_use_args(
             self,
             opaque_type_key,
             instantiated_ty.span,
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
index 50e20a19eda..bc8abdde052 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
@@ -127,6 +127,10 @@ impl<'tcx> TaitConstraintLocator<'tcx> {
     }
 
     fn non_defining_use_in_defining_scope(&mut self, item_def_id: LocalDefId) {
+        // We make sure that all opaque types get defined while
+        // type checking the defining scope, so this error is unreachable
+        // with the new solver.
+        assert!(!self.tcx.next_trait_solver_globally());
         let guar = self.tcx.dcx().emit_err(TaitForwardCompat2 {
             span: self
                 .tcx
@@ -252,9 +256,7 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
             } else if let Some(hidden_ty) = tables.concrete_opaque_types.get(&def_id) {
                 hidden_ty.ty
             } else {
-                // FIXME(-Znext-solver): This should not be necessary and we should
-                // instead rely on inference variable fallback inside of typeck itself.
-
+                assert!(!tcx.next_trait_solver_globally());
                 // We failed to resolve the opaque type or it
                 // resolves to itself. We interpret this as the
                 // no values of the hidden type ever being constructed,
@@ -273,6 +275,7 @@ pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
                     if let Err(guar) = hir_ty.error_reported() {
                         Ty::new_error(tcx, guar)
                     } else {
+                        assert!(!tcx.next_trait_solver_globally());
                         hir_ty
                     }
                 }
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 26a98722b34..fc3dca73caf 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -413,7 +413,7 @@ pub(crate) struct UnconstrainedOpaqueType {
 #[derive(Diagnostic)]
 #[diag(hir_analysis_tait_forward_compat2)]
 #[note]
-pub(crate) struct TaitForwardCompat2 {
+pub struct TaitForwardCompat2 {
     #[primary_span]
     pub span: Span,
     #[note(hir_analysis_opaque)]
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 3a153ab089a..44a5ceed469 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -82,7 +82,7 @@ mod coherence;
 mod collect;
 mod constrained_generic_params;
 mod delegation;
-mod errors;
+pub mod errors;
 pub mod hir_ty_lowering;
 pub mod hir_wf_check;
 mod impl_wf_check;
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 634647a595a..879b7472736 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -247,6 +247,13 @@ fn typeck_with_inspect<'tcx>(
 
     debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
 
+    // We need to handle opaque types before emitting ambiguity errors as applying
+    // defining uses may guide type inference.
+    if fcx.next_trait_solver() {
+        fcx.handle_opaque_type_uses_next();
+    }
+
+    fcx.select_obligations_where_possible(|_| {});
     if let None = fcx.infcx.tainted_by_errors() {
         fcx.report_ambiguity_errors();
     }
diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs
index e0224f8c6e1..8188c0eb7d1 100644
--- a/compiler/rustc_hir_typeck/src/opaque_types.rs
+++ b/compiler/rustc_hir_typeck/src/opaque_types.rs
@@ -1,5 +1,221 @@
-use super::FnCtxt;
+use rustc_hir::def::DefKind;
+use rustc_hir_analysis::errors::TaitForwardCompat2;
+use rustc_infer::traits::ObligationCause;
+use rustc_middle::ty::{
+    self, DefiningScopeKind, EarlyBinder, OpaqueHiddenType, OpaqueTypeKey, TypeVisitableExt,
+    TypingMode,
+};
+use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
+use rustc_trait_selection::opaque_types::{
+    NonDefiningUseReason, opaque_type_has_defining_use_args,
+};
+use rustc_trait_selection::solve;
+use tracing::{debug, instrument};
+
+use crate::FnCtxt;
+
 impl<'tcx> FnCtxt<'_, 'tcx> {
+    /// This takes all the opaque type uses during HIR typeck. It first computes
+    /// the concrete hidden type by iterating over all defining uses.
+    ///
+    /// A use during HIR typeck is defining if all non-lifetime arguments are
+    /// unique generic parameters and the hidden type does not reference any
+    /// inference variables.
+    ///
+    /// It then uses these defining uses to guide inference for all other uses.
+    #[instrument(level = "debug", skip(self))]
+    pub(super) fn handle_opaque_type_uses_next(&mut self) {
+        // We clone the opaques instead of stealing them here as they are still used for
+        // normalization in the next generation trait solver.
+        let mut opaque_types: Vec<_> = self.infcx.clone_opaque_types();
+        let num_entries = self.inner.borrow_mut().opaque_types().num_entries();
+        let prev = self.checked_opaque_types_storage_entries.replace(Some(num_entries));
+        debug_assert_eq!(prev, None);
+        for entry in &mut opaque_types {
+            *entry = self.resolve_vars_if_possible(*entry);
+        }
+        debug!(?opaque_types);
+
+        self.compute_concrete_opaque_types(&opaque_types);
+        self.apply_computed_concrete_opaque_types(&opaque_types);
+    }
+}
+
+enum UsageKind<'tcx> {
+    None,
+    NonDefiningUse(OpaqueHiddenType<'tcx>),
+    UnconstrainedHiddenType(OpaqueHiddenType<'tcx>),
+    HasDefiningUse,
+}
+
+impl<'tcx> UsageKind<'tcx> {
+    fn merge(&mut self, other: UsageKind<'tcx>) {
+        match (&*self, &other) {
+            (UsageKind::HasDefiningUse, _) | (_, UsageKind::None) => unreachable!(),
+            (UsageKind::None, _) => *self = other,
+            // When mergining non-defining uses, prefer earlier ones. This means
+            // the error happens as early as possible.
+            (
+                UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..),
+                UsageKind::NonDefiningUse(..),
+            ) => {}
+            // When merging unconstrained hidden types, we prefer later ones. This is
+            // used as in most cases, the defining use is the final return statement
+            // of our function, and other uses with defining arguments are likely not
+            // intended to be defining.
+            (
+                UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..),
+                UsageKind::UnconstrainedHiddenType(..) | UsageKind::HasDefiningUse,
+            ) => *self = other,
+        }
+    }
+}
+
+impl<'tcx> FnCtxt<'_, 'tcx> {
+    fn compute_concrete_opaque_types(
+        &mut self,
+        opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
+    ) {
+        let tcx = self.tcx;
+        let TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode()
+        else {
+            unreachable!();
+        };
+
+        for def_id in defining_opaque_types_and_generators {
+            match tcx.def_kind(def_id) {
+                DefKind::OpaqueTy => {}
+                DefKind::Closure => continue,
+                _ => unreachable!("not opaque or generator: {def_id:?}"),
+            }
+
+            let mut usage_kind = UsageKind::None;
+            for &(opaque_type_key, hidden_type) in opaque_types {
+                if opaque_type_key.def_id != def_id {
+                    continue;
+                }
+
+                usage_kind.merge(self.consider_opaque_type_use(opaque_type_key, hidden_type));
+                if let UsageKind::HasDefiningUse = usage_kind {
+                    break;
+                }
+            }
+
+            let guar = match usage_kind {
+                UsageKind::None => {
+                    if let Some(guar) = self.tainted_by_errors() {
+                        guar
+                    } else {
+                        self.tcx.dcx().emit_err(TaitForwardCompat2 {
+                            span: self
+                                .tcx
+                                .def_ident_span(self.body_id)
+                                .unwrap_or_else(|| self.tcx.def_span(self.body_id)),
+                            opaque_type_span: self.tcx.def_span(def_id),
+                            opaque_type: self.tcx.def_path_str(def_id),
+                        })
+                    }
+                }
+                UsageKind::NonDefiningUse(hidden_type) => {
+                    tcx.dcx().span_err(hidden_type.span, "non-defining use in the defining scope")
+                }
+                UsageKind::UnconstrainedHiddenType(hidden_type) => {
+                    let infer_var = hidden_type
+                        .ty
+                        .walk()
+                        .filter_map(ty::GenericArg::as_term)
+                        .find(|term| term.is_infer())
+                        .unwrap_or_else(|| hidden_type.ty.into());
+                    self.err_ctxt()
+                        .emit_inference_failure_err(
+                            self.body_id,
+                            hidden_type.span,
+                            infer_var,
+                            TypeAnnotationNeeded::E0282,
+                            false,
+                        )
+                        .emit()
+                }
+                UsageKind::HasDefiningUse => continue,
+            };
+
+            self.typeck_results
+                .borrow_mut()
+                .concrete_opaque_types
+                .insert(def_id, OpaqueHiddenType::new_error(tcx, guar));
+            self.set_tainted_by_errors(guar);
+        }
+    }
+
+    fn consider_opaque_type_use(
+        &mut self,
+        opaque_type_key: OpaqueTypeKey<'tcx>,
+        hidden_type: OpaqueHiddenType<'tcx>,
+    ) -> UsageKind<'tcx> {
+        if let Err(err) = opaque_type_has_defining_use_args(
+            &self,
+            opaque_type_key,
+            hidden_type.span,
+            DefiningScopeKind::HirTypeck,
+        ) {
+            match err {
+                NonDefiningUseReason::Tainted(guar) => {
+                    self.typeck_results.borrow_mut().concrete_opaque_types.insert(
+                        opaque_type_key.def_id,
+                        OpaqueHiddenType::new_error(self.tcx, guar),
+                    );
+                    return UsageKind::HasDefiningUse;
+                }
+                _ => return UsageKind::NonDefiningUse(hidden_type),
+            };
+        }
+
+        // We ignore uses of the opaque if they have any inference variables
+        // as this can frequently happen with recursive calls.
+        //
+        // See `tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs`.
+        if hidden_type.ty.has_non_region_infer() {
+            return UsageKind::UnconstrainedHiddenType(hidden_type);
+        }
+
+        let cause = ObligationCause::misc(hidden_type.span, self.body_id);
+        let at = self.at(&cause, self.param_env);
+        let hidden_type = match solve::deeply_normalize(at, hidden_type) {
+            Ok(hidden_type) => hidden_type,
+            Err(errors) => {
+                let guar = self.err_ctxt().report_fulfillment_errors(errors);
+                OpaqueHiddenType::new_error(self.tcx, guar)
+            }
+        };
+        let hidden_type = hidden_type.remap_generic_params_to_declaration_params(
+            opaque_type_key,
+            self.tcx,
+            DefiningScopeKind::HirTypeck,
+        );
+
+        let prev = self
+            .typeck_results
+            .borrow_mut()
+            .concrete_opaque_types
+            .insert(opaque_type_key.def_id, hidden_type);
+        assert!(prev.is_none());
+        UsageKind::HasDefiningUse
+    }
+
+    fn apply_computed_concrete_opaque_types(
+        &mut self,
+        opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)],
+    ) {
+        let tcx = self.tcx;
+        for &(key, hidden_type) in opaque_types {
+            let expected =
+                *self.typeck_results.borrow_mut().concrete_opaque_types.get(&key.def_id).unwrap();
+
+            let expected = EarlyBinder::bind(expected.ty).instantiate(tcx, key.args);
+            self.demand_eqtype(hidden_type.span, expected, hidden_type.ty);
+        }
+    }
+
     /// We may in theory add further uses of an opaque after cloning the opaque
     /// types storage during writeback when computing the defining uses.
     ///
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 131c687bddc..253eacbf353 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -27,7 +27,7 @@ use rustc_middle::ty::{
 };
 use rustc_span::{Span, sym};
 use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
-use rustc_trait_selection::opaque_types::check_opaque_type_parameter_valid;
+use rustc_trait_selection::opaque_types::opaque_type_has_defining_use_args;
 use rustc_trait_selection::solve;
 use tracing::{debug, instrument};
 
@@ -546,8 +546,24 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
         }
     }
 
+    fn visit_opaque_types_next(&mut self) {
+        let mut fcx_typeck_results = self.fcx.typeck_results.borrow_mut();
+        assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
+        for hidden_ty in fcx_typeck_results.concrete_opaque_types.values() {
+            assert!(!hidden_ty.has_infer());
+        }
+
+        assert_eq!(self.typeck_results.concrete_opaque_types.len(), 0);
+        self.typeck_results.concrete_opaque_types =
+            mem::take(&mut fcx_typeck_results.concrete_opaque_types);
+    }
+
     #[instrument(skip(self), level = "debug")]
     fn visit_opaque_types(&mut self) {
+        if self.fcx.next_trait_solver() {
+            return self.visit_opaque_types_next();
+        }
+
         let tcx = self.tcx();
         // We clone the opaques instead of stealing them here as they are still used for
         // normalization in the next generation trait solver.
@@ -558,17 +574,14 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
         for (opaque_type_key, hidden_type) in opaque_types {
             let hidden_type = self.resolve(hidden_type, &hidden_type.span);
             let opaque_type_key = self.resolve(opaque_type_key, &hidden_type.span);
-
-            if !self.fcx.next_trait_solver() {
-                if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind()
-                    && alias_ty.def_id == opaque_type_key.def_id.to_def_id()
-                    && alias_ty.args == opaque_type_key.args
-                {
-                    continue;
-                }
+            if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind()
+                && alias_ty.def_id == opaque_type_key.def_id.to_def_id()
+                && alias_ty.args == opaque_type_key.args
+            {
+                continue;
             }
 
-            if let Err(err) = check_opaque_type_parameter_valid(
+            if let Err(err) = opaque_type_has_defining_use_args(
                 &self.fcx,
                 opaque_type_key,
                 hidden_type.span,
@@ -923,6 +936,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
         }
     }
 
+    #[instrument(level = "debug", skip(self, outer_exclusive_binder, new_err))]
     fn handle_term<T>(
         &mut self,
         value: T,
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index 0230f784e46..4f87902e46e 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -4,7 +4,6 @@ use std::ops::ControlFlow;
 #[cfg(feature = "nightly")]
 use rustc_macros::HashStable_NoContext;
 use rustc_type_ir::data_structures::{HashMap, HashSet};
-use rustc_type_ir::fast_reject::DeepRejectCtxt;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::relate::Relate;
 use rustc_type_ir::relate::solver_relating::RelateExt;
@@ -1128,6 +1127,7 @@ where
         self.delegate.fetch_eligible_assoc_item(goal_trait_ref, trait_assoc_def_id, impl_def_id)
     }
 
+    #[instrument(level = "debug", skip(self), ret)]
     pub(super) fn register_hidden_type_in_storage(
         &mut self,
         opaque_type_key: ty::OpaqueTypeKey<I>,
@@ -1154,29 +1154,6 @@ where
         self.add_goals(GoalSource::AliasWellFormed, goals);
     }
 
-    // Do something for each opaque/hidden pair defined with `def_id` in the
-    // current inference context.
-    pub(super) fn probe_existing_opaque_ty(
-        &mut self,
-        key: ty::OpaqueTypeKey<I>,
-    ) -> Option<(ty::OpaqueTypeKey<I>, I::Ty)> {
-        // We shouldn't have any duplicate entries when using
-        // this function during `TypingMode::Analysis`.
-        let duplicate_entries = self.delegate.clone_duplicate_opaque_types();
-        assert!(duplicate_entries.is_empty(), "unexpected duplicates: {duplicate_entries:?}");
-        let mut matching = self.delegate.clone_opaque_types_lookup_table().into_iter().filter(
-            |(candidate_key, _)| {
-                candidate_key.def_id == key.def_id
-                    && DeepRejectCtxt::relate_rigid_rigid(self.cx())
-                        .args_may_unify(candidate_key.args, key.args)
-            },
-        );
-        let first = matching.next();
-        let second = matching.next();
-        assert_eq!(second, None);
-        first
-    }
-
     // Try to evaluate a const, or return `None` if the const is too generic.
     // This doesn't mean the const isn't evaluatable, though, and should be treated
     // as an ambiguity rather than no-solution.
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
index df3ad1e468b..a5f857a1dd8 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
@@ -1,13 +1,12 @@
 //! Computes a normalizes-to (projection) goal for opaque types. This goal
 //! behaves differently depending on the current `TypingMode`.
 
-use rustc_index::bit_set::GrowableBitSet;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::solve::GoalSource;
 use rustc_type_ir::{self as ty, Interner, TypingMode, fold_regions};
 
 use crate::delegate::SolverDelegate;
-use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult, inspect};
+use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult};
 
 impl<D, I> EvalCtxt<'_, D>
 where
@@ -39,100 +38,68 @@ where
                 self.add_goal(GoalSource::Misc, goal.with(cx, ty::PredicateKind::Ambiguous));
                 self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
             }
-            TypingMode::Analysis { defining_opaque_types_and_generators } => {
+            TypingMode::Analysis {
+                defining_opaque_types_and_generators: defining_opaque_types,
+            }
+            | TypingMode::Borrowck { defining_opaque_types } => {
                 let Some(def_id) = opaque_ty
                     .def_id
                     .as_local()
-                    .filter(|&def_id| defining_opaque_types_and_generators.contains(&def_id))
+                    .filter(|&def_id| defining_opaque_types.contains(&def_id))
                 else {
+                    // If we're not in the defining scope, treat the alias as rigid.
                     self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
                     return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
                 };
 
-                // FIXME: This may have issues when the args contain aliases...
-                match uses_unique_placeholders_ignoring_regions(self.cx(), opaque_ty.args) {
-                    Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
-                        return self.evaluate_added_goals_and_make_canonical_response(
-                            Certainty::AMBIGUOUS,
-                        );
-                    }
-                    Err(_) => {
-                        return Err(NoSolution);
-                    }
-                    Ok(()) => {}
-                }
-                // Prefer opaques registered already.
-                let opaque_type_key = ty::OpaqueTypeKey { def_id, args: opaque_ty.args };
-                // FIXME: This also unifies the previous hidden type with the expected.
+                // We structurally normalize the args so that we're able to detect defining uses
+                // later on.
                 //
-                // If that fails, we insert `expected` as a new hidden type instead of
-                // eagerly emitting an error.
-                let existing = self.probe_existing_opaque_ty(opaque_type_key);
-                if let Some((candidate_key, candidate_ty)) = existing {
-                    return self
-                        .probe(|result| inspect::ProbeKind::OpaqueTypeStorageLookup {
-                            result: *result,
-                        })
-                        .enter(|ecx| {
-                            for (a, b) in std::iter::zip(
-                                candidate_key.args.iter(),
-                                opaque_type_key.args.iter(),
-                            ) {
-                                ecx.eq(goal.param_env, a, b)?;
-                            }
-                            ecx.eq(goal.param_env, candidate_ty, expected)?;
-                            ecx.add_item_bounds_for_hidden_type(
-                                def_id.into(),
-                                candidate_key.args,
-                                goal.param_env,
-                                candidate_ty,
-                            );
-                            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-                        });
-                }
+                // This reduces the amount of duplicate definitions in the `opaque_type_storage` and
+                // strengthens inference. This causes us to subtly depend on the normalization behavior
+                // when inferring the hidden type of opaques.
+                //
+                // E.g. it's observable that we don't normalize nested aliases with bound vars in
+                // `structurally_normalize` and because we use structural lookup, we also don't
+                // reuse an entry for `Tait<for<'a> fn(&'a ())>` for `Tait<for<'b> fn(&'b ())>`.
+                let normalized_args =
+                    cx.mk_args_from_iter(opaque_ty.args.iter().map(|arg| match arg.kind() {
+                        ty::GenericArgKind::Lifetime(lt) => Ok(lt.into()),
+                        ty::GenericArgKind::Type(ty) => {
+                            self.structurally_normalize_ty(goal.param_env, ty).map(Into::into)
+                        }
+                        ty::GenericArgKind::Const(ct) => {
+                            self.structurally_normalize_const(goal.param_env, ct).map(Into::into)
+                        }
+                    }))?;
 
-                // Otherwise, define a new opaque type
-                let prev = self.register_hidden_type_in_storage(opaque_type_key, expected);
-                assert_eq!(prev, None);
-                self.add_item_bounds_for_hidden_type(
-                    def_id.into(),
-                    opaque_ty.args,
-                    goal.param_env,
-                    expected,
-                );
-                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-            }
-            // Very similar to `TypingMode::Analysis` with some notably differences:
-            // - we accept opaque types even if they have non-universal arguments
-            // - we do a structural lookup instead of semantically unifying regions
-            // - the hidden type starts out as the type from HIR typeck with fresh region
-            //   variables instead of a fully unconstrained inference variable
-            TypingMode::Borrowck { defining_opaque_types } => {
-                let Some(def_id) = opaque_ty
-                    .def_id
-                    .as_local()
-                    .filter(|&def_id| defining_opaque_types.contains(&def_id))
-                else {
-                    self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
-                    return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
-                };
+                let opaque_type_key = ty::OpaqueTypeKey { def_id, args: normalized_args };
+                if let Some(prev) = self.register_hidden_type_in_storage(opaque_type_key, expected)
+                {
+                    self.eq(goal.param_env, expected, prev)?;
+                } else {
+                    // During HIR typeck, opaque types start out as unconstrained
+                    // inference variables. In borrowck we instead use the type
+                    // computed in HIR typeck as the initial value.
+                    match self.typing_mode() {
+                        TypingMode::Analysis { .. } => {}
+                        TypingMode::Borrowck { .. } => {
+                            let actual = cx
+                                .type_of_opaque_hir_typeck(def_id)
+                                .instantiate(cx, opaque_ty.args);
+                            let actual = fold_regions(cx, actual, |re, _dbi| match re.kind() {
+                                ty::ReErased => self.next_region_var(),
+                                _ => re,
+                            });
+                            self.eq(goal.param_env, expected, actual)?;
+                        }
+                        _ => unreachable!(),
+                    }
+                }
 
-                let opaque_type_key = ty::OpaqueTypeKey { def_id, args: opaque_ty.args };
-                let actual = self
-                    .register_hidden_type_in_storage(opaque_type_key, expected)
-                    .unwrap_or_else(|| {
-                        let actual =
-                            cx.type_of_opaque_hir_typeck(def_id).instantiate(cx, opaque_ty.args);
-                        let actual = fold_regions(cx, actual, |re, _dbi| match re.kind() {
-                            ty::ReErased => self.next_region_var(),
-                            _ => re,
-                        });
-                        actual
-                    });
-                self.eq(goal.param_env, expected, actual)?;
                 self.add_item_bounds_for_hidden_type(
                     def_id.into(),
-                    opaque_ty.args,
+                    normalized_args,
                     goal.param_env,
                     expected,
                 );
@@ -168,44 +135,3 @@ where
         }
     }
 }
-
-/// Checks whether each generic argument is simply a unique generic placeholder.
-///
-/// FIXME: Interner argument is needed to constrain the `I` parameter.
-fn uses_unique_placeholders_ignoring_regions<I: Interner>(
-    _cx: I,
-    args: I::GenericArgs,
-) -> Result<(), NotUniqueParam<I>> {
-    let mut seen = GrowableBitSet::default();
-    for arg in args.iter() {
-        match arg.kind() {
-            // Ignore regions, since we can't resolve those in a canonicalized
-            // query in the trait solver.
-            ty::GenericArgKind::Lifetime(_) => {}
-            ty::GenericArgKind::Type(t) => match t.kind() {
-                ty::Placeholder(p) => {
-                    if !seen.insert(p.var()) {
-                        return Err(NotUniqueParam::DuplicateParam(t.into()));
-                    }
-                }
-                _ => return Err(NotUniqueParam::NotParam(t.into())),
-            },
-            ty::GenericArgKind::Const(c) => match c.kind() {
-                ty::ConstKind::Placeholder(p) => {
-                    if !seen.insert(p.var()) {
-                        return Err(NotUniqueParam::DuplicateParam(c.into()));
-                    }
-                }
-                _ => return Err(NotUniqueParam::NotParam(c.into())),
-            },
-        }
-    }
-
-    Ok(())
-}
-
-// FIXME: This should check for dupes and non-params first, then infer vars.
-enum NotUniqueParam<I: Interner> {
-    DuplicateParam(I::GenericArg),
-    NotParam(I::GenericArg),
-}
diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs
index 70a08f34f33..46bde670207 100644
--- a/compiler/rustc_trait_selection/src/opaque_types.rs
+++ b/compiler/rustc_trait_selection/src/opaque_types.rs
@@ -14,22 +14,22 @@ use crate::regions::OutlivesEnvironmentBuildExt;
 use crate::traits::ObligationCtxt;
 
 #[derive(Debug)]
-pub enum InvalidOpaqueTypeArgs<'tcx> {
-    AlreadyReported(ErrorGuaranteed),
+pub enum NonDefiningUseReason<'tcx> {
+    Tainted(ErrorGuaranteed),
     NotAParam { opaque_type_key: OpaqueTypeKey<'tcx>, param_index: usize, span: Span },
     DuplicateParam { opaque_type_key: OpaqueTypeKey<'tcx>, param_indices: Vec<usize>, span: Span },
 }
-impl From<ErrorGuaranteed> for InvalidOpaqueTypeArgs<'_> {
+impl From<ErrorGuaranteed> for NonDefiningUseReason<'_> {
     fn from(guar: ErrorGuaranteed) -> Self {
-        InvalidOpaqueTypeArgs::AlreadyReported(guar)
+        NonDefiningUseReason::Tainted(guar)
     }
 }
-impl<'tcx> InvalidOpaqueTypeArgs<'tcx> {
+impl<'tcx> NonDefiningUseReason<'tcx> {
     pub fn report(self, infcx: &InferCtxt<'tcx>) -> ErrorGuaranteed {
         let tcx = infcx.tcx;
         match self {
-            InvalidOpaqueTypeArgs::AlreadyReported(guar) => guar,
-            InvalidOpaqueTypeArgs::NotAParam { opaque_type_key, param_index, span } => {
+            NonDefiningUseReason::Tainted(guar) => guar,
+            NonDefiningUseReason::NotAParam { opaque_type_key, param_index, span } => {
                 let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
                 let opaque_param = opaque_generics.param_at(param_index, tcx);
                 let kind = opaque_param.kind.descr();
@@ -40,7 +40,7 @@ impl<'tcx> InvalidOpaqueTypeArgs<'tcx> {
                     param_span: tcx.def_span(opaque_param.def_id),
                 })
             }
-            InvalidOpaqueTypeArgs::DuplicateParam { opaque_type_key, param_indices, span } => {
+            NonDefiningUseReason::DuplicateParam { opaque_type_key, param_indices, span } => {
                 let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
                 let descr = opaque_generics.param_at(param_indices[0], tcx).kind.descr();
                 let spans: Vec<_> = param_indices
@@ -58,15 +58,17 @@ impl<'tcx> InvalidOpaqueTypeArgs<'tcx> {
 }
 
 /// Opaque type parameter validity check as documented in the [rustc-dev-guide chapter].
+/// With the new solver, uses which fail this check are simply treated as non-defining
+/// and we only emit an error if no defining use exists.
 ///
 /// [rustc-dev-guide chapter]:
 /// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
-pub fn check_opaque_type_parameter_valid<'tcx>(
+pub fn opaque_type_has_defining_use_args<'tcx>(
     infcx: &InferCtxt<'tcx>,
     opaque_type_key: OpaqueTypeKey<'tcx>,
     span: Span,
     defining_scope_kind: DefiningScopeKind,
-) -> Result<(), InvalidOpaqueTypeArgs<'tcx>> {
+) -> Result<(), NonDefiningUseReason<'tcx>> {
     let tcx = infcx.tcx;
     let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id);
     let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
@@ -105,13 +107,13 @@ pub fn check_opaque_type_parameter_valid<'tcx>(
         } else {
             // Prevent `fn foo() -> Foo<u32>` from being defining.
             opaque_env.param_is_error(i)?;
-            return Err(InvalidOpaqueTypeArgs::NotAParam { opaque_type_key, param_index: i, span });
+            return Err(NonDefiningUseReason::NotAParam { opaque_type_key, param_index: i, span });
         }
     }
 
     for (_, param_indices) in seen_params {
         if param_indices.len() > 1 {
-            return Err(InvalidOpaqueTypeArgs::DuplicateParam {
+            return Err(NonDefiningUseReason::DuplicateParam {
                 opaque_type_key,
                 param_indices,
                 span,
diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr
index d624fb1e42b..d6294efbd28 100644
--- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr
+++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.next.stderr
@@ -1,30 +1,14 @@
-error[E0283]: type annotations needed
-  --> $DIR/ambig-hr-projection-issue-93340.rs:17:5
+error[E0282]: type annotations needed
+  --> $DIR/ambig-hr-projection-issue-93340.rs:16:5
    |
 LL |     cmp_eq
    |     ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq`
    |
-   = note: cannot satisfy `_: Scalar`
-note: required by a bound in `cmp_eq`
-  --> $DIR/ambig-hr-projection-issue-93340.rs:10:22
-   |
-LL | fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefType<'b>) -> O {
-   |                      ^^^^^^ required by this bound in `cmp_eq`
 help: consider specifying the generic arguments
    |
 LL |     cmp_eq::<A, B, O>
    |           +++++++++++
 
-error[E0277]: expected a `Fn(<A as Scalar>::RefType<'_>, <B as Scalar>::RefType<'_>)` closure, found `for<'a, 'b> fn(<O as Scalar>::RefType<'a>, <_ as Scalar>::RefType<'b>) -> O {cmp_eq::<O, _, O>}`
-  --> $DIR/ambig-hr-projection-issue-93340.rs:14:1
-   |
-LL | / fn build_expression<A: Scalar, B: Scalar, O: Scalar>(
-LL | | ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O {
-   | |_________________________________________________^ expected an `Fn(<A as Scalar>::RefType<'_>, <B as Scalar>::RefType<'_>)` closure, found `for<'a, 'b> fn(<O as Scalar>::RefType<'a>, <_ as Scalar>::RefType<'b>) -> O {cmp_eq::<O, _, O>}`
-   |
-   = help: the trait `for<'a, 'b> Fn(<A as Scalar>::RefType<'a>, <B as Scalar>::RefType<'b>)` is not implemented for fn item `for<'a, 'b> fn(<O as Scalar>::RefType<'a>, <_ as Scalar>::RefType<'b>) -> O {cmp_eq::<O, _, O>}`
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
-Some errors have detailed explanations: E0277, E0283.
-For more information about an error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr
index 4a293d44e0e..d913b2e91ca 100644
--- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr
+++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.old.stderr
@@ -1,5 +1,5 @@
 error[E0283]: type annotations needed
-  --> $DIR/ambig-hr-projection-issue-93340.rs:17:5
+  --> $DIR/ambig-hr-projection-issue-93340.rs:16:5
    |
 LL |     cmp_eq
    |     ^^^^^^ cannot infer type of the type parameter `A` declared on the function `cmp_eq`
diff --git a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs
index 6ba3c4c65d0..acfebad38db 100644
--- a/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs
+++ b/tests/ui/generic-associated-types/ambig-hr-projection-issue-93340.rs
@@ -13,7 +13,6 @@ fn cmp_eq<'a, 'b, A: Scalar, B: Scalar, O: Scalar>(a: A::RefType<'a>, b: B::RefT
 
 fn build_expression<A: Scalar, B: Scalar, O: Scalar>(
 ) -> impl Fn(A::RefType<'_>, B::RefType<'_>) -> O {
-    //[next]~^^ ERROR expected a `Fn(<A as Scalar>::RefType<'_>, <B as Scalar>::RefType<'_>)` closure
     cmp_eq
     //~^ ERROR type annotations needed
 }
diff --git a/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr b/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr
index 5caf0eb2fd4..7170efc8638 100644
--- a/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr
+++ b/tests/ui/impl-trait/auto-trait-selection-freeze.next.stderr
@@ -1,17 +1,9 @@
-error[E0283]: type annotations needed
+error[E0282]: type annotations needed
   --> $DIR/auto-trait-selection-freeze.rs:19:16
    |
 LL |     if false { is_trait(foo()) } else { Default::default() }
-   |                ^^^^^^^^ ----- type must be known at this point
-   |                |
-   |                cannot infer type of the type parameter `T` declared on the function `is_trait`
+   |                ^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `is_trait`
    |
-   = note: cannot satisfy `_: Trait<_>`
-note: required by a bound in `is_trait`
-  --> $DIR/auto-trait-selection-freeze.rs:11:16
-   |
-LL | fn is_trait<T: Trait<U>, U: Default>(_: T) -> U {
-   |                ^^^^^^^^ required by this bound in `is_trait`
 help: consider specifying the generic arguments
    |
 LL |     if false { is_trait::<T, U>(foo()) } else { Default::default() }
@@ -19,4 +11,4 @@ LL |     if false { is_trait::<T, U>(foo()) } else { Default::default() }
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0283`.
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/impl-trait/auto-trait-selection.next.stderr b/tests/ui/impl-trait/auto-trait-selection.next.stderr
index d34fdcc4496..0f33aca3019 100644
--- a/tests/ui/impl-trait/auto-trait-selection.next.stderr
+++ b/tests/ui/impl-trait/auto-trait-selection.next.stderr
@@ -1,17 +1,9 @@
-error[E0283]: type annotations needed
+error[E0282]: type annotations needed
   --> $DIR/auto-trait-selection.rs:15:16
    |
 LL |     if false { is_trait(foo()) } else { Default::default() }
-   |                ^^^^^^^^ ----- type must be known at this point
-   |                |
-   |                cannot infer type of the type parameter `T` declared on the function `is_trait`
+   |                ^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `is_trait`
    |
-   = note: cannot satisfy `_: Trait<_>`
-note: required by a bound in `is_trait`
-  --> $DIR/auto-trait-selection.rs:7:16
-   |
-LL | fn is_trait<T: Trait<U>, U: Default>(_: T) -> U {
-   |                ^^^^^^^^ required by this bound in `is_trait`
 help: consider specifying the generic arguments
    |
 LL |     if false { is_trait::<T, U>(foo()) } else { Default::default() }
@@ -19,4 +11,4 @@ LL |     if false { is_trait::<T, U>(foo()) } else { Default::default() }
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0283`.
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/impl-trait/recursive-in-exhaustiveness.current.stderr b/tests/ui/impl-trait/recursive-in-exhaustiveness.current.stderr
index 080c3284641..11a88b0918c 100644
--- a/tests/ui/impl-trait/recursive-in-exhaustiveness.current.stderr
+++ b/tests/ui/impl-trait/recursive-in-exhaustiveness.current.stderr
@@ -11,7 +11,7 @@ LL | fn build2<T>(x: T) -> impl Sized {
    |                       ^^^^^^^^^^
 
 error[E0720]: cannot resolve opaque type
-  --> $DIR/recursive-in-exhaustiveness.rs:39:23
+  --> $DIR/recursive-in-exhaustiveness.rs:37:23
    |
 LL | fn build3<T>(x: T) -> impl Sized {
    |                       ^^^^^^^^^^
diff --git a/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr b/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr
index db57be73acc..45df8cc9c0c 100644
--- a/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr
+++ b/tests/ui/impl-trait/recursive-in-exhaustiveness.next.stderr
@@ -1,80 +1,21 @@
-error[E0284]: type annotations needed: cannot normalize `build<_>::{opaque#0}`
-  --> $DIR/recursive-in-exhaustiveness.rs:20:5
+error[E0282]: type annotations needed
+  --> $DIR/recursive-in-exhaustiveness.rs:19:17
    |
-LL |     build(x)
-   |     ^^^^^^^^ cannot normalize `build<_>::{opaque#0}`
+LL |     let (x,) = (build(x),);
+   |                 ^^^^^^^^ cannot infer type
 
-error[E0271]: type mismatch resolving `build2<(_,)>::{opaque#0} normalizes-to _`
-  --> $DIR/recursive-in-exhaustiveness.rs:30:6
+error[E0282]: type annotations needed
+  --> $DIR/recursive-in-exhaustiveness.rs:29:17
    |
-LL |     (build2(x),)
-   |      ^^^^^^^^^ types differ
+LL |     let (x,) = (build2(x),);
+   |                 ^^^^^^^^^ cannot infer type
 
-error[E0271]: type mismatch resolving `build2<(_,)>::{opaque#0} normalizes-to _`
-  --> $DIR/recursive-in-exhaustiveness.rs:30:5
+error[E0282]: type annotations needed
+  --> $DIR/recursive-in-exhaustiveness.rs:40:5
    |
-LL |     (build2(x),)
-   |     ^^^^^^^^^^^^ types differ
+LL |     build3(x)
+   |     ^^^^^^^^^ cannot infer type
 
-error[E0277]: the size for values of type `(impl Sized,)` cannot be known at compilation time
-  --> $DIR/recursive-in-exhaustiveness.rs:30:5
-   |
-LL |     (build2(x),)
-   |     ^^^^^^^^^^^^ doesn't have a size known at compile-time
-   |
-   = help: the trait `Sized` is not implemented for `(impl Sized,)`
-   = note: tuples must have a statically known size to be initialized
-
-error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _`
-  --> $DIR/recursive-in-exhaustiveness.rs:41:17
-   |
-LL |     let (x,) = (build3((x,)),);
-   |                 ^^^^^^^^^^^^ types differ
-
-error[E0277]: the size for values of type `(impl Sized,)` cannot be known at compilation time
-  --> $DIR/recursive-in-exhaustiveness.rs:41:16
-   |
-LL |     let (x,) = (build3((x,)),);
-   |                ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
-   |
-   = help: the trait `Sized` is not implemented for `(impl Sized,)`
-   = note: tuples must have a statically known size to be initialized
-
-error[E0308]: mismatched types
-  --> $DIR/recursive-in-exhaustiveness.rs:41:16
-   |
-LL | fn build3<T>(x: T) -> impl Sized {
-   |                       ---------- the found opaque type
-LL |
-LL |     let (x,) = (build3((x,)),);
-   |                ^^^^^^^^^^^^^^^ types differ
-   |
-   = note: expected type `_`
-             found tuple `(impl Sized,)`
-
-error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _`
-  --> $DIR/recursive-in-exhaustiveness.rs:41:17
-   |
-LL |     let (x,) = (build3((x,)),);
-   |                 ^^^^^^^^^^^^ types differ
-   |
-   = note: the return type of a function must have a statically known size
-
-error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _`
-  --> $DIR/recursive-in-exhaustiveness.rs:41:16
-   |
-LL |     let (x,) = (build3((x,)),);
-   |                ^^^^^^^^^^^^^^^ types differ
-
-error[E0271]: type mismatch resolving `build3<(T,)>::{opaque#0} normalizes-to _`
-  --> $DIR/recursive-in-exhaustiveness.rs:41:17
-   |
-LL |     let (x,) = (build3((x,)),);
-   |                 ^^^^^^^^^^^^ types differ
-   |
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: aborting due to 10 previous errors
+error: aborting due to 3 previous errors
 
-Some errors have detailed explanations: E0271, E0277, E0284, E0308.
-For more information about an error, try `rustc --explain E0271`.
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/impl-trait/recursive-in-exhaustiveness.rs b/tests/ui/impl-trait/recursive-in-exhaustiveness.rs
index dabef22af86..7aee8a630a5 100644
--- a/tests/ui/impl-trait/recursive-in-exhaustiveness.rs
+++ b/tests/ui/impl-trait/recursive-in-exhaustiveness.rs
@@ -17,8 +17,8 @@
 fn build<T>(x: T) -> impl Sized {
     //[current]~^ ERROR cannot resolve opaque type
     let (x,) = (build(x),);
+    //[next]~^ ERROR type annotations needed
     build(x)
-    //[next]~^ ERROR type annotations needed: cannot normalize `build<_>::{opaque#0}`
 }
 
 // Opaque<T> = (Opaque<T>,)
@@ -27,10 +27,8 @@ fn build<T>(x: T) -> impl Sized {
 fn build2<T>(x: T) -> impl Sized {
     //[current]~^ ERROR cannot resolve opaque type
     let (x,) = (build2(x),);
+    //[next]~^ ERROR type annotations needed
     (build2(x),)
-    //[next]~^ ERROR type mismatch resolving
-    //[next]~| ERROR type mismatch resolving
-    //[next]~| ERROR the size for values of type
 }
 
 // Opaque<T> = Opaque<(T,)>
@@ -39,13 +37,8 @@ fn build2<T>(x: T) -> impl Sized {
 fn build3<T>(x: T) -> impl Sized {
     //[current]~^ ERROR cannot resolve opaque type
     let (x,) = (build3((x,)),);
-    //[next]~^ ERROR type mismatch resolving
-    //[next]~| ERROR type mismatch resolving
-    //[next]~| ERROR type mismatch resolving
-    //[next]~| ERROR type mismatch resolving
-    //[next]~| ERROR the size for values of type
-    //[next]~| ERROR mismatched types
     build3(x)
+    //[next]~^ ERROR type annotations needed
 }
 
 fn main() {}
diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr
index fac4776905d..785e5fdeb64 100644
--- a/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr
+++ b/tests/ui/impl-trait/two_tait_defining_each_other2.next.stderr
@@ -1,9 +1,15 @@
 error[E0282]: type annotations needed
-  --> $DIR/two_tait_defining_each_other2.rs:12:11
+  --> $DIR/two_tait_defining_each_other2.rs:12:8
    |
 LL | fn muh(x: A) -> B {
-   |           ^ cannot infer type
+   |        ^ cannot infer type
 
-error: aborting due to 1 previous error
+error[E0282]: type annotations needed
+  --> $DIR/two_tait_defining_each_other2.rs:14:5
+   |
+LL |     x // B's hidden type is A (opaquely)
+   |     ^ cannot infer type
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/impl-trait/two_tait_defining_each_other2.rs b/tests/ui/impl-trait/two_tait_defining_each_other2.rs
index ec2963249f9..99262f4bc4b 100644
--- a/tests/ui/impl-trait/two_tait_defining_each_other2.rs
+++ b/tests/ui/impl-trait/two_tait_defining_each_other2.rs
@@ -12,7 +12,8 @@ trait Foo {}
 fn muh(x: A) -> B {
     //[next]~^ ERROR: type annotations needed
     x // B's hidden type is A (opaquely)
-    //[current]~^ ERROR opaque type's hidden type cannot be another opaque type
+    //[next]~^ ERROR: type annotations needed
+    //[current]~^^ ERROR opaque type's hidden type cannot be another opaque type
 }
 
 struct Bar;
diff --git a/tests/ui/traits/next-solver/opaques/different-bound-vars.current.stderr b/tests/ui/traits/next-solver/opaques/different-bound-vars.current.stderr
new file mode 100644
index 00000000000..32e92e46a88
--- /dev/null
+++ b/tests/ui/traits/next-solver/opaques/different-bound-vars.current.stderr
@@ -0,0 +1,14 @@
+error: concrete type differs from previous defining opaque type use
+  --> $DIR/different-bound-vars.rs:13:37
+   |
+LL |         let _: for<'b> fn(&'b ()) = foo::<U, T>(false);
+   |                                     ^^^^^^^^^^^^^^^^^^ expected `for<'a> fn(&'a ())`, got `for<'b> fn(&'b ())`
+   |
+note: previous use here
+  --> $DIR/different-bound-vars.rs:12:37
+   |
+LL |         let _: for<'a> fn(&'a ()) = foo::<T, U>(false);
+   |                                     ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/traits/next-solver/opaques/different-bound-vars.rs b/tests/ui/traits/next-solver/opaques/different-bound-vars.rs
new file mode 100644
index 00000000000..5801a5edeb4
--- /dev/null
+++ b/tests/ui/traits/next-solver/opaques/different-bound-vars.rs
@@ -0,0 +1,20 @@
+// Check whether we support defining uses with different bound vars.
+// This needs to handle both mismatches for the same opaque type storage
+// entry, but also between different entries.
+
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+//@[next] check-pass
+
+fn foo<T, U>(b: bool) -> impl Sized {
+    if b {
+        let _: for<'a> fn(&'a ()) = foo::<T, U>(false);
+        let _: for<'b> fn(&'b ()) = foo::<U, T>(false);
+        //[current]~^ ERROR concrete type differs from previous defining opaque type use
+    }
+
+    (|&()| ()) as for<'c> fn(&'c ())
+}
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr
index 6d2bbd8b08b..a188629a475 100644
--- a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr
+++ b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.is_send.stderr
@@ -1,16 +1,9 @@
-error[E0283]: type annotations needed: cannot satisfy `Foo: Send`
-  --> $DIR/dont-type_of-tait-in-defining-scope.rs:16:18
+error[E0282]: type annotations needed
+  --> $DIR/dont-type_of-tait-in-defining-scope.rs:15:12
    |
-LL |     needs_send::<Foo>();
-   |                  ^^^
-   |
-   = note: cannot satisfy `Foo: Send`
-note: required by a bound in `needs_send`
-  --> $DIR/dont-type_of-tait-in-defining-scope.rs:12:18
-   |
-LL | fn needs_send<T: Send>() {}
-   |                  ^^^^ required by this bound in `needs_send`
+LL | fn test(_: Foo) {
+   |            ^^^ cannot infer type
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0283`.
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr
index 6d2bbd8b08b..a188629a475 100644
--- a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr
+++ b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.not_send.stderr
@@ -1,16 +1,9 @@
-error[E0283]: type annotations needed: cannot satisfy `Foo: Send`
-  --> $DIR/dont-type_of-tait-in-defining-scope.rs:16:18
+error[E0282]: type annotations needed
+  --> $DIR/dont-type_of-tait-in-defining-scope.rs:15:12
    |
-LL |     needs_send::<Foo>();
-   |                  ^^^
-   |
-   = note: cannot satisfy `Foo: Send`
-note: required by a bound in `needs_send`
-  --> $DIR/dont-type_of-tait-in-defining-scope.rs:12:18
-   |
-LL | fn needs_send<T: Send>() {}
-   |                  ^^^^ required by this bound in `needs_send`
+LL | fn test(_: Foo) {
+   |            ^^^ cannot infer type
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0283`.
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.rs b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.rs
index fddf892e1ef..8ff99d32f06 100644
--- a/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.rs
+++ b/tests/ui/traits/next-solver/opaques/dont-type_of-tait-in-defining-scope.rs
@@ -13,8 +13,8 @@ fn needs_send<T: Send>() {}
 
 #[define_opaque(Foo)]
 fn test(_: Foo) {
-    needs_send::<Foo>();
     //~^ ERROR type annotations needed
+    needs_send::<Foo>();
 }
 
 #[define_opaque(Foo)]
diff --git a/tests/ui/traits/next-solver/opaques/non-defining-use-hir-typeck.rs b/tests/ui/traits/next-solver/opaques/non-defining-use-hir-typeck.rs
new file mode 100644
index 00000000000..a55be5fde9e
--- /dev/null
+++ b/tests/ui/traits/next-solver/opaques/non-defining-use-hir-typeck.rs
@@ -0,0 +1,28 @@
+//@ ignore-compare-mode-next-solver
+//@ compile-flags: -Znext-solver
+//@ check-pass
+#![feature(type_alias_impl_trait)]
+
+// Make sure that we support non-defining uses in HIR typeck.
+// Regression test for trait-system-refactor-initiative#135.
+
+fn non_defining_recurse<T>(b: bool) -> impl Sized {
+    if b {
+        // This results in an opaque type use `opaque<()> = ?unconstrained`
+        // during HIR typeck.
+        non_defining_recurse::<()>(false);
+    }
+}
+
+trait Eq<T, U> {}
+impl<T> Eq<T, T> for () {}
+fn is_eq<T: Eq<U, V>, U, V>() {}
+type Tait<T> = impl Sized;
+#[define_opaque(Tait)]
+fn non_defining_explicit<T>() {
+    is_eq::<(), Tait<_>, u32>(); // constrains opaque type args via hidden type
+    is_eq::<(), Tait<u64>, _>(); // constraints hidden type via args
+    is_eq::<(), Tait<T>, T>(); // actually defines
+}
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs b/tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs
new file mode 100644
index 00000000000..5e7e9738616
--- /dev/null
+++ b/tests/ui/traits/next-solver/opaques/universal-args-non-defining.rs
@@ -0,0 +1,16 @@
+//@ check-pass
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@[next] compile-flags: -Znext-solver
+
+// The recursive call to `foo` results in the opaque type use `opaque<U, T> = ?unconstrained`.
+// This needs to be supported and treated as a revealing use.
+
+fn foo<T, U>(b: bool) -> impl Sized {
+    if b {
+        foo::<U, T>(b);
+    }
+    1u16
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr b/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr
index 72a253c4be8..b50d1b60c43 100644
--- a/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr
+++ b/tests/ui/type-alias-impl-trait/constrain_in_projection2.next.stderr
@@ -1,24 +1,9 @@
-error[E0283]: type annotations needed: cannot satisfy `Foo: Trait<Bar>`
-  --> $DIR/constrain_in_projection2.rs:28:14
+error[E0282]: type annotations needed
+  --> $DIR/constrain_in_projection2.rs:28:13
    |
 LL |     let x = <Foo as Trait<Bar>>::Assoc::default();
-   |              ^^^
-   |
-note: multiple `impl`s satisfying `Foo: Trait<Bar>` found
-  --> $DIR/constrain_in_projection2.rs:18:1
-   |
-LL | impl Trait<()> for Foo {
-   | ^^^^^^^^^^^^^^^^^^^^^^
-...
-LL | impl Trait<u32> for Foo {
-   | ^^^^^^^^^^^^^^^^^^^^^^^
-   = note: associated types cannot be accessed directly on a `trait`, they can only be accessed through a specific `impl`
-help: use the fully qualified path to an implementation
-   |
-LL -     let x = <Foo as Trait<Bar>>::Assoc::default();
-LL +     let x = <<Type as Trait>::Assoc as Trait<Bar>>::Assoc::default();
-   |
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0283`.
+For more information about this error, try `rustc --explain E0282`.
diff --git a/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs b/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs
index 61773cf59d4..c4aa6f32eab 100644
--- a/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs
+++ b/tests/ui/type-alias-impl-trait/constrain_in_projection2.rs
@@ -26,7 +26,7 @@ impl Trait<u32> for Foo {
 #[define_opaque(Bar)]
 fn bop() {
     let x = <Foo as Trait<Bar>>::Assoc::default();
-    //[next]~^ ERROR: cannot satisfy `Foo: Trait<Bar>`
+    //[next]~^ ERROR: type annotations needed
     //[current]~^^ ERROR: `Foo: Trait<Bar>` is not satisfied
     //[current]~| ERROR: `Foo: Trait<Bar>` is not satisfied
 }
diff --git a/tests/ui/type-alias-impl-trait/normalize-args-before-defining-use-check.rs b/tests/ui/type-alias-impl-trait/normalize-args-before-defining-use-check.rs
new file mode 100644
index 00000000000..2879b8fe94c
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/normalize-args-before-defining-use-check.rs
@@ -0,0 +1,33 @@
+#![feature(type_alias_impl_trait)]
+
+//@ revisions: current next
+//@ ignore-compare-mode-next-solver (explicit revisions)
+//@ [next] compile-flags: -Znext-solver
+//@ check-pass
+
+// Regression test for trait-system-refactor-initiative#49.
+
+trait Mirror<'a> {
+    type Assoc;
+}
+impl<'a, T> Mirror<'a> for T {
+    type Assoc = T;
+}
+
+type HrAmbigAlias<T> = impl Sized;
+fn ret_tait<T>() -> for<'a> fn(HrAmbigAlias<<T as Mirror<'a>>::Assoc>) {
+    |_| ()
+}
+
+#[define_opaque(HrAmbigAlias)]
+fn define_hr_ambig_alias<T>() {
+    let _: fn(T) = ret_tait::<T>();
+}
+
+type InUserType<T> = impl Sized;
+#[define_opaque(InUserType)]
+fn in_user_type<T>() {
+    let x: InUserType<<T as Mirror<'static>>::Assoc> = ();
+}
+
+fn main() {}