about summary refs log tree commit diff
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
parent5572759b8d7012fa34eba47f4885c76fa06d9251 (diff)
downloadrust-7ee97f93dabd0be0cfbee437c42dd6555f7bdfd4.tar.gz
rust-7ee97f93dabd0be0cfbee437c42dd6555f7bdfd4.zip
Delegation: support coercion for target expression
-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
-rw-r--r--tests/ui/delegation/bad-resolve.rs3
-rw-r--r--tests/ui/delegation/bad-resolve.stderr24
-rw-r--r--tests/ui/delegation/explicit-paths-pass.rs4
-rw-r--r--tests/ui/delegation/explicit-paths.rs4
-rw-r--r--tests/ui/delegation/explicit-paths.stderr9
-rw-r--r--tests/ui/delegation/ice-issue-122550.stderr25
-rw-r--r--tests/ui/delegation/method-call-choice.rs25
-rw-r--r--tests/ui/delegation/method-call-choice.stderr21
-rw-r--r--tests/ui/delegation/method-call-priority.rs34
-rw-r--r--tests/ui/delegation/self-coercion.rs26
13 files changed, 264 insertions, 37 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)
         })
diff --git a/tests/ui/delegation/bad-resolve.rs b/tests/ui/delegation/bad-resolve.rs
index f378e05304b..f15e6aa81af 100644
--- a/tests/ui/delegation/bad-resolve.rs
+++ b/tests/ui/delegation/bad-resolve.rs
@@ -34,6 +34,9 @@ impl Trait for S {
 
     reuse foo { &self.0 }
     //~^ ERROR cannot find function `foo` in this scope
+    reuse Trait::foo2 { self.0 }
+    //~^ ERROR cannot find function `foo2` in trait `Trait`
+    //~| ERROR method `foo2` is not a member of trait `Trait`
 }
 
 mod prefix {}
diff --git a/tests/ui/delegation/bad-resolve.stderr b/tests/ui/delegation/bad-resolve.stderr
index 883ff523bcf..32d2f3b26cb 100644
--- a/tests/ui/delegation/bad-resolve.stderr
+++ b/tests/ui/delegation/bad-resolve.stderr
@@ -25,6 +25,15 @@ LL |     reuse <F as Trait>::baz;
    |     |                   help: there is an associated function with a similar name: `bar`
    |     not a member of trait `Trait`
 
+error[E0407]: method `foo2` is not a member of trait `Trait`
+  --> $DIR/bad-resolve.rs:37:5
+   |
+LL |     reuse Trait::foo2 { self.0 }
+   |     ^^^^^^^^^^^^^----^^^^^^^^^^^
+   |     |            |
+   |     |            help: there is an associated function with a similar name: `foo`
+   |     not a member of trait `Trait`
+
 error[E0423]: expected function, found associated constant `Trait::C`
   --> $DIR/bad-resolve.rs:24:11
    |
@@ -54,6 +63,15 @@ error[E0425]: cannot find function `foo` in this scope
 LL |     reuse foo { &self.0 }
    |           ^^^ not found in this scope
 
+error[E0425]: cannot find function `foo2` in trait `Trait`
+  --> $DIR/bad-resolve.rs:37:18
+   |
+LL |     fn foo(&self, x: i32) -> i32 { x }
+   |     ---------------------------- similarly named associated function `foo` defined here
+...
+LL |     reuse Trait::foo2 { self.0 }
+   |                  ^^^^ help: an associated function with a similar name exists: `foo`
+
 error[E0046]: not all trait items implemented, missing: `Type`
   --> $DIR/bad-resolve.rs:22:1
    |
@@ -64,18 +82,18 @@ LL | impl Trait for S {
    | ^^^^^^^^^^^^^^^^ missing `Type` in implementation
 
 error[E0433]: failed to resolve: use of undeclared crate or module `unresolved_prefix`
-  --> $DIR/bad-resolve.rs:40:7
+  --> $DIR/bad-resolve.rs:43:7
    |
 LL | reuse unresolved_prefix::{a, b, c};
    |       ^^^^^^^^^^^^^^^^^ use of undeclared crate or module `unresolved_prefix`
 
 error[E0433]: failed to resolve: `crate` in paths can only be used in start position
-  --> $DIR/bad-resolve.rs:41:29
+  --> $DIR/bad-resolve.rs:44:29
    |
 LL | reuse prefix::{self, super, crate};
    |                             ^^^^^ `crate` in paths can only be used in start position
 
-error: aborting due to 10 previous errors
+error: aborting due to 12 previous errors
 
 Some errors have detailed explanations: E0046, E0324, E0407, E0423, E0425, E0433, E0575, E0576.
 For more information about an error, try `rustc --explain E0046`.
diff --git a/tests/ui/delegation/explicit-paths-pass.rs b/tests/ui/delegation/explicit-paths-pass.rs
index fada793bd11..dd0ee2c732f 100644
--- a/tests/ui/delegation/explicit-paths-pass.rs
+++ b/tests/ui/delegation/explicit-paths-pass.rs
@@ -24,8 +24,8 @@ reuse to_reuse::zero_args { self }
 
 struct S(F);
 impl Trait for S {
-    reuse Trait::bar { &self.0 }
-    reuse Trait::description { &self.0 }
+    reuse Trait::bar { self.0 }
+    reuse Trait::description { self.0 }
     reuse <F as Trait>::static_method;
     reuse <F as Trait>::static_method2 { S::static_method(self) }
 }
diff --git a/tests/ui/delegation/explicit-paths.rs b/tests/ui/delegation/explicit-paths.rs
index a91ca4cb931..d42e305b252 100644
--- a/tests/ui/delegation/explicit-paths.rs
+++ b/tests/ui/delegation/explicit-paths.rs
@@ -34,7 +34,7 @@ mod inherent_impl_assoc_fn_to_other {
     use crate::*;
 
     impl S {
-        reuse Trait::foo1 { &self.0 }
+        reuse Trait::foo1 { self.0 }
         reuse <S as Trait>::foo2;
         reuse to_reuse::foo3;
         reuse F::foo4 { &self.0 }
@@ -46,7 +46,7 @@ mod trait_impl_assoc_fn_to_other {
     use crate::*;
 
     impl Trait for S {
-        reuse Trait::foo1 { &self.0 }
+        reuse Trait::foo1 { self.0 }
         reuse <F as Trait>::foo2;
         reuse to_reuse::foo3;
         //~^ ERROR method `foo3` is not a member of trait `Trait`
diff --git a/tests/ui/delegation/explicit-paths.stderr b/tests/ui/delegation/explicit-paths.stderr
index d33c5da4377..b5afe19f878 100644
--- a/tests/ui/delegation/explicit-paths.stderr
+++ b/tests/ui/delegation/explicit-paths.stderr
@@ -91,10 +91,17 @@ error[E0308]: mismatched types
 LL |     trait Trait2 : Trait {
    |     -------------------- found this type parameter
 LL |         reuse <F as Trait>::foo1 { self }
-   |                                    ^^^^ expected `&F`, found `&Self`
+   |                             ----   ^^^^ expected `&F`, found `&Self`
+   |                             |
+   |                             arguments to this function are incorrect
    |
    = note: expected reference `&F`
               found reference `&Self`
+note: method defined here
+  --> $DIR/explicit-paths.rs:5:8
+   |
+LL |     fn foo1(&self, x: i32) -> i32 { x }
+   |        ^^^^ -----
 
 error[E0277]: the trait bound `S2: Trait` is not satisfied
   --> $DIR/explicit-paths.rs:78:16
diff --git a/tests/ui/delegation/ice-issue-122550.stderr b/tests/ui/delegation/ice-issue-122550.stderr
index c92170644e7..1a01bee3e1e 100644
--- a/tests/ui/delegation/ice-issue-122550.stderr
+++ b/tests/ui/delegation/ice-issue-122550.stderr
@@ -4,15 +4,6 @@ error[E0308]: mismatched types
 LL |     fn description(&self) -> &str {}
    |                                   ^^ expected `&str`, found `()`
 
-error[E0308]: mismatched types
-  --> $DIR/ice-issue-122550.rs:13:39
-   |
-LL |     reuse <S as Trait>::description { &self.0 }
-   |                                       ^^^^^^^ expected `&S`, found `&F`
-   |
-   = note: expected reference `&S`
-              found reference `&F`
-
 error[E0277]: the trait bound `S: Trait` is not satisfied
   --> $DIR/ice-issue-122550.rs:13:12
    |
@@ -25,6 +16,22 @@ help: this trait has no implementations, consider adding one
 LL | trait Trait {
    | ^^^^^^^^^^^
 
+error[E0308]: mismatched types
+  --> $DIR/ice-issue-122550.rs:13:39
+   |
+LL |     reuse <S as Trait>::description { &self.0 }
+   |                         -----------   ^^^^^^^ expected `&S`, found `&F`
+   |                         |
+   |                         arguments to this function are incorrect
+   |
+   = note: expected reference `&S`
+              found reference `&F`
+note: method defined here
+  --> $DIR/ice-issue-122550.rs:5:8
+   |
+LL |     fn description(&self) -> &str {}
+   |        ^^^^^^^^^^^ -----
+
 error: aborting due to 3 previous errors
 
 Some errors have detailed explanations: E0277, E0308.
diff --git a/tests/ui/delegation/method-call-choice.rs b/tests/ui/delegation/method-call-choice.rs
new file mode 100644
index 00000000000..8d53d8bfdb7
--- /dev/null
+++ b/tests/ui/delegation/method-call-choice.rs
@@ -0,0 +1,25 @@
+#![feature(fn_delegation)]
+#![allow(incomplete_features)]
+
+trait Trait {
+    fn foo(&self) {}
+}
+
+struct F;
+impl Trait for F {}
+struct S(F);
+
+pub mod to_reuse {
+    use crate::F;
+
+    pub fn foo(_: &F) {}
+}
+
+impl Trait for S {
+    // Make sure that the method call is not generated if the path resolution
+    // does not have a `self` parameter.
+    reuse to_reuse::foo { self.0 }
+    //~^ ERROR mismatched types
+}
+
+fn main() {}
diff --git a/tests/ui/delegation/method-call-choice.stderr b/tests/ui/delegation/method-call-choice.stderr
new file mode 100644
index 00000000000..6757af20a6b
--- /dev/null
+++ b/tests/ui/delegation/method-call-choice.stderr
@@ -0,0 +1,21 @@
+error[E0308]: mismatched types
+  --> $DIR/method-call-choice.rs:21:27
+   |
+LL |     reuse to_reuse::foo { self.0 }
+   |                     ---   ^^^^^^ expected `&F`, found `F`
+   |                     |
+   |                     arguments to this function are incorrect
+   |
+note: function defined here
+  --> $DIR/method-call-choice.rs:15:12
+   |
+LL |     pub fn foo(_: &F) {}
+   |            ^^^ -----
+help: consider borrowing here
+   |
+LL |     reuse to_reuse::foo { &self.0 }
+   |                           +
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/delegation/method-call-priority.rs b/tests/ui/delegation/method-call-priority.rs
new file mode 100644
index 00000000000..8d68740d181
--- /dev/null
+++ b/tests/ui/delegation/method-call-priority.rs
@@ -0,0 +1,34 @@
+//@ run-pass
+
+#![feature(fn_delegation)]
+#![allow(incomplete_features)]
+#![allow(dead_code)]
+
+trait Trait1 {
+    fn foo(&self) -> i32 { 1 }
+}
+
+trait Trait2 {
+    fn foo(&self) -> i32 { 2 }
+}
+
+struct F;
+impl Trait1 for F {}
+impl Trait2 for F {}
+
+impl F {
+    fn foo(&self) -> i32 { 3 }
+}
+
+struct S(F);
+
+impl Trait1 for S {
+    // Make sure that the generated `self.0.foo()` does not turn into the inherent method `F::foo`
+    // that has a higher priority than methods from traits.
+    reuse Trait1::foo { self.0 }
+}
+
+fn main() {
+    let s = S(F);
+    assert_eq!(s.foo(), 1);
+}
diff --git a/tests/ui/delegation/self-coercion.rs b/tests/ui/delegation/self-coercion.rs
new file mode 100644
index 00000000000..96c1f1b140b
--- /dev/null
+++ b/tests/ui/delegation/self-coercion.rs
@@ -0,0 +1,26 @@
+//@ run-pass
+
+#![feature(fn_delegation)]
+#![allow(incomplete_features)]
+
+trait Trait : Sized {
+    fn by_value(self) -> i32 { 1 }
+    fn by_mut_ref(&mut self) -> i32 { 2 }
+    fn by_ref(&self) -> i32 { 3 }
+}
+
+struct F;
+impl Trait for F {}
+
+struct S(F);
+
+impl Trait for S {
+    reuse Trait::{by_value, by_mut_ref, by_ref} { self.0 }
+}
+
+fn main() {
+    let mut s = S(F);
+    assert_eq!(s.by_ref(), 3);
+    assert_eq!(s.by_mut_ref(), 2);
+    assert_eq!(s.by_value(), 1);
+}