about summary refs log tree commit diff
diff options
context:
space:
mode:
authorYuki Okushi <jtitor@2k36.org>2021-07-24 04:31:11 +0900
committerGitHub <noreply@github.com>2021-07-24 04:31:11 +0900
commit3fc79fde63cfa8472e8c867c0a54de58cffb5cd0 (patch)
tree3c2fd5582b70f542e5fb60d4826cb78097efd37a
parent82a14e57e7754310dd78c08386d801a33555d2f6 (diff)
parenta1518f091552afdf370c699a359af0b7d34e402d (diff)
downloadrust-3fc79fde63cfa8472e8c867c0a54de58cffb5cd0.tar.gz
rust-3fc79fde63cfa8472e8c867c0a54de58cffb5cd0.zip
Rollup merge of #87322 - chazkiker2:fix/suggestion-ref-sync-send, r=estebank
fix: clarify suggestion that `&T` must refer to `T: Sync` for `&T: Send`

### Description

- [x] fix #86507
- [x] add UI test for relevant code from issue
- [x] change `rustc_trait_selection/src/traits/error_reporting/suggestions.rs` to include a more clear suggestion when `&T` fails to satisfy `Send` bounds due to the fact that `T` fails to implement `Sync`
- [x] update UI test in Clippy: `src/tools/tests/ui/future_not_send.stderr`
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs35
-rw-r--r--src/test/ui/async-await/issue-86507.rs25
-rw-r--r--src/test/ui/async-await/issue-86507.stderr23
-rw-r--r--src/tools/clippy/tests/ui/future_not_send.stderr4
4 files changed, 80 insertions, 7 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 1c6a83b5783..9a33875d6e4 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -1857,12 +1857,37 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 }
             }
             GeneratorInteriorOrUpvar::Upvar(upvar_span) => {
+                // `Some(ref_ty)` if `target_ty` is `&T` and `T` fails to impl `Sync`
+                let refers_to_non_sync = match target_ty.kind() {
+                    ty::Ref(_, ref_ty, _) => match self.evaluate_obligation(&obligation) {
+                        Ok(eval) if !eval.may_apply() => Some(ref_ty),
+                        _ => None,
+                    },
+                    _ => None,
+                };
+
+                let (span_label, span_note) = match refers_to_non_sync {
+                    // if `target_ty` is `&T` and `T` fails to impl `Sync`,
+                    // include suggestions to make `T: Sync` so that `&T: Send`
+                    Some(ref_ty) => (
+                        format!(
+                            "has type `{}` which {}, because `{}` is not `Sync`",
+                            target_ty, trait_explanation, ref_ty
+                        ),
+                        format!(
+                            "captured value {} because `&` references cannot be sent unless their referent is `Sync`",
+                            trait_explanation
+                        ),
+                    ),
+                    None => (
+                        format!("has type `{}` which {}", target_ty, trait_explanation),
+                        format!("captured value {}", trait_explanation),
+                    ),
+                };
+
                 let mut span = MultiSpan::from_span(upvar_span);
-                span.push_span_label(
-                    upvar_span,
-                    format!("has type `{}` which {}", target_ty, trait_explanation),
-                );
-                err.span_note(span, &format!("captured value {}", trait_explanation));
+                span.push_span_label(upvar_span, span_label);
+                err.span_note(span, &span_note);
             }
         }
 
diff --git a/src/test/ui/async-await/issue-86507.rs b/src/test/ui/async-await/issue-86507.rs
new file mode 100644
index 00000000000..317f0317664
--- /dev/null
+++ b/src/test/ui/async-await/issue-86507.rs
@@ -0,0 +1,25 @@
+// edition:2018
+
+use ::core::pin::Pin;
+use ::core::future::Future;
+use ::core::marker::Send;
+
+trait Foo {
+    fn bar<'me, 'async_trait, T: Send>(x: &'me T)
+        -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
+        where 'me: 'async_trait;
+}
+
+impl Foo for () {
+    fn bar<'me, 'async_trait, T: Send>(x: &'me T)
+        -> Pin<Box<dyn Future<Output = ()> + Send + 'async_trait>>
+        where 'me:'async_trait {
+            Box::pin( //~ ERROR future cannot be sent between threads safely
+                async move {
+                    let x = x;
+                }
+            )
+         }
+}
+
+fn main() { }
diff --git a/src/test/ui/async-await/issue-86507.stderr b/src/test/ui/async-await/issue-86507.stderr
new file mode 100644
index 00000000000..51e8f61085b
--- /dev/null
+++ b/src/test/ui/async-await/issue-86507.stderr
@@ -0,0 +1,23 @@
+error: future cannot be sent between threads safely
+  --> $DIR/issue-86507.rs:17:13
+   |
+LL | /             Box::pin(
+LL | |                 async move {
+LL | |                     let x = x;
+LL | |                 }
+LL | |             )
+   | |_____________^ future created by async block is not `Send`
+   |
+note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
+  --> $DIR/issue-86507.rs:19:29
+   |
+LL |                     let x = x;
+   |                             ^ has type `&T` which is not `Send`, because `T` is not `Sync`
+   = note: required for the cast to the object type `dyn Future<Output = ()> + Send`
+help: consider further restricting type parameter `T`
+   |
+LL |         where 'me:'async_trait, T: std::marker::Sync {
+   |                               ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/future_not_send.stderr b/src/tools/clippy/tests/ui/future_not_send.stderr
index b59dbb3e76c..c734051ccf3 100644
--- a/src/tools/clippy/tests/ui/future_not_send.stderr
+++ b/src/tools/clippy/tests/ui/future_not_send.stderr
@@ -55,11 +55,11 @@ note: captured value is not `Send`
 LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
    |                          ^^ has type `std::rc::Rc<[u8]>` which is not `Send`
    = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
-note: captured value is not `Send`
+note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
   --> $DIR/future_not_send.rs:20:40
    |
 LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
-   |                                        ^^^^ has type `&std::cell::Cell<usize>` which is not `Send`
+   |                                        ^^^^ has type `&std::cell::Cell<usize>` which is not `Send`, because `std::cell::Cell<usize>` is not `Sync`
    = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
 
 error: future cannot be sent between threads safely