Skip to content

Commit 27bedfa

Browse files
committed
give better error messages when a cycle arises
1 parent 5e0e8ae commit 27bedfa

9 files changed

+134
-29
lines changed

src/librustc/diagnostics.rs

+32
Original file line numberDiff line numberDiff line change
@@ -1969,8 +1969,40 @@ fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32 {
19691969
```
19701970
"##,
19711971

1972+
E0644: r##"
1973+
A closure or generator was constructed that references its own type.
1974+
1975+
Erroneous example:
1976+
1977+
```rust
1978+
fn fix<F>(f: &F)
1979+
where F: Fn(&F)
1980+
{
1981+
f(&f);
19721982
}
19731983
1984+
fn main() {
1985+
let x = |y| {
1986+
// Here, when `x` is called, the parameter `y` is equal to `x`.
1987+
};
1988+
fix(&x);
1989+
}
1990+
```
1991+
1992+
Rust does not permit a closure to directly reference its own type,
1993+
either through an argument (as in the example above) or by capturing
1994+
itself through its environment. This restriction helps keep closure
1995+
inference tractable.
1996+
1997+
The easiest fix is to rewrite your closure into a top-level function,
1998+
or into a method. In some cases, you may also be able to have your
1999+
closure call itself by capturing a `&Fn()` object or `fn()` pointer
2000+
that refers to itself. That is permitting, since the closure would be
2001+
invoking itself via a virtual call, and hence does not directly
2002+
reference its own *type*.
2003+
2004+
"##, }
2005+
19742006

19752007
register_diagnostics! {
19762008
// E0006 // merged with E0005

src/librustc/infer/combine.rs

+16-2
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
270270
for_vid_sub_root: self.infcx.type_variables.borrow_mut().sub_root_var(for_vid),
271271
ambient_variance,
272272
needs_wf: false,
273+
root_ty: ty,
273274
};
274275

275276
let ty = generalize.relate(&ty, &ty)?;
@@ -280,10 +281,23 @@ impl<'infcx, 'gcx, 'tcx> CombineFields<'infcx, 'gcx, 'tcx> {
280281

281282
struct Generalizer<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
282283
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
284+
285+
/// Span, used when creating new type variables and things.
283286
span: Span,
287+
288+
/// The vid of the type variable that is in the process of being
289+
/// instantiated; if we find this within the type we are folding,
290+
/// that means we would have created a cyclic type.
284291
for_vid_sub_root: ty::TyVid,
292+
293+
/// Track the variance as we descend into the type.
285294
ambient_variance: ty::Variance,
286-
needs_wf: bool, // see the field `needs_wf` in `Generalization`
295+
296+
/// See the field `needs_wf` in `Generalization`.
297+
needs_wf: bool,
298+
299+
/// The root type that we are generalizing. Used when reporting cycles.
300+
root_ty: Ty<'tcx>,
287301
}
288302

289303
/// Result from a generalization operation. This includes
@@ -386,7 +400,7 @@ impl<'cx, 'gcx, 'tcx> TypeRelation<'cx, 'gcx, 'tcx> for Generalizer<'cx, 'gcx, '
386400
if sub_vid == self.for_vid_sub_root {
387401
// If sub-roots are equal, then `for_vid` and
388402
// `vid` are related via subtyping.
389-
return Err(TypeError::CyclicTy);
403+
return Err(TypeError::CyclicTy(self.root_ty));
390404
} else {
391405
match variables.probe_root(vid) {
392406
Some(u) => {

src/librustc/infer/error_reporting/mod.rs

+45-18
Original file line numberDiff line numberDiff line change
@@ -689,9 +689,16 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
689689
diag: &mut DiagnosticBuilder<'tcx>,
690690
cause: &ObligationCause<'tcx>,
691691
secondary_span: Option<(Span, String)>,
692-
values: Option<ValuePairs<'tcx>>,
692+
mut values: Option<ValuePairs<'tcx>>,
693693
terr: &TypeError<'tcx>)
694694
{
695+
// For some types of errors, expected-found does not make
696+
// sense, so just ignore the values we were given.
697+
match terr {
698+
TypeError::CyclicTy(_) => { values = None; }
699+
_ => { }
700+
}
701+
695702
let (expected_found, exp_found, is_simple_error) = match values {
696703
None => (None, None, false),
697704
Some(values) => {
@@ -780,17 +787,20 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
780787
terr);
781788

782789
let span = trace.cause.span;
783-
let failure_str = trace.cause.as_failure_str();
784-
let mut diag = match trace.cause.code {
785-
ObligationCauseCode::IfExpressionWithNoElse => {
790+
let failure_code = trace.cause.as_failure_code(terr);
791+
let mut diag = match failure_code {
792+
FailureCode::Error0317(failure_str) => {
786793
struct_span_err!(self.tcx.sess, span, E0317, "{}", failure_str)
787794
}
788-
ObligationCauseCode::MainFunctionType => {
795+
FailureCode::Error0580(failure_str) => {
789796
struct_span_err!(self.tcx.sess, span, E0580, "{}", failure_str)
790797
}
791-
_ => {
798+
FailureCode::Error0308(failure_str) => {
792799
struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str)
793800
}
801+
FailureCode::Error0644(failure_str) => {
802+
struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str)
803+
}
794804
};
795805
self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr);
796806
diag
@@ -1040,23 +1050,40 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
10401050
}
10411051
}
10421052

1053+
enum FailureCode {
1054+
Error0317(&'static str),
1055+
Error0580(&'static str),
1056+
Error0308(&'static str),
1057+
Error0644(&'static str),
1058+
}
1059+
10431060
impl<'tcx> ObligationCause<'tcx> {
1044-
fn as_failure_str(&self) -> &'static str {
1061+
fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode {
1062+
use self::FailureCode::*;
10451063
use traits::ObligationCauseCode::*;
10461064
match self.code {
1047-
CompareImplMethodObligation { .. } => "method not compatible with trait",
1048-
MatchExpressionArm { source, .. } => match source {
1065+
CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"),
1066+
MatchExpressionArm { source, .. } => Error0308(match source {
10491067
hir::MatchSource::IfLetDesugar{..} => "`if let` arms have incompatible types",
10501068
_ => "match arms have incompatible types",
1051-
},
1052-
IfExpression => "if and else have incompatible types",
1053-
IfExpressionWithNoElse => "if may be missing an else clause",
1054-
EquatePredicate => "equality predicate not satisfied",
1055-
MainFunctionType => "main function has wrong type",
1056-
StartFunctionType => "start function has wrong type",
1057-
IntrinsicType => "intrinsic has wrong type",
1058-
MethodReceiver => "mismatched method receiver",
1059-
_ => "mismatched types",
1069+
}),
1070+
IfExpression => Error0308("if and else have incompatible types"),
1071+
IfExpressionWithNoElse => Error0317("if may be missing an else clause"),
1072+
EquatePredicate => Error0308("equality predicate not satisfied"),
1073+
MainFunctionType => Error0580("main function has wrong type"),
1074+
StartFunctionType => Error0308("start function has wrong type"),
1075+
IntrinsicType => Error0308("intrinsic has wrong type"),
1076+
MethodReceiver => Error0308("mismatched method receiver"),
1077+
1078+
// In the case where we have no more specific thing to
1079+
// say, also take a look at the error code, maybe we can
1080+
// tailor to that.
1081+
_ => match terr {
1082+
TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() =>
1083+
Error0644("closure/generator type that references itself"),
1084+
_ =>
1085+
Error0308("mismatched types"),
1086+
}
10601087
}
10611088
}
10621089

src/librustc/ty/error.rs

+14-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ pub enum TypeError<'tcx> {
4949
FloatMismatch(ExpectedFound<ast::FloatTy>),
5050
Traits(ExpectedFound<DefId>),
5151
VariadicMismatch(ExpectedFound<bool>),
52-
CyclicTy,
52+
53+
/// Instantiating a type variable with the given type would have
54+
/// created a cycle (because it appears somewhere within that
55+
/// type).
56+
CyclicTy(Ty<'tcx>),
5357
ProjectionMismatched(ExpectedFound<DefId>),
5458
ProjectionBoundsLength(ExpectedFound<usize>),
5559
TyParamDefaultMismatch(ExpectedFound<type_variable::Default<'tcx>>),
@@ -84,7 +88,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
8488
}
8589

8690
match *self {
87-
CyclicTy => write!(f, "cyclic type of infinite size"),
91+
CyclicTy(_) => write!(f, "cyclic type of infinite size"),
8892
Mismatch => write!(f, "types differ"),
8993
UnsafetyMismatch(values) => {
9094
write!(f, "expected {} fn, found {} fn",
@@ -304,6 +308,14 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
304308

305309
self.note_and_explain_type_err(db, &err, sp);
306310
}
311+
CyclicTy(ty) => {
312+
// Watch out for various cases of cyclic types and try to explain.
313+
if ty.is_closure() || ty.is_generator() {
314+
db.note("closures cannot capture themselves or take themselves as argument;\n\
315+
this error may be the result of a recent compiler bug-fix,\n\
316+
see https://github.com/rust-lang/rust/issues/46062 for more details");
317+
}
318+
}
307319
_ => {}
308320
}
309321
}

src/librustc/ty/structural_impls.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
423423
FloatMismatch(x) => FloatMismatch(x),
424424
Traits(x) => Traits(x),
425425
VariadicMismatch(x) => VariadicMismatch(x),
426-
CyclicTy => CyclicTy,
426+
CyclicTy(t) => return tcx.lift(&t).map(|t| CyclicTy(t)),
427427
ProjectionMismatched(x) => ProjectionMismatched(x),
428428
ProjectionBoundsLength(x) => ProjectionBoundsLength(x),
429429

@@ -1173,7 +1173,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
11731173
FloatMismatch(x) => FloatMismatch(x),
11741174
Traits(x) => Traits(x),
11751175
VariadicMismatch(x) => VariadicMismatch(x),
1176-
CyclicTy => CyclicTy,
1176+
CyclicTy(t) => CyclicTy(t.fold_with(folder)),
11771177
ProjectionMismatched(x) => ProjectionMismatched(x),
11781178
ProjectionBoundsLength(x) => ProjectionBoundsLength(x),
11791179
Sorts(x) => Sorts(x.fold_with(folder)),
@@ -1200,6 +1200,7 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
12001200
OldStyleLUB(ref x) => x.visit_with(visitor),
12011201
TyParamDefaultMismatch(ref x) => x.visit_with(visitor),
12021202
ExistentialMismatch(x) => x.visit_with(visitor),
1203+
CyclicTy(t) => t.visit_with(visitor),
12031204
Mismatch |
12041205
Mutability |
12051206
TupleSize(_) |
@@ -1209,7 +1210,6 @@ impl<'tcx> TypeFoldable<'tcx> for ty::error::TypeError<'tcx> {
12091210
FloatMismatch(_) |
12101211
Traits(_) |
12111212
VariadicMismatch(_) |
1212-
CyclicTy |
12131213
ProjectionMismatched(_) |
12141214
ProjectionBoundsLength(_) => false,
12151215
}

src/librustc/ty/sty.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1368,6 +1368,13 @@ impl<'a, 'gcx, 'tcx> TyS<'tcx> {
13681368
}
13691369
}
13701370

1371+
pub fn is_generator(&self) -> bool {
1372+
match self.sty {
1373+
TyGenerator(..) => true,
1374+
_ => false,
1375+
}
1376+
}
1377+
13711378
pub fn is_integral(&self) -> bool {
13721379
match self.sty {
13731380
TyInfer(IntVar(_)) | TyInt(_) | TyUint(_) => true,

src/test/ui/span/coerce-suggestions.stderr

-3
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,6 @@ error[E0308]: mismatched types
4343
|
4444
41 | f = box f;
4545
| ^^^^^ cyclic type of infinite size
46-
|
47-
= note: expected type `_`
48-
found type `std::boxed::Box<_>`
4946

5047
error[E0308]: mismatched types
5148
--> $DIR/coerce-suggestions.rs:48:9

src/test/ui/unboxed-closure-no-cyclic-sig.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
// Test that unboxed closures cannot capture their own type.
12+
//
13+
// Also regression test for issue #21410.
14+
1115
fn g<F>(_: F) where F: FnOnce(Option<F>) {}
1216

1317
fn main() {
14-
g(|_| { }); //~ ERROR mismatched types
18+
g(|_| { });
1519
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0644]: closure/generator type that references itself
2+
--> $DIR/unboxed-closure-no-cyclic-sig.rs:18:7
3+
|
4+
18 | g(|_| { });
5+
| ^^^^^^^^ cyclic type of infinite size
6+
|
7+
= note: closures cannot capture themselves or take themselves as argument;
8+
this error may be the result of a recent compiler bug-fix,
9+
see https://github.com/rust-lang/rust/issues/46062 for more details
10+
11+
error: aborting due to previous error
12+

0 commit comments

Comments
 (0)