about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGiles Cope <gilescope@gmail.com>2019-03-11 18:46:20 +0000
committerGiles Cope <gilescope@gmail.com>2019-04-25 16:42:59 +0100
commit66e41bc675e9e7c8fc649bc088b1d48857610fb2 (patch)
tree4385ecb3a6d347f59c17c6a94e46514264bc33dd
parent3bee49f42b6dfb039d2a8e59e5181e26531c3c11 (diff)
downloadrust-66e41bc675e9e7c8fc649bc088b1d48857610fb2.tar.gz
rust-66e41bc675e9e7c8fc649bc088b1d48857610fb2.zip
Improved error message when type must be bound due to generator.
Error now mentions type var name and span is highlighted.
-rw-r--r--src/librustc/error_codes.rs30
-rw-r--r--src/librustc/infer/error_reporting/need_type_info.rs44
-rw-r--r--src/librustc/infer/mod.rs7
-rw-r--r--src/librustc/infer/resolve.rs36
-rw-r--r--src/librustc/traits/project.rs6
-rw-r--r--src/librustc_typeck/check/generator_interior.rs16
-rw-r--r--src/librustc_typeck/error_codes.rs1
-rw-r--r--src/test/ui/generator/unresolved_type_param.rs14
-rw-r--r--src/test/ui/generator/unresolved_type_param.stderr16
9 files changed, 135 insertions, 35 deletions
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 @@ 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.
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 @@ 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)
+        }
+    }
 }
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 @@ 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> {
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 @@ 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
 
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<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`. =)
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 @@ 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();
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 @@ 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
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<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() {}
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`.