diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 389cf4e313216..f1f5070a732dd 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -730,7 +730,7 @@ impl<'hir> LoweringContext<'_, 'hir> { /// Desugar `.await` into: /// ```ignore (pseudo-rust) - /// match ::std::future::IntoFuture::into_future() { + /// match ().<::std::future::IntoFuture::into_future>() { /// mut __awaitee => loop { /// match unsafe { ::std::future::Future::poll( /// <::std::pin::Pin>::new_unchecked(&mut __awaitee), @@ -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() { ... }` let into_future_expr = match await_kind { - FutureKind::Future => self.expr_call_lang_item_fn( - span, - hir::LangItem::IntoFutureIntoFuture, - arena_vec![self; *expr], - ), + // `match ().<::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, diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index e9752d7a4a8a4..c2b36fbb307be 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -182,8 +182,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_expr: &'tcx hir::Expr<'tcx>, args: &'tcx [hir::Expr<'tcx>], ) -> Result, 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); @@ -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(); } @@ -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 }); @@ -255,13 +255,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr: &'tcx hir::Expr<'tcx>, self_expr: &'tcx hir::Expr<'tcx>, ) -> Result, 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) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 4e63600dbdffa..32b5aea593955 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -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> { @@ -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) }) @@ -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, diff --git a/tests/ui/async-await/await-into-future-autoref.rs b/tests/ui/async-await/await-into-future-autoref.rs new file mode 100644 index 0000000000000..da545ca659534 --- /dev/null +++ b/tests/ui/async-await/await-into-future-autoref.rs @@ -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>>; + + 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>>; + + 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()); +}