about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2022-12-06 23:55:26 +0000
committerMichael Goulet <michael@errs.io>2022-12-27 17:56:56 +0000
commit0817b1d3ed26898e7d9ed71f6a2218d912ea6de5 (patch)
tree1eda03d9c6e80bb47ba06a9b3c712e5186f49888
parentb38a6d373cb254697411147c0e49cd2e84864258 (diff)
downloadrust-0817b1d3ed26898e7d9ed71f6a2218d912ea6de5.tar.gz
rust-0817b1d3ed26898e7d9ed71f6a2218d912ea6de5.zip
Don't deduce a signature that makes a closure cyclic
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs38
-rw-r--r--src/test/ui/closures/supertrait-hint-cycle.rs65
-rw-r--r--src/test/ui/issues/issue-25439.stderr21
-rw-r--r--src/test/ui/unboxed-closures/unboxed-closure-no-cyclic-sig.stderr21
4 files changed, 122 insertions, 23 deletions
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index 5bd02dff73b..8167e7e070c 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -13,7 +13,7 @@ use rustc_infer::infer::{InferOk, InferResult};
 use rustc_macros::{TypeFoldable, TypeVisitable};
 use rustc_middle::ty::subst::InternalSubsts;
 use rustc_middle::ty::visit::TypeVisitable;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Ty, TypeSuperVisitable, TypeVisitor};
 use rustc_span::source_map::Span;
 use rustc_target::spec::abi::Abi;
 use rustc_trait_selection::traits;
@@ -21,6 +21,7 @@ use rustc_trait_selection::traits::error_reporting::ArgKind;
 use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
 use std::cmp;
 use std::iter;
+use std::ops::ControlFlow;
 
 /// What signature do we *expect* the closure to have from context?
 #[derive(Debug, Clone, TypeFoldable, TypeVisitable)]
@@ -54,7 +55,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // closure sooner rather than later, so first examine the expected
         // type, and see if can glean a closure kind from there.
         let (expected_sig, expected_kind) = match expected.to_option(self) {
-            Some(ty) => self.deduce_expectations_from_expected_type(ty),
+            Some(ty) => self.deduce_closure_signature(ty),
             None => (None, None),
         };
         let body = self.tcx.hir().body(closure.body);
@@ -162,13 +163,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Given the expected type, figures out what it can about this closure we
     /// are about to type check:
     #[instrument(skip(self), level = "debug")]
-    fn deduce_expectations_from_expected_type(
+    fn deduce_closure_signature(
         &self,
         expected_ty: Ty<'tcx>,
     ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
         match *expected_ty.kind() {
             ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => self
-                .deduce_signature_from_predicates(
+                .deduce_closure_signature_from_predicates(
+                    expected_ty,
                     self.tcx.bound_explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs),
                 ),
             ty::Dynamic(ref object_type, ..) => {
@@ -181,7 +183,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .and_then(|did| self.tcx.fn_trait_kind_from_def_id(did));
                 (sig, kind)
             }
-            ty::Infer(ty::TyVar(vid)) => self.deduce_signature_from_predicates(
+            ty::Infer(ty::TyVar(vid)) => self.deduce_closure_signature_from_predicates(
+                self.tcx.mk_ty_var(self.root_var(vid)),
                 self.obligations_for_self_ty(vid).map(|obl| (obl.predicate, obl.cause.span)),
             ),
             ty::FnPtr(sig) => {
@@ -192,8 +195,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    fn deduce_signature_from_predicates(
+    fn deduce_closure_signature_from_predicates(
         &self,
+        expected_ty: Ty<'tcx>,
         predicates: impl DoubleEndedIterator<Item = (ty::Predicate<'tcx>, Span)>,
     ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) {
         let mut expected_sig = None;
@@ -214,13 +218,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             if expected_sig.is_none()
                 && let ty::PredicateKind::Clause(ty::Clause::Projection(proj_predicate)) = bound_predicate.skip_binder()
             {
-                expected_sig = self.normalize(
+                let inferred_sig = self.normalize(
                     obligation.cause.span,
                     self.deduce_sig_from_projection(
                     Some(obligation.cause.span),
                         bound_predicate.rebind(proj_predicate),
                     ),
                 );
+                // Make sure that we didn't infer a signature that mentions itself.
+                // This can happen when we elaborate certain supertrait bounds that
+                // mention projections containing the `Self` type. See 
+                struct MentionsTy<'tcx> {
+                    expected_ty: Ty<'tcx>,
+                }
+                impl<'tcx> TypeVisitor<'tcx> for MentionsTy<'tcx> {
+                    type BreakTy = ();
+
+                    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+                        if t == self.expected_ty {
+                            ControlFlow::BREAK
+                        } else {
+                            t.super_visit_with(self)
+                        }
+                    }
+                }
+                if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() {
+                    expected_sig = inferred_sig;
+                }
             }
 
             // Even if we can't infer the full signature, we may be able to
diff --git a/src/test/ui/closures/supertrait-hint-cycle.rs b/src/test/ui/closures/supertrait-hint-cycle.rs
new file mode 100644
index 00000000000..dbb06b2ef7a
--- /dev/null
+++ b/src/test/ui/closures/supertrait-hint-cycle.rs
@@ -0,0 +1,65 @@
+// edition:2021
+// check-pass
+
+#![feature(type_alias_impl_trait)]
+#![feature(closure_lifetime_binder)]
+
+use std::future::Future;
+
+trait AsyncFn<I, R>: FnMut(I) -> Self::Fut {
+    type Fut: Future<Output = R>;
+}
+
+impl<F, I, R, Fut> AsyncFn<I, R> for F
+where
+    Fut: Future<Output = R>,
+    F: FnMut(I) -> Fut,
+{
+    type Fut = Fut;
+}
+
+async fn call<C, R, F>(mut ctx: C, mut f: F) -> Result<R, ()>
+where
+    F: for<'a> AsyncFn<&'a mut C, Result<R, ()>>,
+{
+    loop {
+        match f(&mut ctx).await {
+            Ok(val) => return Ok(val),
+            Err(_) => continue,
+        }
+    }
+}
+
+trait Cap<'a> {}
+impl<T> Cap<'_> for T {}
+
+fn works(ctx: &mut usize) {
+    let mut inner = 0;
+
+    type Ret<'a, 'b: 'a> = impl Future<Output = Result<usize, ()>> + 'a + Cap<'b>;
+
+    let callback = for<'a, 'b> |c: &'a mut &'b mut usize| -> Ret<'a, 'b> {
+        inner += 1;
+        async move {
+            let _c = c;
+            Ok(1usize)
+        }
+    };
+    call(ctx, callback);
+}
+
+fn doesnt_work_but_should(ctx: &mut usize) {
+    let mut inner = 0;
+
+    type Ret<'a, 'b: 'a> = impl Future<Output = Result<usize, ()>> + 'a + Cap<'b>;
+
+    call(ctx, for<'a, 'b> |c: &'a mut &'b mut usize| -> Ret<'a, 'b> {
+        inner += 1;
+        async move {
+            let _c = c;
+            Ok(1usize)
+        }
+    });
+}
+
+fn main() {}
diff --git a/src/test/ui/issues/issue-25439.stderr b/src/test/ui/issues/issue-25439.stderr
index 325c28c15e2..938c9d9f18c 100644
--- a/src/test/ui/issues/issue-25439.stderr
+++ b/src/test/ui/issues/issue-25439.stderr
@@ -1,14 +1,19 @@
-error[E0644]: closure/generator type that references itself
-  --> $DIR/issue-25439.rs:8:9
+error[E0631]: type mismatch in closure arguments
+  --> $DIR/issue-25439.rs:8:5
    |
 LL |     fix(|_, x| x);
-   |         ^^^^^^^^ cyclic type of infinite size
+   |     ^^^ ------ found signature defined here
+   |     |
+   |     expected due to this
    |
-   = note: closures cannot capture themselves or take themselves as argument;
-           this error may be the result of a recent compiler bug-fix,
-           see issue #46062 <https://github.com/rust-lang/rust/issues/46062>
-           for more information
+   = note: expected closure signature `for<'a> fn(Helper<'a, [closure@$DIR/issue-25439.rs:8:9: 8:15]>, i32) -> _`
+              found closure signature `fn(_, _) -> _`
+note: required by a bound in `fix`
+  --> $DIR/issue-25439.rs:3:33
+   |
+LL | fn fix<F>(f: F) -> i32 where F: Fn(Helper<F>, i32) -> i32 {
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `fix`
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0644`.
+For more information about this error, try `rustc --explain E0631`.
diff --git a/src/test/ui/unboxed-closures/unboxed-closure-no-cyclic-sig.stderr b/src/test/ui/unboxed-closures/unboxed-closure-no-cyclic-sig.stderr
index 167479270b5..cfea13c1127 100644
--- a/src/test/ui/unboxed-closures/unboxed-closure-no-cyclic-sig.stderr
+++ b/src/test/ui/unboxed-closures/unboxed-closure-no-cyclic-sig.stderr
@@ -1,14 +1,19 @@
-error[E0644]: closure/generator type that references itself
-  --> $DIR/unboxed-closure-no-cyclic-sig.rs:8:7
+error[E0631]: type mismatch in closure arguments
+  --> $DIR/unboxed-closure-no-cyclic-sig.rs:8:5
    |
 LL |     g(|_| {  });
-   |       ^^^^^^^^ cyclic type of infinite size
+   |     ^ --- found signature defined here
+   |     |
+   |     expected due to this
    |
-   = note: closures cannot capture themselves or take themselves as argument;
-           this error may be the result of a recent compiler bug-fix,
-           see issue #46062 <https://github.com/rust-lang/rust/issues/46062>
-           for more information
+   = note: expected closure signature `fn(Option<[closure@$DIR/unboxed-closure-no-cyclic-sig.rs:8:7: 8:10]>) -> _`
+              found closure signature `fn(_) -> _`
+note: required by a bound in `g`
+  --> $DIR/unboxed-closure-no-cyclic-sig.rs:5:24
+   |
+LL | fn g<F>(_: F) where F: FnOnce(Option<F>) {}
+   |                        ^^^^^^^^^^^^^^^^^ required by this bound in `g`
 
 error: aborting due to previous error
 
-For more information about this error, try `rustc --explain E0644`.
+For more information about this error, try `rustc --explain E0631`.