Skip to content

Commit badcf26

Browse files
authored
Rollup merge of #71948 - csmoe:issue-61076, r=oli-obk
Suggest to await future before ? operator Closes #71811 cc #61076
2 parents 154db50 + 10d7da4 commit badcf26

File tree

13 files changed

+246
-37
lines changed

13 files changed

+246
-37
lines changed

src/librustc_middle/hir/map/mod.rs

+1-5
Original file line numberDiff line numberDiff line change
@@ -390,11 +390,7 @@ impl<'hir> Map<'hir> {
390390
/// Given a `HirId`, returns the `BodyId` associated with it,
391391
/// if the node is a body owner, otherwise returns `None`.
392392
pub fn maybe_body_owned_by(&self, hir_id: HirId) -> Option<BodyId> {
393-
if let Some(node) = self.find(hir_id) {
394-
associated_body(node)
395-
} else {
396-
bug!("no entry for id `{}`", hir_id)
397-
}
393+
self.find(hir_id).map(associated_body).flatten()
398394
}
399395

400396
/// Given a body owner's id, returns the `BodyId` associated with it.

src/librustc_middle/query/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,12 @@ rustc_queries! {
11641164
desc { "evaluating trait selection obligation `{}`", goal.value }
11651165
}
11661166

1167+
query type_implements_trait(
1168+
key: (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>, )
1169+
) -> bool {
1170+
desc { "evaluating `type_implements_trait` `{:?}`", key }
1171+
}
1172+
11671173
/// Do not call this query directly: part of the `Eq` type-op
11681174
query type_op_ascribe_user_type(
11691175
goal: CanonicalTypeOpAscribeUserTypeGoal<'tcx>

src/librustc_middle/ty/query/keys.rs

+12
Original file line numberDiff line numberDiff line change
@@ -295,3 +295,15 @@ impl Key for (Symbol, u32, u32) {
295295
DUMMY_SP
296296
}
297297
}
298+
299+
impl<'tcx> Key for (DefId, Ty<'tcx>, SubstsRef<'tcx>, ty::ParamEnv<'tcx>) {
300+
type CacheSelector = DefaultCacheSelector;
301+
302+
fn query_crate(&self) -> CrateNum {
303+
LOCAL_CRATE
304+
}
305+
306+
fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
307+
DUMMY_SP
308+
}
309+
}

src/librustc_trait_selection/traits/error_reporting/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
402402
self.suggest_remove_reference(&obligation, &mut err, &trait_ref);
403403
self.suggest_semicolon_removal(&obligation, &mut err, span, &trait_ref);
404404
self.note_version_mismatch(&mut err, &trait_ref);
405+
self.suggest_await_before_try(&mut err, &obligation, &trait_ref, span);
405406
if self.suggest_impl_trait(&mut err, span, &obligation, &trait_ref) {
406407
err.emit();
407408
return;

src/librustc_trait_selection/traits/error_reporting/suggestions.rs

+101
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
use super::{
22
EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
3+
SelectionContext,
34
};
45

56
use crate::infer::InferCtxt;
7+
use crate::traits::normalize_projection_type;
68

79
use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder, Style};
810
use rustc_hir as hir;
911
use rustc_hir::def::DefKind;
1012
use rustc_hir::def_id::DefId;
1113
use rustc_hir::intravisit::Visitor;
14+
use rustc_hir::lang_items;
1215
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
1316
use rustc_middle::ty::TypeckTables;
1417
use rustc_middle::ty::{
@@ -150,6 +153,15 @@ pub trait InferCtxtExt<'tcx> {
150153
T: fmt::Display;
151154

152155
fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>);
156+
157+
/// Suggest to await before try: future? => future.await?
158+
fn suggest_await_before_try(
159+
&self,
160+
err: &mut DiagnosticBuilder<'_>,
161+
obligation: &PredicateObligation<'tcx>,
162+
trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
163+
span: Span,
164+
);
153165
}
154166

155167
fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
@@ -1822,6 +1834,95 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
18221834
suggested_limit, self.tcx.crate_name,
18231835
));
18241836
}
1837+
1838+
fn suggest_await_before_try(
1839+
&self,
1840+
err: &mut DiagnosticBuilder<'_>,
1841+
obligation: &PredicateObligation<'tcx>,
1842+
trait_ref: &ty::Binder<ty::TraitRef<'tcx>>,
1843+
span: Span,
1844+
) {
1845+
debug!(
1846+
"suggest_await_befor_try: obligation={:?}, span={:?}, trait_ref={:?}, trait_ref_self_ty={:?}",
1847+
obligation,
1848+
span,
1849+
trait_ref,
1850+
trait_ref.self_ty()
1851+
);
1852+
let body_hir_id = obligation.cause.body_id;
1853+
let item_id = self.tcx.hir().get_parent_node(body_hir_id);
1854+
1855+
if let Some(body_id) = self.tcx.hir().maybe_body_owned_by(item_id) {
1856+
let body = self.tcx.hir().body(body_id);
1857+
if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
1858+
let future_trait =
1859+
self.tcx.require_lang_item(lang_items::FutureTraitLangItem, None);
1860+
1861+
let self_ty = self.resolve_vars_if_possible(&trait_ref.self_ty());
1862+
1863+
let impls_future = self.tcx.type_implements_trait((
1864+
future_trait,
1865+
self_ty,
1866+
ty::List::empty(),
1867+
obligation.param_env,
1868+
));
1869+
1870+
let item_def_id = self
1871+
.tcx
1872+
.associated_items(future_trait)
1873+
.in_definition_order()
1874+
.next()
1875+
.unwrap()
1876+
.def_id;
1877+
// `<T as Future>::Output`
1878+
let projection_ty = ty::ProjectionTy {
1879+
// `T`
1880+
substs: self.tcx.mk_substs_trait(
1881+
trait_ref.self_ty(),
1882+
self.fresh_substs_for_item(span, item_def_id),
1883+
),
1884+
// `Future::Output`
1885+
item_def_id,
1886+
};
1887+
1888+
let mut selcx = SelectionContext::new(self);
1889+
1890+
let mut obligations = vec![];
1891+
let normalized_ty = normalize_projection_type(
1892+
&mut selcx,
1893+
obligation.param_env,
1894+
projection_ty,
1895+
obligation.cause.clone(),
1896+
0,
1897+
&mut obligations,
1898+
);
1899+
1900+
debug!(
1901+
"suggest_await_befor_try: normalized_projection_type {:?}",
1902+
self.resolve_vars_if_possible(&normalized_ty)
1903+
);
1904+
let try_obligation = self.mk_obligation_for_def_id(
1905+
trait_ref.def_id(),
1906+
normalized_ty,
1907+
obligation.cause.clone(),
1908+
obligation.param_env,
1909+
);
1910+
debug!("suggest_await_befor_try: try_trait_obligation {:?}", try_obligation);
1911+
if self.predicate_may_hold(&try_obligation) && impls_future {
1912+
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
1913+
if snippet.ends_with('?') {
1914+
err.span_suggestion(
1915+
span,
1916+
"consider using `.await` here",
1917+
format!("{}.await?", snippet.trim_end_matches('?')),
1918+
Applicability::MaybeIncorrect,
1919+
);
1920+
}
1921+
}
1922+
}
1923+
}
1924+
}
1925+
}
18251926
}
18261927

18271928
/// Collect all the returned expressions within the input expression.

src/librustc_trait_selection/traits/mod.rs

+41-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@ use rustc_hir::def_id::DefId;
3131
use rustc_middle::middle::region;
3232
use rustc_middle::ty::fold::TypeFoldable;
3333
use rustc_middle::ty::subst::{InternalSubsts, SubstsRef};
34-
use rustc_middle::ty::{self, GenericParamDefKind, ToPredicate, Ty, TyCtxt, WithConstness};
34+
use rustc_middle::ty::{
35+
self, GenericParamDefKind, ParamEnv, ToPredicate, Ty, TyCtxt, WithConstness,
36+
};
3537
use rustc_span::Span;
3638

3739
use std::fmt::Debug;
@@ -523,6 +525,43 @@ fn vtable_methods<'tcx>(
523525
}))
524526
}
525527

528+
/// Check whether a `ty` implements given trait(trait_def_id).
529+
///
530+
/// NOTE: Always return `false` for a type which needs inference.
531+
fn type_implements_trait<'tcx>(
532+
tcx: TyCtxt<'tcx>,
533+
key: (
534+
DefId, // trait_def_id,
535+
Ty<'tcx>, // type
536+
SubstsRef<'tcx>,
537+
ParamEnv<'tcx>,
538+
),
539+
) -> bool {
540+
let (trait_def_id, ty, params, param_env) = key;
541+
542+
debug!(
543+
"type_implements_trait: trait_def_id={:?}, type={:?}, params={:?}, param_env={:?}",
544+
trait_def_id, ty, params, param_env
545+
);
546+
547+
// Do not check on infer_types to avoid panic in evaluate_obligation.
548+
if ty.has_infer_types() {
549+
return false;
550+
}
551+
552+
let ty = tcx.erase_regions(&ty);
553+
554+
let trait_ref = ty::TraitRef { def_id: trait_def_id, substs: tcx.mk_substs_trait(ty, params) };
555+
556+
let obligation = Obligation {
557+
cause: ObligationCause::dummy(),
558+
param_env,
559+
recursion_depth: 0,
560+
predicate: trait_ref.without_const().to_predicate(),
561+
};
562+
tcx.infer_ctxt().enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
563+
}
564+
526565
pub fn provide(providers: &mut ty::query::Providers<'_>) {
527566
object_safety::provide(providers);
528567
*providers = ty::query::Providers {
@@ -531,6 +570,7 @@ pub fn provide(providers: &mut ty::query::Providers<'_>) {
531570
codegen_fulfill_obligation: codegen::codegen_fulfill_obligation,
532571
vtable_methods,
533572
substitute_normalize_and_test_predicates,
573+
type_implements_trait,
534574
..*providers
535575
};
536576
}

src/librustc_typeck/check/mod.rs

+14-10
Original file line numberDiff line numberDiff line change
@@ -5282,6 +5282,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
52825282
expected: Ty<'tcx>,
52835283
found: Ty<'tcx>,
52845284
) {
5285+
debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found);
52855286
// `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the
52865287
// body isn't `async`.
52875288
let item_id = self.tcx().hir().get_parent_node(self.body_id);
@@ -5299,22 +5300,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
52995300
.next()
53005301
.unwrap()
53015302
.def_id;
5303+
// `<T as Future>::Output`
5304+
let projection_ty = ty::ProjectionTy {
5305+
// `T`
5306+
substs: self
5307+
.tcx
5308+
.mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)),
5309+
// `Future::Output`
5310+
item_def_id,
5311+
};
5312+
53025313
let predicate =
53035314
ty::Predicate::Projection(ty::Binder::bind(ty::ProjectionPredicate {
5304-
// `<T as Future>::Output`
5305-
projection_ty: ty::ProjectionTy {
5306-
// `T`
5307-
substs: self.tcx.mk_substs_trait(
5308-
found,
5309-
self.fresh_substs_for_item(sp, item_def_id),
5310-
),
5311-
// `Future::Output`
5312-
item_def_id,
5313-
},
5315+
projection_ty,
53145316
ty: expected,
53155317
}));
53165318
let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
5319+
53175320
debug!("suggest_missing_await: trying obligation {:?}", obligation);
5321+
53185322
if self.infcx.predicate_may_hold(&obligation) {
53195323
debug!("suggest_missing_await: obligation held: {:?}", obligation);
53205324
if let Ok(code) = self.sess().source_map().span_to_snippet(sp) {

src/test/ui/async-await/await-keyword/incorrect-syntax-suggestions.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,10 @@ error[E0277]: the `?` operator can only be applied to values that implement `std
237237
--> $DIR/incorrect-syntax-suggestions.rs:16:19
238238
|
239239
LL | let _ = await bar()?;
240-
| ^^^^^^ the `?` operator cannot be applied to type `impl std::future::Future`
240+
| ^^^^^^
241+
| |
242+
| the `?` operator cannot be applied to type `impl std::future::Future`
243+
| help: consider using `.await` here: `bar().await?`
241244
|
242245
= help: the trait `std::ops::Try` is not implemented for `impl std::future::Future`
243246
= note: required by `std::ops::Try::into_result`
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// edition:2018
2+
3+
use core::future::Future;
4+
use core::pin::Pin;
5+
use core::task::{Context, Poll};
6+
7+
struct T;
8+
9+
impl Future for T {
10+
type Output = Result<(), ()>;
11+
12+
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
13+
Poll::Pending
14+
}
15+
}
16+
17+
async fn foo() -> Result<(), ()> {
18+
Ok(())
19+
}
20+
21+
async fn bar() -> Result<(), ()> {
22+
foo()?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
23+
Ok(())
24+
}
25+
26+
async fn baz() -> Result<(), ()> {
27+
let t = T;
28+
t?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
29+
Ok(())
30+
}
31+
32+
fn main() {}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
2+
--> $DIR/issue-61076.rs:22:5
3+
|
4+
LL | foo()?;
5+
| ^^^^^^
6+
| |
7+
| the `?` operator cannot be applied to type `impl std::future::Future`
8+
| help: consider using `.await` here: `foo().await?`
9+
|
10+
= help: the trait `std::ops::Try` is not implemented for `impl std::future::Future`
11+
= note: required by `std::ops::Try::into_result`
12+
13+
error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try`
14+
--> $DIR/issue-61076.rs:28:5
15+
|
16+
LL | t?;
17+
| ^^
18+
| |
19+
| the `?` operator cannot be applied to type `T`
20+
| help: consider using `.await` here: `t.await?`
21+
|
22+
= help: the trait `std::ops::Try` is not implemented for `T`
23+
= note: required by `std::ops::Try::into_result`
24+
25+
error: aborting due to 2 previous errors
26+
27+
For more information about this error, try `rustc --explain E0277`.

src/test/ui/async-await/try-on-option-in-async.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ async fn an_async_block() -> u32 {
77
let x: Option<u32> = None;
88
x?; //~ ERROR the `?` operator
99
22
10-
}.await
10+
}
11+
.await
1112
}
1213

1314
async fn async_closure_containing_fn() -> u32 {

src/test/ui/async-await/try-on-option-in-async.stderr

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ LL | | let x: Option<u32> = None;
77
LL | | x?;
88
| | ^^ cannot use the `?` operator in an async block that returns `{integer}`
99
LL | | 22
10-
LL | | }.await
10+
LL | | }
1111
| |_____- this function should return `Result` or `Option` to accept `?`
1212
|
1313
= help: the trait `std::ops::Try` is not implemented for `{integer}`
1414
= note: required by `std::ops::Try::from_error`
1515

1616
error[E0277]: the `?` operator can only be used in an async closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
17-
--> $DIR/try-on-option-in-async.rs:16:9
17+
--> $DIR/try-on-option-in-async.rs:17:9
1818
|
1919
LL | let async_closure = async || {
2020
| __________________________________-
@@ -29,7 +29,7 @@ LL | | };
2929
= note: required by `std::ops::Try::from_error`
3030

3131
error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
32-
--> $DIR/try-on-option-in-async.rs:25:5
32+
--> $DIR/try-on-option-in-async.rs:26:5
3333
|
3434
LL | async fn an_async_function() -> u32 {
3535
| _____________________________________-

0 commit comments

Comments
 (0)