From ee53452aeccae1bc162126ce3dabdecc534d445b Mon Sep 17 00:00:00 2001 From: Matthew House Date: Sat, 17 Dec 2022 15:15:57 -0500 Subject: [PATCH] Suggest a `T: Send` bound for `&mut T` upvars in `Send` generators --- .../src/traits/error_reporting/suggestions.rs | 39 ++++++++------- src/test/ui/generator/ref-upvar-not-send.rs | 31 ++++++++++++ .../ui/generator/ref-upvar-not-send.stderr | 50 +++++++++++++++++++ 3 files changed, 103 insertions(+), 17 deletions(-) create mode 100644 src/test/ui/generator/ref-upvar-not-send.rs create mode 100644 src/test/ui/generator/ref-upvar-not-send.stderr 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 d47a5ea3e3706..911798b721049 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2344,28 +2344,33 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, '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), + // `Some((ref_ty, is_mut))` if `target_ty` is `&T` or `&mut T` and fails to impl `Send` + let non_send = match target_ty.kind() { + ty::Ref(_, ref_ty, mutability) => match self.evaluate_obligation(&obligation) { + Ok(eval) if !eval.may_apply() => Some((ref_ty, mutability.is_mut())), _ => 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 - ), - ), + let (span_label, span_note) = match non_send { + // if `target_ty` is `&T` or `&mut T` and fails to impl `Send`, + // include suggestions to make `T: Sync` so that `&T: Send`, + // or to make `T: Send` so that `&mut T: Send` + Some((ref_ty, is_mut)) => { + let ref_ty_trait = if is_mut { "Send" } else { "Sync" }; + let ref_kind = if is_mut { "&mut" } else { "&" }; + ( + format!( + "has type `{}` which {}, because `{}` is not `{}`", + target_ty, trait_explanation, ref_ty, ref_ty_trait + ), + format!( + "captured value {} because `{}` references cannot be sent unless their referent is `{}`", + trait_explanation, ref_kind, ref_ty_trait + ), + ) + } None => ( format!("has type `{}` which {}", target_ty, trait_explanation), format!("captured value {}", trait_explanation), diff --git a/src/test/ui/generator/ref-upvar-not-send.rs b/src/test/ui/generator/ref-upvar-not-send.rs new file mode 100644 index 0000000000000..eb9ef63ecfcb4 --- /dev/null +++ b/src/test/ui/generator/ref-upvar-not-send.rs @@ -0,0 +1,31 @@ +// For `Send` generators, suggest a `T: Sync` requirement for `&T` upvars, +// and suggest a `T: Send` requirement for `&mut T` upvars. + +#![feature(generators)] + +fn assert_send(_: T) {} +//~^ NOTE required by a bound in `assert_send` +//~| NOTE required by this bound in `assert_send` +//~| NOTE required by a bound in `assert_send` +//~| NOTE required by this bound in `assert_send` + +fn main() { + let x: &*mut () = &std::ptr::null_mut(); + let y: &mut *mut () = &mut std::ptr::null_mut(); + assert_send(move || { + //~^ ERROR generator cannot be sent between threads safely + //~| NOTE generator is not `Send` + yield; + let _x = x; + }); + //~^^ NOTE captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` + //~| NOTE has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync` + assert_send(move || { + //~^ ERROR generator cannot be sent between threads safely + //~| NOTE generator is not `Send` + yield; + let _y = y; + }); + //~^^ NOTE captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send` + //~| NOTE has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send` +} diff --git a/src/test/ui/generator/ref-upvar-not-send.stderr b/src/test/ui/generator/ref-upvar-not-send.stderr new file mode 100644 index 0000000000000..689ace67e34ef --- /dev/null +++ b/src/test/ui/generator/ref-upvar-not-send.stderr @@ -0,0 +1,50 @@ +error: generator cannot be sent between threads safely + --> $DIR/ref-upvar-not-send.rs:15:17 + | +LL | assert_send(move || { + | _________________^ +LL | | +LL | | +LL | | yield; +LL | | let _x = x; +LL | | }); + | |_____^ generator is not `Send` + | + = help: the trait `Sync` is not implemented for `*mut ()` +note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync` + --> $DIR/ref-upvar-not-send.rs:19:18 + | +LL | let _x = x; + | ^ has type `&*mut ()` which is not `Send`, because `*mut ()` is not `Sync` +note: required by a bound in `assert_send` + --> $DIR/ref-upvar-not-send.rs:6:19 + | +LL | fn assert_send(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: generator cannot be sent between threads safely + --> $DIR/ref-upvar-not-send.rs:23:17 + | +LL | assert_send(move || { + | _________________^ +LL | | +LL | | +LL | | yield; +LL | | let _y = y; +LL | | }); + | |_____^ generator is not `Send` + | + = help: within `[generator@$DIR/ref-upvar-not-send.rs:23:17: 23:24]`, the trait `Send` is not implemented for `*mut ()` +note: captured value is not `Send` because `&mut` references cannot be sent unless their referent is `Send` + --> $DIR/ref-upvar-not-send.rs:27:18 + | +LL | let _y = y; + | ^ has type `&mut *mut ()` which is not `Send`, because `*mut ()` is not `Send` +note: required by a bound in `assert_send` + --> $DIR/ref-upvar-not-send.rs:6:19 + | +LL | fn assert_send(_: T) {} + | ^^^^ required by this bound in `assert_send` + +error: aborting due to 2 previous errors +