Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit fb6a1a3

Browse files
authored
Unrolled build for rust-lang#131549
Rollup merge of rust-lang#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. 🤷
2 parents bca5fde + c8b71ef commit fb6a1a3

File tree

7 files changed

+135
-52
lines changed

7 files changed

+135
-52
lines changed

compiler/rustc_hir_typeck/src/expr.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
1010
use rustc_data_structures::unord::UnordMap;
1111
use rustc_errors::codes::*;
1212
use rustc_errors::{
13-
Applicability, Diag, ErrorGuaranteed, StashKey, Subdiagnostic, pluralize, struct_span_code_err,
13+
Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, Subdiagnostic, pluralize,
14+
struct_span_code_err,
1415
};
1516
use rustc_hir::def::{CtorKind, DefKind, Res};
1617
use rustc_hir::def_id::DefId;
@@ -2763,12 +2764,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
27632764
field_ident.span,
27642765
"field not available in `impl Future`, but it is available in its `Output`",
27652766
);
2766-
err.span_suggestion_verbose(
2767-
base.span.shrink_to_hi(),
2768-
"consider `await`ing on the `Future` and access the field of its `Output`",
2769-
".await",
2770-
Applicability::MaybeIncorrect,
2771-
);
2767+
match self.tcx.coroutine_kind(self.body_id) {
2768+
Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => {
2769+
err.span_suggestion_verbose(
2770+
base.span.shrink_to_hi(),
2771+
"consider `await`ing on the `Future` to access the field",
2772+
".await",
2773+
Applicability::MaybeIncorrect,
2774+
);
2775+
}
2776+
_ => {
2777+
let mut span: MultiSpan = base.span.into();
2778+
span.push_span_label(self.tcx.def_span(self.body_id), "this is not `async`");
2779+
err.span_note(
2780+
span,
2781+
"this implements `Future` and its output type has the field, \
2782+
but the future cannot be awaited in a synchronous function",
2783+
);
2784+
}
2785+
}
27722786
}
27732787

27742788
fn ban_nonexisting_field(

compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3594,52 +3594,64 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
35943594
trait_pred: ty::PolyTraitPredicate<'tcx>,
35953595
span: Span,
35963596
) {
3597-
if let Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) =
3598-
self.tcx.coroutine_kind(obligation.cause.body_id)
3599-
{
3600-
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
3597+
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
36013598

3602-
let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
3603-
let impls_future = self.type_implements_trait(
3604-
future_trait,
3605-
[self.tcx.instantiate_bound_regions_with_erased(self_ty)],
3606-
obligation.param_env,
3607-
);
3608-
if !impls_future.must_apply_modulo_regions() {
3609-
return;
3610-
}
3599+
let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
3600+
let impls_future = self.type_implements_trait(
3601+
future_trait,
3602+
[self.tcx.instantiate_bound_regions_with_erased(self_ty)],
3603+
obligation.param_env,
3604+
);
3605+
if !impls_future.must_apply_modulo_regions() {
3606+
return;
3607+
}
36113608

3612-
let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
3613-
// `<T as Future>::Output`
3614-
let projection_ty = trait_pred.map_bound(|trait_pred| {
3615-
Ty::new_projection(
3616-
self.tcx,
3617-
item_def_id,
3618-
// Future::Output has no args
3619-
[trait_pred.self_ty()],
3620-
)
3621-
});
3622-
let InferOk { value: projection_ty, .. } =
3623-
self.at(&obligation.cause, obligation.param_env).normalize(projection_ty);
3609+
let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
3610+
// `<T as Future>::Output`
3611+
let projection_ty = trait_pred.map_bound(|trait_pred| {
3612+
Ty::new_projection(
3613+
self.tcx,
3614+
item_def_id,
3615+
// Future::Output has no args
3616+
[trait_pred.self_ty()],
3617+
)
3618+
});
3619+
let InferOk { value: projection_ty, .. } =
3620+
self.at(&obligation.cause, obligation.param_env).normalize(projection_ty);
36243621

3625-
debug!(
3626-
normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty)
3627-
);
3628-
let try_obligation = self.mk_trait_obligation_with_new_self_ty(
3629-
obligation.param_env,
3630-
trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
3631-
);
3632-
debug!(try_trait_obligation = ?try_obligation);
3633-
if self.predicate_may_hold(&try_obligation)
3634-
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
3635-
&& snippet.ends_with('?')
3636-
{
3637-
err.span_suggestion_verbose(
3638-
span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(),
3639-
"consider `await`ing on the `Future`",
3640-
".await",
3641-
Applicability::MaybeIncorrect,
3642-
);
3622+
debug!(
3623+
normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty)
3624+
);
3625+
let try_obligation = self.mk_trait_obligation_with_new_self_ty(
3626+
obligation.param_env,
3627+
trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
3628+
);
3629+
debug!(try_trait_obligation = ?try_obligation);
3630+
if self.predicate_may_hold(&try_obligation)
3631+
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
3632+
&& snippet.ends_with('?')
3633+
{
3634+
match self.tcx.coroutine_kind(obligation.cause.body_id) {
3635+
Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => {
3636+
err.span_suggestion_verbose(
3637+
span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(),
3638+
"consider `await`ing on the `Future`",
3639+
".await",
3640+
Applicability::MaybeIncorrect,
3641+
);
3642+
}
3643+
_ => {
3644+
let mut span: MultiSpan = span.with_lo(span.hi() - BytePos(1)).into();
3645+
span.push_span_label(
3646+
self.tcx.def_span(obligation.cause.body_id),
3647+
"this is not `async`",
3648+
);
3649+
err.span_note(
3650+
span,
3651+
"this implements `Future` and its output type supports \
3652+
`?`, but the future cannot be awaited in a synchronous function",
3653+
);
3654+
}
36433655
}
36443656
}
36453657
}

tests/ui/async-await/field-in-sync.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//@ edition: 2021
2+
3+
struct S {
4+
field: (),
5+
}
6+
7+
async fn foo() -> S { todo!() }
8+
9+
fn main() -> Result<(), ()> {
10+
foo().field;
11+
//~^ ERROR no field `field` on type `impl Future<Output = S>`
12+
Ok(())
13+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0609]: no field `field` on type `impl Future<Output = S>`
2+
--> $DIR/field-in-sync.rs:10:11
3+
|
4+
LL | foo().field;
5+
| ^^^^^ field not available in `impl Future`, but it is available in its `Output`
6+
|
7+
note: this implements `Future` and its output type has the field, but the future cannot be awaited in a synchronous function
8+
--> $DIR/field-in-sync.rs:10:5
9+
|
10+
LL | fn main() -> Result<(), ()> {
11+
| --------------------------- this is not `async`
12+
LL | foo().field;
13+
| ^^^^^
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0609`.

tests/ui/async-await/issue-61076.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ error[E0609]: no field `0` on type `impl Future<Output = Tuple>`
2828
LL | let _: i32 = tuple().0;
2929
| ^ field not available in `impl Future`, but it is available in its `Output`
3030
|
31-
help: consider `await`ing on the `Future` and access the field of its `Output`
31+
help: consider `await`ing on the `Future` to access the field
3232
|
3333
LL | let _: i32 = tuple().await.0;
3434
| ++++++
@@ -39,7 +39,7 @@ error[E0609]: no field `a` on type `impl Future<Output = Struct>`
3939
LL | let _: i32 = struct_().a;
4040
| ^ field not available in `impl Future`, but it is available in its `Output`
4141
|
42-
help: consider `await`ing on the `Future` and access the field of its `Output`
42+
help: consider `await`ing on the `Future` to access the field
4343
|
4444
LL | let _: i32 = struct_().await.a;
4545
| ++++++

tests/ui/async-await/try-in-sync.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//@ edition: 2021
2+
3+
async fn foo() -> Result<(), ()> { todo!() }
4+
5+
fn main() -> Result<(), ()> {
6+
foo()?;
7+
//~^ ERROR the `?` operator can only be applied to values that implement `Try`
8+
Ok(())
9+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error[E0277]: the `?` operator can only be applied to values that implement `Try`
2+
--> $DIR/try-in-sync.rs:6:5
3+
|
4+
LL | foo()?;
5+
| ^^^^^^ the `?` operator cannot be applied to type `impl Future<Output = Result<(), ()>>`
6+
|
7+
= help: the trait `Try` is not implemented for `impl Future<Output = Result<(), ()>>`
8+
note: this implements `Future` and its output type supports `?`, but the future cannot be awaited in a synchronous function
9+
--> $DIR/try-in-sync.rs:6:10
10+
|
11+
LL | fn main() -> Result<(), ()> {
12+
| --------------------------- this is not `async`
13+
LL | foo()?;
14+
| ^
15+
16+
error: aborting due to 1 previous error
17+
18+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)