1
Fork 0

Use the "nice E0277 errors"[1] for !Send impl Future from foreign crate

Partly address #78543 by making the error quieter.

We don't have access to the `typeck` tables from foreign crates, so we
used to completely skip the new code when checking foreign crates. Now,
we carry on and don't provide as nice output (we don't clarify *what* is
making the `Future: !Send`), but at least we no longer emit a sea of
derived obligations in the output.

[1]: https://blog.rust-lang.org/inside-rust/2019/10/11/AsyncAwait-Not-Send-Error-Improvements.html
This commit is contained in:
Esteban Kuber 2021-10-14 17:57:39 +00:00
parent ed08a67566
commit 6dcff4e9f6
3 changed files with 73 additions and 91 deletions

View file

@ -151,7 +151,7 @@ pub trait InferCtxtExt<'tcx> {
outer_generator: Option<DefId>, outer_generator: Option<DefId>,
trait_ref: ty::TraitRef<'tcx>, trait_ref: ty::TraitRef<'tcx>,
target_ty: Ty<'tcx>, target_ty: Ty<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>, typeck_results: Option<&ty::TypeckResults<'tcx>>,
obligation: &PredicateObligation<'tcx>, obligation: &PredicateObligation<'tcx>,
next_code: Option<&ObligationCauseCode<'tcx>>, next_code: Option<&ObligationCauseCode<'tcx>>,
); );
@ -1463,11 +1463,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
} }
// Only continue if a generator was found. // Only continue if a generator was found.
debug!( debug!(?generator, ?trait_ref, ?target_ty, "maybe_note_obligation_cause_for_async_await");
"maybe_note_obligation_cause_for_async_await: generator={:?} trait_ref={:?} \
target_ty={:?}",
generator, trait_ref, target_ty
);
let (generator_did, trait_ref, target_ty) = match (generator, trait_ref, target_ty) { let (generator_did, trait_ref, target_ty) = match (generator, trait_ref, target_ty) {
(Some(generator_did), Some(trait_ref), Some(target_ty)) => { (Some(generator_did), Some(trait_ref), Some(target_ty)) => {
(generator_did, trait_ref, target_ty) (generator_did, trait_ref, target_ty)
@ -1477,14 +1473,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
let span = self.tcx.def_span(generator_did); let span = self.tcx.def_span(generator_did);
// Do not ICE on closure typeck (#66868).
if !generator_did.is_local() {
return false;
}
// Get the typeck results from the infcx if the generator is the function we are
// currently type-checking; otherwise, get them by performing a query.
// This is needed to avoid cycles.
let in_progress_typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()); let in_progress_typeck_results = self.in_progress_typeck_results.map(|t| t.borrow());
let generator_did_root = self.tcx.closure_base_def_id(generator_did); let generator_did_root = self.tcx.closure_base_def_id(generator_did);
debug!( debug!(
@ -1495,14 +1483,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
in_progress_typeck_results.as_ref().map(|t| t.hir_owner), in_progress_typeck_results.as_ref().map(|t| t.hir_owner),
span span
); );
let query_typeck_results;
let typeck_results: &TypeckResults<'tcx> = match &in_progress_typeck_results {
Some(t) if t.hir_owner.to_def_id() == generator_did_root => t,
_ => {
query_typeck_results = self.tcx.typeck(generator_did.expect_local());
&query_typeck_results
}
};
let generator_body = generator_did let generator_body = generator_did
.as_local() .as_local()
@ -1545,6 +1525,20 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
let mut interior_or_upvar_span = None; let mut interior_or_upvar_span = None;
let mut interior_extra_info = None; let mut interior_extra_info = None;
// Get the typeck results from the infcx if the generator is the function we are currently
// type-checking; otherwise, get them by performing a query. This is needed to avoid
// cycles. If we can't use resolved types because the generator comes from another crate,
// we still provide a targeted error but without all the relevant spans.
let query_typeck_results;
let typeck_results: Option<&TypeckResults<'tcx>> = match &in_progress_typeck_results {
Some(t) if t.hir_owner.to_def_id() == generator_did_root => Some(&t),
_ if generator_did.is_local() => {
query_typeck_results = self.tcx.typeck(generator_did.expect_local());
Some(&query_typeck_results)
}
_ => None, // Do not ICE on closure typeck (#66868).
};
if let Some(typeck_results) = typeck_results {
if let Some(upvars) = self.tcx.upvars_mentioned(generator_did) { if let Some(upvars) = self.tcx.upvars_mentioned(generator_did) {
interior_or_upvar_span = upvars.iter().find_map(|(upvar_id, upvar)| { interior_or_upvar_span = upvars.iter().find_map(|(upvar_id, upvar)| {
let upvar_ty = typeck_results.node_type(*upvar_id); let upvar_ty = typeck_results.node_type(*upvar_id);
@ -1571,25 +1565,19 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
.into_iter() .into_iter()
.map(|id| hir.expect_expr(id)) .map(|id| hir.expect_expr(id))
.find(|await_expr| { .find(|await_expr| {
let ty = typeck_results.expr_ty_adjusted(&await_expr); ty_matches(ty::Binder::dummy(typeck_results.expr_ty_adjusted(&await_expr)))
debug!(
"maybe_note_obligation_cause_for_async_await: await_expr={:?}",
await_expr
);
ty_matches(ty::Binder::dummy(ty))
}) })
.map(|expr| expr.span); .map(|expr| expr.span);
let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } = cause; let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } =
cause;
interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(*span)); interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(*span));
interior_extra_info = Some((*scope_span, *yield_span, *expr, from_awaited_ty)); interior_extra_info = Some((*scope_span, *yield_span, *expr, from_awaited_ty));
}; };
} else {
interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(span));
}
debug!(
"maybe_note_obligation_cause_for_async_await: interior_or_upvar={:?} \
generator_interior_types={:?}",
interior_or_upvar_span, typeck_results.generator_interior_types
);
if let Some(interior_or_upvar_span) = interior_or_upvar_span { if let Some(interior_or_upvar_span) = interior_or_upvar_span {
self.note_obligation_cause_for_async_await( self.note_obligation_cause_for_async_await(
err, err,
@ -1620,7 +1608,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
outer_generator: Option<DefId>, outer_generator: Option<DefId>,
trait_ref: ty::TraitRef<'tcx>, trait_ref: ty::TraitRef<'tcx>,
target_ty: Ty<'tcx>, target_ty: Ty<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>, typeck_results: Option<&ty::TypeckResults<'tcx>>,
obligation: &PredicateObligation<'tcx>, obligation: &PredicateObligation<'tcx>,
next_code: Option<&ObligationCauseCode<'tcx>>, next_code: Option<&ObligationCauseCode<'tcx>>,
) { ) {
@ -1831,7 +1819,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
// Look at the last interior type to get a span for the `.await`. // Look at the last interior type to get a span for the `.await`.
debug!( debug!(
"note_obligation_cause_for_async_await generator_interior_types: {:#?}", "note_obligation_cause_for_async_await generator_interior_types: {:#?}",
typeck_results.generator_interior_types typeck_results.as_ref().map(|t| &t.generator_interior_types)
); );
explain_yield(interior_span, yield_span, scope_span); explain_yield(interior_span, yield_span, scope_span);
} }
@ -1852,10 +1840,14 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
// ^^^^^^^ 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 = typeck_results let is_region_borrow = if let Some(typeck_results) = typeck_results {
typeck_results
.expr_adjustments(expr) .expr_adjustments(expr)
.iter() .iter()
.any(|adj| adj.is_region_borrow()); .any(|adj| adj.is_region_borrow())
} else {
false
};
// ```rust // ```rust
// struct Foo(*const u8); // struct Foo(*const u8);
@ -1868,7 +1860,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
DefKind::Fn | DefKind::Ctor(..) => target_ty.is_unsafe_ptr(), DefKind::Fn | DefKind::Ctor(..) => target_ty.is_unsafe_ptr(),
_ => false, _ => false,
}; };
if let Some(typeck_results) = typeck_results {
if (typeck_results.is_method_call(e) && is_region_borrow) if (typeck_results.is_method_call(e) && is_region_borrow)
|| is_raw_borrow_inside_fn_like_call || is_raw_borrow_inside_fn_like_call
{ {
@ -1882,6 +1874,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
} }
} }
} }
}
GeneratorInteriorOrUpvar::Upvar(upvar_span) => { GeneratorInteriorOrUpvar::Upvar(upvar_span) => {
// `Some(ref_ty)` if `target_ty` is `&T` and `T` fails to impl `Sync` // `Some(ref_ty)` if `target_ty` is `&T` and `T` fails to impl `Sync`
let refers_to_non_sync = match target_ty.kind() { let refers_to_non_sync = match target_ty.kind() {

View file

@ -7,5 +7,5 @@ fn g(_: impl Send) {}
fn main() { fn main() {
g(issue_67893::run()) g(issue_67893::run())
//~^ ERROR: `MutexGuard<'_, ()>` cannot be sent between threads safely //~^ ERROR generator cannot be sent between threads safely
} }

View file

@ -1,20 +1,10 @@
error[E0277]: `MutexGuard<'_, ()>` cannot be sent between threads safely error: generator cannot be sent between threads safely
--> $DIR/issue-67893.rs:9:5 --> $DIR/issue-67893.rs:9:5
| |
LL | g(issue_67893::run()) LL | g(issue_67893::run())
| ^ `MutexGuard<'_, ()>` cannot be sent between threads safely | ^ generator is not `Send`
|
::: $DIR/auxiliary/issue_67893.rs:7:20
|
LL | pub async fn run() {
| - within this `impl Future`
| |
= help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, ()>` = help: within `impl Future`, the trait `Send` is not implemented for `MutexGuard<'_, ()>`
= note: required because it appears within the type `for<'r, 's, 't0, 't1, 't2, 't3> {ResumeTy, Arc<Mutex<()>>, &'r Mutex<()>, Result<MutexGuard<'s, ()>, PoisonError<MutexGuard<'t0, ()>>>, &'t1 MutexGuard<'t2, ()>, MutexGuard<'t3, ()>, (), impl Future}`
= note: required because it appears within the type `[static generator@run::{closure#0}]`
= note: required because it appears within the type `from_generator::GenFuture<[static generator@run::{closure#0}]>`
= note: required because it appears within the type `impl Future`
= note: required because it appears within the type `impl Future`
note: required by a bound in `g` note: required by a bound in `g`
--> $DIR/issue-67893.rs:6:14 --> $DIR/issue-67893.rs:6:14
| |
@ -23,4 +13,3 @@ LL | fn g(_: impl Send) {}
error: aborting due to previous error error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.