Skip to content

Commit

Permalink
Auto merge of #59111 - gilescope:generator-better-errors, r=nikomatsakis
Browse files Browse the repository at this point in the history
Improved error message when type must be bound due to generator.

Fixes #58930.

Keen to get some feedback - is this as minimal as we can get it or is there an existing visitor I could repurpose?
  • Loading branch information
bors committed Apr 25, 2019
2 parents c32171b + 66e41bc commit 3991285
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 35 deletions.
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 @@ -1309,17 +1309,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`.

0 comments on commit 3991285

Please sign in to comment.