mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-27 17:24:06 +00:00
give better error messages when a cycle arises
This commit is contained in:
parent
5e0e8ae291
commit
27bedfa36b
@ -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
|
||||
|
@ -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) => {
|
||||
|
@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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(|_| { });
|
||||
}
|
||||
|
12
src/test/ui/unboxed-closure-no-cyclic-sig.stderr
Normal file
12
src/test/ui/unboxed-closure-no-cyclic-sig.stderr
Normal 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
|
||||
|
Loading…
Reference in New Issue
Block a user