1
Fork 0

Auto merge of #71923 - csmoe:issue-70818, r=tmandry

Check non-Send/Sync upvars captured by generator

Closes #70818
r? @tmandry
This commit is contained in:
bors 2020-05-20 09:28:25 +00:00
commit f182c4af8a
3 changed files with 164 additions and 105 deletions

View file

@ -25,6 +25,14 @@ use std::fmt;
use super::InferCtxtPrivExt; use super::InferCtxtPrivExt;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _; use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
#[derive(Debug)]
pub enum GeneratorInteriorOrUpvar {
// span of interior type
Interior(Span),
// span of upvar
Upvar(Span),
}
// This trait is public to expose the diagnostics methods to clippy. // This trait is public to expose the diagnostics methods to clippy.
pub trait InferCtxtExt<'tcx> { pub trait InferCtxtExt<'tcx> {
fn suggest_restricting_param_bound( fn suggest_restricting_param_bound(
@ -128,11 +136,8 @@ pub trait InferCtxtExt<'tcx> {
fn note_obligation_cause_for_async_await( fn note_obligation_cause_for_async_await(
&self, &self,
err: &mut DiagnosticBuilder<'_>, err: &mut DiagnosticBuilder<'_>,
target_span: Span, interior_or_upvar_span: GeneratorInteriorOrUpvar,
scope_span: &Option<Span>, interior_extra_info: Option<(Option<Span>, Span, Option<hir::HirId>, Option<Span>)>,
await_span: Span,
expr: Option<hir::HirId>,
snippet: String,
inner_generator_body: Option<&hir::Body<'_>>, inner_generator_body: Option<&hir::Body<'_>>,
outer_generator: Option<DefId>, outer_generator: Option<DefId>,
trait_ref: ty::TraitRef<'_>, trait_ref: ty::TraitRef<'_>,
@ -140,7 +145,6 @@ pub trait InferCtxtExt<'tcx> {
tables: &ty::TypeckTables<'_>, tables: &ty::TypeckTables<'_>,
obligation: &PredicateObligation<'tcx>, obligation: &PredicateObligation<'tcx>,
next_code: Option<&ObligationCauseCode<'tcx>>, next_code: Option<&ObligationCauseCode<'tcx>>,
from_awaited_ty: Option<Span>,
); );
fn note_obligation_cause_code<T>( fn note_obligation_cause_code<T>(
@ -1205,7 +1209,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
obligation.cause.span={:?}", obligation.cause.span={:?}",
obligation.predicate, obligation.cause.span obligation.predicate, obligation.cause.span
); );
let source_map = self.tcx.sess.source_map();
let hir = self.tcx.hir(); let hir = self.tcx.hir();
// Attempt to detect an async-await error by looking at the obligation causes, looking // 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> {
); );
eq eq
}; };
let target_span = tables
let mut interior_or_upvar_span = None;
let mut interior_extra_info = None;
if let Some(upvars) = self.tcx.upvars(generator_did) {
interior_or_upvar_span = upvars.iter().find_map(|(upvar_id, upvar)| {
let upvar_ty = tables.node_type(*upvar_id);
let upvar_ty = self.resolve_vars_if_possible(&upvar_ty);
if ty_matches(&upvar_ty) {
Some(GeneratorInteriorOrUpvar::Upvar(upvar.span))
} else {
None
}
});
};
tables
.generator_interior_types .generator_interior_types
.iter() .iter()
.find(|ty::GeneratorInteriorTypeCause { ty, .. }| ty_matches(ty)) .find(|ty::GeneratorInteriorTypeCause { ty, .. }| ty_matches(ty))
@ -1375,31 +1394,21 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
.map(|expr| expr.span); .map(|expr| expr.span);
let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } = let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } =
cause; cause;
(
span, interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(*span));
source_map.span_to_snippet(*span), interior_extra_info = Some((*scope_span, *yield_span, *expr, from_awaited_ty));
scope_span,
yield_span,
expr,
from_awaited_ty,
)
}); });
debug!( debug!(
"maybe_note_obligation_cause_for_async_await: target_ty={:?} \ "maybe_note_obligation_cause_for_async_await: interior_or_upvar={:?} \
generator_interior_types={:?} target_span={:?}", generator_interior_types={:?}",
target_ty, tables.generator_interior_types, target_span interior_or_upvar_span, tables.generator_interior_types
); );
if let Some((target_span, Ok(snippet), scope_span, yield_span, expr, from_awaited_ty)) = if let Some(interior_or_upvar_span) = interior_or_upvar_span {
target_span
{
self.note_obligation_cause_for_async_await( self.note_obligation_cause_for_async_await(
err, err,
*target_span, interior_or_upvar_span,
scope_span, interior_extra_info,
*yield_span,
*expr,
snippet,
generator_body, generator_body,
outer_generator, outer_generator,
trait_ref, trait_ref,
@ -1407,7 +1416,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
tables, tables,
obligation, obligation,
next_code, next_code,
from_awaited_ty,
); );
true true
} else { } else {
@ -1420,11 +1428,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
fn note_obligation_cause_for_async_await( fn note_obligation_cause_for_async_await(
&self, &self,
err: &mut DiagnosticBuilder<'_>, err: &mut DiagnosticBuilder<'_>,
target_span: Span, interior_or_upvar_span: GeneratorInteriorOrUpvar,
scope_span: &Option<Span>, interior_extra_info: Option<(Option<Span>, Span, Option<hir::HirId>, Option<Span>)>,
yield_span: Span,
expr: Option<hir::HirId>,
snippet: String,
inner_generator_body: Option<&hir::Body<'_>>, inner_generator_body: Option<&hir::Body<'_>>,
outer_generator: Option<DefId>, outer_generator: Option<DefId>,
trait_ref: ty::TraitRef<'_>, trait_ref: ty::TraitRef<'_>,
@ -1432,7 +1437,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
tables: &ty::TypeckTables<'_>, tables: &ty::TypeckTables<'_>,
obligation: &PredicateObligation<'tcx>, obligation: &PredicateObligation<'tcx>,
next_code: Option<&ObligationCauseCode<'tcx>>, next_code: Option<&ObligationCauseCode<'tcx>>,
from_awaited_ty: Option<Span>,
) { ) {
let source_map = self.tcx.sess.source_map(); let source_map = self.tcx.sess.source_map();
@ -1493,47 +1497,28 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
format!("does not implement `{}`", trait_ref.print_only_trait_path()) format!("does not implement `{}`", trait_ref.print_only_trait_path())
}; };
if let Some(await_span) = from_awaited_ty { let mut explain_yield = |interior_span: Span,
// The type causing this obligation is one being awaited at await_span. yield_span: Span,
let mut span = MultiSpan::from_span(await_span); scope_span: Option<Span>| {
span.push_span_label(
await_span,
format!("await occurs here on type `{}`, which {}", target_ty, trait_explanation),
);
err.span_note(
span,
&format!(
"future {not_trait} as it awaits another future which {not_trait}",
not_trait = trait_explanation
),
);
} else {
// Look at the last interior type to get a span for the `.await`.
debug!(
"note_obligation_cause_for_async_await generator_interior_types: {:#?}",
tables.generator_interior_types
);
let mut span = MultiSpan::from_span(yield_span); let mut span = MultiSpan::from_span(yield_span);
if let Ok(snippet) = source_map.span_to_snippet(interior_span) {
span.push_span_label(
yield_span,
format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet),
);
// If available, use the scope span to annotate the drop location.
if let Some(scope_span) = scope_span {
span.push_span_label(
source_map.end_point(scope_span),
format!("`{}` is later dropped here", snippet),
);
}
}
span.push_span_label( span.push_span_label(
yield_span, interior_span,
format!("{} occurs here, with `{}` maybe used later", await_or_yield, snippet),
);
span.push_span_label(
target_span,
format!("has type `{}` which {}", target_ty, trait_explanation), format!("has type `{}` which {}", target_ty, trait_explanation),
); );
// If available, use the scope span to annotate the drop location.
if let Some(scope_span) = scope_span {
span.push_span_label(
source_map.end_point(*scope_span),
format!("`{}` is later dropped here", snippet),
);
}
err.span_note( err.span_note(
span, span,
&format!( &format!(
@ -1541,48 +1526,90 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
future_or_generator, trait_explanation, an_await_or_yield future_or_generator, trait_explanation, an_await_or_yield
), ),
); );
} };
match interior_or_upvar_span {
GeneratorInteriorOrUpvar::Interior(interior_span) => {
if let Some((scope_span, yield_span, expr, from_awaited_ty)) = interior_extra_info {
if let Some(await_span) = from_awaited_ty {
// The type causing this obligation is one being awaited at await_span.
let mut span = MultiSpan::from_span(await_span);
span.push_span_label(
await_span,
format!(
"await occurs here on type `{}`, which {}",
target_ty, trait_explanation
),
);
err.span_note(
span,
&format!(
"future {not_trait} as it awaits another future which {not_trait}",
not_trait = trait_explanation
),
);
} else {
// Look at the last interior type to get a span for the `.await`.
debug!(
"note_obligation_cause_for_async_await generator_interior_types: {:#?}",
tables.generator_interior_types
);
explain_yield(interior_span, yield_span, scope_span);
}
if let Some(expr_id) = expr { if let Some(expr_id) = expr {
let expr = hir.expect_expr(expr_id); let expr = hir.expect_expr(expr_id);
debug!("target_ty evaluated from {:?}", expr); debug!("target_ty evaluated from {:?}", expr);
let parent = hir.get_parent_node(expr_id); let parent = hir.get_parent_node(expr_id);
if let Some(hir::Node::Expr(e)) = hir.find(parent) { if let Some(hir::Node::Expr(e)) = hir.find(parent) {
let parent_span = hir.span(parent); let parent_span = hir.span(parent);
let parent_did = parent.owner.to_def_id(); let parent_did = parent.owner.to_def_id();
// ```rust // ```rust
// impl T { // impl T {
// fn foo(&self) -> i32 {} // fn foo(&self) -> i32 {}
// } // }
// T.foo(); // T.foo();
// ^^^^^^^ a temporary `&T` created inside this method call due to `&self` // ^^^^^^^ a temporary `&T` created inside this method call due to `&self`
// ``` // ```
// //
let is_region_borrow = let is_region_borrow = tables
tables.expr_adjustments(expr).iter().any(|adj| adj.is_region_borrow()); .expr_adjustments(expr)
.iter()
.any(|adj| adj.is_region_borrow());
// ```rust // ```rust
// struct Foo(*const u8); // struct Foo(*const u8);
// bar(Foo(std::ptr::null())).await; // bar(Foo(std::ptr::null())).await;
// ^^^^^^^^^^^^^^^^^^^^^ raw-ptr `*T` created inside this struct ctor. // ^^^^^^^^^^^^^^^^^^^^^ raw-ptr `*T` created inside this struct ctor.
// ``` // ```
debug!("parent_def_kind: {:?}", self.tcx.def_kind(parent_did)); debug!("parent_def_kind: {:?}", self.tcx.def_kind(parent_did));
let is_raw_borrow_inside_fn_like_call = match self.tcx.def_kind(parent_did) { let is_raw_borrow_inside_fn_like_call =
DefKind::Fn | DefKind::Ctor(..) => target_ty.is_unsafe_ptr(), match self.tcx.def_kind(parent_did) {
_ => false, DefKind::Fn | DefKind::Ctor(..) => target_ty.is_unsafe_ptr(),
}; _ => false,
};
if (tables.is_method_call(e) && is_region_borrow) if (tables.is_method_call(e) && is_region_borrow)
|| is_raw_borrow_inside_fn_like_call || is_raw_borrow_inside_fn_like_call
{ {
err.span_help( err.span_help(
parent_span, parent_span,
"consider moving this into a `let` \ "consider moving this into a `let` \
binding to create a shorter lived borrow", binding to create a shorter lived borrow",
); );
}
}
}
} }
} }
GeneratorInteriorOrUpvar::Upvar(upvar_span) => {
let mut span = MultiSpan::from_span(upvar_span);
span.push_span_label(
upvar_span,
format!("has type `{}` which {}", target_ty, trait_explanation),
);
err.span_note(span, &format!("captured value {}", trait_explanation));
}
} }
// Add a note for the item obligation that remains - normally a note pointing to the // Add a note for the item obligation that remains - normally a note pointing to the

View file

@ -0,0 +1,9 @@
// edition:2018
use std::future::Future;
fn foo<T: Send, U>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send {
//~^ Error future cannot be sent between threads safely
async { (ty, ty1) }
}
fn main() {}

View file

@ -0,0 +1,23 @@
error: future cannot be sent between threads safely
--> $DIR/issue-70818.rs:4:38
|
LL | fn foo<T: Send, U>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
LL |
LL | async { (ty, ty1) }
| ------------------- this returned value is of type `impl std::future::Future`
|
= help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `U`
note: captured value is not `Send`
--> $DIR/issue-70818.rs:6:18
|
LL | async { (ty, ty1) }
| ^^^ has type `U` which is not `Send`
= note: the return type of a function must have a statically known size
help: consider restricting type parameter `U`
|
LL | fn foo<T: Send, U: std::marker::Send>(ty: T, ty1: U) -> impl Future<Output = (T, U)> + Send {
| ^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error