about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2017-11-17 11:13:13 -0500
committerNiko Matsakis <niko@alum.mit.edu>2017-11-18 07:49:52 -0500
commit27bedfa36bda9ec75751b0f0b5dee782108b56d3 (patch)
tree976752daec16d4248576c9cba719ace1745049b7
parent5e0e8ae2912e7f6ca5416d4166d42653d0eedf8b (diff)
downloadrust-27bedfa36bda9ec75751b0f0b5dee782108b56d3.tar.gz
rust-27bedfa36bda9ec75751b0f0b5dee782108b56d3.zip
give better error messages when a cycle arises
-rw-r--r--src/librustc/diagnostics.rs32
-rw-r--r--src/librustc/infer/combine.rs18
-rw-r--r--src/librustc/infer/error_reporting/mod.rs63
-rw-r--r--src/librustc/ty/error.rs16
-rw-r--r--src/librustc/ty/structural_impls.rs6
-rw-r--r--src/librustc/ty/sty.rs7
-rw-r--r--src/test/ui/span/coerce-suggestions.stderr3
-rw-r--r--src/test/ui/unboxed-closure-no-cyclic-sig.rs6
-rw-r--r--src/test/ui/unboxed-closure-no-cyclic-sig.stderr12
9 files changed, 134 insertions, 29 deletions
diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs
index bab3bded77b..9b0483d00a4 100644
--- a/src/librustc/diagnostics.rs
+++ b/src/librustc/diagnostics.rs
@@ -1969,8 +1969,40 @@ fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 {
 ```
 "##,
 
+E0644: r##"
+A closure or generator was constructed that references its own type.
+
+Erroneous example:
+
+```rust
+fn fix<F>(f: &F)
+  where F: Fn(&F)
+{
+  f(&f);
 }
 
+fn main() {
+  let x = |y| {
+    // Here, when `x` is called, the parameter `y` is equal to `x`.
+  };
+  fix(&x);
+}
+```
+
+Rust does not permit a closure to directly reference its own type,
+either through an argument (as in the example above) or by capturing
+itself through its environment. This restriction helps keep closure
+inference tractable.
+
+The easiest fix is to rewrite your closure into a top-level function,
+or into a method. In some cases, you may also be able to have your
+closure call itself by capturing a `&Fn()` object or `fn()` pointer
+that refers to itself. That is permitting, since the closure would be
+invoking itself via a virtual call, and hence does not directly
+reference its own *type*.
+
+"##, }
+
 
 register_diagnostics! {
 //  E0006 // merged with E0005
diff --git a/src/librustc/infer/combine.rs b/src/librustc/infer/combine.rs
index 40e933b26a2..50a37e12531 100644
--- a/src/librustc/infer/combine.rs
+++ b/src/librustc/infer/combine.rs
@@ -270,6 +270,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
             for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid),
             ambient_variance,
             needs_wf: false,
+            root_ty: ty,
         };
 
         let ty = generalize.relate(&ty, &ty)?;
@@ -280,10 +281,23 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
 
 struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
     infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
+
+    /// Span, used when creating new type variables and things.
     span: Span,
+
+    /// The vid of the type variable that is in the process of being
+    /// instantiated; if we find this within the type we are folding,
+    /// that means we would have created a cyclic type.
     for_vid_sub_root: ty::TyVid,
+
+    /// Track the variance as we descend into the type.
     ambient_variance: ty::Variance,
-    needs_wf: bool, // see the field `needs_wf` in `Generalization`
+
+    /// See the field `needs_wf` in `Generalization`.
+    needs_wf: bool,
+
+    /// The root type that we are generalizing. Used when reporting cycles.
+    root_ty: Ty<'tcx>,
 }
 
 /// Result from a generalization operation. This includes
@@ -386,7 +400,7 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
                 if sub_vid == self.for_vid_sub_root {
                     // If sub-roots are equal, then `for_vid` and
                     // `vid` are related via subtyping.
-                    return Err(TypeError::CyclicTy);
+                    return Err(TypeError::CyclicTy(self.root_ty));
                 } else {
                     match variables.probe_root(vid) {
                         Some(u) => {
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 4f36193e197..6fadafc7b97 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -689,9 +689,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                          diag: &mut DiagnosticBuilder<'tcx>,
                          cause: &ObligationCause<'tcx>,
                          secondary_span: Option<(Span, String)>,
-                         values: Option<ValuePairs<'tcx>>,
+                         mut values: Option<ValuePairs<'tcx>>,
                          terr: &TypeError<'tcx>)
     {
+        // For some types of errors, expected-found does not make
+        // sense, so just ignore the values we were given.
+        match terr {
+            TypeError::CyclicTy(_) => { values = None; }
+            _ => { }
+        }
+
         let (expected_found, exp_found, is_simple_error) = match values {
             None => (None, None, false),
             Some(values) => {
@@ -780,17 +787,20 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                terr);
 
         let span = trace.cause.span;
-        let failure_str = trace.cause.as_failure_str();
-        let mut diag = match trace.cause.code {
-            ObligationCauseCode::IfExpressionWithNoElse => {
+        let failure_code = trace.cause.as_failure_code(terr);
+        let mut diag = match failure_code {
+            FailureCode::Error0317(failure_str) => {
                 struct_span_err!(self.tcx.sess, span, E0317, "{}", failure_str)
             }
-            ObligationCauseCode::MainFunctionType => {
+            FailureCode::Error0580(failure_str) => {
                 struct_span_err!(self.tcx.sess, span, E0580, "{}", failure_str)
             }
-            _ => {
+            FailureCode::Error0308(failure_str) => {
                 struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str)
             }
+            FailureCode::Error0644(failure_str) => {
+                struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str)
+            }
         };
         self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr);
         diag
@@ -1040,23 +1050,40 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
     }
 }
 
+enum FailureCode {
+    Error0317(&'static str),
+    Error0580(&'static str),
+    Error0308(&'static str),
+    Error0644(&'static str),
+}
+
 impl<'tcx> ObligationCause<'tcx> {
-    fn as_failure_str(&self) -> &'static str {
+    fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode {
+        use self::FailureCode::*;
         use traits::ObligationCauseCode::*;
         match self.code {
-            CompareImplMethodObligation { .. } => "method not compatible with trait",
-            MatchExpressionArm { source, .. } => match source {
+            CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"),
+            MatchExpressionArm { source, .. } => Error0308(match source {
                 hir::MatchSource::IfLetDesugar{..} => "`if let` arms have incompatible types",
                 _ => "match arms have incompatible types",
-            },
-            IfExpression => "if and else have incompatible types",
-            IfExpressionWithNoElse => "if may be missing an else clause",
-            EquatePredicate => "equality predicate not satisfied",
-            MainFunctionType => "main function has wrong type",
-            StartFunctionType => "start function has wrong type",
-            IntrinsicType => "intrinsic has wrong type",
-            MethodReceiver => "mismatched method receiver",
-            _ => "mismatched types",
+            }),
+            IfExpression => Error0308("if and else have incompatible types"),
+            IfExpressionWithNoElse => Error0317("if may be missing an else clause"),
+            EquatePredicate => Error0308("equality predicate not satisfied"),
+            MainFunctionType => Error0580("main function has wrong type"),
+            StartFunctionType => Error0308("start function has wrong type"),
+            IntrinsicType => Error0308("intrinsic has wrong type"),
+            MethodReceiver => Error0308("mismatched method receiver"),
+
+            // In the case where we have no more specific thing to
+            // say, also take a look at the error code, maybe we can
+            // tailor to that.
+            _ => match terr {
+                TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() =>
+                    Error0644("closure/generator type that references itself"),
+                _ =>
+                    Error0308("mismatched types"),
+            }
         }
     }
 
diff --git a/src/librustc/ty/error.rs b/src/librustc/ty/error.rs
index 228ca76ed9a..cb68e576e5a 100644
--- a/src/librustc/ty/error.rs
+++ b/src/librustc/ty/error.rs
@@ -49,7 +49,11 @@ pub enum TypeError<'tcx> {
     FloatMismatch(ExpectedFound<ast::FloatTy>),
     Traits(ExpectedFound<DefId>),
     VariadicMismatch(ExpectedFound<bool>),
-    CyclicTy,
+
+    /// Instantiating a type variable with the given type would have
+    /// created a cycle (because it appears somewhere within that
+    /// type).
+    CyclicTy(Ty<'tcx>),
     ProjectionMismatched(ExpectedFound<DefId>),
     ProjectionBoundsLength(ExpectedFound<usize>),
     TyParamDefaultMismatch(ExpectedFound<type_variable::Default<'tcx>>),
@@ -84,7 +88,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
         }
 
         match *self {
-            CyclicTy => write!(f, "cyclic type of infinite size"),
+            CyclicTy(_) => write!(f, "cyclic type of infinite size"),
             Mismatch => write!(f, "types differ"),
             UnsafetyMismatch(values) => {
                 write!(f, "expected {} fn, found {} fn",
@@ -304,6 +308,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
 
                 self.note_and_explain_type_err(db, &err, sp);
             }
+            CyclicTy(ty) => {
+                // Watch out for various cases of cyclic types and try to explain.
+                if ty.is_closure() || ty.is_generator() {
+                    db.note("closures cannot capture themselves or take themselves as argument;\n\
+                             this error may be the result of a recent compiler bug-fix,\n\
+                             see https://github.com/rust-lang/rust/issues/46062 for more details");
+                }
+            }
             _ => {}
         }
     }
diff --git a/src/librustc/ty/structural_impls.rs b/src/librustc/ty/structural_impls.rs
index 73d1b4c3400..83207fbe3c3 100644
--- a/src/librustc/ty/structural_impls.rs
+++ b/src/librustc/ty/structural_impls.rs
@@ -423,7 +423,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
             FloatMismatch(x) => FloatMismatch(x),
             Traits(x) => Traits(x),
             VariadicMismatch(x) => VariadicMismatch(x),
-            CyclicTy => CyclicTy,
+            CyclicTy(t) => return tcx.lift(&t).map(|t| CyclicTy(t)),
             ProjectionMismatched(x) => ProjectionMismatched(x),
             ProjectionBoundsLength(x) => ProjectionBoundsLength(x),
 
@@ -1173,7 +1173,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
             FloatMismatch(x) => FloatMismatch(x),
             Traits(x) => Traits(x),
             VariadicMismatch(x) => VariadicMismatch(x),
-            CyclicTy => CyclicTy,
+            CyclicTy(t) => CyclicTy(t.fold_with(folder)),
             ProjectionMismatched(x) => ProjectionMismatched(x),
             ProjectionBoundsLength(x) => ProjectionBoundsLength(x),
             Sorts(x) => Sorts(x.fold_with(folder)),
@@ -1200,6 +1200,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
             OldStyleLUB(ref x) => x.visit_with(visitor),
             TyParamDefaultMismatch(ref x) => x.visit_with(visitor),
             ExistentialMismatch(x) => x.visit_with(visitor),
+            CyclicTy(t) => t.visit_with(visitor),
             Mismatch |
             Mutability |
             TupleSize(_) |
@@ -1209,7 +1210,6 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
             FloatMismatch(_) |
             Traits(_) |
             VariadicMismatch(_) |
-            CyclicTy |
             ProjectionMismatched(_) |
             ProjectionBoundsLength(_) => false,
         }
diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs
index 436238b5e37..d2b6d14e284 100644
--- a/src/librustc/ty/sty.rs
+++ b/src/librustc/ty/sty.rs
@@ -1368,6 +1368,13 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
         }
     }
 
+    pub fn is_generator(&self) -> bool {
+        match self.sty {
+            TyGenerator(..) => true,
+            _ => false,
+        }
+    }
+
     pub fn is_integral(&self) -> bool {
         match self.sty {
             TyInfer(IntVar(_)) | TyInt(_) | TyUint(_) => true,
diff --git a/src/test/ui/span/coerce-suggestions.stderr b/src/test/ui/span/coerce-suggestions.stderr
index b703632bf90..3cf0fd9a9ee 100644
--- a/src/test/ui/span/coerce-suggestions.stderr
+++ b/src/test/ui/span/coerce-suggestions.stderr
@@ -43,9 +43,6 @@ error[E0308]: mismatched types
    |
 41 |     f = box f;
    |         ^^^^^ cyclic type of infinite size
-   |
-   = note: expected type `_`
-              found type `std::boxed::Box<_>`
 
 error[E0308]: mismatched types
   --> $DIR/coerce-suggestions.rs:48:9
diff --git a/src/test/ui/unboxed-closure-no-cyclic-sig.rs b/src/test/ui/unboxed-closure-no-cyclic-sig.rs
index 731cfa2b04d..78d119ef329 100644
--- a/src/test/ui/unboxed-closure-no-cyclic-sig.rs
+++ b/src/test/ui/unboxed-closure-no-cyclic-sig.rs
@@ -8,8 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+// Test that unboxed closures cannot capture their own type.
+//
+// Also regression test for issue #21410.
+
 fn g<F>(_: F) where F: FnOnce(Option<F>) {}
 
 fn main() {
-    g(|_| {  }); //~ ERROR mismatched types
+    g(|_| {  });
 }
diff --git a/src/test/ui/unboxed-closure-no-cyclic-sig.stderr b/src/test/ui/unboxed-closure-no-cyclic-sig.stderr
new file mode 100644
index 00000000000..a4279a2afac
--- /dev/null
+++ b/src/test/ui/unboxed-closure-no-cyclic-sig.stderr
@@ -0,0 +1,12 @@
+error[E0644]: closure/generator type that references itself
+  --> $DIR/unboxed-closure-no-cyclic-sig.rs:18:7
+   |
+18 |     g(|_| {  });
+   |       ^^^^^^^^ cyclic type of infinite size
+   |
+   = note: closures cannot capture themselves or take themselves as argument;
+           this error may be the result of a recent compiler bug-fix,
+           see https://github.com/rust-lang/rust/issues/46062 for more details
+
+error: aborting due to previous error
+