Skip to content

Commit f182c4a

Browse files
committed
Auto merge of #71923 - csmoe:issue-70818, r=tmandry
Check non-Send/Sync upvars captured by generator Closes #70818 r? @tmandry
2 parents 64ad709 + c9e1465 commit f182c4a

File tree

3 files changed

+167
-108
lines changed

3 files changed

+167
-108
lines changed

src/librustc_trait_selection/traits/error_reporting/suggestions.rs

+135-108
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,14 @@ use std::fmt;
2525
use super::InferCtxtPrivExt;
2626
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
2727

28+
#[derive(Debug)]
29+
pub enum GeneratorInteriorOrUpvar {
30+
// span of interior type
31+
Interior(Span),
32+
// span of upvar
33+
Upvar(Span),
34+
}
35+
2836
// This trait is public to expose the diagnostics methods to clippy.
2937
pub trait InferCtxtExt<'tcx> {
3038
fn suggest_restricting_param_bound(
@@ -128,19 +136,15 @@ pub trait InferCtxtExt<'tcx> {
128136
fn note_obligation_cause_for_async_await(
129137
&self,
130138
err: &mut DiagnosticBuilder<'_>,
131-
target_span: Span,
132-
scope_span: &Option<Span>,
133-
await_span: Span,
134-
expr: Option<hir::HirId>,
135-
snippet: String,
139+
interior_or_upvar_span: GeneratorInteriorOrUpvar,
140+
interior_extra_info: Option<(Option<Span>, Span, Option<hir::HirId>, Option<Span>)>,
136141
inner_generator_body: Option<&hir::Body<'_>>,
137142
outer_generator: Option<DefId>,
138143
trait_ref: ty::TraitRef<'_>,
139144
target_ty: Ty<'tcx>,
140145
tables: &ty::TypeckTables<'_>,
141146
obligation: &PredicateObligation<'tcx>,
142147
next_code: Option<&ObligationCauseCode<'tcx>>,
143-
from_awaited_ty: Option<Span>,
144148
);
145149

146150
fn note_obligation_cause_code<T>(
@@ -1205,7 +1209,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
12051209
obligation.cause.span={:?}",
12061210
obligation.predicate, obligation.cause.span
12071211
);
1208-
let source_map = self.tcx.sess.source_map();
12091212
let hir = self.tcx.hir();
12101213

12111214
// Attempt to detect an async-await error by looking at the obligation causes, looking
@@ -1354,7 +1357,23 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
13541357
);
13551358
eq
13561359
};
1357-
let target_span = tables
1360+
1361+
let mut interior_or_upvar_span = None;
1362+
let mut interior_extra_info = None;
1363+
1364+
if let Some(upvars) = self.tcx.upvars(generator_did) {
1365+
interior_or_upvar_span = upvars.iter().find_map(|(upvar_id, upvar)| {
1366+
let upvar_ty = tables.node_type(*upvar_id);
1367+
let upvar_ty = self.resolve_vars_if_possible(&upvar_ty);
1368+
if ty_matches(&upvar_ty) {
1369+
Some(GeneratorInteriorOrUpvar::Upvar(upvar.span))
1370+
} else {
1371+
None
1372+
}
1373+
});
1374+
};
1375+
1376+
tables
13581377
.generator_interior_types
13591378
.iter()
13601379
.find(|ty::GeneratorInteriorTypeCause { ty, .. }| ty_matches(ty))
@@ -1375,39 +1394,28 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
13751394
.map(|expr| expr.span);
13761395
let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } =
13771396
cause;
1378-
(
1379-
span,
1380-
source_map.span_to_snippet(*span),
1381-
scope_span,
1382-
yield_span,
1383-
expr,
1384-
from_awaited_ty,
1385-
)
1397+
1398+
interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(*span));
1399+
interior_extra_info = Some((*scope_span, *yield_span, *expr, from_awaited_ty));
13861400
});
13871401

13881402
debug!(
1389-
"maybe_note_obligation_cause_for_async_await: target_ty={:?} \
1390-
generator_interior_types={:?} target_span={:?}",
1391-
target_ty, tables.generator_interior_types, target_span
1403+
"maybe_note_obligation_cause_for_async_await: interior_or_upvar={:?} \
1404+
generator_interior_types={:?}",
1405+
interior_or_upvar_span, tables.generator_interior_types
13921406
);
1393-
if let Some((target_span, Ok(snippet), scope_span, yield_span, expr, from_awaited_ty)) =
1394-
target_span
1395-
{
1407+
if let Some(interior_or_upvar_span) = interior_or_upvar_span {
13961408
self.note_obligation_cause_for_async_await(
13971409
err,
1398-
*target_span,
1399-
scope_span,
1400-
*yield_span,
1401-
*expr,
1402-
snippet,
1410+
interior_or_upvar_span,
1411+
interior_extra_info,
14031412
generator_body,
14041413
outer_generator,
14051414
trait_ref,
14061415
target_ty,
14071416
tables,
14081417
obligation,
14091418
next_code,
1410-
from_awaited_ty,
14111419
);
14121420
true
14131421
} else {
@@ -1420,19 +1428,15 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
14201428
fn note_obligation_cause_for_async_await(
14211429
&self,
14221430
err: &mut DiagnosticBuilder<'_>,
1423-
target_span: Span,
1424-
scope_span: &Option<Span>,
1425-
yield_span: Span,
1426-
expr: Option<hir::HirId>,
1427-
snippet: String,
1431+
interior_or_upvar_span: GeneratorInteriorOrUpvar,
1432+
interior_extra_info: Option<(Option<Span>, Span, Option<hir::HirId>, Option<Span>)>,
14281433
inner_generator_body: Option<&hir::Body<'_>>,
14291434
outer_generator: Option<DefId>,
14301435
trait_ref: ty::TraitRef<'_>,
14311436
target_ty: Ty<'tcx>,
14321437
tables: &ty::TypeckTables<'_>,
14331438
obligation: &PredicateObligation<'tcx>,
14341439
next_code: Option<&ObligationCauseCode<'tcx>>,
1435-
from_awaited_ty: Option<Span>,
14361440
) {
14371441
let source_map = self.tcx.sess.source_map();
14381442

@@ -1493,46 +1497,27 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
14931497
format!("does not implement `{}`", trait_ref.print_only_trait_path())
14941498
};
14951499

1496-
if let Some(await_span) = from_awaited_ty {
1497-
// The type causing this obligation is one being awaited at await_span.
1498-
let mut span = MultiSpan::from_span(await_span);
1499-
1500-
span.push_span_label(
1501-
await_span,
1502-
format!("await occurs here on type `{}`, which {}", target_ty, trait_explanation),
1503-
);
1504-
1505-
err.span_note(
1506-
span,
1507-
&format!(
1508-
"future {not_trait} as it awaits another future which {not_trait}",
1509-
not_trait = trait_explanation
1510-
),
1511-
);
1512-
} else {
1513-
// Look at the last interior type to get a span for the `.await`.
1514-
debug!(
1515-
"note_obligation_cause_for_async_await generator_interior_types: {:#?}",
1516-
tables.generator_interior_types
1517-
);
1500+
let mut explain_yield = |interior_span: Span,
1501+
yield_span: Span,
1502+
scope_span: Option<Span>| {
15181503
let mut span = MultiSpan::from_span(yield_span);
1519-
span.push_span_label(
1520-
yield_span,
1521-
format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet),
1522-
);
1523-
1524-
span.push_span_label(
1525-
target_span,
1526-
format!("has type `{}` which {}", target_ty, trait_explanation),
1527-
);
1528-
1529-
// If available, use the scope span to annotate the drop location.
1530-
if let Some(scope_span) = scope_span {
1504+
if let Ok(snippet) = source_map.span_to_snippet(interior_span) {
15311505
span.push_span_label(
1532-
source_map.end_point(*scope_span),
1533-
format!("`{}` is later dropped here", snippet),
1506+
yield_span,
1507+
format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet),
15341508
);
1509+
// If available, use the scope span to annotate the drop location.
1510+
if let Some(scope_span) = scope_span {
1511+
span.push_span_label(
1512+
source_map.end_point(scope_span),
1513+
format!("`{}` is later dropped here", snippet),
1514+
);
1515+
}
15351516
}
1517+
span.push_span_label(
1518+
interior_span,
1519+
format!("has type `{}` which {}", target_ty, trait_explanation),
1520+
);
15361521

15371522
err.span_note(
15381523
span,
@@ -1541,48 +1526,90 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
15411526
future_or_generator, trait_explanation, an_await_or_yield
15421527
),
15431528
);
1544-
}
1545-
1546-
if let Some(expr_id) = expr {
1547-
let expr = hir.expect_expr(expr_id);
1548-
debug!("target_ty evaluated from {:?}", expr);
1549-
1550-
let parent = hir.get_parent_node(expr_id);
1551-
if let Some(hir::Node::Expr(e)) = hir.find(parent) {
1552-
let parent_span = hir.span(parent);
1553-
let parent_did = parent.owner.to_def_id();
1554-
// ```rust
1555-
// impl T {
1556-
// fn foo(&self) -> i32 {}
1557-
// }
1558-
// T.foo();
1559-
// ^^^^^^^ a temporary `&T` created inside this method call due to `&self`
1560-
// ```
1561-
//
1562-
let is_region_borrow =
1563-
tables.expr_adjustments(expr).iter().any(|adj| adj.is_region_borrow());
1564-
1565-
// ```rust
1566-
// struct Foo(*const u8);
1567-
// bar(Foo(std::ptr::null())).await;
1568-
// ^^^^^^^^^^^^^^^^^^^^^ raw-ptr `*T` created inside this struct ctor.
1569-
// ```
1570-
debug!("parent_def_kind: {:?}", self.tcx.def_kind(parent_did));
1571-
let is_raw_borrow_inside_fn_like_call = match self.tcx.def_kind(parent_did) {
1572-
DefKind::Fn | DefKind::Ctor(..) => target_ty.is_unsafe_ptr(),
1573-
_ => false,
1574-
};
1529+
};
1530+
match interior_or_upvar_span {
1531+
GeneratorInteriorOrUpvar::Interior(interior_span) => {
1532+
if let Some((scope_span, yield_span, expr, from_awaited_ty)) = interior_extra_info {
1533+
if let Some(await_span) = from_awaited_ty {
1534+
// The type causing this obligation is one being awaited at await_span.
1535+
let mut span = MultiSpan::from_span(await_span);
1536+
span.push_span_label(
1537+
await_span,
1538+
format!(
1539+
"await occurs here on type `{}`, which {}",
1540+
target_ty, trait_explanation
1541+
),
1542+
);
1543+
err.span_note(
1544+
span,
1545+
&format!(
1546+
"future {not_trait} as it awaits another future which {not_trait}",
1547+
not_trait = trait_explanation
1548+
),
1549+
);
1550+
} else {
1551+
// Look at the last interior type to get a span for the `.await`.
1552+
debug!(
1553+
"note_obligation_cause_for_async_await generator_interior_types: {:#?}",
1554+
tables.generator_interior_types
1555+
);
1556+
explain_yield(interior_span, yield_span, scope_span);
1557+
}
15751558

1576-
if (tables.is_method_call(e) && is_region_borrow)
1577-
|| is_raw_borrow_inside_fn_like_call
1578-
{
1579-
err.span_help(
1580-
parent_span,
1581-
"consider moving this into a `let` \
1559+
if let Some(expr_id) = expr {
1560+
let expr = hir.expect_expr(expr_id);
1561+
debug!("target_ty evaluated from {:?}", expr);
1562+
1563+
let parent = hir.get_parent_node(expr_id);
1564+
if let Some(hir::Node::Expr(e)) = hir.find(parent) {
1565+
let parent_span = hir.span(parent);
1566+
let parent_did = parent.owner.to_def_id();
1567+
// ```rust
1568+
// impl T {
1569+
// fn foo(&self) -> i32 {}
1570+
// }
1571+
// T.foo();
1572+
// ^^^^^^^ a temporary `&T` created inside this method call due to `&self`
1573+
// ```
1574+
//
1575+
let is_region_borrow = tables
1576+
.expr_adjustments(expr)
1577+
.iter()
1578+
.any(|adj| adj.is_region_borrow());
1579+
1580+
// ```rust
1581+
// struct Foo(*const u8);
1582+
// bar(Foo(std::ptr::null())).await;
1583+
// ^^^^^^^^^^^^^^^^^^^^^ raw-ptr `*T` created inside this struct ctor.
1584+
// ```
1585+
debug!("parent_def_kind: {:?}", self.tcx.def_kind(parent_did));
1586+
let is_raw_borrow_inside_fn_like_call =
1587+
match self.tcx.def_kind(parent_did) {
1588+
DefKind::Fn | DefKind::Ctor(..) => target_ty.is_unsafe_ptr(),
1589+
_ => false,
1590+
};
1591+
1592+
if (tables.is_method_call(e) && is_region_borrow)
1593+
|| is_raw_borrow_inside_fn_like_call
1594+
{
1595+
err.span_help(
1596+
parent_span,
1597+
"consider moving this into a `let` \
15821598
binding to create a shorter lived borrow",
1583-
);
1599+
);
1600+
}
1601+
}
1602+
}
15841603
}
15851604
}
1605+
GeneratorInteriorOrUpvar::Upvar(upvar_span) => {
1606+
let mut span = MultiSpan::from_span(upvar_span);
1607+
span.push_span_label(
1608+
upvar_span,
1609+
format!("has type `{}` which {}", target_ty, trait_explanation),
1610+
);
1611+
err.span_note(span, &format!("captured value {}", trait_explanation));
1612+
}
15861613
}
15871614

15881615
// Add a note for the item obligation that remains - normally a note pointing to the
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// edition:2018
2+
3+
use std::future::Future;
4+
fn foo<T: Send, U>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send {
5+
//~^ Error future cannot be sent between threads safely
6+
async { (ty, ty1) }
7+
}
8+
9+
fn main() {}
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error: future cannot be sent between threads safely
2+
--> $DIR/issue-70818.rs:4:38
3+
|
4+
LL | fn foo<T: Send, U>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
6+
LL |
7+
LL | async { (ty, ty1) }
8+
| ------------------- this returned value is of type `impl std::future::Future`
9+
|
10+
= help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `U`
11+
note: captured value is not `Send`
12+
--> $DIR/issue-70818.rs:6:18
13+
|
14+
LL | async { (ty, ty1) }
15+
| ^^^ has type `U` which is not `Send`
16+
= note: the return type of a function must have a statically known size
17+
help: consider restricting type parameter `U`
18+
|
19+
LL | fn foo<T: Send, U: std::marker::Send>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send {
20+
| ^^^^^^^^^^^^^^^^^^^
21+
22+
error: aborting due to previous error
23+

0 commit comments

Comments
 (0)