diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs index 6a111895b5637..3bb83eabc8da7 100644 --- a/src/librustc/traits/error_reporting.rs +++ b/src/librustc/traits/error_reporting.rs @@ -32,6 +32,7 @@ use crate::ty::fast_reject; use crate::ty::fold::TypeFolder; use crate::ty::subst::Subst; use crate::ty::SubtypePredicate; +use crate::ty::TraitPredicate; use crate::util::nodemap::{FxHashMap, FxHashSet}; use errors::{Applicability, DiagnosticBuilder, pluralize, Style}; @@ -2103,12 +2104,28 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { err: &mut DiagnosticBuilder<'_>, obligation: &PredicateObligation<'tcx>, ) { + + debug!("note_obligation_cause: obligation.predicate={:?} \ + obligation.cause.span={:?}", obligation.predicate, obligation.cause.span); + // First, attempt to add note to this error with an async-await-specific // message, and fall back to regular note otherwise. - if !self.maybe_note_obligation_cause_for_async_await(err, obligation) { - self.note_obligation_cause_code(err, &obligation.predicate, &obligation.cause.code, - &mut vec![]); + if let ty::Predicate::Trait(trait_predicate) = obligation.predicate { + if self.maybe_note_obligation_cause_for_async_await( + err, + trait_predicate.skip_binder(), + &obligation.cause.code + ) { + return; + } } + + self.note_obligation_cause_code( + err, + &obligation.predicate, + &obligation.cause.code, + &mut vec![] + ); } /// Adds an async-await specific note to the diagnostic when the future does not implement @@ -2156,12 +2173,17 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { fn maybe_note_obligation_cause_for_async_await( &self, err: &mut DiagnosticBuilder<'_>, - obligation: &PredicateObligation<'tcx>, + trait_predicate: &TraitPredicate<'tcx>, + code: &ObligationCauseCode<'tcx>, ) -> bool { - debug!("maybe_note_obligation_cause_for_async_await: obligation.predicate={:?} \ - obligation.cause.span={:?}", obligation.predicate, obligation.cause.span); + debug!("note_obligation_cause_for_async_await: trait_predicate={:?} \ + code={:?}", trait_predicate, code); let source_map = self.tcx.sess.source_map(); + let (trait_ref, target_ty) = (trait_predicate.trait_ref, trait_predicate.self_ty()); + + debug!("note_obligation_cause_for_async_await: target_ty={:?}", target_ty); + // Attempt to detect an async-await error by looking at the obligation causes, looking // for a generator to be present. // @@ -2187,51 +2209,99 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // 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. - 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())), - _ => (None, None), - }; let mut generator = None; - let mut last_generator = None; - let mut next_code = Some(&obligation.cause.code); + let mut next_code = Some(code); + let mut next_predicate = None; + + // First, find an `ObligationCause` code referencing a generator. + // Skip over intermediate causes generated by `.await` lowering while let Some(code) = next_code { - debug!("maybe_note_obligation_cause_for_async_await: code={:?}", code); + debug!("note_obligation_cause_for_async_await: code={:?}", code); match code { ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) | ObligationCauseCode::ImplDerivedObligation(derived_obligation) => { - let ty = derived_obligation.parent_trait_ref.self_ty(); - debug!("maybe_note_obligation_cause_for_async_await: \ - parent_trait_ref={:?} self_ty.kind={:?}", - derived_obligation.parent_trait_ref, ty.kind); + debug!("note_obligation_cause_for_async_await: self_ty.kind={:?}", + derived_obligation.parent_trait_ref.self_ty().kind); + + next_code = Some(derived_obligation.parent_code.as_ref()); + next_predicate = Some( + self.resolve_vars_if_possible(&derived_obligation.parent_trait_ref) + .to_predicate() + ); - match ty.kind { + match derived_obligation.parent_trait_ref.self_ty().kind { + ty::Adt(ty::AdtDef { did, .. }, ..) if + self.tcx.is_diagnostic_item(sym::gen_future, *did) => {}, ty::Generator(did, ..) => { - generator = generator.or(Some(did)); - last_generator = Some(did); + generator = Some(did); + debug!("note_obligation_cause_for_async_await: found generator {:?}", + generator); + break; }, - ty::GeneratorWitness(..) => {}, - _ if generator.is_none() => { - trait_ref = Some(*derived_obligation.parent_trait_ref.skip_binder()); - target_ty = Some(ty); - }, - _ => {}, + ty::GeneratorWitness(_) | ty::Opaque(..) => {}, + _ => return false, + } + + }, + _ => return false, + } + }; + + // Now that we've found a generator, try to determine if we should + // skip over any subsequence causes. + while let Some(code) = next_code { + debug!("note_obligation_cause_for_async_await: inspecting code={:?}", code); + match code { + ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) | + ObligationCauseCode::ImplDerivedObligation(derived_obligation) => { + debug!("note_obligation_cause_for_async_await: inspecting self_ty.kind={:?}", + derived_obligation.parent_trait_ref.self_ty().kind); + + + match derived_obligation.parent_trait_ref.self_ty().kind { + ty::Adt(ty::AdtDef { did, .. }, ..) if + self.tcx.is_diagnostic_item(sym::gen_future, *did) => {}, + ty::Opaque(did, _) if + self.tcx.parent(did).map(|parent| { + self.tcx.is_diagnostic_item(sym::from_generator, parent) + }).unwrap_or(false) => {} + _ => break, } next_code = Some(derived_obligation.parent_code.as_ref()); + next_predicate = Some( + self.resolve_vars_if_possible(&derived_obligation.parent_trait_ref) + .to_predicate() + ) }, - _ => break, + _ => break + } + } + + let generator_did = generator.expect("can only reach this if there was a generator"); + + // Finally, see if there are any generator types remaining in the 'error stack'. + // If there aren't, then treat the current generator as the 'last generator', + // which will make `note_obligation_cause_for_async_await` emit an extra + // message for it + let mut target_code = next_code; + let mut last_generator = Some(generator_did); + while let Some(code) = target_code { + debug!("note_obligation_cause_for_async_await: last_generator code={:?}", code); + if let ObligationCauseCode::BuiltinDerivedObligation(obligation) | + ObligationCauseCode::ImplDerivedObligation(obligation) = code { + if let ty::Generator(..) = obligation.parent_trait_ref.self_ty().kind { + // We found another generator, so our current generator can't be + // the last one + last_generator = None; + break; + } + target_code = Some(obligation.parent_code.as_ref()); + } else { + break; } } - // Only continue if a generator was found. - debug!("maybe_note_obligation_cause_for_async_await: generator={:?} trait_ref={:?} \ - target_ty={:?}", generator, trait_ref, target_ty); - let (generator_did, trait_ref, target_ty) = match (generator, trait_ref, target_ty) { - (Some(generator_did), Some(trait_ref), Some(target_ty)) => - (generator_did, trait_ref, target_ty), - _ => return false, - }; let span = self.tcx.def_span(generator_did); @@ -2290,7 +2360,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { if let Some((target_span, Ok(snippet), scope_span)) = target_span { self.note_obligation_cause_for_async_await( err, *target_span, scope_span, snippet, generator_did, last_generator, - trait_ref, target_ty, tables, obligation, next_code, + trait_ref, target_ty, tables, &next_predicate.unwrap(), next_code, ); true } else { @@ -2311,7 +2381,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { trait_ref: ty::TraitRef<'_>, target_ty: Ty<'tcx>, tables: &ty::TypeckTables<'_>, - obligation: &PredicateObligation<'tcx>, + predicate: &ty::Predicate<'tcx>, next_code: Option<&ObligationCauseCode<'tcx>>, ) { let source_map = self.tcx.sess.source_map(); @@ -2342,9 +2412,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ("`Sync`", "shared") }; - err.clear_code(); err.set_primary_message( - format!("future cannot be {} between threads safely", trait_verb) + &format!("future cannot be {} between threads safely", trait_verb) ); let original_span = err.span.primary_span().unwrap(); @@ -2396,7 +2465,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { debug!("note_obligation_cause_for_async_await: next_code={:?}", next_code); self.note_obligation_cause_code( err, - &obligation.predicate, + &predicate, next_code.unwrap(), &mut Vec::new(), ); @@ -2410,6 +2479,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { where T: fmt::Display { let tcx = self.tcx; + + + let mut handle_async_await = |trait_ref, parent_code| { + self.maybe_note_obligation_cause_for_async_await( + err, + &TraitPredicate { trait_ref }, + parent_code + ) + }; + match *cause_code { ObligationCauseCode::ExprAssignable | ObligationCauseCode::MatchExpressionArm { .. } | @@ -2550,6 +2629,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } ObligationCauseCode::BuiltinDerivedObligation(ref data) => { let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); + + if handle_async_await(*parent_trait_ref.skip_binder(), &data.parent_code) { + return; + } + let ty = parent_trait_ref.skip_binder().self_ty(); err.note(&format!("required because it appears within the type `{}`", ty)); obligated_types.push(ty); @@ -2564,6 +2648,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } ObligationCauseCode::ImplDerivedObligation(ref data) => { let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref); + + if handle_async_await(*parent_trait_ref.skip_binder(), &data.parent_code) { + return; + } + err.note( &format!("required because of the requirements on the impl of `{}` for `{}`", parent_trait_ref.print_only_trait_path(), diff --git a/src/libstd/future.rs b/src/libstd/future.rs index ac1ef3e1d8b72..d99d90a195a27 100644 --- a/src/libstd/future.rs +++ b/src/libstd/future.rs @@ -18,6 +18,7 @@ pub use core::future::*; /// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`). #[doc(hidden)] #[unstable(feature = "gen_future", issue = "50547")] +#[cfg_attr(not(test), rustc_diagnostic_item = "from_generator")] pub fn from_generator>(x: T) -> impl Future { GenFuture(x) } @@ -26,6 +27,7 @@ pub fn from_generator>(x: T) -> impl Future>(T); // We rely on the fact that async/await futures are immovable in order to create diff --git a/src/test/ui/async-await/async-fn-nonsend.stderr b/src/test/ui/async-await/async-fn-nonsend.stderr index 5c870ca2d0276..bc86a55056de8 100644 --- a/src/test/ui/async-await/async-fn-nonsend.stderr +++ b/src/test/ui/async-await/async-fn-nonsend.stderr @@ -1,4 +1,4 @@ -error: future cannot be sent between threads safely +error[E0277]: future cannot be sent between threads safely --> $DIR/async-fn-nonsend.rs:50:5 | LL | fn assert_send(_: impl Send) {} @@ -18,8 +18,9 @@ LL | fut().await; | ^^^^^^^^^^^ await occurs here, with `x` maybe used later LL | } | - `x` is later dropped here + = note: required because it appears within the type `impl std::future::Future` -error: future cannot be sent between threads safely +error[E0277]: future cannot be sent between threads safely --> $DIR/async-fn-nonsend.rs:52:5 | LL | fn assert_send(_: impl Send) {} @@ -39,8 +40,9 @@ LL | Some(_) => fut().await, ... LL | } | - `non_send()` is later dropped here + = note: required because it appears within the type `impl std::future::Future` -error: future cannot be sent between threads safely +error[E0277]: future cannot be sent between threads safely --> $DIR/async-fn-nonsend.rs:54:5 | LL | fn assert_send(_: impl Send) {} @@ -50,6 +52,8 @@ LL | assert_send(non_sync_with_method_call()); | ^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send` | = help: the trait `std::marker::Send` is not implemented for `dyn std::fmt::Write` + = note: required because of the requirements on the impl of `std::marker::Send` for `&mut dyn std::fmt::Write` + = note: required because it appears within the type `std::fmt::Formatter<'_>` note: future is not `Send` as this value is used across an await --> $DIR/async-fn-nonsend.rs:43:9 | @@ -61,8 +65,9 @@ LL | fut().await; LL | } LL | } | - `f` is later dropped here + = note: required because it appears within the type `impl std::future::Future` -error: future cannot be sent between threads safely +error[E0277]: future cannot be sent between threads safely --> $DIR/async-fn-nonsend.rs:54:5 | LL | fn assert_send(_: impl Send) {} @@ -72,6 +77,12 @@ LL | assert_send(non_sync_with_method_call()); | ^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send` | = help: within `std::fmt::ArgumentV1<'_>`, the trait `std::marker::Sync` is not implemented for `*mut (dyn std::ops::Fn() + 'static)` + = note: required because it appears within the type `std::marker::PhantomData<*mut (dyn std::ops::Fn() + 'static)>` + = note: required because it appears within the type `core::fmt::Void` + = note: required because it appears within the type `&core::fmt::Void` + = note: required because it appears within the type `std::fmt::ArgumentV1<'_>` + = note: required because of the requirements on the impl of `std::marker::Send` for `std::slice::Iter<'_, std::fmt::ArgumentV1<'_>>` + = note: required because it appears within the type `std::fmt::Formatter<'_>` note: future is not `Send` as this value is used across an await --> $DIR/async-fn-nonsend.rs:43:9 | @@ -83,6 +94,8 @@ LL | fut().await; LL | } LL | } | - `f` is later dropped here + = note: required because it appears within the type `impl std::future::Future` error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0277`. 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 8beb31f152a9d..3375b095b4e46 100644 --- a/src/test/ui/async-await/issue-64130-1-sync.stderr +++ b/src/test/ui/async-await/issue-64130-1-sync.stderr @@ -1,4 +1,4 @@ -error: future cannot be shared between threads safely +error[E0277]: future cannot be shared between threads safely --> $DIR/issue-64130-1-sync.rs:21:5 | LL | fn is_sync(t: T) { } @@ -17,6 +17,8 @@ LL | baz().await; | ^^^^^^^^^^^ await occurs here, with `x` maybe used later LL | } | - `x` is later dropped here + = note: required because it appears within the type `impl std::future::Future` error: aborting due to previous error +For more information about this error, try `rustc --explain E0277`. 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 823b88e18c5b6..c5f71c3d48012 100644 --- a/src/test/ui/async-await/issue-64130-2-send.stderr +++ b/src/test/ui/async-await/issue-64130-2-send.stderr @@ -1,4 +1,4 @@ -error: future cannot be sent between threads safely +error[E0277]: future cannot be sent between threads safely --> $DIR/issue-64130-2-send.rs:21:5 | LL | fn is_send(t: T) { } @@ -17,6 +17,8 @@ LL | baz().await; | ^^^^^^^^^^^ await occurs here, with `x` maybe used later LL | } | - `x` is later dropped here + = note: required because it appears within the type `impl std::future::Future` error: aborting due to previous error +For more information about this error, try `rustc --explain E0277`. 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 155c5cc8ea137..d95703462be60 100644 --- a/src/test/ui/async-await/issue-64130-3-other.stderr +++ b/src/test/ui/async-await/issue-64130-3-other.stderr @@ -18,6 +18,7 @@ LL | baz().await; | ^^^^^^^^^^^ await occurs here, with `x` maybe used later LL | } | - `x` is later dropped here + = note: required because it appears within the type `impl std::future::Future` error: aborting due to previous error 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 ddbb469b99c24..a8b6876f0522d 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 @@ -1,10 +1,13 @@ -error: future cannot be sent between threads safely +error[E0277]: 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` | = help: the trait `std::marker::Sync` is not implemented for `(dyn std::any::Any + std::marker::Send + 'static)` + = note: required because of the requirements on the impl of `std::marker::Sync` for `std::ptr::Unique<(dyn std::any::Any + std::marker::Send + 'static)>` + = note: required because it appears within the type `std::boxed::Box<(dyn std::any::Any + std::marker::Send + 'static)>` + = note: required because it appears within the type `Client` note: future is not `Send` as this value is used across an await --> $DIR/issue-64130-4-async-move.rs:21:26 | @@ -20,3 +23,4 @@ LL | } error: aborting due to previous error +For more information about this error, try `rustc --explain E0277`. 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 662407f7017f5..4652eb7924ade 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 @@ -1,4 +1,4 @@ -error: future cannot be sent between threads safely +error[E0277]: future cannot be sent between threads safely --> $DIR/issue-64130-non-send-future-diags.rs:21:5 | LL | fn is_send(t: T) { } @@ -17,6 +17,16 @@ LL | baz().await; | ^^^^^^^^^^^ await occurs here, with `g` maybe used later LL | } | - `g` is later dropped here +note: future is not `Send` as this value is used across an await + --> $DIR/issue-64130-non-send-future-diags.rs:10:5 + | +LL | bar(&Mutex::new(22)).await; + | --------------------^^^^^^- `bar(&Mutex::new(22))` is later dropped here + | | + | await occurs here, with `bar(&Mutex::new(22))` maybe used later + | has type `impl std::future::Future` + = note: required because it appears within the type `impl std::future::Future` error: aborting due to previous error +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/async-await/nested-async-calls.rs b/src/test/ui/async-await/nested-async-calls.rs new file mode 100644 index 0000000000000..1c4ca5ac765c5 --- /dev/null +++ b/src/test/ui/async-await/nested-async-calls.rs @@ -0,0 +1,28 @@ +// edition:2018 + +async fn first() { + second().await; +} + +async fn second() { + third().await; +} + +async fn third() { + struct NotSend(*const ()); + struct Outer(NotSend); + async fn dummy() {} + + let _a: Outer; + dummy().await; +} + +fn require_send(_val: T) {} + +fn main() { + struct Wrapper(T); + let wrapped = Wrapper(first()); + + require_send(wrapped); + //~^ ERROR future cannot be sent between threads safely +} diff --git a/src/test/ui/async-await/nested-async-calls.stderr b/src/test/ui/async-await/nested-async-calls.stderr new file mode 100644 index 0000000000000..000fd7706d145 --- /dev/null +++ b/src/test/ui/async-await/nested-async-calls.stderr @@ -0,0 +1,42 @@ +error[E0277]: future cannot be sent between threads safely + --> $DIR/nested-async-calls.rs:26:5 + | +LL | fn require_send(_val: T) {} + | ------------ ---- required by this bound in `require_send` +... +LL | require_send(wrapped); + | ^^^^^^^^^^^^ future returned by `first` is not `Send` + | + = help: within `main::Wrapper`, the trait `std::marker::Send` is not implemented for `*const ()` + = note: required because it appears within the type `third::{{closure}}#0::NotSend` +note: future is not `Send` as this value is used across an await + --> $DIR/nested-async-calls.rs:17:5 + | +LL | let _a: Outer; + | -- has type `third::{{closure}}#0::Outer` +LL | dummy().await; + | ^^^^^^^^^^^^^ await occurs here, with `_a` maybe used later +LL | } + | - `_a` is later dropped here +note: future is not `Send` as this value is used across an await + --> $DIR/nested-async-calls.rs:8:5 + | +LL | third().await; + | -------^^^^^^- `third()` is later dropped here + | | + | await occurs here, with `third()` maybe used later + | has type `impl std::future::Future` +note: future is not `Send` as this value is used across an await + --> $DIR/nested-async-calls.rs:4:5 + | +LL | second().await; + | --------^^^^^^- `second()` is later dropped here + | | + | await occurs here, with `second()` maybe used later + | has 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 `main::Wrapper` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/generator/not-send-sync.stderr b/src/test/ui/generator/not-send-sync.stderr index 0ac1d189b79b0..aa7fe971f13d9 100644 --- a/src/test/ui/generator/not-send-sync.stderr +++ b/src/test/ui/generator/not-send-sync.stderr @@ -11,7 +11,7 @@ LL | assert_send(|| { = note: required because of the requirements on the impl of `std::marker::Send` for `&std::cell::Cell` = note: required because it appears within the type `[generator@$DIR/not-send-sync.rs:16:17: 20:6 a:&std::cell::Cell _]` -error: future cannot be shared between threads safely +error[E0277]: future cannot be shared between threads safely --> $DIR/not-send-sync.rs:9:5 | LL | fn assert_sync(_: T) {}