From 6a2bd5acd664e6bd118563ea9033245c624fec2e Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Thu, 4 Jan 2024 01:48:39 +0000
Subject: [PATCH] Use `resolutions(()).effective_visiblities` to avoid cycle
 errors

---
 .../src/traits/error_reporting/mod.rs         |  5 +-
 ...ctive-visibilities-during-object-safety.rs | 28 +++++++
 ...e-visibilities-during-object-safety.stderr | 78 +++++++++++++++++++
 3 files changed, 110 insertions(+), 1 deletion(-)
 create mode 100644 tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-object-safety.rs
 create mode 100644 tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-object-safety.stderr

diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
index d89c205da3f52..34163111d3c78 100644
--- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
@@ -132,7 +132,10 @@ pub fn report_object_safety_error<'tcx>(
     };
     let externally_visible = if !impls.is_empty()
         && let Some(def_id) = trait_def_id.as_local()
-        && tcx.effective_visibilities(()).is_exported(def_id)
+        // We may be executing this during typeck, which would result in cycle
+        // if we used effective_visibilities query, which looks into opaque types
+        // (and therefore calls typeck).
+        && tcx.resolutions(()).effective_visibilities.is_exported(def_id)
     {
         true
     } else {
diff --git a/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-object-safety.rs b/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-object-safety.rs
new file mode 100644
index 0000000000000..650cb3870d581
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-object-safety.rs
@@ -0,0 +1,28 @@
+trait Marker {}
+impl Marker for u32 {}
+
+trait MyTrait {
+    fn foo(&self) -> impl Marker;
+}
+
+struct Outer;
+
+impl MyTrait for Outer {
+    fn foo(&self) -> impl Marker {
+        42
+    }
+}
+
+impl dyn MyTrait {
+    //~^ ERROR the trait `MyTrait` cannot be made into an object
+    fn other(&self) -> impl Marker {
+        //~^ ERROR the trait `MyTrait` cannot be made into an object
+        MyTrait::foo(&self)
+        //~^ ERROR the trait bound `&dyn MyTrait: MyTrait` is not satisfied
+        //~| ERROR the trait bound `&dyn MyTrait: MyTrait` is not satisfied
+        //~| ERROR the trait bound `&dyn MyTrait: MyTrait` is not satisfied
+        //~| ERROR the trait `MyTrait` cannot be made into an object
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-object-safety.stderr b/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-object-safety.stderr
new file mode 100644
index 0000000000000..01de3e531952c
--- /dev/null
+++ b/tests/ui/impl-trait/in-trait/cycle-effective-visibilities-during-object-safety.stderr
@@ -0,0 +1,78 @@
+error[E0277]: the trait bound `&dyn MyTrait: MyTrait` is not satisfied
+  --> $DIR/cycle-effective-visibilities-during-object-safety.rs:20:22
+   |
+LL |         MyTrait::foo(&self)
+   |         ------------ ^^^^^ the trait `MyTrait` is not implemented for `&dyn MyTrait`
+   |         |
+   |         required by a bound introduced by this call
+   |
+   = help: the trait `MyTrait` is implemented for `Outer`
+
+error[E0038]: the trait `MyTrait` cannot be made into an object
+  --> $DIR/cycle-effective-visibilities-during-object-safety.rs:20:9
+   |
+LL |         MyTrait::foo(&self)
+   |         ^^^^^^^^^^^^ `MyTrait` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/cycle-effective-visibilities-during-object-safety.rs:5:22
+   |
+LL | trait MyTrait {
+   |       ------- this trait cannot be made into an object...
+LL |     fn foo(&self) -> impl Marker;
+   |                      ^^^^^^^^^^^ ...because method `foo` references an `impl Trait` type in its return type
+   = help: consider moving `foo` to another trait
+   = help: only type `Outer` implements the trait, consider using it directly instead
+
+error[E0277]: the trait bound `&dyn MyTrait: MyTrait` is not satisfied
+  --> $DIR/cycle-effective-visibilities-during-object-safety.rs:20:9
+   |
+LL |         MyTrait::foo(&self)
+   |         ^^^^^^^^^^^^^^^^^^^ the trait `MyTrait` is not implemented for `&dyn MyTrait`
+   |
+   = help: the trait `MyTrait` is implemented for `Outer`
+
+error[E0277]: the trait bound `&dyn MyTrait: MyTrait` is not satisfied
+  --> $DIR/cycle-effective-visibilities-during-object-safety.rs:20:9
+   |
+LL |         MyTrait::foo(&self)
+   |         ^^^^^^^^^^^^ the trait `MyTrait` is not implemented for `&dyn MyTrait`
+   |
+   = help: the trait `MyTrait` is implemented for `Outer`
+
+error[E0038]: the trait `MyTrait` cannot be made into an object
+  --> $DIR/cycle-effective-visibilities-during-object-safety.rs:16:6
+   |
+LL | impl dyn MyTrait {
+   |      ^^^^^^^^^^^ `MyTrait` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/cycle-effective-visibilities-during-object-safety.rs:5:22
+   |
+LL | trait MyTrait {
+   |       ------- this trait cannot be made into an object...
+LL |     fn foo(&self) -> impl Marker;
+   |                      ^^^^^^^^^^^ ...because method `foo` references an `impl Trait` type in its return type
+   = help: consider moving `foo` to another trait
+   = help: only type `Outer` implements the trait, consider using it directly instead
+
+error[E0038]: the trait `MyTrait` cannot be made into an object
+  --> $DIR/cycle-effective-visibilities-during-object-safety.rs:18:15
+   |
+LL |     fn other(&self) -> impl Marker {
+   |               ^^^^ `MyTrait` cannot be made into an object
+   |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+  --> $DIR/cycle-effective-visibilities-during-object-safety.rs:5:22
+   |
+LL | trait MyTrait {
+   |       ------- this trait cannot be made into an object...
+LL |     fn foo(&self) -> impl Marker;
+   |                      ^^^^^^^^^^^ ...because method `foo` references an `impl Trait` type in its return type
+   = help: consider moving `foo` to another trait
+   = help: only type `Outer` implements the trait, consider using it directly instead
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0038, E0277.
+For more information about an error, try `rustc --explain E0038`.