give better error messages when a cycle arises

This commit is contained in:
Niko Matsakis 2017-11-17 11:13:13 -05:00
parent 5e0e8ae291
commit 27bedfa36b
9 changed files with 134 additions and 29 deletions

View File

@ -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

View File

@ -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) => {

View File

@ -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"),
}
}
}

View File

@ -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");
}
}
_ => {}
}
}

View File

@ -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,
}

View File

@ -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,

View File

@ -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

View File

@ -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(|_| { });
}

View File

@ -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