about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-04-14 06:47:38 +0000
committerbors <bors@rust-lang.org>2020-04-14 06:47:38 +0000
commita3ef360368ac4c97883dc45287e752517e308f0e (patch)
tree6ef048145715383fd1dbaa53a1e1f7fb1798f3c5 /src
parent513a6473d69b3af34e6cdaa4efb288fe5283c3e9 (diff)
parent4326d959f460321ed6f818a015f157af46ec5848 (diff)
downloadrust-a3ef360368ac4c97883dc45287e752517e308f0e.tar.gz
rust-a3ef360368ac4c97883dc45287e752517e308f0e.zip
Auto merge of #70679 - tmandry:issue-68112, r=nikomatsakis
Improve async-await/generator obligation errors in some cases

Fixes #68112.

This change is best read one commit at a time (I add a test at the beginning and update it in each change after).

The `test2` function is a case I found while writing the test that we don't handle with this code yet. I don't attempt to fix it in this PR, but it's a good candidate for future work.

r? @davidtwco, @nikomatsakis
Diffstat (limited to 'src')
-rw-r--r--src/librustc_ast_lowering/expr.rs4
-rw-r--r--src/librustc_hir/hir.rs15
-rw-r--r--src/librustc_trait_selection/traits/error_reporting/suggestions.rs244
-rw-r--r--src/librustc_typeck/check/expr.rs2
-rw-r--r--src/test/ui/async-await/async-fn-nonsend.stderr6
-rw-r--r--src/test/ui/async-await/issue-64130-1-sync.stderr2
-rw-r--r--src/test/ui/async-await/issue-64130-2-send.stderr2
-rw-r--r--src/test/ui/async-await/issue-64130-3-other.stderr2
-rw-r--r--src/test/ui/async-await/issue-64130-4-async-move.stderr4
-rw-r--r--src/test/ui/async-await/issue-64130-non-send-future-diags.stderr2
-rw-r--r--src/test/ui/async-await/issue-67252-unnamed-future.stderr4
-rw-r--r--src/test/ui/async-await/issue-68112.rs64
-rw-r--r--src/test/ui/async-await/issue-68112.stderr56
-rw-r--r--src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.stderr4
-rw-r--r--src/test/ui/generator/issue-68112.rs56
-rw-r--r--src/test/ui/generator/issue-68112.stderr40
-rw-r--r--src/test/ui/generator/not-send-sync.rs2
-rw-r--r--src/test/ui/generator/not-send-sync.stderr8
18 files changed, 411 insertions, 106 deletions
diff --git a/src/librustc_ast_lowering/expr.rs b/src/librustc_ast_lowering/expr.rs
index 9984eb4e282..2fc58efbf0e 100644
--- a/src/librustc_ast_lowering/expr.rs
+++ b/src/librustc_ast_lowering/expr.rs
@@ -590,6 +590,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             await_span,
             self.allow_gen_future.clone(),
         );
+        let expr = self.lower_expr(expr);
 
         let pinned_ident = Ident::with_dummy_span(sym::pinned);
         let (pinned_pat, pinned_pat_hid) =
@@ -671,7 +672,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             let unit = self.expr_unit(span);
             let yield_expr = self.expr(
                 span,
-                hir::ExprKind::Yield(unit, hir::YieldSource::Await),
+                hir::ExprKind::Yield(unit, hir::YieldSource::Await { expr: Some(expr.hir_id) }),
                 ThinVec::new(),
             );
             let yield_expr = self.arena.alloc(yield_expr);
@@ -704,7 +705,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
         // match <expr> {
         //     mut pinned => loop { .. }
         // }
-        let expr = self.lower_expr(expr);
         hir::ExprKind::Match(expr, arena_vec![self; pinned_arm], hir::MatchSource::AwaitDesugar)
     }
 
diff --git a/src/librustc_hir/hir.rs b/src/librustc_hir/hir.rs
index 172d1e263ea..b66e6101b50 100644
--- a/src/librustc_hir/hir.rs
+++ b/src/librustc_hir/hir.rs
@@ -1736,15 +1736,24 @@ pub struct Destination {
 #[derive(Copy, Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)]
 pub enum YieldSource {
     /// An `<expr>.await`.
-    Await,
+    Await { expr: Option<HirId> },
     /// A plain `yield`.
     Yield,
 }
 
+impl YieldSource {
+    pub fn is_await(&self) -> bool {
+        match self {
+            YieldSource::Await { .. } => true,
+            YieldSource::Yield => false,
+        }
+    }
+}
+
 impl fmt::Display for YieldSource {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.write_str(match self {
-            YieldSource::Await => "`await`",
+            YieldSource::Await { .. } => "`await`",
             YieldSource::Yield => "`yield`",
         })
     }
@@ -1755,7 +1764,7 @@ impl From<GeneratorKind> for YieldSource {
         match kind {
             // Guess based on the kind of the current generator.
             GeneratorKind::Gen => Self::Yield,
-            GeneratorKind::Async(_) => Self::Await,
+            GeneratorKind::Async(_) => Self::Await { expr: None },
         }
     }
 }
diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
index 53e73ed3906..254db6cb869 100644
--- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
+++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
@@ -10,7 +10,7 @@ use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
-use rustc_hir::Node;
+use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
 use rustc_middle::ty::TypeckTables;
 use rustc_middle::ty::{
     self, AdtKind, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
@@ -127,13 +127,14 @@ pub trait InferCtxtExt<'tcx> {
         scope_span: &Option<Span>,
         expr: Option<hir::HirId>,
         snippet: String,
-        first_generator: DefId,
-        last_generator: Option<DefId>,
+        inner_generator_body: Option<&hir::Body<'_>>,
+        outer_generator: Option<DefId>,
         trait_ref: ty::TraitRef<'_>,
         target_ty: Ty<'tcx>,
         tables: &ty::TypeckTables<'_>,
         obligation: &PredicateObligation<'tcx>,
         next_code: Option<&ObligationCauseCode<'tcx>>,
+        from_awaited_ty: Option<Span>,
     );
 
     fn note_obligation_cause_code<T>(
@@ -1094,6 +1095,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             obligation.predicate, obligation.cause.span
         );
         let source_map = self.tcx.sess.source_map();
+        let hir = self.tcx.hir();
 
         // Attempt to detect an async-await error by looking at the obligation causes, looking
         // for a generator to be present.
@@ -1118,8 +1120,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         // - `BindingObligation` with `impl_send (Send requirement)
         //
         // The first obligation in the chain is the most useful and has the generator that captured
-        // the type. The last generator has information about where the bound was introduced. At
-        // least one generator should be present for this diagnostic to be modified.
+        // the type. The last generator (`outer_generator` below) has information about where the
+        // bound was introduced. At least one generator should be present for this diagnostic to be
+        // modified.
         let (mut trait_ref, mut target_ty) = match obligation.predicate {
             ty::Predicate::Trait(p, _) => {
                 (Some(p.skip_binder().trait_ref), Some(p.skip_binder().self_ty()))
@@ -1127,7 +1130,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             _ => (None, None),
         };
         let mut generator = None;
-        let mut last_generator = None;
+        let mut outer_generator = None;
         let mut next_code = Some(&obligation.cause.code);
         while let Some(code) = next_code {
             debug!("maybe_note_obligation_cause_for_async_await: code={:?}", code);
@@ -1144,7 +1147,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                     match ty.kind {
                         ty::Generator(did, ..) => {
                             generator = generator.or(Some(did));
-                            last_generator = Some(did);
+                            outer_generator = Some(did);
                         }
                         ty::GeneratorWitness(..) => {}
                         _ if generator.is_none() => {
@@ -1176,7 +1179,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         let span = self.tcx.def_span(generator_did);
 
         // Do not ICE on closure typeck (#66868).
-        if self.tcx.hir().as_local_hir_id(generator_did).is_none() {
+        if hir.as_local_hir_id(generator_did).is_none() {
             return false;
         }
 
@@ -1202,37 +1205,63 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             }
         };
 
+        let generator_body = hir
+            .as_local_hir_id(generator_did)
+            .and_then(|hir_id| hir.maybe_body_owned_by(hir_id))
+            .map(|body_id| hir.body(body_id));
+        let mut visitor = AwaitsVisitor::default();
+        if let Some(body) = generator_body {
+            visitor.visit_body(body);
+        }
+        debug!("maybe_note_obligation_cause_for_async_await: awaits = {:?}", visitor.awaits);
+
         // Look for a type inside the generator interior that matches the target type to get
         // a span.
         let target_ty_erased = self.tcx.erase_regions(&target_ty);
+        let ty_matches = |ty| -> bool {
+            // Careful: the regions for types that appear in the
+            // generator interior are not generally known, so we
+            // want to erase them when comparing (and anyway,
+            // `Send` and other bounds are generally unaffected by
+            // the choice of region).  When erasing regions, we
+            // also have to erase late-bound regions. This is
+            // because the types that appear in the generator
+            // interior generally contain "bound regions" to
+            // represent regions that are part of the suspended
+            // generator frame. Bound regions are preserved by
+            // `erase_regions` and so we must also call
+            // `erase_late_bound_regions`.
+            let ty_erased = self.tcx.erase_late_bound_regions(&ty::Binder::bind(ty));
+            let ty_erased = self.tcx.erase_regions(&ty_erased);
+            let eq = ty::TyS::same_type(ty_erased, target_ty_erased);
+            debug!(
+                "maybe_note_obligation_cause_for_async_await: ty_erased={:?} \
+                    target_ty_erased={:?} eq={:?}",
+                ty_erased, target_ty_erased, eq
+            );
+            eq
+        };
         let target_span = tables
             .generator_interior_types
             .iter()
-            .find(|ty::GeneratorInteriorTypeCause { ty, .. }| {
-                // Careful: the regions for types that appear in the
-                // generator interior are not generally known, so we
-                // want to erase them when comparing (and anyway,
-                // `Send` and other bounds are generally unaffected by
-                // the choice of region).  When erasing regions, we
-                // also have to erase late-bound regions. This is
-                // because the types that appear in the generator
-                // interior generally contain "bound regions" to
-                // represent regions that are part of the suspended
-                // generator frame. Bound regions are preserved by
-                // `erase_regions` and so we must also call
-                // `erase_late_bound_regions`.
-                let ty_erased = self.tcx.erase_late_bound_regions(&ty::Binder::bind(*ty));
-                let ty_erased = self.tcx.erase_regions(&ty_erased);
-                let eq = ty::TyS::same_type(ty_erased, target_ty_erased);
-                debug!(
-                    "maybe_note_obligation_cause_for_async_await: ty_erased={:?} \
-                        target_ty_erased={:?} eq={:?}",
-                    ty_erased, target_ty_erased, eq
-                );
-                eq
-            })
-            .map(|ty::GeneratorInteriorTypeCause { span, scope_span, expr, .. }| {
-                (span, source_map.span_to_snippet(*span), scope_span, expr)
+            .find(|ty::GeneratorInteriorTypeCause { ty, .. }| ty_matches(ty))
+            .map(|cause| {
+                // Check to see if any awaited expressions have the target type.
+                let from_awaited_ty = visitor
+                    .awaits
+                    .into_iter()
+                    .map(|id| hir.expect_expr(id))
+                    .find(|await_expr| {
+                        let ty = tables.expr_ty_adjusted(&await_expr);
+                        debug!(
+                            "maybe_note_obligation_cause_for_async_await: await_expr={:?}",
+                            await_expr
+                        );
+                        ty_matches(ty)
+                    })
+                    .map(|expr| expr.span);
+                let ty::GeneratorInteriorTypeCause { span, scope_span, expr, .. } = cause;
+                (span, source_map.span_to_snippet(*span), scope_span, expr, from_awaited_ty)
             });
 
         debug!(
@@ -1240,20 +1269,21 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 generator_interior_types={:?} target_span={:?}",
             target_ty, tables.generator_interior_types, target_span
         );
-        if let Some((target_span, Ok(snippet), scope_span, expr)) = target_span {
+        if let Some((target_span, Ok(snippet), scope_span, expr, from_awaited_ty)) = target_span {
             self.note_obligation_cause_for_async_await(
                 err,
                 *target_span,
                 scope_span,
                 *expr,
                 snippet,
-                generator_did,
-                last_generator,
+                generator_body,
+                outer_generator,
                 trait_ref,
                 target_ty,
                 tables,
                 obligation,
                 next_code,
+                from_awaited_ty,
             );
             true
         } else {
@@ -1270,35 +1300,27 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         scope_span: &Option<Span>,
         expr: Option<hir::HirId>,
         snippet: String,
-        first_generator: DefId,
-        last_generator: Option<DefId>,
+        inner_generator_body: Option<&hir::Body<'_>>,
+        outer_generator: Option<DefId>,
         trait_ref: ty::TraitRef<'_>,
         target_ty: Ty<'tcx>,
         tables: &ty::TypeckTables<'_>,
         obligation: &PredicateObligation<'tcx>,
         next_code: Option<&ObligationCauseCode<'tcx>>,
+        from_awaited_ty: Option<Span>,
     ) {
         let source_map = self.tcx.sess.source_map();
 
-        let is_async_fn = self
-            .tcx
-            .parent(first_generator)
-            .map(|parent_did| self.tcx.asyncness(parent_did))
-            .map(|parent_asyncness| parent_asyncness == hir::IsAsync::Async)
-            .unwrap_or(false);
-        let is_async_move = self
-            .tcx
-            .hir()
-            .as_local_hir_id(first_generator)
-            .and_then(|hir_id| self.tcx.hir().maybe_body_owned_by(hir_id))
-            .map(|body_id| self.tcx.hir().body(body_id))
+        let is_async = inner_generator_body
             .and_then(|body| body.generator_kind())
             .map(|generator_kind| match generator_kind {
                 hir::GeneratorKind::Async(..) => true,
                 _ => false,
             })
             .unwrap_or(false);
-        let await_or_yield = if is_async_fn || is_async_move { "await" } else { "yield" };
+        let (await_or_yield, an_await_or_yield) =
+            if is_async { ("await", "an await") } else { ("yield", "a yield") };
+        let future_or_generator = if is_async { "future" } else { "generator" };
 
         // Special case the primary error message when send or sync is the trait that was
         // not implemented.
@@ -1311,22 +1333,34 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
 
             err.clear_code();
             err.set_primary_message(format!(
-                "future cannot be {} between threads safely",
-                trait_verb
+                "{} cannot be {} between threads safely",
+                future_or_generator, trait_verb
             ));
 
             let original_span = err.span.primary_span().unwrap();
             let mut span = MultiSpan::from_span(original_span);
 
-            let message = if let Some(name) = last_generator
-                .and_then(|generator_did| self.tcx.parent(generator_did))
-                .and_then(|parent_did| hir.as_local_hir_id(parent_did))
-                .and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
-            {
-                format!("future returned by `{}` is not {}", name, trait_name)
-            } else {
-                format!("future is not {}", trait_name)
-            };
+            let message = outer_generator
+                .and_then(|generator_did| {
+                    Some(match self.tcx.generator_kind(generator_did).unwrap() {
+                        GeneratorKind::Gen => format!("generator is not {}", trait_name),
+                        GeneratorKind::Async(AsyncGeneratorKind::Fn) => self
+                            .tcx
+                            .parent(generator_did)
+                            .and_then(|parent_did| hir.as_local_hir_id(parent_did))
+                            .and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
+                            .map(|name| {
+                                format!("future returned by `{}` is not {}", name, trait_name)
+                            })?,
+                        GeneratorKind::Async(AsyncGeneratorKind::Block) => {
+                            format!("future created by async block is not {}", trait_name)
+                        }
+                        GeneratorKind::Async(AsyncGeneratorKind::Closure) => {
+                            format!("future created by async closure is not {}", trait_name)
+                        }
+                    })
+                })
+                .unwrap_or_else(|| format!("{} is not {}", future_or_generator, trait_name));
 
             span.push_span_label(original_span, message);
             err.set_span(span);
@@ -1336,31 +1370,56 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
             format!("does not implement `{}`", trait_ref.print_only_trait_path())
         };
 
-        // Look at the last interior type to get a span for the `.await`.
-        let await_span = tables.generator_interior_types.iter().map(|t| t.span).last().unwrap();
-        let mut span = MultiSpan::from_span(await_span);
-        span.push_span_label(
-            await_span,
-            format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet),
-        );
+        if let Some(await_span) = from_awaited_ty {
+            // The type causing this obligation is one being awaited at await_span.
+            let mut span = MultiSpan::from_span(await_span);
 
-        span.push_span_label(target_span, format!("has type `{}`", target_ty));
+            span.push_span_label(
+                await_span,
+                format!("await occurs here on type `{}`, which {}", target_ty, trait_explanation),
+            );
 
-        // If available, use the scope span to annotate the drop location.
-        if let Some(scope_span) = scope_span {
+            err.span_note(
+                span,
+                &format!(
+                    "future {not_trait} as it awaits another future which {not_trait}",
+                    not_trait = trait_explanation
+                ),
+            );
+        } else {
+            // Look at the last interior type to get a span for the `.await`.
+            debug!(
+                "note_obligation_cause_for_async_await generator_interior_types: {:#?}",
+                tables.generator_interior_types
+            );
+            let await_span = tables.generator_interior_types.iter().map(|t| t.span).last().unwrap();
+            let mut span = MultiSpan::from_span(await_span);
             span.push_span_label(
-                source_map.end_point(*scope_span),
-                format!("`{}` is later dropped here", snippet),
+                await_span,
+                format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet),
             );
-        }
 
-        err.span_note(
-            span,
-            &format!(
-                "future {} as this value is used across an {}",
-                trait_explanation, await_or_yield,
-            ),
-        );
+            span.push_span_label(
+                target_span,
+                format!("has type `{}` which {}", target_ty, trait_explanation),
+            );
+
+            // If available, use the scope span to annotate the drop location.
+            if let Some(scope_span) = scope_span {
+                span.push_span_label(
+                    source_map.end_point(*scope_span),
+                    format!("`{}` is later dropped here", snippet),
+                );
+            }
+
+            err.span_note(
+                span,
+                &format!(
+                    "{} {} as this value is used across {}",
+                    future_or_generator, trait_explanation, an_await_or_yield
+                ),
+            );
+        }
 
         if let Some(expr_id) = expr {
             let expr = hir.expect_expr(expr_id);
@@ -1705,6 +1764,27 @@ impl<'v> Visitor<'v> for ReturnsVisitor<'v> {
     }
 }
 
+/// Collect all the awaited expressions within the input expression.
+#[derive(Default)]
+struct AwaitsVisitor {
+    awaits: Vec<hir::HirId>,
+}
+
+impl<'v> Visitor<'v> for AwaitsVisitor {
+    type Map = hir::intravisit::ErasedMap<'v>;
+
+    fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
+        hir::intravisit::NestedVisitorMap::None
+    }
+
+    fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
+        if let hir::ExprKind::Yield(_, hir::YieldSource::Await { expr: Some(id) }) = ex.kind {
+            self.awaits.push(id)
+        }
+        hir::intravisit::walk_expr(self, ex)
+    }
+}
+
 pub trait NextTypeParamName {
     fn next_type_param_name(&self, name: Option<&str>) -> String;
 }
diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index 57e2349bb2e..7cb51b4d6d8 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -1797,7 +1797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // we know that the yield type must be `()`; however, the context won't contain this
             // information. Hence, we check the source of the yield expression here and check its
             // value's type against `()` (this check should always hold).
-            None if src == &hir::YieldSource::Await => {
+            None if src.is_await() => {
                 self.check_expr_coercable_to_type(&value, self.tcx.mk_unit());
                 self.tcx.mk_unit()
             }
diff --git a/src/test/ui/async-await/async-fn-nonsend.stderr b/src/test/ui/async-await/async-fn-nonsend.stderr
index 04df6203e43..d36d59f1f68 100644
--- a/src/test/ui/async-await/async-fn-nonsend.stderr
+++ b/src/test/ui/async-await/async-fn-nonsend.stderr
@@ -12,7 +12,7 @@ note: future is not `Send` as this value is used across an await
   --> $DIR/async-fn-nonsend.rs:24:5
    |
 LL |     let x = non_send();
-   |         - has type `impl std::fmt::Debug`
+   |         - has type `impl std::fmt::Debug` which is not `Send`
 LL |     drop(x);
 LL |     fut().await;
    |     ^^^^^^^^^^^ await occurs here, with `x` maybe used later
@@ -33,7 +33,7 @@ note: future is not `Send` as this value is used across an await
   --> $DIR/async-fn-nonsend.rs:33:20
    |
 LL |     match Some(non_send()) {
-   |                ---------- has type `impl std::fmt::Debug`
+   |                ---------- has type `impl std::fmt::Debug` which is not `Send`
 LL |         Some(_) => fut().await,
    |                    ^^^^^^^^^^^ await occurs here, with `non_send()` maybe used later
 ...
@@ -54,7 +54,7 @@ note: future is not `Send` as this value is used across an await
   --> $DIR/async-fn-nonsend.rs:42:9
    |
 LL |     let f: &mut std::fmt::Formatter = panic!();
-   |         - has type `&mut std::fmt::Formatter<'_>`
+   |         - has type `&mut std::fmt::Formatter<'_>` which is not `Send`
 LL |     if non_sync().fmt(f).unwrap() == () {
 LL |         fut().await;
    |         ^^^^^^^^^^^ await occurs here, with `f` maybe used later
diff --git a/src/test/ui/async-await/issue-64130-1-sync.stderr b/src/test/ui/async-await/issue-64130-1-sync.stderr
index b7a88c10e74..42e9e4642ce 100644
--- a/src/test/ui/async-await/issue-64130-1-sync.stderr
+++ b/src/test/ui/async-await/issue-64130-1-sync.stderr
@@ -12,7 +12,7 @@ note: future is not `Sync` as this value is used across an await
   --> $DIR/issue-64130-1-sync.rs:15:5
    |
 LL |     let x = Foo;
-   |         - has type `Foo`
+   |         - has type `Foo` which is not `Sync`
 LL |     baz().await;
    |     ^^^^^^^^^^^ await occurs here, with `x` maybe used later
 LL | }
diff --git a/src/test/ui/async-await/issue-64130-2-send.stderr b/src/test/ui/async-await/issue-64130-2-send.stderr
index ec183088771..f6f834618d3 100644
--- a/src/test/ui/async-await/issue-64130-2-send.stderr
+++ b/src/test/ui/async-await/issue-64130-2-send.stderr
@@ -12,7 +12,7 @@ note: future is not `Send` as this value is used across an await
   --> $DIR/issue-64130-2-send.rs:15:5
    |
 LL |     let x = Foo;
-   |         - has type `Foo`
+   |         - has type `Foo` which is not `Send`
 LL |     baz().await;
    |     ^^^^^^^^^^^ await occurs here, with `x` maybe used later
 LL | }
diff --git a/src/test/ui/async-await/issue-64130-3-other.stderr b/src/test/ui/async-await/issue-64130-3-other.stderr
index 6b40cc9184d..3475b66b375 100644
--- a/src/test/ui/async-await/issue-64130-3-other.stderr
+++ b/src/test/ui/async-await/issue-64130-3-other.stderr
@@ -16,7 +16,7 @@ note: future does not implement `Qux` as this value is used across an await
   --> $DIR/issue-64130-3-other.rs:18:5
    |
 LL |     let x = Foo;
-   |         - has type `Foo`
+   |         - has type `Foo` which does not implement `Qux`
 LL |     baz().await;
    |     ^^^^^^^^^^^ await occurs here, with `x` maybe used later
 LL | }
diff --git a/src/test/ui/async-await/issue-64130-4-async-move.stderr b/src/test/ui/async-await/issue-64130-4-async-move.stderr
index 1e52d74f155..fc231d394c1 100644
--- a/src/test/ui/async-await/issue-64130-4-async-move.stderr
+++ b/src/test/ui/async-await/issue-64130-4-async-move.stderr
@@ -2,7 +2,7 @@ error: future cannot be sent between threads safely
   --> $DIR/issue-64130-4-async-move.rs:15:17
    |
 LL |   pub fn foo() -> impl Future + Send {
-   |                   ^^^^^^^^^^^^^^^^^^ future returned by `foo` is not `Send`
+   |                   ^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
 ...
 LL | /     async move {
 LL | |         match client.status() {
@@ -18,7 +18,7 @@ note: future is not `Send` as this value is used across an await
   --> $DIR/issue-64130-4-async-move.rs:21:26
    |
 LL |         match client.status() {
-   |               ------ has type `&Client`
+   |               ------ has type `&Client` which is not `Send`
 LL |             200 => {
 LL |                 let _x = get().await;
    |                          ^^^^^^^^^^^ await occurs here, with `client` maybe used later
diff --git a/src/test/ui/async-await/issue-64130-non-send-future-diags.stderr b/src/test/ui/async-await/issue-64130-non-send-future-diags.stderr
index 0f4441edb13..f72757339cc 100644
--- a/src/test/ui/async-await/issue-64130-non-send-future-diags.stderr
+++ b/src/test/ui/async-await/issue-64130-non-send-future-diags.stderr
@@ -12,7 +12,7 @@ note: future is not `Send` as this value is used across an await
   --> $DIR/issue-64130-non-send-future-diags.rs:15:5
    |
 LL |     let g = x.lock().unwrap();
-   |         - has type `std::sync::MutexGuard<'_, u32>`
+   |         - has type `std::sync::MutexGuard<'_, u32>` which is not `Send`
 LL |     baz().await;
    |     ^^^^^^^^^^^ await occurs here, with `g` maybe used later
 LL | }
diff --git a/src/test/ui/async-await/issue-67252-unnamed-future.stderr b/src/test/ui/async-await/issue-67252-unnamed-future.stderr
index cbcc3cf5d78..b43478ee207 100644
--- a/src/test/ui/async-await/issue-67252-unnamed-future.stderr
+++ b/src/test/ui/async-await/issue-67252-unnamed-future.stderr
@@ -5,14 +5,14 @@ LL | fn spawn<T: Send>(_: T) {}
    |             ---- required by this bound in `spawn`
 ...
 LL |     spawn(async {
-   |     ^^^^^ future is not `Send`
+   |     ^^^^^ future created by async block is not `Send`
    |
    = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `*mut ()`
 note: future is not `Send` as this value is used across an await
   --> $DIR/issue-67252-unnamed-future.rs:20:9
    |
 LL |         let _a = std::ptr::null_mut::<()>(); // `*mut ()` is not `Send`
-   |             -- has type `*mut ()`
+   |             -- has type `*mut ()` which is not `Send`
 LL |         AFuture.await;
    |         ^^^^^^^^^^^^^ await occurs here, with `_a` maybe used later
 LL |     });
diff --git a/src/test/ui/async-await/issue-68112.rs b/src/test/ui/async-await/issue-68112.rs
new file mode 100644
index 00000000000..11b17836808
--- /dev/null
+++ b/src/test/ui/async-await/issue-68112.rs
@@ -0,0 +1,64 @@
+// edition:2018
+
+use std::{
+    future::Future,
+    cell::RefCell,
+    sync::Arc,
+    pin::Pin,
+    task::{Context, Poll},
+};
+
+fn require_send(_: impl Send) {}
+
+struct Ready<T>(Option<T>);
+impl<T> Future for Ready<T> {
+    type Output = T;
+    fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<T> {
+        Poll::Ready(self.0.take().unwrap())
+    }
+}
+fn ready<T>(t: T) -> Ready<T> {
+    Ready(Some(t))
+}
+
+fn make_non_send_future1() -> impl Future<Output = Arc<RefCell<i32>>> {
+    ready(Arc::new(RefCell::new(0)))
+}
+
+fn test1() {
+    let send_fut = async {
+        let non_send_fut = make_non_send_future1();
+        let _ = non_send_fut.await;
+        ready(0).await;
+    };
+    require_send(send_fut);
+    //~^ ERROR future cannot be sent between threads
+}
+
+fn test1_no_let() {
+    let send_fut = async {
+        let _ = make_non_send_future1().await;
+        ready(0).await;
+    };
+    require_send(send_fut);
+    //~^ ERROR future cannot be sent between threads
+}
+
+async fn ready2<T>(t: T) -> T { t }
+fn make_non_send_future2() -> impl Future<Output = Arc<RefCell<i32>>> {
+    ready2(Arc::new(RefCell::new(0)))
+}
+
+// Ideally this test would have diagnostics similar to the test above, but right
+// now it doesn't.
+fn test2() {
+    let send_fut = async {
+        let non_send_fut = make_non_send_future2();
+        let _ = non_send_fut.await;
+        ready(0).await;
+    };
+    require_send(send_fut);
+    //~^ ERROR `std::cell::RefCell<i32>` cannot be shared between threads safely
+}
+
+fn main() {}
diff --git a/src/test/ui/async-await/issue-68112.stderr b/src/test/ui/async-await/issue-68112.stderr
new file mode 100644
index 00000000000..6ded3e475bc
--- /dev/null
+++ b/src/test/ui/async-await/issue-68112.stderr
@@ -0,0 +1,56 @@
+error: future cannot be sent between threads safely
+  --> $DIR/issue-68112.rs:34:5
+   |
+LL | fn require_send(_: impl Send) {}
+   |                         ---- required by this bound in `require_send`
+...
+LL |     require_send(send_fut);
+   |     ^^^^^^^^^^^^ future created by async block is not `Send`
+   |
+   = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
+note: future is not `Send` as it awaits another future which is not `Send`
+  --> $DIR/issue-68112.rs:31:17
+   |
+LL |         let _ = non_send_fut.await;
+   |                 ^^^^^^^^^^^^ await occurs here on type `impl std::future::Future`, which is not `Send`
+
+error: future cannot be sent between threads safely
+  --> $DIR/issue-68112.rs:43:5
+   |
+LL | fn require_send(_: impl Send) {}
+   |                         ---- required by this bound in `require_send`
+...
+LL |     require_send(send_fut);
+   |     ^^^^^^^^^^^^ future created by async block is not `Send`
+   |
+   = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
+note: future is not `Send` as it awaits another future which is not `Send`
+  --> $DIR/issue-68112.rs:40:17
+   |
+LL |         let _ = make_non_send_future1().await;
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^ await occurs here on type `impl std::future::Future`, which is not `Send`
+
+error[E0277]: `std::cell::RefCell<i32>` cannot be shared between threads safely
+  --> $DIR/issue-68112.rs:60:5
+   |
+LL | fn require_send(_: impl Send) {}
+   |                         ---- required by this bound in `require_send`
+...
+LL |     require_send(send_fut);
+   |     ^^^^^^^^^^^^ `std::cell::RefCell<i32>` cannot be shared between threads safely
+   |
+   = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
+   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<std::cell::RefCell<i32>>`
+   = note: required because it appears within the type `[static generator@$DIR/issue-68112.rs:47:31: 47:36 t:std::sync::Arc<std::cell::RefCell<i32>> {}]`
+   = note: required because it appears within the type `std::future::from_generator::GenFuture<[static generator@$DIR/issue-68112.rs:47:31: 47:36 t:std::sync::Arc<std::cell::RefCell<i32>> {}]>`
+   = note: required because it appears within the type `impl std::future::Future`
+   = note: required because it appears within the type `impl std::future::Future`
+   = note: required because it appears within the type `impl std::future::Future`
+   = note: required because it appears within the type `{std::future::ResumeTy, impl std::future::Future, (), i32, Ready<i32>}`
+   = note: required because it appears within the type `[static generator@$DIR/issue-68112.rs:55:26: 59:6 {std::future::ResumeTy, impl std::future::Future, (), i32, Ready<i32>}]`
+   = note: required because it appears within the type `std::future::from_generator::GenFuture<[static generator@$DIR/issue-68112.rs:55:26: 59:6 {std::future::ResumeTy, impl std::future::Future, (), i32, Ready<i32>}]>`
+   = note: required because it appears within the type `impl std::future::Future`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.stderr b/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.stderr
index 73e2a92d815..49cd30e11a0 100644
--- a/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.stderr
+++ b/src/test/ui/async-await/issues/issue-65436-raw-ptr-not-send.stderr
@@ -5,7 +5,7 @@ LL | fn assert_send<T: Send>(_: T) {}
    |                   ---- required by this bound in `assert_send`
 ...
 LL |     assert_send(async {
-   |     ^^^^^^^^^^^ future returned by `main` is not `Send`
+   |     ^^^^^^^^^^^ future created by async block is not `Send`
    |
    = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `*const u8`
 note: future is not `Send` as this value is used across an await
@@ -14,7 +14,7 @@ note: future is not `Send` as this value is used across an await
 LL |         bar(Foo(std::ptr::null())).await;
    |         ^^^^^^^^----------------^^^^^^^^- `std::ptr::null()` is later dropped here
    |         |       |
-   |         |       has type `*const u8`
+   |         |       has type `*const u8` which is not `Send`
    |         await occurs here, with `std::ptr::null()` maybe used later
 help: consider moving this into a `let` binding to create a shorter lived borrow
   --> $DIR/issue-65436-raw-ptr-not-send.rs:14:13
diff --git a/src/test/ui/generator/issue-68112.rs b/src/test/ui/generator/issue-68112.rs
new file mode 100644
index 00000000000..9ab2abf7405
--- /dev/null
+++ b/src/test/ui/generator/issue-68112.rs
@@ -0,0 +1,56 @@
+#![feature(generators, generator_trait)]
+
+use std::{
+    cell::RefCell,
+    sync::Arc,
+    pin::Pin,
+    ops::{Generator, GeneratorState},
+};
+
+pub struct Ready<T>(Option<T>);
+impl<T> Generator<()> for Ready<T> {
+    type Return = T;
+    type Yield = ();
+    fn resume(mut self: Pin<&mut Self>, _args: ()) -> GeneratorState<(), T> {
+        GeneratorState::Complete(self.0.take().unwrap())
+    }
+}
+pub fn make_gen1<T>(t: T) -> Ready<T> {
+    Ready(Some(t))
+}
+
+fn require_send(_: impl Send) {}
+
+fn make_non_send_generator() -> impl Generator<Return = Arc<RefCell<i32>>> {
+    make_gen1(Arc::new(RefCell::new(0)))
+}
+
+fn test1() {
+    let send_gen = || {
+        let _non_send_gen = make_non_send_generator();
+        yield;
+    };
+    require_send(send_gen);
+    //~^ ERROR generator cannot be sent between threads
+}
+
+pub fn make_gen2<T>(t: T) -> impl Generator<Return = T> {
+    || {
+        yield;
+        t
+    }
+}
+fn make_non_send_generator2() -> impl Generator<Return = Arc<RefCell<i32>>> {
+    make_gen2(Arc::new(RefCell::new(0)))
+}
+
+fn test2() {
+    let send_gen = || {
+        let _non_send_gen = make_non_send_generator2();
+        yield;
+    };
+    require_send(send_gen);
+    //~^ ERROR `std::cell::RefCell<i32>` cannot be shared between threads safely
+}
+
+fn main() {}
diff --git a/src/test/ui/generator/issue-68112.stderr b/src/test/ui/generator/issue-68112.stderr
new file mode 100644
index 00000000000..83536f2af14
--- /dev/null
+++ b/src/test/ui/generator/issue-68112.stderr
@@ -0,0 +1,40 @@
+error: generator cannot be sent between threads safely
+  --> $DIR/issue-68112.rs:33:5
+   |
+LL | fn require_send(_: impl Send) {}
+   |                         ---- required by this bound in `require_send`
+...
+LL |     require_send(send_gen);
+   |     ^^^^^^^^^^^^ generator is not `Send`
+   |
+   = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
+note: generator is not `Send` as this value is used across a yield
+  --> $DIR/issue-68112.rs:31:9
+   |
+LL |         let _non_send_gen = make_non_send_generator();
+   |             ------------- has type `impl std::ops::Generator` which is not `Send`
+LL |         yield;
+   |         ^^^^^ yield occurs here, with `_non_send_gen` maybe used later
+LL |     };
+   |     - `_non_send_gen` is later dropped here
+
+error[E0277]: `std::cell::RefCell<i32>` cannot be shared between threads safely
+  --> $DIR/issue-68112.rs:52:5
+   |
+LL | fn require_send(_: impl Send) {}
+   |                         ---- required by this bound in `require_send`
+...
+LL |     require_send(send_gen);
+   |     ^^^^^^^^^^^^ `std::cell::RefCell<i32>` cannot be shared between threads safely
+   |
+   = help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
+   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<std::cell::RefCell<i32>>`
+   = note: required because it appears within the type `[generator@$DIR/issue-68112.rs:38:5: 41:6 t:std::sync::Arc<std::cell::RefCell<i32>> {()}]`
+   = note: required because it appears within the type `impl std::ops::Generator`
+   = note: required because it appears within the type `impl std::ops::Generator`
+   = note: required because it appears within the type `{impl std::ops::Generator, ()}`
+   = note: required because it appears within the type `[generator@$DIR/issue-68112.rs:48:20: 51:6 {impl std::ops::Generator, ()}]`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/generator/not-send-sync.rs b/src/test/ui/generator/not-send-sync.rs
index 0db01c6f756..8ca5565fb2a 100644
--- a/src/test/ui/generator/not-send-sync.rs
+++ b/src/test/ui/generator/not-send-sync.rs
@@ -7,7 +7,7 @@ fn main() {
     fn assert_send<T: Send>(_: T) {}
 
     assert_sync(|| {
-        //~^ ERROR: future cannot be shared between threads safely
+        //~^ ERROR: generator cannot be shared between threads safely
         let a = Cell::new(2);
         yield;
     });
diff --git a/src/test/ui/generator/not-send-sync.stderr b/src/test/ui/generator/not-send-sync.stderr
index 5f5211b5092..5df2c1b52fb 100644
--- a/src/test/ui/generator/not-send-sync.stderr
+++ b/src/test/ui/generator/not-send-sync.stderr
@@ -11,21 +11,21 @@ LL |     assert_send(|| {
    = note: required because of the requirements on the impl of `std::marker::Send` for `&std::cell::Cell<i32>`
    = note: required because it appears within the type `[generator@$DIR/not-send-sync.rs:16:17: 20:6 a:&std::cell::Cell<i32> _]`
 
-error: future cannot be shared between threads safely
+error: generator cannot be shared between threads safely
   --> $DIR/not-send-sync.rs:9:5
    |
 LL |     fn assert_sync<T: Sync>(_: T) {}
    |                       ---- required by this bound in `main::assert_sync`
 ...
 LL |     assert_sync(|| {
-   |     ^^^^^^^^^^^ future returned by `main` is not `Sync`
+   |     ^^^^^^^^^^^ generator is not `Sync`
    |
    = help: within `[generator@$DIR/not-send-sync.rs:9:17: 13:6 {std::cell::Cell<i32>, ()}]`, the trait `std::marker::Sync` is not implemented for `std::cell::Cell<i32>`
-note: future is not `Sync` as this value is used across an yield
+note: generator is not `Sync` as this value is used across a yield
   --> $DIR/not-send-sync.rs:12:9
    |
 LL |         let a = Cell::new(2);
-   |             - has type `std::cell::Cell<i32>`
+   |             - has type `std::cell::Cell<i32>` which is not `Sync`
 LL |         yield;
    |         ^^^^^ yield occurs here, with `a` maybe used later
 LL |     });