Skip to content

Commit

Permalink
Autoref/deref for .await
Browse files Browse the repository at this point in the history
  • Loading branch information
Jules-Bertholet committed Mar 25, 2024
1 parent 60b5ca6 commit 62df79d
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 44 deletions.
34 changes: 27 additions & 7 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,7 @@ impl<'hir> LoweringContext<'_, 'hir> {

/// Desugar `<expr>.await` into:
/// ```ignore (pseudo-rust)
/// match ::std::future::IntoFuture::into_future(<expr>) {
/// match (<expr>).<::std::future::IntoFuture::into_future>() {
/// mut __awaitee => loop {
/// match unsafe { ::std::future::Future::poll(
/// <::std::pin::Pin>::new_unchecked(&mut __awaitee),
Expand Down Expand Up @@ -930,13 +930,33 @@ impl<'hir> LoweringContext<'_, 'hir> {
// mut __awaitee => loop { ... }
let awaitee_arm = self.arm(awaitee_pat, loop_expr);

// `match ::std::future::IntoFuture::into_future(<expr>) { ... }`
let into_future_expr = match await_kind {
FutureKind::Future => self.expr_call_lang_item_fn(
span,
hir::LangItem::IntoFutureIntoFuture,
arena_vec![self; *expr],
),
// `match (<expr>).<::std::future::IntoFuture::into_future>() { ... }`
// The `into_future` call is a little special,
// it resolves directly to the `into_future` lang item
// but goes through autoref/autoderef like any method call.
FutureKind::Future => {
let into_future_id = self
.tcx
.require_lang_item(hir::LangItem::IntoFutureIntoFuture, Some(await_kw_span));

let segment_hid = self.next_id();

self.arena.alloc(self.expr(
span,
hir::ExprKind::MethodCall(
self.arena.alloc(hir::PathSegment::new(
Ident::new(sym::into_future, await_kw_span),
segment_hid,
Res::Def(DefKind::AssocFn, into_future_id),
)),
self.arena.alloc(expr),
&[],
span,
),
))
}

// Not needed for `for await` because we expect to have already called
// `IntoAsyncIterator::into_async_iter` on it.
FutureKind::AsyncIterator => expr,
Expand Down
72 changes: 35 additions & 37 deletions compiler/rustc_hir_typeck/src/method/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self_expr: &'tcx hir::Expr<'tcx>,
args: &'tcx [hir::Expr<'tcx>],
) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
let pick =
self.lookup_probe(segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?;
let probe_scope =
segment.res.opt_def_id().map_or(ProbeScope::TraitsInScope, ProbeScope::DefId);
let pick = self.lookup_probe(segment.ident, self_ty, call_expr, probe_scope)?;

self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args);

Expand All @@ -202,12 +203,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if let ty::Ref(region, t_type, mutability) = self_ty.kind() {
let trait_type = Ty::new_ref(self.tcx, *region, *t_type, mutability.invert());
// We probe again to see if there might be a borrow mutability discrepancy.
match self.lookup_probe(
segment.ident,
trait_type,
call_expr,
ProbeScope::TraitsInScope,
) {
match self.lookup_probe(segment.ident, trait_type, call_expr, probe_scope) {
Ok(ref new_pick) if pick.differs_from(new_pick) => {
needs_mut = new_pick.self_ty.ref_mutability() != self_ty.ref_mutability();
}
Expand All @@ -216,29 +212,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

// We probe again, taking all traits into account (not only those in scope).
let candidates = match self.lookup_probe_for_diagnostic(
segment.ident,
self_ty,
call_expr,
ProbeScope::AllTraits,
None,
) {
// If we find a different result the caller probably forgot to import a trait.
Ok(ref new_pick) if pick.differs_from(new_pick) => {
vec![new_pick.item.container_id(self.tcx)]
let candidates = if probe_scope == ProbeScope::TraitsInScope {
match self.lookup_probe_for_diagnostic(
segment.ident,
self_ty,
call_expr,
ProbeScope::AllTraits,
None,
) {
// If we find a different result the caller probably forgot to import a trait.
Ok(ref new_pick) if pick.differs_from(new_pick) => {
vec![new_pick.item.container_id(self.tcx)]
}
Err(Ambiguity(ref sources)) => sources
.iter()
.filter_map(|source| {
match *source {
// Note: this cannot come from an inherent impl,
// because the first probing succeeded.
CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
CandidateSource::Trait(_) => None,
}
})
.collect(),
_ => Vec::new(),
}
Err(Ambiguity(ref sources)) => sources
.iter()
.filter_map(|source| {
match *source {
// Note: this cannot come from an inherent impl,
// because the first probing succeeded.
CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def),
CandidateSource::Trait(_) => None,
}
})
.collect(),
_ => Vec::new(),
} else {
Vec::new()
};

return Err(IllegalSizedBound { candidates, needs_mut, bound_span: span, self_expr });
Expand All @@ -255,13 +255,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
call_expr: &'tcx hir::Expr<'tcx>,
self_expr: &'tcx hir::Expr<'tcx>,
) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
let pick = self.lookup_probe_for_diagnostic(
segment.ident,
self_ty,
call_expr,
ProbeScope::TraitsInScope,
None,
)?;
let probe_scope =
segment.res.opt_def_id().map_or(ProbeScope::TraitsInScope, ProbeScope::DefId);

let pick =
self.lookup_probe_for_diagnostic(segment.ident, self_ty, call_expr, probe_scope, None)?;

Ok(self
.confirm_method_for_diagnostic(span, self_expr, call_expr, self_ty, &pick, segment)
Expand Down
24 changes: 24 additions & 0 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ pub enum ProbeScope {

// Assemble candidates coming from all traits.
AllTraits,

// Consider only the given `DefId`.
DefId(DefId),
}

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Expand Down Expand Up @@ -495,6 +498,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
probe_cx.assemble_extension_candidates_for_traits_in_scope()
}
ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits(),
ProbeScope::DefId(id) => probe_cx.assemble_extension_candidate_for_def_id(id),
};
op(probe_cx)
})
Expand Down Expand Up @@ -919,6 +923,26 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
}
}

fn assemble_extension_candidate_for_def_id(&mut self, fn_id: DefId) {
let item: AssocItem = self.tcx.associated_item(fn_id);
let trait_def_id = item.trait_container(self.tcx).expect("method should have a trait");
let trait_args = self.fresh_args_for_item(self.span, trait_def_id);
let trait_ref = ty::TraitRef::new(self.tcx, trait_def_id, trait_args);
let (xform_self_ty, xform_ret_ty) =
self.xform_self_ty(item, trait_ref.self_ty(), trait_args);

self.push_candidate(
Candidate {
xform_self_ty,
xform_ret_ty,
item,
import_ids: smallvec![],
kind: TraitCandidate(trait_ref),
},
false,
);
}

fn matches_return_type(
&self,
method: ty::AssocItem,
Expand Down
44 changes: 44 additions & 0 deletions tests/ui/async-await/await-into-future-autoref.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// run-pass
// aux-build: issue-72470-lib.rs
// edition:2021
extern crate issue_72470_lib;
use std::{future::{Future, IntoFuture}, pin::Pin};

struct AwaitMe;

impl IntoFuture for &AwaitMe {
type Output = i32;
type IntoFuture = Pin<Box<dyn Future<Output = i32>>>;

fn into_future(self) -> Self::IntoFuture {
Box::pin(me())
}
}

async fn me() -> i32 {
41
}

async fn run() {
assert_eq!(AwaitMe.await, 41);
}

struct AwaitMeToo;

impl IntoFuture for &mut AwaitMeToo {
type Output = i32;
type IntoFuture = Pin<Box<dyn Future<Output = i32>>>;

fn into_future(self) -> Self::IntoFuture {
Box::pin(me())
}
}

async fn run_too() {
assert_eq!(AwaitMeToo.await, 41);
}

fn main() {
issue_72470_lib::run(run());
issue_72470_lib::run(run_too());
}

0 comments on commit 62df79d

Please sign in to comment.