diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index bab3bded77b0..9b0483d00a44 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) + 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 40e933b26a25..50a37e12531a 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 4f36193e197d..6fadafc7b97a 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>, + mut values: Option>, 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 228ca76ed9a7..cb68e576e5af 100644 --- a/src/librustc/ty/error.rs +++ b/src/librustc/ty/error.rs @@ -49,7 +49,11 @@ pub enum TypeError<'tcx> { FloatMismatch(ExpectedFound), Traits(ExpectedFound), VariadicMismatch(ExpectedFound), - 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), ProjectionBoundsLength(ExpectedFound), TyParamDefaultMismatch(ExpectedFound>), @@ -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 73d1b4c34003..83207fbe3c34 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 436238b5e374..d2b6d14e2847 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 b703632bf90c..3cf0fd9a9eed 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 731cfa2b04d2..78d119ef329d 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) where F: FnOnce(Option) {} 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 000000000000..a4279a2afac6 --- /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 +