Skip to content

Commit 7227a87

Browse files
committed
When .await is called on a non-Future expression, suggest removal
Keep track of the origin of a `T: Future` obligation when caused by an `.await` expression. Address #66731.
1 parent 06a6674 commit 7227a87

36 files changed

+190
-121
lines changed

Diff for: compiler/rustc_ast_lowering/src/expr.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
607607
/// }
608608
/// ```
609609
fn lower_expr_await(&mut self, await_span: Span, expr: &Expr) -> hir::ExprKind<'hir> {
610+
let dot_await_span = expr.span.shrink_to_hi().to(await_span);
610611
match self.generator_kind {
611612
Some(hir::GeneratorKind::Async(_)) => {}
612613
Some(hir::GeneratorKind::Gen) | None => {
@@ -623,7 +624,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
623624
err.emit();
624625
}
625626
}
626-
let span = self.mark_span_with_reason(DesugaringKind::Await, await_span, None);
627+
let span = self.mark_span_with_reason(DesugaringKind::Await, dot_await_span, None);
627628
let gen_future_span = self.mark_span_with_reason(
628629
DesugaringKind::Await,
629630
await_span,
@@ -682,7 +683,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
682683
let break_x = self.with_loop_scope(loop_node_id, move |this| {
683684
let expr_break =
684685
hir::ExprKind::Break(this.lower_loop_destination(None), Some(x_expr));
685-
this.arena.alloc(this.expr(await_span, expr_break, ThinVec::new()))
686+
this.arena.alloc(this.expr(span, expr_break, ThinVec::new()))
686687
});
687688
self.arm(ready_pat, break_x)
688689
};

Diff for: compiler/rustc_middle/src/traits/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,8 @@ pub enum ObligationCauseCode<'tcx> {
348348
/// If `X` is the concrete type of an opaque type `impl Y`, then `X` must implement `Y`
349349
OpaqueType,
350350

351+
AwaitableExpr,
352+
351353
/// Well-formed checking. If a `WellFormedLoc` is provided,
352354
/// then it will be used to eprform HIR-based wf checking
353355
/// after an error occurs, in order to generate a more precise error span.

Diff for: compiler/rustc_parse/src/parser/expr.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2838,8 +2838,8 @@ impl<'a> Parser<'a> {
28382838
ExprKind::Call(f, args)
28392839
}
28402840

2841-
fn mk_await_expr(&mut self, self_arg: P<Expr>, lo: Span) -> P<Expr> {
2842-
let span = lo.to(self.prev_token.span);
2841+
fn mk_await_expr(&mut self, self_arg: P<Expr>, _lo: Span) -> P<Expr> {
2842+
let span = self.prev_token.span;
28432843
let await_expr = self.mk_expr(span, ExprKind::Await(self_arg), AttrVec::new());
28442844
self.recover_from_await_method_call();
28452845
await_expr

Diff for: compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
439439
self.suggest_remove_reference(&obligation, &mut err, trait_ref);
440440
self.suggest_semicolon_removal(&obligation, &mut err, span, trait_ref);
441441
self.note_version_mismatch(&mut err, &trait_ref);
442+
self.suggest_remove_await(&obligation, &mut err);
442443

443444
if Some(trait_ref.def_id()) == tcx.lang_items().try_trait() {
444445
self.suggest_await_before_try(&mut err, &obligation, trait_ref, span);

Diff for: compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+26
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ pub trait InferCtxtExt<'tcx> {
8989
trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
9090
);
9191

92+
fn suggest_remove_await(
93+
&self,
94+
obligation: &PredicateObligation<'tcx>,
95+
err: &mut DiagnosticBuilder<'_>,
96+
);
97+
9298
fn suggest_change_mut(
9399
&self,
94100
obligation: &PredicateObligation<'tcx>,
@@ -873,6 +879,25 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
873879
}
874880
}
875881

882+
fn suggest_remove_await(
883+
&self,
884+
obligation: &PredicateObligation<'tcx>,
885+
err: &mut DiagnosticBuilder<'_>,
886+
) {
887+
let span = obligation.cause.span;
888+
889+
if let ObligationCauseCode::AwaitableExpr = obligation.cause.code {
890+
// FIXME: use `trait_ref.self_ty().no_bound_vars()` to typecheck if `()` and if not
891+
// maybe suggest returning instead?
892+
err.span_suggestion_verbose(
893+
span,
894+
"do not `.await` the expression",
895+
String::new(),
896+
Applicability::MachineApplicable,
897+
);
898+
}
899+
}
900+
876901
/// Check if the trait bound is implemented for a different mutability and note it in the
877902
/// final error.
878903
fn suggest_change_mut(
@@ -1935,6 +1960,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
19351960
| ObligationCauseCode::ReturnType
19361961
| ObligationCauseCode::ReturnValue(_)
19371962
| ObligationCauseCode::BlockTailExpression(_)
1963+
| ObligationCauseCode::AwaitableExpr
19381964
| ObligationCauseCode::LetElse => {}
19391965
ObligationCauseCode::SliceOrArrayElem => {
19401966
err.note("slice and array elements must have `Sized` type");

Diff for: compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs

+28-3
Original file line numberDiff line numberDiff line change
@@ -804,7 +804,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
804804
let ty = item_ty.subst(self.tcx, substs);
805805

806806
self.write_resolution(hir_id, Ok((def_kind, def_id)));
807-
self.add_required_obligations(span, def_id, &substs);
807+
self.add_required_obligations_with_code(
808+
span,
809+
def_id,
810+
&substs,
811+
match lang_item {
812+
hir::LangItem::FuturePoll => ObligationCauseCode::AwaitableExpr,
813+
// FIXME: see if there are other obligation specializations we could do here beyond
814+
// what we do above for `.await`.
815+
_ => traits::ItemObligation(def_id),
816+
},
817+
);
808818
(Res::Def(def_kind, def_id), ty)
809819
}
810820

@@ -1486,12 +1496,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14861496
}
14871497

14881498
/// Add all the obligations that are required, substituting and normalized appropriately.
1489-
#[tracing::instrument(level = "debug", skip(self, span, def_id, substs))]
14901499
crate fn add_required_obligations(&self, span: Span, def_id: DefId, substs: &SubstsRef<'tcx>) {
1500+
self.add_required_obligations_with_code(
1501+
span,
1502+
def_id,
1503+
substs,
1504+
traits::ItemObligation(def_id),
1505+
)
1506+
}
1507+
1508+
#[tracing::instrument(level = "debug", skip(self, span, def_id, substs))]
1509+
fn add_required_obligations_with_code(
1510+
&self,
1511+
span: Span,
1512+
def_id: DefId,
1513+
substs: &SubstsRef<'tcx>,
1514+
code: ObligationCauseCode<'tcx>,
1515+
) {
14911516
let (bounds, _) = self.instantiate_bounds(span, def_id, &substs);
14921517

14931518
for obligation in traits::predicates_for_generics(
1494-
traits::ObligationCause::new(span, self.body_id, traits::ItemObligation(def_id)),
1519+
traits::ObligationCause::new(span, self.body_id, code),
14951520
self.param_env,
14961521
bounds,
14971522
) {

Diff for: src/test/ui/async-await/async-error-span.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ LL | let a;
1414
| ^ cannot infer type
1515
|
1616
note: the type is part of the `async fn` body because of this `await`
17-
--> $DIR/async-error-span.rs:14:5
17+
--> $DIR/async-error-span.rs:14:17
1818
|
1919
LL | get_future().await;
20-
| ^^^^^^^^^^^^^^^^^^
20+
| ^^^^^^
2121

2222
error: aborting due to 2 previous errors
2323

Diff for: src/test/ui/async-await/async-fn-nonsend.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ LL | assert_send(local_dropped_before_await());
66
|
77
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
88
note: future is not `Send` as this value is used across an await
9-
--> $DIR/async-fn-nonsend.rs:24:5
9+
--> $DIR/async-fn-nonsend.rs:24:10
1010
|
1111
LL | let x = non_send();
1212
| - has type `impl Debug` which is not `Send`
1313
LL | drop(x);
1414
LL | fut().await;
15-
| ^^^^^^^^^^^ await occurs here, with `x` maybe used later
15+
| ^^^^^^ await occurs here, with `x` maybe used later
1616
LL | }
1717
| - `x` is later dropped here
1818
note: required by a bound in `assert_send`
@@ -29,12 +29,12 @@ LL | assert_send(non_send_temporary_in_match());
2929
|
3030
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Rc<()>`
3131
note: future is not `Send` as this value is used across an await
32-
--> $DIR/async-fn-nonsend.rs:33:20
32+
--> $DIR/async-fn-nonsend.rs:33:25
3333
|
3434
LL | match Some(non_send()) {
3535
| ---------- has type `impl Debug` which is not `Send`
3636
LL | Some(_) => fut().await,
37-
| ^^^^^^^^^^^ await occurs here, with `non_send()` maybe used later
37+
| ^^^^^^ await occurs here, with `non_send()` maybe used later
3838
...
3939
LL | }
4040
| - `non_send()` is later dropped here
@@ -52,13 +52,13 @@ LL | assert_send(non_sync_with_method_call());
5252
|
5353
= help: the trait `Send` is not implemented for `dyn std::fmt::Write`
5454
note: future is not `Send` as this value is used across an await
55-
--> $DIR/async-fn-nonsend.rs:42:9
55+
--> $DIR/async-fn-nonsend.rs:42:14
5656
|
5757
LL | let f: &mut std::fmt::Formatter = panic!();
5858
| - has type `&mut Formatter<'_>` which is not `Send`
5959
LL | if non_sync().fmt(f).unwrap() == () {
6060
LL | fut().await;
61-
| ^^^^^^^^^^^ await occurs here, with `f` maybe used later
61+
| ^^^^^^ await occurs here, with `f` maybe used later
6262
LL | }
6363
LL | }
6464
| - `f` is later dropped here

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

+12-12
Original file line numberDiff line numberDiff line change
@@ -162,52 +162,52 @@ LL | let _ = (await bar())?;
162162
| ^^^^^^^^^^^ only allowed inside `async` functions and blocks
163163

164164
error[E0728]: `await` is only allowed inside `async` functions and blocks
165-
--> $DIR/incorrect-syntax-suggestions.rs:71:13
165+
--> $DIR/incorrect-syntax-suggestions.rs:71:19
166166
|
167167
LL | fn foo13() -> Result<(), ()> {
168168
| ----- this is not `async`
169169
LL | let _ = bar().await();
170-
| ^^^^^^^^^^^ only allowed inside `async` functions and blocks
170+
| ^^^^^ only allowed inside `async` functions and blocks
171171

172172
error[E0728]: `await` is only allowed inside `async` functions and blocks
173-
--> $DIR/incorrect-syntax-suggestions.rs:76:13
173+
--> $DIR/incorrect-syntax-suggestions.rs:76:19
174174
|
175175
LL | fn foo14() -> Result<(), ()> {
176176
| ----- this is not `async`
177177
LL | let _ = bar().await()?;
178-
| ^^^^^^^^^^^ only allowed inside `async` functions and blocks
178+
| ^^^^^ only allowed inside `async` functions and blocks
179179

180180
error[E0728]: `await` is only allowed inside `async` functions and blocks
181-
--> $DIR/incorrect-syntax-suggestions.rs:81:13
181+
--> $DIR/incorrect-syntax-suggestions.rs:81:19
182182
|
183183
LL | fn foo15() -> Result<(), ()> {
184184
| ----- this is not `async`
185185
LL | let _ = bar().await;
186-
| ^^^^^^^^^^^ only allowed inside `async` functions and blocks
186+
| ^^^^^ only allowed inside `async` functions and blocks
187187

188188
error[E0728]: `await` is only allowed inside `async` functions and blocks
189-
--> $DIR/incorrect-syntax-suggestions.rs:85:13
189+
--> $DIR/incorrect-syntax-suggestions.rs:85:19
190190
|
191191
LL | fn foo16() -> Result<(), ()> {
192192
| ----- this is not `async`
193193
LL | let _ = bar().await?;
194-
| ^^^^^^^^^^^ only allowed inside `async` functions and blocks
194+
| ^^^^^ only allowed inside `async` functions and blocks
195195

196196
error[E0728]: `await` is only allowed inside `async` functions and blocks
197-
--> $DIR/incorrect-syntax-suggestions.rs:90:17
197+
--> $DIR/incorrect-syntax-suggestions.rs:90:23
198198
|
199199
LL | fn foo() -> Result<(), ()> {
200200
| --- this is not `async`
201201
LL | let _ = bar().await?;
202-
| ^^^^^^^^^^^ only allowed inside `async` functions and blocks
202+
| ^^^^^ only allowed inside `async` functions and blocks
203203

204204
error[E0728]: `await` is only allowed inside `async` functions and blocks
205-
--> $DIR/incorrect-syntax-suggestions.rs:97:17
205+
--> $DIR/incorrect-syntax-suggestions.rs:97:23
206206
|
207207
LL | let foo = || {
208208
| -- this is not `async`
209209
LL | let _ = bar().await?;
210-
| ^^^^^^^^^^^ only allowed inside `async` functions and blocks
210+
| ^^^^^ only allowed inside `async` functions and blocks
211211

212212
error[E0728]: `await` is only allowed inside `async` functions and blocks
213213
--> $DIR/incorrect-syntax-suggestions.rs:113:17

Diff for: src/test/ui/async-await/issue-64130-1-sync.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ LL | is_sync(bar());
66
|
77
= help: within `impl Future<Output = ()>`, the trait `Sync` is not implemented for `Foo`
88
note: future is not `Sync` as this value is used across an await
9-
--> $DIR/issue-64130-1-sync.rs:15:5
9+
--> $DIR/issue-64130-1-sync.rs:15:10
1010
|
1111
LL | let x = Foo;
1212
| - has type `Foo` which is not `Sync`
1313
LL | baz().await;
14-
| ^^^^^^^^^^^ await occurs here, with `x` maybe used later
14+
| ^^^^^^ await occurs here, with `x` maybe used later
1515
LL | }
1616
| - `x` is later dropped here
1717
note: required by a bound in `is_sync`

Diff for: src/test/ui/async-await/issue-64130-2-send.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ LL | is_send(bar());
66
|
77
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `Foo`
88
note: future is not `Send` as this value is used across an await
9-
--> $DIR/issue-64130-2-send.rs:15:5
9+
--> $DIR/issue-64130-2-send.rs:15:10
1010
|
1111
LL | let x = Foo;
1212
| - has type `Foo` which is not `Send`
1313
LL | baz().await;
14-
| ^^^^^^^^^^^ await occurs here, with `x` maybe used later
14+
| ^^^^^^ await occurs here, with `x` maybe used later
1515
LL | }
1616
| - `x` is later dropped here
1717
note: required by a bound in `is_send`

Diff for: src/test/ui/async-await/issue-64130-3-other.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ LL | is_qux(bar());
88
| ^^^^^ within `impl Future<Output = ()>`, the trait `Qux` is not implemented for `Foo`
99
|
1010
note: future does not implement `Qux` as this value is used across an await
11-
--> $DIR/issue-64130-3-other.rs:18:5
11+
--> $DIR/issue-64130-3-other.rs:18:10
1212
|
1313
LL | let x = Foo;
1414
| - has type `Foo` which does not implement `Qux`
1515
LL | baz().await;
16-
| ^^^^^^^^^^^ await occurs here, with `x` maybe used later
16+
| ^^^^^^ await occurs here, with `x` maybe used later
1717
LL | }
1818
| - `x` is later dropped here
1919
note: required by a bound in `is_qux`

Diff for: src/test/ui/async-await/issue-64130-4-async-move.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ LL | pub fn foo() -> impl Future + Send {
66
|
77
= help: the trait `Sync` is not implemented for `(dyn Any + Send + 'static)`
88
note: future is not `Send` as this value is used across an await
9-
--> $DIR/issue-64130-4-async-move.rs:21:26
9+
--> $DIR/issue-64130-4-async-move.rs:21:31
1010
|
1111
LL | match client.status() {
1212
| ------ has type `&Client` which is not `Send`
1313
LL | 200 => {
1414
LL | let _x = get().await;
15-
| ^^^^^^^^^^^ await occurs here, with `client` maybe used later
15+
| ^^^^^^ await occurs here, with `client` maybe used later
1616
...
1717
LL | }
1818
| - `client` is later dropped here

Diff for: src/test/ui/async-await/issue-64130-non-send-future-diags.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ LL | is_send(foo());
66
|
77
= help: within `impl Future<Output = ()>`, the trait `Send` is not implemented for `MutexGuard<'_, u32>`
88
note: future is not `Send` as this value is used across an await
9-
--> $DIR/issue-64130-non-send-future-diags.rs:17:5
9+
--> $DIR/issue-64130-non-send-future-diags.rs:17:10
1010
|
1111
LL | let g = x.lock().unwrap();
1212
| - has type `MutexGuard<'_, u32>` which is not `Send`
1313
LL | baz().await;
14-
| ^^^^^^^^^^^ await occurs here, with `g` maybe used later
14+
| ^^^^^^ await occurs here, with `g` maybe used later
1515
LL | }
1616
| - `g` is later dropped here
1717
note: required by a bound in `is_send`

Diff for: src/test/ui/async-await/issue-67252-unnamed-future.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ LL | spawn(async {
66
|
77
= help: within `impl Future<Output = [async output]>`, the trait `Send` is not implemented for `*mut ()`
88
note: future is not `Send` as this value is used across an await
9-
--> $DIR/issue-67252-unnamed-future.rs:20:9
9+
--> $DIR/issue-67252-unnamed-future.rs:20:16
1010
|
1111
LL | let _a = std::ptr::null_mut::<()>(); // `*mut ()` is not `Send`
1212
| -- has type `*mut ()` which is not `Send`
1313
LL | AFuture.await;
14-
| ^^^^^^^^^^^^^ await occurs here, with `_a` maybe used later
14+
| ^^^^^^ await occurs here, with `_a` maybe used later
1515
LL | });
1616
| - `_a` is later dropped here
1717
note: required by a bound in `spawn`

Diff for: src/test/ui/async-await/issue-70594.rs

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ async fn fun() {
66
//~| error: `.await` is not allowed in a `const`
77
//~| error: `.await` is not allowed in a `const`
88
//~| error: `()` is not a future
9+
//~| error: `()` is not a future
910
}
1011

1112
fn main() {}

0 commit comments

Comments
 (0)