Skip to content

Commit 9862bbe

Browse files
committed
Use clearer message when obligation is caused by await expr
1 parent 455537e commit 9862bbe

File tree

5 files changed

+126
-46
lines changed

5 files changed

+126
-46
lines changed

src/librustc_ast_lowering/expr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
590590
await_span,
591591
self.allow_gen_future.clone(),
592592
);
593+
let expr = self.lower_expr(expr);
593594

594595
let pinned_ident = Ident::with_dummy_span(sym::pinned);
595596
let (pinned_pat, pinned_pat_hid) =
@@ -671,7 +672,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
671672
let unit = self.expr_unit(span);
672673
let yield_expr = self.expr(
673674
span,
674-
hir::ExprKind::Yield(unit, hir::YieldSource::Await),
675+
hir::ExprKind::Yield(unit, hir::YieldSource::Await { expr: Some(expr.hir_id) }),
675676
ThinVec::new(),
676677
);
677678
let yield_expr = self.arena.alloc(yield_expr);
@@ -704,7 +705,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
704705
// match <expr> {
705706
// mut pinned => loop { .. }
706707
// }
707-
let expr = self.lower_expr(expr);
708708
hir::ExprKind::Match(expr, arena_vec![self; pinned_arm], hir::MatchSource::AwaitDesugar)
709709
}
710710

src/librustc_hir/hir.rs

+12-3
Original file line numberDiff line numberDiff line change
@@ -1736,15 +1736,24 @@ pub struct Destination {
17361736
#[derive(Copy, Clone, PartialEq, Eq, Debug, RustcEncodable, RustcDecodable, HashStable_Generic)]
17371737
pub enum YieldSource {
17381738
/// An `<expr>.await`.
1739-
Await,
1739+
Await { expr: Option<HirId> },
17401740
/// A plain `yield`.
17411741
Yield,
17421742
}
17431743

1744+
impl YieldSource {
1745+
pub fn is_await(&self) -> bool {
1746+
match self {
1747+
YieldSource::Await { .. } => true,
1748+
YieldSource::Yield => false,
1749+
}
1750+
}
1751+
}
1752+
17441753
impl fmt::Display for YieldSource {
17451754
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
17461755
f.write_str(match self {
1747-
YieldSource::Await => "`await`",
1756+
YieldSource::Await { .. } => "`await`",
17481757
YieldSource::Yield => "`yield`",
17491758
})
17501759
}
@@ -1755,7 +1764,7 @@ impl From<GeneratorKind> for YieldSource {
17551764
match kind {
17561765
// Guess based on the kind of the current generator.
17571766
GeneratorKind::Gen => Self::Yield,
1758-
GeneratorKind::Async(_) => Self::Await,
1767+
GeneratorKind::Async(_) => Self::Await { expr: None },
17591768
}
17601769
}
17611770
}

src/librustc_trait_selection/traits/error_reporting/suggestions.rs

+108-34
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,14 @@ crate trait InferCtxtExt<'tcx> {
126126
scope_span: &Option<Span>,
127127
expr: Option<hir::HirId>,
128128
snippet: String,
129-
inner_generator: DefId,
129+
inner_generator_body: Option<&hir::Body<'_>>,
130130
outer_generator: Option<DefId>,
131131
trait_ref: ty::TraitRef<'_>,
132132
target_ty: Ty<'tcx>,
133133
tables: &ty::TypeckTables<'_>,
134134
obligation: &PredicateObligation<'tcx>,
135135
next_code: Option<&ObligationCauseCode<'tcx>>,
136+
from_awaited_ty: Option<Span>,
136137
);
137138

138139
fn note_obligation_cause_code<T>(
@@ -1088,6 +1089,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
10881089
}
10891090
};
10901091

1092+
let generator_body = self.tcx
1093+
.hir()
1094+
.as_local_hir_id(generator_did)
1095+
.and_then(|hir_id| self.tcx.hir().maybe_body_owned_by(hir_id))
1096+
.map(|body_id| self.tcx.hir().body(body_id));
1097+
let mut visitor = AwaitsVisitor::default();
1098+
if let Some(body) = generator_body {
1099+
visitor.visit_body(body);
1100+
}
1101+
debug!("maybe_note_obligation_cause_for_async_await: awaits = {:?}", visitor.awaits);
1102+
10911103
// Look for a type inside the generator interior that matches the target type to get
10921104
// a span.
10931105
let target_ty_erased = self.tcx.erase_regions(&target_ty);
@@ -1117,29 +1129,48 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
11171129
);
11181130
eq
11191131
})
1120-
.map(|ty::GeneratorInteriorTypeCause { span, scope_span, expr, .. }| {
1121-
(span, source_map.span_to_snippet(*span), scope_span, expr)
1132+
.map(|cause| {
1133+
// Check to see if any awaited expressions have the target type.
1134+
let from_awaited_ty = visitor.awaits.into_iter()
1135+
.map(|id| self.tcx.hir().expect_expr(id))
1136+
.find(|expr| {
1137+
let ty = tables.expr_ty_adjusted(&expr);
1138+
// Compare types using the same logic as above.
1139+
let ty_erased = self.tcx.erase_late_bound_regions(&ty::Binder::bind(ty));
1140+
let ty_erased = self.tcx.erase_regions(&ty_erased);
1141+
let eq = ty::TyS::same_type(ty_erased, target_ty_erased);
1142+
debug!(
1143+
"maybe_note_obligation_cause_for_async_await: await_expr={:?} \
1144+
await_ty_erased={:?} target_ty_erased={:?} eq={:?}",
1145+
expr, ty_erased, target_ty_erased, eq
1146+
);
1147+
eq
1148+
})
1149+
.map(|expr| expr.span);
1150+
let ty::GeneratorInteriorTypeCause { span, scope_span, expr, .. } = cause;
1151+
(span, source_map.span_to_snippet(*span), scope_span, expr, from_awaited_ty)
11221152
});
11231153

11241154
debug!(
11251155
"maybe_note_obligation_cause_for_async_await: target_ty={:?} \
11261156
generator_interior_types={:?} target_span={:?}",
11271157
target_ty, tables.generator_interior_types, target_span
11281158
);
1129-
if let Some((target_span, Ok(snippet), scope_span, expr)) = target_span {
1159+
if let Some((target_span, Ok(snippet), scope_span, expr, from_awaited_ty)) = target_span {
11301160
self.note_obligation_cause_for_async_await(
11311161
err,
11321162
*target_span,
11331163
scope_span,
11341164
*expr,
11351165
snippet,
1136-
generator_did,
1166+
generator_body,
11371167
outer_generator,
11381168
trait_ref,
11391169
target_ty,
11401170
tables,
11411171
obligation,
11421172
next_code,
1173+
from_awaited_ty,
11431174
);
11441175
true
11451176
} else {
@@ -1156,22 +1187,18 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
11561187
scope_span: &Option<Span>,
11571188
expr: Option<hir::HirId>,
11581189
snippet: String,
1159-
inner_generator: DefId,
1190+
inner_generator_body: Option<&hir::Body<'_>>,
11601191
outer_generator: Option<DefId>,
11611192
trait_ref: ty::TraitRef<'_>,
11621193
target_ty: Ty<'tcx>,
11631194
tables: &ty::TypeckTables<'_>,
11641195
obligation: &PredicateObligation<'tcx>,
11651196
next_code: Option<&ObligationCauseCode<'tcx>>,
1197+
from_awaited_ty: Option<Span>,
11661198
) {
11671199
let source_map = self.tcx.sess.source_map();
11681200

1169-
let is_async = self
1170-
.tcx
1171-
.hir()
1172-
.as_local_hir_id(inner_generator)
1173-
.and_then(|hir_id| self.tcx.hir().maybe_body_owned_by(hir_id))
1174-
.map(|body_id| self.tcx.hir().body(body_id))
1201+
let is_async = inner_generator_body
11751202
.and_then(|body| body.generator_kind())
11761203
.map(|generator_kind| match generator_kind {
11771204
hir::GeneratorKind::Async(..) => true,
@@ -1230,33 +1257,57 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
12301257
)
12311258
};
12321259

1233-
// Look at the last interior type to get a span for the `.await`.
1234-
let await_span = tables.generator_interior_types.iter().map(|t| t.span).last().unwrap();
1235-
let mut span = MultiSpan::from_span(await_span);
1236-
span.push_span_label(
1237-
await_span,
1238-
format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet),
1239-
);
1260+
let push_target_span = |span: &mut MultiSpan| {
1261+
if target_ty.is_impl_trait() {
1262+
// It's not very useful to tell the user the type if it's opaque.
1263+
span.push_span_label(target_span, "created here".to_string());
1264+
} else {
1265+
span.push_span_label(target_span, format!("has type `{}`", target_ty));
1266+
}
1267+
};
12401268

1241-
if target_ty.is_impl_trait() {
1242-
// It's not very useful to tell the user the type if it's opaque.
1243-
span.push_span_label(target_span, "created here".to_string());
1244-
} else {
1245-
span.push_span_label(target_span, format!("has type `{}`", target_ty));
1246-
}
1269+
if let Some(await_span) = from_awaited_ty {
1270+
// The type causing this obligation is one being awaited at await_span.
1271+
let mut span = MultiSpan::from_span(await_span);
1272+
span.push_span_label(
1273+
await_span,
1274+
"await occurs here".to_string(),
1275+
);
1276+
1277+
push_target_span(&mut span);
12471278

1248-
// If available, use the scope span to annotate the drop location.
1249-
if let Some(scope_span) = scope_span {
1279+
err.span_note(
1280+
span,
1281+
&format!("{} as this value is used in an await", trait_explanation),
1282+
);
1283+
} else {
1284+
// Look at the last interior type to get a span for the `.await`.
1285+
debug!(
1286+
"note_obligation_cause_for_async_await generator_interior_types: {:#?}",
1287+
tables.generator_interior_types
1288+
);
1289+
let await_span = tables.generator_interior_types.iter().map(|t| t.span).last().unwrap();
1290+
let mut span = MultiSpan::from_span(await_span);
12501291
span.push_span_label(
1251-
source_map.end_point(*scope_span),
1252-
format!("`{}` is later dropped here", snippet),
1292+
await_span,
1293+
format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet),
12531294
);
1254-
}
12551295

1256-
err.span_note(
1257-
span,
1258-
&format!("{} as this value is used across an {}", trait_explanation, await_or_yield),
1259-
);
1296+
push_target_span(&mut span);
1297+
1298+
// If available, use the scope span to annotate the drop location.
1299+
if let Some(scope_span) = scope_span {
1300+
span.push_span_label(
1301+
source_map.end_point(*scope_span),
1302+
format!("`{}` is later dropped here", snippet),
1303+
);
1304+
}
1305+
1306+
err.span_note(
1307+
span,
1308+
&format!("{} as this value is used across an {}", trait_explanation, await_or_yield),
1309+
);
1310+
}
12601311

12611312
if let Some(expr_id) = expr {
12621313
let expr = hir.expect_expr(expr_id);
@@ -1593,3 +1644,26 @@ impl<'v> Visitor<'v> for ReturnsVisitor<'v> {
15931644
hir::intravisit::walk_body(self, body);
15941645
}
15951646
}
1647+
1648+
/// Collect all the awaited expressions within the input expression.
1649+
#[derive(Default)]
1650+
struct AwaitsVisitor {
1651+
awaits: Vec<hir::HirId>,
1652+
}
1653+
1654+
impl<'v> Visitor<'v> for AwaitsVisitor {
1655+
type Map = hir::intravisit::ErasedMap<'v>;
1656+
1657+
fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
1658+
hir::intravisit::NestedVisitorMap::None
1659+
}
1660+
1661+
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
1662+
match ex.kind {
1663+
hir::ExprKind::Yield(_, hir::YieldSource::Await { expr: Some(id) }) =>
1664+
self.awaits.push(id),
1665+
_ => (),
1666+
}
1667+
hir::intravisit::walk_expr(self, ex)
1668+
}
1669+
}

src/librustc_typeck/check/expr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1797,7 +1797,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17971797
// we know that the yield type must be `()`; however, the context won't contain this
17981798
// information. Hence, we check the source of the yield expression here and check its
17991799
// value's type against `()` (this check should always hold).
1800-
None if src == &hir::YieldSource::Await => {
1800+
None if src.is_await() => {
18011801
self.check_expr_coercable_to_type(&value, self.tcx.mk_unit());
18021802
self.tcx.mk_unit()
18031803
}

src/test/ui/async-await/issue-68112.stderr

+3-6
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,13 @@ LL | require_send(send_fut);
88
| ^^^^^^^^^^^^ future created by async block is not `Send`
99
|
1010
= help: the trait `std::marker::Sync` is not implemented for `std::cell::RefCell<i32>`
11-
note: future is not `Send` as this value is used across an await
12-
--> $DIR/issue-68112.rs:32:9
11+
note: future is not `Send` as this value is used in an await
12+
--> $DIR/issue-68112.rs:31:17
1313
|
1414
LL | let non_send_fut = make_non_send_future1();
1515
| ------------ created here
1616
LL | let _ = non_send_fut.await;
17-
LL | ready(0).await;
18-
| ^^^^^^^^ await occurs here, with `non_send_fut` maybe used later
19-
LL | };
20-
| - `non_send_fut` is later dropped here
17+
| ^^^^^^^^^^^^ await occurs here
2118

2219
error[E0277]: `std::cell::RefCell<i32>` cannot be shared between threads safely
2320
--> $DIR/issue-68112.rs:49:5

0 commit comments

Comments
 (0)