about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorBryanskiy <ivakin.kir@gmail.com>2024-06-18 14:04:28 +0300
committerBryanskiy <ivakin.kir@gmail.com>2024-07-16 18:03:15 +0300
commit7ee97f93dabd0be0cfbee437c42dd6555f7bdfd4 (patch)
tree5321c3a7f87c89b52c24168038ec19be78a347ae /compiler
parent5572759b8d7012fa34eba47f4885c76fa06d9251 (diff)
downloadrust-7ee97f93dabd0be0cfbee437c42dd6555f7bdfd4.tar.gz
rust-7ee97f93dabd0be0cfbee437c42dd6555f7bdfd4.zip
Delegation: support coercion for target expression
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast_lowering/src/delegation.rs84
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs9
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs33
3 files changed, 106 insertions, 20 deletions
diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs
index 678cac210f4..6df2c15ce60 100644
--- a/compiler/rustc_ast_lowering/src/delegation.rs
+++ b/compiler/rustc_ast_lowering/src/delegation.rs
@@ -38,7 +38,7 @@
 
 use crate::{ImplTraitPosition, ResolverAstLoweringExt};
 
-use super::{ImplTraitContext, LoweringContext, ParamMode};
+use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
 
 use ast::visit::Visitor;
 use hir::def::{DefKind, PartialRes, Res};
@@ -259,8 +259,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         self_param_id: pat_node_id,
                     };
                     self_resolver.visit_block(block);
-                    let block = this.lower_block(block, false);
-                    this.mk_expr(hir::ExprKind::Block(block, None), block.span)
+                    this.lower_target_expr(&block)
                 } else {
                     let pat_hir_id = this.lower_node_id(pat_node_id);
                     this.generate_arg(pat_hir_id, span)
@@ -273,26 +272,81 @@ impl<'hir> LoweringContext<'_, 'hir> {
         })
     }
 
-    // Generates fully qualified call for the resulting body.
+    // FIXME(fn_delegation): Alternatives for target expression lowering:
+    // https://github.com/rust-lang/rfcs/pull/3530#issuecomment-2197170600.
+    fn lower_target_expr(&mut self, block: &Block) -> hir::Expr<'hir> {
+        if block.stmts.len() == 1
+            && let StmtKind::Expr(expr) = &block.stmts[0].kind
+        {
+            return self.lower_expr_mut(expr);
+        }
+
+        let block = self.lower_block(block, false);
+        self.mk_expr(hir::ExprKind::Block(block, None), block.span)
+    }
+
+    // Generates expression for the resulting body. If possible, `MethodCall` is used
+    // to allow autoref/autoderef for target expression. For example in:
+    //
+    // trait Trait : Sized {
+    //     fn by_value(self) -> i32 { 1 }
+    //     fn by_mut_ref(&mut self) -> i32 { 2 }
+    //     fn by_ref(&self) -> i32 { 3 }
+    // }
+    //
+    // struct NewType(SomeType);
+    // impl Trait for NewType {
+    //     reuse Trait::* { self.0 }
+    // }
+    //
+    // `self.0` will automatically coerce.
     fn finalize_body_lowering(
         &mut self,
         delegation: &Delegation,
         args: Vec<hir::Expr<'hir>>,
         span: Span,
     ) -> hir::Expr<'hir> {
-        let path = self.lower_qpath(
-            delegation.id,
-            &delegation.qself,
-            &delegation.path,
-            ParamMode::Optional,
-            ImplTraitContext::Disallowed(ImplTraitPosition::Path),
-            None,
-        );
-
         let args = self.arena.alloc_from_iter(args);
-        let path_expr = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span));
-        let call = self.arena.alloc(self.mk_expr(hir::ExprKind::Call(path_expr, args), span));
 
+        let has_generic_args =
+            delegation.path.segments.iter().rev().skip(1).any(|segment| segment.args.is_some());
+
+        let call = if self
+            .get_resolution_id(delegation.id, span)
+            .and_then(|def_id| Ok(self.has_self(def_id, span)))
+            .unwrap_or_default()
+            && delegation.qself.is_none()
+            && !has_generic_args
+        {
+            let ast_segment = delegation.path.segments.last().unwrap();
+            let segment = self.lower_path_segment(
+                delegation.path.span,
+                ast_segment,
+                ParamMode::Optional,
+                ParenthesizedGenericArgs::Err,
+                ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+                None,
+            );
+            let segment = self.arena.alloc(segment);
+
+            self.arena.alloc(hir::Expr {
+                hir_id: self.next_id(),
+                kind: hir::ExprKind::MethodCall(segment, &args[0], &args[1..], span),
+                span,
+            })
+        } else {
+            let path = self.lower_qpath(
+                delegation.id,
+                &delegation.qself,
+                &delegation.path,
+                ParamMode::Optional,
+                ImplTraitContext::Disallowed(ImplTraitPosition::Path),
+                None,
+            );
+
+            let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(path), span));
+            self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span))
+        };
         let block = self.arena.alloc(hir::Block {
             stmts: &[],
             expr: Some(call),
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index dc1b888374c..daf4ef5cdb3 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -182,8 +182,13 @@ 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 scope = if let Some(only_method) = segment.res.opt_def_id() {
+            ProbeScope::Single(only_method)
+        } else {
+            ProbeScope::TraitsInScope
+        };
+
+        let pick = self.lookup_probe(segment.ident, self_ty, call_expr, scope)?;
 
         self.lint_edition_dependent_dot_call(
             self_ty, segment, span, call_expr, self_expr, &pick, args,
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 6a7af5510e0..1c308d4af3e 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -20,6 +20,7 @@ use rustc_middle::middle::stability;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::fast_reject::{simplify_type, TreatParams};
 use rustc_middle::ty::AssocItem;
+use rustc_middle::ty::AssocItemContainer;
 use rustc_middle::ty::GenericParamDefKind;
 use rustc_middle::ty::Upcast;
 use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt};
@@ -216,6 +217,9 @@ pub enum Mode {
 
 #[derive(PartialEq, Eq, Copy, Clone, Debug)]
 pub enum ProbeScope {
+    // Single candidate coming from pre-resolved delegation method.
+    Single(DefId),
+
     // Assemble candidates coming only from traits in scope.
     TraitsInScope,
 
@@ -480,12 +484,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 is_suggestion,
             );
 
-            probe_cx.assemble_inherent_candidates();
             match scope {
                 ProbeScope::TraitsInScope => {
-                    probe_cx.assemble_extension_candidates_for_traits_in_scope()
+                    probe_cx.assemble_inherent_candidates();
+                    probe_cx.assemble_extension_candidates_for_traits_in_scope();
+                }
+                ProbeScope::AllTraits => {
+                    probe_cx.assemble_inherent_candidates();
+                    probe_cx.assemble_extension_candidates_for_all_traits();
+                }
+                ProbeScope::Single(def_id) => {
+                    let item = self.tcx.associated_item(def_id);
+                    // FIXME(fn_delegation): Delegation to inherent methods is not yet supported.
+                    assert_eq!(item.container, AssocItemContainer::TraitContainer);
+
+                    let trait_def_id = self.tcx.parent(def_id);
+                    let trait_span = self.tcx.def_span(trait_def_id);
+
+                    let trait_args = self.fresh_args_for_item(trait_span, trait_def_id);
+                    let trait_ref = ty::TraitRef::new_from_args(self.tcx, trait_def_id, trait_args);
+
+                    probe_cx.push_candidate(
+                        Candidate {
+                            item,
+                            kind: CandidateKind::TraitCandidate(ty::Binder::dummy(trait_ref)),
+                            import_ids: smallvec![],
+                        },
+                        false,
+                    );
                 }
-                ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits(),
             };
             op(probe_cx)
         })