Skip to content

Suggest move closures when a Sync bound is not satisfied through a closure #33307

Open

Description

@Manishearth
use std::thread;
use std::sync::mpsc;

fn main() {
    let (send, recv) = mpsc::channel();
    let t = thread::spawn(|| {
        recv.recv().unwrap();
    });

    send.send(());

    t.join().unwrap();
}

playpen

This currently gives the error

<anon>:6:13: 6:26 error: the trait `core::marker::Sync` is not implemented for the type `core::cell::UnsafeCell<std::sync::mpsc::Flavor<()>>` [E0277]
<anon>:6     let t = thread::spawn(|| {
                     ^~~~~~~~~~~~~
<anon>:6:13: 6:26 help: see the detailed explanation for E0277
<anon>:6:13: 6:26 note: `core::cell::UnsafeCell<std::sync::mpsc::Flavor<()>>` cannot be shared between threads safely
<anon>:6:13: 6:26 note: required because it appears within the type `std::sync::mpsc::Receiver<()>`
<anon>:6:13: 6:26 note: required because it appears within the type `[closure@<anon>:6:27: 8:6 recv:&std::sync::mpsc::Receiver<()>]`
<anon>:6:13: 6:26 note: required by `std::thread::spawn`
error: aborting due to previous error
playpen: application terminated with error code 101

If a closure needs to be Sync, we should perhaps suggest that it be made a move closure.

Perhaps we should verify that the closure can be made move (i.e. it is : 'static when made move), but I'm not sure how easy that is to do.

We already suggest move closures when you do the same thing with a Vec, because the error reaches borrowck. Typeck sync issues don't suggest move.

cc @nikomatsakis

Activity

  1. arielb1 commented on May 1, 2016

    @arielb1
    Contributor

    Why isn't the

    impl<'a, T: Sync> Send for &'a T

    impl on the stack?

  2. Manishearth commented on May 1, 2016

    @Manishearth
    MemberAuthor

    Not sure, really.

  3. Manishearth commented on May 1, 2016

    @Manishearth
    MemberAuthor

    My patch so far

    diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
    index d7ddfc9..b70a813 100644
    --- a/src/librustc/traits/error_reporting.rs
    +++ b/src/librustc/traits/error_reporting.rs
    @@ -861,10 +861,26 @@ fn note_obligation_cause_code<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>,
             }
             ObligationCauseCode::BuiltinDerivedObligation(ref data) => {
                 let parent_trait_ref = infcx.resolve_type_vars_if_possible(&data.parent_trait_ref);
    +            let ty = parent_trait_ref.0.self_ty();
                 err.fileline_note(
                     cause_span,
                     &format!("required because it appears within the type `{}`",
    -                         parent_trait_ref.0.self_ty()));
    +                         ty));
    +
    +            // In case the requirement is that a closure be Send,
    +            if Some(parent_trait_ref.0.def_id) == infcx.tcx.lang_items.send_trait() {
    +                if let ty::TyClosure(did, _) = ty.sty {
    +                    if let Some(span) = infcx.tcx.map.span_if_local(did) {
    +                        let suggestion = match infcx.tcx.sess.codemap().span_to_snippet(span) {
    +                            Ok(string) => format!("move {}", string),
    +                            Err(_) => format!("move |<args>| <body>")
    +                        };
    +                        err.span_suggestion(span,
    +                                            "Perhaps try marking this closure as a `move` closure?",
    +                                            suggestion);
    +                    }
    +                }
    +            }
                 let parent_predicate = parent_trait_ref.to_predicate();
                 note_obligation_cause_code(infcx,
                                            err,

    Still needs to check if the closure is move already or if move-ifying it will have any effect.

  4. arielb1 commented on May 1, 2016

    @arielb1
    Contributor

    After not dropping impls:

    closure-example.rs:6:13: 6:26 error: the trait bound `std::sync::mpsc::Receiver<()>: std::marker::Sync` is not satisfied [E0277]
    closure-example.rs:6     let t = thread::spawn(|| {
                                     ^~~~~~~~~~~~~
    closure-example.rs:6:13: 6:26 help: run `rustc --explain E0277` to see a detailed explanation
    closure-example.rs:6:13: 6:26 note: `std::sync::mpsc::Receiver<()>` cannot be shared between threads safely
    closure-example.rs:6:13: 6:26 note: required because of the requirements on the impl of `std::marker::Send` for `&std::sync::mpsc::Receiver<()>`
    closure-example.rs:6:13: 6:26 note: required because it appears within the type `[closure@closure-example.rs:6:27: 8:6 recv:&std::sync::mpsc::Receiver<()>]`
    closure-example.rs:6:13: 6:26 note: required by `std::thread::spawn`
    error: aborting due to previous error
    
  5. jonhoo commented on May 3, 2016

    @jonhoo
    Contributor

    @Manishearth: I'd love to see it also spell out which variable is of the non-Sync type. In this example, it's obvious, but for more complex examples, it can be tricky to figure out where you accidentally borrowed something (e.g., by using the wrong variable name).

  6. Manishearth commented on May 3, 2016

    @Manishearth
    MemberAuthor

    Yeah, that's a good idea. It shouldn't be too hard to do either.

  7. Mark-Simulacrum commented on May 3, 2017

    @Mark-Simulacrum
    Member

    @arielb1 You were assigned to this issue, but I don't believe it's been implemented. Should we unassign you?

  8. nikomatsakis commented on May 4, 2017

    @nikomatsakis
    Contributor

    So, to spell this out:

    • The problem is not that "closures that must be sync" should be move -- that is overly broad and might not help anything.
    • Rather, the problem is:
      • the closure must be Send
      • it is not Send because it captures some variable "by ref"
      • and that variable's type is not Sync

    I think we have to be careful to not go advising move all the time when it won't help. Here are some examples where it would not help:

    fn want_send<T: Send>(t: T) { }
    fn want_sync<T: Sync>(t: T) { }
    
    // move will not help because `x` is itself a reference:
    fn foo(x: &Cell<T>) {
        want_send(|| x.set(1));
    }
    
    // move will not help because `Cell<T>` is not sync:
    fn foo(x: Cell<T>) {
        want_sync(|| x.set(1));
    }
  9. estebank commented on Feb 5, 2020

    @estebank
    Contributor

    Triage: no change.

    Current output:

    error[E0277]: `std::sync::mpsc::Receiver<()>` cannot be shared between threads safely
       --> file4.rs:6:13
        |
    6   |     let t = thread::spawn(|| {
        |             ^^^^^^^^^^^^^ `std::sync::mpsc::Receiver<()>` cannot be shared between threads safely
        |
       ::: /Users/ekuber/workspace/rust/src/libstd/thread/mod.rs:616:8
        |
    616 |     F: Send + 'static,
        |        ---- required by this bound in `std::thread::spawn`
        |
        = help: the trait `std::marker::Sync` is not implemented for `std::sync::mpsc::Receiver<()>`
        = note: required because of the requirements on the impl of `std::marker::Send` for `&std::sync::mpsc::Receiver<()>`
        = note: required because it appears within the type `[closure@file4.rs:6:27: 8:6 recv:&std::sync::mpsc::Receiver<()>]`
    
    error[E0277]: `std::cell::Cell<usize>` cannot be shared between threads safely
     --> file4.rs:8:5
      |
    3 | fn want_send<T: Send>(t: T) { }
      |    ---------    ---- required by this bound in `want_send`
    ...
    8 |     want_send(|| x.set(1));
      |     ^^^^^^^^^ `std::cell::Cell<usize>` cannot be shared between threads safely
      |
      = help: within `&std::cell::Cell<usize>`, the trait `std::marker::Sync` is not implemented for `std::cell::Cell<usize>`
      = note: required because it appears within the type `&std::cell::Cell<usize>`
      = note: required because of the requirements on the impl of `std::marker::Send` for `&&std::cell::Cell<usize>`
      = note: required because it appears within the type `[closure@file4.rs:8:15: 8:26 x:&&std::cell::Cell<usize>]`
    
    error[E0277]: `std::cell::Cell<usize>` cannot be shared between threads safely
      --> file4.rs:13:5
       |
    4  | fn want_sync<T: Sync>(t: T) { }
       |    ---------    ---- required by this bound in `want_sync`
    ...
    13 |     want_sync(|| x.set(1));
       |     ^^^^^^^^^ ----------- within this `[closure@file4.rs:13:15: 13:26 x:&std::cell::Cell<usize>]`
       |     |
       |     `std::cell::Cell<usize>` cannot be shared between threads safely
       |
       = help: within `[closure@file4.rs:13:15: 13:26 x:&std::cell::Cell<usize>]`, the trait `std::marker::Sync` is not implemented for `std::cell::Cell<usize>`
       = note: required because it appears within the type `&std::cell::Cell<usize>`
       = note: required because it appears within the type `[closure@file4.rs:13:15: 13:26 x:&std::cell::Cell<usize>]`
    
  10. added
    A-closuresArea: Closures (`|…| { … }`)
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Feb 5, 2020
  11. added
    D-lack-of-suggestionDiagnostics: Adding a (structured) suggestion would increase the quality of the diagnostic.
    and removed
    C-enhancementCategory: An issue proposing an enhancement or a PR with one.
    on Mar 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-closuresArea: Closures (`|…| { … }`)A-diagnosticsArea: Messages for errors, warnings, and lintsA-trait-systemArea: Trait systemD-lack-of-suggestionDiagnostics: Adding a (structured) suggestion would increase the quality of the diagnostic.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions