From 66e41bc675e9e7c8fc649bc088b1d48857610fb2 Mon Sep 17 00:00:00 2001 From: Giles Cope Date: Mon, 11 Mar 2019 18:46:20 +0000 Subject: [PATCH] Improved error message when type must be bound due to generator. Error now mentions type var name and span is highlighted. --- src/librustc/error_codes.rs | 30 +++++++++++++ .../infer/error_reporting/need_type_info.rs | 44 +++++++++++++------ src/librustc/infer/mod.rs | 7 +-- src/librustc/infer/resolve.rs | 36 +++++++++++---- src/librustc/traits/project.rs | 6 +-- .../check/generator_interior.rs | 16 ++++--- src/librustc_typeck/error_codes.rs | 1 - .../ui/generator/unresolved_type_param.rs | 14 ++++++ .../ui/generator/unresolved_type_param.stderr | 16 +++++++ 9 files changed, 135 insertions(+), 35 deletions(-) create mode 100644 src/test/ui/generator/unresolved_type_param.rs create mode 100644 src/test/ui/generator/unresolved_type_param.stderr diff --git a/src/librustc/error_codes.rs b/src/librustc/error_codes.rs index 00f9fa3a938..f6917c45d57 100644 --- a/src/librustc/error_codes.rs +++ b/src/librustc/error_codes.rs @@ -2043,6 +2043,36 @@ struct Foo { 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() -> () {} + +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() -> () {} + +async fn foo() { + await!(bar::()); + // ^^^^^^^^ specify type explicitly +} +``` +"##, + E0700: r##" The `impl Trait` return type captures lifetime parameters that do not appear within the `impl Trait` itself. diff --git a/src/librustc/infer/error_reporting/need_type_info.rs b/src/librustc/infer/error_reporting/need_type_info.rs index 0a83b839201..2c01e1c0de3 100644 --- a/src/librustc/infer/error_reporting/need_type_info.rs +++ b/src/librustc/infer/error_reporting/need_type_info.rs @@ -88,23 +88,17 @@ pub fn extract_type_name( s } - pub fn need_type_info_err(&self, - body_id: Option, - span: Span, - ty: Ty<'tcx>) - -> DiagnosticBuilder<'gcx> { + pub fn need_type_info_err( + &self, + body_id: Option, + 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 @@ pub fn need_type_info_err(&self, 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) + } + } } diff --git a/src/librustc/infer/mod.rs b/src/librustc/infer/mod.rs index 747f0a6ae87..f2ccffd9c92 100644 --- a/src/librustc/infer/mod.rs +++ b/src/librustc/infer/mod.rs @@ -1312,17 +1312,18 @@ pub fn resolve_type_vars_if_possible(&self, value: &T) -> T 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(&self, value: &T) -> bool + pub fn unresolved_type_vars(&self, value: &T) -> Option<(Ty<'tcx>, Option)> 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>(&self, value: &T) -> FixupResult { diff --git a/src/librustc/infer/resolve.rs b/src/librustc/infer/resolve.rs index 4a8f0c34ead..6adbf2bcef8 100644 --- a/src/librustc/infer/resolve.rs +++ b/src/librustc/infer/resolve.rs @@ -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 @@ fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { /////////////////////////////////////////////////////////////////////////// // 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)>, } 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 diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index 360e2323b64..882635e21f5 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -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` and `?1: Bar`). 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` predicates, whre // `U` references one of `unresolved_type_vars`. =) diff --git a/src/librustc_typeck/check/generator_interior.rs b/src/librustc_typeck/check/generator_interior.rs index 7f4b0a96a15..0866c57616e 100644 --- a/src/librustc_typeck/check/generator_interior.rs +++ b/src/librustc_typeck/check/generator_interior.rs @@ -54,12 +54,16 @@ fn record(&mut self, 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(); diff --git a/src/librustc_typeck/error_codes.rs b/src/librustc_typeck/error_codes.rs index 22f24df450f..0b435be7bfc 100644 --- a/src/librustc_typeck/error_codes.rs +++ b/src/librustc_typeck/error_codes.rs @@ -4728,7 +4728,6 @@ fn make_recursive_type() -> impl Sized { 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 diff --git a/src/test/ui/generator/unresolved_type_param.rs b/src/test/ui/generator/unresolved_type_param.rs new file mode 100644 index 00000000000..f49369b125f --- /dev/null +++ b/src/test/ui/generator/unresolved_type_param.rs @@ -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() -> () {} + +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() {} diff --git a/src/test/ui/generator/unresolved_type_param.stderr b/src/test/ui/generator/unresolved_type_param.stderr new file mode 100644 index 00000000000..57ccdda3f43 --- /dev/null +++ b/src/test/ui/generator/unresolved_type_param.stderr @@ -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`. -- GitLab