Skip to content

Commit 5381ddf

Browse files
committed
Work on better error message
1 parent 3eebe05 commit 5381ddf

9 files changed

+201
-141
lines changed

src/librustc/traits/error_reporting.rs

+110-25
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use crate::ty::fast_reject;
3232
use crate::ty::fold::TypeFolder;
3333
use crate::ty::subst::Subst;
3434
use crate::ty::SubtypePredicate;
35+
use crate::ty::TraitPredicate;
3536
use crate::util::nodemap::{FxHashMap, FxHashSet};
3637

3738
use errors::{Applicability, DiagnosticBuilder, pluralize, Style};
@@ -2103,12 +2104,28 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
21032104
err: &mut DiagnosticBuilder<'_>,
21042105
obligation: &PredicateObligation<'tcx>,
21052106
) {
2107+
2108+
debug!("note_obligation_cause: obligation.predicate={:?} \
2109+
obligation.cause.span={:?}", obligation.predicate, obligation.cause.span);
2110+
21062111
// First, attempt to add note to this error with an async-await-specific
21072112
// message, and fall back to regular note otherwise.
2108-
if !self.maybe_note_obligation_cause_for_async_await(err, obligation) {
2109-
self.note_obligation_cause_code(err, &obligation.predicate, &obligation.cause.code,
2110-
&mut vec![]);
2113+
if let ty::Predicate::Trait(trait_predicate) = obligation.predicate {
2114+
if self.maybe_note_obligation_cause_for_async_await(
2115+
err,
2116+
trait_predicate.skip_binder(),
2117+
&obligation.cause.code
2118+
) {
2119+
return;
2120+
}
21112121
}
2122+
2123+
self.note_obligation_cause_code(
2124+
err,
2125+
&obligation.predicate,
2126+
&obligation.cause.code,
2127+
&mut vec![]
2128+
);
21122129
}
21132130

21142131
/// Adds an async-await specific note to the diagnostic when the future does not implement
@@ -2156,12 +2173,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
21562173
fn maybe_note_obligation_cause_for_async_await(
21572174
&self,
21582175
err: &mut DiagnosticBuilder<'_>,
2159-
obligation: &PredicateObligation<'tcx>,
2176+
trait_predicate: &TraitPredicate<'tcx>,
2177+
code: &ObligationCauseCode<'tcx>,
21602178
) -> bool {
2161-
debug!("maybe_note_obligation_cause_for_async_await: obligation.predicate={:?} \
2162-
obligation.cause.span={:?}", obligation.predicate, obligation.cause.span);
2179+
/*debug!("note_obligation_cause_for_async_await: obligation.predicate={:?} \
2180+
obligation.cause.span={:?}", obligation.predicate, obligation.cause.span);*/
21632181
let source_map = self.tcx.sess.source_map();
21642182

2183+
// Look into the obligation predicate to determine the type in the generator which meant
2184+
// that the predicate was not satisifed.
2185+
/*let (trait_ref, target_ty) = match obligation.predicate {
2186+
ty::Predicate::Trait(trait_predicate) =>
2187+
(trait_predicate.skip_binder().trait_ref, trait_predicate.skip_binder().self_ty()),
2188+
_ => return false,
2189+
};*/
2190+
2191+
let (mut trait_ref, mut target_ty) = (trait_predicate.trait_ref, trait_predicate.self_ty());
2192+
2193+
debug!("note_obligation_cause_for_async_await: target_ty={:?}", target_ty);
2194+
21652195
// Attempt to detect an async-await error by looking at the obligation causes, looking
21662196
// for a generator to be present.
21672197
//
@@ -2187,19 +2217,25 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
21872217
// The first obligation in the chain is the most useful and has the generator that captured
21882218
// the type. The last generator has information about where the bound was introduced. At
21892219
// least one generator should be present for this diagnostic to be modified.
2190-
let (mut trait_ref, mut target_ty) = match obligation.predicate {
2191-
ty::Predicate::Trait(p) =>
2192-
(Some(p.skip_binder().trait_ref), Some(p.skip_binder().self_ty())),
2193-
_ => (None, None),
2194-
};
21952220
let mut generator = None;
21962221
let mut last_generator = None;
2197-
let mut next_code = Some(&obligation.cause.code);
2222+
let mut next_code = Some(code);
2223+
let mut next_predicate = None;
2224+
2225+
// First, find an `ObligationCause` code referencing a generator.
2226+
// Skip over intermediate causes generated by `.await` lowering
21982227
while let Some(code) = next_code {
21992228
debug!("maybe_note_obligation_cause_for_async_await: code={:?}", code);
22002229
match code {
22012230
ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) |
22022231
ObligationCauseCode::ImplDerivedObligation(derived_obligation) => {
2232+
2233+
next_code = Some(derived_obligation.parent_code.as_ref());
2234+
next_predicate = Some(
2235+
self.resolve_vars_if_possible(&derived_obligation.parent_trait_ref)
2236+
.to_predicate()
2237+
);
2238+
22032239
let ty = derived_obligation.parent_trait_ref.self_ty();
22042240
debug!("maybe_note_obligation_cause_for_async_await: \
22052241
parent_trait_ref={:?} self_ty.kind={:?}",
@@ -2212,25 +2248,55 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
22122248
},
22132249
ty::GeneratorWitness(..) => {},
22142250
_ if generator.is_none() => {
2215-
trait_ref = Some(*derived_obligation.parent_trait_ref.skip_binder());
2216-
target_ty = Some(ty);
2251+
trait_ref = *derived_obligation.parent_trait_ref.skip_binder();
2252+
target_ty = ty;
22172253
},
22182254
_ => {},
22192255
}
22202256

2257+
},
2258+
_ => return false,
2259+
}
2260+
};
2261+
2262+
// Now that we've found a generator, try to determine if we should
2263+
// skip over any subsequence causes.
2264+
while let Some(code) = next_code {
2265+
debug!("note_obligation_cause_for_async_await: code={:?}", code);
2266+
match code {
2267+
ObligationCauseCode::BuiltinDerivedObligation(derived_obligation) |
2268+
ObligationCauseCode::ImplDerivedObligation(derived_obligation) => {
2269+
debug!("note_obligation_cause_for_async_await: self_ty.kind={:?}",
2270+
derived_obligation.parent_trait_ref.self_ty().kind);
2271+
2272+
2273+
match derived_obligation.parent_trait_ref.self_ty().kind {
2274+
ty::Adt(ty::AdtDef { did, .. }, ..) if
2275+
self.tcx.is_diagnostic_item(sym::gen_future, *did) => {},
2276+
ty::Opaque(did, _) if
2277+
self.tcx.parent(did).map(|parent| {
2278+
self.tcx.is_diagnostic_item(sym::from_generator, parent)
2279+
}).unwrap_or(false) => {}
2280+
_ => break,
2281+
}
2282+
22212283
next_code = Some(derived_obligation.parent_code.as_ref());
2284+
next_predicate = Some(
2285+
self.resolve_vars_if_possible(&derived_obligation.parent_trait_ref)
2286+
.to_predicate()
2287+
)
22222288
},
2223-
_ => break,
2289+
_ => break
22242290
}
22252291
}
22262292

22272293
// Only continue if a generator was found.
22282294
debug!("maybe_note_obligation_cause_for_async_await: generator={:?} trait_ref={:?} \
22292295
target_ty={:?}", generator, trait_ref, target_ty);
2230-
let (generator_did, trait_ref, target_ty) = match (generator, trait_ref, target_ty) {
2231-
(Some(generator_did), Some(trait_ref), Some(target_ty)) =>
2232-
(generator_did, trait_ref, target_ty),
2233-
_ => return false,
2296+
let generator_did = if let Some(generator_did) = generator {
2297+
generator_did
2298+
} else {
2299+
return false;
22342300
};
22352301

22362302
let span = self.tcx.def_span(generator_did);
@@ -2290,7 +2356,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
22902356
if let Some((target_span, Ok(snippet), scope_span)) = target_span {
22912357
self.note_obligation_cause_for_async_await(
22922358
err, *target_span, scope_span, snippet, generator_did, last_generator,
2293-
trait_ref, target_ty, tables, obligation, next_code,
2359+
trait_ref, target_ty, tables, &next_predicate.unwrap(), next_code,
22942360
);
22952361
true
22962362
} else {
@@ -2311,7 +2377,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
23112377
trait_ref: ty::TraitRef<'_>,
23122378
target_ty: Ty<'tcx>,
23132379
tables: &ty::TypeckTables<'_>,
2314-
obligation: &PredicateObligation<'tcx>,
2380+
predicate: &ty::Predicate<'tcx>,
23152381
next_code: Option<&ObligationCauseCode<'tcx>>,
23162382
) {
23172383
let source_map = self.tcx.sess.source_map();
@@ -2342,9 +2408,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
23422408
("`Sync`", "shared")
23432409
};
23442410

2345-
err.clear_code();
2346-
err.set_primary_message(
2347-
format!("future cannot be {} between threads safely", trait_verb)
2411+
err.note(
2412+
&format!("future cannot be {} between threads safely", trait_verb)
23482413
);
23492414

23502415
let original_span = err.span.primary_span().unwrap();
@@ -2396,7 +2461,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
23962461
debug!("note_obligation_cause_for_async_await: next_code={:?}", next_code);
23972462
self.note_obligation_cause_code(
23982463
err,
2399-
&obligation.predicate,
2464+
&predicate,
24002465
next_code.unwrap(),
24012466
&mut Vec::new(),
24022467
);
@@ -2410,6 +2475,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
24102475
where T: fmt::Display
24112476
{
24122477
let tcx = self.tcx;
2478+
2479+
2480+
let mut handle_async_await = |trait_ref, parent_code| {
2481+
self.maybe_note_obligation_cause_for_async_await(
2482+
err,
2483+
&TraitPredicate { trait_ref },
2484+
parent_code
2485+
)
2486+
};
2487+
24132488
match *cause_code {
24142489
ObligationCauseCode::ExprAssignable |
24152490
ObligationCauseCode::MatchExpressionArm { .. } |
@@ -2550,6 +2625,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
25502625
}
25512626
ObligationCauseCode::BuiltinDerivedObligation(ref data) => {
25522627
let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref);
2628+
2629+
if handle_async_await(*parent_trait_ref.skip_binder(), &data.parent_code) {
2630+
return;
2631+
}
2632+
25532633
let ty = parent_trait_ref.skip_binder().self_ty();
25542634
err.note(&format!("required because it appears within the type `{}`", ty));
25552635
obligated_types.push(ty);
@@ -2564,6 +2644,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
25642644
}
25652645
ObligationCauseCode::ImplDerivedObligation(ref data) => {
25662646
let parent_trait_ref = self.resolve_vars_if_possible(&data.parent_trait_ref);
2647+
2648+
if handle_async_await(*parent_trait_ref.skip_binder(), &data.parent_code) {
2649+
return;
2650+
}
2651+
25672652
err.note(
25682653
&format!("required because of the requirements on the impl of `{}` for `{}`",
25692654
parent_trait_ref.print_only_trait_path(),

src/libstd/future.rs

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub use core::future::*;
1818
/// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`).
1919
#[doc(hidden)]
2020
#[unstable(feature = "gen_future", issue = "50547")]
21+
#[cfg_attr(not(test), rustc_diagnostic_item = "from_generator")]
2122
pub fn from_generator<T: Generator<Yield = ()>>(x: T) -> impl Future<Output = T::Return> {
2223
GenFuture(x)
2324
}
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,79 @@
1-
error: future cannot be sent between threads safely
1+
error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely
22
--> $DIR/async-fn-nonsend.rs:50:5
33
|
44
LL | fn assert_send(_: impl Send) {}
55
| ----------- ---- required by this bound in `assert_send`
66
...
77
LL | assert_send(local_dropped_before_await());
8-
| ^^^^^^^^^^^ future returned by `local_dropped_before_await` is not `Send`
8+
| ^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely
99
|
1010
= help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`
11-
note: future is not `Send` as this value is used across an await
12-
--> $DIR/async-fn-nonsend.rs:25:5
13-
|
14-
LL | let x = non_send();
15-
| - has type `impl std::fmt::Debug`
16-
LL | drop(x);
17-
LL | fut().await;
18-
| ^^^^^^^^^^^ await occurs here, with `x` maybe used later
19-
LL | }
20-
| - `x` is later dropped here
11+
= note: required because it appears within the type `impl std::fmt::Debug`
12+
= note: required because it appears within the type `{impl std::fmt::Debug, impl std::future::Future, impl std::future::Future, ()}`
13+
= note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:21:39: 26:2 {impl std::fmt::Debug, impl std::future::Future, impl std::future::Future, ()}]`
14+
= note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:21:39: 26:2 {impl std::fmt::Debug, impl std::future::Future, impl std::future::Future, ()}]>`
15+
= note: required because it appears within the type `impl std::future::Future`
16+
= note: required because it appears within the type `impl std::future::Future`
2117

22-
error: future cannot be sent between threads safely
18+
error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely
2319
--> $DIR/async-fn-nonsend.rs:52:5
2420
|
2521
LL | fn assert_send(_: impl Send) {}
2622
| ----------- ---- required by this bound in `assert_send`
2723
...
2824
LL | assert_send(non_send_temporary_in_match());
29-
| ^^^^^^^^^^^ future returned by `non_send_temporary_in_match` is not `Send`
25+
| ^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely
3026
|
3127
= help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`
32-
note: future is not `Send` as this value is used across an await
33-
--> $DIR/async-fn-nonsend.rs:34:20
34-
|
35-
LL | match Some(non_send()) {
36-
| ---------- has type `impl std::fmt::Debug`
37-
LL | Some(_) => fut().await,
38-
| ^^^^^^^^^^^ await occurs here, with `non_send()` maybe used later
39-
...
40-
LL | }
41-
| - `non_send()` is later dropped here
28+
= note: required because it appears within the type `impl std::fmt::Debug`
29+
= note: required because it appears within the type `{impl std::fmt::Debug, std::option::Option<impl std::fmt::Debug>, impl std::future::Future, impl std::future::Future, ()}`
30+
= note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:28:40: 37:2 {impl std::fmt::Debug, std::option::Option<impl std::fmt::Debug>, impl std::future::Future, impl std::future::Future, ()}]`
31+
= note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:28:40: 37:2 {impl std::fmt::Debug, std::option::Option<impl std::fmt::Debug>, impl std::future::Future, impl std::future::Future, ()}]>`
32+
= note: required because it appears within the type `impl std::future::Future`
33+
= note: required because it appears within the type `impl std::future::Future`
4234

43-
error: future cannot be sent between threads safely
35+
error[E0277]: `dyn std::fmt::Write` cannot be sent between threads safely
4436
--> $DIR/async-fn-nonsend.rs:54:5
4537
|
4638
LL | fn assert_send(_: impl Send) {}
4739
| ----------- ---- required by this bound in `assert_send`
4840
...
4941
LL | assert_send(non_sync_with_method_call());
50-
| ^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send`
42+
| ^^^^^^^^^^^ `dyn std::fmt::Write` cannot be sent between threads safely
5143
|
5244
= help: the trait `std::marker::Send` is not implemented for `dyn std::fmt::Write`
53-
note: future is not `Send` as this value is used across an await
54-
--> $DIR/async-fn-nonsend.rs:43:9
55-
|
56-
LL | let f: &mut std::fmt::Formatter = panic!();
57-
| - has type `&mut std::fmt::Formatter<'_>`
58-
LL | if non_sync().fmt(f).unwrap() == () {
59-
LL | fut().await;
60-
| ^^^^^^^^^^^ await occurs here, with `f` maybe used later
61-
LL | }
62-
LL | }
63-
| - `f` is later dropped here
45+
= note: required because of the requirements on the impl of `std::marker::Send` for `&mut dyn std::fmt::Write`
46+
= note: required because it appears within the type `std::fmt::Formatter<'_>`
47+
= note: required because of the requirements on the impl of `std::marker::Send` for `&mut std::fmt::Formatter<'_>`
48+
= note: required because it appears within the type `for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, bool, impl std::future::Future, impl std::future::Future, ()}`
49+
= note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, bool, impl std::future::Future, impl std::future::Future, ()}]`
50+
= note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, bool, impl std::future::Future, impl std::future::Future, ()}]>`
51+
= note: required because it appears within the type `impl std::future::Future`
52+
= note: required because it appears within the type `impl std::future::Future`
6453

65-
error: future cannot be sent between threads safely
54+
error[E0277]: `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely
6655
--> $DIR/async-fn-nonsend.rs:54:5
6756
|
6857
LL | fn assert_send(_: impl Send) {}
6958
| ----------- ---- required by this bound in `assert_send`
7059
...
7160
LL | assert_send(non_sync_with_method_call());
72-
| ^^^^^^^^^^^ future returned by `non_sync_with_method_call` is not `Send`
61+
| ^^^^^^^^^^^ `*mut (dyn std::ops::Fn() + 'static)` cannot be shared between threads safely
7362
|
7463
= help: within `std::fmt::ArgumentV1<'_>`, the trait `std::marker::Sync` is not implemented for `*mut (dyn std::ops::Fn() + 'static)`
75-
note: future is not `Send` as this value is used across an await
76-
--> $DIR/async-fn-nonsend.rs:43:9
77-
|
78-
LL | let f: &mut std::fmt::Formatter = panic!();
79-
| - has type `&mut std::fmt::Formatter<'_>`
80-
LL | if non_sync().fmt(f).unwrap() == () {
81-
LL | fut().await;
82-
| ^^^^^^^^^^^ await occurs here, with `f` maybe used later
83-
LL | }
84-
LL | }
85-
| - `f` is later dropped here
64+
= note: required because it appears within the type `std::marker::PhantomData<*mut (dyn std::ops::Fn() + 'static)>`
65+
= note: required because it appears within the type `core::fmt::Void`
66+
= note: required because it appears within the type `&core::fmt::Void`
67+
= note: required because it appears within the type `std::fmt::ArgumentV1<'_>`
68+
= note: required because of the requirements on the impl of `std::marker::Send` for `std::slice::Iter<'_, std::fmt::ArgumentV1<'_>>`
69+
= note: required because it appears within the type `std::fmt::Formatter<'_>`
70+
= note: required because of the requirements on the impl of `std::marker::Send` for `&mut std::fmt::Formatter<'_>`
71+
= note: required because it appears within the type `for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, bool, impl std::future::Future, impl std::future::Future, ()}`
72+
= note: required because it appears within the type `[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, bool, impl std::future::Future, impl std::future::Future, ()}]`
73+
= note: required because it appears within the type `std::future::GenFuture<[static generator@$DIR/async-fn-nonsend.rs:39:38: 45:2 for<'r, 's> {&'r mut std::fmt::Formatter<'s>, bool, bool, impl std::future::Future, impl std::future::Future, ()}]>`
74+
= note: required because it appears within the type `impl std::future::Future`
75+
= note: required because it appears within the type `impl std::future::Future`
8676

8777
error: aborting due to 4 previous errors
8878

79+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)