From f58af9ba28f1bb49f880da43227c65cc10673be2 Mon Sep 17 00:00:00 2001
From: Oli Scherer <git-spam-no-reply9815368754983@oli-obk.de>
Date: Tue, 9 Jan 2024 11:12:30 +0000
Subject: [PATCH 1/6] Add a simpler and more targetted code path for impl trait
 in assoc items

---
 .../rustc_hir_analysis/src/collect/type_of.rs |  6 +-
 .../src/collect/type_of/opaque.rs             | 71 +++++++++++++++++--
 compiler/rustc_middle/src/query/mod.rs        |  9 +++
 compiler/rustc_ty_utils/src/opaque_types.rs   | 10 ++-
 4 files changed, 88 insertions(+), 8 deletions(-)

diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index bfa9dc42422cb..3674a760cbf9d 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -530,9 +530,13 @@ pub(super) fn type_of_opaque(
         Ok(ty::EarlyBinder::bind(match tcx.hir_node_by_def_id(def_id) {
             Node::Item(item) => match item.kind {
                 ItemKind::OpaqueTy(OpaqueTy {
-                    origin: hir::OpaqueTyOrigin::TyAlias { .. },
+                    origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false },
                     ..
                 }) => opaque::find_opaque_ty_constraints_for_tait(tcx, def_id),
+                ItemKind::OpaqueTy(OpaqueTy {
+                    origin: hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: true },
+                    ..
+                }) => opaque::find_opaque_ty_constraints_for_impl_trait_in_assoc_type(tcx, def_id),
                 // Opaque types desugared from `impl Trait`.
                 ItemKind::OpaqueTy(&OpaqueTy {
                     origin:
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 85093bc12b378..51b2401531c7a 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
@@ -23,6 +23,60 @@ pub fn test_opaque_hidden_types(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed>
     res
 }
 
+/// Checks "defining uses" of opaque `impl Trait` in associated types.
+/// These can only be defined by associated items of the same trait.
+#[instrument(skip(tcx), level = "debug")]
+pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
+    tcx: TyCtxt<'_>,
+    def_id: LocalDefId,
+) -> Ty<'_> {
+    let mut parent_def_id = def_id;
+    while tcx.def_kind(parent_def_id) == def::DefKind::OpaqueTy {
+        // Account for `type Alias = impl Trait<Foo = impl Trait>;` (#116031)
+        parent_def_id = tcx.local_parent(parent_def_id);
+    }
+    let impl_def_id = tcx.local_parent(parent_def_id);
+    match tcx.def_kind(impl_def_id) {
+        DefKind::Impl { .. } => {}
+        other => bug!("invalid impl trait in assoc type parent: {other:?}"),
+    }
+
+    let mut locator = TaitConstraintLocator { def_id, tcx, found: None, typeck_types: vec![] };
+
+    for &assoc_id in tcx.associated_item_def_ids(impl_def_id) {
+        let assoc = tcx.associated_item(assoc_id);
+        match assoc.kind {
+            ty::AssocKind::Const | ty::AssocKind::Fn => {
+                locator.check(assoc_id.expect_local(), true)
+            }
+            // Associated types don't have bodies, so they can't constrain hidden types
+            ty::AssocKind::Type => {}
+        }
+    }
+
+    if let Some(hidden) = locator.found {
+        // Only check against typeck if we didn't already error
+        if !hidden.ty.references_error() {
+            for concrete_type in locator.typeck_types {
+                if concrete_type.ty != tcx.erase_regions(hidden.ty)
+                    && !(concrete_type, hidden).references_error()
+                {
+                    hidden.report_mismatch(&concrete_type, def_id, tcx).emit();
+                }
+            }
+        }
+
+        hidden.ty
+    } else {
+        let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType {
+            span: tcx.def_span(def_id),
+            name: tcx.item_name(parent_def_id.to_def_id()),
+            what: "impl",
+        });
+        Ty::new_error(tcx, reported)
+    }
+}
+
 /// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions
 /// laid for "higher-order pattern unification".
 /// This ensures that inference is tractable.
@@ -130,7 +184,7 @@ struct TaitConstraintLocator<'tcx> {
 
 impl TaitConstraintLocator<'_> {
     #[instrument(skip(self), level = "debug")]
-    fn check(&mut self, item_def_id: LocalDefId) {
+    fn check(&mut self, item_def_id: LocalDefId, impl_trait_in_assoc_type: bool) {
         // Don't try to check items that cannot possibly constrain the type.
         if !self.tcx.has_typeck_results(item_def_id) {
             debug!("no constraint: no typeck results");
@@ -182,7 +236,12 @@ impl TaitConstraintLocator<'_> {
                 continue;
             }
             constrained = true;
-            if !self.tcx.opaque_types_defined_by(item_def_id).contains(&self.def_id) {
+            let opaque_types_defined_by = if impl_trait_in_assoc_type {
+                self.tcx.impl_trait_in_assoc_types_defined_by(item_def_id)
+            } else {
+                self.tcx.opaque_types_defined_by(item_def_id)
+            };
+            if !opaque_types_defined_by.contains(&self.def_id) {
                 self.tcx.dcx().emit_err(TaitForwardCompat {
                     span: hidden_type.span,
                     item_span: self
@@ -240,7 +299,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
     }
     fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
         if let hir::ExprKind::Closure(closure) = ex.kind {
-            self.check(closure.def_id);
+            self.check(closure.def_id, false);
         }
         intravisit::walk_expr(self, ex);
     }
@@ -248,7 +307,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
         trace!(?it.owner_id);
         // The opaque type itself or its children are not within its reveal scope.
         if it.owner_id.def_id != self.def_id {
-            self.check(it.owner_id.def_id);
+            self.check(it.owner_id.def_id, false);
             intravisit::walk_item(self, it);
         }
     }
@@ -256,13 +315,13 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
         trace!(?it.owner_id);
         // The opaque type itself or its children are not within its reveal scope.
         if it.owner_id.def_id != self.def_id {
-            self.check(it.owner_id.def_id);
+            self.check(it.owner_id.def_id, false);
             intravisit::walk_impl_item(self, it);
         }
     }
     fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
         trace!(?it.owner_id);
-        self.check(it.owner_id.def_id);
+        self.check(it.owner_id.def_id, false);
         intravisit::walk_trait_item(self, it);
     }
     fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index a9dc7f5d11a20..23daefd5a65b9 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -343,6 +343,15 @@ rustc_queries! {
         }
     }
 
+    query impl_trait_in_assoc_types_defined_by(
+        key: LocalDefId
+    ) -> &'tcx ty::List<LocalDefId> {
+        desc {
+            |tcx| "computing the opaque types defined by `{}`",
+            tcx.def_path_str(key.to_def_id())
+        }
+    }
+
     /// Returns the list of bounds that can be used for
     /// `SelectionCandidate::ProjectionCandidate(_)` and
     /// `ProjectionTyCandidate::TraitDef`.
diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs
index 0bb833c66fa76..eae4c57fc9989 100644
--- a/compiler/rustc_ty_utils/src/opaque_types.rs
+++ b/compiler/rustc_ty_utils/src/opaque_types.rs
@@ -272,6 +272,13 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
     }
 }
 
+fn impl_trait_in_assoc_types_defined_by<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    item: LocalDefId,
+) -> &'tcx ty::List<LocalDefId> {
+    opaque_types_defined_by(tcx, item)
+}
+
 fn opaque_types_defined_by<'tcx>(
     tcx: TyCtxt<'tcx>,
     item: LocalDefId,
@@ -321,5 +328,6 @@ fn opaque_types_defined_by<'tcx>(
 }
 
 pub(super) fn provide(providers: &mut Providers) {
-    *providers = Providers { opaque_types_defined_by, ..*providers };
+    *providers =
+        Providers { opaque_types_defined_by, impl_trait_in_assoc_types_defined_by, ..*providers };
 }

From ac332bd9163990491d225e2a24ff92af89030dac Mon Sep 17 00:00:00 2001
From: Oli Scherer <git-spam-no-reply9815368754983@oli-obk.de>
Date: Tue, 9 Jan 2024 11:16:32 +0000
Subject: [PATCH 2/6] Pull opaque type check into a separate method

---
 compiler/rustc_ty_utils/src/opaque_types.rs | 125 ++++++++++----------
 1 file changed, 64 insertions(+), 61 deletions(-)

diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs
index eae4c57fc9989..1d9c0405b7d76 100644
--- a/compiler/rustc_ty_utils/src/opaque_types.rs
+++ b/compiler/rustc_ty_utils/src/opaque_types.rs
@@ -118,6 +118,69 @@ impl<'tcx> OpaqueTypeCollector<'tcx> {
         }
         TaitInBodyFinder { collector: self }.visit_expr(body);
     }
+
+    fn visit_opaque_ty(&mut self, alias_ty: &ty::AliasTy<'tcx>) {
+        if !self.seen.insert(alias_ty.def_id.expect_local()) {
+            return;
+        }
+
+        // TAITs outside their defining scopes are ignored.
+        let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local());
+        trace!(?origin);
+        match origin {
+            rustc_hir::OpaqueTyOrigin::FnReturn(_) | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {}
+            rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => {
+                if !in_assoc_ty {
+                    if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
+                        return;
+                    }
+                }
+            }
+        }
+
+        self.opaques.push(alias_ty.def_id.expect_local());
+
+        let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count;
+        // Only check that the parent generics of the TAIT/RPIT are unique.
+        // the args owned by the opaque are going to always be duplicate
+        // lifetime params for RPITs, and empty for TAITs.
+        match self
+            .tcx
+            .uses_unique_generic_params(&alias_ty.args[..parent_count], CheckRegions::FromFunction)
+        {
+            Ok(()) => {
+                // FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not
+                // supported at all, so this is sound to do, but once we want to support them, you'll
+                // start seeing the error below.
+
+                // Collect opaque types nested within the associated type bounds of this opaque type.
+                // We use identity args here, because we already know that the opaque type uses
+                // only generic parameters, and thus substituting would not give us more information.
+                for (pred, span) in self
+                    .tcx
+                    .explicit_item_bounds(alias_ty.def_id)
+                    .instantiate_identity_iter_copied()
+                {
+                    trace!(?pred);
+                    self.visit_spanned(span, pred);
+                }
+            }
+            Err(NotUniqueParam::NotParam(arg)) => {
+                self.tcx.dcx().emit_err(NotParam {
+                    arg,
+                    span: self.span(),
+                    opaque_span: self.tcx.def_span(alias_ty.def_id),
+                });
+            }
+            Err(NotUniqueParam::DuplicateParam(arg)) => {
+                self.tcx.dcx().emit_err(DuplicateArg {
+                    arg,
+                    span: self.span(),
+                    opaque_span: self.tcx.def_span(alias_ty.def_id),
+                });
+            }
+        }
+    }
 }
 
 impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for OpaqueTypeCollector<'tcx> {
@@ -134,67 +197,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
         t.super_visit_with(self)?;
         match t.kind() {
             ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
-                if !self.seen.insert(alias_ty.def_id.expect_local()) {
-                    return ControlFlow::Continue(());
-                }
-
-                // TAITs outside their defining scopes are ignored.
-                let origin = self.tcx.opaque_type_origin(alias_ty.def_id.expect_local());
-                trace!(?origin);
-                match origin {
-                    rustc_hir::OpaqueTyOrigin::FnReturn(_)
-                    | rustc_hir::OpaqueTyOrigin::AsyncFn(_) => {}
-                    rustc_hir::OpaqueTyOrigin::TyAlias { in_assoc_ty } => {
-                        if !in_assoc_ty {
-                            if !self.check_tait_defining_scope(alias_ty.def_id.expect_local()) {
-                                return ControlFlow::Continue(());
-                            }
-                        }
-                    }
-                }
-
-                self.opaques.push(alias_ty.def_id.expect_local());
-
-                let parent_count = self.tcx.generics_of(alias_ty.def_id).parent_count;
-                // Only check that the parent generics of the TAIT/RPIT are unique.
-                // the args owned by the opaque are going to always be duplicate
-                // lifetime params for RPITs, and empty for TAITs.
-                match self.tcx.uses_unique_generic_params(
-                    &alias_ty.args[..parent_count],
-                    CheckRegions::FromFunction,
-                ) {
-                    Ok(()) => {
-                        // FIXME: implement higher kinded lifetime bounds on nested opaque types. They are not
-                        // supported at all, so this is sound to do, but once we want to support them, you'll
-                        // start seeing the error below.
-
-                        // Collect opaque types nested within the associated type bounds of this opaque type.
-                        // We use identity args here, because we already know that the opaque type uses
-                        // only generic parameters, and thus substituting would not give us more information.
-                        for (pred, span) in self
-                            .tcx
-                            .explicit_item_bounds(alias_ty.def_id)
-                            .instantiate_identity_iter_copied()
-                        {
-                            trace!(?pred);
-                            self.visit_spanned(span, pred);
-                        }
-                    }
-                    Err(NotUniqueParam::NotParam(arg)) => {
-                        self.tcx.dcx().emit_err(NotParam {
-                            arg,
-                            span: self.span(),
-                            opaque_span: self.tcx.def_span(alias_ty.def_id),
-                        });
-                    }
-                    Err(NotUniqueParam::DuplicateParam(arg)) => {
-                        self.tcx.dcx().emit_err(DuplicateArg {
-                            arg,
-                            span: self.span(),
-                            opaque_span: self.tcx.def_span(alias_ty.def_id),
-                        });
-                    }
-                }
+                self.visit_opaque_ty(alias_ty);
             }
             ty::Alias(ty::Weak, alias_ty) if alias_ty.def_id.is_local() => {
                 self.tcx

From f75361fec7aecfcede9cf6f0b97f2b1e7756e47b Mon Sep 17 00:00:00 2001
From: Oli Scherer <git-spam-no-reply9815368754983@oli-obk.de>
Date: Tue, 9 Jan 2024 11:28:05 +0000
Subject: [PATCH 3/6] Limit impl trait in assoc type defining scope

---
 compiler/rustc_ty_utils/src/opaque_types.rs   | 80 ++++++++++++++++++-
 .../hidden_behind_struct_field2.rs            |  8 +-
 .../hidden_behind_struct_field2.stderr        | 15 ++++
 3 files changed, 98 insertions(+), 5 deletions(-)
 create mode 100644 tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr

diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs
index 1d9c0405b7d76..ef67317a601ac 100644
--- a/compiler/rustc_ty_utils/src/opaque_types.rs
+++ b/compiler/rustc_ty_utils/src/opaque_types.rs
@@ -275,11 +275,89 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector<'tcx> {
     }
 }
 
+struct ImplTraitInAssocTypeCollector<'tcx>(OpaqueTypeCollector<'tcx>);
+
+impl<'tcx> super::sig_types::SpannedTypeVisitor<'tcx> for ImplTraitInAssocTypeCollector<'tcx> {
+    #[instrument(skip(self), ret, level = "trace")]
+    fn visit(&mut self, span: Span, value: impl TypeVisitable<TyCtxt<'tcx>>) -> ControlFlow<!> {
+        let old = self.0.span;
+        self.0.span = Some(span);
+        value.visit_with(self);
+        self.0.span = old;
+
+        ControlFlow::Continue(())
+    }
+}
+
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInAssocTypeCollector<'tcx> {
+    #[instrument(skip(self), ret, level = "trace")]
+    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<!> {
+        t.super_visit_with(self)?;
+        match t.kind() {
+            ty::Alias(ty::Opaque, alias_ty) if alias_ty.def_id.is_local() => {
+                self.0.visit_opaque_ty(alias_ty);
+            }
+            ty::Alias(ty::Projection, alias_ty) => {
+                // This avoids having to do normalization of `Self::AssocTy` by only
+                // supporting the case of a method defining opaque types from assoc types
+                // in the same impl block.
+                let parent_trait_ref = self
+                    .0
+                    .parent_trait_ref()
+                    .expect("impl trait in assoc type collector used on non-assoc item");
+                // If the trait ref of the associated item and the impl differs,
+                // then we can't use the impl's identity substitutions below, so
+                // just skip.
+                if alias_ty.trait_ref(self.0.tcx) == parent_trait_ref {
+                    let parent = self.0.parent().expect("we should have a parent here");
+
+                    for &assoc in self.0.tcx.associated_items(parent).in_definition_order() {
+                        trace!(?assoc);
+                        if assoc.trait_item_def_id != Some(alias_ty.def_id) {
+                            continue;
+                        }
+
+                        // If the type is further specializable, then the type_of
+                        // is not actually correct below.
+                        if !assoc.defaultness(self.0.tcx).is_final() {
+                            continue;
+                        }
+
+                        let impl_args = alias_ty.args.rebase_onto(
+                            self.0.tcx,
+                            parent_trait_ref.def_id,
+                            ty::GenericArgs::identity_for_item(self.0.tcx, parent),
+                        );
+
+                        if check_args_compatible(self.0.tcx, assoc, impl_args) {
+                            return self
+                                .0
+                                .tcx
+                                .type_of(assoc.def_id)
+                                .instantiate(self.0.tcx, impl_args)
+                                .visit_with(self);
+                        } else {
+                            self.0.tcx.dcx().span_delayed_bug(
+                                self.0.tcx.def_span(assoc.def_id),
+                                "item had incorrect args",
+                            );
+                        }
+                    }
+                }
+            }
+            _ => trace!(kind=?t.kind()),
+        }
+        ControlFlow::Continue(())
+    }
+}
+
 fn impl_trait_in_assoc_types_defined_by<'tcx>(
     tcx: TyCtxt<'tcx>,
     item: LocalDefId,
 ) -> &'tcx ty::List<LocalDefId> {
-    opaque_types_defined_by(tcx, item)
+    let mut collector = ImplTraitInAssocTypeCollector(OpaqueTypeCollector::new(tcx, item));
+    super::sig_types::walk_types(tcx, item, &mut collector);
+    tcx.mk_local_def_ids(&collector.0.opaques)
 }
 
 fn opaque_types_defined_by<'tcx>(
diff --git a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs
index e440dce5e514f..4c881dd133086 100644
--- a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs
+++ b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.rs
@@ -1,9 +1,8 @@
-//! This test shows that we can even follow projections
-//! into associated types of the same impl if they are
-//! indirectly mentioned in a struct field.
+//! This test shows that we do not treat opaque types
+//! as defined by a method if the opaque type is
+//! only indirectly mentioned in a struct field.
 
 #![feature(impl_trait_in_assoc_type)]
-// check-pass
 
 struct Bar;
 
@@ -16,6 +15,7 @@ impl Trait for Bar {
     type Assoc = impl std::fmt::Debug;
     fn foo() -> Foo {
         Foo { field: () }
+        //~^ ERROR: item constrains opaque type that is not in its signature
     }
 }
 
diff --git a/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr
new file mode 100644
index 0000000000000..5c53dfa3a7500
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/hidden_behind_struct_field2.stderr
@@ -0,0 +1,15 @@
+error: item constrains opaque type that is not in its signature
+  --> $DIR/hidden_behind_struct_field2.rs:17:22
+   |
+LL |         Foo { field: () }
+   |                      ^^
+   |
+   = note: this item must mention the opaque type in its signature in order to be able to register hidden types
+note: this item must mention the opaque type in its signature in order to be able to register hidden types
+  --> $DIR/hidden_behind_struct_field2.rs:16:8
+   |
+LL |     fn foo() -> Foo {
+   |        ^^^
+
+error: aborting due to 1 previous error
+

From 4e0769956bf5c966fccb54acbb0866f029c79818 Mon Sep 17 00:00:00 2001
From: Oli Scherer <git-spam-no-reply9815368754983@oli-obk.de>
Date: Tue, 9 Jan 2024 14:12:59 +0000
Subject: [PATCH 4/6] Add some tests

---
 tests/ui/impl-trait/rpit/early_bound.rs       | 13 ++++++
 tests/ui/impl-trait/rpit/early_bound.stderr   | 26 ++++++++++++
 ...pl_trait_in_trait_defined_outside_trait.rs | 38 +++++++++++++++++
 ...rait_in_trait_defined_outside_trait.stderr | 41 +++++++++++++++++++
 ...l_trait_in_trait_defined_outside_trait2.rs | 22 ++++++++++
 ...ait_in_trait_defined_outside_trait2.stderr | 17 ++++++++
 ...l_trait_in_trait_defined_outside_trait3.rs | 38 +++++++++++++++++
 .../in-assoc-ty-early-bound.rs                | 17 ++++++++
 .../in-assoc-ty-early-bound.stderr            | 13 ++++++
 .../in-assoc-ty-early-bound2.rs               | 21 ++++++++++
 .../in-assoc-ty-early-bound2.stderr           | 22 ++++++++++
 11 files changed, 268 insertions(+)
 create mode 100644 tests/ui/impl-trait/rpit/early_bound.rs
 create mode 100644 tests/ui/impl-trait/rpit/early_bound.stderr
 create mode 100644 tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.rs
 create mode 100644 tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr
 create mode 100644 tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.rs
 create mode 100644 tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.stderr
 create mode 100644 tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait3.rs
 create mode 100644 tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs
 create mode 100644 tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr
 create mode 100644 tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs
 create mode 100644 tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr

diff --git a/tests/ui/impl-trait/rpit/early_bound.rs b/tests/ui/impl-trait/rpit/early_bound.rs
new file mode 100644
index 0000000000000..03bd64d4d76d0
--- /dev/null
+++ b/tests/ui/impl-trait/rpit/early_bound.rs
@@ -0,0 +1,13 @@
+use std::convert::identity;
+
+fn test<'a: 'a>(n: bool) -> impl Sized + 'a {
+    //~^ ERROR concrete type differs from previous defining opaque type use
+    let true = n else { loop {} };
+    let _ = || {
+        let _ = identity::<&'a ()>(test(false));
+        //~^ ERROR hidden type for `impl Sized + 'a` captures lifetime that does not appear in bounds
+    };
+    loop {}
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/rpit/early_bound.stderr b/tests/ui/impl-trait/rpit/early_bound.stderr
new file mode 100644
index 0000000000000..815368f250e35
--- /dev/null
+++ b/tests/ui/impl-trait/rpit/early_bound.stderr
@@ -0,0 +1,26 @@
+error[E0700]: hidden type for `impl Sized + 'a` captures lifetime that does not appear in bounds
+  --> $DIR/early_bound.rs:7:17
+   |
+LL | fn test<'a: 'a>(n: bool) -> impl Sized + 'a {
+   |         --                  --------------- opaque type defined here
+   |         |
+   |         hidden type `&'a ()` captures the lifetime `'a` as defined here
+...
+LL |         let _ = identity::<&'a ()>(test(false));
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: concrete type differs from previous defining opaque type use
+  --> $DIR/early_bound.rs:3:29
+   |
+LL | fn test<'a: 'a>(n: bool) -> impl Sized + 'a {
+   |                             ^^^^^^^^^^^^^^^ expected `&()`, got `()`
+   |
+note: previous use here
+  --> $DIR/early_bound.rs:7:36
+   |
+LL |         let _ = identity::<&'a ()>(test(false));
+   |                                    ^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0700`.
diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.rs b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.rs
new file mode 100644
index 0000000000000..a788563ab779e
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.rs
@@ -0,0 +1,38 @@
+//! Check that we cannot instantiate a hidden type in the body
+//! of an assoc fn or const unless mentioned in the signature.
+
+#![feature(impl_trait_in_assoc_type)]
+
+trait Trait: Sized {
+    type Assoc;
+    fn foo();
+    fn bar() -> Self::Assoc;
+}
+
+impl Trait for () {
+    type Assoc = impl std::fmt::Debug;
+    fn foo() {
+        let x: Self::Assoc = 42; //~ ERROR: mismatched types
+    }
+    fn bar() -> Self::Assoc {
+        ""
+    }
+}
+
+trait Trait2: Sized {
+    type Assoc;
+    const FOO: ();
+    fn bar() -> Self::Assoc;
+}
+
+impl Trait2 for () {
+    type Assoc = impl std::fmt::Debug;
+    const FOO: () = {
+        let x: Self::Assoc = 42; //~ ERROR: mismatched types
+    };
+    fn bar() -> Self::Assoc {
+        ""
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr
new file mode 100644
index 0000000000000..1d7a97c536713
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait.stderr
@@ -0,0 +1,41 @@
+error[E0308]: mismatched types
+  --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:15:30
+   |
+LL |     type Assoc = impl std::fmt::Debug;
+   |                  -------------------- the expected opaque type
+LL |     fn foo() {
+LL |         let x: Self::Assoc = 42;
+   |                -----------   ^^ expected opaque type, found integer
+   |                |
+   |                expected due to this
+   |
+   = note: expected opaque type `<() as Trait>::Assoc`
+                     found type `{integer}`
+note: this item must have the opaque type in its signature in order to be able to register hidden types
+  --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:14:8
+   |
+LL |     fn foo() {
+   |        ^^^
+
+error[E0308]: mismatched types
+  --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:31:30
+   |
+LL |     type Assoc = impl std::fmt::Debug;
+   |                  -------------------- the expected opaque type
+LL |     const FOO: () = {
+LL |         let x: Self::Assoc = 42;
+   |                -----------   ^^ expected opaque type, found integer
+   |                |
+   |                expected due to this
+   |
+   = note: expected opaque type `<() as Trait2>::Assoc`
+                     found type `{integer}`
+note: this item must have the opaque type in its signature in order to be able to register hidden types
+  --> $DIR/impl_trait_in_trait_defined_outside_trait.rs:30:11
+   |
+LL |     const FOO: () = {
+   |           ^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.rs b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.rs
new file mode 100644
index 0000000000000..77cdca198daea
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.rs
@@ -0,0 +1,22 @@
+//! Check that we cannot instantiate a hidden type from another assoc type.
+
+#![feature(impl_trait_in_assoc_type)]
+
+trait Trait: Sized {
+    type Assoc;
+    type Foo;
+    fn foo() -> Self::Assoc;
+}
+
+impl Trait for () {
+    type Assoc = impl std::fmt::Debug;
+    type Foo = [(); {
+        let x: Self::Assoc = 42; //~ ERROR: mismatched types
+        3
+    }];
+    fn foo() -> Self::Assoc {
+        ""
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.stderr b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.stderr
new file mode 100644
index 0000000000000..708c3f28d2d2e
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait2.stderr
@@ -0,0 +1,17 @@
+error[E0308]: mismatched types
+  --> $DIR/impl_trait_in_trait_defined_outside_trait2.rs:14:30
+   |
+LL |     type Assoc = impl std::fmt::Debug;
+   |                  -------------------- the expected opaque type
+LL |     type Foo = [(); {
+LL |         let x: Self::Assoc = 42;
+   |                -----------   ^^ expected opaque type, found integer
+   |                |
+   |                expected due to this
+   |
+   = note: expected opaque type `<() as Trait>::Assoc`
+                     found type `{integer}`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait3.rs b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait3.rs
new file mode 100644
index 0000000000000..dfcf733653376
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/impl_trait_in_trait_defined_outside_trait3.rs
@@ -0,0 +1,38 @@
+//! Check that non-defining assoc items can use the opaque type
+//! opaquely.
+
+// check-pass
+
+#![feature(impl_trait_in_assoc_type)]
+
+trait Trait: Sized {
+    type Assoc;
+    fn foo();
+    fn bar() -> Self::Assoc;
+}
+
+impl Trait for () {
+    type Assoc = impl std::fmt::Debug;
+    fn foo() {
+        let x: Self::Assoc = Self::bar();
+    }
+    fn bar() -> Self::Assoc {
+        ""
+    }
+}
+
+trait Trait2: Sized {
+    type Assoc;
+    const FOO: ();
+    const BAR: Self::Assoc;
+}
+
+impl Trait2 for () {
+    type Assoc = impl Copy;
+    const FOO: () = {
+        let x: Self::Assoc = Self::BAR;
+    };
+    const BAR: Self::Assoc = "";
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs
new file mode 100644
index 0000000000000..baeba1d3de635
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.rs
@@ -0,0 +1,17 @@
+#![feature(impl_trait_in_assoc_type)]
+
+trait Foo {
+    type Assoc<'a, 'b>;
+    fn bar<'a: 'a, 'b: 'b>(_: &'a ()) -> Self::Assoc<'a, 'b>;
+}
+
+impl Foo for () {
+    type Assoc<'a, 'b> = impl Sized;
+    fn bar<'a: 'a, 'b: 'b>(x: &'a ()) -> Self::Assoc<'a, 'b> {
+        let closure = |x: &'a ()| -> Self::Assoc<'b, 'a> { x };
+        //~^ ERROR `<() as Foo>::Assoc<'b, 'a>` captures lifetime that does not appear in bounds
+        x
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr
new file mode 100644
index 0000000000000..a7d3e7f0be403
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound.stderr
@@ -0,0 +1,13 @@
+error[E0700]: hidden type for `<() as Foo>::Assoc<'b, 'a>` captures lifetime that does not appear in bounds
+  --> $DIR/in-assoc-ty-early-bound.rs:11:60
+   |
+LL |     type Assoc<'a, 'b> = impl Sized;
+   |                          ---------- opaque type defined here
+LL |     fn bar<'a: 'a, 'b: 'b>(x: &'a ()) -> Self::Assoc<'a, 'b> {
+   |            -- hidden type `&'a ()` captures the lifetime `'a` as defined here
+LL |         let closure = |x: &'a ()| -> Self::Assoc<'b, 'a> { x };
+   |                                                            ^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0700`.
diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs
new file mode 100644
index 0000000000000..7452000b65dc8
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.rs
@@ -0,0 +1,21 @@
+#![feature(impl_trait_in_assoc_type)]
+
+trait Foo {
+    type Assoc<'a>;
+    fn bar<'a: 'a>();
+}
+
+impl Foo for () {
+    type Assoc<'a> = impl Sized; //~ ERROR unconstrained opaque type
+    fn bar<'a: 'a>()
+    where
+        Self::Assoc<'a>:,
+    {
+        let _ = |x: &'a ()| {
+            let _: Self::Assoc<'a> = x;
+            //~^ ERROR `<() as Foo>::Assoc<'a>` captures lifetime that does not appear in bound
+        };
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr
new file mode 100644
index 0000000000000..1274a8b60dea0
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/in-assoc-ty-early-bound2.stderr
@@ -0,0 +1,22 @@
+error[E0700]: hidden type for `<() as Foo>::Assoc<'a>` captures lifetime that does not appear in bounds
+  --> $DIR/in-assoc-ty-early-bound2.rs:15:20
+   |
+LL |     type Assoc<'a> = impl Sized;
+   |                      ---------- opaque type defined here
+LL |     fn bar<'a: 'a>()
+   |            -- hidden type `&'a ()` captures the lifetime `'a` as defined here
+...
+LL |             let _: Self::Assoc<'a> = x;
+   |                    ^^^^^^^^^^^^^^^
+
+error: unconstrained opaque type
+  --> $DIR/in-assoc-ty-early-bound2.rs:9:22
+   |
+LL |     type Assoc<'a> = impl Sized;
+   |                      ^^^^^^^^^^
+   |
+   = note: `Assoc` must be used in combination with a concrete type within the same impl
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0700`.

From 1829aa631fe3df4db1217922ec420e7c57501d50 Mon Sep 17 00:00:00 2001
From: Oli Scherer <git-spam-no-reply9815368754983@oli-obk.de>
Date: Tue, 16 Jan 2024 09:21:54 +0000
Subject: [PATCH 5/6] Use an enum instead of a bool

---
 .../src/collect/type_of/opaque.rs             | 27 ++++++++++++-------
 1 file changed, 17 insertions(+), 10 deletions(-)

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 51b2401531c7a..79cb384c5bded 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
@@ -47,7 +47,7 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type(
         let assoc = tcx.associated_item(assoc_id);
         match assoc.kind {
             ty::AssocKind::Const | ty::AssocKind::Fn => {
-                locator.check(assoc_id.expect_local(), true)
+                locator.check(assoc_id.expect_local(), ImplTraitSource::AssocTy)
             }
             // Associated types don't have bodies, so they can't constrain hidden types
             ty::AssocKind::Type => {}
@@ -182,9 +182,15 @@ struct TaitConstraintLocator<'tcx> {
     typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>,
 }
 
+#[derive(Debug)]
+enum ImplTraitSource {
+    AssocTy,
+    TyAlias,
+}
+
 impl TaitConstraintLocator<'_> {
     #[instrument(skip(self), level = "debug")]
-    fn check(&mut self, item_def_id: LocalDefId, impl_trait_in_assoc_type: bool) {
+    fn check(&mut self, item_def_id: LocalDefId, source: ImplTraitSource) {
         // Don't try to check items that cannot possibly constrain the type.
         if !self.tcx.has_typeck_results(item_def_id) {
             debug!("no constraint: no typeck results");
@@ -236,10 +242,11 @@ impl TaitConstraintLocator<'_> {
                 continue;
             }
             constrained = true;
-            let opaque_types_defined_by = if impl_trait_in_assoc_type {
-                self.tcx.impl_trait_in_assoc_types_defined_by(item_def_id)
-            } else {
-                self.tcx.opaque_types_defined_by(item_def_id)
+            let opaque_types_defined_by = match source {
+                ImplTraitSource::AssocTy => {
+                    self.tcx.impl_trait_in_assoc_types_defined_by(item_def_id)
+                }
+                ImplTraitSource::TyAlias => self.tcx.opaque_types_defined_by(item_def_id),
             };
             if !opaque_types_defined_by.contains(&self.def_id) {
                 self.tcx.dcx().emit_err(TaitForwardCompat {
@@ -299,7 +306,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
     }
     fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
         if let hir::ExprKind::Closure(closure) = ex.kind {
-            self.check(closure.def_id, false);
+            self.check(closure.def_id, ImplTraitSource::TyAlias);
         }
         intravisit::walk_expr(self, ex);
     }
@@ -307,7 +314,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
         trace!(?it.owner_id);
         // The opaque type itself or its children are not within its reveal scope.
         if it.owner_id.def_id != self.def_id {
-            self.check(it.owner_id.def_id, false);
+            self.check(it.owner_id.def_id, ImplTraitSource::TyAlias);
             intravisit::walk_item(self, it);
         }
     }
@@ -315,13 +322,13 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
         trace!(?it.owner_id);
         // The opaque type itself or its children are not within its reveal scope.
         if it.owner_id.def_id != self.def_id {
-            self.check(it.owner_id.def_id, false);
+            self.check(it.owner_id.def_id, ImplTraitSource::TyAlias);
             intravisit::walk_impl_item(self, it);
         }
     }
     fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
         trace!(?it.owner_id);
-        self.check(it.owner_id.def_id, false);
+        self.check(it.owner_id.def_id, ImplTraitSource::TyAlias);
         intravisit::walk_trait_item(self, it);
     }
     fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {

From 5e5d1350b600ad8922a7c4bee44611bea597d082 Mon Sep 17 00:00:00 2001
From: Oli Scherer <git-spam-no-reply9815368754983@oli-obk.de>
Date: Tue, 16 Jan 2024 10:17:58 +0000
Subject: [PATCH 6/6] Check that we forbid nested items, but not nested
 closures

---
 .../itiat-allow-nested-closures.bad.stderr    | 23 ++++++++++++++++
 .../itiat-allow-nested-closures.rs            | 26 +++++++++++++++++++
 .../itiat-forbid-nested-items.rs              | 20 ++++++++++++++
 .../itiat-forbid-nested-items.stderr          | 22 ++++++++++++++++
 4 files changed, 91 insertions(+)
 create mode 100644 tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.bad.stderr
 create mode 100644 tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.rs
 create mode 100644 tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.rs
 create mode 100644 tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr

diff --git a/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.bad.stderr b/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.bad.stderr
new file mode 100644
index 0000000000000..4acc47eaef220
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.bad.stderr
@@ -0,0 +1,23 @@
+error[E0308]: mismatched types
+  --> $DIR/itiat-allow-nested-closures.rs:21:22
+   |
+LL |     type Assoc = impl Sized;
+   |                  ---------- the found opaque type
+...
+LL |         let _: i32 = closure();
+   |                ---   ^^^^^^^^^ expected `i32`, found opaque type
+   |                |
+   |                expected due to this
+
+error[E0308]: mismatched types
+  --> $DIR/itiat-allow-nested-closures.rs:22:9
+   |
+LL |     fn bar() -> Self::Assoc {
+   |                 ----------- expected `()` because of return type
+...
+LL |         1i32
+   |         ^^^^ expected `()`, found `i32`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.rs b/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.rs
new file mode 100644
index 0000000000000..55994d6a3259d
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/itiat-allow-nested-closures.rs
@@ -0,0 +1,26 @@
+#![feature(impl_trait_in_assoc_type)]
+
+// revisions: ok bad
+// [ok] check-pass
+
+trait Foo {
+    type Assoc;
+    fn bar() -> Self::Assoc;
+}
+
+impl Foo for () {
+    type Assoc = impl Sized;
+    fn bar() -> Self::Assoc {
+        let closure = || -> Self::Assoc {
+            #[cfg(ok)]
+            let x: Self::Assoc = 42_i32;
+            #[cfg(bad)]
+            let x: Self::Assoc = ();
+            x
+        };
+        let _: i32 = closure(); //[bad]~ ERROR mismatched types
+        1i32 //[bad]~ ERROR mismatched types
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.rs b/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.rs
new file mode 100644
index 0000000000000..8c9d780c1119d
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.rs
@@ -0,0 +1,20 @@
+#![feature(impl_trait_in_assoc_type)]
+
+trait Foo {
+    type Assoc;
+    fn bar() -> Self::Assoc;
+}
+
+impl Foo for () {
+    type Assoc = impl Sized;
+    fn bar() -> Self::Assoc {
+        fn foo() -> <() as Foo>::Assoc {
+            let x: <() as Foo>::Assoc = 42_i32; //~ ERROR mismatched types
+            x
+        };
+        let _: i32 = foo();
+        1i32
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr b/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr
new file mode 100644
index 0000000000000..c177201431a61
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/itiat-forbid-nested-items.stderr
@@ -0,0 +1,22 @@
+error[E0308]: mismatched types
+  --> $DIR/itiat-forbid-nested-items.rs:12:41
+   |
+LL |     type Assoc = impl Sized;
+   |                  ---------- the expected opaque type
+...
+LL |             let x: <() as Foo>::Assoc = 42_i32;
+   |                    ------------------   ^^^^^^ expected opaque type, found `i32`
+   |                    |
+   |                    expected due to this
+   |
+   = note: expected opaque type `<() as Foo>::Assoc`
+                     found type `i32`
+note: this item must have the opaque type in its signature in order to be able to register hidden types
+  --> $DIR/itiat-forbid-nested-items.rs:11:12
+   |
+LL |         fn foo() -> <() as Foo>::Assoc {
+   |            ^^^
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.