Skip to content

Commit aed7c30

Browse files
committed
Use clearer message when obligation is caused by await expr
1 parent 6edfd66 commit aed7c30

File tree

5 files changed

+126
-46
lines changed

5 files changed

+126
-46
lines changed

src/librustc_ast_lowering/expr.rs

Lines changed: 2 additions & 2 deletions
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

Lines changed: 12 additions & 3 deletions
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

Lines changed: 108 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,14 @@ pub trait InferCtxtExt<'tcx> {
127127
scope_span: &Option<Span>,
128128
expr: Option<hir::HirId>,
129129
snippet: String,
130-
inner_generator: DefId,
130+
inner_generator_body: Option<&hir::Body<'_>>,
131131
outer_generator: Option<DefId>,
132132
trait_ref: ty::TraitRef<'_>,
133133
target_ty: Ty<'tcx>,
134134
tables: &ty::TypeckTables<'_>,
135135
obligation: &PredicateObligation<'tcx>,
136136
next_code: Option<&ObligationCauseCode<'tcx>>,
137+
from_awaited_ty: Option<Span>,
137138
);
138139

139140
fn note_obligation_cause_code<T>(
@@ -1203,6 +1204,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
12031204
}
12041205
};
12051206

1207+
let generator_body = self.tcx
1208+
.hir()
1209+
.as_local_hir_id(generator_did)
1210+
.and_then(|hir_id| self.tcx.hir().maybe_body_owned_by(hir_id))
1211+
.map(|body_id| self.tcx.hir().body(body_id));
1212+
let mut visitor = AwaitsVisitor::default();
1213+
if let Some(body) = generator_body {
1214+
visitor.visit_body(body);
1215+
}
1216+
debug!("maybe_note_obligation_cause_for_async_await: awaits = {:?}", visitor.awaits);
1217+
12061218
// Look for a type inside the generator interior that matches the target type to get
12071219
// a span.
12081220
let target_ty_erased = self.tcx.erase_regions(&target_ty);
@@ -1232,29 +1244,48 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
12321244
);
12331245
eq
12341246
})
1235-
.map(|ty::GeneratorInteriorTypeCause { span, scope_span, expr, .. }| {
1236-
(span, source_map.span_to_snippet(*span), scope_span, expr)
1247+
.map(|cause| {
1248+
// Check to see if any awaited expressions have the target type.
1249+
let from_awaited_ty = visitor.awaits.into_iter()
1250+
.map(|id| self.tcx.hir().expect_expr(id))
1251+
.find(|expr| {
1252+
let ty = tables.expr_ty_adjusted(&expr);
1253+
// Compare types using the same logic as above.
1254+
let ty_erased = self.tcx.erase_late_bound_regions(&ty::Binder::bind(ty));
1255+
let ty_erased = self.tcx.erase_regions(&ty_erased);
1256+
let eq = ty::TyS::same_type(ty_erased, target_ty_erased);
1257+
debug!(
1258+
"maybe_note_obligation_cause_for_async_await: await_expr={:?} \
1259+
await_ty_erased={:?} target_ty_erased={:?} eq={:?}",
1260+
expr, ty_erased, target_ty_erased, eq
1261+
);
1262+
eq
1263+
})
1264+
.map(|expr| expr.span);
1265+
let ty::GeneratorInteriorTypeCause { span, scope_span, expr, .. } = cause;
1266+
(span, source_map.span_to_snippet(*span), scope_span, expr, from_awaited_ty)
12371267
});
12381268

12391269
debug!(
12401270
"maybe_note_obligation_cause_for_async_await: target_ty={:?} \
12411271
generator_interior_types={:?} target_span={:?}",
12421272
target_ty, tables.generator_interior_types, target_span
12431273
);
1244-
if let Some((target_span, Ok(snippet), scope_span, expr)) = target_span {
1274+
if let Some((target_span, Ok(snippet), scope_span, expr, from_awaited_ty)) = target_span {
12451275
self.note_obligation_cause_for_async_await(
12461276
err,
12471277
*target_span,
12481278
scope_span,
12491279
*expr,
12501280
snippet,
1251-
generator_did,
1281+
generator_body,
12521282
outer_generator,
12531283
trait_ref,
12541284
target_ty,
12551285
tables,
12561286
obligation,
12571287
next_code,
1288+
from_awaited_ty,
12581289
);
12591290
true
12601291
} else {
@@ -1271,22 +1302,18 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
12711302
scope_span: &Option<Span>,
12721303
expr: Option<hir::HirId>,
12731304
snippet: String,
1274-
inner_generator: DefId,
1305+
inner_generator_body: Option<&hir::Body<'_>>,
12751306
outer_generator: Option<DefId>,
12761307
trait_ref: ty::TraitRef<'_>,
12771308
target_ty: Ty<'tcx>,
12781309
tables: &ty::TypeckTables<'_>,
12791310
obligation: &PredicateObligation<'tcx>,
12801311
next_code: Option<&ObligationCauseCode<'tcx>>,
1312+
from_awaited_ty: Option<Span>,
12811313
) {
12821314
let source_map = self.tcx.sess.source_map();
12831315

1284-
let is_async = self
1285-
.tcx
1286-
.hir()
1287-
.as_local_hir_id(inner_generator)
1288-
.and_then(|hir_id| self.tcx.hir().maybe_body_owned_by(hir_id))
1289-
.map(|body_id| self.tcx.hir().body(body_id))
1316+
let is_async = inner_generator_body
12901317
.and_then(|body| body.generator_kind())
12911318
.map(|generator_kind| match generator_kind {
12921319
hir::GeneratorKind::Async(..) => true,
@@ -1345,33 +1372,57 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
13451372
)
13461373
};
13471374

1348-
// Look at the last interior type to get a span for the `.await`.
1349-
let await_span = tables.generator_interior_types.iter().map(|t| t.span).last().unwrap();
1350-
let mut span = MultiSpan::from_span(await_span);
1351-
span.push_span_label(
1352-
await_span,
1353-
format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet),
1354-
);
1375+
let push_target_span = |span: &mut MultiSpan| {
1376+
if target_ty.is_impl_trait() {
1377+
// It's not very useful to tell the user the type if it's opaque.
1378+
span.push_span_label(target_span, "created here".to_string());
1379+
} else {
1380+
span.push_span_label(target_span, format!("has type `{}`", target_ty));
1381+
}
1382+
};
13551383

1356-
if target_ty.is_impl_trait() {
1357-
// It's not very useful to tell the user the type if it's opaque.
1358-
span.push_span_label(target_span, "created here".to_string());
1359-
} else {
1360-
span.push_span_label(target_span, format!("has type `{}`", target_ty));
1361-
}
1384+
if let Some(await_span) = from_awaited_ty {
1385+
// The type causing this obligation is one being awaited at await_span.
1386+
let mut span = MultiSpan::from_span(await_span);
1387+
span.push_span_label(
1388+
await_span,
1389+
"await occurs here".to_string(),
1390+
);
1391+
1392+
push_target_span(&mut span);
13621393

1363-
// If available, use the scope span to annotate the drop location.
1364-
if let Some(scope_span) = scope_span {
1394+
err.span_note(
1395+
span,
1396+
&format!("{} as this value is used in an await", trait_explanation),
1397+
);
1398+
} else {
1399+
// Look at the last interior type to get a span for the `.await`.
1400+
debug!(
1401+
"note_obligation_cause_for_async_await generator_interior_types: {:#?}",
1402+
tables.generator_interior_types
1403+
);
1404+
let await_span = tables.generator_interior_types.iter().map(|t| t.span).last().unwrap();
1405+
let mut span = MultiSpan::from_span(await_span);
13651406
span.push_span_label(
1366-
source_map.end_point(*scope_span),
1367-
format!("`{}` is later dropped here", snippet),
1407+
await_span,
1408+
format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet),
13681409
);
1369-
}
13701410

1371-
err.span_note(
1372-
span,
1373-
&format!("{} as this value is used across an {}", trait_explanation, await_or_yield),
1374-
);
1411+
push_target_span(&mut span);
1412+
1413+
// If available, use the scope span to annotate the drop location.
1414+
if let Some(scope_span) = scope_span {
1415+
span.push_span_label(
1416+
source_map.end_point(*scope_span),
1417+
format!("`{}` is later dropped here", snippet),
1418+
);
1419+
}
1420+
1421+
err.span_note(
1422+
span,
1423+
&format!("{} as this value is used across an {}", trait_explanation, await_or_yield),
1424+
);
1425+
}
13751426

13761427
if let Some(expr_id) = expr {
13771428
let expr = hir.expect_expr(expr_id);
@@ -1716,6 +1767,29 @@ impl<'v> Visitor<'v> for ReturnsVisitor<'v> {
17161767
}
17171768
}
17181769

1770+
/// Collect all the awaited expressions within the input expression.
1771+
#[derive(Default)]
1772+
struct AwaitsVisitor {
1773+
awaits: Vec<hir::HirId>,
1774+
}
1775+
1776+
impl<'v> Visitor<'v> for AwaitsVisitor {
1777+
type Map = hir::intravisit::ErasedMap<'v>;
1778+
1779+
fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
1780+
hir::intravisit::NestedVisitorMap::None
1781+
}
1782+
1783+
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
1784+
match ex.kind {
1785+
hir::ExprKind::Yield(_, hir::YieldSource::Await { expr: Some(id) }) =>
1786+
self.awaits.push(id),
1787+
_ => (),
1788+
}
1789+
hir::intravisit::walk_expr(self, ex)
1790+
}
1791+
}
1792+
17191793
pub trait NextTypeParamName {
17201794
fn next_type_param_name(&self, name: Option<&str>) -> String;
17211795
}

src/librustc_typeck/check/expr.rs

Lines changed: 1 addition & 1 deletion
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

Lines changed: 3 additions & 6 deletions
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)