Skip to content

Improved error message when type must be bound due to generator. #59111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/librustc/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
44 changes: 31 additions & 13 deletions src/librustc/infer/error_reporting/need_type_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
}
}
}
7 changes: 4 additions & 3 deletions src/librustc/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
36 changes: 27 additions & 9 deletions src/librustc/infer/resolve.rs
Original file line number Diff line number Diff line change
@@ -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};

Expand Down Expand Up @@ -77,40 +77,58 @@ 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 }
}
}

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

Expand Down
6 changes: 3 additions & 3 deletions src/librustc/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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![] };
}

Expand All @@ -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`. =)
Expand Down
16 changes: 10 additions & 6 deletions src/librustc_typeck/check/generator_interior.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
1 change: 0 additions & 1 deletion src/librustc_typeck/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 14 additions & 0 deletions src/test/ui/generator/unresolved_type_param.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 16 additions & 0 deletions src/test/ui/generator/unresolved_type_param.stderr
Original file line number Diff line number Diff line change
@@ -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`.