diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 11b5131b8d788..ef2aafc964988 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -860,7 +860,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), @@ -1020,12 +1020,28 @@ 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 = self.expr_call_lang_item_fn( + // `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. + let into_future_id = + self.tcx.require_lang_item(hir::LangItem::IntoFutureIntoFuture, Some(await_kw_span)); + + let segment_hid = self.next_id(); + + let into_future_expr = self.arena.alloc(self.expr( span, - hir::LangItem::IntoFutureIntoFuture, - arena_vec![self; expr], - ); + 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), + &[], + await_kw_span, + ), + )); // match { // mut __awaitee => loop { .. } diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index f820835acef96..31e3e87c53deb 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -183,8 +183,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); @@ -207,12 +208,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::TypeAndMut { ty: *t_type, mutbl: 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(); } @@ -221,29 +217,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 }); @@ -260,13 +260,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 fe2d43a3c9244..0b11ee1580523 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> { @@ -498,6 +501,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) }) @@ -920,6 +924,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..295d4d0335965 --- /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()); +}