Improved error message when type must be bound due to generator.
Error now mentions type var name and span is highlighted.
This commit is contained in:
parent
3bee49f42b
commit
66e41bc675
9 changed files with 135 additions and 35 deletions
|
|
@ -2043,6 +2043,36 @@ a (non-transparent) struct containing a single float, while `Grams` is a
|
|||
transparent wrapper around a float. This can make a difference for the ABI.
|
||||
"##,
|
||||
|
||||
E0698: r##"
|
||||
When using generators (or async) all type variables must be bound so a
|
||||
generator can be constructed.
|
||||
|
||||
Erroneous code example:
|
||||
|
||||
```edition2018,compile-fail,E0698
|
||||
#![feature(futures_api, async_await, await_macro)]
|
||||
async fn bar<T>() -> () {}
|
||||
|
||||
async fn foo() {
|
||||
await!(bar()); // error: cannot infer type for `T`
|
||||
}
|
||||
```
|
||||
|
||||
In the above example `T` is unknowable by the compiler.
|
||||
To fix this you must bind `T` to a concrete type such as `String`
|
||||
so that a generator can then be constructed:
|
||||
|
||||
```edition2018
|
||||
#![feature(futures_api, async_await, await_macro)]
|
||||
async fn bar<T>() -> () {}
|
||||
|
||||
async fn foo() {
|
||||
await!(bar::<String>());
|
||||
// ^^^^^^^^ specify type explicitly
|
||||
}
|
||||
```
|
||||
"##,
|
||||
|
||||
E0700: r##"
|
||||
The `impl Trait` return type captures lifetime parameters that do not
|
||||
appear within the `impl Trait` itself.
|
||||
|
|
|
|||
|
|
@ -88,23 +88,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||
s
|
||||
}
|
||||
|
||||
pub fn need_type_info_err(&self,
|
||||
body_id: Option<hir::BodyId>,
|
||||
span: Span,
|
||||
ty: Ty<'tcx>)
|
||||
-> DiagnosticBuilder<'gcx> {
|
||||
pub fn need_type_info_err(
|
||||
&self,
|
||||
body_id: Option<hir::BodyId>,
|
||||
span: Span,
|
||||
ty: Ty<'tcx>
|
||||
) -> DiagnosticBuilder<'gcx> {
|
||||
let ty = self.resolve_type_vars_if_possible(&ty);
|
||||
let name = self.extract_type_name(&ty, None);
|
||||
|
||||
let mut err_span = span;
|
||||
let mut labels = vec![(
|
||||
span,
|
||||
if &name == "_" {
|
||||
"cannot infer type".to_owned()
|
||||
} else {
|
||||
format!("cannot infer type for `{}`", name)
|
||||
},
|
||||
)];
|
||||
let mut labels = vec![(span, InferCtxt::missing_type_msg(&name))];
|
||||
|
||||
let mut local_visitor = FindLocalByTypeVisitor {
|
||||
infcx: &self,
|
||||
|
|
@ -166,4 +160,28 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||
|
||||
err
|
||||
}
|
||||
|
||||
pub fn need_type_info_err_in_generator(
|
||||
&self,
|
||||
span: Span,
|
||||
ty: Ty<'tcx>
|
||||
) -> DiagnosticBuilder<'gcx> {
|
||||
let ty = self.resolve_type_vars_if_possible(&ty);
|
||||
let name = self.extract_type_name(&ty, None);
|
||||
|
||||
let mut err = struct_span_err!(self.tcx.sess,
|
||||
span,
|
||||
E0698,
|
||||
"type inside generator must be known in this context");
|
||||
err.span_label(span, InferCtxt::missing_type_msg(&name));
|
||||
err
|
||||
}
|
||||
|
||||
fn missing_type_msg(type_name: &str) -> String {
|
||||
if type_name == "_" {
|
||||
"cannot infer type".to_owned()
|
||||
} else {
|
||||
format!("cannot infer type for `{}`", type_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1312,17 +1312,18 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
|
|||
value.fold_with(&mut r)
|
||||
}
|
||||
|
||||
/// Returns `true` if `T` contains unresolved type variables. In the
|
||||
/// Returns first unresolved variable contained in `T`. In the
|
||||
/// process of visiting `T`, this will resolve (where possible)
|
||||
/// type variables in `T`, but it never constructs the final,
|
||||
/// resolved type, so it's more efficient than
|
||||
/// `resolve_type_vars_if_possible()`.
|
||||
pub fn any_unresolved_type_vars<T>(&self, value: &T) -> bool
|
||||
pub fn unresolved_type_vars<T>(&self, value: &T) -> Option<(Ty<'tcx>, Option<Span>)>
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
let mut r = resolve::UnresolvedTypeFinder::new(self);
|
||||
value.visit_with(&mut r)
|
||||
value.visit_with(&mut r);
|
||||
r.first_unresolved
|
||||
}
|
||||
|
||||
pub fn fully_resolve<T: TypeFoldable<'tcx>>(&self, value: &T) -> FixupResult<T> {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use super::{InferCtxt, FixupError, FixupResult};
|
||||
use super::{InferCtxt, FixupError, FixupResult, Span, type_variable::TypeVariableOrigin};
|
||||
use crate::ty::{self, Ty, TyCtxt, TypeFoldable};
|
||||
use crate::ty::fold::{TypeFolder, TypeVisitor};
|
||||
|
||||
|
|
@ -77,17 +77,20 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for OpportunisticTypeAndRegionResolv
|
|||
///////////////////////////////////////////////////////////////////////////
|
||||
// UNRESOLVED TYPE FINDER
|
||||
|
||||
/// The unresolved type **finder** walks your type and searches for
|
||||
/// type variables that don't yet have a value. They get pushed into a
|
||||
/// vector. It does not construct the fully resolved type (which might
|
||||
/// The unresolved type **finder** walks a type searching for
|
||||
/// type variables that don't yet have a value. The first unresolved type is stored.
|
||||
/// It does not construct the fully resolved type (which might
|
||||
/// involve some hashing and so forth).
|
||||
pub struct UnresolvedTypeFinder<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
||||
|
||||
/// Used to find the type parameter name and location for error reporting.
|
||||
pub first_unresolved: Option<(Ty<'tcx>,Option<Span>)>,
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> UnresolvedTypeFinder<'a, 'gcx, 'tcx> {
|
||||
pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
|
||||
UnresolvedTypeFinder { infcx }
|
||||
UnresolvedTypeFinder { infcx, first_unresolved: None }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,22 +98,37 @@ impl<'a, 'gcx, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'gcx, 'tcx>
|
|||
fn visit_ty(&mut self, t: Ty<'tcx>) -> bool {
|
||||
let t = self.infcx.shallow_resolve(t);
|
||||
if t.has_infer_types() {
|
||||
if let ty::Infer(_) = t.sty {
|
||||
if let ty::Infer(infer_ty) = t.sty {
|
||||
// Since we called `shallow_resolve` above, this must
|
||||
// be an (as yet...) unresolved inference variable.
|
||||
true
|
||||
let ty_var_span =
|
||||
if let ty::TyVar(ty_vid) = infer_ty {
|
||||
let ty_vars = self.infcx.type_variables.borrow();
|
||||
if let TypeVariableOrigin::TypeParameterDefinition(span, _name)
|
||||
= *ty_vars.var_origin(ty_vid)
|
||||
{
|
||||
Some(span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.first_unresolved = Some((t, ty_var_span));
|
||||
true // Halt visiting.
|
||||
} else {
|
||||
// Otherwise, visit its contents.
|
||||
t.super_visit_with(self)
|
||||
}
|
||||
} else {
|
||||
// Micro-optimize: no inference types at all Can't have unresolved type
|
||||
// variables, no need to visit the contents.
|
||||
// All type variables in inference types must already be resolved,
|
||||
// - no need to visit the contents, continue visiting.
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// FULL TYPE RESOLUTION
|
||||
|
||||
|
|
|
|||
|
|
@ -594,7 +594,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
|
|||
|
||||
// Once we have inferred everything we need to know, we
|
||||
// can ignore the `obligations` from that point on.
|
||||
if !infcx.any_unresolved_type_vars(&ty.value) {
|
||||
if infcx.unresolved_type_vars(&ty.value).is_none() {
|
||||
infcx.projection_cache.borrow_mut().complete_normalized(cache_key, &ty);
|
||||
// No need to extend `obligations`.
|
||||
} else {
|
||||
|
|
@ -704,7 +704,7 @@ fn opt_normalize_projection_type<'a, 'b, 'gcx, 'tcx>(
|
|||
fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
|
||||
result: &NormalizedTy<'tcx>)
|
||||
-> NormalizedTy<'tcx> {
|
||||
if !infcx.any_unresolved_type_vars(&result.value) {
|
||||
if infcx.unresolved_type_vars(&result.value).is_none() {
|
||||
return NormalizedTy { value: result.value, obligations: vec![] };
|
||||
}
|
||||
|
||||
|
|
@ -722,7 +722,7 @@ fn prune_cache_value_obligations<'a, 'gcx, 'tcx>(infcx: &'a InferCtxt<'a, 'gcx,
|
|||
// but we have `T: Foo<X = ?1>` and `?1: Bar<X =
|
||||
// ?0>`).
|
||||
ty::Predicate::Projection(ref data) =>
|
||||
infcx.any_unresolved_type_vars(&data.ty()),
|
||||
infcx.unresolved_type_vars(&data.ty()).is_some(),
|
||||
|
||||
// We are only interested in `T: Foo<X = U>` predicates, whre
|
||||
// `U` references one of `unresolved_type_vars`. =)
|
||||
|
|
|
|||
|
|
@ -54,12 +54,16 @@ impl<'a, 'gcx, 'tcx> InteriorVisitor<'a, 'gcx, 'tcx> {
|
|||
debug!("type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}",
|
||||
expr, scope, ty, self.expr_count, yield_span);
|
||||
|
||||
if self.fcx.any_unresolved_type_vars(&ty) {
|
||||
let mut err = struct_span_err!(self.fcx.tcx.sess, source_span, E0698,
|
||||
"type inside generator must be known in this context");
|
||||
err.span_note(yield_span,
|
||||
"the type is part of the generator because of this `yield`");
|
||||
err.emit();
|
||||
if let Some((unresolved_type, unresolved_type_span)) =
|
||||
self.fcx.unresolved_type_vars(&ty)
|
||||
{
|
||||
// If unresolved type isn't a ty_var then unresolved_type_span is None
|
||||
self.fcx.need_type_info_err_in_generator(
|
||||
unresolved_type_span.unwrap_or(yield_span),
|
||||
unresolved_type)
|
||||
.span_note(yield_span,
|
||||
"the type is part of the generator because of this `yield`")
|
||||
.emit();
|
||||
} else {
|
||||
// Map the type to the number of types added before it
|
||||
let entries = self.types.len();
|
||||
|
|
|
|||
|
|
@ -4728,7 +4728,6 @@ register_diagnostics! {
|
|||
E0640, // infer outlives requirements
|
||||
E0641, // cannot cast to/from a pointer with an unknown kind
|
||||
E0645, // trait aliases not finished
|
||||
E0698, // type inside generator must be known in this context
|
||||
E0719, // duplicate values for associated type binding
|
||||
E0722, // Malformed #[optimize] attribute
|
||||
E0724, // `#[ffi_returns_twice]` is only allowed in foreign functions
|
||||
|
|
|
|||
14
src/test/ui/generator/unresolved_type_param.rs
Normal file
14
src/test/ui/generator/unresolved_type_param.rs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
// Provoke an unresolved type error (T).
|
||||
// Error message should pinpoint the type parameter T as needing to be bound
|
||||
// (rather than give a general error message)
|
||||
// edition:2018
|
||||
#![feature(futures_api, async_await, await_macro)]
|
||||
async fn bar<T>() -> () {}
|
||||
|
||||
async fn foo() {
|
||||
await!(bar());
|
||||
//~^ ERROR type inside generator must be known in this context
|
||||
//~| NOTE cannot infer type for `T`
|
||||
//~| NOTE the type is part of the generator because of this `yield`
|
||||
}
|
||||
fn main() {}
|
||||
16
src/test/ui/generator/unresolved_type_param.stderr
Normal file
16
src/test/ui/generator/unresolved_type_param.stderr
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
error[E0698]: type inside generator must be known in this context
|
||||
--> $DIR/unresolved_type_param.rs:9:16
|
||||
|
|
||||
LL | await!(bar());
|
||||
| ^^^ cannot infer type for `T`
|
||||
|
|
||||
note: the type is part of the generator because of this `yield`
|
||||
--> $DIR/unresolved_type_param.rs:9:9
|
||||
|
|
||||
LL | await!(bar());
|
||||
| ^^^^^^^^^^^^^^
|
||||
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0698`.
|
||||
Loading…
Add table
Add a link
Reference in a new issue