mirror of
https://github.com/rust-lang/rust.git
synced 2024-10-30 14:01:51 +00:00
Rollup merge of #131549 - compiler-errors:try-in-sync, r=spastorino
Add a note for `?` on a `impl Future<Output = Result<..>>` in sync function
It's confusing to `?` a future of a result in a sync function. We have a suggestion to `.await` it if we're in an async function, but not a sync function. Note that this is the case for sync functions, at least.
Let's be a bit more vague about a fix, since it's somewhat context dependent. For example, you could block on it, or you could make your function asynchronous. 🤷
This commit is contained in:
commit
457087ed29
@ -10,7 +10,8 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
|
|||||||
use rustc_data_structures::unord::UnordMap;
|
use rustc_data_structures::unord::UnordMap;
|
||||||
use rustc_errors::codes::*;
|
use rustc_errors::codes::*;
|
||||||
use rustc_errors::{
|
use rustc_errors::{
|
||||||
Applicability, Diag, ErrorGuaranteed, StashKey, Subdiagnostic, pluralize, struct_span_code_err,
|
Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, Subdiagnostic, pluralize,
|
||||||
|
struct_span_code_err,
|
||||||
};
|
};
|
||||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
@ -2763,12 +2764,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
field_ident.span,
|
field_ident.span,
|
||||||
"field not available in `impl Future`, but it is available in its `Output`",
|
"field not available in `impl Future`, but it is available in its `Output`",
|
||||||
);
|
);
|
||||||
err.span_suggestion_verbose(
|
match self.tcx.coroutine_kind(self.body_id) {
|
||||||
base.span.shrink_to_hi(),
|
Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => {
|
||||||
"consider `await`ing on the `Future` and access the field of its `Output`",
|
err.span_suggestion_verbose(
|
||||||
".await",
|
base.span.shrink_to_hi(),
|
||||||
Applicability::MaybeIncorrect,
|
"consider `await`ing on the `Future` to access the field",
|
||||||
);
|
".await",
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let mut span: MultiSpan = base.span.into();
|
||||||
|
span.push_span_label(self.tcx.def_span(self.body_id), "this is not `async`");
|
||||||
|
err.span_note(
|
||||||
|
span,
|
||||||
|
"this implements `Future` and its output type has the field, \
|
||||||
|
but the future cannot be awaited in a synchronous function",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ban_nonexisting_field(
|
fn ban_nonexisting_field(
|
||||||
|
@ -3594,52 +3594,64 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
|
|||||||
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
trait_pred: ty::PolyTraitPredicate<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) {
|
) {
|
||||||
if let Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) =
|
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
|
||||||
self.tcx.coroutine_kind(obligation.cause.body_id)
|
|
||||||
|
let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
|
||||||
|
let impls_future = self.type_implements_trait(
|
||||||
|
future_trait,
|
||||||
|
[self.tcx.instantiate_bound_regions_with_erased(self_ty)],
|
||||||
|
obligation.param_env,
|
||||||
|
);
|
||||||
|
if !impls_future.must_apply_modulo_regions() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
|
||||||
|
// `<T as Future>::Output`
|
||||||
|
let projection_ty = trait_pred.map_bound(|trait_pred| {
|
||||||
|
Ty::new_projection(
|
||||||
|
self.tcx,
|
||||||
|
item_def_id,
|
||||||
|
// Future::Output has no args
|
||||||
|
[trait_pred.self_ty()],
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let InferOk { value: projection_ty, .. } =
|
||||||
|
self.at(&obligation.cause, obligation.param_env).normalize(projection_ty);
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty)
|
||||||
|
);
|
||||||
|
let try_obligation = self.mk_trait_obligation_with_new_self_ty(
|
||||||
|
obligation.param_env,
|
||||||
|
trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
|
||||||
|
);
|
||||||
|
debug!(try_trait_obligation = ?try_obligation);
|
||||||
|
if self.predicate_may_hold(&try_obligation)
|
||||||
|
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
|
||||||
|
&& snippet.ends_with('?')
|
||||||
{
|
{
|
||||||
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
|
match self.tcx.coroutine_kind(obligation.cause.body_id) {
|
||||||
|
Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => {
|
||||||
let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
|
err.span_suggestion_verbose(
|
||||||
let impls_future = self.type_implements_trait(
|
span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(),
|
||||||
future_trait,
|
"consider `await`ing on the `Future`",
|
||||||
[self.tcx.instantiate_bound_regions_with_erased(self_ty)],
|
".await",
|
||||||
obligation.param_env,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
if !impls_future.must_apply_modulo_regions() {
|
}
|
||||||
return;
|
_ => {
|
||||||
}
|
let mut span: MultiSpan = span.with_lo(span.hi() - BytePos(1)).into();
|
||||||
|
span.push_span_label(
|
||||||
let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
|
self.tcx.def_span(obligation.cause.body_id),
|
||||||
// `<T as Future>::Output`
|
"this is not `async`",
|
||||||
let projection_ty = trait_pred.map_bound(|trait_pred| {
|
);
|
||||||
Ty::new_projection(
|
err.span_note(
|
||||||
self.tcx,
|
span,
|
||||||
item_def_id,
|
"this implements `Future` and its output type supports \
|
||||||
// Future::Output has no args
|
`?`, but the future cannot be awaited in a synchronous function",
|
||||||
[trait_pred.self_ty()],
|
);
|
||||||
)
|
}
|
||||||
});
|
|
||||||
let InferOk { value: projection_ty, .. } =
|
|
||||||
self.at(&obligation.cause, obligation.param_env).normalize(projection_ty);
|
|
||||||
|
|
||||||
debug!(
|
|
||||||
normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty)
|
|
||||||
);
|
|
||||||
let try_obligation = self.mk_trait_obligation_with_new_self_ty(
|
|
||||||
obligation.param_env,
|
|
||||||
trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
|
|
||||||
);
|
|
||||||
debug!(try_trait_obligation = ?try_obligation);
|
|
||||||
if self.predicate_may_hold(&try_obligation)
|
|
||||||
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
|
|
||||||
&& snippet.ends_with('?')
|
|
||||||
{
|
|
||||||
err.span_suggestion_verbose(
|
|
||||||
span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(),
|
|
||||||
"consider `await`ing on the `Future`",
|
|
||||||
".await",
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
tests/ui/async-await/field-in-sync.rs
Normal file
13
tests/ui/async-await/field-in-sync.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
//@ edition: 2021
|
||||||
|
|
||||||
|
struct S {
|
||||||
|
field: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn foo() -> S { todo!() }
|
||||||
|
|
||||||
|
fn main() -> Result<(), ()> {
|
||||||
|
foo().field;
|
||||||
|
//~^ ERROR no field `field` on type `impl Future<Output = S>`
|
||||||
|
Ok(())
|
||||||
|
}
|
17
tests/ui/async-await/field-in-sync.stderr
Normal file
17
tests/ui/async-await/field-in-sync.stderr
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
error[E0609]: no field `field` on type `impl Future<Output = S>`
|
||||||
|
--> $DIR/field-in-sync.rs:10:11
|
||||||
|
|
|
||||||
|
LL | foo().field;
|
||||||
|
| ^^^^^ field not available in `impl Future`, but it is available in its `Output`
|
||||||
|
|
|
||||||
|
note: this implements `Future` and its output type has the field, but the future cannot be awaited in a synchronous function
|
||||||
|
--> $DIR/field-in-sync.rs:10:5
|
||||||
|
|
|
||||||
|
LL | fn main() -> Result<(), ()> {
|
||||||
|
| --------------------------- this is not `async`
|
||||||
|
LL | foo().field;
|
||||||
|
| ^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0609`.
|
@ -28,7 +28,7 @@ error[E0609]: no field `0` on type `impl Future<Output = Tuple>`
|
|||||||
LL | let _: i32 = tuple().0;
|
LL | let _: i32 = tuple().0;
|
||||||
| ^ field not available in `impl Future`, but it is available in its `Output`
|
| ^ field not available in `impl Future`, but it is available in its `Output`
|
||||||
|
|
|
|
||||||
help: consider `await`ing on the `Future` and access the field of its `Output`
|
help: consider `await`ing on the `Future` to access the field
|
||||||
|
|
|
|
||||||
LL | let _: i32 = tuple().await.0;
|
LL | let _: i32 = tuple().await.0;
|
||||||
| ++++++
|
| ++++++
|
||||||
@ -39,7 +39,7 @@ error[E0609]: no field `a` on type `impl Future<Output = Struct>`
|
|||||||
LL | let _: i32 = struct_().a;
|
LL | let _: i32 = struct_().a;
|
||||||
| ^ field not available in `impl Future`, but it is available in its `Output`
|
| ^ field not available in `impl Future`, but it is available in its `Output`
|
||||||
|
|
|
|
||||||
help: consider `await`ing on the `Future` and access the field of its `Output`
|
help: consider `await`ing on the `Future` to access the field
|
||||||
|
|
|
|
||||||
LL | let _: i32 = struct_().await.a;
|
LL | let _: i32 = struct_().await.a;
|
||||||
| ++++++
|
| ++++++
|
||||||
|
9
tests/ui/async-await/try-in-sync.rs
Normal file
9
tests/ui/async-await/try-in-sync.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
//@ edition: 2021
|
||||||
|
|
||||||
|
async fn foo() -> Result<(), ()> { todo!() }
|
||||||
|
|
||||||
|
fn main() -> Result<(), ()> {
|
||||||
|
foo()?;
|
||||||
|
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
|
||||||
|
Ok(())
|
||||||
|
}
|
18
tests/ui/async-await/try-in-sync.stderr
Normal file
18
tests/ui/async-await/try-in-sync.stderr
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
error[E0277]: the `?` operator can only be applied to values that implement `Try`
|
||||||
|
--> $DIR/try-in-sync.rs:6:5
|
||||||
|
|
|
||||||
|
LL | foo()?;
|
||||||
|
| ^^^^^^ the `?` operator cannot be applied to type `impl Future<Output = Result<(), ()>>`
|
||||||
|
|
|
||||||
|
= help: the trait `Try` is not implemented for `impl Future<Output = Result<(), ()>>`
|
||||||
|
note: this implements `Future` and its output type supports `?`, but the future cannot be awaited in a synchronous function
|
||||||
|
--> $DIR/try-in-sync.rs:6:10
|
||||||
|
|
|
||||||
|
LL | fn main() -> Result<(), ()> {
|
||||||
|
| --------------------------- this is not `async`
|
||||||
|
LL | foo()?;
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0277`.
|
Loading…
Reference in New Issue
Block a user