Skip to content

Commit f7cbb7a

Browse files
committed
Auto merge of #72784 - csmoe:issue-61076, r=estebank
Await on mismatched future types Closes #61076 This PR suggests to `await` on: 1. `async_fn().bar() => async_fn().await.bar()` 2. `async_fn().field => async_fn().await.field` 3. ` if let x = async() {} => if let x = async().await {}` r? @tmandry @estebank
2 parents 4e701af + 7cfcefd commit f7cbb7a

File tree

8 files changed

+299
-7
lines changed

8 files changed

+299
-7
lines changed

src/librustc_infer/infer/error_reporting/mod.rs

+60
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ use super::region_constraints::GenericKind;
5050
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
5151

5252
use crate::infer;
53+
use crate::infer::OriginalQueryValues;
5354
use crate::traits::error_reporting::report_object_safety_error;
5455
use crate::traits::{
5556
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
@@ -60,8 +61,10 @@ use rustc_errors::{pluralize, struct_span_err};
6061
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
6162
use rustc_hir as hir;
6263
use rustc_hir::def_id::DefId;
64+
use rustc_hir::lang_items::LangItem;
6365
use rustc_hir::{Item, ItemKind, Node};
6466
use rustc_middle::ty::error::TypeError;
67+
use rustc_middle::ty::ParamEnvAnd;
6568
use rustc_middle::ty::{
6669
self,
6770
subst::{Subst, SubstsRef},
@@ -1529,6 +1532,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
15291532
};
15301533
if let Some(exp_found) = exp_found {
15311534
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
1535+
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
15321536
}
15331537

15341538
// In some (most?) cases cause.body_id points to actual body, but in some cases
@@ -1547,6 +1551,62 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
15471551
self.note_error_origin(diag, cause, exp_found);
15481552
}
15491553

1554+
fn suggest_await_on_expect_found(
1555+
&self,
1556+
cause: &ObligationCause<'tcx>,
1557+
exp_span: Span,
1558+
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
1559+
diag: &mut DiagnosticBuilder<'tcx>,
1560+
) {
1561+
debug!(
1562+
"suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
1563+
exp_span, exp_found.expected, exp_found.found
1564+
);
1565+
1566+
if let ty::Opaque(def_id, _) = exp_found.expected.kind {
1567+
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
1568+
// Future::Output
1569+
let item_def_id = self
1570+
.tcx
1571+
.associated_items(future_trait)
1572+
.in_definition_order()
1573+
.next()
1574+
.unwrap()
1575+
.def_id;
1576+
1577+
let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
1578+
if let Some(projection_ty) = projection_ty {
1579+
let projection_query = self.canonicalize_query(
1580+
&ParamEnvAnd { param_env: self.tcx.param_env(def_id), value: projection_ty },
1581+
&mut OriginalQueryValues::default(),
1582+
);
1583+
if let Ok(resp) = self.tcx.normalize_projection_ty(projection_query) {
1584+
let normalized_ty = resp.value.value.normalized_ty;
1585+
debug!("suggest_await_on_expect_found: normalized={:?}", normalized_ty);
1586+
if ty::TyS::same_type(normalized_ty, exp_found.found) {
1587+
let span = if let ObligationCauseCode::Pattern {
1588+
span,
1589+
origin_expr: _,
1590+
root_ty: _,
1591+
} = cause.code
1592+
{
1593+
// scrutinee's span
1594+
span.unwrap_or(exp_span)
1595+
} else {
1596+
exp_span
1597+
};
1598+
diag.span_suggestion_verbose(
1599+
span.shrink_to_hi(),
1600+
"consider awaiting on the future",
1601+
".await".to_string(),
1602+
Applicability::MaybeIncorrect,
1603+
);
1604+
}
1605+
}
1606+
}
1607+
}
1608+
}
1609+
15501610
/// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
15511611
/// suggests it.
15521612
fn suggest_as_ref_where_appropriate(

src/librustc_middle/query/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,10 @@ rustc_queries! {
173173
desc { |tcx| "finding projection predicates for `{}`", tcx.def_path_str(key) }
174174
}
175175

176+
query projection_ty_from_predicates(key: (DefId, DefId)) -> Option<ty::ProjectionTy<'tcx>> {
177+
desc { |tcx| "finding projection type inside predicates of `{}`", tcx.def_path_str(key.0) }
178+
}
179+
176180
query native_libraries(_: CrateNum) -> Lrc<Vec<NativeLib>> {
177181
desc { "looking up the native libraries of a linked crate" }
178182
}

src/librustc_typeck/check/_match.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2828
};
2929

3030
// Type check the descriminant and get its type.
31-
let scrut_ty = if force_scrutinee_bool {
31+
let scrutinee_ty = if force_scrutinee_bool {
3232
// Here we want to ensure:
3333
//
3434
// 1. That default match bindings are *not* accepted in the condition of an
@@ -55,7 +55,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
5555

5656
// #55810: Type check patterns first so we get types for all bindings.
5757
for arm in arms {
58-
self.check_pat_top(&arm.pat, scrut_ty, Some(scrut.span), true);
58+
self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut.span), true);
5959
}
6060

6161
// Now typecheck the blocks.

src/librustc_typeck/check/expr.rs

+56-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use rustc_middle::ty::{AdtKind, Visibility};
3737
use rustc_span::hygiene::DesugaringKind;
3838
use rustc_span::source_map::Span;
3939
use rustc_span::symbol::{kw, sym, Ident, Symbol};
40-
use rustc_trait_selection::traits::{self, ObligationCauseCode};
40+
use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
4141

4242
use std::fmt::Display;
4343

@@ -1509,13 +1509,65 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15091509
self.tcx().ty_error()
15101510
}
15111511

1512+
fn suggest_await_on_field_access(
1513+
&self,
1514+
err: &mut DiagnosticBuilder<'_>,
1515+
field_ident: Ident,
1516+
base: &'tcx hir::Expr<'tcx>,
1517+
expr: &'tcx hir::Expr<'tcx>,
1518+
def_id: DefId,
1519+
) {
1520+
let param_env = self.tcx().param_env(def_id);
1521+
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
1522+
// Future::Output
1523+
let item_def_id =
1524+
self.tcx.associated_items(future_trait).in_definition_order().next().unwrap().def_id;
1525+
1526+
let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
1527+
debug!("suggest_await_on_field_access: projection_ty={:?}", projection_ty);
1528+
1529+
let cause = self.misc(expr.span);
1530+
let mut selcx = SelectionContext::new(&self.infcx);
1531+
1532+
let mut obligations = vec![];
1533+
if let Some(projection_ty) = projection_ty {
1534+
let normalized_ty = rustc_trait_selection::traits::normalize_projection_type(
1535+
&mut selcx,
1536+
param_env,
1537+
projection_ty,
1538+
cause,
1539+
0,
1540+
&mut obligations,
1541+
);
1542+
debug!(
1543+
"suggest_await_on_field_access: normalized_ty={:?}, ty_kind={:?}",
1544+
self.resolve_vars_if_possible(&normalized_ty),
1545+
normalized_ty.kind,
1546+
);
1547+
if let ty::Adt(def, _) = normalized_ty.kind {
1548+
if def.non_enum_variant().fields.iter().any(|field| field.ident == field_ident) {
1549+
err.span_suggestion_verbose(
1550+
base.span.shrink_to_hi(),
1551+
"consider awaiting before field access",
1552+
".await".to_string(),
1553+
Applicability::MaybeIncorrect,
1554+
);
1555+
}
1556+
}
1557+
}
1558+
}
1559+
15121560
fn ban_nonexisting_field(
15131561
&self,
15141562
field: Ident,
15151563
base: &'tcx hir::Expr<'tcx>,
15161564
expr: &'tcx hir::Expr<'tcx>,
15171565
expr_t: Ty<'tcx>,
15181566
) {
1567+
debug!(
1568+
"ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}",
1569+
field, base, expr, expr_t
1570+
);
15191571
let mut err = self.no_such_field_err(field.span, field, expr_t);
15201572

15211573
match expr_t.peel_refs().kind {
@@ -1531,6 +1583,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15311583
ty::Param(param_ty) => {
15321584
self.point_at_param_definition(&mut err, param_ty);
15331585
}
1586+
ty::Opaque(def_id, _) => {
1587+
self.suggest_await_on_field_access(&mut err, field, base, expr, def_id);
1588+
}
15341589
_ => {}
15351590
}
15361591

src/librustc_typeck/check/method/suggest.rs

+59
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use rustc_span::symbol::{kw, sym, Ident};
2121
use rustc_span::{source_map, FileName, Span};
2222
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
2323
use rustc_trait_selection::traits::Obligation;
24+
use rustc_trait_selection::traits::SelectionContext;
2425

2526
use std::cmp::Ordering;
2627

@@ -392,6 +393,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
392393
actual.prefix_string(),
393394
ty_str,
394395
);
396+
if let Mode::MethodCall = mode {
397+
if let SelfSource::MethodCall(call) = source {
398+
self.suggest_await_before_method(
399+
&mut err, item_name, actual, call, span,
400+
);
401+
}
402+
}
395403
if let Some(span) =
396404
tcx.sess.confused_type_with_std_module.borrow().get(&span)
397405
{
@@ -854,6 +862,57 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
854862
}
855863
}
856864

865+
fn suggest_await_before_method(
866+
&self,
867+
err: &mut DiagnosticBuilder<'_>,
868+
item_name: Ident,
869+
ty: Ty<'tcx>,
870+
call: &hir::Expr<'_>,
871+
span: Span,
872+
) {
873+
if let ty::Opaque(def_id, _) = ty.kind {
874+
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
875+
// Future::Output
876+
let item_def_id = self
877+
.tcx
878+
.associated_items(future_trait)
879+
.in_definition_order()
880+
.next()
881+
.unwrap()
882+
.def_id;
883+
884+
let projection_ty = self.tcx.projection_ty_from_predicates((def_id, item_def_id));
885+
let cause = self.misc(span);
886+
let mut selcx = SelectionContext::new(&self.infcx);
887+
let mut obligations = vec![];
888+
if let Some(projection_ty) = projection_ty {
889+
let normalized_ty = rustc_trait_selection::traits::normalize_projection_type(
890+
&mut selcx,
891+
self.param_env,
892+
projection_ty,
893+
cause,
894+
0,
895+
&mut obligations,
896+
);
897+
debug!(
898+
"suggest_await_before_method: normalized_ty={:?}, ty_kind={:?}",
899+
self.resolve_vars_if_possible(&normalized_ty),
900+
normalized_ty.kind,
901+
);
902+
let method_exists = self.method_exists(item_name, normalized_ty, call.hir_id, true);
903+
debug!("suggest_await_before_method: is_method_exist={}", method_exists);
904+
if method_exists {
905+
err.span_suggestion_verbose(
906+
span.shrink_to_lo(),
907+
"consider awaiting before this method call",
908+
"await.".to_string(),
909+
Applicability::MaybeIncorrect,
910+
);
911+
}
912+
}
913+
}
914+
}
915+
857916
fn suggest_use_candidates(
858917
&self,
859918
err: &mut DiagnosticBuilder<'_>,

src/librustc_typeck/collect.rs

+23
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub fn provide(providers: &mut Providers) {
7070
generics_of,
7171
predicates_of,
7272
predicates_defined_on,
73+
projection_ty_from_predicates,
7374
explicit_predicates_of,
7475
super_predicates_of,
7576
type_param_predicates,
@@ -2051,6 +2052,28 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
20512052
result
20522053
}
20532054

2055+
fn projection_ty_from_predicates(
2056+
tcx: TyCtxt<'tcx>,
2057+
key: (
2058+
// ty_def_id
2059+
DefId,
2060+
// def_id of `N` in `<T as Trait>::N`
2061+
DefId,
2062+
),
2063+
) -> Option<ty::ProjectionTy<'tcx>> {
2064+
let (ty_def_id, item_def_id) = key;
2065+
let mut projection_ty = None;
2066+
for (predicate, _) in tcx.predicates_of(ty_def_id).predicates {
2067+
if let ty::PredicateAtom::Projection(projection_predicate) = predicate.skip_binders() {
2068+
if item_def_id == projection_predicate.projection_ty.item_def_id {
2069+
projection_ty = Some(projection_predicate.projection_ty);
2070+
break;
2071+
}
2072+
}
2073+
}
2074+
projection_ty
2075+
}
2076+
20542077
fn trait_associated_item_predicates(
20552078
tcx: TyCtxt<'tcx>,
20562079
def_id: DefId,

src/test/ui/async-await/issue-61076.rs

+41
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,26 @@ use core::task::{Context, Poll};
66

77
struct T;
88

9+
struct Tuple(i32);
10+
11+
struct Struct {
12+
a: i32
13+
}
14+
15+
impl Struct {
16+
fn method(&self) {}
17+
}
18+
19+
impl Future for Struct {
20+
type Output = Struct;
21+
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { Poll::Pending }
22+
}
23+
24+
impl Future for Tuple {
25+
type Output = Tuple;
26+
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { Poll::Pending }
27+
}
28+
929
impl Future for T {
1030
type Output = Result<(), ()>;
1131

@@ -23,10 +43,31 @@ async fn bar() -> Result<(), ()> {
2343
Ok(())
2444
}
2545

46+
async fn struct_() -> Struct {
47+
Struct { a: 1 }
48+
}
49+
50+
async fn tuple() -> Tuple {
51+
Tuple(1i32)
52+
}
53+
2654
async fn baz() -> Result<(), ()> {
2755
let t = T;
2856
t?; //~ ERROR the `?` operator can only be applied to values that implement `std::ops::Try`
57+
58+
let _: i32 = tuple().0; //~ ERROR no field `0`
59+
60+
let _: i32 = struct_().a; //~ ERROR no field `a`
61+
62+
struct_().method(); //~ ERROR no method named
63+
2964
Ok(())
3065
}
3166

67+
async fn match_() {
68+
match tuple() {
69+
Tuple(_) => {} //~ ERROR mismatched types
70+
}
71+
}
72+
3273
fn main() {}

0 commit comments

Comments
 (0)